Linux Audio

Check our new training course

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