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