1
2
3
4
5
6#include "libfdt_env.h"
7
8#include <fdt.h>
9#include <libfdt.h>
10
11#include "libfdt_internal.h"
12
13static 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
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
34const 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
75 err = -FDT_ERR_TRUNCATED;
76 goto fail;
77 }
78
79 if (lenp)
80 *lenp = n - s;
81 return s;
82
83fail:
84 if (lenp)
85 *lenp = err;
86 return NULL;
87}
88
89const char *fdt_string(const void *fdt, int stroffset)
90{
91 return fdt_get_string(fdt, stroffset, NULL);
92}
93
94static 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
103int 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
131int 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
149static 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
161int 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
175int 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
187static 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
211int 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;
228}
229
230int 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
236int 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
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
281int fdt_path_offset(const void *fdt, const char *path)
282{
283 return fdt_path_offset_namelen(fdt, path, strlen(path));
284}
285
286const 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
301
302
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
324int 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
334int 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
342static 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
363const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
364 int offset,
365 int *lenp)
366{
367
368
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
379static 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
409const struct fdt_property *fdt_get_property_namelen(const void *fdt,
410 int offset,
411 const char *name,
412 int namelen, int *lenp)
413{
414
415
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
427const 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
435const 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
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
453const 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
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
481const 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
487uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
488{
489 const fdt32_t *php;
490 int len;
491
492
493
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
504const 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
516const char *fdt_get_alias(const void *fdt, const char *name)
517{
518 return fdt_get_alias_namelen(fdt, name, strlen(name));
519}
520
521int 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)
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;
571}
572
573int 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;
607}
608
609int 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
620int 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
630int 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
641
642
643
644
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;
655}
656
657int 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
667
668
669
670
671
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;
680}
681
682int 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;
693 listlen -= (p-strlist) + 1;
694 strlist = p + 1;
695 }
696 return 0;
697}
698
699int 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
714 if (list + length > end)
715 return -FDT_ERR_BADVALUE;
716
717 list += length;
718 count++;
719 }
720
721 return count;
722}
723
724int 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
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
754const 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
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
799int 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
812int 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
820
821
822
823
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;
835}
836
837int 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