Loading...
Note: File does not exist in v4.6.
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2020 Arm Limited
4 *
5 * Based on arch/arm64/kernel/machine_kexec_file.c:
6 * Copyright (C) 2018 Linaro Limited
7 *
8 * And arch/powerpc/kexec/file_load.c:
9 * Copyright (C) 2016 IBM Corporation
10 */
11
12#include <linux/kernel.h>
13#include <linux/kexec.h>
14#include <linux/memblock.h>
15#include <linux/libfdt.h>
16#include <linux/of.h>
17#include <linux/of_fdt.h>
18#include <linux/random.h>
19#include <linux/types.h>
20
21/* relevant device tree properties */
22#define FDT_PROP_KEXEC_ELFHDR "linux,elfcorehdr"
23#define FDT_PROP_MEM_RANGE "linux,usable-memory-range"
24#define FDT_PROP_INITRD_START "linux,initrd-start"
25#define FDT_PROP_INITRD_END "linux,initrd-end"
26#define FDT_PROP_BOOTARGS "bootargs"
27#define FDT_PROP_KASLR_SEED "kaslr-seed"
28#define FDT_PROP_RNG_SEED "rng-seed"
29#define RNG_SEED_SIZE 128
30
31/*
32 * Additional space needed for the FDT buffer so that we can add initrd,
33 * bootargs, kaslr-seed, rng-seed, useable-memory-range and elfcorehdr.
34 */
35#define FDT_EXTRA_SPACE 0x1000
36
37/**
38 * fdt_find_and_del_mem_rsv - delete memory reservation with given address and size
39 *
40 * @fdt: Flattened device tree for the current kernel.
41 * @start: Starting address of the reserved memory.
42 * @size: Size of the reserved memory.
43 *
44 * Return: 0 on success, or negative errno on error.
45 */
46static int fdt_find_and_del_mem_rsv(void *fdt, unsigned long start, unsigned long size)
47{
48 int i, ret, num_rsvs = fdt_num_mem_rsv(fdt);
49
50 for (i = 0; i < num_rsvs; i++) {
51 u64 rsv_start, rsv_size;
52
53 ret = fdt_get_mem_rsv(fdt, i, &rsv_start, &rsv_size);
54 if (ret) {
55 pr_err("Malformed device tree.\n");
56 return -EINVAL;
57 }
58
59 if (rsv_start == start && rsv_size == size) {
60 ret = fdt_del_mem_rsv(fdt, i);
61 if (ret) {
62 pr_err("Error deleting device tree reservation.\n");
63 return -EINVAL;
64 }
65
66 return 0;
67 }
68 }
69
70 return -ENOENT;
71}
72
73/**
74 * get_addr_size_cells - Get address and size of root node
75 *
76 * @addr_cells: Return address of the root node
77 * @size_cells: Return size of the root node
78 *
79 * Return: 0 on success, or negative errno on error.
80 */
81static int get_addr_size_cells(int *addr_cells, int *size_cells)
82{
83 struct device_node *root;
84
85 root = of_find_node_by_path("/");
86 if (!root)
87 return -EINVAL;
88
89 *addr_cells = of_n_addr_cells(root);
90 *size_cells = of_n_size_cells(root);
91
92 of_node_put(root);
93
94 return 0;
95}
96
97/**
98 * do_get_kexec_buffer - Get address and size of device tree property
99 *
100 * @prop: Device tree property
101 * @len: Size of @prop
102 * @addr: Return address of the node
103 * @size: Return size of the node
104 *
105 * Return: 0 on success, or negative errno on error.
106 */
107static int do_get_kexec_buffer(const void *prop, int len, unsigned long *addr,
108 size_t *size)
109{
110 int ret, addr_cells, size_cells;
111
112 ret = get_addr_size_cells(&addr_cells, &size_cells);
113 if (ret)
114 return ret;
115
116 if (len < 4 * (addr_cells + size_cells))
117 return -ENOENT;
118
119 *addr = of_read_number(prop, addr_cells);
120 *size = of_read_number(prop + 4 * addr_cells, size_cells);
121
122 return 0;
123}
124
125/**
126 * ima_get_kexec_buffer - get IMA buffer from the previous kernel
127 * @addr: On successful return, set to point to the buffer contents.
128 * @size: On successful return, set to the buffer size.
129 *
130 * Return: 0 on success, negative errno on error.
131 */
132int ima_get_kexec_buffer(void **addr, size_t *size)
133{
134 int ret, len;
135 unsigned long tmp_addr;
136 size_t tmp_size;
137 const void *prop;
138
139 if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
140 return -ENOTSUPP;
141
142 prop = of_get_property(of_chosen, "linux,ima-kexec-buffer", &len);
143 if (!prop)
144 return -ENOENT;
145
146 ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size);
147 if (ret)
148 return ret;
149
150 *addr = __va(tmp_addr);
151 *size = tmp_size;
152
153 return 0;
154}
155
156/**
157 * ima_free_kexec_buffer - free memory used by the IMA buffer
158 */
159int ima_free_kexec_buffer(void)
160{
161 int ret;
162 unsigned long addr;
163 size_t size;
164 struct property *prop;
165
166 if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
167 return -ENOTSUPP;
168
169 prop = of_find_property(of_chosen, "linux,ima-kexec-buffer", NULL);
170 if (!prop)
171 return -ENOENT;
172
173 ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size);
174 if (ret)
175 return ret;
176
177 ret = of_remove_property(of_chosen, prop);
178 if (ret)
179 return ret;
180
181 return memblock_free(addr, size);
182
183}
184
185/**
186 * remove_ima_buffer - remove the IMA buffer property and reservation from @fdt
187 *
188 * @fdt: Flattened Device Tree to update
189 * @chosen_node: Offset to the chosen node in the device tree
190 *
191 * The IMA measurement buffer is of no use to a subsequent kernel, so we always
192 * remove it from the device tree.
193 */
194static void remove_ima_buffer(void *fdt, int chosen_node)
195{
196 int ret, len;
197 unsigned long addr;
198 size_t size;
199 const void *prop;
200
201 if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
202 return;
203
204 prop = fdt_getprop(fdt, chosen_node, "linux,ima-kexec-buffer", &len);
205 if (!prop)
206 return;
207
208 ret = do_get_kexec_buffer(prop, len, &addr, &size);
209 fdt_delprop(fdt, chosen_node, "linux,ima-kexec-buffer");
210 if (ret)
211 return;
212
213 ret = fdt_find_and_del_mem_rsv(fdt, addr, size);
214 if (!ret)
215 pr_debug("Removed old IMA buffer reservation.\n");
216}
217
218#ifdef CONFIG_IMA_KEXEC
219/**
220 * setup_ima_buffer - add IMA buffer information to the fdt
221 * @image: kexec image being loaded.
222 * @fdt: Flattened device tree for the next kernel.
223 * @chosen_node: Offset to the chosen node.
224 *
225 * Return: 0 on success, or negative errno on error.
226 */
227static int setup_ima_buffer(const struct kimage *image, void *fdt,
228 int chosen_node)
229{
230 int ret;
231
232 if (!image->ima_buffer_size)
233 return 0;
234
235 ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
236 "linux,ima-kexec-buffer",
237 image->ima_buffer_addr,
238 image->ima_buffer_size);
239 if (ret < 0)
240 return -EINVAL;
241
242 ret = fdt_add_mem_rsv(fdt, image->ima_buffer_addr,
243 image->ima_buffer_size);
244 if (ret)
245 return -EINVAL;
246
247 pr_debug("IMA buffer at 0x%llx, size = 0x%zx\n",
248 image->ima_buffer_addr, image->ima_buffer_size);
249
250 return 0;
251}
252#else /* CONFIG_IMA_KEXEC */
253static inline int setup_ima_buffer(const struct kimage *image, void *fdt,
254 int chosen_node)
255{
256 return 0;
257}
258#endif /* CONFIG_IMA_KEXEC */
259
260/*
261 * of_kexec_alloc_and_setup_fdt - Alloc and setup a new Flattened Device Tree
262 *
263 * @image: kexec image being loaded.
264 * @initrd_load_addr: Address where the next initrd will be loaded.
265 * @initrd_len: Size of the next initrd, or 0 if there will be none.
266 * @cmdline: Command line for the next kernel, or NULL if there will
267 * be none.
268 * @extra_fdt_size: Additional size for the new FDT buffer.
269 *
270 * Return: fdt on success, or NULL errno on error.
271 */
272void *of_kexec_alloc_and_setup_fdt(const struct kimage *image,
273 unsigned long initrd_load_addr,
274 unsigned long initrd_len,
275 const char *cmdline, size_t extra_fdt_size)
276{
277 void *fdt;
278 int ret, chosen_node;
279 const void *prop;
280 size_t fdt_size;
281
282 fdt_size = fdt_totalsize(initial_boot_params) +
283 (cmdline ? strlen(cmdline) : 0) +
284 FDT_EXTRA_SPACE +
285 extra_fdt_size;
286 fdt = kvmalloc(fdt_size, GFP_KERNEL);
287 if (!fdt)
288 return NULL;
289
290 ret = fdt_open_into(initial_boot_params, fdt, fdt_size);
291 if (ret < 0) {
292 pr_err("Error %d setting up the new device tree.\n", ret);
293 goto out;
294 }
295
296 /* Remove memory reservation for the current device tree. */
297 ret = fdt_find_and_del_mem_rsv(fdt, __pa(initial_boot_params),
298 fdt_totalsize(initial_boot_params));
299 if (ret == -EINVAL) {
300 pr_err("Error removing memory reservation.\n");
301 goto out;
302 }
303
304 chosen_node = fdt_path_offset(fdt, "/chosen");
305 if (chosen_node == -FDT_ERR_NOTFOUND)
306 chosen_node = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
307 "chosen");
308 if (chosen_node < 0) {
309 ret = chosen_node;
310 goto out;
311 }
312
313 ret = fdt_delprop(fdt, chosen_node, FDT_PROP_KEXEC_ELFHDR);
314 if (ret && ret != -FDT_ERR_NOTFOUND)
315 goto out;
316 ret = fdt_delprop(fdt, chosen_node, FDT_PROP_MEM_RANGE);
317 if (ret && ret != -FDT_ERR_NOTFOUND)
318 goto out;
319
320 /* Did we boot using an initrd? */
321 prop = fdt_getprop(fdt, chosen_node, "linux,initrd-start", NULL);
322 if (prop) {
323 u64 tmp_start, tmp_end, tmp_size;
324
325 tmp_start = fdt64_to_cpu(*((const fdt64_t *) prop));
326
327 prop = fdt_getprop(fdt, chosen_node, "linux,initrd-end", NULL);
328 if (!prop) {
329 ret = -EINVAL;
330 goto out;
331 }
332
333 tmp_end = fdt64_to_cpu(*((const fdt64_t *) prop));
334
335 /*
336 * kexec reserves exact initrd size, while firmware may
337 * reserve a multiple of PAGE_SIZE, so check for both.
338 */
339 tmp_size = tmp_end - tmp_start;
340 ret = fdt_find_and_del_mem_rsv(fdt, tmp_start, tmp_size);
341 if (ret == -ENOENT)
342 ret = fdt_find_and_del_mem_rsv(fdt, tmp_start,
343 round_up(tmp_size, PAGE_SIZE));
344 if (ret == -EINVAL)
345 goto out;
346 }
347
348 /* add initrd-* */
349 if (initrd_load_addr) {
350 ret = fdt_setprop_u64(fdt, chosen_node, FDT_PROP_INITRD_START,
351 initrd_load_addr);
352 if (ret)
353 goto out;
354
355 ret = fdt_setprop_u64(fdt, chosen_node, FDT_PROP_INITRD_END,
356 initrd_load_addr + initrd_len);
357 if (ret)
358 goto out;
359
360 ret = fdt_add_mem_rsv(fdt, initrd_load_addr, initrd_len);
361 if (ret)
362 goto out;
363
364 } else {
365 ret = fdt_delprop(fdt, chosen_node, FDT_PROP_INITRD_START);
366 if (ret && (ret != -FDT_ERR_NOTFOUND))
367 goto out;
368
369 ret = fdt_delprop(fdt, chosen_node, FDT_PROP_INITRD_END);
370 if (ret && (ret != -FDT_ERR_NOTFOUND))
371 goto out;
372 }
373
374 if (image->type == KEXEC_TYPE_CRASH) {
375 /* add linux,elfcorehdr */
376 ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
377 FDT_PROP_KEXEC_ELFHDR,
378 image->elf_load_addr,
379 image->elf_headers_sz);
380 if (ret)
381 goto out;
382
383 /*
384 * Avoid elfcorehdr from being stomped on in kdump kernel by
385 * setting up memory reserve map.
386 */
387 ret = fdt_add_mem_rsv(fdt, image->elf_load_addr,
388 image->elf_headers_sz);
389 if (ret)
390 goto out;
391
392 /* add linux,usable-memory-range */
393 ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
394 FDT_PROP_MEM_RANGE,
395 crashk_res.start,
396 crashk_res.end - crashk_res.start + 1);
397 if (ret)
398 goto out;
399 }
400
401 /* add bootargs */
402 if (cmdline) {
403 ret = fdt_setprop_string(fdt, chosen_node, FDT_PROP_BOOTARGS, cmdline);
404 if (ret)
405 goto out;
406 } else {
407 ret = fdt_delprop(fdt, chosen_node, FDT_PROP_BOOTARGS);
408 if (ret && (ret != -FDT_ERR_NOTFOUND))
409 goto out;
410 }
411
412 /* add kaslr-seed */
413 ret = fdt_delprop(fdt, chosen_node, FDT_PROP_KASLR_SEED);
414 if (ret == -FDT_ERR_NOTFOUND)
415 ret = 0;
416 else if (ret)
417 goto out;
418
419 if (rng_is_initialized()) {
420 u64 seed = get_random_u64();
421
422 ret = fdt_setprop_u64(fdt, chosen_node, FDT_PROP_KASLR_SEED, seed);
423 if (ret)
424 goto out;
425 } else {
426 pr_notice("RNG is not initialised: omitting \"%s\" property\n",
427 FDT_PROP_KASLR_SEED);
428 }
429
430 /* add rng-seed */
431 if (rng_is_initialized()) {
432 void *rng_seed;
433
434 ret = fdt_setprop_placeholder(fdt, chosen_node, FDT_PROP_RNG_SEED,
435 RNG_SEED_SIZE, &rng_seed);
436 if (ret)
437 goto out;
438 get_random_bytes(rng_seed, RNG_SEED_SIZE);
439 } else {
440 pr_notice("RNG is not initialised: omitting \"%s\" property\n",
441 FDT_PROP_RNG_SEED);
442 }
443
444 ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0);
445 if (ret)
446 goto out;
447
448 remove_ima_buffer(fdt, chosen_node);
449 ret = setup_ima_buffer(image, fdt, fdt_path_offset(fdt, "/chosen"));
450
451out:
452 if (ret) {
453 kvfree(fdt);
454 fdt = NULL;
455 }
456
457 return fdt;
458}