From 80e80a703e18aa7d55fe26e4504c58a6f6c4ff07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 9 Mar 2026 13:17:22 +0100 Subject: [PATCH 1/8] efi: make efi_mem_type() and efi_mem_attributes() work on Xen PV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Xen doesn't give direct access to the EFI memory map, but provides a hypercall interface for it. efi_mem_desc_lookup() was already adjusted in aca1d27ac38a "efi: xen: Implement memory descriptor lookup based on hypercall" to (optionally) use it. Now make efi_mem_type() and efi_mem_attributes() use common efi_mem_desc_lookup() too. This also reduces code duplication a bit. efi_mem_type() retains separate check for -ENOTSUPP error case (even though no caller seems to rely on this currently). Signed-off-by: Marek Marczykowski-Górecki [ardb: Drop erroneous 'const' qualifier] Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index b2fb92a4bbd1..288834a193d1 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -983,18 +983,12 @@ char * __init efi_md_typeattr_format(char *buf, size_t size, */ u64 efi_mem_attributes(unsigned long phys_addr) { - efi_memory_desc_t *md; + efi_memory_desc_t md; - if (!efi_enabled(EFI_MEMMAP)) + if (efi_mem_desc_lookup(phys_addr, &md)) return 0; - for_each_efi_memory_desc(md) { - if ((md->phys_addr <= phys_addr) && - (phys_addr < (md->phys_addr + - (md->num_pages << EFI_PAGE_SHIFT)))) - return md->attribute; - } - return 0; + return md.attribute; } /* @@ -1007,18 +1001,15 @@ u64 efi_mem_attributes(unsigned long phys_addr) */ int efi_mem_type(unsigned long phys_addr) { - const efi_memory_desc_t *md; + efi_memory_desc_t md; - if (!efi_enabled(EFI_MEMMAP)) + if (!efi_enabled(EFI_MEMMAP) && !efi_enabled(EFI_PARAVIRT)) return -ENOTSUPP; - for_each_efi_memory_desc(md) { - if ((md->phys_addr <= phys_addr) && - (phys_addr < (md->phys_addr + - (md->num_pages << EFI_PAGE_SHIFT)))) - return md->type; - } - return -EINVAL; + if (efi_mem_desc_lookup(phys_addr, &md)) + return -EINVAL; + + return md.type; } int efi_status_to_err(efi_status_t status) From eaeff8411fcafc33da66a78b10566749d353ca2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 9 Mar 2026 13:17:23 +0100 Subject: [PATCH 2/8] efi: Enable BGRT loading under Xen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BGRT table can be parsed if EFI_PARAVIRT is enabled, even if EFI_MEMMAP is not. Xen will take care of preserving the image even if EfiBootServicesData memory is reclaimed already, or invalidate the table if it didn't preserve it - in both cases accesing the table itself under virt is safe. Also allow the ESRT to be in reclaimable memory, as that is where future Xen versions will put it. This is similar approach as was taken for ESRT table in 01de145dc7fb "efi: Actually enable the ESRT under Xen". Signed-off-by: Marek Marczykowski-Górecki Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi-bgrt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/efi-bgrt.c b/drivers/firmware/efi/efi-bgrt.c index 6aafdb67dbca..1da451582812 100644 --- a/drivers/firmware/efi/efi-bgrt.c +++ b/drivers/firmware/efi/efi-bgrt.c @@ -29,11 +29,12 @@ void __init efi_bgrt_init(struct acpi_table_header *table) void *image; struct bmp_header bmp_header; struct acpi_table_bgrt *bgrt = &bgrt_tab; + int mem_type; if (acpi_disabled) return; - if (!efi_enabled(EFI_MEMMAP)) + if (!efi_enabled(EFI_MEMMAP) && !efi_enabled(EFI_PARAVIRT)) return; if (table->length < sizeof(bgrt_tab)) { @@ -62,7 +63,9 @@ void __init efi_bgrt_init(struct acpi_table_header *table) goto out; } - if (efi_mem_type(bgrt->image_address) != EFI_BOOT_SERVICES_DATA) { + mem_type = efi_mem_type(bgrt->image_address); + if (mem_type != EFI_BOOT_SERVICES_DATA && + mem_type != EFI_ACPI_RECLAIM_MEMORY) { pr_notice("Ignoring BGRT: invalid image address\n"); goto out; } From 77dd8adabbc8ff845177b460de48b9d2cd579966 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 9 Mar 2026 13:52:22 +0100 Subject: [PATCH 3/8] efi: Drop unused efi_range_is_wc() function efi_range_is_wc() has no callers, so remove it. Reviewed-by: Ilias Apalodimas Signed-off-by: Ard Biesheuvel --- include/linux/efi.h | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/include/linux/efi.h b/include/linux/efi.h index 664898d09ff5..72e76ec54641 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -832,27 +832,6 @@ extern int __init parse_efi_signature_list( const void *data, size_t size, efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *)); -/** - * efi_range_is_wc - check the WC bit on an address range - * @start: starting kvirt address - * @len: length of range - * - * Consult the EFI memory map and make sure it's ok to set this range WC. - * Returns true or false. - */ -static inline int efi_range_is_wc(unsigned long start, unsigned long len) -{ - unsigned long i; - - for (i = 0; i < len; i += (1UL << EFI_PAGE_SHIFT)) { - unsigned long paddr = __pa(start + i); - if (!(efi_mem_attributes(paddr) & EFI_MEMORY_WC)) - return 0; - } - /* The range checked out */ - return 1; -} - /* * We play games with efi_enabled so that the compiler will, if * possible, remove EFI-related code altogether. From 56e2ef8f5f9161d08d45656d373da83d4a789be1 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 9 Mar 2026 17:10:12 +0000 Subject: [PATCH 4/8] efi: libstub: fix type of fdt 32 and 64bit variables In update_fdt_memmap() an update_fdt() the fdt values should be of the fd32_t and fdt64_t types. Make the relevant changes to remove the following sparse warnings: drivers/firmware/efi/libstub/fdt.c:97:19: warning: incorrect type in assignment (different base types) drivers/firmware/efi/libstub/fdt.c:97:19: expected unsigned long long [usertype] fdt_val64 drivers/firmware/efi/libstub/fdt.c:97:19: got restricted __be64 [usertype] drivers/firmware/efi/libstub/fdt.c:157:19: warning: incorrect type in assignment (different base types) drivers/firmware/efi/libstub/fdt.c:157:19: expected unsigned long long [usertype] fdt_val64 drivers/firmware/efi/libstub/fdt.c:157:19: got restricted __be64 [usertype] drivers/firmware/efi/libstub/fdt.c:163:19: warning: incorrect type in assignment (different base types) drivers/firmware/efi/libstub/fdt.c:163:19: expected unsigned int [usertype] fdt_val32 drivers/firmware/efi/libstub/fdt.c:163:19: got restricted __be32 [usertype] drivers/firmware/efi/libstub/fdt.c:169:19: warning: incorrect type in assignment (different base types) drivers/firmware/efi/libstub/fdt.c:169:19: expected unsigned int [addressable] [usertype] fdt_val32 drivers/firmware/efi/libstub/fdt.c:169:19: got restricted __be32 [usertype] drivers/firmware/efi/libstub/fdt.c:175:19: warning: incorrect type in assignment (different base types) drivers/firmware/efi/libstub/fdt.c:175:19: expected unsigned int [addressable] [usertype] fdt_val32 drivers/firmware/efi/libstub/fdt.c:175:19: got restricted __be32 [usertype] Signed-off-by: Ben Dooks Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/fdt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 6a337f1f8787..23b3543d3041 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -32,8 +32,8 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, { int node, num_rsv; int status; - u32 fdt_val32; - u64 fdt_val64; + fdt32_t fdt_val32; + fdt64_t fdt_val64; /* Do some checks on provided FDT, if it exists: */ if (orig_fdt) { @@ -100,13 +100,13 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, if (status) goto fdt_set_fail; - fdt_val64 = U64_MAX; /* placeholder */ + fdt_val64 = cpu_to_fdt64(U64_MAX); /* placeholder */ status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-start", fdt_val64); if (status) goto fdt_set_fail; - fdt_val32 = U32_MAX; /* placeholder */ + fdt_val32 = cpu_to_fdt32(U32_MAX); /* placeholder */ status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-size", fdt_val32); if (status) @@ -147,8 +147,8 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map) { int node = fdt_path_offset(fdt, "/chosen"); - u64 fdt_val64; - u32 fdt_val32; + fdt64_t fdt_val64; + fdt32_t fdt_val32; int err; if (node < 0) From 5d0faa8e8369b9a48498f6f132c2ced5f0549acc Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 26 Mar 2026 14:26:57 +0100 Subject: [PATCH 5/8] efi/memattr: Fix thinko in table size sanity check While it is true that each PE/COFF runtime driver in memory can generally be split into 3 different regions (the header, the code/rodata region and the data/bss region), each with different permissions, it does not mean that 3x the size of the memory map is a suitable upper bound. This is due to the fact that all runtime drivers could be coalesced into a single EFI runtime code region by the firmware, and if the firmware does a good job of keeping the fragmentation down, it is conceivable that the memory attributes table has more entries than the EFI memory map itself. So instead, base the sanity check on whether the descriptor size matches the EFI memory map's descriptor size closely enough (which is not mandated by the spec but extremely unlikely to differ in practice), and whether the size of the whole table does not exceed 64k entries. Reviewed-by: Breno Leitao Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/memattr.c | 37 +++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c index e727cc5909cb..5edbffd4e4b7 100644 --- a/drivers/firmware/efi/memattr.c +++ b/drivers/firmware/efi/memattr.c @@ -22,7 +22,6 @@ unsigned long __ro_after_init efi_mem_attr_table = EFI_INVALID_TABLE_ADDR; void __init efi_memattr_init(void) { efi_memory_attributes_table_t *tbl; - unsigned long size; if (efi_mem_attr_table == EFI_INVALID_TABLE_ADDR) return; @@ -40,22 +39,42 @@ void __init efi_memattr_init(void) goto unmap; } + /* + * The EFI memory attributes table descriptors might potentially be + * smaller than those used by the EFI memory map, as long as they can + * fit a efi_memory_desc_t. However, a larger descriptor size makes no + * sense, and might be an indication that the table is corrupted. + * + * The only exception is kexec_load(), where the EFI memory map is + * reconstructed by user space, and may use a smaller descriptor size + * than the original. Given that, ignoring this companion table is + * still the right thing to do here, but don't complain too loudly when + * this happens. + */ + if (tbl->desc_size < sizeof(efi_memory_desc_t) || + tbl->desc_size > efi.memmap.desc_size) { + pr_warn("Unexpected EFI Memory Attributes descriptor size %u (expected: %lu)\n", + tbl->desc_size, efi.memmap.desc_size); + goto unmap; + } /* - * Sanity check: the Memory Attributes Table contains up to 3 entries - * for each entry of type EfiRuntimeServicesCode in the EFI memory map. - * So if the size of the table exceeds 3x the size of the entire EFI - * memory map, there is clearly something wrong, and the table should - * just be ignored altogether. + * Sanity check: the Memory Attributes Table contains multiple entries + * for each EFI runtime services code or data region in the EFI memory + * map, each with the permission attributes that may be applied when + * mapping the region. There is no upper bound for the number of + * entries, as it could conceivably contain more entries than the EFI + * memory map itself. So pick an arbitrary limit of 64k, which is + * ludicrously high. This prevents a corrupted table from eating all + * system RAM. */ - size = tbl->num_entries * tbl->desc_size; - if (size > 3 * efi.memmap.nr_map * efi.memmap.desc_size) { + if (tbl->num_entries > SZ_64K) { pr_warn(FW_BUG "Corrupted EFI Memory Attributes Table detected! (version == %u, desc_size == %u, num_entries == %u)\n", tbl->version, tbl->desc_size, tbl->num_entries); goto unmap; } - tbl_size = sizeof(*tbl) + size; + tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size; memblock_reserve(efi_mem_attr_table, tbl_size); set_bit(EFI_MEM_ATTR, &efi.flags); From a142d0ae9f2ceb0fc7417e19ecfafc8179282e35 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 25 Feb 2026 13:39:48 +0100 Subject: [PATCH 6/8] memblock: Permit existing reserved regions to be marked RSRV_KERN Permit existing memblock reservations to be marked as RSRV_KERN. This will be used by the EFI code on x86 to distinguish between reservations of boot services data regions that have actual significance to the kernel and regions that are reserved temporarily to work around buggy firmware. Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Ard Biesheuvel --- include/linux/memblock.h | 1 + mm/memblock.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 6ec5e9ac0699..9eac4f268359 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -155,6 +155,7 @@ int memblock_mark_mirror(phys_addr_t base, phys_addr_t size); int memblock_mark_nomap(phys_addr_t base, phys_addr_t size); int memblock_clear_nomap(phys_addr_t base, phys_addr_t size); int memblock_reserved_mark_noinit(phys_addr_t base, phys_addr_t size); +int memblock_reserved_mark_kern(phys_addr_t base, phys_addr_t size); int memblock_mark_kho_scratch(phys_addr_t base, phys_addr_t size); int memblock_clear_kho_scratch(phys_addr_t base, phys_addr_t size); diff --git a/mm/memblock.c b/mm/memblock.c index b3ddfdec7a80..2505ce8b319c 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -1115,6 +1115,21 @@ int __init_memblock memblock_reserved_mark_noinit(phys_addr_t base, phys_addr_t MEMBLOCK_RSRV_NOINIT); } +/** + * memblock_reserved_mark_kern - Mark a reserved memory region with flag + * MEMBLOCK_RSRV_KERN + * + * @base: the base phys addr of the region + * @size: the size of the region + * + * Return: 0 on success, -errno on failure. + */ +int __init_memblock memblock_reserved_mark_kern(phys_addr_t base, phys_addr_t size) +{ + return memblock_setclr_flag(&memblock.reserved, base, size, 1, + MEMBLOCK_RSRV_KERN); +} + /** * memblock_mark_kho_scratch - Mark a memory region as MEMBLOCK_KHO_SCRATCH. * @base: the base phys addr of the region From 259e3e6f9382b6a9fe570313d97c59a233f7d72f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 25 Feb 2026 12:03:04 +0100 Subject: [PATCH 7/8] efi: Tag memblock reservations of boot services regions as RSRV_KERN By definition, EFI memory regions of type boot services code or data have no special significance to the firmware at runtime, only to the OS. In some cases, the firmware will allocate tables and other assets that are passed in memory in regions of this type, and leave it up to the OS to decide whether or not to treat the allocation as special, or simply consume the contents at boot and recycle the RAM for ordinary use. The reason for this approach is that it avoids needless memory reservations for assets that the OS knows nothing about, and therefore doesn't know how to free either. This means that any memblock reservations covering such regions can be marked as MEMBLOCK_RSRV_KERN - this is a better match semantically, and is useful on x86 to distinguish true reservations from temporary reservations that are only needed to work around firmware bugs. Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 288834a193d1..8600dd31a586 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -600,7 +600,9 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size) return; if (!memblock_is_region_reserved(addr, size)) - memblock_reserve(addr, size); + memblock_reserve_kern(addr, size); + else + memblock_reserved_mark_kern(addr, size); /* * Some architectures (x86) reserve all boot services ranges From 48a428215782321b56956974f23593e40ce84b7a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 10 Apr 2026 17:46:37 +0200 Subject: [PATCH 8/8] efi/capsule-loader: fix incorrect sizeof in phys array reallocation The krealloc() call for cap_info->phys in __efi_capsule_setup_info() uses sizeof(phys_addr_t *) instead of sizeof(phys_addr_t), which might be causing an undersized allocation. The allocation is also inconsistent with the initial array allocation in efi_capsule_open() that allocates one entry with sizeof(phys_addr_t), and the efi_capsule_write() function that stores phys_addr_t values (not pointers) via page_to_phys(). On 64-bit systems where sizeof(phys_addr_t) == sizeof(phys_addr_t *), this goes unnoticed. On 32-bit systems with PAE where phys_addr_t is 64-bit but pointers are 32-bit, this allocates half the required space, which might lead to a heap buffer overflow when storing physical addresses. This is similar to the bug fixed in commit fccfa646ef36 ("efi/capsule-loader: fix incorrect allocation size") which fixed the same issue at the initial allocation site. Fixes: f24c4d478013 ("efi/capsule-loader: Reinstate virtual capsule mapping") Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: Thomas Huth Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/capsule-loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c index 2c628a127091..8e8f81f0a5a0 100644 --- a/drivers/firmware/efi/capsule-loader.c +++ b/drivers/firmware/efi/capsule-loader.c @@ -67,7 +67,7 @@ int __efi_capsule_setup_info(struct capsule_info *cap_info) cap_info->pages = temp_page; temp_page = krealloc(cap_info->phys, - pages_needed * sizeof(phys_addr_t *), + pages_needed * sizeof(phys_addr_t), GFP_KERNEL | __GFP_ZERO); if (!temp_page) return -ENOMEM;