[kvm-unit-tests PATCH v4 5/9] x86/access: Add forced emulation support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Mathias Krause <minipli@xxxxxxxxxxxxxx>

Add support to force access tests to be handled by the emulator, if
supported by KVM.  Due to emulation being rather slow, make the forced
emulation nodefault, i.e. a manual-only testcase.  Note, "manual" just
means the test runner needs to specify a specific group(s), i.e. the
test is still easy to enable in CI for compatible setups.

Manually check for FEP support in the test instead of using the "check"
option to query the module param, as any non-zero force_emulation_prefix
value enables FEP (see KVM commit d500e1ed3dc8 ("KVM: x86: Allow clearing
RFLAGS.RF on forced emulation to test code #DBs") and scripts/runtime.bash
only supports checking for a single value.

Defer enabling FEP for the nested VMX variants to a future patch.  KVM has
a bug related to emulating NOP (yes, NOP) for L2, and the nVMX varaints
will require additional massaging to propagate the "allow emulation" flag.

Signed-off-by: Mathias Krause <minipli@xxxxxxxxxxxxxx>
[sean: make generic fep testcase nodefault instead of impossible]
Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
 x86/access.c      | 38 +++++++++++++++++++++++++++++++-------
 x86/access.h      |  2 +-
 x86/access_test.c |  8 +++++---
 x86/unittests.cfg |  6 ++++++
 x86/vmx_tests.c   |  2 +-
 5 files changed, 44 insertions(+), 12 deletions(-)

diff --git a/x86/access.c b/x86/access.c
index 1677d52e..4a3ca265 100644
--- a/x86/access.c
+++ b/x86/access.c
@@ -82,7 +82,9 @@ enum {
 	AC_CPU_CR4_SMEP_BIT,
 	AC_CPU_CR4_PKE_BIT,
 
-	NR_AC_FLAGS
+	AC_FEP_BIT,
+
+	NR_AC_FLAGS,
 };
 
 #define AC_PTE_PRESENT_MASK   (1 << AC_PTE_PRESENT_BIT)
@@ -121,6 +123,8 @@ enum {
 #define AC_CPU_CR4_SMEP_MASK  (1 << AC_CPU_CR4_SMEP_BIT)
 #define AC_CPU_CR4_PKE_MASK   (1 << AC_CPU_CR4_PKE_BIT)
 
+#define AC_FEP_MASK           (1 << AC_FEP_BIT)
+
 const char *ac_names[] = {
 	[AC_PTE_PRESENT_BIT] = "pte.p",
 	[AC_PTE_ACCESSED_BIT] = "pte.a",
@@ -152,6 +156,7 @@ const char *ac_names[] = {
 	[AC_CPU_CR0_WP_BIT] = "cr0.wp",
 	[AC_CPU_CR4_SMEP_BIT] = "cr4.smep",
 	[AC_CPU_CR4_PKE_BIT] = "cr4.pke",
+	[AC_FEP_BIT] = "fep",
 };
 
 static inline void *va(pt_element_t phys)
@@ -799,10 +804,13 @@ static int ac_test_do_access(ac_test_t *at)
 
 	if (F(AC_ACCESS_TWICE)) {
 		asm volatile ("mov $fixed2, %%rsi \n\t"
-			      "mov (%[addr]), %[reg] \n\t"
+			      "cmp $0, %[fep] \n\t"
+			      "jz 1f \n\t"
+			      KVM_FEP
+			      "1: mov (%[addr]), %[reg] \n\t"
 			      "fixed2:"
 			      : [reg]"=r"(r), [fault]"=a"(fault), "=b"(e)
-			      : [addr]"r"(at->virt)
+			      : [addr]"r"(at->virt), [fep]"r"(F(AC_FEP))
 			      : "rsi");
 		fault = 0;
 	}
@@ -823,9 +831,15 @@ static int ac_test_do_access(ac_test_t *at)
 		      "jnz 2f \n\t"
 		      "cmp $0, %[write] \n\t"
 		      "jnz 1f \n\t"
-		      "mov (%[addr]), %[reg] \n\t"
+		      "cmp $0, %[fep] \n\t"
+		      "jz 0f \n\t"
+		      KVM_FEP
+		      "0: mov (%[addr]), %[reg] \n\t"
 		      "jmp done \n\t"
-		      "1: mov %[reg], (%[addr]) \n\t"
+		      "1: cmp $0, %[fep] \n\t"
+		      "jz 0f \n\t"
+		      KVM_FEP
+		      "0: mov %[reg], (%[addr]) \n\t"
 		      "jmp done \n\t"
 		      "2: call *%[addr] \n\t"
 		      "done: \n"
@@ -843,6 +857,7 @@ static int ac_test_do_access(ac_test_t *at)
 			[write]"r"(F(AC_ACCESS_WRITE)),
 			[user]"r"(F(AC_ACCESS_USER)),
 			[fetch]"r"(F(AC_ACCESS_FETCH)),
+			[fep]"r"(F(AC_FEP)),
 			[user_ds]"i"(USER_DS),
 			[user_cs]"i"(USER_CS),
 			[user_stack_top]"r"(user_stack + sizeof user_stack),
@@ -1209,12 +1224,17 @@ const ac_test_fn ac_test_cases[] =
 	check_effective_sp_permissions,
 };
 
-void ac_test_run(int pt_levels)
+void ac_test_run(int pt_levels, bool force_emulation)
 {
 	ac_test_t at;
 	ac_pt_env_t pt_env;
 	int i, tests, successes;
 
+	if (force_emulation && !is_fep_available()) {
+		report_skip("Forced emulation prefix (FEP) not available\n");
+		return;
+	}
+
 	printf("run\n");
 	tests = successes = 0;
 
@@ -1232,6 +1252,9 @@ void ac_test_run(int pt_levels)
 		invalid_mask |= AC_PTE_BIT36_MASK;
 	}
 
+	if (!force_emulation)
+		invalid_mask |= AC_FEP_MASK;
+
 	ac_env_int(&pt_env, pt_levels);
 	ac_test_init(&at, 0xffff923400000000ul, &pt_env);
 
@@ -1292,5 +1315,6 @@ void ac_test_run(int pt_levels)
 
 	printf("\n%d tests, %d failures\n", tests, tests - successes);
 
-	report(successes == tests, "%d-level paging tests", pt_levels);
+	report(successes == tests, "%d-level paging tests%s", pt_levels,
+	       force_emulation ? " (with forced emulation)" : "");
 }
diff --git a/x86/access.h b/x86/access.h
index 9a6c5628..206a1c86 100644
--- a/x86/access.h
+++ b/x86/access.h
@@ -4,6 +4,6 @@
 #define PT_LEVEL_PML4 4
 #define PT_LEVEL_PML5 5
 
-void ac_test_run(int page_table_levels);
+void ac_test_run(int page_table_levels, bool force_emulation);
 
 #endif // X86_ACCESS_H
\ No newline at end of file
diff --git a/x86/access_test.c b/x86/access_test.c
index 2ac649d2..025294da 100644
--- a/x86/access_test.c
+++ b/x86/access_test.c
@@ -3,10 +3,12 @@
 #include "x86/vm.h"
 #include "access.h"
 
-int main(void)
+int main(int argc, const char *argv[])
 {
+	bool force_emulation = argc >= 2 && !strcmp(argv[1], "force_emulation");
+
 	printf("starting test\n\n");
-	ac_test_run(PT_LEVEL_PML4);
+	ac_test_run(PT_LEVEL_PML4, force_emulation);
 
 #ifndef CONFIG_EFI
 	/*
@@ -16,7 +18,7 @@ int main(void)
 	if (this_cpu_has(X86_FEATURE_LA57)) {
 		printf("starting 5-level paging test.\n\n");
 		setup_5level_page_table();
-		ac_test_run(PT_LEVEL_PML5);
+		ac_test_run(PT_LEVEL_PML5, force_emulation);
 	}
 #endif
 
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index f324e32d..6194e0ea 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -143,6 +143,12 @@ file = access_test.flat
 arch = x86_64
 extra_params = -cpu max
 
+[access_fep]
+file = access_test.flat
+arch = x86_64
+extra_params = -cpu max -append force_emulation
+groups = nodefault
+
 [access-reduced-maxphyaddr]
 file = access_test.flat
 arch = x86_64
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 7bba8165..617b97b3 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -10460,7 +10460,7 @@ static void atomic_switch_overflow_msrs_test(void)
 
 static void vmx_pf_exception_test_guest(void)
 {
-	ac_test_run(PT_LEVEL_PML4);
+	ac_test_run(PT_LEVEL_PML4, false);
 }
 
 typedef void (*invalidate_tlb_t)(void *data);
-- 
2.40.0.348.gf938b09366-goog




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux