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 <[email protected]>
---
 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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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


Reply via email to