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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.