[PATCH kvm-unit-tests 3/3] nSVM: Test G_PAT fields

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

 



According to section "Nested Paging and VMRUN/#VMEXIT" in APM vol 2, the
following guest state is illegal:

    "Any G_PAT.PA field has an unsupported type encoding or any
     reserved field in G_PAT has a nonzero value."

Signed-off-by: Krish Sadhukhan <krish.sadhukhan@xxxxxxxxxx>
---
 lib/x86/asm/page.h | 11 ++++++++
 x86/svm.c          |  8 ++++++
 x86/svm.h          |  1 +
 x86/svm_tests.c    | 66 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+)

diff --git a/lib/x86/asm/page.h b/lib/x86/asm/page.h
index fc14160..9ff9329 100644
--- a/lib/x86/asm/page.h
+++ b/lib/x86/asm/page.h
@@ -57,5 +57,16 @@ typedef unsigned long pgd_t;
 #define PGDIR_BITS(lvl)        (((lvl) - 1) * PGDIR_WIDTH + PAGE_SHIFT)
 #define PGDIR_OFFSET(va, lvl)  (((va) >> PGDIR_BITS(lvl)) & PGDIR_MASK)
 
+#ifdef __x86_64__
+enum {
+	PAT_UC = 0,             /* uncached */
+	PAT_WC = 1,             /* Write combining */
+	PAT_WT = 4,             /* Write Through */
+	PAT_WP = 5,             /* Write Protected */
+	PAT_WB = 6,             /* Write Back (default) */
+	PAT_UC_MINUS = 7,       /* UC, but can be overridden by MTRR */
+};
+#endif
+
 #endif /* !__ASSEMBLY__ */
 #endif
diff --git a/x86/svm.c b/x86/svm.c
index d03f011..c949003 100644
--- a/x86/svm.c
+++ b/x86/svm.c
@@ -94,6 +94,14 @@ bool pat_supported(void)
 	return this_cpu_has(X86_FEATURE_PAT);
 }
 
+bool pat_valid(u64 data)
+{
+	if (data & 0xF8F8F8F8F8F8F8F8ull)
+		return false;
+	/* 0, 1, 4, 5, 6, 7 are valid values.  */
+	return (data | ((data & 0x0202020202020202ull) << 1)) == data;
+}
+
 int get_test_stage(struct svm_test *test)
 {
 	barrier();
diff --git a/x86/svm.h b/x86/svm.h
index d4db4c1..d4c6e1c 100644
--- a/x86/svm.h
+++ b/x86/svm.h
@@ -410,6 +410,7 @@ void default_prepare_gif_clear(struct svm_test *test);
 bool default_finished(struct svm_test *test);
 bool npt_supported(void);
 bool pat_supported(void);
+bool pat_valid(u64 data);
 int get_test_stage(struct svm_test *test);
 void set_test_stage(struct svm_test *test, int s);
 void inc_test_stage(struct svm_test *test);
diff --git a/x86/svm_tests.c b/x86/svm_tests.c
index 8ad6122..4536362 100644
--- a/x86/svm_tests.c
+++ b/x86/svm_tests.c
@@ -2547,6 +2547,71 @@ static void guest_rflags_test_db_handler(struct ex_regs *r)
 	r->rflags &= ~X86_EFLAGS_TF;
 }
 
+#define G_PAT_VMRUN(nested_ctl, val, i, field_val)		\
+{								\
+	u32 ret, xret;						\
+								\
+	if (nested_ctl) {					\
+		if (pat_valid(val))				\
+			xret = SVM_EXIT_VMMCALL;		\
+		else						\
+			xret = SVM_EXIT_ERR;			\
+	} else {						\
+		xret = SVM_EXIT_VMMCALL;			\
+	}							\
+	vmcb->save.g_pat = val;					\
+	ret = svm_vmrun();					\
+	report (ret == xret, "Test G_PAT[%d]: %lx, wanted "	\
+           "exit 0x%x, got 0x%x", i, field_val, xret, ret);	\
+}
+
+#define TEST_G_PAT(g_pat_saved, nested_ctl)			\
+{								\
+	int i, field_shift;					\
+	u64 g_pat_mask, field_val, val, j;			\
+								\
+	for (i = 0; i < 8; i++) {				\
+		/*						\
+		 * Test each PAT field's encodings and		\
+		 * reserved values				\
+		 */						\
+		field_shift = i * 8;				\
+		g_pat_mask = ~(0x7ul << field_shift) &		\
+				g_pat_saved;			\
+		for (j = PAT_UC; j <= PAT_UC_MINUS; j++) {	\
+			val = g_pat_mask | j << field_shift;	\
+			G_PAT_VMRUN(nested_ctl, val, i, j);	\
+		}						\
+		field_shift = i * 8 + 3;			\
+		g_pat_mask = ~(0x1ful << field_shift) &		\
+				g_pat_saved;			\
+		for (j = 0; j < 5; j++) {			\
+			field_val = 1ul << j;			\
+			val = g_pat_mask |			\
+			      field_val << field_shift;		\
+			G_PAT_VMRUN(nested_ctl, val, i,		\
+				    field_val);			\
+		}						\
+	}							\
+}
+
+static void test_g_pat(void)
+{
+	u64 g_pat_saved = vmcb->save.g_pat;
+	u64 nested_ctl_saved = vmcb->control.nested_ctl;
+
+	if (!npt_supported() || !pat_supported()) {
+		report_skip("NPT or PAT or both not supported");
+		return;
+	}
+
+	TEST_G_PAT(g_pat_saved, (vmcb->control.nested_ctl = 0));
+	TEST_G_PAT(g_pat_saved, (vmcb->control.nested_ctl = 1));
+
+	vmcb->control.nested_ctl = nested_ctl_saved;
+	vmcb->save.g_pat = g_pat_saved;
+}
+
 static void svm_guest_state_test(void)
 {
 	test_set_guest(basic_guest_main);
@@ -2557,6 +2622,7 @@ static void svm_guest_state_test(void)
 	test_dr();
 	test_msrpm_iopm_bitmap_addrs();
 	test_canonicalization();
+	test_g_pat();
 }
 
 extern void guest_rflags_test_guest(struct svm_test *test);
-- 
2.27.0




[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