2 * Copyright (c) 2014 Roger Pau Monné <roger.pau@citrix.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/systm.h>
34 #include <sys/malloc.h>
35 #include <sys/kernel.h>
37 #include <sys/mutex.h>
38 #include <sys/rwlock.h>
39 #include <sys/selinfo.h>
42 #include <sys/fcntl.h>
43 #include <sys/ioccom.h>
46 #include <sys/module.h>
48 #include <sys/bitset.h>
51 #include <vm/vm_param.h>
52 #include <vm/vm_extern.h>
53 #include <vm/vm_kern.h>
54 #include <vm/vm_page.h>
55 #include <vm/vm_map.h>
56 #include <vm/vm_object.h>
57 #include <vm/vm_pager.h>
58 #include <vm/vm_phys.h>
60 #include <machine/md_var.h>
62 #include <xen/xen-os.h>
63 #include <xen/hypervisor.h>
64 #include <xen/privcmd.h>
65 #include <xen/error.h>
67 MALLOC_DEFINE(M_PRIVCMD, "privcmd_dev", "Xen privcmd user-space device");
72 struct resource *pseudo_phys_res;
73 int pseudo_phys_res_id;
74 vm_paddr_t phys_base_addr;
76 BITSET_DEFINE_VAR() *err;
79 static d_ioctl_t privcmd_ioctl;
80 static d_mmap_single_t privcmd_mmap_single;
82 static struct cdevsw privcmd_devsw = {
83 .d_version = D_VERSION,
84 .d_ioctl = privcmd_ioctl,
85 .d_mmap_single = privcmd_mmap_single,
89 static int privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
90 vm_ooffset_t foff, struct ucred *cred, u_short *color);
91 static void privcmd_pg_dtor(void *handle);
92 static int privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
93 int prot, vm_page_t *mres);
95 static struct cdev_pager_ops privcmd_pg_ops = {
96 .cdev_pg_fault = privcmd_pg_fault,
97 .cdev_pg_ctor = privcmd_pg_ctor,
98 .cdev_pg_dtor = privcmd_pg_dtor,
101 static device_t privcmd_dev = NULL;
103 /*------------------------- Privcmd Pager functions --------------------------*/
105 privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
106 vm_ooffset_t foff, struct ucred *cred, u_short *color)
113 privcmd_pg_dtor(void *handle)
115 struct xen_remove_from_physmap rm = { .domid = DOMID_SELF };
116 struct privcmd_map *map = handle;
122 * Remove the mappings from the used pages. This will remove the
123 * underlying p2m bindings in Xen second stage translation.
125 if (map->mapped == true) {
126 VM_OBJECT_WLOCK(map->mem);
128 for (i = 0; i < map->size; i++) {
129 m = vm_page_lookup(map->mem, i);
132 if (vm_page_sleep_if_busy(m, "pcmdum"))
134 cdev_pager_free_page(map->mem, m);
136 VM_OBJECT_WUNLOCK(map->mem);
138 for (i = 0; i < map->size; i++) {
139 rm.gpfn = atop(map->phys_base_addr) + i;
140 HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rm);
142 free(map->err, M_PRIVCMD);
145 error = xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
146 map->pseudo_phys_res);
147 KASSERT(error == 0, ("Unable to release memory resource: %d", error));
149 free(map, M_PRIVCMD);
153 privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
154 int prot, vm_page_t *mres)
156 struct privcmd_map *map = object->handle;
158 vm_page_t page, oldm;
160 if (map->mapped != true)
161 return (VM_PAGER_FAIL);
163 pidx = OFF_TO_IDX(offset);
164 if (pidx >= map->size || BIT_ISSET(map->size, pidx, map->err))
165 return (VM_PAGER_FAIL);
167 page = PHYS_TO_VM_PAGE(map->phys_base_addr + offset);
169 return (VM_PAGER_FAIL);
171 KASSERT((page->flags & PG_FICTITIOUS) != 0,
172 ("not fictitious %p", page));
173 KASSERT(page->wire_count == 1, ("wire_count not 1 %p", page));
174 KASSERT(vm_page_busied(page) == 0, ("page %p is busy", page));
180 vm_page_unlock(oldm);
184 vm_page_insert(page, object, pidx);
185 page->valid = VM_PAGE_BITS_ALL;
188 return (VM_PAGER_OK);
191 /*----------------------- Privcmd char device methods ------------------------*/
193 privcmd_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
194 vm_object_t *object, int nprot)
196 struct privcmd_map *map;
198 map = malloc(sizeof(*map), M_PRIVCMD, M_WAITOK | M_ZERO);
200 map->size = OFF_TO_IDX(size);
201 map->pseudo_phys_res_id = 0;
203 map->pseudo_phys_res = xenmem_alloc(privcmd_dev,
204 &map->pseudo_phys_res_id, size);
205 if (map->pseudo_phys_res == NULL) {
206 free(map, M_PRIVCMD);
210 map->phys_base_addr = rman_get_start(map->pseudo_phys_res);
211 map->mem = cdev_pager_allocate(map, OBJT_MGTDEVICE, &privcmd_pg_ops,
212 size, nprot, *offset, NULL);
213 if (map->mem == NULL) {
214 xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
215 map->pseudo_phys_res);
216 free(map, M_PRIVCMD);
226 privcmd_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
227 int mode, struct thread *td)
232 case IOCTL_PRIVCMD_HYPERCALL: {
233 struct ioctl_privcmd_hypercall *hcall;
235 hcall = (struct ioctl_privcmd_hypercall *)arg;
237 error = privcmd_hypercall(hcall->op, hcall->arg[0],
238 hcall->arg[1], hcall->arg[2], hcall->arg[3], hcall->arg[4]);
240 hcall->retval = error;
243 error = xen_translate_error(error);
248 case IOCTL_PRIVCMD_MMAPBATCH: {
249 struct ioctl_privcmd_mmapbatch *mmap;
251 vm_map_entry_t entry;
256 struct xen_add_to_physmap_range add;
260 struct privcmd_map *umap;
263 mmap = (struct ioctl_privcmd_mmapbatch *)arg;
265 if ((mmap->num == 0) ||
266 ((mmap->addr & PAGE_MASK) != 0)) {
271 map = &td->td_proc->p_vmspace->vm_map;
272 error = vm_map_lookup(&map, mmap->addr, VM_PROT_NONE, &entry,
273 &mem, &pindex, &prot, &wired);
274 if (error != KERN_SUCCESS) {
278 if ((entry->start != mmap->addr) ||
279 (entry->end != mmap->addr + (mmap->num * PAGE_SIZE))) {
280 vm_map_lookup_done(map, entry);
284 vm_map_lookup_done(map, entry);
285 if ((mem->type != OBJT_MGTDEVICE) ||
286 (mem->un_pager.devp.ops != &privcmd_pg_ops)) {
292 add.domid = DOMID_SELF;
293 add.space = XENMAPSPACE_gmfn_foreign;
294 add.foreign_domid = mmap->dom;
297 * The 'size' field in the xen_add_to_physmap_range only
298 * allows for UINT16_MAX mappings in a single hypercall.
300 num = MIN(mmap->num, UINT16_MAX);
302 idxs = malloc(sizeof(*idxs) * num, M_PRIVCMD, M_WAITOK);
303 gpfns = malloc(sizeof(*gpfns) * num, M_PRIVCMD, M_WAITOK);
304 errs = malloc(sizeof(*errs) * num, M_PRIVCMD, M_WAITOK);
306 set_xen_guest_handle(add.idxs, idxs);
307 set_xen_guest_handle(add.gpfns, gpfns);
308 set_xen_guest_handle(add.errs, errs);
310 /* Allocate a bitset to store broken page mappings. */
311 umap->err = BITSET_ALLOC(mmap->num, M_PRIVCMD,
314 for (index = 0; index < mmap->num; index += num) {
315 num = MIN(mmap->num - index, UINT16_MAX);
318 error = copyin(&mmap->arr[index], idxs,
319 sizeof(idxs[0]) * num);
323 for (i = 0; i < num; i++)
324 gpfns[i] = atop(umap->phys_base_addr +
325 (i + index) * PAGE_SIZE);
327 bzero(errs, sizeof(*errs) * num);
329 error = HYPERVISOR_memory_op(
330 XENMEM_add_to_physmap_range, &add);
332 error = xen_translate_error(error);
336 for (i = 0; i < num; i++) {
338 errs[i] = xen_translate_error(errs[i]);
340 /* Mark the page as invalid. */
341 BIT_SET(mmap->num, index + i,
346 error = copyout(errs, &mmap->err[index],
347 sizeof(errs[0]) * num);
355 free(idxs, M_PRIVCMD);
356 free(gpfns, M_PRIVCMD);
357 free(errs, M_PRIVCMD);
359 free(umap->err, M_PRIVCMD);
372 /*------------------ Private Device Attachment Functions --------------------*/
374 privcmd_identify(driver_t *driver, device_t parent)
377 KASSERT(xen_domain(),
378 ("Trying to attach privcmd device on non Xen domain"));
380 if (BUS_ADD_CHILD(parent, 0, "privcmd", 0) == NULL)
381 panic("unable to attach privcmd user-space device");
385 privcmd_probe(device_t dev)
389 device_set_desc(dev, "Xen privileged interface user-space device");
390 return (BUS_PROBE_NOWILDCARD);
394 privcmd_attach(device_t dev)
397 make_dev_credf(MAKEDEV_ETERNAL, &privcmd_devsw, 0, NULL, UID_ROOT,
398 GID_WHEEL, 0600, "xen/privcmd");
402 /*-------------------- Private Device Attachment Data -----------------------*/
403 static device_method_t privcmd_methods[] = {
404 DEVMETHOD(device_identify, privcmd_identify),
405 DEVMETHOD(device_probe, privcmd_probe),
406 DEVMETHOD(device_attach, privcmd_attach),
411 static driver_t privcmd_driver = {
417 devclass_t privcmd_devclass;
419 DRIVER_MODULE(privcmd, xenpv, privcmd_driver, privcmd_devclass, 0, 0);
420 MODULE_DEPEND(privcmd, xenpv, 1, 1, 1);