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