summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2015-01-18 03:08:30 +0000
committerneel <neel@FreeBSD.org>2015-01-18 03:08:30 +0000
commitd9f07f98412532f2077e8a8503651536223931ca (patch)
tree059a656c08c5c81e9f08be1740cebcdf26e3df13 /usr.sbin
parent359b23588446cd5af725d5a06bf6058c4ffa57b9 (diff)
downloadFreeBSD-src-d9f07f98412532f2077e8a8503651536223931ca.zip
FreeBSD-src-d9f07f98412532f2077e8a8503651536223931ca.tar.gz
Simplify instruction restart logic in bhyve.
Keep track of the next instruction to be executed by the vcpu as 'nextrip'. As a result the VM_RUN ioctl no longer takes the %rip where a vcpu should start execution. Also, instruction restart happens implicitly via 'vm_inject_exception()' or explicitly via 'vm_restart_instruction()'. The APIs behave identically in both kernel and userspace contexts. The main beneficiary is the instruction emulation code that executes in both contexts. bhyve(8) VM exit handlers now treat 'vmexit->rip' and 'vmexit->inst_length' as readonly: - Restarting an instruction is now done by calling 'vm_restart_instruction()' as opposed to setting 'vmexit->inst_length' to 0 (e.g. emulate_inout()) - Resuming vcpu at an arbitrary %rip is now done by setting VM_REG_GUEST_RIP as opposed to changing 'vmexit->rip' (e.g. vmexit_task_switch()) Differential Revision: https://reviews.freebsd.org/D1526 Reviewed by: grehan MFC after: 2 weeks
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/bhyve/bhyverun.c45
-rw-r--r--usr.sbin/bhyve/bhyverun.h5
-rw-r--r--usr.sbin/bhyve/inout.c20
-rw-r--r--usr.sbin/bhyve/task_switch.c16
-rw-r--r--usr.sbin/bhyvectl/bhyvectl.c5
5 files changed, 36 insertions, 55 deletions
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index 5971993..347e436 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -185,20 +185,14 @@ vm_inject_fault(void *arg, int vcpu, int vector, int errcode_valid,
int errcode)
{
struct vmctx *ctx;
- int error;
+ int error, restart_instruction;
ctx = arg;
- if (errcode_valid)
- error = vm_inject_exception2(ctx, vcpu, vector, errcode);
- else
- error = vm_inject_exception(ctx, vcpu, vector);
- assert(error == 0);
+ restart_instruction = 1;
- /*
- * Set the instruction length to 0 to ensure that the instruction is
- * restarted when the fault handler returns.
- */
- vmexit[vcpu].inst_length = 0;
+ error = vm_inject_exception(ctx, vcpu, vector, errcode_valid, errcode,
+ restart_instruction);
+ assert(error == 0);
}
void *
@@ -329,12 +323,6 @@ vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
}
error = emulate_inout(ctx, vcpu, vme, strictio);
- if (!error && in && !string) {
- error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX,
- vme->u.inout.eax);
- assert(error == 0);
- }
-
if (error) {
fprintf(stderr, "Unhandled %s%c 0x%04x\n", in ? "in" : "out",
bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'), port);
@@ -358,7 +346,7 @@ vmexit_rdmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
vme->u.msr.code, *pvcpu);
if (strictmsr) {
vm_inject_gp(ctx, *pvcpu);
- return (VMEXIT_RESTART);
+ return (VMEXIT_CONTINUE);
}
}
@@ -384,7 +372,7 @@ vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
vme->u.msr.code, vme->u.msr.wval, *pvcpu);
if (strictmsr) {
vm_inject_gp(ctx, *pvcpu);
- return (VMEXIT_RESTART);
+ return (VMEXIT_CONTINUE);
}
}
return (VMEXIT_CONTINUE);
@@ -462,9 +450,11 @@ static int
vmexit_bogus(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
+ assert(vmexit->inst_length == 0);
+
stats.vmexit_bogus++;
- return (VMEXIT_RESTART);
+ return (VMEXIT_CONTINUE);
}
static int
@@ -494,9 +484,11 @@ static int
vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
+ assert(vmexit->inst_length == 0);
+
stats.vmexit_mtrap++;
- return (VMEXIT_RESTART);
+ return (VMEXIT_CONTINUE);
}
static int
@@ -581,7 +573,7 @@ static vmexit_handler_t handler[VM_EXITCODE_MAX] = {
};
static void
-vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip)
+vm_loop(struct vmctx *ctx, int vcpu, uint64_t startrip)
{
int error, rc, prevcpu;
enum vm_exitcode exitcode;
@@ -596,8 +588,11 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip)
error = vm_active_cpus(ctx, &active_cpus);
assert(CPU_ISSET(vcpu, &active_cpus));
+ error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RIP, startrip);
+ assert(error == 0);
+
while (1) {
- error = vm_run(ctx, vcpu, rip, &vmexit[vcpu]);
+ error = vm_run(ctx, vcpu, &vmexit[vcpu]);
if (error != 0)
break;
@@ -614,10 +609,6 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip)
switch (rc) {
case VMEXIT_CONTINUE:
- rip = vmexit[vcpu].rip + vmexit[vcpu].inst_length;
- break;
- case VMEXIT_RESTART:
- rip = vmexit[vcpu].rip;
break;
case VMEXIT_ABORT:
abort();
diff --git a/usr.sbin/bhyve/bhyverun.h b/usr.sbin/bhyve/bhyverun.h
index 87824ef..c51bf48 100644
--- a/usr.sbin/bhyve/bhyverun.h
+++ b/usr.sbin/bhyve/bhyverun.h
@@ -35,9 +35,8 @@
#define __CTASSERT(x, y) typedef char __assert ## y[(x) ? 1 : -1]
#endif
-#define VMEXIT_CONTINUE 1 /* continue from next instruction */
-#define VMEXIT_RESTART 2 /* restart current instruction */
-#define VMEXIT_ABORT 3 /* abort the vm run loop */
+#define VMEXIT_CONTINUE (0)
+#define VMEXIT_ABORT (-1)
struct vmctx;
extern int guest_ncpus;
diff --git a/usr.sbin/bhyve/inout.c b/usr.sbin/bhyve/inout.c
index 1041a59..402b953 100644
--- a/usr.sbin/bhyve/inout.c
+++ b/usr.sbin/bhyve/inout.c
@@ -104,7 +104,7 @@ int
emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
{
int addrsize, bytes, flags, in, port, prot, rep;
- uint32_t val;
+ uint32_t eax, val;
inout_func_t handler;
void *arg;
int error, retval;
@@ -214,16 +214,20 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
}
/* Restart the instruction if more iterations remain */
- if (retval == 0 && count != 0)
- vmexit->inst_length = 0;
- } else {
- if (!in) {
- val = vmexit->u.inout.eax & vie_size2mask(bytes);
+ if (retval == 0 && count != 0) {
+ error = vm_restart_instruction(ctx, vcpu);
+ assert(error == 0);
}
+ } else {
+ eax = vmexit->u.inout.eax;
+ val = 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);
+ eax &= ~vie_size2mask(bytes);
+ eax |= val & vie_size2mask(bytes);
+ error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX,
+ eax);
+ assert(error == 0);
}
}
return (retval);
diff --git a/usr.sbin/bhyve/task_switch.c b/usr.sbin/bhyve/task_switch.c
index b939c1a..ba6a9d2 100644
--- a/usr.sbin/bhyve/task_switch.c
+++ b/usr.sbin/bhyve/task_switch.c
@@ -725,21 +725,11 @@ vmexit_task_switch(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
assert(paging->cpu_mode == CPU_MODE_PROTECTED);
/*
- * Calculate the %eip to store in the old TSS before modifying the
- * 'inst_length'.
+ * Calculate the instruction pointer to store in the old TSS.
*/
eip = vmexit->rip + vmexit->inst_length;
/*
- * Set the 'inst_length' to '0'.
- *
- * If an exception is triggered during emulation of the task switch
- * then the exception handler should return to the instruction that
- * caused the task switch as opposed to the subsequent instruction.
- */
- vmexit->inst_length = 0;
-
- /*
* Section 4.6, "Access Rights" in Intel SDM Vol 3.
* The following page table accesses are implicitly supervisor mode:
* - accesses to GDT or LDT to load segment descriptors
@@ -883,8 +873,8 @@ vmexit_task_switch(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
* after this point will be handled in the context of the new task and
* the saved instruction pointer will belong to the new task.
*/
- vmexit->rip = newtss.tss_eip;
- assert(vmexit->inst_length == 0);
+ error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RIP, newtss.tss_eip);
+ assert(error == 0);
/* Load processor state from new TSS */
error = tss32_restore(ctx, vcpu, task_switch, ot_sel, &newtss, nt_iov);
diff --git a/usr.sbin/bhyvectl/bhyvectl.c b/usr.sbin/bhyvectl/bhyvectl.c
index 170ca21..e2b514d 100644
--- a/usr.sbin/bhyvectl/bhyvectl.c
+++ b/usr.sbin/bhyvectl/bhyvectl.c
@@ -2118,10 +2118,7 @@ main(int argc, char *argv[])
}
if (!error && run) {
- error = vm_get_register(ctx, vcpu, VM_REG_GUEST_RIP, &rip);
- assert(error == 0);
-
- error = vm_run(ctx, vcpu, rip, &vmexit);
+ error = vm_run(ctx, vcpu, &vmexit);
if (error == 0)
dump_vm_run_exitcode(&vmexit, vcpu);
else
OpenPOWER on IntegriCloud