uboot/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
 112int fdt_subnode_offset_namelen(const void *fdt, int offset,
 113                               const char *name, int namelen)
 114{
 115        int depth = 0;
 116
 117        FDT_CHECK_HEADER(fdt);
 118
 119        for (depth = 0, offset = fdt_next_node(fdt, offset, &depth);
 120             (offset >= 0) && (depth > 0);
 121             offset = fdt_next_node(fdt, offset, &depth)) {
 122                if (depth < 0)
 123                        return -FDT_ERR_NOTFOUND;
 124                else if ((depth == 1)
 125                         && _fdt_nodename_eq(fdt, offset, name, namelen))
 126                        return offset;
 127        }
 128
 129        if (offset < 0)
 130                return offset; /* error */
 131        else
 132                return -FDT_ERR_NOTFOUND;
 133}
 134
 135int fdt_subnode_offset(const void *fdt, int parentoffset,
 136                       const char *name)
 137{
 138        return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
 139}
 140
 141int fdt_path_offset(const void *fdt, const char *path)
 142{
 143        const char *end = path + strlen(path);
 144        const char *p = path;
 145        int offset = 0;
 146
 147        FDT_CHECK_HEADER(fdt);
 148
 149        /* see if we have an alias */
 150        if (*path != '/') {
 151                const char *q = strchr(path, '/');
 152
 153                if (!q)
 154                        q = end;
 155
 156                p = fdt_get_alias_namelen(fdt, p, q - p);
 157                if (!p)
 158                        return -FDT_ERR_BADPATH;
 159                offset = fdt_path_offset(fdt, p);
 160
 161                p = q;
 162        }
 163
 164        while (*p) {
 165                const char *q;
 166
 167                while (*p == '/')
 168                        p++;
 169                if (! *p)
 170                        return offset;
 171                q = strchr(p, '/');
 172                if (! q)
 173                        q = end;
 174
 175                offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
 176                if (offset < 0)
 177                        return offset;
 178
 179                p = q;
 180        }
 181
 182        return offset;
 183}
 184
 185const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
 186{
 187        const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
 188        int err;
 189
 190        if (((err = fdt_check_header(fdt)) != 0)
 191            || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
 192                        goto fail;
 193
 194        if (len)
 195                *len = strlen(nh->name);
 196
 197        return nh->name;
 198
 199 fail:
 200        if (len)
 201                *len = err;
 202        return NULL;
 203}
 204
 205const struct fdt_property *fdt_get_property_namelen(const void *fdt,
 206                                                    int nodeoffset,
 207                                                    const char *name,
 208                                                    int namelen, int *lenp)
 209{
 210        uint32_t tag;
 211        const struct fdt_property *prop;
 212        int namestroff;
 213        int offset, nextoffset;
 214        int err;
 215
 216        if (((err = fdt_check_header(fdt)) != 0)
 217            || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
 218                        goto fail;
 219
 220        nextoffset = err;
 221        do {
 222                offset = nextoffset;
 223
 224                tag = fdt_next_tag(fdt, offset, &nextoffset);
 225                switch (tag) {
 226                case FDT_END:
 227                        err = -FDT_ERR_TRUNCATED;
 228                        goto fail;
 229
 230                case FDT_BEGIN_NODE:
 231                case FDT_END_NODE:
 232                case FDT_NOP:
 233                        break;
 234
 235                case FDT_PROP:
 236                        err = -FDT_ERR_BADSTRUCTURE;
 237                        prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
 238                        if (! prop)
 239                                goto fail;
 240                        namestroff = fdt32_to_cpu(prop->nameoff);
 241                        if (_fdt_string_eq(fdt, namestroff, name, namelen)) {
 242                                /* Found it! */
 243                                int len = fdt32_to_cpu(prop->len);
 244                                prop = fdt_offset_ptr(fdt, offset,
 245                                                      sizeof(*prop)+len);
 246                                if (! prop)
 247                                        goto fail;
 248
 249                                if (lenp)
 250                                        *lenp = len;
 251
 252                                return prop;
 253                        }
 254                        break;
 255
 256                default:
 257                        err = -FDT_ERR_BADSTRUCTURE;
 258                        goto fail;
 259                }
 260        } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
 261
 262        err = -FDT_ERR_NOTFOUND;
 263 fail:
 264        if (lenp)
 265                *lenp = err;
 266        return NULL;
 267}
 268
 269const struct fdt_property *fdt_get_property(const void *fdt,
 270                                            int nodeoffset,
 271                                            const char *name, int *lenp)
 272{
 273        return fdt_get_property_namelen(fdt, nodeoffset, name,
 274                                        strlen(name), lenp);
 275}
 276
 277const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
 278                                const char *name, int namelen, int *lenp)
 279{
 280        const struct fdt_property *prop;
 281
 282        prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
 283        if (! prop)
 284                return NULL;
 285
 286        return prop->data;
 287}
 288
 289const void *fdt_getprop(const void *fdt, int nodeoffset,
 290                        const char *name, int *lenp)
 291{
 292        return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
 293}
 294
 295uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
 296{
 297        const uint32_t *php;
 298        int len;
 299
 300        php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
 301        if (!php || (len != sizeof(*php)))
 302                return 0;
 303
 304        return fdt32_to_cpu(*php);
 305}
 306
 307const char *fdt_get_alias_namelen(const void *fdt,
 308                                  const char *name, int namelen)
 309{
 310        int aliasoffset;
 311
 312        aliasoffset = fdt_path_offset(fdt, "/aliases");
 313        if (aliasoffset < 0)
 314                return NULL;
 315
 316        return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
 317}
 318
 319const char *fdt_get_alias(const void *fdt, const char *name)
 320{
 321        return fdt_get_alias_namelen(fdt, name, strlen(name));
 322}
 323
 324int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
 325{
 326        int pdepth = 0, p = 0;
 327        int offset, depth, namelen;
 328        const char *name;
 329
 330        FDT_CHECK_HEADER(fdt);
 331
 332        if (buflen < 2)
 333                return -FDT_ERR_NOSPACE;
 334
 335        for (offset = 0, depth = 0;
 336             (offset >= 0) && (offset <= nodeoffset);
 337             offset = fdt_next_node(fdt, offset, &depth)) {
 338                while (pdepth > depth) {
 339                        do {
 340                                p--;
 341                        } while (buf[p-1] != '/');
 342                        pdepth--;
 343                }
 344
 345                if (pdepth >= depth) {
 346                        name = fdt_get_name(fdt, offset, &namelen);
 347                        if (!name)
 348                                return namelen;
 349                        if ((p + namelen + 1) <= buflen) {
 350                                memcpy(buf + p, name, namelen);
 351                                p += namelen;
 352                                buf[p++] = '/';
 353                                pdepth++;
 354                        }
 355                }
 356
 357                if (offset == nodeoffset) {
 358                        if (pdepth < (depth + 1))
 359                                return -FDT_ERR_NOSPACE;
 360
 361                        if (p > 1) /* special case so that root path is "/", not "" */
 362                                p--;
 363                        buf[p] = '\0';
 364                        return 0;
 365                }
 366        }
 367
 368        if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
 369                return -FDT_ERR_BADOFFSET;
 370        else if (offset == -FDT_ERR_BADOFFSET)
 371                return -FDT_ERR_BADSTRUCTURE;
 372
 373        return offset; /* error from fdt_next_node() */
 374}
 375
 376int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
 377                                 int supernodedepth, int *nodedepth)
 378{
 379        int offset, depth;
 380        int supernodeoffset = -FDT_ERR_INTERNAL;
 381
 382        FDT_CHECK_HEADER(fdt);
 383
 384        if (supernodedepth < 0)
 385                return -FDT_ERR_NOTFOUND;
 386
 387        for (offset = 0, depth = 0;
 388             (offset >= 0) && (offset <= nodeoffset);
 389             offset = fdt_next_node(fdt, offset, &depth)) {
 390                if (depth == supernodedepth)
 391                        supernodeoffset = offset;
 392
 393                if (offset == nodeoffset) {
 394                        if (nodedepth)
 395                                *nodedepth = depth;
 396
 397                        if (supernodedepth > depth)
 398                                return -FDT_ERR_NOTFOUND;
 399                        else
 400                                return supernodeoffset;
 401                }
 402        }
 403
 404        if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
 405                return -FDT_ERR_BADOFFSET;
 406        else if (offset == -FDT_ERR_BADOFFSET)
 407                return -FDT_ERR_BADSTRUCTURE;
 408
 409        return offset; /* error from fdt_next_node() */
 410}
 411
 412int fdt_node_depth(const void *fdt, int nodeoffset)
 413{
 414        int nodedepth;
 415        int err;
 416
 417        err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
 418        if (err)
 419                return (err < 0) ? err : -FDT_ERR_INTERNAL;
 420        return nodedepth;
 421}
 422
 423int fdt_parent_offset(const void *fdt, int nodeoffset)
 424{
 425        int nodedepth = fdt_node_depth(fdt, nodeoffset);
 426
 427        if (nodedepth < 0)
 428                return nodedepth;
 429        return fdt_supernode_atdepth_offset(fdt, nodeoffset,
 430                                            nodedepth - 1, NULL);
 431}
 432
 433int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
 434                                  const char *propname,
 435                                  const void *propval, int proplen)
 436{
 437        int offset;
 438        const void *val;
 439        int len;
 440
 441        FDT_CHECK_HEADER(fdt);
 442
 443        /* FIXME: The algorithm here is pretty horrible: we scan each
 444         * property of a node in fdt_getprop(), then if that didn't
 445         * find what we want, we scan over them again making our way
 446         * to the next node.  Still it's the easiest to implement
 447         * approach; performance can come later. */
 448        for (offset = fdt_next_node(fdt, startoffset, NULL);
 449             offset >= 0;
 450             offset = fdt_next_node(fdt, offset, NULL)) {
 451                val = fdt_getprop(fdt, offset, propname, &len);
 452                if (val && (len == proplen)
 453                    && (memcmp(val, propval, len) == 0))
 454                        return offset;
 455        }
 456
 457        return offset; /* error from fdt_next_node() */
 458}
 459
 460int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
 461{
 462        if ((phandle == 0) || (phandle == -1))
 463                return -FDT_ERR_BADPHANDLE;
 464        phandle = cpu_to_fdt32(phandle);
 465        return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
 466                                             &phandle, sizeof(phandle));
 467}
 468
 469static int _fdt_stringlist_contains(const char *strlist, int listlen,
 470                                    const char *str)
 471{
 472        int len = strlen(str);
 473        const char *p;
 474
 475        while (listlen >= len) {
 476                if (memcmp(str, strlist, len+1) == 0)
 477                        return 1;
 478                p = memchr(strlist, '\0', listlen);
 479                if (!p)
 480                        return 0; /* malformed strlist.. */
 481                listlen -= (p-strlist) + 1;
 482                strlist = p + 1;
 483        }
 484        return 0;
 485}
 486
 487int fdt_node_check_compatible(const void *fdt, int nodeoffset,
 488                              const char *compatible)
 489{
 490        const void *prop;
 491        int len;
 492
 493        prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
 494        if (!prop)
 495                return len;
 496        if (_fdt_stringlist_contains(prop, len, compatible))
 497                return 0;
 498        else
 499                return 1;
 500}
 501
 502int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
 503                                  const char *compatible)
 504{
 505        int offset, err;
 506
 507        FDT_CHECK_HEADER(fdt);
 508
 509        /* FIXME: The algorithm here is pretty horrible: we scan each
 510         * property of a node in fdt_node_check_compatible(), then if
 511         * that didn't find what we want, we scan over them again
 512         * making our way to the next node.  Still it's the easiest to
 513         * implement approach; performance can come later. */
 514        for (offset = fdt_next_node(fdt, startoffset, NULL);
 515             offset >= 0;
 516             offset = fdt_next_node(fdt, offset, NULL)) {
 517                err = fdt_node_check_compatible(fdt, offset, compatible);
 518                if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
 519                        return err;
 520                else if (err == 0)
 521                        return offset;
 522        }
 523
 524        return offset; /* error from fdt_next_node() */
 525}
 526