fdt_ro.c revision 204433
1204431Sraj/* 2204431Sraj * libfdt - Flat Device Tree manipulation 3204431Sraj * Copyright (C) 2006 David Gibson, IBM Corporation. 4204431Sraj * 5204431Sraj * libfdt is dual licensed: you can use it either under the terms of 6204431Sraj * the GPL, or the BSD license, at your option. 7204431Sraj * 8204431Sraj * a) This library is free software; you can redistribute it and/or 9204431Sraj * modify it under the terms of the GNU General Public License as 10204431Sraj * published by the Free Software Foundation; either version 2 of the 11204431Sraj * License, or (at your option) any later version. 12204431Sraj * 13204431Sraj * This library is distributed in the hope that it will be useful, 14204431Sraj * but WITHOUT ANY WARRANTY; without even the implied warranty of 15204431Sraj * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16204431Sraj * GNU General Public License for more details. 17204431Sraj * 18204431Sraj * You should have received a copy of the GNU General Public 19204431Sraj * License along with this library; if not, write to the Free 20204431Sraj * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21204431Sraj * MA 02110-1301 USA 22204431Sraj * 23204431Sraj * Alternatively, 24204431Sraj * 25204431Sraj * b) Redistribution and use in source and binary forms, with or 26204431Sraj * without modification, are permitted provided that the following 27204431Sraj * conditions are met: 28204431Sraj * 29204431Sraj * 1. Redistributions of source code must retain the above 30204431Sraj * copyright notice, this list of conditions and the following 31204431Sraj * disclaimer. 32204431Sraj * 2. Redistributions in binary form must reproduce the above 33204431Sraj * copyright notice, this list of conditions and the following 34204431Sraj * disclaimer in the documentation and/or other materials 35204431Sraj * provided with the distribution. 36204431Sraj * 37204431Sraj * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38204431Sraj * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39204431Sraj * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40204431Sraj * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41204431Sraj * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42204431Sraj * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43204431Sraj * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44204431Sraj * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45204431Sraj * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46204431Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47204431Sraj * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48204431Sraj * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49204431Sraj * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50204431Sraj */ 51204431Sraj#include "libfdt_env.h" 52204431Sraj 53204431Sraj#include <fdt.h> 54204431Sraj#include <libfdt.h> 55204431Sraj 56204431Sraj#include "libfdt_internal.h" 57204431Sraj 58204431Srajstatic int _fdt_nodename_eq(const void *fdt, int offset, 59204431Sraj const char *s, int len) 60204431Sraj{ 61204431Sraj const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); 62204431Sraj 63204431Sraj if (! p) 64204431Sraj /* short match */ 65204431Sraj return 0; 66204431Sraj 67204431Sraj if (memcmp(p, s, len) != 0) 68204431Sraj return 0; 69204431Sraj 70204431Sraj if (p[len] == '\0') 71204431Sraj return 1; 72204431Sraj else if (!memchr(s, '@', len) && (p[len] == '@')) 73204431Sraj return 1; 74204431Sraj else 75204431Sraj return 0; 76204431Sraj} 77204431Sraj 78204431Srajconst char *fdt_string(const void *fdt, int stroffset) 79204431Sraj{ 80204431Sraj return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 81204431Sraj} 82204431Sraj 83204433Srajstatic int _fdt_string_eq(const void *fdt, int stroffset, 84204433Sraj const char *s, int len) 85204433Sraj{ 86204433Sraj const char *p = fdt_string(fdt, stroffset); 87204433Sraj 88204433Sraj return (strlen(p) == len) && (memcmp(p, s, len) == 0); 89204433Sraj} 90204433Sraj 91204431Srajint fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 92204431Sraj{ 93204431Sraj FDT_CHECK_HEADER(fdt); 94204431Sraj *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); 95204431Sraj *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); 96204431Sraj return 0; 97204431Sraj} 98204431Sraj 99204431Srajint fdt_num_mem_rsv(const void *fdt) 100204431Sraj{ 101204431Sraj int i = 0; 102204431Sraj 103204431Sraj while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) 104204431Sraj i++; 105204431Sraj return i; 106204431Sraj} 107204431Sraj 108204431Srajint fdt_subnode_offset_namelen(const void *fdt, int offset, 109204431Sraj const char *name, int namelen) 110204431Sraj{ 111204431Sraj int depth; 112204431Sraj 113204431Sraj FDT_CHECK_HEADER(fdt); 114204431Sraj 115204431Sraj for (depth = 0; 116204433Sraj (offset >= 0) && (depth >= 0); 117204433Sraj offset = fdt_next_node(fdt, offset, &depth)) 118204433Sraj if ((depth == 1) 119204433Sraj && _fdt_nodename_eq(fdt, offset, name, namelen)) 120204431Sraj return offset; 121204431Sraj 122204433Sraj if (depth < 0) 123204433Sraj return -FDT_ERR_NOTFOUND; 124204431Sraj return offset; /* error */ 125204431Sraj} 126204431Sraj 127204431Srajint fdt_subnode_offset(const void *fdt, int parentoffset, 128204431Sraj const char *name) 129204431Sraj{ 130204431Sraj return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 131204431Sraj} 132204431Sraj 133204431Srajint fdt_path_offset(const void *fdt, const char *path) 134204431Sraj{ 135204431Sraj const char *end = path + strlen(path); 136204431Sraj const char *p = path; 137204431Sraj int offset = 0; 138204431Sraj 139204431Sraj FDT_CHECK_HEADER(fdt); 140204431Sraj 141204433Sraj /* see if we have an alias */ 142204433Sraj if (*path != '/') { 143204433Sraj const char *q = strchr(path, '/'); 144204431Sraj 145204433Sraj if (!q) 146204433Sraj q = end; 147204433Sraj 148204433Sraj p = fdt_get_alias_namelen(fdt, p, q - p); 149204433Sraj if (!p) 150204433Sraj return -FDT_ERR_BADPATH; 151204433Sraj offset = fdt_path_offset(fdt, p); 152204433Sraj 153204433Sraj p = q; 154204433Sraj } 155204433Sraj 156204431Sraj while (*p) { 157204431Sraj const char *q; 158204431Sraj 159204431Sraj while (*p == '/') 160204431Sraj p++; 161204431Sraj if (! *p) 162204431Sraj return offset; 163204431Sraj q = strchr(p, '/'); 164204431Sraj if (! q) 165204431Sraj q = end; 166204431Sraj 167204431Sraj offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 168204431Sraj if (offset < 0) 169204431Sraj return offset; 170204431Sraj 171204431Sraj p = q; 172204431Sraj } 173204431Sraj 174204431Sraj return offset; 175204431Sraj} 176204431Sraj 177204431Srajconst char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 178204431Sraj{ 179204431Sraj const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 180204431Sraj int err; 181204431Sraj 182204431Sraj if (((err = fdt_check_header(fdt)) != 0) 183204431Sraj || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 184204431Sraj goto fail; 185204431Sraj 186204431Sraj if (len) 187204431Sraj *len = strlen(nh->name); 188204431Sraj 189204431Sraj return nh->name; 190204431Sraj 191204431Sraj fail: 192204431Sraj if (len) 193204431Sraj *len = err; 194204431Sraj return NULL; 195204431Sraj} 196204431Sraj 197204433Srajconst struct fdt_property *fdt_get_property_namelen(const void *fdt, 198204433Sraj int nodeoffset, 199204433Sraj const char *name, 200204433Sraj int namelen, int *lenp) 201204431Sraj{ 202204431Sraj uint32_t tag; 203204431Sraj const struct fdt_property *prop; 204204431Sraj int offset, nextoffset; 205204431Sraj int err; 206204431Sraj 207204431Sraj if (((err = fdt_check_header(fdt)) != 0) 208204431Sraj || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 209204431Sraj goto fail; 210204431Sraj 211204431Sraj nextoffset = err; 212204431Sraj do { 213204431Sraj offset = nextoffset; 214204431Sraj 215204431Sraj tag = fdt_next_tag(fdt, offset, &nextoffset); 216204431Sraj switch (tag) { 217204431Sraj case FDT_END: 218204433Sraj if (nextoffset < 0) 219204433Sraj err = nextoffset; 220204433Sraj else 221204433Sraj /* FDT_END tag with unclosed nodes */ 222204433Sraj err = -FDT_ERR_BADSTRUCTURE; 223204431Sraj goto fail; 224204431Sraj 225204431Sraj case FDT_PROP: 226204433Sraj prop = _fdt_offset_ptr(fdt, offset); 227204433Sraj if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 228204433Sraj name, namelen)) { 229204431Sraj /* Found it! */ 230204431Sraj if (lenp) 231204433Sraj *lenp = fdt32_to_cpu(prop->len); 232204431Sraj 233204431Sraj return prop; 234204431Sraj } 235204431Sraj break; 236204431Sraj } 237204431Sraj } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE)); 238204431Sraj 239204431Sraj err = -FDT_ERR_NOTFOUND; 240204431Sraj fail: 241204431Sraj if (lenp) 242204431Sraj *lenp = err; 243204431Sraj return NULL; 244204431Sraj} 245204431Sraj 246204433Srajconst struct fdt_property *fdt_get_property(const void *fdt, 247204433Sraj int nodeoffset, 248204433Sraj const char *name, int *lenp) 249204431Sraj{ 250204433Sraj return fdt_get_property_namelen(fdt, nodeoffset, name, 251204433Sraj strlen(name), lenp); 252204433Sraj} 253204433Sraj 254204433Srajconst void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 255204433Sraj const char *name, int namelen, int *lenp) 256204433Sraj{ 257204431Sraj const struct fdt_property *prop; 258204431Sraj 259204433Sraj prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 260204431Sraj if (! prop) 261204431Sraj return NULL; 262204431Sraj 263204431Sraj return prop->data; 264204431Sraj} 265204431Sraj 266204433Srajconst void *fdt_getprop(const void *fdt, int nodeoffset, 267204433Sraj const char *name, int *lenp) 268204433Sraj{ 269204433Sraj return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 270204433Sraj} 271204433Sraj 272204431Srajuint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 273204431Sraj{ 274204431Sraj const uint32_t *php; 275204431Sraj int len; 276204431Sraj 277204433Sraj /* FIXME: This is a bit sub-optimal, since we potentially scan 278204433Sraj * over all the properties twice. */ 279204433Sraj php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 280204433Sraj if (!php || (len != sizeof(*php))) { 281204433Sraj php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 282204433Sraj if (!php || (len != sizeof(*php))) 283204433Sraj return 0; 284204433Sraj } 285204431Sraj 286204431Sraj return fdt32_to_cpu(*php); 287204431Sraj} 288204431Sraj 289204433Srajconst char *fdt_get_alias_namelen(const void *fdt, 290204433Sraj const char *name, int namelen) 291204433Sraj{ 292204433Sraj int aliasoffset; 293204433Sraj 294204433Sraj aliasoffset = fdt_path_offset(fdt, "/aliases"); 295204433Sraj if (aliasoffset < 0) 296204433Sraj return NULL; 297204433Sraj 298204433Sraj return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 299204433Sraj} 300204433Sraj 301204433Srajconst char *fdt_get_alias(const void *fdt, const char *name) 302204433Sraj{ 303204433Sraj return fdt_get_alias_namelen(fdt, name, strlen(name)); 304204433Sraj} 305204433Sraj 306204431Srajint fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 307204431Sraj{ 308204431Sraj int pdepth = 0, p = 0; 309204431Sraj int offset, depth, namelen; 310204431Sraj const char *name; 311204431Sraj 312204431Sraj FDT_CHECK_HEADER(fdt); 313204431Sraj 314204431Sraj if (buflen < 2) 315204431Sraj return -FDT_ERR_NOSPACE; 316204431Sraj 317204431Sraj for (offset = 0, depth = 0; 318204431Sraj (offset >= 0) && (offset <= nodeoffset); 319204431Sraj offset = fdt_next_node(fdt, offset, &depth)) { 320204431Sraj while (pdepth > depth) { 321204431Sraj do { 322204431Sraj p--; 323204431Sraj } while (buf[p-1] != '/'); 324204431Sraj pdepth--; 325204431Sraj } 326204431Sraj 327204433Sraj if (pdepth >= depth) { 328204433Sraj name = fdt_get_name(fdt, offset, &namelen); 329204433Sraj if (!name) 330204433Sraj return namelen; 331204433Sraj if ((p + namelen + 1) <= buflen) { 332204433Sraj memcpy(buf + p, name, namelen); 333204433Sraj p += namelen; 334204433Sraj buf[p++] = '/'; 335204433Sraj pdepth++; 336204433Sraj } 337204431Sraj } 338204431Sraj 339204431Sraj if (offset == nodeoffset) { 340204431Sraj if (pdepth < (depth + 1)) 341204431Sraj return -FDT_ERR_NOSPACE; 342204431Sraj 343204431Sraj if (p > 1) /* special case so that root path is "/", not "" */ 344204431Sraj p--; 345204431Sraj buf[p] = '\0'; 346204433Sraj return 0; 347204431Sraj } 348204431Sraj } 349204431Sraj 350204431Sraj if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 351204431Sraj return -FDT_ERR_BADOFFSET; 352204431Sraj else if (offset == -FDT_ERR_BADOFFSET) 353204431Sraj return -FDT_ERR_BADSTRUCTURE; 354204431Sraj 355204431Sraj return offset; /* error from fdt_next_node() */ 356204431Sraj} 357204431Sraj 358204431Srajint fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 359204431Sraj int supernodedepth, int *nodedepth) 360204431Sraj{ 361204431Sraj int offset, depth; 362204431Sraj int supernodeoffset = -FDT_ERR_INTERNAL; 363204431Sraj 364204431Sraj FDT_CHECK_HEADER(fdt); 365204431Sraj 366204431Sraj if (supernodedepth < 0) 367204431Sraj return -FDT_ERR_NOTFOUND; 368204431Sraj 369204431Sraj for (offset = 0, depth = 0; 370204431Sraj (offset >= 0) && (offset <= nodeoffset); 371204431Sraj offset = fdt_next_node(fdt, offset, &depth)) { 372204431Sraj if (depth == supernodedepth) 373204431Sraj supernodeoffset = offset; 374204431Sraj 375204431Sraj if (offset == nodeoffset) { 376204431Sraj if (nodedepth) 377204431Sraj *nodedepth = depth; 378204431Sraj 379204431Sraj if (supernodedepth > depth) 380204431Sraj return -FDT_ERR_NOTFOUND; 381204431Sraj else 382204431Sraj return supernodeoffset; 383204431Sraj } 384204431Sraj } 385204431Sraj 386204431Sraj if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 387204431Sraj return -FDT_ERR_BADOFFSET; 388204431Sraj else if (offset == -FDT_ERR_BADOFFSET) 389204431Sraj return -FDT_ERR_BADSTRUCTURE; 390204431Sraj 391204431Sraj return offset; /* error from fdt_next_node() */ 392204431Sraj} 393204431Sraj 394204431Srajint fdt_node_depth(const void *fdt, int nodeoffset) 395204431Sraj{ 396204431Sraj int nodedepth; 397204431Sraj int err; 398204431Sraj 399204431Sraj err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 400204431Sraj if (err) 401204431Sraj return (err < 0) ? err : -FDT_ERR_INTERNAL; 402204431Sraj return nodedepth; 403204431Sraj} 404204431Sraj 405204431Srajint fdt_parent_offset(const void *fdt, int nodeoffset) 406204431Sraj{ 407204431Sraj int nodedepth = fdt_node_depth(fdt, nodeoffset); 408204431Sraj 409204431Sraj if (nodedepth < 0) 410204431Sraj return nodedepth; 411204431Sraj return fdt_supernode_atdepth_offset(fdt, nodeoffset, 412204431Sraj nodedepth - 1, NULL); 413204431Sraj} 414204431Sraj 415204431Srajint fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 416204431Sraj const char *propname, 417204431Sraj const void *propval, int proplen) 418204431Sraj{ 419204431Sraj int offset; 420204431Sraj const void *val; 421204431Sraj int len; 422204431Sraj 423204431Sraj FDT_CHECK_HEADER(fdt); 424204431Sraj 425204431Sraj /* FIXME: The algorithm here is pretty horrible: we scan each 426204431Sraj * property of a node in fdt_getprop(), then if that didn't 427204431Sraj * find what we want, we scan over them again making our way 428204431Sraj * to the next node. Still it's the easiest to implement 429204431Sraj * approach; performance can come later. */ 430204431Sraj for (offset = fdt_next_node(fdt, startoffset, NULL); 431204431Sraj offset >= 0; 432204431Sraj offset = fdt_next_node(fdt, offset, NULL)) { 433204431Sraj val = fdt_getprop(fdt, offset, propname, &len); 434204431Sraj if (val && (len == proplen) 435204431Sraj && (memcmp(val, propval, len) == 0)) 436204431Sraj return offset; 437204431Sraj } 438204431Sraj 439204431Sraj return offset; /* error from fdt_next_node() */ 440204431Sraj} 441204431Sraj 442204431Srajint fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 443204431Sraj{ 444204433Sraj int offset; 445204433Sraj 446204431Sraj if ((phandle == 0) || (phandle == -1)) 447204431Sraj return -FDT_ERR_BADPHANDLE; 448204433Sraj 449204433Sraj FDT_CHECK_HEADER(fdt); 450204433Sraj 451204433Sraj /* FIXME: The algorithm here is pretty horrible: we 452204433Sraj * potentially scan each property of a node in 453204433Sraj * fdt_get_phandle(), then if that didn't find what 454204433Sraj * we want, we scan over them again making our way to the next 455204433Sraj * node. Still it's the easiest to implement approach; 456204433Sraj * performance can come later. */ 457204433Sraj for (offset = fdt_next_node(fdt, -1, NULL); 458204433Sraj offset >= 0; 459204433Sraj offset = fdt_next_node(fdt, offset, NULL)) { 460204433Sraj if (fdt_get_phandle(fdt, offset) == phandle) 461204433Sraj return offset; 462204433Sraj } 463204433Sraj 464204433Sraj return offset; /* error from fdt_next_node() */ 465204431Sraj} 466204431Sraj 467204433Srajstatic int _fdt_stringlist_contains(const char *strlist, int listlen, 468204433Sraj const char *str) 469204431Sraj{ 470204431Sraj int len = strlen(str); 471204431Sraj const char *p; 472204431Sraj 473204431Sraj while (listlen >= len) { 474204431Sraj if (memcmp(str, strlist, len+1) == 0) 475204431Sraj return 1; 476204431Sraj p = memchr(strlist, '\0', listlen); 477204431Sraj if (!p) 478204431Sraj return 0; /* malformed strlist.. */ 479204431Sraj listlen -= (p-strlist) + 1; 480204431Sraj strlist = p + 1; 481204431Sraj } 482204431Sraj return 0; 483204431Sraj} 484204431Sraj 485204431Srajint fdt_node_check_compatible(const void *fdt, int nodeoffset, 486204431Sraj const char *compatible) 487204431Sraj{ 488204431Sraj const void *prop; 489204431Sraj int len; 490204431Sraj 491204431Sraj prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 492204431Sraj if (!prop) 493204431Sraj return len; 494204433Sraj if (_fdt_stringlist_contains(prop, len, compatible)) 495204431Sraj return 0; 496204431Sraj else 497204431Sraj return 1; 498204431Sraj} 499204431Sraj 500204431Srajint fdt_node_offset_by_compatible(const void *fdt, int startoffset, 501204431Sraj const char *compatible) 502204431Sraj{ 503204431Sraj int offset, err; 504204431Sraj 505204431Sraj FDT_CHECK_HEADER(fdt); 506204431Sraj 507204431Sraj /* FIXME: The algorithm here is pretty horrible: we scan each 508204431Sraj * property of a node in fdt_node_check_compatible(), then if 509204431Sraj * that didn't find what we want, we scan over them again 510204431Sraj * making our way to the next node. Still it's the easiest to 511204431Sraj * implement approach; performance can come later. */ 512204431Sraj for (offset = fdt_next_node(fdt, startoffset, NULL); 513204431Sraj offset >= 0; 514204431Sraj offset = fdt_next_node(fdt, offset, NULL)) { 515204431Sraj err = fdt_node_check_compatible(fdt, offset, compatible); 516204431Sraj if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 517204431Sraj return err; 518204431Sraj else if (err == 0) 519204431Sraj return offset; 520204431Sraj } 521204431Sraj 522204431Sraj return offset; /* error from fdt_next_node() */ 523204431Sraj} 524