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 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 208 { 209 const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 210 int err; 211 212 if (((err = fdt_check_header(fdt)) != 0) 213 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 214 goto fail; 215 216 if (len) 217 *len = strlen(nh->name); 218 219 return nh->name; 220 221 fail: 222 if (len) 223 *len = err; 224 return NULL; 225 } 226 227 int fdt_first_property_offset(const void *fdt, int nodeoffset) 228 { 229 int offset; 230 231 if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) 232 return offset; 233 234 return _nextprop(fdt, offset); 235 } 236 237 int fdt_next_property_offset(const void *fdt, int offset) 238 { 239 if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 240 return offset; 241 242 return _nextprop(fdt, offset); 243 } 244 245 const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 246 int offset, 247 int *lenp) 248 { 249 int err; 250 const struct fdt_property *prop; 251 252 if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { 253 if (lenp) 254 *lenp = err; 255 return NULL; 256 } 257 258 prop = _fdt_offset_ptr(fdt, offset); 259 260 if (lenp) 261 *lenp = fdt32_to_cpu(prop->len); 262 263 return prop; 264 } 265 266 const struct fdt_property *fdt_get_property_namelen(const void *fdt, 267 int offset, 268 const char *name, 269 int namelen, int *lenp) 270 { 271 for (offset = fdt_first_property_offset(fdt, offset); 272 (offset >= 0); 273 (offset = fdt_next_property_offset(fdt, offset))) { 274 const struct fdt_property *prop; 275 276 if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { 277 offset = -FDT_ERR_INTERNAL; 278 break; 279 } 280 if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 281 name, namelen)) 282 return prop; 283 } 284 285 if (lenp) 286 *lenp = offset; 287 return NULL; 288 } 289 290 const struct fdt_property *fdt_get_property(const void *fdt, 291 int nodeoffset, 292 const char *name, int *lenp) 293 { 294 return fdt_get_property_namelen(fdt, nodeoffset, name, 295 strlen(name), lenp); 296 } 297 298 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 299 const char *name, int namelen, int *lenp) 300 { 301 const struct fdt_property *prop; 302 303 prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 304 if (!prop) 305 return NULL; 306 307 return prop->data; 308 } 309 310 const void *fdt_getprop_by_offset(const void *fdt, int offset, 311 const char **namep, int *lenp) 312 { 313 const struct fdt_property *prop; 314 315 prop = fdt_get_property_by_offset(fdt, offset, lenp); 316 if (!prop) 317 return NULL; 318 if (namep) 319 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 320 return prop->data; 321 } 322 323 const void *fdt_getprop(const void *fdt, int nodeoffset, 324 const char *name, int *lenp) 325 { 326 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 327 } 328 329 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 330 { 331 const fdt32_t *php; 332 int len; 333 334 /* FIXME: This is a bit sub-optimal, since we potentially scan 335 * over all the properties twice. */ 336 php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 337 if (!php || (len != sizeof(*php))) { 338 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 339 if (!php || (len != sizeof(*php))) 340 return 0; 341 } 342 343 return fdt32_to_cpu(*php); 344 } 345 346 const char *fdt_get_alias_namelen(const void *fdt, 347 const char *name, int namelen) 348 { 349 int aliasoffset; 350 351 aliasoffset = fdt_path_offset(fdt, "/aliases"); 352 if (aliasoffset < 0) 353 return NULL; 354 355 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 356 } 357 358 const char *fdt_get_alias(const void *fdt, const char *name) 359 { 360 return fdt_get_alias_namelen(fdt, name, strlen(name)); 361 } 362 363 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 364 { 365 int pdepth = 0, p = 0; 366 int offset, depth, namelen; 367 const char *name; 368 369 FDT_CHECK_HEADER(fdt); 370 371 if (buflen < 2) 372 return -FDT_ERR_NOSPACE; 373 374 for (offset = 0, depth = 0; 375 (offset >= 0) && (offset <= nodeoffset); 376 offset = fdt_next_node(fdt, offset, &depth)) { 377 while (pdepth > depth) { 378 do { 379 p--; 380 } while (buf[p-1] != '/'); 381 pdepth--; 382 } 383 384 if (pdepth >= depth) { 385 name = fdt_get_name(fdt, offset, &namelen); 386 if (!name) 387 return namelen; 388 if ((p + namelen + 1) <= buflen) { 389 memcpy(buf + p, name, namelen); 390 p += namelen; 391 buf[p++] = '/'; 392 pdepth++; 393 } 394 } 395 396 if (offset == nodeoffset) { 397 if (pdepth < (depth + 1)) 398 return -FDT_ERR_NOSPACE; 399 400 if (p > 1) /* special case so that root path is "/", not "" */ 401 p--; 402 buf[p] = '\0'; 403 return 0; 404 } 405 } 406 407 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 408 return -FDT_ERR_BADOFFSET; 409 else if (offset == -FDT_ERR_BADOFFSET) 410 return -FDT_ERR_BADSTRUCTURE; 411 412 return offset; /* error from fdt_next_node() */ 413 } 414 415 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 416 int supernodedepth, int *nodedepth) 417 { 418 int offset, depth; 419 int supernodeoffset = -FDT_ERR_INTERNAL; 420 421 FDT_CHECK_HEADER(fdt); 422 423 if (supernodedepth < 0) 424 return -FDT_ERR_NOTFOUND; 425 426 for (offset = 0, depth = 0; 427 (offset >= 0) && (offset <= nodeoffset); 428 offset = fdt_next_node(fdt, offset, &depth)) { 429 if (depth == supernodedepth) 430 supernodeoffset = offset; 431 432 if (offset == nodeoffset) { 433 if (nodedepth) 434 *nodedepth = depth; 435 436 if (supernodedepth > depth) 437 return -FDT_ERR_NOTFOUND; 438 else 439 return supernodeoffset; 440 } 441 } 442 443 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 444 return -FDT_ERR_BADOFFSET; 445 else if (offset == -FDT_ERR_BADOFFSET) 446 return -FDT_ERR_BADSTRUCTURE; 447 448 return offset; /* error from fdt_next_node() */ 449 } 450 451 int fdt_node_depth(const void *fdt, int nodeoffset) 452 { 453 int nodedepth; 454 int err; 455 456 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 457 if (err) 458 return (err < 0) ? err : -FDT_ERR_INTERNAL; 459 return nodedepth; 460 } 461 462 int fdt_parent_offset(const void *fdt, int nodeoffset) 463 { 464 int nodedepth = fdt_node_depth(fdt, nodeoffset); 465 466 if (nodedepth < 0) 467 return nodedepth; 468 return fdt_supernode_atdepth_offset(fdt, nodeoffset, 469 nodedepth - 1, NULL); 470 } 471 472 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 473 const char *propname, 474 const void *propval, int proplen) 475 { 476 int offset; 477 const void *val; 478 int len; 479 480 FDT_CHECK_HEADER(fdt); 481 482 /* FIXME: The algorithm here is pretty horrible: we scan each 483 * property of a node in fdt_getprop(), then if that didn't 484 * find what we want, we scan over them again making our way 485 * to the next node. Still it's the easiest to implement 486 * approach; performance can come later. */ 487 for (offset = fdt_next_node(fdt, startoffset, NULL); 488 offset >= 0; 489 offset = fdt_next_node(fdt, offset, NULL)) { 490 val = fdt_getprop(fdt, offset, propname, &len); 491 if (val && (len == proplen) 492 && (memcmp(val, propval, len) == 0)) 493 return offset; 494 } 495 496 return offset; /* error from fdt_next_node() */ 497 } 498 499 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 500 { 501 int offset; 502 503 if ((phandle == 0) || (phandle == -1)) 504 return -FDT_ERR_BADPHANDLE; 505 506 FDT_CHECK_HEADER(fdt); 507 508 /* FIXME: The algorithm here is pretty horrible: we 509 * potentially scan each property of a node in 510 * fdt_get_phandle(), then if that didn't find what 511 * we want, we scan over them again making our way to the next 512 * node. Still it's the easiest to implement approach; 513 * performance can come later. */ 514 for (offset = fdt_next_node(fdt, -1, NULL); 515 offset >= 0; 516 offset = fdt_next_node(fdt, offset, NULL)) { 517 if (fdt_get_phandle(fdt, offset) == phandle) 518 return offset; 519 } 520 521 return offset; /* error from fdt_next_node() */ 522 } 523 524 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 525 { 526 int len = strlen(str); 527 const char *p; 528 529 while (listlen >= len) { 530 if (memcmp(str, strlist, len+1) == 0) 531 return 1; 532 p = memchr(strlist, '\0', listlen); 533 if (!p) 534 return 0; /* malformed strlist.. */ 535 listlen -= (p-strlist) + 1; 536 strlist = p + 1; 537 } 538 return 0; 539 } 540 541 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 542 { 543 const char *list, *end; 544 int length, count = 0; 545 546 list = fdt_getprop(fdt, nodeoffset, property, &length); 547 if (!list) 548 return -length; 549 550 end = list + length; 551 552 while (list < end) { 553 length = strnlen(list, end - list) + 1; 554 555 /* Abort if the last string isn't properly NUL-terminated. */ 556 if (list + length > end) 557 return -FDT_ERR_BADVALUE; 558 559 list += length; 560 count++; 561 } 562 563 return count; 564 } 565 566 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 567 const char *string) 568 { 569 int length, len, idx = 0; 570 const char *list, *end; 571 572 list = fdt_getprop(fdt, nodeoffset, property, &length); 573 if (!list) 574 return -length; 575 576 len = strlen(string) + 1; 577 end = list + length; 578 579 while (list < end) { 580 length = strnlen(list, end - list) + 1; 581 582 /* Abort if the last string isn't properly NUL-terminated. */ 583 if (list + length > end) 584 return -FDT_ERR_BADVALUE; 585 586 if (length == len && memcmp(list, string, length) == 0) 587 return idx; 588 589 list += length; 590 idx++; 591 } 592 593 return -FDT_ERR_NOTFOUND; 594 } 595 596 const char *fdt_stringlist_get(const void *fdt, int nodeoffset, 597 const char *property, int idx, 598 int *lenp) 599 { 600 const char *list, *end; 601 int length; 602 603 list = fdt_getprop(fdt, nodeoffset, property, &length); 604 if (!list) { 605 if (lenp) 606 *lenp = length; 607 608 return NULL; 609 } 610 611 end = list + length; 612 613 while (list < end) { 614 length = strnlen(list, end - list) + 1; 615 616 /* Abort if the last string isn't properly NUL-terminated. */ 617 if (list + length > end) { 618 if (lenp) 619 *lenp = -FDT_ERR_BADVALUE; 620 621 return NULL; 622 } 623 624 if (idx == 0) { 625 if (lenp) 626 *lenp = length - 1; 627 628 return list; 629 } 630 631 list += length; 632 idx--; 633 } 634 635 if (lenp) 636 *lenp = -FDT_ERR_NOTFOUND; 637 638 return NULL; 639 } 640 641 int fdt_node_check_compatible(const void *fdt, int nodeoffset, 642 const char *compatible) 643 { 644 const void *prop; 645 int len; 646 647 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 648 if (!prop) 649 return len; 650 651 return !fdt_stringlist_contains(prop, len, compatible); 652 } 653 654 int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 655 const char *compatible) 656 { 657 int offset, err; 658 659 FDT_CHECK_HEADER(fdt); 660 661 /* FIXME: The algorithm here is pretty horrible: we scan each 662 * property of a node in fdt_node_check_compatible(), then if 663 * that didn't find what we want, we scan over them again 664 * making our way to the next node. Still it's the easiest to 665 * implement approach; performance can come later. */ 666 for (offset = fdt_next_node(fdt, startoffset, NULL); 667 offset >= 0; 668 offset = fdt_next_node(fdt, offset, NULL)) { 669 err = fdt_node_check_compatible(fdt, offset, compatible); 670 if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 671 return err; 672 else if (err == 0) 673 return offset; 674 } 675 676 return offset; /* error from fdt_next_node() */ 677 } 678