source: rtems/cpukit/dtc/libfdt/fdt_ro.c

Last change on this file was 6f794359, checked in by LoveSy <shana@…>, on 12/15/21 at 09:30:11

Fix a UB when fdt_get_string return null

When fdt_get_string return null, namep is not correctly reset.
From the document of fdt_getprop_by_offset, the parameter namep will
be always overwritten (that is, it will be overwritten without exception
of error occurance).

As for the caller (like
https://github.com/topjohnwu/Magisk/blob/e097c097feb881f6097b6d1dc346f310bc92f5d6/native/jni/magiskboot/dtb.cpp#L42),
the code may be like:
`cpp
size_t size;
const char *name;
auto *value = fdt_getprop_by_offset(fdt, prop, &name, &size);
`
and if value == nullptr, size is also be overwritten correctly but
name is not, which is quite inconsistent.

This commit makes sure name and size behavior consistently (reset to
reasonable value) when error occurs.

Signed-off-by: LoveSy? <shana@…>
Signed-off-by: David Gibson <david@…>

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