xref: /openbmc/linux/drivers/of/resolver.c (revision a3958323)
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 
live_tree_max_phandle(void)22f94823f2SFrank Rowand static phandle live_tree_max_phandle(void)
237941b27bSPantelis Antoniou {
247941b27bSPantelis Antoniou 	struct device_node *node;
257941b27bSPantelis Antoniou 	phandle phandle;
267941b27bSPantelis Antoniou 	unsigned long flags;
277941b27bSPantelis Antoniou 
287941b27bSPantelis Antoniou 	raw_spin_lock_irqsave(&devtree_lock, flags);
297941b27bSPantelis Antoniou 	phandle = 0;
307941b27bSPantelis Antoniou 	for_each_of_allnodes(node) {
317941b27bSPantelis Antoniou 		if (node->phandle != OF_PHANDLE_ILLEGAL &&
327941b27bSPantelis Antoniou 				node->phandle > phandle)
337941b27bSPantelis Antoniou 			phandle = node->phandle;
347941b27bSPantelis Antoniou 	}
357941b27bSPantelis Antoniou 	raw_spin_unlock_irqrestore(&devtree_lock, flags);
367941b27bSPantelis Antoniou 
377941b27bSPantelis Antoniou 	return phandle;
387941b27bSPantelis Antoniou }
397941b27bSPantelis Antoniou 
adjust_overlay_phandles(struct device_node * overlay,int phandle_delta)4025e16877SFrank Rowand static void adjust_overlay_phandles(struct device_node *overlay,
417941b27bSPantelis Antoniou 		int phandle_delta)
427941b27bSPantelis Antoniou {
437941b27bSPantelis Antoniou 	struct device_node *child;
447941b27bSPantelis Antoniou 	struct property *prop;
457941b27bSPantelis Antoniou 	phandle phandle;
467941b27bSPantelis Antoniou 
47269f1a67SFrank Rowand 	/* adjust node's phandle in node */
4825e16877SFrank Rowand 	if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
4925e16877SFrank Rowand 		overlay->phandle += phandle_delta;
507941b27bSPantelis Antoniou 
51269f1a67SFrank Rowand 	/* copy adjusted phandle into *phandle properties */
5225e16877SFrank Rowand 	for_each_property_of_node(overlay, prop) {
537941b27bSPantelis Antoniou 
549f27ede4SFrank Rowand 		if (of_prop_cmp(prop->name, "phandle") &&
559f27ede4SFrank Rowand 		    of_prop_cmp(prop->name, "linux,phandle"))
567941b27bSPantelis Antoniou 			continue;
577941b27bSPantelis Antoniou 
587941b27bSPantelis Antoniou 		if (prop->length < 4)
597941b27bSPantelis Antoniou 			continue;
607941b27bSPantelis Antoniou 
617941b27bSPantelis Antoniou 		phandle = be32_to_cpup(prop->value);
62a67976ecSFrank Rowand 		if (phandle == OF_PHANDLE_ILLEGAL)
637941b27bSPantelis Antoniou 			continue;
647941b27bSPantelis Antoniou 
6517a70355SRob Herring 		*(__be32 *)prop->value = cpu_to_be32(overlay->phandle);
667941b27bSPantelis Antoniou 	}
677941b27bSPantelis Antoniou 
6825e16877SFrank Rowand 	for_each_child_of_node(overlay, child)
69f94823f2SFrank Rowand 		adjust_overlay_phandles(child, phandle_delta);
707941b27bSPantelis Antoniou }
717941b27bSPantelis Antoniou 
update_usages_of_a_phandle_reference(struct device_node * overlay,struct property * prop_fixup,phandle phandle)7225e16877SFrank Rowand static int update_usages_of_a_phandle_reference(struct device_node *overlay,
7325e16877SFrank Rowand 		struct property *prop_fixup, phandle phandle)
747941b27bSPantelis Antoniou {
757941b27bSPantelis Antoniou 	struct device_node *refnode;
7625e16877SFrank Rowand 	struct property *prop;
7725e16877SFrank Rowand 	char *value, *cur, *end, *node_path, *prop_name, *s;
7825e16877SFrank Rowand 	int offset, len;
797941b27bSPantelis Antoniou 	int err = 0;
807941b27bSPantelis Antoniou 
81eeb09506SStephen Boyd 	value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
8225e16877SFrank Rowand 	if (!value)
837941b27bSPantelis Antoniou 		return -ENOMEM;
847941b27bSPantelis Antoniou 
85269f1a67SFrank Rowand 	/* prop_fixup contains a list of tuples of path:property_name:offset */
8625e16877SFrank Rowand 	end = value + prop_fixup->length;
8725e16877SFrank Rowand 	for (cur = value; cur < end; cur += len + 1) {
8825e16877SFrank Rowand 		len = strlen(cur);
897941b27bSPantelis Antoniou 
9025e16877SFrank Rowand 		node_path = cur;
9125e16877SFrank Rowand 		s = strchr(cur, ':');
927941b27bSPantelis Antoniou 		if (!s) {
937941b27bSPantelis Antoniou 			err = -EINVAL;
947941b27bSPantelis Antoniou 			goto err_fail;
957941b27bSPantelis Antoniou 		}
967941b27bSPantelis Antoniou 		*s++ = '\0';
977941b27bSPantelis Antoniou 
9825e16877SFrank Rowand 		prop_name = s;
997941b27bSPantelis Antoniou 		s = strchr(s, ':');
1007941b27bSPantelis Antoniou 		if (!s) {
1017941b27bSPantelis Antoniou 			err = -EINVAL;
1027941b27bSPantelis Antoniou 			goto err_fail;
1037941b27bSPantelis Antoniou 		}
1047941b27bSPantelis Antoniou 		*s++ = '\0';
105624ab2a4SFrank Rowand 
1067941b27bSPantelis Antoniou 		err = kstrtoint(s, 10, &offset);
1079f27ede4SFrank Rowand 		if (err)
1087941b27bSPantelis Antoniou 			goto err_fail;
1097941b27bSPantelis Antoniou 
11027497e11SRob Herring 		refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path);
11196d1c8e8SFrank Rowand 		if (!refnode)
1127941b27bSPantelis Antoniou 			continue;
1137941b27bSPantelis Antoniou 
11425e16877SFrank Rowand 		for_each_property_of_node(refnode, prop) {
11525e16877SFrank Rowand 			if (!of_prop_cmp(prop->name, prop_name))
1167941b27bSPantelis Antoniou 				break;
1177941b27bSPantelis Antoniou 		}
11882f68756SAmitoj Kaur Chawla 		of_node_put(refnode);
1197941b27bSPantelis Antoniou 
12025e16877SFrank Rowand 		if (!prop) {
1217941b27bSPantelis Antoniou 			err = -ENOENT;
1227941b27bSPantelis Antoniou 			goto err_fail;
1237941b27bSPantelis Antoniou 		}
1247941b27bSPantelis Antoniou 
125482137bfSFrank Rowand 		if (offset < 0 || offset + sizeof(__be32) > prop->length) {
126482137bfSFrank Rowand 			err = -EINVAL;
127482137bfSFrank Rowand 			goto err_fail;
128482137bfSFrank Rowand 		}
129482137bfSFrank Rowand 
13025e16877SFrank Rowand 		*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
1317941b27bSPantelis Antoniou 	}
1327941b27bSPantelis Antoniou 
1337941b27bSPantelis Antoniou err_fail:
13425e16877SFrank Rowand 	kfree(value);
1357941b27bSPantelis Antoniou 	return err;
1367941b27bSPantelis Antoniou }
1377941b27bSPantelis Antoniou 
138da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */
node_name_cmp(const struct device_node * dn1,const struct device_node * dn2)139fad556bfSFrank Rowand static int node_name_cmp(const struct device_node *dn1,
140da56d04cSPantelis Antoniou 		const struct device_node *dn2)
141da56d04cSPantelis Antoniou {
14295e6b1faSRob Herring 	const char *n1 = kbasename(dn1->full_name);
14395e6b1faSRob Herring 	const char *n2 = kbasename(dn2->full_name);
144da56d04cSPantelis Antoniou 
145da56d04cSPantelis Antoniou 	return of_node_cmp(n1, n2);
146da56d04cSPantelis Antoniou }
147da56d04cSPantelis Antoniou 
1487941b27bSPantelis Antoniou /*
1497941b27bSPantelis Antoniou  * Adjust the local phandle references by the given phandle delta.
150269f1a67SFrank Rowand  *
151269f1a67SFrank Rowand  * Subtree @local_fixups, which is overlay node __local_fixups__,
152269f1a67SFrank Rowand  * mirrors the fragment node structure at the root of the overlay.
153269f1a67SFrank Rowand  *
154269f1a67SFrank Rowand  * For each property in the fragments that contains a phandle reference,
155269f1a67SFrank Rowand  * @local_fixups has a property of the same name that contains a list
156269f1a67SFrank Rowand  * of offsets of the phandle reference(s) within the respective property
157269f1a67SFrank Rowand  * value(s).  The values at these offsets will be fixed up.
1587941b27bSPantelis Antoniou  */
adjust_local_phandle_references(struct device_node * local_fixups,struct device_node * overlay,int phandle_delta)15925e16877SFrank Rowand static int adjust_local_phandle_references(struct device_node *local_fixups,
16025e16877SFrank Rowand 		struct device_node *overlay, int phandle_delta)
1617941b27bSPantelis Antoniou {
16225e16877SFrank Rowand 	struct device_node *child, *overlay_child;
16325e16877SFrank Rowand 	struct property *prop_fix, *prop;
164da56d04cSPantelis Antoniou 	int err, i, count;
165da56d04cSPantelis Antoniou 	unsigned int off;
1667941b27bSPantelis Antoniou 
16725e16877SFrank Rowand 	if (!local_fixups)
1687941b27bSPantelis Antoniou 		return 0;
1697941b27bSPantelis Antoniou 
17025e16877SFrank Rowand 	for_each_property_of_node(local_fixups, prop_fix) {
171da56d04cSPantelis Antoniou 
1727941b27bSPantelis Antoniou 		/* skip properties added automatically */
17325e16877SFrank Rowand 		if (!of_prop_cmp(prop_fix->name, "name") ||
17425e16877SFrank Rowand 		    !of_prop_cmp(prop_fix->name, "phandle") ||
17525e16877SFrank Rowand 		    !of_prop_cmp(prop_fix->name, "linux,phandle"))
1767941b27bSPantelis Antoniou 			continue;
1777941b27bSPantelis Antoniou 
17825e16877SFrank Rowand 		if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
179da56d04cSPantelis Antoniou 			return -EINVAL;
18025e16877SFrank Rowand 		count = prop_fix->length / sizeof(__be32);
181da56d04cSPantelis Antoniou 
18225e16877SFrank Rowand 		for_each_property_of_node(overlay, prop) {
18325e16877SFrank Rowand 			if (!of_prop_cmp(prop->name, prop_fix->name))
184da56d04cSPantelis Antoniou 				break;
185da56d04cSPantelis Antoniou 		}
186da56d04cSPantelis Antoniou 
18725e16877SFrank Rowand 		if (!prop)
188da56d04cSPantelis Antoniou 			return -EINVAL;
189da56d04cSPantelis Antoniou 
190da56d04cSPantelis Antoniou 		for (i = 0; i < count; i++) {
19125e16877SFrank Rowand 			off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
192ea8229b7SFrank Rowand 			if ((off + 4) > prop->length)
193da56d04cSPantelis Antoniou 				return -EINVAL;
194da56d04cSPantelis Antoniou 
195d35d623fSStephen Boyd 			be32_add_cpu(prop->value + off, phandle_delta);
196da56d04cSPantelis Antoniou 		}
197da56d04cSPantelis Antoniou 	}
198da56d04cSPantelis Antoniou 
199269f1a67SFrank Rowand 	/*
200269f1a67SFrank Rowand 	 * These nested loops recurse down two subtrees in parallel, where the
201269f1a67SFrank Rowand 	 * node names in the two subtrees match.
202269f1a67SFrank Rowand 	 *
203269f1a67SFrank Rowand 	 * The roots of the subtrees are the overlay's __local_fixups__ node
204269f1a67SFrank Rowand 	 * and the overlay's root node.
205269f1a67SFrank Rowand 	 */
20625e16877SFrank Rowand 	for_each_child_of_node(local_fixups, child) {
207da56d04cSPantelis Antoniou 
20825e16877SFrank Rowand 		for_each_child_of_node(overlay, overlay_child)
20960d437bbSNishka Dasgupta 			if (!node_name_cmp(child, overlay_child)) {
21060d437bbSNishka Dasgupta 				of_node_put(overlay_child);
211da56d04cSPantelis Antoniou 				break;
21260d437bbSNishka Dasgupta 			}
213da56d04cSPantelis Antoniou 
21460d437bbSNishka Dasgupta 		if (!overlay_child) {
21560d437bbSNishka Dasgupta 			of_node_put(child);
216da56d04cSPantelis Antoniou 			return -EINVAL;
21760d437bbSNishka Dasgupta 		}
218da56d04cSPantelis Antoniou 
21925e16877SFrank Rowand 		err = adjust_local_phandle_references(child, overlay_child,
220da56d04cSPantelis Antoniou 				phandle_delta);
22160d437bbSNishka Dasgupta 		if (err) {
22260d437bbSNishka Dasgupta 			of_node_put(child);
2237941b27bSPantelis Antoniou 			return err;
2247941b27bSPantelis Antoniou 		}
22560d437bbSNishka Dasgupta 	}
2267941b27bSPantelis Antoniou 
2277941b27bSPantelis Antoniou 	return 0;
2287941b27bSPantelis Antoniou }
2297941b27bSPantelis Antoniou 
2307941b27bSPantelis Antoniou /**
231269f1a67SFrank Rowand  * of_resolve_phandles - Relocate and resolve overlay against live tree
2327941b27bSPantelis Antoniou  *
233269f1a67SFrank Rowand  * @overlay:	Pointer to devicetree overlay to relocate and resolve
2347941b27bSPantelis Antoniou  *
235269f1a67SFrank Rowand  * Modify (relocate) values of local phandles in @overlay to a range that
236269f1a67SFrank Rowand  * does not conflict with the live expanded devicetree.  Update references
237269f1a67SFrank Rowand  * to the local phandles in @overlay.  Update (resolve) phandle references
238269f1a67SFrank Rowand  * in @overlay that refer to the live expanded devicetree.
239269f1a67SFrank Rowand  *
240269f1a67SFrank Rowand  * Phandle values in the live tree are in the range of
241269f1a67SFrank Rowand  * 1 .. live_tree_max_phandle().  The range of phandle values in the overlay
242269f1a67SFrank Rowand  * also begin with at 1.  Adjust the phandle values in the overlay to begin
243269f1a67SFrank Rowand  * at live_tree_max_phandle() + 1.  Update references to the phandles to
244269f1a67SFrank Rowand  * the adjusted phandle values.
245269f1a67SFrank Rowand  *
246269f1a67SFrank Rowand  * The name of each property in the "__fixups__" node in the overlay matches
247269f1a67SFrank Rowand  * the name of a symbol (a label) in the live tree.  The values of each
248269f1a67SFrank Rowand  * property in the "__fixups__" node is a list of the property values in the
249269f1a67SFrank Rowand  * overlay that need to be updated to contain the phandle reference
250269f1a67SFrank Rowand  * corresponding to that symbol in the live tree.  Update the references in
251269f1a67SFrank Rowand  * the overlay with the phandle values in the live tree.
252269f1a67SFrank Rowand  *
253269f1a67SFrank Rowand  * @overlay must be detached.
254269f1a67SFrank Rowand  *
255269f1a67SFrank Rowand  * Resolving and applying @overlay to the live expanded devicetree must be
256269f1a67SFrank Rowand  * protected by a mechanism to ensure that multiple overlays are processed
257269f1a67SFrank Rowand  * in a single threaded manner so that multiple overlays will not relocate
258269f1a67SFrank Rowand  * phandles to overlapping ranges.  The mechanism to enforce this is not
259269f1a67SFrank Rowand  * yet implemented.
260269f1a67SFrank Rowand  *
261269f1a67SFrank Rowand  * Return: %0 on success or a negative error value on error.
2627941b27bSPantelis Antoniou  */
of_resolve_phandles(struct device_node * overlay)26325e16877SFrank Rowand int of_resolve_phandles(struct device_node *overlay)
2647941b27bSPantelis Antoniou {
26525e16877SFrank Rowand 	struct device_node *child, *local_fixups, *refnode;
2665581a95fSFrank Rowand 	struct device_node *tree_symbols, *overlay_fixups;
26725e16877SFrank Rowand 	struct property *prop;
2687941b27bSPantelis Antoniou 	const char *refpath;
2697941b27bSPantelis Antoniou 	phandle phandle, phandle_delta;
2707941b27bSPantelis Antoniou 	int err;
2717941b27bSPantelis Antoniou 
272d9181b20SFrank Rowand 	tree_symbols = NULL;
273d9181b20SFrank Rowand 
274624ab2a4SFrank Rowand 	if (!overlay) {
275624ab2a4SFrank Rowand 		pr_err("null overlay\n");
276d9181b20SFrank Rowand 		err = -EINVAL;
27732bed310SMoritz Fischer 		goto out;
278624ab2a4SFrank Rowand 	}
279f948d6d8SFrank Rowand 
280624ab2a4SFrank Rowand 	if (!of_node_check_flag(overlay, OF_DETACHED)) {
281624ab2a4SFrank Rowand 		pr_err("overlay not detached\n");
282d9181b20SFrank Rowand 		err = -EINVAL;
28332bed310SMoritz Fischer 		goto out;
284624ab2a4SFrank Rowand 	}
2857941b27bSPantelis Antoniou 
286f94823f2SFrank Rowand 	phandle_delta = live_tree_max_phandle() + 1;
28725e16877SFrank Rowand 	adjust_overlay_phandles(overlay, phandle_delta);
288da56d04cSPantelis Antoniou 
28925e16877SFrank Rowand 	for_each_child_of_node(overlay, local_fixups)
290b3e46d1aSRob Herring 		if (of_node_name_eq(local_fixups, "__local_fixups__"))
291da56d04cSPantelis Antoniou 			break;
292da56d04cSPantelis Antoniou 
293624ab2a4SFrank Rowand 	err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
2949f27ede4SFrank Rowand 	if (err)
29532bed310SMoritz Fischer 		goto out;
2967941b27bSPantelis Antoniou 
29725e16877SFrank Rowand 	overlay_fixups = NULL;
2987941b27bSPantelis Antoniou 
29925e16877SFrank Rowand 	for_each_child_of_node(overlay, child) {
300b3e46d1aSRob Herring 		if (of_node_name_eq(child, "__fixups__"))
30125e16877SFrank Rowand 			overlay_fixups = child;
3027941b27bSPantelis Antoniou 	}
3037941b27bSPantelis Antoniou 
30425e16877SFrank Rowand 	if (!overlay_fixups) {
305a67976ecSFrank Rowand 		err = 0;
3067941b27bSPantelis Antoniou 		goto out;
3077941b27bSPantelis Antoniou 	}
3087941b27bSPantelis Antoniou 
3094458db4cSFrank Rowand 	tree_symbols = of_find_node_by_path("/__symbols__");
31025e16877SFrank Rowand 	if (!tree_symbols) {
311624ab2a4SFrank Rowand 		pr_err("no symbols in root of device tree.\n");
3127941b27bSPantelis Antoniou 		err = -EINVAL;
31332bed310SMoritz Fischer 		goto out;
3147941b27bSPantelis Antoniou 	}
3157941b27bSPantelis Antoniou 
31625e16877SFrank Rowand 	for_each_property_of_node(overlay_fixups, prop) {
3177941b27bSPantelis Antoniou 
3187941b27bSPantelis Antoniou 		/* skip properties added automatically */
31925e16877SFrank Rowand 		if (!of_prop_cmp(prop->name, "name"))
3207941b27bSPantelis Antoniou 			continue;
3217941b27bSPantelis Antoniou 
32225e16877SFrank Rowand 		err = of_property_read_string(tree_symbols,
32325e16877SFrank Rowand 				prop->name, &refpath);
324a3958323SLuca Ceresoli 		if (err) {
325a3958323SLuca Ceresoli 			pr_err("node label '%s' not found in live devicetree symbols table\n",
326a3958323SLuca Ceresoli 			       prop->name);
32732bed310SMoritz Fischer 			goto out;
328a3958323SLuca Ceresoli 		}
3297941b27bSPantelis Antoniou 
3307941b27bSPantelis Antoniou 		refnode = of_find_node_by_path(refpath);
3317941b27bSPantelis Antoniou 		if (!refnode) {
3327941b27bSPantelis Antoniou 			err = -ENOENT;
33332bed310SMoritz Fischer 			goto out;
3347941b27bSPantelis Antoniou 		}
3357941b27bSPantelis Antoniou 
3367941b27bSPantelis Antoniou 		phandle = refnode->phandle;
3377941b27bSPantelis Antoniou 		of_node_put(refnode);
3387941b27bSPantelis Antoniou 
33925e16877SFrank Rowand 		err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
3407941b27bSPantelis Antoniou 		if (err)
3417941b27bSPantelis Antoniou 			break;
3427941b27bSPantelis Antoniou 	}
3437941b27bSPantelis Antoniou 
3447941b27bSPantelis Antoniou out:
34532bed310SMoritz Fischer 	if (err)
34632bed310SMoritz Fischer 		pr_err("overlay phandle fixup failed: %d\n", err);
34725e16877SFrank Rowand 	of_node_put(tree_symbols);
3487941b27bSPantelis Antoniou 
3497941b27bSPantelis Antoniou 	return err;
3507941b27bSPantelis Antoniou }
3517941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles);
352