source: rtems/cpukit/dtc/libfdt/fdt_ro.c @ 72273b6

Last change on this file since 72273b6 was 72273b6, checked in by David Gibson <david@…>, on Nov 14, 2017 at 11:45:56 AM

libfdt: Safer access to strings section

fdt_string() is used to retrieve strings from a DT blob's strings section.
It's rarely used directly, but is widely used internally.

However, it doesn't do any bounds checking, which means in the case of a
corrupted blob it could access bad memory, which libfdt is supposed to
avoid.

This write a safe alternative to fdt_string, fdt_get_string(). It checks
both that the given offset is within the string section and that the string
it points to is properly \0 terminated within the section. It also returns
the string's length as a convenience (since it needs to determine to do the
checks anyway).

fdt_string() is rewritten in terms of fdt_get_string() for compatibility.

Most of the diff here is actually testing infrastructure.

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

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