summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bhyve
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2014-07-22 04:39:16 +0000
committerjhb <jhb@FreeBSD.org>2014-07-22 04:39:16 +0000
commitce450da4301f9fcfa34afaa8d4e1b69a4877a2f7 (patch)
treee884913964691296030a1dcf7a5ec58905d567c2 /usr.sbin/bhyve
parentc1fe945ebd0d209b238eb98b47c40c115576f2e4 (diff)
downloadFreeBSD-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/bhyve')
-rw-r--r--usr.sbin/bhyve/bhyverun.c26
-rw-r--r--usr.sbin/bhyve/inout.c151
-rw-r--r--usr.sbin/bhyve/inout.h10
-rw-r--r--usr.sbin/bhyve/mem.c1
-rw-r--r--usr.sbin/bhyve/pci_virtio_block.c6
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:
OpenPOWER on IntegriCloud