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