From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 40DE0374A0D; Fri, 7 Aug 2026 13:52:43 +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=1786110769; cv=none; b=Goecf2ALQ46/+gERDMaYuros0jkUNEyt/Jho3uuYZq/qMOxvQPJGcDsAAcNRLpwm+M6OILYY0SKyUwFNTdqXwX0ulTSO8XUOnl5WwM8YAe9/ucVqaPc/RuN41RLInbh8BhXkMmtJmepdahjB8WDsczgaoM1EgfPhrBc49R+lxbc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786110769; c=relaxed/simple; bh=PY6rHumBImEmrG+6pXV5VmiF3kGiV4s/mAxhNg9hy0M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iUJ+T10/yUcn/6TxCKVPYIN7P6Lm41fa86ub3075fAKE/3JDlebEVRJU9gkztnHz9vmjKRZgi9Abndvcsk8Teo5S96sDOB1crde58gTcVUUss8ejKMr3HzAAUOcSn1EkRC590SELEXPobfVBHwwQo43V9php36dBrJNwGxBSbxQ= 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=Ty7QET6I; 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="Ty7QET6I" Received: from weh-cvm-dev-vm.y50bckvjo0hefgfnzfztsfttff.phxx.internal.cloudapp.net (unknown [20.169.55.37]) by linux.microsoft.com (Postfix) with ESMTPSA id E4B8D20B7129; Fri, 7 Aug 2026 06:52:18 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com E4B8D20B7129 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1786110739; bh=n3bof7xe1Wda82T4vjSVy6uHj6gwOmW80a7ri+5FKB8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ty7QET6I6BLr9EzwyEhC1O3bMuXQwlsyfUFcWbNW5wl6xSq6jf6sGLgw2J7d94UKQ nlQmrNHpENFIeSTp2uogptWVKv5olZAkFxlND4cfnR7Mgb23El1nM4QfgosZlVeO8A KixZ35pUMXN3QL7Jjlm3GxWBpdQArC6xc+jns4fY= From: Wei Hu To: linux-hyperv@vger.kernel.org Cc: linux-kernel@vger.kernel.org, "K. Y. Srinivasan" , Haiyang Zhang , Wei Liu , Dexuan Cui , Long Li , Wei Hu , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Ard Biesheuvel , Ilias Apalodimas , Arnd Bergmann , 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 Message-ID: <20260807135134.303943-13-weh@linux.microsoft.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260807135134.303943-1-weh@linux.microsoft.com> References: <20260807135134.303943-1-weh@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: 8bit From: Wei Hu 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 --- 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 #include #include +#include +#include #include #include #include @@ -22,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -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 +#include +#include + +#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=!
,!
,...' + * + * @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 + +#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 #include #include +#include +#include +#include +#include #include #include #include @@ -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=!
,!
,...", 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 "!
", 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