Clear blocking due to STI or MOV SS in the hypervisor when an instruction is
emulated or when the vcpu incurs an exception. This matches the CPU behavior. Remove special case code in HLT processing that was clearing the interrupt shadow. This is now redundant because the interrupt shadow is always cleared when the vcpu is resumed after an instruction is emulated. Reported by: David Reed (david.reed@tidalscale.com) MFC after: 2 weeks
This commit is contained in:
parent
78ec874dd3
commit
2ce1242309
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=276763
@ -554,6 +554,7 @@ svm_vminit(struct vm *vm, pmap_t pmap)
|
||||
pml4_pa = svm_sc->nptp;
|
||||
for (i = 0; i < VM_MAXCPU; i++) {
|
||||
vcpu = svm_get_vcpu(svm_sc, i);
|
||||
vcpu->nextrip = ~0;
|
||||
vcpu->lastcpu = NOCPU;
|
||||
vcpu->vmcb_pa = vtophys(&vcpu->vmcb);
|
||||
vmcb_init(svm_sc, i, iopm_pa, msrpm_pa, pml4_pa);
|
||||
@ -1479,15 +1480,24 @@ svm_inj_interrupts(struct svm_softc *sc, int vcpu, struct vlapic *vlapic)
|
||||
{
|
||||
struct vmcb_ctrl *ctrl;
|
||||
struct vmcb_state *state;
|
||||
struct svm_vcpu *vcpustate;
|
||||
uint8_t v_tpr;
|
||||
int vector, need_intr_window, pending_apic_vector;
|
||||
|
||||
state = svm_get_vmcb_state(sc, vcpu);
|
||||
ctrl = svm_get_vmcb_ctrl(sc, vcpu);
|
||||
vcpustate = svm_get_vcpu(sc, vcpu);
|
||||
|
||||
need_intr_window = 0;
|
||||
pending_apic_vector = 0;
|
||||
|
||||
if (vcpustate->nextrip != state->rip) {
|
||||
ctrl->intr_shadow = 0;
|
||||
VCPU_CTR2(sc->vm, vcpu, "Guest interrupt blocking "
|
||||
"cleared due to rip change: %#lx/%#lx",
|
||||
vcpustate->nextrip, state->rip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inject pending events or exceptions for this vcpu.
|
||||
*
|
||||
@ -1958,6 +1968,9 @@ svm_vmrun(void *arg, int vcpu, register_t rip, pmap_t pmap,
|
||||
/* #VMEXIT disables interrupts so re-enable them here. */
|
||||
enable_gintr();
|
||||
|
||||
/* Update 'nextrip' */
|
||||
vcpustate->nextrip = state->rip;
|
||||
|
||||
/* Handle #VMEXIT and if required return to user space. */
|
||||
handled = svm_vmexit(svm_sc, vcpu, vmexit);
|
||||
} while (handled);
|
||||
|
@ -45,6 +45,7 @@ struct svm_vcpu {
|
||||
struct vmcb vmcb; /* hardware saved vcpu context */
|
||||
struct svm_regctx swctx; /* software saved vcpu context */
|
||||
uint64_t vmcb_pa; /* VMCB physical address */
|
||||
uint64_t nextrip; /* next instruction to be executed by guest */
|
||||
int lastcpu; /* host cpu that the vcpu last ran on */
|
||||
uint32_t dirty; /* state cache bits that must be cleared */
|
||||
long eptgen; /* pmap->pm_eptgen when the vcpu last ran */
|
||||
|
@ -941,6 +941,7 @@ vmx_vminit(struct vm *vm, pmap_t pmap)
|
||||
vmx->cap[i].proc_ctls = procbased_ctls;
|
||||
vmx->cap[i].proc_ctls2 = procbased_ctls2;
|
||||
|
||||
vmx->state[i].nextrip = ~0;
|
||||
vmx->state[i].lastcpu = NOCPU;
|
||||
vmx->state[i].vpid = vpid[i];
|
||||
|
||||
@ -1169,12 +1170,24 @@ vmx_inject_nmi(struct vmx *vmx, int vcpu)
|
||||
}
|
||||
|
||||
static void
|
||||
vmx_inject_interrupts(struct vmx *vmx, int vcpu, struct vlapic *vlapic)
|
||||
vmx_inject_interrupts(struct vmx *vmx, int vcpu, struct vlapic *vlapic,
|
||||
uint64_t guestrip)
|
||||
{
|
||||
int vector, need_nmi_exiting, extint_pending;
|
||||
uint64_t rflags, entryinfo;
|
||||
uint32_t gi, info;
|
||||
|
||||
if (vmx->state[vcpu].nextrip != guestrip) {
|
||||
gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY);
|
||||
if (gi & HWINTR_BLOCKING) {
|
||||
VCPU_CTR2(vmx->vm, vcpu, "Guest interrupt blocking "
|
||||
"cleared due to rip change: %#lx/%#lx",
|
||||
vmx->state[vcpu].nextrip, guestrip);
|
||||
gi &= ~HWINTR_BLOCKING;
|
||||
vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, gi);
|
||||
}
|
||||
}
|
||||
|
||||
if (vm_entry_intinfo(vmx->vm, vcpu, &entryinfo)) {
|
||||
KASSERT((entryinfo & VMCS_INTR_VALID) != 0, ("%s: entry "
|
||||
"intinfo is not valid: %#lx", __func__, entryinfo));
|
||||
@ -2540,7 +2553,7 @@ vmx_exit_handle_nmi(struct vmx *vmx, int vcpuid, struct vm_exit *vmexit)
|
||||
}
|
||||
|
||||
static int
|
||||
vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
vmx_run(void *arg, int vcpu, register_t rip, pmap_t pmap,
|
||||
void *rendezvous_cookie, void *suspend_cookie)
|
||||
{
|
||||
int rc, handled, launched;
|
||||
@ -2550,7 +2563,6 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
struct vmcs *vmcs;
|
||||
struct vm_exit *vmexit;
|
||||
struct vlapic *vlapic;
|
||||
uint64_t rip;
|
||||
uint32_t exit_reason;
|
||||
|
||||
vmx = arg;
|
||||
@ -2578,11 +2590,13 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
*/
|
||||
vmcs_write(VMCS_HOST_CR3, rcr3());
|
||||
|
||||
vmcs_write(VMCS_GUEST_RIP, startrip);
|
||||
vmcs_write(VMCS_GUEST_RIP, rip);
|
||||
vmx_set_pcpu_defaults(vmx, vcpu, pmap);
|
||||
do {
|
||||
handled = UNHANDLED;
|
||||
KASSERT(vmcs_guest_rip() == rip, ("%s: vmcs guest rip mismatch "
|
||||
"%#lx/%#lx", __func__, vmcs_guest_rip(), rip));
|
||||
|
||||
handled = UNHANDLED;
|
||||
/*
|
||||
* Interrupts are disabled from this point on until the
|
||||
* guest starts executing. This is done for the following
|
||||
@ -2602,7 +2616,7 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
* pmap_invalidate_ept().
|
||||
*/
|
||||
disable_intr();
|
||||
vmx_inject_interrupts(vmx, vcpu, vlapic);
|
||||
vmx_inject_interrupts(vmx, vcpu, vlapic, rip);
|
||||
|
||||
/*
|
||||
* Check for vcpu suspension after injecting events because
|
||||
@ -2611,20 +2625,20 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
*/
|
||||
if (vcpu_suspended(suspend_cookie)) {
|
||||
enable_intr();
|
||||
vm_exit_suspended(vmx->vm, vcpu, vmcs_guest_rip());
|
||||
vm_exit_suspended(vmx->vm, vcpu, rip);
|
||||
break;
|
||||
}
|
||||
|
||||
if (vcpu_rendezvous_pending(rendezvous_cookie)) {
|
||||
enable_intr();
|
||||
vm_exit_rendezvous(vmx->vm, vcpu, vmcs_guest_rip());
|
||||
vm_exit_rendezvous(vmx->vm, vcpu, rip);
|
||||
break;
|
||||
}
|
||||
|
||||
if (vcpu_should_yield(vm, vcpu)) {
|
||||
enable_intr();
|
||||
vm_exit_astpending(vmx->vm, vcpu, vmcs_guest_rip());
|
||||
vmx_astpending_trace(vmx, vcpu, vmexit->rip);
|
||||
vm_exit_astpending(vmx->vm, vcpu, rip);
|
||||
vmx_astpending_trace(vmx, vcpu, rip);
|
||||
handled = HANDLED;
|
||||
break;
|
||||
}
|
||||
@ -2638,6 +2652,9 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
vmexit->u.vmx.exit_reason = exit_reason = vmcs_exit_reason();
|
||||
vmexit->u.vmx.exit_qualification = vmcs_exit_qualification();
|
||||
|
||||
/* Update 'nextrip' */
|
||||
vmx->state[vcpu].nextrip = rip;
|
||||
|
||||
if (rc == VMX_GUEST_VMEXIT) {
|
||||
vmx_exit_handle_nmi(vmx, vcpu, vmexit);
|
||||
enable_intr();
|
||||
@ -2648,6 +2665,7 @@ vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
|
||||
}
|
||||
launched = 1;
|
||||
vmx_exit_trace(vmx, vcpu, rip, exit_reason, handled);
|
||||
rip = vmexit->rip;
|
||||
} while (handled);
|
||||
|
||||
/*
|
||||
|
@ -78,6 +78,7 @@ struct vmxcap {
|
||||
};
|
||||
|
||||
struct vmxstate {
|
||||
uint64_t nextrip; /* next instruction to be executed by guest */
|
||||
int lastcpu; /* host cpu that this 'vcpu' last ran on */
|
||||
uint16_t vpid;
|
||||
};
|
||||
|
@ -1109,7 +1109,7 @@ vm_handle_hlt(struct vm *vm, int vcpuid, bool intr_disabled, bool *retu)
|
||||
{
|
||||
struct vcpu *vcpu;
|
||||
const char *wmesg;
|
||||
int error, t, vcpu_halted, vm_halted;
|
||||
int t, vcpu_halted, vm_halted;
|
||||
|
||||
KASSERT(!CPU_ISSET(vcpuid, &vm->halted_cpus), ("vcpu already halted"));
|
||||
|
||||
@ -1117,22 +1117,6 @@ vm_handle_hlt(struct vm *vm, int vcpuid, bool intr_disabled, bool *retu)
|
||||
vcpu_halted = 0;
|
||||
vm_halted = 0;
|
||||
|
||||
/*
|
||||
* The typical way to halt a cpu is to execute: "sti; hlt"
|
||||
*
|
||||
* STI sets RFLAGS.IF to enable interrupts. However, the processor
|
||||
* remains in an "interrupt shadow" for an additional instruction
|
||||
* following the STI. This guarantees that "sti; hlt" sequence is
|
||||
* atomic and a pending interrupt will be recognized after the HLT.
|
||||
*
|
||||
* After the HLT emulation is done the vcpu is no longer in an
|
||||
* interrupt shadow and a pending interrupt can be injected on
|
||||
* the next entry into the guest.
|
||||
*/
|
||||
error = vm_set_register(vm, vcpuid, VM_REG_GUEST_INTR_SHADOW, 0);
|
||||
KASSERT(error == 0, ("%s: error %d clearing interrupt shadow",
|
||||
__func__, error));
|
||||
|
||||
vcpu_lock(vcpu);
|
||||
while (1) {
|
||||
/*
|
||||
@ -1741,6 +1725,7 @@ int
|
||||
vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
|
||||
{
|
||||
struct vcpu *vcpu;
|
||||
int error;
|
||||
|
||||
if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
|
||||
return (EINVAL);
|
||||
@ -1765,6 +1750,16 @@ vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
/*
|
||||
* From section 26.6.1 "Interruptibility State" in Intel SDM:
|
||||
*
|
||||
* Event blocking by "STI" or "MOV SS" is cleared after guest executes
|
||||
* one instruction or incurs an exception.
|
||||
*/
|
||||
error = vm_set_register(vm, vcpuid, VM_REG_GUEST_INTR_SHADOW, 0);
|
||||
KASSERT(error == 0, ("%s: error %d clearing interrupt shadow",
|
||||
__func__, error));
|
||||
|
||||
vcpu->exception_pending = 1;
|
||||
vcpu->exception = *exception;
|
||||
VCPU_CTR1(vm, vcpuid, "Exception %d pending", exception->vector);
|
||||
|
Loading…
Reference in New Issue
Block a user