From: Thara Gopinath <tgopinath@linux.microsoft.com>
To: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, tglx@kernel.org, mingo@redhat.com,
bp@alien8.de, dave.hansen@linux.intel.com, hpa@zytor.com,
ardb@kernel.org, ilias.apalodimas@linaro.org
Cc: James.Bottomley@HansenPartnership.com,
"longli@microsoft.com--cc=tzimmermann"@suse.de,
javierm@redhat.com, lszubowi@redhat.com,
francescopompo2@gmail.com, tgopinath@microsoft.com,
x86@kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-efi@vger.kernel.org,
Thara Gopinath <tgopinath@linux.microsoft.com>
Subject: [RFC PATCH 06/12] drivers: hv: Add VSM boot driver and enable VTL1 at the partition level
Date: Tue, 1 Sep 2026 09:55:20 -0700 [thread overview]
Message-ID: <20260901165647.3160413-7-tgopinath@linux.microsoft.com> (raw)
In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com>
Linux VBS (LVBS) uses Hyper-V's Virtual Secure Mode to run a small
trusted kernel in VTL1 alongside the regular VTL0 kernel, so that
security-sensitive state (e.g. hypervisor-enforced code integrity,
credential isolation) can live behind a higher-privilege boundary
that VTL0 compromise cannot cross. Bringing that up from Linux
requires the VTL0 kernel to drive the VSM setup itself.
Add drivers/hv/hv_vsm_boot.c as the entry point for that sequence.
This first step handles partition-level VTL1 enable only:
- Probe VSM / VP-register privileges and SynIC availability before
doing anything.
- Pin init to the VTL0 boot CPU so VTL1 comes up on the same CPU
(later patches rely on this).
- Read HV_REGISTER_VSM_PARTITION_STATUS, and if VTL1 is not already
enabled, issue HVCALL_ENABLE_PARTITION_VTL with MBEC and confirm
by re-reading the register.
Signed-off-by: Thara Gopinath <tgopinath@linux.microsoft.com>
---
drivers/hv/Makefile | 4 +-
drivers/hv/hv_vsm_boot.c | 178 +++++++++++++++++++++++++++++++++++++++
2 files changed, 180 insertions(+), 2 deletions(-)
create mode 100644 drivers/hv/hv_vsm_boot.c
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index 880c570832381..563ebc36d2700 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -22,7 +22,7 @@ mshv_vtl-y := mshv_vtl_main.o
# Code that must be built-in
obj-$(CONFIG_HYPERV) += hv_common.o
obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) += hv_proc.o
-ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL),)
+ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL)$(CONFIG_HYPERV_VSM),)
obj-y += mshv_common.o
endif
-obj-$(subst m,y,$(CONFIG_HYPERV_VSM)) += hv_vsm_securekernel.o
+obj-$(subst m,y,$(CONFIG_HYPERV_VSM)) += hv_vsm_securekernel.o hv_vsm_boot.o
diff --git a/drivers/hv/hv_vsm_boot.c b/drivers/hv/hv_vsm_boot.c
new file mode 100644
index 0000000000000..99e4dc8695837
--- /dev/null
+++ b/drivers/hv/hv_vsm_boot.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * VSM boot framework that enables VTL1, loads secure kernel
+ * and boots VTL1.
+ *
+ * Copyright (c) 2023-2025, Microsoft Corporation.
+ *
+ * Author: Thara Gopinath <tgopinath@linux.microsoft.com>
+ *
+ */
+
+#define pr_fmt(fmt) "vsm: " fmt
+
+#include <linux/hyperv.h>
+#include <linux/cpumask.h>
+#include <asm/mshyperv.h>
+#include "mshv.h"
+
+#define HV_VTL1_ENABLE_BIT BIT(1)
+
+static int hv_vsm_get_register(u32 reg_name, u64 *result)
+{
+ struct hv_register_assoc reg = {
+ .name = reg_name,
+ };
+ union hv_input_vtl input_vtl = {
+ .as_uint8 = 0,
+ };
+ int ret;
+
+ ret = hv_call_get_vp_registers(HV_VP_INDEX_SELF,
+ HV_PARTITION_ID_SELF,
+ 1, input_vtl, ®);
+ if (ret)
+ return ret;
+
+ *result = reg.value.reg64;
+ return 0;
+}
+
+static int __init hv_vsm_enable_partition_vtl(void)
+{
+ u64 status = 0;
+ unsigned long flags;
+ struct hv_input_enable_partition_vtl *hvin = NULL;
+
+ local_irq_save(flags);
+
+ hvin = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ memset(hvin, 0, sizeof(*hvin));
+
+ hvin->partition_id = HV_PARTITION_ID_SELF;
+ hvin->target_vtl.as_uint8 = 1;
+ hvin->flags.enable_mbec = 1;
+
+ status = hv_do_hypercall(HVCALL_ENABLE_PARTITION_VTL, hvin, NULL);
+ if (hv_result(status))
+ pr_err("Enable Partition VTL failed. status=0x%x\n",
+ hv_result(status));
+
+ local_irq_restore(flags);
+
+ return hv_result(status);
+}
+
+static int __init hv_vsm_get_partition_status(u16 *enabled_vtl_set, u8 *max_vtl,
+ u16 *mbec_enabled_vtl_set)
+{
+ u64 result;
+ int ret;
+ union hv_register_vsm_partition_status vsm_partition_status = { 0 };
+
+ ret = hv_vsm_get_register(HV_REGISTER_VSM_PARTITION_STATUS, &result);
+ if (ret)
+ return ret;
+
+ vsm_partition_status = (union hv_register_vsm_partition_status)result;
+ *enabled_vtl_set = vsm_partition_status.enabled_vtl_set;
+ *max_vtl = vsm_partition_status.max_vtl;
+ *mbec_enabled_vtl_set = vsm_partition_status.mbec_enabled_vtl_set;
+ return 0;
+}
+
+static int __init hv_vsm_bootstrap_vtl(void)
+{
+ u16 partition_enabled_vtl_set = 0, partition_mbec_enabled_vtl_set = 0;
+ u8 partition_max_vtl;
+ int ret;
+
+ /* Check and enable VTL1 at the partition level */
+ ret = hv_vsm_get_partition_status(&partition_enabled_vtl_set, &partition_max_vtl,
+ &partition_mbec_enabled_vtl_set);
+ if (ret)
+ return ret;
+
+ if (partition_max_vtl < HV_VTL_SECURE) {
+ pr_err("VTL1 is not supported by the partition\n");
+ return -EINVAL;
+ }
+
+ if (partition_enabled_vtl_set & HV_VTL1_ENABLE_BIT) {
+ pr_info("Partition VTL1 is already enabled\n");
+ } else {
+ ret = hv_vsm_enable_partition_vtl();
+ if (ret) {
+ pr_err("Enabling Partition VTL1 failed with status 0x%x\n",
+ ret);
+ return -EINVAL;
+ }
+ ret = hv_vsm_get_partition_status(&partition_enabled_vtl_set, &partition_max_vtl,
+ &partition_mbec_enabled_vtl_set);
+ if (ret)
+ return ret;
+ if (!(partition_enabled_vtl_set & HV_VTL1_ENABLE_BIT)) {
+ pr_err("Tried Enabling Partition VTL 1 and still failed\n");
+ return -EINVAL;
+ }
+ if (!partition_mbec_enabled_vtl_set) {
+ pr_err("Tried Enabling Partition MBEC and failed\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static bool __init vsm_arch_has_vsm_access(void)
+{
+ if (!(ms_hyperv.features & HV_MSR_SYNIC_AVAILABLE))
+ return false;
+ if (!(ms_hyperv.priv_high & HV_ACCESS_VSM))
+ return false;
+ if (!(ms_hyperv.priv_high & HV_ACCESS_VP_REGS))
+ return false;
+ return true;
+}
+
+static int __init hv_vsm_boot_init(void)
+{
+ cpumask_var_t mask;
+ unsigned int boot_cpu;
+ int ret;
+
+ if (!vsm_arch_has_vsm_access())
+ return 0;
+
+ /*
+ * Copy the current cpu mask and pin rest of the running code to boot cpu.
+ * Important since we want boot cpu of VTL0 to be the boot cpu for VTL1.
+ * ToDo: Check if copying and restoring current->cpus_mask is enough
+ * ToDo: Verify the assumption that cpumask_first(cpu_online_mask) is
+ * the boot cpu
+ */
+ if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+ panic("Could not allocate cpumask");
+
+ cpumask_copy(mask, ¤t->cpus_mask);
+ boot_cpu = cpumask_first(cpu_online_mask);
+ set_cpus_allowed_ptr(current, cpumask_of(boot_cpu));
+
+ ret = hv_vsm_bootstrap_vtl();
+ /*
+ * At this point VTL0 has already advertised VSM support to the
+ * bootloader/firmware via the Hyper-V OsLoaderIndications EFI
+ * variable (see the x86-stub change). That signals the platform
+ * that a trusted VTL1 will be brought up. If we fail to actually
+ * set VTL1 up here, the partition is left in a state where an
+ * attacker could race to configure VTL1 themselves and gain a
+ * higher-privilege foothold than VTL0. Panic rather than continue
+ * running with that exposure.
+ */
+ if (ret)
+ panic("VTL1 boot failure caused kernel panic; consult log for more details.\n");
+
+ set_cpus_allowed_ptr(current, mask);
+ free_cpumask_var(mask);
+ return ret;
+}
+device_initcall(hv_vsm_boot_init);
--
2.34.1
next prev parent reply other threads:[~2026-09-01 16:57 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-01 16:55 [RFC PATCH 00/12] Introduce LVBS support for Hyper-V guests Thara Gopinath
2026-09-01 16:55 ` [RFC PATCH 01/12] drivers: hv: Add HYPERV_VSM kconfig option Thara Gopinath
2026-09-01 16:55 ` [RFC PATCH 02/12] drivers: hv: hv_common: Allocate Hyper-V output arg page when VSM is enabled Thara Gopinath
2026-09-01 17:12 ` sashiko-bot
2026-09-01 22:56 ` Wei Liu
2026-09-01 16:55 ` [RFC PATCH 03/12] drivers: hv: Reserve memory for VSM secure kernel during early boot Thara Gopinath
2026-09-01 17:10 ` sashiko-bot
2026-09-02 0:59 ` Wei Liu
2026-09-02 13:38 ` Thara Gopinath
2026-09-01 16:55 ` [RFC PATCH 04/12] firmware: efi: libstub: x86-stub: Enable VSM awareness in efi os indications variable Thara Gopinath
2026-09-01 17:09 ` sashiko-bot
2026-09-02 1:09 ` Wei Liu
2026-09-02 14:23 ` Thara Gopinath
2026-09-01 16:55 ` [RFC PATCH 05/12] include: hyperv: hvgdk_mini.h: Add VTL-specific structures and bits Thara Gopinath
2026-09-01 16:55 ` Thara Gopinath [this message]
2026-09-01 17:24 ` [RFC PATCH 06/12] drivers: hv: Add VSM boot driver and enable VTL1 at the partition level sashiko-bot
2026-09-02 1:16 ` Wei Liu
2026-09-02 14:28 ` Thara Gopinath
2026-09-02 4:43 ` Wei Liu
2026-09-04 13:23 ` Thara Gopinath
2026-09-01 16:55 ` [RFC PATCH 07/12] drivers: hv: hv_vsm_boot: load secure kernel image from firmware Thara Gopinath
2026-09-01 17:20 ` sashiko-bot
2026-09-02 4:37 ` Wei Liu
2026-09-02 16:22 ` Thara Gopinath
2026-09-02 22:58 ` Wei Liu
2026-09-01 16:55 ` [RFC PATCH 08/12] arch: x86: hyperv: Build initial vCPU context for VTL1 secure kernel Thara Gopinath
2026-09-01 17:25 ` sashiko-bot
2026-09-01 16:55 ` [RFC PATCH 09/12] drivers: hv: hv_vsm_boot: Enable VTL1 on the boot processor Thara Gopinath
2026-09-01 17:36 ` sashiko-bot
2026-09-01 16:55 ` [RFC PATCH 10/12] arch: x86: hyperv: hv_vtl_vsm: Introduce vtlcall Thara Gopinath
2026-09-01 16:55 ` [RFC PATCH 11/12] drivers: hv: hv_vsm_boot: Boot primary processor in VTL1 Thara Gopinath
2026-09-01 17:35 ` sashiko-bot
2026-09-01 16:55 ` [RFC PATCH 12/12] drivers: hv: hv_vsm_boot: Boot secondary processors " Thara Gopinath
2026-09-01 17:44 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260901165647.3160413-7-tgopinath@linux.microsoft.com \
--to=tgopinath@linux.microsoft.com \
--cc="longli@microsoft.com--cc=tzimmermann"@suse.de \
--cc=James.Bottomley@HansenPartnership.com \
--cc=ardb@kernel.org \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=decui@microsoft.com \
--cc=francescopompo2@gmail.com \
--cc=haiyangz@microsoft.com \
--cc=hpa@zytor.com \
--cc=ilias.apalodimas@linaro.org \
--cc=javierm@redhat.com \
--cc=kys@microsoft.com \
--cc=linux-efi@vger.kernel.org \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lszubowi@redhat.com \
--cc=mingo@redhat.com \
--cc=tglx@kernel.org \
--cc=tgopinath@microsoft.com \
--cc=wei.liu@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.