summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bhyve/inout.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bhyve/inout.c')
-rw-r--r--usr.sbin/bhyve/inout.c151
1 files changed, 122 insertions, 29 deletions
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
OpenPOWER on IntegriCloud