Verify that an LMSW in L2 that exits to L0 only leaves an L1-owned CR0.TS set, and that L2 reads an L1-owned CR0.NE from the read shadow. Assisted-by: LLM Signed-off-by: Mahmoud Abumandour --- .../kvm/x86/nested_exceptions_test.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c index f47432526e..e33d2e0db0 100644 --- a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c @@ -109,6 +109,40 @@ static void l1_svm_code(struct svm_test_data *svm) GUEST_DONE(); } +static u64 l2_cr0; + +static void l2_lmsw_test(void) +{ + /* Toggle CR0.MP, which KVM intercepts and L1 doesn't. */ + asm volatile("lmsw %w0" : : "r" (get_cr0() ^ X86_CR0_MP)); + l2_cr0 = get_cr0(); + asm volatile("fnop" ::: "memory"); + GUEST_FAIL("L2's FNOP didn't take #NM"); +} + +/* + * L1 owns CR0.TS (set) and CR0.NE, both with a read shadow of 0. L2's LMSW + * must leave both alone: FNOP takes #NM and L2 reads both as 0. + */ +static void vmx_run_l2_lmsw(void) +{ + u64 cr0 = vmread(GUEST_CR0); + + vmwrite(CR0_GUEST_HOST_MASK, X86_CR0_TS | X86_CR0_NE); + vmwrite(CR0_READ_SHADOW, cr0 & ~(X86_CR0_TS | X86_CR0_NE)); + vmwrite(GUEST_CR0, cr0 | X86_CR0_TS); + vmwrite(EXCEPTION_BITMAP, BIT(NM_VECTOR)); + vmwrite(GUEST_RIP, (u64)l2_lmsw_test); + vmresume(); + + GUEST_ASSERT_EQ(vmread(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI); + GUEST_ASSERT_EQ(vmread(VM_EXIT_INTR_INFO) & 0xff, NM_VECTOR); + GUEST_ASSERT_EQ(l2_cr0, (cr0 & ~(X86_CR0_TS | X86_CR0_NE)) ^ X86_CR0_MP); + + vmwrite(CR0_GUEST_HOST_MASK, 0); + vmwrite(GUEST_CR0, cr0); +} + static void vmx_run_l2(void *l2_code, int vector, u32 error_code) { vmwrite(GUEST_RIP, (u64)l2_code); @@ -149,6 +183,8 @@ static void l1_vmx_code(struct vmx_pages *vmx) vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS_DF); vmx_run_l2(l2_ss_injected_df_test, DF_VECTOR, DF_ERROR_CODE); + vmx_run_l2_lmsw(); + vmwrite(EXCEPTION_BITMAP, INTERCEPT_SS); vmx_run_l2(l2_ss_injected_tf_test, FAKE_TRIPLE_FAULT_VECTOR, 0); GUEST_ASSERT_EQ(vmread(VM_EXIT_REASON), EXIT_REASON_TRIPLE_FAULT); -- 2.25.1