summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2015-01-13 22:00:47 +0000
committerneel <neel@FreeBSD.org>2015-01-13 22:00:47 +0000
commit5c965bc58340b2ad1674080fadd1f3023b668584 (patch)
tree34bac79a7201ec2c856b1d8e89128080e1e56a1a
parentdf2eab9144b347f4ccb70222cebe87ba099e2349 (diff)
downloadFreeBSD-src-5c965bc58340b2ad1674080fadd1f3023b668584.zip
FreeBSD-src-5c965bc58340b2ad1674080fadd1f3023b668584.tar.gz
'struct vm_exception' was intended to be used only as the collateral for the
VM_INJECT_EXCEPTION ioctl. However it morphed into other uses like keeping track pending exceptions for a vcpu. This in turn causes confusion because some fields in 'struct vm_exception' like 'vcpuid' make sense only in the ioctl context. It also makes it harder to add or remove structure fields. Fix this by using 'struct vm_exception' only to communicate information from userspace to vmm.ko when injecting an exception. Also, add a field 'restart_instruction' to 'struct vm_exception'. This field is set to '1' for exceptions where the faulting instruction is restarted after the exception is handled. MFC after: 1 week
-rw-r--r--sys/amd64/include/vmm.h7
-rw-r--r--sys/amd64/include/vmm_dev.h1
-rw-r--r--sys/amd64/vmm/amd/svm.c15
-rw-r--r--sys/amd64/vmm/intel/vmx.c18
-rw-r--r--sys/amd64/vmm/vmm.c68
-rw-r--r--sys/amd64/vmm/vmm_dev.c4
6 files changed, 60 insertions, 53 deletions
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h
index eef5efa..253654d 100644
--- a/sys/amd64/include/vmm.h
+++ b/sys/amd64/include/vmm.h
@@ -289,7 +289,7 @@ struct vpmtmr *vm_pmtmr(struct vm *vm);
struct vrtc *vm_rtc(struct vm *vm);
/*
- * Inject exception 'vme' into the guest vcpu. This function returns 0 on
+ * Inject exception 'vector' into the guest vcpu. This function returns 0 on
* success and non-zero on failure.
*
* Wrapper functions like 'vm_inject_gp()' should be preferred to calling
@@ -299,7 +299,8 @@ struct vrtc *vm_rtc(struct vm *vm);
* This function should only be called in the context of the thread that is
* executing this vcpu.
*/
-int vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *vme);
+int vm_inject_exception(struct vm *vm, int vcpuid, int vector, int err_valid,
+ uint32_t errcode, int restart_instruction);
/*
* This function is called after a VM-exit that occurred during exception or
@@ -628,4 +629,6 @@ vm_inject_ss(void *vm, int vcpuid, int errcode)
void vm_inject_pf(void *vm, int vcpuid, int error_code, uint64_t cr2);
+int vm_restart_instruction(void *vm, int vcpuid);
+
#endif /* _VMM_H_ */
diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h
index 3097386..f3354e3 100644
--- a/sys/amd64/include/vmm_dev.h
+++ b/sys/amd64/include/vmm_dev.h
@@ -63,6 +63,7 @@ struct vm_exception {
int vector;
uint32_t error_code;
int error_code_valid;
+ int restart_instruction;
};
struct vm_lapic_msi {
diff --git a/sys/amd64/vmm/amd/svm.c b/sys/amd64/vmm/amd/svm.c
index 3a96e56..31164d7 100644
--- a/sys/amd64/vmm/amd/svm.c
+++ b/sys/amd64/vmm/amd/svm.c
@@ -1201,7 +1201,6 @@ svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit)
struct vmcb_state *state;
struct vmcb_ctrl *ctrl;
struct svm_regctx *ctx;
- struct vm_exception exception;
uint64_t code, info1, info2, val;
uint32_t eax, ecx, edx;
int error, errcode_valid, handled, idtvec, reflect;
@@ -1315,6 +1314,7 @@ svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit)
/* fallthru */
default:
errcode_valid = 0;
+ info1 = 0;
break;
}
KASSERT(vmexit->inst_length == 0, ("invalid inst_length (%d) "
@@ -1323,17 +1323,10 @@ svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit)
if (reflect) {
/* Reflect the exception back into the guest */
- bzero(&exception, sizeof(struct vm_exception));
- exception.vector = idtvec;
- if (errcode_valid) {
- exception.error_code = info1;
- exception.error_code_valid = 1;
- }
VCPU_CTR2(svm_sc->vm, vcpu, "Reflecting exception "
- "%d/%#x into the guest", exception.vector,
- exception.error_code);
- error = vm_inject_exception(svm_sc->vm, vcpu,
- &exception);
+ "%d/%#x into the guest", idtvec, (int)info1);
+ error = vm_inject_exception(svm_sc->vm, vcpu, idtvec,
+ errcode_valid, info1, 0);
KASSERT(error == 0, ("%s: vm_inject_exception error %d",
__func__, error));
}
diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c
index a3fc16a..a10a591 100644
--- a/sys/amd64/vmm/intel/vmx.c
+++ b/sys/amd64/vmm/intel/vmx.c
@@ -1784,7 +1784,7 @@ vmexit_inst_emul(struct vm_exit *vmexit, uint64_t gpa, uint64_t gla)
{
struct vm_guest_paging *paging;
uint32_t csar;
-
+
paging = &vmexit->u.inst_emul.paging;
vmexit->exitcode = VM_EXITCODE_INST_EMUL;
@@ -2073,12 +2073,11 @@ emulate_rdmsr(struct vmx *vmx, int vcpuid, u_int num, bool *retu)
static int
vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
{
- int error, handled, in;
+ int error, errcode, errcode_valid, handled, in;
struct vmxctx *vmxctx;
struct vlapic *vlapic;
struct vm_inout_str *vis;
struct vm_task_switch *ts;
- struct vm_exception vmexc;
uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, inst_info;
uint32_t intr_type, intr_vec, reason;
uint64_t exitintinfo, qual, gpa;
@@ -2263,6 +2262,7 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
case EXIT_REASON_MTF:
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_MTRAP, 1);
vmexit->exitcode = VM_EXITCODE_MTRAP;
+ vmexit->inst_length = 0;
break;
case EXIT_REASON_PAUSE:
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_PAUSE, 1);
@@ -2389,15 +2389,15 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
vmcs_write(VMCS_ENTRY_INST_LENGTH, vmexit->inst_length);
/* Reflect all other exceptions back into the guest */
- bzero(&vmexc, sizeof(struct vm_exception));
- vmexc.vector = intr_vec;
+ errcode_valid = errcode = 0;
if (intr_info & VMCS_INTR_DEL_ERRCODE) {
- vmexc.error_code_valid = 1;
- vmexc.error_code = vmcs_read(VMCS_EXIT_INTR_ERRCODE);
+ errcode_valid = 1;
+ errcode = vmcs_read(VMCS_EXIT_INTR_ERRCODE);
}
VCPU_CTR2(vmx->vm, vcpu, "Reflecting exception %d/%#x into "
- "the guest", vmexc.vector, vmexc.error_code);
- error = vm_inject_exception(vmx->vm, vcpu, &vmexc);
+ "the guest", intr_vec, errcode);
+ error = vm_inject_exception(vmx->vm, vcpu, intr_vec,
+ errcode_valid, errcode, 0);
KASSERT(error == 0, ("%s: vm_inject_exception error %d",
__func__, error));
return (1);
diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c
index 6fd3d46..ff32b33 100644
--- a/sys/amd64/vmm/vmm.c
+++ b/sys/amd64/vmm/vmm.c
@@ -101,8 +101,10 @@ struct vcpu {
uint64_t exitintinfo; /* (i) events pending at VM exit */
int nmi_pending; /* (i) NMI pending */
int extint_pending; /* (i) INTR pending */
- struct vm_exception exception; /* (x) exception collateral */
int exception_pending; /* (i) exception pending */
+ int exc_vector; /* (x) exception collateral */
+ int exc_errcode_valid;
+ uint32_t exc_errcode;
struct savefpu *guestfpu; /* (a,i) guest fpu state */
uint64_t guest_xcr0; /* (i) guest %xcr0 register */
void *stats; /* (a,i) statistics */
@@ -1223,7 +1225,7 @@ vm_handle_paging(struct vm *vm, int vcpuid, bool *retu)
return (EFAULT);
done:
/* restart execution at the faulting instruction */
- vme->inst_length = 0;
+ vm_restart_instruction(vm, vcpuid);
return (0);
}
@@ -1526,6 +1528,20 @@ restart:
}
int
+vm_restart_instruction(void *arg, int vcpuid)
+{
+ struct vcpu *vcpu;
+ struct vm *vm = arg;
+
+ if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
+ return (EINVAL);
+
+ vcpu = &vm->vcpu[vcpuid];
+ vcpu->exitinfo.inst_length = 0;
+ return (0);
+}
+
+int
vm_exit_intinfo(struct vm *vm, int vcpuid, uint64_t info)
{
struct vcpu *vcpu;
@@ -1655,11 +1671,11 @@ vcpu_exception_intinfo(struct vcpu *vcpu)
uint64_t info = 0;
if (vcpu->exception_pending) {
- info = vcpu->exception.vector & 0xff;
+ info = vcpu->exc_vector & 0xff;
info |= VM_INTINFO_VALID | VM_INTINFO_HWEXCEPTION;
- if (vcpu->exception.error_code_valid) {
+ if (vcpu->exc_errcode_valid) {
info |= VM_INTINFO_DEL_ERRCODE;
- info |= (uint64_t)vcpu->exception.error_code << 32;
+ info |= (uint64_t)vcpu->exc_errcode << 32;
}
}
return (info);
@@ -1684,7 +1700,7 @@ vm_entry_intinfo(struct vm *vm, int vcpuid, uint64_t *retinfo)
info2 = vcpu_exception_intinfo(vcpu);
vcpu->exception_pending = 0;
VCPU_CTR2(vm, vcpuid, "Exception %d delivered: %#lx",
- vcpu->exception.vector, info2);
+ vcpu->exc_vector, info2);
}
if ((info1 & VM_INTINFO_VALID) && (info2 & VM_INTINFO_VALID)) {
@@ -1722,7 +1738,8 @@ vm_get_intinfo(struct vm *vm, int vcpuid, uint64_t *info1, uint64_t *info2)
}
int
-vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
+vm_inject_exception(struct vm *vm, int vcpuid, int vector, int errcode_valid,
+ uint32_t errcode, int restart_instruction)
{
struct vcpu *vcpu;
int error;
@@ -1730,7 +1747,7 @@ vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
return (EINVAL);
- if (exception->vector < 0 || exception->vector >= 32)
+ if (vector < 0 || vector >= 32)
return (EINVAL);
/*
@@ -1738,15 +1755,14 @@ vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
* the guest. It is a derived exception that results from specific
* combinations of nested faults.
*/
- if (exception->vector == IDT_DF)
+ if (vector == IDT_DF)
return (EINVAL);
vcpu = &vm->vcpu[vcpuid];
if (vcpu->exception_pending) {
VCPU_CTR2(vm, vcpuid, "Unable to inject exception %d due to "
- "pending exception %d", exception->vector,
- vcpu->exception.vector);
+ "pending exception %d", vector, vcpu->exc_vector);
return (EBUSY);
}
@@ -1760,9 +1776,14 @@ vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
KASSERT(error == 0, ("%s: error %d clearing interrupt shadow",
__func__, error));
+ if (restart_instruction)
+ vm_restart_instruction(vm, vcpuid);
+
vcpu->exception_pending = 1;
- vcpu->exception = *exception;
- VCPU_CTR1(vm, vcpuid, "Exception %d pending", exception->vector);
+ vcpu->exc_vector = vector;
+ vcpu->exc_errcode = errcode;
+ vcpu->exc_errcode_valid = errcode_valid;
+ VCPU_CTR1(vm, vcpuid, "Exception %d pending", vector);
return (0);
}
@@ -1770,28 +1791,15 @@ void
vm_inject_fault(void *vmarg, int vcpuid, int vector, int errcode_valid,
int errcode)
{
- struct vm_exception exception;
- struct vm_exit *vmexit;
struct vm *vm;
- int error;
+ int error, restart_instruction;
vm = vmarg;
+ restart_instruction = 1;
- exception.vector = vector;
- exception.error_code = errcode;
- exception.error_code_valid = errcode_valid;
- error = vm_inject_exception(vm, vcpuid, &exception);
+ error = vm_inject_exception(vm, vcpuid, vector, errcode_valid,
+ errcode, restart_instruction);
KASSERT(error == 0, ("vm_inject_exception error %d", error));
-
- /*
- * A fault-like exception allows the instruction to be restarted
- * after the exception handler returns.
- *
- * By setting the inst_length to 0 we ensure that the instruction
- * pointer remains at the faulting instruction.
- */
- vmexit = vm_exitinfo(vm, vcpuid);
- vmexit->inst_length = 0;
}
void
diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c
index a6491d6..9b39f14 100644
--- a/sys/amd64/vmm/vmm_dev.c
+++ b/sys/amd64/vmm/vmm_dev.c
@@ -310,7 +310,9 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
break;
case VM_INJECT_EXCEPTION:
vmexc = (struct vm_exception *)data;
- error = vm_inject_exception(sc->vm, vmexc->cpuid, vmexc);
+ error = vm_inject_exception(sc->vm, vmexc->cpuid,
+ vmexc->vector, vmexc->error_code_valid, vmexc->error_code,
+ vmexc->restart_instruction);
break;
case VM_INJECT_NMI:
vmnmi = (struct vm_nmi *)data;
OpenPOWER on IntegriCloud