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@xxxxxxxxxx> --- x86/vmx_tests.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) 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); -- 2.18.4