]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/xen/privcmd/privcmd.c
Update llvm, clang and lldb to 3.7.0 release.
[FreeBSD/FreeBSD.git] / sys / dev / xen / privcmd / privcmd.c
1 /*
2  * Copyright (c) 2014 Roger Pau MonnĂ© <roger.pau@citrix.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/uio.h>
33 #include <sys/bus.h>
34 #include <sys/malloc.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/mutex.h>
38 #include <sys/rwlock.h>
39 #include <sys/selinfo.h>
40 #include <sys/poll.h>
41 #include <sys/conf.h>
42 #include <sys/fcntl.h>
43 #include <sys/ioccom.h>
44 #include <sys/rman.h>
45 #include <sys/tree.h>
46 #include <sys/module.h>
47 #include <sys/proc.h>
48
49 #include <vm/vm.h>
50 #include <vm/vm_param.h>
51 #include <vm/vm_extern.h>
52 #include <vm/vm_kern.h>
53 #include <vm/vm_page.h>
54 #include <vm/vm_map.h>
55 #include <vm/vm_object.h>
56 #include <vm/vm_pager.h>
57 #include <vm/vm_phys.h>
58
59 #include <machine/md_var.h>
60
61 #include <xen/xen-os.h>
62 #include <xen/hypervisor.h>
63 #include <xen/privcmd.h>
64 #include <xen/error.h>
65
66 MALLOC_DEFINE(M_PRIVCMD, "privcmd_dev", "Xen privcmd user-space device");
67
68 struct privcmd_map {
69         vm_object_t mem;
70         vm_size_t size;
71         struct resource *pseudo_phys_res;
72         int pseudo_phys_res_id;
73         vm_paddr_t phys_base_addr;
74         boolean_t mapped;
75         int *errs;
76 };
77
78 static d_ioctl_t     privcmd_ioctl;
79 static d_mmap_single_t  privcmd_mmap_single;
80
81 static struct cdevsw privcmd_devsw = {
82         .d_version = D_VERSION,
83         .d_ioctl = privcmd_ioctl,
84         .d_mmap_single = privcmd_mmap_single,
85         .d_name = "privcmd",
86 };
87
88 static int privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
89     vm_ooffset_t foff, struct ucred *cred, u_short *color);
90 static void privcmd_pg_dtor(void *handle);
91 static int privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
92     int prot, vm_page_t *mres);
93
94 static struct cdev_pager_ops privcmd_pg_ops = {
95         .cdev_pg_fault = privcmd_pg_fault,
96         .cdev_pg_ctor = privcmd_pg_ctor,
97         .cdev_pg_dtor = privcmd_pg_dtor,
98 };
99
100 static device_t privcmd_dev = NULL;
101
102 /*------------------------- Privcmd Pager functions --------------------------*/
103 static int
104 privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
105     vm_ooffset_t foff, struct ucred *cred, u_short *color)
106 {
107
108         return (0);
109 }
110
111 static void
112 privcmd_pg_dtor(void *handle)
113 {
114         struct xen_remove_from_physmap rm = { .domid = DOMID_SELF };
115         struct privcmd_map *map = handle;
116         int error;
117         vm_size_t i;
118         vm_page_t m;
119
120         /*
121          * Remove the mappings from the used pages. This will remove the
122          * underlying p2m bindings in Xen second stage translation.
123          */
124         if (map->mapped == true) {
125                 VM_OBJECT_WLOCK(map->mem);
126 retry:
127                 for (i = 0; i < map->size; i++) {
128                         m = vm_page_lookup(map->mem, i);
129                         if (m == NULL)
130                                 continue;
131                         if (vm_page_sleep_if_busy(m, "pcmdum"))
132                                 goto retry;
133                         cdev_pager_free_page(map->mem, m);
134                 }
135                 VM_OBJECT_WUNLOCK(map->mem);
136
137                 for (i = 0; i < map->size; i++) {
138                         rm.gpfn = atop(map->phys_base_addr) + i;
139                         HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rm);
140                 }
141                 free(map->errs, M_PRIVCMD);
142         }
143
144         error = xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
145             map->pseudo_phys_res);
146         KASSERT(error == 0, ("Unable to release memory resource: %d", error));
147
148         free(map, M_PRIVCMD);
149 }
150
151 static int
152 privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
153     int prot, vm_page_t *mres)
154 {
155         struct privcmd_map *map = object->handle;
156         vm_pindex_t pidx;
157         vm_page_t page, oldm;
158
159         if (map->mapped != true)
160                 return (VM_PAGER_FAIL);
161
162         pidx = OFF_TO_IDX(offset);
163         if (pidx >= map->size || map->errs[pidx] != 0)
164                 return (VM_PAGER_FAIL);
165
166         page = PHYS_TO_VM_PAGE(map->phys_base_addr + offset);
167         if (page == NULL)
168                 return (VM_PAGER_FAIL);
169
170         KASSERT((page->flags & PG_FICTITIOUS) != 0,
171             ("not fictitious %p", page));
172         KASSERT(page->wire_count == 1, ("wire_count not 1 %p", page));
173         KASSERT(vm_page_busied(page) == 0, ("page %p is busy", page));
174
175         if (*mres != NULL) {
176                 oldm = *mres;
177                 vm_page_lock(oldm);
178                 vm_page_free(oldm);
179                 vm_page_unlock(oldm);
180                 *mres = NULL;
181         }
182
183         vm_page_insert(page, object, pidx);
184         page->valid = VM_PAGE_BITS_ALL;
185         vm_page_xbusy(page);
186         *mres = page;
187         return (VM_PAGER_OK);
188 }
189
190 /*----------------------- Privcmd char device methods ------------------------*/
191 static int
192 privcmd_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
193     vm_object_t *object, int nprot)
194 {
195         struct privcmd_map *map;
196
197         map = malloc(sizeof(*map), M_PRIVCMD, M_WAITOK | M_ZERO);
198
199         map->size = OFF_TO_IDX(size);
200         map->pseudo_phys_res_id = 0;
201
202         map->pseudo_phys_res = xenmem_alloc(privcmd_dev,
203             &map->pseudo_phys_res_id, size);
204         if (map->pseudo_phys_res == NULL) {
205                 free(map, M_PRIVCMD);
206                 return (ENOMEM);
207         }
208
209         map->phys_base_addr = rman_get_start(map->pseudo_phys_res);
210         map->mem = cdev_pager_allocate(map, OBJT_MGTDEVICE, &privcmd_pg_ops,
211             size, nprot, *offset, NULL);
212         if (map->mem == NULL) {
213                 xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
214                     map->pseudo_phys_res);
215                 free(map, M_PRIVCMD);
216                 return (ENOMEM);
217         }
218
219         *object = map->mem;
220
221         return (0);
222 }
223
224 static int
225 privcmd_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
226               int mode, struct thread *td)
227 {
228         int error, i;
229
230         switch (cmd) {
231         case IOCTL_PRIVCMD_HYPERCALL: {
232                 struct ioctl_privcmd_hypercall *hcall;
233
234                 hcall = (struct ioctl_privcmd_hypercall *)arg;
235
236                 error = privcmd_hypercall(hcall->op, hcall->arg[0],
237                     hcall->arg[1], hcall->arg[2], hcall->arg[3], hcall->arg[4]);
238                 if (error >= 0) {
239                         hcall->retval = error;
240                         error = 0;
241                 } else {
242                         error = xen_translate_error(error);
243                         hcall->retval = 0;
244                 }
245                 break;
246         }
247         case IOCTL_PRIVCMD_MMAPBATCH: {
248                 struct ioctl_privcmd_mmapbatch *mmap;
249                 vm_map_t map;
250                 vm_map_entry_t entry;
251                 vm_object_t mem;
252                 vm_pindex_t index;
253                 vm_prot_t prot;
254                 boolean_t wired;
255                 struct xen_add_to_physmap_range add;
256                 xen_ulong_t *idxs;
257                 xen_pfn_t *gpfns;
258                 int *errs;
259                 struct privcmd_map *umap;
260
261                 mmap = (struct ioctl_privcmd_mmapbatch *)arg;
262
263                 if ((mmap->num == 0) ||
264                     ((mmap->addr & PAGE_MASK) != 0)) {
265                         error = EINVAL;
266                         break;
267                 }
268
269                 map = &td->td_proc->p_vmspace->vm_map;
270                 error = vm_map_lookup(&map, mmap->addr, VM_PROT_NONE, &entry,
271                     &mem, &index, &prot, &wired);
272                 if (error != KERN_SUCCESS) {
273                         error = EINVAL;
274                         break;
275                 }
276                 if ((entry->start != mmap->addr) ||
277                     (entry->end != mmap->addr + (mmap->num * PAGE_SIZE))) {
278                         vm_map_lookup_done(map, entry);
279                         error = EINVAL;
280                         break;
281                 }
282                 vm_map_lookup_done(map, entry);
283                 if ((mem->type != OBJT_MGTDEVICE) ||
284                     (mem->un_pager.devp.ops != &privcmd_pg_ops)) {
285                         error = EINVAL;
286                         break;
287                 }
288                 umap = mem->handle;
289
290                 add.domid = DOMID_SELF;
291                 add.space = XENMAPSPACE_gmfn_foreign;
292                 add.size = mmap->num;
293                 add.foreign_domid = mmap->dom;
294
295                 idxs = malloc(sizeof(*idxs) * mmap->num, M_PRIVCMD,
296                     M_WAITOK | M_ZERO);
297                 gpfns = malloc(sizeof(*gpfns) * mmap->num, M_PRIVCMD,
298                     M_WAITOK | M_ZERO);
299                 errs = malloc(sizeof(*errs) * mmap->num, M_PRIVCMD,
300                     M_WAITOK | M_ZERO);
301
302                 set_xen_guest_handle(add.idxs, idxs);
303                 set_xen_guest_handle(add.gpfns, gpfns);
304                 set_xen_guest_handle(add.errs, errs);
305
306                 error = copyin(&mmap->arr[0], idxs,
307                     sizeof(idxs[0]) * mmap->num);
308                 if (error != 0)
309                         goto mmap_out;
310
311                 for (i = 0; i < mmap->num; i++)
312                         gpfns[i] = atop(umap->phys_base_addr + i * PAGE_SIZE);
313
314                 error = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &add);
315                 if (error) {
316                         error = xen_translate_error(error);
317                         goto mmap_out;
318                 }
319
320                 for (i = 0; i < mmap->num; i++) {
321                         if (errs[i] != 0)
322                                 errs[i] = xen_translate_error(errs[i]);
323                 }
324
325                 /*
326                  * Save errs, so we know which pages have been
327                  * successfully mapped.
328                  */
329                 umap->errs = errs;
330                 umap->mapped = true;
331
332                 error = copyout(errs, &mmap->err[0],
333                     sizeof(errs[0]) * mmap->num);
334
335 mmap_out:
336                 free(idxs, M_PRIVCMD);
337                 free(gpfns, M_PRIVCMD);
338                 if (!umap->mapped)
339                         free(errs, M_PRIVCMD);
340
341                 break;
342         }
343
344         default:
345                 error = ENOSYS;
346                 break;
347         }
348
349         return (error);
350 }
351
352 /*------------------ Private Device Attachment Functions  --------------------*/
353 static void
354 privcmd_identify(driver_t *driver, device_t parent)
355 {
356
357         KASSERT(xen_domain(),
358             ("Trying to attach privcmd device on non Xen domain"));
359
360         if (BUS_ADD_CHILD(parent, 0, "privcmd", 0) == NULL)
361                 panic("unable to attach privcmd user-space device");
362 }
363
364 static int
365 privcmd_probe(device_t dev)
366 {
367
368         privcmd_dev = dev;
369         device_set_desc(dev, "Xen privileged interface user-space device");
370         return (BUS_PROBE_NOWILDCARD);
371 }
372
373 static int
374 privcmd_attach(device_t dev)
375 {
376
377         make_dev_credf(MAKEDEV_ETERNAL, &privcmd_devsw, 0, NULL, UID_ROOT,
378             GID_WHEEL, 0600, "xen/privcmd");
379         return (0);
380 }
381
382 /*-------------------- Private Device Attachment Data  -----------------------*/
383 static device_method_t privcmd_methods[] = {
384         DEVMETHOD(device_identify,      privcmd_identify),
385         DEVMETHOD(device_probe,         privcmd_probe),
386         DEVMETHOD(device_attach,        privcmd_attach),
387
388         DEVMETHOD_END
389 };
390
391 static driver_t privcmd_driver = {
392         "privcmd",
393         privcmd_methods,
394         0,
395 };
396
397 devclass_t privcmd_devclass;
398
399 DRIVER_MODULE(privcmd, xenpv, privcmd_driver, privcmd_devclass, 0, 0);
400 MODULE_DEPEND(privcmd, xenpv, 1, 1, 1);