History

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.

Brian Mak committed 1 year ago

Commit f3674741 parent 14406484 files · +53 −4Browse files

Unified · Split

4 files
  1. arch/x86/kernel/kexec-bzimage64.c +44 −3
  2. include/linux/kexec.h +4 −1
  3. include/uapi/linux/kexec.h +4 −0
  4. kernel/kexec_file.c +1 −0
arch/x86/kernel/kexec-bzimage64.c+44−3
@@ -16,6 +16,8 @@ · 15 unmodified lines
1616 #include <linux/kexec.h>
1717 #include <linux/kernel.h>
1818 #include <linux/mm.h>
19+#include <linux/libfdt.h>
20+#include <linux/of_fdt.h>
1921 #include <linux/efi.h>
2022 #include <linux/random.h>
2123
@@ -212,6 +214,28 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr, · 190 unmodified lines
212214 }
213215 #endif /* CONFIG_EFI */
214216
217+#ifdef CONFIG_OF_FLATTREE
218+static void setup_dtb(struct boot_params *params,
219+ unsigned long params_load_addr,
220+ unsigned int dtb_setup_data_offset)
221+{
222+ struct setup_data *sd = (void *)params + dtb_setup_data_offset;
223+ unsigned long setup_data_phys, dtb_len;
224+
225+ dtb_len = fdt_totalsize(initial_boot_params);
226+ sd->type = SETUP_DTB;
227+ sd->len = dtb_len;
228+
229+ /* Carry over current boot DTB with setup_data */
230+ memcpy(sd->data, initial_boot_params, dtb_len);
231+
232+ /* Add setup data */
233+ setup_data_phys = params_load_addr + dtb_setup_data_offset;
234+ sd->next = params->hdr.setup_data;
235+ params->hdr.setup_data = setup_data_phys;
236+}
237+#endif /* CONFIG_OF_FLATTREE */
238+
215239 static void
216240 setup_ima_state(const struct kimage *image, struct boot_params *params,
217241 unsigned long params_load_addr,
@@ -336,6 +360,17 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params, · 118 unmodified lines
336360 sizeof(struct efi_setup_data);
337361 #endif
338362
363+#ifdef CONFIG_OF_FLATTREE
364+ if (image->force_dtb && initial_boot_params) {
365+ setup_dtb(params, params_load_addr, setup_data_offset);
366+ setup_data_offset += sizeof(struct setup_data) +
367+ fdt_totalsize(initial_boot_params);
368+ } else {
369+ pr_debug("Not carrying over DTB, force_dtb = %d\n",
370+ image->force_dtb);
371+ }
372+#endif
373+
339374 if (IS_ENABLED(CONFIG_IMA_KEXEC)) {
340375 /* Setup IMA log buffer state */
341376 setup_ima_state(image, params, params_load_addr,
@@ -529,6 +564,12 @@ static void *bzImage64_load(struct kimage *image, char *kernel, · 187 unmodified lines
529564 sizeof(struct setup_data) +
530565 RNG_SEED_LENGTH;
531566
567+#ifdef CONFIG_OF_FLATTREE
568+ if (image->force_dtb && initial_boot_params)
569+ kbuf.bufsz += sizeof(struct setup_data) +
570+ fdt_totalsize(initial_boot_params);
571+#endif
572+
532573 if (IS_ENABLED(CONFIG_IMA_KEXEC))
533574 kbuf.bufsz += sizeof(struct setup_data) +
534575 sizeof(struct ima_setup_data);
@@ -537,7 +578,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel, · 2 unmodified lines
537578 kbuf.bufsz += sizeof(struct setup_data) +
538579 sizeof(struct kho_data);
539580
540− params = kzalloc(kbuf.bufsz, GFP_KERNEL);
581+ params = kvzalloc(kbuf.bufsz, GFP_KERNEL);
541582 if (!params)
542583 return ERR_PTR(-ENOMEM);
543584 efi_map_offset = params_cmdline_sz;
@@ -647,7 +688,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel, · 103 unmodified lines
647688 return ldata;
648689
649690 out_free_params:
650− kfree(params);
691+ kvfree(params);
651692 return ERR_PTR(ret);
652693 }
653694
@@ -659,7 +700,7 @@ static int bzImage64_cleanup(void *loader_data) · 5 unmodified lines
659700 if (!ldata)
660701 return 0;
661702
662− kfree(ldata->bootparams_buf);
703+ kvfree(ldata->bootparams_buf);
663704 ldata->bootparams_buf = NULL;
664705
665706 return 0;
include/linux/kexec.h+4−1
@@ -395,6 +395,9 @@ struct kimage { · 394 unmodified lines
395395
396396 /* Information for loading purgatory */
397397 struct purgatory_info purgatory_info;
398+
399+ /* Force carrying over the DTB from the current boot */
400+ bool force_dtb;
398401 #endif
399402
400403 #ifdef CONFIG_CRASH_HOTPLUG
@@ -461,7 +464,7 @@ bool kexec_load_permitted(int kexec_image_type); · 60 unmodified lines
461464 /* List of defined/legal kexec file flags */
462465 #define KEXEC_FILE_FLAGS (KEXEC_FILE_UNLOAD | KEXEC_FILE_ON_CRASH | \
463466 KEXEC_FILE_NO_INITRAMFS | KEXEC_FILE_DEBUG | \
464− KEXEC_FILE_NO_CMA)
467+ KEXEC_FILE_NO_CMA | KEXEC_FILE_FORCE_DTB)
465468
466469 /* flag to track if kexec reboot is in progress */
467470 extern bool kexec_in_progress;
include/uapi/linux/kexec.h+4−0
@@ -22,12 +22,16 @@ · 21 unmodified lines
2222 * KEXEC_FILE_ON_CRASH : Load/unload operation belongs to kdump image.
2323 * KEXEC_FILE_NO_INITRAMFS : No initramfs is being loaded. Ignore the initrd
2424 * 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.
2528 */
2629 #define KEXEC_FILE_UNLOAD 0x00000001
2730 #define KEXEC_FILE_ON_CRASH 0x00000002
2831 #define KEXEC_FILE_NO_INITRAMFS 0x00000004
2932 #define KEXEC_FILE_DEBUG 0x00000008
3033 #define KEXEC_FILE_NO_CMA 0x00000010
34+#define KEXEC_FILE_FORCE_DTB 0x00000020
3135
3236 /* These values match the ELF architecture values.
3337 * Unless there is a good reason that should continue to be the case.
kernel/kexec_file.c+1−0
@@ -255,6 +255,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd, · 254 unmodified lines
255255 }
256256
257257 image->no_cma = !!(flags & KEXEC_FILE_NO_CMA);
258+ image->force_dtb = flags & KEXEC_FILE_FORCE_DTB;
258259
259260 if (cmdline_len) {
260261 image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len);