[PATCH] x86/sev: Skip DR7 write during kexec when it would trigger an unserviceable #VC

Ashish Kalra <[email protected]>
Newsgroups dev.linux.lists.linux-coco,org.kernel.vger.kvm,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Ashish Kalra <[email protected]>

machine_kexec() calls hw_breakpoint_disable(), which writes DR7. On
SEV-ES/SNP guests that do not have DebugSwap enabled, DR7 accesses are
intercepted and delivered to the guest as a #VC exception, which must be
serviced through the GHCB.

By the time machine_kexec() runs, snp_kexec_finish() (called from
native_machine_shutdown()) has already converted the per-CPU GHCBs and
the boot GHCB back to private and set boot_ghcb to NULL, tearing down the
GHCB infrastructure. The DR7 write therefore raises a #VC that
dereferences a NULL GHCB pointer, causing a page fault and the kexec to
fail.

The NULL boot_ghcb pointer is a result of commit 3645eb7e3915 ("x86/fred:
Fix early boot failures on SEV-ES/SNP guests"): that change makes
__sev_get_ghcb() return the now-NULL boot_ghcb in this window, turning
the previously-benign teardown-window #VC (a bogus VMGEXIT the host
rejected but survived) into a NULL dereference.

Skip the DR7 write in this case. Its value is not needed across kexec
because the new kernel re-initializes DR7. Guests with DebugSwap enabled
context-switch DR7 in hardware and do not intercept the write, and
non-encrypted guests are unaffected, so both continue to call
hw_breakpoint_disable() as before.

Add a CC_ATTR_GUEST_DEBUG_VIRT confidential-computing attribute, so the
debug-register-virtualization state (AMD DebugSwap) is queried through
cc_platform_has().

Reported-by: Srikanth Aithal <[email protected]>
Suggested-by: Tom Lendacky <[email protected]>
Fixes: 3074152e56c9 ("x86/sev: Convert shared memory back to private on kexec")
Cc: [email protected]
Signed-off-by: Ashish Kalra <[email protected]>
Reviewed-by: Nikunj A Dadhania <[email protected]>
Reviewed-by: Tom Lendacky <[email protected]>
Tested-by: Srikanth Aithal <[email protected]>
---
 arch/x86/coco/core.c               |  3 +++
 arch/x86/kernel/machine_kexec_64.c | 11 ++++++++++-
 include/linux/cc_platform.h        |  8 ++++++++
 3 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/arch/x86/coco/core.c b/arch/x86/coco/core.c
index 989ca9f72ba3..d4c90a2da2bf 100644
--- a/arch/x86/coco/core.c
+++ b/arch/x86/coco/core.c
@@ -87,6 +87,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)
 	case CC_ATTR_GUEST_STATE_ENCRYPT:
 		return sev_status & MSR_AMD64_SEV_ES_ENABLED;
 
+	case CC_ATTR_GUEST_DEBUG_VIRT:
+		return sev_status & MSR_AMD64_SNP_DEBUG_SWAP;
+
 	/*
 	 * With SEV, the rep string I/O instructions need to be unrolled
 	 * but SEV-ES supports them through the #VC handler.
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index c3f4a389992d..60d25ceada03 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -398,7 +398,16 @@ void __nocfi machine_kexec(struct kimage *image)
 
 	/* Interrupts aren't acceptable while we reboot */
 	local_irq_disable();
-	hw_breakpoint_disable();
+
+	/*
+	 * On SEV-ES/SEV-SNP guests without debug virtualization enabled, DR7
+	 * writes are intercepted and generate a #VC. The GHCBs have already
+	 * been torn down at this point so the #VC cannot be handled. Skip the
+	 * DR7 write as the new kernel re-initializes DR7 during boot.
+	 */
+	if (!cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT) ||
+	    cc_platform_has(CC_ATTR_GUEST_DEBUG_VIRT))
+		hw_breakpoint_disable();
 	cet_disable();
 
 	if (image->preserve_context) {
diff --git a/include/linux/cc_platform.h b/include/linux/cc_platform.h
index 559353ad64ac..24ba1000ac94 100644
--- a/include/linux/cc_platform.h
+++ b/include/linux/cc_platform.h
@@ -62,6 +62,14 @@ enum cc_attr {
 	 */
 	CC_ATTR_GUEST_STATE_ENCRYPT,
 
+	/**
+	 * @CC_ATTR_GUEST_DEBUG_VIRT: Guest debug register virtualization is active.
+	 *
+	 * The platform/OS is running as a guest/virtual machine and is
+	 * virtualizing the debug register state.
+	 */
+	CC_ATTR_GUEST_DEBUG_VIRT,
+
 	/**
 	 * @CC_ATTR_GUEST_UNROLL_STRING_IO: String I/O is implemented with
 	 *                                  IN/OUT instructions
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.