1
2
3
4
5
6
7
8
9
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
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
33
34
35#define FDT_EXTRA_SPACE 0x1000
36
37
38
39
40
41
42
43
44
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
75
76
77
78
79
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
99
100
101
102
103
104
105
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
127
128
129
130
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
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
187
188
189
190
191
192
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
221
222
223
224
225
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
253static inline int setup_ima_buffer(const struct kimage *image, void *fdt,
254 int chosen_node)
255{
256 return 0;
257}
258#endif
259
260
261
262
263
264
265
266
267
268
269
270
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
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
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
337
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
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
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
385
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
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
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
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
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}
459