From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=91.218.175.147 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793295016; b=X83qtVJmFdJ1QXxXRcNL1iGgWXnae+8rvMFRlOQXMojStev/E2Nk0+m67Qam0n6j6hdR Iqv+XHLO51+KKlqbdSTKY+RGN+EWpYyvYAB4qbyUzSlWfOWCXhII5Sad//f/UYOZ+RI8g JOF6EQQWmyx8fdm6rivU6B8UUphn3UBoz1d8rI1hmA51NcCD3GxFXF0apDe4nb6eAk3IN 1gkB7vlJMOQvA9wmry5lp0VpUnTwp7hBn5znOaHvCBSYTw5GxOj5uFnX6znM2lnssTDZd INpC4uN68PnlHAyCAZzxxYBUtIEeJgcgahesNldCjyxaWdeuWKZYFNt+gtaZhi3PIMQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793295016; h=DMARC-Filter:X-Envelope-To:DKIM-Signature:X-Envelope-To:Received: X-Mizu-Trace-ID:X-Migadu-Flow:From:To:Cc:Subject:Date:Message-ID: X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=lAtuW+PmqcIRrD32GU2FmHnRSvRZSE8WSvkIZYFlqEI=; b=eplwQ4vip5QnQyR5yIb9JDT2wLABsZ4B9GXLz+unilVJGzEupSYBau+BWJw+FLBFmjr7 /7UszE28Zm/lpOcsvI0xi3Lu9C1apzgFPe9S1dYCgto/XY0lutrA+Gk9YT7hBfouKuJ6q 0KPU0Kmy7xzOQ/Fad5L+EieYbbT2Op8HOeNQbG9MAzix3lAJDVC/ihzrmYSc56OcZYesk jGxe9aoY2N4bPchkMjg/rHppgUFqDElVfzVZ1p4ag/BaCtIZ6tT/Ghb+CtwE8UkbEQzpj 239VkQ/+TgGoRYUl1u8yX1/yXeU/utg9xjyAIPRJFrbrVTPl/4BJ04rHaNfugFS2jkg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass header.d=linux.dev header.i=@linux.dev header.a=rsa-sha256 header.s=key1 header.b=FXCOndxr; arc=none smtp.remote-ip=91.218.175.147 Received: from mta0.migadu.com (out-147.mta0.migadu.com [91.218.175.147]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 058CA606A32C for ; Thu, 29 Oct 2026 17:30:15 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 058CA606A32C Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux.dev header.i=@linux.dev header.a=rsa-sha256 header.s=key1 header.b=FXCOndxr X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=BakrBcWbSK1C/V/NJmrPg1yKTLbeIP3Nrz+xI4DnuzI=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1793295014; v=1; x=1793899814; b=FXCOndxrulaEk0PxCyRH3ODvX5d3H9KFQ/BLQhZ/42tZQJIVurIT+3imfuqAwzD1q+Y1BJnD 4JvTGc7qolIhrjWi2jfh9huotl7MPypHZoqmKTMBsCMiCGKHpNDFW9Gf6EAizwVBp5O0s/yPpCn cySA8bh/YyZjIdDq9yN5mPho= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 0e3683f53b12271c; Thu, 29 Oct 2026 17:30:10 +0000 X-Mizu-Trace-ID: 0e3683f53b12271c X-Migadu-Flow: FLOW_OUT From: George Guo To: rppt@kernel.org, pasha.tatashin@soleen.com, pratyush@kernel.org, chenhuacai@kernel.org, ardb@kernel.org, shuah@kernel.org Cc: ilias.apalodimas@linaro.org, akpm@linux-foundation.org, baoquan.he@linux.dev, ruirui.yang@linux.dev, guodongtai@kylinos.cn, kernel@xen0n.name, graf@amazon.com, liukexin@kylinos.cn, loongarch@lists.linux.dev, linux-kernel@vger.kernel.org, kexec@lists.infradead.org, linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-efi@vger.kernel.org Subject: [PATCH v6 1/8] efi: Pass KHO state to the next kernel in a configuration table Date: Fri, 30 Oct 2026 02:29:48 +0900 Message-ID: <20261029172956.20791-2-dongtai.guo@linux.dev> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261029172956.20791-1-dongtai.guo@linux.dev> References: <20261029172956.20791-1-dongtai.guo@linux.dev> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: George Guo KHO has to give the next kernel two addresses: the KHO FDT and the bootmem area list. arm64 and riscv put them under /chosen in the boot FDT. LoongArch systems booting with EFI and ACPI do not require a boot FDT, so KHO needs another way to pass this information. Add a Linux EFI configuration table for them, under LINUX_EFI_KEXEC_HANDOVER_GUID. It points to a struct linux_efi_kho_data. - On a cold boot, the LoongArch EFI stub installs the table, zeroed. Each kexec'd kernel is given the same EFI system table, so it finds the same table. - In early boot, efi_config_parse_tables() reserves the table so neither the page allocator nor kexec segments use it, and calls kho_populate() if the table holds KHO state. - Provide efi_kho_update() to update the table before kexec. Map the table during boot so updates do not require allocation or mapping at panic time. - efi_kho_table_found() tells architecture code whether the table was found at boot, so it can turn KHO off when the table is missing. Only the LoongArch stub installs the table, and only with CONFIG_KEXEC_HANDOVER. Elsewhere there is no table and this code does nothing. Signed-off-by: George Guo --- drivers/firmware/efi/efi.c | 68 ++++++++++++++++++++++++ drivers/firmware/efi/libstub/efi-stub.c | 22 ++++++++ drivers/firmware/efi/libstub/efistub.h | 2 + drivers/firmware/efi/libstub/loongarch.c | 2 + include/linux/efi.h | 40 ++++++++++++++ 5 files changed, 134 insertions(+) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 0327a39d31fa..e409e4a57378 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ unsigned long __ro_after_init efi_rng_seed = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR; +static unsigned long efi_kho __initdata = EFI_INVALID_TABLE_ADDR; extern unsigned long primary_display_table; @@ -629,6 +631,7 @@ static const efi_config_table_type_t common_tables[] __initconst = { {EFI_TCG2_FINAL_EVENTS_TABLE_GUID, &efi.tpm_final_log, "TPMFinalLog" }, {EFI_CC_FINAL_EVENTS_TABLE_GUID, &efi.tpm_final_log, "CCFinalLog" }, {LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" }, + {LINUX_EFI_KEXEC_HANDOVER_GUID, &efi_kho, "KHO" }, {LINUX_EFI_INITRD_MEDIA_GUID, &initrd, "INITRD" }, {EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" }, #ifdef CONFIG_OVMF_DEBUG_LOG @@ -806,6 +809,23 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, } } + if (efi_kho != EFI_INVALID_TABLE_ADDR) { + struct linux_efi_kho_data *kho; + + /* Keep the buddy allocator and kexec segments off the table. */ + memblock_reserve(efi_kho, sizeof(*kho)); + + kho = early_memremap(efi_kho, sizeof(*kho)); + if (kho) { + if (kho->fdt_addr) + kho_populate((phys_addr_t)kho->fdt_addr, kho->fdt_size, + (phys_addr_t)kho->bootmem_addr, kho->bootmem_size); + early_memunmap(kho, sizeof(*kho)); + } else { + pr_warn("Could not map UEFI KHO table!\n"); + } + } + if (rt_prop != EFI_INVALID_TABLE_ADDR) { efi_rt_properties_table_t *tbl; @@ -1171,6 +1191,54 @@ static int __init efi_memreserve_root_init(void) } early_initcall(efi_memreserve_root_init); +static struct linux_efi_kho_data *efi_kho_table __ro_after_init; + +/* Did the previous kernel or the EFI stub pass a KHO table? */ +bool __init efi_kho_table_found(void) +{ + return efi_kho != EFI_INVALID_TABLE_ADDR; +} + +static int __init efi_kho_table_init(void) +{ + if (efi_kho == EFI_INVALID_TABLE_ADDR) + return 0; + + /* Map it now: the table is also written at panic time. */ + efi_kho_table = memremap(efi_kho, sizeof(*efi_kho_table), MEMREMAP_WB); + if (WARN_ON_ONCE(!efi_kho_table)) + efi_kho_table = ERR_PTR(-ENOMEM); + + return 0; +} +early_initcall(efi_kho_table_init); + +/* + * Write the KHO addresses the next kernel should use, or zeroes + * if it should boot without KHO. + */ +int efi_kho_update(phys_addr_t fdt_addr, u64 fdt_size, + phys_addr_t bootmem_addr, u64 bootmem_size) +{ + struct linux_efi_kho_data *kho = efi_kho_table; + + /* + * No table: either the architecture passes the KHO state some other + * way, or it turned KHO off at boot because the table was missing. + */ + if (!kho) + return 0; + if (IS_ERR(kho)) + return PTR_ERR(kho); + + kho->fdt_addr = fdt_addr; + kho->fdt_size = fdt_size; + kho->bootmem_addr = bootmem_addr; + kho->bootmem_size = bootmem_size; + + return 0; +} + #ifdef CONFIG_KEXEC static int update_efi_random_seed(struct notifier_block *nb, unsigned long code, void *unused) diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 42d6073bcd06..0fb0d4041d1c 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -100,6 +100,28 @@ static void install_memreserve_table(void) efi_err("Failed to install memreserve config table!\n"); } +void install_kho_table(void) +{ + struct linux_efi_kho_data *kho; + efi_guid_t kho_table_guid = LINUX_EFI_KEXEC_HANDOVER_GUID; + efi_status_t status; + + if (!IS_ENABLED(CONFIG_KEXEC_HANDOVER)) + return; + + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*kho), (void **)&kho); + if (status != EFI_SUCCESS) { + efi_err("Failed to allocate KHO config table!\n"); + return; + } + + *kho = (struct linux_efi_kho_data){}; + + status = efi_bs_call(install_configuration_table, &kho_table_guid, kho); + if (status != EFI_SUCCESS) + efi_err("Failed to install KHO config table!\n"); +} + static u32 get_supported_rt_services(void) { const efi_rt_properties_table_t *rt_prop_table; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index fd91fc15ec81..9e0034c1260a 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -1057,6 +1057,8 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv, efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, unsigned long kernel_addr, char *cmdline_ptr); +void install_kho_table(void); + void *get_fdt(unsigned long *fdt_size); efi_status_t efi_alloc_virtmap(efi_memory_desc_t **virtmap, diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c index 2b0c87dc9908..c2df097771e4 100644 --- a/drivers/firmware/efi/libstub/loongarch.c +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -74,6 +74,8 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, efi_status_t status; u32 desc_ver; + install_kho_table(); + status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver); if (status != EFI_SUCCESS) { efi_err("Unable to retrieve UEFI memory map.\n"); diff --git a/include/linux/efi.h b/include/linux/efi.h index aa15ff88539b..253e98bf9922 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -422,6 +422,7 @@ void efi_native_runtime_setup(void); #define LINUX_EFI_COCO_SECRET_AREA_GUID EFI_GUID(0xadf956ad, 0xe98c, 0x484c, 0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47) #define LINUX_EFI_BOOT_MEMMAP_GUID EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4) #define LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID EFI_GUID(0xd5d1de3c, 0x105c, 0x44f9, 0x9e, 0xa9, 0xbc, 0xef, 0x98, 0x12, 0x00, 0x31) +#define LINUX_EFI_KEXEC_HANDOVER_GUID EFI_GUID(0xc941b6c7, 0x7b3f, 0x4af6, 0x9e, 0x50, 0xfc, 0xb3, 0xa8, 0x86, 0x8a, 0x17) #define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec, 0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf) @@ -1273,6 +1274,45 @@ struct linux_efi_memreserve { void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size); +/** + * struct linux_efi_kho_data - KHO state passed to the next kernel + * @fdt_addr: physical address of the KHO FDT, 0 if there is none + * @fdt_size: size of the KHO FDT + * @bootmem_addr: physical address of the bootmem area list + * @bootmem_size: size of the bootmem area list + * + * Pointed to by the LINUX_EFI_KEXEC_HANDOVER_GUID configuration table. + * + * The EFI stub installs this table zeroed on a cold boot. It stays + * in place across kexec; the running kernel rewrites it just before + * each kexec, and the next kernel reads it during early boot. + * + * The layout is fixed. Changing it requires a new GUID. + */ +struct linux_efi_kho_data { + u64 fdt_addr; + u64 fdt_size; + u64 bootmem_addr; + u64 bootmem_size; +}; + +#ifdef CONFIG_EFI +bool efi_kho_table_found(void); +int efi_kho_update(phys_addr_t fdt_addr, u64 fdt_size, + phys_addr_t bootmem_addr, u64 bootmem_size); +#else +static inline bool efi_kho_table_found(void) +{ + return false; +} + +static inline int efi_kho_update(phys_addr_t fdt_addr, u64 fdt_size, + phys_addr_t bootmem_addr, u64 bootmem_size) +{ + return 0; +} +#endif + /* * The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table can be provided * to the kernel by an EFI boot loader. The table contains a packed -- 2.53.0