diff options
Diffstat (limited to 'drivers/of/fdt.c')
-rw-r--r-- | drivers/of/fdt.c | 263 |
1 files changed, 70 insertions, 193 deletions
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index bf502ba8da95..aedd0e2dcd89 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -16,7 +16,6 @@ #include <linux/mutex.h> #include <linux/of.h> #include <linux/of_fdt.h> -#include <linux/of_reserved_mem.h> #include <linux/sizes.h> #include <linux/string.h> #include <linux/errno.h> @@ -33,6 +32,13 @@ #include "of_private.h" /* + * __dtb_empty_root_begin[] and __dtb_empty_root_end[] magically created by + * cmd_wrap_S_dtb in scripts/Makefile.dtbs + */ +extern uint8_t __dtb_empty_root_begin[]; +extern uint8_t __dtb_empty_root_end[]; + +/* * of_fdt_limit_memory - limit the number of regions in the /memory node * @limit: maximum entries * @@ -45,28 +51,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) { @@ -80,7 +65,7 @@ void __init of_fdt_limit_memory(int limit) } } -static bool of_fdt_device_is_available(const void *blob, unsigned long node) +bool of_fdt_device_is_available(const void *blob, unsigned long node) { const char *status = fdt_getprop(blob, node, "status", NULL); @@ -471,131 +456,12 @@ 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 static u32 of_fdt_crc32; -static int __init early_init_dt_reserve_memory(phys_addr_t base, - phys_addr_t size, bool nomap) -{ - if (nomap) { - /* - * If the memory is already reserved (by another region), we - * should not allow it to be marked nomap, but don't worry - * if the region isn't memory as it won't be mapped. - */ - if (memblock_overlaps_region(&memblock.memory, base, size) && - memblock_is_region_reserved(base, size)) - return -EBUSY; - - return memblock_mark_nomap(base, size); - } - return memblock_reserve(base, size); -} - -/* - * __reserved_mem_reserve_reg() - reserve all memory described in 'reg' property - */ -static int __init __reserved_mem_reserve_reg(unsigned long node, - const char *uname) -{ - int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); - phys_addr_t base, size; - int len; - const __be32 *prop; - int first = 1; - bool nomap; - - prop = of_get_flat_dt_prop(node, "reg", &len); - if (!prop) - return -ENOENT; - - if (len && len % t_len != 0) { - pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", - uname); - return -EINVAL; - } - - nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - - while (len >= t_len) { - base = dt_mem_next_cell(dt_root_addr_cells, &prop); - size = dt_mem_next_cell(dt_root_size_cells, &prop); - - if (size && - early_init_dt_reserve_memory(base, size, nomap) == 0) - pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n", - uname, &base, (unsigned long)(size / SZ_1M)); - else - pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n", - uname, &base, (unsigned long)(size / SZ_1M)); - - len -= t_len; - if (first) { - fdt_reserved_mem_save_node(node, uname, base, size); - first = 0; - } - } - return 0; -} - -/* - * __reserved_mem_check_root() - check if #size-cells, #address-cells provided - * in /reserved-memory matches the values supported by the current implementation, - * also check if ranges property has been provided - */ -static int __init __reserved_mem_check_root(unsigned long node) -{ - const __be32 *prop; - - prop = of_get_flat_dt_prop(node, "#size-cells", NULL); - if (!prop || be32_to_cpup(prop) != dt_root_size_cells) - return -EINVAL; - - prop = of_get_flat_dt_prop(node, "#address-cells", NULL); - if (!prop || be32_to_cpup(prop) != dt_root_addr_cells) - return -EINVAL; - - prop = of_get_flat_dt_prop(node, "ranges", NULL); - if (!prop) - return -EINVAL; - return 0; -} - -/* - * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory - */ -static int __init fdt_scan_reserved_mem(void) -{ - int node, child; - const void *fdt = initial_boot_params; - - node = fdt_path_offset(fdt, "/reserved-memory"); - if (node < 0) - return -ENODEV; - - if (__reserved_mem_check_root(node) != 0) { - pr_err("Reserved memory: unsupported node format, ignoring\n"); - return -EINVAL; - } - - fdt_for_each_subnode(child, fdt, node) { - const char *uname; - int err; - - if (!of_fdt_device_is_available(fdt, child)) - continue; - - uname = fdt_get_name(fdt, child, NULL); - - err = __reserved_mem_reserve_reg(child, uname); - if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) - fdt_reserved_mem_save_node(child, uname, 0, 0); - } - return 0; -} - /* * fdt_reserve_elfcorehdr() - reserves memory for elf core header * @@ -630,6 +496,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) @@ -640,13 +507,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(); } /** @@ -1072,12 +941,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); @@ -1261,28 +1130,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; } @@ -1291,9 +1159,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) @@ -1306,11 +1171,11 @@ void __init early_init_dt_scan_nodes(void) early_init_dt_check_for_usable_mem_range(); } -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; @@ -1318,6 +1183,21 @@ bool __init early_init_dt_scan(void *params) return true; } +static void *__init copy_device_tree(void *fdt) +{ + int size; + void *dt; + + size = fdt_totalsize(fdt); + dt = early_init_dt_alloc_memory_arch(size, + roundup_pow_of_two(FDT_V17_SIZE)); + + if (dt) + memcpy(dt, fdt, size); + + return dt; +} + /** * unflatten_device_tree - create tree of device_nodes from flat blob * @@ -1328,7 +1208,25 @@ bool __init early_init_dt_scan(void *params) */ void __init unflatten_device_tree(void) { - __unflatten_device_tree(initial_boot_params, NULL, &of_root, + void *fdt = initial_boot_params; + + /* Save the statically-placed regions in the reserved_mem array */ + fdt_scan_reserved_mem_reg_nodes(); + + /* 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 */ + if (fdt_totalsize(fdt) > + __dtb_empty_root_end - __dtb_empty_root_begin) { + pr_err("invalid size in dtb_empty_root\n"); + return; + } + of_fdt_crc32 = crc32_be(~0, fdt, fdt_totalsize(fdt)); + fdt = copy_device_tree(fdt); + } + + __unflatten_device_tree(fdt, NULL, &of_root, early_init_dt_alloc_memory_arch, false); /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ @@ -1350,38 +1248,16 @@ void __init unflatten_device_tree(void) */ void __init unflatten_and_copy_device_tree(void) { - int size; - void *dt; + if (initial_boot_params) + initial_boot_params = copy_device_tree(initial_boot_params); - if (!initial_boot_params) { - pr_warn("No valid device tree found, continuing without\n"); - return; - } - - size = fdt_totalsize(initial_boot_params); - dt = early_init_dt_alloc_memory_arch(size, - roundup_pow_of_two(FDT_V17_SIZE)); - - if (dt) { - memcpy(dt, initial_boot_params, size); - initial_boot_params = dt; - } unflatten_device_tree(); } #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; @@ -1391,8 +1267,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 |