1 /* 2 * libfdt - Flat Device Tree manipulation 3 * Copyright (C) 2006 David Gibson, IBM Corporation. 4 * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause 5 */ 6 #include <libfdt_env.h> 7 8 #ifndef USE_HOSTCC 9 #include <fdt.h> 10 #include <libfdt.h> 11 #else 12 #include "fdt_host.h" 13 #endif 14 15 #include "libfdt_internal.h" 16 17 static int _fdt_nodename_eq(const void *fdt, int offset, 18 const char *s, int len) 19 { 20 const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); 21 22 if (!p) 23 /* short match */ 24 return 0; 25 26 if (memcmp(p, s, len) != 0) 27 return 0; 28 29 if (p[len] == '\0') 30 return 1; 31 else if (!memchr(s, '@', len) && (p[len] == '@')) 32 return 1; 33 else 34 return 0; 35 } 36 37 const char *fdt_string(const void *fdt, int stroffset) 38 { 39 return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 40 } 41 42 static int _fdt_string_eq(const void *fdt, int stroffset, 43 const char *s, int len) 44 { 45 const char *p = fdt_string(fdt, stroffset); 46 47 return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0); 48 } 49 50 uint32_t fdt_get_max_phandle(const void *fdt) 51 { 52 uint32_t max_phandle = 0; 53 int offset; 54 55 for (offset = fdt_next_node(fdt, -1, NULL);; 56 offset = fdt_next_node(fdt, offset, NULL)) { 57 uint32_t phandle; 58 59 if (offset == -FDT_ERR_NOTFOUND) 60 return max_phandle; 61 62 if (offset < 0) 63 return (uint32_t)-1; 64 65 phandle = fdt_get_phandle(fdt, offset); 66 if (phandle == (uint32_t)-1) 67 continue; 68 69 if (phandle > max_phandle) 70 max_phandle = phandle; 71 } 72 73 return 0; 74 } 75 76 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 77 { 78 FDT_CHECK_HEADER(fdt); 79 *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); 80 *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); 81 return 0; 82 } 83 84 int fdt_num_mem_rsv(const void *fdt) 85 { 86 int i = 0; 87 88 while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) 89 i++; 90 return i; 91 } 92 93 static int _nextprop(const void *fdt, int offset) 94 { 95 uint32_t tag; 96 int nextoffset; 97 98 do { 99 tag = fdt_next_tag(fdt, offset, &nextoffset); 100 101 switch (tag) { 102 case FDT_END: 103 if (nextoffset >= 0) 104 return -FDT_ERR_BADSTRUCTURE; 105 else 106 return nextoffset; 107 108 case FDT_PROP: 109 return offset; 110 } 111 offset = nextoffset; 112 } while (tag == FDT_NOP); 113 114 return -FDT_ERR_NOTFOUND; 115 } 116 117 int fdt_subnode_offset_namelen(const void *fdt, int offset, 118 const char *name, int namelen) 119 { 120 int depth; 121 122 FDT_CHECK_HEADER(fdt); 123 124 for (depth = 0; 125 (offset >= 0) && (depth >= 0); 126 offset = fdt_next_node(fdt, offset, &depth)) 127 if ((depth == 1) 128 && _fdt_nodename_eq(fdt, offset, name, namelen)) 129 return offset; 130 131 if (depth < 0) 132 return -FDT_ERR_NOTFOUND; 133 return offset; /* error */ 134 } 135 136 int fdt_subnode_offset(const void *fdt, int parentoffset, 137 const char *name) 138 { 139 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 140 } 141 142 /* 143 * Find the next of path separator, note we need to search for both '/' and ':' 144 * and then take the first one so that we do the right thing for e.g. 145 * "foo/bar:option" and "bar:option/otheroption", both of which happen, so 146 * first searching for either ':' or '/' does not work. 147 */ 148 static const char *fdt_path_next_separator(const char *path, int len) 149 { 150 const void *sep1 = memchr(path, '/', len); 151 const void *sep2 = memchr(path, ':', len); 152 153 if (sep1 && sep2) 154 return (sep1 < sep2) ? sep1 : sep2; 155 else if (sep1) 156 return sep1; 157 else 158 return sep2; 159 } 160 161 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) 162 { 163 const char *end = path + namelen; 164 const char *p = path; 165 int offset = 0; 166 167 FDT_CHECK_HEADER(fdt); 168 169 /* see if we have an alias */ 170 if (*path != '/') { 171 const char *q = fdt_path_next_separator(path, namelen); 172 173 if (!q) 174 q = end; 175 176 p = fdt_get_alias_namelen(fdt, p, q - p); 177 if (!p) 178 return -FDT_ERR_BADPATH; 179 offset = fdt_path_offset(fdt, p); 180 181 p = q; 182 } 183 184 while (*p && (p < end)) { 185 const char *q; 186 187 while (*p == '/') 188 p++; 189 190 if (*p == '\0' || *p == ':') 191 return offset; 192 193 q = fdt_path_next_separator(p, end - p); 194 if (!q) 195 q = end; 196 197 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 198 if (offset < 0) 199 return offset; 200 201 p = q; 202 } 203 204 return offset; 205 } 206 207 int fdt_path_offset(const void *fdt, const char *path) 208 { 209 return fdt_path_offset_namelen(fdt, path, strlen(path)); 210 } 211 212 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 213 { 214 const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 215 int err; 216 217 if (((err = fdt_check_header(fdt)) != 0) 218 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 219 goto fail; 220 221 if (len) 222 *len = strlen(nh->name); 223 224 return nh->name; 225 226 fail: 227 if (len) 228 *len = err; 229 return NULL; 230 } 231 232 int fdt_first_property_offset(const void *fdt, int nodeoffset) 233 { 234 int offset; 235 236 if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) 237 return offset; 238 239 return _nextprop(fdt, offset); 240 } 241 242 int fdt_next_property_offset(const void *fdt, int offset) 243 { 244 if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 245 return offset; 246 247 return _nextprop(fdt, offset); 248 } 249 250 const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 251 int offset, 252 int *lenp) 253 { 254 int err; 255 const struct fdt_property *prop; 256 257 if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { 258 if (lenp) 259 *lenp = err; 260 return NULL; 261 } 262 263 prop = _fdt_offset_ptr(fdt, offset); 264 265 if (lenp) 266 *lenp = fdt32_to_cpu(prop->len); 267 268 return prop; 269 } 270 271 const struct fdt_property *fdt_get_property_namelen(const void *fdt, 272 int offset, 273 const char *name, 274 int namelen, int *lenp) 275 { 276 for (offset = fdt_first_property_offset(fdt, offset); 277 (offset >= 0); 278 (offset = fdt_next_property_offset(fdt, offset))) { 279 const struct fdt_property *prop; 280 281 if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { 282 offset = -FDT_ERR_INTERNAL; 283 break; 284 } 285 if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 286 name, namelen)) 287 return prop; 288 } 289 290 if (lenp) 291 *lenp = offset; 292 return NULL; 293 } 294 295 const struct fdt_property *fdt_get_property(const void *fdt, 296 int nodeoffset, 297 const char *name, int *lenp) 298 { 299 return fdt_get_property_namelen(fdt, nodeoffset, name, 300 strlen(name), lenp); 301 } 302 303 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 304 const char *name, int namelen, int *lenp) 305 { 306 const struct fdt_property *prop; 307 308 prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 309 if (!prop) 310 return NULL; 311 312 return prop->data; 313 } 314 315 const void *fdt_getprop_by_offset(const void *fdt, int offset, 316 const char **namep, int *lenp) 317 { 318 const struct fdt_property *prop; 319 320 prop = fdt_get_property_by_offset(fdt, offset, lenp); 321 if (!prop) 322 return NULL; 323 if (namep) 324 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 325 return prop->data; 326 } 327 328 const void *fdt_getprop(const void *fdt, int nodeoffset, 329 const char *name, int *lenp) 330 { 331 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 332 } 333 334 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 335 { 336 const fdt32_t *php; 337 int len; 338 339 /* FIXME: This is a bit sub-optimal, since we potentially scan 340 * over all the properties twice. */ 341 php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 342 if (!php || (len != sizeof(*php))) { 343 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 344 if (!php || (len != sizeof(*php))) 345 return 0; 346 } 347 348 return fdt32_to_cpu(*php); 349 } 350 351 const char *fdt_get_alias_namelen(const void *fdt, 352 const char *name, int namelen) 353 { 354 int aliasoffset; 355 356 aliasoffset = fdt_path_offset(fdt, "/aliases"); 357 if (aliasoffset < 0) 358 return NULL; 359 360 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 361 } 362 363 const char *fdt_get_alias(const void *fdt, const char *name) 364 { 365 return fdt_get_alias_namelen(fdt, name, strlen(name)); 366 } 367 368 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 369 { 370 int pdepth = 0, p = 0; 371 int offset, depth, namelen; 372 const char *name; 373 374 FDT_CHECK_HEADER(fdt); 375 376 if (buflen < 2) 377 return -FDT_ERR_NOSPACE; 378 379 for (offset = 0, depth = 0; 380 (offset >= 0) && (offset <= nodeoffset); 381 offset = fdt_next_node(fdt, offset, &depth)) { 382 while (pdepth > depth) { 383 do { 384 p--; 385 } while (buf[p-1] != '/'); 386 pdepth--; 387 } 388 389 if (pdepth >= depth) { 390 name = fdt_get_name(fdt, offset, &namelen); 391 if (!name) 392 return namelen; 393 if ((p + namelen + 1) <= buflen) { 394 memcpy(buf + p, name, namelen); 395 p += namelen; 396 buf[p++] = '/'; 397 pdepth++; 398 } 399 } 400 401 if (offset == nodeoffset) { 402 if (pdepth < (depth + 1)) 403 return -FDT_ERR_NOSPACE; 404 405 if (p > 1) /* special case so that root path is "/", not "" */ 406 p--; 407 buf[p] = '\0'; 408 return 0; 409 } 410 } 411 412 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 413 return -FDT_ERR_BADOFFSET; 414 else if (offset == -FDT_ERR_BADOFFSET) 415 return -FDT_ERR_BADSTRUCTURE; 416 417 return offset; /* error from fdt_next_node() */ 418 } 419 420 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 421 int supernodedepth, int *nodedepth) 422 { 423 int offset, depth; 424 int supernodeoffset = -FDT_ERR_INTERNAL; 425 426 FDT_CHECK_HEADER(fdt); 427 428 if (supernodedepth < 0) 429 return -FDT_ERR_NOTFOUND; 430 431 for (offset = 0, depth = 0; 432 (offset >= 0) && (offset <= nodeoffset); 433 offset = fdt_next_node(fdt, offset, &depth)) { 434 if (depth == supernodedepth) 435 supernodeoffset = offset; 436 437 if (offset == nodeoffset) { 438 if (nodedepth) 439 *nodedepth = depth; 440 441 if (supernodedepth > depth) 442 return -FDT_ERR_NOTFOUND; 443 else 444 return supernodeoffset; 445 } 446 } 447 448 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 449 return -FDT_ERR_BADOFFSET; 450 else if (offset == -FDT_ERR_BADOFFSET) 451 return -FDT_ERR_BADSTRUCTURE; 452 453 return offset; /* error from fdt_next_node() */ 454 } 455 456 int fdt_node_depth(const void *fdt, int nodeoffset) 457 { 458 int nodedepth; 459 int err; 460 461 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 462 if (err) 463 return (err < 0) ? err : -FDT_ERR_INTERNAL; 464 return nodedepth; 465 } 466 467 int fdt_parent_offset(const void *fdt, int nodeoffset) 468 { 469 int nodedepth = fdt_node_depth(fdt, nodeoffset); 470 471 if (nodedepth < 0) 472 return nodedepth; 473 return fdt_supernode_atdepth_offset(fdt, nodeoffset, 474 nodedepth - 1, NULL); 475 } 476 477 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 478 const char *propname, 479 const void *propval, int proplen) 480 { 481 int offset; 482 const void *val; 483 int len; 484 485 FDT_CHECK_HEADER(fdt); 486 487 /* FIXME: The algorithm here is pretty horrible: we scan each 488 * property of a node in fdt_getprop(), then if that didn't 489 * find what we want, we scan over them again making our way 490 * to the next node. Still it's the easiest to implement 491 * approach; performance can come later. */ 492 for (offset = fdt_next_node(fdt, startoffset, NULL); 493 offset >= 0; 494 offset = fdt_next_node(fdt, offset, NULL)) { 495 val = fdt_getprop(fdt, offset, propname, &len); 496 if (val && (len == proplen) 497 && (memcmp(val, propval, len) == 0)) 498 return offset; 499 } 500 501 return offset; /* error from fdt_next_node() */ 502 } 503 504 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 505 { 506 int offset; 507 508 if ((phandle == 0) || (phandle == -1)) 509 return -FDT_ERR_BADPHANDLE; 510 511 FDT_CHECK_HEADER(fdt); 512 513 /* FIXME: The algorithm here is pretty horrible: we 514 * potentially scan each property of a node in 515 * fdt_get_phandle(), then if that didn't find what 516 * we want, we scan over them again making our way to the next 517 * node. Still it's the easiest to implement approach; 518 * performance can come later. */ 519 for (offset = fdt_next_node(fdt, -1, NULL); 520 offset >= 0; 521 offset = fdt_next_node(fdt, offset, NULL)) { 522 if (fdt_get_phandle(fdt, offset) == phandle) 523 return offset; 524 } 525 526 return offset; /* error from fdt_next_node() */ 527 } 528 529 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 530 { 531 int len = strlen(str); 532 const char *p; 533 534 while (listlen >= len) { 535 if (memcmp(str, strlist, len+1) == 0) 536 return 1; 537 p = memchr(strlist, '\0', listlen); 538 if (!p) 539 return 0; /* malformed strlist.. */ 540 listlen -= (p-strlist) + 1; 541 strlist = p + 1; 542 } 543 return 0; 544 } 545 546 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 547 { 548 const char *list, *end; 549 int length, count = 0; 550 551 list = fdt_getprop(fdt, nodeoffset, property, &length); 552 if (!list) 553 return length; 554 555 end = list + length; 556 557 while (list < end) { 558 length = strnlen(list, end - list) + 1; 559 560 /* Abort if the last string isn't properly NUL-terminated. */ 561 if (list + length > end) 562 return -FDT_ERR_BADVALUE; 563 564 list += length; 565 count++; 566 } 567 568 return count; 569 } 570 571 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 572 const char *string) 573 { 574 int length, len, idx = 0; 575 const char *list, *end; 576 577 list = fdt_getprop(fdt, nodeoffset, property, &length); 578 if (!list) 579 return length; 580 581 len = strlen(string) + 1; 582 end = list + length; 583 584 while (list < end) { 585 length = strnlen(list, end - list) + 1; 586 587 /* Abort if the last string isn't properly NUL-terminated. */ 588 if (list + length > end) 589 return -FDT_ERR_BADVALUE; 590 591 if (length == len && memcmp(list, string, length) == 0) 592 return idx; 593 594 list += length; 595 idx++; 596 } 597 598 return -FDT_ERR_NOTFOUND; 599 } 600 601 const char *fdt_stringlist_get(const void *fdt, int nodeoffset, 602 const char *property, int idx, 603 int *lenp) 604 { 605 const char *list, *end; 606 int length; 607 608 list = fdt_getprop(fdt, nodeoffset, property, &length); 609 if (!list) { 610 if (lenp) 611 *lenp = length; 612 613 return NULL; 614 } 615 616 end = list + length; 617 618 while (list < end) { 619 length = strnlen(list, end - list) + 1; 620 621 /* Abort if the last string isn't properly NUL-terminated. */ 622 if (list + length > end) { 623 if (lenp) 624 *lenp = -FDT_ERR_BADVALUE; 625 626 return NULL; 627 } 628 629 if (idx == 0) { 630 if (lenp) 631 *lenp = length - 1; 632 633 return list; 634 } 635 636 list += length; 637 idx--; 638 } 639 640 if (lenp) 641 *lenp = -FDT_ERR_NOTFOUND; 642 643 return NULL; 644 } 645 646 int fdt_node_check_compatible(const void *fdt, int nodeoffset, 647 const char *compatible) 648 { 649 const void *prop; 650 int len; 651 652 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 653 if (!prop) 654 return len; 655 656 return !fdt_stringlist_contains(prop, len, compatible); 657 } 658 659 int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 660 const char *compatible) 661 { 662 int offset, err; 663 664 FDT_CHECK_HEADER(fdt); 665 666 /* FIXME: The algorithm here is pretty horrible: we scan each 667 * property of a node in fdt_node_check_compatible(), then if 668 * that didn't find what we want, we scan over them again 669 * making our way to the next node. Still it's the easiest to 670 * implement approach; performance can come later. */ 671 for (offset = fdt_next_node(fdt, startoffset, NULL); 672 offset >= 0; 673 offset = fdt_next_node(fdt, offset, NULL)) { 674 err = fdt_node_check_compatible(fdt, offset, compatible); 675 if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 676 return err; 677 else if (err == 0) 678 return offset; 679 } 680 681 return offset; /* error from fdt_next_node() */ 682 } 683