From e65ca16463112677923c61f58cc09e121be1bbce Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 13 Feb 2026 17:51:51 +0100 Subject: [PATCH 1/6] efi: export sysfb_primary_display for EDID The sysfb_primary_display structure is now part of efi-init.c but conditionally defined. One of the users is missing in the condition: aarch64-linux-ld: drivers/video/fbdev/core/fbmon.o: in function `fb_firmware_edid': fbmon.c:(.text.fb_firmware_edid+0x3c): undefined reference to `sysfb_primary_display' Export it whenever CONFIG_FIRMWARE_EDID is set, so the fbdev core code can use it. Fixes: 4fcae6358871 ("sysfb: Move edid_info into sysfb_primary_display") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202602111543.Do4nkY5l-lkp@intel.com/ Signed-off-by: Arnd Bergmann Acked-by: Thomas Zimmermann Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi-init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c index 432301dce76f..5a595d026f58 100644 --- a/drivers/firmware/efi/efi-init.c +++ b/drivers/firmware/efi/efi-init.c @@ -60,7 +60,7 @@ extern __weak const efi_config_table_type_t efi_arch_tables[]; * x86 defines its own instance of sysfb_primary_display and uses * it even without EFI, everything else can get them from here. */ -#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON)) +#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON)) || defined(CONFIG_FIRMWARE_EDID) struct sysfb_display_info sysfb_primary_display __section(".data"); EXPORT_SYMBOL_GPL(sysfb_primary_display); #endif From e00ac9e5afb5d80c0168ec88d8e8662a54af8249 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 17 Feb 2026 12:09:35 +0100 Subject: [PATCH 2/6] x86/kexec: Copy ACPI root pointer address from config table Dave reports that kexec may fail when the first kernel boots via the EFI stub but without EFI runtime services, as in that case, the RSDP address field in struct bootparams is never assigned. Kexec copies this value into the version of struct bootparams that it provides to the incoming kernel, which may have no other means to locate the ACPI root pointer. So take the value from the EFI config tables if no root pointer has been set in the first kernel's struct bootparams. Fixes: a1b87d54f4e4 ("x86/efistub: Avoid legacy decompressor when doing EFI boot") Cc: # v6.1 Reported-by: Dave Young Tested-by: Dave Young Link: https://lore.kernel.org/linux-efi/aZQg_tRQmdKNadCg@darkstar.users.ipa.redhat.com/ Signed-off-by: Ard Biesheuvel --- arch/x86/kernel/kexec-bzimage64.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index 7508d0ccc740..251edc5a040f 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -193,6 +193,13 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr, struct efi_info *current_ei = &boot_params.efi_info; struct efi_info *ei = ¶ms->efi_info; + if (!params->acpi_rsdp_addr) { + if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) + params->acpi_rsdp_addr = efi.acpi20; + else if (efi.acpi != EFI_INVALID_TABLE_ADDR) + params->acpi_rsdp_addr = efi.acpi; + } + if (!efi_enabled(EFI_RUNTIME_SERVICES)) return 0; From 62cb7abdef118ffdc9748ad4de69bb9b38771340 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 15 Feb 2026 12:06:28 +0100 Subject: [PATCH 3/6] efi: stmm: Constify struct efivar_operations The 'struct efivar_operations' is not modified by the driver after initialization, so it should follow typical practice of being static const for increased code safety and readability. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Ilias Apalodimas Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/stmm/tee_stmm_efi.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/efi/stmm/tee_stmm_efi.c b/drivers/firmware/efi/stmm/tee_stmm_efi.c index 7b04dd649629..3bea2ef50ef3 100644 --- a/drivers/firmware/efi/stmm/tee_stmm_efi.c +++ b/drivers/firmware/efi/stmm/tee_stmm_efi.c @@ -14,7 +14,6 @@ #include "mm_communication.h" static struct efivars tee_efivars; -static struct efivar_operations tee_efivar_ops; static size_t max_buffer_size; /* comm + var + func + data */ static size_t max_payload_size; /* func + data */ @@ -520,6 +519,15 @@ static void tee_stmm_restore_efivars_generic_ops(void) efivars_generic_ops_register(); } +static const struct efivar_operations tee_efivar_ops = { + .get_variable = tee_get_variable, + .get_next_variable = tee_get_next_variable, + .set_variable = tee_set_variable, + .set_variable_nonblocking = tee_set_variable_nonblocking, + .query_variable_store = efi_query_variable_store, + .query_variable_info = tee_query_variable_info, +}; + static int tee_stmm_efi_probe(struct tee_client_device *tee_dev) { struct device *dev = &tee_dev->dev; @@ -558,13 +566,6 @@ static int tee_stmm_efi_probe(struct tee_client_device *tee_dev) MM_VARIABLE_COMMUNICATE_SIZE + max_payload_size; - tee_efivar_ops.get_variable = tee_get_variable; - tee_efivar_ops.get_next_variable = tee_get_next_variable; - tee_efivar_ops.set_variable = tee_set_variable; - tee_efivar_ops.set_variable_nonblocking = tee_set_variable_nonblocking; - tee_efivar_ops.query_variable_store = efi_query_variable_store; - tee_efivar_ops.query_variable_info = tee_query_variable_info; - efivars_generic_ops_unregister(); pr_info("Using TEE-based EFI runtime variable services\n"); efivars_register(&tee_efivars, &tee_efivar_ops); From 21279b1096b1546cc71dbf2f0b50df9151bd3421 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Tue, 17 Feb 2026 14:01:56 +0200 Subject: [PATCH 4/6] MAINTAINERS: Add a reviewer entry for EFI Over the years I've contributed patches to the EFI subsystem mostly around TPM and EFI variables. Add me as a reviewer. Signed-off-by: Ilias Apalodimas Signed-off-by: Ard Biesheuvel --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index eaf55e463bb4..090602787bf3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9603,6 +9603,7 @@ F: security/integrity/evm/ EXTENSIBLE FIRMWARE INTERFACE (EFI) M: Ard Biesheuvel +R: Ilias Apalodimas L: linux-efi@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git From 0862438c90487e79822d5647f854977d50381505 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Tue, 17 Feb 2026 10:49:56 +0000 Subject: [PATCH 5/6] efi: Fix reservation of unaccepted memory table The reserve_unaccepted() function incorrectly calculates the size of the memblock reservation for the unaccepted memory table. It aligns the size of the table, but fails to account for cases where the table's starting physical address (efi.unaccepted) is not page-aligned. If the table starts at an offset within a page and its end crosses into a subsequent page that the aligned size does not cover, the end of the table will not be reserved. This can lead to the table being overwritten or inaccessible, causing a kernel panic in accept_memory(). This issue was observed when starting Intel TDX VMs with specific memory sizes (e.g., > 64GB). Fix this by calculating the end address first (including the unaligned start) and then aligning it up, ensuring the entire range is covered by the reservation. Fixes: 8dbe33956d96 ("efi/unaccepted: Make sure unaccepted table is mapped") Reported-by: Moritz Sanft Signed-off-by: Kiryl Shutsemau (Meta) Reviewed-by: Tom Lendacky Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 111e87a618e5..56e9d73412fa 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -692,13 +692,13 @@ static __init int match_config_table(const efi_guid_t *guid, static __init void reserve_unaccepted(struct efi_unaccepted_memory *unaccepted) { - phys_addr_t start, size; + phys_addr_t start, end; start = PAGE_ALIGN_DOWN(efi.unaccepted); - size = PAGE_ALIGN(sizeof(*unaccepted) + unaccepted->size); + end = PAGE_ALIGN(efi.unaccepted + sizeof(*unaccepted) + unaccepted->size); - memblock_add(start, size); - memblock_reserve(start, size); + memblock_add(start, end - start); + memblock_reserve(start, end - start); } int __init efi_config_parse_tables(const efi_config_table_t *config_tables, From 948a013a54c47d5eba06e644b99d4927a8bc62f8 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Tue, 17 Feb 2026 10:49:57 +0000 Subject: [PATCH 6/6] efi: Align unaccepted memory range to page boundary The accept_memory() and range_contains_unaccepted_memory() functions employ a "guard page" logic to prevent crashes with load_unaligned_zeropad(). This logic extends the range to be accepted (or checked) by one unit_size if the end of the range is aligned to a unit_size boundary. However, if the caller passes a range that is not page-aligned, the 'end' of the range might not be numerically aligned to unit_size, even if it covers the last page of a unit. This causes the "if (!(end % unit_size))" check to fail, skipping the necessary extension and leaving the next unit unaccepted, which can lead to a kernel panic when accessed by load_unaligned_zeropad(). Align the start address down and the size up to the nearest page boundary before performing the unit_size alignment check. This ensures that the guard unit is correctly added when the range effectively ends on a unit boundary. Signed-off-by: Kiryl Shutsemau (Meta) Reviewed-by: Tom Lendacky Acked-by: Mike Rapoport (Microsoft) Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/unaccepted_memory.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c index c2c067eff634..4a8ec8d6a571 100644 --- a/drivers/firmware/efi/unaccepted_memory.c +++ b/drivers/firmware/efi/unaccepted_memory.c @@ -35,14 +35,17 @@ void accept_memory(phys_addr_t start, unsigned long size) struct efi_unaccepted_memory *unaccepted; unsigned long range_start, range_end; struct accept_range range, *entry; - phys_addr_t end = start + size; unsigned long flags; + phys_addr_t end; u64 unit_size; unaccepted = efi_get_unaccepted_table(); if (!unaccepted) return; + end = PAGE_ALIGN(start + size); + start = PAGE_ALIGN_DOWN(start); + unit_size = unaccepted->unit_size; /* @@ -160,15 +163,18 @@ retry: bool range_contains_unaccepted_memory(phys_addr_t start, unsigned long size) { struct efi_unaccepted_memory *unaccepted; - phys_addr_t end = start + size; unsigned long flags; bool ret = false; + phys_addr_t end; u64 unit_size; unaccepted = efi_get_unaccepted_table(); if (!unaccepted) return false; + end = PAGE_ALIGN(start + size); + start = PAGE_ALIGN_DOWN(start); + unit_size = unaccepted->unit_size; /*