diff options
author | jhb <jhb@FreeBSD.org> | 2014-07-22 04:39:16 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2014-07-22 04:39:16 +0000 |
commit | ce450da4301f9fcfa34afaa8d4e1b69a4877a2f7 (patch) | |
tree | e884913964691296030a1dcf7a5ec58905d567c2 /usr.sbin | |
parent | c1fe945ebd0d209b238eb98b47c40c115576f2e4 (diff) | |
download | FreeBSD-src-ce450da4301f9fcfa34afaa8d4e1b69a4877a2f7.zip FreeBSD-src-ce450da4301f9fcfa34afaa8d4e1b69a4877a2f7.tar.gz |
MFC 266424,266476,266524,266573,266595,266626,266627,266633,266641,266642,
266708,266724,266934,266935,268521:
Emulation of the "ins" and "outs" instructions.
Various fixes for translating guest linear addresses to guest physical
addresses.
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/bhyve/bhyverun.c | 26 | ||||
-rw-r--r-- | usr.sbin/bhyve/inout.c | 151 | ||||
-rw-r--r-- | usr.sbin/bhyve/inout.h | 10 | ||||
-rw-r--r-- | usr.sbin/bhyve/mem.c | 1 | ||||
-rw-r--r-- | usr.sbin/bhyve/pci_virtio_block.c | 6 |
5 files changed, 144 insertions, 50 deletions
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c index d9b4418..f9a67cb 100644 --- a/usr.sbin/bhyve/bhyverun.c +++ b/usr.sbin/bhyve/bhyverun.c @@ -288,33 +288,34 @@ static int vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) { int error; - int bytes, port, in, out; - uint32_t eax; + int bytes, port, in, out, string; int vcpu; vcpu = *pvcpu; port = vme->u.inout.port; bytes = vme->u.inout.bytes; - eax = vme->u.inout.eax; + string = vme->u.inout.string; in = vme->u.inout.in; out = !in; - /* We don't deal with these */ - if (vme->u.inout.string || vme->u.inout.rep) - return (VMEXIT_ABORT); - /* Extra-special case of host notifications */ - if (out && port == GUEST_NIO_PORT) - return (vmexit_handle_notify(ctx, vme, pvcpu, eax)); + if (out && port == GUEST_NIO_PORT) { + error = vmexit_handle_notify(ctx, vme, pvcpu, vme->u.inout.eax); + return (error); + } - error = emulate_inout(ctx, vcpu, in, port, bytes, &eax, strictio); - if (error == INOUT_OK && in) - error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX, eax); + error = emulate_inout(ctx, vcpu, vme, strictio); + if (error == INOUT_OK && in && !string) { + error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX, + vme->u.inout.eax); + } switch (error) { case INOUT_OK: return (VMEXIT_CONTINUE); + case INOUT_RESTART: + return (VMEXIT_RESTART); case INOUT_RESET: stats.io_reset++; return (VMEXIT_RESET); @@ -514,6 +515,7 @@ vmexit_suspend(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) static vmexit_handler_t handler[VM_EXITCODE_MAX] = { [VM_EXITCODE_INOUT] = vmexit_inout, + [VM_EXITCODE_INOUT_STR] = vmexit_inout, [VM_EXITCODE_VMX] = vmexit_vmx, [VM_EXITCODE_BOGUS] = vmexit_bogus, [VM_EXITCODE_RDMSR] = vmexit_rdmsr, diff --git a/usr.sbin/bhyve/inout.c b/usr.sbin/bhyve/inout.c index 5fbe99b..fe9e0d8 100644 --- a/usr.sbin/bhyve/inout.c +++ b/usr.sbin/bhyve/inout.c @@ -31,11 +31,21 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/linker_set.h> +#include <sys/_iovec.h> +#include <sys/mman.h> + +#include <x86/psl.h> +#include <x86/segments.h> + +#include <machine/vmm.h> +#include <machine/vmm_instruction_emul.h> +#include <vmmapi.h> #include <stdio.h> #include <string.h> #include <assert.h> +#include "bhyverun.h" #include "inout.h" SET_DECLARE(inout_port_set, struct inout_port); @@ -91,52 +101,135 @@ register_default_iohandler(int start, int size) } int -emulate_inout(struct vmctx *ctx, int vcpu, int in, int port, int bytes, - uint32_t *eax, int strict) +emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict) { - int flags; - uint32_t mask, val; + int addrsize, bytes, flags, in, port, prot, rep; + uint32_t val; inout_func_t handler; void *arg; - int error; + int error, retval; + enum vm_reg_name idxreg; + uint64_t gla, index, iterations, count; + struct vm_inout_str *vis; + struct iovec iov[2]; + + bytes = vmexit->u.inout.bytes; + in = vmexit->u.inout.in; + port = vmexit->u.inout.port; assert(port < MAX_IOPORTS); + assert(bytes == 1 || bytes == 2 || bytes == 4); handler = inout_handlers[port].handler; if (strict && handler == default_inout) return (-1); - switch (bytes) { - case 1: - mask = 0xff; - break; - case 2: - mask = 0xffff; - break; - default: - mask = 0xffffffff; - break; - } + flags = inout_handlers[port].flags; + arg = inout_handlers[port].arg; - if (!in) { - val = *eax & mask; + if (in) { + if (!(flags & IOPORT_F_IN)) + return (-1); + } else { + if (!(flags & IOPORT_F_OUT)) + return (-1); } - flags = inout_handlers[port].flags; - arg = inout_handlers[port].arg; + retval = 0; + if (vmexit->u.inout.string) { + vis = &vmexit->u.inout_str; + rep = vis->inout.rep; + addrsize = vis->addrsize; + prot = in ? PROT_WRITE : PROT_READ; + assert(addrsize == 2 || addrsize == 4 || addrsize == 8); + + /* Index register */ + idxreg = in ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI; + index = vis->index & vie_size2mask(addrsize); + + /* Count register */ + count = vis->count & vie_size2mask(addrsize); + + /* Limit number of back-to-back in/out emulations to 16 */ + iterations = MIN(count, 16); + while (iterations > 0) { + if (vie_calculate_gla(vis->paging.cpu_mode, + vis->seg_name, &vis->seg_desc, index, bytes, + addrsize, prot, &gla)) { + error = vm_inject_exception2(ctx, vcpu, + IDT_GP, 0); + assert(error == 0); + retval = INOUT_RESTART; + break; + } + + error = vm_gla2gpa(ctx, vcpu, &vis->paging, gla, bytes, + prot, iov, nitems(iov)); + assert(error == 0 || error == 1 || error == -1); + if (error) { + retval = (error == 1) ? INOUT_RESTART : + INOUT_ERROR; + break; + } + + if (vie_alignment_check(vis->paging.cpl, bytes, + vis->cr0, vis->rflags, gla)) { + error = vm_inject_exception2(ctx, vcpu, + IDT_AC, 0); + assert(error == 0); + return (INOUT_RESTART); + } + + val = 0; + if (!in) + vm_copyin(ctx, vcpu, iov, &val, bytes); + + retval = handler(ctx, vcpu, in, port, bytes, &val, arg); + if (retval != 0) + break; + + if (in) + vm_copyout(ctx, vcpu, &val, iov, bytes); + + /* Update index */ + if (vis->rflags & PSL_D) + index -= bytes; + else + index += bytes; + + count--; + iterations--; + } - if ((in && (flags & IOPORT_F_IN)) || (!in && (flags & IOPORT_F_OUT))) - error = (*handler)(ctx, vcpu, in, port, bytes, &val, arg); - else - error = -1; + /* Update index register */ + error = vie_update_register(ctx, vcpu, idxreg, index, addrsize); + assert(error == 0); + + /* + * Update count register only if the instruction had a repeat + * prefix. + */ + if (rep) { + error = vie_update_register(ctx, vcpu, VM_REG_GUEST_RCX, + count, addrsize); + assert(error == 0); + } - if (!error && in) { - *eax &= ~mask; - *eax |= val & mask; + /* Restart the instruction if more iterations remain */ + if (retval == INOUT_OK && count != 0) + retval = INOUT_RESTART; + } else { + if (!in) { + val = vmexit->u.inout.eax & vie_size2mask(bytes); + } + retval = handler(ctx, vcpu, in, port, bytes, &val, arg); + if (retval == 0 && in) { + vmexit->u.inout.eax &= ~vie_size2mask(bytes); + vmexit->u.inout.eax |= val & vie_size2mask(bytes); + } } - - return (error); + return (retval); } void diff --git a/usr.sbin/bhyve/inout.h b/usr.sbin/bhyve/inout.h index 5117d79..f15a2c8 100644 --- a/usr.sbin/bhyve/inout.h +++ b/usr.sbin/bhyve/inout.h @@ -32,12 +32,14 @@ #include <sys/linker_set.h> struct vmctx; +struct vm_exit; /* Handler return values. */ #define INOUT_ERROR -1 #define INOUT_OK 0 -#define INOUT_RESET 1 -#define INOUT_POWEROFF 2 +#define INOUT_RESTART 1 +#define INOUT_RESET 2 +#define INOUT_POWEROFF 3 typedef int (*inout_func_t)(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg); @@ -72,8 +74,8 @@ struct inout_port { DATA_SET(inout_port_set, __CONCAT(__inout_port, __LINE__)) void init_inout(void); -int emulate_inout(struct vmctx *, int vcpu, int in, int port, int bytes, - uint32_t *eax, int strict); +int emulate_inout(struct vmctx *, int vcpu, struct vm_exit *vmexit, + int strict); int register_inout(struct inout_port *iop); int unregister_inout(struct inout_port *iop); void init_bvmcons(void); diff --git a/usr.sbin/bhyve/mem.c b/usr.sbin/bhyve/mem.c index d5c7935..7ea630f 100644 --- a/usr.sbin/bhyve/mem.c +++ b/usr.sbin/bhyve/mem.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include <sys/tree.h> #include <sys/errno.h> #include <machine/vmm.h> +#include <machine/vmm_instruction_emul.h> #include <stdio.h> #include <stdlib.h> diff --git a/usr.sbin/bhyve/pci_virtio_block.c b/usr.sbin/bhyve/pci_virtio_block.c index 3474fd7..cf1c655 100644 --- a/usr.sbin/bhyve/pci_virtio_block.c +++ b/usr.sbin/bhyve/pci_virtio_block.c @@ -52,10 +52,6 @@ __FBSDID("$FreeBSD$"); #include "pci_emul.h" #include "virtio.h" -#ifndef min -#define min(a, b) ((a) < (b) ? (a) : (b)) -#endif - #define VTBLK_RINGSZ 64 #define VTBLK_MAXSEGS 32 @@ -217,7 +213,7 @@ pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq) case VBH_OP_IDENT: /* Assume a single buffer */ strlcpy(iov[1].iov_base, sc->vbsc_ident, - min(iov[1].iov_len, sizeof(sc->vbsc_ident))); + MIN(iov[1].iov_len, sizeof(sc->vbsc_ident))); err = 0; break; default: |