// SPDX-License-Identifier: GPL-2.0 /* * Functions for working with the Flattened Device Tree data format * * Copyright 2009 Benjamin Herrenschmidt, IBM Corp * [email protected] */ #define pr_fmt(fmt) … #include <linux/acpi.h> #include <linux/crash_dump.h> #include <linux/crc32.h> #include <linux/kernel.h> #include <linux/initrd.h> #include <linux/memblock.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/of_fdt.h> #include <linux/sizes.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/libfdt.h> #include <linux/debugfs.h> #include <linux/serial_core.h> #include <linux/sysfs.h> #include <linux/random.h> #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_dt_S_dtb in scripts/Makefile.lib */ 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) { … } bool of_fdt_device_is_available(const void *blob, unsigned long node) { … } static void *unflatten_dt_alloc(void **mem, unsigned long size, unsigned long align) { … } static void populate_properties(const void *blob, int offset, void **mem, struct device_node *np, const char *nodename, bool dryrun) { … } static int populate_node(const void *blob, int offset, void **mem, struct device_node *dad, struct device_node **pnp, bool dryrun) { … } static void reverse_nodes(struct device_node *parent) { … } /** * 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 */ static int unflatten_dt_nodes(const void *blob, void *mem, struct device_node *dad, struct device_node **nodepp) { … } /** * __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(const void *blob, struct device_node *dad, struct device_node **mynodes, void *(*dt_alloc)(u64 size, u64 align), bool detached) { … } static void *kernel_tree_alloc(u64 size, u64 align) { … } static DEFINE_MUTEX(of_fdt_unflatten_mutex); /** * 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(const unsigned long *blob, struct device_node *dad, struct device_node **mynodes) { … } EXPORT_SYMBOL_GPL(…); /* Everything below here references initial_boot_params directly. */ int __initdata dt_root_addr_cells; int __initdata dt_root_size_cells; void *initial_boot_params __ro_after_init; #ifdef CONFIG_OF_EARLY_FLATTREE static u32 of_fdt_crc32; /* * fdt_reserve_elfcorehdr() - reserves memory for elf core header * * This function reserves the memory occupied by an elf core header * described in the device tree. This region contains all the * information about primary kernel's core image and is used by a dump * capture kernel to access the system memory on primary kernel. */ static void __init fdt_reserve_elfcorehdr(void) { … } /** * 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) { … } /** * early_init_fdt_reserve_self() - reserve the memory used by the FDT blob */ void __init early_init_fdt_reserve_self(void) { … } /** * 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)(unsigned long node, const char *uname, int depth, void *data), void *data) { … } /** * 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(unsigned long parent, int (*it)(unsigned long node, const char *uname, void *data), void *data) { … } /** * 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 */ int __init of_get_flat_dt_subnode_by_name(unsigned long node, const char *uname) { … } /* * of_get_flat_dt_root - find the root node in the flat blob */ unsigned long __init of_get_flat_dt_root(void) { … } /* * 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 */ const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, int *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. */ static int of_fdt_is_compatible(const void *blob, unsigned long node, const char *compat) { … } /** * 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(unsigned long node, const char *compat) { … } /* * of_flat_dt_match - Return true if node matches a list of compatible values */ static int __init of_flat_dt_match(unsigned long node, const char *const *compat) { … } /* * of_get_flat_dt_phandle - Given a node in the flat blob, return the phandle */ uint32_t __init of_get_flat_dt_phandle(unsigned long node) { … } const char * __init of_flat_dt_get_machine_name(void) { … } /** * 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. */ const void * __init of_flat_dt_match_machine(const void *default_match, const void * (*get_next_compat)(const char * const**)) { … } static void __early_init_dt_declare_initrd(unsigned long start, unsigned long end) { … } /** * early_init_dt_check_for_initrd - Decode initrd location from flat tree * @node: reference to node containing initrd location ('chosen') */ static void __init early_init_dt_check_for_initrd(unsigned long node) { … } /** * early_init_dt_check_for_elfcorehdr - Decode elfcorehdr location from flat * tree * @node: reference to node containing elfcorehdr location ('chosen') */ static void __init early_init_dt_check_for_elfcorehdr(unsigned long node) { … } static unsigned long chosen_node_offset = …; /* * 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 … /** * 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) { … } #ifdef CONFIG_SERIAL_EARLYCON int __init early_init_dt_scan_chosen_stdout(void) { … } #endif /* * early_init_dt_scan_root - fetch the top level address and size cells */ int __init early_init_dt_scan_root(void) { … } u64 __init dt_mem_next_cell(int s, const __be32 **cellp) { … } /* * early_init_dt_scan_memory - Look for and parse memory nodes */ int __init early_init_dt_scan_memory(void) { … } int __init early_init_dt_scan_chosen(char *cmdline) { … } #ifndef MIN_MEMBLOCK_ADDR #define MIN_MEMBLOCK_ADDR … #endif #ifndef MAX_MEMBLOCK_ADDR #define MAX_MEMBLOCK_ADDR … #endif void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) { … } static void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) { … } bool __init early_init_dt_verify(void *params) { … } void __init early_init_dt_scan_nodes(void) { … } bool __init early_init_dt_scan(void *params) { … } static void *__init copy_device_tree(void *fdt) { … } /** * 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) { … } /** * 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) { … } #ifdef CONFIG_SYSFS static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { … } static int __init of_fdt_raw_init(void) { … } late_initcall(of_fdt_raw_init); #endif #endif /* CONFIG_OF_EARLY_FLATTREE */