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