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