From nobody Sun Feb 8 19:40:02 2026 Received: from out-179.mta0.migadu.com (out-179.mta0.migadu.com [91.218.175.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0AF634321D for ; Mon, 10 Nov 2025 23:27:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762817247; cv=none; b=GoPNOaS+El6C6zizmtk02sdJNVq+UyHGSuxAZ9R7o+z6FiHZ+2xgIfrTgiN4zqmRSIq3SV95rI+yMfvmieYUq7KXj7QisR8uE6TZl9yET8DZnziC+OZAyGy0mreNhI3mJysnyGQ8v0u1NkuCxsCpSFDOlOvpuY+ubmFHEHOq5sI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762817247; c=relaxed/simple; bh=MyCaylPcd6jL0fIRkiI4QdgSLFfZ2xzQS4bB56Mbxgc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cd3ds8Oye7f/EPotHrBq31PMpevze2Ho2qdLhNgx1D1KA8zI4bpqAHafr7zHK7oACjS6K8dPzo+NxX1CPlSXAIZF/FjpeTzySBB60u7QuLK2uVAygWzo46sgb2JMAKpUHbif3Q33PVsF1LXvIFA2y/qqESY9/P7gkkZXYV0zJ2k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=PcjpC3Rl; arc=none smtp.client-ip=91.218.175.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="PcjpC3Rl" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1762817241; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AJL9VPq8VyFGZh9HkMtyxbYE/+SCo1Ep+KzagzKZdJ8=; b=PcjpC3RlbL4DgCATn6T1Bu68WMVT3+Uk74PwQclGz/QBdPSthJ7VrpGFs5AUwEsYK+4iQj xT42a21N2CgSfq7cqKJty6ps2qwUgSxOk2yDkwAa6iiVxrK++jqZMNWOycLy40bKmLEiQc M+9xfGGc69VPQjWdvl8kYBiT1m1/Bhg= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , Kevin Cheng , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 12/14] x86/svm: Cleanup LBRV tests Date: Mon, 10 Nov 2025 23:26:40 +0000 Message-ID: <20251110232642.633672-13-yosry.ahmed@linux.dev> In-Reply-To: <20251110232642.633672-1-yosry.ahmed@linux.dev> References: <20251110232642.633672-1-yosry.ahmed@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" In LBRV tests, failures in the guest trigger a #UD and do not convey useful debugging info (i.e. expected and actual values of MSRs). Also, a lot of macros are used to perform branch checks, obscuring the tests to an extent. Instead, add a helper to read the branch IPs, and remove the check macros. Consistently use TEST_EXPECT_EQ() in both virtual host and guest code, instead of a mix of report(), TEST_EXPECT_EQ(), and #UD. Opportunisitcally slightly reorder test checks to improve semantics, and replace the report(true, ..) calls that document the test with comments. Signed-off-by: Yosry Ahmed Reviewed-by: Shivansh Dhiman Tested-by: Shivansh Dhiman --- x86/svm_tests.c | 138 +++++++++++++++++++++++------------------------- 1 file changed, 65 insertions(+), 73 deletions(-) diff --git a/x86/svm_tests.c b/x86/svm_tests.c index 40e9e7e344ed8..33c92b17c87db 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -3006,34 +3006,17 @@ static void svm_no_nm_test(void) "fnop with CR0.TS and CR0.EM unset no #NM exception"); } =20 -static u64 amd_get_lbr_rip(u32 msr) +/* These functions have to be inlined to avoid affecting LBR registers */ +static __always_inline u64 amd_get_lbr_rip(u32 msr) { return rdmsr(msr) & ~AMD_LBR_RECORD_MISPREDICT; } =20 -#define HOST_CHECK_LBR(from_expected, to_expected) \ -do { \ - TEST_EXPECT_EQ((u64)from_expected, amd_get_lbr_rip(MSR_IA32_LASTBRANCHFRO= MIP)); \ - TEST_EXPECT_EQ((u64)to_expected, amd_get_lbr_rip(MSR_IA32_LASTBRANCHTOIP)= ); \ -} while (0) - -/* - * FIXME: Do something other than generate an exception to communicate fai= lure. - * Debugging without expected vs. actual is an absolute nightmare. - */ -#define GUEST_CHECK_LBR(from_expected, to_expected) \ -do { \ - if ((u64)(from_expected) !=3D amd_get_lbr_rip(MSR_IA32_LASTBRANCHFROMIP))= \ - asm volatile("ud2"); \ - if ((u64)(to_expected) !=3D amd_get_lbr_rip(MSR_IA32_LASTBRANCHTOIP)) \ - asm volatile("ud2"); \ -} while (0) - -#define REPORT_GUEST_LBR_ERROR(vmcb) \ - report(false, "LBR guest test failed. Exit reason 0x%x, RIP =3D %lx, fro= m =3D %lx, to =3D %lx, ex from =3D %lx, ex to =3D %lx", \ - vmcb->control.exit_code, vmcb->save.rip, \ - vmcb->save.br_from, vmcb->save.br_to, \ - vmcb->save.last_excp_from, vmcb->save.last_excp_to) +static __always_inline void get_lbr_ips(u64 *from, u64 *to) +{ + *from =3D amd_get_lbr_rip(MSR_IA32_LASTBRANCHFROMIP); + *to =3D amd_get_lbr_rip(MSR_IA32_LASTBRANCHTOIP); +} =20 #define DO_BRANCH(branch_name) \ asm volatile ( \ @@ -3058,55 +3041,64 @@ u64 dbgctl; =20 static void svm_lbrv_test_guest1(void) { + u64 from_ip, to_ip; + /* * This guest expects the LBR to be already enabled when it starts, * it does a branch, and then disables the LBR and then checks. */ + dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); + TEST_EXPECT_EQ(dbgctl, DEBUGCTLMSR_LBR); =20 DO_BRANCH(guest_branch0); =20 - dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); + /* Disable LBR before the checks to avoid changing the last branch */ wrmsr(MSR_IA32_DEBUGCTLMSR, 0); + dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); + TEST_EXPECT_EQ(dbgctl, 0); =20 - if (dbgctl !=3D DEBUGCTLMSR_LBR) - asm volatile("ud2\n"); - if (rdmsr(MSR_IA32_DEBUGCTLMSR) !=3D 0) - asm volatile("ud2\n"); + get_lbr_ips(&from_ip, &to_ip); + TEST_EXPECT_EQ((u64)&guest_branch0_from, from_ip); + TEST_EXPECT_EQ((u64)&guest_branch0_to, to_ip); =20 - GUEST_CHECK_LBR(&guest_branch0_from, &guest_branch0_to); asm volatile ("vmmcall\n"); } =20 static void svm_lbrv_test_guest2(void) { + u64 from_ip, to_ip; + /* * This guest expects the LBR to be disabled when it starts, * enables it, does a branch, disables it and then checks. */ - - DO_BRANCH(guest_branch1); dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); + TEST_EXPECT_EQ(dbgctl, 0); =20 - if (dbgctl !=3D 0) - asm volatile("ud2\n"); + DO_BRANCH(guest_branch1); =20 - GUEST_CHECK_LBR(&host_branch2_from, &host_branch2_to); + get_lbr_ips(&from_ip, &to_ip); + TEST_EXPECT_EQ((u64)&host_branch2_from, from_ip); + TEST_EXPECT_EQ((u64)&host_branch2_to, to_ip); =20 wrmsr(MSR_IA32_DEBUGCTLMSR, DEBUGCTLMSR_LBR); dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); + TEST_EXPECT_EQ(dbgctl, DEBUGCTLMSR_LBR); + DO_BRANCH(guest_branch2); wrmsr(MSR_IA32_DEBUGCTLMSR, 0); =20 - if (dbgctl !=3D DEBUGCTLMSR_LBR) - asm volatile("ud2\n"); - GUEST_CHECK_LBR(&guest_branch2_from, &guest_branch2_to); + get_lbr_ips(&from_ip, &to_ip); + TEST_EXPECT_EQ((u64)&guest_branch2_from, from_ip); + TEST_EXPECT_EQ((u64)&guest_branch2_to, to_ip); =20 asm volatile ("vmmcall\n"); } =20 static void svm_lbrv_test0(void) { - report(true, "Basic LBR test"); + u64 from_ip, to_ip; + wrmsr(MSR_IA32_DEBUGCTLMSR, DEBUGCTLMSR_LBR); DO_BRANCH(host_branch0); dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); @@ -3116,12 +3108,15 @@ static void svm_lbrv_test0(void) dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); TEST_EXPECT_EQ(dbgctl, 0); =20 - HOST_CHECK_LBR(&host_branch0_from, &host_branch0_to); + get_lbr_ips(&from_ip, &to_ip); + TEST_EXPECT_EQ((u64)&host_branch0_from, from_ip); + TEST_EXPECT_EQ((u64)&host_branch0_to, to_ip); } =20 +/* Test that without LBRV enabled, guest LBR state does 'leak' to the host= (1) */ static void svm_lbrv_test1(void) { - report(true, "Test that without LBRV enabled, guest LBR state does 'leak'= to the host(1)"); + u64 from_ip, to_ip; =20 svm_setup_vmrun((u64)svm_lbrv_test_guest1); vmcb->control.virt_ext =3D 0; @@ -3130,19 +3125,19 @@ static void svm_lbrv_test1(void) DO_BRANCH(host_branch1); SVM_BARE_VMRUN; dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); + TEST_EXPECT_EQ(dbgctl, 0); =20 - if (vmcb->control.exit_code !=3D SVM_EXIT_VMMCALL) { - REPORT_GUEST_LBR_ERROR(vmcb); - return; - } + TEST_EXPECT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL); =20 - TEST_EXPECT_EQ(dbgctl, 0); - HOST_CHECK_LBR(&guest_branch0_from, &guest_branch0_to); + get_lbr_ips(&from_ip, &to_ip); + TEST_EXPECT_EQ((u64)&guest_branch0_from, from_ip); + TEST_EXPECT_EQ((u64)&guest_branch0_to, to_ip); } =20 +/* Test that without LBRV enabled, guest LBR state does 'leak' to the host= (2) */ static void svm_lbrv_test2(void) { - report(true, "Test that without LBRV enabled, guest LBR state does 'leak'= to the host(2)"); + u64 from_ip, to_ip; =20 svm_setup_vmrun((u64)svm_lbrv_test_guest2); vmcb->control.virt_ext =3D 0; @@ -3152,25 +3147,25 @@ static void svm_lbrv_test2(void) wrmsr(MSR_IA32_DEBUGCTLMSR, 0); SVM_BARE_VMRUN; dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); - wrmsr(MSR_IA32_DEBUGCTLMSR, 0); + TEST_EXPECT_EQ(dbgctl, 0); =20 - if (vmcb->control.exit_code !=3D SVM_EXIT_VMMCALL) { - REPORT_GUEST_LBR_ERROR(vmcb); - return; - } + TEST_EXPECT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL); =20 - TEST_EXPECT_EQ(dbgctl, 0); - HOST_CHECK_LBR(&guest_branch2_from, &guest_branch2_to); + get_lbr_ips(&from_ip, &to_ip); + TEST_EXPECT_EQ((u64)&guest_branch2_from, from_ip); + TEST_EXPECT_EQ((u64)&guest_branch2_to, to_ip); } =20 +/* Test that with LBRV enabled, guest LBR state doesn't leak (1) */ static void svm_lbrv_nested_test1(void) { + u64 from_ip, to_ip; + if (!lbrv_supported()) { report_skip("LBRV not supported in the guest"); return; } =20 - report(true, "Test that with LBRV enabled, guest LBR state doesn't leak (= 1)"); svm_setup_vmrun((u64)svm_lbrv_test_guest1); vmcb->control.virt_ext =3D LBR_CTL_ENABLE_MASK; vmcb->save.dbgctl =3D DEBUGCTLMSR_LBR; @@ -3181,28 +3176,26 @@ static void svm_lbrv_nested_test1(void) dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); wrmsr(MSR_IA32_DEBUGCTLMSR, 0); =20 - if (vmcb->control.exit_code !=3D SVM_EXIT_VMMCALL) { - REPORT_GUEST_LBR_ERROR(vmcb); - return; - } - - if (vmcb->save.dbgctl !=3D 0) { - report(false, "unexpected virtual guest MSR_IA32_DEBUGCTLMSR value 0x%lx= ", vmcb->save.dbgctl); - return; - } + TEST_EXPECT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL); =20 TEST_EXPECT_EQ(dbgctl, DEBUGCTLMSR_LBR); - HOST_CHECK_LBR(&host_branch3_from, &host_branch3_to); + TEST_EXPECT_EQ(vmcb->save.dbgctl, 0); + + get_lbr_ips(&from_ip, &to_ip); + TEST_EXPECT_EQ((u64)&host_branch3_from, from_ip); + TEST_EXPECT_EQ((u64)&host_branch3_to, to_ip); } =20 +/* Test that with LBRV enabled, guest LBR state doesn't leak (2) */ static void svm_lbrv_nested_test2(void) { + u64 from_ip, to_ip; + if (!lbrv_supported()) { report_skip("LBRV not supported in the guest"); return; } =20 - report(true, "Test that with LBRV enabled, guest LBR state doesn't leak (= 2)"); svm_setup_vmrun((u64)svm_lbrv_test_guest2); vmcb->control.virt_ext =3D LBR_CTL_ENABLE_MASK; =20 @@ -3215,14 +3208,13 @@ static void svm_lbrv_nested_test2(void) SVM_BARE_VMRUN; dbgctl =3D rdmsr(MSR_IA32_DEBUGCTLMSR); wrmsr(MSR_IA32_DEBUGCTLMSR, 0); + TEST_EXPECT_EQ(dbgctl, DEBUGCTLMSR_LBR); =20 - if (vmcb->control.exit_code !=3D SVM_EXIT_VMMCALL) { - REPORT_GUEST_LBR_ERROR(vmcb); - return; - } + TEST_EXPECT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL); =20 - TEST_EXPECT_EQ(dbgctl, DEBUGCTLMSR_LBR); - HOST_CHECK_LBR(&host_branch4_from, &host_branch4_to); + get_lbr_ips(&from_ip, &to_ip); + TEST_EXPECT_EQ((u64)&host_branch4_from, from_ip); + TEST_EXPECT_EQ((u64)&host_branch4_to, to_ip); } =20 =20 --=20 2.51.2.1041.gc1ab5b90ca-goog