arm64, LoongArch and RISC-V duplicate the elf core header and initrd
loading.  Move it into a common helper with one arch hook for the boot
parameters; a failure releases everything added for the attempt.

Cc: Andrew Morton <[email protected]>
Cc: Baoquan He <[email protected]>
Cc: Mike Rapoport <[email protected]>
Suggested-by: Mike Rapoport <[email protected]>
Signed-off-by: Jinjie Ruan <[email protected]>
---
 include/linux/kexec.h | 15 ++++++++
 kernel/kexec_file.c   | 90 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 105 insertions(+)

diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index faba26f871a4..de1d83ddc38b 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -259,6 +259,21 @@ 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);
 
+struct kexec_load_params {
+       unsigned long kernel_load_addr;
+       unsigned long kernel_size;
+       char *initrd;
+       unsigned long initrd_len;
+       char *cmdline;
+       unsigned long cmdline_len;
+};
+
+int arch_kexec_setup_other_segments(struct kimage *image,
+                                   struct kexec_load_params *params,
+                                   unsigned long initrd_load_addr);
+int kexec_load_other_segments(struct kimage *image,
+                             struct kexec_load_params *params, bool top_down);
+
 #ifndef arch_kexec_locate_mem_hole
 /**
  * arch_kexec_locate_mem_hole - Find free memory to place the segments.
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 62d4cba278a5..8b80f1186875 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -1295,3 +1295,93 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, 
const char *name,
        return 0;
 }
 #endif /* CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY */
+
+int __weak arch_kexec_setup_other_segments(struct kimage *image,
+                                          struct kexec_load_params *params,
+                                          unsigned long initrd_load_addr)
+{
+       return 0;
+}
+
+int kexec_load_other_segments(struct kimage *image,
+                             struct kexec_load_params *params, bool top_down)
+{
+       struct kexec_buf kbuf = {
+               .image = image,
+               /* Do not allocate anything below the kernel */
+               .buf_min = params->kernel_load_addr + params->kernel_size,
+               .buf_max = top_down ? PFN_PHYS(max_low_pfn) : ULONG_MAX,
+       };
+
+       unsigned long orig_segments = image->nr_segments;
+       unsigned long initrd_load_addr = 0;
+       int ret = 0;
+
+#ifdef CONFIG_CRASH_DUMP
+       if (image->type == KEXEC_TYPE_CRASH) {
+               ret = crash_prepare_headers(true, &kbuf.buffer, &kbuf.bufsz, 
NULL);
+               if (ret) {
+                       pr_err("Preparing elf core header failed\n");
+                       goto out_err;
+               }
+
+               if (unlikely(image->elf_headers))
+                       vfree(image->elf_headers);
+
+               image->elf_headers = kbuf.buffer;
+               image->elf_headers_sz = kbuf.bufsz;
+
+               kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
+               kbuf.memsz = kbuf.bufsz;
+               kbuf.buf_align = SZ_64K; /* largest supported page size */
+               kbuf.top_down = true;
+
+               ret = kexec_add_buffer(&kbuf);
+               if (ret)
+                       goto out_err;
+
+               image->elf_load_addr = kbuf.mem;
+
+               kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx 
memsz=0x%lx\n",
+                             image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
+       }
+#endif
+
+       if (params->initrd) {
+               kbuf.buffer = params->initrd;
+               kbuf.bufsz = params->initrd_len;
+               kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
+               kbuf.memsz = params->initrd_len;
+               kbuf.buf_align = 0;
+               /* within 1GB-aligned window of up to 32GB in size */
+               if (!top_down)
+                       kbuf.buf_max = round_down(params->kernel_load_addr, 
SZ_1G) +
+                                       (unsigned long)SZ_1G * 32;
+               kbuf.top_down = top_down;
+
+               ret = kexec_add_buffer(&kbuf);
+               if (ret)
+                       goto out_err;
+               initrd_load_addr = kbuf.mem;
+
+               kexec_dprintk("Loaded initrd at 0x%lx bufsz=0x%lx 
memsz=0x%lx\n",
+                             initrd_load_addr, kbuf.bufsz, kbuf.memsz);
+       }
+
+       ret = arch_kexec_setup_other_segments(image, params,
+                                             initrd_load_addr);
+       if (ret)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       while (image->nr_segments > orig_segments) {
+               kexec_free_segment_cma(image, image->nr_segments - 1);
+               image->nr_segments--;
+       }
+
+       kexec_free_elf_headers(image);
+
+       return ret;
+}
-- 
2.34.1


Reply via email to