From: Wei Hu <weh@linux.microsoft.com>
To: linux-hyperv@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
"K. Y. Srinivasan" <kys@microsoft.com>,
Haiyang Zhang <haiyangz@microsoft.com>,
Wei Liu <wei.liu@kernel.org>, Dexuan Cui <decui@microsoft.com>,
Long Li <longli@microsoft.com>, Wei Hu <weh@microsoft.com>,
Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
x86@kernel.org, "H. Peter Anvin" <hpa@zytor.com>,
Ard Biesheuvel <ardb@kernel.org>,
Ilias Apalodimas <ilias.apalodimas@linaro.org>,
Arnd Bergmann <arnd@arndb.de>,
linux-efi@vger.kernel.org, linux-arch@vger.kernel.org
Subject: [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader)
Date: Fri, 7 Aug 2026 13:51:18 +0000 [thread overview]
Message-ID: <20260807135134.303943-13-weh@linux.microsoft.com> (raw)
In-Reply-To: <20260807135134.303943-1-weh@linux.microsoft.com>
From: Wei Hu <weh@microsoft.com>
Port the EFI HvLoader protocol handshake (efi-mshv stub + SETUP_MSHV) and
the hyperv_resvd_new memory reservation so an upstream kernel can boot as
a Microsoft Hypervisor (MSHV) root partition. Mainline lacks this
Linux-side enablement, so lxhvloader declines to hand off a stock kernel
as root.
Signed-off-by: Wei Hu <weh@microsoft.com>
---
MAINTAINERS | 2 +
arch/x86/hyperv/hv_init.c | 3 +
arch/x86/include/uapi/asm/setup_data.h | 3 +-
arch/x86/kernel/cpu/mshyperv.c | 80 ++++++-
drivers/firmware/efi/libstub/Makefile | 7 +
drivers/firmware/efi/libstub/arm64-efi-mshv.c | 41 ++++
.../firmware/efi/libstub/efi-mshv-common.c | 136 ++++++++++++
drivers/firmware/efi/libstub/efi-mshv.h | 118 ++++++++++
drivers/firmware/efi/libstub/x86-efi-mshv.c | 202 ++++++++++++++++++
drivers/firmware/efi/libstub/x86-stub.c | 18 +-
drivers/hv/hv_common.c | 82 +++++++
include/asm-generic/mshyperv.h | 7 +
include/linux/efi.h | 1 +
13 files changed, 697 insertions(+), 3 deletions(-)
create mode 100644 drivers/firmware/efi/libstub/arm64-efi-mshv.c
create mode 100644 drivers/firmware/efi/libstub/efi-mshv-common.c
create mode 100644 drivers/firmware/efi/libstub/efi-mshv.h
create mode 100644 drivers/firmware/efi/libstub/x86-efi-mshv.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 716acfc3d7c1..0da3c7929aef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12098,6 +12098,8 @@ F: arch/x86/include/asm/mshyperv.h
F: arch/x86/include/asm/trace/hyperv.h
F: arch/x86/kernel/cpu/mshyperv.c
F: drivers/clocksource/hyperv_timer.c
+F: drivers/firmware/efi/libstub/*-efi-mshv.c
+F: drivers/firmware/efi/libstub/efi-mshv*
F: drivers/hid/hid-hyperv.c
F: drivers/hv/
F: drivers/input/serio/hyperv-keyboard.c
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
index 55a8b6de2865..4ee8035cb990 100644
--- a/arch/x86/hyperv/hv_init.c
+++ b/arch/x86/hyperv/hv_init.c
@@ -557,6 +557,9 @@ void __init hyperv_init(void)
hv_remap_tsc_clocksource();
hv_sleep_notifiers_register();
+
+ /* mark ram reserved for hypervisor as owned by hypervisor */
+ hv_mark_resources();
} else {
hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg);
wrmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
diff --git a/arch/x86/include/uapi/asm/setup_data.h b/arch/x86/include/uapi/asm/setup_data.h
index 2671c4e1b3a0..e88f7bf709ab 100644
--- a/arch/x86/include/uapi/asm/setup_data.h
+++ b/arch/x86/include/uapi/asm/setup_data.h
@@ -14,7 +14,8 @@
#define SETUP_IMA 8
#define SETUP_RNG_SEED 9
#define SETUP_KEXEC_KHO 10
-#define SETUP_ENUM_MAX SETUP_KEXEC_KHO
+#define SETUP_MSHV 11
+#define SETUP_ENUM_MAX SETUP_MSHV
#define SETUP_INDIRECT (1<<31)
#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT)
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index 185d4f677ec0..46c3fca3d99d 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -13,6 +13,8 @@
#include <linux/export.h>
#include <linux/hardirq.h>
#include <linux/efi.h>
+#include <linux/memblock.h>
+#include <linux/crash_dump.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kexec.h>
@@ -22,6 +24,7 @@
#include <asm/cpuid/api.h>
#include <hyperv/hvhdk.h>
#include <asm/mshyperv.h>
+#include <asm/e820/api.h>
#include <asm/desc.h>
#include <asm/idtentry.h>
#include <asm/irq_regs.h>
@@ -496,6 +499,75 @@ EXPORT_SYMBOL_GPL(hv_get_hypervisor_version);
* Reserved vectors hard coded in the hypervisor. If used outside, the hypervisor
* will either crash or hang or attempt to break into debugger.
*/
+bool mshv_loader_new = true;
+
+static int hv_resvd_ranges[HV_MAX_RESVD_RANGES] = {
+ [0 ... HV_MAX_RESVD_RANGES - 1] = -1};
+
+/*
+ * Parse eg "hyperv_resvd=3,7,20" where 3, 7, and 20 are indexes into the e820
+ * table for ranges that are reserved by the loader for the hypervisor
+ */
+static int __init hv_parse_hyperv_resvd(char *arg)
+{
+ int idx, max = ARRAY_SIZE(hv_resvd_ranges);
+ int i = 0;
+
+ mshv_loader_new = false;
+
+ if (is_kdump_kernel())
+ return 0;
+
+ if (hv_resvd_ranges[0] != -1) {
+ pr_err("Hyper-V: multiple hyperv_resvd not supported\n");
+ return 0;
+ }
+
+ while (get_option(&arg, &idx)) {
+ if (i >= max) {
+ pr_err("Hyper-V: resvd ranges tbl full %d\n", idx);
+ break;
+ }
+
+ hv_resvd_ranges[i++] = idx;
+ }
+
+ return 0;
+}
+early_param("hyperv_resvd", hv_parse_hyperv_resvd);
+
+/*
+ * Reserve memory that the hypervisor is using early on. The ranges are marked
+ * reserved by a custom bootloader, change that to usable and reserve that
+ * range. Note, the bootloader sanitizes the e820 before passing on here.
+ */
+static void __init hv_resv_mshv_memory(void)
+{
+ u64 start, end, size;
+ int i, idx, max = ARRAY_SIZE(hv_resvd_ranges);
+
+ for (i = 0; i < max && hv_resvd_ranges[i] != -1; i++) {
+ idx = hv_resvd_ranges[i];
+ if (idx < 0 || idx >= e820_table->nr_entries) {
+ pr_info("Hyper-V: invalid resvd idx %d\n", idx);
+ continue;
+ }
+
+ start = e820_table->entries[idx].addr;
+ size = e820_table->entries[idx].size;
+ end = start + size - 1;
+
+ memblock_reserve(start, size);
+ e820_table->entries[idx].type = E820_TYPE_RAM;
+ pr_info("Hyper-V reserve [mem %#018Lx-%#018Lx]\n", start, end);
+
+ hv_mshv_res[i].name = "Hypervisor Code and Data";
+ hv_mshv_res[i].flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM;
+ hv_mshv_res[i].start = start;
+ hv_mshv_res[i].end = end;
+ }
+}
+
static void hv_reserve_irq_vectors(void)
{
#define HYPERV_DBG_FASTFAIL_VECTOR 0x29
@@ -547,8 +619,14 @@ static void __init ms_hyperv_init_platform(void)
hv_identify_partition_type();
- if (hv_root_partition())
+ if (hv_root_partition()) {
+ /* very first thing, reserve/log exclusive hypervisor memory */
+ if (mshv_loader_new)
+ hv_dump_mshv_memory();
+ else
+ hv_resv_mshv_memory();
hv_reserve_irq_vectors();
+ }
if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
ms_hyperv.hints |= HV_DEPRECATING_AEOI_RECOMMENDED;
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 77a2b2d74f3f..d116a6c99527 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -87,6 +87,13 @@ lib-$(CONFIG_X86) += x86-stub.o smbios.o
lib-$(CONFIG_X86_64) += x86-5lvl.o
lib-$(CONFIG_RISCV) += kaslr.o riscv.o riscv-stub.o
lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o
+lib-$(subst m,y,$(CONFIG_MSHV_ROOT)) += efi-mshv-common.o
+ifdef CONFIG_X86_64
+lib-$(CONFIG_MSHV_ROOT) += x86-efi-mshv.o
+endif
+ifdef CONFIG_ARM64
+lib-$(subst m,y,$(CONFIG_MSHV_ROOT)) += arm64-efi-mshv.o
+endif
CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
diff --git a/drivers/firmware/efi/libstub/arm64-efi-mshv.c b/drivers/firmware/efi/libstub/arm64-efi-mshv.c
new file mode 100644
index 000000000000..374ca1b41e03
--- /dev/null
+++ b/drivers/firmware/efi/libstub/arm64-efi-mshv.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+#include <asm/setup.h>
+
+#include "efistub.h"
+#include "efi-mshv.h"
+
+efi_status_t mshv_efi_setup(char **cmdline_ptr)
+{
+ efi_status_t status;
+ efi_memory_desc_t *mem_map;
+ unsigned long map_sz, desc_sz, new_cmdline_addr;
+
+ status = mshv_efi_init();
+ if (status == EFI_NOT_FOUND) // we are in a standard Linux boot
+ return EFI_SUCCESS;
+
+ map_sz = 0;
+ mshv_get_hv_ranges((void *)&mem_map, &map_sz, &desc_sz);
+
+ status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, COMMAND_LINE_SIZE,
+ (void **)&new_cmdline_addr);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate space for cmdline with code %d",
+ status);
+
+ mshv_efi_update_cmdline(mem_map, map_sz, desc_sz,
+ *cmdline_ptr,
+ (char *)new_cmdline_addr, COMMAND_LINE_SIZE);
+
+ status = efi_bs_call(free_pool, *cmdline_ptr);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to free old cmdline with code %d",
+ status);
+
+ *cmdline_ptr = (char *)new_cmdline_addr;
+
+ return EFI_SUCCESS;
+}
diff --git a/drivers/firmware/efi/libstub/efi-mshv-common.c b/drivers/firmware/efi/libstub/efi-mshv-common.c
new file mode 100644
index 000000000000..2b631a9f50fc
--- /dev/null
+++ b/drivers/firmware/efi/libstub/efi-mshv-common.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "efistub.h"
+#include "efi-mshv.h"
+
+struct efi_hvloader_protocol *efi_mshv;
+
+efi_status_t mshv_efi_init(void)
+{
+ efi_status_t status;
+ static efi_guid_t hv_proto_guid = EFI_MSHV_MEDIA_PROTOCOL_GUID;
+
+ status = efi_bs_call(locate_protocol,
+ &hv_proto_guid, NULL, (void **)&efi_mshv);
+ if (status == EFI_NOT_FOUND) {
+ /* If the protocol is not installed we are in a standard Linux boot */
+ return status;
+ } else if (status != EFI_SUCCESS) {
+ mshv_efi_reboot("LocateProtocol failed unexpectedly with code %d",
+ status);
+ }
+
+ status = efi_mshv->get_loader_init_status();
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("mshv protocol installed but seems to have failed with code %d",
+ status);
+
+ return EFI_SUCCESS;
+}
+
+void mshv_get_hv_ranges(efi_memory_desc_t **mem_map, unsigned long *map_sz,
+ unsigned long *desc_sz)
+{
+ efi_status_t status;
+
+ status = efi_mshv->get_hv_ranges((void **)mem_map, map_sz, desc_sz);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to retrieve mshv ranges: error code %d",
+ status);
+}
+
+/*
+ * Concatenate the hypervisor reserved ranges to the command line.
+ *
+ * The reserved ranges are formatted as follows:
+ * 'hyperv_resvd_new=<size>!<address>,<size>!<address>,...'
+ *
+ * @mem_map: EFI memory map with the hypervisor reserved ranges
+ * @map_sz: size of the memory map
+ * @desc_sz: size of each descriptor in the memory map
+ * @old_cmdline: old command line
+ * @buf: buffer to hold the new command line
+ */
+void mshv_efi_update_cmdline(efi_memory_desc_t *mem_map,
+ unsigned long map_sz, unsigned long desc_sz, char *old_cmdline,
+ char *buf, unsigned long buf_sz)
+{
+ int i, cmdline_len, nr_desc;
+
+ cmdline_len = strlen(old_cmdline);
+ memcpy(buf, old_cmdline, cmdline_len + 1);
+
+ cmdline_len += snprintf(buf + cmdline_len,
+ buf_sz - cmdline_len,
+ " hyperv_resvd_new=");
+
+ nr_desc = map_sz / desc_sz;
+ for (i = 0; i < nr_desc; ++i) {
+ efi_memory_desc_t *d;
+ u64 start, end, sz;
+
+ d = efi_memdesc_ptr(mem_map, desc_sz, i);
+ start = d->phys_addr;
+ sz = d->num_pages << PAGE_SHIFT;
+ end = start + sz - 1;
+
+ cmdline_len += snprintf(buf + cmdline_len,
+ buf_sz - cmdline_len,
+ "%s0x%llx!0x%llx", i > 0 ? "," : "",
+ sz, start);
+ }
+}
+
+efi_status_t mshv_set_efi_rt_range(struct efi_boot_memmap *map)
+{
+ u32 nr_desc;
+ int i;
+ efi_status_t status;
+
+ if (!efi_mshv)
+ return EFI_SUCCESS;
+
+ nr_desc = map->map_size / map->desc_size;
+
+ for (i = 0; i < nr_desc; i++) {
+ efi_memory_desc_t *d;
+
+ d = efi_memdesc_ptr(map->map, map->desc_size, i);
+ switch (d->type) {
+ case EFI_RUNTIME_SERVICES_CODE:
+ case EFI_RUNTIME_SERVICES_DATA:
+ status = efi_mshv->register_range(d->phys_addr >> PAGE_SHIFT,
+ d->num_pages);
+ if (status != EFI_SUCCESS)
+ return status;
+ break;
+ default:
+ /* default case: range is not relevant to mshv */
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Launch mshv, if enabled.
+ *
+ * If mshv reports a bad status at this point, abort the boot.
+ * To get more information about the failure, the HV loader's internal
+ * logging can be used, which is exposed via efi_hv->get_next_log_msg(...).
+ *
+ */
+efi_status_t mshv_launch(void)
+{
+ struct hvl_return_data ret;
+
+ if (!efi_mshv)
+ return EFI_SUCCESS;
+
+ efi_mshv->launch_hv(NULL, &ret);
+ /* TODO: Where/how do we dump the hv loader logs? */
+ if (ret.launch_data.launch_status != 0)
+ efi_rt_call(reset_system, EFI_RESET_COLD, EFI_ABORTED, 0, NULL);
+ return EFI_SUCCESS;
+}
diff --git a/drivers/firmware/efi/libstub/efi-mshv.h b/drivers/firmware/efi/libstub/efi-mshv.h
new file mode 100644
index 000000000000..38e10f457a6c
--- /dev/null
+++ b/drivers/firmware/efi/libstub/efi-mshv.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _DRIVERS_FIRMWARE_EFI_MSHV_H
+#define _DRIVERS_FIRMWARE_EFI_MSHV_H
+
+#include "efistub.h"
+
+#if IS_ENABLED(CONFIG_MSHV_ROOT)
+
+extern struct efi_hvloader_protocol *efi_mshv;
+
+static inline void mshv_efi_reboot(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ efi_printk(fmt, args);
+ va_end(args);
+
+ efi_bs_call(stall, 5 * EFI_USEC_PER_SEC);
+ efi_rt_call(reset_system, EFI_RESET_COLD, EFI_ABORTED, 0, NULL);
+}
+
+efi_status_t mshv_efi_init(void);
+void mshv_get_hv_ranges(efi_memory_desc_t **mem_map, unsigned long *map_sz,
+ unsigned long *desc_sz);
+void mshv_efi_update_cmdline(efi_memory_desc_t *mem_map,
+ unsigned long map_sz, unsigned long desc_sz, char *cmdline,
+ char *buf, unsigned long buf_sz);
+
+#ifdef CONFIG_X86_64
+efi_status_t mshv_efi_setup(struct boot_params *boot_params);
+#endif /* CONFIG_X86_64 */
+
+#ifdef CONFIG_ARM64
+efi_status_t mshv_efi_setup(char **cmdline_ptr);
+#endif /* CONFIG_ARM64 */
+
+efi_status_t mshv_set_efi_rt_range(struct efi_boot_memmap *map);
+efi_status_t mshv_launch(void);
+
+struct hvl_dbg_data {
+ u8 unused[552];
+} __packed;
+
+struct hvl_launch_data {
+ u64 launch_status;
+ u64 launch_substatus1;
+} __packed;
+
+struct hvl_load_data {
+ u32 is_unsafe_config:1;
+ u32 reserved:31;
+} __packed;
+
+struct hvl_return_data {
+ u32 crash_dump_area_page_count;
+ u32 unused;
+ u64 crashdump_area_spa;
+ union {
+ struct hvl_launch_data launch_data;
+ struct hvl_load_data load_data;
+ };
+ struct hvl_dbg_data debug_data;
+ void *spa_page_range_array;
+ u32 range_count;
+
+ struct {
+ u32 base_checksum;
+ u32 base_timestamp;
+ u32 patch_checksum;
+ u32 patch_timestamp;
+ u32 base_hpat_entries_used;
+ u32 patch_hpat_entries_used;
+ u32 patch_sequence_number;
+ } patch_details;
+} __packed;
+
+struct efi_hvloader_protocol {
+ void (__efiapi * launch_hv)(void *, struct hvl_return_data *);
+ efi_status_t (__efiapi * register_range)(u64, u64);
+ efi_status_t (__efiapi * get_memory_map)(unsigned long *, void *,
+ unsigned long *,
+ unsigned long *, u32 *);
+ efi_status_t (__efiapi * get_hv_ranges)(void **,
+ unsigned long *,
+ unsigned long *);
+ efi_status_t (__efiapi * get_loader_init_status)(void);
+ efi_char16_t *(__efiapi * get_next_log_msg)(size_t *);
+};
+
+#else /* CONFIG_MSHV_ROOT */
+#ifdef CONFIG_X86_64
+static inline efi_status_t mshv_efi_setup(struct boot_params *boot_params)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_X86_64 */
+
+#ifdef CONFIG_ARM64
+static inline efi_status_t mshv_efi_setup(char **cmdline_ptr)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_ARM64 */
+
+static inline efi_status_t mshv_set_efi_rt_range(struct efi_boot_memmap *map)
+{
+ return EFI_SUCCESS;
+}
+
+static inline efi_status_t mshv_launch(void)
+{
+ return EFI_SUCCESS;
+}
+#endif /* !CONFIG_MSHV_ROOT */
+
+#endif /* _DRIVERS_FIRMWARE_EFI_MSHV_H */
diff --git a/drivers/firmware/efi/libstub/x86-efi-mshv.c b/drivers/firmware/efi/libstub/x86-efi-mshv.c
new file mode 100644
index 000000000000..bd65c7d82556
--- /dev/null
+++ b/drivers/firmware/efi/libstub/x86-efi-mshv.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <asm/setup.h>
+
+#include "efistub.h"
+#include "efi-mshv.h"
+
+/* Initial number of MSHV reserved ranges, extended as needed */
+#define MSHV_RESERVED_RANGES_COUNT 16
+
+struct mshv_setup_data {
+ struct setup_data sd;
+ struct setup_indirect si;
+} __packed;
+
+static int mshv_realloc_ranges(struct resource **data,
+ unsigned long *data_sz, int nr_ranges)
+{
+ struct resource *new_data;
+ unsigned long new_sz;
+ int status;
+
+ new_sz = sizeof(struct resource) * nr_ranges;
+ status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, new_sz,
+ (void **)&new_data);
+ if (status != EFI_SUCCESS) {
+ efi_err("mshv failed to allocate setup_data\n");
+ return status;
+ }
+
+ memset(new_data, 0, new_sz);
+ if (*data) {
+ memcpy(new_data, *data, *data_sz);
+ efi_bs_call(free_pool, *data);
+ }
+
+ *data = new_data;
+ *data_sz = new_sz;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t mshv_populate_ranges(struct boot_params *boot_params,
+ efi_memory_desc_t *mem_map, unsigned long map_sz,
+ unsigned long desc_sz)
+{
+ unsigned long cmdline_ptr;
+ u32 cmdline_size;
+ static u8 mshv_cmdline[COMMAND_LINE_SIZE];
+
+ memset(mshv_cmdline, 0, sizeof(mshv_cmdline));
+
+ cmdline_ptr = boot_params->hdr.cmd_line_ptr;
+ cmdline_ptr |= (u64)boot_params->ext_cmd_line_ptr << 32;
+ cmdline_size = boot_params->hdr.cmdline_size;
+
+ mshv_efi_update_cmdline(mem_map, map_sz, desc_sz,
+ (char *)cmdline_ptr,
+ (char *)mshv_cmdline, COMMAND_LINE_SIZE);
+
+ boot_params->hdr.cmd_line_ptr = (u32)((unsigned long)mshv_cmdline);
+ boot_params->ext_cmd_line_ptr = (u32)((unsigned long)mshv_cmdline >> 32);
+ boot_params->hdr.cmdline_size = sizeof(mshv_cmdline);
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Prepare for running as root partition with mshv.
+ * - Open the hypervisor loader EFI protocol, used for launching mshv after
+ * 'exit boot services'.
+ * - Get mshv reserved memory ranges from the loader, and populates those
+ * via a command line parameter 'hyperv_resvd_new'.
+ * If mshv_efi_setup() fails, boot continues as a bare-metal boot.
+ */
+efi_status_t mshv_efi_setup(struct boot_params *boot_params)
+{
+ struct setup_data **setup_data_itr;
+ struct mshv_setup_data *sd_block;
+ efi_memory_desc_t *mem_map;
+ unsigned long map_sz, key, desc_sz, setup_data_sz;
+ u32 desc_ver;
+ u64 start, end;
+ struct resource *mshv_range, *prev;
+ struct resource *mshv_reserved;
+ unsigned long mshv_reserved_sz;
+ u32 nr_desc;
+ int i, nr_ranges, max_ranges;
+ efi_status_t status;
+
+ mem_map = NULL;
+ mshv_reserved = NULL;
+
+ status = mshv_efi_init();
+ if (status == EFI_NOT_FOUND) {
+ /*
+ * If the protocol is not installed
+ * we are in a standard Linux boot
+ */
+ return EFI_SUCCESS;
+ }
+
+ /*
+ * Get mshv memory map to figure out mshv reserved ranges.
+ */
+
+ map_sz = 0;
+ mshv_get_hv_ranges((void *)&mem_map, &map_sz, &desc_sz);
+
+ /*
+ * Build an array of kernel 'struct resource' objects that contain mshv
+ * reserved ranges. This array is populated via a command line parameter
+ * called 'hyperv_resvd_new'.
+ */
+
+ status = mshv_realloc_ranges(&mshv_reserved,
+ &mshv_reserved_sz,
+ MSHV_RESERVED_RANGES_COUNT);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate space for hv ranges with code %d",
+ status);
+
+ max_ranges = MSHV_RESERVED_RANGES_COUNT;
+ mshv_range = mshv_reserved;
+ prev = NULL;
+ nr_desc = map_sz / desc_sz;
+ for (i = 0, nr_ranges = 0; i < nr_desc; i++) {
+ efi_memory_desc_t *d;
+
+ d = efi_memdesc_ptr(mem_map, desc_sz, i);
+
+ /* Merge adjacent ranges */
+ if (prev && ((prev->end + 1) == d->phys_addr)) {
+ prev->end += (d->num_pages << PAGE_SHIFT);
+ continue;
+ }
+
+ mshv_range->name = "Hypervisor Code and Data";
+ mshv_range->flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM;
+ mshv_range->start = d->phys_addr;
+ mshv_range->end = d->phys_addr + (d->num_pages << PAGE_SHIFT) - 1;
+
+ prev = mshv_range++;
+ nr_ranges++;
+ if (nr_ranges >= max_ranges) {
+ /* Extend the array to accommodate more ranges */
+ max_ranges += MSHV_RESERVED_RANGES_COUNT;
+ status = mshv_realloc_ranges(&mshv_reserved, &mshv_reserved_sz,
+ max_ranges);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate hv ranges: %d", status);
+
+ prev = &mshv_reserved[nr_ranges - 1];
+ mshv_range = prev + 1;
+ }
+ }
+
+ status = mshv_populate_ranges(boot_params, mem_map, map_sz, desc_sz);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate space for hv ranges with code %d",
+ status);
+
+ /* Build an indirect setup_data for each mshv reserved range. */
+ status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
+ nr_ranges * sizeof(struct mshv_setup_data),
+ (void **)&sd_block);
+ if (status != EFI_SUCCESS)
+ mshv_efi_reboot("failed to allocate space for hv ranges: error code %d", status);
+
+ memset((void *)sd_block, 0, nr_ranges * sizeof(struct mshv_setup_data));
+ setup_data_itr = (struct setup_data **)&boot_params->hdr.setup_data;
+
+ while (*setup_data_itr && (*setup_data_itr)->next)
+ setup_data_itr = (struct setup_data **)&(*setup_data_itr)->next;
+
+ *setup_data_itr = (struct setup_data *)sd_block;
+
+ for (i = 0; i < nr_ranges; i++) {
+ start = mshv_reserved[i].start;
+ end = mshv_reserved[i].end;
+
+ sd_block[i].sd.type = SETUP_INDIRECT;
+ sd_block[i].sd.len = sizeof(struct setup_indirect);
+ sd_block[i].sd.next = (__u64)&sd_block[i + 1];
+
+ sd_block[i].si.type = SETUP_MSHV;
+ sd_block[i].si.reserved = 0;
+ sd_block[i].si.len = end - start + 1;
+ sd_block[i].si.addr = start;
+ }
+
+ /*
+ * Remove the trailing 'next' pointer which is currently
+ * outside of the struct mshv_setup_data buffer.
+ */
+
+ sd_block[nr_ranges - 1].sd.next = 0;
+
+ efi_bs_call(free_pool, mem_map);
+
+ return EFI_SUCCESS;
+}
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index cef32e2c82d8..eab2539488ee 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -20,6 +20,7 @@
#include "efistub.h"
#include "x86-stub.h"
+#include "efi-mshv.h"
extern char _bss[], _ebss[];
@@ -737,6 +738,7 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
void *priv)
{
const char *signature;
+ efi_status_t status;
struct exit_boot_struct *p = priv;
signature = efi_is_64bit() ? EFI64_LOADER_SIGNATURE
@@ -751,6 +753,11 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
&p->efi->efi_memmap, &p->efi->efi_memmap_hi);
p->efi->efi_memmap_size = map->map_size;
+ /* Notify hypervisor of efi runtime services pages */
+ status = mshv_set_efi_rt_range(map);
+ if (status != EFI_SUCCESS)
+ return status;
+
return EFI_SUCCESS;
}
@@ -918,7 +925,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
const struct linux_efi_initrd *initrd = NULL;
unsigned long kernel_entry;
struct setup_header *hdr;
- efi_status_t status;
+ efi_status_t status, mshv_status;
efi_system_table = sys_table_arg;
/* Check if we were booted by the EFI firmware */
@@ -1011,6 +1018,8 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
/* Ask the firmware to clear memory on unclean shutdown */
efi_enable_reset_attack_mitigation();
+ mshv_status = mshv_efi_setup(boot_params);
+
efi_random_get_seed();
efi_retrieve_eventlog();
@@ -1035,6 +1044,13 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
*/
sev_enable(boot_params);
+ /*
+ * Launch the hypervisor before switching to 5 level paging.
+ * The hypervisor does not support being launched with LA57 enabled.
+ */
+ if (mshv_status == EFI_SUCCESS)
+ mshv_status = mshv_launch();
+
efi_5level_switch();
enter_kernel(kernel_entry, boot_params);
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 6b67ac616789..a642c110cf5d 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -26,6 +26,10 @@
#include <linux/kmsg_dump.h>
#include <linux/sizes.h>
#include <linux/slab.h>
+#include <linux/memblock.h>
+#include <linux/crash_dump.h>
+#include <linux/kstrtox.h>
+#include <linux/string.h>
#include <linux/dma-map-ops.h>
#include <linux/set_memory.h>
#include <hyperv/hvhdk.h>
@@ -863,3 +867,81 @@ const char *hv_result_to_string(u64 status)
return "Unknown";
}
EXPORT_SYMBOL_GPL(hv_result_to_string);
+
+struct resource hv_mshv_res[HV_MAX_RESVD_RANGES];
+u32 ranges_nr;
+
+/*
+ * Parse "hyperv_resvd_new=<size>!<address>,<size>!<address>,...", specifying a
+ * list of memory ranges that are reserved by the loader for the hypervisor.
+ */
+static int __init hv_parse_hyperv_resvd_new(char *arg)
+{
+ int i = 0;
+
+ if (is_kdump_kernel())
+ return 0;
+
+ while (arg && *arg) {
+ unsigned long long region_start, region_sz;
+ char *range, *addr_str;
+
+ if (i >= HV_MAX_RESVD_RANGES) {
+ pr_err("Hyper-V: too many hyperv_resvd_new ranges specified\n");
+ return 0;
+ }
+
+ /* Each range is "<size>!<address>", ranges separated by ','. */
+ range = strsep(&arg, ",");
+ addr_str = strchr(range, '!');
+ if (!addr_str) {
+ pr_err("Hyper-V: invalid format for hyperv_resvd_new: %s\n", range);
+ return 0;
+ }
+ *addr_str++ = '\0';
+
+ if (kstrtoull(range, 16, ®ion_sz) || !region_sz ||
+ kstrtoull(addr_str, 16, ®ion_start) || !region_start) {
+ pr_err("Hyper-V: invalid format for hyperv_resvd_new\n");
+ return 0;
+ }
+
+ memblock_reserve(region_start, region_sz);
+
+ hv_mshv_res[i].name = "Hypervisor Code and Data";
+ hv_mshv_res[i].flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM;
+ hv_mshv_res[i].start = region_start;
+ hv_mshv_res[i].end = region_start + region_sz - 1;
+
+ ++i;
+ }
+
+ ranges_nr = i;
+
+ return 0;
+}
+early_param("hyperv_resvd_new", hv_parse_hyperv_resvd_new);
+
+/*
+ * Log memory ranges that the hypervisor uses. The ranges are marked
+ * by a custom bootloader.
+ */
+void __init hv_dump_mshv_memory(void)
+{
+ u64 start, end;
+ int i;
+
+ for (i = 0; i < ranges_nr; i++) {
+ start = hv_mshv_res[i].start;
+ end = hv_mshv_res[i].end;
+ pr_info("Hyper-V reserve [mem %#018Lx-%#018Lx]\n", start, end);
+ }
+}
+
+void __init hv_mark_resources(void)
+{
+ int i, max = ARRAY_SIZE(hv_mshv_res);
+
+ for (i = 0; i < max && hv_mshv_res[i].end; i++)
+ insert_resource(&iomem_resource, &hv_mshv_res[i]);
+}
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index bf601d67cecb..83e0dff5371a 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -212,6 +212,13 @@ int hv_common_cpu_init(unsigned int cpu);
int hv_common_cpu_die(unsigned int cpu);
void hv_identify_partition_type(void);
+#define HV_MAX_RESVD_RANGES 32
+extern struct resource hv_mshv_res[HV_MAX_RESVD_RANGES];
+extern u32 ranges_nr;
+
+void __init hv_dump_mshv_memory(void);
+void __init hv_mark_resources(void);
+
/**
* hv_cpu_number_to_vp_number() - Map CPU to VP.
* @cpu_number: CPU number in Linux terms
diff --git a/include/linux/efi.h b/include/linux/efi.h
index ccbc35479684..be3fb83194b9 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -391,6 +391,7 @@ void efi_native_runtime_setup(void);
#define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
#define EFI_RT_PROPERTIES_TABLE_GUID EFI_GUID(0xeb66918a, 0x7eef, 0x402a, 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9)
+#define EFI_MSHV_MEDIA_PROTOCOL_GUID EFI_GUID(0x098d423a, 0x6ca5, 0x4ad4, 0x90, 0xfa, 0x72, 0xc3, 0xce, 0x22, 0xc8, 0xd0)
#define EFI_DXE_SERVICES_TABLE_GUID EFI_GUID(0x05ad34ba, 0x6f02, 0x4214, 0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9)
#define EFI_SMBIOS_PROTOCOL_GUID EFI_GUID(0x03583ff6, 0xcb36, 0x4940, 0x94, 0x7e, 0xb9, 0xb3, 0x9f, 0x4a, 0xfa, 0xf7)
#define EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID EFI_GUID(0xf4560cf6, 0x40ec, 0x4b4a, 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89)
--
2.43.0
next prev parent reply other threads:[~2026-08-07 13:52 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 13:51 [PATCH v1 00/13] mshv: add SEV-SNP support for MSHV root partitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 01/13] mshv: add SEV-SNP UAPI definitions Wei Hu
2026-08-07 13:51 ` [PATCH v1 02/13] mshv: add SEV-SNP PSP request hypercall Wei Hu
2026-08-07 13:51 ` [PATCH v1 03/13] mshv: add SEV-SNP isolated page hypercalls Wei Hu
2026-08-07 13:51 ` [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls Wei Hu
2026-08-07 13:51 ` [PATCH v1 05/13] hyperv: fix hv_input_get_system_property layout for SNP status Wei Hu
2026-08-07 13:51 ` [PATCH v1 06/13] mshv: detect and report SEV-SNP support at init Wei Hu
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
2026-08-07 13:51 ` [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection Wei Hu
2026-08-07 13:51 ` Wei Hu [this message]
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260807135134.303943-13-weh@linux.microsoft.com \
--to=weh@linux.microsoft.com \
--cc=ardb@kernel.org \
--cc=arnd@arndb.de \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=decui@microsoft.com \
--cc=haiyangz@microsoft.com \
--cc=hpa@zytor.com \
--cc=ilias.apalodimas@linaro.org \
--cc=kys@microsoft.com \
--cc=linux-arch@vger.kernel.org \
--cc=linux-efi@vger.kernel.org \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=longli@microsoft.com \
--cc=mingo@redhat.com \
--cc=tglx@kernel.org \
--cc=weh@microsoft.com \
--cc=wei.liu@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox