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>
18 #include <linux/of_fdt.h>
19 #include <linux/random.h>
20 #include <linux/string.h>
21 #include <linux/types.h>
22 #include <linux/vmalloc.h>
23 #include <asm/byteorder.h>
25 /* relevant device tree properties */
26 #define FDT_PROP_INITRD_START "linux,initrd-start"
27 #define FDT_PROP_INITRD_END "linux,initrd-end"
28 #define FDT_PROP_BOOTARGS "bootargs"
29 #define FDT_PROP_KASLR_SEED "kaslr-seed"
31 const struct kexec_file_ops
* const kexec_file_loaders
[] = {
36 int arch_kimage_file_post_load_cleanup(struct kimage
*image
)
38 vfree(image
->arch
.dtb
);
39 image
->arch
.dtb
= NULL
;
41 return kexec_image_post_load_cleanup_default(image
);
44 static int setup_dtb(struct kimage
*image
,
45 unsigned long initrd_load_addr
, unsigned long initrd_len
,
46 char *cmdline
, void *dtb
)
50 ret
= fdt_path_offset(dtb
, "/chosen");
58 ret
= fdt_setprop_string(dtb
, off
, FDT_PROP_BOOTARGS
, cmdline
);
62 ret
= fdt_delprop(dtb
, off
, FDT_PROP_BOOTARGS
);
63 if (ret
&& (ret
!= -FDT_ERR_NOTFOUND
))
68 if (initrd_load_addr
) {
69 ret
= fdt_setprop_u64(dtb
, off
, FDT_PROP_INITRD_START
,
74 ret
= fdt_setprop_u64(dtb
, off
, FDT_PROP_INITRD_END
,
75 initrd_load_addr
+ initrd_len
);
79 ret
= fdt_delprop(dtb
, off
, FDT_PROP_INITRD_START
);
80 if (ret
&& (ret
!= -FDT_ERR_NOTFOUND
))
83 ret
= fdt_delprop(dtb
, off
, FDT_PROP_INITRD_END
);
84 if (ret
&& (ret
!= -FDT_ERR_NOTFOUND
))
89 ret
= fdt_delprop(dtb
, off
, FDT_PROP_KASLR_SEED
);
90 if (ret
== -FDT_ERR_NOTFOUND
)
95 if (rng_is_initialized()) {
96 u64 seed
= get_random_u64();
97 ret
= fdt_setprop_u64(dtb
, off
, FDT_PROP_KASLR_SEED
, seed
);
101 pr_notice("RNG is not initialised: omitting \"%s\" property\n",
102 FDT_PROP_KASLR_SEED
);
107 return (ret
== -FDT_ERR_NOSPACE
) ? -ENOMEM
: -EINVAL
;
113 * More space needed so that we can add initrd, bootargs and kaslr-seed.
115 #define DTB_EXTRA_SPACE 0x1000
117 static int create_dtb(struct kimage
*image
,
118 unsigned long initrd_load_addr
, unsigned long initrd_len
,
119 char *cmdline
, void **dtb
)
126 cmdline_len
= cmdline
? strlen(cmdline
) : 0;
127 buf_size
= fdt_totalsize(initial_boot_params
)
128 + cmdline_len
+ DTB_EXTRA_SPACE
;
131 buf
= vmalloc(buf_size
);
135 /* duplicate a device tree blob */
136 ret
= fdt_open_into(initial_boot_params
, buf
, buf_size
);
140 ret
= setup_dtb(image
, initrd_load_addr
, initrd_len
,
144 if (ret
== -ENOMEM
) {
145 /* unlikely, but just in case */
146 buf_size
+= DTB_EXTRA_SPACE
;
161 int load_other_segments(struct kimage
*image
,
162 unsigned long kernel_load_addr
,
163 unsigned long kernel_size
,
164 char *initrd
, unsigned long initrd_len
,
167 struct kexec_buf kbuf
;
169 unsigned long initrd_load_addr
= 0, dtb_len
;
173 /* not allocate anything below the kernel */
174 kbuf
.buf_min
= kernel_load_addr
+ kernel_size
;
178 kbuf
.buffer
= initrd
;
179 kbuf
.bufsz
= initrd_len
;
181 kbuf
.memsz
= initrd_len
;
183 /* within 1GB-aligned window of up to 32GB in size */
184 kbuf
.buf_max
= round_down(kernel_load_addr
, SZ_1G
)
185 + (unsigned long)SZ_1G
* 32;
186 kbuf
.top_down
= false;
188 ret
= kexec_add_buffer(&kbuf
);
191 initrd_load_addr
= kbuf
.mem
;
193 pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
194 initrd_load_addr
, initrd_len
, initrd_len
);
198 ret
= create_dtb(image
, initrd_load_addr
, initrd_len
, cmdline
, &dtb
);
200 pr_err("Preparing for new dtb failed\n");
204 dtb_len
= fdt_totalsize(dtb
);
206 kbuf
.bufsz
= dtb_len
;
208 kbuf
.memsz
= dtb_len
;
209 /* not across 2MB boundary */
210 kbuf
.buf_align
= SZ_2M
;
211 kbuf
.buf_max
= ULONG_MAX
;
212 kbuf
.top_down
= true;
214 ret
= kexec_add_buffer(&kbuf
);
217 image
->arch
.dtb
= dtb
;
218 image
->arch
.dtb_mem
= kbuf
.mem
;
220 pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
221 kbuf
.mem
, dtb_len
, dtb_len
);