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