x86, arm64, LoongArch, RISC-V and powerpc all build an elf core header, load the initrd by hand before loading the kdump kernel, while arm64 and RISC-V also duplicate the code that builds the device tree for the kexec kernel
Add the following 4 helpers for reuse: - kexec_prepare_elf_core_header() builds the header once per image and returns the number of memory ranges, which x86 needs to reserve room for the crash hotplug updates. - kexec_add_elf_core_header() places the prepared header as a segment. The caller provides the kexec_buf with its placement policy and the size to reserve. On success it also records the segment index that the crash hotplug code uses to find the header. - kexec_add_initrd() places the initrd from the image with the placement policy the caller put into the kexec_buf. - kexec_add_fdt() builds the device tree for the kexec kernel, packs it and places it with the kexec_buf the caller passes in. Cc: Andrew Morton <[email protected]> Cc: Baoquan He <[email protected]> Cc: Mike Rapoport <[email protected]> Link: https://lore.kernel.org/all/179430504583.425130.15302016434972990362.b4-review@b4/ Suggested-by: Mike Rapoport <[email protected]> Signed-off-by: Jinjie Ruan <[email protected]> --- include/linux/kexec.h | 11 +++++ kernel/kexec_file.c | 104 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/include/linux/kexec.h b/include/linux/kexec.h index bac11e940e74..89cb47521d6d 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -258,6 +258,17 @@ int kexec_kernel_verify_pe_sig(const char *kernel, unsigned long kernel_len); extern int kexec_add_buffer(struct kexec_buf *kbuf); int kexec_locate_mem_hole(struct kexec_buf *kbuf); +int kexec_add_initrd(struct kimage *image, struct kexec_buf *kbuf); +#ifdef CONFIG_CRASH_DUMP +int kexec_prepare_elf_core_header(struct kimage *image, bool need_kernel_map, + unsigned long *nr_ranges); +int kexec_add_elf_core_header(struct kimage *image, struct kexec_buf *kbuf); +#endif +#if defined(CONFIG_OF) && defined(CONFIG_OF_FLATTREE) +int kexec_add_fdt(struct kimage *image, struct kexec_buf *kbuf, + unsigned long initrd_load_addr, const char *cmdline, + void **fdt); +#endif #ifndef arch_kexec_apply_relocations_add /* diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 1f128c6e6fa3..25c9bef639d2 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -14,7 +14,9 @@ #include <linux/file.h> #include <linux/slab.h> #include <linux/kexec.h> +#include <linux/libfdt.h> #include <linux/memblock.h> +#include <linux/of.h> #include <linux/mutex.h> #include <linux/list.h> #include <linux/fs.h> @@ -109,6 +111,108 @@ static void kexec_free_elf_headers(struct kimage *image) image->elf_headers_sz = 0; } +#ifdef CONFIG_CRASH_DUMP +int kexec_prepare_elf_core_header(struct kimage *image, bool need_kernel_map, + unsigned long *nr_ranges) +{ + int ret; + + if (image->elf_headers) + return 0; + + ret = crash_prepare_headers(need_kernel_map, &image->elf_headers, + &image->elf_headers_sz, nr_ranges); + if (ret) + pr_err("Preparing elf core header failed\n"); + + return ret; +} + +int kexec_add_elf_core_header(struct kimage *image, struct kexec_buf *kbuf) +{ + unsigned long memsz = kbuf->memsz; + int ret; + + kbuf->buffer = image->elf_headers; + kbuf->bufsz = image->elf_headers_sz; + kbuf->mem = KEXEC_BUF_MEM_UNKNOWN; + + ret = kexec_add_buffer(kbuf); + if (ret) + return ret; + + image->elf_load_addr = kbuf->mem; + + /* The crash hotplug reservation can exceed the header size. */ + if (memsz > image->elf_headers_sz) { + image->elf_headers_sz = memsz; +#ifdef CONFIG_CRASH_HOTPLUG + image->elfcorehdr_index = image->nr_segments - 1; +#endif + } + + kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n", + image->elf_load_addr, kbuf->bufsz, kbuf->memsz); + + return 0; +} +#endif /* CONFIG_CRASH_DUMP */ + +int kexec_add_initrd(struct kimage *image, struct kexec_buf *kbuf) +{ + int ret; + + kbuf->buffer = image->initrd_buf; + kbuf->bufsz = kbuf->memsz = image->initrd_buf_len; + kbuf->mem = KEXEC_BUF_MEM_UNKNOWN; + + ret = kexec_add_buffer(kbuf); + if (ret) + return ret; + + kexec_dprintk("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n", + kbuf->mem, kbuf->bufsz, kbuf->memsz); + + return 0; +} + +#if defined(CONFIG_OF) && defined(CONFIG_OF_FLATTREE) +int kexec_add_fdt(struct kimage *image, struct kexec_buf *kbuf, + unsigned long initrd_load_addr, const char *cmdline, + void **fdt) +{ + void *new_fdt; + int ret; + + new_fdt = of_kexec_alloc_and_setup_fdt(image, initrd_load_addr, + image->initrd_buf_len, + cmdline, 0); + if (!new_fdt) { + pr_err("Preparing for new dtb failed\n"); + return -EINVAL; + } + + fdt_pack(new_fdt); + + kbuf->buffer = new_fdt; + kbuf->bufsz = kbuf->memsz = fdt_totalsize(new_fdt); + kbuf->mem = KEXEC_BUF_MEM_UNKNOWN; + + ret = kexec_add_buffer(kbuf); + if (ret) { + kvfree(new_fdt); + return ret; + } + + *fdt = new_fdt; + + kexec_dprintk("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n", + kbuf->mem, kbuf->bufsz, kbuf->memsz); + + return 0; +} +#endif /* CONFIG_OF && CONFIG_OF_FLATTREE */ + /* * Free up memory used by kernel, initrd, and command line. This is temporary * memory allocation which is not needed any more after these buffers have -- 2.34.1
