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