diff options
author | Roger Pau Monné <royger@FreeBSD.org> | 2014-10-22 17:07:20 +0000 |
---|---|---|
committer | Roger Pau Monné <royger@FreeBSD.org> | 2014-10-22 17:07:20 +0000 |
commit | bf7313e3b79a97459a499380221cde238271b028 (patch) | |
tree | 55aa165c8081697ca81872e6d35c62170f353661 | |
parent | 317db2799e6103a3eeb62c2d2a6f5b8cd0ada7a1 (diff) | |
download | src-test2-bf7313e3b79a97459a499380221cde238271b028.tar.gz src-test2-bf7313e3b79a97459a499380221cde238271b028.zip |
Notes
-rw-r--r-- | sys/amd64/include/xen/hypercall.h | 22 | ||||
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/dev/xen/privcmd/privcmd.c | 414 | ||||
-rw-r--r-- | sys/i386/include/xen/hypercall.h | 20 | ||||
-rw-r--r-- | sys/x86/xen/hvm.c | 2 | ||||
-rw-r--r-- | sys/xen/interface/memory.h | 33 | ||||
-rw-r--r-- | sys/xen/interface/xen.h | 1 | ||||
-rw-r--r-- | sys/xen/privcmd.h | 58 |
8 files changed, 549 insertions, 2 deletions
diff --git a/sys/amd64/include/xen/hypercall.h b/sys/amd64/include/xen/hypercall.h index 499fb4db1e7f..594ffd280ff8 100644 --- a/sys/amd64/include/xen/hypercall.h +++ b/sys/amd64/include/xen/hypercall.h @@ -45,6 +45,8 @@ # error "please don't include this file directly" #endif +extern char *hypercall_page; + #define __STR(x) #x #define STR(x) __STR(x) #define ENOXENSYS 38 @@ -134,6 +136,26 @@ __res; \ }) +static inline int +privcmd_hypercall(long op, long a1, long a2, long a3, long a4, long a5) +{ + int __res; + long __ign1, __ign2, __ign3; + register long __arg4 __asm__("r10") = (long)(a4); + register long __arg5 __asm__("r8") = (long)(a5); + long __call = (long)&hypercall_page + (op * 32); + + __asm__ volatile ( + "call *%[call]" + : "=a" (__res), "=D" (__ign1), "=S" (__ign2), + "=d" (__ign3), "+r" (__arg4), "+r" (__arg5) + : "1" ((long)(a1)), "2" ((long)(a2)), + "3" ((long)(a3)), [call] "a" (__call) + : "memory" ); + + return (__res); +} + static inline int __must_check HYPERVISOR_set_trap_table( const trap_info_t *table) diff --git a/sys/conf/files b/sys/conf/files index d8791b5bd9bf..3e89dbb18a97 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2648,6 +2648,7 @@ dev/xen/xenstore/xenstore.c optional xen | xenhvm dev/xen/xenstore/xenstore_dev.c optional xen | xenhvm dev/xen/xenstore/xenstored_dev.c optional xen | xenhvm dev/xen/evtchn/evtchn_dev.c optional xen | xenhvm +dev/xen/privcmd/privcmd.c optional xen | xenhvm dev/xl/if_xl.c optional xl pci dev/xl/xlphy.c optional xl pci fs/autofs/autofs.c optional autofs diff --git a/sys/dev/xen/privcmd/privcmd.c b/sys/dev/xen/privcmd/privcmd.c new file mode 100644 index 000000000000..761fb037b163 --- /dev/null +++ b/sys/dev/xen/privcmd/privcmd.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2014 Roger Pau Monné <roger.pau@citrix.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/rwlock.h> +#include <sys/selinfo.h> +#include <sys/poll.h> +#include <sys/conf.h> +#include <sys/fcntl.h> +#include <sys/ioccom.h> +#include <sys/rman.h> +#include <sys/tree.h> +#include <sys/module.h> +#include <sys/proc.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_extern.h> +#include <vm/vm_kern.h> +#include <vm/vm_page.h> +#include <vm/vm_map.h> +#include <vm/vm_object.h> +#include <vm/vm_pager.h> +#include <vm/vm_phys.h> + +#include <machine/md_var.h> + +#include <xen/xen-os.h> +#include <xen/hypervisor.h> +#include <xen/privcmd.h> +#include <xen/error.h> + +MALLOC_DEFINE(M_PRIVCMD, "privcmd_dev", "Xen privcmd user-space device"); + +struct privcmd_map { + vm_object_t mem; + vm_size_t size; + struct resource *pseudo_phys_res; + int pseudo_phys_res_id; + vm_paddr_t phys_base_addr; + boolean_t mapped; + int *errs; +}; + +static d_ioctl_t privcmd_ioctl; +static d_mmap_single_t privcmd_mmap_single; + +static struct cdevsw privcmd_devsw = { + .d_version = D_VERSION, + .d_ioctl = privcmd_ioctl, + .d_mmap_single = privcmd_mmap_single, + .d_name = "privcmd", +}; + +static int privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, + vm_ooffset_t foff, struct ucred *cred, u_short *color); +static void privcmd_pg_dtor(void *handle); +static int privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset, + int prot, vm_page_t *mres); + +static struct cdev_pager_ops privcmd_pg_ops = { + .cdev_pg_fault = privcmd_pg_fault, + .cdev_pg_ctor = privcmd_pg_ctor, + .cdev_pg_dtor = privcmd_pg_dtor, +}; + +static device_t privcmd_dev = NULL; + +/*------------------------- Privcmd Pager functions --------------------------*/ +static int +privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, + vm_ooffset_t foff, struct ucred *cred, u_short *color) +{ + + return (0); +} + +static void +privcmd_pg_dtor(void *handle) +{ + struct xen_remove_from_physmap rm = { .domid = DOMID_SELF }; + struct privcmd_map *map = handle; + int error; + vm_size_t i; + vm_page_t m; + + /* + * Remove the mappings from the used pages. This will remove the + * underlying p2m bindings in Xen second stage translation. + */ + if (map->mapped == true) { + VM_OBJECT_WLOCK(map->mem); +retry: + for (i = 0; i < map->size; i++) { + m = vm_page_lookup(map->mem, i); + if (m == NULL) + continue; + if (vm_page_sleep_if_busy(m, "pcmdum")) + goto retry; + cdev_pager_free_page(map->mem, m); + } + VM_OBJECT_WUNLOCK(map->mem); + + for (i = 0; i < map->size; i++) { + rm.gpfn = atop(map->phys_base_addr) + i; + HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rm); + } + free(map->errs, M_PRIVCMD); + } + + vm_phys_fictitious_unreg_range(map->phys_base_addr, + map->phys_base_addr + map->size * PAGE_SIZE); + + error = bus_release_resource(privcmd_dev, SYS_RES_MEMORY, + map->pseudo_phys_res_id, map->pseudo_phys_res); + KASSERT(error == 0, ("Unable to release memory resource: %d", error)); + + free(map, M_PRIVCMD); +} + +static int +privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset, + int prot, vm_page_t *mres) +{ + struct privcmd_map *map = object->handle; + vm_pindex_t pidx; + vm_page_t page, oldm; + + if (map->mapped != true) + return (VM_PAGER_FAIL); + + pidx = OFF_TO_IDX(offset); + if (pidx >= map->size || map->errs[pidx] != 0) + return (VM_PAGER_FAIL); + + page = PHYS_TO_VM_PAGE(map->phys_base_addr + offset); + if (page == NULL) + return (VM_PAGER_FAIL); + + KASSERT((page->flags & PG_FICTITIOUS) != 0, + ("not fictitious %p", page)); + KASSERT(page->wire_count == 1, ("wire_count not 1 %p", page)); + KASSERT(vm_page_busied(page) == 0, ("page %p is busy", page)); + + if (*mres != NULL) { + oldm = *mres; + vm_page_lock(oldm); + vm_page_free(oldm); + vm_page_unlock(oldm); + *mres = NULL; + } + + vm_page_insert(page, object, pidx); + page->valid = VM_PAGE_BITS_ALL; + vm_page_xbusy(page); + *mres = page; + return (VM_PAGER_OK); +} + +/*----------------------- Privcmd char device methods ------------------------*/ +static int +privcmd_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size, + vm_object_t *object, int nprot) +{ + struct privcmd_map *map; + int error; + + map = malloc(sizeof(*map), M_PRIVCMD, M_WAITOK | M_ZERO); + + map->size = OFF_TO_IDX(size); + map->pseudo_phys_res_id = 0; + + map->pseudo_phys_res = bus_alloc_resource(privcmd_dev, SYS_RES_MEMORY, + &map->pseudo_phys_res_id, 0, ~0, size, RF_ACTIVE); + if (map->pseudo_phys_res == NULL) { + free(map, M_PRIVCMD); + return (ENOMEM); + } + + map->phys_base_addr = rman_get_start(map->pseudo_phys_res); + + error = vm_phys_fictitious_reg_range(map->phys_base_addr, + map->phys_base_addr + size, VM_MEMATTR_DEFAULT); + if (error) { + bus_release_resource(privcmd_dev, SYS_RES_MEMORY, + map->pseudo_phys_res_id, map->pseudo_phys_res); + free(map, M_PRIVCMD); + return (error); + } + + map->mem = cdev_pager_allocate(map, OBJT_MGTDEVICE, &privcmd_pg_ops, + size, nprot, *offset, NULL); + if (map->mem == NULL) { + bus_release_resource(privcmd_dev, SYS_RES_MEMORY, + map->pseudo_phys_res_id, map->pseudo_phys_res); + free(map, M_PRIVCMD); + return (ENOMEM); + } + + *object = map->mem; + + return (0); +} + +static int +privcmd_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg, + int mode, struct thread *td) +{ + int error, i; + + switch (cmd) { + case IOCTL_PRIVCMD_HYPERCALL: { + struct ioctl_privcmd_hypercall *hcall; + + hcall = (struct ioctl_privcmd_hypercall *)arg; + + error = privcmd_hypercall(hcall->op, hcall->arg[0], + hcall->arg[1], hcall->arg[2], hcall->arg[3], hcall->arg[4]); + if (error >= 0) { + hcall->retval = error; + error = 0; + } else { + error = xen_translate_error(error); + hcall->retval = 0; + } + break; + } + case IOCTL_PRIVCMD_MMAPBATCH: { + struct ioctl_privcmd_mmapbatch *mmap; + vm_map_t map; + vm_map_entry_t entry; + vm_object_t mem; + vm_pindex_t index; + vm_prot_t prot; + boolean_t wired; + struct xen_add_to_physmap_range add; + xen_ulong_t *idxs; + xen_pfn_t *gpfns; + int *errs; + struct privcmd_map *umap; + + mmap = (struct ioctl_privcmd_mmapbatch *)arg; + + if ((mmap->num == 0) || + ((mmap->addr & PAGE_MASK) != 0)) { + error = EINVAL; + break; + } + + map = &td->td_proc->p_vmspace->vm_map; + error = vm_map_lookup(&map, mmap->addr, VM_PROT_NONE, &entry, + &mem, &index, &prot, &wired); + if (error != KERN_SUCCESS) { + error = EINVAL; + break; + } + if ((entry->start != mmap->addr) || + (entry->end != mmap->addr + (mmap->num * PAGE_SIZE))) { + vm_map_lookup_done(map, entry); + error = EINVAL; + break; + } + vm_map_lookup_done(map, entry); + if ((mem->type != OBJT_MGTDEVICE) || + (mem->un_pager.devp.ops != &privcmd_pg_ops)) { + error = EINVAL; + break; + } + umap = mem->handle; + + add.domid = DOMID_SELF; + add.space = XENMAPSPACE_gmfn_foreign; + add.size = mmap->num; + add.foreign_domid = mmap->dom; + + idxs = malloc(sizeof(*idxs) * mmap->num, M_PRIVCMD, + M_WAITOK | M_ZERO); + gpfns = malloc(sizeof(*gpfns) * mmap->num, M_PRIVCMD, + M_WAITOK | M_ZERO); + errs = malloc(sizeof(*errs) * mmap->num, M_PRIVCMD, + M_WAITOK | M_ZERO); + + set_xen_guest_handle(add.idxs, idxs); + set_xen_guest_handle(add.gpfns, gpfns); + set_xen_guest_handle(add.errs, errs); + + error = copyin(&mmap->arr[0], idxs, + sizeof(idxs[0]) * mmap->num); + if (error != 0) + goto mmap_out; + + for (i = 0; i < mmap->num; i++) + gpfns[i] = atop(umap->phys_base_addr + i * PAGE_SIZE); + + error = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &add); + if (error) { + error = xen_translate_error(error); + goto mmap_out; + } + + for (i = 0; i < mmap->num; i++) { + if (errs[i] != 0) + errs[i] = xen_translate_error(errs[i]); + } + + /* + * Save errs, so we know which pages have been + * successfully mapped. + */ + umap->errs = errs; + umap->mapped = true; + + error = copyout(errs, &mmap->err[0], + sizeof(errs[0]) * mmap->num); + +mmap_out: + free(idxs, M_PRIVCMD); + free(gpfns, M_PRIVCMD); + if (!umap->mapped) + free(errs, M_PRIVCMD); + + break; + } + + default: + error = ENOSYS; + break; + } + + return (error); +} + +/*------------------ Private Device Attachment Functions --------------------*/ +static void +privcmd_identify(driver_t *driver, device_t parent) +{ + + KASSERT(xen_domain(), + ("Trying to attach privcmd device on non Xen domain")); + + if (BUS_ADD_CHILD(parent, 0, "privcmd", 0) == NULL) + panic("unable to attach privcmd user-space device"); +} + +static int +privcmd_probe(device_t dev) +{ + + privcmd_dev = dev; + device_set_desc(dev, "Xen privileged interface user-space device"); + return (BUS_PROBE_NOWILDCARD); +} + +static int +privcmd_attach(device_t dev) +{ + + make_dev_credf(MAKEDEV_ETERNAL, &privcmd_devsw, 0, NULL, UID_ROOT, + GID_WHEEL, 0600, "xen/privcmd"); + return (0); +} + +/*-------------------- Private Device Attachment Data -----------------------*/ +static device_method_t privcmd_methods[] = { + DEVMETHOD(device_identify, privcmd_identify), + DEVMETHOD(device_probe, privcmd_probe), + DEVMETHOD(device_attach, privcmd_attach), + + DEVMETHOD_END +}; + +static driver_t privcmd_driver = { + "privcmd", + privcmd_methods, + 0, +}; + +devclass_t privcmd_devclass; + +DRIVER_MODULE(privcmd, xenpv, privcmd_driver, privcmd_devclass, 0, 0); +MODULE_DEPEND(privcmd, xenpv, 1, 1, 1); diff --git a/sys/i386/include/xen/hypercall.h b/sys/i386/include/xen/hypercall.h index 16b5ee253c3c..c7e2a00ef23d 100644 --- a/sys/i386/include/xen/hypercall.h +++ b/sys/i386/include/xen/hypercall.h @@ -34,6 +34,8 @@ #include <xen/interface/xen.h> #include <xen/interface/sched.h> +extern char *hypercall_page; + #define __STR(x) #x #define STR(x) __STR(x) #define ENOXENSYS 38 @@ -115,6 +117,24 @@ (type)__res; \ }) +static inline long +privcmd_hypercall(long op, long a1, long a2, long a3, long a4, long a5) +{ + long __res, __ign1, __ign2, __ign3, __ign4, __ign5, __call; + + __call = (long)&hypercall_page + (op * 32); + __asm__ volatile ( + "call *%[call]" + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), + "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) + : "1" ((long)(a1)), "2" ((long)(a2)), + "3" ((long)(a3)), "4" ((long)(a4)), + "5" ((long)(a5)), [call] "a" (__call) + : "memory" ); + + return __res; +} + static inline int HYPERVISOR_set_trap_table( trap_info_t *table) diff --git a/sys/x86/xen/hvm.c b/sys/x86/xen/hvm.c index 8fda923b8a60..9ad196a83f4f 100644 --- a/sys/x86/xen/hvm.c +++ b/sys/x86/xen/hvm.c @@ -97,8 +97,6 @@ DPCPU_DEFINE(struct vcpu_info, vcpu_local_info); DPCPU_DEFINE(struct vcpu_info *, vcpu_info); /*------------------ Hypervisor Access Shared Memory Regions -----------------*/ -/** Hypercall table accessed via HYPERVISOR_*_op() methods. */ -extern char *hypercall_page; shared_info_t *HYPERVISOR_shared_info; start_info_t *HYPERVISOR_start_info; diff --git a/sys/xen/interface/memory.h b/sys/xen/interface/memory.h index 86d02c8b88e4..cdb7a5810ba4 100644 --- a/sys/xen/interface/memory.h +++ b/sys/xen/interface/memory.h @@ -198,6 +198,14 @@ struct xen_machphys_mapping { typedef struct xen_machphys_mapping xen_machphys_mapping_t; DEFINE_XEN_GUEST_HANDLE(xen_machphys_mapping_t); +#define XENMAPSPACE_shared_info 0 /* shared info page */ +#define XENMAPSPACE_grant_table 1 /* grant table page */ +#define XENMAPSPACE_gmfn 2 /* GMFN */ +#define XENMAPSPACE_gmfn_range 3 /* GMFN range, XENMEM_add_to_physmap only. */ +#define XENMAPSPACE_gmfn_foreign 4 /* GMFN from another dom, + * XENMEM_add_to_physmap_range only. + */ + /* * Sets the GPFN at which a particular page appears in the specified guest's * pseudophysical address space. @@ -248,6 +256,31 @@ DEFINE_XEN_GUEST_HANDLE(xen_remove_from_physmap_t); /*** REMOVED ***/ /*#define XENMEM_translate_gpfn_list 8*/ +#define XENMEM_add_to_physmap_range 23 +struct xen_add_to_physmap_range { + /* IN */ + /* Which domain to change the mapping for. */ + domid_t domid; + uint16_t space; /* => enum phys_map_space */ + + /* Number of pages to go through */ + uint16_t size; + domid_t foreign_domid; /* IFF gmfn_foreign */ + + /* Indexes into space being mapped. */ + XEN_GUEST_HANDLE(xen_ulong_t) idxs; + + /* GPFN in domid where the source mapping page should appear. */ + XEN_GUEST_HANDLE(xen_pfn_t) gpfns; + + /* OUT */ + + /* Per index error code. */ + XEN_GUEST_HANDLE(int) errs; +}; +typedef struct xen_add_to_physmap_range xen_add_to_physmap_range_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_range_t); + /* * Returns the pseudo-physical memory map as it was when the domain * was started (specified by XENMEM_set_memory_map). diff --git a/sys/xen/interface/xen.h b/sys/xen/interface/xen.h index b2f6c507b9f7..9425520e590d 100644 --- a/sys/xen/interface/xen.h +++ b/sys/xen/interface/xen.h @@ -51,6 +51,7 @@ DEFINE_XEN_GUEST_HANDLE(void); DEFINE_XEN_GUEST_HANDLE(uint64_t); DEFINE_XEN_GUEST_HANDLE(xen_pfn_t); +DEFINE_XEN_GUEST_HANDLE(xen_ulong_t); #endif /* diff --git a/sys/xen/privcmd.h b/sys/xen/privcmd.h new file mode 100644 index 000000000000..a3cc4e110e41 --- /dev/null +++ b/sys/xen/privcmd.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * privcmd.h + * + * Interface to /proc/xen/privcmd. + * + * Copyright (c) 2003-2005, K A Fraser + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * $FreeBSD$ + */ + +#ifndef __XEN_PRIVCMD_H__ +#define __XEN_PRIVCMD_H__ + +struct ioctl_privcmd_hypercall +{ + unsigned long op; /* hypercall number */ + unsigned long arg[5]; /* arguments */ + long retval; /* return value */ +}; + +struct ioctl_privcmd_mmapbatch { + int num; /* number of pages to populate */ + domid_t dom; /* target domain */ + unsigned long addr; /* virtual address */ + const xen_pfn_t *arr; /* array of mfns */ + int *err; /* array of error codes */ +}; + +#define IOCTL_PRIVCMD_HYPERCALL \ + _IOWR('E', 0, struct ioctl_privcmd_hypercall) +#define IOCTL_PRIVCMD_MMAPBATCH \ + _IOWR('E', 1, struct ioctl_privcmd_mmapbatch) + +#endif /* !__XEN_PRIVCMD_H__ */ |