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