[PATCH v6 2/8] kho: Write KHO state to the EFI table at kexec time
From: George Guo
Date: Thu Oct 29 2026 - 13:31:17 EST
From: George Guo <guodongtai@xxxxxxxxxx>
On arm64 and riscv, KHO addresses and sizes are passed through /chosen
properties in the boot FDT prepared for each kexec image. On x86, they
are passed through a SETUP_KEXEC_KHO record linked from the image's boot
parameters. These records are per-image, unlike the shared EFI table.
The EFI table is shared. A KHO-enabled normal image and a crash image
can both be loaded, but need different contents: the normal image needs
the KHO addresses, while the crash image needs an empty table. Updating
the table at load time cannot satisfy both.
Write the table in kernel_kexec() and __crash_kexec(), once the image
that will run is known. kho_update_efi_table() fills the table from that
image, or zeroes it if the image has no KHO state, including images
loaded with kexec_load().
Clear the table even with CONFIG_KEXEC_HANDOVER disabled, since the
kernel may have inherited a populated table. This prevents a later
kernel from trying to revive obsolete KHO state.
The table stores the size of the bootmem area list (bufsz), not of its
page-aligned segment, as the FDT and x86 code do. If updating or clearing
the table fails, kernel_kexec() returns before liveupdate_reboot(), so
LUO does not save state that the next kernel would not find.
__crash_kexec() ignores the error so the crash boot can still proceed.
Signed-off-by: George Guo <guodongtai@xxxxxxxxxx>
---
kernel/crash_core.c | 6 ++++++
kernel/kexec_core.c | 5 +++++
kernel/kexec_internal.h | 8 ++++++++
kernel/liveupdate/kexec_handover.c | 26 ++++++++++++++++++++++++++
4 files changed, 45 insertions(+)
diff --git a/kernel/crash_core.c b/kernel/crash_core.c
index 2b36aa9fade0..0f77adc0928b 100644
--- a/kernel/crash_core.c
+++ b/kernel/crash_core.c
@@ -138,6 +138,12 @@ void __noclone __crash_kexec(struct pt_regs *regs)
if (kexec_crash_image) {
struct pt_regs fixed_regs;
+ /*
+ * A crash image has no KHO state. Zero the EFI
+ * table so the crash kernel has no inherited KHO state.
+ */
+ (void)kho_update_efi_table(kexec_crash_image);
+
crash_setup_regs(&fixed_regs, regs);
crash_save_vmcoreinfo();
machine_crash_shutdown(&fixed_regs);
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index dc770b9a6d05..37597091770a 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -1146,6 +1146,11 @@ int kernel_kexec(void)
goto Unlock;
}
+ /* Write this image's KHO state into the EFI table. */
+ error = kho_update_efi_table(kexec_image);
+ if (error)
+ goto Unlock;
+
if (!kexec_image->preserve_context) {
error = liveupdate_reboot();
if (error)
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index 228bb88c018b..cfdd0b158f72 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -2,6 +2,7 @@
#ifndef LINUX_KEXEC_INTERNAL_H
#define LINUX_KEXEC_INTERNAL_H
+#include <linux/efi.h>
#include <linux/kexec.h>
struct kexec_segment;
@@ -46,6 +47,7 @@ struct kexec_buf;
int kho_locate_mem_hole(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *));
int kho_fill_kimage(struct kimage *image);
+int kho_update_efi_table(struct kimage *image);
#else
static inline int kho_locate_mem_hole(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *))
@@ -54,5 +56,11 @@ static inline int kho_locate_mem_hole(struct kexec_buf *kbuf,
}
static inline int kho_fill_kimage(struct kimage *image) { return 0; }
+
+static inline int kho_update_efi_table(struct kimage *image)
+{
+ /* The previous kernel may have left KHO state in the EFI table. */
+ return efi_kho_update(0, 0, 0, 0);
+}
#endif /* CONFIG_KEXEC_HANDOVER */
#endif /* LINUX_KEXEC_INTERNAL_H */
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 9a53d7750367..74c4633f5afc 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -15,6 +15,7 @@
#include <linux/crash_dump.h>
#include <linux/kmemleak.h>
#include <linux/count_zeros.h>
+#include <linux/efi.h>
#include <linux/kasan.h>
#include <linux/kexec.h>
#include <linux/kexec_handover.h>
@@ -2094,6 +2095,31 @@ int kho_fill_kimage(struct kimage *image)
return 0;
}
+/*
+ * Write the KHO state of @image into the EFI table, or zero the table if
+ * @image has none.
+ *
+ * There is one table, but the normal and the crash image can both be loaded,
+ * so the table is written just before @image runs rather than at load time.
+ * Crash images, images loaded with kexec_load() and images loaded with KHO
+ * disabled have no KHO state. Zeroing the table for them makes the next
+ * kernel boot without inherited KHO state instead of reading stale addresses.
+ */
+int kho_update_efi_table(struct kimage *image)
+{
+ int err;
+
+ if (!image->kho.fdt || !image->kho.bootmem)
+ return efi_kho_update(0, 0, 0, 0);
+
+ err = efi_kho_update(image->kho.fdt, PAGE_SIZE,
+ image->kho.bootmem->mem, image->kho.bootmem->bufsz);
+ if (err)
+ pr_warn("failed to update EFI config table: %pe\n", ERR_PTR(err));
+
+ return err;
+}
+
static int kho_walk_bootmem(struct kexec_buf *kbuf,
int (*func)(struct resource *, void *))
{
--
2.53.0