From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 01E654611C9; Wed, 5 Aug 2026 11:04:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785927853; cv=none; b=uwWnABoJFEqJj9GhCaHUSHtiATjy6f0yY6EmL6TQi70A9lv7heOFigS25S0/6kykHN2G4d83qM6s8XN25iDL+Zk35sjmVJe+P65jlZbQaK/DEMY60evjAjSsLZFUYmY0/iepOG+SzU/MKISEdEKiNokkHY5wsOhl4cfAOEkYYuk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785927853; c=relaxed/simple; bh=zvjJV8tV8BE05JUgc1FNynSS0+u5Qt0BznrK47FN39g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lqylFYLIF2XsqzDRwDg+DNtVt7vwxmtNZk5iwuOaWK30FAoBu8dV7PpzO15Ae4kuMOxRu/sLUXJqkFFbsEY84c1j6behS744bBLJb3cnOK2Nb2tXbU0mpX3Zyt0u0sBfRTGUpe+Pm/utRakUclRT8JQf80Xkf7Fys2/KnKZOAxY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=qGrVX46l; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="qGrVX46l" Received: from fedora.hsd1.wa.comcast.net (unknown [52.148.140.42]) by linux.microsoft.com (Postfix) with ESMTPSA id 9075020B716C; Wed, 5 Aug 2026 04:03:49 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 9075020B716C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1785927829; bh=bI49O7Gio+nXUuiVovapNuGTXo7eViF48ve8j7ZvVjM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qGrVX46lltvkMIYvrvJjjOesOgiErdHK0KSfU7RG8vgTO7nnPH6pvxNMqqSFBaXJ1 Qwk18H4HLMtLRtzg9BmcS8KAiYjAf+HUPNoTp1Rsh+uU1tlESsvRfcCHip7pF4km7V L2Vv7luez3Ct/vq1MRoz2AqdBPFNtIP3NRUYwWws= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v1 33/42] drivers/virt: rename VBS park loop to secure_monitor Date: Wed, 5 Aug 2026 04:03:15 -0700 Message-ID: <20260805110324.25067-34-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260805110324.25067-1-snambakam@linux.microsoft.com> References: <20260805110324.25067-1-snambakam@linux.microsoft.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the secure-plane park loop (drivers/virt/vbs_park.c, CONFIG_VBS_PARK) with drivers/virt/secure_monitor.c (CONFIG_VBS_SECURE_MONITOR), activated by the "secure_monitor" kernel command-line option. Behaviour is unchanged: a late_initcall spawns the "vbs-secmon" kthread which hands control back to the normal plane via KVM_HC_VBS_VTL_RETURN and acknowledges VTL calls as no-ops; real per-call handlers are plumbed in incrementally. Also drop the unused security/vbs/secure_monitor.c, which was never wired into the running secure-plane path (it keyed off "vbs_secure_plane" under CONFIG_VBS_KVM_PLANES but was never activated). --- drivers/virt/Kconfig | 16 +- drivers/virt/Makefile | 2 +- drivers/virt/{vbs_park.c => secure_monitor.c} | 77 ++--- security/vbs/Makefile | 1 - security/vbs/secure_monitor.c | 266 ------------------ 5 files changed, 52 insertions(+), 310 deletions(-) rename drivers/virt/{vbs_park.c => secure_monitor.c} (50%) delete mode 100644 security/vbs/secure_monitor.c diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 88e40eaba1c2..5d964f124afe 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -13,20 +13,20 @@ menuconfig VIRT_DRIVERS if VIRT_DRIVERS -config VBS_PARK - bool "KVM VM-planes secure-plane park loop" +config VBS_SECURE_MONITOR + bool "KVM VM-planes secure-plane monitor" depends on X86 && KVM_GUEST help - Minimal in-kernel handler for the secure plane (plane >0) of a KVM - VM-planes guest. When enabled and the "vbs_park" kernel command-line + In-kernel monitor for the secure plane (plane >0) of a KVM VM-planes + guest. When enabled and the "secure_monitor" kernel command-line option is present, a kernel thread hands control back to the normal plane via the KVM_HC_VBS_VTL_RETURN hypercall and then services VTL calls from a shared calling area. - This is independent of the full VBS/HEKI stack (CONFIG_VBS): it - implements only the park/dispatch handshake so that any secure kernel - can act as plane 1. Calls are acknowledged as no-ops. Say N unless - this kernel is used as a VM-planes secure plane. + This is independent of the full VBS/HEKI stack (CONFIG_VBS) so that + any secure kernel can act as plane 1. Per-call handlers are plumbed + in incrementally; until then calls are acknowledged as no-ops. Say N + unless this kernel is used as a VM-planes secure plane. config VMGENID tristate "Virtual Machine Generation ID driver" diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index fa91899a356d..22d1121ba5bd 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_FSL_HV_MANAGER) += fsl_hypervisor.o obj-$(CONFIG_VMGENID) += vmgenid.o -obj-$(CONFIG_VBS_PARK) += vbs_park.o +obj-$(CONFIG_VBS_SECURE_MONITOR) += secure_monitor.o obj-y += vboxguest/ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ diff --git a/drivers/virt/vbs_park.c b/drivers/virt/secure_monitor.c similarity index 50% rename from drivers/virt/vbs_park.c rename to drivers/virt/secure_monitor.c index fabb6beeea7b..2d181c32c439 100644 --- a/drivers/virt/vbs_park.c +++ b/drivers/virt/secure_monitor.c @@ -1,31 +1,41 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * vbs_park - minimal KVM VM-planes secure-plane park loop + * secure_monitor - KVM VM-planes secure-plane monitor * - * This provides only the secure-plane (plane >0) side of the VM-planes - * park/dispatch handshake so that an otherwise ordinary kernel can act as - * plane 1. It is deliberately independent of the full VBS/HEKI stack - * (CONFIG_VBS): it implements no security policy. Its single job is to hand - * control back to the normal plane (plane 0) via the KVM_HC_VBS_VTL_RETURN - * hypercall and then service VTL calls from the shared calling area. + * This is the secure-plane (plane >0) side of the VM-planes park/dispatch + * handshake. It lets an otherwise ordinary kernel act as the secure plane + * (conventionally plane 1 / VTL1 / VMPL0, though the index is not hard-coded) + * without pulling in the full VBS/HEKI stack (CONFIG_VBS). Its single job is + * to hand control back to the normal plane (plane 0) via the + * KVM_HC_VBS_VTL_RETURN hypercall and then service VTL calls from the shared + * calling area. * * Control flow (all within plane 0's single KVM_RUN; see * arch/x86/kvm/x86.c __kvm_emulate_hypercall): * - * plane 0 KVM plane 1 (here) - * ------- --- -------------- + * normal plane KVM secure plane + * ------------ --- ------------ * fill calling area * HC_VBS_VTL_CALL(ca_gpa) ─────▶ switch_plane ───────────▶ resume in - * (RAX := ca_gpa) vtl_return() - * handle call_id + * (RAX := ca_gpa) secmon_vtl_return() + * dispatch(call_id) * write ca->status * resume after VTL_CALL ◀─────── switch_plane ◀─────────── HC_VBS_VTL_RETURN * - * Activated by the "vbs_park" kernel command-line option; without it this - * kernel boots normally and never parks. + * Because all planes of a VM share the same memslots (struct kvm_plane has no + * memslots of its own; they live in struct kvm), the secure plane sees the + * same guest-physical address space as the normal plane and can read the + * calling area and the GPAs referenced by each request directly. + * + * For now every VTL call is acknowledged as a no-op so the normal plane can + * make progress; the real per-call handlers (self-protection, HEKI memory + * protection, kernel sealing, …) are plumbed in incrementally. + * + * Activated by the "secure_monitor" kernel command-line option; without it + * this kernel boots normally and never parks. */ -#define pr_fmt(fmt) "vbs-park: " fmt +#define pr_fmt(fmt) "vbs-secmon: " fmt #include #include @@ -44,7 +54,7 @@ * * [ call_pending | call_id | status | arg_size | resp_size | buffer ] */ -struct vtl_ca { +struct vbs_kvm_ca { __u8 call_pending; /* 1 while call is in flight */ __u8 rsvd[3]; __u32 call_id; /* request id (set by caller) */ @@ -54,15 +64,15 @@ struct vtl_ca { __u8 buffer[]; /* request data in, response data out */ } __packed; -/* Set from the "vbs_park" kernel command-line option. */ -static bool vbs_park_active __ro_after_init; +/* Set from the "secure_monitor" kernel command-line option. */ +static bool secmon_active __ro_after_init; -static int __init vbs_park_setup(char *str) +static int __init secmon_setup(char *str) { - vbs_park_active = true; + secmon_active = true; return 1; } -__setup("vbs_park", vbs_park_setup); +__setup("secure_monitor", secmon_setup); /* * Park the secure plane and hand control back to the normal plane. On the @@ -70,23 +80,23 @@ __setup("vbs_park", vbs_park_setup); * hypercall return value (RAX). @status is carried for tracing only; the * real result is already in the calling area. */ -static u64 vtl_return(long status) +static u64 secmon_vtl_return(long status) { return kvm_hypercall1(KVM_HC_VBS_VTL_RETURN, (unsigned long)status); } -static int vbs_park_fn(void *unused) +static int secmon_monitor_fn(void *unused) { long status = 0; - pr_info("secure-plane park loop started\n"); + pr_info("secure monitor started\n"); for (;;) { - struct vtl_ca *ca; + struct vbs_kvm_ca *ca; u64 ca_gpa; /* Park; resume with the next request's calling-area GPA. */ - ca_gpa = vtl_return(status); + ca_gpa = secmon_vtl_return(status); if (!ca_gpa) { status = -EINVAL; continue; @@ -101,10 +111,9 @@ static int vbs_park_fn(void *unused) } /* - * No security policy lives here: acknowledge the call as a - * no-op so the normal plane can make progress. Replace this - * with real handlers (or move plane 1 to a dedicated SVSM) to - * enforce actual VBS semantics. + * No handlers are plumbed in yet: acknowledge the call as a + * no-op so the normal plane can make progress. Real per-call + * dispatch is added incrementally. */ pr_info_ratelimited("VTL call id=0x%x arg_size=%u (no-op)\n", ca->call_id, ca->arg_size); @@ -118,19 +127,19 @@ static int vbs_park_fn(void *unused) return 0; } -static int __init vbs_park_init(void) +static int __init secmon_init(void) { struct task_struct *t; - if (!vbs_park_active) + if (!secmon_active) return 0; - t = kthread_run(vbs_park_fn, NULL, "vbs-park"); + t = kthread_run(secmon_monitor_fn, NULL, "vbs-secmon"); if (IS_ERR(t)) { - pr_err("failed to start park loop: %ld\n", PTR_ERR(t)); + pr_err("failed to start secure monitor: %ld\n", PTR_ERR(t)); return PTR_ERR(t); } return 0; } -late_initcall(vbs_park_init); +late_initcall(secmon_init); diff --git a/security/vbs/Makefile b/security/vbs/Makefile index f24f31727a65..01e831e28ac7 100644 --- a/security/vbs/Makefile +++ b/security/vbs/Makefile @@ -7,7 +7,6 @@ vbs-y := probe.o core.o vbs-$(CONFIG_VBS_HEKI) += heki.o obj-$(CONFIG_VBS_KVM_PLANES) += kvm_planes.o -obj-$(CONFIG_VBS_KVM_PLANES) += secure_monitor.o obj-$(CONFIG_VBS_SEV_SNP) += sev_snp.o obj-$(CONFIG_VBS_TDX) += tdx.o obj-$(CONFIG_VBS_HV_VSM) += hv_vsm.o diff --git a/security/vbs/secure_monitor.c b/security/vbs/secure_monitor.c deleted file mode 100644 index c1221ad5019b..000000000000 --- a/security/vbs/secure_monitor.c +++ /dev/null @@ -1,266 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * VBS secure-plane monitor — in-guest VTL dispatcher - * - * This is the secure-plane counterpart of the normal-plane kvm_planes - * backend. The SAME kernel image boots as both the normal plane and the - * secure plane; when booted as the secure plane (selected via the - * "vbs_secure_plane" kernel command-line option) this monitor takes over - * and services VTL calls issued by the normal plane. - * - * "Secure plane" is the highest-privilege plane of the VM (conventionally - * plane 1 / VTL1 / VMPL0, but a VM may have up to KVM_MAX_PLANES planes and - * the index is not hard-coded here). "Normal plane" is the requesting, - * lower-privilege plane (conventionally plane 0). - * - * Control flow (all within the normal plane's single KVM_RUN, see - * arch/x86/kvm/x86.c ____kvm_emulate_hypercall): - * - * normal plane KVM secure plane - * ------------ --- ------------ - * fill calling area - * HC_VBS_VTL_CALL(ca_gpa) ─────▶ switch_plane ───────────▶ resume in - * (RAX := ca_gpa) secmon_vtl_return() - * dispatch(call_id) - * write ca->status - * resume after VTL_CALL ◀─────── switch_plane ◀─────────── HC_VBS_VTL_RETURN(status) - * read ca->status - * - * Because all planes of a VM share the same memslots (struct kvm_plane has - * no memslots of its own; they live in struct kvm), the secure plane sees - * the same guest-physical address space as the normal plane and can read - * the calling area and the GPAs referenced by each request directly. - */ - -#define pr_fmt(fmt) "vbs-secmon: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "heki.h" - -/* - * Shared-memory calling area — must match struct vbs_kvm_ca in kvm_planes.c - * (this is the normal-plane <-> secure-plane wire ABI). - * - * [ call_pending | call_id | status | arg_size | resp_size | buffer ] - */ -struct vbs_kvm_ca { - __u8 call_pending; /* 1 while call is in flight */ - __u8 rsvd[3]; - __u32 call_id; /* enum vbs_call_id (set by caller) */ - __s32 status; /* return code (set by responder) */ - __u32 arg_size; /* request payload size */ - __u32 resp_size; /* response payload size */ - __u8 buffer[]; /* request data in, response data out */ -} __packed; - -/* Set from the "vbs_secure_plane" kernel command-line option. */ -static bool secmon_active __ro_after_init; - -static int __init secmon_setup(char *str) -{ - secmon_active = true; - return 1; -} -__setup("vbs_secure_plane", secmon_setup); - -/* - * Park the secure plane and hand control back to the normal plane. On the - * next VTL call, KVM resumes us here with the calling-area GPA in the - * hypercall return value (RAX). @status is carried for tracing only; the - * real result is already in the calling area. - */ -static u64 secmon_vtl_return(long status) -{ - return kvm_hypercall1(KVM_HC_VBS_VTL_RETURN, (unsigned long)status); -} - -/* - * Apply EPT permissions on a normal-plane GPA range from the secure plane. - * - * The secure plane cannot issue the host KVM_SET_MEMORY_ATTRIBUTES ioctl, - * so it asks KVM to do it via the KVM_HC_VBS_SET_MEM_ATTRS hypercall, which - * KVM honours only for a higher-privilege plane. @perms carries the access - * bits the normal plane should retain (VBS_MEM_*); KVM translates a cleared - * write/exec bit into NO_WRITE / NO_EXEC memory attributes. - */ -static int secmon_apply_attrs(u64 gpa, u64 size, u32 perms) -{ - long ret; - - pr_debug("apply_attrs gpa=0x%llx size=0x%llx perms=%c%c%c\n", - gpa, size, - (perms & VBS_MEM_READ) ? 'r' : '-', - (perms & VBS_MEM_WRITE) ? 'w' : '-', - (perms & VBS_MEM_EXEC) ? 'x' : '-'); - - ret = kvm_hypercall3(KVM_HC_VBS_SET_MEM_ATTRS, gpa, size, perms); - if (ret) - return (int)ret; - - return 0; -} - -/* ── per-call handlers ────────────────────────────────────────────────── */ - -static int secmon_do_protect_memory(const void *arg, u32 arg_size) -{ - const struct vbs_protect_memory_req *r = arg; - - if (arg_size < sizeof(*r)) - return -EINVAL; - - return secmon_apply_attrs(r->gpa, r->size, r->perms); -} - -static int secmon_do_seal_kernel(const void *arg, u32 arg_size) -{ - const struct vbs_seal_kernel_req *r = arg; - int ret; - - if (arg_size < sizeof(*r)) - return -EINVAL; - - /* Kernel text: read + execute, no write. */ - ret = secmon_apply_attrs(r->text_gpa, r->text_size, - VBS_MEM_READ | VBS_MEM_EXEC); - if (ret) - return ret; - - /* Kernel rodata: read only, no write, no execute. */ - return secmon_apply_attrs(r->rodata_gpa, r->rodata_size, - VBS_MEM_READ); -} - -static int secmon_do_set_module_perms(const void *arg, u32 arg_size) -{ - const struct vbs_set_module_perms_req *hdr = arg; - const struct vbs_module_section *sec; - u32 i, n; - - if (arg_size < sizeof(*hdr)) - return -EINVAL; - - n = hdr->nr_sections; - if (arg_size < sizeof(*hdr) + n * sizeof(*sec)) - return -EINVAL; - - sec = (const struct vbs_module_section *)(hdr + 1); - for (i = 0; i < n; i++) { - int ret = secmon_apply_attrs(sec[i].gpa, sec[i].size, - sec[i].perms); - if (ret) - return ret; - } - - return 0; -} - -static long secmon_dispatch(u32 call_id, const void *arg, u32 arg_size, - u32 *resp_size) -{ - *resp_size = 0; - - switch (call_id) { - case VBS_CALL_INIT: - case VBS_CALL_SHUTDOWN: - return 0; - - case VBS_CALL_PROTECT_MEMORY: - return secmon_do_protect_memory(arg, arg_size); - case VBS_CALL_SEAL_KERNEL: - return secmon_do_seal_kernel(arg, arg_size); - - case VBS_CALL_SET_MODULE_PERMS: - return secmon_do_set_module_perms(arg, arg_size); - - /* - * Module/kexec validation and key management are acknowledged for - * now (mirroring the previous userspace dispatcher); real signature - * verification runs here in a later stage. - */ - case VBS_CALL_VALIDATE_MODULE: - case VBS_CALL_UNLOAD_MODULE: - case VBS_CALL_ADD_KEY: - case VBS_CALL_REVOKE_KEY: - case VBS_CALL_SEND_CERTS: - case VBS_CALL_KEXEC_VALIDATE: - case VBS_CALL_KEXEC_INVALIDATE: - return 0; - - default: - pr_warn_ratelimited("unknown call_id 0x%x\n", call_id); - return -ENOSYS; - } -} - -/* ── monitor loop ─────────────────────────────────────────────────────── */ - -static int secmon_monitor_fn(void *unused) -{ - long status = 0; - - pr_info("secure monitor started\n"); - - for (;;) { - struct vbs_kvm_ca *ca; - u64 ca_gpa; - u32 call_id, arg_size, resp_size = 0; - - /* Park; resume with the next request's calling-area GPA. */ - ca_gpa = secmon_vtl_return(status); - if (!ca_gpa) { - status = -EINVAL; - continue; - } - - ca = memremap(ca_gpa, PAGE_SIZE, MEMREMAP_WB); - if (!ca) { - pr_err_ratelimited("failed to map calling area 0x%llx\n", - ca_gpa); - status = -EFAULT; - continue; - } - - call_id = ca->call_id; - arg_size = ca->arg_size; - if (arg_size > PAGE_SIZE - sizeof(*ca)) - arg_size = PAGE_SIZE - sizeof(*ca); - - status = secmon_dispatch(call_id, ca->buffer, arg_size, - &resp_size); - - ca->status = (s32)status; - ca->resp_size = resp_size; - - memunmap(ca); - } - - return 0; -} - -static int __init secmon_init(void) -{ - struct task_struct *t; - - if (!secmon_active) - return 0; - - t = kthread_run(secmon_monitor_fn, NULL, "vbs-secmon"); - if (IS_ERR(t)) { - pr_err("failed to start secure monitor: %ld\n", PTR_ERR(t)); - return PTR_ERR(t); - } - - return 0; -} -late_initcall(secmon_init); -- 2.55.0