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