If L1 allows L2 to modify CR4.OSXSAVE, then L0 kvm recalculates the guest's CPUID.01H:ECX.OSXSAVE bit when the L2 guest changes CR4.OSXSAVE via MOV-to-CR4. Verify that kvm also recalculates this CPUID bit when loading L1's CR4 from the save.cr4 field of the hsave area. Signed-off-by: Jim Mattson <jmattson@xxxxxxxxxx> Reviewed-by: Ricardo Koller <ricarkol@xxxxxxxxxx> Reviewed-by: Peter Shier <pshier@xxxxxxxxxx> --- x86/svm_tests.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/x86/svm_tests.c b/x86/svm_tests.c index 3b0424a..e2455c8 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -1917,6 +1917,40 @@ static bool reg_corruption_check(struct svm_test *test) * v2 tests */ +/* + * Ensure that kvm recalculates the L1 guest's CPUID.01H:ECX.OSXSAVE + * after VM-exit from an L2 guest that sets CR4.OSXSAVE to a different + * value than in L1. + */ + +static void svm_cr4_osxsave_test_guest(struct svm_test *test) +{ + write_cr4(read_cr4() & ~X86_CR4_OSXSAVE); +} + +static void svm_cr4_osxsave_test(void) +{ + if (!this_cpu_has(X86_FEATURE_XSAVE)) { + report_skip("XSAVE not detected"); + return; + } + + if (!(read_cr4() & X86_CR4_OSXSAVE)) { + unsigned long cr4 = read_cr4() | X86_CR4_OSXSAVE; + + write_cr4(cr4); + vmcb->save.cr4 = cr4; + } + + report(cpuid_osxsave(), "CPUID.01H:ECX.XSAVE set before VMRUN"); + + test_set_guest(svm_cr4_osxsave_test_guest); + report(svm_vmrun() == SVM_EXIT_VMMCALL, + "svm_cr4_osxsave_test_guest finished with VMMCALL"); + + report(cpuid_osxsave(), "CPUID.01H:ECX.XSAVE set after VMRUN"); +} + static void basic_guest_main(struct svm_test *test) { } @@ -2301,6 +2335,7 @@ struct svm_test svm_tests[] = { { "reg_corruption", default_supported, reg_corruption_prepare, default_prepare_gif_clear, reg_corruption_test, reg_corruption_finished, reg_corruption_check }, + TEST(svm_cr4_osxsave_test), TEST(svm_guest_state_test), { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; -- 2.29.1.341.ge80a0c044ae-goog