This checks for interrupt delivery to L2, unintercepted hlt in L2 and explicit L2 suspension via the activity state HLT. Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> --- x86/vmx.c | 3 +- x86/vmx.h | 3 ++ x86/vmx_tests.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/x86/vmx.c b/x86/vmx.c index fe950e6..a475aec 100644 --- a/x86/vmx.c +++ b/x86/vmx.c @@ -457,7 +457,7 @@ static void init_vmcs_guest(void) vmcs_write(GUEST_RFLAGS, 0x2); /* 26.3.1.5 */ - vmcs_write(GUEST_ACTV_STATE, 0); + vmcs_write(GUEST_ACTV_STATE, ACTV_ACTIVE); vmcs_write(GUEST_INTR_STATE, 0); } @@ -482,7 +482,6 @@ static int init_vmcs(struct vmcs **vmcs) ctrl_pin |= PIN_EXTINT | PIN_NMI | PIN_VIRT_NMI; ctrl_exit = EXI_LOAD_EFER | EXI_HOST_64; ctrl_enter = (ENT_LOAD_EFER | ENT_GUEST_64); - ctrl_cpu[0] |= CPU_HLT; /* DIsable IO instruction VMEXIT now */ ctrl_cpu[0] &= (~(CPU_IO | CPU_IO_BITMAP)); ctrl_cpu[1] = 0; diff --git a/x86/vmx.h b/x86/vmx.h index bc8c86f..3867793 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -500,6 +500,9 @@ enum Ctrl1 { #define INVEPT_SINGLE 1 #define INVEPT_GLOBAL 2 +#define ACTV_ACTIVE 0 +#define ACTV_HLT 1 + extern struct regs regs; extern union vmx_basic basic; diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 36c56b4..27ab032 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -9,6 +9,8 @@ #include "vm.h" #include "io.h" #include "fwcfg.h" +#include "isr.h" +#include "apic.h" u64 ia32_pat; u64 ia32_efer; @@ -1103,6 +1105,127 @@ static int ept_exit_handler() return VMX_TEST_VMEXIT; } +#define TIMER_VECTOR 222 + +static volatile bool timer_fired; + +static void timer_isr(isr_regs_t *regs) +{ + timer_fired = true; + apic_write(APIC_EOI, 0); +} + +static int interrupt_init(struct vmcs *vmcs) +{ + msr_bmp_init(); + vmcs_write(PIN_CONTROLS, vmcs_read(PIN_CONTROLS) & ~PIN_EXTINT); + handle_irq(TIMER_VECTOR, timer_isr); + return VMX_TEST_START; +} + +static void interrupt_main(void) +{ + long long start; + + set_stage(0); + + apic_write(APIC_LVTT, TIMER_VECTOR); + irq_enable(); + + apic_write(APIC_TMICT, 1); + asm volatile ("nop"); + report("direct interrupt without delay", timer_fired); + + timer_fired = false; + irq_disable(); + start = rdtsc(); + apic_write(APIC_TMICT, 1000000); + + asm volatile ("sti; hlt"); + + report("direct interrupt + hlt", + (rdtsc() - start > 1000000 && timer_fired)); + + vmcall(); + apic_write(APIC_TMICT, 0); + irq_disable(); + timer_fired = false; + start = rdtsc(); + apic_write(APIC_TMICT, 1000000); + + asm volatile ("sti; hlt"); + + report("intercepted interrupt + hlt", + (rdtsc() - start > 10000 && timer_fired)); + + apic_write(APIC_TMICT, 0); + irq_disable(); + timer_fired = false; + start = rdtsc(); + apic_write(APIC_TMICT, 1000000); + + irq_enable(); + asm volatile ("nop"); + vmcall(); + + report("direct interrupt + activity state hlt", + (rdtsc() - start > 10000 && timer_fired && get_stage() == 2)); + + vmcall(); + apic_write(APIC_TMICT, 0); + irq_disable(); + timer_fired = false; + start = rdtsc(); + apic_write(APIC_TMICT, 1000000); + + irq_enable(); + asm volatile ("nop"); + vmcall(); + + report("intercepted interrupt + activity state hlt", + (rdtsc() - start > 10000 && timer_fired && get_stage() == 4)); +} + +static int interrupt_exit_handler(void) +{ + u64 guest_rip = vmcs_read(GUEST_RIP); + ulong reason = vmcs_read(EXI_REASON) & 0xff; + u32 insn_len = vmcs_read(EXI_INST_LEN); + + switch (reason) { + case VMX_VMCALL: + switch (get_stage()) { + case 0: + case 2: + set_stage(get_stage() + 1); + vmcs_write(PIN_CONTROLS, + vmcs_read(PIN_CONTROLS) | PIN_EXTINT); + break; + case 1: + case 3: + vmcs_write(GUEST_ACTV_STATE, ACTV_HLT); + set_stage(get_stage() + 1); + break; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + case VMX_EXTINT: + irq_enable(); + asm volatile ("nop"); + irq_disable(); + vmcs_write(PIN_CONTROLS, + vmcs_read(PIN_CONTROLS) & ~PIN_EXTINT); + vmcs_write(GUEST_ACTV_STATE, ACTV_ACTIVE); + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + default: + printf("Unknown exit reason, %d\n", reason); + print_vmexit_info(); + } + + return VMX_TEST_VMEXIT; +} + /* name/init/guest_main/exit_handler/syscall_handler/guest_regs */ struct vmx_test vmx_tests[] = { { "null", NULL, basic_guest_main, basic_exit_handler, NULL, {0} }, @@ -1120,5 +1243,7 @@ struct vmx_test vmx_tests[] = { { "instruction intercept", insn_intercept_init, insn_intercept_main, insn_intercept_exit_handler, NULL, {0} }, { "EPT framework", ept_init, ept_main, ept_exit_handler, NULL, {0} }, + { "interrupt", interrupt_init, interrupt_main, + interrupt_exit_handler, NULL, {0} }, { NULL, NULL, NULL, NULL, NULL, {0} }, }; -- 1.8.1.1.298.ge7eed54 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html