diff options
Diffstat (limited to 'x86/svm.c')
-rw-r--r-- | x86/svm.c | 141 |
1 files changed, 141 insertions, 0 deletions
@@ -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; |