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