diff mbox series

nVMX: Test 'Type' and 'S' portions of Access Rights field in guest segment registers

Message ID 20201110014556.14913-1-krish.sadhukhan@oracle.com (mailing list archive)
State New, archived
Headers show
Series nVMX: Test 'Type' and 'S' portions of Access Rights field in guest segment registers | expand

Commit Message

Krish Sadhukhan Nov. 10, 2020, 1:45 a.m. UTC
According to section "Checks on Guest Segment Registers" in Intel SDM vol 3C,
the following checks are performed on the Guest Segment Registers on vmentry
of nested guests:

    Access-rights field:
	— Bits 3:0 (Type):
	    - CS: The values allowed depend on the setting of the “unrestricted
	          guest” VM-execution control:
		    - If the control is 0, the Type must be 9, 11, 13, or 15
		      (accessed code segment).
		    - If the control is 1, the Type must be either 3 (read/write
		      accessed expand-up data segment) or one of 9, 11, 13, and
		      15 (accessed code segment).
	    - SS: If SS is usable, the Type must be 3 or 7 (read/write,
		  accessed data segment).
	    - DS, ES, FS, GS: The following checks apply if the register is
	      		      usable:
			- Bit 0 of the Type must be 1 (accessed).
			- If bit 3 of the Type is 1 (code segment), then bit 1
		  	  of the Type must be 1 (readable).
	— Bit 4 (S): If the register is CS or if the register is usable, S must
	  be 1.

Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
---
 x86/vmx_tests.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)
diff mbox series

Patch

diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index d2084ae..e7481bf 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -8177,6 +8177,170 @@  static void test_guest_segment_base_addr_fields(void)
 	vmcs_write(GUEST_AR_ES, ar_saved);
 }
 
