kvm-unit-tests: nSVM: Test that MBZ bits in CR3 and CR4 are not set on vmrun of nested guests

According to section "Canonicalization and Consistency Checks" in APM vol. 2,
the following guest state is illegal:

	"Any MBZ bit of CR3 is set."
	"Any MBZ bit of CR4 is set."

Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
Message-Id: <1594168797-29444-4-git-send-email-krish.sadhukhan@oracle.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/x86/svm.h b/x86/svm.h
index 457ce3c..f8e7429 100644
--- a/x86/svm.h
+++ b/x86/svm.h
@@ -325,6 +325,11 @@
 #define SVM_CR0_SELECTIVE_MASK (X86_CR0_TS | X86_CR0_MP)
 
 #define	SVM_CR0_RESERVED_MASK			0xffffffff00000000U
+#define	SVM_CR3_LEGACY_RESERVED_MASK		0xfe7U
+#define	SVM_CR3_LEGACY_PAE_RESERVED_MASK	0x7U
+#define	SVM_CR3_LONG_RESERVED_MASK		0xfff0000000000fe7U
+#define	SVM_CR4_LEGACY_RESERVED_MASK		0xff88f000U
+#define	SVM_CR4_RESERVED_MASK			0xffffffffff88f000U
 #define	SVM_DR6_RESERVED_MASK			0xffffffffffff1ff0U
 #define	SVM_DR7_RESERVED_MASK			0xffffffff0000cc00U
 #define	SVM_EFER_RESERVED_MASK			0xffffffffffff0200U
diff --git a/x86/svm_tests.c b/x86/svm_tests.c
index d4d130f..af50252 100644
--- a/x86/svm_tests.c
+++ b/x86/svm_tests.c
@@ -1913,10 +1913,33 @@
         }								\
 }
 
