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