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