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