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 58 static 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 78 const char *fdt_string(const void *fdt, int stroffset) 79 { 80 return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 81 } 82 83 static 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 91 int 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 99 int 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 108 static 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 132 int 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 151 int 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 157 int 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 202 int fdt_path_offset(const void *fdt, const char *path) 203 { 204 return fdt_path_offset_namelen(fdt, path, strlen(path)); 205 } 206 207 const 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 227 int 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 237 int 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 245 const 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 266 const 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 290 const 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 298 const 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 310 const 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 323 const 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 329 uint32_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 346 const 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 358 const char *fdt_get_alias(const void *fdt, const char *name) 359 { 360 return fdt_get_alias_namelen(fdt, name, strlen(name)); 361 } 362 363 int 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 415 int 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 451 int 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 462 int 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 472 int 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 499 int 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 524 int 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 541 int 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 566 int 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 596 const 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 641 int 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 654 int 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