aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPantelis Antoniou <pantelis.antoniou@konsulko.com>2014-07-04 12:59:20 -0400
committerGrant Likely <grant.likely@linaro.org>2014-10-04 16:24:26 -0400
commit7941b27b16e3282f6ec8817e36492f1deec753a7 (patch)
treeea2619faa7f82e42703550bf01ee9ea15c7b3f62
parent841ec21357eee222416e3b7f1b6ef23cfc6ee43f (diff)
of: Introduce Device Tree resolve support.
Introduce support for dynamic device tree resolution. Using it, it is possible to prepare a device tree that's been loaded on runtime to be modified and inserted at the kernel live tree. Export of of_resolve and bug fix of double free by Guenter Roeck <groeck@juniper.net> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> [grant.likely: Don't need to select CONFIG_OF_DYNAMIC and CONFIG_OF_DEVICE] [grant.likely: Don't need to depend on OF or !SPARC] [grant.likely: Factor out duplicate code blocks into single function] Signed-off-by: Grant Likely <grant.likely@linaro.org>
-rw-r--r--Documentation/devicetree/dynamic-resolution-notes.txt25
-rw-r--r--drivers/of/Kconfig3
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/resolver.c336
-rw-r--r--include/linux/of.h3
5 files changed, 368 insertions, 0 deletions
diff --git a/Documentation/devicetree/dynamic-resolution-notes.txt b/Documentation/devicetree/dynamic-resolution-notes.txt
new file mode 100644
index 000000000000..083d23262abe
--- /dev/null
+++ b/Documentation/devicetree/dynamic-resolution-notes.txt
@@ -0,0 +1,25 @@
1Device Tree Dynamic Resolver Notes
2----------------------------------
3
4This document describes the implementation of the in-kernel
5Device Tree resolver, residing in drivers/of/resolver.c and is a
6companion document to Documentation/devicetree/dt-object-internal.txt[1]
7
8How the resolver works
9----------------------
10
11The resolver is given as an input an arbitrary tree compiled with the
12proper dtc option and having a /plugin/ tag. This generates the
13appropriate __fixups__ & __local_fixups__ nodes as described in [1].
14
15In sequence the resolver works by the following steps:
16
171. Get the maximum device tree phandle value from the live tree + 1.
182. Adjust all the local phandles of the tree to resolve by that amount.
193. Using the __local__fixups__ node information adjust all local references
20 by the same amount.
214. For each property in the __fixups__ node locate the node it references
22 in the live tree. This is the label used to tag the node.
235. Retrieve the phandle of the target of the fixup.
246. For each fixup in the property locate the node:property:offset location
25 and replace it with the phandle value.
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 5160c4eb73c2..6b81a36f6420 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -79,4 +79,7 @@ config OF_RESERVED_MEM
79 help 79 help
80 Helpers to allow for reservation of memory regions 80 Helpers to allow for reservation of memory regions
81 81
82config OF_RESOLVE
83 bool
84
82endmenu # OF 85endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 2b6a7b129d10..ca9209ce50cd 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_OF_PCI) += of_pci.o
13obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o 13obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
14obj-$(CONFIG_OF_MTD) += of_mtd.o 14obj-$(CONFIG_OF_MTD) += of_mtd.o
15obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o 15obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
16obj-$(CONFIG_OF_RESOLVE) += resolver.o
16 17
17CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt 18CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
18CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt 19CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
new file mode 100644
index 000000000000..aed7959f800d
--- /dev/null
+++ b/drivers/of/resolver.c
@@ -0,0 +1,336 @@
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 */
29static 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 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 return found;
45 }
46
47 return NULL;
48}
49
50/*
51 * Find live tree's maximum phandle value.
52 */
53static phandle of_get_tree_max_phandle(void)
54{
55 struct device_node *node;
56 phandle phandle;
57 unsigned long flags;
58
59 /* now search recursively */
60 raw_spin_lock_irqsave(&devtree_lock, flags);
61 phandle = 0;
62 for_each_of_allnodes(node) {
63 if (node->phandle != OF_PHANDLE_ILLEGAL &&
64 node->phandle > phandle)
65 phandle = node->phandle;
66 }
67 raw_spin_unlock_irqrestore(&devtree_lock, flags);
68
69 return phandle;
70}
71
72/*
73 * Adjust a subtree's phandle values by a given delta.
74 * Makes sure not to just adjust the device node's phandle value,
75 * but modify the phandle properties values as well.
76 */
77static void __of_adjust_tree_phandles(struct device_node *node,
78 int phandle_delta)
79{
80 struct device_node *child;
81 struct property *prop;
82 phandle phandle;
83
84 /* first adjust the node's phandle direct value */
85 if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
86 node->phandle += phandle_delta;
87
88 /* now adjust phandle & linux,phandle values */
89 for_each_property_of_node(node, prop) {
90
91 /* only look for these two */
92 if (of_prop_cmp(prop->name, "phandle") != 0 &&
93 of_prop_cmp(prop->name, "linux,phandle") != 0)
94 continue;
95
96 /* must be big enough */
97 if (prop->length < 4)
98 continue;
99
100 /* read phandle value */
101 phandle = be32_to_cpup(prop->value);
102 if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */
103 continue;
104
105 /* adjust */
106 *(uint32_t *)prop->value = cpu_to_be32(node->phandle);
107 }
108
109 /* now do the children recursively */
110 for_each_child_of_node(node, child)
111 __of_adjust_tree_phandles(child, phandle_delta);
112}
113
114static int __of_adjust_phandle_ref(struct device_node *node, struct property *rprop, int value, bool is_delta)
115{
116 phandle phandle;
117 struct device_node *refnode;
118 struct property *sprop;
119 char *propval, *propcur, *propend, *nodestr, *propstr, *s;
120 int offset, propcurlen;
121 int err = 0;
122
123 /* make a copy */
124 propval = kmalloc(rprop->length, GFP_KERNEL);
125 if (!propval) {
126 pr_err("%s: Could not copy value of '%s'\n",
127 __func__, rprop->name);
128 return -ENOMEM;
129 }
130 memcpy(propval, rprop->value, rprop->length);
131
132 propend = propval + rprop->length;
133 for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
134 propcurlen = strlen(propcur);
135
136 nodestr = propcur;
137 s = strchr(propcur, ':');
138 if (!s) {
139 pr_err("%s: Illegal symbol entry '%s' (1)\n",
140 __func__, propcur);
141 err = -EINVAL;
142 goto err_fail;
143 }
144 *s++ = '\0';
145
146 propstr = s;
147 s = strchr(s, ':');
148 if (!s) {
149 pr_err("%s: Illegal symbol entry '%s' (2)\n",
150 __func__, (char *)rprop->value);
151 err = -EINVAL;
152 goto err_fail;
153 }
154
155 *s++ = '\0';
156 err = kstrtoint(s, 10, &offset);
157 if (err != 0) {
158 pr_err("%s: Could get offset '%s'\n",
159 __func__, (char *)rprop->value);
160 goto err_fail;
161 }
162
163 /* look into the resolve node for the full path */
164 refnode = __of_find_node_by_full_name(node, nodestr);
165 if (!refnode) {
166 pr_warn("%s: Could not find refnode '%s'\n",
167 __func__, (char *)rprop->value);
168 continue;
169 }
170
171 /* now find the property */
172 for_each_property_of_node(refnode, sprop) {
173 if (of_prop_cmp(sprop->name, propstr) == 0)
174 break;
175 }
176
177 if (!sprop) {
178 pr_err("%s: Could not find property '%s'\n",
179 __func__, (char *)rprop->value);
180 err = -ENOENT;
181 goto err_fail;
182 }
183
184 phandle = is_delta ? be32_to_cpup(sprop->value + offset) + value : value;
185 *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
186 }
187
188err_fail:
189 kfree(propval);
190 return err;
191}
192
193/*
194 * Adjust the local phandle references by the given phandle delta.
195 * Assumes the existances of a __local_fixups__ node at the root
196 * of the tree. Does not take any devtree locks so make sure you
197 * call this on a tree which is at the detached state.
198 */
199static int __of_adjust_tree_phandle_references(struct device_node *node,
200 int phandle_delta)
201{
202 struct device_node *child;
203 struct property *rprop;
204 int err;
205
206 /* locate the symbols & fixups nodes on resolve */
207 for_each_child_of_node(node, child)
208 if (of_node_cmp(child->name, "__local_fixups__") == 0)
209 break;
210
211 /* no local fixups */
212 if (!child)
213 return 0;
214
215 /* find the local fixups property */
216 for_each_property_of_node(child, rprop) {
217 /* skip properties added automatically */
218 if (of_prop_cmp(rprop->name, "name") == 0)
219 continue;
220
221 err = __of_adjust_phandle_ref(node, rprop, phandle_delta, true);
222 if (err)
223 return err;
224 }
225
226 return 0;
227}
228
229/**
230 * of_resolve - Resolve the given node against the live tree.
231 *
232 * @resolve: Node to resolve
233 *
234 * Perform dynamic Device Tree resolution against the live tree
235 * to the given node to resolve. This depends on the live tree
236 * having a __symbols__ node, and the resolve node the __fixups__ &
237 * __local_fixups__ nodes (if needed).
238 * The result of the operation is a resolve node that it's contents
239 * are fit to be inserted or operate upon the live tree.
240 * Returns 0 on success or a negative error value on error.
241 */
242int of_resolve_phandles(struct device_node *resolve)
243{
244 struct device_node *child, *refnode;
245 struct device_node *root_sym, *resolve_sym, *resolve_fix;
246 struct property *rprop;
247 const char *refpath;
248 phandle phandle, phandle_delta;
249 int err;
250
251 /* the resolve node must exist, and be detached */
252 if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
253 return -EINVAL;
254
255 /* first we need to adjust the phandles */
256 phandle_delta = of_get_tree_max_phandle() + 1;
257 __of_adjust_tree_phandles(resolve, phandle_delta);
258 err = __of_adjust_tree_phandle_references(resolve, phandle_delta);
259 if (err != 0)
260 return err;
261
262 root_sym = NULL;
263 resolve_sym = NULL;
264 resolve_fix = NULL;
265
266 /* this may fail (if no fixups are required) */
267 root_sym = of_find_node_by_path("/__symbols__");
268
269 /* locate the symbols & fixups nodes on resolve */
270 for_each_child_of_node(resolve, child) {
271
272 if (!resolve_sym &&
273 of_node_cmp(child->name, "__symbols__") == 0)
274 resolve_sym = child;
275
276 if (!resolve_fix &&
277 of_node_cmp(child->name, "__fixups__") == 0)
278 resolve_fix = child;
279
280 /* both found, don't bother anymore */
281 if (resolve_sym && resolve_fix)
282 break;
283 }
284
285 /* we do allow for the case where no fixups are needed */
286 if (!resolve_fix) {
287 err = 0; /* no error */
288 goto out;
289 }
290
291 /* we need to fixup, but no root symbols... */
292 if (!root_sym) {
293 err = -EINVAL;
294 goto out;
295 }
296
297 for_each_property_of_node(resolve_fix, rprop) {
298
299 /* skip properties added automatically */
300 if (of_prop_cmp(rprop->name, "name") == 0)
301 continue;
302
303 err = of_property_read_string(root_sym,
304 rprop->name, &refpath);
305 if (err != 0) {
306 pr_err("%s: Could not find symbol '%s'\n",
307 __func__, rprop->name);
308 goto out;
309 }
310
311 refnode = of_find_node_by_path(refpath);
312 if (!refnode) {
313 pr_err("%s: Could not find node by path '%s'\n",
314 __func__, refpath);
315 err = -ENOENT;
316 goto out;
317 }
318
319 phandle = refnode->phandle;
320 of_node_put(refnode);
321
322 pr_debug("%s: %s phandle is 0x%08x\n",
323 __func__, rprop->name, phandle);
324
325 err = __of_adjust_phandle_ref(resolve, rprop, phandle, false);
326 if (err)
327 break;
328 }
329
330out:
331 /* NULL is handled by of_node_put as NOP */
332 of_node_put(root_sym);
333
334 return err;
335}
336EXPORT_SYMBOL_GPL(of_resolve_phandles);
diff --git a/include/linux/of.h b/include/linux/of.h
index 6c4363b8ddc3..6545e7aec7bb 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -863,4 +863,7 @@ static inline int of_changeset_update_property(struct of_changeset *ocs,
863} 863}
864#endif 864#endif
865 865
866/* CONFIG_OF_RESOLVE api */
867extern int of_resolve_phandles(struct device_node *tree);
868
866#endif /* _LINUX_OF_H */ 869#endif /* _LINUX_OF_H */