xref: /openbmc/linux/drivers/of/resolver.c (revision 624ab2a4)
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/string.h>
227941b27bSPantelis Antoniou #include <linux/slab.h>
237941b27bSPantelis Antoniou 
247941b27bSPantelis Antoniou /* illegal phandle value (set when unresolved) */
257941b27bSPantelis Antoniou #define OF_PHANDLE_ILLEGAL	0xdeadbeef
267941b27bSPantelis Antoniou 
277941b27bSPantelis Antoniou /**
287941b27bSPantelis Antoniou  * Find a node with the give full name by recursively following any of
297941b27bSPantelis Antoniou  * the child node links.
307941b27bSPantelis Antoniou  */
31fad556bfSFrank Rowand static struct device_node *find_node_by_full_name(struct device_node *node,
327941b27bSPantelis Antoniou 		const char *full_name)
337941b27bSPantelis Antoniou {
347941b27bSPantelis Antoniou 	struct device_node *child, *found;
357941b27bSPantelis Antoniou 
369f27ede4SFrank Rowand 	if (!node)
377941b27bSPantelis Antoniou 		return NULL;
387941b27bSPantelis Antoniou 
399f27ede4SFrank Rowand 	if (!of_node_cmp(node->full_name, full_name))
4082f68756SAmitoj Kaur Chawla 		return of_node_get(node);
417941b27bSPantelis Antoniou 
427941b27bSPantelis Antoniou 	for_each_child_of_node(node, child) {
43fad556bfSFrank Rowand 		found = find_node_by_full_name(child, full_name);
4482f68756SAmitoj Kaur Chawla 		if (found != NULL) {
4582f68756SAmitoj Kaur Chawla 			of_node_put(child);
467941b27bSPantelis Antoniou 			return found;
477941b27bSPantelis Antoniou 		}
4882f68756SAmitoj Kaur Chawla 	}
497941b27bSPantelis Antoniou 
507941b27bSPantelis Antoniou 	return NULL;
517941b27bSPantelis Antoniou }
527941b27bSPantelis Antoniou 
537941b27bSPantelis Antoniou /*
547941b27bSPantelis Antoniou  * Find live tree's maximum phandle value.
557941b27bSPantelis Antoniou  */
56f94823f2SFrank Rowand static phandle live_tree_max_phandle(void)
577941b27bSPantelis Antoniou {
587941b27bSPantelis Antoniou 	struct device_node *node;
597941b27bSPantelis Antoniou 	phandle phandle;
607941b27bSPantelis Antoniou 	unsigned long flags;
617941b27bSPantelis Antoniou 
627941b27bSPantelis Antoniou 	raw_spin_lock_irqsave(&devtree_lock, flags);
637941b27bSPantelis Antoniou 	phandle = 0;
647941b27bSPantelis Antoniou 	for_each_of_allnodes(node) {
657941b27bSPantelis Antoniou 		if (node->phandle != OF_PHANDLE_ILLEGAL &&
667941b27bSPantelis Antoniou 				node->phandle > phandle)
677941b27bSPantelis Antoniou 			phandle = node->phandle;
687941b27bSPantelis Antoniou 	}
697941b27bSPantelis Antoniou 	raw_spin_unlock_irqrestore(&devtree_lock, flags);
707941b27bSPantelis Antoniou 
717941b27bSPantelis Antoniou 	return phandle;
727941b27bSPantelis Antoniou }
737941b27bSPantelis Antoniou 
747941b27bSPantelis Antoniou /*
757941b27bSPantelis Antoniou  * Adjust a subtree's phandle values by a given delta.
767941b27bSPantelis Antoniou  */
7725e16877SFrank Rowand static void adjust_overlay_phandles(struct device_node *overlay,
787941b27bSPantelis Antoniou 		int phandle_delta)
797941b27bSPantelis Antoniou {
807941b27bSPantelis Antoniou 	struct device_node *child;
817941b27bSPantelis Antoniou 	struct property *prop;
827941b27bSPantelis Antoniou 	phandle phandle;
837941b27bSPantelis Antoniou 
8425e16877SFrank Rowand 	if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
8525e16877SFrank Rowand 		overlay->phandle += phandle_delta;
867941b27bSPantelis Antoniou 
8725e16877SFrank Rowand 	for_each_property_of_node(overlay, prop) {
887941b27bSPantelis Antoniou 
899f27ede4SFrank Rowand 		if (of_prop_cmp(prop->name, "phandle") &&
909f27ede4SFrank Rowand 		    of_prop_cmp(prop->name, "linux,phandle"))
917941b27bSPantelis Antoniou 			continue;
927941b27bSPantelis Antoniou 
937941b27bSPantelis Antoniou 		if (prop->length < 4)
947941b27bSPantelis Antoniou 			continue;
957941b27bSPantelis Antoniou 
967941b27bSPantelis Antoniou 		phandle = be32_to_cpup(prop->value);
97a67976ecSFrank Rowand 		if (phandle == OF_PHANDLE_ILLEGAL)
987941b27bSPantelis Antoniou 			continue;
997941b27bSPantelis Antoniou 
10025e16877SFrank Rowand 		*(uint32_t *)prop->value = cpu_to_be32(overlay->phandle);
1017941b27bSPantelis Antoniou 	}
1027941b27bSPantelis Antoniou 
10325e16877SFrank Rowand 	for_each_child_of_node(overlay, child)
104f94823f2SFrank Rowand 		adjust_overlay_phandles(child, phandle_delta);
1057941b27bSPantelis Antoniou }
1067941b27bSPantelis Antoniou 
10725e16877SFrank Rowand static int update_usages_of_a_phandle_reference(struct device_node *overlay,
10825e16877SFrank Rowand 		struct property *prop_fixup, phandle phandle)
1097941b27bSPantelis Antoniou {
1107941b27bSPantelis Antoniou 	struct device_node *refnode;
11125e16877SFrank Rowand 	struct property *prop;
11225e16877SFrank Rowand 	char *value, *cur, *end, *node_path, *prop_name, *s;
11325e16877SFrank Rowand 	int offset, len;
1147941b27bSPantelis Antoniou 	int err = 0;
1157941b27bSPantelis Antoniou 
11625e16877SFrank Rowand 	value = kmalloc(prop_fixup->length, GFP_KERNEL);
11725e16877SFrank Rowand 	if (!value)
1187941b27bSPantelis Antoniou 		return -ENOMEM;
11925e16877SFrank Rowand 	memcpy(value, prop_fixup->value, prop_fixup->length);
1207941b27bSPantelis Antoniou 
12125e16877SFrank Rowand 	end = value + prop_fixup->length;
12225e16877SFrank Rowand 	for (cur = value; cur < end; cur += len + 1) {
12325e16877SFrank Rowand 		len = strlen(cur);
1247941b27bSPantelis Antoniou 
12525e16877SFrank Rowand 		node_path = cur;
12625e16877SFrank Rowand 		s = strchr(cur, ':');
1277941b27bSPantelis Antoniou 		if (!s) {
1287941b27bSPantelis Antoniou 			err = -EINVAL;
1297941b27bSPantelis Antoniou 			goto err_fail;
1307941b27bSPantelis Antoniou 		}
1317941b27bSPantelis Antoniou 		*s++ = '\0';
1327941b27bSPantelis Antoniou 
13325e16877SFrank Rowand 		prop_name = s;
1347941b27bSPantelis Antoniou 		s = strchr(s, ':');
1357941b27bSPantelis Antoniou 		if (!s) {
1367941b27bSPantelis Antoniou 			err = -EINVAL;
1377941b27bSPantelis Antoniou 			goto err_fail;
1387941b27bSPantelis Antoniou 		}
1397941b27bSPantelis Antoniou 		*s++ = '\0';
140624ab2a4SFrank Rowand 
1417941b27bSPantelis Antoniou 		err = kstrtoint(s, 10, &offset);
1429f27ede4SFrank Rowand 		if (err)
1437941b27bSPantelis Antoniou 			goto err_fail;
1447941b27bSPantelis Antoniou 
14525e16877SFrank Rowand 		refnode = find_node_by_full_name(overlay, node_path);
14696d1c8e8SFrank Rowand 		if (!refnode)
1477941b27bSPantelis Antoniou 			continue;
1487941b27bSPantelis Antoniou 
14925e16877SFrank Rowand 		for_each_property_of_node(refnode, prop) {
15025e16877SFrank Rowand 			if (!of_prop_cmp(prop->name, prop_name))
1517941b27bSPantelis Antoniou 				break;
1527941b27bSPantelis Antoniou 		}
15382f68756SAmitoj Kaur Chawla 		of_node_put(refnode);
1547941b27bSPantelis Antoniou 
15525e16877SFrank Rowand 		if (!prop) {
1567941b27bSPantelis Antoniou 			err = -ENOENT;
1577941b27bSPantelis Antoniou 			goto err_fail;
1587941b27bSPantelis Antoniou 		}
1597941b27bSPantelis Antoniou 
16025e16877SFrank Rowand 		*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
1617941b27bSPantelis Antoniou 	}
1627941b27bSPantelis Antoniou 
1637941b27bSPantelis Antoniou err_fail:
16425e16877SFrank Rowand 	kfree(value);
1657941b27bSPantelis Antoniou 	return err;
1667941b27bSPantelis Antoniou }
1677941b27bSPantelis Antoniou 
168da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */
169fad556bfSFrank Rowand static int node_name_cmp(const struct device_node *dn1,
170da56d04cSPantelis Antoniou 		const struct device_node *dn2)
171da56d04cSPantelis Antoniou {
172da56d04cSPantelis Antoniou 	const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
173da56d04cSPantelis Antoniou 	const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
174da56d04cSPantelis Antoniou 
175da56d04cSPantelis Antoniou 	return of_node_cmp(n1, n2);
176da56d04cSPantelis Antoniou }
177da56d04cSPantelis Antoniou 
1787941b27bSPantelis Antoniou /*
1797941b27bSPantelis Antoniou  * Adjust the local phandle references by the given phandle delta.
180da56d04cSPantelis Antoniou  * Assumes the existances of a __local_fixups__ node at the root.
181da56d04cSPantelis Antoniou  * Assumes that __of_verify_tree_phandle_references has been called.
182da56d04cSPantelis Antoniou  * Does not take any devtree locks so make sure you call this on a tree
183da56d04cSPantelis Antoniou  * which is at the detached state.
1847941b27bSPantelis Antoniou  */
18525e16877SFrank Rowand static int adjust_local_phandle_references(struct device_node *local_fixups,
18625e16877SFrank Rowand 		struct device_node *overlay, int phandle_delta)
1877941b27bSPantelis Antoniou {
18825e16877SFrank Rowand 	struct device_node *child, *overlay_child;
18925e16877SFrank Rowand 	struct property *prop_fix, *prop;
190da56d04cSPantelis Antoniou 	int err, i, count;
191da56d04cSPantelis Antoniou 	unsigned int off;
192da56d04cSPantelis Antoniou 	phandle phandle;
1937941b27bSPantelis Antoniou 
19425e16877SFrank Rowand 	if (!local_fixups)
1957941b27bSPantelis Antoniou 		return 0;
1967941b27bSPantelis Antoniou 
19725e16877SFrank Rowand 	for_each_property_of_node(local_fixups, prop_fix) {
198da56d04cSPantelis Antoniou 
1997941b27bSPantelis Antoniou 		/* skip properties added automatically */
20025e16877SFrank Rowand 		if (!of_prop_cmp(prop_fix->name, "name") ||
20125e16877SFrank Rowand 		    !of_prop_cmp(prop_fix->name, "phandle") ||
20225e16877SFrank Rowand 		    !of_prop_cmp(prop_fix->name, "linux,phandle"))
2037941b27bSPantelis Antoniou 			continue;
2047941b27bSPantelis Antoniou 
20525e16877SFrank Rowand 		if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
206da56d04cSPantelis Antoniou 			return -EINVAL;
20725e16877SFrank Rowand 		count = prop_fix->length / sizeof(__be32);
208da56d04cSPantelis Antoniou 
20925e16877SFrank Rowand 		for_each_property_of_node(overlay, prop) {
21025e16877SFrank Rowand 			if (!of_prop_cmp(prop->name, prop_fix->name))
211da56d04cSPantelis Antoniou 				break;
212da56d04cSPantelis Antoniou 		}
213da56d04cSPantelis Antoniou 
21425e16877SFrank Rowand 		if (!prop)
215da56d04cSPantelis Antoniou 			return -EINVAL;
216da56d04cSPantelis Antoniou 
217da56d04cSPantelis Antoniou 		for (i = 0; i < count; i++) {
21825e16877SFrank Rowand 			off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
21925e16877SFrank Rowand 			if (off >= prop->length || (off + 4) > prop->length)
220da56d04cSPantelis Antoniou 				return -EINVAL;
221da56d04cSPantelis Antoniou 
22225e16877SFrank Rowand 			phandle = be32_to_cpu(*(__be32 *)(prop->value + off));
223da56d04cSPantelis Antoniou 			phandle += phandle_delta;
22425e16877SFrank Rowand 			*(__be32 *)(prop->value + off) = cpu_to_be32(phandle);
225da56d04cSPantelis Antoniou 		}
226da56d04cSPantelis Antoniou 	}
227da56d04cSPantelis Antoniou 
22825e16877SFrank Rowand 	for_each_child_of_node(local_fixups, child) {
229da56d04cSPantelis Antoniou 
23025e16877SFrank Rowand 		for_each_child_of_node(overlay, overlay_child)
23125e16877SFrank Rowand 			if (!node_name_cmp(child, overlay_child))
232da56d04cSPantelis Antoniou 				break;
233da56d04cSPantelis Antoniou 
23425e16877SFrank Rowand 		if (!overlay_child)
235da56d04cSPantelis Antoniou 			return -EINVAL;
236da56d04cSPantelis Antoniou 
23725e16877SFrank Rowand 		err = adjust_local_phandle_references(child, overlay_child,
238da56d04cSPantelis Antoniou 				phandle_delta);
2399f27ede4SFrank Rowand 		if (err)
2407941b27bSPantelis Antoniou 			return err;
2417941b27bSPantelis Antoniou 	}
2427941b27bSPantelis Antoniou 
2437941b27bSPantelis Antoniou 	return 0;
2447941b27bSPantelis Antoniou }
2457941b27bSPantelis Antoniou 
2467941b27bSPantelis Antoniou /**
2477941b27bSPantelis Antoniou  * of_resolve	- Resolve the given node against the live tree.
2487941b27bSPantelis Antoniou  *
2497941b27bSPantelis Antoniou  * @resolve:	Node to resolve
2507941b27bSPantelis Antoniou  *
2517941b27bSPantelis Antoniou  * Perform dynamic Device Tree resolution against the live tree
2527941b27bSPantelis Antoniou  * to the given node to resolve. This depends on the live tree
2537941b27bSPantelis Antoniou  * having a __symbols__ node, and the resolve node the __fixups__ &
2547941b27bSPantelis Antoniou  * __local_fixups__ nodes (if needed).
2557941b27bSPantelis Antoniou  * The result of the operation is a resolve node that it's contents
2567941b27bSPantelis Antoniou  * are fit to be inserted or operate upon the live tree.
2577941b27bSPantelis Antoniou  * Returns 0 on success or a negative error value on error.
2587941b27bSPantelis Antoniou  */
25925e16877SFrank Rowand int of_resolve_phandles(struct device_node *overlay)
2607941b27bSPantelis Antoniou {
26125e16877SFrank Rowand 	struct device_node *child, *local_fixups, *refnode;
26225e16877SFrank Rowand 	struct device_node *tree_symbols, *overlay_symbols, *overlay_fixups;
26325e16877SFrank Rowand 	struct property *prop;
2647941b27bSPantelis Antoniou 	const char *refpath;
2657941b27bSPantelis Antoniou 	phandle phandle, phandle_delta;
2667941b27bSPantelis Antoniou 	int err;
2677941b27bSPantelis Antoniou 
268624ab2a4SFrank Rowand 	if (!overlay) {
269624ab2a4SFrank Rowand 		pr_err("null overlay\n");
2707941b27bSPantelis Antoniou 		return -EINVAL;
271624ab2a4SFrank Rowand 	}
272624ab2a4SFrank Rowand 	if (!of_node_check_flag(overlay, OF_DETACHED)) {
273624ab2a4SFrank Rowand 		pr_err("overlay not detached\n");
274624ab2a4SFrank Rowand 		return -EINVAL;
275624ab2a4SFrank Rowand 	}
2767941b27bSPantelis Antoniou 
277f94823f2SFrank Rowand 	phandle_delta = live_tree_max_phandle() + 1;
27825e16877SFrank Rowand 	adjust_overlay_phandles(overlay, phandle_delta);
279da56d04cSPantelis Antoniou 
28025e16877SFrank Rowand 	for_each_child_of_node(overlay, local_fixups)
28125e16877SFrank Rowand 		if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
282da56d04cSPantelis Antoniou 			break;
283da56d04cSPantelis Antoniou 
284624ab2a4SFrank Rowand 	err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
2859f27ede4SFrank Rowand 	if (err)
2867941b27bSPantelis Antoniou 		return err;
2877941b27bSPantelis Antoniou 
28825e16877SFrank Rowand 	overlay_symbols = NULL;
28925e16877SFrank Rowand 	overlay_fixups = NULL;
2907941b27bSPantelis Antoniou 
29125e16877SFrank Rowand 	tree_symbols = of_find_node_by_path("/__symbols__");
2927941b27bSPantelis Antoniou 
29325e16877SFrank Rowand 	for_each_child_of_node(overlay, child) {
294624ab2a4SFrank Rowand 		if (!of_node_cmp(child->name, "__symbols__"))
29525e16877SFrank Rowand 			overlay_symbols = child;
296624ab2a4SFrank Rowand 		if (!of_node_cmp(child->name, "__fixups__"))
29725e16877SFrank Rowand 			overlay_fixups = child;
2987941b27bSPantelis Antoniou 	}
2997941b27bSPantelis Antoniou 
30025e16877SFrank Rowand 	if (!overlay_fixups) {
301a67976ecSFrank Rowand 		err = 0;
3027941b27bSPantelis Antoniou 		goto out;
3037941b27bSPantelis Antoniou 	}
3047941b27bSPantelis Antoniou 
30525e16877SFrank Rowand 	if (!tree_symbols) {
306624ab2a4SFrank Rowand 		pr_err("no symbols in root of device tree.\n");
3077941b27bSPantelis Antoniou 		err = -EINVAL;
3087941b27bSPantelis Antoniou 		goto out;
3097941b27bSPantelis Antoniou 	}
3107941b27bSPantelis Antoniou 
31125e16877SFrank Rowand 	for_each_property_of_node(overlay_fixups, prop) {
3127941b27bSPantelis Antoniou 
3137941b27bSPantelis Antoniou 		/* skip properties added automatically */
31425e16877SFrank Rowand 		if (!of_prop_cmp(prop->name, "name"))
3157941b27bSPantelis Antoniou 			continue;
3167941b27bSPantelis Antoniou 
31725e16877SFrank Rowand 		err = of_property_read_string(tree_symbols,
31825e16877SFrank Rowand 				prop->name, &refpath);
3199f27ede4SFrank Rowand 		if (err)
3207941b27bSPantelis Antoniou 			goto out;
3217941b27bSPantelis Antoniou 
3227941b27bSPantelis Antoniou 		refnode = of_find_node_by_path(refpath);
3237941b27bSPantelis Antoniou 		if (!refnode) {
3247941b27bSPantelis Antoniou 			err = -ENOENT;
3257941b27bSPantelis Antoniou 			goto out;
3267941b27bSPantelis Antoniou 		}
3277941b27bSPantelis Antoniou 
3287941b27bSPantelis Antoniou 		phandle = refnode->phandle;
3297941b27bSPantelis Antoniou 		of_node_put(refnode);
3307941b27bSPantelis Antoniou 
33125e16877SFrank Rowand 		err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
3327941b27bSPantelis Antoniou 		if (err)
3337941b27bSPantelis Antoniou 			break;
3347941b27bSPantelis Antoniou 	}
3357941b27bSPantelis Antoniou 
3367941b27bSPantelis Antoniou out:
33725e16877SFrank Rowand 	of_node_put(tree_symbols);
3387941b27bSPantelis Antoniou 
3397941b27bSPantelis Antoniou 	return err;
3407941b27bSPantelis Antoniou }
3417941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles);
342