Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/kexec: carry forward the boot DTB on kexec

Currently, the kexec_file_load syscall on x86 does not support passing a
device tree blob to the new kernel. Some embedded x86 systems use device
trees. On these systems, failing to pass a device tree to the new kernel
causes a boot failure.

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.

This behavior is gated behind the KEXEC_FILE_FORCE_DTB flag.

Link: https://lkml.kernel.org/r/20250805211527.122367-3-makb@juniper.net
Signed-off-by: Brian Mak <makb@juniper.net>
Cc: Alexander Graf <graf@amazon.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Borislav Betkov <bp@alien8.de>
Cc: Dave Young <dyoung@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Thomas Gleinxer <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Brian Mak and committed by
Andrew Morton
f367474b 1440648c

+53 -4
+44 -3
arch/x86/kernel/kexec-bzimage64.c
··· 16 16 #include <linux/kexec.h> 17 17 #include <linux/kernel.h> 18 18 #include <linux/mm.h> 19 + #include <linux/libfdt.h> 20 + #include <linux/of_fdt.h> 19 21 #include <linux/efi.h> 20 22 #include <linux/random.h> 21 23 ··· 214 212 } 215 213 #endif /* CONFIG_EFI */ 216 214 215 + #ifdef CONFIG_OF_FLATTREE 216 + static void setup_dtb(struct boot_params *params, 217 + unsigned long params_load_addr, 218 + unsigned int dtb_setup_data_offset) 219 + { 220 + struct setup_data *sd = (void *)params + dtb_setup_data_offset; 221 + unsigned long setup_data_phys, dtb_len; 222 + 223 + dtb_len = fdt_totalsize(initial_boot_params); 224 + sd->type = SETUP_DTB; 225 + sd->len = dtb_len; 226 + 227 + /* Carry over current boot DTB with setup_data */ 228 + memcpy(sd->data, initial_boot_params, dtb_len); 229 + 230 + /* Add setup data */ 231 + setup_data_phys = params_load_addr + dtb_setup_data_offset; 232 + sd->next = params->hdr.setup_data; 233 + params->hdr.setup_data = setup_data_phys; 234 + } 235 + #endif /* CONFIG_OF_FLATTREE */ 236 + 217 237 static void 218 238 setup_ima_state(const struct kimage *image, struct boot_params *params, 219 239 unsigned long params_load_addr, ··· 358 334 setup_data_offset); 359 335 setup_data_offset += sizeof(struct setup_data) + 360 336 sizeof(struct efi_setup_data); 337 + #endif 338 + 339 + #ifdef CONFIG_OF_FLATTREE 340 + if (image->force_dtb && initial_boot_params) { 341 + setup_dtb(params, params_load_addr, setup_data_offset); 342 + setup_data_offset += sizeof(struct setup_data) + 343 + fdt_totalsize(initial_boot_params); 344 + } else { 345 + pr_debug("Not carrying over DTB, force_dtb = %d\n", 346 + image->force_dtb); 347 + } 361 348 #endif 362 349 363 350 if (IS_ENABLED(CONFIG_IMA_KEXEC)) { ··· 564 529 sizeof(struct setup_data) + 565 530 RNG_SEED_LENGTH; 566 531 532 + #ifdef CONFIG_OF_FLATTREE 533 + if (image->force_dtb && initial_boot_params) 534 + kbuf.bufsz += sizeof(struct setup_data) + 535 + fdt_totalsize(initial_boot_params); 536 + #endif 537 + 567 538 if (IS_ENABLED(CONFIG_IMA_KEXEC)) 568 539 kbuf.bufsz += sizeof(struct setup_data) + 569 540 sizeof(struct ima_setup_data); ··· 578 537 kbuf.bufsz += sizeof(struct setup_data) + 579 538 sizeof(struct kho_data); 580 539 581 - params = kzalloc(kbuf.bufsz, GFP_KERNEL); 540 + params = kvzalloc(kbuf.bufsz, GFP_KERNEL); 582 541 if (!params) 583 542 return ERR_PTR(-ENOMEM); 584 543 efi_map_offset = params_cmdline_sz; ··· 688 647 return ldata; 689 648 690 649 out_free_params: 691 - kfree(params); 650 + kvfree(params); 692 651 return ERR_PTR(ret); 693 652 } 694 653 ··· 700 659 if (!ldata) 701 660 return 0; 702 661 703 - kfree(ldata->bootparams_buf); 662 + kvfree(ldata->bootparams_buf); 704 663 ldata->bootparams_buf = NULL; 705 664 706 665 return 0;
+4 -1
include/linux/kexec.h
··· 395 395 396 396 /* Information for loading purgatory */ 397 397 struct purgatory_info purgatory_info; 398 + 399 + /* Force carrying over the DTB from the current boot */ 400 + bool force_dtb; 398 401 #endif 399 402 400 403 #ifdef CONFIG_CRASH_HOTPLUG ··· 464 461 /* List of defined/legal kexec file flags */ 465 462 #define KEXEC_FILE_FLAGS (KEXEC_FILE_UNLOAD | KEXEC_FILE_ON_CRASH | \ 466 463 KEXEC_FILE_NO_INITRAMFS | KEXEC_FILE_DEBUG | \ 467 - KEXEC_FILE_NO_CMA) 464 + KEXEC_FILE_NO_CMA | KEXEC_FILE_FORCE_DTB) 468 465 469 466 /* flag to track if kexec reboot is in progress */ 470 467 extern bool kexec_in_progress;
+4
include/uapi/linux/kexec.h
··· 22 22 * KEXEC_FILE_ON_CRASH : Load/unload operation belongs to kdump image. 23 23 * KEXEC_FILE_NO_INITRAMFS : No initramfs is being loaded. Ignore the initrd 24 24 * fd field. 25 + * KEXEC_FILE_FORCE_DTB : Force carrying over the current boot's DTB to the new 26 + * kernel on x86. This is already the default behavior on 27 + * some other architectures, like ARM64 and PowerPC. 25 28 */ 26 29 #define KEXEC_FILE_UNLOAD 0x00000001 27 30 #define KEXEC_FILE_ON_CRASH 0x00000002 28 31 #define KEXEC_FILE_NO_INITRAMFS 0x00000004 29 32 #define KEXEC_FILE_DEBUG 0x00000008 30 33 #define KEXEC_FILE_NO_CMA 0x00000010 34 + #define KEXEC_FILE_FORCE_DTB 0x00000020 31 35 32 36 /* These values match the ELF architecture values. 33 37 * Unless there is a good reason that should continue to be the case.
+1
kernel/kexec_file.c
··· 255 255 } 256 256 257 257 image->no_cma = !!(flags & KEXEC_FILE_NO_CMA); 258 + image->force_dtb = flags & KEXEC_FILE_FORCE_DTB; 258 259 259 260 if (cmdline_len) { 260 261 image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len);