summaryrefslogtreecommitdiff
path: root/usr/src/cmd/bhyve/bhyverun.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/bhyve/bhyverun.c')
-rw-r--r--usr/src/cmd/bhyve/bhyverun.c202
1 files changed, 165 insertions, 37 deletions
diff --git a/usr/src/cmd/bhyve/bhyverun.c b/usr/src/cmd/bhyve/bhyverun.c
index 18bfda76f0..bb3e0721c8 100644
--- a/usr/src/cmd/bhyve/bhyverun.c
+++ b/usr/src/cmd/bhyve/bhyverun.c
@@ -217,6 +217,7 @@ static cpuset_t cpumask;
static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip);
static struct vm_exit vmexit[VM_MAXCPU];
+static struct vm_entry vmentry[VM_MAXCPU];
struct bhyvestats {
uint64_t vmexit_bogus;
@@ -224,15 +225,18 @@ struct bhyvestats {
uint64_t vmexit_hlt;
uint64_t vmexit_pause;
uint64_t vmexit_mtrap;
- uint64_t vmexit_inst_emul;
+ uint64_t vmexit_mmio;
+ uint64_t vmexit_inout;
uint64_t cpu_switch_rotate;
uint64_t cpu_switch_direct;
+ uint64_t mmio_unhandled;
} stats;
struct mt_vmm_info {
pthread_t mt_thr;
struct vmctx *mt_ctx;
- int mt_vcpu;
+ int mt_vcpu;
+ uint64_t mt_startrip;
} mt_vmm_info[VM_MAXCPU];
#ifdef __FreeBSD__
@@ -502,7 +506,7 @@ fbsdrun_start_thread(void *param)
if (gdb_port != 0)
gdb_cpu_add(vcpu);
- vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip);
+ vm_loop(mtp->mt_ctx, vcpu, mtp->mt_startrip);
/* not reached */
exit(1);
@@ -543,11 +547,9 @@ fbsdrun_addcpu(struct vmctx *ctx, int fromcpu, int newcpu, uint64_t rip,
* Set up the vmexit struct to allow execution to start
* at the given RIP
*/
- vmexit[newcpu].rip = rip;
- vmexit[newcpu].inst_length = 0;
-
mt_vmm_info[newcpu].mt_ctx = ctx;
mt_vmm_info[newcpu].mt_vcpu = newcpu;
+ mt_vmm_info[newcpu].mt_startrip = rip;
error = pthread_create(&mt_vmm_info[newcpu].mt_thr, NULL,
fbsdrun_start_thread, &mt_vmm_info[newcpu]);
@@ -567,6 +569,66 @@ fbsdrun_deletecpu(struct vmctx *ctx, int vcpu)
return (CPU_EMPTY(&cpumask));
}
+static void
+vmentry_mmio_read(int vcpu, uint64_t gpa, uint8_t bytes, uint64_t data)
+{
+ struct vm_entry *entry = &vmentry[vcpu];
+ struct vm_mmio *mmio = &entry->u.mmio;
+
+ assert(entry->cmd == VEC_DEFAULT);
+
+ entry->cmd = VEC_COMPLETE_MMIO;
+ mmio->bytes = bytes;
+ mmio->read = 1;
+ mmio->gpa = gpa;
+ mmio->data = data;
+}
+
+static void
+vmentry_mmio_write(int vcpu, uint64_t gpa, uint8_t bytes)
+{
+ struct vm_entry *entry = &vmentry[vcpu];
+ struct vm_mmio *mmio = &entry->u.mmio;
+
+ assert(entry->cmd == VEC_DEFAULT);
+
+ entry->cmd = VEC_COMPLETE_MMIO;
+ mmio->bytes = bytes;
+ mmio->read = 0;
+ mmio->gpa = gpa;
+ mmio->data = 0;
+}
+
+static void
+vmentry_inout_read(int vcpu, uint16_t port, uint8_t bytes, uint32_t data)
+{
+ struct vm_entry *entry = &vmentry[vcpu];
+ struct vm_inout *inout = &entry->u.inout;
+
+ assert(entry->cmd == VEC_DEFAULT);
+
+ entry->cmd = VEC_COMPLETE_INOUT;
+ inout->bytes = bytes;
+ inout->flags = INOUT_IN;
+ inout->port = port;
+ inout->eax = data;
+}
+
+static void
+vmentry_inout_write(int vcpu, uint16_t port, uint8_t bytes)
+{
+ struct vm_entry *entry = &vmentry[vcpu];
+ struct vm_inout *inout = &entry->u.inout;
+
+ assert(entry->cmd == VEC_DEFAULT);
+
+ entry->cmd = VEC_COMPLETE_INOUT;
+ inout->bytes = bytes;
+ inout->flags = 0;
+ inout->port = port;
+ inout->eax = 0;
+}
+
static int
vmexit_handle_notify(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu,
uint32_t eax)
@@ -583,30 +645,42 @@ static int
vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
{
int error;
- int bytes, port, in, out;
int vcpu;
+ struct vm_inout inout;
+ bool in;
+ uint8_t bytes;
- vcpu = *pvcpu;
+ stats.vmexit_inout++;
- port = vme->u.inout.port;
- bytes = vme->u.inout.bytes;
- in = vme->u.inout.in;
- out = !in;
+ vcpu = *pvcpu;
+ inout = vme->u.inout;
+ in = (inout.flags & INOUT_IN) != 0;
+ bytes = inout.bytes;
/* Extra-special case of host notifications */
- if (out && port == GUEST_NIO_PORT) {
- error = vmexit_handle_notify(ctx, vme, pvcpu, vme->u.inout.eax);
+ if (!in && inout.port == GUEST_NIO_PORT) {
+ error = vmexit_handle_notify(ctx, vme, pvcpu, inout.eax);
+ vmentry_inout_write(vcpu, inout.port, bytes);
return (error);
}
- error = emulate_inout(ctx, vcpu, vme, strictio);
+ error = emulate_inout(ctx, vcpu, &inout, strictio != 0);
if (error) {
fprintf(stderr, "Unhandled %s%c 0x%04x at 0x%lx\n",
in ? "in" : "out",
bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'),
- port, vmexit->rip);
+ inout.port, vmexit->rip);
return (VMEXIT_ABORT);
} else {
+ /*
+ * Communicate the status of the inout operation back to the
+ * in-kernel instruction emulation.
+ */
+ if (in) {
+ vmentry_inout_read(vcpu, inout.port, bytes, inout.eax);
+ } else {
+ vmentry_inout_write(vcpu, inout.port, bytes);
+ }
return (VMEXIT_CONTINUE);
}
}
@@ -796,29 +870,70 @@ vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
static int
vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
- int err, i;
- struct vie *vie;
+ uint8_t i, valid;
+
+ fprintf(stderr, "Failed to emulate instruction sequence ");
+
+ valid = vmexit->u.inst_emul.num_valid;
+ if (valid != 0) {
+ assert(valid <= sizeof (vmexit->u.inst_emul.inst));
+ fprintf(stderr, "[");
+ for (i = 0; i < valid; i++) {
+ if (i == 0) {
+ fprintf(stderr, "%02x",
+ vmexit->u.inst_emul.inst[i]);
+ } else {
+ fprintf(stderr, ", %02x",
+ vmexit->u.inst_emul.inst[i]);
+ }
+ }
+ fprintf(stderr, "] ");
+ }
+ fprintf(stderr, "@ %rip = %x\n", vmexit->rip);
- stats.vmexit_inst_emul++;
+ return (VMEXIT_ABORT);
+}
- vie = &vmexit->u.inst_emul.vie;
- err = emulate_mem(ctx, *pvcpu, vmexit->u.inst_emul.gpa,
- vie, &vmexit->u.inst_emul.paging);
+static int
+vmexit_mmio(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
+{
+ int vcpu, err;
+ struct vm_mmio mmio;
+ bool is_read;
- if (err) {
- if (err == ESRCH) {
- EPRINTLN("Unhandled memory access to 0x%lx\n",
- vmexit->u.inst_emul.gpa);
- }
+ stats.vmexit_mmio++;
- fprintf(stderr, "Failed to emulate instruction sequence [ ");
- for (i = 0; i < vie->num_valid; i++)
- fprintf(stderr, "%02x", vie->inst[i]);
- FPRINTLN(stderr, " ] at 0x%lx", vmexit->rip);
- return (VMEXIT_ABORT);
+ vcpu = *pvcpu;
+ mmio = vmexit->u.mmio;
+ is_read = (mmio.read != 0);
+
+ err = emulate_mem(ctx, vcpu, &mmio);
+
+ if (err == ESRCH) {
+ fprintf(stderr, "Unhandled memory access to 0x%lx\n", mmio.gpa);
+ stats.mmio_unhandled++;
+
+ /*
+ * Access to non-existent physical addresses is not likely to
+ * result in fatal errors on hardware machines, but rather reads
+ * of all-ones or discarded-but-acknowledged writes.
+ */
+ mmio.data = ~0UL;
+ err = 0;
}
- return (VMEXIT_CONTINUE);
+ if (err == 0) {
+ if (is_read) {
+ vmentry_mmio_read(vcpu, mmio.gpa, mmio.bytes,
+ mmio.data);
+ } else {
+ vmentry_mmio_write(vcpu, mmio.gpa, mmio.bytes);
+ }
+ return (VMEXIT_CONTINUE);
+ }
+
+ fprintf(stderr, "Unhandled mmio error to 0x%lx: %d\n", mmio.gpa, err);
+ return (VMEXIT_ABORT);
}
static pthread_mutex_t resetcpu_mtx = PTHREAD_MUTEX_INITIALIZER;
@@ -888,7 +1003,7 @@ vmexit_breakpoint(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_MMIO] = vmexit_mmio,
[VM_EXITCODE_VMX] = vmexit_vmx,
[VM_EXITCODE_SVM] = vmexit_svm,
[VM_EXITCODE_BOGUS] = vmexit_bogus,
@@ -910,6 +1025,8 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t startrip)
int error, rc;
enum vm_exitcode exitcode;
cpuset_t active_cpus;
+ struct vm_exit *vexit;
+ struct vm_entry *ventry;
#ifdef __FreeBSD__
if (vcpumap[vcpu] != NULL) {
@@ -924,19 +1041,30 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t startrip)
error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RIP, startrip);
assert(error == 0);
+ ventry = &vmentry[vcpu];
+ vexit = &vmexit[vcpu];
+
while (1) {
- error = vm_run(ctx, vcpu, &vmexit[vcpu]);
+ error = vm_run(ctx, vcpu, ventry, vexit);
if (error != 0)
break;
- exitcode = vmexit[vcpu].exitcode;
+ if (ventry->cmd != VEC_DEFAULT) {
+ /*
+ * Discard any lingering entry state after it has been
+ * submitted via vm_run().
+ */
+ bzero(ventry, sizeof (*ventry));
+ }
+
+ exitcode = vexit->exitcode;
if (exitcode >= VM_EXITCODE_MAX || handler[exitcode] == NULL) {
fprintf(stderr, "vm_loop: unexpected exitcode 0x%x\n",
exitcode);
exit(4);
}
- rc = (*handler[exitcode])(ctx, &vmexit[vcpu], &vcpu);
+ rc = (*handler[exitcode])(ctx, vexit, &vcpu);
switch (rc) {
case VMEXIT_CONTINUE: