[PATCH v2 1/2] kexec: history: track previous kernel version

Breno Leitao posted 2 patches 1 month, 1 week ago
[PATCH v2 1/2] kexec: history: track previous kernel version
Posted by Breno Leitao 1 month, 1 week ago
Add CONFIG_KEXEC_HISTORY to store and display the kernel version from
the previous kexec boot.

When enabled, the current kernel's release string is saved to the
"previous-release" property in the KHO device tree 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.

Disabled by default to avoid overhead for users who don't need this
information.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 kernel/Kconfig.kexec               | 13 +++++++++++++
 kernel/liveupdate/kexec_handover.c | 29 +++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
index 15632358bcf7..b770c68a3800 100644
--- a/kernel/Kconfig.kexec
+++ b/kernel/Kconfig.kexec
@@ -94,6 +94,19 @@ config KEXEC_JUMP
 	  Jump between original kernel and kexeced kernel and invoke
 	  code in physical address mode via KEXEC
 
+config KEXEC_HISTORY
+	bool "Track kexec kernel history"
+	depends on KEXEC_HANDOVER
+	help
+	  When enabled, the kernel will store its release version in the
+	  KHO FDT before kexec, and the newly booted kernel will read and
+	  print this information during early boot.
+
+	  This is useful for debugging and auditing to know which kernel
+	  version performed the kexec that booted the current kernel.
+
+	  If unsure, say N.
+
 config CRASH_DUMP
 	bool "kernel crash dumps"
 	default ARCH_DEFAULT_CRASH_DUMP
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 73da00aeaa99..06d99627bb3c 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -21,6 +21,7 @@
 #include <linux/page-isolation.h>
 #include <linux/unaligned.h>
 #include <linux/vmalloc.h>
+#include <linux/utsname.h>
 
 #include <asm/early_ioremap.h>
 
@@ -36,6 +37,7 @@
 #define KHO_FDT_COMPATIBLE "kho-v1"
 #define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map"
 #define PROP_SUB_FDT "fdt"
+#define PROP_PREVIOUS_RELEASE "previous-release"
 
 #define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */
 
@@ -1253,6 +1255,9 @@ bool kho_finalized(void)
 struct kho_in {
 	phys_addr_t fdt_phys;
 	phys_addr_t scratch_phys;
+#ifdef CONFIG_KEXEC_HISTORY
+	char previous_release[__NEW_UTS_LEN + 1];
+#endif
 	struct kho_debugfs dbg;
 };
 
@@ -1332,6 +1337,10 @@ static __init int kho_out_fdt_setup(void)
 	err |= fdt_property_string(root, "compatible", KHO_FDT_COMPATIBLE);
 	err |= fdt_property(root, PROP_PRESERVED_MEMORY_MAP, &empty_mem_map,
 			    sizeof(empty_mem_map));
+#ifdef CONFIG_KEXEC_HISTORY
+	err |= fdt_property_string(root, PROP_PREVIOUS_RELEASE,
+				   init_uts_ns.name.release);
+#endif
 	err |= fdt_end_node(root);
 	err |= fdt_finish(root);
 
@@ -1455,6 +1464,25 @@ void __init kho_memory_init(void)
 	}
 }
 
