From nobody Tue Sep 29 07:39:12 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 518B32580F2; Tue, 11 Aug 2026 01:53:01 +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=1786413182; cv=none; b=Xsxs6iYrQDKNpomTSpQLb0y7PSzjHAuRtcsLE0VdAa3yl2EURVJ06qhPQhNJcBo9nqSO1Gn6qEdvLNzAmZij9AQcc+542XPrr0IcTJNxgBZibdQjhCanDtIiDBXWcSRjPpKH9Ln16jF7HSmJJY/wUvVSL11jzl6nN7MI81sPT50= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786413182; c=relaxed/simple; bh=z5KSzQQztbLFUsJjUyfDCuhUxbkHoDL2C0yRGoAy1ko=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=agcAZn/DJNd/C4H+sLSK2n5Q0ufYHFZIRiH5evfCXVV12jG6t3lGzAWrAg0LtS8dNNaG3rjfLAsLmLJ4rdAHQbRVtcUaJ+qL5IW/FOnawI6K7smnBoDsyXEkr6DWX63IBueMrEdRjy6DS0w/bsSPbylVUFgdBuww6Eyp/dNnCdY= 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=pfmFUfLC; 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="pfmFUfLC" Received: from fedora (unknown [20.191.74.188]) by linux.microsoft.com (Postfix) with ESMTPSA id AA0B220B7168; Mon, 10 Aug 2026 18:52:36 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com AA0B220B7168 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786413156; bh=rDc3QKOVYWs5lYCY9tzJ9FOVXximpBXicuIVrwI13E8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pfmFUfLCZ9E+tnFOWKH20Cs81dLF1xjnUY1rGLg8F2sbw/4OtaSUQ/dD33T5NtQe+ Bhkx0ngAr26avqXkhIdm60pgWumLYFrbEsbBax6+EAFlQhuPWNxuDyOuTKFUOlpnb1 tznjgKCegIcCDS7kRugCsgCrQkOn2pm9ukLRBqcs= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 1/8] KVM: x86: raise the default maximum planes to two Date: Mon, 10 Aug 2026 18:52:36 -0700 Message-ID: <20260811015243.188486-2-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com> References: <20260811015243.188486-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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The default max-planes callback returns 1, which limits KVM_CAP_PLANES to a single plane on VMX (and on SVM for non-SEV-SNP guests). VBS needs a normal plane (0) and one secure plane (1), so return 2 by default. This is still gated by irqchip=3Dsplit in kvm_arch_max_planes(). --- arch/x86/kvm/x86.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 35fbe0776a3e..29766c19c289 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -478,7 +478,8 @@ static unsigned int num_msr_based_features; =20 unsigned kvm_x86_default_max_planes(struct kvm *kvm) { - return 1; + /* Support a normal plane (0) and one secure plane (1) for VBS. */ + return 2; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_x86_default_max_planes); =20 --=20 2.55.0 From nobody Tue Sep 29 07:39:12 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DA973310645; Tue, 11 Aug 2026 01:53:01 +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=1786413183; cv=none; b=gF4FqyQnfAX2i3eRVEUnB1dvVUlkRjaBSN1upFjODyUD5CVRCT1QTcc1MBhHx56OPsjBGMvx9HLQUP1jTOEUQaaSpHf1Cp1X0AWscb0aa43R3SR1wzmRYXb18+bbZRzcPTeol5N65xqjv6F9DZSeSf5If80F/sL8HpBTaRcrapU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786413183; c=relaxed/simple; bh=3HzGSloaHwdnzgYudZn5YoDKVMeGxd6aw19ukxs2pNg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=eAapbkAEL7TpDaXTv/Tmp5/Uchki77S9HIigDWxU4ecQZmxhs/meCwM0nMV9SZuNwKeL6ho+AgBQwxuZbKvOEAufInwRVJK5tYcWRBh/qM4l8/qus3pR5vhonp5mqyOFTqBb742THlX3xAyT3Z9HigrDUnREg8qDpsu4USKZw0I= 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=nXF7+ZyQ; 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="nXF7+ZyQ" Received: from fedora (unknown [20.191.74.188]) by linux.microsoft.com (Postfix) with ESMTPSA id 7D8A020B710C; Mon, 10 Aug 2026 18:52:37 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 7D8A020B710C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786413157; bh=9NkAdRTD+ZL7dlgrQYyb7R5hRKIetH0WGmfqdzWL6vQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nXF7+ZyQltMxmW0h93FtcC0N/DxiKr1l28/HPIuc7rVfO/l/TZQoD8aYvBc4qHwuC RkB0mGQtnwxMtCHX2rEAdzKCgBwCOtufTlMVj4zJkBYjjGPKbL520rpslRgiaYeBSK E7LmLhkKMOaXqb+Kx2esJgxAGmD2l2QjNLjQBSd8= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 2/8] security/vbs: introduce core VBS framework Date: Mon, 10 Aug 2026 18:52:37 -0700 Message-ID: <20260811015243.188486-3-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com> References: <20260811015243.188486-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: quoted-printable Add the transport-agnostic Virtualization-Based Security (VBS) core: a small dispatch layer between the guest OS (plane-0) and a secure kernel running in a higher-privileged plane-1. Backends register a struct vbs_ops via vbs_register_backend(); the core exposes vbs_available() and a generic vbs_vtl_call() that forwards to the active backend. No backend is registered yet. Gated by CONFIG_VBS (off by default). --- include/linux/vbs.h | 74 +++++++++++++++++++++++++++++++++++++++++++ security/Kconfig | 2 ++ security/Makefile | 1 + security/vbs/Kconfig | 16 ++++++++++ security/vbs/Makefile | 3 ++ security/vbs/core.c | 56 ++++++++++++++++++++++++++++++++ 6 files changed, 152 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/core.c diff --git a/include/linux/vbs.h b/include/linux/vbs.h new file mode 100644 index 000000000000..a154396bf070 --- /dev/null +++ b/include/linux/vbs.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * VBS =E2=80=94 Virtualization-Based Security + * + * Transport-agnostic interface between the guest OS (plane-0) and a secure + * kernel running in a higher-privileged plane-1. The guest kernel calls = the + * vbs_*() functions; the active backend translates them into the appropri= ate + * transport (e.g. a KVM paravirt hypercall). + * + * This is the core framework only. VBS is software-only: backends are + * software/hypervisor planes (KVM software planes now, Hyper-V VSM later). + * Backends register via vbs_register_backend(). + */ + +#ifndef _LINUX_VBS_H +#define _LINUX_VBS_H + +#include +#include + +/* VTL-call request codes (plane-0 -> plane-1 direction). */ +enum vbs_call_id { + VBS_CALL_INIT =3D 0x0001, /* plane-0 boot complete: load plane */ + VBS_CALL_SHUTDOWN =3D 0x0002, /* plane-0 shutting down: unload */ +}; + +/** + * struct vbs_ops - operations provided by a VBS backend + * @name: backend name, e.g. "kvm-planes" + * @init: load/connect the secure plane; called once after drivers init + * @shutdown: unload the secure plane; called on reboot/halt + * @vtl_call: send an arbitrary request to the secure kernel and wait for a + * response. Returns 0 on success, negative errno on failure. + * + * Callbacks run from process context with preemption enabled. + */ +struct vbs_ops { + const char *name; + + int (*init)(void); + void (*shutdown)(void); + + int (*vtl_call)(enum vbs_call_id id, + const void *arg, size_t arg_size, + void *resp, size_t resp_size); +}; + +#ifdef CONFIG_VBS + +/** + * vbs_register_backend() - register the platform-specific backend. + * + * Called once during boot by the platform detection code. Only one backe= nd + * can be active at a time. + */ +int vbs_register_backend(const struct vbs_ops *ops); + +/** vbs_available() - true if a backend is registered. */ +bool vbs_available(void); + +/** vbs_vtl_call() - dispatch a raw VTL call through the active backend. */ +int vbs_vtl_call(enum vbs_call_id id, + const void *arg, size_t arg_size, + void *resp, size_t resp_size); + +#else /* !CONFIG_VBS */ + +static inline bool vbs_available(void) { return false; } +static inline int vbs_vtl_call(enum vbs_call_id id, + const void *arg, size_t arg_size, + void *resp, size_t resp_size) { 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 =20 If unsure, say N. =20 +source "security/vbs/Kconfig" + source "security/Kconfig.hardening" =20 endmenu diff --git a/security/Makefile b/security/Makefile index 4601230ba442..80214c702ddc 100644 --- a/security/Makefile +++ b/security/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_CGROUPS) +=3D device_cgroup.o obj-$(CONFIG_BPF_LSM) +=3D bpf/ obj-$(CONFIG_SECURITY_LANDLOCK) +=3D landlock/ obj-$(CONFIG_SECURITY_IPE) +=3D ipe/ +obj-$(CONFIG_VBS) +=3D vbs/ =20 # Object integrity file lists obj-$(CONFIG_INTEGRITY) +=3D integrity/ diff --git a/security/vbs/Kconfig b/security/vbs/Kconfig new file mode 100644 index 000000000000..0e482196c5b7 --- /dev/null +++ b/security/vbs/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VBS + bool "Virtualization-Based Security (VBS) support" + depends on X86_64 + help + Enable a transport-agnostic interface between the guest OS + (plane-0) and a secure kernel running in a higher-privileged + plane-1. + + The core VBS layer dispatches calls from kernel subsystems to a + platform-specific backend. VBS is software-only: backends are + software/hypervisor planes (KVM software planes now, Hyper-V VSM + later). Hardware confidential-compute is out of scope. + + If unsure, say N. diff --git a/security/vbs/Makefile b/security/vbs/Makefile new file mode 100644 index 000000000000..952c2b855465 --- /dev/null +++ b/security/vbs/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VBS) +=3D vbs.o +vbs-y :=3D core.o diff --git a/security/vbs/core.c b/security/vbs/core.c new file mode 100644 index 000000000000..407d49a91b8f --- /dev/null +++ b/security/vbs/core.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VBS =E2=80=94 Virtualization-Based Security core + * + * Dispatches calls from guest kernel subsystems to the active + * platform-specific backend. + */ + +#include +#include +#include +#include + +static const struct vbs_ops *vbs_backend; +static DEFINE_MUTEX(vbs_lock); + +int vbs_register_backend(const struct vbs_ops *ops) +{ + int ret =3D 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 =3D -EBUSY; + } else { + vbs_backend =3D 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) !=3D NULL; +} +EXPORT_SYMBOL_GPL(vbs_available); + +int vbs_vtl_call(enum vbs_call_id id, + const void *arg, size_t arg_size, + void *resp, size_t resp_size) +{ + const struct vbs_ops *ops =3D READ_ONCE(vbs_backend); + + if (!ops) + return -ENODEV; + if (!ops->vtl_call) + return -EOPNOTSUPP; + return ops->vtl_call(id, arg, arg_size, resp, resp_size); +} +EXPORT_SYMBOL_GPL(vbs_vtl_call); --=20 2.55.0 From nobody Tue Sep 29 07:39:12 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4E79D383990; Tue, 11 Aug 2026 01:53:02 +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=1786413183; cv=none; b=iaSf3HNFO6GqGGAJ1CLj7YPK77Cd+xY8NPPs3uNzr5A3gwsYLurHrWuWUiR2Kx9fKU7Vc3ykSZMshVNvmRch3VrAD5PwJPWHpk1ET7es0mJlOsapxJ5PSEyfG1dB3CGhNqMF2+9gm/kA9rnZQF6/e91XWN0/ssBDxZV+CipIj24= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786413183; c=relaxed/simple; bh=0l9DIclTDpXjx3i1cFtpMSQFaXHSkKJlPuMUCa1YKdo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=SrcHS9rWbieKgkZYNHjqo58D9QmGM/qeF8xA0njZHJ5g6beITJY4tcpBy1BOVydojl4MP/7Y/LgN6HIDhATqIQYhoLqM4YuFSX98A8OLF3ul+m2NCpgZWcyCAVCWgOZArqOFvWZO64mF4VO3l0MyKJZZDbTSKLEcyc8NEhaSElw= 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=K8ExAJ9U; 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="K8ExAJ9U" Received: from fedora (unknown [20.191.74.188]) by linux.microsoft.com (Postfix) with ESMTPSA id 4F8BC20B7128; Mon, 10 Aug 2026 18:52:38 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 4F8BC20B7128 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786413158; bh=Zgh5bRf8sGih4Q3VLyYalBD7y3hgzGAu3FT1cCAe5KM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=K8ExAJ9UjnFqM2lA9OAa5e6/rj2Bk3keq1jcArtvwc9HueoS2fV3sBB2aNvbbj6vJ n9mKi6BVxJK1LWCCi70BUKIjtuKJme1vtrsCWWffGx4+x9WJsF3bSK77nEXEhQo2TO SUUJL8Z2NCvFQlR6LH2Vkuy8CoInRNmIibAgLdEY= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 3/8] security/vbs: add platform probe and backend registration Date: Mon, 10 Aug 2026 18:52:38 -0700 Message-ID: <20260811015243.188486-4-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com> References: <20260811015243.188486-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: quoted-printable Add a single rootfs_initcall that walks a probe table and registers the first backend whose detect() succeeds. The table currently holds only the KVM software-planes entry; when that backend is not configured a local stub keeps the probe self-contained and buildable. Registration only records the backend at this stage. --- security/vbs/Makefile | 4 ++- security/vbs/internal.h | 20 ++++++++++++++ security/vbs/probe.c | 61 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 security/vbs/internal.h create mode 100644 security/vbs/probe.c diff --git a/security/vbs/Makefile b/security/vbs/Makefile index 952c2b855465..0fcbb6640ec1 100644 --- a/security/vbs/Makefile +++ b/security/vbs/Makefile @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_VBS) +=3D vbs.o -vbs-y :=3D core.o +# probe.o links before core.o so the backend is registered (vbs_probe_init) +# early in the rootfs_initcall level. +vbs-y :=3D probe.o core.o diff --git a/security/vbs/internal.h b/security/vbs/internal.h new file mode 100644 index 000000000000..2f444781b390 --- /dev/null +++ b/security/vbs/internal.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * VBS internal header =E2=80=94 shared between probe.c and backend implem= entations. + */ +#ifndef _SECURITY_VBS_INTERNAL_H +#define _SECURITY_VBS_INTERNAL_H + +#include +#include +#include +#include + +/* Each backend exports a detect + get_ops pair for the centralized probe.= */ + +#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/probe.c b/security/vbs/probe.c new file mode 100644 index 000000000000..ccaaba93b18b --- /dev/null +++ b/security/vbs/probe.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VBS platform detection and backend selection + * + * A single boot-time initcall probes the platform and registers the + * appropriate VBS backend. Only one backend can be active; the first + * successful probe wins. VBS is software-only: the only backend today is + * KVM software planes; other software backends (e.g. Hyper-V VSM) may be + * added later. + */ + +#include "internal.h" + +/* Stub for the backend when it is not configured in. */ +#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 + +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 =3D { + { "KVM planes", vbs_kvm_planes_detect, vbs_kvm_planes_get_ops }, +}; + +static int __init vbs_probe_init(void) +{ + int i, ret; + + for (i =3D 0; i < ARRAY_SIZE(vbs_probe_table); i++) { + const struct vbs_probe_entry *e =3D &vbs_probe_table[i]; + + if (!e->detect()) + continue; + + pr_info("vbs: detected %s platform\n", e->name); + + ret =3D 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 rootfs_initcall level: platform detection is complete and the VM + * planes have been set up (init/ links before security/), but subsystems + * that consume VBS have not yet started. Registration only records the + * backend; the plane is loaded later, after device drivers initialise. + */ +rootfs_initcall(vbs_probe_init); --=20 2.55.0 From nobody Tue Sep 29 07:39:12 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9634E39099C; Tue, 11 Aug 2026 01:53:03 +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=1786413185; cv=none; b=mT1y5xDx2cPEJxl5kU9q8sQ6lwkYep/3BUsTt4DNpJN6pIORT/JiFB9/zlCoEvLrBXsTf9fmYAcVhagwpq6ASCXQdDXD00LxlSfMuDHFnGFDYdOu/Ee8IWq/ZINlOpb1Tp8AkLkmKtT9i5ANtB3JYGVHFa6G23O0MER03Ivq1sI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786413185; c=relaxed/simple; bh=PYYhG6O67cZx5HWElLWmRoe/kV+A1dDZB69Ko8VDD5s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=oHLTttsQ5ShW7/9QrmFIPijP+PaAJbPrsRcX5yaTliEFzCF6F5emHixJO06D7gDflhs/+T0Wq5393ZBJL9N4okAB3TyxMtcpgrNKdZoQfvwYTzHylbJgHGoHBOGwqamJAZDuyS4l5ptFclzz48ZbwWCca8mVoDmmjdTZJxUv324= 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=Po8uoG/X; 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="Po8uoG/X" Received: from fedora (unknown [20.191.74.188]) by linux.microsoft.com (Postfix) with ESMTPSA id CF0B320B7167; Mon, 10 Aug 2026 18:52:38 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com CF0B320B7167 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786413158; bh=gCrdw1OtMtYEvetubz01UCuisBvUuvkH+1QHdHeTasU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Po8uoG/XE56sy0794KZMPXdih7tk1/FXCVjB7lFGtXMEabeLta22pdFvwRXqX80W6 z+LgYKjyL2Uz0noqjkSrM6n1typrBYtCN6+736IZnJm8+m/aZ4olmZz9TZn0CKFSg2 6Sa08H7YVY5cuQBvnDDSaWzykHsRE6gdr6Vm0kT8= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 4/8] security/vbs: add KVM software planes backend Date: Mon, 10 Aug 2026 18:52:39 -0700 Message-ID: <20260811015243.188486-5-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com> References: <20260811015243.188486-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: quoted-printable Add the KVM software-planes VBS backend. It uses a synchronous shared-memory calling area and the KVM_HC_VBS_VTL_CALL paravirt hypercall (handled by the host) to reach plane-1. Only the plane lifecycle is implemented: init() allocates the calling area and issues VBS_CALL_INIT to load (connect to) the secure plane; shutdown() issues VBS_CALL_SHUTDOWN to unload it. The BSP-pinned work_on_cpu() ensures the plane switch always lands on CPU0. Gated by CONFIG_VBS_KVM_PLANES. --- security/vbs/Kconfig | 15 ++++ security/vbs/Makefile | 2 + security/vbs/kvm_planes.c | 177 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 security/vbs/kvm_planes.c diff --git a/security/vbs/Kconfig b/security/vbs/Kconfig index 0e482196c5b7..e21f4f30b6cf 100644 --- a/security/vbs/Kconfig +++ b/security/vbs/Kconfig @@ -14,3 +14,18 @@ config VBS later). Hardware confidential-compute is out of scope. =20 If unsure, say N. + +config VBS_KVM_PLANES + bool "VBS backend: KVM software planes" + depends on VBS && KVM_GUEST + help + VBS backend that uses a KVM paravirt hypercall to communicate + between plane-0 (the normal guest kernel) and plane-1 (a secure + kernel running in a separate KVM VM plane managed by QEMU). + + This minimal backend supports loading (connecting to) and + unloading the secure plane via a shared-memory calling area. + + Select this if you are running under KVM with VM planes support. + + If unsure, say N. diff --git a/security/vbs/Makefile b/security/vbs/Makefile index 0fcbb6640ec1..3a161e7cc279 100644 --- a/security/vbs/Makefile +++ b/security/vbs/Makefile @@ -3,3 +3,5 @@ obj-$(CONFIG_VBS) +=3D vbs.o # probe.o links before core.o so the backend is registered (vbs_probe_init) # early in the rootfs_initcall level. vbs-y :=3D probe.o core.o + +obj-$(CONFIG_VBS_KVM_PLANES) +=3D kvm_planes.o diff --git a/security/vbs/kvm_planes.c b/security/vbs/kvm_planes.c new file mode 100644 index 000000000000..af9118c6e74f --- /dev/null +++ b/security/vbs/kvm_planes.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VBS backend =E2=80=94 KVM software planes + * + * Uses a KVM paravirt hypercall to communicate between plane-0 (the normal + * guest kernel) and plane-1 (a secure kernel running in a separate KVM pl= ane + * managed by QEMU). + * + * Transport: kvm_hypercall1(KVM_HC_VBS_VTL_CALL, gpa) -> KVM_EXIT_HYPERCA= LL. + * + * The shared-memory VTL-call protocol is synchronous: + * 1. Plane-0 fills the request buffer in the shared calling area. + * 2. Plane-0 issues the hypercall carrying the physical address of the = area. + * 3. Plane-1 processes the request and writes a response. + * 4. Plane-0 reads the response from the same page. + * + * This minimal backend implements only the plane lifecycle: init() loads + * (connects to) the secure plane, shutdown() unloads it. + */ + +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include + +/* =E2=94=80=E2=94=80 shared-memory calling area =E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80 */ + +/* + * Single shared page used for both request and response data. The protoc= ol + * is synchronous, so no concurrent access is possible. + * + * Layout (within one 4 KiB page): + * [ 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; + +#define VBS_CA_BUF_SIZE (PAGE_SIZE - sizeof(struct vbs_kvm_ca)) + +static void *kvm_ca_page; /* single calling-area page */ + +/* =E2=94=80=E2=94=80 low-level VTL call =E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80 */ + +struct kvm_vtl_call_ctx { + enum vbs_call_id id; + const void *arg; + size_t arg_size; + void *resp; + size_t resp_size; +}; + +/* + * Issue the VTL-call hypercall. MUST run on the BSP (CPU0): KVM switches + * planes per logical CPU and the secure plane boots only on CPU0's siblin= g. + * Driven via work_on_cpu() so the hypercall always lands on CPU0. + */ +static long kvm_planes_vtl_call_on_cpu(void *data) +{ + struct kvm_vtl_call_ctx *ctx =3D data; + struct vbs_kvm_ca *ca =3D kvm_ca_page; + long hc_ret; + + ca->call_id =3D ctx->id; + ca->arg_size =3D ctx->arg_size; + ca->status =3D 0; + ca->resp_size =3D 0; + if (ctx->arg_size && ctx->arg) + memcpy(ca->buffer, ctx->arg, ctx->arg_size); + ca->call_pending =3D 1; + + hc_ret =3D kvm_hypercall1(KVM_HC_VBS_VTL_CALL, virt_to_phys(kvm_ca_page)); + ca->call_pending =3D 0; + + if (hc_ret) { + pr_err_ratelimited("vbs-kvm: hypercall failed (%ld)\n", hc_ret); + return -EIO; + } + + if (ca->status) + return ca->status; + + if (ctx->resp && ctx->resp_size && ca->resp_size) { + size_t copy =3D min_t(size_t, ctx->resp_size, ca->resp_size); + + memcpy(ctx->resp, ca->buffer, copy); + } + return 0; +} + +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 kvm_vtl_call_ctx ctx =3D { + .id =3D id, + .arg =3D arg, + .arg_size =3D arg_size, + .resp =3D resp, + .resp_size =3D resp_size, + }; + + if (!kvm_ca_page) + return -ENOMEM; + + if (arg_size > VBS_CA_BUF_SIZE) + return -E2BIG; + + /* Pin the plane switch to CPU0's secure sibling. */ + return work_on_cpu(0, kvm_planes_vtl_call_on_cpu, &ctx); +} + +/* =E2=94=80=E2=94=80 lifecycle: load / unload the secure plane =E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80 */ + +static int kvm_planes_init(void) +{ + int ret; + + kvm_ca_page =3D (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + if (!kvm_ca_page) + return -ENOMEM; + + ret =3D 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); + free_page((unsigned long)kvm_ca_page); + kvm_ca_page =3D NULL; + return ret; + } + + pr_info("vbs-kvm: connected to plane-1 secure kernel\n"); + return 0; +} + +static void kvm_planes_shutdown(void) +{ + if (!kvm_ca_page) + return; + + kvm_planes_vtl_call(VBS_CALL_SHUTDOWN, NULL, 0, NULL, 0); + free_page((unsigned long)kvm_ca_page); + kvm_ca_page =3D NULL; +} + +/* =E2=94=80=E2=94=80 ops table & registration =E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80 */ + +static const struct vbs_ops kvm_planes_ops =3D { + .name =3D "kvm-planes", + .init =3D kvm_planes_init, + .shutdown =3D kvm_planes_shutdown, + .vtl_call =3D kvm_planes_vtl_call, +}; + +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; +} --=20 2.55.0 From nobody Tue Sep 29 07:39:12 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 147A53921D6; Tue, 11 Aug 2026 01:53:04 +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=1786413185; cv=none; b=RGOrgkF97+eGXcmmlebp8BKhbXnDMC/4MmnMJUYCQxOlDEHGM/EDtggphMbuQMgUOuYyU2NY8u+U7N0OS32cImBnOIbXDt+d5rgAUhFWw9XpG6YRGldykiYw+nGJ0AOrUjI84N8FYnLVofNp0FmAmBsy7rmMrsG2rLfx1TC5+bc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786413185; c=relaxed/simple; bh=3IAelxGwB9DPSAJL7/G76sGbJjTkykEJaZ67McK3h4U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=G4UmAIYuPc78cItKIYNBFcdTdbWJaEx5O6vwLYIYFWgME7dd/cCTmtSo5ARKmS8+v9Fdgh5rm1F3Ad50rNl+JLHexH9O+KYhVORPRnwYvEu8AS1tR8Mz8Tab2fX7lm2x6mglkSSAy2ygNZ+YEnOY8QJZPSi1ZesqpFs8ivwAT0Y= 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=HBU9hAQE; 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="HBU9hAQE" Received: from fedora (unknown [20.191.74.188]) by linux.microsoft.com (Postfix) with ESMTPSA id D490220B7168; Mon, 10 Aug 2026 18:52:39 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com D490220B7168 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786413159; bh=iJvMXxgvjmbLh1wHtZxpYG68qJFVjlcVsr0dm5cygfg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HBU9hAQESeezbGP5swV3Z8I2FAlw9LGq824dQdTdizIIvP9XNYmRQg3FrVlWM3MMv KCz8k8aXbinQs2t4YtJsANlJOMoI58IO3nt2FzSJ1VYsUp7L3xDsqkD6N4jux3P+Wp dcaTE5Tpdljt60PUYac0FywoJljvMjQYZ/oMSJCY= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 5/8] security/vbs: enable the backend after driver init Date: Mon, 10 Aug 2026 18:52:40 -0700 Message-ID: <20260811015243.188486-6-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com> References: <20260811015243.188486-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: quoted-printable Activate a registered VBS backend from a late_initcall, once plane-0 is otherwise up. Enabling is opt-in and requires two conditions: 1. the operator passes enable-kvm-planes=3D1 on the kernel command line, = and 2. the boot image advertises a provisioned secure plane via /etc/Kconfig.kvm-planes containing CONFIG_VM_PLANES=3Dy. When both hold, call the backend's init() to load the secure plane and register a reboot notifier that invokes shutdown() to unload it. --- security/vbs/core.c | 114 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/security/vbs/core.c b/security/vbs/core.c index 407d49a91b8f..c006b6d53a14 100644 --- a/security/vbs/core.c +++ b/security/vbs/core.c @@ -8,8 +8,15 @@ =20 #include #include +#include +#include +#include #include #include +#include +#include +#include +#include =20 static const struct vbs_ops *vbs_backend; static DEFINE_MUTEX(vbs_lock); @@ -54,3 +61,110 @@ int vbs_vtl_call(enum vbs_call_id id, return ops->vtl_call(id, arg, arg_size, resp, resp_size); } EXPORT_SYMBOL_GPL(vbs_vtl_call); + +/* =E2=94=80=E2=94=80 enable after driver init =E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80 */ + +/* + * A registered backend is only activated when two conditions hold: + * 1. the operator opts in on the kernel command line (enable-kvm-planes= =3D1), + * and + * 2. the boot image advertises a provisioned secure plane via a plane + * configuration file that enables the expected option. + */ +#define VBS_KCONFIG_PATH "/etc/Kconfig.kvm-planes" +#define VBS_KCONFIG_TOKEN "CONFIG_VM_PLANES=3Dy" + +static bool vbs_enable_requested; + +static int __init vbs_parse_enable_kvm_planes(char *str) +{ + bool val; + + /* Bare "enable-kvm-planes" (no value) means enabled. */ + if (!str || !*str) + vbs_enable_requested =3D true; + else if (!kstrtobool(str, &val)) + vbs_enable_requested =3D val; + return 0; +} +early_param("enable-kvm-planes", vbs_parse_enable_kvm_planes); + +static int vbs_reboot_notify(struct notifier_block *nb, unsigned long acti= on, + void *data) +{ + const struct vbs_ops *ops =3D READ_ONCE(vbs_backend); + + if (ops && ops->shutdown) + ops->shutdown(); + return NOTIFY_DONE; +} + +static struct notifier_block vbs_reboot_nb =3D { + .notifier_call =3D vbs_reboot_notify, +}; + +/* Return true if VBS_KCONFIG_PATH exists and enables the plane config. */ +static bool __init vbs_plane_config_present(void) +{ + void *buf =3D NULL; + size_t sz =3D 0; + bool ok =3D false; + int ret; + + ret =3D kernel_read_file_from_path(VBS_KCONFIG_PATH, 0, &buf, SZ_1M, &sz, + READING_UNKNOWN); + if (ret < 0) { + pr_info("vbs: %s unavailable (%d); backend left idle\n", + VBS_KCONFIG_PATH, ret); + return false; + } + + if (buf && sz) + ok =3D strnstr(buf, VBS_KCONFIG_TOKEN, sz) !=3D NULL; + vfree(buf); + + if (!ok) + pr_info("vbs: %s present but %s not set; backend left idle\n", + VBS_KCONFIG_PATH, VBS_KCONFIG_TOKEN); + return ok; +} + +/* + * Enable the registered backend after device drivers have initialised. + * Runs at late_initcall so the plane is loaded only once the plane-0 kern= el + * is otherwise up, the operator requested it (enable-kvm-planes=3D1), and= the + * boot image advertises a plane config. + */ +static int __init vbs_enable(void) +{ + const struct vbs_ops *ops =3D READ_ONCE(vbs_backend); + int ret; + + if (!ops) { + pr_debug("vbs: no backend registered; nothing to enable\n"); + return 0; + } + + if (!vbs_enable_requested) { + pr_info("vbs: enable-kvm-planes not set; backend \"%s\" left idle\n", + ops->name); + return 0; + } + + if (!vbs_plane_config_present()) + return 0; + + if (ops->init) { + ret =3D ops->init(); + if (ret) { + pr_warn("vbs: backend \"%s\" init failed (%d)\n", + ops->name, ret); + return 0; + } + } + + register_reboot_notifier(&vbs_reboot_nb); + pr_info("vbs: enabled backend \"%s\"\n", ops->name); + return 0; +} +late_initcall(vbs_enable); --=20 2.55.0 From nobody Tue Sep 29 07:39:12 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5571F395D86; Tue, 11 Aug 2026 01:53:05 +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=1786413187; cv=none; b=c1pzupoyuzOhK3tya7osbcAqQNLNKzgLYdKFjJKlxq1joWVy0Ul1zOUhirvwgMlOWd7VWW88vSKcWNhUDL39KhZdi0PpNe5HC3SMurnulPIDrYyQuSxOQwJ5qvpprfa1x7RDx+PGkyMLdTMUamXJJZ4twcW5XJW3U81vxNgEgu0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786413187; c=relaxed/simple; bh=6OVOtGTA+sjya6LwVxpYHYQdTEbProZZVAaovAUfrJk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=XptKdz086hjQtTBT0afikBVYqi/Q7ZiMvHeWePQ1kscqy0t4DlEaNS7az/hhrltrWtoNvWzd3MaRAKRhtvLpUL9shjHR5Wc0pMmcAb8pQwaYhoTv0OEFJPS0VRhvmGu7vBgBqzPibCjbq2i0cj53J/ihh6vKfPNB1PaRXgFW4No= 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=lp5/9gbV; 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="lp5/9gbV" Received: from fedora (unknown [20.191.74.188]) by linux.microsoft.com (Postfix) with ESMTPSA id 9A0A620B710C; Mon, 10 Aug 2026 18:52:40 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 9A0A620B710C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786413160; bh=G0+h7b+JA2RKfzrEGJ1icedRhM5cCmsBT1fq7WO/dkQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lp5/9gbVoqN2af8dk6ywRu1hkAdxc7PhnZNWZmMCj4ucURce+AD/GavqMG+WgiZQA cQ88AiSwsz65QRn4XdrZGwsgILarNK9qehxXzsFE0EfJFxFxeReT7OKnIqPJ5DJX5/ UBXoYnfMEX2owMGXQp+uqYmS/zpVtRCp6w7c5B3s= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 6/8] vm_planes: add hypervisor-assisted plane bootstrap Date: Mon, 10 Aug 2026 18:52:41 -0700 Message-ID: <20260811015243.188486-7-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com> References: <20260811015243.188486-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: quoted-printable Add the guest-side VM-planes bootstrap under CONFIG_VM_PLANES. It reads the per-plane configuration (config-vm-planes) and the plane kernel images from the initramfs, allocates plane memory and creates the planes via the KVM_HC_VM_PLANES_CONFIG / KVM_HC_VM_PLANES_ACTIVATE paravirt hypercalls, and loads RAW or ELF plane kernels into plane memory. The orchestration is exposed as vm_planes_bootstrap() so a consumer can drive it at the right point in boot; the x86 hypercall wrappers alloc_vm_planes() / activate_vm_planes() live in cpu/common.c. Only software planes are supported; there is no memory protection or sealing here. --- arch/x86/kernel/cpu/common.c | 64 ++++ include/linux/vm_planes.h | 46 +++ init/Kconfig | 18 + init/Makefile | 4 + init/vm_planes.c | 636 +++++++++++++++++++++++++++++++++++ 5 files changed, 768 insertions(+) create mode 100644 include/linux/vm_planes.h create mode 100644 init/vm_planes.c diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index a3df21d26460..2489af105c18 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -28,8 +28,11 @@ #include #include #include +#include +#include =20 #include +#include #include #include #include @@ -2664,3 +2667,64 @@ void __init arch_cpu_finalize_init(void) */ mem_encrypt_init(); } + +#ifdef CONFIG_VM_PLANES +int __init alloc_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg) +{ + phys_addr_t phys; + long ret; + + if (!plane_count || !plane_cfg) + return -EINVAL; + + if (!kvm_para_available()) { + pr_warn("vm_planes: hypercall interface unavailable\n"); + return -ENODEV; + } + + phys =3D virt_to_phys((void *)plane_cfg); + + if (sizeof(unsigned long) < sizeof(phys_addr_t) && phys > ULONG_MAX) { + pr_warn("vm_planes: shared config address exceeds hypercall register wid= th\n"); + return -EOVERFLOW; + } + + ret =3D kvm_hypercall2(KVM_HC_VM_PLANES_CONFIG, + (unsigned long)phys, + plane_count); + if (ret < 0) { + pr_warn("vm_planes: hypercall failed: %ld\n", ret); + return (int)ret; + } + + return 0; +} + +int __init activate_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg) +{ + phys_addr_t phys; + long ret; + + if (!plane_count || !plane_cfg) + return -EINVAL; + + if (!kvm_para_available()) { + pr_warn("vm_planes: hypercall interface unavailable\n"); + return -ENODEV; + } + + phys =3D virt_to_phys((void *)plane_cfg); + + ret =3D kvm_hypercall2(KVM_HC_VM_PLANES_ACTIVATE, + (unsigned long)phys, + plane_count); + if (ret < 0) { + pr_warn("vm_planes: activate hypercall failed: %ld\n", ret); + return (int)ret; + } + + return 0; +} +#endif /* CONFIG_VM_PLANES */ diff --git a/include/linux/vm_planes.h b/include/linux/vm_planes.h new file mode 100644 index 000000000000..e76cbfd99c6d --- /dev/null +++ b/include/linux/vm_planes.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_VM_PLANES_H +#define _LINUX_VM_PLANES_H + +#include +#include +#include + +#ifdef CONFIG_VM_PLANES + +#define VM_PLANE_KERNEL_NAME_MAX 128 +#define VM_PLANE_CMDLINE_MAX 512 + +enum vm_plane_kernel_format { + VM_PLANE_KFMT_RAW =3D 0, + VM_PLANE_KFMT_BZIMAGE, + VM_PLANE_KFMT_ELF, +}; + +struct vm_plane_config { + phys_addr_t load_offset; + phys_addr_t memory_size; + phys_addr_t entry_point; + unsigned int kernel_format; + char kernel[VM_PLANE_KERNEL_NAME_MAX]; + char cmdline[VM_PLANE_CMDLINE_MAX]; +}; + +int __init load_vm_plane_kernels(unsigned int plane_count, + struct vm_plane_config *plane_cfg); + +int __init alloc_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg); + +int __init activate_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg); + +int __init vm_planes_bootstrap(void); + +#else /* !CONFIG_VM_PLANES */ + +static inline int vm_planes_bootstrap(void) { return -ENODEV; } + +#endif /* CONFIG_VM_PLANES */ + +#endif /* _LINUX_VM_PLANES_H */ diff --git a/init/Kconfig b/init/Kconfig index 10f2013b5321..bdb692f38a90 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -2259,6 +2259,24 @@ source "kernel/Kconfig.kexec" =20 source "kernel/liveupdate/Kconfig" =20 +config VM_PLANES + bool "Enable VM planes early boot support" if EXPERT + depends on KVM_GUEST + default n + help + Enable hypervisor-assisted multi-kernel (VM planes) support. + + When enabled, and when the VBS backend requests it, the kernel sets + up the configured planes from the VBS enable path (late_initcall, + after device drivers and before userspace). The plane configuration + and per-plane kernel images are read from the initramfs. + + The initrd config-vm-planes file is expected to provide per-plane + entries for PLANE__KERNEL, PLANE__LOAD_OFFSET, and + PLANE__MEMORY_SIZE. + + If unsure, say N. + endmenu # General setup =20 source "arch/Kconfig" diff --git a/init/Makefile b/init/Makefile index d6f75d8907e0..100b3fcda37a 100644 --- a/init/Makefile +++ b/init/Makefile @@ -14,6 +14,10 @@ endif obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) +=3D calibrate.o obj-$(CONFIG_INITRAMFS_TEST) +=3D initramfs_test.o =20 +# vm_planes.o must link AFTER initramfs.o so that it reads the plane config +# and kernels from the populated rootfs. +obj-$(CONFIG_VM_PLANES) +=3D vm_planes.o + obj-y +=3D init_task.o =20 mounts-y :=3D do_mounts.o diff --git a/init/vm_planes.c b/init/vm_planes.c new file mode 100644 index 000000000000..27994e4a915a --- /dev/null +++ b/init/vm_planes.c @@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_VM_PLANES + +#define VM_PLANES_CONFIG_FILE "config-vm-planes" +#define VM_PLANES_DEFAULT_COUNT 1 + +struct vm_plane_parse_state { + phys_addr_t load_offset; + phys_addr_t memory_size; + unsigned int kernel_format; + char kernel[VM_PLANE_KERNEL_NAME_MAX]; + char cmdline[VM_PLANE_CMDLINE_MAX]; +}; + +#define VM_PLANES_UNSET_VALUE ((phys_addr_t)~0) + +/* + * Read a file from the rootfs into a newly allocated buffer. + * Caller must kfree(*out_data) when done. + */ +static int __init vm_planes_read_file(const char *path, + void **out_data, loff_t *out_size) +{ + struct file *fp; + loff_t fsize; + void *buf; + ssize_t rd; + + fp =3D filp_open(path, O_RDONLY, 0); + if (IS_ERR(fp)) + return PTR_ERR(fp); + + fsize =3D i_size_read(file_inode(fp)); + if (fsize <=3D 0) { + fput(fp); + return -ENODATA; + } + + buf =3D kvmalloc(fsize, GFP_KERNEL); + if (!buf) { + fput(fp); + return -ENOMEM; + } + + rd =3D kernel_read(fp, buf, fsize, &(loff_t){0}); + fput(fp); + + if (rd !=3D fsize) { + kvfree(buf); + return (rd < 0) ? (int)rd : -EIO; + } + + *out_data =3D buf; + *out_size =3D fsize; + return 0; +} + +/* ---- Config file parser (unchanged) ---- */ + +static int __init parse_plane_count_line(const char *line, size_t len, + unsigned int *plane_count) +{ + const char *keys[] =3D { "PLANE_COUNT=3D", "CONFIG_PLANE_COUNT=3D" }; + unsigned int i; + + while (len && (*line =3D=3D ' ' || *line =3D=3D '\t')) { + line++; + len--; + } + + if (!len || *line =3D=3D '#') + return -ENOENT; + + for (i =3D 0; i < ARRAY_SIZE(keys); i++) { + size_t key_len =3D strlen(keys[i]); + size_t val_len =3D 0; + char tmp[32]; + + if (len <=3D key_len || strncmp(line, keys[i], key_len)) + continue; + + line +=3D key_len; + len -=3D key_len; + while (val_len < len && line[val_len] !=3D ' ' && + line[val_len] !=3D '\t' && line[val_len] !=3D '#') + val_len++; + + if (!val_len || val_len >=3D sizeof(tmp)) + return -EINVAL; + + memcpy(tmp, line, val_len); + tmp[val_len] =3D '\0'; + + if (kstrtouint(tmp, 0, plane_count)) + return -EINVAL; + if (!*plane_count) + return -EINVAL; + + return 0; + } + + return -ENOENT; +} + +static int __init parse_plane_count_kconfig(const char *buf, size_t len, + unsigned int *plane_count) +{ + const char *p =3D buf; + const char *end =3D buf + len; + + while (p < end) { + const char *eol =3D memchr(p, '\n', end - p); + size_t line_len =3D eol ? (size_t)(eol - p) : (size_t)(end - p); + int ret =3D parse_plane_count_line(p, line_len, plane_count); + + if (!ret) + return 0; + + p +=3D line_len; + if (p < end && *p =3D=3D '\n') + p++; + } + + return -ENOENT; +} + +static int __init parse_plane_cfg_line(const char *line, size_t len, + unsigned int plane_count, + struct vm_plane_config *plane_cfg, + struct vm_plane_parse_state *state) +{ + char tmp[VM_PLANE_CMDLINE_MAX + 64]; + char *p, *key, *val; + unsigned int plane_id; + u64 parsed_u64; + phys_addr_t parsed; + + if (len >=3D sizeof(tmp)) + return -E2BIG; + + memcpy(tmp, line, len); + tmp[len] =3D '\0'; + + p =3D strim(tmp); + if (!*p || *p =3D=3D '#') + return -ENOENT; + + val =3D strchr(p, '#'); + if (val) + *val =3D '\0'; + p =3D strim(p); + if (!*p) + return -ENOENT; + + if (!strncmp(p, "CONFIG_", 7)) + p +=3D 7; + + if (strncmp(p, "PLANE_", 6)) + return -ENOENT; + p +=3D 6; + + key =3D strchr(p, '_'); + if (!key) + return -ENOENT; + *key++ =3D '\0'; + + if (kstrtouint(p, 10, &plane_id) || plane_id >=3D plane_count) + return -EINVAL; + + val =3D strchr(key, '=3D'); + if (!val) + return -EINVAL; + *val++ =3D '\0'; + + key =3D strim(key); + val =3D strim(val); + if (!*val) + return -EINVAL; + + if (!strcmp(key, "KERNEL")) { + size_t val_len =3D strlen(val); + + if (val[0] =3D=3D '"') { + if (val_len < 2 || val[val_len - 1] !=3D '"') + return -EINVAL; + val[val_len - 1] =3D '\0'; + val++; + val =3D strim(val); + } + + if (!*val) + return -EINVAL; + + if (strscpy(plane_cfg[plane_id].kernel, val, + sizeof(plane_cfg[plane_id].kernel)) < 0) + return -EINVAL; + + strscpy(state[plane_id].kernel, val, + sizeof(state[plane_id].kernel)); + return 0; + } + + if (!strcmp(key, "KERNEL_FORMAT")) { + unsigned int fmt; + + if (!strcasecmp(val, "raw")) + fmt =3D VM_PLANE_KFMT_RAW; + else if (!strcasecmp(val, "bzimage")) + fmt =3D VM_PLANE_KFMT_BZIMAGE; + else if (!strcasecmp(val, "elf")) + fmt =3D VM_PLANE_KFMT_ELF; + else + return -EINVAL; + + plane_cfg[plane_id].kernel_format =3D fmt; + state[plane_id].kernel_format =3D fmt; + return 0; + } + + if (!strcmp(key, "CMDLINE")) { + size_t val_len =3D strlen(val); + + if (val_len >=3D 2 && val[0] =3D=3D '"') { + if (val[val_len - 1] !=3D '"') + return -EINVAL; + val[val_len - 1] =3D '\0'; + val++; + } + + if (strscpy(plane_cfg[plane_id].cmdline, val, + sizeof(plane_cfg[plane_id].cmdline)) < 0) + return -E2BIG; + + strscpy(state[plane_id].cmdline, val, + sizeof(state[plane_id].cmdline)); + return 0; + } + + if (kstrtou64(val, 0, &parsed_u64)) + return -EINVAL; + + if (parsed_u64 > (u64)VM_PLANES_UNSET_VALUE) + return -ERANGE; + + parsed =3D (phys_addr_t)parsed_u64; + + if (!strcmp(key, "LOAD_OFFSET")) { + plane_cfg[plane_id].load_offset =3D parsed; + state[plane_id].load_offset =3D parsed; + return 0; + } + + if (!strcmp(key, "MEMORY_SIZE")) { + plane_cfg[plane_id].memory_size =3D parsed; + state[plane_id].memory_size =3D parsed; + return 0; + } + + return -ENOENT; +} + +static int __init parse_vm_planes_kconfig(const char *buf, size_t len, + unsigned int *plane_count, + struct vm_plane_config **plane_cfg) +{ + const char *p =3D buf; + const char *end =3D buf + len; + struct vm_plane_parse_state *state; + unsigned int i; + int ret; + + ret =3D parse_plane_count_kconfig(buf, len, plane_count); + if (ret) + return ret; + + if (*plane_count > UINT_MAX / sizeof(**plane_cfg)) + return -E2BIG; + + *plane_cfg =3D kzalloc(*plane_count * sizeof(**plane_cfg), GFP_KERNEL); + if (!*plane_cfg) + return -ENOMEM; + + state =3D kzalloc(*plane_count * sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + for (i =3D 0; i < *plane_count; i++) { + state[i].load_offset =3D VM_PLANES_UNSET_VALUE; + state[i].memory_size =3D VM_PLANES_UNSET_VALUE; + state[i].kernel[0] =3D '\0'; + state[i].cmdline[0] =3D '\0'; + } + + while (p < end) { + const char *eol =3D memchr(p, '\n', end - p); + size_t line_len =3D eol ? (size_t)(eol - p) : (size_t)(end - p); + + ret =3D parse_plane_cfg_line(p, line_len, *plane_count, + *plane_cfg, state); + if (ret && ret !=3D -ENOENT) + return ret; + + p +=3D line_len; + if (p < end && *p =3D=3D '\n') + p++; + } + + for (i =3D 1; i < *plane_count; i++) { + if (state[i].load_offset =3D=3D VM_PLANES_UNSET_VALUE || + state[i].memory_size =3D=3D VM_PLANES_UNSET_VALUE || + !state[i].kernel[0]) + return -EINVAL; + } + + kfree(state); + return 0; +} + +/* ---- Config loading via VFS ---- */ + +static int __init vm_planes_get_cfg(unsigned int *plane_count, + struct vm_plane_config **plane_cfg) +{ + void *buf; + loff_t size; + int ret; + + ret =3D vm_planes_read_file("/" VM_PLANES_CONFIG_FILE, &buf, &size); + if (ret) { + pr_err("vm_planes: cannot read /%s: %d\n", + VM_PLANES_CONFIG_FILE, ret); + return ret; + } + + ret =3D parse_vm_planes_kconfig(buf, (size_t)size, plane_count, plane_cfg= ); + kvfree(buf); + return ret; +} + +/* ---- Kernel loading ---- */ + +static int __init copy_to_early_mem(phys_addr_t dest, const void *src, + unsigned long size) +{ + void *p; + + if (!size) + return 0; + p =3D memremap(dest, size, MEMREMAP_WB); + if (!p) + return -ENOMEM; + memcpy(p, src, size); + memunmap(p); + return 0; +} + +static int __init zero_early_mem(phys_addr_t dest, unsigned long size) +{ + void *p; + + if (!size) + return 0; + p =3D memremap(dest, size, MEMREMAP_WB); + if (!p) + return -ENOMEM; + memset(p, 0, size); + memunmap(p); + return 0; +} + +static int __init load_plane_kernel_elf(const u8 *data, u32 size, + struct vm_plane_config *cfg) +{ + const Elf64_Ehdr *ehdr; + const Elf64_Phdr *phdr; + unsigned int i; + int ret; + + if (size < sizeof(*ehdr)) { + pr_err("vm_planes: ELF image too small (%u bytes)\n", size); + return -EINVAL; + } + + ehdr =3D (const Elf64_Ehdr *)data; + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + pr_err("vm_planes: not a valid ELF image\n"); + return -EINVAL; + } + + if (ehdr->e_ident[EI_CLASS] !=3D ELFCLASS64 || + ehdr->e_ident[EI_DATA] !=3D ELFDATA2LSB || + ehdr->e_type !=3D ET_EXEC || + ehdr->e_machine !=3D EM_X86_64) { + pr_err("vm_planes: unsupported ELF format (need x86_64 ET_EXEC LE)\n"); + return -EINVAL; + } + + if (!ehdr->e_phnum || ehdr->e_phentsize !=3D sizeof(Elf64_Phdr)) { + pr_err("vm_planes: invalid ELF program headers\n"); + return -EINVAL; + } + + if (ehdr->e_phoff + (u64)ehdr->e_phnum * sizeof(Elf64_Phdr) > size) { + pr_err("vm_planes: ELF program headers extend beyond file\n"); + return -EINVAL; + } + + phdr =3D (const Elf64_Phdr *)(data + ehdr->e_phoff); + + for (i =3D 0; i < ehdr->e_phnum; i++, phdr++) { + phys_addr_t dest; + u64 bss_size; + + if (phdr->p_type !=3D PT_LOAD) + continue; + + if (!phdr->p_memsz) + continue; + + /* + * Bias the ELF physical address by load_offset so that the + * kernel's link-time p_paddr values are treated as offsets + * within the plane's memory region. + */ + dest =3D cfg->load_offset + phdr->p_paddr; + + if (dest < cfg->load_offset || + dest + phdr->p_memsz > cfg->load_offset + cfg->memory_size) { + pr_err("vm_planes: ELF PT_LOAD at 0x%llx+0x%llx outside plane [0x%llx..= 0x%llx]\n", + (unsigned long long)dest, + (unsigned long long)phdr->p_memsz, + (unsigned long long)cfg->load_offset, + (unsigned long long)(cfg->load_offset + cfg->memory_size)); + return -EINVAL; + } + + if (phdr->p_offset + phdr->p_filesz > size) { + pr_err("vm_planes: ELF PT_LOAD file data beyond image\n"); + return -EINVAL; + } + + if (phdr->p_filesz) { + ret =3D copy_to_early_mem(dest, data + phdr->p_offset, + phdr->p_filesz); + if (ret) + return ret; + } + + bss_size =3D phdr->p_memsz - phdr->p_filesz; + if (bss_size) { + ret =3D zero_early_mem(dest + phdr->p_filesz, bss_size); + if (ret) + return ret; + } + + /* + * Compute the physical entry point: if e_entry falls within + * this segment's virtual range, convert vaddr=E2=86=92paddr and bias. + * Also handle kernels where e_entry is already a physical + * address by checking the p_paddr range as a fallback. + */ + if (ehdr->e_entry >=3D phdr->p_vaddr && + ehdr->e_entry < phdr->p_vaddr + phdr->p_memsz) + cfg->entry_point =3D cfg->load_offset + + phdr->p_paddr + (ehdr->e_entry - phdr->p_vaddr); + else if (ehdr->e_entry >=3D phdr->p_paddr && + ehdr->e_entry < phdr->p_paddr + phdr->p_memsz) + cfg->entry_point =3D cfg->load_offset + ehdr->e_entry; + + pr_info("vm_planes: ELF PT_LOAD: paddr=3D0x%llx filesz=3D0x%llx memsz=3D= 0x%llx\n", + (unsigned long long)dest, + (unsigned long long)phdr->p_filesz, + (unsigned long long)phdr->p_memsz); + } + + if (!cfg->entry_point) { + pr_err("vm_planes: ELF entry point 0x%llx not in any PT_LOAD segment\n", + (unsigned long long)ehdr->e_entry); + return -EINVAL; + } + pr_info("vm_planes: ELF entry point: 0x%llx (virt 0x%llx)\n", + (unsigned long long)cfg->entry_point, + (unsigned long long)ehdr->e_entry); + + return 0; +} + +static int __init load_plane_kernel_raw(const u8 *data, u32 size, + struct vm_plane_config *cfg) +{ + if (size > cfg->memory_size) { + pr_err("vm_planes: raw kernel image (%u bytes) exceeds plane memory (%ll= u bytes)\n", + size, (unsigned long long)cfg->memory_size); + return -ENOMEM; + } + + cfg->entry_point =3D cfg->load_offset; + return copy_to_early_mem(cfg->load_offset, data, size); +} + +int __init load_vm_plane_kernels(unsigned int plane_count, + struct vm_plane_config *plane_cfg) +{ + unsigned int i; + int err =3D 0; + + for (i =3D 1; i < plane_count; i++) { + void *data; + loff_t fsize; + int ret; + + ret =3D vm_planes_read_file(plane_cfg[i].kernel, &data, &fsize); + if (ret) { + pr_err("vm_planes: plane %u: kernel '%s' not found: %d\n", + i, plane_cfg[i].kernel, ret); + err =3D ret; + continue; + } + + switch (plane_cfg[i].kernel_format) { + case VM_PLANE_KFMT_RAW: + ret =3D load_plane_kernel_raw(data, (u32)fsize, + &plane_cfg[i]); + break; + case VM_PLANE_KFMT_ELF: + ret =3D load_plane_kernel_elf(data, (u32)fsize, + &plane_cfg[i]); + break; + case VM_PLANE_KFMT_BZIMAGE: + pr_err("vm_planes: plane %u: bzImage format not yet supported\n", + i); + err =3D -ENOSYS; + kvfree(data); + continue; + default: + pr_err("vm_planes: plane %u: unknown kernel format %u\n", + i, plane_cfg[i].kernel_format); + err =3D -EINVAL; + kvfree(data); + continue; + } + + if (ret) { + pr_err("vm_planes: plane %u: failed to load kernel: %d\n", + i, ret); + err =3D ret; + } else { + pr_info("vm_planes: plane %u: loaded '%s' (%lld bytes) at 0x%llx\n", + i, plane_cfg[i].kernel, fsize, + (unsigned long long)plane_cfg[i].load_offset); + } + + kvfree(data); + } + + return err; +} + +/* ---- Activation ---- */ + +int __init __weak alloc_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg) { return -ENOSYS; } + +int __init __weak activate_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg) { return -ENOSYS; } + +/* + * Set up VM planes during boot. + * + * Invoked from the VBS enable path at late_initcall: after device drivers + * have initialised (so the rootfs is populated and the plane config and + * kernels can be read) and before userspace starts, so the secure plane + * vcpu exists by the time the first VTL call is issued. + */ +int __init vm_planes_bootstrap(void) +{ + unsigned int plane_count =3D VM_PLANES_DEFAULT_COUNT; + struct vm_plane_config *plane_cfg; + int ret; + + /* Ensure any asynchronous initramfs unpacking has completed. */ + wait_for_initramfs(); + + if (!kvm_para_available()) { + pr_info("vm_planes: KVM paravirt unavailable, skipping plane setup\n"); + return -ENODEV; + } + + ret =3D vm_planes_get_cfg(&plane_count, &plane_cfg); + if (ret) { + pr_warn("vm_planes: failed to parse %s: %d\n", + VM_PLANES_CONFIG_FILE, ret); + return ret; + } + + pr_info("vm_planes: enabling %u planes (ids 0..%u)\n", + plane_count, plane_count - 1); + + ret =3D alloc_vm_planes(plane_count, plane_cfg); + if (ret) { + pr_err("vm_planes: failed to allocate planes: %d\n", ret); + return ret; + } + + ret =3D load_vm_plane_kernels(plane_count, plane_cfg); + if (ret) { + pr_err("vm_planes: failed to load plane kernels: %d\n", ret); + return ret; + } + + ret =3D activate_vm_planes(plane_count, plane_cfg); + if (ret) + pr_err("vm_planes: failed to activate planes: %d\n", ret); + + return ret; +} + +#endif /* CONFIG_VM_PLANES */ --=20 2.55.0 From nobody Tue Sep 29 07:39:12 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C5478397928; Tue, 11 Aug 2026 01:53:05 +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=1786413187; cv=none; b=YzHQ0rgAhyPnWuBF/biUSUtVj5RMXM3D7QmEwzuaR9vaqOSi3dp42ncaB5yEDn7SQBaq+tnQg/vbFhhmf7FScI5qN7+N9gMd8zgXImePMuQyQNNwsSsx9COGuGIQnKYL96YE3xT3GR3PDvbkx6heh1vvqKKNOnbygd8qcclFxiI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786413187; c=relaxed/simple; bh=USSkt37zjiXpaOXdgBUUDjvtIRuME9JAeB4aUsmjfyE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=M5uOqjem3XVFz1IakFJH/6FGcYEOfv00j8D06UUW72fud00gK7/VMGc0ymBIzqc0nwDHJ0kbJ+tKLnjPt0Er1qRrTvAxhDjHmAGQRX0bLlfgAw/inigDcV2Nx4R92WNYcpyrf0dDb8nKO2SoznPIf3WIotYMhN1F6ll3z2/5YME= 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=prelG2wa; 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="prelG2wa" Received: from fedora (unknown [20.191.74.188]) by linux.microsoft.com (Postfix) with ESMTPSA id BAF9F20B7167; Mon, 10 Aug 2026 18:52:41 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com BAF9F20B7167 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786413161; bh=Pcwn2qTvc4F7UFe7dpQt3hY0hptv0VmVSVeDqOtxU0I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=prelG2waOEkg+YHrpjADM1dxegjor/NYa52LbtEHFtAwKCEQbss793J4A3cARuI5H LgffnUJUV7OP5tcyoX6lqgAsvoEgcg6godkTUx7cR5W97Z80XZ4QzAmu69hDV3jUdv 9UzBHEzdhse5a0VIotnLLrcBYoOa6ykfMB3CaCHE= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 7/8] security/vbs: bootstrap the plane from the enable path Date: Mon, 10 Aug 2026 18:52:42 -0700 Message-ID: <20260811015243.188486-8-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com> References: <20260811015243.188486-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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When the KVM software-planes backend is enabled, create and activate the secure plane before the backend issues its first VTL call. vbs_enable() runs at late_initcall -- after device drivers have initialised and before userspace starts -- which is where vm_planes_bootstrap() now runs. Select VM_PLANES from VBS_KVM_PLANES so the plane bootstrap is built in whenever the backend is. --- security/vbs/Kconfig | 1 + security/vbs/core.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/security/vbs/Kconfig b/security/vbs/Kconfig index e21f4f30b6cf..7a2ebc13e479 100644 --- a/security/vbs/Kconfig +++ b/security/vbs/Kconfig @@ -18,6 +18,7 @@ config VBS config VBS_KVM_PLANES bool "VBS backend: KVM software planes" depends on VBS && KVM_GUEST + select VM_PLANES help VBS backend that uses a KVM paravirt hypercall to communicate between plane-0 (the normal guest kernel) and plane-1 (a secure diff --git a/security/vbs/core.c b/security/vbs/core.c index c006b6d53a14..8dd4567bcb9c 100644 --- a/security/vbs/core.c +++ b/security/vbs/core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include =20 static const struct vbs_ops *vbs_backend; @@ -154,6 +155,17 @@ static int __init vbs_enable(void) if (!vbs_plane_config_present()) return 0; =20 + /* + * Create and activate the secure plane before the backend issues its + * first VTL call. A failure here leaves the backend idle. + */ + ret =3D vm_planes_bootstrap(); + if (ret) { + pr_warn("vbs: plane bootstrap failed (%d); backend \"%s\" left idle\n", + ret, ops->name); + return 0; + } + if (ops->init) { ret =3D ops->init(); if (ret) { --=20 2.55.0 From nobody Tue Sep 29 07:39:12 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 28B88396D1C; Tue, 11 Aug 2026 01:53:07 +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=1786413188; cv=none; b=Sp12GlY6vPKLbxiDoKIabXQbRK9QlJ0a+eUGdL4562v0HaE5YfnVdQEJh60BHic3mK627k6jzXx/H4PqwbBIdGm4erz0/vVcKLV81enmCwSVSbKL2Io96OelI4EJEJoJbM5HmemBbAr+4svbcosZ8n2IMkyc2UvStDEpW1RqO+I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786413188; c=relaxed/simple; bh=Ye+FKb+O4vDOZT+EEdbqG3BJt9HeESDb5F9zr4JuXXM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=CWnKeI7gZYa0Vsz9hrF2x5Qoidrg7oKBCXlS9dRHbGjJ9pf9OgvXrCdillziA12HykAqtZXVfjBNiOei3KKlqXV3rye1J17/zYMg2f6t71VTAML97clnZMfDPIRYaKDfRbxMUqdxkcqWvmpx5AbgS87f25D5RuZTfwWa24ZDQAQ= 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=QzIPO/SV; 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="QzIPO/SV" Received: from fedora (unknown [20.191.74.188]) by linux.microsoft.com (Postfix) with ESMTPSA id 6068520B7129; Mon, 10 Aug 2026 18:52:42 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 6068520B7129 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786413162; bh=7OQNua6H09fypXrgEyrMYCrbqK4v9ks9MhiALwXkFTM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QzIPO/SVo1ofRn+EWdsKs5Rbw1AbCUdyP6cCVkBX92ZfMv5QCVLFiuko8hI51SsJN w+zobnBQXM9pwspjSZEjzLnlX0+gWFH0rmkRc8c68wBpr/aaMuDnRXRJVJphbt28Eq q1yK/sLgpqucvVDbupW+HzEf07KPGpv689a9OKtw= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 8/8] drivers/virt: add KVM VM-planes secure-plane monitor Date: Mon, 10 Aug 2026 18:52:43 -0700 Message-ID: <20260811015243.188486-9-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com> References: <20260811015243.188486-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: quoted-printable Add the secure-plane (plane >0) side of the VM-planes park/dispatch handshake, so an otherwise ordinary kernel can act as the secure plane without the full VBS stack. Activated by the "secure_monitor" kernel command-line option, a late_initcall kthread hands control back to the normal plane via KVM_HC_VBS_VTL_RETURN and then services VTL calls from the shared calling area (matching struct vbs_kvm_ca in security/vbs). Calls are acknowledged as no-ops for now; real per-call handlers are added incrementally. The same option also skips the sub-1M real-mode trampoline (arch/x86/realmode/init.c), which the secure plane neither has memory for nor uses. Gated by CONFIG_VBS_SECURE_MONITOR. --- arch/x86/realmode/init.c | 21 +++++ drivers/virt/Kconfig | 15 ++++ drivers/virt/Makefile | 1 + drivers/virt/secure_monitor.c | 141 ++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 drivers/virt/secure_monitor.c diff --git a/arch/x86/realmode/init.c b/arch/x86/realmode/init.c index 694d80a5c68e..d9d73cd892bf 100644 --- a/arch/x86/realmode/init.c +++ b/arch/x86/realmode/init.c @@ -44,6 +44,27 @@ void load_trampoline_pgtable(void) __flush_tlb_all(); } =20 +#ifdef CONFIG_VBS_SECURE_MONITOR +/* + * A KVM VM-planes secure plane (plane > 0) is entered directly in 64-bit = long + * mode and boots from a single carved-out high-memory region that contain= s no + * RAM below 1 MiB. It runs with no firmware, ACPI sleep, or hibernation,= so + * the 16-bit real-mode trampoline can neither be allocated (there is no + * sub-1M memory) nor is it ever used. Disable the real-mode setup from an + * early_param so it takes effect before setup_arch() calls + * x86_platform.realmode_reserve(); triggered by the "secure_monitor" opti= on, + * the same switch that activates the in-kernel secure-plane monitor. + */ +static int __init secure_plane_no_real_mode(char *arg) +{ + x86_platform.realmode_reserve =3D x86_init_noop; + x86_platform.realmode_init =3D x86_init_noop; + pr_info("realmode: secure plane: skipping sub-1M trampoline\n"); + return 0; +} +early_param("secure_monitor", secure_plane_no_real_mode); +#endif /* CONFIG_VBS_SECURE_MONITOR */ + void __init reserve_real_mode(void) { phys_addr_t mem, limit =3D x86_init.resources.realmode_limit; diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 52eb7e4ba71f..bb1a7de559c3 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -13,6 +13,21 @@ menuconfig VIRT_DRIVERS =20 if VIRT_DRIVERS =20 +config VBS_SECURE_MONITOR + bool "KVM VM-planes secure-plane monitor" + depends on X86 && KVM_GUEST + help + 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 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" default y diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index f29901bd7820..22d1121ba5bd 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -5,6 +5,7 @@ =20 obj-$(CONFIG_FSL_HV_MANAGER) +=3D fsl_hypervisor.o obj-$(CONFIG_VMGENID) +=3D vmgenid.o +obj-$(CONFIG_VBS_SECURE_MONITOR) +=3D secure_monitor.o obj-y +=3D vboxguest/ =20 obj-$(CONFIG_NITRO_ENCLAVES) +=3D nitro_enclaves/ diff --git a/drivers/virt/secure_monitor.c b/drivers/virt/secure_monitor.c new file mode 100644 index 000000000000..4fe3ceb051e3 --- /dev/null +++ b/drivers/virt/secure_monitor.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * secure_monitor - KVM VM-planes secure-plane monitor + * + * 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, though the index is not hard-coded) without pu= lling + * in the full VBS 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 a= nd + * 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): + * + * normal plane KVM secure plane + * ------------ --- ------------ + * fill calling area + * HC_VBS_VTL_CALL(ca_gpa) =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=96=B6 switch_plane =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=96=B6 resume in + * (RAX :=3D ca_gpa) secmon_vt= l_return() + * dispatch(cal= l_id) + * write ca->st= atus + * resume after VTL_CALL =E2=97=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80 switch_plane =E2=97=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= HC_VBS_VTL_RETURN + * + * All planes of a VM share the same memslots, so the secure plane sees the + * same guest-physical address space as the normal plane and can read the + * calling area directly. Every VTL call is acknowledged as a no-op so the + * normal plane can make progress; real per-call handlers 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-secmon: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Shared-memory calling area. MUST match struct vbs_kvm_ca in + * security/vbs/kvm_planes.c (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; /* request 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 "secure_monitor" kernel command-line option. */ +static bool secmon_active __ro_after_init; + +static int __init secmon_setup(char *str) +{ + secmon_active =3D true; + return 1; +} +__setup("secure_monitor", 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); +} + +static int secmon_monitor_fn(void *unused) +{ + long status =3D 0; + + pr_info("secure monitor started\n"); + + for (;;) { + struct vbs_kvm_ca *ca; + u64 ca_gpa; + + /* Park; resume with the next request's calling-area GPA. */ + ca_gpa =3D secmon_vtl_return(status); + if (!ca_gpa) { + status =3D -EINVAL; + continue; + } + + ca =3D memremap(ca_gpa, PAGE_SIZE, MEMREMAP_WB); + if (!ca) { + pr_err_ratelimited("failed to map calling area 0x%llx\n", + ca_gpa); + status =3D -EFAULT; + continue; + } + + /* + * 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=3D0x%x arg_size=3D%u (no-op)\n", + ca->call_id, ca->arg_size); + ca->status =3D 0; + ca->resp_size =3D 0; + status =3D 0; + + memunmap(ca); + } + + return 0; +} + +static int __init secmon_init(void) +{ + struct task_struct *t; + + if (!secmon_active) + return 0; + + t =3D 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); --=20 2.55.0