From: Cathy Avery <cavery@xxxxxxxxxx> This test checks for interrupt delivery to L2 and unintercepted hlt in L2. All tests are performed both with direct interrupt injection and external interrupt interception. Based on VMX test by Jan Kiszka <jan.kiszka@xxxxxxxxxxx> Signed-off-by: Cathy Avery <cavery@xxxxxxxxxx> Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx> --- x86/svm.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/x86/svm.c b/x86/svm.c index f300c8a..df7a7c4 100644 --- a/x86/svm.c +++ b/x86/svm.c @@ -1502,6 +1502,144 @@ static bool pending_event_cli_check(struct test *test) return get_test_stage(test) == 2; } +#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 void interrupt_prepare(struct test *test) +{ + default_prepare(test); + handle_irq(TIMER_VECTOR, timer_isr); + timer_fired = false; + set_test_stage(test, 0); +} + +static void interrupt_test(struct test *test) +{ + long long start, loops; + + apic_write(APIC_LVTT, TIMER_VECTOR); + irq_enable(); + apic_write(APIC_TMICT, 1); //Timer Initial Count Register 0x380 one-shot + for (loops = 0; loops < 10000000 && !timer_fired; loops++) + asm volatile ("nop"); + + report(timer_fired, "direct interrupt while running guest"); + + if (!timer_fired) { + set_test_stage(test, -1); + vmmcall(); + } + + apic_write(APIC_TMICT, 0); + irq_disable(); + vmmcall(); + + timer_fired = false; + apic_write(APIC_TMICT, 1); + for (loops = 0; loops < 10000000 && !timer_fired; loops++) + asm volatile ("nop"); + + report(timer_fired, "intercepted interrupt while running guest"); + + if (!timer_fired) { + set_test_stage(test, -1); + vmmcall(); + } + + irq_enable(); + apic_write(APIC_TMICT, 0); + irq_disable(); + + timer_fired = false; + start = rdtsc(); + apic_write(APIC_TMICT, 1000000); + asm volatile ("sti; hlt"); + + report(rdtsc() - start > 10000 && timer_fired, + "direct interrupt + hlt"); + + if (!timer_fired) { + set_test_stage(test, -1); + vmmcall(); + } + + apic_write(APIC_TMICT, 0); + irq_disable(); + vmmcall(); + + timer_fired = false; + start = rdtsc(); + apic_write(APIC_TMICT, 1000000); + asm volatile ("hlt"); + + report(rdtsc() - start > 10000 && timer_fired, + "intercepted interrupt + hlt"); + + if (!timer_fired) { + set_test_stage(test, -1); + vmmcall(); + } + + apic_write(APIC_TMICT, 0); + irq_disable(); +} + +static bool interrupt_finished(struct test *test) +{ + switch (get_test_stage(test)) { + case 0: + case 2: + if (test->vmcb->control.exit_code != SVM_EXIT_VMMCALL) { + report(false, "VMEXIT not due to vmmcall. Exit reason 0x%x", + test->vmcb->control.exit_code); + return true; + } + test->vmcb->save.rip += 3; + + test->vmcb->control.intercept |= (1ULL << INTERCEPT_INTR); + test->vmcb->control.int_ctl |= V_INTR_MASKING_MASK; + break; + + case 1: + case 3: + if (test->vmcb->control.exit_code != SVM_EXIT_INTR) { + report(false, "VMEXIT not due to intr intercept. Exit reason 0x%x", + test->vmcb->control.exit_code); + return true; + } + + irq_enable(); + asm volatile ("nop"); + irq_disable(); + + test->vmcb->control.intercept &= ~(1ULL << INTERCEPT_INTR); + test->vmcb->control.int_ctl &= ~V_INTR_MASKING_MASK; + break; + + case 4: + break; + + default: + return true; + } + + inc_test_stage(test); + + return get_test_stage(test) == 5; +} + +static bool interrupt_check(struct test *test) +{ + return get_test_stage(test) == 5; +} + static struct test tests[] = { { "null", default_supported, default_prepare, default_prepare_gif_clear, null_test, @@ -1582,6 +1720,9 @@ static struct test tests[] = { pending_event_cli_prepare_gif_clear, pending_event_cli_test, pending_event_cli_finished, pending_event_cli_check }, + { "interrupt", default_supported, interrupt_prepare, + default_prepare_gif_clear, interrupt_test, + interrupt_finished, interrupt_check }, }; int matched; -- 1.8.3.1