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