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