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, &reg);
113262306a36Sopenharmony_ci			size = dt_mem_next_cell(dt_root_size_cells, &reg);
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