// SPDX-License-Identifier: GPL-2.0 /* * Functions for working with the Flattened Device Tree data format * * Copyright 2009 Benjamin Herrenschmidt, IBM Corp * benh@kernel.crashing.org
*/
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #include <asm/page.h>
#include"of_private.h"
/* * __dtb_empty_root_begin[] and __dtb_empty_root_end[] magically created by * cmd_wrap_S_dtb in scripts/Makefile.dtbs
*/ extern uint8_t __dtb_empty_root_begin[]; extern uint8_t __dtb_empty_root_end[];
/* * of_fdt_limit_memory - limit the number of regions in the /memory node * @limit: maximum entries * * Adjust the flattened device tree to have at most 'limit' number of * memory entries in the /memory node. This function may be called * any time after initial_boot_param is set.
*/ void __init of_fdt_limit_memory(int limit)
{ int memory; int len; constvoid *val; int cell_size = sizeof(uint32_t)*(dt_root_addr_cells + dt_root_size_cells);
memory = fdt_path_offset(initial_boot_params, "/memory"); if (memory > 0) {
val = fdt_getprop(initial_boot_params, memory, "reg", &len); if (len > limit*cell_size) {
len = limit*cell_size;
pr_debug("Limiting number of entries to %d\n", limit);
fdt_setprop(initial_boot_params, memory, "reg", val,
len);
}
}
}
pprev = &np->properties; for (cur = fdt_first_property_offset(blob, offset);
cur >= 0;
cur = fdt_next_property_offset(blob, cur)) { const __be32 *val; constchar *pname;
u32 sz;
val = fdt_getprop_by_offset(blob, cur, &pname, &sz); if (!val) {
pr_warn("Cannot locate property at 0x%x\n", cur); continue;
}
if (!pname) {
pr_warn("Cannot find property name at 0x%x\n", cur); continue;
}
if (!strcmp(pname, "name"))
has_name = true;
pp = unflatten_dt_alloc(mem, sizeof(struct property),
__alignof__(struct property)); if (dryrun) continue;
/* We accept flattened tree phandles either in * ePAPR-style "phandle" properties, or the * legacy "linux,phandle" properties. If both * appear and have different values, things * will get weird. Don't do that.
*/ if (!strcmp(pname, "phandle") ||
!strcmp(pname, "linux,phandle")) { if (!np->phandle)
np->phandle = be32_to_cpup(val);
}
/* And we process the "ibm,phandle" property * used in pSeries dynamic device tree * stuff
*/ if (!strcmp(pname, "ibm,phandle"))
np->phandle = be32_to_cpup(val);
/* With version 0x10 we may not have the name property, * recreate it here from the unit name if absent
*/ if (!has_name) { constchar *p = nodename, *ps = p, *pa = NULL; int len;
while (*p) { if ((*p) == '@')
pa = p; elseif ((*p) == '/')
ps = p + 1;
p++;
}
if (pa < ps)
pa = p;
len = (pa - ps) + 1;
pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,
__alignof__(struct property)); if (!dryrun) {
pp->name = "name";
pp->length = len;
pp->value = pp + 1;
*pprev = pp;
memcpy(pp->value, ps, len - 1);
((char *)pp->value)[len - 1] = 0;
pr_debug("fixed up name for %s -> %s\n",
nodename, (char *)pp->value);
}
}
}
/** * unflatten_dt_nodes - Alloc and populate a device_node from the flat tree * @blob: The parent device tree blob * @mem: Memory chunk to use for allocating device nodes and properties * @dad: Parent struct device_node * @nodepp: The device_node tree created by the call * * Return: The size of unflattened device tree or error code
*/ staticint unflatten_dt_nodes(constvoid *blob, void *mem, struct device_node *dad, struct device_node **nodepp)
{ struct device_node *root; int offset = 0, depth = 0, initial_depth = 0; #define FDT_MAX_DEPTH 64 struct device_node *nps[FDT_MAX_DEPTH]; void *base = mem; bool dryrun = !base; int ret;
if (nodepp)
*nodepp = NULL;
/* * We're unflattening device sub-tree if @dad is valid. There are * possibly multiple nodes in the first level of depth. We need * set @depth to 1 to make fdt_next_node() happy as it bails * immediately when negative @depth is found. Otherwise, the device * nodes except the first one won't be unflattened successfully.
*/ if (dad)
depth = initial_depth = 1;
/* * Reverse the child list. Some drivers assumes node order matches .dts * node order
*/ if (!dryrun)
reverse_nodes(root);
return mem - base;
}
/** * __unflatten_device_tree - create tree of device_nodes from flat blob * @blob: The blob to expand * @dad: Parent device node * @mynodes: The device_node tree created by the call * @dt_alloc: An allocator that provides a virtual address to memory * for the resulting tree * @detached: if true set OF_DETACHED on @mynodes * * unflattens a device-tree, creating the tree of struct device_node. It also * fills the "name" and "type" pointers of the nodes so the normal device-tree * walking functions can be used. * * Return: NULL on failure or the memory chunk containing the unflattened * device tree on success.
*/ void *__unflatten_device_tree(constvoid *blob, struct device_node *dad, struct device_node **mynodes, void *(*dt_alloc)(u64 size, u64 align), bool detached)
{ int size; void *mem; int ret;
if (mynodes)
*mynodes = NULL;
pr_debug(" -> unflatten_device_tree()\n");
if (!blob) {
pr_debug("No device tree pointer\n"); return NULL;
}
/** * of_fdt_unflatten_tree - create tree of device_nodes from flat blob * @blob: Flat device tree blob * @dad: Parent device node * @mynodes: The device tree created by the call * * unflattens the device-tree passed by the firmware, creating the * tree of struct device_node. It also fills the "name" and "type" * pointers of the nodes so the normal device-tree walking functions * can be used. * * Return: NULL on failure or the memory chunk containing the unflattened * device tree on success.
*/ void *of_fdt_unflatten_tree(constunsignedlong *blob, struct device_node *dad, struct device_node **mynodes)
{ void *mem;
mutex_lock(&of_fdt_unflatten_mutex);
mem = __unflatten_device_tree(blob, dad, mynodes, &kernel_tree_alloc, true);
mutex_unlock(&of_fdt_unflatten_mutex);
/* * fdt_reserve_elfcorehdr() - reserves memory for elf core header * * This function reserves the memory occupied by an elf core header * described in the device tree. This region contains all the * information about primary kernel's core image and is used by a dump * capture kernel to access the system memory on primary kernel.
*/ staticvoid __init fdt_reserve_elfcorehdr(void)
{ if (!IS_ENABLED(CONFIG_CRASH_DUMP) || !elfcorehdr_size) return;
if (memblock_is_region_reserved(elfcorehdr_addr, elfcorehdr_size)) {
pr_warn("elfcorehdr is overlapped\n"); return;
}
pr_info("Reserving %llu KiB of memory at 0x%llx for elfcorehdr\n",
elfcorehdr_size >> 10, elfcorehdr_addr);
}
/** * early_init_fdt_scan_reserved_mem() - create reserved memory regions * * This function grabs memory from early allocator for device exclusive use * defined in device tree structures. It should be called by arch specific code * once the early allocator (i.e. memblock) has been fully activated.
*/ void __init early_init_fdt_scan_reserved_mem(void)
{ int n; int res;
u64 base, size;
/* Process header /memreserve/ fields */ for (n = 0; ; n++) {
res = fdt_get_mem_rsv(initial_boot_params, n, &base, &size); if (res) {
pr_err("Invalid memory reservation block index %d\n", n); break;
} if (!size) break;
memblock_reserve(base, size);
}
}
/** * early_init_fdt_reserve_self() - reserve the memory used by the FDT blob
*/ void __init early_init_fdt_reserve_self(void)
{ if (!initial_boot_params) return;
/* Reserve the dtb region */
memblock_reserve(__pa(initial_boot_params),
fdt_totalsize(initial_boot_params));
}
/** * of_scan_flat_dt - scan flattened tree blob and call callback on each. * @it: callback function * @data: context data pointer * * This function is used to scan the flattened device-tree, it is * used to extract the memory information at boot before we can * unflatten the tree
*/ int __init of_scan_flat_dt(int (*it)(unsignedlong node, constchar *uname, int depth, void *data), void *data)
{ constvoid *blob = initial_boot_params; constchar *pathp; int offset, rc = 0, depth = -1;
/** * of_scan_flat_dt_subnodes - scan sub-nodes of a node call callback on each. * @parent: parent node * @it: callback function * @data: context data pointer * * This function is used to scan sub-nodes of a node.
*/ int __init of_scan_flat_dt_subnodes(unsignedlong parent, int (*it)(unsignedlong node, constchar *uname, void *data), void *data)
{ constvoid *blob = initial_boot_params; int node;
fdt_for_each_subnode(node, blob, parent) { constchar *pathp; int rc;
/** * of_get_flat_dt_subnode_by_name - get the subnode by given name * * @node: the parent node * @uname: the name of subnode * @return offset of the subnode, or -FDT_ERR_NOTFOUND if there is none
*/
/* * of_get_flat_dt_root - find the root node in the flat blob
*/ unsignedlong __init of_get_flat_dt_root(void)
{ return 0;
}
/* * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr * * This function can be used within scan_flattened_dt callback to get * access to properties
*/ constvoid *__init of_get_flat_dt_prop(unsignedlong node, constchar *name, int *size)
{ return fdt_getprop(initial_boot_params, node, name, size);
}
/** * of_fdt_is_compatible - Return true if given node from the given blob has * compat in its compatible list * @blob: A device tree blob * @node: node to test * @compat: compatible string to compare with compatible list. * * Return: a non-zero value on match with smaller values returned for more * specific compatible values.
*/ staticint of_fdt_is_compatible(constvoid *blob, unsignedlong node, constchar *compat)
{ constchar *cp; int cplen; unsignedlong l, score = 0;
cp = fdt_getprop(blob, node, "compatible", &cplen); if (cp == NULL) return 0; while (cplen > 0) {
score++; if (of_compat_cmp(cp, compat, strlen(compat)) == 0) return score;
l = strlen(cp) + 1;
cp += l;
cplen -= l;
}
return 0;
}
/** * of_flat_dt_is_compatible - Return true if given node has compat in compatible list * @node: node to test * @compat: compatible string to compare with compatible list.
*/ int __init of_flat_dt_is_compatible(unsignedlong node, constchar *compat)
{ return of_fdt_is_compatible(initial_boot_params, node, compat);
}
/* * of_flat_dt_match - Return true if node matches a list of compatible values
*/ staticint __init of_flat_dt_match(unsignedlong node, constchar *const *compat)
{ unsignedint tmp, score = 0;
/* * of_get_flat_dt_phandle - Given a node in the flat blob, return the phandle
*/
uint32_t __init of_get_flat_dt_phandle(unsignedlong node)
{ return fdt_get_phandle(initial_boot_params, node);
}
name = of_get_flat_dt_prop(dt_root, "model", NULL); if (!name)
name = of_get_flat_dt_prop(dt_root, "compatible", NULL); return name;
}
/** * of_flat_dt_match_machine - Iterate match tables to find matching machine. * * @default_match: A machine specific ptr to return in case of no match. * @get_next_compat: callback function to return next compatible match table. * * Iterate through machine match tables to find the best match for the machine * compatible string in the FDT.
*/ constvoid * __init of_flat_dt_match_machine(constvoid *default_match, constvoid * (*get_next_compat)(constchar * const**))
{ constvoid *data = NULL; constvoid *best_data = default_match; constchar *const *compat; unsignedlong dt_root; unsignedint best_score = ~1, score = 0;
dt_root = of_get_flat_dt_root(); while ((data = get_next_compat(&compat))) {
score = of_flat_dt_match(dt_root, compat); if (score > 0 && score < best_score) {
best_data = data;
best_score = score;
}
} if (!best_data) { constchar *prop; int size;
staticvoid __early_init_dt_declare_initrd(unsignedlong start, unsignedlong end)
{ /* * __va() is not yet available this early on some platforms. In that * case, the platform uses phys_initrd_start/phys_initrd_size instead * and does the VA conversion itself.
*/ if (!IS_ENABLED(CONFIG_ARM64) &&
!(IS_ENABLED(CONFIG_RISCV) && IS_ENABLED(CONFIG_64BIT))) {
initrd_start = (unsignedlong)__va(start);
initrd_end = (unsignedlong)__va(end);
initrd_below_start_ok = 1;
}
}
/** * early_init_dt_check_for_initrd - Decode initrd location from flat tree * @node: reference to node containing initrd location ('chosen')
*/ staticvoid __init early_init_dt_check_for_initrd(unsignedlong node)
{
u64 start, end; int len; const __be32 *prop;
/* * The main usage of linux,usable-memory-range is for crash dump kernel. * Originally, the number of usable-memory regions is one. Now there may * be two regions, low region and high region. * To make compatibility with existing user-space and older kdump, the low * region is always the last range of linux,usable-memory-range if exist.
*/ #define MAX_USABLE_RANGES 2
/** * early_init_dt_check_for_usable_mem_range - Decode usable memory range * location from flat tree
*/ void __init early_init_dt_check_for_usable_mem_range(void)
{ struct memblock_region rgn[MAX_USABLE_RANGES] = {0}; const __be32 *prop, *endp; int len, i; unsignedlong node = chosen_node_offset;
if ((long)node < 0) return;
pr_debug("Looking for usable-memory-range property... ");
if (memblock_mark_hotplug(base, size))
pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n",
base, base + size);
}
} return found_memory;
}
int __init early_init_dt_scan_chosen(char *cmdline)
{ int l, node; constchar *p; constvoid *rng_seed; constvoid *fdt = initial_boot_params;
node = fdt_path_offset(fdt, "/chosen"); if (node < 0)
node = fdt_path_offset(fdt, "/chosen@0"); if (node < 0) /* Handle the cmdline config options even if no /chosen node */ goto handle_cmdline;
/* Retrieve command line */
p = of_get_flat_dt_prop(node, "bootargs", &l); if (p != NULL && l > 0)
strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE));
handle_cmdline: /* * CONFIG_CMDLINE is meant to be a default in case nothing else * managed to set the command line, unless CONFIG_CMDLINE_FORCE * is set in which case we override whatever was found earlier.
*/ #ifdef CONFIG_CMDLINE #ifdefined(CONFIG_CMDLINE_EXTEND)
strlcat(cmdline, " ", COMMAND_LINE_SIZE);
strlcat(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #elifdefined(CONFIG_CMDLINE_FORCE)
strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #else /* No arguments from boot loader, use kernel's cmdl*/ if (!((char *)cmdline)[0])
strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif #endif/* CONFIG_CMDLINE */
pr_debug("Command line is: %s\n", (char *)cmdline);
/* Initialize {size,address}-cells info */
early_init_dt_scan_root();
returntrue;
}
void __init early_init_dt_scan_nodes(void)
{ int rc;
/* Retrieve various information from the /chosen node */
rc = early_init_dt_scan_chosen(boot_command_line); if (rc)
pr_warn("No chosen node found, continuing without\n");
/** * unflatten_device_tree - create tree of device_nodes from flat blob * * unflattens the device-tree passed by the firmware, creating the * tree of struct device_node. It also fills the "name" and "type" * pointers of the nodes so the normal device-tree walking functions * can be used.
*/ void __init unflatten_device_tree(void)
{ void *fdt = initial_boot_params;
/* Save the statically-placed regions in the reserved_mem array */
fdt_scan_reserved_mem_reg_nodes();
/* Populate an empty root node when bootloader doesn't provide one */ if (!fdt) {
fdt = (void *) __dtb_empty_root_begin; /* fdt_totalsize() will be used for copy size */ if (fdt_totalsize(fdt) >
__dtb_empty_root_end - __dtb_empty_root_begin) {
pr_err("invalid size in dtb_empty_root\n"); return;
}
of_fdt_crc32 = crc32_be(~0, fdt, fdt_totalsize(fdt));
fdt = copy_device_tree(fdt);
}
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
unittest_unflatten_overlay_base();
}
/** * unflatten_and_copy_device_tree - copy and create tree of device_nodes from flat blob * * Copies and unflattens the device-tree passed by the firmware, creating the * tree of struct device_node. It also fills the "name" and "type" * pointers of the nodes so the normal device-tree walking functions * can be used. This should only be used when the FDT memory has not been * reserved such is the case when the FDT is built-in to the kernel init * section. If the FDT memory is reserved already then unflatten_device_tree * should be used instead.
*/ void __init unflatten_and_copy_device_tree(void)
{ if (initial_boot_params)
initial_boot_params = copy_device_tree(initial_boot_params);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.