xref: /openbmc/linux/drivers/of/resolver.c (revision 96d1c8e8)
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  */
317941b27bSPantelis Antoniou static struct device_node *__of_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 
367941b27bSPantelis Antoniou 	if (node == NULL)
377941b27bSPantelis Antoniou 		return NULL;
387941b27bSPantelis Antoniou 
397941b27bSPantelis Antoniou 	if (of_node_cmp(node->full_name, full_name) == 0)
4082f68756SAmitoj Kaur Chawla 		return of_node_get(node);
417941b27bSPantelis Antoniou 
427941b27bSPantelis Antoniou 	for_each_child_of_node(node, child) {
437941b27bSPantelis Antoniou 		found = __of_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  */
567941b27bSPantelis Antoniou static phandle of_get_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  */
777941b27bSPantelis Antoniou static void __of_adjust_tree_phandles(struct device_node *node,
787941b27bSPantelis Antoniou 		int phandle_delta)
797941b27bSPantelis Antoniou {
807941b27bSPantelis Antoniou 	struct device_node *child;
817941b27bSPantelis Antoniou 	struct property *prop;
827941b27bSPantelis Antoniou 	phandle phandle;
837941b27bSPantelis Antoniou 
847941b27bSPantelis Antoniou 	if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
857941b27bSPantelis Antoniou 		node->phandle += phandle_delta;
867941b27bSPantelis Antoniou 
877941b27bSPantelis Antoniou 	for_each_property_of_node(node, prop) {
887941b27bSPantelis Antoniou 
897941b27bSPantelis Antoniou 		if (of_prop_cmp(prop->name, "phandle") != 0 &&
907941b27bSPantelis Antoniou 		    of_prop_cmp(prop->name, "linux,phandle") != 0)
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 
1007941b27bSPantelis Antoniou 		*(uint32_t *)prop->value = cpu_to_be32(node->phandle);
1017941b27bSPantelis Antoniou 	}
1027941b27bSPantelis Antoniou 
1037941b27bSPantelis Antoniou 	for_each_child_of_node(node, child)
1047941b27bSPantelis Antoniou 		__of_adjust_tree_phandles(child, phandle_delta);
1057941b27bSPantelis Antoniou }
1067941b27bSPantelis Antoniou 
107da56d04cSPantelis Antoniou static int __of_adjust_phandle_ref(struct device_node *node,
108da56d04cSPantelis Antoniou 		struct property *rprop, int value)
1097941b27bSPantelis Antoniou {
1107941b27bSPantelis Antoniou 	phandle phandle;
1117941b27bSPantelis Antoniou 	struct device_node *refnode;
1127941b27bSPantelis Antoniou 	struct property *sprop;
1137941b27bSPantelis Antoniou 	char *propval, *propcur, *propend, *nodestr, *propstr, *s;
1147941b27bSPantelis Antoniou 	int offset, propcurlen;
1157941b27bSPantelis Antoniou 	int err = 0;
1167941b27bSPantelis Antoniou 
1177941b27bSPantelis Antoniou 	propval = kmalloc(rprop->length, GFP_KERNEL);
11896d1c8e8SFrank Rowand 	if (!propval)
1197941b27bSPantelis Antoniou 		return -ENOMEM;
1207941b27bSPantelis Antoniou 	memcpy(propval, rprop->value, rprop->length);
1217941b27bSPantelis Antoniou 
1227941b27bSPantelis Antoniou 	propend = propval + rprop->length;
1237941b27bSPantelis Antoniou 	for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
1247941b27bSPantelis Antoniou 		propcurlen = strlen(propcur);
1257941b27bSPantelis Antoniou 
1267941b27bSPantelis Antoniou 		nodestr = propcur;
1277941b27bSPantelis Antoniou 		s = strchr(propcur, ':');
1287941b27bSPantelis Antoniou 		if (!s) {
1297941b27bSPantelis Antoniou 			err = -EINVAL;
1307941b27bSPantelis Antoniou 			goto err_fail;
1317941b27bSPantelis Antoniou 		}
1327941b27bSPantelis Antoniou 		*s++ = '\0';
1337941b27bSPantelis Antoniou 
1347941b27bSPantelis Antoniou 		propstr = s;
1357941b27bSPantelis Antoniou 		s = strchr(s, ':');
1367941b27bSPantelis Antoniou 		if (!s) {
1377941b27bSPantelis Antoniou 			err = -EINVAL;
1387941b27bSPantelis Antoniou 			goto err_fail;
1397941b27bSPantelis Antoniou 		}
1407941b27bSPantelis Antoniou 
1417941b27bSPantelis Antoniou 		*s++ = '\0';
1427941b27bSPantelis Antoniou 		err = kstrtoint(s, 10, &offset);
14396d1c8e8SFrank Rowand 		if (err != 0)
1447941b27bSPantelis Antoniou 			goto err_fail;
1457941b27bSPantelis Antoniou 
1467941b27bSPantelis Antoniou 		refnode = __of_find_node_by_full_name(node, nodestr);
14796d1c8e8SFrank Rowand 		if (!refnode)
1487941b27bSPantelis Antoniou 			continue;
1497941b27bSPantelis Antoniou 
1507941b27bSPantelis Antoniou 		for_each_property_of_node(refnode, sprop) {
1517941b27bSPantelis Antoniou 			if (of_prop_cmp(sprop->name, propstr) == 0)
1527941b27bSPantelis Antoniou 				break;
1537941b27bSPantelis Antoniou 		}
15482f68756SAmitoj Kaur Chawla 		of_node_put(refnode);
1557941b27bSPantelis Antoniou 
1567941b27bSPantelis Antoniou 		if (!sprop) {
1577941b27bSPantelis Antoniou 			err = -ENOENT;
1587941b27bSPantelis Antoniou 			goto err_fail;
1597941b27bSPantelis Antoniou 		}
1607941b27bSPantelis Antoniou 
161da56d04cSPantelis Antoniou 		phandle = value;
1627941b27bSPantelis Antoniou 		*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
1637941b27bSPantelis Antoniou 	}
1647941b27bSPantelis Antoniou 
1657941b27bSPantelis Antoniou err_fail:
1667941b27bSPantelis Antoniou 	kfree(propval);
1677941b27bSPantelis Antoniou 	return err;
1687941b27bSPantelis Antoniou }
1697941b27bSPantelis Antoniou 
170da56d04cSPantelis Antoniou /* compare nodes taking into account that 'name' strips out the @ part */
171da56d04cSPantelis Antoniou static int __of_node_name_cmp(const struct device_node *dn1,
172da56d04cSPantelis Antoniou 		const struct device_node *dn2)
173da56d04cSPantelis Antoniou {
174da56d04cSPantelis Antoniou 	const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
175da56d04cSPantelis Antoniou 	const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
176da56d04cSPantelis Antoniou 
177da56d04cSPantelis Antoniou 	return of_node_cmp(n1, n2);
178da56d04cSPantelis Antoniou }
179da56d04cSPantelis Antoniou 
1807941b27bSPantelis Antoniou /*
1817941b27bSPantelis Antoniou  * Adjust the local phandle references by the given phandle delta.
182da56d04cSPantelis Antoniou  * Assumes the existances of a __local_fixups__ node at the root.
183da56d04cSPantelis Antoniou  * Assumes that __of_verify_tree_phandle_references has been called.
184da56d04cSPantelis Antoniou  * Does not take any devtree locks so make sure you call this on a tree
185da56d04cSPantelis Antoniou  * which is at the detached state.
1867941b27bSPantelis Antoniou  */
1877941b27bSPantelis Antoniou static int __of_adjust_tree_phandle_references(struct device_node *node,
188da56d04cSPantelis Antoniou 		struct device_node *target, int phandle_delta)
1897941b27bSPantelis Antoniou {
190da56d04cSPantelis Antoniou 	struct device_node *child, *childtarget;
191da56d04cSPantelis Antoniou 	struct property *rprop, *sprop;
192da56d04cSPantelis Antoniou 	int err, i, count;
193da56d04cSPantelis Antoniou 	unsigned int off;
194da56d04cSPantelis Antoniou 	phandle phandle;
1957941b27bSPantelis Antoniou 
196da56d04cSPantelis Antoniou 	if (node == NULL)
1977941b27bSPantelis Antoniou 		return 0;
1987941b27bSPantelis Antoniou 
199da56d04cSPantelis Antoniou 	for_each_property_of_node(node, rprop) {
200da56d04cSPantelis Antoniou 
2017941b27bSPantelis Antoniou 		/* skip properties added automatically */
202da56d04cSPantelis Antoniou 		if (of_prop_cmp(rprop->name, "name") == 0 ||
203da56d04cSPantelis Antoniou 		    of_prop_cmp(rprop->name, "phandle") == 0 ||
204da56d04cSPantelis Antoniou 		    of_prop_cmp(rprop->name, "linux,phandle") == 0)
2057941b27bSPantelis Antoniou 			continue;
2067941b27bSPantelis Antoniou 
20796d1c8e8SFrank Rowand 		if ((rprop->length % 4) != 0 || rprop->length == 0)
208da56d04cSPantelis Antoniou 			return -EINVAL;
209da56d04cSPantelis Antoniou 		count = rprop->length / sizeof(__be32);
210da56d04cSPantelis Antoniou 
211da56d04cSPantelis Antoniou 		for_each_property_of_node(target, sprop) {
212da56d04cSPantelis Antoniou 			if (of_prop_cmp(sprop->name, rprop->name) == 0)
213da56d04cSPantelis Antoniou 				break;
214da56d04cSPantelis Antoniou 		}
215da56d04cSPantelis Antoniou 
21696d1c8e8SFrank Rowand 		if (sprop == NULL)
217da56d04cSPantelis Antoniou 			return -EINVAL;
218da56d04cSPantelis Antoniou 
219da56d04cSPantelis Antoniou 		for (i = 0; i < count; i++) {
220da56d04cSPantelis Antoniou 			off = be32_to_cpu(((__be32 *)rprop->value)[i]);
22196d1c8e8SFrank Rowand 			if (off >= sprop->length || (off + 4) > sprop->length)
222da56d04cSPantelis Antoniou 				return -EINVAL;
223da56d04cSPantelis Antoniou 
224da56d04cSPantelis Antoniou 			if (phandle_delta) {
225da56d04cSPantelis Antoniou 				phandle = be32_to_cpu(*(__be32 *)(sprop->value + off));
226da56d04cSPantelis Antoniou 				phandle += phandle_delta;
227da56d04cSPantelis Antoniou 				*(__be32 *)(sprop->value + off) = cpu_to_be32(phandle);
228da56d04cSPantelis Antoniou 			}
229da56d04cSPantelis Antoniou 		}
230da56d04cSPantelis Antoniou 	}
231da56d04cSPantelis Antoniou 
232da56d04cSPantelis Antoniou 	for_each_child_of_node(node, child) {
233da56d04cSPantelis Antoniou 
234da56d04cSPantelis Antoniou 		for_each_child_of_node(target, childtarget)
235da56d04cSPantelis Antoniou 			if (__of_node_name_cmp(child, childtarget) == 0)
236da56d04cSPantelis Antoniou 				break;
237da56d04cSPantelis Antoniou 
23896d1c8e8SFrank Rowand 		if (!childtarget)
239da56d04cSPantelis Antoniou 			return -EINVAL;
240da56d04cSPantelis Antoniou 
241da56d04cSPantelis Antoniou 		err = __of_adjust_tree_phandle_references(child, childtarget,
242da56d04cSPantelis Antoniou 				phandle_delta);
243da56d04cSPantelis Antoniou 		if (err != 0)
2447941b27bSPantelis Antoniou 			return err;
2457941b27bSPantelis Antoniou 	}
2467941b27bSPantelis Antoniou 
2477941b27bSPantelis Antoniou 	return 0;
2487941b27bSPantelis Antoniou }
2497941b27bSPantelis Antoniou 
2507941b27bSPantelis Antoniou /**
2517941b27bSPantelis Antoniou  * of_resolve	- Resolve the given node against the live tree.
2527941b27bSPantelis Antoniou  *
2537941b27bSPantelis Antoniou  * @resolve:	Node to resolve
2547941b27bSPantelis Antoniou  *
2557941b27bSPantelis Antoniou  * Perform dynamic Device Tree resolution against the live tree
2567941b27bSPantelis Antoniou  * to the given node to resolve. This depends on the live tree
2577941b27bSPantelis Antoniou  * having a __symbols__ node, and the resolve node the __fixups__ &
2587941b27bSPantelis Antoniou  * __local_fixups__ nodes (if needed).
2597941b27bSPantelis Antoniou  * The result of the operation is a resolve node that it's contents
2607941b27bSPantelis Antoniou  * are fit to be inserted or operate upon the live tree.
2617941b27bSPantelis Antoniou  * Returns 0 on success or a negative error value on error.
2627941b27bSPantelis Antoniou  */
2637941b27bSPantelis Antoniou int of_resolve_phandles(struct device_node *resolve)
2647941b27bSPantelis Antoniou {
265da56d04cSPantelis Antoniou 	struct device_node *child, *childroot, *refnode;
2667941b27bSPantelis Antoniou 	struct device_node *root_sym, *resolve_sym, *resolve_fix;
2677941b27bSPantelis Antoniou 	struct property *rprop;
2687941b27bSPantelis Antoniou 	const char *refpath;
2697941b27bSPantelis Antoniou 	phandle phandle, phandle_delta;
2707941b27bSPantelis Antoniou 	int err;
2717941b27bSPantelis Antoniou 
2725de3bbc8SMichal Suchanek 	if (!resolve)
2735de3bbc8SMichal Suchanek 		pr_err("%s: null node\n", __func__);
2745de3bbc8SMichal Suchanek 	if (resolve && !of_node_check_flag(resolve, OF_DETACHED))
2755de3bbc8SMichal Suchanek 		pr_err("%s: node %s not detached\n", __func__,
2765de3bbc8SMichal Suchanek 			 resolve->full_name);
2777941b27bSPantelis Antoniou 	if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
2787941b27bSPantelis Antoniou 		return -EINVAL;
2797941b27bSPantelis Antoniou 
2807941b27bSPantelis Antoniou 	phandle_delta = of_get_tree_max_phandle() + 1;
2817941b27bSPantelis Antoniou 	__of_adjust_tree_phandles(resolve, phandle_delta);
282da56d04cSPantelis Antoniou 
283da56d04cSPantelis Antoniou 	childroot = NULL;
284da56d04cSPantelis Antoniou 	for_each_child_of_node(resolve, childroot)
285da56d04cSPantelis Antoniou 		if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
286da56d04cSPantelis Antoniou 			break;
287da56d04cSPantelis Antoniou 
288da56d04cSPantelis Antoniou 	if (childroot != NULL) {
289da56d04cSPantelis Antoniou 		err = __of_adjust_tree_phandle_references(childroot,
290da56d04cSPantelis Antoniou 				resolve, 0);
2917941b27bSPantelis Antoniou 		if (err != 0)
2927941b27bSPantelis Antoniou 			return err;
2937941b27bSPantelis Antoniou 
294da56d04cSPantelis Antoniou 		BUG_ON(__of_adjust_tree_phandle_references(childroot,
295da56d04cSPantelis Antoniou 				resolve, phandle_delta));
296da56d04cSPantelis Antoniou 	}
297da56d04cSPantelis Antoniou 
2987941b27bSPantelis Antoniou 	root_sym = NULL;
2997941b27bSPantelis Antoniou 	resolve_sym = NULL;
3007941b27bSPantelis Antoniou 	resolve_fix = NULL;
3017941b27bSPantelis Antoniou 
3027941b27bSPantelis Antoniou 	root_sym = of_find_node_by_path("/__symbols__");
3037941b27bSPantelis Antoniou 
3047941b27bSPantelis Antoniou 	for_each_child_of_node(resolve, child) {
3057941b27bSPantelis Antoniou 
3067941b27bSPantelis Antoniou 		if (!resolve_sym &&
3077941b27bSPantelis Antoniou 				of_node_cmp(child->name, "__symbols__") == 0)
3087941b27bSPantelis Antoniou 			resolve_sym = child;
3097941b27bSPantelis Antoniou 
3107941b27bSPantelis Antoniou 		if (!resolve_fix &&
3117941b27bSPantelis Antoniou 				of_node_cmp(child->name, "__fixups__") == 0)
3127941b27bSPantelis Antoniou 			resolve_fix = child;
3137941b27bSPantelis Antoniou 
3147941b27bSPantelis Antoniou 		if (resolve_sym && resolve_fix)
3157941b27bSPantelis Antoniou 			break;
3167941b27bSPantelis Antoniou 	}
3177941b27bSPantelis Antoniou 
3187941b27bSPantelis Antoniou 	if (!resolve_fix) {
319a67976ecSFrank Rowand 		err = 0;
3207941b27bSPantelis Antoniou 		goto out;
3217941b27bSPantelis Antoniou 	}
3227941b27bSPantelis Antoniou 
3237941b27bSPantelis Antoniou 	if (!root_sym) {
3245de3bbc8SMichal Suchanek 		pr_err("%s: no symbols in root of device tree.\n", __func__);
3257941b27bSPantelis Antoniou 		err = -EINVAL;
3267941b27bSPantelis Antoniou 		goto out;
3277941b27bSPantelis Antoniou 	}
3287941b27bSPantelis Antoniou 
3297941b27bSPantelis Antoniou 	for_each_property_of_node(resolve_fix, rprop) {
3307941b27bSPantelis Antoniou 
3317941b27bSPantelis Antoniou 		/* skip properties added automatically */
3327941b27bSPantelis Antoniou 		if (of_prop_cmp(rprop->name, "name") == 0)
3337941b27bSPantelis Antoniou 			continue;
3347941b27bSPantelis Antoniou 
3357941b27bSPantelis Antoniou 		err = of_property_read_string(root_sym,
3367941b27bSPantelis Antoniou 				rprop->name, &refpath);
33796d1c8e8SFrank Rowand 		if (err != 0)
3387941b27bSPantelis Antoniou 			goto out;
3397941b27bSPantelis Antoniou 
3407941b27bSPantelis Antoniou 		refnode = of_find_node_by_path(refpath);
3417941b27bSPantelis Antoniou 		if (!refnode) {
3427941b27bSPantelis Antoniou 			err = -ENOENT;
3437941b27bSPantelis Antoniou 			goto out;
3447941b27bSPantelis Antoniou 		}
3457941b27bSPantelis Antoniou 
3467941b27bSPantelis Antoniou 		phandle = refnode->phandle;
3477941b27bSPantelis Antoniou 		of_node_put(refnode);
3487941b27bSPantelis Antoniou 
349da56d04cSPantelis Antoniou 		err = __of_adjust_phandle_ref(resolve, rprop, phandle);
3507941b27bSPantelis Antoniou 		if (err)
3517941b27bSPantelis Antoniou 			break;
3527941b27bSPantelis Antoniou 	}
3537941b27bSPantelis Antoniou 
3547941b27bSPantelis Antoniou out:
3557941b27bSPantelis Antoniou 	of_node_put(root_sym);
3567941b27bSPantelis Antoniou 
3577941b27bSPantelis Antoniou 	return err;
3587941b27bSPantelis Antoniou }
3597941b27bSPantelis Antoniou EXPORT_SYMBOL_GPL(of_resolve_phandles);
360