Store and display the kernel version from the previous kexec boot.
The current kernel's release string is saved to the "previous-release"
property in the KHO FDT before kexec. On the next boot, if this property
exists, the previous kernel version is retrieved and printed during
early boot.
This helps diagnose bugs that only manifest when kexecing from specific
kernel versions, making it easier to correlate crashes with the kernel
that initiated the kexec.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
include/linux/kho/abi/kexec_handover.h | 3 +++
kernel/liveupdate/kexec_handover.c | 25 ++++++++++++++++++++++++-
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi/kexec_handover.h
index 285eda8a36e4..f4f31e8f575b 100644
--- a/include/linux/kho/abi/kexec_handover.h
+++ b/include/linux/kho/abi/kexec_handover.h
@@ -84,6 +84,9 @@
/* The FDT property for sub-FDTs. */
#define KHO_FDT_SUB_TREE_PROP_NAME "fdt"
+/* The FDT property to track previous kernel (kexec caller) */
+#define KHO_PROP_PREVIOUS_RELEASE "previous-release"
+
/**
* DOC: Kexec Handover ABI for vmalloc Preservation
*
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 3cf2dc6840c9..b2d57868d22f 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -22,6 +22,7 @@
#include <linux/page-isolation.h>
#include <linux/unaligned.h>
#include <linux/vmalloc.h>
+#include <linux/utsname.h>
#include <asm/early_ioremap.h>
@@ -1246,6 +1247,7 @@ struct kho_in {
phys_addr_t fdt_phys;
phys_addr_t scratch_phys;
phys_addr_t mem_map_phys;
+ char previous_release[__NEW_UTS_LEN + 1];
struct kho_debugfs dbg;
};
@@ -1325,6 +1327,8 @@ static __init int kho_out_fdt_setup(void)
err |= fdt_property_string(root, "compatible", KHO_FDT_COMPATIBLE);
err |= fdt_property(root, KHO_FDT_MEMORY_MAP_PROP_NAME, &empty_mem_map,
sizeof(empty_mem_map));
+ err |= fdt_property_string(root, KHO_PROP_PREVIOUS_RELEASE,
+ init_uts_ns.name.release);
err |= fdt_end_node(root);
err |= fdt_finish(root);
@@ -1436,6 +1440,22 @@ void __init kho_memory_init(void)
}
}
+static int __init kho_print_previous_kernel(const void *fdt)
+{
+ const char *prev_release;
+ int len;
+
+ prev_release = fdt_getprop(fdt, 0, KHO_PROP_PREVIOUS_RELEASE, &len);
+ if (!prev_release || len <= 0)
+ return -ENOENT;
+
+ strscpy(kho_in.previous_release, prev_release,
+ sizeof(kho_in.previous_release));
+ pr_info("exec from: %s\n", kho_in.previous_release);
+
+ return 0;
+}
+
void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
phys_addr_t scratch_phys, u64 scratch_len)
{
@@ -1513,7 +1533,10 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
kho_in.scratch_phys = scratch_phys;
kho_in.mem_map_phys = mem_map_phys;
kho_scratch_cnt = scratch_cnt;
- pr_info("found kexec handover data.\n");
+
+ if (kho_print_previous_kernel(fdt))
+ /* Fallback message when previous kernel info unavailable */
+ pr_info("found kexec handover data.\n");
out:
if (fdt)
--
2.47.3
Hi Breno, On Thu, Jan 08 2026, Breno Leitao wrote: > Store and display the kernel version from the previous kexec boot. > > The current kernel's release string is saved to the "previous-release" > property in the KHO FDT before kexec. On the next boot, if this property > exists, the previous kernel version is retrieved and printed during > early boot. > > This helps diagnose bugs that only manifest when kexecing from specific > kernel versions, making it easier to correlate crashes with the kernel > that initiated the kexec. The KHO FDT is ABI. So you should be bumping the version number when you make changes to it. But honestly, adding this "optional" stuff to the core KHO ABI makes me uneasy. I say optional since it is not needed for the main functionality of KHO. Making this a part of the ABI increases the surface area we have. The more things we stuff in the ABI, the more inflexible it gets over time. Any changes to the KHO ABI means all consumers also need a version bump. This includes LUO and all its users for example. So I would really like to avoid adding optional things in core KHO FDT. The easy fix is to add a separate subtree for the optional metadata. You would still need to create an ABI for the data format, but being independent of core KHO, it will make it more flexible and easier to change in the future. You can keep the code in kexec_handover.c. > > Signed-off-by: Breno Leitao <leitao@debian.org> [...] -- Regards, Pratyush Yadav
Hello Pratyush,
On Wed, Jan 14, 2026 at 07:19:11PM +0000, Pratyush Yadav wrote:
> On Thu, Jan 08 2026, Breno Leitao wrote:
>
> > Store and display the kernel version from the previous kexec boot.
> >
> > The current kernel's release string is saved to the "previous-release"
> > property in the KHO FDT before kexec. On the next boot, if this property
> > exists, the previous kernel version is retrieved and printed during
> > early boot.
> >
> > This helps diagnose bugs that only manifest when kexecing from specific
> > kernel versions, making it easier to correlate crashes with the kernel
> > that initiated the kexec.
>
> The KHO FDT is ABI. So you should be bumping the version number when you
> make changes to it.
>
> But honestly, adding this "optional" stuff to the core KHO ABI makes me
> uneasy. I say optional since it is not needed for the main functionality
> of KHO. Making this a part of the ABI increases the surface area we
> have. The more things we stuff in the ABI, the more inflexible it gets
> over time.
>
> Any changes to the KHO ABI means all consumers also need a version bump.
> This includes LUO and all its users for example. So I would really like
> to avoid adding optional things in core KHO FDT.
>
> The easy fix is to add a separate subtree for the optional metadata. You
> would still need to create an ABI for the data format, but being
> independent of core KHO, it will make it more flexible and easier to
> change in the future. You can keep the code in kexec_handover.c.
Thanks for the feedback and guidance!
I was able to hack this a bit and I came up with something like the
follow. Is this what you have in mind?
Author: Breno Leitao <leitao@debian.org>
Date: Fri Jan 16 06:42:56 2026 -0800
kho: history: track previous kernel version and kexec count
Use Kexec Handover (KHO) to pass the previous kernel's version string
and the number of kexec reboots since the last cold boot to the next
kernel, and print it at boot time.
Example output:
[ 0.000000] KHO: exec from: 6.19.0-rc4-next-20260107 (count 1)
Motivation
==========
Bugs that only reproduce when kexecing from specific kernel versions
are difficult to diagnose. These issues occur when a buggy kernel
kexecs into a new kernel, with the bug manifesting only in the second
kernel.
Recent examples include:
* eb2266312507 ("x86/boot: Fix page table access in 5-level to 4-level paging transition")
* 77d48d39e991 ("efistub/tpm: Use ACPI reclaim memory for event log to avoid corruption")
* 64b45dd46e15 ("x86/efi: skip memattr table on kexec boot")
As kexec-based reboots become more common, these version-dependent bugs
are appearing more frequently. At scale, correlating crashes to the
previous kernel version is challenging, especially when issues only
occur in specific transition scenarios.
Implementation
==============
The history metadata is stored in a separate FDT subtree registered via
kho_add_subtree(), rather than being embedded directly in the root KHO
FDT. This design choice:
- Keeps the core KHO ABI minimal and stable
- Allows the history format to evolve independently
- Avoids requiring version bumps for all KHO consumers (LUO, etc.)
when the history format changes
The history subtree uses its own compatible string "kho-history-v1" and
contains two properties:
- previous-release: The kernel version that initiated the kexec
- kexec-count: Number of kexec boots since last cold boot
On cold boot, kexec-count starts at 0 and increments with each kexec.
The count helps identify issues that only manifest after multiple
consecutive kexec reboots.
Signed-off-by: Breno Leitao <leitao@debian.org>
diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi/kexec_handover.h
index 285eda8a36e4..da19d6029815 100644
--- a/include/linux/kho/abi/kexec_handover.h
+++ b/include/linux/kho/abi/kexec_handover.h
@@ -84,6 +84,29 @@
/* The FDT property for sub-FDTs. */
#define KHO_FDT_SUB_TREE_PROP_NAME "fdt"
+/*
+ * The "history" subtree stores optional metadata about the kexec chain.
+ * It is registered as a separate FDT via kho_add_subtree(), keeping it
+ * independent from the core KHO ABI. This allows the history format to
+ * evolve without affecting other KHO consumers.
+ *
+ * The history FDT structure:
+ *
+ * / {
+ * compatible = "kho-history-v1";
+ * previous-release = "6.x.y-...";
+ * kexec-count = <N>;
+ * };
+ */
+#define KHO_HISTORY_NODE_NAME "history"
+#define KHO_HISTORY_COMPATIBLE "kho-history-v1"
+
+/* The FDT property to track previous kernel (kexec caller) */
+#define KHO_PROP_PREVIOUS_RELEASE "previous-release"
+
+/* The FDT property to track number of kexec counts so far */
+#define KHO_PROP_KEXEC_COUNT "kexec-count"
+
/**
* DOC: Kexec Handover ABI for vmalloc Preservation
*
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 3cf2dc6840c9..fd22b0947587 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -15,6 +15,7 @@
#include <linux/count_zeros.h>
#include <linux/kexec.h>
#include <linux/kexec_handover.h>
+#include <linux/utsname.h>
#include <linux/kho/abi/kexec_handover.h>
#include <linux/libfdt.h>
#include <linux/list.h>
@@ -1246,6 +1247,8 @@ struct kho_in {
phys_addr_t fdt_phys;
phys_addr_t scratch_phys;
phys_addr_t mem_map_phys;
+ char previous_release[__NEW_UTS_LEN + 1];
+ u32 kexec_count;
struct kho_debugfs dbg;
};
@@ -1331,6 +1334,48 @@ static __init int kho_out_fdt_setup(void)
return err;
}
+/*
+ * Create a separate FDT subtree for optional history metadata.
+ * This keeps the core KHO ABI minimal and allows the history format
+ * to evolve independently.
+ */
+static __init int kho_history_init(void)
+{
+ u32 kexec_count;
+ void *fdt;
+ int err;
+
+ fdt = kho_alloc_preserve(PAGE_SIZE);
+ if (IS_ERR(fdt))
+ return PTR_ERR(fdt);
+
+ err = fdt_create(fdt, PAGE_SIZE);
+ err |= fdt_finish_reservemap(fdt);
+ err |= fdt_begin_node(fdt, "");
+ err |= fdt_property_string(fdt, "compatible", KHO_HISTORY_COMPATIBLE);
+ err |= fdt_property_string(fdt, KHO_PROP_PREVIOUS_RELEASE,
+ init_uts_ns.name.release);
+ /* kho_in.kexec_count is set to 0 on cold boot */
+ kexec_count = kho_in.kexec_count + 1;
+ err |= fdt_property(fdt, KHO_PROP_KEXEC_COUNT, &kexec_count,
+ sizeof(kexec_count));
+ err |= fdt_end_node(fdt);
+ err |= fdt_finish(fdt);
+
+ if (err) {
+ kho_unpreserve_free(fdt);
+ return err;
+ }
+
+ err = kho_add_subtree(KHO_HISTORY_NODE_NAME, fdt);
+ if (err) {
+ kho_unpreserve_free(fdt);
+ return err;
+ }
+
+ return 0;
+}
+
static __init int kho_init(void)
{
const void *fdt = kho_get_fdt();
@@ -1357,6 +1402,10 @@ static __init int kho_init(void)
if (err)
goto err_free_fdt;
+ err = kho_history_init();
+ if (err)
+ pr_warn("failed to initialize history subtree: %d\n", err);
+
if (fdt) {
kho_in_debugfs_init(&kho_in.dbg, fdt);
return 0;
@@ -1425,6 +1474,61 @@ static void __init kho_release_scratch(void)
}
}
+static int __init kho_print_previous_kernel(const void *fdt)
+{
+ const char *prev_release;
+ const u64 *history_phys;
+ const u32 *count_ptr;
+ void *history_fdt;
+ int history_node;
+ int len;
+ int ret;
+
+ /* Find the history subtree reference in root FDT */
+ history_node = fdt_subnode_offset(fdt, 0, KHO_HISTORY_NODE_NAME);
+ if (history_node < 0)
+ /* This is fine, previous kernel didn't export history */
+ return -ENOENT;
+
+ /* Get the physical address of the history FDT */
+ history_phys = fdt_getprop(fdt, history_node, KHO_FDT_SUB_TREE_PROP_NAME, &len);
+ if (!history_phys || len != sizeof(*history_phys))
+ return -ENOENT;
+
+ /* Map the history FDT */
+ history_fdt = early_memremap(*history_phys, PAGE_SIZE);
+ if (!history_fdt)
+ return -ENOMEM;
+
+ prev_release = fdt_getprop(history_fdt, 0, KHO_PROP_PREVIOUS_RELEASE, &len);
+ if (!prev_release || len <= 0) {
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ /* Read the kexec count from the previous kernel */
+ count_ptr = fdt_getprop(history_fdt, 0, KHO_PROP_KEXEC_COUNT, &len);
+ if (WARN_ON(!count_ptr || len != sizeof(u32))) {
+ ret = -ENOENT;
+ goto exit;
+ }
+ /*
+ * This populate the kernel structure that will be persisted during
+ * kernel life time, and the fdt will be unmapped
+ */
+ kho_in.kexec_count = *count_ptr;
+
+ strscpy(kho_in.previous_release, prev_release,
+ sizeof(kho_in.previous_release));
+ pr_info("exec from: %s (count %u)\n", kho_in.previous_release,
+ kho_in.kexec_count);
+
+ ret = 0;
+exit:
+ early_memunmap(history_fdt, PAGE_SIZE);
+ return ret;
+}
+
void __init kho_memory_init(void)
{
if (kho_in.mem_map_phys) {
@@ -1513,7 +1617,10 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
kho_in.scratch_phys = scratch_phys;
kho_in.mem_map_phys = mem_map_phys;
kho_scratch_cnt = scratch_cnt;
- pr_info("found kexec handover data.\n");
+
+ if (kho_print_previous_kernel(fdt))
+ /* Fallback message when previous kernel info unavailable */
+ pr_info("found kexec handover data.\n");
out:
if (fdt)
On Fri, Jan 16 2026, Breno Leitao wrote:
> Hello Pratyush,
>
> On Wed, Jan 14, 2026 at 07:19:11PM +0000, Pratyush Yadav wrote:
>> On Thu, Jan 08 2026, Breno Leitao wrote:
>>
>> > Store and display the kernel version from the previous kexec boot.
>> >
>> > The current kernel's release string is saved to the "previous-release"
>> > property in the KHO FDT before kexec. On the next boot, if this property
>> > exists, the previous kernel version is retrieved and printed during
>> > early boot.
>> >
>> > This helps diagnose bugs that only manifest when kexecing from specific
>> > kernel versions, making it easier to correlate crashes with the kernel
>> > that initiated the kexec.
>>
>> The KHO FDT is ABI. So you should be bumping the version number when you
>> make changes to it.
>>
>> But honestly, adding this "optional" stuff to the core KHO ABI makes me
>> uneasy. I say optional since it is not needed for the main functionality
>> of KHO. Making this a part of the ABI increases the surface area we
>> have. The more things we stuff in the ABI, the more inflexible it gets
>> over time.
>>
>> Any changes to the KHO ABI means all consumers also need a version bump.
>> This includes LUO and all its users for example. So I would really like
>> to avoid adding optional things in core KHO FDT.
>>
>> The easy fix is to add a separate subtree for the optional metadata. You
>> would still need to create an ABI for the data format, but being
>> independent of core KHO, it will make it more flexible and easier to
>> change in the future. You can keep the code in kexec_handover.c.
>
> Thanks for the feedback and guidance!
>
> I was able to hack this a bit and I came up with something like the
> follow. Is this what you have in mind?
Thanks! Yes, this looks much better. Some comments below.
>
> Author: Breno Leitao <leitao@debian.org>
> Date: Fri Jan 16 06:42:56 2026 -0800
>
> kho: history: track previous kernel version and kexec count
>
> Use Kexec Handover (KHO) to pass the previous kernel's version string
> and the number of kexec reboots since the last cold boot to the next
> kernel, and print it at boot time.
>
> Example output:
> [ 0.000000] KHO: exec from: 6.19.0-rc4-next-20260107 (count 1)
>
> Motivation
> ==========
>
> Bugs that only reproduce when kexecing from specific kernel versions
> are difficult to diagnose. These issues occur when a buggy kernel
> kexecs into a new kernel, with the bug manifesting only in the second
> kernel.
>
> Recent examples include:
>
> * eb2266312507 ("x86/boot: Fix page table access in 5-level to 4-level paging transition")
> * 77d48d39e991 ("efistub/tpm: Use ACPI reclaim memory for event log to avoid corruption")
> * 64b45dd46e15 ("x86/efi: skip memattr table on kexec boot")
>
> As kexec-based reboots become more common, these version-dependent bugs
> are appearing more frequently. At scale, correlating crashes to the
> previous kernel version is challenging, especially when issues only
> occur in specific transition scenarios.
>
> Implementation
> ==============
>
> The history metadata is stored in a separate FDT subtree registered via
> kho_add_subtree(), rather than being embedded directly in the root KHO
> FDT. This design choice:
>
> - Keeps the core KHO ABI minimal and stable
> - Allows the history format to evolve independently
> - Avoids requiring version bumps for all KHO consumers (LUO, etc.)
> when the history format changes
>
> The history subtree uses its own compatible string "kho-history-v1" and
> contains two properties:
> - previous-release: The kernel version that initiated the kexec
> - kexec-count: Number of kexec boots since last cold boot
>
> On cold boot, kexec-count starts at 0 and increments with each kexec.
> The count helps identify issues that only manifest after multiple
> consecutive kexec reboots.
Very well written changelog!
>
> Signed-off-by: Breno Leitao <leitao@debian.org>
>
> diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi/kexec_handover.h
> index 285eda8a36e4..da19d6029815 100644
> --- a/include/linux/kho/abi/kexec_handover.h
> +++ b/include/linux/kho/abi/kexec_handover.h
> @@ -84,6 +84,29 @@
> /* The FDT property for sub-FDTs. */
> #define KHO_FDT_SUB_TREE_PROP_NAME "fdt"
>
> +/*
> + * The "history" subtree stores optional metadata about the kexec chain.
> + * It is registered as a separate FDT via kho_add_subtree(), keeping it
> + * independent from the core KHO ABI. This allows the history format to
> + * evolve without affecting other KHO consumers.
> + *
> + * The history FDT structure:
I don't have a strong preference here, but you don't _have_ to use FDT.
For example, with memfd, we moved from FDT to plain C structs during the
evolution of the patchset. Main reason is that FDT programming is a bit
annoying. C structs make many things much easier. For example, you can
always assume a certain property always exists and is of a given size,
and you don't have to validate every single property you read.
Anyway, I don't mind either way.
> + *
> + * / {
> + * compatible = "kho-history-v1";
> + * previous-release = "6.x.y-...";
> + * kexec-count = <N>;
> + * };
> + */
> +#define KHO_HISTORY_NODE_NAME "history"
Do we want to call it history? Perhaps "kexec-metadata" instead? So we
could use it for other misc information if needed later.
Mike/Pasha, any thoughts?
> +#define KHO_HISTORY_COMPATIBLE "kho-history-v1"
> +
> +/* The FDT property to track previous kernel (kexec caller) */
> +#define KHO_PROP_PREVIOUS_RELEASE "previous-release"
> +
> +/* The FDT property to track number of kexec counts so far */
> +#define KHO_PROP_KEXEC_COUNT "kexec-count"
> +
> /**
> * DOC: Kexec Handover ABI for vmalloc Preservation
> *
> diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
> index 3cf2dc6840c9..fd22b0947587 100644
> --- a/kernel/liveupdate/kexec_handover.c
> +++ b/kernel/liveupdate/kexec_handover.c
> @@ -15,6 +15,7 @@
> #include <linux/count_zeros.h>
> #include <linux/kexec.h>
> #include <linux/kexec_handover.h>
> +#include <linux/utsname.h>
> #include <linux/kho/abi/kexec_handover.h>
> #include <linux/libfdt.h>
> #include <linux/list.h>
> @@ -1246,6 +1247,8 @@ struct kho_in {
> phys_addr_t fdt_phys;
> phys_addr_t scratch_phys;
> phys_addr_t mem_map_phys;
> + char previous_release[__NEW_UTS_LEN + 1];
> + u32 kexec_count;
> struct kho_debugfs dbg;
> };
>
> @@ -1331,6 +1334,48 @@ static __init int kho_out_fdt_setup(void)
> return err;
> }
>
> +/*
> + * Create a separate FDT subtree for optional history metadata.
> + * This keeps the core KHO ABI minimal and allows the history format
> + * to evolve independently.
> + */
> +static __init int kho_history_init(void)
> +{
> + u32 kexec_count;
> + void *fdt;
> + int err;
> +
> + fdt = kho_alloc_preserve(PAGE_SIZE);
> + if (IS_ERR(fdt))
> + return PTR_ERR(fdt);
> +
> + err = fdt_create(fdt, PAGE_SIZE);
> + err |= fdt_finish_reservemap(fdt);
> + err |= fdt_begin_node(fdt, "");
> + err |= fdt_property_string(fdt, "compatible", KHO_HISTORY_COMPATIBLE);
> + err |= fdt_property_string(fdt, KHO_PROP_PREVIOUS_RELEASE,
> + init_uts_ns.name.release);
> + /* kho_in.kexec_count is set to 0 on cold boot */
> + kexec_count = kho_in.kexec_count + 1;
> + err |= fdt_property(fdt, KHO_PROP_KEXEC_COUNT, &kexec_count,
> + sizeof(kexec_count));
> + err |= fdt_end_node(fdt);
> + err |= fdt_finish(fdt);
> +
> + if (err) {
> + kho_unpreserve_free(fdt);
> + return err;
> + }
> +
> + err = kho_add_subtree(KHO_HISTORY_NODE_NAME, fdt);
> + if (err) {
> + kho_unpreserve_free(fdt);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> static __init int kho_init(void)
> {
> const void *fdt = kho_get_fdt();
> @@ -1357,6 +1402,10 @@ static __init int kho_init(void)
> if (err)
> goto err_free_fdt;
>
> + err = kho_history_init();
> + if (err)
> + pr_warn("failed to initialize history subtree: %d\n", err);
> +
> if (fdt) {
> kho_in_debugfs_init(&kho_in.dbg, fdt);
> return 0;
> @@ -1425,6 +1474,61 @@ static void __init kho_release_scratch(void)
> }
> }
>
> +static int __init kho_print_previous_kernel(const void *fdt)
> +{
> + const char *prev_release;
> + const u64 *history_phys;
> + const u32 *count_ptr;
> + void *history_fdt;
> + int history_node;
> + int len;
> + int ret;
> +
> + /* Find the history subtree reference in root FDT */
> + history_node = fdt_subnode_offset(fdt, 0, KHO_HISTORY_NODE_NAME);
> + if (history_node < 0)
> + /* This is fine, previous kernel didn't export history */
> + return -ENOENT;
> +
> + /* Get the physical address of the history FDT */
> + history_phys = fdt_getprop(fdt, history_node, KHO_FDT_SUB_TREE_PROP_NAME, &len);
> + if (!history_phys || len != sizeof(*history_phys))
> + return -ENOENT;
> +
> + /* Map the history FDT */
> + history_fdt = early_memremap(*history_phys, PAGE_SIZE);
> + if (!history_fdt)
> + return -ENOMEM;
There is no real reason to call this so early in boot. You can call it
from an initcall or from kho_init(). Then you won't need the
early_memremap(). And you should also not poke into the KHO FDT
directly. Use kho_retrieve_subtree() instead.
> +
> + prev_release = fdt_getprop(history_fdt, 0, KHO_PROP_PREVIOUS_RELEASE, &len);
> + if (!prev_release || len <= 0) {
> + ret = -ENOENT;
> + goto exit;
> + }
> +
> + /* Read the kexec count from the previous kernel */
> + count_ptr = fdt_getprop(history_fdt, 0, KHO_PROP_KEXEC_COUNT, &len);
> + if (WARN_ON(!count_ptr || len != sizeof(u32))) {
> + ret = -ENOENT;
> + goto exit;
> + }
> + /*
> + * This populate the kernel structure that will be persisted during
> + * kernel life time, and the fdt will be unmapped
> + */
> + kho_in.kexec_count = *count_ptr;
This is another annoying thing about FDT. Alignment is only guaranteed
at 32 bits. So you should use get_unaligned() AFAIK.
> +
> + strscpy(kho_in.previous_release, prev_release,
> + sizeof(kho_in.previous_release));
> + pr_info("exec from: %s (count %u)\n", kho_in.previous_release,
> + kho_in.kexec_count);
> +
> + ret = 0;
> +exit:
> + early_memunmap(history_fdt, PAGE_SIZE);
> + return ret;
> +}
> +
> void __init kho_memory_init(void)
> {
> if (kho_in.mem_map_phys) {
> @@ -1513,7 +1617,10 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
> kho_in.scratch_phys = scratch_phys;
> kho_in.mem_map_phys = mem_map_phys;
> kho_scratch_cnt = scratch_cnt;
> - pr_info("found kexec handover data.\n");
> +
> + if (kho_print_previous_kernel(fdt))
> + /* Fallback message when previous kernel info unavailable */
> + pr_info("found kexec handover data.\n");
>
> out:
> if (fdt)
--
Regards,
Pratyush Yadav
Hi,
On Tue, Jan 20, 2026 at 03:40:12PM +0000, Pratyush Yadav wrote:
> On Fri, Jan 16 2026, Breno Leitao wrote:
> >
> > On cold boot, kexec-count starts at 0 and increments with each kexec.
> > The count helps identify issues that only manifest after multiple
> > consecutive kexec reboots.
>
> Very well written changelog!
@Breno, the submission would be perfect if you'd start a new thread rather
than reply to v1 ;-)
> > +/*
> > + * The "history" subtree stores optional metadata about the kexec chain.
> > + * It is registered as a separate FDT via kho_add_subtree(), keeping it
> > + * independent from the core KHO ABI. This allows the history format to
> > + * evolve without affecting other KHO consumers.
> > + *
> > + * The history FDT structure:
>
> I don't have a strong preference here, but you don't _have_ to use FDT.
> For example, with memfd, we moved from FDT to plain C structs during the
> evolution of the patchset. Main reason is that FDT programming is a bit
> annoying. C structs make many things much easier. For example, you can
> always assume a certain property always exists and is of a given size,
> and you don't have to validate every single property you read.
>
> Anyway, I don't mind either way.
Yeah, I agree that a plain C structure with an array of chars and u64 will
make things simpler.
> > + *
> > + * / {
> > + * compatible = "kho-history-v1";
> > + * previous-release = "6.x.y-...";
> > + * kexec-count = <N>;
> > + * };
> > + */
> > +#define KHO_HISTORY_NODE_NAME "history"
>
> Do we want to call it history? Perhaps "kexec-metadata" instead? So we
> could use it for other misc information if needed later.
>
> Mike/Pasha, any thoughts?
I like kexec-metadata.
--
Sincerely yours,
Mike.
© 2016 - 2026 Red Hat, Inc.