Skip to contentSkip to conversation

x86/kexec: Carry forward the boot DTB on kexec

PATCH RESEND Superseded Brian Mak 3 participants 9 messages Jul 29, 2025 – Aug 5, 2025

Subsystems (3)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Brian Mak

author
#0 lore raw

PATCH RESEND 1/1 x86/kexec: Carry forward the boot DTB on kexec

The kexec_file_load syscall on x86 currently does not support passing a device tree blob to the new kernel.

To add support for this, we copy the behavior of ARM64 and PowerPC and copy the current boot's device tree blob for use in the new kernel. We do this on x86 by passing the device tree blob as a setup_data entry in accordance with the x86 boot protocol.

---
 arch/x86/kernel/kexec-bzimage64.c | 46 +++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 3 deletions(-)
arch/x86/kernel/kexec-bzimage64.c +43 -3
index 24a41f0e0cf1..c24536c25f98 100644
@@ -16,6 +16,8 @@
 #include <linux/kexec.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/libfdt.h>
+#include <linux/of_fdt.h>
 #include <linux/efi.h>
 #include <linux/random.h>
 
@@ -212,6 +214,28 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
 }
 #endif /* CONFIG_EFI */
 
+#ifdef CONFIG_OF_FLATTREE
+static void setup_dtb(struct boot_params *params,
+		      unsigned long params_load_addr,
+		      unsigned int dtb_setup_data_offset)
+{
+	struct setup_data *sd = (void *)params + dtb_setup_data_offset;
+	unsigned long setup_data_phys, dtb_len;
+
+	dtb_len = fdt_totalsize(initial_boot_params);
+	sd->type = SETUP_DTB;
+	sd->len = dtb_len;
+
+	/* Carry over current boot DTB with setup_data */
+	memcpy(sd->data, initial_boot_params, dtb_len);
+
+	/* Add setup data */
+	setup_data_phys = params_load_addr + dtb_setup_data_offset;
+	sd->next = params->hdr.setup_data;
+	params->hdr.setup_data = setup_data_phys;
+}
+#endif /* CONFIG_OF_FLATTREE */
+
 static void
 setup_ima_state(const struct kimage *image, struct boot_params *params,
 		unsigned long params_load_addr,
@@ -336,6 +360,16 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
 			sizeof(struct efi_setup_data);
 #endif
 
+#ifdef CONFIG_OF_FLATTREE
+	if (initial_boot_params) {
+		setup_dtb(params, params_load_addr, setup_data_offset);
+		setup_data_offset += sizeof(struct setup_data) +
+				     fdt_totalsize(initial_boot_params);
+	} else {
+		pr_info("No DTB\n");
+	}
+#endif
+
 	if (IS_ENABLED(CONFIG_IMA_KEXEC)) {
 		/* Setup IMA log buffer state */
 		setup_ima_state(image, params, params_load_addr,
@@ -529,6 +563,12 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
 				sizeof(struct setup_data) +
 				RNG_SEED_LENGTH;
 
+#ifdef CONFIG_OF_FLATTREE
+	if (initial_boot_params)
+		kbuf.bufsz += sizeof(struct setup_data) +
+			      fdt_totalsize(initial_boot_params);
+#endif
+
 	if (IS_ENABLED(CONFIG_IMA_KEXEC))
 		kbuf.bufsz += sizeof(struct setup_data) +
 			      sizeof(struct ima_setup_data);
@@ -537,7 +577,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
 		kbuf.bufsz += sizeof(struct setup_data) +
 			      sizeof(struct kho_data);
 
-	params = kzalloc(kbuf.bufsz, GFP_KERNEL);
+	params = kvzalloc(kbuf.bufsz, GFP_KERNEL);
 	if (!params)
 		return ERR_PTR(-ENOMEM);
 	efi_map_offset = params_cmdline_sz;
@@ -647,7 +687,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
 	return ldata;
 
 out_free_params:
-	kfree(params);
+	kvfree(params);
 	return ERR_PTR(ret);
 }
 
@@ -659,7 +699,7 @@ static int bzImage64_cleanup(void *loader_data)
 	if (!ldata)
 		return 0;
 
-	kfree(ldata->bootparams_buf);
+	kvfree(ldata->bootparams_buf);
 	ldata->bootparams_buf = NULL;
 
 	return 0;

base-commit: d7b8f8e20813f0179d8ef519541a3527e7661d3a

Signature · 1 line
2.25.1
#1 lore raw
Quoted 56 lines · Brian Mak

The kexec_file_load syscall on x86 currently does not support passing a device tree blob to the new kernel.

To add support for this, we copy the behavior of ARM64 and PowerPC and copy the current boot's device tree blob for use in the new kernel. We do this on x86 by passing the device tree blob as a setup_data entry in accordance with the x86 boot protocol.

---
 arch/x86/kernel/kexec-bzimage64.c | 46 +++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 3 deletions(-)
arch/x86/kernel/kexec-bzimage64.c +24 -0
index 24a41f0e0cf1..c24536c25f98 100644
@@ -16,6 +16,8 @@
 #include <linux/kexec.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/libfdt.h>
+#include <linux/of_fdt.h>
 #include <linux/efi.h>
 #include <linux/random.h>
 
@@ -212,6 +214,28 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
 }
 #endif /* CONFIG_EFI */
 
+#ifdef CONFIG_OF_FLATTREE
+static void setup_dtb(struct boot_params *params,
+		      unsigned long params_load_addr,
+		      unsigned int dtb_setup_data_offset)
+{
+	struct setup_data *sd = (void *)params + dtb_setup_data_offset;
+	unsigned long setup_data_phys, dtb_len;
+
+	dtb_len = fdt_totalsize(initial_boot_params);
+	sd->type = SETUP_DTB;
+	sd->len = dtb_len;
+
+	/* Carry over current boot DTB with setup_data */
+	memcpy(sd->data, initial_boot_params, dtb_len);
+
+	/* Add setup data */
+	setup_data_phys = params_load_addr + dtb_setup_data_offset;
+	sd->next = params->hdr.setup_data;
+	params->hdr.setup_data = setup_data_phys;
+}
+#endif /* CONFIG_OF_FLATTREE */
+
 static void
 setup_ima_state(const struct kimage *image, struct boot_params *params,
 		unsigned long params_load_addr,

arch/x86/kernel/kexec-bzimage64.c setup_boot_parameters(struct kimage *image, struct boot_params *params, · line 367

Quoted 8 lines
arch/x86/kernel/kexec-bzimage64.c +5 -0
@@ -336,6 +360,16 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
 			sizeof(struct efi_setup_data);
 #endif
 
+#ifdef CONFIG_OF_FLATTREE
+	if (initial_boot_params) {
+		setup_dtb(params, params_load_addr, setup_data_offset);
+		setup_data_offset += sizeof(struct setup_data) +
+				     fdt_totalsize(initial_boot_params);

I suppose current boot dtb should be valid for the current runnning kernel, if you use kexec to load another kernel the next kexec reboot could fail due to unmatching dtb.

Make this unconditionally could break the previous working kexec?

arch/x86/kernel/kexec-bzimage64.c setup_boot_parameters(struct kimage *image, struct boot_params *params, · line 369

Quoted 2 lines
arch/x86/kernel/kexec-bzimage64.c +2 -0
+	} else {
+		pr_info("No DTB\n");

pr_debug sounds better.

Quoted 47 lines
arch/x86/kernel/kexec-bzimage64.c +12 -3
+	}
+#endif
+
 	if (IS_ENABLED(CONFIG_IMA_KEXEC)) {
 		/* Setup IMA log buffer state */
 		setup_ima_state(image, params, params_load_addr,
@@ -529,6 +563,12 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
 				sizeof(struct setup_data) +
 				RNG_SEED_LENGTH;
 
+#ifdef CONFIG_OF_FLATTREE
+	if (initial_boot_params)
+		kbuf.bufsz += sizeof(struct setup_data) +
+			      fdt_totalsize(initial_boot_params);
+#endif
+
 	if (IS_ENABLED(CONFIG_IMA_KEXEC))
 		kbuf.bufsz += sizeof(struct setup_data) +
 			      sizeof(struct ima_setup_data);
@@ -537,7 +577,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
 		kbuf.bufsz += sizeof(struct setup_data) +
 			      sizeof(struct kho_data);
 
-	params = kzalloc(kbuf.bufsz, GFP_KERNEL);
+	params = kvzalloc(kbuf.bufsz, GFP_KERNEL);
 	if (!params)
 		return ERR_PTR(-ENOMEM);
 	efi_map_offset = params_cmdline_sz;
@@ -647,7 +687,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
 	return ldata;
 
 out_free_params:
-	kfree(params);
+	kvfree(params);
 	return ERR_PTR(ret);
 }
 
@@ -659,7 +699,7 @@ static int bzImage64_cleanup(void *loader_data)
 	if (!ldata)
 		return 0;
 
-	kfree(ldata->bootparams_buf);
+	kvfree(ldata->bootparams_buf);
 	ldata->bootparams_buf = NULL;
 
 	return 0;
Quoted 4 lines

base-commit: d7b8f8e20813f0179d8ef519541a3527e7661d3a

Signature · 1 line
2.25.1

Thanks
Dave

author
#2 lore raw

On Jul 30, 2025, at 12:31 AM, Dave Young <[email protected]> wrote

Quoted 11 lines

arch/x86/kernel/kexec-bzimage64.c setup_boot_parameters(struct kimage *image, struct boot_params *params, · line 367

Quoted 5 lines
arch/x86/kernel/kexec-bzimage64.c +5 -0
+#ifdef CONFIG_OF_FLATTREE
+     if (initial_boot_params) {
+             setup_dtb(params, params_load_addr, setup_data_offset);
+             setup_data_offset += sizeof(struct setup_data) +
+                                  fdt_totalsize(initial_boot_params);

I suppose current boot dtb should be valid for the current runnning kernel, if you use kexec to load another kernel the next kexec reboot could fail due to unmatching dtb.

Make this unconditionally could break the previous working kexec?

Hi Dave,

Thanks for taking the time to look at this change!

The behavior on ARM64 for carrying over the current boot DTB is unconditional, which is why I've made it unconditional here as well. I'm open to suggestions on this though. Realistically, would there be a case where having no DTB wouldn't break, but carrying over the current DTB would?

Quoted 5 lines

arch/x86/kernel/kexec-bzimage64.c setup_boot_parameters(struct kimage *image, struct boot_params *params, · line 369

Quoted 3 lines
arch/x86/kernel/kexec-bzimage64.c +2 -0
+     } else {
+             pr_info("No DTB\n");

pr_debug sounds better.

Agreed. I'll raise it in a v2 in a few days if there's no other comments.

Thanks,
Brian

#3 lore raw

Hi Brian,

Quoted 17 lines · Brian Mak

On Jul 30, 2025, at 12:31 AM, Dave Young <[email protected]> wrote

Quoted 11 lines

arch/x86/kernel/kexec-bzimage64.c setup_boot_parameters(struct kimage *image, struct boot_params *params, · line 367

Quoted 5 lines
arch/x86/kernel/kexec-bzimage64.c +5 -0
+#ifdef CONFIG_OF_FLATTREE
+     if (initial_boot_params) {
+             setup_dtb(params, params_load_addr, setup_data_offset);
+             setup_data_offset += sizeof(struct setup_data) +
+                                  fdt_totalsize(initial_boot_params);

I suppose current boot dtb should be valid for the current runnning kernel, if you use kexec to load another kernel the next kexec reboot could fail due to unmatching dtb.

Make this unconditionally could break the previous working kexec?

Hi Dave,

Quoted 7 lines

Thanks for taking the time to look at this change!

The behavior on ARM64 for carrying over the current boot DTB is unconditional, which is why I've made it unconditional here as well. I'm open to suggestions on this though. Realistically, would there be a case where having no DTB wouldn't break, but carrying over the current DTB would?

I worry about it since dtb is for providing boot related information, weird things could happen if the kernel versions are different.

About arm64 and powerpc I think maybe just nobody noticed this problem. IMO it is wrong as kexec is designed to load different kernels not limited to current running kernels.

Otherwise the current kexec_file_load syscall only supports passing kernel_fd, initrd_fd and cmdline, no extra param designed for dtb, I don't know if there are other ways via attaching dtb to kernel or initrd.

Probably you can try to add a new flag to the kexec_file_load syscall,
when this flag is set 1 then use the current dtb, otherwise do
nothing. For arm64 and power, ideally doing the same is better, but I'm not sure if we should change the old behavior, maybe they can go with default to load the dtb, but users can choose not to do that.

Thanks
Dave

author
#4 lore raw
Quoted 12 lines · Dave Young

I worry about it since dtb is for providing boot related information, weird things could happen if the kernel versions are different.

About arm64 and powerpc I think maybe just nobody noticed this problem. IMO it is wrong as kexec is designed to load different kernels not limited to current running kernels.

Otherwise the current kexec_file_load syscall only supports passing kernel_fd, initrd_fd and cmdline, no extra param designed for dtb, I don't know if there are other ways via attaching dtb to kernel or initrd.

Quoted 5 lines

Probably you can try to add a new flag to the kexec_file_load syscall, when this flag is set 1 then use the current dtb, otherwise do nothing. For arm64 and power, ideally doing the same is better, but I'm not sure if we should change the old behavior, maybe they can go with default to load the dtb, but users can choose not to do that.

Hi Dave,

Since we're changing the default behavior on x86 by carrying over the current DTB, and this could potentially affect users, maybe it'd be best to put this behavior behind a kexec_file_load flag, as you suggested.

I'll take this up in v2 if no one else has any objections to this.

For backward compatibility reasons, changing the current behavior on ARM64 and PPC probably isn't advisable, so we'd probably want to default to loading the boot DTB, and users can choose not to do so. I won't take that up in this patchset though. That can be taken up in the future.

Thanks,
Brian

#5 lore raw
In reply to Brian Mak
Quoted 7 lines · Brian Mak

The kexec_file_load syscall on x86 currently does not support passing a device tree blob to the new kernel.

To add support for this, we copy the behavior of ARM64 and PowerPC and copy the current boot's device tree blob for use in the new kernel. We do this on x86 by passing the device tree blob as a setup_data entry in accordance with the x86 boot protocol.

I see how, but no why. Why do we need to add DTB for x86?

author
#6 lore raw
Quoted 10 lines · Baoquan He
Quoted 7 lines · Brian Mak

The kexec_file_load syscall on x86 currently does not support passing a device tree blob to the new kernel.

To add support for this, we copy the behavior of ARM64 and PowerPC and copy the current boot's device tree blob for use in the new kernel. We do this on x86 by passing the device tree blob as a setup_data entry in accordance with the x86 boot protocol.

I see how, but no why. Why do we need to add DTB for x86?

Hi Baoquan,

Thanks for your comment. Some embedded x86 systems, such as ours at Juniper, use device trees. Currently, the x86 kexec_file_load syscall has no support for passing the device tree to the new kernel, which is a problem for us since we use kexec for some software upgrade related features. Not passing a device tree to the new kernel would cause a boot failure here.

Thanks,
Brian

#7 lore raw
Quoted 13 lines · Brian Mak
Quoted 10 lines · Baoquan He
Quoted 7 lines · Brian Mak

The kexec_file_load syscall on x86 currently does not support passing a device tree blob to the new kernel.

To add support for this, we copy the behavior of ARM64 and PowerPC and copy the current boot's device tree blob for use in the new kernel. We do this on x86 by passing the device tree blob as a setup_data entry in accordance with the x86 boot protocol.

I see how, but no why. Why do we need to add DTB for x86?

Quoted 8 lines

Hi Baoquan,

Thanks for your comment. Some embedded x86 systems, such as ours at Juniper, use device trees. Currently, the x86 kexec_file_load syscall has no support for passing the device tree to the new kernel, which is a problem for us since we use kexec for some software upgrade related features. Not passing a device tree to the new kernel would cause a boot failure here.

Thanks for the info. Please add these into patch log to let reviewers know them. We don't add code w/o objective.