uboot/lib/libfdt/fdt_ro.c
<<
>>
Prefs
   1/*
   2 * libfdt - Flat Device Tree manipulation
   3 * Copyright (C) 2006 David Gibson, IBM Corporation.
   4 * SPDX-License-Identifier:     GPL-2.0+ BSD-2-Clause
   5 */
   6#include <libfdt_env.h>
   7
   8#ifndef USE_HOSTCC
   9#include <fdt.h>
  10#include <libfdt.h>
  11#else
  12#include "fdt_host.h"
  13#endif
  14
  15#include "libfdt_internal.h"
  16
  17static int _fdt_nodename_eq(const void *fdt, int offset,
  18                            const char *s, int len)
  19{
  20        const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
  21
  22        if (!p)
  23                /* short match */
  24                return 0;
  25
  26        if (memcmp(p, s, len) != 0)
  27                return 0;
  28
  29        if (p[len] == '\0')
  30                return 1;
  31        else if (!memchr(s, '@', len) && (p[len] == '@'))
  32                return 1;
  33        else
  34                return 0;
  35}
  36
  37const char *fdt_string(const void *fdt, int stroffset)
  38{
  39        return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
  40}
  41
  42static int _fdt_string_eq(const void *fdt, int stroffset,
  43                          const char *s, int len)
  44{
  45        const char *p = fdt_string(fdt, stroffset);
  46
  47        return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0);
  48}
  49
  50uint32_t fdt_get_max_phandle(const void *fdt)
  51{
  52        uint32_t max_phandle = 0;
  53        int offset;
  54
  55        for (offset = fdt_next_node(fdt, -1, NULL);;
  56             offset = fdt_next_node(fdt, offset, NULL)) {
  57                uint32_t phandle;
  58
  59                if (offset == -FDT_ERR_NOTFOUND)
  60                        return max_phandle;
  61
  62                if (offset < 0)
  63                        return (uint32_t)-1;
  64
  65                phandle = fdt_get_phandle(fdt, offset);
  66                if (phandle == (uint32_t)-1)
  67                        continue;
  68
  69                if (phandle > max_phandle)
  70                        max_phandle = phandle;
  71        }
  72
  73        return 0;
  74}
  75
  76int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
  77{
  78        FDT_CHECK_HEADER(fdt);
  79        *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
  80        *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
  81        return 0;
  82}
  83
  84int fdt_num_mem_rsv(const void *fdt)
  85{
  86        int i = 0;
  87
  88        while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
  89                i++;
  90        return i;
  91}
  92
  93static int _nextprop(const void *fdt, int offset)
  94{
  95        uint32_t tag;
  96        int nextoffset;
  97
  98        do {
  99                tag = fdt_next_tag(fdt, offset, &nextoffset);
 100
 101                switch (tag) {
 102                case FDT_END:
 103                        if (nextoffset >= 0)
 104                                return -FDT_ERR_BADSTRUCTURE;
 105                        else
 106                                return nextoffset;
 107
 108                case FDT_PROP:
 109                        return offset;
 110                }
 111                offset = nextoffset;
 112        } while (tag == FDT_NOP);
 113
 114        return -FDT_ERR_NOTFOUND;
 115}
 116
 117int fdt_subnode_offset_namelen(const void *fdt, int offset,
 118                               const char *name, int namelen)
 119{
 120        int depth;
 121
 122        FDT_CHECK_HEADER(fdt);
 123
 124        for (depth = 0;
 125             (offset >= 0) && (depth >= 0);
 126             offset = fdt_next_node(fdt, offset, &depth))
 127                if ((depth == 1)
 128                    && _fdt_nodename_eq(fdt, offset, name, namelen))
 129                        return offset;
 130
 131        if (depth < 0)
 132                return -FDT_ERR_NOTFOUND;
 133        return offset; /* error */
 134}
 135
 136int fdt_subnode_offset(const void *fdt, int parentoffset,
 137                       const char *name)
 138{
 139        return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
 140}
 141
 142/*
 143 * Find the next of path separator, note we need to search for both '/' and ':'
 144 * and then take the first one so that we do the right thing for e.g.
 145 * "foo/bar:option" and "bar:option/otheroption", both of which happen, so
 146 * first searching for either ':' or '/' does not work.
 147 */
 148static const char *fdt_path_next_separator(const char *path, int len)
 149{
 150        const void *sep1 = memchr(path, '/', len);
 151        const void *sep2 = memchr(path, ':', len);
 152
 153        if (sep1 && sep2)
 154                return (sep1 < sep2) ? sep1 : sep2;
 155        else if (sep1)
 156                return sep1;
 157        else
 158                return sep2;
 159}
 160
 161int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
 162{
 163        const char *end = path + namelen;
 164        const char *p = path;
 165        int offset = 0;
 166
 167        FDT_CHECK_HEADER(fdt);
 168
 169        /* see if we have an alias */
 170        if (*path != '/') {
 171                const char *q = fdt_path_next_separator(path, namelen);
 172
 173                if (!q)
 174                        q = end;
 175
 176                p = fdt_get_alias_namelen(fdt, p, q - p);
 177                if (!p)
 178                        return -FDT_ERR_BADPATH;
 179                offset = fdt_path_offset(fdt, p);
 180
 181                p = q;
 182        }
 183
 184        while (*p && (p < end)) {
 185                const char *q;
 186
 187                while (*p == '/')
 188                        p++;
 189
 190                if (*p == '\0' || *p == ':')
 191                        return offset;
 192
 193                q = fdt_path_next_separator(p, end - p);
 194                if (!q)
 195                        q = end;
 196
 197                offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
 198                if (offset < 0)
 199                        return offset;
 200
 201                p = q;
 202        }
 203
 204        return offset;
 205}
 206
 207int fdt_path_offset(const void *fdt, const char *path)
 208{
 209        return fdt_path_offset_namelen(fdt, path, strlen(path));
 210}
 211
 212const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
 213{
 214        const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
 215        int err;
 216
 217        if (((err = fdt_check_header(fdt)) != 0)
 218            || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
 219                        goto fail;
 220
 221        if (len)
 222                *len = strlen(nh->name);
 223
 224        return nh->name;
 225
 226 fail:
 227        if (len)
 228                *len = err;
 229        return NULL;
 230}
 231
 232int fdt_first_property_offset(const void *fdt, int nodeoffset)
 233{
 234        int offset;
 235
 236        if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
 237                return offset;
 238
 239        return _nextprop(fdt, offset);
 240}
 241
 242int fdt_next_property_offset(const void *fdt, int offset)
 243{
 244        if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
 245                return offset;
 246
 247        return _nextprop(fdt, offset);
 248}
 249
 250const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
 251                                                      int offset,
 252                                                      int *lenp)
 253{
 254        int err;
 255        const struct fdt_property *prop;
 256
 257        if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
 258                if (lenp)
 259                        *lenp = err;
 260                return NULL;
 261        }
 262
 263        prop = _fdt_offset_ptr(fdt, offset);
 264
 265        if (lenp)
 266                *lenp = fdt32_to_cpu(prop->len);
 267
 268        return prop;
 269}
 270
 271const struct fdt_property *fdt_get_property_namelen(const void *fdt,
 272                                                    int offset,
 273                                                    const char *name,
 274                                                    int namelen, int *lenp)
 275{
 276        for (offset = fdt_first_property_offset(fdt, offset);
 277             (offset >= 0);
 278             (offset = fdt_next_property_offset(fdt, offset))) {
 279                const struct fdt_property *prop;
 280
 281                if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
 282                        offset = -FDT_ERR_INTERNAL;
 283                        break;
 284                }
 285                if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
 286                                   name, namelen))
 287                        return prop;
 288        }
 289
 290        if (lenp)
 291                *lenp = offset;
 292        return NULL;
 293}
 294
 295const struct fdt_property *fdt_get_property(const void *fdt,
 296                                            int nodeoffset,
 297                                            const char *name, int *lenp)
 298{
 299        return fdt_get_property_namelen(fdt, nodeoffset, name,
 300                                        strlen(name), lenp);
 301}
 302
 303const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
 304                                const char *name, int namelen, int *lenp)
 305{
 306        const struct fdt_property *prop;
 307
 308        prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
 309        if (!prop)
 310                return NULL;
 311
 312        return prop->data;
 313}
 314
 315const void *fdt_getprop_by_offset(const void *fdt, int offset,
 316                                  const char **namep, int *lenp)
 317{
 318        const struct fdt_property *prop;
 319
 320        prop = fdt_get_property_by_offset(fdt, offset, lenp);
 321        if (!prop)
 322                return NULL;
 323        if (namep)
 324                *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
 325        return prop->data;
 326}
 327
 328const void *fdt_getprop(const void *fdt, int nodeoffset,
 329                        const char *name, int *lenp)
 330{
 331        return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
 332}
 333
 334uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
 335{
 336        const fdt32_t *php;
 337        int len;
 338
 339        /* FIXME: This is a bit sub-optimal, since we potentially scan
 340         * over all the properties twice. */
 341        php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
 342        if (!php || (len != sizeof(*php))) {
 343                php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
 344                if (!php || (len != sizeof(*php)))
 345                        return 0;
 346        }
 347
 348        return fdt32_to_cpu(*php);
 349}
 350
 351const char *fdt_get_alias_namelen(const void *fdt,
 352                                  const char *name, int namelen)
 353{
 354        int aliasoffset;
 355
 356        aliasoffset = fdt_path_offset(fdt, "/aliases");
 357        if (aliasoffset < 0)
 358                return NULL;
 359
 360        return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
 361}
 362
 363const char *fdt_get_alias(const void *fdt, const char *name)
 364{
 365        return fdt_get_alias_namelen(fdt, name, strlen(name));
 366}
 367
 368int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
 369{
 370        int pdepth = 0, p = 0;
 371        int offset, depth, namelen;
 372        const char *name;
 373
 374        FDT_CHECK_HEADER(fdt);
 375
 376        if (buflen < 2)
 377                return -FDT_ERR_NOSPACE;
 378
 379        for (offset = 0, depth = 0;
 380             (offset >= 0) && (offset <= nodeoffset);
 381             offset = fdt_next_node(fdt, offset, &depth)) {
 382                while (pdepth > depth) {
 383                        do {
 384                                p--;
 385                        } while (buf[p-1] != '/');
 386                        pdepth--;
 387                }
 388
 389                if (pdepth >= depth) {
 390                        name = fdt_get_name(fdt, offset, &namelen);
 391                        if (!name)
 392                                return namelen;
 393                        if ((p + namelen + 1) <= buflen) {
 394                                memcpy(buf + p, name, namelen);
 395                                p += namelen;
 396                                buf[p++] = '/';
 397                                pdepth++;
 398                        }
 399                }
 400
 401                if (offset == nodeoffset) {
 402                        if (pdepth < (depth + 1))
 403                                return -FDT_ERR_NOSPACE;
 404
 405                        if (p > 1) /* special case so that root path is "/", not "" */
 406                                p--;
 407                        buf[p] = '\0';
 408                        return 0;
 409                }
 410        }
 411
 412        if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
 413                return -FDT_ERR_BADOFFSET;
 414        else if (offset == -FDT_ERR_BADOFFSET)
 415                return -FDT_ERR_BADSTRUCTURE;
 416
 417        return offset; /* error from fdt_next_node() */
 418}
 419
 420int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
 421                                 int supernodedepth, int *nodedepth)
 422{
 423        int offset, depth;
 424        int supernodeoffset = -FDT_ERR_INTERNAL;
 425
 426        FDT_CHECK_HEADER(fdt);
 427
 428        if (supernodedepth < 0)
 429                return -FDT_ERR_NOTFOUND;
 430
 431        for (offset = 0, depth = 0;
 432             (offset >= 0) && (offset <= nodeoffset);
 433             offset = fdt_next_node(fdt, offset, &depth)) {
 434                if (depth == supernodedepth)
 435                        supernodeoffset = offset;
 436
 437                if (offset == nodeoffset) {
 438                        if (nodedepth)
 439                                *nodedepth = depth;
 440
 441                        if (supernodedepth > depth)
 442                                return -FDT_ERR_NOTFOUND;
 443                        else
 444                                return supernodeoffset;
 445                }
 446        }
 447
 448        if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
 449                return -FDT_ERR_BADOFFSET;
 450        else if (offset == -FDT_ERR_BADOFFSET)
 451                return -FDT_ERR_BADSTRUCTURE;
 452
 453        return offset; /* error from fdt_next_node() */
 454}
 455
 456int fdt_node_depth(const void *fdt, int nodeoffset)
 457{
 458        int nodedepth;
 459        int err;
 460
 461        err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
 462        if (err)
 463                return (err < 0) ? err : -FDT_ERR_INTERNAL;
 464        return nodedepth;
 465}
 466
 467int fdt_parent_offset(const void *fdt, int nodeoffset)
 468{
 469        int nodedepth = fdt_node_depth(fdt, nodeoffset);
 470
 471        if (nodedepth < 0)
 472                return nodedepth;
 473        return fdt_supernode_atdepth_offset(fdt, nodeoffset,
 474                                            nodedepth - 1, NULL);
 475}
 476
 477int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
 478                                  const char *propname,
 479                                  const void *propval, int proplen)
 480{
 481        int offset;
 482        const void *val;
 483        int len;
 484
 485        FDT_CHECK_HEADER(fdt);
 486
 487        /* FIXME: The algorithm here is pretty horrible: we scan each
 488         * property of a node in fdt_getprop(), then if that didn't
 489         * find what we want, we scan over them again making our way
 490         * to the next node.  Still it's the easiest to implement
 491         * approach; performance can come later. */
 492        for (offset = fdt_next_node(fdt, startoffset, NULL);
 493             offset >= 0;
 494             offset = fdt_next_node(fdt, offset, NULL)) {
 495                val = fdt_getprop(fdt, offset, propname, &len);
 496                if (val && (len == proplen)
 497                    && (memcmp(val, propval, len) == 0))
 498                        return offset;
 499        }
 500
 501        return offset; /* error from fdt_next_node() */
 502}
 503
 504int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
 505{
 506        int offset;
 507
 508        if ((phandle == 0) || (phandle == -1))
 509                return -FDT_ERR_BADPHANDLE;
 510
 511        FDT_CHECK_HEADER(fdt);
 512
 513        /* FIXME: The algorithm here is pretty horrible: we
 514         * potentially scan each property of a node in
 515         * fdt_get_phandle(), then if that didn't find what
 516         * we want, we scan over them again making our way to the next
 517         * node.  Still it's the easiest to implement approach;
 518         * performance can come later. */
 519        for (offset = fdt_next_node(fdt, -1, NULL);
 520             offset >= 0;
 521             offset = fdt_next_node(fdt, offset, NULL)) {
 522                if (fdt_get_phandle(fdt, offset) == phandle)
 523                        return offset;
 524        }
 525
 526        return offset; /* error from fdt_next_node() */
 527}
 528
 529int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
 530{
 531        int len = strlen(str);
 532        const char *p;
 533
 534        while (listlen >= len) {
 535                if (memcmp(str, strlist, len+1) == 0)
 536                        return 1;
 537                p = memchr(strlist, '\0', listlen);
 538                if (!p)
 539                        return 0; /* malformed strlist.. */
 540                listlen -= (p-strlist) + 1;
 541                strlist = p + 1;
 542        }
 543        return 0;
 544}
 545
 546int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
 547{
 548        const char *list, *end;
 549        int length, count = 0;
 550
 551        list = fdt_getprop(fdt, nodeoffset, property, &length);
 552        if (!list)
 553                return length;
 554
 555        end = list + length;
 556
 557        while (list < end) {
 558                length = strnlen(list, end - list) + 1;
 559
 560                /* Abort if the last string isn't properly NUL-terminated. */
 561                if (list + length > end)
 562                        return -FDT_ERR_BADVALUE;
 563
 564                list += length;
 565                count++;
 566        }
 567
 568        return count;
 569}
 570
 571int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
 572                          const char *string)
 573{
 574        int length, len, idx = 0;
 575        const char *list, *end;
 576
 577        list = fdt_getprop(fdt, nodeoffset, property, &length);
 578        if (!list)
 579                return length;
 580
 581        len = strlen(string) + 1;
 582        end = list + length;
 583
 584        while (list < end) {
 585                length = strnlen(list, end - list) + 1;
 586
 587                /* Abort if the last string isn't properly NUL-terminated. */
 588                if (list + length > end)
 589                        return -FDT_ERR_BADVALUE;
 590
 591                if (length == len && memcmp(list, string, length) == 0)
 592                        return idx;
 593
 594                list += length;
 595                idx++;
 596        }
 597
 598        return -FDT_ERR_NOTFOUND;
 599}
 600
 601const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
 602                               const char *property, int idx,
 603                               int *lenp)
 604{
 605        const char *list, *end;
 606        int length;
 607
 608        list = fdt_getprop(fdt, nodeoffset, property, &length);
 609        if (!list) {
 610                if (lenp)
 611                        *lenp = length;
 612
 613                return NULL;
 614        }
 615
 616        end = list + length;
 617
 618        while (list < end) {
 619                length = strnlen(list, end - list) + 1;
 620
 621                /* Abort if the last string isn't properly NUL-terminated. */
 622                if (list + length > end) {
 623                        if (lenp)
 624                                *lenp = -FDT_ERR_BADVALUE;
 625
 626                        return NULL;
 627                }
 628
 629                if (idx == 0) {
 630                        if (lenp)
 631                                *lenp = length - 1;
 632
 633                        return list;
 634                }
 635
 636                list += length;
 637                idx--;
 638        }
 639
 640        if (lenp)
 641                *lenp = -FDT_ERR_NOTFOUND;
 642
 643        return NULL;
 644}
 645
 646int fdt_node_check_compatible(const void *fdt, int nodeoffset,
 647                              const char *compatible)
 648{
 649        const void *prop;
 650        int len;
 651
 652        prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
 653        if (!prop)
 654                return len;
 655
 656        return !fdt_stringlist_contains(prop, len, compatible);
 657}
 658
 659int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
 660                                  const char *compatible)
 661{
 662        int offset, err;
 663
 664        FDT_CHECK_HEADER(fdt);
 665
 666        /* FIXME: The algorithm here is pretty horrible: we scan each
 667         * property of a node in fdt_node_check_compatible(), then if
 668         * that didn't find what we want, we scan over them again
 669         * making our way to the next node.  Still it's the easiest to
 670         * implement approach; performance can come later. */
 671        for (offset = fdt_next_node(fdt, startoffset, NULL);
 672             offset >= 0;
 673             offset = fdt_next_node(fdt, offset, NULL)) {
 674                err = fdt_node_check_compatible(fdt, offset, compatible);
 675                if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
 676                        return err;
 677                else if (err == 0)
 678                        return offset;
 679        }
 680
 681        return offset; /* error from fdt_next_node() */
 682}
 683