-static void svm_guest_state_test(void)
-{
-	test_set_guest(basic_guest_main);
+#define SVM_TEST_CR_RESERVED_BITS(start, end, inc, cr, val, resv_mask)	\
+{									\
+	u64 tmp, mask;							\
+	int i;								\
+									\
+	for (i = start; i <= end; i = i + inc) {			\
+		mask = 1ull << i;					\
+		if (!(mask & resv_mask))				\
+			continue;					\
+		tmp = val | mask;					\
+		switch (cr) {						\
+		case 0:							\
+			vmcb->save.cr0 = tmp;				\
+			break;						\
+		case 3:							\
+			vmcb->save.cr3 = tmp;				\
+			break;						\
+		case 4:							\
+			vmcb->save.cr4 = tmp;				\
+		}							\
+		report(svm_vmrun() == SVM_EXIT_ERR, "Test CR%d %d:%d: %lx",\
+		    cr, end, start, tmp);				\
+	}								\
+}
 
+static void test_efer(void)
+{
 	/*
 	 * Un-setting EFER.SVME is illegal
 	 */
@@ -1930,6 +1953,21 @@
 	vmcb->save.efer = efer_saved;
 
 	/*
+	 * EFER MBZ bits: 63:16, 9
+	 */
+	efer_saved = vmcb->save.efer;
+
+	SVM_TEST_REG_RESERVED_BITS(8, 9, 1, "EFER", vmcb->save.efer,
+	    efer_saved, SVM_EFER_RESERVED_MASK);
+	SVM_TEST_REG_RESERVED_BITS(16, 63, 4, "EFER", vmcb->save.efer,
+	    efer_saved, SVM_EFER_RESERVED_MASK);
+
+	vmcb->save.efer = efer_saved;
+}
+
+static void test_cr0(void)
+{
+	/*
 	 * Un-setting CR0.CD and setting CR0.NW is illegal combination
 	 */
 	u64 cr0_saved = vmcb->save.cr0;
@@ -1938,17 +1976,21 @@
 	cr0 |= X86_CR0_CD;
 	cr0 &= ~X86_CR0_NW;
 	vmcb->save.cr0 = cr0;
-	report (svm_vmrun() == SVM_EXIT_VMMCALL, "CR0: %lx", cr0);
+	report (svm_vmrun() == SVM_EXIT_VMMCALL, "Test CR0 CD=1,NW=0: %lx",
+	    cr0);
 	cr0 |= X86_CR0_NW;
 	vmcb->save.cr0 = cr0;
-	report (svm_vmrun() == SVM_EXIT_VMMCALL, "CR0: %lx", cr0);
+	report (svm_vmrun() == SVM_EXIT_VMMCALL, "Test CR0 CD=1,NW=1: %lx",
+	    cr0);
 	cr0 &= ~X86_CR0_NW;
 	cr0 &= ~X86_CR0_CD;
 	vmcb->save.cr0 = cr0;
-	report (svm_vmrun() == SVM_EXIT_VMMCALL, "CR0: %lx", cr0);
+	report (svm_vmrun() == SVM_EXIT_VMMCALL, "Test CR0 CD=0,NW=0: %lx",
+	    cr0);
 	cr0 |= X86_CR0_NW;
 	vmcb->save.cr0 = cr0;
-	report (svm_vmrun() == SVM_EXIT_ERR, "CR0: %lx", cr0);
+	report (svm_vmrun() == SVM_EXIT_ERR, "Test CR0 CD=0,NW=1: %lx",
+	    cr0);
 	vmcb->save.cr0 = cr0_saved;
 
 	/*
@@ -1959,7 +2001,75 @@
 	SVM_TEST_REG_RESERVED_BITS(32, 63, 4, "CR0", vmcb->save.cr0, cr0_saved,
 	    SVM_CR0_RESERVED_MASK);
 	vmcb->save.cr0 = cr0_saved;
+}
 
+static void test_cr3(void)
+{
+	/*
+	 * CR3 MBZ bits based on different modes:
+	 *   [2:0]		    - legacy PAE
+	 *   [2:0], [11:5]	    - legacy non-PAE
+	 *   [2:0], [11:5], [63:52] - long mode
+	 */
+	u64 cr3_saved = vmcb->save.cr3;
+	u64 cr4_saved = vmcb->save.cr4;
+	u64 cr4 = cr4_saved;
+	u64 efer_saved = vmcb->save.efer;
+	u64 efer = efer_saved;
+
+	efer &= ~EFER_LME;
+	vmcb->save.efer = efer;
+	cr4 |= X86_CR4_PAE;
+	vmcb->save.cr4 = cr4;
+	SVM_TEST_CR_RESERVED_BITS(0, 2, 1, 3, cr3_saved,
+	    SVM_CR3_LEGACY_PAE_RESERVED_MASK);
+
+	cr4 = cr4_saved & ~X86_CR4_PAE;
+	vmcb->save.cr4 = cr4;
+	SVM_TEST_CR_RESERVED_BITS(0, 11, 1, 3, cr3_saved,
+	    SVM_CR3_LEGACY_RESERVED_MASK);
+
+	cr4 |= X86_CR4_PAE;
+	vmcb->save.cr4 = cr4;
+	efer |= EFER_LME;
+	vmcb->save.efer = efer;
+	SVM_TEST_CR_RESERVED_BITS(0, 63, 1, 3, cr3_saved,
+	    SVM_CR3_LONG_RESERVED_MASK);
+
+	vmcb->save.cr4 = cr4_saved;
+	vmcb->save.cr3 = cr3_saved;
+	vmcb->save.efer = efer_saved;
+}
+
+static void test_cr4(void)
+{
+	/*
+	 * CR4 MBZ bits based on different modes:
+	 *   [15:12], 17, 19, [31:22] - legacy mode
+	 *   [15:12], 17, 19, [63:22] - long mode
+	 */
+	u64 cr4_saved = vmcb->save.cr4;
+	u64 efer_saved = vmcb->save.efer;
+	u64 efer = efer_saved;
+
+	efer &= ~EFER_LME;
+	vmcb->save.efer = efer;
+	SVM_TEST_CR_RESERVED_BITS(12, 31, 1, 4, cr4_saved,
+	    SVM_CR4_LEGACY_RESERVED_MASK);
+
+	efer |= EFER_LME;
+	vmcb->save.efer = efer;
+	SVM_TEST_CR_RESERVED_BITS(12, 31, 1, 4, cr4_saved,
+	    SVM_CR4_RESERVED_MASK);
+	SVM_TEST_CR_RESERVED_BITS(32, 63, 4, 4, cr4_saved,
+	    SVM_CR4_RESERVED_MASK);
+
+	vmcb->save.cr4 = cr4_saved;
+	vmcb->save.efer = efer_saved;
+}
+
+static void test_dr(void)
+{
 	/*
 	 * DR6[63:32] and DR7[63:32] are MBZ
 	 */
@@ -1974,18 +2084,17 @@
 	    SVM_DR7_RESERVED_MASK);
 
 	vmcb->save.dr7 = dr_saved;
+}
 
-	/*
-	 * EFER MBZ bits: 63:16, 9
-	 */
-	efer_saved = vmcb->save.efer;
+static void svm_guest_state_test(void)
+{
+	test_set_guest(basic_guest_main);
 
-	SVM_TEST_REG_RESERVED_BITS(8, 9, 1, "EFER", vmcb->save.efer,
-	    efer_saved, SVM_EFER_RESERVED_MASK);
-	SVM_TEST_REG_RESERVED_BITS(16, 63, 4, "EFER", vmcb->save.efer,
-	    efer_saved, SVM_EFER_RESERVED_MASK);
-
-	vmcb->save.efer = efer_saved;
+	test_efer();
+	test_cr0();
+	test_cr3();
+	test_cr4();
+	test_dr();
 }
 
 struct svm_test svm_tests[] = {