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