OSDN Git Service

scripts/dtc: Update to upstream version v1.6.0-2-g87a656ae5ff9
[tomoyo/tomoyo-test1.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         int32_t totalsize;
37         uint32_t absoffset;
38         size_t len;
39         int err;
40         const char *s, *n;
41
42         if (can_assume(VALID_INPUT)) {
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 (can_assume(LATEST) || 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 (!can_assume(VALID_INPUT)) {
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 (!can_assume(VALID_INPUT) && !re)
180                 return -FDT_ERR_BADOFFSET;
181
182         *address = fdt64_ld(&re->address);
183         *size = fdt64_ld(&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_ld(&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 (((err = fdt_ro_probe_(fdt)) < 0)
305             || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
306                         goto fail;
307
308         nameptr = nh->name;
309
310         if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
311                 /*
312                  * For old FDT versions, match the naming conventions of V16:
313                  * give only the leaf name (after all /). The actual tree
314                  * contents are loosely checked.
315                  */
316                 const char *leaf;
317                 leaf = strrchr(nameptr, '/');
318                 if (leaf == NULL) {
319                         err = -FDT_ERR_BADSTRUCTURE;
320                         goto fail;
321                 }
322                 nameptr = leaf+1;
323         }
324
325         if (len)
326                 *len = strlen(nameptr);
327
328         return nameptr;
329
330  fail:
331         if (len)
332                 *len = err;
333         return NULL;
334 }
335
336 int fdt_first_property_offset(const void *fdt, int nodeoffset)
337 {
338         int offset;
339
340         if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
341                 return offset;
342
343         return nextprop_(fdt, offset);
344 }
345
346 int fdt_next_property_offset(const void *fdt, int offset)
347 {
348         if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
349                 return offset;
350
351         return nextprop_(fdt, offset);
352 }
353
354 static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
355                                                               int offset,
356                                                               int *lenp)
357 {
358         int err;
359         const struct fdt_property *prop;
360
361         if (!can_assume(VALID_INPUT) &&
362             (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_ld(&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 (!can_assume(LATEST) && 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 (!can_assume(LIBFDT_FLAWLESS) && !prop) {
406                         offset = -FDT_ERR_INTERNAL;
407                         break;
408                 }
409                 if (fdt_string_eq_(fdt, fdt32_ld(&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 (!can_assume(LATEST) && 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 (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
462             (poffset + sizeof(*prop)) % 8 && fdt32_ld(&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 (!can_assume(VALID_INPUT)) {
480                         name = fdt_get_string(fdt, fdt32_ld(&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_ld(&prop->nameoff));
490                 }
491         }
492
493         /* Handle realignment */
494         if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
495             (offset + sizeof(*prop)) % 8 && fdt32_ld(&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_ld(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 (!can_assume(VALID_INPUT)) {
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 (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
638                         -FDT_ERR_INTERNAL;
639         return nodedepth;
640 }
641
642 int fdt_parent_offset(const void *fdt, int nodeoffset)
643 {
644         int nodedepth = fdt_node_depth(fdt, nodeoffset);
645
646         if (nodedepth < 0)
647                 return nodedepth;
648         return fdt_supernode_atdepth_offset(fdt, nodeoffset,
649                                             nodedepth - 1, NULL);
650 }
651
652 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
653                                   const char *propname,
654                                   const void *propval, int proplen)
655 {
656         int offset;
657         const void *val;
658         int len;
659
660         FDT_RO_PROBE(fdt);
661
662         /* FIXME: The algorithm here is pretty horrible: we scan each
663          * property of a node in fdt_getprop(), then if that didn't
664          * find what we want, we scan over them again making our way
665          * to the next node.  Still it's the easiest to implement
666          * approach; performance can come later. */
667         for (offset = fdt_next_node(fdt, startoffset, NULL);
668              offset >= 0;
669              offset = fdt_next_node(fdt, offset, NULL)) {
670                 val = fdt_getprop(fdt, offset, propname, &len);
671                 if (val && (len == proplen)
672                     && (memcmp(val, propval, len) == 0))
673                         return offset;
674         }
675
676         return offset; /* error from fdt_next_node() */
677 }
678
679 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
680 {
681         int offset;
682
683         if ((phandle == 0) || (phandle == -1))
684                 return -FDT_ERR_BADPHANDLE;
685
686         FDT_RO_PROBE(fdt);
687
688         /* FIXME: The algorithm here is pretty horrible: we
689          * potentially scan each property of a node in
690          * fdt_get_phandle(), then if that didn't find what
691          * we want, we scan over them again making our way to the next
692          * node.  Still it's the easiest to implement approach;
693          * performance can come later. */
694         for (offset = fdt_next_node(fdt, -1, NULL);
695              offset >= 0;
696              offset = fdt_next_node(fdt, offset, NULL)) {
697                 if (fdt_get_phandle(fdt, offset) == phandle)
698                         return offset;
699         }
700
701         return offset; /* error from fdt_next_node() */
702 }
703
704 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
705 {
706         int len = strlen(str);
707         const char *p;
708
709         while (listlen >= len) {
710                 if (memcmp(str, strlist, len+1) == 0)
711                         return 1;
712                 p = memchr(strlist, '\0', listlen);
713                 if (!p)
714                         return 0; /* malformed strlist.. */
715                 listlen -= (p-strlist) + 1;
716                 strlist = p + 1;
717         }
718         return 0;
719 }
720
721 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
722 {
723         const char *list, *end;
724         int length, count = 0;
725
726         list = fdt_getprop(fdt, nodeoffset, property, &length);
727         if (!list)
728                 return length;
729
730         end = list + length;
731
732         while (list < end) {
733                 length = strnlen(list, end - list) + 1;
734
735                 /* Abort if the last string isn't properly NUL-terminated. */
736                 if (list + length > end)
737                         return -FDT_ERR_BADVALUE;
738
739                 list += length;
740                 count++;
741         }
742
743         return count;
744 }
745
746 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
747                           const char *string)
748 {
749         int length, len, idx = 0;
750         const char *list, *end;
751
752         list = fdt_getprop(fdt, nodeoffset, property, &length);
753         if (!list)
754                 return length;
755
756         len = strlen(string) + 1;
757         end = list + length;
758
759         while (list < end) {
760                 length = strnlen(list, end - list) + 1;
761
762                 /* Abort if the last string isn't properly NUL-terminated. */
763                 if (list + length > end)
764                         return -FDT_ERR_BADVALUE;
765
766                 if (length == len && memcmp(list, string, length) == 0)
767                         return idx;
768
769                 list += length;
770                 idx++;
771         }
772
773         return -FDT_ERR_NOTFOUND;
774 }
775
776 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
777                                const char *property, int idx,
778                                int *lenp)
779 {
780         const char *list, *end;
781         int length;
782
783         list = fdt_getprop(fdt, nodeoffset, property, &length);
784         if (!list) {
785                 if (lenp)
786                         *lenp = length;
787
788                 return NULL;
789         }
790
791         end = list + length;
792
793         while (list < end) {
794                 length = strnlen(list, end - list) + 1;
795
796                 /* Abort if the last string isn't properly NUL-terminated. */
797                 if (list + length > end) {
798                         if (lenp)
799                                 *lenp = -FDT_ERR_BADVALUE;
800
801                         return NULL;
802                 }
803
804                 if (idx == 0) {
805                         if (lenp)
806                                 *lenp = length - 1;
807
808                         return list;
809                 }
810
811                 list += length;
812                 idx--;
813         }
814
815         if (lenp)
816                 *lenp = -FDT_ERR_NOTFOUND;
817
818         return NULL;
819 }
820
821 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
822                               const char *compatible)
823 {
824         const void *prop;
825         int len;
826
827         prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
828         if (!prop)
829                 return len;
830
831         return !fdt_stringlist_contains(prop, len, compatible);
832 }
833
834 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
835                                   const char *compatible)
836 {
837         int offset, err;
838
839         FDT_RO_PROBE(fdt);
840
841         /* FIXME: The algorithm here is pretty horrible: we scan each
842          * property of a node in fdt_node_check_compatible(), then if
843          * that didn't find what we want, we scan over them again
844          * making our way to the next node.  Still it's the easiest to
845          * implement approach; performance can come later. */
846         for (offset = fdt_next_node(fdt, startoffset, NULL);
847              offset >= 0;
848              offset = fdt_next_node(fdt, offset, NULL)) {
849                 err = fdt_node_check_compatible(fdt, offset, compatible);
850                 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
851                         return err;
852                 else if (err == 0)
853                         return offset;
854         }
855
856         return offset; /* error from fdt_next_node() */
857 }