From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D9E8748422F; Tue, 1 Sep 2026 16:57:30 +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=1788281852; cv=none; b=hW/3nnLiChiGFbf33qkbUwrMjaRHMKa+F+PZTbLQ5vXfgIPJFd0x9pN3g1WysAmo5S7iW+twAKxe+khlQxjW76Ucmt/oWqotlMKMSvLcFBxyHFaDcTyzY9Jpa6hU6MezzJNehThkvNV3Fq4R7SVMcgxSgiAokPdpdQLI6scV2ME= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281852; c=relaxed/simple; bh=BYLNSpftBMqiAsSmrofow/DtQEm1FVYqb+nofRHTU0U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hzX0g5dYF9mYqKcuNEe+0vfKNQagwCiae4cIHpKlaOefuNXGfmtvHyBpA6nKHdQhjCF5hEQZVNHhPKJ4TdOSTeDKm7jldq0l63Dpv9WRWfrA+Yqu7v0y2gylMENZltzmcf8OnSIf0Eizj5M1OTIGODBRPZol5yCdi0VMvJpMmwE= 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=ByuK+gb5; 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="ByuK+gb5" Received: by linux.microsoft.com (Postfix, from userid 1130) id B049220B7128; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com B049220B7128 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281814; bh=pq3olvd2U6K9Wem0Ry+RFBQUO82EsXIj9NKtu2tnT/w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ByuK+gb5R6+Yu1yunYrmlM2ifm0fOym4YVQQvwo/SeSNZMCjDpGIcAKg9ZN1qw3qV hHuuIg2KUU1rzKgbVPtiNfFgQe5sBu/3Pw0GyjVsh5ypIeJf+WUN9R/8YgQizAJd36 sIeTfmow7t8EW9ZXC0XSwsL6Am6QiwL2q9X7tm/I= From: Thara Gopinath 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 Subject: [RFC PATCH 01/12] drivers: hv: Add HYPERV_VSM kconfig option Date: Tue, 1 Sep 2026 09:55:15 -0700 Message-ID: <20260901165647.3160413-2-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" Introduce CONFIG_HYPERV_VSM, the top-level switch that gates VTL0-side support for Microsoft Hyper-V's Virtual Secure Mode (VSM). Enabling it lets the VTL0 kernel load a small secure kernel into VTL1 and stand up the VTL0<->VTL1 interfaces needed to request VSM services. Depends on HYPERV and on X86_64 which is the only architecture currently implemented. Signed-off-by: Thara Gopinath --- drivers/hv/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig index aa11bcefddf2c..a325fc61be604 100644 --- a/drivers/hv/Kconfig +++ b/drivers/hv/Kconfig @@ -40,6 +40,15 @@ config HYPERV_VTL_MODE =20 If unsure, say N =20 +config HYPERV_VSM + bool "Microsoft Hyper-V VSM driver" + depends on HYPERV && X86_64 + help + Select this option to enable Hyper-V Virtual Secure Mode. + Enabling this option will load a secure kernel in VTL1 and + establish an interface between VTL0 and VTL1 to request for + VSM services. + config HYPERV_TIMER def_bool HYPERV && X86 =20 --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 29E0E48423A; Tue, 1 Sep 2026 16:57:31 +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=1788281852; cv=none; b=vBPJTy//exInRlPfwpYN9X8ZEc4RUJGTnU/ROfUA6GQ+mAvHEgSJ15donbdU/wDI7RbEDoxPnEc47XKb1UVj/MHxtVdo8oXE7a9/b9wqLwjTmedWaU+b3KOEe2P6JfnfNcx5y83nZM3BWUza4pr2XNdPGjkynVO2DmsFo+Qy8cQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281852; c=relaxed/simple; bh=8zgXpZdNVn//9tbb2bdapPC5ryUwN+H5qsHCR4ob8Y4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MOnrXW6BaX5QFFufV4LM+Vsl9AGsCrH8oLcHcspPd3y0VLJF5Lf/NbmTbD9LZX8IXVlm7Wut3qUwbzflqnyCO8R79HTGMTcHrpnEz5IkJ913mBO+qs+92jnLlL5IlMV0CkEtlvyQ8Ye/z7oVCjidTX0sNpvEShXnNKAlPGwTQBA= 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=rjDaZCxy; 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="rjDaZCxy" Received: by linux.microsoft.com (Postfix, from userid 1130) id BD50E20B7129; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com BD50E20B7129 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281814; bh=SDFmvrBzzsvWUwoeHWjK4dfGUsNJ6xbYPIoV56miAzI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rjDaZCxyN+xcNG6Dc9USNRZBdxcyhF+2KpoFV7HZnSKaQo2vPIYL2PZa2oKzQozhx Q7czDurjmnaVFFmdFpZ7ExSS2Md4XMVGgz4Jf1ILkdaUWsuGFSJQIN30GGdpUT3uY8 aWXqsbSb+P910MuICL+aMPRT7NZxVbq/02BTsqMk= From: Thara Gopinath 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 Subject: [RFC PATCH 02/12] drivers: hv: hv_common: Allocate Hyper-V output arg page when VSM is enabled Date: Tue, 1 Sep 2026 09:55:16 -0700 Message-ID: <20260901165647.3160413-3-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" hv_output_page_exists() controls whether the per-CPU Hyper-V output argument page is allocated at boot. This page is required for hypercalls that return results via an output GPA, like HvCallGetVpRegisters. The VSM drivers read VSM-specific VP registers (e.g. HvRegisterVsmVpStatus, HvRegisterVsmCapabilities) via get_vp_registers(), which issues HvCallGetVpRegisters and therefore depends on the output argument page being present. Without it, those hypercalls fail and VSM initialization cannot proceed. Extend hv_output_page_exists() to return true when CONFIG_HYPERV_VSM is enabled. Signed-off-by: Thara Gopinath --- drivers/hv/hv_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 31256cb22b39e..44f4a10984618 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -259,7 +259,8 @@ static void hv_kmsg_dump_register(void) =20 static inline bool hv_output_page_exists(void) { - return hv_parent_partition() || IS_ENABLED(CONFIG_HYPERV_VTL_MODE); + return hv_parent_partition() || IS_ENABLED(CONFIG_HYPERV_VTL_MODE) || + IS_ENABLED(CONFIG_HYPERV_VSM); } =20 void __init hv_get_partition_id(void) --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 29D5F484239; Tue, 1 Sep 2026 16:57:31 +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=1788281854; cv=none; b=knq5RGPLwVqEroSniBn2k901utUPCau0EtAoThLBmLJVlbibvJjNSiZ2og2ccZIIi3czDvT+ZVPkxjw3BZhmPl6Jg0F9aYITkGuUyc+FB0fbyVzZNLQ2Yq80Fz6i5AeUUVVNx5oHJxrivBtWePqXHxkgbslK8VBswmcinzd7Gsc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281854; c=relaxed/simple; bh=Iqe6izHv2sjvSqP3sRxw8kvU2zTeZp9vlAzd2EYirTg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=TWRpM5BnHLYA/L2WLPYqQHiFmzg3UjN64/F5ybilyNjbvazCkpRhs7VmeH5L8eUgSGkm0c4eid2g9eu15Bbf1pwPRa39u5133HwhFU9ycUKsHQigQ30n10NlpwmKUuezDTjaoxR8gGzneDPe0mhBgi4jHyXRGQ51FENeHYx3T0w= 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=Uyfr1+RQ; 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="Uyfr1+RQ" Received: by linux.microsoft.com (Postfix, from userid 1130) id CAFDF20B712B; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com CAFDF20B712B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281814; bh=w84MF6kutcf25MX1Bag58CHWArSB1QOCs7TafbfwmXw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Uyfr1+RQ3QeKLT9GhHlFhdpLb0xn6vXYmdZrajpZ3mUJVPfgtXdNC1wPiJ6wK/Jwe oDqBXJXQiua4AAlMwSaUgvYhuwVDktKOqWb9nsK9tXFvQf1HJyddVJqlB+Q0Cwh2YH 8obBRFKr9SF0oI1qbAqmUF6+otceT5J/S4VL27ak= From: Thara Gopinath 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 , Anna Trikalinou Subject: [RFC PATCH 03/12] drivers: hv: Reserve memory for VSM secure kernel during early boot Date: Tue, 1 Sep 2026 09:55:17 -0700 Message-ID: <20260901165647.3160413-4-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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 The VSM secure kernel runs in VTL1 and cannot use the normal Linux kernel memory allocators =E2=80=94 its memory must be reserved before the general-purpose allocator takes ownership of the physical address space. Add hv_vsm_securekernel.c to handle this early boot reservation. The driver parses a new "securekernel=3D[K|M|G][@]" kernel command line argument to allow explicit control over the reserved region's size and base address. If the argument is absent or specifies a region smaller than the computed minimum, the driver falls back to an automatic allocation. The minimum reservation is calculated as: 16 MB (base) + (num_possible_cpus * 4 MB per-CPU) Memory is reserved via memblock_phys_alloc_range() with 2 MB alignment (SECKERNEL_ALIGN) and registered as an IORESOURCE_SYSTEM_RAM entry named "vsm" in the iomem resource tree so it is visible in /proc/iomem and protected from reuse. The reservation is skipped if sk_res.start is already set, preventing duplicate reservations if the setup hook is called more than once. Co-developed-by: Anna Trikalinou Signed-off-by: Anna Trikalinou Signed-off-by: Thara Gopinath --- drivers/hv/Makefile | 1 + drivers/hv/hv_vsm_securekernel.c | 188 +++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 drivers/hv/hv_vsm_securekernel.c diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index 888a748cc7cb9..880c570832381 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -25,3 +25,4 @@ obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) +=3D hv_proc.o ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL),) obj-y +=3D mshv_common.o endif +obj-$(subst m,y,$(CONFIG_HYPERV_VSM)) +=3D hv_vsm_securekernel.o diff --git a/drivers/hv/hv_vsm_securekernel.c b/drivers/hv/hv_vsm_secureker= nel.c new file mode 100644 index 0000000000000..f90f6204b4d18 --- /dev/null +++ b/drivers/hv/hv_vsm_securekernel.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024, Microsoft Corporation. + * + * Authors: + * Anna Trikalinou + * Thara Gopinath + */ + +#include + +/* Define Memory Reservation for Secure Kernel */ +#define SECKERNEL_ALIGN SZ_2M +#define SECKERNEL_ADDR_MAX (max_low_pfn_mapped << PAGE_SHIFT) +/* Secure kernel map (16MB minimum) + * Region Offset Size + * VSM PAGES 0 < 2MB + * SKERNEL ?(ELF) 16MB+ (based on config) + */ +#define SECKERNEL_BASE_SIZE (16 * 1024 * 1024) +#define SECKERNEL_PERCPU_SIZE (4 * 1024 * 1024) + +/* Estimate amount of memory needed for Secure Kernel */ +#define SECKERNEL_MIN_SIZE (SECKERNEL_BASE_SIZE + num_possible_cpus() * SE= CKERNEL_PERCPU_SIZE) + +struct resource sk_res =3D { + .name =3D "vsm", + .start =3D 0, + .end =3D 0, + .flags =3D IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM, + .desc =3D IORES_DESC_RESERVED +}; + +/* + * That function parses "simple" securekernel command lines like + * + * securekernel=3Dsize[@offset] + * + * It returns 0 on success and -EINVAL on failure. + */ +static int __init parse_securekernel_simple(char *cmdline, + unsigned long long *securekernel_size, + unsigned long long *securekernel_base) +{ + char *cur =3D cmdline; + + *securekernel_size =3D memparse(cmdline, &cur); + if (cmdline =3D=3D cur) { + pr_warn("securekernel: memory value expected\n"); + return -EINVAL; + } + + if (*cur =3D=3D '@') { + *securekernel_base =3D memparse(cur + 1, &cur); + } else if (*cur !=3D ' ' && *cur !=3D '\0') { + pr_warn("securekernel: unrecognized char: %c\n", *cur); + return -EINVAL; + } + + return 0; +} + +static __init char *get_last_securekernel(char *cmdline, const char *name) +{ + char *p =3D cmdline, *sk_cmdline =3D NULL; + + /* find securekernel and use the last one if there are more */ + p =3D strstr(p, name); + while (p) { + sk_cmdline =3D p; + p =3D strstr(p + 1, name); + } + + if (!sk_cmdline) + return NULL; + + return sk_cmdline; +} + +static int __init __parse_securekernel(char *cmdline, + unsigned long long *securekernel_size, + unsigned long long *securekernel_base, + const char *name) +{ + char *sk_cmdline; + + if (!securekernel_size || !securekernel_base) + return -EINVAL; + + *securekernel_size =3D 0; + *securekernel_base =3D 0; + + sk_cmdline =3D get_last_securekernel(cmdline, name); + + if (!sk_cmdline) + return -EINVAL; + + sk_cmdline +=3D strlen(name); + + return parse_securekernel_simple(sk_cmdline, securekernel_size, secureker= nel_base); +} + +/* + * That function is the entry point for command line parsing and should be + * called from the arch-specific code. + */ +static int __init parse_securekernel(char *cmdline, + unsigned long long *securekernel_size, + unsigned long long *securekernel_base) +{ + return __parse_securekernel(cmdline, securekernel_size, securekernel_base, + "securekernel=3D"); +} + +static int __init hv_vsm_seckernel_mem_init(char *__unused) +{ + unsigned long long securekernel_size =3D 0, securekernel_base =3D 0; + int ret; + + /* Secure Kernel memory is already reserved. Avoid duplicate reservation = */ + if (sk_res.start) + return 0; + /* + * Reserve Secure Kernel memory. + * Check command line first, if secure kernel memory was defined + */ + ret =3D parse_securekernel(boot_command_line, &securekernel_size, + &securekernel_base); + + if (ret !=3D 0 || securekernel_size < SECKERNEL_MIN_SIZE) { + if (ret !=3D 0) + pr_info("%s: securekernel cmd line not defined. Falling back to default= .\n", + __func__); + else if (securekernel_size < SECKERNEL_MIN_SIZE) + pr_info("%s: securekernel cmd line too small. Falling back to default.\= n", + __func__); + + securekernel_size =3D SECKERNEL_MIN_SIZE; + securekernel_base =3D 0; + } + + /* If securekernel_base was specified from command line, + * try to reserve memory starting from that address + */ + if (securekernel_base) { + unsigned long long start, end; + + end =3D securekernel_base + securekernel_size; + if (end > SECKERNEL_ADDR_MAX || end < securekernel_base) { + pr_warn("%s: Invalid Securekernel base address %llx. Falling back to de= fault.\n", + __func__, securekernel_base); + securekernel_base =3D 0; + } else { + start =3D memblock_phys_alloc_range(securekernel_size, SECKERNEL_ALIGN, + securekernel_base, + securekernel_base + securekernel_size); + if (start !=3D securekernel_base) { + pr_warn("%s: memory reservation @ %llx failed-memory is in use\n", + __func__, securekernel_base); + pr_warn("%s:Falling back to default mem allocation\n", __func__); + securekernel_base =3D 0; + } + } + } + /* Default: Find the base address automatically */ + if (!securekernel_base) { + securekernel_base =3D memblock_phys_alloc_range(securekernel_size, SECKE= RNEL_ALIGN, + 0, SECKERNEL_ADDR_MAX); + if (!securekernel_base) { + pr_err("%s: Securekernel reservation failed-VSM will not be enabled.\n", + __func__); + return -EINVAL; + } + } + + pr_info("Reserving %ldMB of memory at 0x%llx(%ld MB) for securekernel(Sys= tem RAM:%ldMB)\n", + (unsigned long)(securekernel_size >> 20), + securekernel_base, + (unsigned long)(securekernel_base >> 20), + (unsigned long)(memblock_phys_mem_size() >> 20)); + + sk_res.start =3D securekernel_base; + sk_res.end =3D securekernel_base + securekernel_size - 1; + insert_resource(&iomem_resource, &sk_res); + + return 0; +} +__setup("securekernel", hv_vsm_seckernel_mem_init); --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 29F5A48423C; Tue, 1 Sep 2026 16:57:31 +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=1788281853; cv=none; b=kPcSCXk0YJAdb6+yzFA/Kmm8Kn7vFoPuMQnIrG2+JmExwfXyLsCRmUpPTEqW08IAMyZ/2Ffk947La3ILiXfDkkFZ09msRd+y64XsgKEVsz7qtqBsxv81/QaRwI1jLyEIxeTaAqsoBOmhDtUh/uXPZwNimtByUoHqS5Lf9uFhYC4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281853; c=relaxed/simple; bh=UC7muPYjZo5J/xb153yhcYG5E3DgOZ0no9RdPqpZqWc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TGuKviewYsvgNQX2vCU5a477B9NU3AqCgM7ZVxLBvtvTQmcU/KpNy6G28sTXOI9ndwaVFliA8PD9ZwuDzJsYTdLaSYMKLVVnQXDG7tZ81IZ5k4xUlKiPNvcGZST5F8n+OvnDHf6rrKKURRfoD1k+FhlTbOVGzJhay9pSVm3uInE= 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=jDDXZ2IT; 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="jDDXZ2IT" Received: by linux.microsoft.com (Postfix, from userid 1130) id D85B220B712D; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com D85B220B712D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281814; bh=NvO9e9mumccyws0coxVvMyj99Z4bKsTR9JTNrI1NFvc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jDDXZ2ITtpv5GYabGjv77s2J+7jYX73uPwezcaV7C8n6uEPdA9GC75QX2k+VZ2sXN c1izd+301KjNhG3IE0qHtaBnxSts2Xg8ROPnZ3RP4F1/qKRSejLsdIaNPPGcLhZKTP XypXtOuRBisuZ1imEByU0P7ZDCUqdhK9G/ZzQtsc= From: Thara Gopinath 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 Subject: [RFC PATCH 04/12] firmware: efi: libstub: x86-stub: Enable VSM awareness in efi os indications variable Date: Tue, 1 Sep 2026 09:55:18 -0700 Message-ID: <20260901165647.3160413-5-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" Set bit 0 of the Hyper-V private OsLoaderIndications EFI variable during exit_boot() so the bootloader/firmware knows the OS intends to enable VTL1. Without this, VTL1 cannot be brought up from the Linux kernel. The support bit is first checked in OsLoaderIndicationsSupported, and the variable is only written when the VSM bit is not already set. Signed-off-by: Thara Gopinath --- drivers/firmware/efi/libstub/x86-stub.c | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi= /libstub/x86-stub.c index cef32e2c82d8f..ab3cd4fe36599 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -21,6 +21,17 @@ #include "efistub.h" #include "x86-stub.h" =20 +#ifdef CONFIG_HYPERV_VSM +#define HYPERV_PRIVATE_EFI_NAMESPACE_GUID \ + EFI_GUID(0x610b9e98, 0xc6f6, 0x47f8, 0x8b, 0x47, 0x2d, 0x2d, 0xa0, 0xd5, = 0x2a, 0x91) + +static const efi_char16_t efi_HvPrivOsloaderIndications_name[] =3D L"OsLoa= derIndications"; +static const efi_char16_t efi_HvPrivOsloaderIndicationsSupported_name[] = =3D + L"OsLoaderIndicationsSupported"; +#define HV_OSLOADER_INDICATION_VSM BIT(0) + +#endif + extern char _bss[], _ebss[]; =20 const efi_system_table_t *efi_system_table; @@ -754,6 +765,47 @@ static efi_status_t exit_boot_func(struct efi_boot_mem= map *map, return EFI_SUCCESS; } =20 +#ifdef CONFIG_HYPERV_VSM +static void efi_set_hv_os_indications(void) +{ + efi_guid_t guid =3D HYPERV_PRIVATE_EFI_NAMESPACE_GUID; + efi_status_t status; + unsigned long size; + u32 attr, val; + + size =3D sizeof(val); + status =3D get_efi_var(efi_HvPrivOsloaderIndicationsSupported_name, + &guid, &attr, &size, &val); + if (status !=3D EFI_SUCCESS) { + efi_err("Could not read Hyper-V OsloaderIndicationsSupported\n"); + return; + } + + if (!(val & HV_OSLOADER_INDICATION_VSM)) { + efi_info("Hyper-V does not support VSM in OsloaderIndicationsSupported\n= "); + return; + } + + size =3D sizeof(val); + status =3D get_efi_var(efi_HvPrivOsloaderIndications_name, &guid, &attr, = &size, &val); + if (status !=3D EFI_SUCCESS) { + efi_err("Could not read Hyper-V OsLoaderIndications\n"); + return; + } + + if (val & HV_OSLOADER_INDICATION_VSM) { + efi_info("VSM is already supported in OsLoaderIndications."); + return; + } + + val |=3D HV_OSLOADER_INDICATION_VSM; + size =3D sizeof(val); + status =3D set_efi_var(efi_HvPrivOsloaderIndications_name, &guid, attr, s= ize, &val); + if (status !=3D EFI_SUCCESS) + efi_err("Could not set Hyper-V OsLoaderIndications to indicate VSM suppo= rt\n"); +} +#endif + static efi_status_t exit_boot(struct boot_params *boot_params, void *handl= e) { struct setup_data *e820ext =3D NULL; @@ -768,6 +820,11 @@ static efi_status_t exit_boot(struct boot_params *boot= _params, void *handle) if (status !=3D EFI_SUCCESS) return status; =20 +#ifdef CONFIG_HYPERV_VSM + /* Indicate to bootloader that we will be enabling VTL1 before exiting bo= ot services */ + efi_set_hv_os_indications(); +#endif + /* Might as well exit boot services now */ status =3D efi_exit_boot_services(handle, &priv, exit_boot_func); if (status !=3D EFI_SUCCESS) --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6DDE34854E9; Tue, 1 Sep 2026 16:57:32 +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=1788281854; cv=none; b=q05X5U8I0aO8bh5DZCf2fXiq7pjmBz8xQ/Sol/leNNkQ6R+1tn8OCQFeUzcT2DcV0O5YF7AXao/jcKwOQPKZnBHhcMtlwg3avhS7otfcFc80H8i2QCqECJrMRs271EVDbskgDR9spNU+gSA1asXUK9HOEslkErsx81L7PE+sflo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281854; c=relaxed/simple; bh=S2Dt3VCKZlPDBnXFbLzMG+pmQmPlj2Atkjq5lqR6CWQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZPYSqctWPDL99JqdhtZ7vSwP1xu6N7VrqSQHfMNGEqTFz0XbQVr0s0zQOlwPW3nC4jTGHh6cUGTEJNe3PlxB9NljWgR0DuEtj85laW8OLHCQINlDCst5vV4IQ0q1LLMJYz7v5G//Df9yGqe7NMA9qkwFE5OoTAqfRpTiFuDHALA= 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=QL94u6KX; 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="QL94u6KX" Received: by linux.microsoft.com (Postfix, from userid 1130) id E579F20B7136; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com E579F20B7136 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281814; bh=Hpw3uP2WjGzMTwKeoL5pQcN469geeudvRdcHZ29vy1I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QL94u6KX39PrItpwS2B/Qhhi1IIUvys+QiLJMrMnu7W6O1xzlVlxNsw+ptOvZ8/Br Ux4sYuVzzM2agoyp5t473tqttqq7HtQUrB63B5XctW7CpH1dhurwdoaCxhgogORhOW 7lb6U1j3vAxvG/cNA2se5oqLek1nG4ruDxq+pVgM= From: Thara Gopinath 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 Subject: [RFC PATCH 05/12] include: hyperv: hvgdk_mini.h: Add VTL-specific structures and bits Date: Tue, 1 Sep 2026 09:55:19 -0700 Message-ID: <20260901165647.3160413-6-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" Add the definitions needed by the upcoming VSM / VTL1 bring-up code: - HV_ACCESS_VSM and HV_ACCESS_VP_REGS partition privilege bits - HVCALL_ENABLE_PARTITION_VTL and HVCALL_MODIFY_VTL_PROTECTION_MASK hypercall IDs - hv_input_enable_partition_vtl input structure and its flags union - hv_register_vsm_{partition_status,vp_status} register layout unions - HV_REGISTER_VSM_PARTITION_STATUS and HV_REGISTER_VSM_VP_SECURE_CONFIG_VTL0 register names Definitions taken from the Hyper-V TLFS. Signed-off-by: Thara Gopinath --- include/hyperv/hvgdk_mini.h | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index 6a4e8b9d570fd..a50af532fc7f9 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -303,6 +303,8 @@ union hv_hypervisor_version_info { #define HV_ACCESS_STATS BIT(8) #define HV_DEBUGGING BIT(11) #define HV_CPU_MANAGEMENT BIT(12) +#define HV_ACCESS_VSM BIT(16) +#define HV_ACCESS_VP_REGS BIT(17) #define HV_ENABLE_EXTENDED_HYPERCALLS BIT(20) #define HV_ISOLATION BIT(22) =20 @@ -438,6 +440,8 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_AS= SIST_PAGE */ #define HVCALL_GET_LOGICAL_PROCESSOR_RUN_TIME 0x0004 #define HVCALL_NOTIFY_LONG_SPIN_WAIT 0x0008 #define HVCALL_SEND_IPI 0x000b +#define HVCALL_MODIFY_VTL_PROTECTION_MASK 0x000c +#define HVCALL_ENABLE_PARTITION_VTL 0x000d #define HVCALL_ENABLE_VP_VTL 0x000f #define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX 0x0013 #define HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX 0x0014 @@ -878,6 +882,46 @@ struct hv_init_vp_context { u64 msr_cr_pat; } __packed; =20 +union hv_enable_partition_vtl_flags { + u8 as_uint8; + struct { + u8 enable_mbec:1; + u8 enable_supervisor_shadow_stack:1; + u8 enable_hardware_hvpt:1; + u8 reserved:5; + }; +} __packed; + +struct hv_input_enable_partition_vtl { + u64 partition_id; + union hv_input_vtl target_vtl; + union hv_enable_partition_vtl_flags flags; + u16 rsvd_z16; + u32 rsvd_z32; +} __packed; + +union hv_register_vsm_partition_status { + u64 as_uint64; + struct { + u64 enabled_vtl_set : 16; + u64 max_vtl : 4; + u64 mbec_enabled_vtl_set: 16; + u64 supervisor_shadow_stack_enabled_vtl_set : 4; + u64 reserved : 24; + }; +} __packed; + +union hv_register_vsm_vp_status { + u64 as_uint64; + struct { + u64 active_vtl : 4; + u64 active_mbec_enabled : 1; + u64 reserved_z0 : 11; + u64 enabled_vtl_set : 16; + u64 reserved_z1 : 32; + }; +} __packed; + struct hv_enable_vp_vtl { u64 partition_id; u32 vp_index; @@ -1061,8 +1105,10 @@ enum hv_register_name { =20 /* Synthetic VSM registers */ HV_REGISTER_VSM_CODE_PAGE_OFFSETS =3D 0x000D0002, + HV_REGISTER_VSM_PARTITION_STATUS =3D 0x000D0004, HV_REGISTER_VSM_CAPABILITIES =3D 0x000D0006, HV_REGISTER_VSM_PARTITION_CONFIG =3D 0x000D0007, + HV_REGISTER_VSM_VP_SECURE_CONFIG_VTL0 =3D 0x000D0010, =20 #if defined(CONFIG_X86) /* X64 Debug Registers */ --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6DCBB484254; Tue, 1 Sep 2026 16:57:32 +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=1788281854; cv=none; b=UYbQDcEBoLV2hz2kmE4hKZuM8LSYfCjfckNtWha4YX7csNfKPbFsZUjzmArWWzy2dwWWZIHgFFJvG9YOiUZeMrBnFEiMFpIA8y7p/ZNUYC0qOOp7O5QvctpggYtzlb1bZX2UYlJYUBqJL9VzIg1C2akO3ZQQ1/8Ftgo4iw+nO9g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281854; c=relaxed/simple; bh=5Dk+oy/IIAwKk745x2+ohPD/deYS0xe5TOjqlDR5Dww=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X5Ama2Jd7xOuuvVVj0cEvzMAaGKpN4ATL1+agEuEbdayUFSd6fpU6tbfA5ZpA4RP4OkU81/A/DIqOwSYVtd+oBrAg3Gkna5HjoZZqJK4o6V8p9L5NJqTc/tnPNfZ3Jv2qj3Eoej6iDbvHzSSR1PtbnephdQN74g7ouZfQGuSrJM= 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=Wj22b+m8; 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="Wj22b+m8" Received: by linux.microsoft.com (Postfix, from userid 1130) id F35C020B7138; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com F35C020B7138 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281815; bh=rHauQ5CkArRSzF2IDix3yMncTDgrieA1wSxwg8lpu9I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Wj22b+m8qm572Qpmlptkc0JlsqJpsl68325zL87knXkvhdUbwK9kxttE53+ZZI+oK XaVFsyfTKyFnAHlqnOs0SW70gsItL3Hgdy03+b9UwArCXKEvv8TFSVExYV4zsppKFk FvxiX9S/gFjnzkxyXOWZes5iF9cR6wVvA/SDKy9I= From: Thara Gopinath 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 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 Message-ID: <20260901165647.3160413-7-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" 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 --- 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 :=3D mshv_vtl_main.o # Code that must be built-in obj-$(CONFIG_HYPERV) +=3D hv_common.o obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) +=3D hv_proc.o -ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL),) +ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL)$(CONFIG_HYPERV_VSM),) obj-y +=3D mshv_common.o endif -obj-$(subst m,y,$(CONFIG_HYPERV_VSM)) +=3D hv_vsm_securekernel.o +obj-$(subst m,y,$(CONFIG_HYPERV_VSM)) +=3D hv_vsm_securekernel.o hv_vsm_bo= ot.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 + * + */ + +#define pr_fmt(fmt) "vsm: " fmt + +#include +#include +#include +#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 =3D { + .name =3D reg_name, + }; + union hv_input_vtl input_vtl =3D { + .as_uint8 =3D 0, + }; + int ret; + + ret =3D hv_call_get_vp_registers(HV_VP_INDEX_SELF, + HV_PARTITION_ID_SELF, + 1, input_vtl, ®); + if (ret) + return ret; + + *result =3D reg.value.reg64; + return 0; +} + +static int __init hv_vsm_enable_partition_vtl(void) +{ + u64 status =3D 0; + unsigned long flags; + struct hv_input_enable_partition_vtl *hvin =3D NULL; + + local_irq_save(flags); + + hvin =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(hvin, 0, sizeof(*hvin)); + + hvin->partition_id =3D HV_PARTITION_ID_SELF; + hvin->target_vtl.as_uint8 =3D 1; + hvin->flags.enable_mbec =3D 1; + + status =3D hv_do_hypercall(HVCALL_ENABLE_PARTITION_VTL, hvin, NULL); + if (hv_result(status)) + pr_err("Enable Partition VTL failed. status=3D0x%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 *ma= x_vtl, + u16 *mbec_enabled_vtl_set) +{ + u64 result; + int ret; + union hv_register_vsm_partition_status vsm_partition_status =3D { 0 }; + + ret =3D hv_vsm_get_register(HV_REGISTER_VSM_PARTITION_STATUS, &result); + if (ret) + return ret; + + vsm_partition_status =3D (union hv_register_vsm_partition_status)result; + *enabled_vtl_set =3D vsm_partition_status.enabled_vtl_set; + *max_vtl =3D vsm_partition_status.max_vtl; + *mbec_enabled_vtl_set =3D vsm_partition_status.mbec_enabled_vtl_set; + return 0; +} + +static int __init hv_vsm_bootstrap_vtl(void) +{ + u16 partition_enabled_vtl_set =3D 0, partition_mbec_enabled_vtl_set =3D 0; + u8 partition_max_vtl; + int ret; + + /* Check and enable VTL1 at the partition level */ + ret =3D hv_vsm_get_partition_status(&partition_enabled_vtl_set, &partitio= n_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 =3D hv_vsm_enable_partition_vtl(); + if (ret) { + pr_err("Enabling Partition VTL1 failed with status 0x%x\n", + ret); + return -EINVAL; + } + ret =3D hv_vsm_get_partition_status(&partition_enabled_vtl_set, &partiti= on_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 =3D cpumask_first(cpu_online_mask); + set_cpus_allowed_ptr(current, cpumask_of(boot_cpu)); + + ret =3D 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 detai= ls.\n"); + + set_cpus_allowed_ptr(current, mask); + free_cpumask_var(mask); + return ret; +} +device_initcall(hv_vsm_boot_init); --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DBD44485505; Tue, 1 Sep 2026 16:57:32 +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=1788281855; cv=none; b=jyK9kVh/YvGu5RaZdFgefPfiTODq1kg3/FEQZ0qh27ytOsyRFJbS8A9j8nq8C/eJwRkUJ3FGfIwa7kRToYS/1Cg/iMUFdJTK0dm78VWcKMoXScQud3sIC+232hbO5sZJN9gR8if3yJdc7SBSKAKmWlmj6YYl5eCipI3M65diL6Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281855; c=relaxed/simple; bh=ziuWZRHDWh2PU3/BB3TR4+/uqBnepWg0Ao5P6VoLQgI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=q2FBGqJtzzNJCQBF7dHLzNHzDzIWsZ1eHllfQpLOpe861V1WG4rL1B4vyTdhBmJU7VydBL9gnKhz4bVCdZ6b3Rmi8aCYHHeX9okeHM/TB/V78DOR0X1nZHKX8h9O43fD5BiVFCNg59CiooZSD6uPiyg1Prag0hUqbgDXyUaxWm4= 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=HGjFoByZ; 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="HGjFoByZ" Received: by linux.microsoft.com (Postfix, from userid 1130) id 0C2EC20B713B; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 0C2EC20B713B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281815; bh=PyfYnkH7UX4a6l5GYM+ronzmhwCwStnMtn2lvCR7eAo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HGjFoByZUo1tYxbGkGVnOWikojULOmwtFj71D5WPTpV8AmwrVzx72KSoq5FKdx9vi 1j2OuH7Qbq9ndac+AzEmYG1yedhTEnatx4zL/ACXTmkEa2KeMi2+FaLids0c8mt7Cw EctTiu2q46rGnNDkPBOQnzToYq+VrdgrnsPSnhUQ= From: Thara Gopinath 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 , Stanislav Kinsburskii Subject: [RFC PATCH 07/12] drivers: hv: hv_vsm_boot: load secure kernel image from firmware Date: Tue, 1 Sep 2026 09:55:21 -0700 Message-ID: <20260901165647.3160413-8-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" LVBS bring-up requires loading a secure kernel image into VTL1 before starting it. Add the VTL0-side loader that stages the image in the memory region reserved by hv_vsm_securekernel, in preparation for the VTL1 bring-up. The image is a 64-bit ELF fetched via request_firmware("vsm_sk"). It is expected to ship inside the signed UKI/initramfs so it is authenticated end-to-end via Secure Boot before the loader consumes it; sourcing it from an unauthenticated location would break the LVBS trust model. The loader validates the ELF header, stages the PT_LOAD segments into the reserved region and records the entry point as a physical address for use at VTL1 start time. If VSM support has been advertised to the hypervisor but no secure kernel region was reserved on the command line, panic: LVBS bring-up is committed at this point and there is no safe way to continue. Signed-off-by: Stanislav Kinsburskii Signed-off-by: Thara Gopinath --- drivers/hv/hv_vsm.h | 17 ++++ drivers/hv/hv_vsm_boot.c | 201 ++++++++++++++++++++++++++++++++++++++- include/hyperv/vsm.h | 21 ++++ 3 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 drivers/hv/hv_vsm.h create mode 100644 include/hyperv/vsm.h diff --git a/drivers/hv/hv_vsm.h b/drivers/hv/hv_vsm.h new file mode 100644 index 0000000000000..88f099f88eeb4 --- /dev/null +++ b/drivers/hv/hv_vsm.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023-2026, Microsoft Corporation. + * + * Author: Thara Gopinath + * + */ + +#ifndef _HV_VSM_H +#define _HV_VSM_H + +#include +#include + +extern struct resource sk_res; + +#endif /* _HV_VSM_H */ diff --git a/drivers/hv/hv_vsm_boot.c b/drivers/hv/hv_vsm_boot.c index 99e4dc8695837..dc20f935da5b2 100644 --- a/drivers/hv/hv_vsm_boot.c +++ b/drivers/hv/hv_vsm_boot.c @@ -6,6 +6,7 @@ * Copyright (c) 2023-2025, Microsoft Corporation. * * Author: Thara Gopinath + * Stanislav Kinsburskii * */ =20 @@ -13,10 +14,26 @@ =20 #include #include +#include +#include +#include +#include +#include #include #include "mshv.h" +#include "hv_vsm.h" =20 #define HV_VTL1_ENABLE_BIT BIT(1) +/* + * Firmware name looked up via request_firmware() under /lib/firmware/. + * + * The secure kernel image is expected to be delivered inside the signed + * UKI/initramfs so that it is authenticated end-to-end via Secure Boot + * before request_firmware() returns it. + */ +#define SK_FW_NAME "vsm_sk" + +static void *vsm_skm_va; =20 static int hv_vsm_get_register(u32 reg_name, u64 *result) { @@ -38,6 +55,167 @@ static int hv_vsm_get_register(u32 reg_name, u64 *resul= t) return 0; } =20 +static Elf64_Addr __init hv_vsm_elf_min_load_paddr(void *image) +{ + Elf64_Ehdr *ehdr =3D image; + Elf64_Phdr *phdr =3D image + ehdr->e_phoff; + Elf64_Addr paddr =3D U64_MAX; + int i; + + for (i =3D 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type !=3D PT_LOAD) + continue; + + if (phdr->p_paddr < paddr) + paddr =3D phdr->p_paddr; + } + + return paddr; +} + +static size_t __init hv_vsm_elf_binary_size(void *image) +{ + Elf64_Ehdr *ehdr =3D image; + Elf64_Phdr *phdr =3D image + ehdr->e_phoff; + Elf64_Addr min_paddr, max_paddr =3D 0; + int i; + + min_paddr =3D hv_vsm_elf_min_load_paddr(image); + if (min_paddr =3D=3D U64_MAX) + return 0; + + for (i =3D 0; i < ehdr->e_phnum; i++, phdr++) { + if (phdr->p_type !=3D PT_LOAD) + continue; + + max_paddr =3D max(max_paddr, phdr->p_paddr + phdr->p_memsz); + } + + return max_paddr - min_paddr; +} + +static int __init hv_vsm_load_elf(void *image, Elf64_Addr *sk_entry_pa) +{ + Elf64_Ehdr *ehdr =3D image; + Elf64_Phdr *phdr =3D image + ehdr->e_phoff; + Elf64_Addr min_paddr; + Elf64_Xword first_load_align =3D 0; + size_t size; + void *base_addr; + int i; + + /* Find alignment of the first PT_LOAD segment. */ + for (i =3D 0; i < ehdr->e_phnum; i++) { + if (phdr[i].p_type =3D=3D PT_LOAD) { + first_load_align =3D phdr[i].p_align; + break; + } + } + if (!first_load_align) { + pr_err("Secure kernel does not have loadable segments\n"); + return -EINVAL; + } + + /* Align the base load address up to the first PT_LOAD segment alignment = */ + base_addr =3D PTR_ALIGN(vsm_skm_va + first_load_align, first_load_align); + + size =3D hv_vsm_elf_binary_size(image); + if (vsm_skm_va + VSM_SK_INITIAL_MAP_SIZE - base_addr < size) { + pr_err("secure kernel does not fit: %zu > %td\n", size, + vsm_skm_va + VSM_SK_INITIAL_MAP_SIZE - base_addr); + return -EFBIG; + } + + pr_debug("secure kernel binary size: %#zx\n", size); + + min_paddr =3D hv_vsm_elf_min_load_paddr(image); + pr_debug("secure kernel minimal paddr: %#llx\n", min_paddr); + + pr_debug("loading secure kernel ELF segments:\n"); + + /* Validate PT_LOAD alignment first, before touching any target memory. */ + for (i =3D 0; i < ehdr->e_phnum; i++) { + if (phdr[i].p_type !=3D PT_LOAD) + continue; + if (phdr[i].p_align % SZ_2M) { + pr_err("LOAD segment is not aligned by 2MB\n"); + return -EINVAL; + } + } + + for (i =3D 0; i < ehdr->e_phnum; i++, phdr++) { + void *load_addr; + + if (phdr->p_type !=3D PT_LOAD) + continue; + + /* + * Adjust the load address by min_paddr to compensate the + * offset. + */ + load_addr =3D base_addr + (phdr->p_paddr - min_paddr); + + pr_debug(" p_offset: %#016llx, p_filesz: %#016llx, p_memsz: %#016llx to= pa %#016llx\n", + phdr->p_offset, phdr->p_filesz, phdr->p_memsz, + virt_to_phys(load_addr)); + memcpy(load_addr, image + phdr->p_offset, phdr->p_filesz); + + if (phdr->p_memsz =3D=3D phdr->p_filesz) + continue; + + pr_debug(" zeroing %#016llx bytes at pa %#016llx\n", + phdr->p_memsz - phdr->p_filesz, + virt_to_phys(load_addr + phdr->p_filesz)); + memset(load_addr + phdr->p_filesz, 0, + phdr->p_memsz - phdr->p_filesz); + } + + *sk_entry_pa =3D virt_to_phys(base_addr + (ehdr->e_entry - min_paddr)); + pr_debug("secure kernel entry pa: %#llx\n", *sk_entry_pa); + + return 0; +} + +static int __init hv_vsm_load_secure_kernel(Elf64_Addr *sk_entry_pa) +{ + const struct firmware *fw; + Elf64_Ehdr *ehdr; + int ret; + + ret =3D request_firmware(&fw, SK_FW_NAME, NULL); + if (ret) { + pr_err("Failed to load %s firmware: %d\n", SK_FW_NAME, ret); + return ret; + } + + ehdr =3D (Elf64_Ehdr *)fw->data; + if (fw->size < sizeof(*ehdr) || + memcmp(ehdr->e_ident, ELFMAG, SELFMAG) || + (ehdr->e_type !=3D ET_EXEC && ehdr->e_type !=3D ET_DYN)) { + pr_err("Not a valid ELF file: %s\n", SK_FW_NAME); + ret =3D -ENOEXEC; + goto out_release; + } + + if (ehdr->e_ident[EI_CLASS] !=3D ELFCLASS64) { + pr_err("Not a 64-bit compatible ELF file: %s\n", SK_FW_NAME); + ret =3D -ENOEXEC; + goto out_release; + } + + if (!elf_check_arch(ehdr)) { + pr_err("Not a valid ELF file: %s\n", SK_FW_NAME); + ret =3D -ENOEXEC; + goto out_release; + } + + ret =3D hv_vsm_load_elf((void *)fw->data, sk_entry_pa); + +out_release: + release_firmware(fw); + return ret; +} + static int __init hv_vsm_enable_partition_vtl(void) { u64 status =3D 0; @@ -85,6 +263,7 @@ static int __init hv_vsm_bootstrap_vtl(void) { u16 partition_enabled_vtl_set =3D 0, partition_mbec_enabled_vtl_set =3D 0; u8 partition_max_vtl; + Elf64_Addr sk_entry_pa; int ret; =20 /* Check and enable VTL1 at the partition level */ @@ -120,7 +299,25 @@ static int __init hv_vsm_bootstrap_vtl(void) return -EINVAL; } } - return 0; + + return hv_vsm_load_secure_kernel(&sk_entry_pa); +} + +static void __init hv_vsm_get_sk_mem(void) +{ + /* + * The reserved secure kernel region is mandatory once VSM support has + * been advertised. Without it we cannot load the secure kernel and + * bringing up VTL1 is impossible, so fail hard rather than continuing + * in an unusable state. + */ + if (!sk_res.start) + panic("No memory reserved in cmdline for secure kernel"); + + vsm_skm_va =3D phys_to_virt(sk_res.start); + + pr_info("secure kernel region: %#llx-%#llx (%lld MB)\n", + sk_res.start, sk_res.end, resource_size(&sk_res) >> 20); } =20 static bool __init vsm_arch_has_vsm_access(void) @@ -143,6 +340,8 @@ static int __init hv_vsm_boot_init(void) if (!vsm_arch_has_vsm_access()) return 0; =20 + hv_vsm_get_sk_mem(); + /* * 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. diff --git a/include/hyperv/vsm.h b/include/hyperv/vsm.h new file mode 100644 index 0000000000000..51555c09413d5 --- /dev/null +++ b/include/hyperv/vsm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Common definitions shared by generic and arch code for enabling VTL1 + * and the Virtual Secure Mode (VSM) framework on Microsoft Hyper-V. + * + * Copyright (c) 2025-2026, Microsoft Corporation. + * + * Author: Thara Gopinath + */ + +#ifndef _HYPERV_VSM_H +#define _HYPERV_VSM_H + +/* + * Size of memory that is initially mapped for the secure kernel by the + * VTL0-side loader. The secure kernel image itself may be larger than + * this and map additional memory on its own. + */ +#define VSM_SK_INITIAL_MAP_SIZE (16 * 1024 * 1024) + +#endif /* _HYPERV_VSM_H */ --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DBC2948423F; Tue, 1 Sep 2026 16:57:32 +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=1788281855; cv=none; b=K5aZ7d9+8cRDS8R4xBVaswdMUoHi6YSbYXhD+9W8iZPofEjPt+JEaiL2cF3QrrFCtq03UBXkcPjfkXE3E3N6wVCpFOuz3i+ciZzc2yug9SyExEB6yQ4BLJuYGxwnU1Ryt2VO4TCHKOZqcAg0+SawecOgFa6SLakfeGEZmdXOnKw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281855; c=relaxed/simple; bh=lFpQ5h8e4gADEnbl2VgWuUPXu3sgbx+mjnfcNij1AVQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YBJpzS5Mtx5GDY/7SyYT7j+iEFKjPRbeXcEaQ3fatQYf+0asSkXh8x/9y3zQBbaV9evXo6kQ0gJXaQyKrKrkqS8vPbwHd/NlzABa8bT/rI+v6zhEdydnTmRRTjdzGub0JRUc/exAfprFlGnRhNqw/IB7EJqDa3OjxfsXkE1gadU= 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=eM8MW6nX; 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="eM8MW6nX" Received: by linux.microsoft.com (Postfix, from userid 1130) id 1928520B7001; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 1928520B7001 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281815; bh=The4NHc+XgE3F3fgWjXVI3lQkaMw3MhSHPODgCgMZ3k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eM8MW6nXJKs3UFTfxFlODALVyjceafXMuuoegne6ZWtcMiylra+HusmWDEgAAWbAq n20emivGohHnz8RRyfQ6qLbXt3Pso95t8wgqj8q7Z8MbfhsDJ1Ff+LZBxOKQZbS8FK SM/3x+iMJNCtPl8gotSXlyNpn8tqTDa0mGVxIJzU= From: Thara Gopinath 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 Subject: [RFC PATCH 08/12] arch: x86: hyperv: Build initial vCPU context for VTL1 secure kernel Date: Tue, 1 Sep 2026 09:55:22 -0700 Message-ID: <20260901165647.3160413-9-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" Enabling VTL1 on the boot processor requires handing Hyper-V a fully populated hv_init_vp_context describing the state VTL1 should start in: initial page tables, GDT/TSS, control registers and entry point. Add the arch-specific builder that assembles this context using the memory region reserved for the secure kernel. Actual enablement of VTL1 using this context is done in a subsequent patch. Signed-off-by: Thara Gopinath --- arch/x86/hyperv/Makefile | 1 + arch/x86/hyperv/hv_vtl_vsm.c | 258 ++++++++++++++++++++++++++++++++ arch/x86/include/asm/mshyperv.h | 9 ++ 3 files changed, 268 insertions(+) create mode 100644 arch/x86/hyperv/hv_vtl_vsm.c diff --git a/arch/x86/hyperv/Makefile b/arch/x86/hyperv/Makefile index 56292102af623..1fdc20e239243 100644 --- a/arch/x86/hyperv/Makefile +++ b/arch/x86/hyperv/Makefile @@ -2,6 +2,7 @@ obj-y :=3D hv_init.o mmu.o nested.o irqdomain.o ivm.o obj-$(CONFIG_X86_64) +=3D hv_apic.o obj-$(CONFIG_HYPERV_VTL_MODE) +=3D hv_vtl.o mshv_vtl_asm.o +obj-$(CONFIG_HYPERV_VSM) +=3D hv_vtl_vsm.o =20 $(obj)/mshv_vtl_asm.o: $(obj)/mshv-asm-offsets.h =20 diff --git a/arch/x86/hyperv/hv_vtl_vsm.c b/arch/x86/hyperv/hv_vtl_vsm.c new file mode 100644 index 0000000000000..edc55264c4d87 --- /dev/null +++ b/arch/x86/hyperv/hv_vtl_vsm.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Architecture-specific bring-up state for the VTL1 secure kernel: build + * the page tables, GDT/TSS and initial vCPU register context that Hyper-V + * loads when transitioning the boot processor to VTL1. + * + * Copyright (c) 2025-2026, Microsoft Corporation. + * + * Author: Thara Gopinath + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Define PAGE size and related variables for initial secure kernel pages = */ +#define VSM_PAGE_SHIFT 12 +#define VSM_PAGE_SIZE BIT(VSM_PAGE_SHIFT) +#define PAGE_AT(addr, idx) ((addr) + (idx) * VSM_PAGE_SIZE) +#define VSM_VA_FROM_PA(pa) (pa) /* Assumes identity mapping in secure ker= nel */ + +/* Number of entries in a page table (all levels) */ +#define VSM_ENTRIES_PER_PT 512 +#define VSM_PMD_SIZE (VSM_PAGE_SIZE * VSM_ENTRIES_PER_PT) + +/* + * Initial memory that will be mapped for secure kernel. + * Secure Kernel memory can be larger than this. + */ +#define VSM_SK_PTE_PAGES_COUNT (ALIGN(VSM_SK_INITIAL_MAP_SIZE, VSM_PMD_SIZ= E) / VSM_PMD_SIZE) + +/* VSM pages */ +enum { + VSM_GDT_PAGE, + VSM_TSS_PAGE, + VSM_PML4E_PAGE, + VSM_PDPE_PAGE, + VSM_PDE_PAGE, + VSM_PTE_PAGES, + /* PTE tables consume several pages */ + VSM_KERNEL_STACK_PAGE =3D VSM_PTE_PAGES + VSM_SK_PTE_PAGES_COUNT, + VSM_PAGES_COUNT +}; + +#define VSM_PT_FLAGS (_PAGE_PRESENT | _PAGE_RW) +#define VSM_PTE_FLAGS (VSM_PT_FLAGS | _PAGE_ACCESSED | _PAGE_DIRTY) + +/* Shifts to compute page table mapping */ +#define VSM_PD_TABLE_SHIFT 21 +#define VSM_PDP_TABLE_SHIFT 30 +#define VSM_PML4_TABLE_SHIFT 39 + +/* Given VA, get index into the page table at a given level */ +#define VSM_GET_PML4_INDEX(addr) (((addr) >> VSM_PML4_TABLE_SHIFT) & 0x1FF) +#define VSM_GET_PDP_INDEX(addr) (((addr) >> VSM_PDP_TABLE_SHIFT) & 0x1FF) +#define VSM_GET_PD_INDEX(addr) (((addr) >> VSM_PD_TABLE_SHIFT) & 0x1FF) + +static void __init hv_vsm_fill_pte_tables(phys_addr_t sk_pa, u64 *pde, + int pd_index, int num_pte_tables) +{ + u16 i, j; + phys_addr_t pte_pa; + u64 *pte; + + /* Fill page tables with entries */ + for (i =3D 0; i < num_pte_tables; i++) { + pte_pa =3D PAGE_AT(sk_pa, VSM_PTE_PAGES + i); + pte =3D phys_to_virt(pte_pa); + *(pde + pd_index + i) =3D pte_pa | VSM_PTE_FLAGS; + for (j =3D 0; j < VSM_ENTRIES_PER_PT; j++) { + *(pte + j) =3D + (sk_pa + ((j + (i * VSM_ENTRIES_PER_PT)) * VSM_PAGE_SIZE)) | + VSM_PTE_FLAGS; + } + } +} + +static void __init hv_vsm_init_page_tables(struct hv_init_vp_context *vp_c= tx, phys_addr_t sk_pa) +{ + unsigned int pml4_index; + unsigned int pdp_index; + unsigned int pd_index; + phys_addr_t pml4e_pa; + phys_addr_t pdpe_pa; + phys_addr_t pde_pa; + u64 *pml4e; + u64 *pdpe; + u64 *pde; + int num_pte_tables; + + /* Compute the page-table indices at which the secure kernel mapping star= ts. */ + pml4_index =3D VSM_GET_PML4_INDEX(sk_pa); + pdp_index =3D VSM_GET_PDP_INDEX(sk_pa); + pd_index =3D VSM_GET_PD_INDEX(sk_pa); + + pml4e_pa =3D PAGE_AT(sk_pa, VSM_PML4E_PAGE); + pdpe_pa =3D PAGE_AT(sk_pa, VSM_PDPE_PAGE); + pde_pa =3D PAGE_AT(sk_pa, VSM_PDE_PAGE); + + pml4e =3D phys_to_virt(pml4e_pa); + pdpe =3D phys_to_virt(pdpe_pa); + pde =3D phys_to_virt(pde_pa); + + /* + * Zero the PML4, PDP, PD and PTE pages before populating them so that + * any entry not explicitly written below has its present bit clear. + */ + memset(pml4e, 0, + (VSM_KERNEL_STACK_PAGE - VSM_PML4E_PAGE) * VSM_PAGE_SIZE); + + *(pml4e + pml4_index) =3D pdpe_pa | VSM_PT_FLAGS; + *(pdpe + pdp_index) =3D pde_pa | VSM_PT_FLAGS; + + /* + * Initial page tables map only the first VSM_SK_INITIAL_MAP_SIZE size of= memory. + * This memory will be used for the Secure Loader and initial Secure Kern= el. + */ + num_pte_tables =3D (VSM_SK_INITIAL_MAP_SIZE / VSM_PAGE_SIZE) / VSM_ENTRIE= S_PER_PT; + hv_vsm_fill_pte_tables(sk_pa, pde, pd_index, num_pte_tables); + + vp_ctx->cr3 =3D pml4e_pa; +} + +static void __init hv_vsm_init_gdt(struct hv_init_vp_context *vp_ctx, phys= _addr_t sk_pa) +{ + phys_addr_t gdt_pa, tss_pa, kstack_pa; + void *gdt_va; + u64 tss_sk_va, gdt; + struct x86_hw_tss *tss; + size_t gdt_size =3D sizeof(gdt), tss_size =3D sizeof(*tss), gdt_offset = =3D 0; + + /* Get a page for the GDT */ + gdt_pa =3D PAGE_AT(sk_pa, VSM_GDT_PAGE); + gdt_va =3D phys_to_virt(gdt_pa); + /* Get a page for the TSS */ + tss_pa =3D PAGE_AT(sk_pa, VSM_TSS_PAGE); + tss =3D phys_to_virt(tss_pa); + /* Compute the VA that secure kernel will see for the TSS */ + tss_sk_va =3D VSM_VA_FROM_PA(tss_pa); + /* Get a page for the secure kernel initial stack */ + kstack_pa =3D PAGE_AT(sk_pa, VSM_KERNEL_STACK_PAGE); + /* Set the initial stack pointer for the kernel to point to bottom of ker= nel stack */ + tss->sp0 =3D VSM_VA_FROM_PA(kstack_pa) + VSM_PAGE_SIZE; + vp_ctx->rsp =3D tss->sp0; + + /* Make and add the NULL descriptor to the GDT */ + gdt =3D 0; + memcpy(gdt_va + gdt_offset, &gdt, gdt_size); + gdt_offset +=3D gdt_size; + + /* Make and add a code segment descriptor to the GDT */ + gdt =3D GDT_ENTRY(DESC_CODE64, 0, 0); + memcpy(gdt_va + gdt_offset, &gdt, gdt_size); + gdt_offset +=3D gdt_size; + + /* Make and add a data segment descriptor to the GDT */ + gdt =3D GDT_ENTRY(DESC_DATA64, 0, 0); + memcpy(gdt_va + gdt_offset, &gdt, gdt_size); + gdt_offset +=3D gdt_size; + + /* + * Make and add a system segment descriptor for the TSS in the GDT. + * + * In 64-bit mode a system-segment descriptor (TSS/LDT) is 16 bytes + * wide: the lower 8 bytes have the same layout as the legacy 32-bit + * descriptor (produced by GDT_ENTRY), and the upper 8 bytes hold + * base[63:32] in the low 32 bits with the high 32 bits reserved 0. + * GDT_ENTRY masks base to 32 bits, so the upper half must be written + * explicitly. + */ + gdt =3D GDT_ENTRY(DESC_TSS32, tss_sk_va, tss_size); + memcpy(gdt_va + gdt_offset, &gdt, gdt_size); + gdt_offset +=3D gdt_size; + gdt =3D tss_sk_va >> 32; + memcpy(gdt_va + gdt_offset, &gdt, gdt_size); + gdt_offset +=3D gdt_size; + + /* Set up the GDT register */ + vp_ctx->gdtr.base =3D VSM_VA_FROM_PA(gdt_pa); + vp_ctx->gdtr.limit =3D gdt_offset - 1; + + /* Set the code segment (CS) selector */ + vp_ctx->cs.base =3D 0; + vp_ctx->cs.limit =3D 0; + vp_ctx->cs.selector =3D 1 << 3; + vp_ctx->cs.attributes =3D _DESC_S | _DESC_PRESENT | _DESC_ACCESSED | + _DESC_CODE_READABLE | _DESC_CODE_EXECUTABLE | + _DESC_LONG_CODE | _DESC_GRANULARITY_4K; + + /* Set the data segment (DS) selector */ + vp_ctx->ds.base =3D 0; + vp_ctx->ds.limit =3D 0; + vp_ctx->ds.selector =3D 2 << 3; + vp_ctx->ds.attributes =3D _DESC_S | _DESC_PRESENT | _DESC_ACCESSED | + _DESC_DATA_WRITABLE | _DESC_GRANULARITY_4K | _DESC_DB; + + /* Set the ES, FS and GS to be the same as DS, for now */ + vp_ctx->es =3D vp_ctx->ds; + vp_ctx->fs =3D vp_ctx->ds; + vp_ctx->gs =3D vp_ctx->ds; + + /* Set the stack selector to 0 (unused in long mode) */ + vp_ctx->ss.selector =3D 0; + + /* Set the task register selector */ + vp_ctx->tr.base =3D tss_sk_va; + vp_ctx->tr.limit =3D tss_size - 1; + vp_ctx->tr.selector =3D 3 << 3; + vp_ctx->tr.attributes =3D _DESC_PRESENT | _DESC_SYSTEM(11); +} + +static void __init hv_vsm_init_cpu(struct hv_init_vp_context *vp_ctx, Elf6= 4_Addr sk_entry_pa) +{ + /* Offset rip by any secure kernel header length */ + vp_ctx->rip =3D VSM_VA_FROM_PA(sk_entry_pa); + + /* ToDo: Check if can be replaced with CR0_STATE */ + vp_ctx->cr0 =3D + X86_CR0_PG | /* Paging */ + X86_CR0_WP | /* Write Protect */ + X86_CR0_NE | /* Numeric Error */ + X86_CR0_ET | /* Extension Type */ + X86_CR0_MP | /* Math Present */ + X86_CR0_PE; /* Protection Enable */ + + vp_ctx->cr4 =3D + X86_CR4_PSE | /* Page Size Extensions */ + X86_CR4_PGE | /* Page Global Enable */ + X86_CR4_PAE; /* Physical Address Extensions */ + + vp_ctx->efer =3D + EFER_LMA | /* Long Mode Active */ + EFER_LME | /* Long Mode Enable */ + EFER_NX | /* No Execute Enable */ + EFER_SCE; /* System Call Enable */ + + /* + * Intel CPUs fail if the architectural read-as-one bit 1 of RFLAGS is not + * set. See Intel SDM Vol 3C, 26.3.1.4 (RFLAGS). + * + * TODO: Has Hyper-V implemented setting this automatically? + */ + vp_ctx->rflags =3D X86_EFLAGS_FIXED; + + vp_ctx->msr_cr_pat =3D PAT_VALUE(WB, WT, UC_MINUS, UC, WB, WT, UC_MINUS, = UC); +} + +void __init hv_vsm_arch_init_vp(struct hv_init_vp_context *vp_ctx, Elf64_A= ddr sk_entry_pa, + phys_addr_t sk_pa) +{ + hv_vsm_init_cpu(vp_ctx, sk_entry_pa); + hv_vsm_init_gdt(vp_ctx, sk_pa); + hv_vsm_init_page_tables(vp_ctx, sk_pa); +} diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyper= v.h index f64393e853ee3..5f0d689641f05 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,14 @@ void hv_crash_asm_end(void); static inline void hv_root_crash_init(void) {} #endif /* CONFIG_MSHV_ROOT && CONFIG_CRASH_DUMP */ =20 +#ifdef CONFIG_HYPERV_VSM +void __init hv_vsm_arch_init_vp(struct hv_init_vp_context *vp_ctx, Elf64_A= ddr sk_entry_pa, + phys_addr_t sk_pa); +#else /* CONFIG_HYPERV_VSM */ +static inline void __init hv_vsm_arch_init_vp(struct hv_init_vp_context *v= p_ctx, + Elf64_Addr sk_entry_pa, phys_addr_t sk_pa) {} +#endif + #else /* CONFIG_HYPERV */ static inline void hyperv_init(void) {} static inline void hyperv_setup_mmu_ops(void) {} --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DDEA6485507; Tue, 1 Sep 2026 16:57:32 +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=1788281855; cv=none; b=aJlFz4aMjdlRmwzuUmUZ3dc1Yii1xIUFKWBjTRd15/vg0vO5vTowyFerPs3EnFDfYq/LbJuKeBrfWrA6fXI3/udGrZ5kYtAkPgAw+I5lKXY1amQfemN9aOVJPS9aJ5vc4dDmfX1FFeQCpf7kiHC93eGWMAvtTGZq2WN5e5igKUI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281855; c=relaxed/simple; bh=lnl15zYdbgw4AvbUTmF/UF1u8SAdM/W5i85Ngfz8ptw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KlyNDXu3eaIEAr6hgX38Oy4AzTOZjpaba2XQ4IQ3iOiw0WW6CrY71nzqq+wDjU20On7aHBV7UfuFmOIIj2jo+wsh67cuX6jMY+ufNSQ0wnQYJOgUH+bCWIAAD54FmXUfGO78HSfzWCSkpswSw4oBX+WmpqJqH4kE9rMoA47fk8o= 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=Vlr3xV28; 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="Vlr3xV28" Received: by linux.microsoft.com (Postfix, from userid 1130) id 264AA20B7007; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 264AA20B7007 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281815; bh=ygiqmv+6eR1BISHHzIGNvCEf/BCpb358EIHFzlwQ96M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Vlr3xV28NUa6ZW9/NIYzNFnpSsRvFTD7/ZN41fm61hlBAIwZRM4okCLvLFjRG4FJ/ MAFD7Xs/KFTkASBrn5x0KXYDN9bLJ0OqO73dSF/twKPJn8WQlrOf6TvY7B2/eVor7x 9PZpk4bQVSlQ8iHCgUrsJXxdU2NMcwxrfowTMmV0= From: Thara Gopinath 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 , Stanislav Kinsburskii Subject: [RFC PATCH 09/12] drivers: hv: hv_vsm_boot: Enable VTL1 on the boot processor Date: Tue, 1 Sep 2026 09:55:23 -0700 Message-ID: <20260901165647.3160413-10-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" With the partition-level VTL1 enabled and the secure kernel image loaded into the reserved region, the next step is to enable VTL1 on the boot virtual processor so that the hypervisor can dispatch it into the secure kernel entry point. Introduce two helpers in hv_vsm_boot.c: - hv_vsm_get_vp_status() reads HV_REGISTER_VSM_VP_STATUS. - hv_vsm_enable_vp_vtl() issues HVCALL_ENABLE_VP_VTL with an initial vCPU context built via hv_vsm_arch_init_vp(). Extend hv_vsm_bootstrap_vtl() to enable VTL1 on the boot VP if it is not already enabled, and re-query the status to confirm the transition. Signed-off-by: Stanislav Kinsburskii Signed-off-by: Thara Gopinath --- drivers/hv/hv_vsm_boot.c | 73 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/drivers/hv/hv_vsm_boot.c b/drivers/hv/hv_vsm_boot.c index dc20f935da5b2..abe82e03e1897 100644 --- a/drivers/hv/hv_vsm_boot.c +++ b/drivers/hv/hv_vsm_boot.c @@ -216,6 +216,47 @@ static int __init hv_vsm_load_secure_kernel(Elf64_Addr= *sk_entry_pa) return ret; } =20 +static int __init hv_vsm_enable_vp_vtl(Elf64_Addr sk_entry_pa) +{ + u64 status =3D 0; + unsigned long flags; + struct hv_enable_vp_vtl *hvin; + + local_irq_save(flags); + + hvin =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(hvin, 0, sizeof(*hvin)); + + hvin->partition_id =3D HV_PARTITION_ID_SELF; + hvin->vp_index =3D HV_VP_INDEX_SELF; + hvin->target_vtl.target_vtl =3D HV_VTL_SECURE; + + hv_vsm_arch_init_vp(&hvin->vp_context, sk_entry_pa, sk_res.start); + + status =3D hv_do_hypercall(HVCALL_ENABLE_VP_VTL, hvin, NULL); + + local_irq_restore(flags); + + return hv_result(status); +} + +static int __init hv_vsm_get_vp_status(u16 *enabled_vtl_set, u8 *active_mb= ec_enabled) +{ + u64 result; + int ret; + union hv_register_vsm_vp_status vsm_vp_status =3D { 0 }; + + ret =3D hv_vsm_get_register(HV_REGISTER_VSM_VP_STATUS, &result); + if (ret) + return ret; + + vsm_vp_status =3D (union hv_register_vsm_vp_status)result; + *enabled_vtl_set =3D vsm_vp_status.enabled_vtl_set; + *active_mbec_enabled =3D vsm_vp_status.active_mbec_enabled; + + return 0; +} + static int __init hv_vsm_enable_partition_vtl(void) { u64 status =3D 0; @@ -262,7 +303,8 @@ static int __init hv_vsm_get_partition_status(u16 *enab= led_vtl_set, u8 *max_vtl, static int __init hv_vsm_bootstrap_vtl(void) { u16 partition_enabled_vtl_set =3D 0, partition_mbec_enabled_vtl_set =3D 0; - u8 partition_max_vtl; + u16 vp_enabled_vtl_set =3D 0; + u8 partition_max_vtl, active_mbec_enabled =3D 0; Elf64_Addr sk_entry_pa; int ret; =20 @@ -300,7 +342,34 @@ static int __init hv_vsm_bootstrap_vtl(void) } } =20 - return hv_vsm_load_secure_kernel(&sk_entry_pa); + ret =3D hv_vsm_load_secure_kernel(&sk_entry_pa); + if (ret) + return ret; + + /* Check and enable VTL1 for the primary virtual processor */ + ret =3D hv_vsm_get_vp_status(&vp_enabled_vtl_set, &active_mbec_enabled); + if (ret) + return ret; + + if (vp_enabled_vtl_set & HV_VTL1_ENABLE_BIT) { + pr_info("VP VTL1 is already enabled\n"); + } else { + ret =3D hv_vsm_enable_vp_vtl(sk_entry_pa); + if (ret) { + pr_err("Enabling VP VTL1 failed with status 0x%x\n", ret); + /* TODO: Should we disable VTL1 at partition level in this case? */ + return -EINVAL; + } + ret =3D hv_vsm_get_vp_status(&vp_enabled_vtl_set, &active_mbec_enabled); + if (ret) + return ret; + + if (!(vp_enabled_vtl_set & HV_VTL1_ENABLE_BIT)) { + pr_err("Tried Enabling VP VTL1 and still failed\n"); + return -EINVAL; + } + } + return 0; } =20 static void __init hv_vsm_get_sk_mem(void) --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DDFCD485509; Tue, 1 Sep 2026 16:57:32 +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=1788281855; cv=none; b=rbMMO2zFMP+2WmrtpJFHCcFq15iyqCwGiXQMIKk/Jjgbhuq99qf09qAq+CNuY2ybDMsb4TIGTpkcp6Ty2fEr8WoffEuHAG0zSNSDIbvWgwLGujrSFpbPxCiBI3BtYyzGwYcVlp7T+TeAFkGFhI+9xTNEan5UngCKzJbbSFexNoM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281855; c=relaxed/simple; bh=6ACAmfuXfuayPo9P9g+Tv5LmBGEjhYAQa2wbUFO4lTE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kdPNF4RLwjoJy9FqrubRIY4MjFriZxhA46/0qvj6TkWsZsFG6u4A22l7ak0RTB1FtKicvm/wU1RWGN2XRd0ar53jpPrnt3rBgB7BGwJa7sCmTYbu+3FNXrEtaL7Jax2SGAEUSAqOMoCwEfxRO+vyge21Sh0+/l+UOm6LmABNiSo= 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=McJBLdbI; 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="McJBLdbI" Received: by linux.microsoft.com (Postfix, from userid 1130) id 33CB820B700D; Tue, 1 Sep 2026 09:56:54 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 33CB820B700D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281815; bh=hQRN4nwUQeOBU7mz2RGA5hRCIuLQcM69ucNk/JxMyq8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=McJBLdbIW34shC6NMxxZowCBbWf/pha9V9i+9tjZkpTT0ieqmUv5i8DdSqK5u6KXH 9tzHtrXxmw5I4AC/gdYenlH8uhNSfdfX5QFy4IcA4WNTiYvhJNe9O8+HnNY4RferGx UyOxDVK2MZxmT8ShmihDRUfuoUlXMegLoWSzIKgk= From: Thara Gopinath 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 Subject: [RFC PATCH 10/12] arch: x86: hyperv: hv_vtl_vsm: Introduce vtlcall Date: Tue, 1 Sep 2026 09:55:24 -0700 Message-ID: <20260901165647.3160413-11-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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" A vtlcall is the mechanism by which a lower VTL (VTL0) transitions into a higher VTL (VTL1) on Microsoft Hyper-V. It is issued as a call to a fixed offset within the hypercall page; the hypervisor saves the lower-VTL context, restores the upper-VTL context, and resumes execution at the upper VTL. Control returns to VTL0 when the upper VTL executes a vtlreturn. Introduce the x86 VTL0-side vtlcall infrastructure: - hv_vsm_vtlcall(): runtime entry point. Preserves state VTL1 can clobber (IRQs, FPU, CR2) and invokes the assembly trampoline. Returns the signed 64-bit status the secure kernel places in a3. - hv_vsm_init_vtlcall(u64 vtl_call_offset): one-shot initializer that installs the vtlcall target into a static_call. The offset is passed in by the caller so the register read stays outside arch code, mirroring mshv_vtl_return_call_init(). - struct hv_vtlcall_param and CONFIG_HYPERV_VSM API in include/hyperv/vsm.h, with no-op stubs when VSM is disabled. - __hv_vsm_vtlcall assembly trampoline in mshv_vtl_asm.S, plus asm-offsets for the argument block. Signed-off-by: Thara Gopinath --- arch/x86/hyperv/Makefile | 2 +- arch/x86/hyperv/hv_vtl_vsm.c | 29 ++++++++++++ arch/x86/hyperv/mshv-asm-offsets.c | 8 ++++ arch/x86/hyperv/mshv_vtl_asm.S | 75 ++++++++++++++++++++++++++++++ include/hyperv/vsm.h | 22 +++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) diff --git a/arch/x86/hyperv/Makefile b/arch/x86/hyperv/Makefile index 1fdc20e239243..430f5f2bca40a 100644 --- a/arch/x86/hyperv/Makefile +++ b/arch/x86/hyperv/Makefile @@ -2,7 +2,7 @@ obj-y :=3D hv_init.o mmu.o nested.o irqdomain.o ivm.o obj-$(CONFIG_X86_64) +=3D hv_apic.o obj-$(CONFIG_HYPERV_VTL_MODE) +=3D hv_vtl.o mshv_vtl_asm.o -obj-$(CONFIG_HYPERV_VSM) +=3D hv_vtl_vsm.o +obj-$(CONFIG_HYPERV_VSM) +=3D hv_vtl_vsm.o mshv_vtl_asm.o =20 $(obj)/mshv_vtl_asm.o: $(obj)/mshv-asm-offsets.h =20 diff --git a/arch/x86/hyperv/hv_vtl_vsm.c b/arch/x86/hyperv/hv_vtl_vsm.c index edc55264c4d87..d533a8e113935 100644 --- a/arch/x86/hyperv/hv_vtl_vsm.c +++ b/arch/x86/hyperv/hv_vtl_vsm.c @@ -15,6 +15,7 @@ #include #include #include +#include #include =20 /* Define PAGE size and related variables for initial secure kernel pages = */ @@ -256,3 +257,31 @@ void __init hv_vsm_arch_init_vp(struct hv_init_vp_cont= ext *vp_ctx, Elf64_Addr sk hv_vsm_init_gdt(vp_ctx, sk_pa); hv_vsm_init_page_tables(vp_ctx, sk_pa); } + +/* Implemented in mshv_vtl_asm.S */ +void __hv_vsm_vtlcall(struct hv_vtlcall_param *args); + +DEFINE_STATIC_CALL_NULL(__hv_vsm_vtlcall_hypercall, void (*)(void)); + +void __init hv_vsm_init_vtlcall(u64 vtl_call_offset) +{ + static_call_update(__hv_vsm_vtlcall_hypercall, + (void *)((u8 *)hv_hypercall_pg + vtl_call_offset)); +} + +s64 hv_vsm_vtlcall(struct hv_vtlcall_param *args) +{ + unsigned long flags; + u64 cr2; + + local_irq_save(flags); + kernel_fpu_begin_mask(0); + cr2 =3D native_read_cr2(); + __hv_vsm_vtlcall(args); + native_write_cr2(cr2); + kernel_fpu_end(); + local_irq_restore(flags); + + /* The secure kernel returns a signed 64-bit status in a3. */ + return (s64)args->a3; +} diff --git a/arch/x86/hyperv/mshv-asm-offsets.c b/arch/x86/hyperv/mshv-asm-= offsets.c index 882c1db6df16c..0f0470ddaee59 100644 --- a/arch/x86/hyperv/mshv-asm-offsets.c +++ b/arch/x86/hyperv/mshv-asm-offsets.c @@ -12,6 +12,7 @@ #define COMPILE_OFFSETS =20 #include +#include #include =20 static void __used common(void) @@ -34,4 +35,11 @@ static void __used common(void) OFFSET(MSHV_VTL_CPU_CONTEXT_r15, mshv_vtl_cpu_context, r15); OFFSET(MSHV_VTL_CPU_CONTEXT_cr2, mshv_vtl_cpu_context, cr2); } + + if (IS_ENABLED(CONFIG_HYPERV_VSM)) { + OFFSET(HV_VTLCALL_PARAM_a0, hv_vtlcall_param, a0); + OFFSET(HV_VTLCALL_PARAM_a1, hv_vtlcall_param, a1); + OFFSET(HV_VTLCALL_PARAM_a2, hv_vtlcall_param, a2); + OFFSET(HV_VTLCALL_PARAM_a3, hv_vtlcall_param, a3); + } } diff --git a/arch/x86/hyperv/mshv_vtl_asm.S b/arch/x86/hyperv/mshv_vtl_asm.S index f595eefad9abf..4d7007848ecd4 100644 --- a/arch/x86/hyperv/mshv_vtl_asm.S +++ b/arch/x86/hyperv/mshv_vtl_asm.S @@ -15,6 +15,8 @@ #include #include "mshv-asm-offsets.h" =20 +#ifdef CONFIG_HYPERV_VTL_MODE + .text .section .noinstr.text, "ax" /* @@ -114,3 +116,76 @@ SYM_FUNC_END(__mshv_vtl_return_call) .size mshv_vtl_return_sym, 8 mshv_vtl_return_sym: .quad __SCK____mshv_vtl_return_hypercall + +#endif /* CONFIG_HYPERV_VTL_MODE */ + +#ifdef CONFIG_HYPERV_VSM + .text +/* + * void __hv_vsm_vtlcall(struct hv_vtlcall_param *args) + * + * Perform a VTL call to switch to the upper VTL. + * + * The %rcx register is zeroed before the call and is clobbered by the + * hypercall. %rax is not restored by the upper VTL (passed via the assist + * page) but is unused and can be ignored. + * + * The args pointer is preserved on the stack across the VTL call since all + * argument registers are repurposed during the VTL call. + * + * Microsoft Hypervisor preserves %rsp during VTL switches. + */ +SYM_FUNC_START(__hv_vsm_vtlcall) + /* Save callee-saved registers */ + pushq %rbp + mov %rsp, %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + pushq %rbx + pushq %rdi + + /* Load struct fields into VTL calling convention registers */ + mov HV_VTLCALL_PARAM_a3(%rdi), %r8 + mov HV_VTLCALL_PARAM_a2(%rdi), %rdx + mov HV_VTLCALL_PARAM_a1(%rdi), %rsi + mov HV_VTLCALL_PARAM_a0(%rdi), %rdi + + /* Zero %rcx */ + xorl %ecx, %ecx + + /* VTL call */ + call STATIC_CALL_TRAMP_STR(__hv_vsm_vtlcall_hypercall) + + /* Restore args pointer from stack */ + popq %rax + + /* Store results back to struct */ + mov %rdi, HV_VTLCALL_PARAM_a0(%rax) + mov %rsi, HV_VTLCALL_PARAM_a1(%rax) + mov %rdx, HV_VTLCALL_PARAM_a2(%rax) + mov %r8, HV_VTLCALL_PARAM_a3(%rax) + + /* Restore callee-saved registers */ + popq %rbx + popq %r15 + popq %r14 + popq %r13 + popq %r12 + + popq %rbp + RET +SYM_FUNC_END(__hv_vsm_vtlcall) + +/* + * Ensure static_call_key symbol __SCK____hv_vsm_vtlcall_hypercall is + * accessible. Inspired by __ADDRESSABLE(sym) macro. + */ + .section .discard.addressable,"aw" + .align 8 + .type hv_vsm_vtlcall_sym, @object + .size hv_vsm_vtlcall_sym, 8 +hv_vsm_vtlcall_sym: + .quad __SCK____hv_vsm_vtlcall_hypercall +#endif /* CONFIG_HYPERV_VSM */ diff --git a/include/hyperv/vsm.h b/include/hyperv/vsm.h index 51555c09413d5..c1fa4ef3bccde 100644 --- a/include/hyperv/vsm.h +++ b/include/hyperv/vsm.h @@ -11,6 +11,8 @@ #ifndef _HYPERV_VSM_H #define _HYPERV_VSM_H =20 +#include + /* * Size of memory that is initially mapped for the secure kernel by the * VTL0-side loader. The secure kernel image itself may be larger than @@ -18,4 +20,24 @@ */ #define VSM_SK_INITIAL_MAP_SIZE (16 * 1024 * 1024) =20 +/* + * Argument block passed from VTL0 to VTL1 across a vtlcall. Layout is + * shared with the arch-specific assembly trampoline that marshals these + * into registers. + */ +struct hv_vtlcall_param { + u64 a0; + u64 a1; + u64 a2; + u64 a3; +} __packed; + +#ifdef CONFIG_HYPERV_VSM +s64 hv_vsm_vtlcall(struct hv_vtlcall_param *args); +void hv_vsm_init_vtlcall(u64 vtl_call_offset); +#else +static inline s64 hv_vsm_vtlcall(struct hv_vtlcall_param *args) { return 0= ; } +static inline void hv_vsm_init_vtlcall(u64 vtl_call_offset) {} +#endif + #endif /* _HYPERV_VSM_H */ --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7602A2C08CF; Tue, 1 Sep 2026 16:57:33 +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=1788281855; cv=none; b=qgGxdD0zXzxQN55yNRNEx/cO3lyoILafUZcqJCASJmnvsmSmlt/auzWcNRRdwMYVn1IOQmW6FpLZOe0LMUyviHKtOgoC1b1NXSoNjGhIsGapiySm8vFbJ4LgnoeL8wwIhIJmqhj09IMseQMvz0PXXqHnDSL+q7HO+P89QK+WFmk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281855; c=relaxed/simple; bh=PLAM//gH3pC8RtY75QNvI4XuHtIFzX+atB5kQfIBgqY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VJIknfmXNhsE0jMqujiECS6l1r/1yxg7dYHNAzkRfK8plzNIN+MTPdroMtQ8n12rxFmkTD6LHq+7kiCNXrR36qGZauZYHYuWPrRmgp/q7OQuiH/tzwlkO0sh4MaywTFekDEGxDoXkB/hcVbE8Ab+K+670Oe2x/061Kcjf/x1dU8= 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=g8NEjFzx; 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="g8NEjFzx" Received: by linux.microsoft.com (Postfix, from userid 1130) id 4138D20B7017; Tue, 1 Sep 2026 09:56:55 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 4138D20B7017 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281815; bh=ZY/1jnT1S4FvnKm9pAuQgFMfXX15iTnB1CdG+D9HuaY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=g8NEjFzxKEUT2f1QYMKQCOhT9IsYFA5PlPTX04nFGSu2c3iZ1lZRYzbEyuGnjrkr2 s604NqHj4AlPuLmqJdpm0Nu79PpnW8+ALOuXP5jLwodZvKD5DjQ3fjhllDxRrLlMyd hMJLXQ8mo9ooFAXbbi9WI7ErGiv72DSR/LKcFkoA= From: Thara Gopinath 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 Subject: [RFC PATCH 11/12] drivers: hv: hv_vsm_boot: Boot primary processor in VTL1 Date: Tue, 1 Sep 2026 09:55:25 -0700 Message-ID: <20260901165647.3160413-12-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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 With partition-level VTL1 enabled, the secure kernel image loaded, its initial vCPU context built, and VTL1 enabled on the boot CPU, the final step of the primary-CPU bring-up is to actually transition into VTL1 and start executing the secure kernel. Hyper-V restores the CPU state from the vp_context supplied during VTL1 enablement when the first vtlcall is issued. Issue that first vtlcall on the primary CPU, passing the number of possible CPUs and the location of the secure kernel reserved region as the VTL1 boot handshake. Signed-off-by: Thara Gopinath --- drivers/hv/hv_vsm_boot.c | 63 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_vsm_boot.c b/drivers/hv/hv_vsm_boot.c index abe82e03e1897..c4f15c42df1f6 100644 --- a/drivers/hv/hv_vsm_boot.c +++ b/drivers/hv/hv_vsm_boot.c @@ -35,6 +35,15 @@ =20 static void *vsm_skm_va; =20 +/* + * By default, when a processor boots in VTL1, we assume that MBEC (Mode-B= ased Execution Control) + * support is also enabled. MBEC distinguishes between user and kernel mem= ory execution permissions. + * After the processor boots in VTL1, we verify whether MBEC is actually e= nabled. If it is not, + * we set a global flag to false. This flag is shared across all processor= s=E2=80=94if any processor fails + * to enable MBEC, the system treats MBEC as disabled. + */ +static bool hv_vsm_mbec_enabled =3D true; + static int hv_vsm_get_register(u32 reg_name, u64 *result) { struct hv_register_assoc reg =3D { @@ -300,6 +309,51 @@ static int __init hv_vsm_get_partition_status(u16 *ena= bled_vtl_set, u8 *max_vtl, return 0; } =20 +static int __init hv_vsm_init_code_page_offsets(void) +{ + union hv_register_vsm_page_offsets offsets; + u64 result; + int ret; + + ret =3D hv_vsm_get_register(HV_REGISTER_VSM_CODE_PAGE_OFFSETS, &result); + if (ret) { + pr_err("Failed to read VSM code page offsets: %d\n", ret); + return ret; + } + + offsets.as_uint64 =3D result; + hv_vsm_init_vtlcall(offsets.vtl_call_offset); + return 0; +} + +static int __init hv_vsm_boot_vtl1(void) +{ + struct hv_vtlcall_param args =3D {0}; + u16 vp_enabled_vtl_set =3D 0; + u8 active_mbec_enabled =3D 0; + int ret; + s64 sk_status; + + args.a0 =3D num_possible_cpus(); + args.a1 =3D sk_res.start; + args.a2 =3D resource_size(&sk_res); + + /* Kick start vtl1 boot on the primary cpu. */ + sk_status =3D hv_vsm_vtlcall(&args); + if (sk_status) + pr_warn("VTL1 boot returned status %lld\n", sk_status); + + ret =3D hv_vsm_get_vp_status(&vp_enabled_vtl_set, &active_mbec_enabled); + if (ret) + return ret; + + if (!active_mbec_enabled) { + pr_err("Failed to enable MBEC for VP0\n"); + hv_vsm_mbec_enabled =3D false; + } + return 0; +} + static int __init hv_vsm_bootstrap_vtl(void) { u16 partition_enabled_vtl_set =3D 0, partition_mbec_enabled_vtl_set =3D 0; @@ -369,7 +423,14 @@ static int __init hv_vsm_bootstrap_vtl(void) return -EINVAL; } } - return 0; + + /* Point the vtlcall trampoline at the correct hypercall page offset */ + ret =3D hv_vsm_init_code_page_offsets(); + if (ret) + return ret; + + /* Boot primary virtual processor in VTL1 */ + return hv_vsm_boot_vtl1(); } =20 static void __init hv_vsm_get_sk_mem(void) --=20 2.34.1 From nobody Sat Sep 26 16:39:32 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 95C69485CC4; Tue, 1 Sep 2026 16:57:33 +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=1788281856; cv=none; b=Hya2gG0Mm15zcy7AxHY/eHpcVh+q2kXwP/mR3JsWYCdulqFqzT8niiT1pGPxecq/7h1LPhKwByZumKW6hYPRSVgXrNe2DABX8bKIPccsc+W0s2LG5PZkbV2v/7hjBI15sHVOo9CCH60+8L4GGybFQcsLmcdVj21Ufl9CDbsvGYo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788281856; c=relaxed/simple; bh=AqlGXiVXf37eh22vHSTx9JaWG/V6ubR35mhahPGPQBM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RCF6PWpQ+hXLyxM9d0y+zz7jBcqCgSSdUJvduHeUxhXfgK9btRrgp1p914uzbQm+sSlmHJCMvNsRJuCtoY6l1R0ItbS8iLw3LFuKAM7fUShO0/TPEdto2HpzbBqQ2M6AAVoQYrDeFqScc0ONcRqbe4vdEI7ffWDI32I9brz0aJM= 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=WX8y3hoB; 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="WX8y3hoB" Received: by linux.microsoft.com (Postfix, from userid 1130) id 4E7F520B703B; Tue, 1 Sep 2026 09:56:55 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 4E7F520B703B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1788281815; bh=Pnx8Bj8E6wa9z9v4waAAekvlC25njxpIEY0WH/R0iXY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WX8y3hoBoGyybXNeAFKK+e/bkxFiPQ1o+ld/V9sMIm3//6oWYQPfpsRuS0uCZwu+n bE6V3bvgRkADF2qA0orsEWApO0QyM005HzYejVGds1mJji4i/k5ylIz4gKvfQzTh3w Droee3zX8Sp0VncsDb586EPDsfebMXHZrvbBy3yU= From: Thara Gopinath 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 Subject: [RFC PATCH 12/12] drivers: hv: hv_vsm_boot: Boot secondary processors in VTL1 Date: Tue, 1 Sep 2026 09:55:26 -0700 Message-ID: <20260901165647.3160413-13-tgopinath@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260901165647.3160413-1-tgopinath@linux.microsoft.com> References: <20260901165647.3160413-1-tgopinath@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 next step after the primary CPU is running in VTL1 is to boot the remaining CPUs in VTL1, so the secure kernel executes on every VP rather than only VP0. Hyper-V does not drive this: VTL1 needs to know which APs to expect, and each AP must issue its own vtlcall from VTL0 to actually transition. As on the boot CPU, VTL1 must be enabled at each VP and its initial VP context (RIP, GDT, page tables, etc.) built before that VP can enter VTL1. This can only be done from VTL1 for non boot CPus. Introduce VSM_VTL_CALL_FUNC_ID_BOOT_APS, a vtlcall the boot cpu issues from VTL0, which asks the secure kernel to enable VTL1 and set up the initial VP context for each AP in the supplied online-CPU mask. The mask is handed to VTL1 via a shared page whose PFN is passed in the vtlcall arguments; VTL1 copies it synchronously so the page can be freed as soon as the call returns. Once VTL1 has prepared the APs, bring them into VTL1 one at a time using a CPU-bound FIFO kthread and a completion. Serialising this way keeps VTL1 entries ordered and lets a per-AP failure be surfaced synchronously to the caller. Uniprocessor systems skip AP bring-up entirely. Signed-off-by: Thara Gopinath --- drivers/hv/hv_vsm.h | 2 + drivers/hv/hv_vsm_boot.c | 125 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_vsm.h b/drivers/hv/hv_vsm.h index 88f099f88eeb4..679cb9dc9afda 100644 --- a/drivers/hv/hv_vsm.h +++ b/drivers/hv/hv_vsm.h @@ -12,6 +12,8 @@ #include #include =20 +#define VSM_VTL_CALL_FUNC_ID_BOOT_APS 0x1FFE1 + extern struct resource sk_res; =20 #endif /* _HV_VSM_H */ diff --git a/drivers/hv/hv_vsm_boot.c b/drivers/hv/hv_vsm_boot.c index c4f15c42df1f6..ad161b2f56653 100644 --- a/drivers/hv/hv_vsm_boot.c +++ b/drivers/hv/hv_vsm_boot.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,20 @@ static int hv_vsm_get_register(u32 reg_name, u64 *result) return 0; } =20 +static __init struct page *hv_vsm_alloc_shared_page(void) +{ + struct page *page; + + page =3D alloc_page(GFP_KERNEL); + if (!page) { + pr_err("Unable to establish VTL0-VTL1 shared page\n"); + return ERR_PTR(-ENOMEM); + } + + memset(page_address(page), 0, PAGE_SIZE); + return page; +} + static Elf64_Addr __init hv_vsm_elf_min_load_paddr(void *image) { Elf64_Ehdr *ehdr =3D image; @@ -326,6 +341,107 @@ static int __init hv_vsm_init_code_page_offsets(void) return 0; } =20 +struct hv_vsm_ap_boot_ctx { + struct completion done; + int ret; +}; + +static int __init hv_vsm_boot_sec_vp_thread_fn(void *arg) +{ + struct hv_vsm_ap_boot_ctx *ctx =3D arg; + struct hv_vtlcall_param args =3D {0}; + int cpu =3D smp_processor_id(); + u16 vp_enabled_vtl_set =3D 0; + u8 active_mbec_enabled =3D 0; + s64 sk_status; + int ret =3D 0; + + pr_info("cpu%d entering vtl1 boot thread\n", cpu); + sk_status =3D hv_vsm_vtlcall(&args); + if (sk_status) + pr_warn("VP%d VTL1 boot returned status %lld\n", cpu, sk_status); + + ret =3D hv_vsm_get_vp_status(&vp_enabled_vtl_set, &active_mbec_enabled); + if (ret) + goto out; + + if (!active_mbec_enabled) { + pr_err("Failed to enable MBEC for VP%d\n", cpu); + hv_vsm_mbec_enabled =3D false; + } +out: + ctx->ret =3D ret; + complete(&ctx->done); + return 0; +} + +static int __init hv_vsm_boot_one_ap(unsigned int cpu) +{ + struct hv_vsm_ap_boot_ctx ctx; + struct task_struct *t; + + init_completion(&ctx.done); + ctx.ret =3D 0; + + t =3D kthread_create(hv_vsm_boot_sec_vp_thread_fn, &ctx, + "hv-vtl1-ap%u", cpu); + if (IS_ERR(t)) + return PTR_ERR(t); + + kthread_bind(t, cpu); + sched_set_fifo(t); + wake_up_process(t); + + wait_for_completion(&ctx.done); + return ctx.ret; +} + +static int __init hv_vsm_boot_ap_vtl(void) +{ + struct hv_vtlcall_param args =3D {0}; + struct page *cpu_online_page; + unsigned int cpu, cur_cpu =3D smp_processor_id(); + s64 sk_status; + int ret; + + cpu_online_page =3D hv_vsm_alloc_shared_page(); + if (IS_ERR(cpu_online_page)) + return PTR_ERR(cpu_online_page); + + cpumask_copy(page_address(cpu_online_page), cpu_online_mask); + + /* + * Hand VTL1 the set of APs to expect. VTL1 copies the mask + * synchronously inside this vtlcall and does not reference the + * page after it returns, so freeing it here is safe. + */ + args.a0 =3D VSM_VTL_CALL_FUNC_ID_BOOT_APS; + args.a1 =3D page_to_pfn(cpu_online_page); + sk_status =3D hv_vsm_vtlcall(&args); + __free_page(cpu_online_page); + if (sk_status) { + pr_err("VTL1 refused BOOT_APS: status %lld\n", sk_status); + return -EIO; + } + + /* + * Bring the APs into VTL1 one at a time. Each AP kthread issues + * a single vtlcall on its bound CPU and signals completion; wait + * for it to finish before starting the next so VTL1 entries stay + * serialized. + */ + for_each_online_cpu(cpu) { + if (cpu =3D=3D cur_cpu) + continue; + ret =3D hv_vsm_boot_one_ap(cpu); + if (ret) { + pr_err("Failed to boot VP%u into VTL1: %d\n", cpu, ret); + return ret; + } + } + return 0; +} + static int __init hv_vsm_boot_vtl1(void) { struct hv_vtlcall_param args =3D {0}; @@ -430,7 +546,14 @@ static int __init hv_vsm_bootstrap_vtl(void) return ret; =20 /* Boot primary virtual processor in VTL1 */ - return hv_vsm_boot_vtl1(); + ret =3D hv_vsm_boot_vtl1(); + if (ret) + return ret; + + if (num_present_cpus() =3D=3D 1) + return 0; + + return hv_vsm_boot_ap_vtl(); } =20 static void __init hv_vsm_get_sk_mem(void) --=20 2.34.1