diff mbox

[5/7] x86: nVMX: Verify vmcs12 EOI-exit-bitmap not corrupted on L1 IOAPIC scan

Message ID 1521674594-12085-6-git-send-email-liran.alon@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Liran Alon March 21, 2018, 11:23 p.m. UTC
From: Arbel Moshe <arbel.moshe@oracle.com>

This test aims to verify that L1 IOAPIC scan requests don't corrupt
vmcs02->eoi_exit_bitmap which should always be equal to
vmcs12->eoi_exit_bitmap.

Test configures CPU0 to run L2 with virtual-interrupt-delivery and
empty EOI-exit-bitmap. Therefore, expecting no vmexit on EOI_INDUCED
when L2 performs EOI. Before L2 performs EOI, test makes CPU1 modify
IOAPIC redirection table which will trigger an IOAPIC scan on all
vCPUs. Then, L2 performs EOI followed by a VMCALL. Test fails if
L1 got a vmexit other than VMCALL.

The issue tested in this patch was fixed in KVM commit
("KVM: nVMX: Do not load EOI-exitmap while running L2").
For more details, refer to that commit message.

Signed-off-by: Arbel Moshe <arbel.moshe@oracle.com>
Suggested-by: Liran Alon <liran.alon@oracle.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
---
 x86/unittests.cfg |   7 ++++
 x86/vmx.c         |   2 +-
 x86/vmx_tests.c   | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 112 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 22c62d5d373d..639e487cd777 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -544,6 +544,13 @@  extra_params = -cpu host,+vmx -m 2560 -append vmx_cr_load_test
 arch = x86_64
 groups = vmx
 
+[vmx_eoi_bitmap_ioapic_scan]
+file = vmx.flat
+smp = 2
+extra_params = -cpu host,+vmx -m 2048 -append vmx_eoi_bitmap_ioapic_scan_test
+arch = x86_64
+groups = vmx
+
 [debug]
 file = debug.flat
 arch = x86_64
diff --git a/x86/vmx.c b/x86/vmx.c
index 75ee8e75ad0a..aa04ac68dab5 100644
--- a/x86/vmx.c
+++ b/x86/vmx.c
@@ -1816,7 +1816,7 @@  int main(int argc, const char *argv[])
 	int i = 0;
 
 	setup_vm();
-	setup_idt();
+	smp_init();
 	hypercall_field = 0;
 
 	argv++;
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 613a8bd18c27..0bf081846533 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -14,6 +14,8 @@ 
 #include "types.h"
 #include "vmalloc.h"
 #include "alloc_page.h"
+#include "smp.h"
+#include "delay.h"
 
 #define NONCANONICAL            0xaaaaaaaaaaaaaaaaull
 
@@ -3916,6 +3918,106 @@  static void vmx_cr_load_test(void)
 	TEST_ASSERT(!write_cr4_checking(cr4 & ~X86_CR4_PCIDE));
 }
 
+static bool cpu_has_apicv(void)
+{
+	return ((ctrl_cpu_rev[1].clr & CPU_APIC_REG_VIRT) &&
+		(ctrl_cpu_rev[1].clr & CPU_VINTD) &&
+		(ctrl_pin_rev.clr & PIN_POST_INTR));
+}
+
+static void trigger_ioapic_scan_thread(void *data)
+{
+	/* Wait until other CPU entered L2 */
+	while (vmx_get_test_stage() != 1)
+		;
+
+	/* Trigger ioapic scan */
+	ioapic_set_redir(0xf, 0x79, TRIGGER_LEVEL);
+	vmx_set_test_stage(2);
+}
+
+static void irq_79_handler_guest(isr_regs_t *regs)
+{
+	eoi();
+
+	/* L1 expects vmexit on VMX_VMCALL and not VMX_EOI_INDUCED */
+	vmcall();
+}
+
+/*
+ * Constant for num of busy-loop iterations after which
+ * a timer interrupt should have happened in host
+ */
+#define TIMER_INTERRUPT_DELAY 100000000
+
+static void vmx_eoi_bitmap_ioapic_scan_test_guest(void)
+{
+	handle_irq(0x79, irq_79_handler_guest);
+	irq_enable();
+
+	/* Signal to L1 CPU to trigger ioapic scan */
+	vmx_set_test_stage(1);
+	/* Wait until L1 CPU to trigger ioapic scan */
+	while (vmx_get_test_stage() != 2)
+		;
+
+	/*
+	 * Wait for L0 timer interrupt to be raised while we run in L2
+	 * such that L0 will process the IOAPIC scan request before
+	 * resuming L2
+	 */
+	delay(TIMER_INTERRUPT_DELAY);
+
+	asm volatile ("int $0x79");
+}
+
+static void vmx_eoi_bitmap_ioapic_scan_test(void)
+{
+	void *msr_bitmap;
+	void *virtual_apic_page;
+
+	if (!cpu_has_apicv() || (cpu_count() < 2)) {
+		report_skip(__func__);
+		return;
+	}
+
+	msr_bitmap = alloc_page();
+	virtual_apic_page = alloc_page();
+
+	u64 cpu_ctrl_0 = CPU_SECONDARY | CPU_TPR_SHADOW | CPU_MSR_BITMAP;
+	u64 cpu_ctrl_1 = CPU_VINTD | CPU_VIRT_X2APIC;
+
+	memset(msr_bitmap, 0x0, PAGE_SIZE);
+	vmcs_write(MSR_BITMAP, (u64)msr_bitmap);
+
+	vmcs_write(APIC_VIRT_ADDR, (u64)virtual_apic_page);
+	vmcs_write(PIN_CONTROLS, vmcs_read(PIN_CONTROLS) | PIN_EXTINT);
+
+	vmcs_write(EOI_EXIT_BITMAP0, 0x0);
+	vmcs_write(EOI_EXIT_BITMAP1, 0x0);
+	vmcs_write(EOI_EXIT_BITMAP2, 0x0);
+	vmcs_write(EOI_EXIT_BITMAP3, 0x0);
+
+	vmcs_write(CPU_EXEC_CTRL0, vmcs_read(CPU_EXEC_CTRL0) | cpu_ctrl_0);
+	vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) | cpu_ctrl_1);
+
+	on_cpu_async(1, trigger_ioapic_scan_thread, NULL);
+	test_set_guest(vmx_eoi_bitmap_ioapic_scan_test_guest);
+
+	/*
+	 * Launch L2.
+	 * We expect the exit reason to be VMX_VMCALL (and not EOI INDUCED).
+	 * In case the reason isn't VMX_VMCALL, the asserion inside
+	 * skip_exit_vmcall() will fail.
+	 */
+	enter_guest();
+	skip_exit_vmcall();
+
+	/* Let L2 finish */
+	enter_guest();
+	report(__func__, 1);
+}
+
 #define TEST(name) { #name, .v2 = name }
 
 /* name/init/guest_main/exit_handler/syscall_handler/guest_regs */
@@ -3982,6 +4084,8 @@  struct vmx_test vmx_tests[] = {
 	/* VM-entry tests */
 	TEST(vmx_controls_test),
 	TEST(vmentry_movss_shadow_test),
+	/* APICv tests */
+	TEST(vmx_eoi_bitmap_ioapic_scan_test),
 	/* Regression tests */
 	TEST(vmx_cr_load_test),
 	{ NULL, NULL, NULL, NULL, NULL, {0} },