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