+#ifdef CONFIG_KEXEC_HISTORY
+static void __init kho_print_previous_kernel(const void *fdt)
+{
+	const char *prev_release;
+	int len;
+
+	prev_release = fdt_getprop(fdt, 0, PROP_PREVIOUS_RELEASE, &len);
+	if (!prev_release || len <= 0)
+		return;
+
+	strscpy(kho_in.previous_release, prev_release,
+		sizeof(kho_in.previous_release));
+	pr_info("This kernel was kexec'ed from kernel release: %s\n",
+		kho_in.previous_release);
+}
+#else
+static void __init kho_print_previous_kernel(const void *fdt) { }
+#endif
+
 void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
 			 phys_addr_t scratch_phys, u64 scratch_len)
 {
@@ -1527,6 +1555,7 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
 	kho_in.scratch_phys = scratch_phys;
 	kho_scratch_cnt = scratch_cnt;
 	pr_info("found kexec handover data.\n");
+	kho_print_previous_kernel(fdt);
 
 out:
 	if (fdt)

-- 
2.47.3
Re: [PATCH v2 1/2] kexec: history: track previous kernel version
Posted by Pratyush Yadav 1 month, 1 week ago
> Subject: [PATCH v2 1/2] kexec: history: track previous kernel version

Nit: please use the prefix "kho: " for KHO patches.

On Fri, Jan 02 2026, Breno Leitao wrote:

> Add CONFIG_KEXEC_HISTORY to store and display the kernel version from
> the previous kexec boot.
>
> When enabled, the current kernel's release string is saved to the
> "previous-release" property in the KHO device tree 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.

Why can't you use journalctl to figure out which kernel was running
previously?

For example, you can do:

	$ journalctl --list-boots
	-2 a1d93be939b64ac4b7b26aa003bfdb93 Wed 2025-12-24 21:34:22 CET  Thu 2025-12-25 02:04:07 CET
	-1 dbbedcf8fff54d5990c6f2a6399cfce1 Fri 2025-12-26 11:46:23 CET  Fri 2025-12-26 11:46:54 CET
	 0 d002d51c28e748d9a7c6c8cdd391aa73 Fri 2025-12-26 17:40:52 CET  Fri 2026-01-02 21:12:09 CET
	[...]

	$ journalctl -k -b -1

This will show you the full logs of the previous boot, or the one
before, and so on. And when you're debugging, you'd likely want to view
previous boot logs anyway.

Everything you add to the KHO FDT is ABI. While we do have some
flexibility in changing the ABI, I'd rather not add something that can
easily be queried by existing userspace tools.

>
> Disabled by default to avoid overhead for users who don't need this
> information.
>
> Signed-off-by: Breno Leitao <leitao@debian.org>
[...]

-- 
Regards,
Pratyush Yadav
Re: [PATCH v2 1/2] kexec: history: track previous kernel version
Posted by Breno Leitao 1 month ago
Hello Pratyush,

On Fri, Jan 02, 2026 at 09:17:27PM +0100, Pratyush Yadav wrote:
> > Subject: [PATCH v2 1/2] kexec: history: track previous kernel version
> 
> Nit: please use the prefix "kho: " for KHO patches.

ack.

> On Fri, Jan 02 2026, Breno Leitao wrote:
> > Add CONFIG_KEXEC_HISTORY to store and display the kernel version from
> > the previous kexec boot.
> >
> > When enabled, the current kernel's release string is saved to the
> > "previous-release" property in the KHO device tree 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.
> 
> Why can't you use journalctl to figure out which kernel was running
> previously?

This is a good question, this is why this doesn't work for me:

1) in some cases you cannot rely on systemd infrastructure.
   - This is very common when you have linux as the boot loader, which
     basically boot linux (UEFI -> Bootloader/linux -> kexec -> target linux)
   - In these cases, the bootloader doesn't have write access to the
     filesyste/journal
   - This is becoming more and more common. For instance, at Meta, Linux
     is the default bootloader.

