1af6074fcSRob Herring // SPDX-License-Identifier: GPL-2.0 27941b27bSPantelis Antoniou /* 37941b27bSPantelis Antoniou * Functions for dealing with DT resolution 47941b27bSPantelis Antoniou * 57941b27bSPantelis Antoniou * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com> 67941b27bSPantelis Antoniou * Copyright (C) 2012 Texas Instruments Inc. 77941b27bSPantelis Antoniou */ 87941b27bSPantelis Antoniou 9606ad42aSRob Herring #define pr_fmt(fmt) "OF: resolver: " fmt 10606ad42aSRob Herring 117941b27bSPantelis Antoniou #include <linux/kernel.h> 127941b27bSPantelis Antoniou #include <linux/module.h> 137941b27bSPantelis Antoniou #include <linux/of.h> 147941b27bSPantelis Antoniou #include <linux/of_device.h> 157941b27bSPantelis Antoniou #include <linux/string.h> 167941b27bSPantelis Antoniou #include <linux/ctype.h> 177941b27bSPantelis Antoniou #include <linux/errno.h> 187941b27bSPantelis Antoniou #include <linux/slab.h> 197941b27bSPantelis Antoniou 2027497e11SRob Herring #include "of_private.h" 2127497e11SRob Herring 227941b27bSPantelis Antoniou /* illegal phandle value (set when unresolved) */ 237941b27bSPantelis Antoniou #define OF_PHANDLE_ILLEGAL 0xdeadbeef 247941b27bSPantelis Antoniou 25f94823f2SFrank Rowand static phandle live_tree_max_phandle(void) 267941b27bSPantelis Antoniou { 277941b27bSPantelis Antoniou struct device_node *node; 287941b27bSPantelis Antoniou phandle phandle; 297941b27bSPantelis Antoniou unsigned long flags; 307941b27bSPantelis Antoniou 317941b27bSPantelis Antoniou raw_spin_lock_irqsave(&devtree_lock, flags); 327941b27bSPantelis Antoniou phandle = 0; 337941b27bSPantelis Antoniou for_each_of_allnodes(node) { 347941b27bSPantelis Antoniou if (node->phandle != OF_PHANDLE_ILLEGAL && 357941b27bSPantelis Antoniou node->phandle > phandle) 367941b27bSPantelis Antoniou phandle = node->phandle; 377941b27bSPantelis Antoniou } 387941b27bSPantelis Antoniou raw_spin_unlock_irqrestore(&devtree_lock, flags); 397941b27bSPantelis Antoniou 407941b27bSPantelis Antoniou return phandle; 417941b27bSPantelis Antoniou } 427941b27bSPantelis Antoniou 4325e16877SFrank Rowand static void adjust_overlay_phandles(struct device_node *overlay, 447941b27bSPantelis Antoniou int phandle_delta) 457941b27bSPantelis Antoniou { 467941b27bSPantelis Antoniou struct device_node *child; 477941b27bSPantelis Antoniou struct property *prop; 487941b27bSPantelis Antoniou phandle phandle; 497941b27bSPantelis Antoniou 50269f1a67SFrank Rowand /* adjust node's phandle in node */ 5125e16877SFrank Rowand if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL) 5225e16877SFrank Rowand overlay->phandle += phandle_delta; 537941b27bSPantelis Antoniou 54269f1a67SFrank Rowand /* copy adjusted phandle into *phandle properties */ 5525e16877SFrank Rowand for_each_property_of_node(overlay, prop) { 567941b27bSPantelis Antoniou 579f27ede4SFrank Rowand if (of_prop_cmp(prop->name, "phandle") && 589f27ede4SFrank Rowand of_prop_cmp(prop->name, "linux,phandle")) 597941b27bSPantelis Antoniou continue; 607941b27bSPantelis Antoniou 617941b27bSPantelis Antoniou if (prop->length < 4) 627941b27bSPantelis Antoniou continue; 637941b27bSPantelis Antoniou 647941b27bSPantelis Antoniou phandle = be32_to_cpup(prop->value); 65a67976ecSFrank Rowand if (phandle == OF_PHANDLE_ILLEGAL) 667941b27bSPantelis Antoniou continue; 677941b27bSPantelis Antoniou 6817a70355SRob Herring *(__be32 *)prop->value = cpu_to_be32(overlay->phandle); 697941b27bSPantelis Antoniou } 707941b27bSPantelis Antoniou 7125e16877SFrank Rowand for_each_child_of_node(overlay, child) 72f94823f2SFrank Rowand adjust_overlay_phandles(child, phandle_delta); 737941b27bSPantelis Antoniou } 747941b27bSPantelis Antoniou 7525e16877SFrank Rowand static int update_usages_of_a_phandle_reference(struct device_node *overlay, 7625e16877SFrank Rowand struct property *prop_fixup, phandle phandle) 777941b27bSPantelis Antoniou { 787941b27bSPantelis Antoniou struct device_node *refnode; 7925e16877SFrank Rowand struct property *prop; 8025e16877SFrank Rowand char *value, *cur, *end, *node_path, *prop_name, *s; 8125e16877SFrank Rowand int offset, len; 827941b27bSPantelis Antoniou int err = 0; 837941b27bSPantelis Antoniou 84eeb09506SStephen Boyd value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL); 8525e16877SFrank Rowand if (!value) 867941b27bSPantelis Antoniou return -ENOMEM; 877941b27bSPantelis Antoniou 88269f1a67SFrank Rowand /* prop_fixup contains a list of tuples of path:property_name:offset */ 8925e16877SFrank Rowand end = value + prop_fixup->length; 9025e16877SFrank Rowand for (cur = value; cur < end; cur += len + 1) { 9125e16877SFrank Rowand len = strlen(cur); 927941b27bSPantelis Antoniou 9325e16877SFrank Rowand node_path = cur; 9425e16877SFrank Rowand s = strchr(cur, ':'); 957941b27bSPantelis Antoniou if (!s) { 967941b27bSPantelis Antoniou err = -EINVAL; 977941b27bSPantelis Antoniou goto err_fail; 987941b27bSPantelis Antoniou } 997941b27bSPantelis Antoniou *s++ = '\0'; 1007941b27bSPantelis Antoniou 10125e16877SFrank Rowand prop_name = s; 1027941b27bSPantelis Antoniou s = strchr(s, ':'); 1037941b27bSPantelis Antoniou if (!s) { 1047941b27bSPantelis Antoniou err = -EINVAL; 1057941b27bSPantelis Antoniou goto err_fail; 1067941b27bSPantelis Antoniou } 1077941b27bSPantelis Antoniou *s++ = '\0'; 108624ab2a4SFrank Rowand 1097941b27bSPantelis Antoniou err = kstrtoint(s, 10, &offset); 1109f27ede4SFrank Rowand if (err) 1117941b27bSPantelis Antoniou goto err_fail; 1127941b27bSPantelis Antoniou 11327497e11SRob Herring refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path); 11496d1c8e8SFrank Rowand if (!refnode) 1157941b27bSPantelis Antoniou continue; 1167941b27bSPantelis Antoniou 11725e16877SFrank Rowand for_each_property_of_node(refnode, prop) { 11825e16877SFrank Rowand if (!of_prop_cmp(prop->name, prop_name)) 1197941b27bSPantelis Antoniou break; 1207941b27bSPantelis Antoniou } 12182f68756SAmitoj Kaur Chawla of_node_put(refnode); 1227941b27bSPantelis Antoniou 12325e16877SFrank Rowand if (!prop) { 1247941b27bSPantelis Antoniou err = -ENOENT; 1257941b27bSPantelis Antoniou goto err_fail; 1267941b27bSPantelis Antoniou } 1277941b27bSPantelis Antoniou 12825e16877SFrank Rowand *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle); 1297941b27bSPantelis Antoniou } 1307941b27bSPantelis Antoniou 1317941b27bSPantelis Antoniou err_fail: 13225e16877SFrank Rowand kfree(value); 1337941b27bSPantelis Antoniou return err; 1347941b27bSPantelis Antoniou } 1357941b27bSPantelis Antoniou 136da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */ 137fad556bfSFrank Rowand static int node_name_cmp(const struct device_node *dn1, 138da56d04cSPantelis Antoniou const struct device_node *dn2) 139da56d04cSPantelis Antoniou { 14095e6b1faSRob Herring const char *n1 = kbasename(dn1->full_name); 14195e6b1faSRob Herring const char *n2 = kbasename(dn2->full_name); 142da56d04cSPantelis Antoniou 143da56d04cSPantelis Antoniou return of_node_cmp(n1, n2); 144da56d04cSPantelis Antoniou } 145da56d04cSPantelis Antoniou 1467941b27bSPantelis Antoniou /* 1477941b27bSPantelis Antoniou * Adjust the local phandle references by the given phandle delta. 148269f1a67SFrank Rowand * 149269f1a67SFrank Rowand * Subtree @local_fixups, which is overlay node __local_fixups__, 150269f1a67SFrank Rowand * mirrors the fragment node structure at the root of the overlay. 151269f1a67SFrank Rowand * 152269f1a67SFrank Rowand * For each property in the fragments that contains a phandle reference, 153269f1a67SFrank Rowand * @local_fixups has a property of the same name that contains a list 154269f1a67SFrank Rowand * of offsets of the phandle reference(s) within the respective property 155269f1a67SFrank Rowand * value(s). The values at these offsets will be fixed up. 1567941b27bSPantelis Antoniou */ 15725e16877SFrank Rowand static int adjust_local_phandle_references(struct device_node *local_fixups, 15825e16877SFrank Rowand struct device_node *overlay, int phandle_delta) 1597941b27bSPantelis Antoniou { 16025e16877SFrank Rowand struct device_node *child, *overlay_child; 16125e16877SFrank Rowand struct property *prop_fix, *prop; 162da56d04cSPantelis Antoniou int err, i, count; 163da56d04cSPantelis Antoniou unsigned int off; 1647941b27bSPantelis Antoniou 16525e16877SFrank Rowand if (!local_fixups) 1667941b27bSPantelis Antoniou return 0; 1677941b27bSPantelis Antoniou 16825e16877SFrank Rowand for_each_property_of_node(local_fixups, prop_fix) { 169da56d04cSPantelis Antoniou 1707941b27bSPantelis Antoniou /* skip properties added automatically */ 17125e16877SFrank Rowand if (!of_prop_cmp(prop_fix->name, "name") || 17225e16877SFrank Rowand !of_prop_cmp(prop_fix->name, "phandle") || 17325e16877SFrank Rowand !of_prop_cmp(prop_fix->name, "linux,phandle")) 1747941b27bSPantelis Antoniou continue; 1757941b27bSPantelis Antoniou 17625e16877SFrank Rowand if ((prop_fix->length % 4) != 0 || prop_fix->length == 0) 177da56d04cSPantelis Antoniou return -EINVAL; 17825e16877SFrank Rowand count = prop_fix->length / sizeof(__be32); 179da56d04cSPantelis Antoniou 18025e16877SFrank Rowand for_each_property_of_node(overlay, prop) { 18125e16877SFrank Rowand if (!of_prop_cmp(prop->name, prop_fix->name)) 182da56d04cSPantelis Antoniou break; 183da56d04cSPantelis Antoniou } 184da56d04cSPantelis Antoniou 18525e16877SFrank Rowand if (!prop) 186da56d04cSPantelis Antoniou return -EINVAL; 187da56d04cSPantelis Antoniou 188da56d04cSPantelis Antoniou for (i = 0; i < count; i++) { 18925e16877SFrank Rowand off = be32_to_cpu(((__be32 *)prop_fix->value)[i]); 190ea8229b7SFrank Rowand if ((off + 4) > prop->length) 191da56d04cSPantelis Antoniou return -EINVAL; 192da56d04cSPantelis Antoniou 193d35d623fSStephen Boyd be32_add_cpu(prop->value + off, phandle_delta); 194da56d04cSPantelis Antoniou } 195da56d04cSPantelis Antoniou } 196da56d04cSPantelis Antoniou 197269f1a67SFrank Rowand /* 198269f1a67SFrank Rowand * These nested loops recurse down two subtrees in parallel, where the 199269f1a67SFrank Rowand * node names in the two subtrees match. 200269f1a67SFrank Rowand * 201269f1a67SFrank Rowand * The roots of the subtrees are the overlay's __local_fixups__ node 202269f1a67SFrank Rowand * and the overlay's root node. 203269f1a67SFrank Rowand */ 20425e16877SFrank Rowand for_each_child_of_node(local_fixups, child) { 205da56d04cSPantelis Antoniou 20625e16877SFrank Rowand for_each_child_of_node(overlay, overlay_child) 20725e16877SFrank Rowand if (!node_name_cmp(child, overlay_child)) 208da56d04cSPantelis Antoniou break; 209da56d04cSPantelis Antoniou 21025e16877SFrank Rowand if (!overlay_child) 211da56d04cSPantelis Antoniou return -EINVAL; 212da56d04cSPantelis Antoniou 21325e16877SFrank Rowand err = adjust_local_phandle_references(child, overlay_child, 214da56d04cSPantelis Antoniou phandle_delta); 2159f27ede4SFrank Rowand if (err) 2167941b27bSPantelis Antoniou return err; 2177941b27bSPantelis Antoniou } 2187941b27bSPantelis Antoniou 2197941b27bSPantelis Antoniou return 0; 2207941b27bSPantelis Antoniou } 2217941b27bSPantelis Antoniou 2227941b27bSPantelis Antoniou /** 223269f1a67SFrank Rowand * of_resolve_phandles - Relocate and resolve overlay against live tree 2247941b27bSPantelis Antoniou * 225269f1a67SFrank Rowand * @overlay: Pointer to devicetree overlay to relocate and resolve 2267941b27bSPantelis Antoniou * 227269f1a67SFrank Rowand * Modify (relocate) values of local phandles in @overlay to a range that 228269f1a67SFrank Rowand * does not conflict with the live expanded devicetree. Update references 229269f1a67SFrank Rowand * to the local phandles in @overlay. Update (resolve) phandle references 230269f1a67SFrank Rowand * in @overlay that refer to the live expanded devicetree. 231269f1a67SFrank Rowand * 232269f1a67SFrank Rowand * Phandle values in the live tree are in the range of 233269f1a67SFrank Rowand * 1 .. live_tree_max_phandle(). The range of phandle values in the overlay 234269f1a67SFrank Rowand * also begin with at 1. Adjust the phandle values in the overlay to begin 235269f1a67SFrank Rowand * at live_tree_max_phandle() + 1. Update references to the phandles to 236269f1a67SFrank Rowand * the adjusted phandle values. 237269f1a67SFrank Rowand * 238269f1a67SFrank Rowand * The name of each property in the "__fixups__" node in the overlay matches 239269f1a67SFrank Rowand * the name of a symbol (a label) in the live tree. The values of each 240269f1a67SFrank Rowand * property in the "__fixups__" node is a list of the property values in the 241269f1a67SFrank Rowand * overlay that need to be updated to contain the phandle reference 242269f1a67SFrank Rowand * corresponding to that symbol in the live tree. Update the references in 243269f1a67SFrank Rowand * the overlay with the phandle values in the live tree. 244269f1a67SFrank Rowand * 245269f1a67SFrank Rowand * @overlay must be detached. 246269f1a67SFrank Rowand * 247269f1a67SFrank Rowand * Resolving and applying @overlay to the live expanded devicetree must be 248269f1a67SFrank Rowand * protected by a mechanism to ensure that multiple overlays are processed 249269f1a67SFrank Rowand * in a single threaded manner so that multiple overlays will not relocate 250269f1a67SFrank Rowand * phandles to overlapping ranges. The mechanism to enforce this is not 251269f1a67SFrank Rowand * yet implemented. 252269f1a67SFrank Rowand * 253269f1a67SFrank Rowand * Return: %0 on success or a negative error value on error. 2547941b27bSPantelis Antoniou */ 25525e16877SFrank Rowand int of_resolve_phandles(struct device_node *overlay) 2567941b27bSPantelis Antoniou { 25725e16877SFrank Rowand struct device_node *child, *local_fixups, *refnode; 2585581a95fSFrank Rowand struct device_node *tree_symbols, *overlay_fixups; 25925e16877SFrank Rowand struct property *prop; 2607941b27bSPantelis Antoniou const char *refpath; 2617941b27bSPantelis Antoniou phandle phandle, phandle_delta; 2627941b27bSPantelis Antoniou int err; 2637941b27bSPantelis Antoniou 264d9181b20SFrank Rowand tree_symbols = NULL; 265d9181b20SFrank Rowand 266624ab2a4SFrank Rowand if (!overlay) { 267624ab2a4SFrank Rowand pr_err("null overlay\n"); 268d9181b20SFrank Rowand err = -EINVAL; 26932bed310SMoritz Fischer goto out; 270624ab2a4SFrank Rowand } 271f948d6d8SFrank Rowand 272f948d6d8SFrank Rowand #if 0 273f948d6d8SFrank Rowand Temporarily disable check so that old style overlay unittests 274f948d6d8SFrank Rowand do not fail when of_resolve_phandles() is moved into 275f948d6d8SFrank Rowand of_overlay_apply(). 276f948d6d8SFrank Rowand 277624ab2a4SFrank Rowand if (!of_node_check_flag(overlay, OF_DETACHED)) { 278624ab2a4SFrank Rowand pr_err("overlay not detached\n"); 279d9181b20SFrank Rowand err = -EINVAL; 28032bed310SMoritz Fischer goto out; 281624ab2a4SFrank Rowand } 282f948d6d8SFrank Rowand #endif 2837941b27bSPantelis Antoniou 284f94823f2SFrank Rowand phandle_delta = live_tree_max_phandle() + 1; 28525e16877SFrank Rowand adjust_overlay_phandles(overlay, phandle_delta); 286da56d04cSPantelis Antoniou 28725e16877SFrank Rowand for_each_child_of_node(overlay, local_fixups) 28825e16877SFrank Rowand if (!of_node_cmp(local_fixups->name, "__local_fixups__")) 289da56d04cSPantelis Antoniou break; 290da56d04cSPantelis Antoniou 291624ab2a4SFrank Rowand err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta); 2929f27ede4SFrank Rowand if (err) 29332bed310SMoritz Fischer goto out; 2947941b27bSPantelis Antoniou 29525e16877SFrank Rowand overlay_fixups = NULL; 2967941b27bSPantelis Antoniou 29725e16877SFrank Rowand for_each_child_of_node(overlay, child) { 298624ab2a4SFrank Rowand if (!of_node_cmp(child->name, "__fixups__")) 29925e16877SFrank Rowand overlay_fixups = child; 3007941b27bSPantelis Antoniou } 3017941b27bSPantelis Antoniou 30225e16877SFrank Rowand if (!overlay_fixups) { 303a67976ecSFrank Rowand err = 0; 3047941b27bSPantelis Antoniou goto out; 3057941b27bSPantelis Antoniou } 3067941b27bSPantelis Antoniou 3074458db4cSFrank Rowand tree_symbols = of_find_node_by_path("/__symbols__"); 30825e16877SFrank Rowand if (!tree_symbols) { 309624ab2a4SFrank Rowand pr_err("no symbols in root of device tree.\n"); 3107941b27bSPantelis Antoniou err = -EINVAL; 31132bed310SMoritz Fischer goto out; 3127941b27bSPantelis Antoniou } 3137941b27bSPantelis Antoniou 31425e16877SFrank Rowand for_each_property_of_node(overlay_fixups, prop) { 3157941b27bSPantelis Antoniou 3167941b27bSPantelis Antoniou /* skip properties added automatically */ 31725e16877SFrank Rowand if (!of_prop_cmp(prop->name, "name")) 3187941b27bSPantelis Antoniou continue; 3197941b27bSPantelis Antoniou 32025e16877SFrank Rowand err = of_property_read_string(tree_symbols, 32125e16877SFrank Rowand prop->name, &refpath); 3229f27ede4SFrank Rowand if (err) 32332bed310SMoritz Fischer goto out; 3247941b27bSPantelis Antoniou 3257941b27bSPantelis Antoniou refnode = of_find_node_by_path(refpath); 3267941b27bSPantelis Antoniou if (!refnode) { 3277941b27bSPantelis Antoniou err = -ENOENT; 32832bed310SMoritz Fischer goto out; 3297941b27bSPantelis Antoniou } 3307941b27bSPantelis Antoniou 3317941b27bSPantelis Antoniou phandle = refnode->phandle; 3327941b27bSPantelis Antoniou of_node_put(refnode); 3337941b27bSPantelis Antoniou 33425e16877SFrank Rowand err = update_usages_of_a_phandle_reference(overlay, prop, phandle); 3357941b27bSPantelis Antoniou if (err) 3367941b27bSPantelis Antoniou break; 3377941b27bSPantelis Antoniou } 3387941b27bSPantelis Antoniou 3397941b27bSPantelis Antoniou out: 34032bed310SMoritz Fischer if (err) 34132bed310SMoritz Fischer pr_err("overlay phandle fixup failed: %d\n", err); 34225e16877SFrank Rowand of_node_put(tree_symbols); 3437941b27bSPantelis Antoniou 3447941b27bSPantelis Antoniou return err; 3457941b27bSPantelis Antoniou } 3467941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles); 347