Branch data Line data Source code
1 : : // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 : : /*
3 : : * libfdt - Flat Device Tree manipulation
4 : : * Copyright (C) 2006 David Gibson, IBM Corporation.
5 : : */
6 : : #include "libfdt_env.h"
7 : :
8 : : #include <fdt.h>
9 : : #include <libfdt.h>
10 : :
11 : : #include "libfdt_internal.h"
12 : :
13 : 3 : static int fdt_nodename_eq_(const void *fdt, int offset,
14 : : const char *s, int len)
15 : : {
16 : : int olen;
17 : 3 : const char *p = fdt_get_name(fdt, offset, &olen);
18 : :
19 : 3 : if (!p || olen < len)
20 : : /* short match */
21 : : return 0;
22 : :
23 : 3 : if (memcmp(p, s, len) != 0)
24 : : return 0;
25 : :
26 : 3 : if (p[len] == '\0')
27 : : return 1;
28 : 0 : else if (!memchr(s, '@', len) && (p[len] == '@'))
29 : : return 1;
30 : : else
31 : 0 : return 0;
32 : : }
33 : :
34 : 3 : const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
35 : : {
36 : 3 : uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt);
37 : : size_t len;
38 : : int err;
39 : : const char *s, *n;
40 : :
41 : 3 : err = fdt_ro_probe_(fdt);
42 : 3 : if (err != 0)
43 : : goto fail;
44 : :
45 : : err = -FDT_ERR_BADOFFSET;
46 : 3 : if (absoffset >= fdt_totalsize(fdt))
47 : : goto fail;
48 : 3 : len = fdt_totalsize(fdt) - absoffset;
49 : :
50 : 3 : if (fdt_magic(fdt) == FDT_MAGIC) {
51 : 3 : if (stroffset < 0)
52 : : goto fail;
53 : 3 : if (fdt_version(fdt) >= 17) {
54 : 3 : if (stroffset >= fdt_size_dt_strings(fdt))
55 : : goto fail;
56 : 3 : if ((fdt_size_dt_strings(fdt) - stroffset) < len)
57 : : len = fdt_size_dt_strings(fdt) - stroffset;
58 : : }
59 : 0 : } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
60 : 0 : if ((stroffset >= 0)
61 : 0 : || (stroffset < -fdt_size_dt_strings(fdt)))
62 : : goto fail;
63 : 0 : if ((-stroffset) < len)
64 : : len = -stroffset;
65 : : } else {
66 : : err = -FDT_ERR_INTERNAL;
67 : : goto fail;
68 : : }
69 : :
70 : 3 : s = (const char *)fdt + absoffset;
71 : 3 : n = memchr(s, '\0', len);
72 : 3 : if (!n) {
73 : : /* missing terminating NULL */
74 : : err = -FDT_ERR_TRUNCATED;
75 : : goto fail;
76 : : }
77 : :
78 : 3 : if (lenp)
79 : 3 : *lenp = n - s;
80 : 3 : return s;
81 : :
82 : : fail:
83 : 0 : if (lenp)
84 : 0 : *lenp = err;
85 : : return NULL;
86 : : }
87 : :
88 : 0 : const char *fdt_string(const void *fdt, int stroffset)
89 : : {
90 : 0 : return fdt_get_string(fdt, stroffset, NULL);
91 : : }
92 : :
93 : 3 : static int fdt_string_eq_(const void *fdt, int stroffset,
94 : : const char *s, int len)
95 : : {
96 : : int slen;
97 : 3 : const char *p = fdt_get_string(fdt, stroffset, &slen);
98 : :
99 : 3 : return p && (slen == len) && (memcmp(p, s, len) == 0);
100 : : }
101 : :
102 : 0 : int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
103 : : {
104 : : uint32_t max = 0;
105 : : int offset = -1;
106 : :
107 : : while (true) {
108 : : uint32_t value;
109 : :
110 : 0 : offset = fdt_next_node(fdt, offset, NULL);
111 : 0 : if (offset < 0) {
112 : 0 : if (offset == -FDT_ERR_NOTFOUND)
113 : : break;
114 : :
115 : 0 : return offset;
116 : : }
117 : :
118 : 0 : value = fdt_get_phandle(fdt, offset);
119 : :
120 : 0 : if (value > max)
121 : : max = value;
122 : : }
123 : :
124 : 0 : if (phandle)
125 : 0 : *phandle = max;
126 : :
127 : : return 0;
128 : : }
129 : :
130 : 0 : int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
131 : : {
132 : : uint32_t max;
133 : : int err;
134 : :
135 : 0 : err = fdt_find_max_phandle(fdt, &max);
136 : 0 : if (err < 0)
137 : : return err;
138 : :
139 : 0 : if (max == FDT_MAX_PHANDLE)
140 : : return -FDT_ERR_NOPHANDLES;
141 : :
142 : 0 : if (phandle)
143 : 0 : *phandle = max + 1;
144 : :
145 : : return 0;
146 : : }
147 : :
148 : 3 : static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
149 : : {
150 : 3 : int offset = n * sizeof(struct fdt_reserve_entry);
151 : 3 : int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
152 : :
153 : 3 : if (absoffset < fdt_off_mem_rsvmap(fdt))
154 : : return NULL;
155 : 3 : if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry))
156 : : return NULL;
157 : 3 : return fdt_mem_rsv_(fdt, n);
158 : : }
159 : :
160 : 3 : int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
161 : : {
162 : : const struct fdt_reserve_entry *re;
163 : :
164 : 3 : FDT_RO_PROBE(fdt);
165 : 3 : re = fdt_mem_rsv(fdt, n);
166 : 3 : if (!re)
167 : : return -FDT_ERR_BADOFFSET;
168 : :
169 : 3 : *address = fdt64_ld(&re->address);
170 : 3 : *size = fdt64_ld(&re->size);
171 : 3 : return 0;
172 : : }
173 : :
174 : 0 : int fdt_num_mem_rsv(const void *fdt)
175 : : {
176 : : int i;
177 : : const struct fdt_reserve_entry *re;
178 : :
179 : 0 : for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
180 : 0 : if (fdt64_ld(&re->size) == 0)
181 : 0 : return i;
182 : : }
183 : : return -FDT_ERR_TRUNCATED;
184 : : }
185 : :
186 : 3 : static int nextprop_(const void *fdt, int offset)
187 : : {
188 : : uint32_t tag;
189 : : int nextoffset;
190 : :
191 : : do {
192 : 3 : tag = fdt_next_tag(fdt, offset, &nextoffset);
193 : :
194 : 3 : switch (tag) {
195 : : case FDT_END:
196 : 0 : if (nextoffset >= 0)
197 : : return -FDT_ERR_BADSTRUCTURE;
198 : : else
199 : 0 : return nextoffset;
200 : :
201 : : case FDT_PROP:
202 : 3 : return offset;
203 : : }
204 : 3 : offset = nextoffset;
205 : 3 : } while (tag == FDT_NOP);
206 : :
207 : : return -FDT_ERR_NOTFOUND;
208 : : }
209 : :
210 : 3 : int fdt_subnode_offset_namelen(const void *fdt, int offset,
211 : : const char *name, int namelen)
212 : : {
213 : : int depth;
214 : :
215 : 3 : FDT_RO_PROBE(fdt);
216 : :
217 : 3 : for (depth = 0;
218 : 3 : (offset >= 0) && (depth >= 0);
219 : 3 : offset = fdt_next_node(fdt, offset, &depth))
220 : 3 : if ((depth == 1)
221 : 3 : && fdt_nodename_eq_(fdt, offset, name, namelen))
222 : 3 : return offset;
223 : :
224 : 0 : if (depth < 0)
225 : : return -FDT_ERR_NOTFOUND;
226 : 0 : return offset; /* error */
227 : : }
228 : :
229 : 3 : int fdt_subnode_offset(const void *fdt, int parentoffset,
230 : : const char *name)
231 : : {
232 : 3 : return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
233 : : }
234 : :
235 : 0 : int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
236 : : {
237 : 0 : const char *end = path + namelen;
238 : : const char *p = path;
239 : : int offset = 0;
240 : :
241 : 0 : FDT_RO_PROBE(fdt);
242 : :
243 : : /* see if we have an alias */
244 : 0 : if (*path != '/') {
245 : 0 : const char *q = memchr(path, '/', end - p);
246 : :
247 : 0 : if (!q)
248 : : q = end;
249 : :
250 : 0 : p = fdt_get_alias_namelen(fdt, p, q - p);
251 : 0 : if (!p)
252 : : return -FDT_ERR_BADPATH;
253 : 0 : offset = fdt_path_offset(fdt, p);
254 : :
255 : : p = q;
256 : : }
257 : :
258 : 0 : while (p < end) {
259 : : const char *q;
260 : :
261 : 0 : while (*p == '/') {
262 : 0 : p++;
263 : 0 : if (p == end)
264 : 0 : return offset;
265 : : }
266 : 0 : q = memchr(p, '/', end - p);
267 : 0 : if (! q)
268 : : q = end;
269 : :
270 : 0 : offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
271 : 0 : if (offset < 0)
272 : 0 : return offset;
273 : :
274 : : p = q;
275 : : }
276 : :
277 : 0 : return offset;
278 : : }
279 : :
280 : 0 : int fdt_path_offset(const void *fdt, const char *path)
281 : : {
282 : 0 : return fdt_path_offset_namelen(fdt, path, strlen(path));
283 : : }
284 : :
285 : 3 : const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
286 : : {
287 : : const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
288 : : const char *nameptr;
289 : : int err;
290 : :
291 : 3 : if (((err = fdt_ro_probe_(fdt)) != 0)
292 : 3 : || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
293 : : goto fail;
294 : :
295 : 3 : nameptr = nh->name;
296 : :
297 : 3 : if (fdt_version(fdt) < 0x10) {
298 : : /*
299 : : * For old FDT versions, match the naming conventions of V16:
300 : : * give only the leaf name (after all /). The actual tree
301 : : * contents are loosely checked.
302 : : */
303 : : const char *leaf;
304 : 0 : leaf = strrchr(nameptr, '/');
305 : 0 : if (leaf == NULL) {
306 : : err = -FDT_ERR_BADSTRUCTURE;
307 : : goto fail;
308 : : }
309 : 0 : nameptr = leaf+1;
310 : : }
311 : :
312 : 3 : if (len)
313 : 3 : *len = strlen(nameptr);
314 : :
315 : 3 : return nameptr;
316 : :
317 : : fail:
318 : 0 : if (len)
319 : 0 : *len = err;
320 : : return NULL;
321 : : }
322 : :
323 : 3 : int fdt_first_property_offset(const void *fdt, int nodeoffset)
324 : : {
325 : : int offset;
326 : :
327 : 3 : if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
328 : : return offset;
329 : :
330 : 3 : return nextprop_(fdt, offset);
331 : : }
332 : :
333 : 3 : int fdt_next_property_offset(const void *fdt, int offset)
334 : : {
335 : 3 : if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
336 : : return offset;
337 : :
338 : 3 : return nextprop_(fdt, offset);
339 : : }
340 : :
341 : 3 : static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
342 : : int offset,
343 : : int *lenp)
344 : : {
345 : : int err;
346 : : const struct fdt_property *prop;
347 : :
348 : 3 : if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
349 : 0 : if (lenp)
350 : 0 : *lenp = err;
351 : : return NULL;
352 : : }
353 : :
354 : : prop = fdt_offset_ptr_(fdt, offset);
355 : :
356 : 3 : if (lenp)
357 : 3 : *lenp = fdt32_ld(&prop->len);
358 : :
359 : 3 : return prop;
360 : : }
361 : :
362 : 0 : const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
363 : : int offset,
364 : : int *lenp)
365 : : {
366 : : /* Prior to version 16, properties may need realignment
367 : : * and this API does not work. fdt_getprop_*() will, however. */
368 : :
369 : 0 : if (fdt_version(fdt) < 0x10) {
370 : 0 : if (lenp)
371 : 0 : *lenp = -FDT_ERR_BADVERSION;
372 : : return NULL;
373 : : }
374 : :
375 : 0 : return fdt_get_property_by_offset_(fdt, offset, lenp);
376 : : }
377 : :
378 : 3 : static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
379 : : int offset,
380 : : const char *name,
381 : : int namelen,
382 : : int *lenp,
383 : : int *poffset)
384 : : {
385 : 3 : for (offset = fdt_first_property_offset(fdt, offset);
386 : : (offset >= 0);
387 : 3 : (offset = fdt_next_property_offset(fdt, offset))) {
388 : : const struct fdt_property *prop;
389 : :
390 : 3 : if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) {
391 : : offset = -FDT_ERR_INTERNAL;
392 : : break;
393 : : }
394 : 3 : if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff),
395 : : name, namelen)) {
396 : 3 : if (poffset)
397 : 3 : *poffset = offset;
398 : 3 : return prop;
399 : : }
400 : : }
401 : :
402 : 3 : if (lenp)
403 : 3 : *lenp = offset;
404 : : return NULL;
405 : : }
406 : :
407 : :
408 : 0 : const struct fdt_property *fdt_get_property_namelen(const void *fdt,
409 : : int offset,
410 : : const char *name,
411 : : int namelen, int *lenp)
412 : : {
413 : : /* Prior to version 16, properties may need realignment
414 : : * and this API does not work. fdt_getprop_*() will, however. */
415 : 0 : if (fdt_version(fdt) < 0x10) {
416 : 0 : if (lenp)
417 : 0 : *lenp = -FDT_ERR_BADVERSION;
418 : : return NULL;
419 : : }
420 : :
421 : 0 : return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
422 : : NULL);
423 : : }
424 : :
425 : :
426 : 0 : const struct fdt_property *fdt_get_property(const void *fdt,
427 : : int nodeoffset,
428 : : const char *name, int *lenp)
429 : : {
430 : 0 : return fdt_get_property_namelen(fdt, nodeoffset, name,
431 : 0 : strlen(name), lenp);
432 : : }
433 : :
434 : 3 : const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
435 : : const char *name, int namelen, int *lenp)
436 : : {
437 : : int poffset;
438 : : const struct fdt_property *prop;
439 : :
440 : 3 : prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
441 : : &poffset);
442 : 3 : if (!prop)
443 : : return NULL;
444 : :
445 : : /* Handle realignment */
446 : 3 : if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 &&
447 : : fdt32_ld(&prop->len) >= 8)
448 : 0 : return prop->data + 4;
449 : 3 : return prop->data;
450 : : }
451 : :
452 : 3 : const void *fdt_getprop_by_offset(const void *fdt, int offset,
453 : : const char **namep, int *lenp)
454 : : {
455 : : const struct fdt_property *prop;
456 : :
457 : 3 : prop = fdt_get_property_by_offset_(fdt, offset, lenp);
458 : 3 : if (!prop)
459 : : return NULL;
460 : 3 : if (namep) {
461 : : const char *name;
462 : : int namelen;
463 : 3 : name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
464 : : &namelen);
465 : 3 : if (!name) {
466 : 0 : if (lenp)
467 : 0 : *lenp = namelen;
468 : 0 : return NULL;
469 : : }
470 : 3 : *namep = name;
471 : : }
472 : :
473 : : /* Handle realignment */
474 : 3 : if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
475 : : fdt32_ld(&prop->len) >= 8)
476 : 0 : return prop->data + 4;
477 : 3 : return prop->data;
478 : : }
479 : :
480 : 3 : const void *fdt_getprop(const void *fdt, int nodeoffset,
481 : : const char *name, int *lenp)
482 : : {
483 : 3 : return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
484 : : }
485 : :
486 : 0 : uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
487 : : {
488 : : const fdt32_t *php;
489 : : int len;
490 : :
491 : : /* FIXME: This is a bit sub-optimal, since we potentially scan
492 : : * over all the properties twice. */
493 : 0 : php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
494 : 0 : if (!php || (len != sizeof(*php))) {
495 : 0 : php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
496 : 0 : if (!php || (len != sizeof(*php)))
497 : : return 0;
498 : : }
499 : :
500 : 0 : return fdt32_ld(php);
501 : : }
502 : :
503 : 0 : const char *fdt_get_alias_namelen(const void *fdt,
504 : : const char *name, int namelen)
505 : : {
506 : : int aliasoffset;
507 : :
508 : 0 : aliasoffset = fdt_path_offset(fdt, "/aliases");
509 : 0 : if (aliasoffset < 0)
510 : : return NULL;
511 : :
512 : 0 : return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
513 : : }
514 : :
515 : 0 : const char *fdt_get_alias(const void *fdt, const char *name)
516 : : {
517 : 0 : return fdt_get_alias_namelen(fdt, name, strlen(name));
518 : : }
519 : :
520 : 0 : int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
521 : : {
522 : : int pdepth = 0, p = 0;
523 : : int offset, depth, namelen;
524 : : const char *name;
525 : :
526 : 0 : FDT_RO_PROBE(fdt);
527 : :
528 : 0 : if (buflen < 2)
529 : : return -FDT_ERR_NOSPACE;
530 : :
531 : 0 : for (offset = 0, depth = 0;
532 : 0 : (offset >= 0) && (offset <= nodeoffset);
533 : 0 : offset = fdt_next_node(fdt, offset, &depth)) {
534 : 0 : while (pdepth > depth) {
535 : : do {
536 : 0 : p--;
537 : 0 : } while (buf[p-1] != '/');
538 : 0 : pdepth--;
539 : : }
540 : :
541 : 0 : if (pdepth >= depth) {
542 : 0 : name = fdt_get_name(fdt, offset, &namelen);
543 : 0 : if (!name)
544 : 0 : return namelen;
545 : 0 : if ((p + namelen + 1) <= buflen) {
546 : 0 : memcpy(buf + p, name, namelen);
547 : : p += namelen;
548 : 0 : buf[p++] = '/';
549 : 0 : pdepth++;
550 : : }
551 : : }
552 : :
553 : 0 : if (offset == nodeoffset) {
554 : 0 : if (pdepth < (depth + 1))
555 : : return -FDT_ERR_NOSPACE;
556 : :
557 : 0 : if (p > 1) /* special case so that root path is "/", not "" */
558 : 0 : p--;
559 : 0 : buf[p] = '\0';
560 : 0 : return 0;
561 : : }
562 : : }
563 : :
564 : 0 : if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
565 : : return -FDT_ERR_BADOFFSET;
566 : 0 : else if (offset == -FDT_ERR_BADOFFSET)
567 : : return -FDT_ERR_BADSTRUCTURE;
568 : :
569 : 0 : return offset; /* error from fdt_next_node() */
570 : : }
571 : :
572 : 0 : int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
573 : : int supernodedepth, int *nodedepth)
574 : : {
575 : : int offset, depth;
576 : : int supernodeoffset = -FDT_ERR_INTERNAL;
577 : :
578 : 0 : FDT_RO_PROBE(fdt);
579 : :
580 : 0 : if (supernodedepth < 0)
581 : : return -FDT_ERR_NOTFOUND;
582 : :
583 : 0 : for (offset = 0, depth = 0;
584 : 0 : (offset >= 0) && (offset <= nodeoffset);
585 : 0 : offset = fdt_next_node(fdt, offset, &depth)) {
586 : 0 : if (depth == supernodedepth)
587 : : supernodeoffset = offset;
588 : :
589 : 0 : if (offset == nodeoffset) {
590 : 0 : if (nodedepth)
591 : 0 : *nodedepth = depth;
592 : :
593 : 0 : if (supernodedepth > depth)
594 : : return -FDT_ERR_NOTFOUND;
595 : : else
596 : 0 : return supernodeoffset;
597 : : }
598 : : }
599 : :
600 : 0 : if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
601 : : return -FDT_ERR_BADOFFSET;
602 : 0 : else if (offset == -FDT_ERR_BADOFFSET)
603 : : return -FDT_ERR_BADSTRUCTURE;
604 : :
605 : 0 : return offset; /* error from fdt_next_node() */
606 : : }
607 : :
608 : 0 : int fdt_node_depth(const void *fdt, int nodeoffset)
609 : : {
610 : : int nodedepth;
611 : : int err;
612 : :
613 : 0 : err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
614 : 0 : if (err)
615 : 0 : return (err < 0) ? err : -FDT_ERR_INTERNAL;
616 : 0 : return nodedepth;
617 : : }
618 : :
619 : 0 : int fdt_parent_offset(const void *fdt, int nodeoffset)
620 : : {
621 : 0 : int nodedepth = fdt_node_depth(fdt, nodeoffset);
622 : :
623 : 0 : if (nodedepth < 0)
624 : : return nodedepth;
625 : 0 : return fdt_supernode_atdepth_offset(fdt, nodeoffset,
626 : : nodedepth - 1, NULL);
627 : : }
628 : :
629 : 0 : int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
630 : : const char *propname,
631 : : const void *propval, int proplen)
632 : : {
633 : : int offset;
634 : : const void *val;
635 : : int len;
636 : :
637 : 0 : FDT_RO_PROBE(fdt);
638 : :
639 : : /* FIXME: The algorithm here is pretty horrible: we scan each
640 : : * property of a node in fdt_getprop(), then if that didn't
641 : : * find what we want, we scan over them again making our way
642 : : * to the next node. Still it's the easiest to implement
643 : : * approach; performance can come later. */
644 : 0 : for (offset = fdt_next_node(fdt, startoffset, NULL);
645 : : offset >= 0;
646 : 0 : offset = fdt_next_node(fdt, offset, NULL)) {
647 : 0 : val = fdt_getprop(fdt, offset, propname, &len);
648 : 0 : if (val && (len == proplen)
649 : 0 : && (memcmp(val, propval, len) == 0))
650 : 0 : return offset;
651 : : }
652 : :
653 : 0 : return offset; /* error from fdt_next_node() */
654 : : }
655 : :
656 : 0 : int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
657 : : {
658 : : int offset;
659 : :
660 : 0 : if ((phandle == 0) || (phandle == -1))
661 : : return -FDT_ERR_BADPHANDLE;
662 : :
663 : 0 : FDT_RO_PROBE(fdt);
664 : :
665 : : /* FIXME: The algorithm here is pretty horrible: we
666 : : * potentially scan each property of a node in
667 : : * fdt_get_phandle(), then if that didn't find what
668 : : * we want, we scan over them again making our way to the next
669 : : * node. Still it's the easiest to implement approach;
670 : : * performance can come later. */
671 : 0 : for (offset = fdt_next_node(fdt, -1, NULL);
672 : : offset >= 0;
673 : 0 : offset = fdt_next_node(fdt, offset, NULL)) {
674 : 0 : if (fdt_get_phandle(fdt, offset) == phandle)
675 : 0 : return offset;
676 : : }
677 : :
678 : 0 : return offset; /* error from fdt_next_node() */
679 : : }
680 : :
681 : 0 : int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
682 : : {
683 : 0 : int len = strlen(str);
684 : : const char *p;
685 : :
686 : 0 : while (listlen >= len) {
687 : 0 : if (memcmp(str, strlist, len+1) == 0)
688 : : return 1;
689 : 0 : p = memchr(strlist, '\0', listlen);
690 : 0 : if (!p)
691 : : return 0; /* malformed strlist.. */
692 : 0 : listlen -= (p-strlist) + 1;
693 : 0 : strlist = p + 1;
694 : : }
695 : : return 0;
696 : : }
697 : :
698 : 0 : int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
699 : : {
700 : : const char *list, *end;
701 : : int length, count = 0;
702 : :
703 : 0 : list = fdt_getprop(fdt, nodeoffset, property, &length);
704 : 0 : if (!list)
705 : 0 : return length;
706 : :
707 : 0 : end = list + length;
708 : :
709 : 0 : while (list < end) {
710 : 0 : length = strnlen(list, end - list) + 1;
711 : :
712 : : /* Abort if the last string isn't properly NUL-terminated. */
713 : 0 : if (list + length > end)
714 : : return -FDT_ERR_BADVALUE;
715 : :
716 : : list += length;
717 : 0 : count++;
718 : : }
719 : :
720 : 0 : return count;
721 : : }
722 : :
723 : 0 : int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
724 : : const char *string)
725 : : {
726 : : int length, len, idx = 0;
727 : : const char *list, *end;
728 : :
729 : 0 : list = fdt_getprop(fdt, nodeoffset, property, &length);
730 : 0 : if (!list)
731 : 0 : return length;
732 : :
733 : 0 : len = strlen(string) + 1;
734 : 0 : end = list + length;
735 : :
736 : 0 : while (list < end) {
737 : 0 : length = strnlen(list, end - list) + 1;
738 : :
739 : : /* Abort if the last string isn't properly NUL-terminated. */
740 : 0 : if (list + length > end)
741 : : return -FDT_ERR_BADVALUE;
742 : :
743 : 0 : if (length == len && memcmp(list, string, length) == 0)
744 : 0 : return idx;
745 : :
746 : : list += length;
747 : 0 : idx++;
748 : : }
749 : :
750 : : return -FDT_ERR_NOTFOUND;
751 : : }
752 : :
753 : 0 : const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
754 : : const char *property, int idx,
755 : : int *lenp)
756 : : {
757 : : const char *list, *end;
758 : : int length;
759 : :
760 : 0 : list = fdt_getprop(fdt, nodeoffset, property, &length);
761 : 0 : if (!list) {
762 : 0 : if (lenp)
763 : 0 : *lenp = length;
764 : :
765 : : return NULL;
766 : : }
767 : :
768 : 0 : end = list + length;
769 : :
770 : 0 : while (list < end) {
771 : 0 : length = strnlen(list, end - list) + 1;
772 : :
773 : : /* Abort if the last string isn't properly NUL-terminated. */
774 : 0 : if (list + length > end) {
775 : 0 : if (lenp)
776 : 0 : *lenp = -FDT_ERR_BADVALUE;
777 : :
778 : : return NULL;
779 : : }
780 : :
781 : 0 : if (idx == 0) {
782 : 0 : if (lenp)
783 : 0 : *lenp = length - 1;
784 : :
785 : 0 : return list;
786 : : }
787 : :
788 : : list += length;
789 : 0 : idx--;
790 : : }
791 : :
792 : 0 : if (lenp)
793 : 0 : *lenp = -FDT_ERR_NOTFOUND;
794 : :
795 : : return NULL;
796 : : }
797 : :
798 : 0 : int fdt_node_check_compatible(const void *fdt, int nodeoffset,
799 : : const char *compatible)
800 : : {
801 : : const void *prop;
802 : : int len;
803 : :
804 : 0 : prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
805 : 0 : if (!prop)
806 : 0 : return len;
807 : :
808 : 0 : return !fdt_stringlist_contains(prop, len, compatible);
809 : : }
810 : :
811 : 0 : int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
812 : : const char *compatible)
813 : : {
814 : : int offset, err;
815 : :
816 : 0 : FDT_RO_PROBE(fdt);
817 : :
818 : : /* FIXME: The algorithm here is pretty horrible: we scan each
819 : : * property of a node in fdt_node_check_compatible(), then if
820 : : * that didn't find what we want, we scan over them again
821 : : * making our way to the next node. Still it's the easiest to
822 : : * implement approach; performance can come later. */
823 : 0 : for (offset = fdt_next_node(fdt, startoffset, NULL);
824 : : offset >= 0;
825 : 0 : offset = fdt_next_node(fdt, offset, NULL)) {
826 : 0 : err = fdt_node_check_compatible(fdt, offset, compatible);
827 : 0 : if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
828 : 0 : return err;
829 : 0 : else if (err == 0)
830 : 0 : return offset;
831 : : }
832 : :
833 : 0 : return offset; /* error from fdt_next_node() */
834 : : }
835 : :
836 : 0 : int fdt_check_full(const void *fdt, size_t bufsize)
837 : : {
838 : : int err;
839 : : int num_memrsv;
840 : 0 : int offset, nextoffset = 0;
841 : : uint32_t tag;
842 : : unsigned depth = 0;
843 : : const void *prop;
844 : : const char *propname;
845 : :
846 : 0 : if (bufsize < FDT_V1_SIZE)
847 : : return -FDT_ERR_TRUNCATED;
848 : 0 : err = fdt_check_header(fdt);
849 : 0 : if (err != 0)
850 : : return err;
851 : 0 : if (bufsize < fdt_totalsize(fdt))
852 : : return -FDT_ERR_TRUNCATED;
853 : :
854 : 0 : num_memrsv = fdt_num_mem_rsv(fdt);
855 : 0 : if (num_memrsv < 0)
856 : : return num_memrsv;
857 : :
858 : : while (1) {
859 : 0 : offset = nextoffset;
860 : 0 : tag = fdt_next_tag(fdt, offset, &nextoffset);
861 : :
862 : 0 : if (nextoffset < 0)
863 : 0 : return nextoffset;
864 : :
865 : 0 : switch (tag) {
866 : : case FDT_NOP:
867 : : break;
868 : :
869 : : case FDT_END:
870 : 0 : if (depth != 0)
871 : : return -FDT_ERR_BADSTRUCTURE;
872 : 0 : return 0;
873 : :
874 : : case FDT_BEGIN_NODE:
875 : 0 : depth++;
876 : 0 : if (depth > INT_MAX)
877 : : return -FDT_ERR_BADSTRUCTURE;
878 : : break;
879 : :
880 : : case FDT_END_NODE:
881 : 0 : if (depth == 0)
882 : : return -FDT_ERR_BADSTRUCTURE;
883 : 0 : depth--;
884 : 0 : break;
885 : :
886 : : case FDT_PROP:
887 : 0 : prop = fdt_getprop_by_offset(fdt, offset, &propname,
888 : : &err);
889 : 0 : if (!prop)
890 : 0 : return err;
891 : : break;
892 : :
893 : : default:
894 : : return -FDT_ERR_INTERNAL;
895 : : }
896 : : }
897 : : }
|