162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Functions for dealing with DT resolution 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com> 662306a36Sopenharmony_ci * Copyright (C) 2012 Texas Instruments Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) "OF: resolver: " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_device.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/ctype.h> 1762306a36Sopenharmony_ci#include <linux/errno.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "of_private.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic phandle live_tree_max_phandle(void) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct device_node *node; 2562306a36Sopenharmony_ci phandle phandle; 2662306a36Sopenharmony_ci unsigned long flags; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci raw_spin_lock_irqsave(&devtree_lock, flags); 2962306a36Sopenharmony_ci phandle = 0; 3062306a36Sopenharmony_ci for_each_of_allnodes(node) { 3162306a36Sopenharmony_ci if (node->phandle != OF_PHANDLE_ILLEGAL && 3262306a36Sopenharmony_ci node->phandle > phandle) 3362306a36Sopenharmony_ci phandle = node->phandle; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&devtree_lock, flags); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return phandle; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void adjust_overlay_phandles(struct device_node *overlay, 4162306a36Sopenharmony_ci int phandle_delta) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct device_node *child; 4462306a36Sopenharmony_ci struct property *prop; 4562306a36Sopenharmony_ci phandle phandle; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* adjust node's phandle in node */ 4862306a36Sopenharmony_ci if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL) 4962306a36Sopenharmony_ci overlay->phandle += phandle_delta; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* copy adjusted phandle into *phandle properties */ 5262306a36Sopenharmony_ci for_each_property_of_node(overlay, prop) { 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (of_prop_cmp(prop->name, "phandle") && 5562306a36Sopenharmony_ci of_prop_cmp(prop->name, "linux,phandle")) 5662306a36Sopenharmony_ci continue; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (prop->length < 4) 5962306a36Sopenharmony_ci continue; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci phandle = be32_to_cpup(prop->value); 6262306a36Sopenharmony_ci if (phandle == OF_PHANDLE_ILLEGAL) 6362306a36Sopenharmony_ci continue; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci *(__be32 *)prop->value = cpu_to_be32(overlay->phandle); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci for_each_child_of_node(overlay, child) 6962306a36Sopenharmony_ci adjust_overlay_phandles(child, phandle_delta); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int update_usages_of_a_phandle_reference(struct device_node *overlay, 7362306a36Sopenharmony_ci struct property *prop_fixup, phandle phandle) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct device_node *refnode; 7662306a36Sopenharmony_ci struct property *prop; 7762306a36Sopenharmony_ci char *value, *cur, *end, *node_path, *prop_name, *s; 7862306a36Sopenharmony_ci int offset, len; 7962306a36Sopenharmony_ci int err = 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL); 8262306a36Sopenharmony_ci if (!value) 8362306a36Sopenharmony_ci return -ENOMEM; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* prop_fixup contains a list of tuples of path:property_name:offset */ 8662306a36Sopenharmony_ci end = value + prop_fixup->length; 8762306a36Sopenharmony_ci for (cur = value; cur < end; cur += len + 1) { 8862306a36Sopenharmony_ci len = strlen(cur); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci node_path = cur; 9162306a36Sopenharmony_ci s = strchr(cur, ':'); 9262306a36Sopenharmony_ci if (!s) { 9362306a36Sopenharmony_ci err = -EINVAL; 9462306a36Sopenharmony_ci goto err_fail; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci *s++ = '\0'; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci prop_name = s; 9962306a36Sopenharmony_ci s = strchr(s, ':'); 10062306a36Sopenharmony_ci if (!s) { 10162306a36Sopenharmony_ci err = -EINVAL; 10262306a36Sopenharmony_ci goto err_fail; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci *s++ = '\0'; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci err = kstrtoint(s, 10, &offset); 10762306a36Sopenharmony_ci if (err) 10862306a36Sopenharmony_ci goto err_fail; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path); 11162306a36Sopenharmony_ci if (!refnode) 11262306a36Sopenharmony_ci continue; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for_each_property_of_node(refnode, prop) { 11562306a36Sopenharmony_ci if (!of_prop_cmp(prop->name, prop_name)) 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci of_node_put(refnode); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (!prop) { 12162306a36Sopenharmony_ci err = -ENOENT; 12262306a36Sopenharmony_ci goto err_fail; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (offset < 0 || offset + sizeof(__be32) > prop->length) { 12662306a36Sopenharmony_ci err = -EINVAL; 12762306a36Sopenharmony_ci goto err_fail; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cierr_fail: 13462306a36Sopenharmony_ci kfree(value); 13562306a36Sopenharmony_ci return err; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* compare nodes taking into account that 'name' strips out the @ part */ 13962306a36Sopenharmony_cistatic int node_name_cmp(const struct device_node *dn1, 14062306a36Sopenharmony_ci const struct device_node *dn2) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci const char *n1 = kbasename(dn1->full_name); 14362306a36Sopenharmony_ci const char *n2 = kbasename(dn2->full_name); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return of_node_cmp(n1, n2); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * Adjust the local phandle references by the given phandle delta. 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * Subtree @local_fixups, which is overlay node __local_fixups__, 15262306a36Sopenharmony_ci * mirrors the fragment node structure at the root of the overlay. 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * For each property in the fragments that contains a phandle reference, 15562306a36Sopenharmony_ci * @local_fixups has a property of the same name that contains a list 15662306a36Sopenharmony_ci * of offsets of the phandle reference(s) within the respective property 15762306a36Sopenharmony_ci * value(s). The values at these offsets will be fixed up. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistatic int adjust_local_phandle_references(struct device_node *local_fixups, 16062306a36Sopenharmony_ci struct device_node *overlay, int phandle_delta) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct device_node *child, *overlay_child; 16362306a36Sopenharmony_ci struct property *prop_fix, *prop; 16462306a36Sopenharmony_ci int err, i, count; 16562306a36Sopenharmony_ci unsigned int off; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!local_fixups) 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci for_each_property_of_node(local_fixups, prop_fix) { 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* skip properties added automatically */ 17362306a36Sopenharmony_ci if (!of_prop_cmp(prop_fix->name, "name") || 17462306a36Sopenharmony_ci !of_prop_cmp(prop_fix->name, "phandle") || 17562306a36Sopenharmony_ci !of_prop_cmp(prop_fix->name, "linux,phandle")) 17662306a36Sopenharmony_ci continue; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if ((prop_fix->length % 4) != 0 || prop_fix->length == 0) 17962306a36Sopenharmony_ci return -EINVAL; 18062306a36Sopenharmony_ci count = prop_fix->length / sizeof(__be32); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for_each_property_of_node(overlay, prop) { 18362306a36Sopenharmony_ci if (!of_prop_cmp(prop->name, prop_fix->name)) 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!prop) 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci for (i = 0; i < count; i++) { 19162306a36Sopenharmony_ci off = be32_to_cpu(((__be32 *)prop_fix->value)[i]); 19262306a36Sopenharmony_ci if ((off + 4) > prop->length) 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci be32_add_cpu(prop->value + off, phandle_delta); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * These nested loops recurse down two subtrees in parallel, where the 20162306a36Sopenharmony_ci * node names in the two subtrees match. 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * The roots of the subtrees are the overlay's __local_fixups__ node 20462306a36Sopenharmony_ci * and the overlay's root node. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci for_each_child_of_node(local_fixups, child) { 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci for_each_child_of_node(overlay, overlay_child) 20962306a36Sopenharmony_ci if (!node_name_cmp(child, overlay_child)) { 21062306a36Sopenharmony_ci of_node_put(overlay_child); 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!overlay_child) { 21562306a36Sopenharmony_ci of_node_put(child); 21662306a36Sopenharmony_ci return -EINVAL; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci err = adjust_local_phandle_references(child, overlay_child, 22062306a36Sopenharmony_ci phandle_delta); 22162306a36Sopenharmony_ci if (err) { 22262306a36Sopenharmony_ci of_node_put(child); 22362306a36Sopenharmony_ci return err; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/** 23162306a36Sopenharmony_ci * of_resolve_phandles - Relocate and resolve overlay against live tree 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * @overlay: Pointer to devicetree overlay to relocate and resolve 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * Modify (relocate) values of local phandles in @overlay to a range that 23662306a36Sopenharmony_ci * does not conflict with the live expanded devicetree. Update references 23762306a36Sopenharmony_ci * to the local phandles in @overlay. Update (resolve) phandle references 23862306a36Sopenharmony_ci * in @overlay that refer to the live expanded devicetree. 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Phandle values in the live tree are in the range of 24162306a36Sopenharmony_ci * 1 .. live_tree_max_phandle(). The range of phandle values in the overlay 24262306a36Sopenharmony_ci * also begin with at 1. Adjust the phandle values in the overlay to begin 24362306a36Sopenharmony_ci * at live_tree_max_phandle() + 1. Update references to the phandles to 24462306a36Sopenharmony_ci * the adjusted phandle values. 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * The name of each property in the "__fixups__" node in the overlay matches 24762306a36Sopenharmony_ci * the name of a symbol (a label) in the live tree. The values of each 24862306a36Sopenharmony_ci * property in the "__fixups__" node is a list of the property values in the 24962306a36Sopenharmony_ci * overlay that need to be updated to contain the phandle reference 25062306a36Sopenharmony_ci * corresponding to that symbol in the live tree. Update the references in 25162306a36Sopenharmony_ci * the overlay with the phandle values in the live tree. 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * @overlay must be detached. 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * Resolving and applying @overlay to the live expanded devicetree must be 25662306a36Sopenharmony_ci * protected by a mechanism to ensure that multiple overlays are processed 25762306a36Sopenharmony_ci * in a single threaded manner so that multiple overlays will not relocate 25862306a36Sopenharmony_ci * phandles to overlapping ranges. The mechanism to enforce this is not 25962306a36Sopenharmony_ci * yet implemented. 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * Return: %0 on success or a negative error value on error. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ciint of_resolve_phandles(struct device_node *overlay) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct device_node *child, *local_fixups, *refnode; 26662306a36Sopenharmony_ci struct device_node *tree_symbols, *overlay_fixups; 26762306a36Sopenharmony_ci struct property *prop; 26862306a36Sopenharmony_ci const char *refpath; 26962306a36Sopenharmony_ci phandle phandle, phandle_delta; 27062306a36Sopenharmony_ci int err; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci tree_symbols = NULL; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!overlay) { 27562306a36Sopenharmony_ci pr_err("null overlay\n"); 27662306a36Sopenharmony_ci err = -EINVAL; 27762306a36Sopenharmony_ci goto out; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (!of_node_check_flag(overlay, OF_DETACHED)) { 28162306a36Sopenharmony_ci pr_err("overlay not detached\n"); 28262306a36Sopenharmony_ci err = -EINVAL; 28362306a36Sopenharmony_ci goto out; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci phandle_delta = live_tree_max_phandle() + 1; 28762306a36Sopenharmony_ci adjust_overlay_phandles(overlay, phandle_delta); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci for_each_child_of_node(overlay, local_fixups) 29062306a36Sopenharmony_ci if (of_node_name_eq(local_fixups, "__local_fixups__")) 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta); 29462306a36Sopenharmony_ci if (err) 29562306a36Sopenharmony_ci goto out; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci overlay_fixups = NULL; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci for_each_child_of_node(overlay, child) { 30062306a36Sopenharmony_ci if (of_node_name_eq(child, "__fixups__")) 30162306a36Sopenharmony_ci overlay_fixups = child; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (!overlay_fixups) { 30562306a36Sopenharmony_ci err = 0; 30662306a36Sopenharmony_ci goto out; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci tree_symbols = of_find_node_by_path("/__symbols__"); 31062306a36Sopenharmony_ci if (!tree_symbols) { 31162306a36Sopenharmony_ci pr_err("no symbols in root of device tree.\n"); 31262306a36Sopenharmony_ci err = -EINVAL; 31362306a36Sopenharmony_ci goto out; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci for_each_property_of_node(overlay_fixups, prop) { 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* skip properties added automatically */ 31962306a36Sopenharmony_ci if (!of_prop_cmp(prop->name, "name")) 32062306a36Sopenharmony_ci continue; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci err = of_property_read_string(tree_symbols, 32362306a36Sopenharmony_ci prop->name, &refpath); 32462306a36Sopenharmony_ci if (err) { 32562306a36Sopenharmony_ci pr_err("node label '%s' not found in live devicetree symbols table\n", 32662306a36Sopenharmony_ci prop->name); 32762306a36Sopenharmony_ci goto out; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci refnode = of_find_node_by_path(refpath); 33162306a36Sopenharmony_ci if (!refnode) { 33262306a36Sopenharmony_ci err = -ENOENT; 33362306a36Sopenharmony_ci goto out; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci phandle = refnode->phandle; 33762306a36Sopenharmony_ci of_node_put(refnode); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci err = update_usages_of_a_phandle_reference(overlay, prop, phandle); 34062306a36Sopenharmony_ci if (err) 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciout: 34562306a36Sopenharmony_ci if (err) 34662306a36Sopenharmony_ci pr_err("overlay phandle fixup failed: %d\n", err); 34762306a36Sopenharmony_ci of_node_put(tree_symbols); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return err; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_resolve_phandles); 352