aboutsummaryrefslogtreecommitdiffstats
path: root/x86/svm.c
diff options
context:
space:
mode:
Diffstat (limited to 'x86/svm.c')
-rw-r--r--x86/svm.c141
1 files changed, 141 insertions, 0 deletions
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;