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