xref: /linux/drivers/of/kexec.c (revision aaa44952bbd1d4db14a4d676bf9595bb5db7e7b0)
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  */
46 static 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  */
81 static 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  */
107 static 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  */
132 int 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  */
159 int 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  */
194 static 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  */
227 static 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 */
253 static 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  */
272 void *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 
451 out:
452 	if (ret) {
453 		kvfree(fdt);
454 		fdt = NULL;
455 	}
456 
457 	return fdt;
458 }
459