All of lore.kernel.org
 help / color / mirror / Atom feed
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, &region_sz) || !region_sz ||
+		    kstrtoull(addr_str, 16, &region_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


  parent reply	other threads:[~2026-08-07 13:52 UTC|newest]

Thread overview: 25+ 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 14:03   ` sashiko-bot
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 14:21   ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 04/13] mshv: wire SEV-SNP partition ioctls Wei Hu
2026-08-07 14:45   ` sashiko-bot
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 15:04   ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 07/13] mshv: default to safe partition CPU features Wei Hu
2026-08-07 15:15   ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 08/13] mshv: accept partial CPU feature banks Wei Hu
2026-08-07 15:30   ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 09/13] mshv: define full processor and xsave feature masks Wei Hu
2026-08-07 15:42   ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 10/13] mshv: unmap SNP memory before state teardown Wei Hu
2026-08-07 15:53   ` sashiko-bot
2026-08-07 13:51 ` [PATCH v1 11/13] mshv: unlock SNP pages on panic for crashdump collection Wei Hu
2026-08-07 16:11   ` sashiko-bot
2026-08-07 13:51 ` Wei Hu [this message]
2026-08-07 16:22   ` [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) sashiko-bot
2026-08-07 13:51 ` [PATCH v1 13/13] mshv: set up own SynIC registers on a nested root partition Wei Hu
2026-08-07 16:36   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.