diff options
Diffstat (limited to 'drivers/of/fdt.c')
| -rw-r--r-- | drivers/of/fdt.c | 205 |
1 files changed, 117 insertions, 88 deletions
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index a8a04f27915b..d378d4b4109f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -8,7 +8,6 @@ #define pr_fmt(fmt) "OF: fdt: " fmt -#include <linux/acpi.h> #include <linux/crash_dump.h> #include <linux/crc32.h> #include <linux/kernel.h> @@ -26,6 +25,7 @@ #include <linux/serial_core.h> #include <linux/sysfs.h> #include <linux/random.h> +#include <linux/kexec_handover.h> #include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #include <asm/page.h> @@ -34,7 +34,7 @@ /* * __dtb_empty_root_begin[] and __dtb_empty_root_end[] magically created by - * cmd_dt_S_dtb in scripts/Makefile.lib + * cmd_wrap_S_dtb in scripts/Makefile.dtbs */ extern uint8_t __dtb_empty_root_begin[]; extern uint8_t __dtb_empty_root_end[]; @@ -52,28 +52,7 @@ void __init of_fdt_limit_memory(int limit) int memory; int len; const void *val; - int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; - int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT; - const __be32 *addr_prop; - const __be32 *size_prop; - int root_offset; - int cell_size; - - root_offset = fdt_path_offset(initial_boot_params, "/"); - if (root_offset < 0) - return; - - addr_prop = fdt_getprop(initial_boot_params, root_offset, - "#address-cells", NULL); - if (addr_prop) - nr_address_cells = fdt32_to_cpu(*addr_prop); - - size_prop = fdt_getprop(initial_boot_params, root_offset, - "#size-cells", NULL); - if (size_prop) - nr_size_cells = fdt32_to_cpu(*size_prop); - - cell_size = sizeof(uint32_t)*(nr_address_cells + nr_size_cells); + int cell_size = sizeof(uint32_t)*(dt_root_addr_cells + dt_root_size_cells); memory = fdt_path_offset(initial_boot_params, "/memory"); if (memory > 0) { @@ -478,6 +457,7 @@ int __initdata dt_root_addr_cells; int __initdata dt_root_size_cells; void *initial_boot_params __ro_after_init; +phys_addr_t initial_boot_params_pa __ro_after_init; #ifdef CONFIG_OF_EARLY_FLATTREE @@ -517,6 +497,7 @@ static void __init fdt_reserve_elfcorehdr(void) void __init early_init_fdt_scan_reserved_mem(void) { int n; + int res; u64 base, size; if (!initial_boot_params) @@ -527,13 +508,15 @@ void __init early_init_fdt_scan_reserved_mem(void) /* Process header /memreserve/ fields */ for (n = 0; ; n++) { - fdt_get_mem_rsv(initial_boot_params, n, &base, &size); + res = fdt_get_mem_rsv(initial_boot_params, n, &base, &size); + if (res) { + pr_err("Invalid memory reservation block index %d\n", n); + break; + } if (!size) break; memblock_reserve(base, size); } - - fdt_init_reserved_mem(); } /** @@ -642,6 +625,47 @@ const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, return fdt_getprop(initial_boot_params, node, name, size); } +const __be32 *__init of_flat_dt_get_addr_size_prop(unsigned long node, + const char *name, + int *entries) +{ + const __be32 *prop; + int len, elen = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); + + prop = of_get_flat_dt_prop(node, name, &len); + if (!prop || len % elen) { + *entries = 0; + return NULL; + } + + *entries = len / elen; + return prop; +} + +bool __init of_flat_dt_get_addr_size(unsigned long node, const char *name, + u64 *addr, u64 *size) +{ + const __be32 *prop; + int entries; + + prop = of_flat_dt_get_addr_size_prop(node, name, &entries); + if (!prop || entries != 1) + return false; + + of_flat_dt_read_addr_size(prop, 0, addr, size); + return true; +} + +void __init of_flat_dt_read_addr_size(const __be32 *prop, int entry_index, + u64 *addr, u64 *size) +{ + int entry_cells = dt_root_addr_cells + dt_root_size_cells; + prop += entry_cells * entry_index; + + *addr = dt_mem_next_cell(dt_root_addr_cells, &prop); + *size = dt_mem_next_cell(dt_root_size_cells, &prop); +} + /** * of_fdt_is_compatible - Return true if given node from the given blob has * compat in its compatible list @@ -829,21 +853,15 @@ static void __init early_init_dt_check_for_initrd(unsigned long node) */ static void __init early_init_dt_check_for_elfcorehdr(unsigned long node) { - const __be32 *prop; - int len; - if (!IS_ENABLED(CONFIG_CRASH_DUMP)) return; pr_debug("Looking for elfcorehdr property... "); - prop = of_get_flat_dt_prop(node, "linux,elfcorehdr", &len); - if (!prop || (len < (dt_root_addr_cells + dt_root_size_cells))) + if (!of_flat_dt_get_addr_size(node, "linux,elfcorehdr", + &elfcorehdr_addr, &elfcorehdr_size)) return; - elfcorehdr_addr = dt_mem_next_cell(dt_root_addr_cells, &prop); - elfcorehdr_size = dt_mem_next_cell(dt_root_size_cells, &prop); - pr_debug("elfcorehdr_start=0x%llx elfcorehdr_size=0x%llx\n", elfcorehdr_addr, elfcorehdr_size); } @@ -866,8 +884,9 @@ static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND; void __init early_init_dt_check_for_usable_mem_range(void) { struct memblock_region rgn[MAX_USABLE_RANGES] = {0}; - const __be32 *prop, *endp; + const __be32 *prop; int len, i; + u64 base, size; unsigned long node = chosen_node_offset; if ((long)node < 0) @@ -875,14 +894,17 @@ void __init early_init_dt_check_for_usable_mem_range(void) pr_debug("Looking for usable-memory-range property... "); - prop = of_get_flat_dt_prop(node, "linux,usable-memory-range", &len); - if (!prop || (len % (dt_root_addr_cells + dt_root_size_cells))) + prop = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory-range", + &len); + if (!prop) return; - endp = prop + (len / sizeof(__be32)); - for (i = 0; i < MAX_USABLE_RANGES && prop < endp; i++) { - rgn[i].base = dt_mem_next_cell(dt_root_addr_cells, &prop); - rgn[i].size = dt_mem_next_cell(dt_root_size_cells, &prop); + len = min(len, MAX_USABLE_RANGES); + + for (i = 0; i < len; i++) { + of_flat_dt_read_addr_size(prop, i, &base, &size); + rgn[i].base = base; + rgn[i].size = size; pr_debug("cap_mem_regions[%d]: base=%pa, size=%pa\n", i, &rgn[i].base, &rgn[i].size); @@ -893,6 +915,28 @@ void __init early_init_dt_check_for_usable_mem_range(void) memblock_add(rgn[i].base, rgn[i].size); } +/** + * early_init_dt_check_kho - Decode info required for kexec handover from DT + */ +static void __init early_init_dt_check_kho(void) +{ + unsigned long node = chosen_node_offset; + u64 fdt_start, fdt_size, scratch_start, scratch_size; + + if (!IS_ENABLED(CONFIG_KEXEC_HANDOVER) || (long)node < 0) + return; + + if (!of_flat_dt_get_addr_size(node, "linux,kho-fdt", + &fdt_start, &fdt_size)) + return; + + if (!of_flat_dt_get_addr_size(node, "linux,kho-scratch", + &scratch_start, &scratch_size)) + return; + + kho_populate(fdt_start, fdt_size, scratch_start, scratch_size); +} + #ifdef CONFIG_SERIAL_EARLYCON int __init early_init_dt_scan_chosen_stdout(void) @@ -959,12 +1003,12 @@ int __init early_init_dt_scan_root(void) dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; prop = of_get_flat_dt_prop(node, "#size-cells", NULL); - if (prop) + if (!WARN(!prop, "No '#size-cells' in root node\n")) dt_root_size_cells = be32_to_cpup(prop); pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells); prop = of_get_flat_dt_prop(node, "#address-cells", NULL); - if (prop) + if (!WARN(!prop, "No '#address-cells' in root node\n")) dt_root_addr_cells = be32_to_cpup(prop); pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells); @@ -989,8 +1033,8 @@ int __init early_init_dt_scan_memory(void) fdt_for_each_subnode(node, fdt, 0) { const char *type = of_get_flat_dt_prop(node, "device_type", NULL); - const __be32 *reg, *endp; - int l; + const __be32 *reg; + int i, l; bool hotpluggable; /* We are scanning "memory" nodes only */ @@ -1000,23 +1044,21 @@ int __init early_init_dt_scan_memory(void) if (!of_fdt_device_is_available(fdt, node)) continue; - reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); + reg = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory", &l); if (reg == NULL) - reg = of_get_flat_dt_prop(node, "reg", &l); + reg = of_flat_dt_get_addr_size_prop(node, "reg", &l); if (reg == NULL) continue; - endp = reg + (l / sizeof(__be32)); hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL); - pr_debug("memory scan node %s, reg size %d,\n", + pr_debug("memory scan node %s, reg {addr,size} entries %d,\n", fdt_get_name(fdt, node, NULL), l); - while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { + for (i = 0; i < l; i++) { u64 base, size; - base = dt_mem_next_cell(dt_root_addr_cells, ®); - size = dt_mem_next_cell(dt_root_size_cells, ®); + of_flat_dt_read_addr_size(reg, i, &base, &size); if (size == 0) continue; @@ -1148,28 +1190,27 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) static void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) { - void *ptr = memblock_alloc(size, align); - - if (!ptr) - panic("%s: Failed to allocate %llu bytes align=0x%llx\n", - __func__, size, align); - - return ptr; + return memblock_alloc_or_panic(size, align); } -bool __init early_init_dt_verify(void *params) +bool __init early_init_dt_verify(void *dt_virt, phys_addr_t dt_phys) { - if (!params) + if (!dt_virt) return false; /* check device tree validity */ - if (fdt_check_header(params)) + if (fdt_check_header(dt_virt)) return false; /* Setup flat device-tree pointer */ - initial_boot_params = params; + initial_boot_params = dt_virt; + initial_boot_params_pa = dt_phys; of_fdt_crc32 = crc32_be(~0, initial_boot_params, fdt_totalsize(initial_boot_params)); + + /* Initialize {size,address}-cells info */ + early_init_dt_scan_root(); + return true; } @@ -1178,9 +1219,6 @@ void __init early_init_dt_scan_nodes(void) { int rc; - /* Initialize {size,address}-cells info */ - early_init_dt_scan_root(); - /* Retrieve various information from the /chosen node */ rc = early_init_dt_scan_chosen(boot_command_line); if (rc) @@ -1191,13 +1229,16 @@ void __init early_init_dt_scan_nodes(void) /* Handle linux,usable-memory-range property */ early_init_dt_check_for_usable_mem_range(); + + /* Handle kexec handover */ + early_init_dt_check_kho(); } -bool __init early_init_dt_scan(void *params) +bool __init early_init_dt_scan(void *dt_virt, phys_addr_t dt_phys) { bool status; - status = early_init_dt_verify(params); + status = early_init_dt_verify(dt_virt, dt_phys); if (!status) return false; @@ -1232,14 +1273,10 @@ void __init unflatten_device_tree(void) { void *fdt = initial_boot_params; - /* Don't use the bootloader provided DTB if ACPI is enabled */ - if (!acpi_disabled) - fdt = NULL; + /* Save the statically-placed regions in the reserved_mem array */ + fdt_scan_reserved_mem_reg_nodes(); - /* - * Populate an empty root node when ACPI is enabled or bootloader - * doesn't provide one. - */ + /* Populate an empty root node when bootloader doesn't provide one */ if (!fdt) { fdt = (void *) __dtb_empty_root_begin; /* fdt_totalsize() will be used for copy size */ @@ -1281,18 +1318,9 @@ void __init unflatten_and_copy_device_tree(void) } #ifdef CONFIG_SYSFS -static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - memcpy(buf, initial_boot_params + off, count); - return count; -} - static int __init of_fdt_raw_init(void) { - static struct bin_attribute of_fdt_raw_attr = - __BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, 0); + static __ro_after_init BIN_ATTR_SIMPLE_ADMIN_RO(fdt); if (!initial_boot_params) return 0; @@ -1302,8 +1330,9 @@ static int __init of_fdt_raw_init(void) pr_warn("not creating '/sys/firmware/fdt': CRC check failed\n"); return 0; } - of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params); - return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr); + bin_attr_fdt.private = initial_boot_params; + bin_attr_fdt.size = fdt_totalsize(initial_boot_params); + return sysfs_create_bin_file(firmware_kobj, &bin_attr_fdt); } late_initcall(of_fdt_raw_init); #endif |
