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