From 359283968a65b318f50501a007b9067dff029408 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Wed, 20 Jul 2022 13:15:42 +0200 Subject: [PATCH] vbox6: improve NMI handling Fixes #4568 --- repos/ports/src/virtualbox6/sup_vcpu.cc | 134 +++++++++++++++++------- 1 file changed, 94 insertions(+), 40 deletions(-) diff --git a/repos/ports/src/virtualbox6/sup_vcpu.cc b/repos/ports/src/virtualbox6/sup_vcpu.cc index ec221099ce..e7f782d648 100644 --- a/repos/ports/src/virtualbox6/sup_vcpu.cc +++ b/repos/ports/src/virtualbox6/sup_vcpu.cc @@ -275,6 +275,40 @@ template void Sup::Vcpu_impl::_transfer_state_to_vcpu(CPUM } +/* + * Based on hmR0VmxImportGuestIntrState() + */ +static void handle_intr_state(PVMCPUCC pVCpu, CPUMCTX &ctx, Vcpu_state &state) +{ + auto const interrupt_state = state.intr_state.value(); + + if (!interrupt_state /* VMX_VMCS_GUEST_INT_STATE_NONE */) { + if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)) + VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS); + CPUMSetGuestNmiBlocking(pVCpu, false); + } else { + if (interrupt_state & (VMX_VMCS_GUEST_INT_STATE_BLOCK_MOVSS | + VMX_VMCS_GUEST_INT_STATE_BLOCK_STI)) + EMSetInhibitInterruptsPC(pVCpu, ctx.rip); + else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)) + VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS); + + bool const block_nmi = RT_BOOL(interrupt_state & + VMX_VMCS_GUEST_INT_STATE_BLOCK_NMI); + CPUMSetGuestNmiBlocking(pVCpu, block_nmi); + } + + /* prepare clearing blocking MOV SS or STI bits for next VM-entry */ + if (interrupt_state & (VMX_VMCS_GUEST_INT_STATE_BLOCK_MOVSS | + VMX_VMCS_GUEST_INT_STATE_BLOCK_STI)) { + state.intr_state.charge(state.intr_state.value() & + ~unsigned(VMX_VMCS_GUEST_INT_STATE_BLOCK_MOVSS | + VMX_VMCS_GUEST_INT_STATE_BLOCK_STI)); + state.actv_state.charge(VMX_VMCS_GUEST_ACTIVITY_ACTIVE); + } +} + + template void Sup::Vcpu_impl::_transfer_state_to_vbox(CPUMCTX &ctx) { Vcpu_state const &state { _vcpu.state() }; @@ -359,24 +393,13 @@ template void Sup::Vcpu_impl::_transfer_state_to_vbox(CPUM _cached_state.ctrl_primary = state.ctrl_primary.value(); _cached_state.ctrl_secondary = state.ctrl_secondary.value(); - /* clear blocking by MOV SS or STI bits */ - if (_vcpu.state().intr_state.value() & 3) { - _vcpu.state().intr_state.charge(state.intr_state.value() & ~3U); - _vcpu.state().actv_state.charge(VMX_VMCS_GUEST_ACTIVITY_ACTIVE); - } + /* handle guest interrupt state */ + handle_intr_state(pVCpu, ctx, _vcpu.state()); VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TO_R3); _vmcpu.cpum.s.fUseFlags |= CPUM_USED_FPU_GUEST; - if (state.intr_state.value() != VMX_VMCS_GUEST_INT_STATE_NONE) { - Assert(state.intr_state.value() == VMX_VMCS_GUEST_INT_STATE_BLOCK_STI || - state.intr_state.value() == VMX_VMCS_GUEST_INT_STATE_BLOCK_MOVSS); - EMSetInhibitInterruptsPC(pVCpu, ctx.rip); - } else { - VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS); - } - APICSetTpr(pVCpu, tpr); /* import FPU state */ @@ -500,12 +523,6 @@ typename Sup::Vcpu_impl::Current_state Sup::Vcpu_impl::_handle_paused() Assert(state.flags.value() & X86_EFL_IF); - if (state.intr_state.value() != VMX_VMCS_GUEST_INT_STATE_NONE) - Genode::log("intr state ", Genode::Hex(state.intr_state.value()), - " ", Genode::Hex(state.intr_state.value() & 0xf)); - - Assert(state.intr_state.value() == VMX_VMCS_GUEST_INT_STATE_NONE); - /* * We got a pause exit during IRQ injection and the guest is ready for * IRQ injection. So, just continue running the vCPU. @@ -544,7 +561,6 @@ typename Sup::Vcpu_impl::Current_state Sup::Vcpu_impl::_handle_irq_window( PVMCPU pVCpu = &_vmcpu; - Assert(state.intr_state.value() == VMX_VMCS_GUEST_INT_STATE_NONE); Assert(state.flags.value() & X86_EFL_IF); Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)); Assert(!VMX_EXIT_INT_INFO_IS_VALID(state.inj_info.value())); @@ -588,34 +604,68 @@ typename Sup::Vcpu_impl::Current_state Sup::Vcpu_impl::_handle_irq_window( Assert(TRPMHasTrap(pVCpu)); /* interrupt can be dispatched */ - uint8_t u8Vector; - TRPMEVENT enmType; - SVMEVENT Event; - uint32_t u32ErrorCode; - RTGCUINT cr2; - - Event.u = 0; + uint8_t u8Vector { }; + TRPMEVENT event_type { TRPM_HARDWARE_INT }; + SVMEVENT event { }; + uint32_t errorcode { }; + RTGCUINT cr2 { }; /* If a new event is pending, then dispatch it now. */ - int rc = TRPMQueryTrapAll(pVCpu, &u8Vector, &enmType, &u32ErrorCode, &cr2, 0, 0); + int rc = TRPMQueryTrapAll(pVCpu, &u8Vector, &event_type, &errorcode, &cr2, 0, 0); AssertRC(rc); - Assert(enmType == TRPM_HARDWARE_INT); - Assert(u8Vector != X86_XCPT_NMI); + if (rc != VINF_SUCCESS) { + Genode::warning("no trap available"); + return RUNNING; + } + + /* based upon hmR0SvmTrpmTrapToPendingEvent */ + switch (event_type) { + case TRPM_TRAP: + event.n.u1Valid = 1; + event.n.u8Vector = u8Vector; + + switch (u8Vector) { + case X86_XCPT_NMI: + event.n.u3Type = SVM_EVENT_NMI; + + static_assert(SVM_EVENT_NMI == VMX_ENTRY_INT_INFO_TYPE_NMI, + "SVM vs VMX mismatch"); + break; + default: + Genode::error("unsupported injection case - " + "TRPM_TRAP, vector=", u8Vector); + Assert(!"unsupported injection case"); + return PAUSED; + } + break; + case TRPM_HARDWARE_INT: + event.n.u1Valid = 1; + event.n.u8Vector = u8Vector; + event.n.u3Type = SVM_EVENT_EXTERNAL_IRQ; + + static_assert(VMX_ENTRY_INT_INFO_TYPE_EXT_INT == SVM_EVENT_EXTERNAL_IRQ, + "SVM vs VMX mismatch"); + + break; + case TRPM_SOFTWARE_INT: + event.n.u1Valid = 1; + event.n.u8Vector = u8Vector; + event.n.u3Type = SVM_EVENT_SOFTWARE_INT; + + static_assert(VMX_ENTRY_INT_INFO_TYPE_SW_INT == SVM_EVENT_SOFTWARE_INT, + "SVM vs VMX mismatch"); + default: + Genode::error("unsupported injection case"); + Assert(!"unsupported injection case"); + return PAUSED; + } /* Clear the pending trap. */ rc = TRPMResetTrap(pVCpu); AssertRC(rc); - Event.n.u8Vector = u8Vector; - Event.n.u1Valid = 1; - Event.n.u32ErrorCode = u32ErrorCode; - - Assert(VMX_ENTRY_INT_INFO_TYPE_EXT_INT == SVM_EVENT_EXTERNAL_IRQ); - - Event.n.u3Type = VMX_ENTRY_INT_INFO_TYPE_EXT_INT; - - state.inj_info.charge(Event.u); - state.inj_error.charge(Event.n.u32ErrorCode); + state.inj_info.charge(event.u); + state.inj_error.charge(errorcode); return RUNNING; } @@ -749,6 +799,10 @@ template VBOXSTRICTRC Sup::Vcpu_impl::run() /* track guest mode changes - see VMM/VMMAll/IEMAllCImpl.cpp.h */ PGMChangeMode(pVCpu, ctx.cr0, ctx.cr4, ctx.msrEFER); + /* avoid assertion in EMHandleRCTmpl.h, normally set by SVMRO/VMXR0 */ + if (TRPMHasTrap(pVCpu)) + return VINF_EM_RAW_INJECT_TRPM_EVENT; + /* evaluated in VMM/include/EMHandleRCTmpl.h */ return rc; }