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