The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/xen/privcmd/privcmd.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 #include <sys/bitset.h>
   49 
   50 #include <vm/vm.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 
   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 #define MAX_DMOP_BUFFERS 16
   69 
   70 struct privcmd_map {
   71         vm_object_t mem;
   72         vm_size_t size;
   73         struct resource *pseudo_phys_res;
   74         int pseudo_phys_res_id;
   75         vm_paddr_t phys_base_addr;
   76         boolean_t mapped;
   77         BITSET_DEFINE_VAR() *err;
   78 };
   79 
   80 static d_ioctl_t     privcmd_ioctl;
   81 static d_open_t      privcmd_open;
   82 static d_mmap_single_t  privcmd_mmap_single;
   83 
   84 static struct cdevsw privcmd_devsw = {
   85         .d_version = D_VERSION,
   86         .d_ioctl = privcmd_ioctl,
   87         .d_mmap_single = privcmd_mmap_single,
   88         .d_open = privcmd_open,
   89         .d_name = "privcmd",
   90 };
   91 
   92 static int privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
   93     vm_ooffset_t foff, struct ucred *cred, u_short *color);
   94 static void privcmd_pg_dtor(void *handle);
   95 static int privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
   96     int prot, vm_page_t *mres);
   97 
   98 static struct cdev_pager_ops privcmd_pg_ops = {
   99         .cdev_pg_fault = privcmd_pg_fault,
  100         .cdev_pg_ctor = privcmd_pg_ctor,
  101         .cdev_pg_dtor = privcmd_pg_dtor,
  102 };
  103 
  104 struct per_user_data {
  105         domid_t dom;
  106 };
  107 
  108 static device_t privcmd_dev = NULL;
  109 
  110 /*------------------------- Privcmd Pager functions --------------------------*/
  111 static int
  112 privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
  113     vm_ooffset_t foff, struct ucred *cred, u_short *color)
  114 {
  115 
  116         return (0);
  117 }
  118 
  119 static void
  120 privcmd_pg_dtor(void *handle)
  121 {
  122         struct xen_remove_from_physmap rm = { .domid = DOMID_SELF };
  123         struct privcmd_map *map = handle;
  124         int error __diagused;
  125         vm_size_t i;
  126         vm_page_t m;
  127 
  128         /*
  129          * Remove the mappings from the used pages. This will remove the
  130          * underlying p2m bindings in Xen second stage translation.
  131          */
  132         if (map->mapped == true) {
  133                 VM_OBJECT_WLOCK(map->mem);
  134 retry:
  135                 for (i = 0; i < map->size; i++) {
  136                         m = vm_page_lookup(map->mem, i);
  137                         if (m == NULL)
  138                                 continue;
  139                         if (vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL) == 0)
  140                                 goto retry;
  141                         cdev_pager_free_page(map->mem, m);
  142                 }
  143                 VM_OBJECT_WUNLOCK(map->mem);
  144 
  145                 for (i = 0; i < map->size; i++) {
  146                         rm.gpfn = atop(map->phys_base_addr) + i;
  147                         HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rm);
  148                 }
  149                 free(map->err, M_PRIVCMD);
  150         }
  151 
  152         error = xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
  153             map->pseudo_phys_res);
  154         KASSERT(error == 0, ("Unable to release memory resource: %d", error));
  155 
  156         free(map, M_PRIVCMD);
  157 }
  158 
  159 static int
  160 privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
  161     int prot, vm_page_t *mres)
  162 {
  163         struct privcmd_map *map = object->handle;
  164         vm_pindex_t pidx;
  165         vm_page_t page;
  166 
  167         if (map->mapped != true)
  168                 return (VM_PAGER_FAIL);
  169 
  170         pidx = OFF_TO_IDX(offset);
  171         if (pidx >= map->size || BIT_ISSET(map->size, pidx, map->err))
  172                 return (VM_PAGER_FAIL);
  173 
  174         page = PHYS_TO_VM_PAGE(map->phys_base_addr + offset);
  175         if (page == NULL)
  176                 return (VM_PAGER_FAIL);
  177 
  178         KASSERT((page->flags & PG_FICTITIOUS) != 0,
  179             ("not fictitious %p", page));
  180         KASSERT(vm_page_wired(page), ("page %p not wired", page));
  181         KASSERT(!vm_page_busied(page), ("page %p is busy", page));
  182 
  183         vm_page_busy_acquire(page, 0);
  184         vm_page_valid(page);
  185 
  186         if (*mres != NULL)
  187                 vm_page_replace(page, object, pidx, *mres);
  188         else
  189                 vm_page_insert(page, object, pidx);
  190         *mres = page;
  191         return (VM_PAGER_OK);
  192 }
  193 
  194 /*----------------------- Privcmd char device methods ------------------------*/
  195 static int
  196 privcmd_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
  197     vm_object_t *object, int nprot)
  198 {
  199         struct privcmd_map *map;
  200 
  201         map = malloc(sizeof(*map), M_PRIVCMD, M_WAITOK | M_ZERO);
  202 
  203         map->size = OFF_TO_IDX(size);
  204         map->pseudo_phys_res_id = 0;
  205 
  206         map->pseudo_phys_res = xenmem_alloc(privcmd_dev,
  207             &map->pseudo_phys_res_id, size);
  208         if (map->pseudo_phys_res == NULL) {
  209                 free(map, M_PRIVCMD);
  210                 return (ENOMEM);
  211         }
  212 
  213         map->phys_base_addr = rman_get_start(map->pseudo_phys_res);
  214         map->mem = cdev_pager_allocate(map, OBJT_MGTDEVICE, &privcmd_pg_ops,
  215             size, nprot, *offset, NULL);
  216         if (map->mem == NULL) {
  217                 xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
  218                     map->pseudo_phys_res);
  219                 free(map, M_PRIVCMD);
  220                 return (ENOMEM);
  221         }
  222 
  223         *object = map->mem;
  224 
  225         return (0);
  226 }
  227 
  228 static struct privcmd_map *
  229 setup_virtual_area(struct thread *td, unsigned long addr, unsigned long num)
  230 {
  231         vm_map_t map;
  232         vm_map_entry_t entry;
  233         vm_object_t mem;
  234         vm_pindex_t pindex;
  235         vm_prot_t prot;
  236         boolean_t wired;
  237         struct privcmd_map *umap;
  238         int error;
  239 
  240         if ((num == 0) || ((addr & PAGE_MASK) != 0))
  241                 return NULL;
  242 
  243         map = &td->td_proc->p_vmspace->vm_map;
  244         error = vm_map_lookup(&map, addr, VM_PROT_NONE, &entry, &mem, &pindex,
  245             &prot, &wired);
  246         if (error != KERN_SUCCESS || (entry->start != addr) ||
  247             (entry->end != addr + (num * PAGE_SIZE)))
  248                 return NULL;
  249 
  250         vm_map_lookup_done(map, entry);
  251         if ((mem->type != OBJT_MGTDEVICE) ||
  252             (mem->un_pager.devp.ops != &privcmd_pg_ops))
  253                 return NULL;
  254 
  255         umap = mem->handle;
  256         /* Allocate a bitset to store broken page mappings. */
  257         umap->err = BITSET_ALLOC(num, M_PRIVCMD, M_WAITOK | M_ZERO);
  258 
  259         return umap;
  260 }
  261 
  262 static int
  263 privcmd_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
  264               int mode, struct thread *td)
  265 {
  266         int error;
  267         unsigned int i;
  268         void *data;
  269         const struct per_user_data *u;
  270 
  271         error = devfs_get_cdevpriv(&data);
  272         if (error != 0)
  273                 return (EINVAL);
  274         /*
  275          * Constify user-data to prevent unintended changes to the restriction
  276          * limits.
  277          */
  278         u = data;
  279 
  280         switch (cmd) {
  281         case IOCTL_PRIVCMD_HYPERCALL: {
  282                 struct ioctl_privcmd_hypercall *hcall;
  283 
  284                 hcall = (struct ioctl_privcmd_hypercall *)arg;
  285 
  286                 /* Forbid hypercalls if restricted. */
  287                 if (u->dom != DOMID_INVALID) {
  288                         error = EPERM;
  289                         break;
  290                 }
  291 
  292 #ifdef __amd64__
  293                 /*
  294                  * The hypervisor page table walker will refuse to access
  295                  * user-space pages if SMAP is enabled, so temporary disable it
  296                  * while performing the hypercall.
  297                  */
  298                 if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
  299                         stac();
  300 #endif
  301                 error = privcmd_hypercall(hcall->op, hcall->arg[0],
  302                     hcall->arg[1], hcall->arg[2], hcall->arg[3], hcall->arg[4]);
  303 #ifdef __amd64__
  304                 if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
  305                         clac();
  306 #endif
  307                 if (error >= 0) {
  308                         hcall->retval = error;
  309                         error = 0;
  310                 } else {
  311                         error = xen_translate_error(error);
  312                         hcall->retval = 0;
  313                 }
  314                 break;
  315         }
  316         case IOCTL_PRIVCMD_MMAPBATCH: {
  317                 struct ioctl_privcmd_mmapbatch *mmap;
  318                 struct xen_add_to_physmap_range add;
  319                 xen_ulong_t *idxs;
  320                 xen_pfn_t *gpfns;
  321                 int *errs;
  322                 unsigned int index;
  323                 struct privcmd_map *umap;
  324                 uint16_t num;
  325 
  326                 mmap = (struct ioctl_privcmd_mmapbatch *)arg;
  327 
  328                 if (u->dom != DOMID_INVALID && u->dom != mmap->dom) {
  329                         error = EPERM;
  330                         break;
  331                 }
  332 
  333                 umap = setup_virtual_area(td, mmap->addr, mmap->num);
  334                 if (umap == NULL) {
  335                         error = EINVAL;
  336                         break;
  337                 }
  338 
  339                 add.domid = DOMID_SELF;
  340                 add.space = XENMAPSPACE_gmfn_foreign;
  341                 add.foreign_domid = mmap->dom;
  342 
  343                 /*
  344                  * The 'size' field in the xen_add_to_physmap_range only
  345                  * allows for UINT16_MAX mappings in a single hypercall.
  346                  */
  347                 num = MIN(mmap->num, UINT16_MAX);
  348 
  349                 idxs = malloc(sizeof(*idxs) * num, M_PRIVCMD, M_WAITOK);
  350                 gpfns = malloc(sizeof(*gpfns) * num, M_PRIVCMD, M_WAITOK);
  351                 errs = malloc(sizeof(*errs) * num, M_PRIVCMD, M_WAITOK);
  352 
  353                 set_xen_guest_handle(add.idxs, idxs);
  354                 set_xen_guest_handle(add.gpfns, gpfns);
  355                 set_xen_guest_handle(add.errs, errs);
  356 
  357                 for (index = 0; index < mmap->num; index += num) {
  358                         num = MIN(mmap->num - index, UINT16_MAX);
  359                         add.size = num;
  360 
  361                         error = copyin(&mmap->arr[index], idxs,
  362                             sizeof(idxs[0]) * num);
  363                         if (error != 0)
  364                                 goto mmap_out;
  365 
  366                         for (i = 0; i < num; i++)
  367                                 gpfns[i] = atop(umap->phys_base_addr +
  368                                     (i + index) * PAGE_SIZE);
  369 
  370                         bzero(errs, sizeof(*errs) * num);
  371 
  372                         error = HYPERVISOR_memory_op(
  373                             XENMEM_add_to_physmap_range, &add);
  374                         if (error != 0) {
  375                                 error = xen_translate_error(error);
  376                                 goto mmap_out;
  377                         }
  378 
  379                         for (i = 0; i < num; i++) {
  380                                 if (errs[i] != 0) {
  381                                         errs[i] = xen_translate_error(errs[i]);
  382 
  383                                         /* Mark the page as invalid. */
  384                                         BIT_SET(mmap->num, index + i,
  385                                             umap->err);
  386                                 }
  387                         }
  388 
  389                         error = copyout(errs, &mmap->err[index],
  390                             sizeof(errs[0]) * num);
  391                         if (error != 0)
  392                                 goto mmap_out;
  393                 }
  394 
  395                 umap->mapped = true;
  396 
  397 mmap_out:
  398                 free(idxs, M_PRIVCMD);
  399                 free(gpfns, M_PRIVCMD);
  400                 free(errs, M_PRIVCMD);
  401                 if (!umap->mapped)
  402                         free(umap->err, M_PRIVCMD);
  403 
  404                 break;
  405         }
  406         case IOCTL_PRIVCMD_MMAP_RESOURCE: {
  407                 struct ioctl_privcmd_mmapresource *mmap;
  408                 struct xen_mem_acquire_resource adq;
  409                 xen_pfn_t *gpfns;
  410                 struct privcmd_map *umap;
  411 
  412                 mmap = (struct ioctl_privcmd_mmapresource *)arg;
  413 
  414                 if (u->dom != DOMID_INVALID && u->dom != mmap->dom) {
  415                         error = EPERM;
  416                         break;
  417                 }
  418 
  419                 bzero(&adq, sizeof(adq));
  420 
  421                 adq.domid = mmap->dom;
  422                 adq.type = mmap->type;
  423                 adq.id = mmap->id;
  424 
  425                 /* Shortcut for getting the resource size. */
  426                 if (mmap->addr == 0 && mmap->num == 0) {
  427                         error = HYPERVISOR_memory_op(XENMEM_acquire_resource,
  428                             &adq);
  429                         if (error != 0)
  430                                 error = xen_translate_error(error);
  431                         else
  432                                 mmap->num = adq.nr_frames;
  433                         break;
  434                 }
  435 
  436                 umap = setup_virtual_area(td, mmap->addr, mmap->num);
  437                 if (umap == NULL) {
  438                         error = EINVAL;
  439                         break;
  440                 }
  441 
  442                 adq.nr_frames = mmap->num;
  443                 adq.frame = mmap->idx;
  444 
  445                 gpfns = malloc(sizeof(*gpfns) * mmap->num, M_PRIVCMD, M_WAITOK);
  446                 for (i = 0; i < mmap->num; i++)
  447                         gpfns[i] = atop(umap->phys_base_addr) + i;
  448                 set_xen_guest_handle(adq.frame_list, gpfns);
  449 
  450                 error = HYPERVISOR_memory_op(XENMEM_acquire_resource, &adq);
  451                 if (error != 0)
  452                         error = xen_translate_error(error);
  453                 else
  454                         umap->mapped = true;
  455 
  456                 free(gpfns, M_PRIVCMD);
  457                 if (!umap->mapped)
  458                         free(umap->err, M_PRIVCMD);
  459 
  460                 break;
  461         }
  462         case IOCTL_PRIVCMD_DM_OP: {
  463                 const struct ioctl_privcmd_dmop *dmop;
  464                 struct privcmd_dmop_buf *bufs;
  465                 struct xen_dm_op_buf *hbufs;
  466 
  467                 dmop = (struct ioctl_privcmd_dmop *)arg;
  468 
  469                 if (u->dom != DOMID_INVALID && u->dom != dmop->dom) {
  470                         error = EPERM;
  471                         break;
  472                 }
  473 
  474                 if (dmop->num == 0)
  475                         break;
  476 
  477                 if (dmop->num > MAX_DMOP_BUFFERS) {
  478                         error = E2BIG;
  479                         break;
  480                 }
  481 
  482                 bufs = malloc(sizeof(*bufs) * dmop->num, M_PRIVCMD, M_WAITOK);
  483 
  484                 error = copyin(dmop->ubufs, bufs, sizeof(*bufs) * dmop->num);
  485                 if (error != 0) {
  486                         free(bufs, M_PRIVCMD);
  487                         break;
  488                 }
  489 
  490                 hbufs = malloc(sizeof(*hbufs) * dmop->num, M_PRIVCMD, M_WAITOK);
  491                 for (i = 0; i < dmop->num; i++) {
  492                         set_xen_guest_handle(hbufs[i].h, bufs[i].uptr);
  493                         hbufs[i].size = bufs[i].size;
  494                 }
  495 
  496 #ifdef __amd64__
  497                 if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
  498                         stac();
  499 #endif
  500                 error = HYPERVISOR_dm_op(dmop->dom, dmop->num, hbufs);
  501 #ifdef __amd64__
  502                 if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
  503                         clac();
  504 #endif
  505                 if (error != 0)
  506                         error = xen_translate_error(error);
  507 
  508                 free(bufs, M_PRIVCMD);
  509                 free(hbufs, M_PRIVCMD);
  510 
  511 
  512                 break;
  513         }
  514         case IOCTL_PRIVCMD_RESTRICT: {
  515                 struct per_user_data *u;
  516                 domid_t dom;
  517 
  518                 dom = *(domid_t *)arg;
  519 
  520                 error = devfs_get_cdevpriv((void **)&u);
  521                 if (error != 0)
  522                         break;
  523 
  524                 if (u->dom != DOMID_INVALID && u->dom != dom) {
  525                         error = -EINVAL;
  526                         break;
  527                 }
  528                 u->dom = dom;
  529 
  530                 break;
  531         }
  532         default:
  533                 error = ENOSYS;
  534                 break;
  535         }
  536 
  537         return (error);
  538 }
  539 
  540 static void
  541 user_release(void *arg)
  542 {
  543 
  544         free(arg, M_PRIVCMD);
  545 }
  546 
  547 static int
  548 privcmd_open(struct cdev *dev, int flag, int otyp, struct thread *td)
  549 {
  550         struct per_user_data *u;
  551         int error;
  552 
  553         u = malloc(sizeof(*u), M_PRIVCMD, M_WAITOK);
  554         u->dom = DOMID_INVALID;
  555 
  556         /* Assign the allocated per_user_data to this open instance. */
  557         error = devfs_set_cdevpriv(u, user_release);
  558         if (error != 0) {
  559                 free(u, M_PRIVCMD);
  560         }
  561 
  562         return (error);
  563 }
  564 
  565 /*------------------ Private Device Attachment Functions  --------------------*/
  566 static void
  567 privcmd_identify(driver_t *driver, device_t parent)
  568 {
  569 
  570         KASSERT(xen_domain(),
  571             ("Trying to attach privcmd device on non Xen domain"));
  572 
  573         if (BUS_ADD_CHILD(parent, 0, "privcmd", 0) == NULL)
  574                 panic("unable to attach privcmd user-space device");
  575 }
  576 
  577 static int
  578 privcmd_probe(device_t dev)
  579 {
  580 
  581         privcmd_dev = dev;
  582         device_set_desc(dev, "Xen privileged interface user-space device");
  583         return (BUS_PROBE_NOWILDCARD);
  584 }
  585 
  586 static int
  587 privcmd_attach(device_t dev)
  588 {
  589 
  590         make_dev_credf(MAKEDEV_ETERNAL, &privcmd_devsw, 0, NULL, UID_ROOT,
  591             GID_WHEEL, 0600, "xen/privcmd");
  592         return (0);
  593 }
  594 
  595 /*-------------------- Private Device Attachment Data  -----------------------*/
  596 static device_method_t privcmd_methods[] = {
  597         DEVMETHOD(device_identify,      privcmd_identify),
  598         DEVMETHOD(device_probe,         privcmd_probe),
  599         DEVMETHOD(device_attach,        privcmd_attach),
  600 
  601         DEVMETHOD_END
  602 };
  603 
  604 static driver_t privcmd_driver = {
  605         "privcmd",
  606         privcmd_methods,
  607         0,
  608 };
  609 
  610 DRIVER_MODULE(privcmd, xenpv, privcmd_driver, 0, 0);
  611 MODULE_DEPEND(privcmd, xenpv, 1, 1, 1);

Cache object: dbaee078af49ae5738be1cd287a88925


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.