2) in some of the bugs I've listed earlier, the machine doesn't even get
   to userspace before the crash. For instance, in the bug fixed by
   commit 77d48d39e991 ("efistub/tpm: Use ACPI reclaim memory for event
   log to avoid corruption"), the kernel was not reach userspace/init,
   thus, it would not be possible to run journalctl.

Thanks for the review,
--breno
Re: [PATCH v2 1/2] kexec: history: track previous kernel version
Posted by Pratyush Yadav 3 weeks, 4 days ago
On Mon, Jan 05 2026, Breno Leitao wrote:

> Hello Pratyush,
>
> On Fri, Jan 02, 2026 at 09:17:27PM +0100, Pratyush Yadav wrote:
>> > Subject: [PATCH v2 1/2] kexec: history: track previous kernel version
>> 
>> Nit: please use the prefix "kho: " for KHO patches.
>
> ack.
>
>> On Fri, Jan 02 2026, Breno Leitao wrote:
>> > Add CONFIG_KEXEC_HISTORY to store and display the kernel version from
>> > the previous kexec boot.
>> >
>> > When enabled, the current kernel's release string is saved to the
>> > "previous-release" property in the KHO device tree 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.
>> 
>> Why can't you use journalctl to figure out which kernel was running
>> previously?
>
> This is a good question, this is why this doesn't work for me:
>
> 1) in some cases you cannot rely on systemd infrastructure.
>    - This is very common when you have linux as the boot loader, which
>      basically boot linux (UEFI -> Bootloader/linux -> kexec -> target linux)
>    - In these cases, the bootloader doesn't have write access to the
>      filesyste/journal
>    - This is becoming more and more common. For instance, at Meta, Linux
>      is the default bootloader.
>
> 2) in some of the bugs I've listed earlier, the machine doesn't even get
>    to userspace before the crash. For instance, in the bug fixed by
>    commit 77d48d39e991 ("efistub/tpm: Use ACPI reclaim memory for event
>    log to avoid corruption"), the kernel was not reach userspace/init,
>    thus, it would not be possible to run journalctl.

Ideally, you should have external ways to track the kernel history of
each machine in your fleet.

But I can see that it might not always exist so I can understand the use
case.

I have some comments on the implementation though. I'll reply on the
latest posting.

-- 
Regards,
Pratyush Yadav
Re: [PATCH v2 1/2] kexec: history: track previous kernel version
Posted by Pasha Tatashin 1 month, 1 week ago
On Fri, Jan 2, 2026 at 9:53 AM Breno Leitao <leitao@debian.org> wrote:
>
> Add CONFIG_KEXEC_HISTORY to store and display the kernel version from
> the previous kexec boot.

I do not think we need a config. Let's just enable this by default, as
I suggested in RFC.

>
> When enabled, the current kernel's release string is saved to the
> "previous-release" property in the KHO device tree 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.
>
> Disabled by default to avoid overhead for users who don't need this
> information.
>
> Signed-off-by: Breno Leitao <leitao@debian.org>
> ---
>  kernel/Kconfig.kexec               | 13 +++++++++++++
>  kernel/liveupdate/kexec_handover.c | 29 +++++++++++++++++++++++++++++
>  2 files changed, 42 insertions(+)
>
> diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
> index 15632358bcf7..b770c68a3800 100644
> --- a/kernel/Kconfig.kexec
> +++ b/kernel/Kconfig.kexec
> @@ -94,6 +94,19 @@ config KEXEC_JUMP
>           Jump between original kernel and kexeced kernel and invoke
>           code in physical address mode via KEXEC
>
> +config KEXEC_HISTORY
> +       bool "Track kexec kernel history"
> +       depends on KEXEC_HANDOVER
> +       help
> +         When enabled, the kernel will store its release version in the
> +         KHO FDT before kexec, and the newly booted kernel will read and
> +         print this information during early boot.
> +
> +         This is useful for debugging and auditing to know which kernel
> +         version performed the kexec that booted the current kernel.
> +
> +         If unsure, say N.
> +
>  config CRASH_DUMP
>         bool "kernel crash dumps"
>         default ARCH_DEFAULT_CRASH_DUMP
> diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
> index 73da00aeaa99..06d99627bb3c 100644
> --- a/kernel/liveupdate/kexec_handover.c
> +++ b/kernel/liveupdate/kexec_handover.c
> @@ -21,6 +21,7 @@
>  #include <linux/page-isolation.h>
>  #include <linux/unaligned.h>
>  #include <linux/vmalloc.h>
> +#include <linux/utsname.h>
>
>  #include <asm/early_ioremap.h>
>
> @@ -36,6 +37,7 @@
>  #define KHO_FDT_COMPATIBLE "kho-v1"
>  #define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map"
>  #define PROP_SUB_FDT "fdt"
> +#define PROP_PREVIOUS_RELEASE "previous-release"
>
>  #define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */
>
> @@ -1253,6 +1255,9 @@ bool kho_finalized(void)
>  struct kho_in {
>         phys_addr_t fdt_phys;
>         phys_addr_t scratch_phys;
> +#ifdef CONFIG_KEXEC_HISTORY
> +       char previous_release[__NEW_UTS_LEN + 1];
> +#endif
>         struct kho_debugfs dbg;
>  };
>
> @@ -1332,6 +1337,10 @@ static __init int kho_out_fdt_setup(void)
>         err |= fdt_property_string(root, "compatible", KHO_FDT_COMPATIBLE);
>         err |= fdt_property(root, PROP_PRESERVED_MEMORY_MAP, &empty_mem_map,
>                             sizeof(empty_mem_map));
> +#ifdef CONFIG_KEXEC_HISTORY
> +       err |= fdt_property_string(root, PROP_PREVIOUS_RELEASE,
> +                                  init_uts_ns.name.release);
> +#endif
>         err |= fdt_end_node(root);
>         err |= fdt_finish(root);
>
> @@ -1455,6 +1464,25 @@ void __init kho_memory_init(void)
>         }
>  }
>
> +#ifdef CONFIG_KEXEC_HISTORY
> +static void __init kho_print_previous_kernel(const void *fdt)
> +{
> +       const char *prev_release;
> +       int len;
> +
> +       prev_release = fdt_getprop(fdt, 0, PROP_PREVIOUS_RELEASE, &len);
> +       if (!prev_release || len <= 0)
> +               return;
> +
> +       strscpy(kho_in.previous_release, prev_release,
> +               sizeof(kho_in.previous_release));
> +       pr_info("This kernel was kexec'ed from kernel release: %s\n",
> +               kho_in.previous_release);

May be reduce the message slightly:
i.e. "kexec from: %s\n, kho_in.previous_release"

> +}
> +#else
> +static void __init kho_print_previous_kernel(const void *fdt) { }
> +#endif
> +
>  void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
>                          phys_addr_t scratch_phys, u64 scratch_len)
>  {
> @@ -1527,6 +1555,7 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
>         kho_in.scratch_phys = scratch_phys;
>         kho_scratch_cnt = scratch_cnt;
>         pr_info("found kexec handover data.\n");

This message is not needed if the previous kernel version is always printed.

> +       kho_print_previous_kernel(fdt);
>
>  out:
>         if (fdt)
>
> --
> 2.47.3
>
Re: [PATCH v2 1/2] kexec: history: track previous kernel version
Posted by Usama Arif 1 month, 1 week ago

On 02/01/2026 17:53, Breno Leitao wrote:
> Add CONFIG_KEXEC_HISTORY to store and display the kernel version from
> the previous kexec boot.
> 
> When enabled, the current kernel's release string is saved to the
> "previous-release" property in the KHO device tree 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.
> 
> Disabled by default to avoid overhead for users who don't need this
> information.
> 
> Signed-off-by: Breno Leitao <leitao@debian.org>
> ---
>  kernel/Kconfig.kexec               | 13 +++++++++++++
>  kernel/liveupdate/kexec_handover.c | 29 +++++++++++++++++++++++++++++
>  2 files changed, 42 insertions(+)
> 
> diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
> index 15632358bcf7..b770c68a3800 100644
> --- a/kernel/Kconfig.kexec
> +++ b/kernel/Kconfig.kexec
> @@ -94,6 +94,19 @@ config KEXEC_JUMP
>  	  Jump between original kernel and kexeced kernel and invoke
>  	  code in physical address mode via KEXEC
>  
> +config KEXEC_HISTORY
> +	bool "Track kexec kernel history"
> +	depends on KEXEC_HANDOVER
> +	help
> +	  When enabled, the kernel will store its release version in the
> +	  KHO FDT before kexec, and the newly booted kernel will read and
> +	  print this information during early boot.
> +
> +	  This is useful for debugging and auditing to know which kernel
> +	  version performed the kexec that booted the current kernel.
> +
> +	  If unsure, say N.
> +


I think we should make this default if KHO is enabled, i.e. not have a Kconfig
option for this. The cost of storing the char array is negligable.

>  config CRASH_DUMP
>  	bool "kernel crash dumps"
>  	default ARCH_DEFAULT_CRASH_DUMP
> diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
> index 73da00aeaa99..06d99627bb3c 100644
> --- a/kernel/liveupdate/kexec_handover.c
> +++ b/kernel/liveupdate/kexec_handover.c
> @@ -21,6 +21,7 @@
>  #include <linux/page-isolation.h>
>  #include <linux/unaligned.h>
>  #include <linux/vmalloc.h>
> +#include <linux/utsname.h>
>  
>  #include <asm/early_ioremap.h>
>  
> @@ -36,6 +37,7 @@
>  #define KHO_FDT_COMPATIBLE "kho-v1"
>  #define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map"
>  #define PROP_SUB_FDT "fdt"
> +#define PROP_PREVIOUS_RELEASE "previous-release"
>  
>  #define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */
>  
> @@ -1253,6 +1255,9 @@ bool kho_finalized(void)
>  struct kho_in {
>  	phys_addr_t fdt_phys;
>  	phys_addr_t scratch_phys;
> +#ifdef CONFIG_KEXEC_HISTORY
> +	char previous_release[__NEW_UTS_LEN + 1];
> +#endif
>  	struct kho_debugfs dbg;
>  };
>  
> @@ -1332,6 +1337,10 @@ static __init int kho_out_fdt_setup(void)
>  	err |= fdt_property_string(root, "compatible", KHO_FDT_COMPATIBLE);
>  	err |= fdt_property(root, PROP_PRESERVED_MEMORY_MAP, &empty_mem_map,
>  			    sizeof(empty_mem_map));
> +#ifdef CONFIG_KEXEC_HISTORY
> +	err |= fdt_property_string(root, PROP_PREVIOUS_RELEASE,
> +				   init_uts_ns.name.release);
> +#endif
>  	err |= fdt_end_node(root);
>  	err |= fdt_finish(root);
>  
> @@ -1455,6 +1464,25 @@ void __init kho_memory_init(void)
>  	}
>  }
>  
> +#ifdef CONFIG_KEXEC_HISTORY
> +static void __init kho_print_previous_kernel(const void *fdt)
> +{
> +	const char *prev_release;
> +	int len;
> +
> +	prev_release = fdt_getprop(fdt, 0, PROP_PREVIOUS_RELEASE, &len);
> +	if (!prev_release || len <= 0)
> +		return;
> +
> +	strscpy(kho_in.previous_release, prev_release,
> +		sizeof(kho_in.previous_release));
> +	pr_info("This kernel was kexec'ed from kernel release: %s\n",
> +		kho_in.previous_release);

Maybe s/release/version everywhere? It might not be a release, but no strong opinion.

> +}
> +#else
> +static void __init kho_print_previous_kernel(const void *fdt) { }
> +#endif
> +
>  void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
>  			 phys_addr_t scratch_phys, u64 scratch_len)
>  {
> @@ -1527,6 +1555,7 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len,
>  	kho_in.scratch_phys = scratch_phys;
>  	kho_scratch_cnt = scratch_cnt;
>  	pr_info("found kexec handover data.\n");
> +	kho_print_previous_kernel(fdt);
>  
>  out:
>  	if (fdt)
>
Re: [PATCH v2 1/2] kexec: history: track previous kernel version
Posted by Breno Leitao 1 month, 1 week ago
Hello Usama,

On Fri, Jan 02, 2026 at 06:02:39PM +0300, Usama Arif wrote:
> On 02/01/2026 17:53, Breno Leitao wrote:
> I think we should make this default if KHO is enabled, i.e. not have a Kconfig
> option for this. The cost of storing the char array is negligable.

Sure, I can get it enabled by default once KHO gets enabled. Thanks for
the feedback.

> > +	pr_info("This kernel was kexec'ed from kernel release: %s\n",
> > +		kho_in.previous_release);
> 
> Maybe s/release/version everywhere? It might not be a release, but no strong opinion.

As I understand, in the kernel parlance, "version" is something
different from "release", and what we want here is "release". Here is an
example of uname, which also matches with kernel source code and UTS.

  # uname --kernel-version
  #1 SMP Mon Nov 17 07:00:42 PST 2025

  # uname --kernel-release
  6.16.1-0_gc0739ee5037a

Thanks for the review,
--breno
Re: [PATCH v2 1/2] kexec: history: track previous kernel version
Posted by Usama Arif 1 month, 1 week ago

On 02/01/2026 18:14, Breno Leitao wrote:
> Hello Usama,
> 
> On Fri, Jan 02, 2026 at 06:02:39PM +0300, Usama Arif wrote:
>> On 02/01/2026 17:53, Breno Leitao wrote:
>> I think we should make this default if KHO is enabled, i.e. not have a Kconfig
>> option for this. The cost of storing the char array is negligable.
> 
> Sure, I can get it enabled by default once KHO gets enabled. Thanks for
> the feedback.
> 
>>> +	pr_info("This kernel was kexec'ed from kernel release: %s\n",
>>> +		kho_in.previous_release);
>>
>> Maybe s/release/version everywhere? It might not be a release, but no strong opinion.
> 
> As I understand, in the kernel parlance, "version" is something
> different from "release", and what we want here is "release". Here is an
> example of uname, which also matches with kernel source code and UTS.
> 
>   # uname --kernel-version
>   #1 SMP Mon Nov 17 07:00:42 PST 2025
> 
>   # uname --kernel-release
>   6.16.1-0_gc0739ee5037a
>

ack