Currently, allocate_unaccepted_bitmap() only scans the initial EFI boot memory map. This misses hotpluggable ranges described in the ACPI SRAT. Without early tracking, hotplug pages are accessed without acceptance and this triggers guest crash.
Introduce a lightweight ACPI SRAT parser to scan these regions early. If a region has both ACPI_SRAT_MEM_ENABLED and ACPI_SRAT_MEM_HOT_PLUGGABLE flags, expand the tracking boundaries. This avoids pulling in the full ACPI subsystem while ensuring the bitmap covers both static memory and hotplug memory. Bail out early with success on non-confidential guests to prevent unnecessary bitmap allocation. Signed-off-by: Zhenzhong Duan <[email protected]> --- drivers/firmware/efi/libstub/efistub.h | 6 ++ arch/x86/boot/compressed/mem.c | 2 +- .../firmware/efi/libstub/unaccepted_memory.c | 94 +++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index fd91fc15ec81..fc0cd33a5962 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -1260,4 +1260,10 @@ void arch_accept_memory(phys_addr_t start, phys_addr_t end); efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size); efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen); +bool early_is_tdx_guest(void); +#ifdef CONFIG_AMD_MEM_ENCRYPT +bool early_is_sevsnp_guest(void); +#else +static inline bool early_is_sevsnp_guest(void) { return false; } +#endif #endif diff --git a/arch/x86/boot/compressed/mem.c b/arch/x86/boot/compressed/mem.c index 0e9f84ab4bdc..40e9c81a2206 100644 --- a/arch/x86/boot/compressed/mem.c +++ b/arch/x86/boot/compressed/mem.c @@ -12,7 +12,7 @@ * * Enumerate TDX directly from the early users. */ -static bool early_is_tdx_guest(void) +bool early_is_tdx_guest(void) { static bool once; static bool is_tdx; diff --git a/drivers/firmware/efi/libstub/unaccepted_memory.c b/drivers/firmware/efi/libstub/unaccepted_memory.c index 757dbe734a47..bfbb78bd7b8a 100644 --- a/drivers/firmware/efi/libstub/unaccepted_memory.c +++ b/drivers/firmware/efi/libstub/unaccepted_memory.c @@ -1,19 +1,109 @@ // SPDX-License-Identifier: GPL-2.0-only #include <linux/efi.h> +#include <linux/acpi.h> #include <asm/efi.h> #include "efistub.h" struct efi_unaccepted_memory *unaccepted_table; +struct srat_parse_ctx { + u64 *mem_start; + u64 *mem_end; +}; + +typedef void (*srat_region_handler_t)(struct acpi_srat_mem_affinity *mem, + struct srat_parse_ctx *ctx); + +/* + * parse_acpi_srat_regions - Loop through ACPI SRAT tables to process + * hotpluggable memory regions via a custom callback handler. + */ +static void parse_acpi_srat_regions(srat_region_handler_t handler, struct srat_parse_ctx *ctx) +{ + u32 hotplug_mask = ACPI_SRAT_MEM_ENABLED | ACPI_SRAT_MEM_HOT_PLUGGABLE; + struct acpi_table_header *xsdt, *srat = NULL; + struct acpi_table_rsdp *rsdp = NULL; + u8 *current_ptr, *end_ptr; + u64 *table_pointers; + u32 entry_count; + unsigned long i; + + rsdp = get_efi_config_table(ACPI_20_TABLE_GUID); + + if (!rsdp || !ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) + return; + + xsdt = (struct acpi_table_header *)(unsigned long)rsdp->xsdt_physical_address; + if (!xsdt || !ACPI_COMPARE_NAMESEG(xsdt->signature, ACPI_SIG_XSDT)) + return; + + if (xsdt->length < sizeof(struct acpi_table_header) + ACPI_XSDT_ENTRY_SIZE) + return; + + entry_count = (xsdt->length - sizeof(struct acpi_table_header)) / ACPI_XSDT_ENTRY_SIZE; + table_pointers = (u64 *)((u8 *)xsdt + sizeof(struct acpi_table_header)); + + for (i = 0; i < entry_count; i++) { + struct acpi_table_header *tbl; + + tbl = (struct acpi_table_header *)(unsigned long)table_pointers[i]; + if (tbl && ACPI_COMPARE_NAMESEG(tbl->signature, ACPI_SIG_SRAT)) { + srat = tbl; + break; + } + } + + if (!srat) + return; + + current_ptr = (u8 *)srat + sizeof(struct acpi_table_srat); + end_ptr = (u8 *)srat + srat->length; + + while (current_ptr < end_ptr) { + struct acpi_subtable_header *sub_header; + u64 range_end; + + sub_header = (struct acpi_subtable_header *)current_ptr; + if (sub_header->length == 0) + break; + + if (sub_header->type == ACPI_SRAT_TYPE_MEMORY_AFFINITY && + sub_header->length >= sizeof(struct acpi_srat_mem_affinity)) { + struct acpi_srat_mem_affinity *mem; + + mem = (struct acpi_srat_mem_affinity *)current_ptr; + if ((mem->flags & hotplug_mask) == hotplug_mask && + !check_add_overflow(mem->base_address, mem->length, &range_end)) + handler(mem, ctx); + } + current_ptr += sub_header->length; + } +} + +static void update_mem_boundaries(struct acpi_srat_mem_affinity *mem, struct srat_parse_ctx *ctx) +{ + u64 range_end = mem->base_address + mem->length; + + if (mem->base_address < *(ctx->mem_start)) + *(ctx->mem_start) = mem->base_address; + + if (range_end > *(ctx->mem_end)) + *(ctx->mem_end) = range_end; +} + efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc, struct efi_boot_memmap *map) { efi_guid_t unaccepted_table_guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID; u64 unaccepted_start = ULLONG_MAX, unaccepted_end = 0, bitmap_size; + struct srat_parse_ctx ctx; efi_status_t status; int i; + if (!early_is_tdx_guest() && !early_is_sevsnp_guest()) + return EFI_SUCCESS; + /* Check if the table is already installed */ unaccepted_table = get_efi_config_table(unaccepted_table_guid); if (unaccepted_table) { @@ -38,6 +128,10 @@ efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc, d->phys_addr + d->num_pages * PAGE_SIZE); } + ctx.mem_start = &unaccepted_start; + ctx.mem_end = &unaccepted_end; + parse_acpi_srat_regions(update_mem_boundaries, &ctx); + if (unaccepted_start == ULLONG_MAX) return EFI_SUCCESS; -- 2.52.0

