diff --git a/sys/amd64/include/xen/hypercall.h b/sys/amd64/include/xen/hypercall.h index e427bbf4d43f..6d00d4a6ebd8 100644 --- a/sys/amd64/include/xen/hypercall.h +++ b/sys/amd64/include/xen/hypercall.h @@ -1,432 +1,439 @@ /****************************************************************************** * hypercall.h * * FreeBSD-specific hypervisor handling. * * Copyright (c) 2002-2004, K A Fraser * * 64-bit updates: * Benjamin Liu * Jun Nakajima * * 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 __MACHINE_XEN_HYPERCALL_H__ #define __MACHINE_XEN_HYPERCALL_H__ #include #ifndef __XEN_HYPERVISOR_H__ # 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 #define CONFIG_XEN_COMPAT 0x030002 #define __must_check #define HYPERCALL_STR(name) \ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)" #define _hypercall0(type, name) \ ({ \ type __res; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res) \ : \ : "memory" ); \ __res; \ }) #define _hypercall1(type, name, a1) \ ({ \ type __res; \ long __ign1; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=D" (__ign1) \ : "1" ((long)(a1)) \ : "memory" ); \ __res; \ }) #define _hypercall2(type, name, a1, a2) \ ({ \ type __res; \ long __ign1, __ign2; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=D" (__ign1), "=S" (__ign2) \ : "1" ((long)(a1)), "2" ((long)(a2)) \ : "memory" ); \ __res; \ }) #define _hypercall3(type, name, a1, a2, a3) \ ({ \ type __res; \ long __ign1, __ign2, __ign3; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \ "=d" (__ign3) \ : "1" ((long)(a1)), "2" ((long)(a2)), \ "3" ((long)(a3)) \ : "memory" ); \ __res; \ }) #define _hypercall4(type, name, a1, a2, a3, a4) \ ({ \ type __res; \ long __ign1, __ign2, __ign3; \ register long __arg4 __asm__("r10") = (long)(a4); \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \ "=d" (__ign3), "+r" (__arg4) \ : "1" ((long)(a1)), "2" ((long)(a2)), \ "3" ((long)(a3)) \ : "memory" ); \ __res; \ }) #define _hypercall5(type, name, a1, a2, a3, a4, a5) \ ({ \ type __res; \ long __ign1, __ign2, __ign3; \ register long __arg4 __asm__("r10") = (long)(a4); \ register long __arg5 __asm__("r8") = (long)(a5); \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \ "=d" (__ign3), "+r" (__arg4), "+r" (__arg5) \ : "1" ((long)(a1)), "2" ((long)(a2)), \ "3" ((long)(a3)) \ : "memory" ); \ __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) { return _hypercall1(int, set_trap_table, table); } static inline int __must_check HYPERVISOR_mmu_update( mmu_update_t *req, unsigned int count, unsigned int *success_count, domid_t domid) { return _hypercall4(int, mmu_update, req, count, success_count, domid); } static inline int __must_check HYPERVISOR_mmuext_op( struct mmuext_op *op, unsigned int count, unsigned int *success_count, domid_t domid) { return _hypercall4(int, mmuext_op, op, count, success_count, domid); } static inline int __must_check HYPERVISOR_set_gdt( unsigned long *frame_list, unsigned int entries) { return _hypercall2(int, set_gdt, frame_list, entries); } static inline int __must_check HYPERVISOR_stack_switch( unsigned long ss, unsigned long esp) { return _hypercall2(int, stack_switch, ss, esp); } static inline int __must_check HYPERVISOR_set_callbacks( unsigned long event_address, unsigned long failsafe_address, unsigned long syscall_address) { return _hypercall3(int, set_callbacks, event_address, failsafe_address, syscall_address); } static inline int HYPERVISOR_fpu_taskswitch( int set) { return _hypercall1(int, fpu_taskswitch, set); } static inline int __must_check HYPERVISOR_sched_op_compat( int cmd, unsigned long arg) { return _hypercall2(int, sched_op_compat, cmd, arg); } static inline int __must_check HYPERVISOR_sched_op( int cmd, void *arg) { return _hypercall2(int, sched_op, cmd, arg); } static inline long __must_check HYPERVISOR_set_timer_op( uint64_t timeout) { return _hypercall1(long, set_timer_op, timeout); } static inline int __must_check HYPERVISOR_platform_op( struct xen_platform_op *platform_op) { platform_op->interface_version = XENPF_INTERFACE_VERSION; return _hypercall1(int, platform_op, platform_op); } static inline int __must_check HYPERVISOR_set_debugreg( unsigned int reg, unsigned long value) { return _hypercall2(int, set_debugreg, reg, value); } static inline unsigned long __must_check HYPERVISOR_get_debugreg( unsigned int reg) { return _hypercall1(unsigned long, get_debugreg, reg); } static inline int __must_check HYPERVISOR_update_descriptor( unsigned long ma, unsigned long word) { return _hypercall2(int, update_descriptor, ma, word); } static inline int __must_check HYPERVISOR_memory_op( unsigned int cmd, void *arg) { return _hypercall2(int, memory_op, cmd, arg); } static inline int __must_check HYPERVISOR_multicall( multicall_entry_t *call_list, unsigned int nr_calls) { return _hypercall2(int, multicall, call_list, nr_calls); } static inline int __must_check HYPERVISOR_update_va_mapping( unsigned long va, uint64_t new_val, unsigned long flags) { return _hypercall3(int, update_va_mapping, va, new_val, flags); } static inline int __must_check HYPERVISOR_event_channel_op( int cmd, void *arg) { int rc = _hypercall2(int, event_channel_op, cmd, arg); #if CONFIG_XEN_COMPAT <= 0x030002 if (__predict_false(rc == -ENOXENSYS)) { struct evtchn_op op; op.cmd = cmd; memcpy(&op.u, arg, sizeof(op.u)); rc = _hypercall1(int, event_channel_op_compat, &op); memcpy(arg, &op.u, sizeof(op.u)); } #endif return rc; } static inline int __must_check HYPERVISOR_xen_version( int cmd, void *arg) { return _hypercall2(int, xen_version, cmd, arg); } static inline int __must_check HYPERVISOR_console_io( int cmd, unsigned int count, const char *str) { return _hypercall3(int, console_io, cmd, count, str); } static inline int __must_check HYPERVISOR_physdev_op( int cmd, void *arg) { int rc = _hypercall2(int, physdev_op, cmd, arg); #if CONFIG_XEN_COMPAT <= 0x030002 if (__predict_false(rc == -ENOXENSYS)) { struct physdev_op op; op.cmd = cmd; memcpy(&op.u, arg, sizeof(op.u)); rc = _hypercall1(int, physdev_op_compat, &op); memcpy(arg, &op.u, sizeof(op.u)); } #endif return rc; } static inline int __must_check HYPERVISOR_grant_table_op( unsigned int cmd, void *uop, unsigned int count) { return _hypercall3(int, grant_table_op, cmd, uop, count); } static inline int __must_check HYPERVISOR_update_va_mapping_otherdomain( unsigned long va, uint64_t new_val, unsigned long flags, domid_t domid) { return _hypercall4(int, update_va_mapping_otherdomain, va, new_val, flags, domid); } static inline int __must_check HYPERVISOR_vm_assist( unsigned int cmd, unsigned int type) { return _hypercall2(int, vm_assist, cmd, type); } static inline int __must_check HYPERVISOR_vcpu_op( int cmd, unsigned int vcpuid, void *extra_args) { return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args); } static inline int __must_check HYPERVISOR_set_segment_base( int reg, unsigned long value) { return _hypercall2(int, set_segment_base, reg, value); } static inline int __must_check HYPERVISOR_suspend( unsigned long srec) { struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_suspend }; int rc = _hypercall3(int, sched_op, SCHEDOP_shutdown, &sched_shutdown, srec); #if CONFIG_XEN_COMPAT <= 0x030002 if (rc == -ENOXENSYS) rc = _hypercall3(int, sched_op_compat, SCHEDOP_shutdown, SHUTDOWN_suspend, srec); #endif return rc; } #if CONFIG_XEN_COMPAT <= 0x030002 static inline int HYPERVISOR_nmi_op( unsigned long op, void *arg) { return _hypercall2(int, nmi_op, op, arg); } #endif #ifndef CONFIG_XEN static inline unsigned long __must_check HYPERVISOR_hvm_op( int op, void *arg) { return _hypercall2(unsigned long, hvm_op, op, arg); } #endif static inline int __must_check HYPERVISOR_callback_op( int cmd, const void *arg) { return _hypercall2(int, callback_op, cmd, arg); } static inline int __must_check HYPERVISOR_xenoprof_op( int op, void *arg) { return _hypercall2(int, xenoprof_op, op, arg); } static inline int __must_check HYPERVISOR_kexec_op( unsigned long op, void *args) { return _hypercall2(int, kexec_op, op, args); } +static inline int __must_check +HYPERVISOR_dm_op( + domid_t domid, unsigned int nr_bufs, const void *bufs) +{ + return _hypercall3(int, dm_op, domid, nr_bufs, bufs); +} + #undef __must_check #endif /* __MACHINE_XEN_HYPERCALL_H__ */ diff --git a/sys/dev/xen/privcmd/privcmd.c b/sys/dev/xen/privcmd/privcmd.c index c31c31bcfe41..d9f11aa0fe7a 100644 --- a/sys/dev/xen/privcmd/privcmd.c +++ b/sys/dev/xen/privcmd/privcmd.c @@ -1,484 +1,533 @@ /* * Copyright (c) 2014 Roger Pau Monné * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_PRIVCMD, "privcmd_dev", "Xen privcmd user-space device"); +#define MAX_DMOP_BUFFERS 16 + 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; BITSET_DEFINE_VAR() *err; }; 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_busy_acquire(m, VM_ALLOC_WAITFAIL) == 0) 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->err, M_PRIVCMD); } error = xenmem_free(privcmd_dev, 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; if (map->mapped != true) return (VM_PAGER_FAIL); pidx = OFF_TO_IDX(offset); if (pidx >= map->size || BIT_ISSET(map->size, pidx, map->err)) 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(vm_page_wired(page), ("page %p not wired", page)); KASSERT(!vm_page_busied(page), ("page %p is busy", page)); vm_page_busy_acquire(page, 0); vm_page_valid(page); if (*mres != NULL) vm_page_replace(page, object, pidx, *mres); else vm_page_insert(page, object, pidx); *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; 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 = xenmem_alloc(privcmd_dev, &map->pseudo_phys_res_id, size); if (map->pseudo_phys_res == NULL) { free(map, M_PRIVCMD); return (ENOMEM); } map->phys_base_addr = rman_get_start(map->pseudo_phys_res); map->mem = cdev_pager_allocate(map, OBJT_MGTDEVICE, &privcmd_pg_ops, size, nprot, *offset, NULL); if (map->mem == NULL) { xenmem_free(privcmd_dev, map->pseudo_phys_res_id, map->pseudo_phys_res); free(map, M_PRIVCMD); return (ENOMEM); } *object = map->mem; return (0); } static struct privcmd_map * setup_virtual_area(struct thread *td, unsigned long addr, unsigned long num) { vm_map_t map; vm_map_entry_t entry; vm_object_t mem; vm_pindex_t pindex; vm_prot_t prot; boolean_t wired; struct privcmd_map *umap; int error; if ((num == 0) || ((addr & PAGE_MASK) != 0)) return NULL; map = &td->td_proc->p_vmspace->vm_map; error = vm_map_lookup(&map, addr, VM_PROT_NONE, &entry, &mem, &pindex, &prot, &wired); if (error != KERN_SUCCESS || (entry->start != addr) || (entry->end != addr + (num * PAGE_SIZE))) return NULL; vm_map_lookup_done(map, entry); if ((mem->type != OBJT_MGTDEVICE) || (mem->un_pager.devp.ops != &privcmd_pg_ops)) return NULL; umap = mem->handle; /* Allocate a bitset to store broken page mappings. */ umap->err = BITSET_ALLOC(num, M_PRIVCMD, M_WAITOK | M_ZERO); return umap; } static int privcmd_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg, int mode, struct thread *td) { int error; unsigned int i; switch (cmd) { case IOCTL_PRIVCMD_HYPERCALL: { struct ioctl_privcmd_hypercall *hcall; hcall = (struct ioctl_privcmd_hypercall *)arg; #ifdef __amd64__ /* * The hypervisor page table walker will refuse to access * user-space pages if SMAP is enabled, so temporary disable it * while performing the hypercall. */ if (cpu_stdext_feature & CPUID_STDEXT_SMAP) stac(); #endif error = privcmd_hypercall(hcall->op, hcall->arg[0], hcall->arg[1], hcall->arg[2], hcall->arg[3], hcall->arg[4]); #ifdef __amd64__ if (cpu_stdext_feature & CPUID_STDEXT_SMAP) clac(); #endif 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; struct xen_add_to_physmap_range add; xen_ulong_t *idxs; xen_pfn_t *gpfns; int *errs; unsigned int index; struct privcmd_map *umap; uint16_t num; mmap = (struct ioctl_privcmd_mmapbatch *)arg; umap = setup_virtual_area(td, mmap->addr, mmap->num); if (umap == NULL) { error = EINVAL; break; } add.domid = DOMID_SELF; add.space = XENMAPSPACE_gmfn_foreign; add.foreign_domid = mmap->dom; /* * The 'size' field in the xen_add_to_physmap_range only * allows for UINT16_MAX mappings in a single hypercall. */ num = MIN(mmap->num, UINT16_MAX); idxs = malloc(sizeof(*idxs) * num, M_PRIVCMD, M_WAITOK); gpfns = malloc(sizeof(*gpfns) * num, M_PRIVCMD, M_WAITOK); errs = malloc(sizeof(*errs) * num, M_PRIVCMD, M_WAITOK); set_xen_guest_handle(add.idxs, idxs); set_xen_guest_handle(add.gpfns, gpfns); set_xen_guest_handle(add.errs, errs); for (index = 0; index < mmap->num; index += num) { num = MIN(mmap->num - index, UINT16_MAX); add.size = num; error = copyin(&mmap->arr[index], idxs, sizeof(idxs[0]) * num); if (error != 0) goto mmap_out; for (i = 0; i < num; i++) gpfns[i] = atop(umap->phys_base_addr + (i + index) * PAGE_SIZE); bzero(errs, sizeof(*errs) * num); error = HYPERVISOR_memory_op( XENMEM_add_to_physmap_range, &add); if (error != 0) { error = xen_translate_error(error); goto mmap_out; } for (i = 0; i < num; i++) { if (errs[i] != 0) { errs[i] = xen_translate_error(errs[i]); /* Mark the page as invalid. */ BIT_SET(mmap->num, index + i, umap->err); } } error = copyout(errs, &mmap->err[index], sizeof(errs[0]) * num); if (error != 0) goto mmap_out; } umap->mapped = true; mmap_out: free(idxs, M_PRIVCMD); free(gpfns, M_PRIVCMD); free(errs, M_PRIVCMD); if (!umap->mapped) free(umap->err, M_PRIVCMD); break; } case IOCTL_PRIVCMD_MMAP_RESOURCE: { struct ioctl_privcmd_mmapresource *mmap; struct xen_mem_acquire_resource adq; xen_pfn_t *gpfns; struct privcmd_map *umap; mmap = (struct ioctl_privcmd_mmapresource *)arg; bzero(&adq, sizeof(adq)); adq.domid = mmap->dom; adq.type = mmap->type; adq.id = mmap->id; /* Shortcut for getting the resource size. */ if (mmap->addr == 0 && mmap->num == 0) { error = HYPERVISOR_memory_op(XENMEM_acquire_resource, &adq); if (error != 0) { error = xen_translate_error(error); break; } error = copyout(&adq.nr_frames, &mmap->num, sizeof(mmap->num)); break; } umap = setup_virtual_area(td, mmap->addr, mmap->num); if (umap == NULL) { error = EINVAL; break; } adq.nr_frames = mmap->num; adq.frame = mmap->idx; gpfns = malloc(sizeof(*gpfns) * mmap->num, M_PRIVCMD, M_WAITOK); for (i = 0; i < mmap->num; i++) gpfns[i] = atop(umap->phys_base_addr) + i; set_xen_guest_handle(adq.frame_list, gpfns); error = HYPERVISOR_memory_op(XENMEM_acquire_resource, &adq); if (error != 0) error = xen_translate_error(error); else umap->mapped = true; free(gpfns, M_PRIVCMD); if (!umap->mapped) free(umap->err, M_PRIVCMD); + break; + } + case IOCTL_PRIVCMD_DM_OP: { + const struct ioctl_privcmd_dmop *dmop; + struct privcmd_dmop_buf *bufs; + struct xen_dm_op_buf *hbufs; + + dmop = (struct ioctl_privcmd_dmop *)arg; + + if (dmop->num == 0) + break; + + if (dmop->num > MAX_DMOP_BUFFERS) { + error = E2BIG; + break; + } + + bufs = malloc(sizeof(*bufs) * dmop->num, M_PRIVCMD, M_WAITOK); + + error = copyin(dmop->ubufs, bufs, sizeof(*bufs) * dmop->num); + if (error != 0) { + free(bufs, M_PRIVCMD); + break; + } + + hbufs = malloc(sizeof(*hbufs) * dmop->num, M_PRIVCMD, M_WAITOK); + for (i = 0; i < dmop->num; i++) { + set_xen_guest_handle(hbufs[i].h, bufs[i].uptr); + hbufs[i].size = bufs[i].size; + } + +#ifdef __amd64__ + if (cpu_stdext_feature & CPUID_STDEXT_SMAP) + stac(); +#endif + error = HYPERVISOR_dm_op(dmop->dom, dmop->num, hbufs); +#ifdef __amd64__ + if (cpu_stdext_feature & CPUID_STDEXT_SMAP) + clac(); +#endif + if (error != 0) + error = xen_translate_error(error); + + free(bufs, M_PRIVCMD); + free(hbufs, 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 d0cd9179665c..78052fac9355 100644 --- a/sys/i386/include/xen/hypercall.h +++ b/sys/i386/include/xen/hypercall.h @@ -1,417 +1,424 @@ /****************************************************************************** * hypercall.h * * Linux-specific hypervisor handling. * * Copyright (c) 2002-2004, K A Fraser * * This file may be 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. */ #ifndef __HYPERCALL_H__ #define __HYPERCALL_H__ #include #include #include extern char *hypercall_page; #define __STR(x) #x #define STR(x) __STR(x) #define ENOXENSYS 38 #define CONFIG_XEN_COMPAT 0x030002 #define HYPERCALL_STR(name) \ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)" #define _hypercall0(type, name) \ ({ \ long __res; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res) \ : \ : "memory" ); \ (type)__res; \ }) #define _hypercall1(type, name, a1) \ ({ \ long __res, __ign1; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=b" (__ign1) \ : "1" ((long)(a1)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall2(type, name, a1, a2) \ ({ \ long __res, __ign1, __ign2; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \ : "1" ((long)(a1)), "2" ((long)(a2)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall3(type, name, a1, a2, a3) \ ({ \ long __res, __ign1, __ign2, __ign3; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ "=d" (__ign3) \ : "1" ((long)(a1)), "2" ((long)(a2)), \ "3" ((long)(a3)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall4(type, name, a1, a2, a3, a4) \ ({ \ long __res, __ign1, __ign2, __ign3, __ign4; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ "=d" (__ign3), "=S" (__ign4) \ : "1" ((long)(a1)), "2" ((long)(a2)), \ "3" ((long)(a3)), "4" ((long)(a4)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall5(type, name, a1, a2, a3, a4, a5) \ ({ \ long __res, __ign1, __ign2, __ign3, __ign4, __ign5; \ __asm__ volatile ( \ HYPERCALL_STR(name) \ : "=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)) \ : "memory" ); \ (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) { return _hypercall1(int, set_trap_table, table); } static inline int HYPERVISOR_mmu_update( mmu_update_t *req, int count, int *success_count, domid_t domid) { return _hypercall4(int, mmu_update, req, count, success_count, domid); } static inline int HYPERVISOR_mmuext_op( mmuext_op_t *op, int count, int *success_count, domid_t domid) { return _hypercall4(int, mmuext_op, op, count, success_count, domid); } static inline int HYPERVISOR_set_gdt( unsigned long *frame_list, int entries) { return _hypercall2(int, set_gdt, frame_list, entries); } static inline int HYPERVISOR_stack_switch( unsigned long ss, unsigned long esp) { return _hypercall2(int, stack_switch, ss, esp); } static inline int HYPERVISOR_set_callbacks( unsigned long event_selector, unsigned long event_address, unsigned long failsafe_selector, unsigned long failsafe_address) { return _hypercall4(int, set_callbacks, event_selector, event_address, failsafe_selector, failsafe_address); } static inline int HYPERVISOR_fpu_taskswitch( int set) { return _hypercall1(int, fpu_taskswitch, set); } static inline int HYPERVISOR_sched_op_compat( int cmd, unsigned long arg) { return _hypercall2(int, sched_op_compat, cmd, arg); } static inline int HYPERVISOR_sched_op( int cmd, void *arg) { return _hypercall2(int, sched_op, cmd, arg); } static inline long HYPERVISOR_set_timer_op( uint64_t timeout) { unsigned long timeout_hi = (unsigned long)(timeout>>32); unsigned long timeout_lo = (unsigned long)timeout; return _hypercall2(long, set_timer_op, timeout_lo, timeout_hi); } static inline int HYPERVISOR_platform_op( struct xen_platform_op *platform_op) { platform_op->interface_version = XENPF_INTERFACE_VERSION; return _hypercall1(int, platform_op, platform_op); } static inline int HYPERVISOR_set_debugreg( int reg, unsigned long value) { return _hypercall2(int, set_debugreg, reg, value); } static inline unsigned long HYPERVISOR_get_debugreg( int reg) { return _hypercall1(unsigned long, get_debugreg, reg); } static inline int HYPERVISOR_update_descriptor( uint64_t ma, uint64_t desc) { return _hypercall4(int, update_descriptor, ma, ma>>32, desc, desc>>32); } static inline int HYPERVISOR_memory_op( unsigned int cmd, void *arg) { return _hypercall2(int, memory_op, cmd, arg); } static inline int HYPERVISOR_multicall( void *call_list, int nr_calls) { return _hypercall2(int, multicall, call_list, nr_calls); } static inline int HYPERVISOR_update_va_mapping( unsigned long va, uint64_t new_val, unsigned long flags) { uint32_t hi, lo; lo = (uint32_t)(new_val & 0xffffffff); hi = (uint32_t)(new_val >> 32); return _hypercall4(int, update_va_mapping, va, lo, hi, flags); } static inline int HYPERVISOR_event_channel_op( int cmd, void *arg) { int rc = _hypercall2(int, event_channel_op, cmd, arg); #if CONFIG_XEN_COMPAT <= 0x030002 if (__predict_false(rc == -ENOXENSYS)) { struct evtchn_op op; op.cmd = cmd; memcpy(&op.u, arg, sizeof(op.u)); rc = _hypercall1(int, event_channel_op_compat, &op); memcpy(arg, &op.u, sizeof(op.u)); } #endif return (rc); } static inline int HYPERVISOR_xen_version( int cmd, void *arg) { return _hypercall2(int, xen_version, cmd, arg); } static inline int HYPERVISOR_console_io( int cmd, int count, const char *str) { return _hypercall3(int, console_io, cmd, count, str); } static inline int HYPERVISOR_physdev_op( int cmd, void *arg) { int rc = _hypercall2(int, physdev_op, cmd, arg); #if CONFIG_XEN_COMPAT <= 0x030002 if (__predict_false(rc == -ENOXENSYS)) { struct physdev_op op; op.cmd = cmd; memcpy(&op.u, arg, sizeof(op.u)); rc = _hypercall1(int, physdev_op_compat, &op); memcpy(arg, &op.u, sizeof(op.u)); } #endif return (rc); } static inline int HYPERVISOR_grant_table_op( unsigned int cmd, void *uop, unsigned int count) { return _hypercall3(int, grant_table_op, cmd, uop, count); } static inline int HYPERVISOR_update_va_mapping_otherdomain( unsigned long va, uint64_t new_val, unsigned long flags, domid_t domid) { uint32_t hi, lo; lo = (uint32_t)(new_val & 0xffffffff); hi = (uint32_t)(new_val >> 32); return _hypercall5(int, update_va_mapping_otherdomain, va, lo, hi, flags, domid); } static inline int HYPERVISOR_vm_assist( unsigned int cmd, unsigned int type) { return _hypercall2(int, vm_assist, cmd, type); } static inline int HYPERVISOR_vcpu_op( int cmd, int vcpuid, void *extra_args) { return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args); } static inline int HYPERVISOR_suspend( unsigned long srec) { struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_suspend }; int rc = _hypercall3(int, sched_op, SCHEDOP_shutdown, &sched_shutdown, srec); #if CONFIG_XEN_COMPAT <= 0x030002 if (rc == -ENOXENSYS) rc = _hypercall3(int, sched_op_compat, SCHEDOP_shutdown, SHUTDOWN_suspend, srec); #endif return (rc); } #if CONFIG_XEN_COMPAT <= 0x030002 static inline int HYPERVISOR_nmi_op( unsigned long op, void *arg) { return _hypercall2(int, nmi_op, op, arg); } #endif static inline int HYPERVISOR_callback_op( int cmd, void *arg) { return _hypercall2(int, callback_op, cmd, arg); } #ifndef CONFIG_XEN static inline unsigned long HYPERVISOR_hvm_op( int op, void *arg) { return _hypercall2(unsigned long, hvm_op, op, arg); } #endif static inline int HYPERVISOR_xenoprof_op( int op, void *arg) { return _hypercall2(int, xenoprof_op, op, arg); } static inline int HYPERVISOR_kexec_op( unsigned long op, void *args) { return _hypercall2(int, kexec_op, op, args); } + +static inline int +HYPERVISOR_dm_op( + domid_t domid, unsigned int nr_bufs, const void *bufs) +{ + return _hypercall3(int, dm_op, domid, nr_bufs, bufs); +} #endif /* __HYPERCALL_H__ */ /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ diff --git a/sys/xen/hypervisor.h b/sys/xen/hypervisor.h index d613e67c5a3b..2be61597fc18 100644 --- a/sys/xen/hypervisor.h +++ b/sys/xen/hypervisor.h @@ -1,100 +1,101 @@ /****************************************************************************** * hypervisor.h * * Linux-specific hypervisor handling. * * Copyright (c) 2002, K A Fraser * * $FreeBSD$ */ #ifndef __XEN_HYPERVISOR_H__ #define __XEN_HYPERVISOR_H__ #include #include #include #include #include #include #include #include #include +#include #include extern uint64_t get_system_time(int ticks); static inline int HYPERVISOR_console_write(const char *str, int count) { return HYPERVISOR_console_io(CONSOLEIO_write, count, str); } static inline int HYPERVISOR_yield(void) { int rc = HYPERVISOR_sched_op(SCHEDOP_yield, NULL); #if CONFIG_XEN_COMPAT <= 0x030002 if (rc == -ENOXENSYS) rc = HYPERVISOR_sched_op_compat(SCHEDOP_yield, 0); #endif return (rc); } static inline int HYPERVISOR_block( void) { int rc = HYPERVISOR_sched_op(SCHEDOP_block, NULL); #if CONFIG_XEN_COMPAT <= 0x030002 if (rc == -ENOXENSYS) rc = HYPERVISOR_sched_op_compat(SCHEDOP_block, 0); #endif return (rc); } static inline void HYPERVISOR_shutdown(unsigned int reason) { struct sched_shutdown sched_shutdown = { .reason = reason }; HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); #if CONFIG_XEN_COMPAT <= 0x030002 HYPERVISOR_sched_op_compat(SCHEDOP_shutdown, reason); #endif } static inline void HYPERVISOR_crash(void) { HYPERVISOR_shutdown(SHUTDOWN_crash); /* NEVER REACHED */ for (;;) ; /* eliminate noreturn error */ } /* Transfer control to hypervisor until an event is detected on one */ /* of the specified ports or the specified number of ticks elapse */ static inline int HYPERVISOR_poll( evtchn_port_t *ports, unsigned int nr_ports, int ticks) { int rc; struct sched_poll sched_poll = { .nr_ports = nr_ports, .timeout = get_system_time(ticks) }; set_xen_guest_handle(sched_poll.ports, ports); rc = HYPERVISOR_sched_op(SCHEDOP_poll, &sched_poll); #if CONFIG_XEN_COMPAT <= 0x030002 if (rc == -ENOXENSYS) rc = HYPERVISOR_sched_op_compat(SCHEDOP_yield, 0); #endif return (rc); } #endif /* __XEN_HYPERVISOR_H__ */ diff --git a/sys/xen/privcmd.h b/sys/xen/privcmd.h index ac2d0c00b845..cd0bc7d550d9 100644 --- a/sys/xen/privcmd.h +++ b/sys/xen/privcmd.h @@ -1,73 +1,86 @@ /****************************************************************************** * 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 */ }; struct ioctl_privcmd_mmapresource { domid_t dom; /* target domain */ unsigned int type; /* type of resource to map */ unsigned int id; /* type-specific resource identifier */ unsigned int idx; /* the index of the initial frame to be mapped */ unsigned long num; /* number of frames of the resource to be mapped */ unsigned long addr; /* physical address to map into */ /* * Note: issuing an ioctl with num = addr = 0 will return the size of * the resource. */ }; +struct privcmd_dmop_buf { + void *uptr; /* pointer to memory (in calling process) */ + size_t size; /* size of the buffer */ +}; + +struct ioctl_privcmd_dmop { + domid_t dom; /* target domain */ + unsigned int num; /* num of buffers */ + const struct privcmd_dmop_buf *ubufs; /* array of buffers */ +}; + #define IOCTL_PRIVCMD_HYPERCALL \ _IOWR('E', 0, struct ioctl_privcmd_hypercall) #define IOCTL_PRIVCMD_MMAPBATCH \ _IOWR('E', 1, struct ioctl_privcmd_mmapbatch) #define IOCTL_PRIVCMD_MMAP_RESOURCE \ _IOW('E', 2, struct ioctl_privcmd_mmapresource) +#define IOCTL_PRIVCMD_DM_OP \ + _IOW('E', 3, struct ioctl_privcmd_dmop) #endif /* !__XEN_PRIVCMD_H__ */