diff mbox series

[kvm-unit-tests,8/8] x86: vmx: Test INIT processing during various CPU VMX states

Message ID 20190919125211.18152-9-liran.alon@oracle.com (mailing list archive)
State New, archived
Headers show
Series : x86: vmx: Test INIT processing in various CPU VMX states | expand

Commit Message

Liran Alon Sept. 19, 2019, 12:52 p.m. UTC
Add vmx test to verify the functionality introduced by KVM commit:
4b9852f4f389 ("KVM: x86: Fix INIT signal handling in various CPU states").

The test verifies the following functionality:
1) An INIT signal received when CPU is in VMX operation
   is blocked until it exits VMX operation.
2) If there is an INIT signal pending when CPU is in
   VMX non-root mode, it result in VMExit with (reason == 3).
3) Exit from VMX non-root mode on VMExit do not clear
   pending INIT signal in LAPIC.
4) When CPU exits VMX operation, pending INIT signal in
   LAPIC is processed.

Note: The test is excluded from the "vmx" test-suite as
it ruins the execution environment of CPU1 because
during the test it process an INIT signal.

Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
---
 x86/unittests.cfg |  10 +++-
 x86/vmx_tests.c   | 145 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 154 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 694ee3d42f3a..8156256146c3 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -227,7 +227,7 @@  extra_params = -cpu qemu64,+umip
 
 [vmx]
 file = vmx.flat
-extra_params = -cpu host,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test"
+extra_params = -cpu host,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test -vmx_init_signal_test"
 arch = x86_64
 groups = vmx
 
@@ -265,6 +265,14 @@  extra_params = -cpu host,+vmx -m 2048 -append vmx_apic_passthrough_thread_test
 arch = x86_64
 groups = vmx
 
+[vmx_init_signal_test]
+file = vmx.flat
+smp = 2
+extra_params = -cpu host,+vmx -m 2048 -append vmx_init_signal_test
+arch = x86_64
+groups = vmx
+timeout = 10
+
 [vmx_vmcs_shadow_test]
 file = vmx.flat
 extra_params = -cpu host,+vmx -append vmx_vmcs_shadow_test
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 096640a56891..962990e30d33 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -8219,6 +8219,150 @@  static void vmx_apic_passthrough_thread_test(void)
 	vmx_apic_passthrough(true);
 }
 
