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