From: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> This checks for interrupt delivery to L2, unintercepted hlt in L2 and explicit L2 suspension via the activity state HLT. All tests are performed both with direct interrupt injection and external interrupt interception. Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> --- x86/vmx.c | 3 +- x86/vmx.h | 3 ++ x86/vmx_tests.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 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 bec34c4..70efb50 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; @@ -1117,6 +1119,146 @@ 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, loops; + + set_stage(0); + + apic_write(APIC_LVTT, TIMER_VECTOR); + irq_enable(); + + apic_write(APIC_TMICT, 1); + for (loops = 0; loops < 10000000 && !timer_fired; loops++) + asm volatile ("nop"); + report("direct interrupt while running guest", timer_fired); + + apic_write(APIC_TMICT, 0); + irq_disable(); + vmcall(); + timer_fired = false; + apic_write(APIC_TMICT, 1); + for (loops = 0; loops < 10000000 && !timer_fired; loops++) + asm volatile ("nop"); + report("intercepted interrupt while running guest", timer_fired); + + irq_enable(); + apic_write(APIC_TMICT, 0); + irq_disable(); + vmcall(); + timer_fired = false; + start = rdtsc(); + apic_write(APIC_TMICT, 1000000); + + asm volatile ("sti; hlt"); + + report("direct interrupt + hlt", + rdtsc() - start > 1000000 && timer_fired); + + apic_write(APIC_TMICT, 0); + irq_disable(); + vmcall(); + 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(); + vmcall(); + 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); + + apic_write(APIC_TMICT, 0); + irq_disable(); + vmcall(); + 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); +} + +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: + case 5: + vmcs_write(PIN_CONTROLS, + vmcs_read(PIN_CONTROLS) | PIN_EXTINT); + break; + case 1: + case 3: + vmcs_write(PIN_CONTROLS, + vmcs_read(PIN_CONTROLS) & ~PIN_EXTINT); + break; + case 4: + case 6: + vmcs_write(GUEST_ACTV_STATE, ACTV_HLT); + break; + } + set_stage(get_stage() + 1); + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + case VMX_EXTINT: + irq_enable(); + asm volatile ("nop"); + irq_disable(); + if (get_stage() >= 2) { + 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} }, @@ -1134,5 +1276,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