+static u64 init_signal_test_exit_reason;
+static bool init_signal_test_thread_continued;
+
+static void init_signal_test_thread(void *data)
+{
+	struct vmcs *test_vmcs = data;
+
+	/* Enter VMX operation (i.e. exec VMXON) */
+	u64 *ap_vmxon_region = alloc_page();
+	enable_vmx();
+	init_vmx(ap_vmxon_region);
+	_vmx_on(ap_vmxon_region);
+
+	/* Signal CPU have entered VMX operation */
+	vmx_set_test_stage(1);
+
+	/* Wait for BSP CPU to send INIT signal */
+	while (vmx_get_test_stage() != 2)
+		;
+
+	/*
+	 * Signal that we continue as usual as INIT signal
+	 * should be blocked while CPU is in VMX operation
+	 */
+	vmx_set_test_stage(3);
+
+	/* Wait for signal to enter VMX non-root mode */
+	while (vmx_get_test_stage() != 4)
+		;
+
+	/* Enter VMX non-root mode */
+	test_set_guest(v2_null_test_guest);
+	make_vmcs_current(test_vmcs);
+	enter_guest();
+	/* Save exit reason for BSP CPU to compare to expected result */
+	init_signal_test_exit_reason = vmcs_read(EXI_REASON);
+	/* VMCLEAR test-vmcs so it could be loaded by BSP CPU */
+	vmcs_clear(test_vmcs);
+	launched = false;
+	/* Signal that CPU exited to VMX root mode */
+	vmx_set_test_stage(5);
+
+	/* Wait for signal to exit VMX operation */
+	while (vmx_get_test_stage() != 6)
+		;
+
+	/* Exit VMX operation (i.e. exec VMXOFF) */
+	vmx_off();
+
+	/*
+	 * Exiting VMX operation should result in latched
+	 * INIT signal being processed. Therefore, we should
+	 * never reach the below code. Thus, signal to BSP
+	 * CPU if we have reached here so it is able to
+	 * report an issue if it happens.
+	 */
+	init_signal_test_thread_continued = true;
+}
+
+#define INIT_SIGNAL_TEST_DELAY	100000000ULL
+
+static void vmx_init_signal_test(void)
+{
+	struct vmcs *test_vmcs;
+
+	if (cpu_count() < 2) {
+		report_skip(__func__);
+		return;
+	}
+
+	/* VMCLEAR test-vmcs so it could be loaded by other CPU */
+	vmcs_save(&test_vmcs);
+	vmcs_clear(test_vmcs);
+
+	vmx_set_test_stage(0);
+	on_cpu_async(1, init_signal_test_thread, test_vmcs);
+
+	/* Wait for other CPU to enter VMX operation */
+	while (vmx_get_test_stage() != 1)
+		;
+
+	/* Send INIT signal to other CPU */
+	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_INIT | APIC_INT_ASSERT,
+				   id_map[1]);
+	/* Signal other CPU we have sent INIT signal */
+	vmx_set_test_stage(2);
+
+	/*
+	 * Wait reasonable amount of time for INIT signal to
+	 * be received on other CPU and verify that other CPU
+	 * have proceed as usual to next test stage as INIT
+	 * signal should be blocked while other CPU in
+	 * VMX operation
+	 */
+	delay(INIT_SIGNAL_TEST_DELAY);
+	report("INIT signal blocked when CPU in VMX operation",
+		   vmx_get_test_stage() == 3);
+	/* No point to continue if we failed at this point */
+	if (vmx_get_test_stage() != 3)
+		return;
+
+	/* Signal other CPU to enter VMX non-root mode */
+	init_signal_test_exit_reason = -1ull;
+	vmx_set_test_stage(4);
+	/*
+	 * Wait reasonable amont of time for other CPU
+	 * to exit to VMX root mode
+	 */
+	delay(INIT_SIGNAL_TEST_DELAY);
+	if (vmx_get_test_stage() != 5) {
+		report("Pending INIT signal haven't resulted in VMX exit", false);
+		return;
+	}
+	report("INIT signal during VMX non-root mode result in exit-reason %s (%lu)",
+			init_signal_test_exit_reason == VMX_INIT,
+			exit_reason_description(init_signal_test_exit_reason),
+			init_signal_test_exit_reason);
+
+	/* Run guest to completion */
+	make_vmcs_current(test_vmcs);
+	enter_guest();
+
+	/* Signal other CPU to exit VMX operation */
+	init_signal_test_thread_continued = false;
+	vmx_set_test_stage(6);
+
+	/*
+	 * Wait reasonable amount of time for other CPU
+	 * to run after INIT signal was processed
+	 */
+	delay(INIT_SIGNAL_TEST_DELAY);
+	report("INIT signal processed after exit VMX operation",
+		   !init_signal_test_thread_continued);
+
+	/*
+	 * TODO: Send SIPI to other CPU to sipi_entry (See x86/cstart64.S)
+	 * to re-init it to kvm-unit-tests standard environment.
+	 * Somehow (?) verify that SIPI was indeed received.
+	 */
+
+	/* Report success */
+	report(__func__, 1);
+}
+
 enum vmcs_access {
 	ACCESS_VMREAD,
 	ACCESS_VMWRITE,
@@ -8629,6 +8773,7 @@  struct vmx_test vmx_tests[] = {
 	/* APIC pass-through tests */
 	TEST(vmx_apic_passthrough_test),
 	TEST(vmx_apic_passthrough_thread_test),
+	TEST(vmx_init_signal_test),
 	/* VMCS Shadowing tests */
 	TEST(vmx_vmcs_shadow_test),
 	/* Regression tests */