aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCathy Avery <cavery@redhat.com>2020-01-09 10:24:32 -0500
committerPaolo Bonzini <pbonzini@redhat.com>2020-03-05 10:06:07 +0100
commit2b2139522d66e0fc1558b5a3542db81d77b31af4 (patch)
treed5e1c0ca55df45d4eb1552c68a42097ac419b55b
parentee7ce25afe6518689027d9379e1da3d525fb6796 (diff)
downloadkvm-unit-tests-2b2139522d66e0fc1558b5a3542db81d77b31af4.tar.gz
svm: Add test cases around interrupt injection and halting
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@siemens.com> Signed-off-by: Cathy Avery <cavery@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-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;