xref: /openbmc/linux/drivers/of/resolver.c (revision f948d6d8)
17941b27bSPantelis Antoniou /*
27941b27bSPantelis Antoniou  * Functions for dealing with DT resolution
37941b27bSPantelis Antoniou  *
47941b27bSPantelis Antoniou  * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
57941b27bSPantelis Antoniou  * Copyright (C) 2012 Texas Instruments Inc.
67941b27bSPantelis Antoniou  *
77941b27bSPantelis Antoniou  * This program is free software; you can redistribute it and/or
87941b27bSPantelis Antoniou  * modify it under the terms of the GNU General Public License
97941b27bSPantelis Antoniou  * version 2 as published by the Free Software Foundation.
107941b27bSPantelis Antoniou  */
117941b27bSPantelis Antoniou 
12606ad42aSRob Herring #define pr_fmt(fmt)	"OF: resolver: " fmt
13606ad42aSRob Herring 
147941b27bSPantelis Antoniou #include <linux/kernel.h>
157941b27bSPantelis Antoniou #include <linux/module.h>
167941b27bSPantelis Antoniou #include <linux/of.h>
177941b27bSPantelis Antoniou #include <linux/of_device.h>
187941b27bSPantelis Antoniou #include <linux/string.h>
197941b27bSPantelis Antoniou #include <linux/ctype.h>
207941b27bSPantelis Antoniou #include <linux/errno.h>
217941b27bSPantelis Antoniou #include <linux/slab.h>
227941b27bSPantelis Antoniou 
2327497e11SRob Herring #include "of_private.h"
2427497e11SRob Herring 
257941b27bSPantelis Antoniou /* illegal phandle value (set when unresolved) */
267941b27bSPantelis Antoniou #define OF_PHANDLE_ILLEGAL	0xdeadbeef
277941b27bSPantelis Antoniou 
28f94823f2SFrank Rowand static phandle live_tree_max_phandle(void)
297941b27bSPantelis Antoniou {
307941b27bSPantelis Antoniou 	struct device_node *node;
317941b27bSPantelis Antoniou 	phandle phandle;
327941b27bSPantelis Antoniou 	unsigned long flags;
337941b27bSPantelis Antoniou 
347941b27bSPantelis Antoniou 	raw_spin_lock_irqsave(&devtree_lock, flags);
357941b27bSPantelis Antoniou 	phandle = 0;
367941b27bSPantelis Antoniou 	for_each_of_allnodes(node) {
377941b27bSPantelis Antoniou 		if (node->phandle != OF_PHANDLE_ILLEGAL &&
387941b27bSPantelis Antoniou 				node->phandle > phandle)
397941b27bSPantelis Antoniou 			phandle = node->phandle;
407941b27bSPantelis Antoniou 	}
417941b27bSPantelis Antoniou 	raw_spin_unlock_irqrestore(&devtree_lock, flags);
427941b27bSPantelis Antoniou 
437941b27bSPantelis Antoniou 	return phandle;
447941b27bSPantelis Antoniou }
457941b27bSPantelis Antoniou 
4625e16877SFrank Rowand static void adjust_overlay_phandles(struct device_node *overlay,
477941b27bSPantelis Antoniou 		int phandle_delta)
487941b27bSPantelis Antoniou {
497941b27bSPantelis Antoniou 	struct device_node *child;
507941b27bSPantelis Antoniou 	struct property *prop;
517941b27bSPantelis Antoniou 	phandle phandle;
527941b27bSPantelis Antoniou 
53269f1a67SFrank Rowand 	/* adjust node's phandle in node */
5425e16877SFrank Rowand 	if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
5525e16877SFrank Rowand 		overlay->phandle += phandle_delta;
567941b27bSPantelis Antoniou 
57269f1a67SFrank Rowand 	/* copy adjusted phandle into *phandle properties */
5825e16877SFrank Rowand 	for_each_property_of_node(overlay, prop) {
597941b27bSPantelis Antoniou 
609f27ede4SFrank Rowand 		if (of_prop_cmp(prop->name, "phandle") &&
619f27ede4SFrank Rowand 		    of_prop_cmp(prop->name, "linux,phandle"))
627941b27bSPantelis Antoniou 			continue;
637941b27bSPantelis Antoniou 
647941b27bSPantelis Antoniou 		if (prop->length < 4)
657941b27bSPantelis Antoniou 			continue;
667941b27bSPantelis Antoniou 
677941b27bSPantelis Antoniou 		phandle = be32_to_cpup(prop->value);
68a67976ecSFrank Rowand 		if (phandle == OF_PHANDLE_ILLEGAL)
697941b27bSPantelis Antoniou 			continue;
707941b27bSPantelis Antoniou 
7117a70355SRob Herring 		*(__be32 *)prop->value = cpu_to_be32(overlay->phandle);
727941b27bSPantelis Antoniou 	}
737941b27bSPantelis Antoniou 
7425e16877SFrank Rowand 	for_each_child_of_node(overlay, child)
75f94823f2SFrank Rowand 		adjust_overlay_phandles(child, phandle_delta);
767941b27bSPantelis Antoniou }
777941b27bSPantelis Antoniou 
7825e16877SFrank Rowand static int update_usages_of_a_phandle_reference(struct device_node *overlay,
7925e16877SFrank Rowand 		struct property *prop_fixup, phandle phandle)
807941b27bSPantelis Antoniou {
817941b27bSPantelis Antoniou 	struct device_node *refnode;
8225e16877SFrank Rowand 	struct property *prop;
8325e16877SFrank Rowand 	char *value, *cur, *end, *node_path, *prop_name, *s;
8425e16877SFrank Rowand 	int offset, len;
857941b27bSPantelis Antoniou 	int err = 0;
867941b27bSPantelis Antoniou 
87eeb09506SStephen Boyd 	value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
8825e16877SFrank Rowand 	if (!value)
897941b27bSPantelis Antoniou 		return -ENOMEM;
907941b27bSPantelis Antoniou 
91269f1a67SFrank Rowand 	/* prop_fixup contains a list of tuples of path:property_name:offset */
9225e16877SFrank Rowand 	end = value + prop_fixup->length;
9325e16877SFrank Rowand 	for (cur = value; cur < end; cur += len + 1) {
9425e16877SFrank Rowand 		len = strlen(cur);
957941b27bSPantelis Antoniou 
9625e16877SFrank Rowand 		node_path = cur;
9725e16877SFrank Rowand 		s = strchr(cur, ':');
987941b27bSPantelis Antoniou 		if (!s) {
997941b27bSPantelis Antoniou 			err = -EINVAL;
1007941b27bSPantelis Antoniou 			goto err_fail;
1017941b27bSPantelis Antoniou 		}
1027941b27bSPantelis Antoniou 		*s++ = '\0';
1037941b27bSPantelis Antoniou 
10425e16877SFrank Rowand 		prop_name = s;
1057941b27bSPantelis Antoniou 		s = strchr(s, ':');
1067941b27bSPantelis Antoniou 		if (!s) {
1077941b27bSPantelis Antoniou 			err = -EINVAL;
1087941b27bSPantelis Antoniou 			goto err_fail;
1097941b27bSPantelis Antoniou 		}
1107941b27bSPantelis Antoniou 		*s++ = '\0';
111624ab2a4SFrank Rowand 
1127941b27bSPantelis Antoniou 		err = kstrtoint(s, 10, &offset);
1139f27ede4SFrank Rowand 		if (err)
1147941b27bSPantelis Antoniou 			goto err_fail;
1157941b27bSPantelis Antoniou 
11627497e11SRob Herring 		refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path);
11796d1c8e8SFrank Rowand 		if (!refnode)
1187941b27bSPantelis Antoniou 			continue;
1197941b27bSPantelis Antoniou 
12025e16877SFrank Rowand 		for_each_property_of_node(refnode, prop) {
12125e16877SFrank Rowand 			if (!of_prop_cmp(prop->name, prop_name))
1227941b27bSPantelis Antoniou 				break;
1237941b27bSPantelis Antoniou 		}
12482f68756SAmitoj Kaur Chawla 		of_node_put(refnode);
1257941b27bSPantelis Antoniou 
12625e16877SFrank Rowand 		if (!prop) {
1277941b27bSPantelis Antoniou 			err = -ENOENT;
1287941b27bSPantelis Antoniou 			goto err_fail;
1297941b27bSPantelis Antoniou 		}
1307941b27bSPantelis Antoniou 
13125e16877SFrank Rowand 		*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
1327941b27bSPantelis Antoniou 	}
1337941b27bSPantelis Antoniou 
1347941b27bSPantelis Antoniou err_fail:
13525e16877SFrank Rowand 	kfree(value);
1367941b27bSPantelis Antoniou 	return err;
1377941b27bSPantelis Antoniou }
1387941b27bSPantelis Antoniou 
139da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */
140fad556bfSFrank Rowand static int node_name_cmp(const struct device_node *dn1,
141da56d04cSPantelis Antoniou 		const struct device_node *dn2)
142da56d04cSPantelis Antoniou {
14395e6b1faSRob Herring 	const char *n1 = kbasename(dn1->full_name);
14495e6b1faSRob Herring 	const char *n2 = kbasename(dn2->full_name);
145da56d04cSPantelis Antoniou 
146da56d04cSPantelis Antoniou 	return of_node_cmp(n1, n2);
147da56d04cSPantelis Antoniou }
148da56d04cSPantelis Antoniou 
1497941b27bSPantelis Antoniou /*
1507941b27bSPantelis Antoniou  * Adjust the local phandle references by the given phandle delta.
151269f1a67SFrank Rowand  *
152269f1a67SFrank Rowand  * Subtree @local_fixups, which is overlay node __local_fixups__,
153269f1a67SFrank Rowand  * mirrors the fragment node structure at the root of the overlay.
154269f1a67SFrank Rowand  *
155269f1a67SFrank Rowand  * For each property in the fragments that contains a phandle reference,
156269f1a67SFrank Rowand  * @local_fixups has a property of the same name that contains a list
157269f1a67SFrank Rowand  * of offsets of the phandle reference(s) within the respective property
158269f1a67SFrank Rowand  * value(s).  The values at these offsets will be fixed up.
1597941b27bSPantelis Antoniou  */
16025e16877SFrank Rowand static int adjust_local_phandle_references(struct device_node *local_fixups,
16125e16877SFrank Rowand 		struct device_node *overlay, int phandle_delta)
1627941b27bSPantelis Antoniou {
16325e16877SFrank Rowand 	struct device_node *child, *overlay_child;
16425e16877SFrank Rowand 	struct property *prop_fix, *prop;
165da56d04cSPantelis Antoniou 	int err, i, count;
166da56d04cSPantelis Antoniou 	unsigned int off;
1677941b27bSPantelis Antoniou 
16825e16877SFrank Rowand 	if (!local_fixups)
1697941b27bSPantelis Antoniou 		return 0;
1707941b27bSPantelis Antoniou 
17125e16877SFrank Rowand 	for_each_property_of_node(local_fixups, prop_fix) {
172da56d04cSPantelis Antoniou 
1737941b27bSPantelis Antoniou 		/* skip properties added automatically */
17425e16877SFrank Rowand 		if (!of_prop_cmp(prop_fix->name, "name") ||
17525e16877SFrank Rowand 		    !of_prop_cmp(prop_fix->name, "phandle") ||
17625e16877SFrank Rowand 		    !of_prop_cmp(prop_fix->name, "linux,phandle"))
1777941b27bSPantelis Antoniou 			continue;
1787941b27bSPantelis Antoniou 
17925e16877SFrank Rowand 		if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
180da56d04cSPantelis Antoniou 			return -EINVAL;
18125e16877SFrank Rowand 		count = prop_fix->length / sizeof(__be32);
182da56d04cSPantelis Antoniou 
18325e16877SFrank Rowand 		for_each_property_of_node(overlay, prop) {
18425e16877SFrank Rowand 			if (!of_prop_cmp(prop->name, prop_fix->name))
185da56d04cSPantelis Antoniou 				break;
186da56d04cSPantelis Antoniou 		}
187da56d04cSPantelis Antoniou 
18825e16877SFrank Rowand 		if (!prop)
189da56d04cSPantelis Antoniou 			return -EINVAL;
190da56d04cSPantelis Antoniou 
191da56d04cSPantelis Antoniou 		for (i = 0; i < count; i++) {
19225e16877SFrank Rowand 			off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
193ea8229b7SFrank Rowand 			if ((off + 4) > prop->length)
194da56d04cSPantelis Antoniou 				return -EINVAL;
195da56d04cSPantelis Antoniou 
196d35d623fSStephen Boyd 			be32_add_cpu(prop->value + off, phandle_delta);
197da56d04cSPantelis Antoniou 		}
198da56d04cSPantelis Antoniou 	}
199da56d04cSPantelis Antoniou 
200269f1a67SFrank Rowand 	/*
201269f1a67SFrank Rowand 	 * These nested loops recurse down two subtrees in parallel, where the
202269f1a67SFrank Rowand 	 * node names in the two subtrees match.
203269f1a67SFrank Rowand 	 *
204269f1a67SFrank Rowand 	 * The roots of the subtrees are the overlay's __local_fixups__ node
205269f1a67SFrank Rowand 	 * and the overlay's root node.
206269f1a67SFrank Rowand 	 */
20725e16877SFrank Rowand 	for_each_child_of_node(local_fixups, child) {
208da56d04cSPantelis Antoniou 
20925e16877SFrank Rowand 		for_each_child_of_node(overlay, overlay_child)
21025e16877SFrank Rowand 			if (!node_name_cmp(child, overlay_child))
211da56d04cSPantelis Antoniou 				break;
212da56d04cSPantelis Antoniou 
21325e16877SFrank Rowand 		if (!overlay_child)
214da56d04cSPantelis Antoniou 			return -EINVAL;
215da56d04cSPantelis Antoniou 
21625e16877SFrank Rowand 		err = adjust_local_phandle_references(child, overlay_child,
217da56d04cSPantelis Antoniou 				phandle_delta);
2189f27ede4SFrank Rowand 		if (err)
2197941b27bSPantelis Antoniou 			return err;
2207941b27bSPantelis Antoniou 	}
2217941b27bSPantelis Antoniou 
2227941b27bSPantelis Antoniou 	return 0;
2237941b27bSPantelis Antoniou }
2247941b27bSPantelis Antoniou 
2257941b27bSPantelis Antoniou /**
226269f1a67SFrank Rowand  * of_resolve_phandles - Relocate and resolve overlay against live tree
2277941b27bSPantelis Antoniou  *
228269f1a67SFrank Rowand  * @overlay:	Pointer to devicetree overlay to relocate and resolve
2297941b27bSPantelis Antoniou  *
230269f1a67SFrank Rowand  * Modify (relocate) values of local phandles in @overlay to a range that
231269f1a67SFrank Rowand  * does not conflict with the live expanded devicetree.  Update references
232269f1a67SFrank Rowand  * to the local phandles in @overlay.  Update (resolve) phandle references
233269f1a67SFrank Rowand  * in @overlay that refer to the live expanded devicetree.
234269f1a67SFrank Rowand  *
235269f1a67SFrank Rowand  * Phandle values in the live tree are in the range of
236269f1a67SFrank Rowand  * 1 .. live_tree_max_phandle().  The range of phandle values in the overlay
237269f1a67SFrank Rowand  * also begin with at 1.  Adjust the phandle values in the overlay to begin
238269f1a67SFrank Rowand  * at live_tree_max_phandle() + 1.  Update references to the phandles to
239269f1a67SFrank Rowand  * the adjusted phandle values.
240269f1a67SFrank Rowand  *
241269f1a67SFrank Rowand  * The name of each property in the "__fixups__" node in the overlay matches
242269f1a67SFrank Rowand  * the name of a symbol (a label) in the live tree.  The values of each
243269f1a67SFrank Rowand  * property in the "__fixups__" node is a list of the property values in the
244269f1a67SFrank Rowand  * overlay that need to be updated to contain the phandle reference
245269f1a67SFrank Rowand  * corresponding to that symbol in the live tree.  Update the references in
246269f1a67SFrank Rowand  * the overlay with the phandle values in the live tree.
247269f1a67SFrank Rowand  *
248269f1a67SFrank Rowand  * @overlay must be detached.
249269f1a67SFrank Rowand  *
250269f1a67SFrank Rowand  * Resolving and applying @overlay to the live expanded devicetree must be
251269f1a67SFrank Rowand  * protected by a mechanism to ensure that multiple overlays are processed
252269f1a67SFrank Rowand  * in a single threaded manner so that multiple overlays will not relocate
253269f1a67SFrank Rowand  * phandles to overlapping ranges.  The mechanism to enforce this is not
254269f1a67SFrank Rowand  * yet implemented.
255269f1a67SFrank Rowand  *
256269f1a67SFrank Rowand  * Return: %0 on success or a negative error value on error.
2577941b27bSPantelis Antoniou  */
25825e16877SFrank Rowand int of_resolve_phandles(struct device_node *overlay)
2597941b27bSPantelis Antoniou {
26025e16877SFrank Rowand 	struct device_node *child, *local_fixups, *refnode;
2615581a95fSFrank Rowand 	struct device_node *tree_symbols, *overlay_fixups;
26225e16877SFrank Rowand 	struct property *prop;
2637941b27bSPantelis Antoniou 	const char *refpath;
2647941b27bSPantelis Antoniou 	phandle phandle, phandle_delta;
2657941b27bSPantelis Antoniou 	int err;
2667941b27bSPantelis Antoniou 
267d9181b20SFrank Rowand 	tree_symbols = NULL;
268d9181b20SFrank Rowand 
269624ab2a4SFrank Rowand 	if (!overlay) {
270624ab2a4SFrank Rowand 		pr_err("null overlay\n");
271d9181b20SFrank Rowand 		err = -EINVAL;
27232bed310SMoritz Fischer 		goto out;
273624ab2a4SFrank Rowand 	}
274f948d6d8SFrank Rowand 
275f948d6d8SFrank Rowand #if 0
276f948d6d8SFrank Rowand 	Temporarily disable check so that old style overlay unittests
277f948d6d8SFrank Rowand 	do not fail when of_resolve_phandles() is moved into
278f948d6d8SFrank Rowand 	of_overlay_apply().
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 	}
285f948d6d8SFrank Rowand #endif
2867941b27bSPantelis Antoniou 
287f94823f2SFrank Rowand 	phandle_delta = live_tree_max_phandle() + 1;
28825e16877SFrank Rowand 	adjust_overlay_phandles(overlay, phandle_delta);
289da56d04cSPantelis Antoniou 
29025e16877SFrank Rowand 	for_each_child_of_node(overlay, local_fixups)
29125e16877SFrank Rowand 		if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
292da56d04cSPantelis Antoniou 			break;
293da56d04cSPantelis Antoniou 
294624ab2a4SFrank Rowand 	err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
2959f27ede4SFrank Rowand 	if (err)
29632bed310SMoritz Fischer 		goto out;
2977941b27bSPantelis Antoniou 
29825e16877SFrank Rowand 	overlay_fixups = NULL;
2997941b27bSPantelis Antoniou 
30025e16877SFrank Rowand 	for_each_child_of_node(overlay, child) {
301624ab2a4SFrank Rowand 		if (!of_node_cmp(child->name, "__fixups__"))
30225e16877SFrank Rowand 			overlay_fixups = child;
3037941b27bSPantelis Antoniou 	}
3047941b27bSPantelis Antoniou 
30525e16877SFrank Rowand 	if (!overlay_fixups) {
306a67976ecSFrank Rowand 		err = 0;
3077941b27bSPantelis Antoniou 		goto out;
3087941b27bSPantelis Antoniou 	}
3097941b27bSPantelis Antoniou 
3104458db4cSFrank Rowand 	tree_symbols = of_find_node_by_path("/__symbols__");
31125e16877SFrank Rowand 	if (!tree_symbols) {
312624ab2a4SFrank Rowand 		pr_err("no symbols in root of device tree.\n");
3137941b27bSPantelis Antoniou 		err = -EINVAL;
31432bed310SMoritz Fischer 		goto out;
3157941b27bSPantelis Antoniou 	}
3167941b27bSPantelis Antoniou 
31725e16877SFrank Rowand 	for_each_property_of_node(overlay_fixups, prop) {
3187941b27bSPantelis Antoniou 
3197941b27bSPantelis Antoniou 		/* skip properties added automatically */
32025e16877SFrank Rowand 		if (!of_prop_cmp(prop->name, "name"))
3217941b27bSPantelis Antoniou 			continue;
3227941b27bSPantelis Antoniou 
32325e16877SFrank Rowand 		err = of_property_read_string(tree_symbols,
32425e16877SFrank Rowand 				prop->name, &refpath);
3259f27ede4SFrank Rowand 		if (err)
32632bed310SMoritz Fischer 			goto out;
3277941b27bSPantelis Antoniou 
3287941b27bSPantelis Antoniou 		refnode = of_find_node_by_path(refpath);
3297941b27bSPantelis Antoniou 		if (!refnode) {
3307941b27bSPantelis Antoniou 			err = -ENOENT;
33132bed310SMoritz Fischer 			goto out;
3327941b27bSPantelis Antoniou 		}
3337941b27bSPantelis Antoniou 
3347941b27bSPantelis Antoniou 		phandle = refnode->phandle;
3357941b27bSPantelis Antoniou 		of_node_put(refnode);
3367941b27bSPantelis Antoniou 
33725e16877SFrank Rowand 		err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
3387941b27bSPantelis Antoniou 		if (err)
3397941b27bSPantelis Antoniou 			break;
3407941b27bSPantelis Antoniou 	}
3417941b27bSPantelis Antoniou 
3427941b27bSPantelis Antoniou out:
34332bed310SMoritz Fischer 	if (err)
34432bed310SMoritz Fischer 		pr_err("overlay phandle fixup failed: %d\n", err);
34525e16877SFrank Rowand 	of_node_put(tree_symbols);
3467941b27bSPantelis Antoniou 
3477941b27bSPantelis Antoniou 	return err;
3487941b27bSPantelis Antoniou }
3497941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles);
350