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