1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51#include "libfdt_env.h"
52
53#ifndef USE_HOSTCC
54#include <fdt.h>
55#include <libfdt.h>
56#else
57#include "fdt_host.h"
58#endif
59
60#include "libfdt_internal.h"
61
62static int _fdt_nodename_eq(const void *fdt, int offset,
63 const char *s, int len)
64{
65 const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
66
67 if (! p)
68
69 return 0;
70
71 if (memcmp(p, s, len) != 0)
72 return 0;
73
74 if (p[len] == '\0')
75 return 1;
76 else if (!memchr(s, '@', len) && (p[len] == '@'))
77 return 1;
78 else
79 return 0;
80}
81
82const char *fdt_string(const void *fdt, int stroffset)
83{
84 return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
85}
86
87static int _fdt_string_eq(const void *fdt, int stroffset,
88 const char *s, int len)
89{
90 const char *p = fdt_string(fdt, stroffset);
91
92 return (strlen(p) == len) && (memcmp(p, s, len) == 0);
93}
94
95int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
96{
97 FDT_CHECK_HEADER(fdt);
98 *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
99 *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
100 return 0;
101}
102
103int fdt_num_mem_rsv(const void *fdt)
104{
105 int i = 0;
106
107 while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
108 i++;
109 return i;
110}
111
112int fdt_subnode_offset_namelen(const void *fdt, int offset,
113 const char *name, int namelen)
114{
115 int depth = 0;
116
117 FDT_CHECK_HEADER(fdt);
118
119 for (depth = 0, offset = fdt_next_node(fdt, offset, &depth);
120 (offset >= 0) && (depth > 0);
121 offset = fdt_next_node(fdt, offset, &depth)) {
122 if (depth < 0)
123 return -FDT_ERR_NOTFOUND;
124 else if ((depth == 1)
125 && _fdt_nodename_eq(fdt, offset, name, namelen))
126 return offset;
127 }
128
129 if (offset < 0)
130 return offset;
131 else
132 return -FDT_ERR_NOTFOUND;
133}
134
135int fdt_subnode_offset(const void *fdt, int parentoffset,
136 const char *name)
137{
138 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
139}
140
141int fdt_path_offset(const void *fdt, const char *path)
142{
143 const char *end = path + strlen(path);
144 const char *p = path;
145 int offset = 0;
146
147 FDT_CHECK_HEADER(fdt);
148
149
150 if (*path != '/') {
151 const char *q = strchr(path, '/');
152
153 if (!q)
154 q = end;
155
156 p = fdt_get_alias_namelen(fdt, p, q - p);
157 if (!p)
158 return -FDT_ERR_BADPATH;
159 offset = fdt_path_offset(fdt, p);
160
161 p = q;
162 }
163
164 while (*p) {
165 const char *q;
166
167 while (*p == '/')
168 p++;
169 if (! *p)
170 return offset;
171 q = strchr(p, '/');
172 if (! q)
173 q = end;
174
175 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
176 if (offset < 0)
177 return offset;
178
179 p = q;
180 }
181
182 return offset;
183}
184
185const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
186{
187 const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
188 int err;
189
190 if (((err = fdt_check_header(fdt)) != 0)
191 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
192 goto fail;
193
194 if (len)
195 *len = strlen(nh->name);
196
197 return nh->name;
198
199 fail:
200 if (len)
201 *len = err;
202 return NULL;
203}
204
205const struct fdt_property *fdt_get_property_namelen(const void *fdt,
206 int nodeoffset,
207 const char *name,
208 int namelen, int *lenp)
209{
210 uint32_t tag;
211 const struct fdt_property *prop;
212 int namestroff;
213 int offset, nextoffset;
214 int err;
215
216 if (((err = fdt_check_header(fdt)) != 0)
217 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
218 goto fail;
219
220 nextoffset = err;
221 do {
222 offset = nextoffset;
223
224 tag = fdt_next_tag(fdt, offset, &nextoffset);
225 switch (tag) {
226 case FDT_END:
227 err = -FDT_ERR_TRUNCATED;
228 goto fail;
229
230 case FDT_BEGIN_NODE:
231 case FDT_END_NODE:
232 case FDT_NOP:
233 break;
234
235 case FDT_PROP:
236 err = -FDT_ERR_BADSTRUCTURE;
237 prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
238 if (! prop)
239 goto fail;
240 namestroff = fdt32_to_cpu(prop->nameoff);
241 if (_fdt_string_eq(fdt, namestroff, name, namelen)) {
242
243 int len = fdt32_to_cpu(prop->len);
244 prop = fdt_offset_ptr(fdt, offset,
245 sizeof(*prop)+len);
246 if (! prop)
247 goto fail;
248
249 if (lenp)
250 *lenp = len;
251
252 return prop;
253 }
254 break;
255
256 default:
257 err = -FDT_ERR_BADSTRUCTURE;
258 goto fail;
259 }
260 } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
261
262 err = -FDT_ERR_NOTFOUND;
263 fail:
264 if (lenp)
265 *lenp = err;
266 return NULL;
267}
268
269const struct fdt_property *fdt_get_property(const void *fdt,
270 int nodeoffset,
271 const char *name, int *lenp)
272{
273 return fdt_get_property_namelen(fdt, nodeoffset, name,
274 strlen(name), lenp);
275}
276
277const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
278 const char *name, int namelen, int *lenp)
279{
280 const struct fdt_property *prop;
281
282 prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
283 if (! prop)
284 return NULL;
285
286 return prop->data;
287}
288
289const void *fdt_getprop(const void *fdt, int nodeoffset,
290 const char *name, int *lenp)
291{
292 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
293}
294
295uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
296{
297 const uint32_t *php;
298 int len;
299
300 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
301 if (!php || (len != sizeof(*php)))
302 return 0;
303
304 return fdt32_to_cpu(*php);
305}
306
307const char *fdt_get_alias_namelen(const void *fdt,
308 const char *name, int namelen)
309{
310 int aliasoffset;
311
312 aliasoffset = fdt_path_offset(fdt, "/aliases");
313 if (aliasoffset < 0)
314 return NULL;
315
316 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
317}
318
319const char *fdt_get_alias(const void *fdt, const char *name)
320{
321 return fdt_get_alias_namelen(fdt, name, strlen(name));
322}
323
324int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
325{
326 int pdepth = 0, p = 0;
327 int offset, depth, namelen;
328 const char *name;
329
330 FDT_CHECK_HEADER(fdt);
331
332 if (buflen < 2)
333 return -FDT_ERR_NOSPACE;
334
335 for (offset = 0, depth = 0;
336 (offset >= 0) && (offset <= nodeoffset);
337 offset = fdt_next_node(fdt, offset, &depth)) {
338 while (pdepth > depth) {
339 do {
340 p--;
341 } while (buf[p-1] != '/');
342 pdepth--;
343 }
344
345 if (pdepth >= depth) {
346 name = fdt_get_name(fdt, offset, &namelen);
347 if (!name)
348 return namelen;
349 if ((p + namelen + 1) <= buflen) {
350 memcpy(buf + p, name, namelen);
351 p += namelen;
352 buf[p++] = '/';
353 pdepth++;
354 }
355 }
356
357 if (offset == nodeoffset) {
358 if (pdepth < (depth + 1))
359 return -FDT_ERR_NOSPACE;
360
361 if (p > 1)
362 p--;
363 buf[p] = '\0';
364 return 0;
365 }
366 }
367
368 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
369 return -FDT_ERR_BADOFFSET;
370 else if (offset == -FDT_ERR_BADOFFSET)
371 return -FDT_ERR_BADSTRUCTURE;
372
373 return offset;
374}
375
376int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
377 int supernodedepth, int *nodedepth)
378{
379 int offset, depth;
380 int supernodeoffset = -FDT_ERR_INTERNAL;
381
382 FDT_CHECK_HEADER(fdt);
383
384 if (supernodedepth < 0)
385 return -FDT_ERR_NOTFOUND;
386
387 for (offset = 0, depth = 0;
388 (offset >= 0) && (offset <= nodeoffset);
389 offset = fdt_next_node(fdt, offset, &depth)) {
390 if (depth == supernodedepth)
391 supernodeoffset = offset;
392
393 if (offset == nodeoffset) {
394 if (nodedepth)
395 *nodedepth = depth;
396
397 if (supernodedepth > depth)
398 return -FDT_ERR_NOTFOUND;
399 else
400 return supernodeoffset;
401 }
402 }
403
404 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
405 return -FDT_ERR_BADOFFSET;
406 else if (offset == -FDT_ERR_BADOFFSET)
407 return -FDT_ERR_BADSTRUCTURE;
408
409 return offset;
410}
411
412int fdt_node_depth(const void *fdt, int nodeoffset)
413{
414 int nodedepth;
415 int err;
416
417 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
418 if (err)
419 return (err < 0) ? err : -FDT_ERR_INTERNAL;
420 return nodedepth;
421}
422
423int fdt_parent_offset(const void *fdt, int nodeoffset)
424{
425 int nodedepth = fdt_node_depth(fdt, nodeoffset);
426
427 if (nodedepth < 0)
428 return nodedepth;
429 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
430 nodedepth - 1, NULL);
431}
432
433int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
434 const char *propname,
435 const void *propval, int proplen)
436{
437 int offset;
438 const void *val;
439 int len;
440
441 FDT_CHECK_HEADER(fdt);
442
443
444
445
446
447
448 for (offset = fdt_next_node(fdt, startoffset, NULL);
449 offset >= 0;
450 offset = fdt_next_node(fdt, offset, NULL)) {
451 val = fdt_getprop(fdt, offset, propname, &len);
452 if (val && (len == proplen)
453 && (memcmp(val, propval, len) == 0))
454 return offset;
455 }
456
457 return offset;
458}
459
460int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
461{
462 if ((phandle == 0) || (phandle == -1))
463 return -FDT_ERR_BADPHANDLE;
464 phandle = cpu_to_fdt32(phandle);
465 return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
466 &phandle, sizeof(phandle));
467}
468
469static int _fdt_stringlist_contains(const char *strlist, int listlen,
470 const char *str)
471{
472 int len = strlen(str);
473 const char *p;
474
475 while (listlen >= len) {
476 if (memcmp(str, strlist, len+1) == 0)
477 return 1;
478 p = memchr(strlist, '\0', listlen);
479 if (!p)
480 return 0;
481 listlen -= (p-strlist) + 1;
482 strlist = p + 1;
483 }
484 return 0;
485}
486
487int fdt_node_check_compatible(const void *fdt, int nodeoffset,
488 const char *compatible)
489{
490 const void *prop;
491 int len;
492
493 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
494 if (!prop)
495 return len;
496 if (_fdt_stringlist_contains(prop, len, compatible))
497 return 0;
498 else
499 return 1;
500}
501
502int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
503 const char *compatible)
504{
505 int offset, err;
506
507 FDT_CHECK_HEADER(fdt);
508
509
510
511
512
513
514 for (offset = fdt_next_node(fdt, startoffset, NULL);
515 offset >= 0;
516 offset = fdt_next_node(fdt, offset, NULL)) {
517 err = fdt_node_check_compatible(fdt, offset, compatible);
518 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
519 return err;
520 else if (err == 0)
521 return offset;
522 }
523
524 return offset;
525}
526