+#define TEST_SEGMENT_AR(seg, name, saved_val, mask, test_val, xfail, msg)\
+{									\
+	u32 val = (saved_val & mask) | test_val;			\
+	vmcs_write(seg, val);						\
+	test_guest_state(msg, xfail, val, name);			\
+}
+
+#define TEST_SEGMENT_AR_TYPE_HELPER(seg, name)				\
+{									\
+	u32 ar_saved = vmcs_read(seg);					\
+									\
+	TEST_SEGMENT_AR(seg, name, (ar_saved | GUEST_SEG_UNUSABLE_MASK),\
+	    AR_TYPE_MASK1, 0x1, false, msg);				\
+	TEST_SEGMENT_AR(seg, name, (ar_saved | GUEST_SEG_UNUSABLE_MASK),\
+	    AR_TYPE_MASK1, 0x0, false, msg);				\
+	TEST_SEGMENT_AR(seg, name, (ar_saved & ~GUEST_SEG_UNUSABLE_MASK),\
+	    AR_TYPE_MASK1, 0x1, false, msg);				\
+	TEST_SEGMENT_AR(seg, name, (ar_saved & ~GUEST_SEG_UNUSABLE_MASK),\
+	    AR_TYPE_MASK1, 0x0, true, msg);				\
+	TEST_SEGMENT_AR(seg, name, (ar_saved | GUEST_SEG_UNUSABLE_MASK),\
+	    AR_TYPE_MASK2, 0x8, false, msg);				\
+	TEST_SEGMENT_AR(seg, name, (ar_saved | GUEST_SEG_UNUSABLE_MASK),\
+	    AR_TYPE_MASK2, 0x2, false, msg);				\
+	TEST_SEGMENT_AR(seg, name, (ar_saved & ~GUEST_SEG_UNUSABLE_MASK),\
+	    AR_TYPE_MASK2, 0x9, true, msg);				\
+	TEST_SEGMENT_AR(seg, name, (ar_saved & ~GUEST_SEG_UNUSABLE_MASK),\
+	    AR_TYPE_MASK2, 0x3, false, msg);				\
+									\
+	vmcs_write(seg, ar_saved);					\
+}
+
+#define TEST_SEGMENT_AR_S_HELPER(seg, name)				\
+{									\
+	u32 ar_saved = vmcs_read(seg);					\
+	char msg[] = "Access_Rights[4]";				\
+									\
+	if (seg == GUEST_AR_CS) {					\
+		TEST_SEGMENT_AR(seg, name, ar_saved, AR_S_MASK, 0x10,	\
+		    false, msg);					\
+		TEST_SEGMENT_AR(seg, name, ar_saved, AR_S_MASK, ~0x10,	\
+		    true, msg);						\
+	} else {							\
+		TEST_SEGMENT_AR(seg, name, (ar_saved |			\
+		    GUEST_SEG_UNUSABLE_MASK), AR_S_MASK, 0x10, false, msg);\
+		TEST_SEGMENT_AR(seg, name, (ar_saved |			\
+		    GUEST_SEG_UNUSABLE_MASK), AR_S_MASK, 0x0, false, msg);\
+		TEST_SEGMENT_AR(seg, name, (ar_saved &			\
+		    ~GUEST_SEG_UNUSABLE_MASK), AR_S_MASK, 0x10, false, msg);\
+		TEST_SEGMENT_AR(seg, name, (ar_saved &			\
+		    ~GUEST_SEG_UNUSABLE_MASK), AR_S_MASK, 0x0, true, msg);\
+	}								\
+	vmcs_write(seg, ar_saved);					\
+}
+
+/*
+ * The following checks are done on the Access Rights field of the Guest
+ * Segment Registers:
+ *
+ * Bits 3:0 (Type):
+ *   CS:
+ *     - If the “unrestricted guest" VM-execution control is 0, value must be
+ *       9, 11, 13, or 15.
+ *     - If the “unrestricted guest" VM-execution control is 1, value must be
+ *       either 3 or one of 9, 11, 13, and 15.
+ *   SS:
+ *     - If SS is usable, the Type must be 3 or 7.
+ *   DS, ES, FS, GS:
+ *     - If the register is usable, bit 0 of the Type must be 1 and if bit 3
+ *	 of the Type is 1, then bit 1 of the Type must be 1.
+ *
+ * Bit 4 (S): If the register is CS or if the register is usable, S must be 1.
+ *
+ *  [Intel SDM]
+ */
+static void test_guest_segment_ar_fields(void)
+{
+	/*
+	 * Type [3:0] of CS
+	 */
+	u32 ar_saved = vmcs_read(GUEST_AR_CS);
+	char msg[] = "Access_Rights[3:0]";
+
+#define	AR_TYPE_MASK	~0xf
+#define	AR_TYPE_MASK1	~0x1
+#define	AR_TYPE_MASK2	~0xa
+
+	TEST_SEGMENT_AR(GUEST_AR_CS, "GUEST_AR_CS", ar_saved,
+	    AR_TYPE_MASK, 0x9, false, msg);
+	TEST_SEGMENT_AR(GUEST_AR_CS, "GUEST_AR_CS", ar_saved,
+	    AR_TYPE_MASK, 0xb, false, msg);
+	TEST_SEGMENT_AR(GUEST_AR_CS, "GUEST_AR_CS", ar_saved,
+	    AR_TYPE_MASK, 0xd, false, msg);
+	TEST_SEGMENT_AR(GUEST_AR_CS, "GUEST_AR_CS", ar_saved,
+	    AR_TYPE_MASK, 0xf, false, msg);
+
+	/* Turn off "unrestricted guest" vm-execution control */
+	u32 cpu_ctrl0_saved = vmcs_read(CPU_EXEC_CTRL0);
+	u32 cpu_ctrl1_saved = vmcs_read(CPU_EXEC_CTRL1);
+	if (cpu_ctrl1_saved | CPU_URG)
+		vmcs_write(CPU_EXEC_CTRL1, cpu_ctrl1_saved & ~CPU_URG);
+
+	TEST_SEGMENT_AR(GUEST_AR_CS, "GUEST_AR_CS", ar_saved,
+	    AR_TYPE_MASK, 0x3, false, msg);
+
+	/* Turn on "unrestricted guest" vm-execution control */
+	vmcs_write(CPU_EXEC_CTRL0, cpu_ctrl0_saved | CPU_SECONDARY);
+	vmcs_write(CPU_EXEC_CTRL1, cpu_ctrl1_saved | CPU_URG);
+	/* EPT and EPTP must be setup when "unrestricted guest" is on */
+	setup_ept(false);
+
+	TEST_SEGMENT_AR(GUEST_AR_CS, "GUEST_AR_CS", ar_saved,
+	    AR_TYPE_MASK, 0x3, false, msg);
+
+	vmcs_write(GUEST_AR_CS, ar_saved);
+	vmcs_write(CPU_EXEC_CTRL0, cpu_ctrl0_saved);
+	vmcs_write(CPU_EXEC_CTRL1, cpu_ctrl1_saved);
+
+	/*
+	 * Type [3:0] of SS
+	 */
+	ar_saved = vmcs_read(GUEST_AR_SS);
+
+	TEST_SEGMENT_AR(GUEST_AR_SS, "GUEST_AR_SS", (ar_saved |
+	    GUEST_SEG_UNUSABLE_MASK), AR_TYPE_MASK, 0x3, false, msg);
+
+	TEST_SEGMENT_AR(GUEST_AR_SS, "GUEST_AR_SS", (ar_saved |
+	    GUEST_SEG_UNUSABLE_MASK), AR_TYPE_MASK, 0x7, false, msg);
+
+	TEST_SEGMENT_AR(GUEST_AR_SS, "GUEST_AR_SS", (ar_saved |
+	    GUEST_SEG_UNUSABLE_MASK), AR_TYPE_MASK, 0x0, false, msg);
+
+	TEST_SEGMENT_AR(GUEST_AR_SS, "GUEST_AR_SS", (ar_saved &
+	    ~GUEST_SEG_UNUSABLE_MASK), AR_TYPE_MASK, 0x3, false, msg);
+
+	TEST_SEGMENT_AR(GUEST_AR_SS, "GUEST_AR_SS", (ar_saved &
+	    ~GUEST_SEG_UNUSABLE_MASK), AR_TYPE_MASK, 0x7, false, msg);
+
+	TEST_SEGMENT_AR(GUEST_AR_SS, "GUEST_AR_SS", (ar_saved &
+	    ~GUEST_SEG_UNUSABLE_MASK), AR_TYPE_MASK, 0x0, true, msg);
+
+	vmcs_write(GUEST_AR_SS, ar_saved);
+
+	/*
+	 * Type [3:0] of DS, ES, FS and GS
+	 */
+	TEST_SEGMENT_AR_TYPE_HELPER(GUEST_AR_DS, "GUEST_AR_DS");
+	TEST_SEGMENT_AR_TYPE_HELPER(GUEST_AR_ES, "GUEST_AR_ES");
+	TEST_SEGMENT_AR_TYPE_HELPER(GUEST_AR_FS, "GUEST_AR_FS");
+	TEST_SEGMENT_AR_TYPE_HELPER(GUEST_AR_GS, "GUEST_AR_GS");
+
+	/*
+	 * S [4] of CS, SS, DS, ES, FS and GS
+	 */
+#define	AR_S_MASK	~0x10
+
+	TEST_SEGMENT_AR_S_HELPER(GUEST_AR_CS, "GUEST_AR_CS");
+	TEST_SEGMENT_AR_S_HELPER(GUEST_AR_CS, "GUEST_AR_CS");
+	TEST_SEGMENT_AR_S_HELPER(GUEST_AR_SS, "GUEST_AR_SS");
+	TEST_SEGMENT_AR_S_HELPER(GUEST_AR_DS, "GUEST_AR_DS");
+	TEST_SEGMENT_AR_S_HELPER(GUEST_AR_ES, "GUEST_AR_ES");
+	TEST_SEGMENT_AR_S_HELPER(GUEST_AR_FS, "GUEST_AR_FS");
+	TEST_SEGMENT_AR_S_HELPER(GUEST_AR_GS, "GUEST_AR_GS");
+}
+
 /*
  * Check that the virtual CPU checks the VMX Guest State Area as
  * documented in the Intel SDM.
@@ -8201,6 +8365,7 @@  static void vmx_guest_state_area_test(void)
 
 	test_guest_segment_sel_fields();
 	test_guest_segment_base_addr_fields();
+	test_guest_segment_ar_fields();
 
 	test_canonical(GUEST_BASE_GDTR, "GUEST_BASE_GDTR", false);
 	test_canonical(GUEST_BASE_IDTR, "GUEST_BASE_IDTR", false);