diff mbox series

[kvm-unit-tests,2/2] svm: Add test cases around interrupt injection and halting

Message ID 1583401006-57136-3-git-send-email-pbonzini@redhat.com (mailing list archive)
State New, archived
Headers show
Series svm: more interrupt injection tests | expand

Commit Message

Paolo Bonzini March 5, 2020, 9:36 a.m. UTC
From: Cathy Avery <cavery@redhat.com>

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>
---
 x86/svm.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 141 insertions(+)
diff mbox series

Patch

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;