Add tests to verify that the PAT MSR is correctly handled during VM-Exit. The following behaviors are verified: 1. The PAT MSR is saved to VMCS during VM-Exit iff the "Save PAT" VM-Exit control is set. 2. The PAT MSR is not saved to VMCS during VM-Exit iff the "Save PAT" VM-Exit control is cleared. 3. The PAT MSR is loaded with the value from VMCS host PAT MSR field iff the "Load PAT" VM-Exit control is set. 4. The PAT MSR is retained iff the "Load PAT" VM-Exit control is cleared. Signed-off-by: Xin Li --- x86/vmx_tests.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index daa1504c..460ed76e 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -12320,6 +12320,124 @@ test_again: test_set_guest_finished(); } +static void vmx_exit_control_pat_test_guest(void) +{ + wrmsr(MSR_IA32_CR_PAT, 0x0506); + + vmcall(); +} + +static void vmx_exit_control_pat_test(void) +{ + bool entry_not_load_pat_tested = false; + u64 host_pat = rdmsr(MSR_IA32_CR_PAT); + + if (!(ctrl_exit_rev.clr & EXI_SAVE_PAT)) { + report_skip("Save PAT exit control is not available"); + return; + } + + if (!(ctrl_exit_rev.clr & EXI_LOAD_PAT)) { + report_skip("Load PAT exit control is not available"); + return; + } + + if (!(ctrl_enter_rev.clr & ENT_LOAD_PAT)) { + report_skip("Load PAT entry control is not available"); + return; + } + + /* Allow the guest to read the PAT MSR directly */ + msr_bmp_init(); + + vmcs_set_bits(ENT_CONTROLS, ENT_LOAD_PAT); + vmcs_write(HOST_PAT, host_pat); + +test_again: + /* + * Case 1: + * + * The PAT MSR is not saved to VMCS during VM-Exit if the "Save PAT" + * VM-Exit control is cleared. + * + * The PAT MSR is retained if the "Load PAT" VM-Exit control is cleared. + */ + vmcs_clear_bits(EXI_CONTROLS, EXI_SAVE_PAT | EXI_LOAD_PAT); + if (entry_not_load_pat_tested) + test_override_guest(vmx_exit_control_pat_test_guest); + else + test_set_guest(vmx_exit_control_pat_test_guest); + vmcs_write(GUEST_PAT, 0x0606); + + enter_guest(); + report(rdmsr(MSR_IA32_CR_PAT) == 0x0506, "The PAT MSR is retained at VM Exit"); + report(vmcs_read(GUEST_PAT) == 0x0606, "VMCS guest PAT is not saved at VM Exit"); + + wrmsr(MSR_IA32_CR_PAT, host_pat); + + /* + * Case 2: + * + * The PAT MSR is not saved to VMCS during VM-Exit if the "Save PAT" + * VM-Exit control is cleared. + * + * The PAT MSR is loaded with the value from VMCS host PAT MSR field + * if the "Load PAT" VM-Exit control is set. + */ + vmcs_clear_bits(EXI_CONTROLS, EXI_SAVE_PAT); + vmcs_set_bits(EXI_CONTROLS, EXI_LOAD_PAT); + test_override_guest(vmx_exit_control_pat_test_guest); + vmcs_write(GUEST_PAT, 0x0606); + + enter_guest(); + report(rdmsr(MSR_IA32_CR_PAT) == host_pat, "VMCS host PAT is loaded at VM Exit"); + report(vmcs_read(GUEST_PAT) == 0x0606, "VMCS guest PAT is not saved at VM Exit"); + + /* + * Case 3: + * + * The PAT MSR is saved to VMCS during VM-Exit if the "Save PAT" VM-Exit + * control is set. + * + * The PAT MSR is retained if the "Load PAT" VM-Exit control is cleared. + */ + vmcs_set_bits(EXI_CONTROLS, EXI_SAVE_PAT); + vmcs_clear_bits(EXI_CONTROLS, EXI_LOAD_PAT); + test_override_guest(vmx_exit_control_pat_test_guest); + vmcs_write(GUEST_PAT, 0x0606); + + enter_guest(); + report(rdmsr(MSR_IA32_CR_PAT) == 0x0506, "The PAT MSR is retained at VM Exit"); + report(vmcs_read(GUEST_PAT) == 0x0506, "VMCS guest PAT is saved at VM Exit"); + + wrmsr(MSR_IA32_CR_PAT, host_pat); + + /* + * Case 4: + * + * The PAT MSR is saved to VMCS during VM-Exit if the "Save PAT" VM-Exit + * control is set. + * + * The PAT MSR is loaded with the value from VMCS host PAT MSR field if + * the "Load PAT" VM-Exit control is set. + */ + vmcs_set_bits(EXI_CONTROLS, EXI_SAVE_PAT | EXI_LOAD_PAT); + test_override_guest(vmx_exit_control_pat_test_guest); + vmcs_write(GUEST_PAT, 0x0606); + + enter_guest(); + report(rdmsr(MSR_IA32_CR_PAT) == host_pat, "VMCS host PAT is loaded at VM Exit"); + report(vmcs_read(GUEST_PAT) == 0x0506, "VMCS guest PAT is saved at VM Exit"); + + if (!entry_not_load_pat_tested) { + entry_not_load_pat_tested = true; + vmcs_clear_bits(ENT_CONTROLS, ENT_LOAD_PAT); + goto test_again; + } + + test_set_guest_finished(); +} + #define TEST(name) { #name, .v2 = name } /* name/init/guest_main/exit_handler/vmfail_handler */ @@ -12440,5 +12558,6 @@ struct vmx_test vmx_tests[] = { TEST(vmx_cet_test), TEST(vmx_exit_control_fred_test), TEST(vmx_exit_control_efer_test), + TEST(vmx_exit_control_pat_test), { NULL, NULL, NULL, NULL }, }; -- 2.54.0