[RFC PATCH 40/46] KVM: SVM: Add Caretaker SVM assembly guest entry/exit routine

From: Pasha Tatashin

Date: Mon Sep 21 2026 - 17:04:06 EST


Add arch/x86/kvm/svm/caretaker_vmenter.S and caretaker.h for standalone
AMD SVM VMRUN world switch on preserved physical cores.

Signed-off-by: Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
---
arch/x86/kvm/svm/caretaker.h | 63 +++++++++++++++++++
arch/x86/kvm/svm/caretaker_vmenter.S | 91 ++++++++++++++++++++++++++++
2 files changed, 154 insertions(+)
create mode 100644 arch/x86/kvm/svm/caretaker.h
create mode 100644 arch/x86/kvm/svm/caretaker_vmenter.S

diff --git a/arch/x86/kvm/svm/caretaker.h b/arch/x86/kvm/svm/caretaker.h
new file mode 100644
index 000000000000..54a0673cb1ea
--- /dev/null
+++ b/arch/x86/kvm/svm/caretaker.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
+ */
+#ifndef __SVM_CARETAKER_H
+#define __SVM_CARETAKER_H
+
+#ifndef __ASSEMBLY__
+#include <linux/types.h>
+#include <linux/kvm_host.h>
+#include <linux/oncore.h>
+#include "svm.h"
+#endif
+
+#include "../caretaker.h"
+
+#define CSP_VMCB_OFFSET 0x1000
+#define CSP_HSAVE_PA_OFFSET 0x3000
+#define CSP_VMCB_PA_OFFSET 0x3010
+#define VMCB_RAX_OFFSET 0x5f8
+
+#ifndef __ASSEMBLY__
+
+struct caretaker_svm_page {
+ struct caretaker_x86_page common;
+
+ /* Page 1 (4KB): Preserved VMCB */
+ struct vmcb vmcb __aligned(PAGE_SIZE);
+
+ /* Page 2 (4KB): Preserved HSAVE area */
+ u8 hsave_area[PAGE_SIZE] __aligned(PAGE_SIZE);
+ u64 hsave_pa;
+ u64 orig_hsave_pa;
+ u64 vmcb_pa;
+ u64 orig_efer;
+
+ /* Preserved MSR and I/O permission bitmaps referenced by VMCB */
+ u8 msrpm[MSRPM_SIZE] __aligned(PAGE_SIZE);
+ u8 iopm[IOPM_SIZE] __aligned(PAGE_SIZE);
+} __aligned(PAGE_SIZE);
+
+static_assert(offsetof(struct caretaker_svm_page, vmcb) == CSP_VMCB_OFFSET);
+static_assert(offsetof(struct caretaker_svm_page, hsave_pa) == CSP_HSAVE_PA_OFFSET);
+static_assert(offsetof(struct caretaker_svm_page, vmcb_pa) == CSP_VMCB_PA_OFFSET);
+
+void svm_recalc_intercepts(struct kvm_vcpu *vcpu);
+
+#ifdef CONFIG_KVM_CARETAKER
+int svm_caretaker_enter(void *page);
+void svm_caretaker_decode_exit(void *page, struct kvm_caretaker_exit *exit);
+void svm_caretaker_register(void);
+void svm_caretaker_unregister(void);
+void svm_caretaker_init(struct kvm_vcpu *vcpu);
+#else
+static inline void svm_caretaker_register(void) {}
+static inline void svm_caretaker_unregister(void) {}
+static inline void svm_caretaker_init(struct kvm_vcpu *vcpu) {}
+#endif
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __SVM_CARETAKER_H */
diff --git a/arch/x86/kvm/svm/caretaker_vmenter.S b/arch/x86/kvm/svm/caretaker_vmenter.S
new file mode 100644
index 000000000000..e0276a34403e
--- /dev/null
+++ b/arch/x86/kvm/svm/caretaker_vmenter.S
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
+ */
+#include <linux/linkage.h>
+#include <linux/cfi_types.h>
+#include <asm/asm.h>
+#include <asm/bitsperlong.h>
+#include <asm/nospec-branch.h>
+#include "caretaker.h"
+#include "../caretaker_asm.h"
+
+.section .text.cpu_preserved, "ax"
+
+/**
+ * svm_caretaker_enter - Low-level transition to SVM guest mode and back
+ * @csp: Pointer to struct caretaker_svm_page (%rdi)
+ */
+SYM_TYPED_FUNC_START(svm_caretaker_enter)
+ ENDBR
+ CARETAKER_PUSH_HOST_REGS
+ movq %cr3, %rax
+ pushq %rax /* saved_cr3 */
+
+ clgi
+ sti
+
+ /* 0. Save host VMCB state (FS, GS, TR, LDTR) to host save area */
+ movq CXP_HSAVE_PA(%rdi), %rax
+ testq %rax, %rax
+ jz 10f
+ vmsave %rax
+10:
+
+ /* 1. Sync guest RAX to VMCB and load guest state (FS, GS, TR, LDTR) */
+ leaq CSP_VMCB_OFFSET(%rdi), %rax
+ movq CXP_REG_RAX(%rdi), %rbx
+ movq %rbx, VMCB_RAX_OFFSET(%rax)
+ movq CSP_VMCB_PA_OFFSET(%rdi), %rax
+ vmload %rax
+
+ /* 2. Restore guest GPRs */
+ CARETAKER_RESTORE_GPRS_NO_RAX %rdi
+
+ /* 3. Run guest */
+ pushq %rdi
+ movq CXP_REG_RDI(%rdi), %rdi
+ movq (%rsp), %rax
+ movq CSP_VMCB_PA_OFFSET(%rax), %rax
+ CARETAKER_CLEAR_CPU_BUFFERS
+ vmrun %rax
+ cli
+
+ /* 4. Guest exited: Save guest GPRs */
+ xchgq %rdi, (%rsp) /* rdi = csp, stack = guest rdi */
+ popq CXP_REG_RDI(%rdi)
+
+ CARETAKER_SAVE_GPRS_NO_RAX %rdi
+
+ /* Stuff Return Stack Buffer to prevent guest RSB poisoning */
+ CARETAKER_FILL_RETURN_BUFFER %rax
+
+ /* 5. Save guest state to VMCB */
+ movq CSP_VMCB_PA_OFFSET(%rdi), %rax
+ vmsave %rax
+ leaq CSP_VMCB_OFFSET(%rdi), %rax
+ movq VMCB_RAX_OFFSET(%rax), %rbx /* save.rax */
+ movq %rbx, CXP_REG_RAX(%rdi)
+
+ /* 5a. Restore host VMCB state (FS, GS, TR, LDTR) from HSAVE */
+ movq CXP_HSAVE_PA(%rdi), %rax
+ testq %rax, %rax
+ jz 40f
+ vmload %rax
+40:
+
+ /* 5b. Restore host CR3 if clobbered */
+ popq %rbx /* saved_cr3 */
+ movq %cr3, %rcx
+ cmpq %rbx, %rcx
+ je 50f
+ movq %rbx, %cr3
+50:
+ stgi
+
+ /* 6. Restore host callee-saved registers & return to C */
+ CARETAKER_POP_HOST_REGS
+ xorq %rax, %rax
+ ret
+SYM_FUNC_END(svm_caretaker_enter)
--
2.55.0.1082.g2b9226bbc0-goog