1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2018 Linaro Limited
6 * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
8 * Most code is derived from arm64 port of kexec-tools
11 #define pr_fmt(fmt) "kexec_file: " fmt
13 #include <linux/ioport.h>
14 #include <linux/kernel.h>
15 #include <linux/kexec.h>
16 #include <linux/libfdt.h>
17 #include <linux/memblock.h>
19 #include <linux/of_fdt.h>
20 #include <linux/slab.h>
21 #include <linux/string.h>
22 #include <linux/types.h>
23 #include <linux/vmalloc.h>
25 const struct kexec_file_ops
* const kexec_file_loaders
[] = {
30 int arch_kimage_file_post_load_cleanup(struct kimage
*image
)
32 kvfree(image
->arch
.dtb
);
33 image
->arch
.dtb
= NULL
;
35 vfree(image
->elf_headers
);
36 image
->elf_headers
= NULL
;
37 image
->elf_headers_sz
= 0;
39 return kexec_image_post_load_cleanup_default(image
);
42 #ifdef CONFIG_CRASH_DUMP
43 static int prepare_elf_headers(void **addr
, unsigned long *sz
)
45 struct crash_mem
*cmem
;
46 unsigned int nr_ranges
;
49 phys_addr_t start
, end
;
51 nr_ranges
= 2; /* for exclusion of crashkernel region */
52 for_each_mem_range(i
, &start
, &end
)
55 cmem
= kmalloc(struct_size(cmem
, ranges
, nr_ranges
), GFP_KERNEL
);
59 cmem
->max_nr_ranges
= nr_ranges
;
61 for_each_mem_range(i
, &start
, &end
) {
62 cmem
->ranges
[cmem
->nr_ranges
].start
= start
;
63 cmem
->ranges
[cmem
->nr_ranges
].end
= end
- 1;
67 /* Exclude crashkernel region */
68 ret
= crash_exclude_mem_range(cmem
, crashk_res
.start
, crashk_res
.end
);
72 if (crashk_low_res
.end
) {
73 ret
= crash_exclude_mem_range(cmem
, crashk_low_res
.start
, crashk_low_res
.end
);
78 ret
= crash_prepare_elf64_headers(cmem
, true, addr
, sz
);
87 * Tries to add the initrd and DTB to the image. If it is not possible to find
88 * valid locations, this function will undo changes to the image and return non
91 int load_other_segments(struct kimage
*image
,
92 unsigned long kernel_load_addr
,
93 unsigned long kernel_size
,
94 char *initrd
, unsigned long initrd_len
,
97 struct kexec_buf kbuf
;
99 unsigned long initrd_load_addr
= 0, dtb_len
,
100 orig_segments
= image
->nr_segments
;
104 /* not allocate anything below the kernel */
105 kbuf
.buf_min
= kernel_load_addr
+ kernel_size
;
107 #ifdef CONFIG_CRASH_DUMP
108 /* load elf core header */
110 unsigned long headers_sz
;
111 if (image
->type
== KEXEC_TYPE_CRASH
) {
112 ret
= prepare_elf_headers(&headers
, &headers_sz
);
114 pr_err("Preparing elf core header failed\n");
118 kbuf
.buffer
= headers
;
119 kbuf
.bufsz
= headers_sz
;
120 kbuf
.mem
= KEXEC_BUF_MEM_UNKNOWN
;
121 kbuf
.memsz
= headers_sz
;
122 kbuf
.buf_align
= SZ_64K
; /* largest supported page size */
123 kbuf
.buf_max
= ULONG_MAX
;
124 kbuf
.top_down
= true;
126 ret
= kexec_add_buffer(&kbuf
);
131 image
->elf_headers
= headers
;
132 image
->elf_load_addr
= kbuf
.mem
;
133 image
->elf_headers_sz
= headers_sz
;
135 kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
136 image
->elf_load_addr
, kbuf
.bufsz
, kbuf
.memsz
);
142 kbuf
.buffer
= initrd
;
143 kbuf
.bufsz
= initrd_len
;
144 kbuf
.mem
= KEXEC_BUF_MEM_UNKNOWN
;
145 kbuf
.memsz
= initrd_len
;
147 /* within 1GB-aligned window of up to 32GB in size */
148 kbuf
.buf_max
= round_down(kernel_load_addr
, SZ_1G
)
149 + (unsigned long)SZ_1G
* 32;
150 kbuf
.top_down
= false;
152 ret
= kexec_add_buffer(&kbuf
);
155 initrd_load_addr
= kbuf
.mem
;
157 kexec_dprintk("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
158 initrd_load_addr
, kbuf
.bufsz
, kbuf
.memsz
);
162 dtb
= of_kexec_alloc_and_setup_fdt(image
, initrd_load_addr
,
163 initrd_len
, cmdline
, 0);
165 pr_err("Preparing for new dtb failed\n");
172 dtb_len
= fdt_totalsize(dtb
);
174 kbuf
.bufsz
= dtb_len
;
175 kbuf
.mem
= KEXEC_BUF_MEM_UNKNOWN
;
176 kbuf
.memsz
= dtb_len
;
177 /* not across 2MB boundary */
178 kbuf
.buf_align
= SZ_2M
;
179 kbuf
.buf_max
= ULONG_MAX
;
180 kbuf
.top_down
= true;
182 ret
= kexec_add_buffer(&kbuf
);
185 image
->arch
.dtb
= dtb
;
186 image
->arch
.dtb_mem
= kbuf
.mem
;
188 kexec_dprintk("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
189 kbuf
.mem
, kbuf
.bufsz
, kbuf
.memsz
);
194 image
->nr_segments
= orig_segments
;