diff options
Diffstat (limited to 'drivers/of/fdt.c')
| -rw-r--r-- | drivers/of/fdt.c | 690 |
1 files changed, 369 insertions, 321 deletions
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 4602e467ca8b..d378d4b4109f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) "OF: fdt: " fmt +#include <linux/crash_dump.h> #include <linux/crc32.h> #include <linux/kernel.h> #include <linux/initrd.h> @@ -15,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> @@ -25,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> @@ -32,6 +33,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 * @@ -44,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) { @@ -79,7 +66,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); @@ -193,19 +180,15 @@ static void populate_properties(const void *blob, pp->length = len; pp->value = pp + 1; *pprev = pp; - pprev = &pp->next; memcpy(pp->value, ps, len - 1); ((char *)pp->value)[len - 1] = 0; pr_debug("fixed up name for %s -> %s\n", nodename, (char *)pp->value); } } - - if (!dryrun) - *pprev = NULL; } -static bool populate_node(const void *blob, +static int populate_node(const void *blob, int offset, void **mem, struct device_node *dad, @@ -214,24 +197,24 @@ static bool populate_node(const void *blob, { struct device_node *np; const char *pathp; - unsigned int l, allocl; + int len; - pathp = fdt_get_name(blob, offset, &l); + pathp = fdt_get_name(blob, offset, &len); if (!pathp) { *pnp = NULL; - return false; + return len; } - allocl = ++l; + len++; - np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl, + np = unflatten_dt_alloc(mem, sizeof(struct device_node) + len, __alignof__(struct device_node)); if (!dryrun) { char *fn; of_node_init(np); np->full_name = fn = ((char *)np) + sizeof(*np); - memcpy(fn, pathp, l); + memcpy(fn, pathp, len); if (dad != NULL) { np->parent = dad; @@ -248,7 +231,7 @@ static bool populate_node(const void *blob, } *pnp = np; - return true; + return 0; } static void reverse_nodes(struct device_node *parent) @@ -282,7 +265,7 @@ static void reverse_nodes(struct device_node *parent) * @dad: Parent struct device_node * @nodepp: The device_node tree created by the call * - * It returns the size of unflattened device tree or error code + * Return: The size of unflattened device tree or error code */ static int unflatten_dt_nodes(const void *blob, void *mem, @@ -295,6 +278,7 @@ static int unflatten_dt_nodes(const void *blob, struct device_node *nps[FDT_MAX_DEPTH]; void *base = mem; bool dryrun = !base; + int ret; if (nodepp) *nodepp = NULL; @@ -315,16 +299,17 @@ static int unflatten_dt_nodes(const void *blob, for (offset = 0; offset >= 0 && depth >= initial_depth; offset = fdt_next_node(blob, offset, &depth)) { - if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH)) + if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH - 1)) continue; if (!IS_ENABLED(CONFIG_OF_KOBJ) && !of_fdt_device_is_available(blob, offset)) continue; - if (!populate_node(blob, offset, &mem, nps[depth], - &nps[depth+1], dryrun)) - return mem - base; + ret = populate_node(blob, offset, &mem, nps[depth], + &nps[depth+1], dryrun); + if (ret < 0) + return ret; if (!dryrun && nodepp && !*nodepp) *nodepp = nps[depth+1]; @@ -349,11 +334,6 @@ static int unflatten_dt_nodes(const void *blob, /** * __unflatten_device_tree - create tree of device_nodes from flat blob - * - * unflattens a device-tree, creating the - * tree of struct device_node. It also fills the "name" and "type" - * pointers of the nodes so the normal device-tree walking functions - * can be used. * @blob: The blob to expand * @dad: Parent device node * @mynodes: The device_node tree created by the call @@ -361,7 +341,11 @@ static int unflatten_dt_nodes(const void *blob, * for the resulting tree * @detached: if true set OF_DETACHED on @mynodes * - * Returns NULL on failure or the memory chunk containing the unflattened + * unflattens a device-tree, creating the tree of struct device_node. It also + * fills the "name" and "type" pointers of the nodes so the normal device-tree + * walking functions can be used. + * + * Return: NULL on failure or the memory chunk containing the unflattened * device tree on success. */ void *__unflatten_device_tree(const void *blob, @@ -372,6 +356,10 @@ void *__unflatten_device_tree(const void *blob, { int size; void *mem; + int ret; + + if (mynodes) + *mynodes = NULL; pr_debug(" -> unflatten_device_tree()\n"); @@ -392,7 +380,7 @@ void *__unflatten_device_tree(const void *blob, /* First pass, scan for size */ size = unflatten_dt_nodes(blob, NULL, dad, NULL); - if (size < 0) + if (size <= 0) return NULL; size = ALIGN(size, 4); @@ -410,12 +398,16 @@ void *__unflatten_device_tree(const void *blob, pr_debug(" unflattening %p...\n", mem); /* Second pass, do actual unflattening */ - unflatten_dt_nodes(blob, mem, dad, mynodes); + ret = unflatten_dt_nodes(blob, mem, dad, mynodes); + if (be32_to_cpup(mem + size) != 0xdeadbeef) pr_warn("End of tree marker overwritten: %08x\n", be32_to_cpup(mem + size)); - if (detached && mynodes) { + if (ret <= 0) + return NULL; + + if (detached && mynodes && *mynodes) { of_node_set_flag(*mynodes, OF_DETACHED); pr_debug("unflattened tree is detached\n"); } @@ -442,7 +434,7 @@ static DEFINE_MUTEX(of_fdt_unflatten_mutex); * pointers of the nodes so the normal device-tree walking functions * can be used. * - * Returns NULL on failure or the memory chunk containing the unflattened + * Return: NULL on failure or the memory chunk containing the unflattened * device tree on success. */ void *of_fdt_unflatten_tree(const unsigned long *blob, @@ -465,115 +457,34 @@ 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; -/** - * __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_arch(base, size, nomap) == 0) - pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n", - uname, &base, (unsigned long)size / SZ_1M); - else - pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld 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 +/* + * fdt_reserve_elfcorehdr() - reserves memory for elf core header + * + * This function reserves the memory occupied by an elf core header + * described in the device tree. This region contains all the + * information about primary kernel's core image and is used by a dump + * capture kernel to access the system memory on primary kernel. */ -static int __init __reserved_mem_check_root(unsigned long node) +static void __init fdt_reserve_elfcorehdr(void) { - 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; -} + if (!IS_ENABLED(CONFIG_CRASH_DUMP) || !elfcorehdr_size) + return; -/** - * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory - */ -static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, - int depth, void *data) -{ - static int found; - int err; - - if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { - if (__reserved_mem_check_root(node) != 0) { - pr_err("Reserved memory: unsupported node format, ignoring\n"); - /* break scan */ - return 1; - } - found = 1; - /* scan next node */ - return 0; - } else if (!found) { - /* scan next node */ - return 0; - } else if (found && depth < 2) { - /* scanning of /reserved-memory has been finished */ - return 1; + if (memblock_is_region_reserved(elfcorehdr_addr, elfcorehdr_size)) { + pr_warn("elfcorehdr is overlapped\n"); + return; } - if (!of_fdt_device_is_available(initial_boot_params, node)) - return 0; - - err = __reserved_mem_reserve_reg(node, uname); - if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL)) - fdt_reserved_mem_save_node(node, uname, 0, 0); + memblock_reserve(elfcorehdr_addr, elfcorehdr_size); - /* scan next node */ - return 0; + pr_info("Reserving %llu KiB of memory at 0x%llx for elfcorehdr\n", + elfcorehdr_size >> 10, elfcorehdr_addr); } /** @@ -586,21 +497,26 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, void __init early_init_fdt_scan_reserved_mem(void) { int n; + int res; u64 base, size; if (!initial_boot_params) return; + fdt_scan_reserved_mem(); + fdt_reserve_elfcorehdr(); + /* 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; - early_init_dt_reserve_memory_arch(base, size, false); + memblock_reserve(base, size); } - - of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); - fdt_init_reserved_mem(); } /** @@ -612,9 +528,8 @@ void __init early_init_fdt_reserve_self(void) return; /* Reserve the dtb region */ - early_init_dt_reserve_memory_arch(__pa(initial_boot_params), - fdt_totalsize(initial_boot_params), - false); + memblock_reserve(__pa(initial_boot_params), + fdt_totalsize(initial_boot_params)); } /** @@ -650,6 +565,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, /** * of_scan_flat_dt_subnodes - scan sub-nodes of a node call callback on each. + * @parent: parent node * @it: callback function * @data: context data pointer * @@ -689,7 +605,7 @@ int __init of_get_flat_dt_subnode_by_name(unsigned long node, const char *uname) return fdt_subnode_offset(initial_boot_params, node, uname); } -/** +/* * of_get_flat_dt_root - find the root node in the flat blob */ unsigned long __init of_get_flat_dt_root(void) @@ -697,7 +613,7 @@ unsigned long __init of_get_flat_dt_root(void) return 0; } -/** +/* * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr * * This function can be used within scan_flattened_dt callback to get @@ -709,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 @@ -716,7 +673,7 @@ const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, * @node: node to test * @compat: compatible string to compare with compatible list. * - * On match, returns a non-zero value with smaller values returned for more + * Return: a non-zero value on match with smaller values returned for more * specific compatible values. */ static int of_fdt_is_compatible(const void *blob, @@ -751,7 +708,7 @@ int __init of_flat_dt_is_compatible(unsigned long node, const char *compat) return of_fdt_is_compatible(initial_boot_params, node, compat); } -/** +/* * of_flat_dt_match - Return true if node matches a list of compatible values */ static int __init of_flat_dt_match(unsigned long node, const char *const *compat) @@ -771,23 +728,14 @@ static int __init of_flat_dt_match(unsigned long node, const char *const *compat return score; } -/** - * of_get_flat_dt_prop - Given a node in the flat blob, return the phandle +/* + * of_get_flat_dt_phandle - Given a node in the flat blob, return the phandle */ uint32_t __init of_get_flat_dt_phandle(unsigned long node) { return fdt_get_phandle(initial_boot_params, node); } -struct fdt_scan_status { - const char *name; - int namelen; - int depth; - int found; - int (*iterator)(unsigned long node, const char *uname, int depth, void *data); - void *data; -}; - const char * __init of_flat_dt_get_machine_name(void) { const char *name; @@ -848,16 +796,16 @@ const void * __init of_flat_dt_match_machine(const void *default_match, return best_data; } -#ifdef CONFIG_BLK_DEV_INITRD static void __early_init_dt_declare_initrd(unsigned long start, unsigned long end) { - /* ARM64 would cause a BUG to occur here when CONFIG_DEBUG_VM is - * enabled since __va() is called too early. ARM64 does make use - * of phys_initrd_start/phys_initrd_size so we can skip this - * conversion. + /* + * __va() is not yet available this early on some platforms. In that + * case, the platform uses phys_initrd_start/phys_initrd_size instead + * and does the VA conversion itself. */ - if (!IS_ENABLED(CONFIG_ARM64)) { + if (!IS_ENABLED(CONFIG_ARM64) && + !(IS_ENABLED(CONFIG_RISCV) && IS_ENABLED(CONFIG_64BIT))) { initrd_start = (unsigned long)__va(start); initrd_end = (unsigned long)__va(end); initrd_below_start_ok = 1; @@ -874,6 +822,9 @@ static void __init early_init_dt_check_for_initrd(unsigned long node) int len; const __be32 *prop; + if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD)) + return; + pr_debug("Looking for initrd properties... "); prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len); @@ -885,19 +836,106 @@ static void __init early_init_dt_check_for_initrd(unsigned long node) if (!prop) return; end = of_read_number(prop, len/4); + if (start > end) + return; __early_init_dt_declare_initrd(start, end); phys_initrd_start = start; phys_initrd_size = end - start; - pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n", - (unsigned long long)start, (unsigned long long)end); + pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n", start, end); } -#else -static inline void early_init_dt_check_for_initrd(unsigned long node) + +/** + * early_init_dt_check_for_elfcorehdr - Decode elfcorehdr location from flat + * tree + * @node: reference to node containing elfcorehdr location ('chosen') + */ +static void __init early_init_dt_check_for_elfcorehdr(unsigned long node) +{ + if (!IS_ENABLED(CONFIG_CRASH_DUMP)) + return; + + pr_debug("Looking for elfcorehdr property... "); + + if (!of_flat_dt_get_addr_size(node, "linux,elfcorehdr", + &elfcorehdr_addr, &elfcorehdr_size)) + return; + + pr_debug("elfcorehdr_start=0x%llx elfcorehdr_size=0x%llx\n", + elfcorehdr_addr, elfcorehdr_size); +} + +static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND; + +/* + * The main usage of linux,usable-memory-range is for crash dump kernel. + * Originally, the number of usable-memory regions is one. Now there may + * be two regions, low region and high region. + * To make compatibility with existing user-space and older kdump, the low + * region is always the last range of linux,usable-memory-range if exist. + */ +#define MAX_USABLE_RANGES 2 + +/** + * early_init_dt_check_for_usable_mem_range - Decode usable memory range + * location from flat tree + */ +void __init early_init_dt_check_for_usable_mem_range(void) +{ + struct memblock_region rgn[MAX_USABLE_RANGES] = {0}; + const __be32 *prop; + int len, i; + u64 base, size; + unsigned long node = chosen_node_offset; + + if ((long)node < 0) + return; + + pr_debug("Looking for usable-memory-range property... "); + + prop = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory-range", + &len); + if (!prop) + return; + + 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); + } + + memblock_cap_memory_range(rgn[0].base, rgn[0].size); + for (i = 1; i < MAX_USABLE_RANGES && rgn[i].size; i++) + 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); } -#endif /* CONFIG_BLK_DEV_INITRD */ #ifdef CONFIG_SERIAL_EARLYCON @@ -906,8 +944,9 @@ int __init early_init_dt_scan_chosen_stdout(void) int offset; const char *p, *q, *options = NULL; int l; - const struct earlycon_id **p_match; + const struct earlycon_id *match; const void *fdt = initial_boot_params; + int ret; offset = fdt_path_offset(fdt, "/chosen"); if (offset < 0) @@ -933,49 +972,47 @@ int __init early_init_dt_scan_chosen_stdout(void) return 0; } - for (p_match = __earlycon_table; p_match < __earlycon_table_end; - p_match++) { - const struct earlycon_id *match = *p_match; - + for (match = __earlycon_table; match < __earlycon_table_end; match++) { if (!match->compatible[0]) continue; if (fdt_node_check_compatible(fdt, offset, match->compatible)) continue; - if (of_setup_earlycon(match, offset, options) == 0) + ret = of_setup_earlycon(match, offset, options); + if (!ret || ret == -EALREADY) return 0; } return -ENODEV; } #endif -/** +/* * early_init_dt_scan_root - fetch the top level address and size cells */ -int __init early_init_dt_scan_root(unsigned long node, const char *uname, - int depth, void *data) +int __init early_init_dt_scan_root(void) { const __be32 *prop; + const void *fdt = initial_boot_params; + int node = fdt_path_offset(fdt, "/"); - if (depth != 0) - return 0; + if (node < 0) + return -ENODEV; dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT; 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); - /* break now */ - return 1; + return 0; } u64 __init dt_mem_next_cell(int s, const __be32 **cellp) @@ -986,76 +1023,99 @@ u64 __init dt_mem_next_cell(int s, const __be32 **cellp) return of_read_number(p, s); } -/** +/* * early_init_dt_scan_memory - Look for and parse memory nodes */ -int __init early_init_dt_scan_memory(unsigned long node, const char *uname, - int depth, void *data) +int __init early_init_dt_scan_memory(void) { - const char *type = of_get_flat_dt_prop(node, "device_type", NULL); - const __be32 *reg, *endp; - int l; - bool hotpluggable; + int node, found_memory = 0; + const void *fdt = initial_boot_params; - /* We are scanning "memory" nodes only */ - if (type == NULL || strcmp(type, "memory") != 0) - return 0; + fdt_for_each_subnode(node, fdt, 0) { + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *reg; + int i, l; + bool hotpluggable; - reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); - if (reg == NULL) - reg = of_get_flat_dt_prop(node, "reg", &l); - if (reg == NULL) - return 0; + /* We are scanning "memory" nodes only */ + if (type == NULL || strcmp(type, "memory") != 0) + continue; - endp = reg + (l / sizeof(__be32)); - hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL); + if (!of_fdt_device_is_available(fdt, node)) + continue; - pr_debug("memory scan node %s, reg size %d,\n", uname, l); + reg = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory", &l); + if (reg == NULL) + reg = of_flat_dt_get_addr_size_prop(node, "reg", &l); + if (reg == NULL) + continue; - while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { - u64 base, size; + hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL); - base = dt_mem_next_cell(dt_root_addr_cells, ®); - size = dt_mem_next_cell(dt_root_size_cells, ®); + pr_debug("memory scan node %s, reg {addr,size} entries %d,\n", + fdt_get_name(fdt, node, NULL), l); - if (size == 0) - continue; - pr_debug(" - %llx , %llx\n", (unsigned long long)base, - (unsigned long long)size); + for (i = 0; i < l; i++) { + u64 base, size; - early_init_dt_add_memory_arch(base, size); + of_flat_dt_read_addr_size(reg, i, &base, &size); - if (!hotpluggable) - continue; + if (size == 0) + continue; + pr_debug(" - %llx, %llx\n", base, size); - if (early_init_dt_mark_hotplug_memory_arch(base, size)) - pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n", - base, base + size); - } + early_init_dt_add_memory_arch(base, size); - return 0; + found_memory = 1; + + if (!hotpluggable) + continue; + + if (memblock_mark_hotplug(base, size)) + pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n", + base, base + size); + } + } + return found_memory; } -int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, - int depth, void *data) +int __init early_init_dt_scan_chosen(char *cmdline) { - int l; + int l, node; const char *p; const void *rng_seed; + const void *fdt = initial_boot_params; - pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); + node = fdt_path_offset(fdt, "/chosen"); + if (node < 0) + node = fdt_path_offset(fdt, "/chosen@0"); + if (node < 0) + /* Handle the cmdline config options even if no /chosen node */ + goto handle_cmdline; - if (depth != 1 || !data || - (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) - return 0; + chosen_node_offset = node; early_init_dt_check_for_initrd(node); + early_init_dt_check_for_elfcorehdr(node); + + rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); + if (rng_seed && l > 0) { + add_bootloader_randomness(rng_seed, l); + + /* try to clear seed so it won't be found. */ + fdt_nop_property(initial_boot_params, node, "rng-seed"); + + /* update CRC check value */ + of_fdt_crc32 = crc32_be(~0, initial_boot_params, + fdt_totalsize(initial_boot_params)); + } /* Retrieve command line */ p = of_get_flat_dt_prop(node, "bootargs", &l); if (p != NULL && l > 0) - strlcpy(data, p, min(l, COMMAND_LINE_SIZE)); + strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE)); +handle_cmdline: /* * CONFIG_CMDLINE is meant to be a default in case nothing else * managed to set the command line, unless CONFIG_CMDLINE_FORCE @@ -1063,33 +1123,20 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, */ #ifdef CONFIG_CMDLINE #if defined(CONFIG_CMDLINE_EXTEND) - strlcat(data, " ", COMMAND_LINE_SIZE); - strlcat(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); + strlcat(cmdline, " ", COMMAND_LINE_SIZE); + strlcat(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #elif defined(CONFIG_CMDLINE_FORCE) - strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); + strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #else /* No arguments from boot loader, use kernel's cmdl*/ - if (!((char *)data)[0]) - strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); + if (!((char *)cmdline)[0]) + strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif #endif /* CONFIG_CMDLINE */ - pr_debug("Command line is: %s\n", (char *)data); - - rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); - if (rng_seed && l > 0) { - add_bootloader_randomness(rng_seed, l); - - /* try to clear seed so it won't be found. */ - fdt_nop_property(initial_boot_params, node, "rng-seed"); - - /* update CRC check value */ - of_fdt_crc32 = crc32_be(~0, initial_boot_params, - fdt_totalsize(initial_boot_params)); - } + pr_debug("Command line is: %s\n", (char *)cmdline); - /* break now */ - return 1; + return 0; } #ifndef MIN_MEMBLOCK_ADDR @@ -1141,68 +1188,57 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) memblock_add(base, size); } -int __init __weak early_init_dt_mark_hotplug_memory_arch(u64 base, u64 size) -{ - return memblock_mark_hotplug(base, size); -} - -int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, - phys_addr_t size, bool nomap) -{ - if (nomap) - return memblock_remove(base, size); - return memblock_reserve(base, 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; } void __init early_init_dt_scan_nodes(void) { - int rc = 0; + int rc; /* Retrieve various information from the /chosen node */ - rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); - if (!rc) + rc = early_init_dt_scan_chosen(boot_command_line); + if (rc) pr_warn("No chosen node found, continuing without\n"); - /* Initialize {size,address}-cells info */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - /* Setup memory, calling early_init_dt_add_memory_arch */ - of_scan_flat_dt(early_init_dt_scan_memory, NULL); + early_init_dt_scan_memory(); + + /* 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; @@ -1210,6 +1246,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 * @@ -1220,7 +1271,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 */ @@ -1242,38 +1311,16 @@ void __init unflatten_device_tree(void) */ void __init unflatten_and_copy_device_tree(void) { - int size; - void *dt; - - if (!initial_boot_params) { - pr_warn("No valid device tree found, continuing without\n"); - return; - } + if (initial_boot_params) + initial_boot_params = copy_device_tree(initial_boot_params); - 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; @@ -1283,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 |
