162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Functions for working with the Flattened Device Tree data format 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2009 Benjamin Herrenschmidt, IBM Corp 662306a36Sopenharmony_ci * benh@kernel.crashing.org 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) "OF: fdt: " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/crash_dump.h> 1262306a36Sopenharmony_ci#include <linux/crc32.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/initrd.h> 1562306a36Sopenharmony_ci#include <linux/memblock.h> 1662306a36Sopenharmony_ci#include <linux/mutex.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_fdt.h> 1962306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 2062306a36Sopenharmony_ci#include <linux/sizes.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci#include <linux/errno.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/libfdt.h> 2562306a36Sopenharmony_ci#include <linux/debugfs.h> 2662306a36Sopenharmony_ci#include <linux/serial_core.h> 2762306a36Sopenharmony_ci#include <linux/sysfs.h> 2862306a36Sopenharmony_ci#include <linux/random.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <asm/setup.h> /* for COMMAND_LINE_SIZE */ 3162306a36Sopenharmony_ci#include <asm/page.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "of_private.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * of_fdt_limit_memory - limit the number of regions in the /memory node 3762306a36Sopenharmony_ci * @limit: maximum entries 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * Adjust the flattened device tree to have at most 'limit' number of 4062306a36Sopenharmony_ci * memory entries in the /memory node. This function may be called 4162306a36Sopenharmony_ci * any time after initial_boot_param is set. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_civoid __init of_fdt_limit_memory(int limit) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci int memory; 4662306a36Sopenharmony_ci int len; 4762306a36Sopenharmony_ci const void *val; 4862306a36Sopenharmony_ci int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; 4962306a36Sopenharmony_ci int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT; 5062306a36Sopenharmony_ci const __be32 *addr_prop; 5162306a36Sopenharmony_ci const __be32 *size_prop; 5262306a36Sopenharmony_ci int root_offset; 5362306a36Sopenharmony_ci int cell_size; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci root_offset = fdt_path_offset(initial_boot_params, "/"); 5662306a36Sopenharmony_ci if (root_offset < 0) 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci addr_prop = fdt_getprop(initial_boot_params, root_offset, 6062306a36Sopenharmony_ci "#address-cells", NULL); 6162306a36Sopenharmony_ci if (addr_prop) 6262306a36Sopenharmony_ci nr_address_cells = fdt32_to_cpu(*addr_prop); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci size_prop = fdt_getprop(initial_boot_params, root_offset, 6562306a36Sopenharmony_ci "#size-cells", NULL); 6662306a36Sopenharmony_ci if (size_prop) 6762306a36Sopenharmony_ci nr_size_cells = fdt32_to_cpu(*size_prop); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci cell_size = sizeof(uint32_t)*(nr_address_cells + nr_size_cells); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci memory = fdt_path_offset(initial_boot_params, "/memory"); 7262306a36Sopenharmony_ci if (memory > 0) { 7362306a36Sopenharmony_ci val = fdt_getprop(initial_boot_params, memory, "reg", &len); 7462306a36Sopenharmony_ci if (len > limit*cell_size) { 7562306a36Sopenharmony_ci len = limit*cell_size; 7662306a36Sopenharmony_ci pr_debug("Limiting number of entries to %d\n", limit); 7762306a36Sopenharmony_ci fdt_setprop(initial_boot_params, memory, "reg", val, 7862306a36Sopenharmony_ci len); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic bool of_fdt_device_is_available(const void *blob, unsigned long node) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci const char *status = fdt_getprop(blob, node, "status", NULL); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!status) 8862306a36Sopenharmony_ci return true; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (!strcmp(status, "ok") || !strcmp(status, "okay")) 9162306a36Sopenharmony_ci return true; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return false; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void *unflatten_dt_alloc(void **mem, unsigned long size, 9762306a36Sopenharmony_ci unsigned long align) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci void *res; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci *mem = PTR_ALIGN(*mem, align); 10262306a36Sopenharmony_ci res = *mem; 10362306a36Sopenharmony_ci *mem += size; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return res; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void populate_properties(const void *blob, 10962306a36Sopenharmony_ci int offset, 11062306a36Sopenharmony_ci void **mem, 11162306a36Sopenharmony_ci struct device_node *np, 11262306a36Sopenharmony_ci const char *nodename, 11362306a36Sopenharmony_ci bool dryrun) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct property *pp, **pprev = NULL; 11662306a36Sopenharmony_ci int cur; 11762306a36Sopenharmony_ci bool has_name = false; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci pprev = &np->properties; 12062306a36Sopenharmony_ci for (cur = fdt_first_property_offset(blob, offset); 12162306a36Sopenharmony_ci cur >= 0; 12262306a36Sopenharmony_ci cur = fdt_next_property_offset(blob, cur)) { 12362306a36Sopenharmony_ci const __be32 *val; 12462306a36Sopenharmony_ci const char *pname; 12562306a36Sopenharmony_ci u32 sz; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci val = fdt_getprop_by_offset(blob, cur, &pname, &sz); 12862306a36Sopenharmony_ci if (!val) { 12962306a36Sopenharmony_ci pr_warn("Cannot locate property at 0x%x\n", cur); 13062306a36Sopenharmony_ci continue; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!pname) { 13462306a36Sopenharmony_ci pr_warn("Cannot find property name at 0x%x\n", cur); 13562306a36Sopenharmony_ci continue; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (!strcmp(pname, "name")) 13962306a36Sopenharmony_ci has_name = true; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci pp = unflatten_dt_alloc(mem, sizeof(struct property), 14262306a36Sopenharmony_ci __alignof__(struct property)); 14362306a36Sopenharmony_ci if (dryrun) 14462306a36Sopenharmony_ci continue; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* We accept flattened tree phandles either in 14762306a36Sopenharmony_ci * ePAPR-style "phandle" properties, or the 14862306a36Sopenharmony_ci * legacy "linux,phandle" properties. If both 14962306a36Sopenharmony_ci * appear and have different values, things 15062306a36Sopenharmony_ci * will get weird. Don't do that. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci if (!strcmp(pname, "phandle") || 15362306a36Sopenharmony_ci !strcmp(pname, "linux,phandle")) { 15462306a36Sopenharmony_ci if (!np->phandle) 15562306a36Sopenharmony_ci np->phandle = be32_to_cpup(val); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* And we process the "ibm,phandle" property 15962306a36Sopenharmony_ci * used in pSeries dynamic device tree 16062306a36Sopenharmony_ci * stuff 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci if (!strcmp(pname, "ibm,phandle")) 16362306a36Sopenharmony_ci np->phandle = be32_to_cpup(val); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci pp->name = (char *)pname; 16662306a36Sopenharmony_ci pp->length = sz; 16762306a36Sopenharmony_ci pp->value = (__be32 *)val; 16862306a36Sopenharmony_ci *pprev = pp; 16962306a36Sopenharmony_ci pprev = &pp->next; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* With version 0x10 we may not have the name property, 17362306a36Sopenharmony_ci * recreate it here from the unit name if absent 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci if (!has_name) { 17662306a36Sopenharmony_ci const char *p = nodename, *ps = p, *pa = NULL; 17762306a36Sopenharmony_ci int len; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci while (*p) { 18062306a36Sopenharmony_ci if ((*p) == '@') 18162306a36Sopenharmony_ci pa = p; 18262306a36Sopenharmony_ci else if ((*p) == '/') 18362306a36Sopenharmony_ci ps = p + 1; 18462306a36Sopenharmony_ci p++; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (pa < ps) 18862306a36Sopenharmony_ci pa = p; 18962306a36Sopenharmony_ci len = (pa - ps) + 1; 19062306a36Sopenharmony_ci pp = unflatten_dt_alloc(mem, sizeof(struct property) + len, 19162306a36Sopenharmony_ci __alignof__(struct property)); 19262306a36Sopenharmony_ci if (!dryrun) { 19362306a36Sopenharmony_ci pp->name = "name"; 19462306a36Sopenharmony_ci pp->length = len; 19562306a36Sopenharmony_ci pp->value = pp + 1; 19662306a36Sopenharmony_ci *pprev = pp; 19762306a36Sopenharmony_ci memcpy(pp->value, ps, len - 1); 19862306a36Sopenharmony_ci ((char *)pp->value)[len - 1] = 0; 19962306a36Sopenharmony_ci pr_debug("fixed up name for %s -> %s\n", 20062306a36Sopenharmony_ci nodename, (char *)pp->value); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int populate_node(const void *blob, 20662306a36Sopenharmony_ci int offset, 20762306a36Sopenharmony_ci void **mem, 20862306a36Sopenharmony_ci struct device_node *dad, 20962306a36Sopenharmony_ci struct device_node **pnp, 21062306a36Sopenharmony_ci bool dryrun) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct device_node *np; 21362306a36Sopenharmony_ci const char *pathp; 21462306a36Sopenharmony_ci int len; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci pathp = fdt_get_name(blob, offset, &len); 21762306a36Sopenharmony_ci if (!pathp) { 21862306a36Sopenharmony_ci *pnp = NULL; 21962306a36Sopenharmony_ci return len; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci len++; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci np = unflatten_dt_alloc(mem, sizeof(struct device_node) + len, 22562306a36Sopenharmony_ci __alignof__(struct device_node)); 22662306a36Sopenharmony_ci if (!dryrun) { 22762306a36Sopenharmony_ci char *fn; 22862306a36Sopenharmony_ci of_node_init(np); 22962306a36Sopenharmony_ci np->full_name = fn = ((char *)np) + sizeof(*np); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci memcpy(fn, pathp, len); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (dad != NULL) { 23462306a36Sopenharmony_ci np->parent = dad; 23562306a36Sopenharmony_ci np->sibling = dad->child; 23662306a36Sopenharmony_ci dad->child = np; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci populate_properties(blob, offset, mem, np, pathp, dryrun); 24162306a36Sopenharmony_ci if (!dryrun) { 24262306a36Sopenharmony_ci np->name = of_get_property(np, "name", NULL); 24362306a36Sopenharmony_ci if (!np->name) 24462306a36Sopenharmony_ci np->name = "<NULL>"; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci *pnp = np; 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void reverse_nodes(struct device_node *parent) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct device_node *child, *next; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* In-depth first */ 25662306a36Sopenharmony_ci child = parent->child; 25762306a36Sopenharmony_ci while (child) { 25862306a36Sopenharmony_ci reverse_nodes(child); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci child = child->sibling; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Reverse the nodes in the child list */ 26462306a36Sopenharmony_ci child = parent->child; 26562306a36Sopenharmony_ci parent->child = NULL; 26662306a36Sopenharmony_ci while (child) { 26762306a36Sopenharmony_ci next = child->sibling; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci child->sibling = parent->child; 27062306a36Sopenharmony_ci parent->child = child; 27162306a36Sopenharmony_ci child = next; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/** 27662306a36Sopenharmony_ci * unflatten_dt_nodes - Alloc and populate a device_node from the flat tree 27762306a36Sopenharmony_ci * @blob: The parent device tree blob 27862306a36Sopenharmony_ci * @mem: Memory chunk to use for allocating device nodes and properties 27962306a36Sopenharmony_ci * @dad: Parent struct device_node 28062306a36Sopenharmony_ci * @nodepp: The device_node tree created by the call 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * Return: The size of unflattened device tree or error code 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic int unflatten_dt_nodes(const void *blob, 28562306a36Sopenharmony_ci void *mem, 28662306a36Sopenharmony_ci struct device_node *dad, 28762306a36Sopenharmony_ci struct device_node **nodepp) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct device_node *root; 29062306a36Sopenharmony_ci int offset = 0, depth = 0, initial_depth = 0; 29162306a36Sopenharmony_ci#define FDT_MAX_DEPTH 64 29262306a36Sopenharmony_ci struct device_node *nps[FDT_MAX_DEPTH]; 29362306a36Sopenharmony_ci void *base = mem; 29462306a36Sopenharmony_ci bool dryrun = !base; 29562306a36Sopenharmony_ci int ret; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (nodepp) 29862306a36Sopenharmony_ci *nodepp = NULL; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * We're unflattening device sub-tree if @dad is valid. There are 30262306a36Sopenharmony_ci * possibly multiple nodes in the first level of depth. We need 30362306a36Sopenharmony_ci * set @depth to 1 to make fdt_next_node() happy as it bails 30462306a36Sopenharmony_ci * immediately when negative @depth is found. Otherwise, the device 30562306a36Sopenharmony_ci * nodes except the first one won't be unflattened successfully. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci if (dad) 30862306a36Sopenharmony_ci depth = initial_depth = 1; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci root = dad; 31162306a36Sopenharmony_ci nps[depth] = dad; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci for (offset = 0; 31462306a36Sopenharmony_ci offset >= 0 && depth >= initial_depth; 31562306a36Sopenharmony_ci offset = fdt_next_node(blob, offset, &depth)) { 31662306a36Sopenharmony_ci if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH - 1)) 31762306a36Sopenharmony_ci continue; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_OF_KOBJ) && 32062306a36Sopenharmony_ci !of_fdt_device_is_available(blob, offset)) 32162306a36Sopenharmony_ci continue; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ret = populate_node(blob, offset, &mem, nps[depth], 32462306a36Sopenharmony_ci &nps[depth+1], dryrun); 32562306a36Sopenharmony_ci if (ret < 0) 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!dryrun && nodepp && !*nodepp) 32962306a36Sopenharmony_ci *nodepp = nps[depth+1]; 33062306a36Sopenharmony_ci if (!dryrun && !root) 33162306a36Sopenharmony_ci root = nps[depth+1]; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (offset < 0 && offset != -FDT_ERR_NOTFOUND) { 33562306a36Sopenharmony_ci pr_err("Error %d processing FDT\n", offset); 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * Reverse the child list. Some drivers assumes node order matches .dts 34162306a36Sopenharmony_ci * node order 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci if (!dryrun) 34462306a36Sopenharmony_ci reverse_nodes(root); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return mem - base; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/** 35062306a36Sopenharmony_ci * __unflatten_device_tree - create tree of device_nodes from flat blob 35162306a36Sopenharmony_ci * @blob: The blob to expand 35262306a36Sopenharmony_ci * @dad: Parent device node 35362306a36Sopenharmony_ci * @mynodes: The device_node tree created by the call 35462306a36Sopenharmony_ci * @dt_alloc: An allocator that provides a virtual address to memory 35562306a36Sopenharmony_ci * for the resulting tree 35662306a36Sopenharmony_ci * @detached: if true set OF_DETACHED on @mynodes 35762306a36Sopenharmony_ci * 35862306a36Sopenharmony_ci * unflattens a device-tree, creating the tree of struct device_node. It also 35962306a36Sopenharmony_ci * fills the "name" and "type" pointers of the nodes so the normal device-tree 36062306a36Sopenharmony_ci * walking functions can be used. 36162306a36Sopenharmony_ci * 36262306a36Sopenharmony_ci * Return: NULL on failure or the memory chunk containing the unflattened 36362306a36Sopenharmony_ci * device tree on success. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_civoid *__unflatten_device_tree(const void *blob, 36662306a36Sopenharmony_ci struct device_node *dad, 36762306a36Sopenharmony_ci struct device_node **mynodes, 36862306a36Sopenharmony_ci void *(*dt_alloc)(u64 size, u64 align), 36962306a36Sopenharmony_ci bool detached) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int size; 37262306a36Sopenharmony_ci void *mem; 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (mynodes) 37662306a36Sopenharmony_ci *mynodes = NULL; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci pr_debug(" -> unflatten_device_tree()\n"); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (!blob) { 38162306a36Sopenharmony_ci pr_debug("No device tree pointer\n"); 38262306a36Sopenharmony_ci return NULL; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci pr_debug("Unflattening device tree:\n"); 38662306a36Sopenharmony_ci pr_debug("magic: %08x\n", fdt_magic(blob)); 38762306a36Sopenharmony_ci pr_debug("size: %08x\n", fdt_totalsize(blob)); 38862306a36Sopenharmony_ci pr_debug("version: %08x\n", fdt_version(blob)); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (fdt_check_header(blob)) { 39162306a36Sopenharmony_ci pr_err("Invalid device tree blob header\n"); 39262306a36Sopenharmony_ci return NULL; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* First pass, scan for size */ 39662306a36Sopenharmony_ci size = unflatten_dt_nodes(blob, NULL, dad, NULL); 39762306a36Sopenharmony_ci if (size <= 0) 39862306a36Sopenharmony_ci return NULL; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci size = ALIGN(size, 4); 40162306a36Sopenharmony_ci pr_debug(" size is %d, allocating...\n", size); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Allocate memory for the expanded device tree */ 40462306a36Sopenharmony_ci mem = dt_alloc(size + 4, __alignof__(struct device_node)); 40562306a36Sopenharmony_ci if (!mem) 40662306a36Sopenharmony_ci return NULL; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci memset(mem, 0, size); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci pr_debug(" unflattening %p...\n", mem); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* Second pass, do actual unflattening */ 41562306a36Sopenharmony_ci ret = unflatten_dt_nodes(blob, mem, dad, mynodes); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (be32_to_cpup(mem + size) != 0xdeadbeef) 41862306a36Sopenharmony_ci pr_warn("End of tree marker overwritten: %08x\n", 41962306a36Sopenharmony_ci be32_to_cpup(mem + size)); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (ret <= 0) 42262306a36Sopenharmony_ci return NULL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (detached && mynodes && *mynodes) { 42562306a36Sopenharmony_ci of_node_set_flag(*mynodes, OF_DETACHED); 42662306a36Sopenharmony_ci pr_debug("unflattened tree is detached\n"); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci pr_debug(" <- unflatten_device_tree()\n"); 43062306a36Sopenharmony_ci return mem; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic void *kernel_tree_alloc(u64 size, u64 align) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci return kzalloc(size, GFP_KERNEL); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic DEFINE_MUTEX(of_fdt_unflatten_mutex); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/** 44162306a36Sopenharmony_ci * of_fdt_unflatten_tree - create tree of device_nodes from flat blob 44262306a36Sopenharmony_ci * @blob: Flat device tree blob 44362306a36Sopenharmony_ci * @dad: Parent device node 44462306a36Sopenharmony_ci * @mynodes: The device tree created by the call 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * unflattens the device-tree passed by the firmware, creating the 44762306a36Sopenharmony_ci * tree of struct device_node. It also fills the "name" and "type" 44862306a36Sopenharmony_ci * pointers of the nodes so the normal device-tree walking functions 44962306a36Sopenharmony_ci * can be used. 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * Return: NULL on failure or the memory chunk containing the unflattened 45262306a36Sopenharmony_ci * device tree on success. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_civoid *of_fdt_unflatten_tree(const unsigned long *blob, 45562306a36Sopenharmony_ci struct device_node *dad, 45662306a36Sopenharmony_ci struct device_node **mynodes) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci void *mem; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci mutex_lock(&of_fdt_unflatten_mutex); 46162306a36Sopenharmony_ci mem = __unflatten_device_tree(blob, dad, mynodes, &kernel_tree_alloc, 46262306a36Sopenharmony_ci true); 46362306a36Sopenharmony_ci mutex_unlock(&of_fdt_unflatten_mutex); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return mem; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_fdt_unflatten_tree); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci/* Everything below here references initial_boot_params directly. */ 47062306a36Sopenharmony_ciint __initdata dt_root_addr_cells; 47162306a36Sopenharmony_ciint __initdata dt_root_size_cells; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_civoid *initial_boot_params __ro_after_init; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci#ifdef CONFIG_OF_EARLY_FLATTREE 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic u32 of_fdt_crc32; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int __init early_init_dt_reserve_memory(phys_addr_t base, 48062306a36Sopenharmony_ci phys_addr_t size, bool nomap) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci if (nomap) { 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * If the memory is already reserved (by another region), we 48562306a36Sopenharmony_ci * should not allow it to be marked nomap, but don't worry 48662306a36Sopenharmony_ci * if the region isn't memory as it won't be mapped. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci if (memblock_overlaps_region(&memblock.memory, base, size) && 48962306a36Sopenharmony_ci memblock_is_region_reserved(base, size)) 49062306a36Sopenharmony_ci return -EBUSY; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return memblock_mark_nomap(base, size); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci return memblock_reserve(base, size); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/* 49862306a36Sopenharmony_ci * __reserved_mem_reserve_reg() - reserve all memory described in 'reg' property 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_cistatic int __init __reserved_mem_reserve_reg(unsigned long node, 50162306a36Sopenharmony_ci const char *uname) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); 50462306a36Sopenharmony_ci phys_addr_t base, size; 50562306a36Sopenharmony_ci int len; 50662306a36Sopenharmony_ci const __be32 *prop; 50762306a36Sopenharmony_ci int first = 1; 50862306a36Sopenharmony_ci bool nomap; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "reg", &len); 51162306a36Sopenharmony_ci if (!prop) 51262306a36Sopenharmony_ci return -ENOENT; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (len && len % t_len != 0) { 51562306a36Sopenharmony_ci pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", 51662306a36Sopenharmony_ci uname); 51762306a36Sopenharmony_ci return -EINVAL; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci while (len >= t_len) { 52362306a36Sopenharmony_ci base = dt_mem_next_cell(dt_root_addr_cells, &prop); 52462306a36Sopenharmony_ci size = dt_mem_next_cell(dt_root_size_cells, &prop); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (size && 52762306a36Sopenharmony_ci early_init_dt_reserve_memory(base, size, nomap) == 0) 52862306a36Sopenharmony_ci pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n", 52962306a36Sopenharmony_ci uname, &base, (unsigned long)(size / SZ_1M)); 53062306a36Sopenharmony_ci else 53162306a36Sopenharmony_ci pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n", 53262306a36Sopenharmony_ci uname, &base, (unsigned long)(size / SZ_1M)); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci len -= t_len; 53562306a36Sopenharmony_ci if (first) { 53662306a36Sopenharmony_ci fdt_reserved_mem_save_node(node, uname, base, size); 53762306a36Sopenharmony_ci first = 0; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/* 54462306a36Sopenharmony_ci * __reserved_mem_check_root() - check if #size-cells, #address-cells provided 54562306a36Sopenharmony_ci * in /reserved-memory matches the values supported by the current implementation, 54662306a36Sopenharmony_ci * also check if ranges property has been provided 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_cistatic int __init __reserved_mem_check_root(unsigned long node) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci const __be32 *prop; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "#size-cells", NULL); 55362306a36Sopenharmony_ci if (!prop || be32_to_cpup(prop) != dt_root_size_cells) 55462306a36Sopenharmony_ci return -EINVAL; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "#address-cells", NULL); 55762306a36Sopenharmony_ci if (!prop || be32_to_cpup(prop) != dt_root_addr_cells) 55862306a36Sopenharmony_ci return -EINVAL; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ranges", NULL); 56162306a36Sopenharmony_ci if (!prop) 56262306a36Sopenharmony_ci return -EINVAL; 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci/* 56762306a36Sopenharmony_ci * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_cistatic int __init fdt_scan_reserved_mem(void) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci int node, child; 57262306a36Sopenharmony_ci const void *fdt = initial_boot_params; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci node = fdt_path_offset(fdt, "/reserved-memory"); 57562306a36Sopenharmony_ci if (node < 0) 57662306a36Sopenharmony_ci return -ENODEV; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (__reserved_mem_check_root(node) != 0) { 57962306a36Sopenharmony_ci pr_err("Reserved memory: unsupported node format, ignoring\n"); 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci fdt_for_each_subnode(child, fdt, node) { 58462306a36Sopenharmony_ci const char *uname; 58562306a36Sopenharmony_ci int err; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (!of_fdt_device_is_available(fdt, child)) 58862306a36Sopenharmony_ci continue; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci uname = fdt_get_name(fdt, child, NULL); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci err = __reserved_mem_reserve_reg(child, uname); 59362306a36Sopenharmony_ci if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) 59462306a36Sopenharmony_ci fdt_reserved_mem_save_node(child, uname, 0, 0); 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* 60062306a36Sopenharmony_ci * fdt_reserve_elfcorehdr() - reserves memory for elf core header 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * This function reserves the memory occupied by an elf core header 60362306a36Sopenharmony_ci * described in the device tree. This region contains all the 60462306a36Sopenharmony_ci * information about primary kernel's core image and is used by a dump 60562306a36Sopenharmony_ci * capture kernel to access the system memory on primary kernel. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_cistatic void __init fdt_reserve_elfcorehdr(void) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_CRASH_DUMP) || !elfcorehdr_size) 61062306a36Sopenharmony_ci return; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (memblock_is_region_reserved(elfcorehdr_addr, elfcorehdr_size)) { 61362306a36Sopenharmony_ci pr_warn("elfcorehdr is overlapped\n"); 61462306a36Sopenharmony_ci return; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci memblock_reserve(elfcorehdr_addr, elfcorehdr_size); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci pr_info("Reserving %llu KiB of memory at 0x%llx for elfcorehdr\n", 62062306a36Sopenharmony_ci elfcorehdr_size >> 10, elfcorehdr_addr); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/** 62462306a36Sopenharmony_ci * early_init_fdt_scan_reserved_mem() - create reserved memory regions 62562306a36Sopenharmony_ci * 62662306a36Sopenharmony_ci * This function grabs memory from early allocator for device exclusive use 62762306a36Sopenharmony_ci * defined in device tree structures. It should be called by arch specific code 62862306a36Sopenharmony_ci * once the early allocator (i.e. memblock) has been fully activated. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_civoid __init early_init_fdt_scan_reserved_mem(void) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci int n; 63362306a36Sopenharmony_ci u64 base, size; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!initial_boot_params) 63662306a36Sopenharmony_ci return; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci fdt_scan_reserved_mem(); 63962306a36Sopenharmony_ci fdt_reserve_elfcorehdr(); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Process header /memreserve/ fields */ 64262306a36Sopenharmony_ci for (n = 0; ; n++) { 64362306a36Sopenharmony_ci fdt_get_mem_rsv(initial_boot_params, n, &base, &size); 64462306a36Sopenharmony_ci if (!size) 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci memblock_reserve(base, size); 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci fdt_init_reserved_mem(); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/** 65362306a36Sopenharmony_ci * early_init_fdt_reserve_self() - reserve the memory used by the FDT blob 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_civoid __init early_init_fdt_reserve_self(void) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci if (!initial_boot_params) 65862306a36Sopenharmony_ci return; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* Reserve the dtb region */ 66162306a36Sopenharmony_ci memblock_reserve(__pa(initial_boot_params), 66262306a36Sopenharmony_ci fdt_totalsize(initial_boot_params)); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/** 66662306a36Sopenharmony_ci * of_scan_flat_dt - scan flattened tree blob and call callback on each. 66762306a36Sopenharmony_ci * @it: callback function 66862306a36Sopenharmony_ci * @data: context data pointer 66962306a36Sopenharmony_ci * 67062306a36Sopenharmony_ci * This function is used to scan the flattened device-tree, it is 67162306a36Sopenharmony_ci * used to extract the memory information at boot before we can 67262306a36Sopenharmony_ci * unflatten the tree 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ciint __init of_scan_flat_dt(int (*it)(unsigned long node, 67562306a36Sopenharmony_ci const char *uname, int depth, 67662306a36Sopenharmony_ci void *data), 67762306a36Sopenharmony_ci void *data) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci const void *blob = initial_boot_params; 68062306a36Sopenharmony_ci const char *pathp; 68162306a36Sopenharmony_ci int offset, rc = 0, depth = -1; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!blob) 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci for (offset = fdt_next_node(blob, -1, &depth); 68762306a36Sopenharmony_ci offset >= 0 && depth >= 0 && !rc; 68862306a36Sopenharmony_ci offset = fdt_next_node(blob, offset, &depth)) { 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci pathp = fdt_get_name(blob, offset, NULL); 69162306a36Sopenharmony_ci rc = it(offset, pathp, depth, data); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci return rc; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci/** 69762306a36Sopenharmony_ci * of_scan_flat_dt_subnodes - scan sub-nodes of a node call callback on each. 69862306a36Sopenharmony_ci * @parent: parent node 69962306a36Sopenharmony_ci * @it: callback function 70062306a36Sopenharmony_ci * @data: context data pointer 70162306a36Sopenharmony_ci * 70262306a36Sopenharmony_ci * This function is used to scan sub-nodes of a node. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ciint __init of_scan_flat_dt_subnodes(unsigned long parent, 70562306a36Sopenharmony_ci int (*it)(unsigned long node, 70662306a36Sopenharmony_ci const char *uname, 70762306a36Sopenharmony_ci void *data), 70862306a36Sopenharmony_ci void *data) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci const void *blob = initial_boot_params; 71162306a36Sopenharmony_ci int node; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci fdt_for_each_subnode(node, blob, parent) { 71462306a36Sopenharmony_ci const char *pathp; 71562306a36Sopenharmony_ci int rc; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci pathp = fdt_get_name(blob, node, NULL); 71862306a36Sopenharmony_ci rc = it(node, pathp, data); 71962306a36Sopenharmony_ci if (rc) 72062306a36Sopenharmony_ci return rc; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci return 0; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci/** 72662306a36Sopenharmony_ci * of_get_flat_dt_subnode_by_name - get the subnode by given name 72762306a36Sopenharmony_ci * 72862306a36Sopenharmony_ci * @node: the parent node 72962306a36Sopenharmony_ci * @uname: the name of subnode 73062306a36Sopenharmony_ci * @return offset of the subnode, or -FDT_ERR_NOTFOUND if there is none 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ciint __init of_get_flat_dt_subnode_by_name(unsigned long node, const char *uname) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci return fdt_subnode_offset(initial_boot_params, node, uname); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci/* 73962306a36Sopenharmony_ci * of_get_flat_dt_root - find the root node in the flat blob 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ciunsigned long __init of_get_flat_dt_root(void) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci/* 74762306a36Sopenharmony_ci * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr 74862306a36Sopenharmony_ci * 74962306a36Sopenharmony_ci * This function can be used within scan_flattened_dt callback to get 75062306a36Sopenharmony_ci * access to properties 75162306a36Sopenharmony_ci */ 75262306a36Sopenharmony_ciconst void *__init of_get_flat_dt_prop(unsigned long node, const char *name, 75362306a36Sopenharmony_ci int *size) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci return fdt_getprop(initial_boot_params, node, name, size); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci/** 75962306a36Sopenharmony_ci * of_fdt_is_compatible - Return true if given node from the given blob has 76062306a36Sopenharmony_ci * compat in its compatible list 76162306a36Sopenharmony_ci * @blob: A device tree blob 76262306a36Sopenharmony_ci * @node: node to test 76362306a36Sopenharmony_ci * @compat: compatible string to compare with compatible list. 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * Return: a non-zero value on match with smaller values returned for more 76662306a36Sopenharmony_ci * specific compatible values. 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_cistatic int of_fdt_is_compatible(const void *blob, 76962306a36Sopenharmony_ci unsigned long node, const char *compat) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci const char *cp; 77262306a36Sopenharmony_ci int cplen; 77362306a36Sopenharmony_ci unsigned long l, score = 0; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci cp = fdt_getprop(blob, node, "compatible", &cplen); 77662306a36Sopenharmony_ci if (cp == NULL) 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci while (cplen > 0) { 77962306a36Sopenharmony_ci score++; 78062306a36Sopenharmony_ci if (of_compat_cmp(cp, compat, strlen(compat)) == 0) 78162306a36Sopenharmony_ci return score; 78262306a36Sopenharmony_ci l = strlen(cp) + 1; 78362306a36Sopenharmony_ci cp += l; 78462306a36Sopenharmony_ci cplen -= l; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci/** 79162306a36Sopenharmony_ci * of_flat_dt_is_compatible - Return true if given node has compat in compatible list 79262306a36Sopenharmony_ci * @node: node to test 79362306a36Sopenharmony_ci * @compat: compatible string to compare with compatible list. 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ciint __init of_flat_dt_is_compatible(unsigned long node, const char *compat) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci return of_fdt_is_compatible(initial_boot_params, node, compat); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/* 80162306a36Sopenharmony_ci * of_flat_dt_match - Return true if node matches a list of compatible values 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_cistatic int __init of_flat_dt_match(unsigned long node, const char *const *compat) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci unsigned int tmp, score = 0; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (!compat) 80862306a36Sopenharmony_ci return 0; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci while (*compat) { 81162306a36Sopenharmony_ci tmp = of_fdt_is_compatible(initial_boot_params, node, *compat); 81262306a36Sopenharmony_ci if (tmp && (score == 0 || (tmp < score))) 81362306a36Sopenharmony_ci score = tmp; 81462306a36Sopenharmony_ci compat++; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return score; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci/* 82162306a36Sopenharmony_ci * of_get_flat_dt_phandle - Given a node in the flat blob, return the phandle 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ciuint32_t __init of_get_flat_dt_phandle(unsigned long node) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci return fdt_get_phandle(initial_boot_params, node); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ciconst char * __init of_flat_dt_get_machine_name(void) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci const char *name; 83162306a36Sopenharmony_ci unsigned long dt_root = of_get_flat_dt_root(); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci name = of_get_flat_dt_prop(dt_root, "model", NULL); 83462306a36Sopenharmony_ci if (!name) 83562306a36Sopenharmony_ci name = of_get_flat_dt_prop(dt_root, "compatible", NULL); 83662306a36Sopenharmony_ci return name; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci/** 84062306a36Sopenharmony_ci * of_flat_dt_match_machine - Iterate match tables to find matching machine. 84162306a36Sopenharmony_ci * 84262306a36Sopenharmony_ci * @default_match: A machine specific ptr to return in case of no match. 84362306a36Sopenharmony_ci * @get_next_compat: callback function to return next compatible match table. 84462306a36Sopenharmony_ci * 84562306a36Sopenharmony_ci * Iterate through machine match tables to find the best match for the machine 84662306a36Sopenharmony_ci * compatible string in the FDT. 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_ciconst void * __init of_flat_dt_match_machine(const void *default_match, 84962306a36Sopenharmony_ci const void * (*get_next_compat)(const char * const**)) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci const void *data = NULL; 85262306a36Sopenharmony_ci const void *best_data = default_match; 85362306a36Sopenharmony_ci const char *const *compat; 85462306a36Sopenharmony_ci unsigned long dt_root; 85562306a36Sopenharmony_ci unsigned int best_score = ~1, score = 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci dt_root = of_get_flat_dt_root(); 85862306a36Sopenharmony_ci while ((data = get_next_compat(&compat))) { 85962306a36Sopenharmony_ci score = of_flat_dt_match(dt_root, compat); 86062306a36Sopenharmony_ci if (score > 0 && score < best_score) { 86162306a36Sopenharmony_ci best_data = data; 86262306a36Sopenharmony_ci best_score = score; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci if (!best_data) { 86662306a36Sopenharmony_ci const char *prop; 86762306a36Sopenharmony_ci int size; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci pr_err("\n unrecognized device tree list:\n[ "); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci prop = of_get_flat_dt_prop(dt_root, "compatible", &size); 87262306a36Sopenharmony_ci if (prop) { 87362306a36Sopenharmony_ci while (size > 0) { 87462306a36Sopenharmony_ci printk("'%s' ", prop); 87562306a36Sopenharmony_ci size -= strlen(prop) + 1; 87662306a36Sopenharmony_ci prop += strlen(prop) + 1; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci printk("]\n\n"); 88062306a36Sopenharmony_ci return NULL; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci pr_info("Machine model: %s\n", of_flat_dt_get_machine_name()); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci return best_data; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic void __early_init_dt_declare_initrd(unsigned long start, 88962306a36Sopenharmony_ci unsigned long end) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci /* 89262306a36Sopenharmony_ci * __va() is not yet available this early on some platforms. In that 89362306a36Sopenharmony_ci * case, the platform uses phys_initrd_start/phys_initrd_size instead 89462306a36Sopenharmony_ci * and does the VA conversion itself. 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_ARM64) && 89762306a36Sopenharmony_ci !(IS_ENABLED(CONFIG_RISCV) && IS_ENABLED(CONFIG_64BIT))) { 89862306a36Sopenharmony_ci initrd_start = (unsigned long)__va(start); 89962306a36Sopenharmony_ci initrd_end = (unsigned long)__va(end); 90062306a36Sopenharmony_ci initrd_below_start_ok = 1; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/** 90562306a36Sopenharmony_ci * early_init_dt_check_for_initrd - Decode initrd location from flat tree 90662306a36Sopenharmony_ci * @node: reference to node containing initrd location ('chosen') 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_cistatic void __init early_init_dt_check_for_initrd(unsigned long node) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci u64 start, end; 91162306a36Sopenharmony_ci int len; 91262306a36Sopenharmony_ci const __be32 *prop; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD)) 91562306a36Sopenharmony_ci return; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci pr_debug("Looking for initrd properties... "); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len); 92062306a36Sopenharmony_ci if (!prop) 92162306a36Sopenharmony_ci return; 92262306a36Sopenharmony_ci start = of_read_number(prop, len/4); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len); 92562306a36Sopenharmony_ci if (!prop) 92662306a36Sopenharmony_ci return; 92762306a36Sopenharmony_ci end = of_read_number(prop, len/4); 92862306a36Sopenharmony_ci if (start > end) 92962306a36Sopenharmony_ci return; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci __early_init_dt_declare_initrd(start, end); 93262306a36Sopenharmony_ci phys_initrd_start = start; 93362306a36Sopenharmony_ci phys_initrd_size = end - start; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n", start, end); 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci/** 93962306a36Sopenharmony_ci * early_init_dt_check_for_elfcorehdr - Decode elfcorehdr location from flat 94062306a36Sopenharmony_ci * tree 94162306a36Sopenharmony_ci * @node: reference to node containing elfcorehdr location ('chosen') 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_cistatic void __init early_init_dt_check_for_elfcorehdr(unsigned long node) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci const __be32 *prop; 94662306a36Sopenharmony_ci int len; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_CRASH_DUMP)) 94962306a36Sopenharmony_ci return; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci pr_debug("Looking for elfcorehdr property... "); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "linux,elfcorehdr", &len); 95462306a36Sopenharmony_ci if (!prop || (len < (dt_root_addr_cells + dt_root_size_cells))) 95562306a36Sopenharmony_ci return; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci elfcorehdr_addr = dt_mem_next_cell(dt_root_addr_cells, &prop); 95862306a36Sopenharmony_ci elfcorehdr_size = dt_mem_next_cell(dt_root_size_cells, &prop); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci pr_debug("elfcorehdr_start=0x%llx elfcorehdr_size=0x%llx\n", 96162306a36Sopenharmony_ci elfcorehdr_addr, elfcorehdr_size); 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/* 96762306a36Sopenharmony_ci * The main usage of linux,usable-memory-range is for crash dump kernel. 96862306a36Sopenharmony_ci * Originally, the number of usable-memory regions is one. Now there may 96962306a36Sopenharmony_ci * be two regions, low region and high region. 97062306a36Sopenharmony_ci * To make compatibility with existing user-space and older kdump, the low 97162306a36Sopenharmony_ci * region is always the last range of linux,usable-memory-range if exist. 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_ci#define MAX_USABLE_RANGES 2 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/** 97662306a36Sopenharmony_ci * early_init_dt_check_for_usable_mem_range - Decode usable memory range 97762306a36Sopenharmony_ci * location from flat tree 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_civoid __init early_init_dt_check_for_usable_mem_range(void) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct memblock_region rgn[MAX_USABLE_RANGES] = {0}; 98262306a36Sopenharmony_ci const __be32 *prop, *endp; 98362306a36Sopenharmony_ci int len, i; 98462306a36Sopenharmony_ci unsigned long node = chosen_node_offset; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if ((long)node < 0) 98762306a36Sopenharmony_ci return; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci pr_debug("Looking for usable-memory-range property... "); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "linux,usable-memory-range", &len); 99262306a36Sopenharmony_ci if (!prop || (len % (dt_root_addr_cells + dt_root_size_cells))) 99362306a36Sopenharmony_ci return; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci endp = prop + (len / sizeof(__be32)); 99662306a36Sopenharmony_ci for (i = 0; i < MAX_USABLE_RANGES && prop < endp; i++) { 99762306a36Sopenharmony_ci rgn[i].base = dt_mem_next_cell(dt_root_addr_cells, &prop); 99862306a36Sopenharmony_ci rgn[i].size = dt_mem_next_cell(dt_root_size_cells, &prop); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci pr_debug("cap_mem_regions[%d]: base=%pa, size=%pa\n", 100162306a36Sopenharmony_ci i, &rgn[i].base, &rgn[i].size); 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci memblock_cap_memory_range(rgn[0].base, rgn[0].size); 100562306a36Sopenharmony_ci for (i = 1; i < MAX_USABLE_RANGES && rgn[i].size; i++) 100662306a36Sopenharmony_ci memblock_add(rgn[i].base, rgn[i].size); 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci#ifdef CONFIG_SERIAL_EARLYCON 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ciint __init early_init_dt_scan_chosen_stdout(void) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci int offset; 101462306a36Sopenharmony_ci const char *p, *q, *options = NULL; 101562306a36Sopenharmony_ci int l; 101662306a36Sopenharmony_ci const struct earlycon_id *match; 101762306a36Sopenharmony_ci const void *fdt = initial_boot_params; 101862306a36Sopenharmony_ci int ret; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci offset = fdt_path_offset(fdt, "/chosen"); 102162306a36Sopenharmony_ci if (offset < 0) 102262306a36Sopenharmony_ci offset = fdt_path_offset(fdt, "/chosen@0"); 102362306a36Sopenharmony_ci if (offset < 0) 102462306a36Sopenharmony_ci return -ENOENT; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci p = fdt_getprop(fdt, offset, "stdout-path", &l); 102762306a36Sopenharmony_ci if (!p) 102862306a36Sopenharmony_ci p = fdt_getprop(fdt, offset, "linux,stdout-path", &l); 102962306a36Sopenharmony_ci if (!p || !l) 103062306a36Sopenharmony_ci return -ENOENT; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci q = strchrnul(p, ':'); 103362306a36Sopenharmony_ci if (*q != '\0') 103462306a36Sopenharmony_ci options = q + 1; 103562306a36Sopenharmony_ci l = q - p; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci /* Get the node specified by stdout-path */ 103862306a36Sopenharmony_ci offset = fdt_path_offset_namelen(fdt, p, l); 103962306a36Sopenharmony_ci if (offset < 0) { 104062306a36Sopenharmony_ci pr_warn("earlycon: stdout-path %.*s not found\n", l, p); 104162306a36Sopenharmony_ci return 0; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci for (match = __earlycon_table; match < __earlycon_table_end; match++) { 104562306a36Sopenharmony_ci if (!match->compatible[0]) 104662306a36Sopenharmony_ci continue; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (fdt_node_check_compatible(fdt, offset, match->compatible)) 104962306a36Sopenharmony_ci continue; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci ret = of_setup_earlycon(match, offset, options); 105262306a36Sopenharmony_ci if (!ret || ret == -EALREADY) 105362306a36Sopenharmony_ci return 0; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci return -ENODEV; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci#endif 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci/* 106062306a36Sopenharmony_ci * early_init_dt_scan_root - fetch the top level address and size cells 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_ciint __init early_init_dt_scan_root(void) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci const __be32 *prop; 106562306a36Sopenharmony_ci const void *fdt = initial_boot_params; 106662306a36Sopenharmony_ci int node = fdt_path_offset(fdt, "/"); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (node < 0) 106962306a36Sopenharmony_ci return -ENODEV; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT; 107262306a36Sopenharmony_ci dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "#size-cells", NULL); 107562306a36Sopenharmony_ci if (prop) 107662306a36Sopenharmony_ci dt_root_size_cells = be32_to_cpup(prop); 107762306a36Sopenharmony_ci pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "#address-cells", NULL); 108062306a36Sopenharmony_ci if (prop) 108162306a36Sopenharmony_ci dt_root_addr_cells = be32_to_cpup(prop); 108262306a36Sopenharmony_ci pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci return 0; 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ciu64 __init dt_mem_next_cell(int s, const __be32 **cellp) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci const __be32 *p = *cellp; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci *cellp = p + s; 109262306a36Sopenharmony_ci return of_read_number(p, s); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/* 109662306a36Sopenharmony_ci * early_init_dt_scan_memory - Look for and parse memory nodes 109762306a36Sopenharmony_ci */ 109862306a36Sopenharmony_ciint __init early_init_dt_scan_memory(void) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci int node, found_memory = 0; 110162306a36Sopenharmony_ci const void *fdt = initial_boot_params; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci fdt_for_each_subnode(node, fdt, 0) { 110462306a36Sopenharmony_ci const char *type = of_get_flat_dt_prop(node, "device_type", NULL); 110562306a36Sopenharmony_ci const __be32 *reg, *endp; 110662306a36Sopenharmony_ci int l; 110762306a36Sopenharmony_ci bool hotpluggable; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* We are scanning "memory" nodes only */ 111062306a36Sopenharmony_ci if (type == NULL || strcmp(type, "memory") != 0) 111162306a36Sopenharmony_ci continue; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (!of_fdt_device_is_available(fdt, node)) 111462306a36Sopenharmony_ci continue; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); 111762306a36Sopenharmony_ci if (reg == NULL) 111862306a36Sopenharmony_ci reg = of_get_flat_dt_prop(node, "reg", &l); 111962306a36Sopenharmony_ci if (reg == NULL) 112062306a36Sopenharmony_ci continue; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci endp = reg + (l / sizeof(__be32)); 112362306a36Sopenharmony_ci hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci pr_debug("memory scan node %s, reg size %d,\n", 112662306a36Sopenharmony_ci fdt_get_name(fdt, node, NULL), l); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { 112962306a36Sopenharmony_ci u64 base, size; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci base = dt_mem_next_cell(dt_root_addr_cells, ®); 113262306a36Sopenharmony_ci size = dt_mem_next_cell(dt_root_size_cells, ®); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (size == 0) 113562306a36Sopenharmony_ci continue; 113662306a36Sopenharmony_ci pr_debug(" - %llx, %llx\n", base, size); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci early_init_dt_add_memory_arch(base, size); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci found_memory = 1; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (!hotpluggable) 114362306a36Sopenharmony_ci continue; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci if (memblock_mark_hotplug(base, size)) 114662306a36Sopenharmony_ci pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n", 114762306a36Sopenharmony_ci base, base + size); 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci return found_memory; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ciint __init early_init_dt_scan_chosen(char *cmdline) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci int l, node; 115662306a36Sopenharmony_ci const char *p; 115762306a36Sopenharmony_ci const void *rng_seed; 115862306a36Sopenharmony_ci const void *fdt = initial_boot_params; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci node = fdt_path_offset(fdt, "/chosen"); 116162306a36Sopenharmony_ci if (node < 0) 116262306a36Sopenharmony_ci node = fdt_path_offset(fdt, "/chosen@0"); 116362306a36Sopenharmony_ci if (node < 0) 116462306a36Sopenharmony_ci /* Handle the cmdline config options even if no /chosen node */ 116562306a36Sopenharmony_ci goto handle_cmdline; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci chosen_node_offset = node; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci early_init_dt_check_for_initrd(node); 117062306a36Sopenharmony_ci early_init_dt_check_for_elfcorehdr(node); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); 117362306a36Sopenharmony_ci if (rng_seed && l > 0) { 117462306a36Sopenharmony_ci add_bootloader_randomness(rng_seed, l); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci /* try to clear seed so it won't be found. */ 117762306a36Sopenharmony_ci fdt_nop_property(initial_boot_params, node, "rng-seed"); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* update CRC check value */ 118062306a36Sopenharmony_ci of_fdt_crc32 = crc32_be(~0, initial_boot_params, 118162306a36Sopenharmony_ci fdt_totalsize(initial_boot_params)); 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* Retrieve command line */ 118562306a36Sopenharmony_ci p = of_get_flat_dt_prop(node, "bootargs", &l); 118662306a36Sopenharmony_ci if (p != NULL && l > 0) 118762306a36Sopenharmony_ci strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE)); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cihandle_cmdline: 119062306a36Sopenharmony_ci /* 119162306a36Sopenharmony_ci * CONFIG_CMDLINE is meant to be a default in case nothing else 119262306a36Sopenharmony_ci * managed to set the command line, unless CONFIG_CMDLINE_FORCE 119362306a36Sopenharmony_ci * is set in which case we override whatever was found earlier. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_ci#ifdef CONFIG_CMDLINE 119662306a36Sopenharmony_ci#if defined(CONFIG_CMDLINE_EXTEND) 119762306a36Sopenharmony_ci strlcat(cmdline, " ", COMMAND_LINE_SIZE); 119862306a36Sopenharmony_ci strlcat(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); 119962306a36Sopenharmony_ci#elif defined(CONFIG_CMDLINE_FORCE) 120062306a36Sopenharmony_ci strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); 120162306a36Sopenharmony_ci#else 120262306a36Sopenharmony_ci /* No arguments from boot loader, use kernel's cmdl*/ 120362306a36Sopenharmony_ci if (!((char *)cmdline)[0]) 120462306a36Sopenharmony_ci strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); 120562306a36Sopenharmony_ci#endif 120662306a36Sopenharmony_ci#endif /* CONFIG_CMDLINE */ 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci pr_debug("Command line is: %s\n", (char *)cmdline); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci return 0; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci#ifndef MIN_MEMBLOCK_ADDR 121462306a36Sopenharmony_ci#define MIN_MEMBLOCK_ADDR __pa(PAGE_OFFSET) 121562306a36Sopenharmony_ci#endif 121662306a36Sopenharmony_ci#ifndef MAX_MEMBLOCK_ADDR 121762306a36Sopenharmony_ci#define MAX_MEMBLOCK_ADDR ((phys_addr_t)~0) 121862306a36Sopenharmony_ci#endif 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_civoid __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci const u64 phys_offset = MIN_MEMBLOCK_ADDR; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (size < PAGE_SIZE - (base & ~PAGE_MASK)) { 122562306a36Sopenharmony_ci pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", 122662306a36Sopenharmony_ci base, base + size); 122762306a36Sopenharmony_ci return; 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (!PAGE_ALIGNED(base)) { 123162306a36Sopenharmony_ci size -= PAGE_SIZE - (base & ~PAGE_MASK); 123262306a36Sopenharmony_ci base = PAGE_ALIGN(base); 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci size &= PAGE_MASK; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci if (base > MAX_MEMBLOCK_ADDR) { 123762306a36Sopenharmony_ci pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", 123862306a36Sopenharmony_ci base, base + size); 123962306a36Sopenharmony_ci return; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (base + size - 1 > MAX_MEMBLOCK_ADDR) { 124362306a36Sopenharmony_ci pr_warn("Ignoring memory range 0x%llx - 0x%llx\n", 124462306a36Sopenharmony_ci ((u64)MAX_MEMBLOCK_ADDR) + 1, base + size); 124562306a36Sopenharmony_ci size = MAX_MEMBLOCK_ADDR - base + 1; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (base + size < phys_offset) { 124962306a36Sopenharmony_ci pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", 125062306a36Sopenharmony_ci base, base + size); 125162306a36Sopenharmony_ci return; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci if (base < phys_offset) { 125462306a36Sopenharmony_ci pr_warn("Ignoring memory range 0x%llx - 0x%llx\n", 125562306a36Sopenharmony_ci base, phys_offset); 125662306a36Sopenharmony_ci size -= phys_offset - base; 125762306a36Sopenharmony_ci base = phys_offset; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci memblock_add(base, size); 126062306a36Sopenharmony_ci} 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci void *ptr = memblock_alloc(size, align); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (!ptr) 126762306a36Sopenharmony_ci panic("%s: Failed to allocate %llu bytes align=0x%llx\n", 126862306a36Sopenharmony_ci __func__, size, align); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci return ptr; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cibool __init early_init_dt_verify(void *params) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci if (!params) 127662306a36Sopenharmony_ci return false; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* check device tree validity */ 127962306a36Sopenharmony_ci if (fdt_check_header(params)) 128062306a36Sopenharmony_ci return false; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci /* Setup flat device-tree pointer */ 128362306a36Sopenharmony_ci initial_boot_params = params; 128462306a36Sopenharmony_ci of_fdt_crc32 = crc32_be(~0, initial_boot_params, 128562306a36Sopenharmony_ci fdt_totalsize(initial_boot_params)); 128662306a36Sopenharmony_ci return true; 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_civoid __init early_init_dt_scan_nodes(void) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci int rc; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* Initialize {size,address}-cells info */ 129562306a36Sopenharmony_ci early_init_dt_scan_root(); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci /* Retrieve various information from the /chosen node */ 129862306a36Sopenharmony_ci rc = early_init_dt_scan_chosen(boot_command_line); 129962306a36Sopenharmony_ci if (rc) 130062306a36Sopenharmony_ci pr_warn("No chosen node found, continuing without\n"); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* Setup memory, calling early_init_dt_add_memory_arch */ 130362306a36Sopenharmony_ci early_init_dt_scan_memory(); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* Handle linux,usable-memory-range property */ 130662306a36Sopenharmony_ci early_init_dt_check_for_usable_mem_range(); 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cibool __init early_init_dt_scan(void *params) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci bool status; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci status = early_init_dt_verify(params); 131462306a36Sopenharmony_ci if (!status) 131562306a36Sopenharmony_ci return false; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci early_init_dt_scan_nodes(); 131862306a36Sopenharmony_ci return true; 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci/** 132262306a36Sopenharmony_ci * unflatten_device_tree - create tree of device_nodes from flat blob 132362306a36Sopenharmony_ci * 132462306a36Sopenharmony_ci * unflattens the device-tree passed by the firmware, creating the 132562306a36Sopenharmony_ci * tree of struct device_node. It also fills the "name" and "type" 132662306a36Sopenharmony_ci * pointers of the nodes so the normal device-tree walking functions 132762306a36Sopenharmony_ci * can be used. 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_civoid __init unflatten_device_tree(void) 133062306a36Sopenharmony_ci{ 133162306a36Sopenharmony_ci __unflatten_device_tree(initial_boot_params, NULL, &of_root, 133262306a36Sopenharmony_ci early_init_dt_alloc_memory_arch, false); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */ 133562306a36Sopenharmony_ci of_alias_scan(early_init_dt_alloc_memory_arch); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci unittest_unflatten_overlay_base(); 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci/** 134162306a36Sopenharmony_ci * unflatten_and_copy_device_tree - copy and create tree of device_nodes from flat blob 134262306a36Sopenharmony_ci * 134362306a36Sopenharmony_ci * Copies and unflattens the device-tree passed by the firmware, creating the 134462306a36Sopenharmony_ci * tree of struct device_node. It also fills the "name" and "type" 134562306a36Sopenharmony_ci * pointers of the nodes so the normal device-tree walking functions 134662306a36Sopenharmony_ci * can be used. This should only be used when the FDT memory has not been 134762306a36Sopenharmony_ci * reserved such is the case when the FDT is built-in to the kernel init 134862306a36Sopenharmony_ci * section. If the FDT memory is reserved already then unflatten_device_tree 134962306a36Sopenharmony_ci * should be used instead. 135062306a36Sopenharmony_ci */ 135162306a36Sopenharmony_civoid __init unflatten_and_copy_device_tree(void) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci int size; 135462306a36Sopenharmony_ci void *dt; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (!initial_boot_params) { 135762306a36Sopenharmony_ci pr_warn("No valid device tree found, continuing without\n"); 135862306a36Sopenharmony_ci return; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci size = fdt_totalsize(initial_boot_params); 136262306a36Sopenharmony_ci dt = early_init_dt_alloc_memory_arch(size, 136362306a36Sopenharmony_ci roundup_pow_of_two(FDT_V17_SIZE)); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci if (dt) { 136662306a36Sopenharmony_ci memcpy(dt, initial_boot_params, size); 136762306a36Sopenharmony_ci initial_boot_params = dt; 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci unflatten_device_tree(); 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci#ifdef CONFIG_SYSFS 137362306a36Sopenharmony_cistatic ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj, 137462306a36Sopenharmony_ci struct bin_attribute *bin_attr, 137562306a36Sopenharmony_ci char *buf, loff_t off, size_t count) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci memcpy(buf, initial_boot_params + off, count); 137862306a36Sopenharmony_ci return count; 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cistatic int __init of_fdt_raw_init(void) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci static struct bin_attribute of_fdt_raw_attr = 138462306a36Sopenharmony_ci __BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, 0); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (!initial_boot_params) 138762306a36Sopenharmony_ci return 0; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci if (of_fdt_crc32 != crc32_be(~0, initial_boot_params, 139062306a36Sopenharmony_ci fdt_totalsize(initial_boot_params))) { 139162306a36Sopenharmony_ci pr_warn("not creating '/sys/firmware/fdt': CRC check failed\n"); 139262306a36Sopenharmony_ci return 0; 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params); 139562306a36Sopenharmony_ci return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr); 139662306a36Sopenharmony_ci} 139762306a36Sopenharmony_cilate_initcall(of_fdt_raw_init); 139862306a36Sopenharmony_ci#endif 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci#endif /* CONFIG_OF_EARLY_FLATTREE */ 1401