socsvn commit: r289211 - in soc2015/stefano/ptnetmap/stable/10: lib/libvmmapi sys/amd64/include sys/amd64/vmm

stefano at FreeBSD.org stefano at FreeBSD.org
Tue Aug 4 15:11:40 UTC 2015


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 <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+
+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 &regh[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 &regh[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);
 


More information about the svn-soc-all mailing list