From owner-svn-soc-all@freebsd.org Tue Aug 4 15:11:40 2015 Return-Path: Delivered-To: svn-soc-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 73B869B3441 for ; Tue, 4 Aug 2015 15:11:40 +0000 (UTC) (envelope-from stefano@FreeBSD.org) Received: from socsvn.freebsd.org (socsvn.freebsd.org [IPv6:2001:1900:2254:206a::50:2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 5EA8E11CB for ; Tue, 4 Aug 2015 15:11:40 +0000 (UTC) (envelope-from stefano@FreeBSD.org) Received: from socsvn.freebsd.org ([127.0.1.124]) by socsvn.freebsd.org (8.15.2/8.15.2) with ESMTP id t74FBejd050982 for ; Tue, 4 Aug 2015 15:11:40 GMT (envelope-from stefano@FreeBSD.org) Received: (from www@localhost) by socsvn.freebsd.org (8.15.2/8.15.2/Submit) id t74FBam3050244 for svn-soc-all@FreeBSD.org; Tue, 4 Aug 2015 15:11:36 GMT (envelope-from stefano@FreeBSD.org) Date: Tue, 4 Aug 2015 15:11:36 GMT Message-Id: <201508041511.t74FBam3050244@socsvn.freebsd.org> X-Authentication-Warning: socsvn.freebsd.org: www set sender to stefano@FreeBSD.org using -f From: stefano@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r289211 - in soc2015/stefano/ptnetmap/stable/10: lib/libvmmapi sys/amd64/include sys/amd64/vmm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-soc-all@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the entire Summer of Code repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 04 Aug 2015 15:11:40 -0000 Author: stefano Date: Tue Aug 4 15:11:36 2015 New Revision: 289211 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=289211 Log: [vmm.ko] add new IOCTL (VM_IO_REG_HANDLER to catch write/read on specific I/O address and send notification. For now I've implemented only 1 type of handler (VM_IO_REGH_KWEVENTS) to use events in the kernel through wakeup() and tsleep()/msleep(), but I wrote the code to be easily extended to support other type of handler (cond_signal, write/ioctl on fd, etc). Modified: soc2015/stefano/ptnetmap/stable/10/lib/libvmmapi/vmmapi.c soc2015/stefano/ptnetmap/stable/10/lib/libvmmapi/vmmapi.h soc2015/stefano/ptnetmap/stable/10/sys/amd64/include/vmm.h soc2015/stefano/ptnetmap/stable/10/sys/amd64/include/vmm_dev.h soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm.c soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_dev.c soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_ioport.c soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_ioport.h Modified: soc2015/stefano/ptnetmap/stable/10/lib/libvmmapi/vmmapi.c ============================================================================== --- soc2015/stefano/ptnetmap/stable/10/lib/libvmmapi/vmmapi.c Tue Aug 4 15:06:06 2015 (r289210) +++ soc2015/stefano/ptnetmap/stable/10/lib/libvmmapi/vmmapi.c Tue Aug 4 15:11:36 2015 (r289211) @@ -704,6 +704,23 @@ } int +vm_io_reg_handler(struct vmctx *ctx, uint16_t port, uint16_t in, uint32_t mask_data, uint32_t data, + enum vm_io_regh_type type, void *arg) +{ + struct vm_io_reg_handler ioregh; + + bzero(&ioregh, sizeof(ioregh)); + ioregh.port = port; + ioregh.in = in; + ioregh.mask_data = mask_data; + ioregh.data = data; + ioregh.type = type; + ioregh.arg = arg; + + return (ioctl(ctx->fd, VM_IO_REG_HANDLER, &ioregh)); +} + +int vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot, int func, uint64_t addr, uint64_t msg, int numvec) { Modified: soc2015/stefano/ptnetmap/stable/10/lib/libvmmapi/vmmapi.h ============================================================================== --- soc2015/stefano/ptnetmap/stable/10/lib/libvmmapi/vmmapi.h Tue Aug 4 15:06:06 2015 (r289210) +++ soc2015/stefano/ptnetmap/stable/10/lib/libvmmapi/vmmapi.h Tue Aug 4 15:11:36 2015 (r289211) @@ -110,7 +110,8 @@ int vm_set_intinfo(struct vmctx *ctx, int vcpu, uint64_t exit_intinfo); int vm_get_fd(struct vmctx *ctx); int vm_map_user_buf(struct vmctx *ctx, vm_paddr_t gpa, size_t len, void *host_buf); - +int vm_io_reg_handler(struct vmctx *ctx, uint16_t port, uint16_t in, + uint32_t mask_data, uint32_t data, enum vm_io_regh_type type, void *arg); /* * Return a pointer to the statistics buffer. Note that this is not MT-safe. */ Modified: soc2015/stefano/ptnetmap/stable/10/sys/amd64/include/vmm.h ============================================================================== --- soc2015/stefano/ptnetmap/stable/10/sys/amd64/include/vmm.h Tue Aug 4 15:06:06 2015 (r289210) +++ soc2015/stefano/ptnetmap/stable/10/sys/amd64/include/vmm.h Tue Aug 4 15:11:36 2015 (r289211) @@ -287,6 +287,7 @@ struct vatpic *vm_atpic(struct vm *vm); struct vatpit *vm_atpit(struct vm *vm); struct vpmtmr *vm_pmtmr(struct vm *vm); +struct ioregh *vm_ioregh(struct vm *vm); /* * Inject exception 'vme' into the guest vcpu. This function returns 0 on @@ -381,7 +382,13 @@ EDGE_TRIGGER, LEVEL_TRIGGER }; - + +enum vm_io_regh_type { + VM_IO_REGH_DELETE, + VM_IO_REGH_KWEVENTS, /* kernel wait events */ + VM_IO_REGH_MAX +}; + /* * The 'access' field has the format specified in Table 21-2 of the Intel * Architecture Manual vol 3b. Modified: soc2015/stefano/ptnetmap/stable/10/sys/amd64/include/vmm_dev.h ============================================================================== --- soc2015/stefano/ptnetmap/stable/10/sys/amd64/include/vmm_dev.h Tue Aug 4 15:06:06 2015 (r289210) +++ soc2015/stefano/ptnetmap/stable/10/sys/amd64/include/vmm_dev.h Tue Aug 4 15:11:36 2015 (r289211) @@ -117,6 +117,15 @@ size_t len; }; +struct vm_io_reg_handler { + uint16_t port; + uint16_t in; + uint32_t mask_data; /* 0 means match anything */ + uint32_t data; + enum vm_io_regh_type type; + void *arg; +}; + struct vm_pptdev_msi { int vcpu; int bus; @@ -262,6 +271,7 @@ IOCNUM_GET_CPUSET = 91, IOCNUM_MAP_USER_BUF = 100, + IOCNUM_IO_REG_HANDLER = 101, }; #define VM_RUN \ @@ -318,6 +328,8 @@ _IOW('v', IOCNUM_MAP_PPTDEV_MMIO, struct vm_pptdev_mmio) #define VM_MAP_USER_BUF \ _IOW('v', IOCNUM_MAP_USER_BUF, struct vm_user_buf) +#define VM_IO_REG_HANDLER \ + _IOW('v', IOCNUM_IO_REG_HANDLER, struct vm_io_reg_handler) #define VM_PPTDEV_MSI \ _IOW('v', IOCNUM_PPTDEV_MSI, struct vm_pptdev_msi) #define VM_PPTDEV_MSIX \ Modified: soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm.c ============================================================================== --- soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm.c Tue Aug 4 15:06:06 2015 (r289210) +++ soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm.c Tue Aug 4 15:11:36 2015 (r289211) @@ -137,6 +137,7 @@ struct vatpic *vatpic; /* (i) virtual atpic */ struct vatpit *vatpit; /* (i) virtual atpit */ struct vpmtmr *vpmtmr; /* (i) virtual ACPI PM timer */ + struct ioregh *ioregh; /* () I/O reg handler */ volatile cpuset_t active_cpus; /* (i) active vcpus */ int suspend; /* (i) stop VM execution */ volatile cpuset_t suspended_cpus; /* (i) suspended vcpus */ @@ -377,6 +378,7 @@ vm->vatpic = vatpic_init(vm); vm->vatpit = vatpit_init(vm); vm->vpmtmr = vpmtmr_init(vm); + vm->ioregh = ioregh_init(vm); CPU_ZERO(&vm->active_cpus); @@ -439,6 +441,7 @@ if (vm->iommu != NULL) iommu_destroy_domain(vm->iommu); + ioregh_cleanup(vm->ioregh); vpmtmr_cleanup(vm->vpmtmr); vatpit_cleanup(vm->vatpit); vhpet_cleanup(vm->vhpet); @@ -505,29 +508,15 @@ return (0); } -static int vm_gpa_wire(struct vm *vm); int vm_map_usermem(struct vm *vm, vm_paddr_t gpa, size_t len, void *buf, struct thread *td) { vm_object_t obj; - int error; - if ((obj = vmm_usermem_alloc(vm->vmspace, gpa, len, buf, td)) == NULL) return (ENOMEM); -#if 0 - error = vm_gpa_wire(vm); /* XXX-ste: is needed? */ - - if (error) - goto err; -#endif - return (0); - -//err: - vmm_usermem_free(vm->vmspace, gpa, len); - return (error); } int @@ -2253,6 +2242,12 @@ return (vm->vpmtmr); } +struct ioregh * +vm_ioregh(struct vm *vm) +{ + return (vm->ioregh); +} + enum vm_reg_name vm_segment_name(int seg) { Modified: soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_dev.c ============================================================================== --- soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_dev.c Tue Aug 4 15:06:06 2015 (r289210) +++ soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_dev.c Tue Aug 4 15:11:36 2015 (r289211) @@ -54,6 +54,7 @@ #include "vmm_lapic.h" #include "vmm_stat.h" #include "vmm_mem.h" +#include "vmm_ioport.h" #include "io/ppt.h" #include "io/vatpic.h" #include "io/vioapic.h" @@ -165,6 +166,7 @@ struct vm_pptdev_msi *pptmsi; struct vm_pptdev_msix *pptmsix; struct vm_user_buf *usermem; + struct vm_io_reg_handler *ioregh; struct vm_nmi *vmnmi; struct vm_stats *vmstats; struct vm_stat_desc *statdesc; @@ -302,6 +304,11 @@ error = vm_map_usermem(sc->vm, usermem->gpa, usermem->len, usermem->addr, td); break; + case VM_IO_REG_HANDLER: + ioregh = (struct vm_io_reg_handler *)data; + error = vmm_ioport_reg_handler(sc->vm, ioregh->port, ioregh->in, ioregh->mask_data, + ioregh->data, ioregh->type, ioregh->arg); + break; case VM_BIND_PPTDEV: pptdev = (struct vm_pptdev *)data; error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot, Modified: soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_ioport.c ============================================================================== --- soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_ioport.c Tue Aug 4 15:06:06 2015 (r289210) +++ soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_ioport.c Tue Aug 4 15:11:36 2015 (r289211) @@ -100,31 +100,246 @@ } #endif /* KTR */ +#ifdef VMM_IOPORT_REG_HANDLER +#include +#include +#include +#include +#include + +static MALLOC_DEFINE(M_IOREGH, "ioregh", "bhyve ioport reg handlers"); + +#define IOREGH_LOCK(ioregh) mtx_lock_spin(&((ioregh)->mtx)) +#define IOREGH_UNLOCK(ioregh) mtx_unlock_spin(&((ioregh)->mtx)) + +#define IOPORT_MAX_REG_HANDLER 12 + +typedef int (*ioport_reg_handler_func_t)(struct vm *vm, struct ioport_reg_handler *regh, + uint32_t *val); + +struct ioport_reg_handler { + uint16_t port; + uint16_t in; + uint32_t mask_data; + uint32_t data; + ioport_reg_handler_func_t handler; + void *handler_arg; +}; + +struct ioregh { + struct mtx mtx; + /* TODO: use hash table is better */ + struct ioport_reg_handler handlers[IOPORT_MAX_REG_HANDLER]; +}; + +/* ----- I/O reg handlers ----- */ + +/* VM_IO_REGH_KWEVENTS handler */ +static int +vmm_ioport_reg_wakeup(struct vm *vm, struct ioport_reg_handler *regh, uint32_t *val) +{ + wakeup(regh->handler_arg); + return (0); +} + +/* call with ioregh->mtx held */ +static struct ioport_reg_handler * +vmm_ioport_find_handler(struct ioregh *ioregh, uint16_t port, uint16_t in, uint32_t mask_data, uint32_t data) +{ + struct ioport_reg_handler *regh; + uint32_t mask; + int i; + + regh = ioregh->handlers; + for (i = 0; i < IOPORT_MAX_REG_HANDLER; i++) { + if (regh[i].handler != NULL) { + mask = regh[i].mask_data & mask_data; + if ((regh[i].port == port) && (regh[i].in == in) + && ((mask & regh[i].data) == (mask & data))) { + return ®h[i]; + } + } + } + + return (NULL); +} + +/* call with ioregh->mtx held */ +static struct ioport_reg_handler * +vmm_ioport_empty_handler(struct ioregh *ioregh) +{ + struct ioport_reg_handler *regh; + int i; + + regh = ioregh->handlers; + for (i = 0; i < IOPORT_MAX_REG_HANDLER; i++) { + if (regh[i].handler == NULL) { + return ®h[i]; + } + } + + return (NULL); +} + + +static int +vmm_ioport_add_handler(struct vm *vm, uint16_t port, uint16_t in, uint32_t mask_data, uint32_t data, + ioport_reg_handler_func_t handler, void *handler_arg) +{ + struct ioport_reg_handler *regh; + struct ioregh *ioregh; + int ret = 0; + + ioregh = vm_ioregh(vm); + + IOREGH_LOCK(ioregh); + + regh = vmm_ioport_find_handler(ioregh, port, in, mask_data, data); + if (regh != NULL) { + printf("%s: handler for port %d in %d mask_data %d data %d already registered\n", + __FUNCTION__, port, in, mask_data, data); + ret = EEXIST; + goto err; + } + + regh = vmm_ioport_empty_handler(ioregh); + if (regh == NULL) { + printf("%s: empty reg_handler slot not found\n", __FUNCTION__); + ret = ENOMEM; + goto err; + } + + regh->port = port; + regh->in = in; + regh->mask_data = mask_data; + regh->data = data; + regh->handler = handler; + regh->handler_arg = handler_arg; + +err: + IOREGH_UNLOCK(ioregh); + return (ret); +} + +static int +vmm_ioport_del_handler(struct vm *vm, uint16_t port, uint16_t in, uint32_t mask_data, uint32_t data) +{ + struct ioport_reg_handler *regh; + struct ioregh *ioregh; + int ret = 0; + + ioregh = vm_ioregh(vm); + + IOREGH_LOCK(ioregh); + + regh = vmm_ioport_find_handler(ioregh, port, in, mask_data, data); + + if (regh == NULL) { + ret = EINVAL; + goto err; + } + + bzero(regh, sizeof(struct ioport_reg_handler)); +err: + IOREGH_UNLOCK(ioregh); + return (ret); +} + +int +vmm_ioport_reg_handler(struct vm *vm, uint16_t port, uint16_t in, uint32_t mask_data, uint32_t data, + enum vm_io_regh_type type, void *arg) +{ + int ret = 0; + + switch (type) { + case VM_IO_REGH_DELETE: + ret = vmm_ioport_del_handler(vm, port, in, mask_data, data); + break; + case VM_IO_REGH_KWEVENTS: + ret = vmm_ioport_add_handler(vm, port, in, mask_data, data, vmm_ioport_reg_wakeup, arg); + break; + default: + printf("%s: unknown reg_handler type\n", __FUNCTION__); + ret = EINVAL; + break; + } + + return (ret); +} + +static int +invoke_reg_handler(struct vm *vm, int vcpuid, struct vm_exit *vmexit, uint32_t *val, int *error) +{ + struct ioport_reg_handler *regh; + struct ioregh *ioregh; + uint32_t mask_data; + + mask_data = vie_size2mask(vmexit->u.inout.bytes); + ioregh = vm_ioregh(vm); + + IOREGH_LOCK(ioregh); + regh = vmm_ioport_find_handler(ioregh, vmexit->u.inout.port, vmexit->u.inout.in, + mask_data, vmexit->u.inout.eax); + if (regh == NULL) { + IOREGH_UNLOCK(ioregh); + return (0); + } + /* XXX: maybe is better to use refcount and lock only find */ + *error = (*(regh->handler))(vm, regh, val); + IOREGH_UNLOCK(ioregh); + return (1); +} + +struct ioregh * +ioregh_init(struct vm *vm) +{ + struct ioregh *ioregh; + + ioregh = malloc(sizeof(struct ioregh), M_IOREGH, M_WAITOK | M_ZERO); + + mtx_init(&ioregh->mtx, "ioregh lock", NULL, MTX_SPIN); + + return (ioregh); +} + +void +ioregh_cleanup(struct ioregh *ioregh) +{ + free(ioregh, M_IOREGH); +} +#else /* !VMM_IOPORT_REG_HANDLER */ +#define invoke_reg_handler(_1, _2, _3, _4, _5) (0) +#endif /* VMM_IOPORT_REG_HANDLER */ + static int emulate_inout_port(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) { ioport_handler_func_t handler; uint32_t mask, val; - int error; + int regh = 0, error = 0; /* * If there is no handler for the I/O port then punt to userspace. */ - if (vmexit->u.inout.port >= MAX_IOPORTS || - (handler = ioport_handler[vmexit->u.inout.port]) == NULL) { + if ((vmexit->u.inout.port >= MAX_IOPORTS || + (handler = ioport_handler[vmexit->u.inout.port]) == NULL) && + (regh = invoke_reg_handler(vm, vcpuid, vmexit, &val, &error)) == 0) { *retu = true; return (0); } - mask = vie_size2mask(vmexit->u.inout.bytes); + if (!regh) { + mask = vie_size2mask(vmexit->u.inout.bytes); + + if (!vmexit->u.inout.in) { + val = vmexit->u.inout.eax & mask; + } - if (!vmexit->u.inout.in) { - val = vmexit->u.inout.eax & mask; + error = (*handler)(vm, vcpuid, vmexit->u.inout.in, + vmexit->u.inout.port, vmexit->u.inout.bytes, &val); } - error = (*handler)(vm, vcpuid, vmexit->u.inout.in, - vmexit->u.inout.port, vmexit->u.inout.bytes, &val); if (error) { /* * The value returned by this function is also the return value Modified: soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_ioport.h ============================================================================== --- soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_ioport.h Tue Aug 4 15:06:06 2015 (r289210) +++ soc2015/stefano/ptnetmap/stable/10/sys/amd64/vmm/vmm_ioport.h Tue Aug 4 15:11:36 2015 (r289211) @@ -29,6 +29,24 @@ #ifndef _VMM_IOPORT_H_ #define _VMM_IOPORT_H_ +#define VMM_IOPORT_REG_HANDLER + +#ifdef VMM_IOPORT_REG_HANDLER +struct ioport_reg_handler; +struct ioregh; + +struct ioregh *ioregh_init(struct vm *vm); +void ioregh_cleanup(struct ioregh *ioregh); + +int +vmm_ioport_reg_handler(struct vm *vm, uint16_t port, uint16_t in, uint32_t mask_data, uint32_t data, + enum vm_io_regh_type type, void *arg); +#else /* !VMM_IOPORT_REG_HANDLER */ +#define ioregh_init(_1) (NULL) +#define ioregh_cleanup(_1) +#define vmm_ioport_reg_handler(_1, _2, _3, _4,_5, _6, _7) (EINVAL) +#endif /* VMM_IOPORT_REG_HANDLER */ + typedef int (*ioport_handler_func_t)(struct vm *vm, int vcpuid, bool in, int port, int bytes, uint32_t *val);