'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
This commit is contained in:
neel 2015-01-13 22:00:47 +00:00
parent df2eab9144
commit 5c965bc583
6 changed files with 60 additions and 53 deletions

View File

@ -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_ */

View File

@ -63,6 +63,7 @@ struct vm_exception {
int vector;
uint32_t error_code;
int error_code_valid;
int restart_instruction;
};
struct vm_lapic_msi {

View File

@ -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));
}

View File

@ -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);

View File

@ -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);
}
@ -1525,6 +1527,20 @@ vm_run(struct vm *vm, struct vm_run *vmrun)
return (error);
}
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)
{
@ -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

View File

@ -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;