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