diff options
author | Pantelis Antoniou <pantelis.antoniou@konsulko.com> | 2014-07-04 12:59:20 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@linaro.org> | 2014-10-04 16:24:26 -0400 |
commit | 7941b27b16e3282f6ec8817e36492f1deec753a7 (patch) | |
tree | ea2619faa7f82e42703550bf01ee9ea15c7b3f62 /drivers/of | |
parent | 841ec21357eee222416e3b7f1b6ef23cfc6ee43f (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>
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/Kconfig | 3 | ||||
-rw-r--r-- | drivers/of/Makefile | 1 | ||||
-rw-r--r-- | drivers/of/resolver.c | 336 |
3 files changed, 340 insertions, 0 deletions
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 | ||
82 | config OF_RESOLVE | ||
83 | bool | ||
84 | |||
82 | endmenu # OF | 85 | endmenu # 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 | |||
13 | obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o | 13 | obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o |
14 | obj-$(CONFIG_OF_MTD) += of_mtd.o | 14 | obj-$(CONFIG_OF_MTD) += of_mtd.o |
15 | obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o | 15 | obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o |
16 | obj-$(CONFIG_OF_RESOLVE) += resolver.o | ||
16 | 17 | ||
17 | CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt | 18 | CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt |
18 | CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt | 19 | CFLAGS_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 | */ | ||
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 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 | */ | ||
53 | static 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 | */ | ||
77 | static 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 | |||
114 | static 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 | |||
188 | err_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 | */ | ||
199 | static 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 | */ | ||
242 | int 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 | |||
330 | out: | ||
331 | /* NULL is handled by of_node_put as NOP */ | ||
332 | of_node_put(root_sym); | ||
333 | |||
334 | return err; | ||
335 | } | ||
336 | EXPORT_SYMBOL_GPL(of_resolve_phandles); | ||