1
2
3
4
5
6
7
8
9
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/slab.h>
22
23#include "of_private.h"
24
25
26#define OF_PHANDLE_ILLEGAL 0xdeadbeef
27
28static phandle live_tree_max_phandle(void)
29{
30 struct device_node *node;
31 phandle phandle;
32 unsigned long flags;
33
34 raw_spin_lock_irqsave(&devtree_lock, flags);
35 phandle = 0;
36 for_each_of_allnodes(node) {
37 if (node->phandle != OF_PHANDLE_ILLEGAL &&
38 node->phandle > phandle)
39 phandle = node->phandle;
40 }
41 raw_spin_unlock_irqrestore(&devtree_lock, flags);
42
43 return phandle;
44}
45
46static void adjust_overlay_phandles(struct device_node *overlay,
47 int phandle_delta)
48{
49 struct device_node *child;
50 struct property *prop;
51 phandle phandle;
52
53
54 if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
55 overlay->phandle += phandle_delta;
56
57
58 for_each_property_of_node(overlay, prop) {
59
60 if (of_prop_cmp(prop->name, "phandle") &&
61 of_prop_cmp(prop->name, "linux,phandle"))
62 continue;
63
64 if (prop->length < 4)
65 continue;
66
67 phandle = be32_to_cpup(prop->value);
68 if (phandle == OF_PHANDLE_ILLEGAL)
69 continue;
70
71 *(__be32 *)prop->value = cpu_to_be32(overlay->phandle);
72 }
73
74 for_each_child_of_node(overlay, child)
75 adjust_overlay_phandles(child, phandle_delta);
76}
77
78static int update_usages_of_a_phandle_reference(struct device_node *overlay,
79 struct property *prop_fixup, phandle phandle)
80{
81 struct device_node *refnode;
82 struct property *prop;
83 char *value, *cur, *end, *node_path, *prop_name, *s;
84 int offset, len;
85 int err = 0;
86
87 value = kmalloc(prop_fixup->length, GFP_KERNEL);
88 if (!value)
89 return -ENOMEM;
90 memcpy(value, prop_fixup->value, prop_fixup->length);
91
92
93 end = value + prop_fixup->length;
94 for (cur = value; cur < end; cur += len + 1) {
95 len = strlen(cur);
96
97 node_path = cur;
98 s = strchr(cur, ':');
99 if (!s) {
100 err = -EINVAL;
101 goto err_fail;
102 }
103 *s++ = '\0';
104
105 prop_name = s;
106 s = strchr(s, ':');
107 if (!s) {
108 err = -EINVAL;
109 goto err_fail;
110 }
111 *s++ = '\0';
112
113 err = kstrtoint(s, 10, &offset);
114 if (err)
115 goto err_fail;
116
117 refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path);
118 if (!refnode)
119 continue;
120
121 for_each_property_of_node(refnode, prop) {
122 if (!of_prop_cmp(prop->name, prop_name))
123 break;
124 }
125 of_node_put(refnode);
126
127 if (!prop) {
128 err = -ENOENT;
129 goto err_fail;
130 }
131
132 *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
133 }
134
135err_fail:
136 kfree(value);
137 return err;
138}
139
140
141static int node_name_cmp(const struct device_node *dn1,
142 const struct device_node *dn2)
143{
144 const char *n1 = kbasename(dn1->full_name);
145 const char *n2 = kbasename(dn2->full_name);
146
147 return of_node_cmp(n1, n2);
148}
149
150
151
152
153
154
155
156
157
158
159
160
161static int adjust_local_phandle_references(struct device_node *local_fixups,
162 struct device_node *overlay, int phandle_delta)
163{
164 struct device_node *child, *overlay_child;
165 struct property *prop_fix, *prop;
166 int err, i, count;
167 unsigned int off;
168 phandle phandle;
169
170 if (!local_fixups)
171 return 0;
172
173 for_each_property_of_node(local_fixups, prop_fix) {
174
175
176 if (!of_prop_cmp(prop_fix->name, "name") ||
177 !of_prop_cmp(prop_fix->name, "phandle") ||
178 !of_prop_cmp(prop_fix->name, "linux,phandle"))
179 continue;
180
181 if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
182 return -EINVAL;
183 count = prop_fix->length / sizeof(__be32);
184
185 for_each_property_of_node(overlay, prop) {
186 if (!of_prop_cmp(prop->name, prop_fix->name))
187 break;
188 }
189
190 if (!prop)
191 return -EINVAL;
192
193 for (i = 0; i < count; i++) {
194 off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
195 if ((off + 4) > prop->length)
196 return -EINVAL;
197
198 phandle = be32_to_cpu(*(__be32 *)(prop->value + off));
199 phandle += phandle_delta;
200 *(__be32 *)(prop->value + off) = cpu_to_be32(phandle);
201 }
202 }
203
204
205
206
207
208
209
210
211 for_each_child_of_node(local_fixups, child) {
212
213 for_each_child_of_node(overlay, overlay_child)
214 if (!node_name_cmp(child, overlay_child))
215 break;
216
217 if (!overlay_child)
218 return -EINVAL;
219
220 err = adjust_local_phandle_references(child, overlay_child,
221 phandle_delta);
222 if (err)
223 return err;
224 }
225
226 return 0;
227}
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262int of_resolve_phandles(struct device_node *overlay)
263{
264 struct device_node *child, *local_fixups, *refnode;
265 struct device_node *tree_symbols, *overlay_fixups;
266 struct property *prop;
267 const char *refpath;
268 phandle phandle, phandle_delta;
269 int err;
270
271 tree_symbols = NULL;
272
273 if (!overlay) {
274 pr_err("null overlay\n");
275 err = -EINVAL;
276 goto out;
277 }
278 if (!of_node_check_flag(overlay, OF_DETACHED)) {
279 pr_err("overlay not detached\n");
280 err = -EINVAL;
281 goto out;
282 }
283
284 phandle_delta = live_tree_max_phandle() + 1;
285 adjust_overlay_phandles(overlay, phandle_delta);
286
287 for_each_child_of_node(overlay, local_fixups)
288 if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
289 break;
290
291 err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
292 if (err)
293 goto out;
294
295 overlay_fixups = NULL;
296
297 for_each_child_of_node(overlay, child) {
298 if (!of_node_cmp(child->name, "__fixups__"))
299 overlay_fixups = child;
300 }
301
302 if (!overlay_fixups) {
303 err = 0;
304 goto out;
305 }
306
307 tree_symbols = of_find_node_by_path("/__symbols__");
308 if (!tree_symbols) {
309 pr_err("no symbols in root of device tree.\n");
310 err = -EINVAL;
311 goto out;
312 }
313
314 for_each_property_of_node(overlay_fixups, prop) {
315
316
317 if (!of_prop_cmp(prop->name, "name"))
318 continue;
319
320 err = of_property_read_string(tree_symbols,
321 prop->name, &refpath);
322 if (err)
323 goto out;
324
325 refnode = of_find_node_by_path(refpath);
326 if (!refnode) {
327 err = -ENOENT;
328 goto out;
329 }
330
331 phandle = refnode->phandle;
332 of_node_put(refnode);
333
334 err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
335 if (err)
336 break;
337 }
338
339out:
340 if (err)
341 pr_err("overlay phandle fixup failed: %d\n", err);
342 of_node_put(tree_symbols);
343
344 return err;
345}
346EXPORT_SYMBOL_GPL(of_resolve_phandles);
347