aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Mattson <jmattson@google.com>2020-04-13 17:10:25 -0700
committerPaolo Bonzini <pbonzini@redhat.com>2020-04-23 09:11:57 -0400
commitaa004fc06323d3796b0350c041bf13cd2f5b547c (patch)
tree4648f942027e849bb269159f68afe3d78f8f54f6
parent1a296ac170f2381ffb7d3d7307eda76573580bb1 (diff)
downloadkvm-unit-tests-aa004fc06323d3796b0350c041bf13cd2f5b547c.tar.gz
x86: nVMX: Add some corner-case VMX-preemption timer tests
Verify that both injected events and debug traps that result from pending debug exceptions take precedence over a "VMX-preemption timer expired" VM-exit resulting from a zero-valued VMX-preemption timer. Signed-off-by: Jim Mattson <jmattson@google.com> Reviewed-by: Oliver Upton <oupton@google.com> Reviewed-by: Peter Shier <pshier@google.com> Message-Id: <20200414001026.50051-1-jmattson@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--x86/vmx_tests.c120
1 files changed, 120 insertions, 0 deletions
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index a91715f..e35f6b9 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -8328,6 +8328,125 @@ static void vmx_store_tsc_test(void)
msr_entry.value, low, high);
}
+static void vmx_preemption_timer_zero_test_db_handler(struct ex_regs *regs)
+{
+}
+
+static void vmx_preemption_timer_zero_test_guest(void)
+{
+ while (vmx_get_test_stage() < 3)
+ vmcall();
+}
+
+static void vmx_preemption_timer_zero_activate_preemption_timer(void)
+{
+ vmcs_set_bits(PIN_CONTROLS, PIN_PREEMPT);
+ vmcs_write(PREEMPT_TIMER_VALUE, 0);
+}
+
+static void vmx_preemption_timer_zero_advance_past_vmcall(void)
+{
+ vmcs_clear_bits(PIN_CONTROLS, PIN_PREEMPT);
+ enter_guest();
+ skip_exit_vmcall();
+}
+
+static void vmx_preemption_timer_zero_inject_db(bool intercept_db)
+{
+ vmx_preemption_timer_zero_activate_preemption_timer();
+ vmcs_write(ENT_INTR_INFO, INTR_INFO_VALID_MASK |
+ INTR_TYPE_HARD_EXCEPTION | DB_VECTOR);
+ vmcs_write(EXC_BITMAP, intercept_db ? 1 << DB_VECTOR : 0);
+ enter_guest();
+}
+
+static void vmx_preemption_timer_zero_set_pending_dbg(u32 exception_bitmap)
+{
+ vmx_preemption_timer_zero_activate_preemption_timer();
+ vmcs_write(GUEST_PENDING_DEBUG, BIT(12) | DR_TRAP1);
+ vmcs_write(EXC_BITMAP, exception_bitmap);
+ enter_guest();
+}
+
+static void vmx_preemption_timer_zero_expect_preempt_at_rip(u64 expected_rip)
+{
+ u32 reason = (u32)vmcs_read(EXI_REASON);
+ u64 guest_rip = vmcs_read(GUEST_RIP);
+
+ report(reason == VMX_PREEMPT && guest_rip == expected_rip,
+ "Exit reason is 0x%x (expected 0x%x) and guest RIP is %lx (0x%lx expected).",
+ reason, VMX_PREEMPT, guest_rip, expected_rip);
+}
+
+/*
+ * This test ensures that when the VMX preemption timer is zero at
+ * VM-entry, a VM-exit occurs after any event injection and after any
+ * pending debug exceptions are raised, but before execution of any
+ * guest instructions.
+ */
+static void vmx_preemption_timer_zero_test(void)
+{
+ u64 db_fault_address = (u64)get_idt_addr(&boot_idt[DB_VECTOR]);
+ handler old_db;
+ u32 reason;
+
+ if (!(ctrl_pin_rev.clr & PIN_PREEMPT)) {
+ report_skip("'Activate VMX-preemption timer' not supported");
+ return;
+ }
+
+ /*
+ * Install a custom #DB handler that doesn't abort.
+ */
+ old_db = handle_exception(DB_VECTOR,
+ vmx_preemption_timer_zero_test_db_handler);
+
+ test_set_guest(vmx_preemption_timer_zero_test_guest);
+
+ /*
+ * VMX-preemption timer should fire after event injection.
+ */
+ vmx_set_test_stage(0);
+ vmx_preemption_timer_zero_inject_db(0);
+ vmx_preemption_timer_zero_expect_preempt_at_rip(db_fault_address);
+ vmx_preemption_timer_zero_advance_past_vmcall();
+
+ /*
+ * VMX-preemption timer should fire after event injection.
+ * Exception bitmap is irrelevant, since you can't intercept
+ * an event that you injected.
+ */
+ vmx_set_test_stage(1);
+ vmx_preemption_timer_zero_inject_db(1 << DB_VECTOR);
+ vmx_preemption_timer_zero_expect_preempt_at_rip(db_fault_address);
+ vmx_preemption_timer_zero_advance_past_vmcall();
+
+ /*
+ * VMX-preemption timer should fire after pending debug exceptions
+ * have delivered a #DB trap.
+ */
+ vmx_set_test_stage(2);
+ vmx_preemption_timer_zero_set_pending_dbg(0);
+ vmx_preemption_timer_zero_expect_preempt_at_rip(db_fault_address);
+ vmx_preemption_timer_zero_advance_past_vmcall();
+
+ /*
+ * VMX-preemption timer would fire after pending debug exceptions
+ * have delivered a #DB trap, but in this case, the #DB trap is
+ * intercepted.
+ */
+ vmx_set_test_stage(3);
+ vmx_preemption_timer_zero_set_pending_dbg(1 << DB_VECTOR);
+ reason = (u32)vmcs_read(EXI_REASON);
+ report(reason == VMX_EXC_NMI, "Exit reason is 0x%x (expected 0x%x)",
+ reason, VMX_EXC_NMI);
+
+ vmcs_clear_bits(PIN_CONTROLS, PIN_PREEMPT);
+ enter_guest();
+
+ handle_exception(DB_VECTOR, old_db);
+}
+
static void vmx_db_test_guest(void)
{
/*
@@ -9632,6 +9751,7 @@ struct vmx_test vmx_tests[] = {
TEST(vmx_pending_event_test),
TEST(vmx_pending_event_hlt_test),
TEST(vmx_store_tsc_test),
+ TEST(vmx_preemption_timer_zero_test),
/* EPT access tests. */
TEST(ept_access_test_not_present),
TEST(ept_access_test_read_only),