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 int olen; 62 const char *p = fdt_get_name(fdt, offset, &olen); 63 64 if (!p || olen < len) 65 /* short match */ 66 return 0; 67 68 if (memcmp(p, s, len) != 0) 69 return 0; 70 71 if (p[len] == '\0') 72 return 1; 73 else if (!memchr(s, '@', len) && (p[len] == '@')) 74 return 1; 75 else 76 return 0; 77 } 78 79 const char *fdt_string(const void *fdt, int stroffset) 80 { 81 return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 82 } 83 84 static int fdt_string_eq_(const void *fdt, int stroffset, 85 const char *s, int len) 86 { 87 const char *p = fdt_string(fdt, stroffset); 88 89 return (strlen(p) == len) && (memcmp(p, s, len) == 0); 90 } 91 92 uint32_t fdt_get_max_phandle(const void *fdt) 93 { 94 uint32_t max_phandle = 0; 95 int offset; 96 97 for (offset = fdt_next_node(fdt, -1, NULL);; 98 offset = fdt_next_node(fdt, offset, NULL)) { 99 uint32_t phandle; 100 101 if (offset == -FDT_ERR_NOTFOUND) 102 return max_phandle; 103 104 if (offset < 0) 105 return (uint32_t)-1; 106 107 phandle = fdt_get_phandle(fdt, offset); 108 if (phandle == (uint32_t)-1) 109 continue; 110 111 if (phandle > max_phandle) 112 max_phandle = phandle; 113 } 114 115 return 0; 116 } 117 118 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 119 { 120 FDT_CHECK_HEADER(fdt); 121 *address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address); 122 *size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size); 123 return 0; 124 } 125 126 int fdt_num_mem_rsv(const void *fdt) 127 { 128 int i = 0; 129 130 while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0) 131 i++; 132 return i; 133 } 134 135 static int nextprop_(const void *fdt, int offset) 136 { 137 uint32_t tag; 138 int nextoffset; 139 140 do { 141 tag = fdt_next_tag(fdt, offset, &nextoffset); 142 143 switch (tag) { 144 case FDT_END: 145 if (nextoffset >= 0) 146 return -FDT_ERR_BADSTRUCTURE; 147 else 148 return nextoffset; 149 150 case FDT_PROP: 151 return offset; 152 } 153 offset = nextoffset; 154 } while (tag == FDT_NOP); 155 156 return -FDT_ERR_NOTFOUND; 157 } 158 159 int fdt_subnode_offset_namelen(const void *fdt, int offset, 160 const char *name, int namelen) 161 { 162 int depth; 163 164 FDT_CHECK_HEADER(fdt); 165 166 for (depth = 0; 167 (offset >= 0) && (depth >= 0); 168 offset = fdt_next_node(fdt, offset, &depth)) 169 if ((depth == 1) 170 && fdt_nodename_eq_(fdt, offset, name, namelen)) 171 return offset; 172 173 if (depth < 0) 174 return -FDT_ERR_NOTFOUND; 175 return offset; /* error */ 176 } 177 178 int fdt_subnode_offset(const void *fdt, int parentoffset, 179 const char *name) 180 { 181 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 182 } 183 184 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) 185 { 186 const char *end = path + namelen; 187 const char *p = path; 188 int offset = 0; 189 190 FDT_CHECK_HEADER(fdt); 191 192 /* see if we have an alias */ 193 if (*path != '/') { 194 const char *q = memchr(path, '/', end - p); 195 196 if (!q) 197 q = end; 198 199 p = fdt_get_alias_namelen(fdt, p, q - p); 200 if (!p) 201 return -FDT_ERR_BADPATH; 202 offset = fdt_path_offset(fdt, p); 203 204 p = q; 205 } 206 207 while (p < end) { 208 const char *q; 209 210 while (*p == '/') { 211 p++; 212 if (p == end) 213 return offset; 214 } 215 q = memchr(p, '/', end - p); 216 if (! q) 217 q = end; 218 219 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 220 if (offset < 0) 221 return offset; 222 223 p = q; 224 } 225 226 return offset; 227 } 228 229 int fdt_path_offset(const void *fdt, const char *path) 230 { 231 return fdt_path_offset_namelen(fdt, path, strlen(path)); 232 } 233 234 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 235 { 236 const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); 237 const char *nameptr; 238 int err; 239 240 if (((err = fdt_check_header(fdt)) != 0) 241 || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) 242 goto fail; 243 244 nameptr = nh->name; 245 246 if (fdt_version(fdt) < 0x10) { 247 /* 248 * For old FDT versions, match the naming conventions of V16: 249 * give only the leaf name (after all /). The actual tree 250 * contents are loosely checked. 251 */ 252 const char *leaf; 253 leaf = strrchr(nameptr, '/'); 254 if (leaf == NULL) { 255 err = -FDT_ERR_BADSTRUCTURE; 256 goto fail; 257 } 258 nameptr = leaf+1; 259 } 260 261 if (len) 262 *len = strlen(nameptr); 263 264 return nameptr; 265 266 fail: 267 if (len) 268 *len = err; 269 return NULL; 270 } 271 272 int fdt_first_property_offset(const void *fdt, int nodeoffset) 273 { 274 int offset; 275 276 if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) 277 return offset; 278 279 return nextprop_(fdt, offset); 280 } 281 282 int fdt_next_property_offset(const void *fdt, int offset) 283 { 284 if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) 285 return offset; 286 287 return nextprop_(fdt, offset); 288 } 289 290 static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, 291 int offset, 292 int *lenp) 293 { 294 int err; 295 const struct fdt_property *prop; 296 297 if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) { 298 if (lenp) 299 *lenp = err; 300 return NULL; 301 } 302 303 prop = fdt_offset_ptr_(fdt, offset); 304 305 if (lenp) 306 *lenp = fdt32_to_cpu(prop->len); 307 308 return prop; 309 } 310 311 const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 312 int offset, 313 int *lenp) 314 { 315 /* Prior to version 16, properties may need realignment 316 * and this API does not work. fdt_getprop_*() will, however. */ 317 318 if (fdt_version(fdt) < 0x10) { 319 if (lenp) 320 *lenp = -FDT_ERR_BADVERSION; 321 return NULL; 322 } 323 324 return fdt_get_property_by_offset_(fdt, offset, lenp); 325 } 326 327 static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, 328 int offset, 329 const char *name, 330 int namelen, 331 int *lenp, 332 int *poffset) 333 { 334 for (offset = fdt_first_property_offset(fdt, offset); 335 (offset >= 0); 336 (offset = fdt_next_property_offset(fdt, offset))) { 337 const struct fdt_property *prop; 338 339 if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { 340 offset = -FDT_ERR_INTERNAL; 341 break; 342 } 343 if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff), 344 name, namelen)) { 345 if (poffset) 346 *poffset = offset; 347 return prop; 348 } 349 } 350 351 if (lenp) 352 *lenp = offset; 353 return NULL; 354 } 355 356 357 const struct fdt_property *fdt_get_property_namelen(const void *fdt, 358 int offset, 359 const char *name, 360 int namelen, int *lenp) 361 { 362 /* Prior to version 16, properties may need realignment 363 * and this API does not work. fdt_getprop_*() will, however. */ 364 if (fdt_version(fdt) < 0x10) { 365 if (lenp) 366 *lenp = -FDT_ERR_BADVERSION; 367 return NULL; 368 } 369 370 return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, 371 NULL); 372 } 373 374 375 const struct fdt_property *fdt_get_property(const void *fdt, 376 int nodeoffset, 377 const char *name, int *lenp) 378 { 379 return fdt_get_property_namelen(fdt, nodeoffset, name, 380 strlen(name), lenp); 381 } 382 383 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 384 const char *name, int namelen, int *lenp) 385 { 386 int poffset; 387 const struct fdt_property *prop; 388 389 prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, 390 &poffset); 391 if (!prop) 392 return NULL; 393 394 /* Handle realignment */ 395 if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && 396 fdt32_to_cpu(prop->len) >= 8) 397 return prop->data + 4; 398 return prop->data; 399 } 400 401 const void *fdt_getprop_by_offset(const void *fdt, int offset, 402 const char **namep, int *lenp) 403 { 404 const struct fdt_property *prop; 405 406 prop = fdt_get_property_by_offset_(fdt, offset, lenp); 407 if (!prop) 408 return NULL; 409 if (namep) 410 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 411 412 /* Handle realignment */ 413 if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && 414 fdt32_to_cpu(prop->len) >= 8) 415 return prop->data + 4; 416 return prop->data; 417 } 418 419 const void *fdt_getprop(const void *fdt, int nodeoffset, 420 const char *name, int *lenp) 421 { 422 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 423 } 424 425 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 426 { 427 const fdt32_t *php; 428 int len; 429 430 /* FIXME: This is a bit sub-optimal, since we potentially scan 431 * over all the properties twice. */ 432 php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 433 if (!php || (len != sizeof(*php))) { 434 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 435 if (!php || (len != sizeof(*php))) 436 return 0; 437 } 438 439 return fdt32_to_cpu(*php); 440 } 441 442 const char *fdt_get_alias_namelen(const void *fdt, 443 const char *name, int namelen) 444 { 445 int aliasoffset; 446 447 aliasoffset = fdt_path_offset(fdt, "/aliases"); 448 if (aliasoffset < 0) 449 return NULL; 450 451 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 452 } 453 454 const char *fdt_get_alias(const void *fdt, const char *name) 455 { 456 return fdt_get_alias_namelen(fdt, name, strlen(name)); 457 } 458 459 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 460 { 461 int pdepth = 0, p = 0; 462 int offset, depth, namelen; 463 const char *name; 464 465 FDT_CHECK_HEADER(fdt); 466 467 if (buflen < 2) 468 return -FDT_ERR_NOSPACE; 469 470 for (offset = 0, depth = 0; 471 (offset >= 0) && (offset <= nodeoffset); 472 offset = fdt_next_node(fdt, offset, &depth)) { 473 while (pdepth > depth) { 474 do { 475 p--; 476 } while (buf[p-1] != '/'); 477 pdepth--; 478 } 479 480 if (pdepth >= depth) { 481 name = fdt_get_name(fdt, offset, &namelen); 482 if (!name) 483 return namelen; 484 if ((p + namelen + 1) <= buflen) { 485 memcpy(buf + p, name, namelen); 486 p += namelen; 487 buf[p++] = '/'; 488 pdepth++; 489 } 490 } 491 492 if (offset == nodeoffset) { 493 if (pdepth < (depth + 1)) 494 return -FDT_ERR_NOSPACE; 495 496 if (p > 1) /* special case so that root path is "/", not "" */ 497 p--; 498 buf[p] = '\0'; 499 return 0; 500 } 501 } 502 503 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 504 return -FDT_ERR_BADOFFSET; 505 else if (offset == -FDT_ERR_BADOFFSET) 506 return -FDT_ERR_BADSTRUCTURE; 507 508 return offset; /* error from fdt_next_node() */ 509 } 510 511 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 512 int supernodedepth, int *nodedepth) 513 { 514 int offset, depth; 515 int supernodeoffset = -FDT_ERR_INTERNAL; 516 517 FDT_CHECK_HEADER(fdt); 518 519 if (supernodedepth < 0) 520 return -FDT_ERR_NOTFOUND; 521 522 for (offset = 0, depth = 0; 523 (offset >= 0) && (offset <= nodeoffset); 524 offset = fdt_next_node(fdt, offset, &depth)) { 525 if (depth == supernodedepth) 526 supernodeoffset = offset; 527 528 if (offset == nodeoffset) { 529 if (nodedepth) 530 *nodedepth = depth; 531 532 if (supernodedepth > depth) 533 return -FDT_ERR_NOTFOUND; 534 else 535 return supernodeoffset; 536 } 537 } 538 539 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 540 return -FDT_ERR_BADOFFSET; 541 else if (offset == -FDT_ERR_BADOFFSET) 542 return -FDT_ERR_BADSTRUCTURE; 543 544 return offset; /* error from fdt_next_node() */ 545 } 546 547 int fdt_node_depth(const void *fdt, int nodeoffset) 548 { 549 int nodedepth; 550 int err; 551 552 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 553 if (err) 554 return (err < 0) ? err : -FDT_ERR_INTERNAL; 555 return nodedepth; 556 } 557 558 int fdt_parent_offset(const void *fdt, int nodeoffset) 559 { 560 int nodedepth = fdt_node_depth(fdt, nodeoffset); 561 562 if (nodedepth < 0) 563 return nodedepth; 564 return fdt_supernode_atdepth_offset(fdt, nodeoffset, 565 nodedepth - 1, NULL); 566 } 567 568 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 569 const char *propname, 570 const void *propval, int proplen) 571 { 572 int offset; 573 const void *val; 574 int len; 575 576 FDT_CHECK_HEADER(fdt); 577 578 /* FIXME: The algorithm here is pretty horrible: we scan each 579 * property of a node in fdt_getprop(), then if that didn't 580 * find what we want, we scan over them again making our way 581 * to the next node. Still it's the easiest to implement 582 * approach; performance can come later. */ 583 for (offset = fdt_next_node(fdt, startoffset, NULL); 584 offset >= 0; 585 offset = fdt_next_node(fdt, offset, NULL)) { 586 val = fdt_getprop(fdt, offset, propname, &len); 587 if (val && (len == proplen) 588 && (memcmp(val, propval, len) == 0)) 589 return offset; 590 } 591 592 return offset; /* error from fdt_next_node() */ 593 } 594 595 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 596 { 597 int offset; 598 599 if ((phandle == 0) || (phandle == -1)) 600 return -FDT_ERR_BADPHANDLE; 601 602 FDT_CHECK_HEADER(fdt); 603 604 /* FIXME: The algorithm here is pretty horrible: we 605 * potentially scan each property of a node in 606 * fdt_get_phandle(), then if that didn't find what 607 * we want, we scan over them again making our way to the next 608 * node. Still it's the easiest to implement approach; 609 * performance can come later. */ 610 for (offset = fdt_next_node(fdt, -1, NULL); 611 offset >= 0; 612 offset = fdt_next_node(fdt, offset, NULL)) { 613 if (fdt_get_phandle(fdt, offset) == phandle) 614 return offset; 615 } 616 617 return offset; /* error from fdt_next_node() */ 618 } 619 620 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 621 { 622 int len = strlen(str); 623 const char *p; 624 625 while (listlen >= len) { 626 if (memcmp(str, strlist, len+1) == 0) 627 return 1; 628 p = memchr(strlist, '\0', listlen); 629 if (!p) 630 return 0; /* malformed strlist.. */ 631 listlen -= (p-strlist) + 1; 632 strlist = p + 1; 633 } 634 return 0; 635 } 636 637 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 638 { 639 const char *list, *end; 640 int length, count = 0; 641 642 list = fdt_getprop(fdt, nodeoffset, property, &length); 643 if (!list) 644 return length; 645 646 end = list + length; 647 648 while (list < end) { 649 length = strnlen(list, end - list) + 1; 650 651 /* Abort if the last string isn't properly NUL-terminated. */ 652 if (list + length > end) 653 return -FDT_ERR_BADVALUE; 654 655 list += length; 656 count++; 657 } 658 659 return count; 660 } 661 662 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 663 const char *string) 664 { 665 int length, len, idx = 0; 666 const char *list, *end; 667 668 list = fdt_getprop(fdt, nodeoffset, property, &length); 669 if (!list) 670 return length; 671 672 len = strlen(string) + 1; 673 end = list + length; 674 675 while (list < end) { 676 length = strnlen(list, end - list) + 1; 677 678 /* Abort if the last string isn't properly NUL-terminated. */ 679 if (list + length > end) 680 return -FDT_ERR_BADVALUE; 681 682 if (length == len && memcmp(list, string, length) == 0) 683 return idx; 684 685 list += length; 686 idx++; 687 } 688 689 return -FDT_ERR_NOTFOUND; 690 } 691 692 const char *fdt_stringlist_get(const void *fdt, int nodeoffset, 693 const char *property, int idx, 694 int *lenp) 695 { 696 const char *list, *end; 697 int length; 698 699 list = fdt_getprop(fdt, nodeoffset, property, &length); 700 if (!list) { 701 if (lenp) 702 *lenp = length; 703 704 return NULL; 705 } 706 707 end = list + length; 708 709 while (list < end) { 710 length = strnlen(list, end - list) + 1; 711 712 /* Abort if the last string isn't properly NUL-terminated. */ 713 if (list + length > end) { 714 if (lenp) 715 *lenp = -FDT_ERR_BADVALUE; 716 717 return NULL; 718 } 719 720 if (idx == 0) { 721 if (lenp) 722 *lenp = length - 1; 723 724 return list; 725 } 726 727 list += length; 728 idx--; 729 } 730 731 if (lenp) 732 *lenp = -FDT_ERR_NOTFOUND; 733 734 return NULL; 735 } 736 737 int fdt_node_check_compatible(const void *fdt, int nodeoffset, 738 const char *compatible) 739 { 740 const void *prop; 741 int len; 742 743 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 744 if (!prop) 745 return len; 746 747 return !fdt_stringlist_contains(prop, len, compatible); 748 } 749 750 int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 751 const char *compatible) 752 { 753 int offset, err; 754 755 FDT_CHECK_HEADER(fdt); 756 757 /* FIXME: The algorithm here is pretty horrible: we scan each 758 * property of a node in fdt_node_check_compatible(), then if 759 * that didn't find what we want, we scan over them again 760 * making our way to the next node. Still it's the easiest to 761 * implement approach; performance can come later. */ 762 for (offset = fdt_next_node(fdt, startoffset, NULL); 763 offset >= 0; 764 offset = fdt_next_node(fdt, offset, NULL)) { 765 err = fdt_node_check_compatible(fdt, offset, compatible); 766 if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 767 return err; 768 else if (err == 0) 769 return offset; 770 } 771 772 return offset; /* error from fdt_next_node() */ 773 } 774