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