From: Sriram Nambakam <snambakam@linux.microsoft.com>
To: kvm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Subject: [RFC PATCH v1 12/42] Add a Virtualization Based Security (VBS) framework. - Add backends for AMD SEV-SNP, Intel TDX, Arm CCA and KVM Planes. - Support VTL on Hyper-V in addition to Planes on KVM.
Date: Wed, 5 Aug 2026 04:02:54 -0700 [thread overview]
Message-ID: <20260805110324.25067-13-snambakam@linux.microsoft.com> (raw)
In-Reply-To: <20260805110324.25067-1-snambakam@linux.microsoft.com>
---
include/linux/vbs.h | 204 ++++++++++++++++++++++++++
security/Kconfig | 2 +
security/Makefile | 3 +
security/vbs/Kconfig | 69 +++++++++
security/vbs/Makefile | 9 ++
security/vbs/arm_cca.c | 300 ++++++++++++++++++++++++++++++++++++++
security/vbs/core.c | 166 +++++++++++++++++++++
security/vbs/hv_vsm.c | 257 ++++++++++++++++++++++++++++++++
security/vbs/internal.h | 41 ++++++
security/vbs/kvm_planes.c | 258 ++++++++++++++++++++++++++++++++
security/vbs/probe.c | 103 +++++++++++++
security/vbs/sev_snp.c | 224 ++++++++++++++++++++++++++++
security/vbs/tdx.c | 280 +++++++++++++++++++++++++++++++++++
13 files changed, 1916 insertions(+)
create mode 100644 include/linux/vbs.h
create mode 100644 security/vbs/Kconfig
create mode 100644 security/vbs/Makefile
create mode 100644 security/vbs/arm_cca.c
create mode 100644 security/vbs/core.c
create mode 100644 security/vbs/hv_vsm.c
create mode 100644 security/vbs/internal.h
create mode 100644 security/vbs/kvm_planes.c
create mode 100644 security/vbs/probe.c
create mode 100644 security/vbs/sev_snp.c
create mode 100644 security/vbs/tdx.c
diff --git a/include/linux/vbs.h b/include/linux/vbs.h
new file mode 100644
index 000000000000..c7dedb90d64c
--- /dev/null
+++ b/include/linux/vbs.h
@@ -0,0 +1,204 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * VBS — Virtualization-Based Security
+ *
+ * Transport-agnostic interface between the guest OS (plane-0 / VTL0 / VMPL2+)
+ * and the secure kernel (plane-1 / VTL1 / VMPL0 / service TD).
+ *
+ * Backends:
+ * - KVM software planes (KVM_X86_DEFAULT_VM, QEMU-managed vCPU threads)
+ * - AMD SEV-SNP VMPL/SVSM (KVM_X86_SNP_VM, hardware VMPLs, SVSM protocol)
+ * - Intel TDX service TD (future — separate TD with shared memory)
+ * - Hyper-V VSM (native VTL hypercalls)
+ * - Arm CCA (RSI host calls from Realm guest to RMM)
+ *
+ * The guest kernel calls vbs_*() functions. The active backend translates
+ * them into the appropriate transport (hypercall, VMGEXIT, shared-memory IPC).
+ */
+
+#ifndef _LINUX_VBS_H
+#define _LINUX_VBS_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+
+struct module;
+
+/* ────────────────────────────────────────────────────────────────────────── */
+/* Memory protection flags */
+/* ────────────────────────────────────────────────────────────────────────── */
+
+/* Permissions that the secure kernel can enforce on lower-plane memory. */
+#define VBS_MEM_READ BIT(0)
+#define VBS_MEM_WRITE BIT(1)
+#define VBS_MEM_EXEC BIT(2)
+
+/* ────────────────────────────────────────────────────────────────────────── */
+/* VTL-call request codes (plane-0 → plane-1 direction) */
+/* ────────────────────────────────────────────────────────────────────────── */
+
+enum vbs_call_id {
+ /* Core lifecycle */
+ VBS_CALL_INIT = 0x0001, /* plane-0 boot complete */
+ VBS_CALL_SHUTDOWN = 0x0002, /* plane-0 shutting down */
+
+ /* Memory protection (HEKI) */
+ VBS_CALL_PROTECT_MEMORY = 0x0100, /* set page permissions */
+ VBS_CALL_SEAL_KERNEL = 0x0101, /* make kernel text immutable */
+
+ /* Module authentication */
+ VBS_CALL_VALIDATE_MODULE = 0x0200, /* verify module signature */
+ VBS_CALL_SET_MODULE_PERMS = 0x0201, /* set module section perms */
+ VBS_CALL_UNLOAD_MODULE = 0x0202, /* module being freed */
+
+ /* Key / certificate management */
+ VBS_CALL_ADD_KEY = 0x0300, /* add runtime key */
+ VBS_CALL_REVOKE_KEY = 0x0301, /* revoke a key */
+ VBS_CALL_SEND_CERTS = 0x0302, /* send system certificates */
+
+ /* Kexec validation */
+ VBS_CALL_KEXEC_VALIDATE = 0x0400, /* validate kexec kernel */
+ VBS_CALL_KEXEC_INVALIDATE = 0x0401, /* invalidate kexec state */
+};
+
+/* ────────────────────────────────────────────────────────────────────────── */
+/* Backend operations (one implementation per platform) */
+/* ────────────────────────────────────────────────────────────────────────── */
+
+/**
+ * struct vbs_ops - operations provided by an VBS backend
+ *
+ * All callbacks are optional; returning -ENOTSUP means the backend does
+ * not implement that feature. The core VBS layer will call these from
+ * process context with preemption enabled.
+ */
+struct vbs_ops {
+ const char *name; /* "kvm-planes", "svsm", "hv-vsm", … */
+
+ /*
+ * Lifecycle
+ */
+
+ /** @init: called once after plane-0 kernel boot is complete. */
+ int (*init)(void);
+
+ /** @shutdown: called before plane-0 halts/reboots. */
+ void (*shutdown)(void);
+
+ /*
+ * Raw VTL call — send an arbitrary request to the secure kernel
+ * and wait for a response. @id is the call code, @arg / @arg_size
+ * point to request-specific data, @resp / @resp_size receive the
+ * reply. Returns 0 on success, negative errno on failure.
+ */
+ int (*vtl_call)(enum vbs_call_id id,
+ const void *arg, size_t arg_size,
+ void *resp, size_t resp_size);
+
+ /*
+ * Memory protection (HEKI)
+ *
+ * Ask the secure kernel to enforce @perms (VBS_MEM_*) on the
+ * physical page range [pfn, pfn + nr_pages) from the perspective
+ * of the lower plane.
+ */
+ int (*protect_memory)(unsigned long pfn, unsigned long nr_pages,
+ unsigned int perms);
+
+ /**
+ * @seal_kernel: make the running kernel's text and rodata immutable.
+ * After this call, any attempt to write to kernel text from the
+ * lower plane traps to the secure kernel.
+ */
+ int (*seal_kernel)(void);
+
+ /*
+ * Module authentication
+ *
+ * @validate_module: send a module's ELF blob to the secure kernel
+ * for signature verification. Returns 0 if the signature is valid.
+ *
+ * @set_module_perms: after relocation, set per-section EPT permissions
+ * for the module (text=RX, rodata=R, data=RW).
+ *
+ * @unload_module: notify the secure kernel that a module is being freed
+ * so it can release EPT overrides.
+ */
+ int (*validate_module)(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size);
+ int (*set_module_perms)(const struct module *mod);
+ int (*unload_module)(const struct module *mod);
+
+ /*
+ * Key / certificate management
+ */
+ int (*add_key)(const void *key, size_t key_size, unsigned int flags);
+ int (*revoke_key)(const void *key_id, size_t id_size);
+ int (*send_certs)(const void *certs, size_t certs_size);
+
+ /*
+ * Kexec validation
+ */
+ int (*kexec_validate)(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size);
+ int (*kexec_invalidate)(void);
+};
+
+/* ────────────────────────────────────────────────────────────────────────── */
+/* Core VBS API (called by guest kernel subsystems) */
+/* ────────────────────────────────────────────────────────────────────────── */
+
+#ifdef CONFIG_VBS
+
+/**
+ * vbs_register_backend() - register the platform-specific backend.
+ *
+ * Called once during early boot by the platform detection code.
+ * Only one backend can be active at a time.
+ */
+int vbs_register_backend(const struct vbs_ops *ops);
+
+/**
+ * vbs_available() - returns true if a backend is registered and ready.
+ */
+bool vbs_available(void);
+
+/* Convenience wrappers — each calls through the active backend's ops. */
+int vbs_protect_memory(unsigned long pfn, unsigned long nr_pages,
+ unsigned int perms);
+int vbs_seal_kernel(void);
+int vbs_validate_module(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size);
+int vbs_set_module_perms(const struct module *mod);
+int vbs_unload_module(const struct module *mod);
+int vbs_add_key(const void *key, size_t key_size, unsigned int flags);
+int vbs_revoke_key(const void *key_id, size_t id_size);
+int vbs_send_certs(const void *certs, size_t certs_size);
+int vbs_kexec_validate(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size);
+int vbs_kexec_invalidate(void);
+
+#else /* !CONFIG_VBS */
+
+static inline bool vbs_available(void) { return false; }
+static inline int vbs_protect_memory(unsigned long pfn,
+ unsigned long nr_pages, unsigned int perms) { return -ENOSYS; }
+static inline int vbs_seal_kernel(void) { return -ENOSYS; }
+static inline int vbs_validate_module(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size) { return -ENOSYS; }
+static inline int vbs_set_module_perms(const struct module *mod)
+ { return -ENOSYS; }
+static inline int vbs_unload_module(const struct module *mod)
+ { return -ENOSYS; }
+static inline int vbs_add_key(const void *key, size_t key_size,
+ unsigned int flags) { return -ENOSYS; }
+static inline int vbs_revoke_key(const void *key_id, size_t id_size)
+ { return -ENOSYS; }
+static inline int vbs_send_certs(const void *certs, size_t certs_size)
+ { return -ENOSYS; }
+static inline int vbs_kexec_validate(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size) { return -ENOSYS; }
+static inline int vbs_kexec_invalidate(void) { return -ENOSYS; }
+
+#endif /* CONFIG_VBS */
+#endif /* _LINUX_VBS_H */
diff --git a/security/Kconfig b/security/Kconfig
index f7bf6cdc6229..31ab9b0fa7d0 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -299,6 +299,8 @@ config SECURITY_COMMONCAP_KUNIT_TEST
If unsure, say N.
+source "security/vbs/Kconfig"
+
source "security/Kconfig.hardening"
endmenu
diff --git a/security/Makefile b/security/Makefile
index 4601230ba442..cd85a7615d01 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -27,5 +27,8 @@ obj-$(CONFIG_BPF_LSM) += bpf/
obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
obj-$(CONFIG_SECURITY_IPE) += ipe/
+# Virtualization-Based Security
+obj-$(CONFIG_VBS) += vbs/
+
# Object integrity file lists
obj-$(CONFIG_INTEGRITY) += integrity/
diff --git a/security/vbs/Kconfig b/security/vbs/Kconfig
new file mode 100644
index 000000000000..3d9fb104b1fc
--- /dev/null
+++ b/security/vbs/Kconfig
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VBS
+ bool "Virtualization-Based Security (VBS) support"
+ depends on (X86_64 || ARM64) && VM_PLANES
+ help
+ Enable a transport-agnostic interface between the guest OS
+ (plane-0 / VTL0 / VMPL2+) and the secure kernel (plane-1 /
+ VTL1 / VMPL0 / service TD / RMM).
+
+ The core VBS layer dispatches calls from kernel subsystems
+ (memory protection, module authentication, key management)
+ to a platform-specific backend such as KVM software planes,
+ AMD SEV-SNP SVSM, Intel TDX, Hyper-V VSM, or Arm CCA.
+
+ If unsure, say N.
+
+config VBS_KVM_PLANES
+ bool "VBS backend: KVM software planes"
+ depends on VBS && KVM_GUEST
+ help
+ VBS backend that uses KVM paravirt hypercalls to communicate
+ between plane-0 (normal guest) and plane-1 (secure kernel
+ running in a separate KVM VM plane managed by QEMU).
+
+ Select this if you are running under KVM with VM planes
+ support enabled.
+
+config VBS_SEV_SNP
+ bool "VBS backend: AMD SEV-SNP"
+ depends on VBS && AMD_MEM_ENCRYPT
+ help
+ VBS backend that uses the SVSM (Secure VM Service Module)
+ protocol to communicate with the SVSM running at VMPL0
+ on AMD SEV-SNP platforms.
+
+ Select this if you are running as an SEV-SNP guest with
+ an SVSM providing security services at VMPL0.
+
+config VBS_TDX
+ bool "VBS backend: Intel TDX service TD"
+ depends on VBS && INTEL_TDX_GUEST
+ help
+ VBS backend that uses TDG.VP.VMCALL (TDVMCALL) to communicate
+ with a service TD providing security services on Intel TDX
+ platforms.
+
+ Note: Service TD support is still evolving in the TDX
+ architecture. Select this for development/testing only.
+
+config VBS_HV_VSM
+ bool "VBS backend: Hyper-V VSM"
+ depends on VBS && HYPERV
+ help
+ VBS backend that uses native Hyper-V hypercalls to communicate
+ between VTL0 (normal kernel) and VTL1 (secure kernel).
+
+ Select this if you are running as a Hyper-V guest with
+ Virtual Secure Mode (VSM) enabled.
+
+config VBS_ARM_CCA
+ bool "VBS backend: Arm CCA (Confidential Compute Architecture)"
+ depends on VBS && ARM64
+ help
+ VBS backend that uses the RSI (Realm Services Interface) to
+ communicate between a Realm guest and the RMM (Realm Management
+ Monitor) or a security service on Arm CCA platforms.
+
+ Select this if you are running as a Realm guest under Arm CCA/RME.
diff --git a/security/vbs/Makefile b/security/vbs/Makefile
new file mode 100644
index 000000000000..4f0f26ef4f71
--- /dev/null
+++ b/security/vbs/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_VBS) += vbs.o
+vbs-y := core.o probe.o
+
+obj-$(CONFIG_VBS_KVM_PLANES) += kvm_planes.o
+obj-$(CONFIG_VBS_SEV_SNP) += sev_snp.o
+obj-$(CONFIG_VBS_TDX) += tdx.o
+obj-$(CONFIG_VBS_HV_VSM) += hv_vsm.o
+obj-$(CONFIG_VBS_ARM_CCA) += arm_cca.o
diff --git a/security/vbs/arm_cca.c b/security/vbs/arm_cca.c
new file mode 100644
index 000000000000..21c8b00bb1d1
--- /dev/null
+++ b/security/vbs/arm_cca.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VBS backend — Arm CCA (Confidential Compute Architecture)
+ *
+ * Uses the RSI (Realm Services Interface) to communicate between the
+ * Realm guest (plane-0) and the RMM (Realm Management Monitor) or a
+ * security service running in a higher-privileged realm.
+ *
+ * Transport: SMC calls via arm_smccc_smc() using SMC_RSI_HOST_CALL for
+ * RPC-style requests to the host/monitor, and direct RSI
+ * commands for memory state management (RIPAS transitions).
+ *
+ * Memory model:
+ * - Protected (RIPAS_RAM): RMM-backed, encrypted, inaccessible to host
+ * - Shared (RIPAS_EMPTY): Host-backed, used for I/O and communication
+ * - The highest IPA bit marks shared vs protected pages
+ */
+
+#include "internal.h"
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/arm-smccc.h>
+
+#include <asm/rsi.h>
+
+/* ── VBS host-call command IDs ────────────────────────────────────────── */
+/*
+ * VBS requests are sent to the host via SMC_RSI_HOST_CALL. The host
+ * call structure is placed in a shared (RIPAS_EMPTY) page. The first
+ * 16 bytes encode the VBS-specific command header.
+ */
+#define CCA_VBS_MAGIC 0x56425343 /* "VBSC" */
+
+struct vbs_cca_host_req {
+ __u32 magic; /* CCA_VBS_MAGIC */
+ __u32 call_id; /* enum vbs_call_id / CCA_VBS_* cmd */
+ __u32 arg_size; /* bytes of payload following this hdr */
+ __u32 reserved;
+ __u8 payload[];
+} __packed;
+
+struct vbs_cca_host_resp {
+ __s32 status; /* 0 = success, negative errno */
+ __u32 resp_size;
+ __u8 payload[];
+} __packed;
+
+/* VBS sub-commands */
+#define CCA_VBS_INIT 0
+#define CCA_VBS_SHUTDOWN 1
+#define CCA_VBS_PROTECT_MEMORY 2
+#define CCA_VBS_SEAL_KERNEL 3
+#define CCA_VBS_VALIDATE_MODULE 4
+#define CCA_VBS_SET_MODULE_PERMS 5
+#define CCA_VBS_UNLOAD_MODULE 6
+#define CCA_VBS_ADD_KEY 7
+#define CCA_VBS_REVOKE_KEY 8
+#define CCA_VBS_SEND_CERTS 9
+#define CCA_VBS_KEXEC_VALIDATE 10
+#define CCA_VBS_KEXEC_INVALIDATE 11
+
+/* Shared pages for host communication (RIPAS_EMPTY / decrypted) */
+static void *cca_req_page;
+static void *cca_resp_page;
+
+/* ── low-level host call ──────────────────────────────────────────────── */
+
+static int cca_vbs_host_call(u32 cmd, const void *arg, size_t arg_size,
+ void *resp, size_t resp_size)
+{
+ struct vbs_cca_host_req *req;
+ struct vbs_cca_host_resp *rsp;
+ struct arm_smccc_res res;
+ unsigned long ret;
+
+ if (!cca_req_page || !cca_resp_page)
+ return -ENOMEM;
+
+ if (arg_size > PAGE_SIZE - sizeof(*req))
+ return -E2BIG;
+
+ /* Build request in the shared page */
+ req = cca_req_page;
+ memset(req, 0, PAGE_SIZE);
+ req->magic = CCA_VBS_MAGIC;
+ req->call_id = cmd;
+ req->arg_size = arg_size;
+ if (arg_size && arg)
+ memcpy(req->payload, arg, arg_size);
+
+ memset(cca_resp_page, 0, PAGE_SIZE);
+
+ /*
+ * SMC_RSI_HOST_CALL: arg1 = IPA of host call structure.
+ * The host (VMM) reads the request, processes it, writes the
+ * response into cca_resp_page, then returns control.
+ */
+ arm_smccc_smc(SMC_RSI_HOST_CALL, virt_to_phys(cca_req_page),
+ 0, 0, 0, 0, 0, 0, &res);
+ ret = res.a0;
+ if (ret != RSI_SUCCESS) {
+ pr_err_ratelimited("vbs-cca: RSI host call failed (%lu)\n",
+ ret);
+ return -EIO;
+ }
+
+ /* Read response */
+ rsp = cca_resp_page;
+ if (rsp->status)
+ return rsp->status;
+
+ if (resp && resp_size) {
+ size_t copy = min_t(size_t, resp_size, rsp->resp_size);
+
+ memcpy(resp, rsp->payload, copy);
+ }
+ return 0;
+}
+
+static int cca_vbs_vtl_call(enum vbs_call_id id,
+ const void *arg, size_t arg_size,
+ void *resp, size_t resp_size)
+{
+ return cca_vbs_host_call(id, arg, arg_size, resp, resp_size);
+}
+
+/* ── memory protection ────────────────────────────────────────────────── */
+
+/*
+ * On Arm CCA, memory protection is handled natively via RIPAS transitions.
+ * The RMM enforces that protected (RIPAS_RAM) pages are inaccessible to
+ * the host. For VBS-style per-page permission control (R/W/X), we
+ * forward the request to the security service via a host call.
+ */
+
+struct vbs_cca_protect_args {
+ __u64 pfn;
+ __u64 nr_pages;
+ __u32 perms;
+} __packed;
+
+static int cca_vbs_protect_memory(unsigned long pfn, unsigned long nr_pages,
+ unsigned int perms)
+{
+ struct vbs_cca_protect_args args = {
+ .pfn = pfn,
+ .nr_pages = nr_pages,
+ .perms = perms,
+ };
+
+ return cca_vbs_host_call(CCA_VBS_PROTECT_MEMORY,
+ &args, sizeof(args), NULL, 0);
+}
+
+static int cca_vbs_seal_kernel(void)
+{
+ return cca_vbs_host_call(CCA_VBS_SEAL_KERNEL, NULL, 0, NULL, 0);
+}
+
+/* ── module authentication ────────────────────────────────────────────── */
+
+static int cca_vbs_validate_module(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size)
+{
+ return cca_vbs_host_call(CCA_VBS_VALIDATE_MODULE, NULL, 0, NULL, 0);
+}
+
+static int cca_vbs_set_module_perms(const struct module *mod)
+{
+ return cca_vbs_host_call(CCA_VBS_SET_MODULE_PERMS, NULL, 0, NULL, 0);
+}
+
+static int cca_vbs_unload_module(const struct module *mod)
+{
+ return cca_vbs_host_call(CCA_VBS_UNLOAD_MODULE, NULL, 0, NULL, 0);
+}
+
+/* ── key / certificate management ─────────────────────────────────────── */
+
+static int cca_vbs_add_key(const void *key, size_t key_size,
+ unsigned int flags)
+{
+ return cca_vbs_host_call(CCA_VBS_ADD_KEY, key, key_size, NULL, 0);
+}
+
+static int cca_vbs_revoke_key(const void *key_id, size_t id_size)
+{
+ return cca_vbs_host_call(CCA_VBS_REVOKE_KEY, key_id, id_size, NULL, 0);
+}
+
+static int cca_vbs_send_certs(const void *certs, size_t certs_size)
+{
+ return cca_vbs_host_call(CCA_VBS_SEND_CERTS,
+ certs, certs_size, NULL, 0);
+}
+
+/* ── kexec validation ─────────────────────────────────────────────────── */
+
+static int cca_vbs_kexec_validate(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size)
+{
+ return cca_vbs_host_call(CCA_VBS_KEXEC_VALIDATE, NULL, 0, NULL, 0);
+}
+
+static int cca_vbs_kexec_invalidate(void)
+{
+ return cca_vbs_host_call(CCA_VBS_KEXEC_INVALIDATE, NULL, 0, NULL, 0);
+}
+
+/* ── lifecycle ────────────────────────────────────────────────────────── */
+
+static int cca_vbs_init(void)
+{
+ int ret;
+
+ /*
+ * Allocate shared pages for host communication. Convert them
+ * to RIPAS_EMPTY so the host/VMM can access them.
+ */
+ cca_req_page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ cca_resp_page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ if (!cca_req_page || !cca_resp_page) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /* Mark as shared (RIPAS_EMPTY) for host access */
+ ret = set_memory_decrypted((unsigned long)cca_req_page, 1);
+ if (ret)
+ goto fail;
+ ret = set_memory_decrypted((unsigned long)cca_resp_page, 1);
+ if (ret)
+ goto fail_re_encrypt_req;
+
+ ret = cca_vbs_host_call(CCA_VBS_INIT, NULL, 0, NULL, 0);
+ if (ret) {
+ pr_err("vbs-cca: realm VBS init failed (%d)\n", ret);
+ goto fail_re_encrypt;
+ }
+
+ pr_info("vbs-cca: connected to Arm CCA security service\n");
+ return 0;
+
+fail_re_encrypt:
+ set_memory_encrypted((unsigned long)cca_resp_page, 1);
+fail_re_encrypt_req:
+ set_memory_encrypted((unsigned long)cca_req_page, 1);
+fail:
+ free_page((unsigned long)cca_req_page);
+ free_page((unsigned long)cca_resp_page);
+ cca_req_page = cca_resp_page = NULL;
+ return ret;
+}
+
+static void cca_vbs_shutdown(void)
+{
+ cca_vbs_host_call(CCA_VBS_SHUTDOWN, NULL, 0, NULL, 0);
+
+ if (cca_resp_page) {
+ set_memory_encrypted((unsigned long)cca_resp_page, 1);
+ free_page((unsigned long)cca_resp_page);
+ }
+ if (cca_req_page) {
+ set_memory_encrypted((unsigned long)cca_req_page, 1);
+ free_page((unsigned long)cca_req_page);
+ }
+ cca_req_page = cca_resp_page = NULL;
+}
+
+/* ── ops table & registration ─────────────────────────────────────────── */
+
+static const struct vbs_ops cca_vbs_ops = {
+ .name = "arm-cca",
+ .init = cca_vbs_init,
+ .shutdown = cca_vbs_shutdown,
+ .vtl_call = cca_vbs_vtl_call,
+ .protect_memory = cca_vbs_protect_memory,
+ .seal_kernel = cca_vbs_seal_kernel,
+ .validate_module = cca_vbs_validate_module,
+ .set_module_perms = cca_vbs_set_module_perms,
+ .unload_module = cca_vbs_unload_module,
+ .add_key = cca_vbs_add_key,
+ .revoke_key = cca_vbs_revoke_key,
+ .send_certs = cca_vbs_send_certs,
+ .kexec_validate = cca_vbs_kexec_validate,
+ .kexec_invalidate = cca_vbs_kexec_invalidate,
+};
+
+/* ── detection & probe (called from probe.c) ──────────────────────────── */
+
+bool __init vbs_cca_detect(void)
+{
+ return is_realm_world();
+}
+
+const struct vbs_ops *vbs_cca_get_ops(void)
+{
+ return &cca_vbs_ops;
+}
diff --git a/security/vbs/core.c b/security/vbs/core.c
new file mode 100644
index 000000000000..352590d88136
--- /dev/null
+++ b/security/vbs/core.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VBS — Virtualization-Based Security core
+ *
+ * Dispatches calls from guest kernel subsystems to the active
+ * platform-specific backend (KVM planes, SVSM, Hyper-V VSM, …).
+ */
+
+#include "internal.h"
+
+#include <linux/mutex.h>
+
+static const struct vbs_ops *vbs_backend;
+static DEFINE_MUTEX(vbs_lock);
+
+int vbs_register_backend(const struct vbs_ops *ops)
+{
+ int ret = 0;
+
+ if (!ops || !ops->name)
+ return -EINVAL;
+
+ mutex_lock(&vbs_lock);
+ if (vbs_backend) {
+ pr_err("vbs: backend \"%s\" already registered, rejecting \"%s\"\n",
+ vbs_backend->name, ops->name);
+ ret = -EBUSY;
+ } else {
+ vbs_backend = ops;
+ pr_info("vbs: registered backend \"%s\"\n", ops->name);
+ }
+ mutex_unlock(&vbs_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vbs_register_backend);
+
+bool vbs_available(void)
+{
+ return READ_ONCE(vbs_backend) != NULL;
+}
+EXPORT_SYMBOL_GPL(vbs_available);
+
+/* ── convenience wrappers ─────────────────────────────────────────────── */
+
+int vbs_protect_memory(unsigned long pfn, unsigned long nr_pages,
+ unsigned int perms)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->protect_memory)
+ return -EOPNOTSUPP;
+ return ops->protect_memory(pfn, nr_pages, perms);
+}
+EXPORT_SYMBOL_GPL(vbs_protect_memory);
+
+int vbs_seal_kernel(void)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->seal_kernel)
+ return -EOPNOTSUPP;
+ return ops->seal_kernel();
+}
+EXPORT_SYMBOL_GPL(vbs_seal_kernel);
+
+int vbs_validate_module(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->validate_module)
+ return -EOPNOTSUPP;
+ return ops->validate_module(elf, elf_size, sig, sig_size);
+}
+EXPORT_SYMBOL_GPL(vbs_validate_module);
+
+int vbs_set_module_perms(const struct module *mod)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->set_module_perms)
+ return -EOPNOTSUPP;
+ return ops->set_module_perms(mod);
+}
+EXPORT_SYMBOL_GPL(vbs_set_module_perms);
+
+int vbs_unload_module(const struct module *mod)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->unload_module)
+ return -EOPNOTSUPP;
+ return ops->unload_module(mod);
+}
+EXPORT_SYMBOL_GPL(vbs_unload_module);
+
+int vbs_add_key(const void *key, size_t key_size, unsigned int flags)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->add_key)
+ return -EOPNOTSUPP;
+ return ops->add_key(key, key_size, flags);
+}
+EXPORT_SYMBOL_GPL(vbs_add_key);
+
+int vbs_revoke_key(const void *key_id, size_t id_size)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->revoke_key)
+ return -EOPNOTSUPP;
+ return ops->revoke_key(key_id, id_size);
+}
+EXPORT_SYMBOL_GPL(vbs_revoke_key);
+
+int vbs_send_certs(const void *certs, size_t certs_size)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->send_certs)
+ return -EOPNOTSUPP;
+ return ops->send_certs(certs, certs_size);
+}
+EXPORT_SYMBOL_GPL(vbs_send_certs);
+
+int vbs_kexec_validate(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->kexec_validate)
+ return -EOPNOTSUPP;
+ return ops->kexec_validate(kernel, kernel_size, sig, sig_size);
+}
+EXPORT_SYMBOL_GPL(vbs_kexec_validate);
+
+int vbs_kexec_invalidate(void)
+{
+ const struct vbs_ops *ops = READ_ONCE(vbs_backend);
+
+ if (!ops)
+ return -ENODEV;
+ if (!ops->kexec_invalidate)
+ return -EOPNOTSUPP;
+ return ops->kexec_invalidate();
+}
+EXPORT_SYMBOL_GPL(vbs_kexec_invalidate);
diff --git a/security/vbs/hv_vsm.c b/security/vbs/hv_vsm.c
new file mode 100644
index 000000000000..981ec7fa95e3
--- /dev/null
+++ b/security/vbs/hv_vsm.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VBS backend — Hyper-V VSM (Virtual Secure Mode)
+ *
+ * Uses native Hyper-V hypercalls to communicate between VTL0 (normal
+ * kernel) and VTL1 (secure kernel / SKCI).
+ *
+ * Transport: hv_do_hypercall() with VTL-targeted input pages.
+ *
+ * On Hyper-V the VTL architecture is a first-class feature:
+ * - VTL0 runs the normal OS kernel.
+ * - VTL1 runs the secure kernel that enforces HVCI / Credential Guard.
+ * - VTL switches are performed via HvVtlCall / HvVtlReturn hypercalls.
+ * - Memory protection is enforced per-VTL via the second-level address
+ * translation (SLAT / EPT / NPT) controlled by the hypervisor.
+ */
+
+#include "internal.h"
+
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include <asm/mshyperv.h>
+#include <asm/hyperv-tlfs.h>
+
+/* ── Hyper-V VTL call / return hypercall numbers ──────────────────────── */
+#define HVCALL_VTL_CALL 0x0011
+#define HVCALL_VTL_RETURN 0x0012
+
+/*
+ * VBS-specific hypercall — used to send structured VBS requests to VTL1.
+ * This sits in the vendor-extension range and is routed by the Hyper-V
+ * secure kernel to the appropriate VBS service handler.
+ */
+#define HVCALL_VBS_REQUEST 0x0200
+
+/* ── shared-memory request / response layout ──────────────────────────── */
+
+struct vbs_hv_request {
+ __u32 call_id; /* enum vbs_call_id */
+ __u32 arg_size; /* bytes of payload following this hdr */
+ __u8 payload[];
+} __packed;
+
+struct vbs_hv_response {
+ __s32 status; /* 0 = success, negative errno */
+ __u32 resp_size;
+ __u8 payload[];
+} __packed;
+
+/* Hypercall input / output pages (one page each, allocated once) */
+static void *hv_input_page;
+static void *hv_output_page;
+
+/* ── low-level VTL call ───────────────────────────────────────────────── */
+
+static int hv_vsm_vtl_call(enum vbs_call_id id,
+ const void *arg, size_t arg_size,
+ void *resp, size_t resp_size)
+{
+ struct vbs_hv_request *req;
+ struct vbs_hv_response *rsp;
+ u64 status;
+
+ if (!hv_input_page || !hv_output_page)
+ return -ENOMEM;
+
+ if (arg_size > PAGE_SIZE - sizeof(*req))
+ return -E2BIG;
+
+ /* Build request in the hypercall input page */
+ req = hv_input_page;
+ memset(req, 0, PAGE_SIZE);
+ req->call_id = id;
+ req->arg_size = arg_size;
+ if (arg_size && arg)
+ memcpy(req->payload, arg, arg_size);
+
+ memset(hv_output_page, 0, PAGE_SIZE);
+
+ status = hv_do_hypercall(HVCALL_VBS_REQUEST,
+ hv_input_page, hv_output_page);
+ if (!hv_result_success(status)) {
+ pr_err_ratelimited("vbs-hv: hypercall failed (0x%llx)\n",
+ status);
+ return -EIO;
+ }
+
+ /* Read response from the output page */
+ rsp = hv_output_page;
+ if (rsp->status)
+ return rsp->status;
+
+ if (resp && resp_size) {
+ size_t copy = min_t(size_t, resp_size, rsp->resp_size);
+
+ memcpy(resp, rsp->payload, copy);
+ }
+ return 0;
+}
+
+/* ── memory protection ────────────────────────────────────────────────── */
+
+struct vbs_hv_protect_args {
+ __u64 pfn;
+ __u64 nr_pages;
+ __u32 perms;
+} __packed;
+
+static int hv_vsm_protect_memory(unsigned long pfn, unsigned long nr_pages,
+ unsigned int perms)
+{
+ struct vbs_hv_protect_args args = {
+ .pfn = pfn,
+ .nr_pages = nr_pages,
+ .perms = perms,
+ };
+
+ return hv_vsm_vtl_call(VBS_CALL_PROTECT_MEMORY,
+ &args, sizeof(args), NULL, 0);
+}
+
+static int hv_vsm_seal_kernel(void)
+{
+ return hv_vsm_vtl_call(VBS_CALL_SEAL_KERNEL, NULL, 0, NULL, 0);
+}
+
+/* ── module authentication ────────────────────────────────────────────── */
+
+static int hv_vsm_validate_module(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size)
+{
+ return hv_vsm_vtl_call(VBS_CALL_VALIDATE_MODULE, NULL, 0, NULL, 0);
+}
+
+static int hv_vsm_set_module_perms(const struct module *mod)
+{
+ return hv_vsm_vtl_call(VBS_CALL_SET_MODULE_PERMS, NULL, 0, NULL, 0);
+}
+
+static int hv_vsm_unload_module(const struct module *mod)
+{
+ return hv_vsm_vtl_call(VBS_CALL_UNLOAD_MODULE, NULL, 0, NULL, 0);
+}
+
+/* ── key / certificate management ─────────────────────────────────────── */
+
+static int hv_vsm_add_key(const void *key, size_t key_size,
+ unsigned int flags)
+{
+ return hv_vsm_vtl_call(VBS_CALL_ADD_KEY, key, key_size, NULL, 0);
+}
+
+static int hv_vsm_revoke_key(const void *key_id, size_t id_size)
+{
+ return hv_vsm_vtl_call(VBS_CALL_REVOKE_KEY, key_id, id_size, NULL, 0);
+}
+
+static int hv_vsm_send_certs(const void *certs, size_t certs_size)
+{
+ return hv_vsm_vtl_call(VBS_CALL_SEND_CERTS,
+ certs, certs_size, NULL, 0);
+}
+
+/* ── kexec validation ─────────────────────────────────────────────────── */
+
+static int hv_vsm_kexec_validate(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size)
+{
+ return hv_vsm_vtl_call(VBS_CALL_KEXEC_VALIDATE, NULL, 0, NULL, 0);
+}
+
+static int hv_vsm_kexec_invalidate(void)
+{
+ return hv_vsm_vtl_call(VBS_CALL_KEXEC_INVALIDATE, NULL, 0, NULL, 0);
+}
+
+/* ── lifecycle ────────────────────────────────────────────────────────── */
+
+static int hv_vsm_init(void)
+{
+ int ret;
+
+ /*
+ * Use the Hyper-V provided hypercall input/output pages.
+ * Allocate our own pair so we don't conflict with other users.
+ */
+ hv_input_page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ hv_output_page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ if (!hv_input_page || !hv_output_page) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = hv_vsm_vtl_call(VBS_CALL_INIT, NULL, 0, NULL, 0);
+ if (ret) {
+ pr_err("vbs-hv: VTL1 secure kernel INIT failed (%d)\n", ret);
+ goto fail;
+ }
+
+ pr_info("vbs-hv: connected to Hyper-V VTL1 secure kernel\n");
+ return 0;
+
+fail:
+ free_page((unsigned long)hv_input_page);
+ free_page((unsigned long)hv_output_page);
+ hv_input_page = hv_output_page = NULL;
+ return ret;
+}
+
+static void hv_vsm_shutdown(void)
+{
+ hv_vsm_vtl_call(VBS_CALL_SHUTDOWN, NULL, 0, NULL, 0);
+ free_page((unsigned long)hv_input_page);
+ free_page((unsigned long)hv_output_page);
+ hv_input_page = hv_output_page = NULL;
+}
+
+/* ── ops table & registration ─────────────────────────────────────────── */
+
+static const struct vbs_ops hv_vsm_ops = {
+ .name = "hv-vsm",
+ .init = hv_vsm_init,
+ .shutdown = hv_vsm_shutdown,
+ .vtl_call = hv_vsm_vtl_call,
+ .protect_memory = hv_vsm_protect_memory,
+ .seal_kernel = hv_vsm_seal_kernel,
+ .validate_module = hv_vsm_validate_module,
+ .set_module_perms = hv_vsm_set_module_perms,
+ .unload_module = hv_vsm_unload_module,
+ .add_key = hv_vsm_add_key,
+ .revoke_key = hv_vsm_revoke_key,
+ .send_certs = hv_vsm_send_certs,
+ .kexec_validate = hv_vsm_kexec_validate,
+ .kexec_invalidate = hv_vsm_kexec_invalidate,
+};
+
+/* ── detection & probe (called from probe.c) ──────────────────────────── */
+
+bool __init vbs_hv_vsm_detect(void)
+{
+ if (!hv_is_hyperv_initialized())
+ return false;
+
+ if (ms_hyperv.vtl != 0) {
+ pr_debug("vbs-hv: not at VTL0 (vtl=%u), skipping\n",
+ ms_hyperv.vtl);
+ return false;
+ }
+
+ return true;
+}
+
+const struct vbs_ops *vbs_hv_vsm_get_ops(void)
+{
+ return &hv_vsm_ops;
+}
diff --git a/security/vbs/internal.h b/security/vbs/internal.h
new file mode 100644
index 000000000000..415621c993b8
--- /dev/null
+++ b/security/vbs/internal.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * VBS internal header — shared between probe.c and backend implementations.
+ */
+#ifndef _SECURITY_VBS_INTERNAL_H
+#define _SECURITY_VBS_INTERNAL_H
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/vbs.h>
+
+/* Each backend exports a detect + get_ops pair for the centralized probe. */
+
+#ifdef CONFIG_VBS_SEV_SNP
+bool __init vbs_sev_snp_detect(void);
+const struct vbs_ops *vbs_sev_snp_get_ops(void);
+#endif
+
+#ifdef CONFIG_VBS_TDX
+bool __init vbs_tdx_detect(void);
+const struct vbs_ops *vbs_tdx_get_ops(void);
+#endif
+
+#ifdef CONFIG_VBS_ARM_CCA
+bool __init vbs_cca_detect(void);
+const struct vbs_ops *vbs_cca_get_ops(void);
+#endif
+
+#ifdef CONFIG_VBS_HV_VSM
+bool __init vbs_hv_vsm_detect(void);
+const struct vbs_ops *vbs_hv_vsm_get_ops(void);
+#endif
+
+#ifdef CONFIG_VBS_KVM_PLANES
+bool __init vbs_kvm_planes_detect(void);
+const struct vbs_ops *vbs_kvm_planes_get_ops(void);
+#endif
+
+#endif /* _SECURITY_VBS_INTERNAL_H */
diff --git a/security/vbs/kvm_planes.c b/security/vbs/kvm_planes.c
new file mode 100644
index 000000000000..3526f7c429c3
--- /dev/null
+++ b/security/vbs/kvm_planes.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VBS backend — KVM software planes
+ *
+ * Uses KVM paravirt hypercalls to communicate between plane-0 (normal
+ * guest kernel) and plane-1 (secure kernel running in a separate KVM
+ * plane managed by QEMU).
+ *
+ * Transport: kvm_hypercall{0..4}() → KVM_EXIT_HYPERCALL → QEMU → plane-1
+ *
+ * The shared-memory VTL-call protocol works as follows:
+ * 1. Plane-0 fills a request buffer in shared memory.
+ * 2. Plane-0 issues a KVM hypercall carrying the physical address
+ * and size of the request.
+ * 3. QEMU (or the host) delivers the request to the plane-1 vCPU.
+ * 4. Plane-1 processes the request and writes a response.
+ * 5. Plane-0 reads the response from shared memory.
+ */
+
+#include "internal.h"
+
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <asm/kvm_para.h>
+
+/* ── hypercall numbers for VBS VTL calls (plane-0 → plane-1) ──────────── */
+/*
+ * These extend the existing KVM_HC_* numbering. The host (KVM + QEMU)
+ * intercepts them and routes them to the secure-kernel plane.
+ */
+#define KVM_HC_VBS_VTL_CALL 15
+
+/* ── shared-memory request / response layout ──────────────────────────── */
+
+struct vbs_kvm_request {
+ __u32 call_id; /* enum vbs_call_id */
+ __u32 arg_size; /* bytes of payload following this hdr */
+ __u8 payload[]; /* variable-length argument data */
+} __packed;
+
+struct vbs_kvm_response {
+ __s32 status; /* 0 = success, negative errno */
+ __u32 resp_size; /* bytes of payload following this hdr */
+ __u8 payload[]; /* variable-length response data */
+} __packed;
+
+/*
+ * A single page is used for each direction. That gives ~4 KiB of
+ * payload per call, which is enough for all current VBS operations.
+ */
+static void *kvm_req_page; /* request (plane-0 writes, plane-1 reads) */
+static void *kvm_resp_page; /* response (plane-1 writes, plane-0 reads) */
+
+/* ── low-level VTL call ───────────────────────────────────────────────── */
+
+static int kvm_planes_vtl_call(enum vbs_call_id id,
+ const void *arg, size_t arg_size,
+ void *resp, size_t resp_size)
+{
+ struct vbs_kvm_request *req;
+ struct vbs_kvm_response *rsp;
+ long hc_ret;
+
+ if (!kvm_req_page || !kvm_resp_page)
+ return -ENOMEM;
+
+ if (arg_size > PAGE_SIZE - sizeof(*req))
+ return -E2BIG;
+
+ /* Build request in the shared page */
+ req = kvm_req_page;
+ req->call_id = id;
+ req->arg_size = arg_size;
+ if (arg_size && arg)
+ memcpy(req->payload, arg, arg_size);
+
+ /* Issue hypercall: pass physical addresses of req & resp pages */
+ hc_ret = kvm_hypercall2(KVM_HC_VBS_VTL_CALL,
+ virt_to_phys(kvm_req_page),
+ virt_to_phys(kvm_resp_page));
+ if (hc_ret) {
+ pr_err_ratelimited("vbs-kvm: hypercall failed (%ld)\n", hc_ret);
+ return -EIO;
+ }
+
+ /* Read response */
+ rsp = kvm_resp_page;
+ if (rsp->status)
+ return rsp->status;
+
+ if (resp && resp_size) {
+ size_t copy = min_t(size_t, resp_size, rsp->resp_size);
+
+ memcpy(resp, rsp->payload, copy);
+ }
+ return 0;
+}
+
+/* ── memory protection ────────────────────────────────────────────────── */
+
+struct vbs_protect_args {
+ __u64 pfn;
+ __u64 nr_pages;
+ __u32 perms;
+} __packed;
+
+static int kvm_planes_protect_memory(unsigned long pfn,
+ unsigned long nr_pages,
+ unsigned int perms)
+{
+ struct vbs_protect_args args = {
+ .pfn = pfn,
+ .nr_pages = nr_pages,
+ .perms = perms,
+ };
+
+ return kvm_planes_vtl_call(VBS_CALL_PROTECT_MEMORY,
+ &args, sizeof(args), NULL, 0);
+}
+
+static int kvm_planes_seal_kernel(void)
+{
+ return kvm_planes_vtl_call(VBS_CALL_SEAL_KERNEL, NULL, 0, NULL, 0);
+}
+
+/* ── module authentication ────────────────────────────────────────────── */
+
+static int kvm_planes_validate_module(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size)
+{
+ /*
+ * Module blobs can be large — for the KVM planes backend we pass
+ * the physical address and size to plane-1 via the VTL call and
+ * let plane-1 map/read the pages directly from its EPT view.
+ * For now, a stub that signals "not yet implemented".
+ */
+ return kvm_planes_vtl_call(VBS_CALL_VALIDATE_MODULE,
+ NULL, 0, NULL, 0);
+}
+
+static int kvm_planes_set_module_perms(const struct module *mod)
+{
+ return kvm_planes_vtl_call(VBS_CALL_SET_MODULE_PERMS,
+ NULL, 0, NULL, 0);
+}
+
+static int kvm_planes_unload_module(const struct module *mod)
+{
+ return kvm_planes_vtl_call(VBS_CALL_UNLOAD_MODULE,
+ NULL, 0, NULL, 0);
+}
+
+/* ── key / certificate management ─────────────────────────────────────── */
+
+static int kvm_planes_add_key(const void *key, size_t key_size,
+ unsigned int flags)
+{
+ return kvm_planes_vtl_call(VBS_CALL_ADD_KEY, key, key_size, NULL, 0);
+}
+
+static int kvm_planes_revoke_key(const void *key_id, size_t id_size)
+{
+ return kvm_planes_vtl_call(VBS_CALL_REVOKE_KEY,
+ key_id, id_size, NULL, 0);
+}
+
+static int kvm_planes_send_certs(const void *certs, size_t certs_size)
+{
+ return kvm_planes_vtl_call(VBS_CALL_SEND_CERTS,
+ certs, certs_size, NULL, 0);
+}
+
+/* ── kexec validation ─────────────────────────────────────────────────── */
+
+static int kvm_planes_kexec_validate(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size)
+{
+ return kvm_planes_vtl_call(VBS_CALL_KEXEC_VALIDATE,
+ NULL, 0, NULL, 0);
+}
+
+static int kvm_planes_kexec_invalidate(void)
+{
+ return kvm_planes_vtl_call(VBS_CALL_KEXEC_INVALIDATE,
+ NULL, 0, NULL, 0);
+}
+
+/* ── lifecycle ────────────────────────────────────────────────────────── */
+
+static int kvm_planes_init(void)
+{
+ int ret;
+
+ kvm_req_page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ kvm_resp_page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ if (!kvm_req_page || !kvm_resp_page) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = kvm_planes_vtl_call(VBS_CALL_INIT, NULL, 0, NULL, 0);
+ if (ret) {
+ pr_err("vbs-kvm: plane-1 INIT call failed (%d)\n", ret);
+ goto fail;
+ }
+
+ pr_info("vbs-kvm: connected to plane-1 secure kernel\n");
+ return 0;
+fail:
+ free_page((unsigned long)kvm_req_page);
+ free_page((unsigned long)kvm_resp_page);
+ kvm_req_page = kvm_resp_page = NULL;
+ return ret;
+}
+
+static void kvm_planes_shutdown(void)
+{
+ kvm_planes_vtl_call(VBS_CALL_SHUTDOWN, NULL, 0, NULL, 0);
+ free_page((unsigned long)kvm_req_page);
+ free_page((unsigned long)kvm_resp_page);
+ kvm_req_page = kvm_resp_page = NULL;
+}
+
+/* ── ops table & registration ─────────────────────────────────────────── */
+
+static const struct vbs_ops kvm_planes_ops = {
+ .name = "kvm-planes",
+ .init = kvm_planes_init,
+ .shutdown = kvm_planes_shutdown,
+ .vtl_call = kvm_planes_vtl_call,
+ .protect_memory = kvm_planes_protect_memory,
+ .seal_kernel = kvm_planes_seal_kernel,
+ .validate_module = kvm_planes_validate_module,
+ .set_module_perms = kvm_planes_set_module_perms,
+ .unload_module = kvm_planes_unload_module,
+ .add_key = kvm_planes_add_key,
+ .revoke_key = kvm_planes_revoke_key,
+ .send_certs = kvm_planes_send_certs,
+ .kexec_validate = kvm_planes_kexec_validate,
+ .kexec_invalidate = kvm_planes_kexec_invalidate,
+};
+
+/* ── detection & probe (called from probe.c) ──────────────────────────── */
+
+bool __init vbs_kvm_planes_detect(void)
+{
+ if (!kvm_para_available()) {
+ pr_debug("vbs-kvm: KVM paravirt not available\n");
+ return false;
+ }
+ return true;
+}
+
+const struct vbs_ops *vbs_kvm_planes_get_ops(void)
+{
+ return &kvm_planes_ops;
+}
diff --git a/security/vbs/probe.c b/security/vbs/probe.c
new file mode 100644
index 000000000000..292f3663a996
--- /dev/null
+++ b/security/vbs/probe.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VBS platform detection and backend selection
+ *
+ * Single initcall that probes the platform and registers the appropriate
+ * VBS backend. Detection order (first match wins):
+ *
+ * 1. Hardware CoCo — these are mutually exclusive by nature:
+ * a. AMD SEV-SNP with SVSM (VMPL > 0, SVSM at VMPL0)
+ * b. Intel TDX service TD (running inside a Trust Domain)
+ * c. Arm CCA (Realm guest with RSI)
+ *
+ * 2. Hypervisor-specific:
+ * d. Hyper-V VSM (Hyper-V guest at VTL0)
+ *
+ * 3. Software emulation:
+ * e. KVM software planes (KVM paravirt guest)
+ *
+ * Only one backend can be active. The first successful probe wins.
+ */
+
+#include "internal.h"
+
+/* Stubs for backends not configured */
+#ifndef CONFIG_VBS_SEV_SNP
+static inline bool vbs_sev_snp_detect(void) { return false; }
+static inline const struct vbs_ops *vbs_sev_snp_get_ops(void) { return NULL; }
+#endif
+#ifndef CONFIG_VBS_TDX
+static inline bool vbs_tdx_detect(void) { return false; }
+static inline const struct vbs_ops *vbs_tdx_get_ops(void) { return NULL; }
+#endif
+#ifndef CONFIG_VBS_ARM_CCA
+static inline bool vbs_cca_detect(void) { return false; }
+static inline const struct vbs_ops *vbs_cca_get_ops(void) { return NULL; }
+#endif
+#ifndef CONFIG_VBS_HV_VSM
+static inline bool vbs_hv_vsm_detect(void) { return false; }
+static inline const struct vbs_ops *vbs_hv_vsm_get_ops(void) { return NULL; }
+#endif
+#ifndef CONFIG_VBS_KVM_PLANES
+static inline bool vbs_kvm_planes_detect(void) { return false; }
+static inline const struct vbs_ops *vbs_kvm_planes_get_ops(void) { return NULL; }
+#endif
+
+/* ── probe table ──────────────────────────────────────────────────────── */
+
+struct vbs_probe_entry {
+ const char *name;
+ bool (*detect)(void);
+ const struct vbs_ops *(*get_ops)(void);
+};
+
+static const struct vbs_probe_entry vbs_probe_table[] __initconst = {
+ /*
+ * Hardware confidential-compute backends first.
+ * These are mutually exclusive — a machine is SEV-SNP *or* TDX
+ * *or* CCA, never more than one.
+ */
+ { "AMD SEV-SNP", vbs_sev_snp_detect, vbs_sev_snp_get_ops },
+ { "Intel TDX", vbs_tdx_detect, vbs_tdx_get_ops },
+ { "Arm CCA", vbs_cca_detect, vbs_cca_get_ops },
+
+ /* Hypervisor-specific */
+ { "Hyper-V VSM", vbs_hv_vsm_detect, vbs_hv_vsm_get_ops },
+
+ /* Software emulation (lowest priority) */
+ { "KVM planes", vbs_kvm_planes_detect, vbs_kvm_planes_get_ops },
+};
+
+/* ── single boot-time probe ───────────────────────────────────────────── */
+
+static int __init vbs_probe_init(void)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(vbs_probe_table); i++) {
+ const struct vbs_probe_entry *e = &vbs_probe_table[i];
+
+ if (!e->detect())
+ continue;
+
+ pr_info("vbs: detected %s platform\n", e->name);
+
+ ret = vbs_register_backend(e->get_ops());
+ if (ret) {
+ pr_err("vbs: failed to register %s backend (%d)\n",
+ e->name, ret);
+ return ret;
+ }
+ return 0;
+ }
+
+ pr_debug("vbs: no supported platform detected\n");
+ return 0;
+}
+
+/*
+ * Run at device_initcall level: platform detection (CPUID, MSRs, SMCCC)
+ * is complete by this point, but subsystems that consume VBS (module
+ * loading, HEKI) have not yet started.
+ */
+device_initcall(vbs_probe_init);
diff --git a/security/vbs/sev_snp.c b/security/vbs/sev_snp.c
new file mode 100644
index 000000000000..510a2245a0e7
--- /dev/null
+++ b/security/vbs/sev_snp.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VBS backend — AMD SEV-SNP
+ *
+ * Uses the SVSM (Secure VM Service Module) protocol to communicate
+ * between the guest (VMPL2+) and the SVSM running at VMPL0.
+ *
+ * Transport: VMGEXIT with SVM_VMGEXIT_SNP_RUN_VMPL exit code,
+ * parameters passed via the SVSM Calling Area (CAA).
+ *
+ * The SVSM already provides core services (PVALIDATE, attestation,
+ * vTPM). This backend extends it with VBS-specific calls for
+ * memory protection, module authentication, and key management
+ * using a new VBS SVSM protocol number.
+ */
+
+#include "internal.h"
+
+#include <linux/cc_platform.h>
+
+#include <asm/sev.h>
+
+/*
+ * VBS SEV-SNP protocol — extends the existing SVSM protocol numbering.
+ * Protocol 0 = core, 1 = attestation, 2 = vTPM, 3 = VBS.
+ */
+#define SEV_SNP_VBS_CALL(x) ((3ULL << 32) | (x))
+
+/* VBS-specific SEV-SNP call IDs (mapped from enum vbs_call_id) */
+#define SEV_SNP_VBS_INIT 0
+#define SEV_SNP_VBS_SHUTDOWN 1
+#define SEV_SNP_VBS_PROTECT_MEMORY 2
+#define SEV_SNP_VBS_SEAL_KERNEL 3
+#define SEV_SNP_VBS_VALIDATE_MODULE 4
+#define SEV_SNP_VBS_SET_MODULE_PERMS 5
+#define SEV_SNP_VBS_UNLOAD_MODULE 6
+#define SEV_SNP_VBS_ADD_KEY 7
+#define SEV_SNP_VBS_REVOKE_KEY 8
+#define SEV_SNP_VBS_SEND_CERTS 9
+#define SEV_SNP_VBS_KEXEC_VALIDATE 10
+#define SEV_SNP_VBS_KEXEC_INVALIDATE 11
+
+/* ── low-level VTL call via SEV-SNP ───────────────────────────────────── */
+
+/*
+ * Issue a VBS call through the SVSM protocol.
+ *
+ * The CAA svsm_buffer is used to pass request/response data.
+ * RAX encodes the protocol (3 = VBS) and call ID.
+ * RCX carries the physical address of any auxiliary data buffer.
+ * RDX carries the size of the auxiliary data.
+ */
+static int sev_snp_vbs_call(u32 call_id, const void *arg, size_t arg_size,
+ void *resp, size_t resp_size)
+{
+ struct svsm_call call = {};
+ int ret;
+
+ call.rax = SEV_SNP_VBS_CALL(call_id);
+ if (arg && arg_size) {
+ call.rcx = __pa(arg);
+ call.rdx = arg_size;
+ }
+ if (resp && resp_size) {
+ call.r8 = __pa(resp);
+ call.r9 = resp_size;
+ }
+
+ ret = svsm_perform_call_protocol(&call);
+ if (ret)
+ pr_err_ratelimited("vbs-sev-snp: call %u failed (%d)\n",
+ call_id, ret);
+ return ret;
+}
+
+static int sev_snp_vbs_vtl_call(enum vbs_call_id id,
+ const void *arg, size_t arg_size,
+ void *resp, size_t resp_size)
+{
+ return sev_snp_vbs_call(id, arg, arg_size, resp, resp_size);
+}
+
+/* ── memory protection ────────────────────────────────────────────────── */
+
+struct vbs_sev_snp_protect_args {
+ __u64 pfn;
+ __u64 nr_pages;
+ __u32 perms;
+} __packed;
+
+static int sev_snp_vbs_protect_memory(unsigned long pfn,
+ unsigned long nr_pages,
+ unsigned int perms)
+{
+ struct vbs_sev_snp_protect_args args = {
+ .pfn = pfn,
+ .nr_pages = nr_pages,
+ .perms = perms,
+ };
+
+ return sev_snp_vbs_call(SEV_SNP_VBS_PROTECT_MEMORY,
+ &args, sizeof(args), NULL, 0);
+}
+
+static int sev_snp_vbs_seal_kernel(void)
+{
+ return sev_snp_vbs_call(SEV_SNP_VBS_SEAL_KERNEL, NULL, 0, NULL, 0);
+}
+
+/* ── module authentication ────────────────────────────────────────────── */
+
+static int sev_snp_vbs_validate_module(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size)
+{
+ /*
+ * Module ELF may be large. Pass its physical address and size
+ * to VMPL0 so the SVSM can read it from the shared address space.
+ */
+ return sev_snp_vbs_call(SEV_SNP_VBS_VALIDATE_MODULE,
+ NULL, 0, NULL, 0);
+}
+
+static int sev_snp_vbs_set_module_perms(const struct module *mod)
+{
+ return sev_snp_vbs_call(SEV_SNP_VBS_SET_MODULE_PERMS,
+ NULL, 0, NULL, 0);
+}
+
+static int sev_snp_vbs_unload_module(const struct module *mod)
+{
+ return sev_snp_vbs_call(SEV_SNP_VBS_UNLOAD_MODULE, NULL, 0, NULL, 0);
+}
+
+/* ── key / certificate management ─────────────────────────────────────── */
+
+static int sev_snp_vbs_add_key(const void *key, size_t key_size,
+ unsigned int flags)
+{
+ return sev_snp_vbs_call(SEV_SNP_VBS_ADD_KEY, key, key_size, NULL, 0);
+}
+
+static int sev_snp_vbs_revoke_key(const void *key_id, size_t id_size)
+{
+ return sev_snp_vbs_call(SEV_SNP_VBS_REVOKE_KEY, key_id, id_size, NULL, 0);
+}
+
+static int sev_snp_vbs_send_certs(const void *certs, size_t certs_size)
+{
+ return sev_snp_vbs_call(SEV_SNP_VBS_SEND_CERTS,
+ certs, certs_size, NULL, 0);
+}
+
+/* ── kexec validation ─────────────────────────────────────────────────── */
+
+static int sev_snp_vbs_kexec_validate(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size)
+{
+ return sev_snp_vbs_call(SEV_SNP_VBS_KEXEC_VALIDATE, NULL, 0, NULL, 0);
+}
+
+static int sev_snp_vbs_kexec_invalidate(void)
+{
+ return sev_snp_vbs_call(SEV_SNP_VBS_KEXEC_INVALIDATE, NULL, 0, NULL, 0);
+}
+
+/* ── lifecycle ────────────────────────────────────────────────────────── */
+
+static int sev_snp_vbs_init(void)
+{
+ int ret;
+
+ ret = sev_snp_vbs_call(SEV_SNP_VBS_INIT, NULL, 0, NULL, 0);
+ if (ret) {
+ pr_err("vbs-sev-snp: VBS init failed (%d)\n", ret);
+ return ret;
+ }
+
+ pr_info("vbs-sev-snp: connected to SVSM at VMPL0\n");
+ return 0;
+}
+
+static void sev_snp_vbs_shutdown(void)
+{
+ sev_snp_vbs_call(SEV_SNP_VBS_SHUTDOWN, NULL, 0, NULL, 0);
+}
+
+/* ── ops table & registration ─────────────────────────────────────────── */
+
+static const struct vbs_ops sev_snp_vbs_ops = {
+ .name = "sev-snp",
+ .init = sev_snp_vbs_init,
+ .shutdown = sev_snp_vbs_shutdown,
+ .vtl_call = sev_snp_vbs_vtl_call,
+ .protect_memory = sev_snp_vbs_protect_memory,
+ .seal_kernel = sev_snp_vbs_seal_kernel,
+ .validate_module = sev_snp_vbs_validate_module,
+ .set_module_perms = sev_snp_vbs_set_module_perms,
+ .unload_module = sev_snp_vbs_unload_module,
+ .add_key = sev_snp_vbs_add_key,
+ .revoke_key = sev_snp_vbs_revoke_key,
+ .send_certs = sev_snp_vbs_send_certs,
+ .kexec_validate = sev_snp_vbs_kexec_validate,
+ .kexec_invalidate = sev_snp_vbs_kexec_invalidate,
+};
+
+/* ── detection & probe (called from probe.c) ──────────────────────────── */
+
+bool __init vbs_sev_snp_detect(void)
+{
+ if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
+ return false;
+
+ if (snp_vmpl == 0) {
+ pr_debug("vbs-sev-snp: running at VMPL0, no SVSM above us\n");
+ return false;
+ }
+
+ return true;
+}
+
+const struct vbs_ops *vbs_sev_snp_get_ops(void)
+{
+ return &sev_snp_vbs_ops;
+}
diff --git a/security/vbs/tdx.c b/security/vbs/tdx.c
new file mode 100644
index 000000000000..43e636e2b591
--- /dev/null
+++ b/security/vbs/tdx.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * VBS backend — Intel TDX service TD
+ *
+ * Uses TDG.VP.VMCALL (TDVMCALL) to communicate between the main TD
+ * (plane-0) and a service TD (plane-1) that provides security services.
+ *
+ * Transport: TDVMCALL with a VBS-specific sub-function leaf. The VMM
+ * (QEMU / KVM) routes the call to the service TD, which
+ * shares memory with the main TD for request/response data.
+ *
+ * Note: Service TD support is still evolving in the TDX architecture.
+ * This backend provides the framework and will be updated as the
+ * inter-TD communication spec is finalised.
+ */
+
+#include "internal.h"
+
+#include <linux/cc_platform.h>
+#include <linux/mm.h>
+
+#include <asm/shared/tdx.h>
+#include <asm/tdx.h>
+
+/*
+ * VBS-specific TDVMCALL sub-function. Chosen from the vendor-specific
+ * range (>= 0x10010000) to avoid conflicts with the GHCI-defined leaves.
+ */
+#define TDVMCALL_VBS 0x10010000ULL
+
+/* VBS sub-commands passed in R12 */
+#define TDX_VBS_INIT 0
+#define TDX_VBS_SHUTDOWN 1
+#define TDX_VBS_PROTECT_MEMORY 2
+#define TDX_VBS_SEAL_KERNEL 3
+#define TDX_VBS_VALIDATE_MODULE 4
+#define TDX_VBS_SET_MODULE_PERMS 5
+#define TDX_VBS_UNLOAD_MODULE 6
+#define TDX_VBS_ADD_KEY 7
+#define TDX_VBS_REVOKE_KEY 8
+#define TDX_VBS_SEND_CERTS 9
+#define TDX_VBS_KEXEC_VALIDATE 10
+#define TDX_VBS_KEXEC_INVALIDATE 11
+
+/* ── shared-memory buffers ────────────────────────────────────────────── */
+
+/*
+ * Shared (decrypted) pages for passing request and response data between
+ * the main TD and the service TD. Marked shared via cc_mkdec() so the
+ * VMM and service TD can access them.
+ */
+static void *tdx_req_page;
+static void *tdx_resp_page;
+
+/* ── low-level VBS TDVMCALL ───────────────────────────────────────────── */
+
+/*
+ * Issue a VBS call to the service TD through the VMM.
+ *
+ * Register usage (TDVMCALL convention):
+ * R11 = sub-function leaf (TDVMCALL_VBS)
+ * R12 = VBS command ID
+ * R13 = physical address of request buffer (shared)
+ * R14 = physical address of response buffer (shared)
+ * R15 = request size
+ */
+static int tdx_vbs_call(u32 cmd, const void *arg, size_t arg_size,
+ void *resp, size_t resp_size)
+{
+ struct tdx_module_args args = {};
+ u64 ret;
+
+ if (arg && arg_size) {
+ if (arg_size > PAGE_SIZE || !tdx_req_page)
+ return -E2BIG;
+ memcpy(tdx_req_page, arg, arg_size);
+ }
+
+ args.r11 = TDVMCALL_VBS;
+ args.r12 = cmd;
+ args.r13 = tdx_req_page ? cc_mkdec(virt_to_phys(tdx_req_page)) : 0;
+ args.r14 = tdx_resp_page ? cc_mkdec(virt_to_phys(tdx_resp_page)) : 0;
+ args.r15 = arg_size;
+
+ ret = __tdx_hypercall(&args);
+ if (ret) {
+ pr_err_ratelimited("vbs-tdx: TDVMCALL failed (0x%llx)\n", ret);
+ return -EIO;
+ }
+
+ /* R10 holds the VMM return status */
+ if (args.r10) {
+ pr_err_ratelimited("vbs-tdx: service TD returned 0x%llx\n",
+ args.r10);
+ return -EREMOTEIO;
+ }
+
+ if (resp && resp_size && tdx_resp_page) {
+ size_t copy = min_t(size_t, resp_size, PAGE_SIZE);
+
+ memcpy(resp, tdx_resp_page, copy);
+ }
+ return 0;
+}
+
+static int tdx_vbs_vtl_call(enum vbs_call_id id,
+ const void *arg, size_t arg_size,
+ void *resp, size_t resp_size)
+{
+ return tdx_vbs_call(id, arg, arg_size, resp, resp_size);
+}
+
+/* ── memory protection ────────────────────────────────────────────────── */
+
+struct vbs_tdx_protect_args {
+ __u64 pfn;
+ __u64 nr_pages;
+ __u32 perms;
+} __packed;
+
+static int tdx_vbs_protect_memory(unsigned long pfn, unsigned long nr_pages,
+ unsigned int perms)
+{
+ struct vbs_tdx_protect_args args = {
+ .pfn = pfn,
+ .nr_pages = nr_pages,
+ .perms = perms,
+ };
+
+ return tdx_vbs_call(TDX_VBS_PROTECT_MEMORY,
+ &args, sizeof(args), NULL, 0);
+}
+
+static int tdx_vbs_seal_kernel(void)
+{
+ return tdx_vbs_call(TDX_VBS_SEAL_KERNEL, NULL, 0, NULL, 0);
+}
+
+/* ── module authentication ────────────────────────────────────────────── */
+
+static int tdx_vbs_validate_module(const void *elf, size_t elf_size,
+ const void *sig, size_t sig_size)
+{
+ return tdx_vbs_call(TDX_VBS_VALIDATE_MODULE, NULL, 0, NULL, 0);
+}
+
+static int tdx_vbs_set_module_perms(const struct module *mod)
+{
+ return tdx_vbs_call(TDX_VBS_SET_MODULE_PERMS, NULL, 0, NULL, 0);
+}
+
+static int tdx_vbs_unload_module(const struct module *mod)
+{
+ return tdx_vbs_call(TDX_VBS_UNLOAD_MODULE, NULL, 0, NULL, 0);
+}
+
+/* ── key / certificate management ─────────────────────────────────────── */
+
+static int tdx_vbs_add_key(const void *key, size_t key_size,
+ unsigned int flags)
+{
+ return tdx_vbs_call(TDX_VBS_ADD_KEY, key, key_size, NULL, 0);
+}
+
+static int tdx_vbs_revoke_key(const void *key_id, size_t id_size)
+{
+ return tdx_vbs_call(TDX_VBS_REVOKE_KEY, key_id, id_size, NULL, 0);
+}
+
+static int tdx_vbs_send_certs(const void *certs, size_t certs_size)
+{
+ return tdx_vbs_call(TDX_VBS_SEND_CERTS, certs, certs_size, NULL, 0);
+}
+
+/* ── kexec validation ─────────────────────────────────────────────────── */
+
+static int tdx_vbs_kexec_validate(const void *kernel, size_t kernel_size,
+ const void *sig, size_t sig_size)
+{
+ return tdx_vbs_call(TDX_VBS_KEXEC_VALIDATE, NULL, 0, NULL, 0);
+}
+
+static int tdx_vbs_kexec_invalidate(void)
+{
+ return tdx_vbs_call(TDX_VBS_KEXEC_INVALIDATE, NULL, 0, NULL, 0);
+}
+
+/* ── lifecycle ────────────────────────────────────────────────────────── */
+
+static int tdx_vbs_init(void)
+{
+ int ret;
+
+ /*
+ * Allocate shared pages for inter-TD communication. These must
+ * be marked as shared (decrypted) so the service TD can read them.
+ */
+ tdx_req_page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ tdx_resp_page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ if (!tdx_req_page || !tdx_resp_page) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * Convert to shared pages. set_memory_decrypted() clears the
+ * encryption bit so the VMM / service TD can access these pages.
+ */
+ ret = set_memory_decrypted((unsigned long)tdx_req_page, 1);
+ if (ret)
+ goto fail;
+ ret = set_memory_decrypted((unsigned long)tdx_resp_page, 1);
+ if (ret)
+ goto fail_re_encrypt_req;
+
+ ret = tdx_vbs_call(TDX_VBS_INIT, NULL, 0, NULL, 0);
+ if (ret) {
+ pr_err("vbs-tdx: service TD init failed (%d)\n", ret);
+ goto fail_re_encrypt;
+ }
+
+ pr_info("vbs-tdx: connected to TDX service TD\n");
+ return 0;
+
+fail_re_encrypt:
+ set_memory_encrypted((unsigned long)tdx_resp_page, 1);
+fail_re_encrypt_req:
+ set_memory_encrypted((unsigned long)tdx_req_page, 1);
+fail:
+ free_page((unsigned long)tdx_req_page);
+ free_page((unsigned long)tdx_resp_page);
+ tdx_req_page = tdx_resp_page = NULL;
+ return ret;
+}
+
+static void tdx_vbs_shutdown(void)
+{
+ tdx_vbs_call(TDX_VBS_SHUTDOWN, NULL, 0, NULL, 0);
+
+ if (tdx_resp_page) {
+ set_memory_encrypted((unsigned long)tdx_resp_page, 1);
+ free_page((unsigned long)tdx_resp_page);
+ }
+ if (tdx_req_page) {
+ set_memory_encrypted((unsigned long)tdx_req_page, 1);
+ free_page((unsigned long)tdx_req_page);
+ }
+ tdx_req_page = tdx_resp_page = NULL;
+}
+
+/* ── ops table & registration ─────────────────────────────────────────── */
+
+static const struct vbs_ops tdx_vbs_ops = {
+ .name = "tdx-service-td",
+ .init = tdx_vbs_init,
+ .shutdown = tdx_vbs_shutdown,
+ .vtl_call = tdx_vbs_vtl_call,
+ .protect_memory = tdx_vbs_protect_memory,
+ .seal_kernel = tdx_vbs_seal_kernel,
+ .validate_module = tdx_vbs_validate_module,
+ .set_module_perms = tdx_vbs_set_module_perms,
+ .unload_module = tdx_vbs_unload_module,
+ .add_key = tdx_vbs_add_key,
+ .revoke_key = tdx_vbs_revoke_key,
+ .send_certs = tdx_vbs_send_certs,
+ .kexec_validate = tdx_vbs_kexec_validate,
+ .kexec_invalidate = tdx_vbs_kexec_invalidate,
+};
+
+/* ── detection & probe (called from probe.c) ──────────────────────────── */
+
+bool __init vbs_tdx_detect(void)
+{
+ return cc_platform_has(CC_ATTR_GUEST_TDX);
+}
+
+const struct vbs_ops *vbs_tdx_get_ops(void)
+{
+ return &tdx_vbs_ops;
+}
--
2.55.0
next prev parent reply other threads:[~2026-08-05 11:03 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-05 11:02 [RFC PATCH v1 00/42] VBS/VSM-on-KVM: VBS integration for KVM VM planes Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 01/42] Fix merge issue - Remove duplicate definition for kvm_arch_has_irq_bypass Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 02/42] Fix compilation Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 03/42] Fix compile error Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 04/42] Fix compile errors Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 05/42] Initial support for VM Planes - Add kernel config for CONFIG_VM_PLANES - Parse vm plane config from initrd for plane configuration - Make hypercalls to allocate memory for the vm planes Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 06/42] Use vcpu count from the plane configuration Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 07/42] skip processing plane configuration for plane 0 - plane 0 is the boot plane Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 08/42] Add plane config param to specify kernel image format Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 09/42] Activate the VM Planes through the Hypervisor - Using KVM as the VMM Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 10/42] allow the command line to be specified for kernels in other planes Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 11/42] Various changes to support VM Planes Sriram Nambakam
2026-08-05 11:02 ` Sriram Nambakam [this message]
2026-08-05 11:02 ` [RFC PATCH v1 13/42] Add a inter-plane communication mechanism through KVM. - model this to use a single page similar to SEV-SNP Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 14/42] KVM: Add per-plane memory attribute support for cross-plane EPT protection Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 15/42] KVM: x86: Add KVM_HC_VBS_VTL_CALL hypercall for VBS inter-plane calls Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 16/42] vbs: Add HEKI kernel sealing and fix KVM plane memory attribute guards Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 17/42] vbs: Add module authentication via VBS/HEKI Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 18/42] vbs: Add kexec validation and make module auth non-fatal Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 19/42] Merge branch 'master' into vm-planes Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 20/42] kvm: x86: fix merged plane API/stat build regressions Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 21/42] KVM: x86: exit VM planes and VBS hypercalls to userspace Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 22/42] kexec: block legacy kexec_load when VBS is active Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 23/42] kvm: x86: fix merged plane API/stat build regressions Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 24/42] KVM: planes: expose memory-attribute setting to in-kernel callers Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 25/42] vm_planes: drop unused per-plane vcpu_count Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 26/42] drivers/virt: add VBS secure-plane park loop Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 27/42] KVM: planes: add arch-neutral in-kernel plane switch helper Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 28/42] KVM: x86: add VBS VTL call/return and cross-plane set-mem-attrs hypercalls Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 29/42] init/vm_planes: set up planes from rootfs_initcall and load ELF payloads Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 30/42] security/vbs: run backend probe and HEKI seal at rootfs_initcall Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 31/42] security/vbs: pin the VTL call hypercall to CPU0 Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 32/42] security/vbs: add secure-plane monitor backend Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 33/42] drivers/virt: rename VBS park loop to secure_monitor Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 34/42] x86/realmode: skip the sub-1M trampoline for the VBS secure plane Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 35/42] KVM: x86: deny normal-plane access to secure-plane memory Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 36/42] KVM: plane: handle KVM_CHECK_EXTENSION on the plane fd Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 37/42] KVM: selftests: run plane tests with a split IRQ chip Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 38/42] kvm: x86: drop obsolete kvm_cache_regs.h Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 39/42] kvm: arch: finalize plane hooks and kvm_arch_vcpu_create signature Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 40/42] kvm: x86: use kvm_vcpu scheduling-state accessors and struct stat fields Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 41/42] kvm: x86: finalize per-plane APIC state and CPUID placement Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 42/42] kvm: planes: reconcile core plane state, UAPI and hypercall exit Sriram Nambakam
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260805110324.25067-13-snambakam@linux.microsoft.com \
--to=snambakam@linux.microsoft.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox