1 // SPDX-License-Identifier: GPL-2.0-only
3 * powerpc code to implement the kexec_file_load syscall
5 * Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
6 * Copyright (C) 2004 IBM Corp.
7 * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation
8 * Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
9 * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com)
10 * Copyright (C) 2016 IBM Corporation
12 * Based on kexec-tools' kexec-elf-ppc64.c, fs2dt.c.
13 * Heavily modified for the kernel by
14 * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
17 #include <linux/slab.h>
18 #include <linux/kexec.h>
19 #include <linux/of_fdt.h>
20 #include <linux/libfdt.h>
21 #include <asm/setup.h>
24 #define SLAVE_CODE_SIZE 256 /* First 0x100 bytes */
27 * setup_kdump_cmdline - Prepend "elfcorehdr=<addr> " to command line
28 * of kdump kernel for exporting the core.
30 * @cmdline: Command line parameters to update.
31 * @cmdline_len: Length of the cmdline parameters.
33 * kdump segment must be setup before calling this function.
35 * Returns new cmdline buffer for kdump kernel on success, NULL otherwise.
37 char *setup_kdump_cmdline(struct kimage
*image
, char *cmdline
,
38 unsigned long cmdline_len
)
40 int elfcorehdr_strlen
;
43 cmdline_ptr
= kzalloc(COMMAND_LINE_SIZE
, GFP_KERNEL
);
47 elfcorehdr_strlen
= sprintf(cmdline_ptr
, "elfcorehdr=0x%lx ",
48 image
->arch
.elfcorehdr_addr
);
50 if (elfcorehdr_strlen
+ cmdline_len
> COMMAND_LINE_SIZE
) {
51 pr_err("Appending elfcorehdr=<addr> exceeds cmdline size\n");
56 memcpy(cmdline_ptr
+ elfcorehdr_strlen
, cmdline
, cmdline_len
);
57 // Ensure it's nul terminated
58 cmdline_ptr
[COMMAND_LINE_SIZE
- 1] = '\0';
63 * setup_purgatory - initialize the purgatory's global variables
64 * @image: kexec image.
65 * @slave_code: Slave code for the purgatory.
66 * @fdt: Flattened device tree for the next kernel.
67 * @kernel_load_addr: Address where the kernel is loaded.
68 * @fdt_load_addr: Address where the flattened device tree is loaded.
70 * Return: 0 on success, or negative errno on error.
72 int setup_purgatory(struct kimage
*image
, const void *slave_code
,
73 const void *fdt
, unsigned long kernel_load_addr
,
74 unsigned long fdt_load_addr
)
76 unsigned int *slave_code_buf
, master_entry
;
79 slave_code_buf
= kmalloc(SLAVE_CODE_SIZE
, GFP_KERNEL
);
83 /* Get the slave code from the new kernel and put it in purgatory. */
84 ret
= kexec_purgatory_get_set_symbol(image
, "purgatory_start",
85 slave_code_buf
, SLAVE_CODE_SIZE
,
88 kfree(slave_code_buf
);
92 master_entry
= slave_code_buf
[0];
93 memcpy(slave_code_buf
, slave_code
, SLAVE_CODE_SIZE
);
94 slave_code_buf
[0] = master_entry
;
95 ret
= kexec_purgatory_get_set_symbol(image
, "purgatory_start",
96 slave_code_buf
, SLAVE_CODE_SIZE
,
98 kfree(slave_code_buf
);
100 ret
= kexec_purgatory_get_set_symbol(image
, "kernel", &kernel_load_addr
,
101 sizeof(kernel_load_addr
), false);
104 ret
= kexec_purgatory_get_set_symbol(image
, "dt_offset", &fdt_load_addr
,
105 sizeof(fdt_load_addr
), false);
113 * delete_fdt_mem_rsv - delete memory reservation with given address and size
115 * Return: 0 on success, or negative errno on error.
117 int delete_fdt_mem_rsv(void *fdt
, unsigned long start
, unsigned long size
)
119 int i
, ret
, num_rsvs
= fdt_num_mem_rsv(fdt
);
121 for (i
= 0; i
< num_rsvs
; i
++) {
122 uint64_t rsv_start
, rsv_size
;
124 ret
= fdt_get_mem_rsv(fdt
, i
, &rsv_start
, &rsv_size
);
126 pr_err("Malformed device tree.\n");
130 if (rsv_start
== start
&& rsv_size
== size
) {
131 ret
= fdt_del_mem_rsv(fdt
, i
);
133 pr_err("Error deleting device tree reservation.\n");
145 * setup_new_fdt - modify /chosen and memory reservation for the next kernel
146 * @image: kexec image being loaded.
147 * @fdt: Flattened device tree for the next kernel.
148 * @initrd_load_addr: Address where the next initrd will be loaded.
149 * @initrd_len: Size of the next initrd, or 0 if there will be none.
150 * @cmdline: Command line for the next kernel, or NULL if there will
153 * Return: 0 on success, or negative errno on error.
155 int setup_new_fdt(const struct kimage
*image
, void *fdt
,
156 unsigned long initrd_load_addr
, unsigned long initrd_len
,
159 int ret
, chosen_node
;
162 /* Remove memory reservation for the current device tree. */
163 ret
= delete_fdt_mem_rsv(fdt
, __pa(initial_boot_params
),
164 fdt_totalsize(initial_boot_params
));
166 pr_debug("Removed old device tree reservation.\n");
167 else if (ret
!= -ENOENT
)
170 chosen_node
= fdt_path_offset(fdt
, "/chosen");
171 if (chosen_node
== -FDT_ERR_NOTFOUND
) {
172 chosen_node
= fdt_add_subnode(fdt
, fdt_path_offset(fdt
, "/"),
174 if (chosen_node
< 0) {
175 pr_err("Error creating /chosen.\n");
178 } else if (chosen_node
< 0) {
179 pr_err("Malformed device tree: error reading /chosen.\n");
183 /* Did we boot using an initrd? */
184 prop
= fdt_getprop(fdt
, chosen_node
, "linux,initrd-start", NULL
);
186 uint64_t tmp_start
, tmp_end
, tmp_size
;
188 tmp_start
= fdt64_to_cpu(*((const fdt64_t
*) prop
));
190 prop
= fdt_getprop(fdt
, chosen_node
, "linux,initrd-end", NULL
);
192 pr_err("Malformed device tree.\n");
195 tmp_end
= fdt64_to_cpu(*((const fdt64_t
*) prop
));
198 * kexec reserves exact initrd size, while firmware may
199 * reserve a multiple of PAGE_SIZE, so check for both.
201 tmp_size
= tmp_end
- tmp_start
;
202 ret
= delete_fdt_mem_rsv(fdt
, tmp_start
, tmp_size
);
204 ret
= delete_fdt_mem_rsv(fdt
, tmp_start
,
205 round_up(tmp_size
, PAGE_SIZE
));
207 pr_debug("Removed old initrd reservation.\n");
208 else if (ret
!= -ENOENT
)
211 /* If there's no new initrd, delete the old initrd's info. */
212 if (initrd_len
== 0) {
213 ret
= fdt_delprop(fdt
, chosen_node
,
214 "linux,initrd-start");
216 pr_err("Error deleting linux,initrd-start.\n");
220 ret
= fdt_delprop(fdt
, chosen_node
, "linux,initrd-end");
222 pr_err("Error deleting linux,initrd-end.\n");
229 ret
= fdt_setprop_u64(fdt
, chosen_node
,
230 "linux,initrd-start",
235 /* initrd-end is the first address after the initrd image. */
236 ret
= fdt_setprop_u64(fdt
, chosen_node
, "linux,initrd-end",
237 initrd_load_addr
+ initrd_len
);
241 ret
= fdt_add_mem_rsv(fdt
, initrd_load_addr
, initrd_len
);
243 pr_err("Error reserving initrd memory: %s\n",
249 if (cmdline
!= NULL
) {
250 ret
= fdt_setprop_string(fdt
, chosen_node
, "bootargs", cmdline
);
254 ret
= fdt_delprop(fdt
, chosen_node
, "bootargs");
255 if (ret
&& ret
!= -FDT_ERR_NOTFOUND
) {
256 pr_err("Error deleting bootargs.\n");
261 if (image
->type
== KEXEC_TYPE_CRASH
) {
263 * Avoid elfcorehdr from being stomped on in kdump kernel by
264 * setting up memory reserve map.
266 ret
= fdt_add_mem_rsv(fdt
, image
->arch
.elfcorehdr_addr
,
267 image
->arch
.elf_headers_sz
);
269 pr_err("Error reserving elfcorehdr memory: %s\n",
275 ret
= setup_ima_buffer(image
, fdt
, chosen_node
);
277 pr_err("Error setting up the new device tree.\n");
281 ret
= fdt_setprop(fdt
, chosen_node
, "linux,booted-from-kexec", NULL
, 0);
288 pr_err("Error setting up the new device tree.\n");