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

Last change on this file since c81f432 was c81f432, checked in by David Gibson <david@…>, on Mar 9, 2018 at 12:28:56 PM

libfdt: Safer access to memory reservations

fdt_num_mem_rsv() and fdt_get_mem_rsv() currently don't sanity check their
parameters, or the memory reserve section offset in the header. That means
that on a corrupted blob they could access outside of the range of memory
that they should.

This improves their safety checking, meaning they shouldn't access outside
the blob's bounds, even if its contents are badly corrupted.

Signed-off-by: David Gibson <david@…>
Tested-by: Alexey Kardashevskiy <aik@…>
Reviewed-by: Alexey Kardashevskiy <aik@…>
Reviewed-by: Simon Glass <sjg@…>

  • Property mode set to 100644
File size: 19.4 KB
Line 
1/*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
4 *
5 * libfdt is dual licensed: you can use it either under the terms of
6 * the GPL, or the BSD license, at your option.
7 *
8 *  a) This library is free software; you can redistribute it and/or
9 *     modify it under the terms of the GNU General Public License as
10 *     published by the Free Software Foundation; either version 2 of the
11 *     License, or (at your option) any later version.
12 *
13 *     This library is distributed in the hope that it will be useful,
14 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *     GNU General Public License for more details.
17 *
18 *     You should have received a copy of the GNU General Public
19 *     License along with this library; if not, write to the Free
20 *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 *     MA 02110-1301 USA
22 *
23 * Alternatively,
24 *
25 *  b) Redistribution and use in source and binary forms, with or
26 *     without modification, are permitted provided that the following
27 *     conditions are met:
28 *
29 *     1. Redistributions of source code must retain the above
30 *        copyright notice, this list of conditions and the following
31 *        disclaimer.
32 *     2. Redistributions in binary form must reproduce the above
33 *        copyright notice, this list of conditions and the following
34 *        disclaimer in the documentation and/or other materials
35 *        provided with the distribution.
36 *
37 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39 *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40 *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41 *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42 *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48 *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49 *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 */
51#include "libfdt_env.h"
52
53#include <fdt.h>
54#include <libfdt.h>
55
56#include "libfdt_internal.h"
57
58static int fdt_nodename_eq_(const void *fdt, int offset,
59                            const char *s, int len)
60{
61        int olen;
62        const char *p = fdt_get_name(fdt, offset, &olen);
63
64        if (!p || olen < len)
65                /* short match */
66                return 0;
67
68        if (memcmp(p, s, len) != 0)
69                return 0;
70
71        if (p[len] == '\0')
72                return 1;
73        else if (!memchr(s, '@', len) && (p[len] == '@'))
74                return 1;
75        else
76                return 0;
77}
78
79const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
80{
81        uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt);
82        size_t len;
83        int err;
84        const char *s, *n;
85
86        err = fdt_ro_probe_(fdt);
87        if (err != 0)
88                goto fail;
89
90        err = -FDT_ERR_BADOFFSET;
91        if (absoffset >= fdt_totalsize(fdt))
92                goto fail;
93        len = fdt_totalsize(fdt) - absoffset;
94
95        if (fdt_magic(fdt) == FDT_MAGIC) {
96                if (stroffset < 0)
97                        goto fail;
98                if (fdt_version(fdt) >= 17) {
99                        if (stroffset >= fdt_size_dt_strings(fdt))
100                                goto fail;
101                        if ((fdt_size_dt_strings(fdt) - stroffset) < len)
102                                len = fdt_size_dt_strings(fdt) - stroffset;
103                }
104        } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
105                if ((stroffset >= 0)
106                    || (stroffset < -fdt_size_dt_strings(fdt)))
107                        goto fail;
108                if ((-stroffset) < len)
109                        len = -stroffset;
110        } else {
111                err = -FDT_ERR_INTERNAL;
112                goto fail;
113        }
114
115        s = (const char *)fdt + absoffset;
116        n = memchr(s, '\0', len);
117        if (!n) {
118                /* missing terminating NULL */
119                err = -FDT_ERR_TRUNCATED;
120                goto fail;
121        }
122
123        if (lenp)
124                *lenp = n - s;
125        return s;
126
127fail:
128        if (lenp)
129                *lenp = err;
130        return NULL;
131}
132
133const char *fdt_string(const void *fdt, int stroffset)
134{
135        return fdt_get_string(fdt, stroffset, NULL);
136}
137
138static int fdt_string_eq_(const void *fdt, int stroffset,
139                          const char *s, int len)
140{
141        int slen;
142        const char *p = fdt_get_string(fdt, stroffset, &slen);
143
144        return p && (slen == len) && (memcmp(p, s, len) == 0);
145}
146
147uint32_t fdt_get_max_phandle(const void *fdt)
148{
149        uint32_t max_phandle = 0;
150        int offset;
151
152        for (offset = fdt_next_node(fdt, -1, NULL);;
153             offset = fdt_next_node(fdt, offset, NULL)) {
154                uint32_t phandle;
155
156                if (offset == -FDT_ERR_NOTFOUND)
157                        return max_phandle;
158
159                if (offset < 0)
160                        return (uint32_t)-1;
161
162                phandle = fdt_get_phandle(fdt, offset);
163                if (phandle == (uint32_t)-1)
164                        continue;
165
166                if (phandle > max_phandle)
167                        max_phandle = phandle;
168        }
169
170        return 0;
171}
172
173static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
174{
175        int offset = n * sizeof(struct fdt_reserve_entry);
176        int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
177
178        if (absoffset < fdt_off_mem_rsvmap(fdt))
179                return NULL;
180        if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry))
181                return NULL;
182        return fdt_mem_rsv_(fdt, n);
183}
184
185int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
186{
187        const struct fdt_reserve_entry *re;
188
189        FDT_RO_PROBE(fdt);
190        re = fdt_mem_rsv(fdt, n);
191        if (!re)
192                return -FDT_ERR_BADOFFSET;
193
194        *address = fdt64_to_cpu(re->address);
195        *size = fdt64_to_cpu(re->size);
196        return 0;
197}
198
199int fdt_num_mem_rsv(const void *fdt)
200{
201        int i;
202        const struct fdt_reserve_entry *re;
203
204        for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
205                if (fdt64_to_cpu(re->size) == 0)
206                        return i;
207        }
208        return -FDT_ERR_TRUNCATED;
209}
210
211static int nextprop_(const void *fdt, int offset)
212{
213        uint32_t tag;
214        int nextoffset;
215
216        do {
217                tag = fdt_next_tag(fdt, offset, &nextoffset);
218
219                switch (tag) {
220                case FDT_END:
221                        if (nextoffset >= 0)
222                                return -FDT_ERR_BADSTRUCTURE;
223                        else
224                                return nextoffset;
225
226                case FDT_PROP:
227                        return offset;
228                }
229                offset = nextoffset;
230        } while (tag == FDT_NOP);
231
232        return -FDT_ERR_NOTFOUND;
233}
234
235int fdt_subnode_offset_namelen(const void *fdt, int offset,
236                               const char *name, int namelen)
237{
238        int depth;
239
240        FDT_RO_PROBE(fdt);
241
242        for (depth = 0;
243             (offset >= 0) && (depth >= 0);
244             offset = fdt_next_node(fdt, offset, &depth))
245                if ((depth == 1)
246                    && fdt_nodename_eq_(fdt, offset, name, namelen))
247                        return offset;
248
249        if (depth < 0)
250                return -FDT_ERR_NOTFOUND;
251        return offset; /* error */
252}
253
254int fdt_subnode_offset(const void *fdt, int parentoffset,
255                       const char *name)
256{
257        return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
258}
259
260int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
261{
262        const char *end = path + namelen;
263        const char *p = path;
264        int offset = 0;
265
266        FDT_RO_PROBE(fdt);
267
268        /* see if we have an alias */
269        if (*path != '/') {
270                const char *q = memchr(path, '/', end - p);
271
272                if (!q)
273                        q = end;
274
275                p = fdt_get_alias_namelen(fdt, p, q - p);
276                if (!p)
277                        return -FDT_ERR_BADPATH;
278                offset = fdt_path_offset(fdt, p);
279
280                p = q;
281        }
282
283        while (p < end) {
284                const char *q;
285
286                while (*p == '/') {
287                        p++;
288                        if (p == end)
289                                return offset;
290                }
291                q = memchr(p, '/', end - p);
292                if (! q)
293                        q = end;
294
295                offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
296                if (offset < 0)
297                        return offset;
298
299                p = q;
300        }
301
302        return offset;
303}
304
305int fdt_path_offset(const void *fdt, const char *path)
306{
307        return fdt_path_offset_namelen(fdt, path, strlen(path));
308}
309
310const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
311{
312        const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
313        const char *nameptr;
314        int err;
315
316        if (((err = fdt_ro_probe_(fdt)) != 0)
317            || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
318                        goto fail;
319
320        nameptr = nh->name;
321
322        if (fdt_version(fdt) < 0x10) {
323                /*
324                 * For old FDT versions, match the naming conventions of V16:
325                 * give only the leaf name (after all /). The actual tree
326                 * contents are loosely checked.
327                 */
328                const char *leaf;
329                leaf = strrchr(nameptr, '/');
330                if (leaf == NULL) {
331                        err = -FDT_ERR_BADSTRUCTURE;
332                        goto fail;
333                }
334                nameptr = leaf+1;
335        }
336
337        if (len)
338                *len = strlen(nameptr);
339
340        return nameptr;
341
342 fail:
343        if (len)
344                *len = err;
345        return NULL;
346}
347
348int fdt_first_property_offset(const void *fdt, int nodeoffset)
349{
350        int offset;
351
352        if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
353                return offset;
354
355        return nextprop_(fdt, offset);
356}
357
358int fdt_next_property_offset(const void *fdt, int offset)
359{
360        if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
361                return offset;
362
363        return nextprop_(fdt, offset);
364}
365
366static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
367                                                              int offset,
368                                                              int *lenp)
369{
370        int err;
371        const struct fdt_property *prop;
372
373        if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
374                if (lenp)
375                        *lenp = err;
376                return NULL;
377        }
378
379        prop = fdt_offset_ptr_(fdt, offset);
380
381        if (lenp)
382                *lenp = fdt32_to_cpu(prop->len);
383
384        return prop;
385}
386
387const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
388                                                      int offset,
389                                                      int *lenp)
390{
391        /* Prior to version 16, properties may need realignment
392         * and this API does not work. fdt_getprop_*() will, however. */
393
394        if (fdt_version(fdt) < 0x10) {
395                if (lenp)
396                        *lenp = -FDT_ERR_BADVERSION;
397                return NULL;
398        }
399
400        return fdt_get_property_by_offset_(fdt, offset, lenp);
401}
402
403static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
404                                                            int offset,
405                                                            const char *name,
406                                                            int namelen,
407                                                            int *lenp,
408                                                            int *poffset)
409{
410        for (offset = fdt_first_property_offset(fdt, offset);
411             (offset >= 0);
412             (offset = fdt_next_property_offset(fdt, offset))) {
413                const struct fdt_property *prop;
414
415                if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) {
416                        offset = -FDT_ERR_INTERNAL;
417                        break;
418                }
419                if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
420                                   name, namelen)) {
421                        if (poffset)
422                                *poffset = offset;
423                        return prop;
424                }
425        }
426
427        if (lenp)
428                *lenp = offset;
429        return NULL;
430}
431
432
433const struct fdt_property *fdt_get_property_namelen(const void *fdt,
434                                                    int offset,
435                                                    const char *name,
436                                                    int namelen, int *lenp)
437{
438        /* Prior to version 16, properties may need realignment
439         * and this API does not work. fdt_getprop_*() will, however. */
440        if (fdt_version(fdt) < 0x10) {
441                if (lenp)
442                        *lenp = -FDT_ERR_BADVERSION;
443                return NULL;
444        }
445
446        return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
447                                         NULL);
448}
449
450
451const struct fdt_property *fdt_get_property(const void *fdt,
452                                            int nodeoffset,
453                                            const char *name, int *lenp)
454{
455        return fdt_get_property_namelen(fdt, nodeoffset, name,
456                                        strlen(name), lenp);
457}
458
459const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
460                                const char *name, int namelen, int *lenp)
461{
462        int poffset;
463        const struct fdt_property *prop;
464
465        prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
466                                         &poffset);
467        if (!prop)
468                return NULL;
469
470        /* Handle realignment */
471        if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 &&
472            fdt32_to_cpu(prop->len) >= 8)
473                return prop->data + 4;
474        return prop->data;
475}
476
477const void *fdt_getprop_by_offset(const void *fdt, int offset,
478                                  const char **namep, int *lenp)
479{
480        const struct fdt_property *prop;
481
482        prop = fdt_get_property_by_offset_(fdt, offset, lenp);
483        if (!prop)
484                return NULL;
485        if (namep) {
486                const char *name;
487                int namelen;
488                name = fdt_get_string(fdt, fdt32_to_cpu(prop->nameoff),
489                                      &namelen);
490                if (!name) {
491                        if (lenp)
492                                *lenp = namelen;
493                        return NULL;
494                }
495                *namep = name;
496        }
497
498        /* Handle realignment */
499        if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
500            fdt32_to_cpu(prop->len) >= 8)
501                return prop->data + 4;
502        return prop->data;
503}
504
505const void *fdt_getprop(const void *fdt, int nodeoffset,
506                        const char *name, int *lenp)
507{
508        return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
509}
510
511uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
512{
513        const fdt32_t *php;
514        int len;
515
516        /* FIXME: This is a bit sub-optimal, since we potentially scan
517         * over all the properties twice. */
518        php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
519        if (!php || (len != sizeof(*php))) {
520                php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
521                if (!php || (len != sizeof(*php)))
522                        return 0;
523        }
524
525        return fdt32_to_cpu(*php);
526}
527
528const char *fdt_get_alias_namelen(const void *fdt,
529                                  const char *name, int namelen)
530{
531        int aliasoffset;
532
533        aliasoffset = fdt_path_offset(fdt, "/aliases");
534        if (aliasoffset < 0)
535                return NULL;
536
537        return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
538}
539
540const char *fdt_get_alias(const void *fdt, const char *name)
541{
542        return fdt_get_alias_namelen(fdt, name, strlen(name));
543}
544
545int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
546{
547        int pdepth = 0, p = 0;
548        int offset, depth, namelen;
549        const char *name;
550
551        FDT_RO_PROBE(fdt);
552
553        if (buflen < 2)
554                return -FDT_ERR_NOSPACE;
555
556        for (offset = 0, depth = 0;
557             (offset >= 0) && (offset <= nodeoffset);
558             offset = fdt_next_node(fdt, offset, &depth)) {
559                while (pdepth > depth) {
560                        do {
561                                p--;
562                        } while (buf[p-1] != '/');
563                        pdepth--;
564                }
565
566                if (pdepth >= depth) {
567                        name = fdt_get_name(fdt, offset, &namelen);
568                        if (!name)
569                                return namelen;
570                        if ((p + namelen + 1) <= buflen) {
571                                memcpy(buf + p, name, namelen);
572                                p += namelen;
573                                buf[p++] = '/';
574                                pdepth++;
575                        }
576                }
577
578                if (offset == nodeoffset) {
579                        if (pdepth < (depth + 1))
580                                return -FDT_ERR_NOSPACE;
581
582                        if (p > 1) /* special case so that root path is "/", not "" */
583                                p--;
584                        buf[p] = '\0';
585                        return 0;
586                }
587        }
588
589        if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
590                return -FDT_ERR_BADOFFSET;
591        else if (offset == -FDT_ERR_BADOFFSET)
592                return -FDT_ERR_BADSTRUCTURE;
593
594        return offset; /* error from fdt_next_node() */
595}
596
597int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
598                                 int supernodedepth, int *nodedepth)
599{
600        int offset, depth;
601        int supernodeoffset = -FDT_ERR_INTERNAL;
602
603        FDT_RO_PROBE(fdt);
604
605        if (supernodedepth < 0)
606                return -FDT_ERR_NOTFOUND;
607
608        for (offset = 0, depth = 0;
609             (offset >= 0) && (offset <= nodeoffset);
610             offset = fdt_next_node(fdt, offset, &depth)) {
611                if (depth == supernodedepth)
612                        supernodeoffset = offset;
613
614                if (offset == nodeoffset) {
615                        if (nodedepth)
616                                *nodedepth = depth;
617
618                        if (supernodedepth > depth)
619                                return -FDT_ERR_NOTFOUND;
620                        else
621                                return supernodeoffset;
622                }
623        }
624
625        if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
626                return -FDT_ERR_BADOFFSET;
627        else if (offset == -FDT_ERR_BADOFFSET)
628                return -FDT_ERR_BADSTRUCTURE;
629
630        return offset; /* error from fdt_next_node() */
631}
632
633int fdt_node_depth(const void *fdt, int nodeoffset)
634{
635        int nodedepth;
636        int err;
637
638        err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
639        if (err)
640                return (err < 0) ? err : -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 == -1))
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.