From: George Guo <dongtai.guo@linux.dev>
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 [thread overview]
Message-ID: <20261029172956.20791-2-dongtai.guo@linux.dev> (raw)
In-Reply-To: <20261029172956.20791-1-dongtai.guo@linux.dev>
From: George Guo <guodongtai@kylinos.cn>
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 <guodongtai@kylinos.cn>
---
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 <linux/initrd.h>
#include <linux/io.h>
#include <linux/kexec.h>
+#include <linux/kexec_handover.h>
#include <linux/platform_device.h>
#include <linux/random.h>
#include <linux/reboot.h>
@@ -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
next prev parent reply other threads:[~2026-10-29 17:30 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 17:29 [PATCH v6 0/8] LoongArch: Add KHO support and selftests George Guo
2026-10-29 17:29 ` George Guo [this message]
2026-10-29 19:28 ` [PATCH v6 1/8] efi: Pass KHO state to the next kernel in a configuration table Ard Biesheuvel
2026-10-29 19:42 ` Pratyush Yadav
2026-10-29 17:29 ` [PATCH v6 2/8] kho: Write KHO state to the EFI table at kexec time George Guo
2026-10-29 17:29 ` [PATCH v6 3/8] kho: Include linux/mm.h in kho_block.c George Guo
2026-10-29 17:29 ` [PATCH v6 4/8] kho: Add kho_disable() George Guo
2026-10-29 17:29 ` [PATCH v6 5/8] LoongArch: Allow memblock arrays to resize George Guo
2026-10-29 17:29 ` [PATCH v6 6/8] LoongArch: Enable kexec handover (KHO) George Guo
2026-10-29 17:29 ` [PATCH v6 7/8] selftests/kho: Add LoongArch vmtest support George Guo
2026-10-29 17:29 ` [PATCH v6 8/8] selftests/liveupdate: " George Guo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261029172956.20791-2-dongtai.guo@linux.dev \
--to=dongtai.guo@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=ardb@kernel.org \
--cc=baoquan.he@linux.dev \
--cc=chenhuacai@kernel.org \
--cc=graf@amazon.com \
--cc=guodongtai@kylinos.cn \
--cc=ilias.apalodimas@linaro.org \
--cc=kernel@xen0n.name \
--cc=kexec@lists.infradead.org \
--cc=linux-efi@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=liukexin@kylinos.cn \
--cc=loongarch@lists.linux.dev \
--cc=pasha.tatashin@soleen.com \
--cc=pratyush@kernel.org \
--cc=rppt@kernel.org \
--cc=ruirui.yang@linux.dev \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox