Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
0002 /*
0003  * libfdt - Flat Device Tree manipulation
0004  * Copyright (C) 2006 David Gibson, IBM Corporation.
0005  */
0006 #include "libfdt_env.h"
0007 
0008 #include <fdt.h>
0009 #include <libfdt.h>
0010 
0011 #include "libfdt_internal.h"
0012 
0013 static int fdt_nodename_eq_(const void *fdt, int offset,
0014                 const char *s, int len)
0015 {
0016     int olen;
0017     const char *p = fdt_get_name(fdt, offset, &olen);
0018 
0019     if (!p || olen < len)
0020         /* short match */
0021         return 0;
0022 
0023     if (memcmp(p, s, len) != 0)
0024         return 0;
0025 
0026     if (p[len] == '\0')
0027         return 1;
0028     else if (!memchr(s, '@', len) && (p[len] == '@'))
0029         return 1;
0030     else
0031         return 0;
0032 }
0033 
0034 const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
0035 {
0036     int32_t totalsize;
0037     uint32_t absoffset;
0038     size_t len;
0039     int err;
0040     const char *s, *n;
0041 
0042     if (can_assume(VALID_INPUT)) {
0043         s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
0044 
0045         if (lenp)
0046             *lenp = strlen(s);
0047         return s;
0048     }
0049     totalsize = fdt_ro_probe_(fdt);
0050     err = totalsize;
0051     if (totalsize < 0)
0052         goto fail;
0053 
0054     err = -FDT_ERR_BADOFFSET;
0055     absoffset = stroffset + fdt_off_dt_strings(fdt);
0056     if (absoffset >= (unsigned)totalsize)
0057         goto fail;
0058     len = totalsize - absoffset;
0059 
0060     if (fdt_magic(fdt) == FDT_MAGIC) {
0061         if (stroffset < 0)
0062             goto fail;
0063         if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
0064             if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
0065                 goto fail;
0066             if ((fdt_size_dt_strings(fdt) - stroffset) < len)
0067                 len = fdt_size_dt_strings(fdt) - stroffset;
0068         }
0069     } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
0070         unsigned int sw_stroffset = -stroffset;
0071 
0072         if ((stroffset >= 0) ||
0073             (sw_stroffset > fdt_size_dt_strings(fdt)))
0074             goto fail;
0075         if (sw_stroffset < len)
0076             len = sw_stroffset;
0077     } else {
0078         err = -FDT_ERR_INTERNAL;
0079         goto fail;
0080     }
0081 
0082     s = (const char *)fdt + absoffset;
0083     n = memchr(s, '\0', len);
0084     if (!n) {
0085         /* missing terminating NULL */
0086         err = -FDT_ERR_TRUNCATED;
0087         goto fail;
0088     }
0089 
0090     if (lenp)
0091         *lenp = n - s;
0092     return s;
0093 
0094 fail:
0095     if (lenp)
0096         *lenp = err;
0097     return NULL;
0098 }
0099 
0100 const char *fdt_string(const void *fdt, int stroffset)
0101 {
0102     return fdt_get_string(fdt, stroffset, NULL);
0103 }
0104 
0105 static int fdt_string_eq_(const void *fdt, int stroffset,
0106               const char *s, int len)
0107 {
0108     int slen;
0109     const char *p = fdt_get_string(fdt, stroffset, &slen);
0110 
0111     return p && (slen == len) && (memcmp(p, s, len) == 0);
0112 }
0113 
0114 int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
0115 {
0116     uint32_t max = 0;
0117     int offset = -1;
0118 
0119     while (true) {
0120         uint32_t value;
0121 
0122         offset = fdt_next_node(fdt, offset, NULL);
0123         if (offset < 0) {
0124             if (offset == -FDT_ERR_NOTFOUND)
0125                 break;
0126 
0127             return offset;
0128         }
0129 
0130         value = fdt_get_phandle(fdt, offset);
0131 
0132         if (value > max)
0133             max = value;
0134     }
0135 
0136     if (phandle)
0137         *phandle = max;
0138 
0139     return 0;
0140 }
0141 
0142 int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
0143 {
0144     uint32_t max;
0145     int err;
0146 
0147     err = fdt_find_max_phandle(fdt, &max);
0148     if (err < 0)
0149         return err;
0150 
0151     if (max == FDT_MAX_PHANDLE)
0152         return -FDT_ERR_NOPHANDLES;
0153 
0154     if (phandle)
0155         *phandle = max + 1;
0156 
0157     return 0;
0158 }
0159 
0160 static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
0161 {
0162     unsigned int offset = n * sizeof(struct fdt_reserve_entry);
0163     unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
0164 
0165     if (!can_assume(VALID_INPUT)) {
0166         if (absoffset < fdt_off_mem_rsvmap(fdt))
0167             return NULL;
0168         if (absoffset > fdt_totalsize(fdt) -
0169             sizeof(struct fdt_reserve_entry))
0170             return NULL;
0171     }
0172     return fdt_mem_rsv_(fdt, n);
0173 }
0174 
0175 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
0176 {
0177     const struct fdt_reserve_entry *re;
0178 
0179     FDT_RO_PROBE(fdt);
0180     re = fdt_mem_rsv(fdt, n);
0181     if (!can_assume(VALID_INPUT) && !re)
0182         return -FDT_ERR_BADOFFSET;
0183 
0184     *address = fdt64_ld_(&re->address);
0185     *size = fdt64_ld_(&re->size);
0186     return 0;
0187 }
0188 
0189 int fdt_num_mem_rsv(const void *fdt)
0190 {
0191     int i;
0192     const struct fdt_reserve_entry *re;
0193 
0194     for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
0195         if (fdt64_ld_(&re->size) == 0)
0196             return i;
0197     }
0198     return -FDT_ERR_TRUNCATED;
0199 }
0200 
0201 static int nextprop_(const void *fdt, int offset)
0202 {
0203     uint32_t tag;
0204     int nextoffset;
0205 
0206     do {
0207         tag = fdt_next_tag(fdt, offset, &nextoffset);
0208 
0209         switch (tag) {
0210         case FDT_END:
0211             if (nextoffset >= 0)
0212                 return -FDT_ERR_BADSTRUCTURE;
0213             else
0214                 return nextoffset;
0215 
0216         case FDT_PROP:
0217             return offset;
0218         }
0219         offset = nextoffset;
0220     } while (tag == FDT_NOP);
0221 
0222     return -FDT_ERR_NOTFOUND;
0223 }
0224 
0225 int fdt_subnode_offset_namelen(const void *fdt, int offset,
0226                    const char *name, int namelen)
0227 {
0228     int depth;
0229 
0230     FDT_RO_PROBE(fdt);
0231 
0232     for (depth = 0;
0233          (offset >= 0) && (depth >= 0);
0234          offset = fdt_next_node(fdt, offset, &depth))
0235         if ((depth == 1)
0236             && fdt_nodename_eq_(fdt, offset, name, namelen))
0237             return offset;
0238 
0239     if (depth < 0)
0240         return -FDT_ERR_NOTFOUND;
0241     return offset; /* error */
0242 }
0243 
0244 int fdt_subnode_offset(const void *fdt, int parentoffset,
0245                const char *name)
0246 {
0247     return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
0248 }
0249 
0250 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
0251 {
0252     const char *end = path + namelen;
0253     const char *p = path;
0254     int offset = 0;
0255 
0256     FDT_RO_PROBE(fdt);
0257 
0258     /* see if we have an alias */
0259     if (*path != '/') {
0260         const char *q = memchr(path, '/', end - p);
0261 
0262         if (!q)
0263             q = end;
0264 
0265         p = fdt_get_alias_namelen(fdt, p, q - p);
0266         if (!p)
0267             return -FDT_ERR_BADPATH;
0268         offset = fdt_path_offset(fdt, p);
0269 
0270         p = q;
0271     }
0272 
0273     while (p < end) {
0274         const char *q;
0275 
0276         while (*p == '/') {
0277             p++;
0278             if (p == end)
0279                 return offset;
0280         }
0281         q = memchr(p, '/', end - p);
0282         if (! q)
0283             q = end;
0284 
0285         offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
0286         if (offset < 0)
0287             return offset;
0288 
0289         p = q;
0290     }
0291 
0292     return offset;
0293 }
0294 
0295 int fdt_path_offset(const void *fdt, const char *path)
0296 {
0297     return fdt_path_offset_namelen(fdt, path, strlen(path));
0298 }
0299 
0300 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
0301 {
0302     const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
0303     const char *nameptr;
0304     int err;
0305 
0306     if (((err = fdt_ro_probe_(fdt)) < 0)
0307         || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
0308             goto fail;
0309 
0310     nameptr = nh->name;
0311 
0312     if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
0313         /*
0314          * For old FDT versions, match the naming conventions of V16:
0315          * give only the leaf name (after all /). The actual tree
0316          * contents are loosely checked.
0317          */
0318         const char *leaf;
0319         leaf = strrchr(nameptr, '/');
0320         if (leaf == NULL) {
0321             err = -FDT_ERR_BADSTRUCTURE;
0322             goto fail;
0323         }
0324         nameptr = leaf+1;
0325     }
0326 
0327     if (len)
0328         *len = strlen(nameptr);
0329 
0330     return nameptr;
0331 
0332  fail:
0333     if (len)
0334         *len = err;
0335     return NULL;
0336 }
0337 
0338 int fdt_first_property_offset(const void *fdt, int nodeoffset)
0339 {
0340     int offset;
0341 
0342     if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
0343         return offset;
0344 
0345     return nextprop_(fdt, offset);
0346 }
0347 
0348 int fdt_next_property_offset(const void *fdt, int offset)
0349 {
0350     if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
0351         return offset;
0352 
0353     return nextprop_(fdt, offset);
0354 }
0355 
0356 static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
0357                                       int offset,
0358                                       int *lenp)
0359 {
0360     int err;
0361     const struct fdt_property *prop;
0362 
0363     if (!can_assume(VALID_INPUT) &&
0364         (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
0365         if (lenp)
0366             *lenp = err;
0367         return NULL;
0368     }
0369 
0370     prop = fdt_offset_ptr_(fdt, offset);
0371 
0372     if (lenp)
0373         *lenp = fdt32_ld_(&prop->len);
0374 
0375     return prop;
0376 }
0377 
0378 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
0379                               int offset,
0380                               int *lenp)
0381 {
0382     /* Prior to version 16, properties may need realignment
0383      * and this API does not work. fdt_getprop_*() will, however. */
0384 
0385     if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
0386         if (lenp)
0387             *lenp = -FDT_ERR_BADVERSION;
0388         return NULL;
0389     }
0390 
0391     return fdt_get_property_by_offset_(fdt, offset, lenp);
0392 }
0393 
0394 static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
0395                                     int offset,
0396                                     const char *name,
0397                                     int namelen,
0398                                 int *lenp,
0399                                 int *poffset)
0400 {
0401     for (offset = fdt_first_property_offset(fdt, offset);
0402          (offset >= 0);
0403          (offset = fdt_next_property_offset(fdt, offset))) {
0404         const struct fdt_property *prop;
0405 
0406         prop = fdt_get_property_by_offset_(fdt, offset, lenp);
0407         if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
0408             offset = -FDT_ERR_INTERNAL;
0409             break;
0410         }
0411         if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
0412                    name, namelen)) {
0413             if (poffset)
0414                 *poffset = offset;
0415             return prop;
0416         }
0417     }
0418 
0419     if (lenp)
0420         *lenp = offset;
0421     return NULL;
0422 }
0423 
0424 
0425 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
0426                             int offset,
0427                             const char *name,
0428                             int namelen, int *lenp)
0429 {
0430     /* Prior to version 16, properties may need realignment
0431      * and this API does not work. fdt_getprop_*() will, however. */
0432     if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
0433         if (lenp)
0434             *lenp = -FDT_ERR_BADVERSION;
0435         return NULL;
0436     }
0437 
0438     return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
0439                      NULL);
0440 }
0441 
0442 
0443 const struct fdt_property *fdt_get_property(const void *fdt,
0444                         int nodeoffset,
0445                         const char *name, int *lenp)
0446 {
0447     return fdt_get_property_namelen(fdt, nodeoffset, name,
0448                     strlen(name), lenp);
0449 }
0450 
0451 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
0452                 const char *name, int namelen, int *lenp)
0453 {
0454     int poffset;
0455     const struct fdt_property *prop;
0456 
0457     prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
0458                      &poffset);
0459     if (!prop)
0460         return NULL;
0461 
0462     /* Handle realignment */
0463     if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
0464         (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
0465         return prop->data + 4;
0466     return prop->data;
0467 }
0468 
0469 const void *fdt_getprop_by_offset(const void *fdt, int offset,
0470                   const char **namep, int *lenp)
0471 {
0472     const struct fdt_property *prop;
0473 
0474     prop = fdt_get_property_by_offset_(fdt, offset, lenp);
0475     if (!prop)
0476         return NULL;
0477     if (namep) {
0478         const char *name;
0479         int namelen;
0480 
0481         if (!can_assume(VALID_INPUT)) {
0482             name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
0483                           &namelen);
0484             if (!name) {
0485                 if (lenp)
0486                     *lenp = namelen;
0487                 return NULL;
0488             }
0489             *namep = name;
0490         } else {
0491             *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
0492         }
0493     }
0494 
0495     /* Handle realignment */
0496     if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
0497         (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
0498         return prop->data + 4;
0499     return prop->data;
0500 }
0501 
0502 const void *fdt_getprop(const void *fdt, int nodeoffset,
0503             const char *name, int *lenp)
0504 {
0505     return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
0506 }
0507 
0508 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
0509 {
0510     const fdt32_t *php;
0511     int len;
0512 
0513     /* FIXME: This is a bit sub-optimal, since we potentially scan
0514      * over all the properties twice. */
0515     php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
0516     if (!php || (len != sizeof(*php))) {
0517         php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
0518         if (!php || (len != sizeof(*php)))
0519             return 0;
0520     }
0521 
0522     return fdt32_ld_(php);
0523 }
0524 
0525 const char *fdt_get_alias_namelen(const void *fdt,
0526                   const char *name, int namelen)
0527 {
0528     int aliasoffset;
0529 
0530     aliasoffset = fdt_path_offset(fdt, "/aliases");
0531     if (aliasoffset < 0)
0532         return NULL;
0533 
0534     return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
0535 }
0536 
0537 const char *fdt_get_alias(const void *fdt, const char *name)
0538 {
0539     return fdt_get_alias_namelen(fdt, name, strlen(name));
0540 }
0541 
0542 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
0543 {
0544     int pdepth = 0, p = 0;
0545     int offset, depth, namelen;
0546     const char *name;
0547 
0548     FDT_RO_PROBE(fdt);
0549 
0550     if (buflen < 2)
0551         return -FDT_ERR_NOSPACE;
0552 
0553     for (offset = 0, depth = 0;
0554          (offset >= 0) && (offset <= nodeoffset);
0555          offset = fdt_next_node(fdt, offset, &depth)) {
0556         while (pdepth > depth) {
0557             do {
0558                 p--;
0559             } while (buf[p-1] != '/');
0560             pdepth--;
0561         }
0562 
0563         if (pdepth >= depth) {
0564             name = fdt_get_name(fdt, offset, &namelen);
0565             if (!name)
0566                 return namelen;
0567             if ((p + namelen + 1) <= buflen) {
0568                 memcpy(buf + p, name, namelen);
0569                 p += namelen;
0570                 buf[p++] = '/';
0571                 pdepth++;
0572             }
0573         }
0574 
0575         if (offset == nodeoffset) {
0576             if (pdepth < (depth + 1))
0577                 return -FDT_ERR_NOSPACE;
0578 
0579             if (p > 1) /* special case so that root path is "/", not "" */
0580                 p--;
0581             buf[p] = '\0';
0582             return 0;
0583         }
0584     }
0585 
0586     if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
0587         return -FDT_ERR_BADOFFSET;
0588     else if (offset == -FDT_ERR_BADOFFSET)
0589         return -FDT_ERR_BADSTRUCTURE;
0590 
0591     return offset; /* error from fdt_next_node() */
0592 }
0593 
0594 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
0595                  int supernodedepth, int *nodedepth)
0596 {
0597     int offset, depth;
0598     int supernodeoffset = -FDT_ERR_INTERNAL;
0599 
0600     FDT_RO_PROBE(fdt);
0601 
0602     if (supernodedepth < 0)
0603         return -FDT_ERR_NOTFOUND;
0604 
0605     for (offset = 0, depth = 0;
0606          (offset >= 0) && (offset <= nodeoffset);
0607          offset = fdt_next_node(fdt, offset, &depth)) {
0608         if (depth == supernodedepth)
0609             supernodeoffset = offset;
0610 
0611         if (offset == nodeoffset) {
0612             if (nodedepth)
0613                 *nodedepth = depth;
0614 
0615             if (supernodedepth > depth)
0616                 return -FDT_ERR_NOTFOUND;
0617             else
0618                 return supernodeoffset;
0619         }
0620     }
0621 
0622     if (!can_assume(VALID_INPUT)) {
0623         if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
0624             return -FDT_ERR_BADOFFSET;
0625         else if (offset == -FDT_ERR_BADOFFSET)
0626             return -FDT_ERR_BADSTRUCTURE;
0627     }
0628 
0629     return offset; /* error from fdt_next_node() */
0630 }
0631 
0632 int fdt_node_depth(const void *fdt, int nodeoffset)
0633 {
0634     int nodedepth;
0635     int err;
0636 
0637     err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
0638     if (err)
0639         return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
0640             -FDT_ERR_INTERNAL;
0641     return nodedepth;
0642 }
0643 
0644 int fdt_parent_offset(const void *fdt, int nodeoffset)
0645 {
0646     int nodedepth = fdt_node_depth(fdt, nodeoffset);
0647 
0648     if (nodedepth < 0)
0649         return nodedepth;
0650     return fdt_supernode_atdepth_offset(fdt, nodeoffset,
0651                         nodedepth - 1, NULL);
0652 }
0653 
0654 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
0655                   const char *propname,
0656                   const void *propval, int proplen)
0657 {
0658     int offset;
0659     const void *val;
0660     int len;
0661 
0662     FDT_RO_PROBE(fdt);
0663 
0664     /* FIXME: The algorithm here is pretty horrible: we scan each
0665      * property of a node in fdt_getprop(), then if that didn't
0666      * find what we want, we scan over them again making our way
0667      * to the next node.  Still it's the easiest to implement
0668      * approach; performance can come later. */
0669     for (offset = fdt_next_node(fdt, startoffset, NULL);
0670          offset >= 0;
0671          offset = fdt_next_node(fdt, offset, NULL)) {
0672         val = fdt_getprop(fdt, offset, propname, &len);
0673         if (val && (len == proplen)
0674             && (memcmp(val, propval, len) == 0))
0675             return offset;
0676     }
0677 
0678     return offset; /* error from fdt_next_node() */
0679 }
0680 
0681 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
0682 {
0683     int offset;
0684 
0685     if ((phandle == 0) || (phandle == ~0U))
0686         return -FDT_ERR_BADPHANDLE;
0687 
0688     FDT_RO_PROBE(fdt);
0689 
0690     /* FIXME: The algorithm here is pretty horrible: we
0691      * potentially scan each property of a node in
0692      * fdt_get_phandle(), then if that didn't find what
0693      * we want, we scan over them again making our way to the next
0694      * node.  Still it's the easiest to implement approach;
0695      * performance can come later. */
0696     for (offset = fdt_next_node(fdt, -1, NULL);
0697          offset >= 0;
0698          offset = fdt_next_node(fdt, offset, NULL)) {
0699         if (fdt_get_phandle(fdt, offset) == phandle)
0700             return offset;
0701     }
0702 
0703     return offset; /* error from fdt_next_node() */
0704 }
0705 
0706 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
0707 {
0708     int len = strlen(str);
0709     const char *p;
0710 
0711     while (listlen >= len) {
0712         if (memcmp(str, strlist, len+1) == 0)
0713             return 1;
0714         p = memchr(strlist, '\0', listlen);
0715         if (!p)
0716             return 0; /* malformed strlist.. */
0717         listlen -= (p-strlist) + 1;
0718         strlist = p + 1;
0719     }
0720     return 0;
0721 }
0722 
0723 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
0724 {
0725     const char *list, *end;
0726     int length, count = 0;
0727 
0728     list = fdt_getprop(fdt, nodeoffset, property, &length);
0729     if (!list)
0730         return length;
0731 
0732     end = list + length;
0733 
0734     while (list < end) {
0735         length = strnlen(list, end - list) + 1;
0736 
0737         /* Abort if the last string isn't properly NUL-terminated. */
0738         if (list + length > end)
0739             return -FDT_ERR_BADVALUE;
0740 
0741         list += length;
0742         count++;
0743     }
0744 
0745     return count;
0746 }
0747 
0748 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
0749               const char *string)
0750 {
0751     int length, len, idx = 0;
0752     const char *list, *end;
0753 
0754     list = fdt_getprop(fdt, nodeoffset, property, &length);
0755     if (!list)
0756         return length;
0757 
0758     len = strlen(string) + 1;
0759     end = list + length;
0760 
0761     while (list < end) {
0762         length = strnlen(list, end - list) + 1;
0763 
0764         /* Abort if the last string isn't properly NUL-terminated. */
0765         if (list + length > end)
0766             return -FDT_ERR_BADVALUE;
0767 
0768         if (length == len && memcmp(list, string, length) == 0)
0769             return idx;
0770 
0771         list += length;
0772         idx++;
0773     }
0774 
0775     return -FDT_ERR_NOTFOUND;
0776 }
0777 
0778 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
0779                    const char *property, int idx,
0780                    int *lenp)
0781 {
0782     const char *list, *end;
0783     int length;
0784 
0785     list = fdt_getprop(fdt, nodeoffset, property, &length);
0786     if (!list) {
0787         if (lenp)
0788             *lenp = length;
0789 
0790         return NULL;
0791     }
0792 
0793     end = list + length;
0794 
0795     while (list < end) {
0796         length = strnlen(list, end - list) + 1;
0797 
0798         /* Abort if the last string isn't properly NUL-terminated. */
0799         if (list + length > end) {
0800             if (lenp)
0801                 *lenp = -FDT_ERR_BADVALUE;
0802 
0803             return NULL;
0804         }
0805 
0806         if (idx == 0) {
0807             if (lenp)
0808                 *lenp = length - 1;
0809 
0810             return list;
0811         }
0812 
0813         list += length;
0814         idx--;
0815     }
0816 
0817     if (lenp)
0818         *lenp = -FDT_ERR_NOTFOUND;
0819 
0820     return NULL;
0821 }
0822 
0823 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
0824                   const char *compatible)
0825 {
0826     const void *prop;
0827     int len;
0828 
0829     prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
0830     if (!prop)
0831         return len;
0832 
0833     return !fdt_stringlist_contains(prop, len, compatible);
0834 }
0835 
0836 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
0837                   const char *compatible)
0838 {
0839     int offset, err;
0840 
0841     FDT_RO_PROBE(fdt);
0842 
0843     /* FIXME: The algorithm here is pretty horrible: we scan each
0844      * property of a node in fdt_node_check_compatible(), then if
0845      * that didn't find what we want, we scan over them again
0846      * making our way to the next node.  Still it's the easiest to
0847      * implement approach; performance can come later. */
0848     for (offset = fdt_next_node(fdt, startoffset, NULL);
0849          offset >= 0;
0850          offset = fdt_next_node(fdt, offset, NULL)) {
0851         err = fdt_node_check_compatible(fdt, offset, compatible);
0852         if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
0853             return err;
0854         else if (err == 0)
0855             return offset;
0856     }
0857 
0858     return offset; /* error from fdt_next_node() */
0859 }