xref: /openbmc/linux/drivers/of/resolver.c (revision 7587eb18)
1 /*
2  * Functions for dealing with DT resolution
3  *
4  * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
5  * Copyright (C) 2012 Texas Instruments Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  */
11 
12 #define pr_fmt(fmt)	"OF: resolver: " fmt
13 
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/of_device.h>
18 #include <linux/string.h>
19 #include <linux/ctype.h>
20 #include <linux/errno.h>
21 #include <linux/string.h>
22 #include <linux/slab.h>
23 
24 /* illegal phandle value (set when unresolved) */
25 #define OF_PHANDLE_ILLEGAL	0xdeadbeef
26 
27 /**
28  * Find a node with the give full name by recursively following any of
29  * the child node links.
30  */
31 static struct device_node *__of_find_node_by_full_name(struct device_node *node,
32 		const char *full_name)
33 {
34 	struct device_node *child, *found;
35 
36 	if (node == NULL)
37 		return NULL;
38 
39 	/* check */
40 	if (of_node_cmp(node->full_name, full_name) == 0)
41 		return of_node_get(node);
42 
43 	for_each_child_of_node(node, child) {
44 		found = __of_find_node_by_full_name(child, full_name);
45 		if (found != NULL) {
46 			of_node_put(child);
47 			return found;
48 		}
49 	}
50 
51 	return NULL;
52 }
53 
54 /*
55  * Find live tree's maximum phandle value.
56  */
57 static phandle of_get_tree_max_phandle(void)
58 {
59 	struct device_node *node;
60 	phandle phandle;
61 	unsigned long flags;
62 
63 	/* now search recursively */
64 	raw_spin_lock_irqsave(&devtree_lock, flags);
65 	phandle = 0;
66 	for_each_of_allnodes(node) {
67 		if (node->phandle != OF_PHANDLE_ILLEGAL &&
68 				node->phandle > phandle)
69 			phandle = node->phandle;
70 	}
71 	raw_spin_unlock_irqrestore(&devtree_lock, flags);
72 
73 	return phandle;
74 }
75 
76 /*
77  * Adjust a subtree's phandle values by a given delta.
78  * Makes sure not to just adjust the device node's phandle value,
79  * but modify the phandle properties values as well.
80  */
81 static void __of_adjust_tree_phandles(struct device_node *node,
82 		int phandle_delta)
83 {
84 	struct device_node *child;
85 	struct property *prop;
86 	phandle phandle;
87 
88 	/* first adjust the node's phandle direct value */
89 	if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
90 		node->phandle += phandle_delta;
91 
92 	/* now adjust phandle & linux,phandle values */
93 	for_each_property_of_node(node, prop) {
94 
95 		/* only look for these two */
96 		if (of_prop_cmp(prop->name, "phandle") != 0 &&
97 		    of_prop_cmp(prop->name, "linux,phandle") != 0)
98 			continue;
99 
100 		/* must be big enough */
101 		if (prop->length < 4)
102 			continue;
103 
104 		/* read phandle value */
105 		phandle = be32_to_cpup(prop->value);
106 		if (phandle == OF_PHANDLE_ILLEGAL)	/* unresolved */
107 			continue;
108 
109 		/* adjust */
110 		*(uint32_t *)prop->value = cpu_to_be32(node->phandle);
111 	}
112 
113 	/* now do the children recursively */
114 	for_each_child_of_node(node, child)
115 		__of_adjust_tree_phandles(child, phandle_delta);
116 }
117 
118 static int __of_adjust_phandle_ref(struct device_node *node,
119 		struct property *rprop, int value)
120 {
121 	phandle phandle;
122 	struct device_node *refnode;
123 	struct property *sprop;
124 	char *propval, *propcur, *propend, *nodestr, *propstr, *s;
125 	int offset, propcurlen;
126 	int err = 0;
127 
128 	/* make a copy */
129 	propval = kmalloc(rprop->length, GFP_KERNEL);
130 	if (!propval) {
131 		pr_err("%s: Could not copy value of '%s'\n",
132 				__func__, rprop->name);
133 		return -ENOMEM;
134 	}
135 	memcpy(propval, rprop->value, rprop->length);
136 
137 	propend = propval + rprop->length;
138 	for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
139 		propcurlen = strlen(propcur);
140 
141 		nodestr = propcur;
142 		s = strchr(propcur, ':');
143 		if (!s) {
144 			pr_err("%s: Illegal symbol entry '%s' (1)\n",
145 				__func__, propcur);
146 			err = -EINVAL;
147 			goto err_fail;
148 		}
149 		*s++ = '\0';
150 
151 		propstr = s;
152 		s = strchr(s, ':');
153 		if (!s) {
154 			pr_err("%s: Illegal symbol entry '%s' (2)\n",
155 				__func__, (char *)rprop->value);
156 			err = -EINVAL;
157 			goto err_fail;
158 		}
159 
160 		*s++ = '\0';
161 		err = kstrtoint(s, 10, &offset);
162 		if (err != 0) {
163 			pr_err("%s: Could get offset '%s'\n",
164 				__func__, (char *)rprop->value);
165 			goto err_fail;
166 		}
167 
168 		/* look into the resolve node for the full path */
169 		refnode = __of_find_node_by_full_name(node, nodestr);
170 		if (!refnode) {
171 			pr_warn("%s: Could not find refnode '%s'\n",
172 				__func__, (char *)rprop->value);
173 			continue;
174 		}
175 
176 		/* now find the property */
177 		for_each_property_of_node(refnode, sprop) {
178 			if (of_prop_cmp(sprop->name, propstr) == 0)
179 				break;
180 		}
181 		of_node_put(refnode);
182 
183 		if (!sprop) {
184 			pr_err("%s: Could not find property '%s'\n",
185 				__func__, (char *)rprop->value);
186 			err = -ENOENT;
187 			goto err_fail;
188 		}
189 
190 		phandle = value;
191 		*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
192 	}
193 
194 err_fail:
195 	kfree(propval);
196 	return err;
197 }
198 
199 /* compare nodes taking into account that 'name' strips out the @ part */
200 static int __of_node_name_cmp(const struct device_node *dn1,
201 		const struct device_node *dn2)
202 {
203 	const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
204 	const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
205 
206 	return of_node_cmp(n1, n2);
207 }
208 
209 /*
210  * Adjust the local phandle references by the given phandle delta.
211  * Assumes the existances of a __local_fixups__ node at the root.
212  * Assumes that __of_verify_tree_phandle_references has been called.
213  * Does not take any devtree locks so make sure you call this on a tree
214  * which is at the detached state.
215  */
216 static int __of_adjust_tree_phandle_references(struct device_node *node,
217 		struct device_node *target, int phandle_delta)
218 {
219 	struct device_node *child, *childtarget;
220 	struct property *rprop, *sprop;
221 	int err, i, count;
222 	unsigned int off;
223 	phandle phandle;
224 
225 	if (node == NULL)
226 		return 0;
227 
228 	for_each_property_of_node(node, rprop) {
229 
230 		/* skip properties added automatically */
231 		if (of_prop_cmp(rprop->name, "name") == 0 ||
232 		    of_prop_cmp(rprop->name, "phandle") == 0 ||
233 		    of_prop_cmp(rprop->name, "linux,phandle") == 0)
234 			continue;
235 
236 		if ((rprop->length % 4) != 0 || rprop->length == 0) {
237 			pr_err("%s: Illegal property (size) '%s' @%s\n",
238 					__func__, rprop->name, node->full_name);
239 			return -EINVAL;
240 		}
241 		count = rprop->length / sizeof(__be32);
242 
243 		/* now find the target property */
244 		for_each_property_of_node(target, sprop) {
245 			if (of_prop_cmp(sprop->name, rprop->name) == 0)
246 				break;
247 		}
248 
249 		if (sprop == NULL) {
250 			pr_err("%s: Could not find target property '%s' @%s\n",
251 					__func__, rprop->name, node->full_name);
252 			return -EINVAL;
253 		}
254 
255 		for (i = 0; i < count; i++) {
256 			off = be32_to_cpu(((__be32 *)rprop->value)[i]);
257 			/* make sure the offset doesn't overstep (even wrap) */
258 			if (off >= sprop->length ||
259 					(off + 4) > sprop->length) {
260 				pr_err("%s: Illegal property '%s' @%s\n",
261 						__func__, rprop->name,
262 						node->full_name);
263 				return -EINVAL;
264 			}
265 
266 			if (phandle_delta) {
267 				/* adjust */
268 				phandle = be32_to_cpu(*(__be32 *)(sprop->value + off));
269 				phandle += phandle_delta;
270 				*(__be32 *)(sprop->value + off) = cpu_to_be32(phandle);
271 			}
272 		}
273 	}
274 
275 	for_each_child_of_node(node, child) {
276 
277 		for_each_child_of_node(target, childtarget)
278 			if (__of_node_name_cmp(child, childtarget) == 0)
279 				break;
280 
281 		if (!childtarget) {
282 			pr_err("%s: Could not find target child '%s' @%s\n",
283 					__func__, child->name, node->full_name);
284 			return -EINVAL;
285 		}
286 
287 		err = __of_adjust_tree_phandle_references(child, childtarget,
288 				phandle_delta);
289 		if (err != 0)
290 			return err;
291 	}
292 
293 	return 0;
294 }
295 
296 /**
297  * of_resolve	- Resolve the given node against the live tree.
298  *
299  * @resolve:	Node to resolve
300  *
301  * Perform dynamic Device Tree resolution against the live tree
302  * to the given node to resolve. This depends on the live tree
303  * having a __symbols__ node, and the resolve node the __fixups__ &
304  * __local_fixups__ nodes (if needed).
305  * The result of the operation is a resolve node that it's contents
306  * are fit to be inserted or operate upon the live tree.
307  * Returns 0 on success or a negative error value on error.
308  */
309 int of_resolve_phandles(struct device_node *resolve)
310 {
311 	struct device_node *child, *childroot, *refnode;
312 	struct device_node *root_sym, *resolve_sym, *resolve_fix;
313 	struct property *rprop;
314 	const char *refpath;
315 	phandle phandle, phandle_delta;
316 	int err;
317 
318 	if (!resolve)
319 		pr_err("%s: null node\n", __func__);
320 	if (resolve && !of_node_check_flag(resolve, OF_DETACHED))
321 		pr_err("%s: node %s not detached\n", __func__,
322 			 resolve->full_name);
323 	/* the resolve node must exist, and be detached */
324 	if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
325 		return -EINVAL;
326 
327 	/* first we need to adjust the phandles */
328 	phandle_delta = of_get_tree_max_phandle() + 1;
329 	__of_adjust_tree_phandles(resolve, phandle_delta);
330 
331 	/* locate the local fixups */
332 	childroot = NULL;
333 	for_each_child_of_node(resolve, childroot)
334 		if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
335 			break;
336 
337 	if (childroot != NULL) {
338 		/* resolve root is guaranteed to be the '/' */
339 		err = __of_adjust_tree_phandle_references(childroot,
340 				resolve, 0);
341 		if (err != 0)
342 			return err;
343 
344 		BUG_ON(__of_adjust_tree_phandle_references(childroot,
345 				resolve, phandle_delta));
346 	}
347 
348 	root_sym = NULL;
349 	resolve_sym = NULL;
350 	resolve_fix = NULL;
351 
352 	/* this may fail (if no fixups are required) */
353 	root_sym = of_find_node_by_path("/__symbols__");
354 
355 	/* locate the symbols & fixups nodes on resolve */
356 	for_each_child_of_node(resolve, child) {
357 
358 		if (!resolve_sym &&
359 				of_node_cmp(child->name, "__symbols__") == 0)
360 			resolve_sym = child;
361 
362 		if (!resolve_fix &&
363 				of_node_cmp(child->name, "__fixups__") == 0)
364 			resolve_fix = child;
365 
366 		/* both found, don't bother anymore */
367 		if (resolve_sym && resolve_fix)
368 			break;
369 	}
370 
371 	/* we do allow for the case where no fixups are needed */
372 	if (!resolve_fix) {
373 		err = 0;	/* no error */
374 		goto out;
375 	}
376 
377 	/* we need to fixup, but no root symbols... */
378 	if (!root_sym) {
379 		pr_err("%s: no symbols in root of device tree.\n", __func__);
380 		err = -EINVAL;
381 		goto out;
382 	}
383 
384 	for_each_property_of_node(resolve_fix, rprop) {
385 
386 		/* skip properties added automatically */
387 		if (of_prop_cmp(rprop->name, "name") == 0)
388 			continue;
389 
390 		err = of_property_read_string(root_sym,
391 				rprop->name, &refpath);
392 		if (err != 0) {
393 			pr_err("%s: Could not find symbol '%s'\n",
394 					__func__, rprop->name);
395 			goto out;
396 		}
397 
398 		refnode = of_find_node_by_path(refpath);
399 		if (!refnode) {
400 			pr_err("%s: Could not find node by path '%s'\n",
401 					__func__, refpath);
402 			err = -ENOENT;
403 			goto out;
404 		}
405 
406 		phandle = refnode->phandle;
407 		of_node_put(refnode);
408 
409 		pr_debug("%s: %s phandle is 0x%08x\n",
410 				__func__, rprop->name, phandle);
411 
412 		err = __of_adjust_phandle_ref(resolve, rprop, phandle);
413 		if (err)
414 			break;
415 	}
416 
417 out:
418 	/* NULL is handled by of_node_put as NOP */
419 	of_node_put(root_sym);
420 
421 	return err;
422 }
423 EXPORT_SYMBOL_GPL(of_resolve_phandles);
424