1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) 2 /* 3 * libfdt - Flat Device Tree manipulation 4 * Copyright (C) 2006 David Gibson, IBM Corporation. 5 */ 6 #include "libfdt_env.h" 7 8 #include <fdt.h> 9 #include <libfdt.h> 10 11 #include "libfdt_internal.h" 12 13 static int fdt_nodename_eq_(const void *fdt, int offset, 14 const char *s, int len) 15 { 16 int olen; 17 const char *p = fdt_get_name(fdt, offset, &olen); 18 19 if (!p || olen < len) 20 /* short match */ 21 return 0; 22 23 if (memcmp(p, s, len) != 0) 24 return 0; 25 26 if (p[len] == '\0') 27 return 1; 28 else if (!memchr(s, '@', len) && (p[len] == '@')) 29 return 1; 30 else 31 return 0; 32 } 33 34 const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) 35 { 36 uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt); 37 size_t len; 38 int err; 39 const char *s, *n; 40 41 err = fdt_ro_probe_(fdt); 42 if (err != 0) 43 goto fail; 44 45 err = -FDT_ERR_BADOFFSET; 46 if (absoffset >= fdt_totalsize(fdt)) 47 goto fail; 48 len = fdt_totalsize(fdt) - absoffset; 49 50 if (fdt_magic(fdt) == FDT_MAGIC) { 51 if (stroffset < 0) 52 goto fail; 53 if (fdt_version(fdt) >= 17) { 54 if (stroffset >= fdt_size_dt_strings(fdt)) 55 goto fail; 56 if ((fdt_size_dt_strings(fdt) - stroffset) < len) 57 len = fdt_size_dt_strings(fdt) - stroffset; 58 } 59 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 60 if ((stroffset >= 0) 61 || (stroffset < -fdt_size_dt_strings(fdt))) 62 goto fail; 63 if ((-stroffset) < len) 64 len = -stroffset; 65 } else { 66 err = -FDT_ERR_INTERNAL; 67 goto fail; 68 } 69 70 s = (const char *)fdt + absoffset; 71 n = memchr(s, '\0', len); 72 if (!n) { 73 /* missing terminating NULL */ 74 err = -FDT_ERR_TRUNCATED; 75 goto fail; 76 } 77 78 if (lenp) 79 *lenp = n - s; 80 return s; 81 82 fail: 83 if (lenp) 84 *lenp = err; 85 return NULL; 86 } 87 88 const char *fdt_string(const void *fdt, int stroffset) 89 { 90 return fdt_get_string(fdt, stroffset, NULL); 91 } 92 93 static int fdt_string_eq_(const void *fdt, int stroffset, 94 const char *s, int len) 95 { 96 int slen; 97 const char *p = fdt_get_string(fdt, stroffset, &slen); 98 99 return p && (slen == len) && (memcmp(p, s, len) == 0); 100 } 101 102 int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) 103 { 104 uint32_t max = 0; 105 int offset = -1; 106 107 while (true) { 108 uint32_t value; 109 110 offset = fdt_next_node(fdt, offset, NULL); 111 if (offset < 0) { 112 if (offset == -FDT_ERR_NOTFOUND) 113 break; 114 115 return offset; 116 } 117 118 value = fdt_get_phandle(fdt, offset); 119 120 if (value > max) 121 max = value; 122 } 123 124 if (phandle) 125 *phandle = max; 126 127 return 0; 128 } 129 130 int fdt_generate_phandle(const void *fdt, uint32_t *phandle) 131 { 132 uint32_t max; 133 int err; 134 135 err = fdt_find_max_phandle(fdt, &max); 136 if (err < 0) 137 return err; 138 139 if (max == FDT_MAX_PHANDLE) 140 return -FDT_ERR_NOPHANDLES; 141 142 if (phandle) 143 *phandle = max + 1; 144 145 return 0; 146 } 147 148 static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) 149 { 150 int offset = n * sizeof(struct fdt_reserve_entry); 151 int absoffset = fdt_off_mem_rsvmap(fdt) + offset; 152 153 if (absoffset < fdt_off_mem_rsvmap(fdt)) 154 return NULL; 155 if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry)) 156 return NULL; 157 return fdt_mem_rsv_(fdt, n); 158 } 159 160 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 161 { 162 const struct fdt_reserve_entry *re; 163 164 FDT_RO_PROBE(fdt); 165 re = fdt_mem_rsv(fdt, n); 166 if (!re) 167 return -FDT_ERR_BADOFFSET; 168 169 *address = fdt64_ld(&re->address); 170 *size = fdt64_ld(&re->size); 171 return 0; 172 } 173 174 int fdt_num_mem_rsv(const void *fdt) 175 { 176 int i; 177 const struct fdt_reserve_entry *re; 178 179 for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { 180 if (fdt64_ld(&re->size) == 0) 181 return i; 182 } 183 return -FDT_ERR_TRUNCATED; 184 } 185 186 static int nextprop_(const void *fdt, int offset) 187 { 188 uint32_t tag; 189 int nextoffset; 190 191 do { 192 tag = fdt_next_tag(fdt, offset, &nextoffset); 193 194 switch (tag) { 195 case FDT_END: 196 if (nextoffset >= 0) 197 return -FDT_ERR_BADSTRUCTURE; 198 else 199 return nextoffset; 200 201 case FDT_PROP: 202 return offset; 203 } 204 offset = nextoffset; 205 } while (tag == FDT_NOP); 206 207 return -FDT_ERR_NOTFOUND; 208 } 209 210 int fdt_subnode_offset_namelen(const void *fdt, int offset, 211 const char *name, int namelen) 212 { 213 int depth; 214 215 FDT_RO_PROBE(fdt); 216 217 for (depth = 0; 218 (offset >= 0) && (depth >= 0); 219 offset = fdt_next_node(fdt, offset, &depth)) 220 if ((depth == 1) 221 && fdt_nodename_eq_(fdt, offset, name, namelen)) 222 return offset; 223 224 if (depth < 0) 225 return -FDT_ERR_NOTFOUND; 226 return offset; /* error */ 227 } 228 229 int fdt_subnode_offset(const void *fdt, int parentoffset, 230 const char *name) 231 { 232 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 233 } 234 235 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) 236 { 237 const char *end = path + namelen; 238 const char *p = path; 239 int offset = 0; 240 241 FDT_RO_PROBE(fdt); 242 243 /* see if we have an alias */ 244 if (*path != '/') { 245 const char *q = memchr(path, '/', end - p); 246 247 if (!q) 248 q = end; 249 250 p = fdt_get_alias_namelen(fdt, p, q - p); 251 if (!p) 252 return -FDT_ERR_BADPATH; 253 offset = fdt_path_offset(fdt, p); 254 255 p = q; 256 } 257 258 while (p < end) { 259 const char *q; 260 261 while (*p == '/') { 262 p++; 263 if (p == end) 264 return offset; 265 } 266 q = memchr(p, '/', end - p); 267 if (! q) 268 q = end; 269 270 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 271 if (offset < 0) 272 return offset; 273 274 p = q; 275 } 276 277 return offset; 278 } 279 280 int fdt_path_offset(const void *fdt, const char *path) 281 { 282 return fdt_path_offset_namelen(fdt, path, strlen(path)); 283 } 284 285 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 286 { 287 const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); 288 const char *nameptr; 289 int err; 290 291 if (((err = fdt_ro_probe_(fdt)) != 0) 292 || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) 293 goto fail; 294 295 nameptr = nh->name; 296 297 if (fdt_version(fdt) < 0x10) { 298 /* 299 * For old FDT versions, match the naming conventions of V16: 300 * give only the leaf name (after all /). The actual tree 301 * contents are loosely checked. 302 */ 303 const char *leaf; 304 leaf = strrchr(nameptr, '/'); 305 if (leaf == NULL) { 306 err = -FDT_ERR_BADSTRUCTURE; 307 goto fail; 308 } 309 nameptr = leaf+1; 310 } 311 312 if (len) 313 *len = strlen(nameptr); 314 315 return nameptr; 316 317 fail: 318 if (len) 319 *len = err; 320 return NULL; 321 } 322 323 int fdt_first_property_offset(const void *fdt, int nodeoffset) 324 { 325 int offset; 326 327 if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) 328 return offset; 329 330 return nextprop_(fdt, offset); 331 } 332 333 int fdt_next_property_offset(const void *fdt, int offset) 334 { 335 if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) 336 return offset; 337 338 return nextprop_(fdt, offset); 339 } 340 341 static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, 342 int offset, 343 int *lenp) 344 { 345 int err; 346 const struct fdt_property *prop; 347 348 if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) { 349 if (lenp) 350 *lenp = err; 351 return NULL; 352 } 353 354 prop = fdt_offset_ptr_(fdt, offset); 355 356 if (lenp) 357 *lenp = fdt32_ld(&prop->len); 358 359 return prop; 360 } 361 362 const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 363 int offset, 364 int *lenp) 365 { 366 /* Prior to version 16, properties may need realignment 367 * and this API does not work. fdt_getprop_*() will, however. */ 368 369 if (fdt_version(fdt) < 0x10) { 370 if (lenp) 371 *lenp = -FDT_ERR_BADVERSION; 372 return NULL; 373 } 374 375 return fdt_get_property_by_offset_(fdt, offset, lenp); 376 } 377 378 static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, 379 int offset, 380 const char *name, 381 int namelen, 382 int *lenp, 383 int *poffset) 384 { 385 for (offset = fdt_first_property_offset(fdt, offset); 386 (offset >= 0); 387 (offset = fdt_next_property_offset(fdt, offset))) { 388 const struct fdt_property *prop; 389 390 if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { 391 offset = -FDT_ERR_INTERNAL; 392 break; 393 } 394 if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), 395 name, namelen)) { 396 if (poffset) 397 *poffset = offset; 398 return prop; 399 } 400 } 401 402 if (lenp) 403 *lenp = offset; 404 return NULL; 405 } 406 407 408 const struct fdt_property *fdt_get_property_namelen(const void *fdt, 409 int offset, 410 const char *name, 411 int namelen, int *lenp) 412 { 413 /* Prior to version 16, properties may need realignment 414 * and this API does not work. fdt_getprop_*() will, however. */ 415 if (fdt_version(fdt) < 0x10) { 416 if (lenp) 417 *lenp = -FDT_ERR_BADVERSION; 418 return NULL; 419 } 420 421 return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, 422 NULL); 423 } 424 425 426 const struct fdt_property *fdt_get_property(const void *fdt, 427 int nodeoffset, 428 const char *name, int *lenp) 429 { 430 return fdt_get_property_namelen(fdt, nodeoffset, name, 431 strlen(name), lenp); 432 } 433 434 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 435 const char *name, int namelen, int *lenp) 436 { 437 int poffset; 438 const struct fdt_property *prop; 439 440 prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, 441 &poffset); 442 if (!prop) 443 return NULL; 444 445 /* Handle realignment */ 446 if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && 447 fdt32_ld(&prop->len) >= 8) 448 return prop->data + 4; 449 return prop->data; 450 } 451 452 const void *fdt_getprop_by_offset(const void *fdt, int offset, 453 const char **namep, int *lenp) 454 { 455 const struct fdt_property *prop; 456 457 prop = fdt_get_property_by_offset_(fdt, offset, lenp); 458 if (!prop) 459 return NULL; 460 if (namep) { 461 const char *name; 462 int namelen; 463 name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), 464 &namelen); 465 if (!name) { 466 if (lenp) 467 *lenp = namelen; 468 return NULL; 469 } 470 *namep = name; 471 } 472 473 /* Handle realignment */ 474 if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && 475 fdt32_ld(&prop->len) >= 8) 476 return prop->data + 4; 477 return prop->data; 478 } 479 480 const void *fdt_getprop(const void *fdt, int nodeoffset, 481 const char *name, int *lenp) 482 { 483 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 484 } 485 486 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 487 { 488 const fdt32_t *php; 489 int len; 490 491 /* FIXME: This is a bit sub-optimal, since we potentially scan 492 * over all the properties twice. */ 493 php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 494 if (!php || (len != sizeof(*php))) { 495 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 496 if (!php || (len != sizeof(*php))) 497 return 0; 498 } 499 500 return fdt32_ld(php); 501 } 502 503 const char *fdt_get_alias_namelen(const void *fdt, 504 const char *name, int namelen) 505 { 506 int aliasoffset; 507 508 aliasoffset = fdt_path_offset(fdt, "/aliases"); 509 if (aliasoffset < 0) 510 return NULL; 511 512 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 513 } 514 515 const char *fdt_get_alias(const void *fdt, const char *name) 516 { 517 return fdt_get_alias_namelen(fdt, name, strlen(name)); 518 } 519 520 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 521 { 522 int pdepth = 0, p = 0; 523 int offset, depth, namelen; 524 const char *name; 525 526 FDT_RO_PROBE(fdt); 527 528 if (buflen < 2) 529 return -FDT_ERR_NOSPACE; 530 531 for (offset = 0, depth = 0; 532 (offset >= 0) && (offset <= nodeoffset); 533 offset = fdt_next_node(fdt, offset, &depth)) { 534 while (pdepth > depth) { 535 do { 536 p--; 537 } while (buf[p-1] != '/'); 538 pdepth--; 539 } 540 541 if (pdepth >= depth) { 542 name = fdt_get_name(fdt, offset, &namelen); 543 if (!name) 544 return namelen; 545 if ((p + namelen + 1) <= buflen) { 546 memcpy(buf + p, name, namelen); 547 p += namelen; 548 buf[p++] = '/'; 549 pdepth++; 550 } 551 } 552 553 if (offset == nodeoffset) { 554 if (pdepth < (depth + 1)) 555 return -FDT_ERR_NOSPACE; 556 557 if (p > 1) /* special case so that root path is "/", not "" */ 558 p--; 559 buf[p] = '\0'; 560 return 0; 561 } 562 } 563 564 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 565 return -FDT_ERR_BADOFFSET; 566 else if (offset == -FDT_ERR_BADOFFSET) 567 return -FDT_ERR_BADSTRUCTURE; 568 569 return offset; /* error from fdt_next_node() */ 570 } 571 572 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 573 int supernodedepth, int *nodedepth) 574 { 575 int offset, depth; 576 int supernodeoffset = -FDT_ERR_INTERNAL; 577 578 FDT_RO_PROBE(fdt); 579 580 if (supernodedepth < 0) 581 return -FDT_ERR_NOTFOUND; 582 583 for (offset = 0, depth = 0; 584 (offset >= 0) && (offset <= nodeoffset); 585 offset = fdt_next_node(fdt, offset, &depth)) { 586 if (depth == supernodedepth) 587 supernodeoffset = offset; 588 589 if (offset == nodeoffset) { 590 if (nodedepth) 591 *nodedepth = depth; 592 593 if (supernodedepth > depth) 594 return -FDT_ERR_NOTFOUND; 595 else 596 return supernodeoffset; 597 } 598 } 599 600 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 601 return -FDT_ERR_BADOFFSET; 602 else if (offset == -FDT_ERR_BADOFFSET) 603 return -FDT_ERR_BADSTRUCTURE; 604 605 return offset; /* error from fdt_next_node() */ 606 } 607 608 int fdt_node_depth(const void *fdt, int nodeoffset) 609 { 610 int nodedepth; 611 int err; 612 613 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 614 if (err) 615 return (err < 0) ? err : -FDT_ERR_INTERNAL; 616 return nodedepth; 617 } 618 619 int fdt_parent_offset(const void *fdt, int nodeoffset) 620 { 621 int nodedepth = fdt_node_depth(fdt, nodeoffset); 622 623 if (nodedepth < 0) 624 return nodedepth; 625 return fdt_supernode_atdepth_offset(fdt, nodeoffset, 626 nodedepth - 1, NULL); 627 } 628 629 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 630 const char *propname, 631 const void *propval, int proplen) 632 { 633 int offset; 634 const void *val; 635 int len; 636 637 FDT_RO_PROBE(fdt); 638 639 /* FIXME: The algorithm here is pretty horrible: we scan each 640 * property of a node in fdt_getprop(), then if that didn't 641 * find what we want, we scan over them again making our way 642 * to the next node. Still it's the easiest to implement 643 * approach; performance can come later. */ 644 for (offset = fdt_next_node(fdt, startoffset, NULL); 645 offset >= 0; 646 offset = fdt_next_node(fdt, offset, NULL)) { 647 val = fdt_getprop(fdt, offset, propname, &len); 648 if (val && (len == proplen) 649 && (memcmp(val, propval, len) == 0)) 650 return offset; 651 } 652 653 return offset; /* error from fdt_next_node() */ 654 } 655 656 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 657 { 658 int offset; 659 660 if ((phandle == 0) || (phandle == -1)) 661 return -FDT_ERR_BADPHANDLE; 662 663 FDT_RO_PROBE(fdt); 664 665 /* FIXME: The algorithm here is pretty horrible: we 666 * potentially scan each property of a node in 667 * fdt_get_phandle(), then if that didn't find what 668 * we want, we scan over them again making our way to the next 669 * node. Still it's the easiest to implement approach; 670 * performance can come later. */ 671 for (offset = fdt_next_node(fdt, -1, NULL); 672 offset >= 0; 673 offset = fdt_next_node(fdt, offset, NULL)) { 674 if (fdt_get_phandle(fdt, offset) == phandle) 675 return offset; 676 } 677 678 return offset; /* error from fdt_next_node() */ 679 } 680 681 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 682 { 683 int len = strlen(str); 684 const char *p; 685 686 while (listlen >= len) { 687 if (memcmp(str, strlist, len+1) == 0) 688 return 1; 689 p = memchr(strlist, '\0', listlen); 690 if (!p) 691 return 0; /* malformed strlist.. */ 692 listlen -= (p-strlist) + 1; 693 strlist = p + 1; 694 } 695 return 0; 696 } 697 698 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 699 { 700 const char *list, *end; 701 int length, count = 0; 702 703 list = fdt_getprop(fdt, nodeoffset, property, &length); 704 if (!list) 705 return length; 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 return -FDT_ERR_BADVALUE; 715 716 list += length; 717 count++; 718 } 719 720 return count; 721 } 722 723 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 724 const char *string) 725 { 726 int length, len, idx = 0; 727 const char *list, *end; 728 729 list = fdt_getprop(fdt, nodeoffset, property, &length); 730 if (!list) 731 return length; 732 733 len = strlen(string) + 1; 734 end = list + length; 735 736 while (list < end) { 737 length = strnlen(list, end - list) + 1; 738 739 /* Abort if the last string isn't properly NUL-terminated. */ 740 if (list + length > end) 741 return -FDT_ERR_BADVALUE; 742 743 if (length == len && memcmp(list, string, length) == 0) 744 return idx; 745 746 list += length; 747 idx++; 748 } 749 750 return -FDT_ERR_NOTFOUND; 751 } 752 753 const char *fdt_stringlist_get(const void *fdt, int nodeoffset, 754 const char *property, int idx, 755 int *lenp) 756 { 757 const char *list, *end; 758 int length; 759 760 list = fdt_getprop(fdt, nodeoffset, property, &length); 761 if (!list) { 762 if (lenp) 763 *lenp = length; 764 765 return NULL; 766 } 767 768 end = list + length; 769 770 while (list < end) { 771 length = strnlen(list, end - list) + 1; 772 773 /* Abort if the last string isn't properly NUL-terminated. */ 774 if (list + length > end) { 775 if (lenp) 776 *lenp = -FDT_ERR_BADVALUE; 777 778 return NULL; 779 } 780 781 if (idx == 0) { 782 if (lenp) 783 *lenp = length - 1; 784 785 return list; 786 } 787 788 list += length; 789 idx--; 790 } 791 792 if (lenp) 793 *lenp = -FDT_ERR_NOTFOUND; 794 795 return NULL; 796 } 797 798 int fdt_node_check_compatible(const void *fdt, int nodeoffset, 799 const char *compatible) 800 { 801 const void *prop; 802 int len; 803 804 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 805 if (!prop) 806 return len; 807 808 return !fdt_stringlist_contains(prop, len, compatible); 809 } 810 811 int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 812 const char *compatible) 813 { 814 int offset, err; 815 816 FDT_RO_PROBE(fdt); 817 818 /* FIXME: The algorithm here is pretty horrible: we scan each 819 * property of a node in fdt_node_check_compatible(), then if 820 * that didn't find what we want, we scan over them again 821 * making our way to the next node. Still it's the easiest to 822 * implement approach; performance can come later. */ 823 for (offset = fdt_next_node(fdt, startoffset, NULL); 824 offset >= 0; 825 offset = fdt_next_node(fdt, offset, NULL)) { 826 err = fdt_node_check_compatible(fdt, offset, compatible); 827 if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 828 return err; 829 else if (err == 0) 830 return offset; 831 } 832 833 return offset; /* error from fdt_next_node() */ 834 } 835 836 int fdt_check_full(const void *fdt, size_t bufsize) 837 { 838 int err; 839 int num_memrsv; 840 int offset, nextoffset = 0; 841 uint32_t tag; 842 unsigned depth = 0; 843 const void *prop; 844 const char *propname; 845 846 if (bufsize < FDT_V1_SIZE) 847 return -FDT_ERR_TRUNCATED; 848 err = fdt_check_header(fdt); 849 if (err != 0) 850 return err; 851 if (bufsize < fdt_totalsize(fdt)) 852 return -FDT_ERR_TRUNCATED; 853 854 num_memrsv = fdt_num_mem_rsv(fdt); 855 if (num_memrsv < 0) 856 return num_memrsv; 857 858 while (1) { 859 offset = nextoffset; 860 tag = fdt_next_tag(fdt, offset, &nextoffset); 861 862 if (nextoffset < 0) 863 return nextoffset; 864 865 switch (tag) { 866 case FDT_NOP: 867 break; 868 869 case FDT_END: 870 if (depth != 0) 871 return -FDT_ERR_BADSTRUCTURE; 872 return 0; 873 874 case FDT_BEGIN_NODE: 875 depth++; 876 if (depth > INT_MAX) 877 return -FDT_ERR_BADSTRUCTURE; 878 break; 879 880 case FDT_END_NODE: 881 if (depth == 0) 882 return -FDT_ERR_BADSTRUCTURE; 883 depth--; 884 break; 885 886 case FDT_PROP: 887 prop = fdt_getprop_by_offset(fdt, offset, &propname, 888 &err); 889 if (!prop) 890 return err; 891 break; 892 893 default: 894 return -FDT_ERR_INTERNAL; 895 } 896 } 897 } 898