162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 Arm Limited 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on arch/arm64/kernel/machine_kexec_file.c: 662306a36Sopenharmony_ci * Copyright (C) 2018 Linaro Limited 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * And arch/powerpc/kexec/file_load.c: 962306a36Sopenharmony_ci * Copyright (C) 2016 IBM Corporation 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/ima.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/kexec.h> 1562306a36Sopenharmony_ci#include <linux/memblock.h> 1662306a36Sopenharmony_ci#include <linux/libfdt.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_fdt.h> 1962306a36Sopenharmony_ci#include <linux/random.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define RNG_SEED_SIZE 128 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Additional space needed for the FDT buffer so that we can add initrd, 2762306a36Sopenharmony_ci * bootargs, kaslr-seed, rng-seed, useable-memory-range and elfcorehdr. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define FDT_EXTRA_SPACE 0x1000 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/** 3262306a36Sopenharmony_ci * fdt_find_and_del_mem_rsv - delete memory reservation with given address and size 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * @fdt: Flattened device tree for the current kernel. 3562306a36Sopenharmony_ci * @start: Starting address of the reserved memory. 3662306a36Sopenharmony_ci * @size: Size of the reserved memory. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Return: 0 on success, or negative errno on error. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic int fdt_find_and_del_mem_rsv(void *fdt, unsigned long start, unsigned long size) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci int i, ret, num_rsvs = fdt_num_mem_rsv(fdt); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci for (i = 0; i < num_rsvs; i++) { 4562306a36Sopenharmony_ci u64 rsv_start, rsv_size; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci ret = fdt_get_mem_rsv(fdt, i, &rsv_start, &rsv_size); 4862306a36Sopenharmony_ci if (ret) { 4962306a36Sopenharmony_ci pr_err("Malformed device tree.\n"); 5062306a36Sopenharmony_ci return -EINVAL; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (rsv_start == start && rsv_size == size) { 5462306a36Sopenharmony_ci ret = fdt_del_mem_rsv(fdt, i); 5562306a36Sopenharmony_ci if (ret) { 5662306a36Sopenharmony_ci pr_err("Error deleting device tree reservation.\n"); 5762306a36Sopenharmony_ci return -EINVAL; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return -ENOENT; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/** 6862306a36Sopenharmony_ci * get_addr_size_cells - Get address and size of root node 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * @addr_cells: Return address of the root node 7162306a36Sopenharmony_ci * @size_cells: Return size of the root node 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * Return: 0 on success, or negative errno on error. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic int get_addr_size_cells(int *addr_cells, int *size_cells) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct device_node *root; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci root = of_find_node_by_path("/"); 8062306a36Sopenharmony_ci if (!root) 8162306a36Sopenharmony_ci return -EINVAL; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci *addr_cells = of_n_addr_cells(root); 8462306a36Sopenharmony_ci *size_cells = of_n_size_cells(root); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci of_node_put(root); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * do_get_kexec_buffer - Get address and size of device tree property 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * @prop: Device tree property 9562306a36Sopenharmony_ci * @len: Size of @prop 9662306a36Sopenharmony_ci * @addr: Return address of the node 9762306a36Sopenharmony_ci * @size: Return size of the node 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Return: 0 on success, or negative errno on error. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic int do_get_kexec_buffer(const void *prop, int len, unsigned long *addr, 10262306a36Sopenharmony_ci size_t *size) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci int ret, addr_cells, size_cells; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ret = get_addr_size_cells(&addr_cells, &size_cells); 10762306a36Sopenharmony_ci if (ret) 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (len < 4 * (addr_cells + size_cells)) 11162306a36Sopenharmony_ci return -ENOENT; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci *addr = of_read_number(prop, addr_cells); 11462306a36Sopenharmony_ci *size = of_read_number(prop + 4 * addr_cells, size_cells); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#ifdef CONFIG_HAVE_IMA_KEXEC 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * ima_get_kexec_buffer - get IMA buffer from the previous kernel 12262306a36Sopenharmony_ci * @addr: On successful return, set to point to the buffer contents. 12362306a36Sopenharmony_ci * @size: On successful return, set to the buffer size. 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Return: 0 on success, negative errno on error. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ciint __init ima_get_kexec_buffer(void **addr, size_t *size) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int ret, len; 13062306a36Sopenharmony_ci unsigned long tmp_addr; 13162306a36Sopenharmony_ci unsigned long start_pfn, end_pfn; 13262306a36Sopenharmony_ci size_t tmp_size; 13362306a36Sopenharmony_ci const void *prop; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci prop = of_get_property(of_chosen, "linux,ima-kexec-buffer", &len); 13662306a36Sopenharmony_ci if (!prop) 13762306a36Sopenharmony_ci return -ENOENT; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size); 14062306a36Sopenharmony_ci if (ret) 14162306a36Sopenharmony_ci return ret; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Do some sanity on the returned size for the ima-kexec buffer */ 14462306a36Sopenharmony_ci if (!tmp_size) 14562306a36Sopenharmony_ci return -ENOENT; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* 14862306a36Sopenharmony_ci * Calculate the PFNs for the buffer and ensure 14962306a36Sopenharmony_ci * they are with in addressable memory. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci start_pfn = PHYS_PFN(tmp_addr); 15262306a36Sopenharmony_ci end_pfn = PHYS_PFN(tmp_addr + tmp_size - 1); 15362306a36Sopenharmony_ci if (!page_is_ram(start_pfn) || !page_is_ram(end_pfn)) { 15462306a36Sopenharmony_ci pr_warn("IMA buffer at 0x%lx, size = 0x%zx beyond memory\n", 15562306a36Sopenharmony_ci tmp_addr, tmp_size); 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci *addr = __va(tmp_addr); 16062306a36Sopenharmony_ci *size = tmp_size; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * ima_free_kexec_buffer - free memory used by the IMA buffer 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ciint __init ima_free_kexec_buffer(void) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int ret; 17162306a36Sopenharmony_ci unsigned long addr; 17262306a36Sopenharmony_ci size_t size; 17362306a36Sopenharmony_ci struct property *prop; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci prop = of_find_property(of_chosen, "linux,ima-kexec-buffer", NULL); 17662306a36Sopenharmony_ci if (!prop) 17762306a36Sopenharmony_ci return -ENOENT; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size); 18062306a36Sopenharmony_ci if (ret) 18162306a36Sopenharmony_ci return ret; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci ret = of_remove_property(of_chosen, prop); 18462306a36Sopenharmony_ci if (ret) 18562306a36Sopenharmony_ci return ret; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci memblock_free_late(addr, size); 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci#endif 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/** 19362306a36Sopenharmony_ci * remove_ima_buffer - remove the IMA buffer property and reservation from @fdt 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci * @fdt: Flattened Device Tree to update 19662306a36Sopenharmony_ci * @chosen_node: Offset to the chosen node in the device tree 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * The IMA measurement buffer is of no use to a subsequent kernel, so we always 19962306a36Sopenharmony_ci * remove it from the device tree. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_cistatic void remove_ima_buffer(void *fdt, int chosen_node) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int ret, len; 20462306a36Sopenharmony_ci unsigned long addr; 20562306a36Sopenharmony_ci size_t size; 20662306a36Sopenharmony_ci const void *prop; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC)) 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci prop = fdt_getprop(fdt, chosen_node, "linux,ima-kexec-buffer", &len); 21262306a36Sopenharmony_ci if (!prop) 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ret = do_get_kexec_buffer(prop, len, &addr, &size); 21662306a36Sopenharmony_ci fdt_delprop(fdt, chosen_node, "linux,ima-kexec-buffer"); 21762306a36Sopenharmony_ci if (ret) 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = fdt_find_and_del_mem_rsv(fdt, addr, size); 22162306a36Sopenharmony_ci if (!ret) 22262306a36Sopenharmony_ci pr_debug("Removed old IMA buffer reservation.\n"); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci#ifdef CONFIG_IMA_KEXEC 22662306a36Sopenharmony_ci/** 22762306a36Sopenharmony_ci * setup_ima_buffer - add IMA buffer information to the fdt 22862306a36Sopenharmony_ci * @image: kexec image being loaded. 22962306a36Sopenharmony_ci * @fdt: Flattened device tree for the next kernel. 23062306a36Sopenharmony_ci * @chosen_node: Offset to the chosen node. 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * Return: 0 on success, or negative errno on error. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_cistatic int setup_ima_buffer(const struct kimage *image, void *fdt, 23562306a36Sopenharmony_ci int chosen_node) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci int ret; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!image->ima_buffer_size) 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, 24362306a36Sopenharmony_ci "linux,ima-kexec-buffer", 24462306a36Sopenharmony_ci image->ima_buffer_addr, 24562306a36Sopenharmony_ci image->ima_buffer_size); 24662306a36Sopenharmony_ci if (ret < 0) 24762306a36Sopenharmony_ci return -EINVAL; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = fdt_add_mem_rsv(fdt, image->ima_buffer_addr, 25062306a36Sopenharmony_ci image->ima_buffer_size); 25162306a36Sopenharmony_ci if (ret) 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci pr_debug("IMA buffer at 0x%llx, size = 0x%zx\n", 25562306a36Sopenharmony_ci image->ima_buffer_addr, image->ima_buffer_size); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci#else /* CONFIG_IMA_KEXEC */ 26062306a36Sopenharmony_cistatic inline int setup_ima_buffer(const struct kimage *image, void *fdt, 26162306a36Sopenharmony_ci int chosen_node) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci#endif /* CONFIG_IMA_KEXEC */ 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * of_kexec_alloc_and_setup_fdt - Alloc and setup a new Flattened Device Tree 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * @image: kexec image being loaded. 27162306a36Sopenharmony_ci * @initrd_load_addr: Address where the next initrd will be loaded. 27262306a36Sopenharmony_ci * @initrd_len: Size of the next initrd, or 0 if there will be none. 27362306a36Sopenharmony_ci * @cmdline: Command line for the next kernel, or NULL if there will 27462306a36Sopenharmony_ci * be none. 27562306a36Sopenharmony_ci * @extra_fdt_size: Additional size for the new FDT buffer. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * Return: fdt on success, or NULL errno on error. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_civoid *of_kexec_alloc_and_setup_fdt(const struct kimage *image, 28062306a36Sopenharmony_ci unsigned long initrd_load_addr, 28162306a36Sopenharmony_ci unsigned long initrd_len, 28262306a36Sopenharmony_ci const char *cmdline, size_t extra_fdt_size) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci void *fdt; 28562306a36Sopenharmony_ci int ret, chosen_node, len; 28662306a36Sopenharmony_ci const void *prop; 28762306a36Sopenharmony_ci size_t fdt_size; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci fdt_size = fdt_totalsize(initial_boot_params) + 29062306a36Sopenharmony_ci (cmdline ? strlen(cmdline) : 0) + 29162306a36Sopenharmony_ci FDT_EXTRA_SPACE + 29262306a36Sopenharmony_ci extra_fdt_size; 29362306a36Sopenharmony_ci fdt = kvmalloc(fdt_size, GFP_KERNEL); 29462306a36Sopenharmony_ci if (!fdt) 29562306a36Sopenharmony_ci return NULL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = fdt_open_into(initial_boot_params, fdt, fdt_size); 29862306a36Sopenharmony_ci if (ret < 0) { 29962306a36Sopenharmony_ci pr_err("Error %d setting up the new device tree.\n", ret); 30062306a36Sopenharmony_ci goto out; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Remove memory reservation for the current device tree. */ 30462306a36Sopenharmony_ci ret = fdt_find_and_del_mem_rsv(fdt, __pa(initial_boot_params), 30562306a36Sopenharmony_ci fdt_totalsize(initial_boot_params)); 30662306a36Sopenharmony_ci if (ret == -EINVAL) { 30762306a36Sopenharmony_ci pr_err("Error removing memory reservation.\n"); 30862306a36Sopenharmony_ci goto out; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci chosen_node = fdt_path_offset(fdt, "/chosen"); 31262306a36Sopenharmony_ci if (chosen_node == -FDT_ERR_NOTFOUND) 31362306a36Sopenharmony_ci chosen_node = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), 31462306a36Sopenharmony_ci "chosen"); 31562306a36Sopenharmony_ci if (chosen_node < 0) { 31662306a36Sopenharmony_ci ret = chosen_node; 31762306a36Sopenharmony_ci goto out; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci ret = fdt_delprop(fdt, chosen_node, "linux,elfcorehdr"); 32162306a36Sopenharmony_ci if (ret && ret != -FDT_ERR_NOTFOUND) 32262306a36Sopenharmony_ci goto out; 32362306a36Sopenharmony_ci ret = fdt_delprop(fdt, chosen_node, "linux,usable-memory-range"); 32462306a36Sopenharmony_ci if (ret && ret != -FDT_ERR_NOTFOUND) 32562306a36Sopenharmony_ci goto out; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Did we boot using an initrd? */ 32862306a36Sopenharmony_ci prop = fdt_getprop(fdt, chosen_node, "linux,initrd-start", &len); 32962306a36Sopenharmony_ci if (prop) { 33062306a36Sopenharmony_ci u64 tmp_start, tmp_end, tmp_size; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci tmp_start = of_read_number(prop, len / 4); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci prop = fdt_getprop(fdt, chosen_node, "linux,initrd-end", &len); 33562306a36Sopenharmony_ci if (!prop) { 33662306a36Sopenharmony_ci ret = -EINVAL; 33762306a36Sopenharmony_ci goto out; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci tmp_end = of_read_number(prop, len / 4); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * kexec reserves exact initrd size, while firmware may 34462306a36Sopenharmony_ci * reserve a multiple of PAGE_SIZE, so check for both. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci tmp_size = tmp_end - tmp_start; 34762306a36Sopenharmony_ci ret = fdt_find_and_del_mem_rsv(fdt, tmp_start, tmp_size); 34862306a36Sopenharmony_ci if (ret == -ENOENT) 34962306a36Sopenharmony_ci ret = fdt_find_and_del_mem_rsv(fdt, tmp_start, 35062306a36Sopenharmony_ci round_up(tmp_size, PAGE_SIZE)); 35162306a36Sopenharmony_ci if (ret == -EINVAL) 35262306a36Sopenharmony_ci goto out; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* add initrd-* */ 35662306a36Sopenharmony_ci if (initrd_load_addr) { 35762306a36Sopenharmony_ci ret = fdt_setprop_u64(fdt, chosen_node, "linux,initrd-start", 35862306a36Sopenharmony_ci initrd_load_addr); 35962306a36Sopenharmony_ci if (ret) 36062306a36Sopenharmony_ci goto out; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret = fdt_setprop_u64(fdt, chosen_node, "linux,initrd-end", 36362306a36Sopenharmony_ci initrd_load_addr + initrd_len); 36462306a36Sopenharmony_ci if (ret) 36562306a36Sopenharmony_ci goto out; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ret = fdt_add_mem_rsv(fdt, initrd_load_addr, initrd_len); 36862306a36Sopenharmony_ci if (ret) 36962306a36Sopenharmony_ci goto out; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci ret = fdt_delprop(fdt, chosen_node, "linux,initrd-start"); 37362306a36Sopenharmony_ci if (ret && (ret != -FDT_ERR_NOTFOUND)) 37462306a36Sopenharmony_ci goto out; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci ret = fdt_delprop(fdt, chosen_node, "linux,initrd-end"); 37762306a36Sopenharmony_ci if (ret && (ret != -FDT_ERR_NOTFOUND)) 37862306a36Sopenharmony_ci goto out; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (image->type == KEXEC_TYPE_CRASH) { 38262306a36Sopenharmony_ci /* add linux,elfcorehdr */ 38362306a36Sopenharmony_ci ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, 38462306a36Sopenharmony_ci "linux,elfcorehdr", image->elf_load_addr, 38562306a36Sopenharmony_ci image->elf_headers_sz); 38662306a36Sopenharmony_ci if (ret) 38762306a36Sopenharmony_ci goto out; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * Avoid elfcorehdr from being stomped on in kdump kernel by 39162306a36Sopenharmony_ci * setting up memory reserve map. 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci ret = fdt_add_mem_rsv(fdt, image->elf_load_addr, 39462306a36Sopenharmony_ci image->elf_headers_sz); 39562306a36Sopenharmony_ci if (ret) 39662306a36Sopenharmony_ci goto out; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* add linux,usable-memory-range */ 39962306a36Sopenharmony_ci ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, 40062306a36Sopenharmony_ci "linux,usable-memory-range", crashk_res.start, 40162306a36Sopenharmony_ci crashk_res.end - crashk_res.start + 1); 40262306a36Sopenharmony_ci if (ret) 40362306a36Sopenharmony_ci goto out; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (crashk_low_res.end) { 40662306a36Sopenharmony_ci ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, 40762306a36Sopenharmony_ci "linux,usable-memory-range", 40862306a36Sopenharmony_ci crashk_low_res.start, 40962306a36Sopenharmony_ci crashk_low_res.end - crashk_low_res.start + 1); 41062306a36Sopenharmony_ci if (ret) 41162306a36Sopenharmony_ci goto out; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* add bootargs */ 41662306a36Sopenharmony_ci if (cmdline) { 41762306a36Sopenharmony_ci ret = fdt_setprop_string(fdt, chosen_node, "bootargs", cmdline); 41862306a36Sopenharmony_ci if (ret) 41962306a36Sopenharmony_ci goto out; 42062306a36Sopenharmony_ci } else { 42162306a36Sopenharmony_ci ret = fdt_delprop(fdt, chosen_node, "bootargs"); 42262306a36Sopenharmony_ci if (ret && (ret != -FDT_ERR_NOTFOUND)) 42362306a36Sopenharmony_ci goto out; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* add kaslr-seed */ 42762306a36Sopenharmony_ci ret = fdt_delprop(fdt, chosen_node, "kaslr-seed"); 42862306a36Sopenharmony_ci if (ret == -FDT_ERR_NOTFOUND) 42962306a36Sopenharmony_ci ret = 0; 43062306a36Sopenharmony_ci else if (ret) 43162306a36Sopenharmony_ci goto out; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (rng_is_initialized()) { 43462306a36Sopenharmony_ci u64 seed = get_random_u64(); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci ret = fdt_setprop_u64(fdt, chosen_node, "kaslr-seed", seed); 43762306a36Sopenharmony_ci if (ret) 43862306a36Sopenharmony_ci goto out; 43962306a36Sopenharmony_ci } else { 44062306a36Sopenharmony_ci pr_notice("RNG is not initialised: omitting \"%s\" property\n", 44162306a36Sopenharmony_ci "kaslr-seed"); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* add rng-seed */ 44562306a36Sopenharmony_ci if (rng_is_initialized()) { 44662306a36Sopenharmony_ci void *rng_seed; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ret = fdt_setprop_placeholder(fdt, chosen_node, "rng-seed", 44962306a36Sopenharmony_ci RNG_SEED_SIZE, &rng_seed); 45062306a36Sopenharmony_ci if (ret) 45162306a36Sopenharmony_ci goto out; 45262306a36Sopenharmony_ci get_random_bytes(rng_seed, RNG_SEED_SIZE); 45362306a36Sopenharmony_ci } else { 45462306a36Sopenharmony_ci pr_notice("RNG is not initialised: omitting \"%s\" property\n", 45562306a36Sopenharmony_ci "rng-seed"); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0); 45962306a36Sopenharmony_ci if (ret) 46062306a36Sopenharmony_ci goto out; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci remove_ima_buffer(fdt, chosen_node); 46362306a36Sopenharmony_ci ret = setup_ima_buffer(image, fdt, fdt_path_offset(fdt, "/chosen")); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ciout: 46662306a36Sopenharmony_ci if (ret) { 46762306a36Sopenharmony_ci kvfree(fdt); 46862306a36Sopenharmony_ci fdt = NULL; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return fdt; 47262306a36Sopenharmony_ci} 473