1 /* $OpenBSD: fdt.c,v 1.27 2021/05/06 19:45:16 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net> 5 * Copyright (c) 2009 Mark Kettenis <kettenis@sfires.net> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/systm.h> 22 #include <sys/malloc.h> 23 24 #include <dev/ofw/fdt.h> 25 #include <dev/ofw/openfirm.h> 26 27 unsigned int fdt_check_head(void *); 28 char *fdt_get_str(u_int32_t); 29 void *skip_property(u_int32_t *); 30 void *skip_props(u_int32_t *); 31 void *skip_node_name(u_int32_t *); 32 void *skip_node(void *); 33 void *skip_nops(u_int32_t *); 34 void *fdt_parent_node_recurse(void *, void *); 35 void *fdt_find_phandle_recurse(void *, uint32_t); 36 int fdt_node_property_int(void *, char *, int *); 37 int fdt_node_property_ints(void *, char *, int *, int); 38 int fdt_translate_reg(void *, struct fdt_reg *); 39 #ifdef DEBUG 40 void fdt_print_node_recurse(void *, int); 41 #endif 42 43 static int tree_inited = 0; 44 static struct fdt tree; 45 46 unsigned int 47 fdt_check_head(void *fdt) 48 { 49 struct fdt_head *fh; 50 u_int32_t *ptr, *tok; 51 52 fh = fdt; 53 ptr = (u_int32_t *)fdt; 54 55 if (betoh32(fh->fh_magic) != FDT_MAGIC) 56 return 0; 57 58 if (betoh32(fh->fh_version) > FDT_CODE_VERSION) 59 return 0; 60 61 tok = skip_nops(ptr + (betoh32(fh->fh_struct_off) / 4)); 62 if (betoh32(*tok) != FDT_NODE_BEGIN) 63 return 0; 64 65 /* check for end signature on version 17 blob */ 66 if ((betoh32(fh->fh_version) >= 17) && 67 (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) + 68 (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END)) 69 return 0; 70 71 return betoh32(fh->fh_version); 72 } 73 74 /* 75 * Initializes internal structures of module. 76 * Has to be called once, preferably in machdep.c. 77 */ 78 int 79 fdt_init(void *fdt) 80 { 81 int version; 82 83 bzero(&tree, sizeof(struct fdt)); 84 tree_inited = 0; 85 86 if (!fdt) 87 return 0; 88 89 if (!(version = fdt_check_head(fdt))) 90 return 0; 91 92 tree.header = (struct fdt_head *)fdt; 93 tree.tree = (char *)fdt + betoh32(tree.header->fh_struct_off); 94 tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off); 95 tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off); 96 tree.end = (char *)fdt + betoh32(tree.header->fh_size); 97 tree.version = version; 98 tree.strings_size = betoh32(tree.header->fh_strings_size); 99 if (tree.version >= 17) 100 tree.struct_size = betoh32(tree.header->fh_struct_size); 101 tree_inited = 1; 102 103 return version; 104 } 105 106 void 107 fdt_finalize(void) 108 { 109 char *start = (char *)tree.header; 110 111 tree.header->fh_size = htobe32(tree.end - start); 112 tree.header->fh_struct_off = htobe32(tree.tree - start); 113 tree.header->fh_strings_off = htobe32(tree.strings - start); 114 tree.header->fh_reserve_off = htobe32(tree.memory - start); 115 tree.header->fh_strings_size = htobe32(tree.strings_size); 116 if (tree.version >= 17) 117 tree.header->fh_struct_size = htobe32(tree.struct_size); 118 } 119 120 /* 121 * Return the size of the FDT. 122 */ 123 size_t 124 fdt_get_size(void *fdt) 125 { 126 if (!fdt) 127 return 0; 128 129 if (!fdt_check_head(fdt)) 130 return 0; 131 132 return betoh32(((struct fdt_head *)fdt)->fh_size); 133 } 134 135 /* 136 * Retrieve string pointer from strings table. 137 */ 138 char * 139 fdt_get_str(u_int32_t num) 140 { 141 if (num > tree.strings_size) 142 return NULL; 143 return (tree.strings) ? (tree.strings + num) : NULL; 144 } 145 146 int 147 fdt_add_str(char *name) 148 { 149 size_t len = roundup(strlen(name) + 1, sizeof(uint32_t)); 150 char *end = tree.strings + tree.strings_size; 151 152 memmove(end + len, end, tree.end - end); 153 tree.strings_size += len; 154 if (tree.tree > tree.strings) 155 tree.tree += len; 156 if (tree.memory > tree.strings) 157 tree.memory += len; 158 tree.end += len; 159 memset(end, 0, len); 160 memcpy(end, name, strlen(name)); 161 162 return (end - tree.strings); 163 } 164 165 /* 166 * Utility functions for skipping parts of tree. 167 */ 168 169 void * 170 skip_nops(u_int32_t *ptr) 171 { 172 while (betoh32(*ptr) == FDT_NOP) 173 ptr++; 174 175 return ptr; 176 } 177 178 void * 179 skip_property(u_int32_t *ptr) 180 { 181 u_int32_t size; 182 183 size = betoh32(*(ptr + 1)); 184 /* move forward by magic + size + nameid + rounded up property size */ 185 ptr += 3 + roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t); 186 187 return skip_nops(ptr); 188 } 189 190 void * 191 skip_props(u_int32_t *ptr) 192 { 193 while (betoh32(*ptr) == FDT_PROPERTY) { 194 ptr = skip_property(ptr); 195 } 196 return ptr; 197 } 198 199 void * 200 skip_node_name(u_int32_t *ptr) 201 { 202 /* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */ 203 ptr += roundup(strlen((char *)ptr) + 1, 204 sizeof(u_int32_t)) / sizeof(u_int32_t); 205 206 return skip_nops(ptr); 207 } 208 209 /* 210 * Retrieves node property, the returned pointer is inside the fdt tree, 211 * so we should not modify content pointed by it directly. 212 */ 213 int 214 fdt_node_property(void *node, char *name, char **out) 215 { 216 u_int32_t *ptr; 217 u_int32_t nameid; 218 char *tmp; 219 220 if (!tree_inited) 221 return -1; 222 223 ptr = (u_int32_t *)node; 224 225 if (betoh32(*ptr) != FDT_NODE_BEGIN) 226 return -1; 227 228 ptr = skip_node_name(ptr + 1); 229 230 while (betoh32(*ptr) == FDT_PROPERTY) { 231 nameid = betoh32(*(ptr + 2)); /* id of name in strings table */ 232 tmp = fdt_get_str(nameid); 233 if (!strcmp(name, tmp)) { 234 *out = (char *)(ptr + 3); /* beginning of the value */ 235 return betoh32(*(ptr + 1)); /* size of value */ 236 } 237 ptr = skip_property(ptr); 238 } 239 return -1; 240 } 241 242 int 243 fdt_node_set_property(void *node, char *name, void *data, int len) 244 { 245 uint32_t *ptr, *next; 246 uint32_t nameid; 247 uint32_t curlen; 248 size_t delta; 249 char *tmp; 250 251 if (!tree_inited) 252 return 0; 253 254 ptr = (uint32_t *)node; 255 256 if (betoh32(*ptr) != FDT_NODE_BEGIN) 257 return 0; 258 259 ptr = skip_node_name(ptr + 1); 260 261 while (betoh32(*ptr) == FDT_PROPERTY) { 262 nameid = betoh32(*(ptr + 2)); /* id of name in strings table */ 263 tmp = fdt_get_str(nameid); 264 next = skip_property(ptr); 265 if (!strcmp(name, tmp)) { 266 curlen = betoh32(*(ptr + 1)); 267 delta = roundup(len, sizeof(uint32_t)) - 268 roundup(curlen, sizeof(uint32_t)); 269 memmove((char *)next + delta, next, 270 tree.end - (char *)next); 271 tree.struct_size += delta; 272 if (tree.strings > tree.tree) 273 tree.strings += delta; 274 if (tree.memory > tree.tree) 275 tree.memory += delta; 276 tree.end += delta; 277 *(ptr + 1) = htobe32(len); 278 memcpy(ptr + 3, data, len); 279 return 1; 280 } 281 ptr = next; 282 } 283 return 0; 284 } 285 286 int 287 fdt_node_add_property(void *node, char *name, void *data, int len) 288 { 289 char *dummy; 290 291 if (!tree_inited) 292 return 0; 293 294 if (fdt_node_property(node, name, &dummy) == -1) { 295 uint32_t *ptr = (uint32_t *)node; 296 297 if (betoh32(*ptr) != FDT_NODE_BEGIN) 298 return 0; 299 300 ptr = skip_node_name(ptr + 1); 301 302 memmove(ptr + 3, ptr, tree.end - (char *)ptr); 303 tree.struct_size += 3 * sizeof(uint32_t); 304 if (tree.strings > tree.tree) 305 tree.strings += 3 * sizeof(uint32_t); 306 if (tree.memory > tree.tree) 307 tree.memory += 3 * sizeof(uint32_t); 308 tree.end += 3 * sizeof(uint32_t); 309 *ptr++ = htobe32(FDT_PROPERTY); 310 *ptr++ = htobe32(0); 311 *ptr++ = htobe32(fdt_add_str(name)); 312 } 313 314 return fdt_node_set_property(node, name, data, len); 315 } 316 317 /* 318 * Retrieves next node, skipping all the children nodes of the pointed node, 319 * returns pointer to next node, no matter if it exists or not. 320 */ 321 void * 322 skip_node(void *node) 323 { 324 u_int32_t *ptr = node; 325 326 ptr++; 327 328 ptr = skip_node_name(ptr); 329 ptr = skip_props(ptr); 330 331 /* skip children */ 332 while (betoh32(*ptr) == FDT_NODE_BEGIN) 333 ptr = skip_node(ptr); 334 335 return skip_nops(ptr + 1); 336 } 337 338 /* 339 * Retrieves next node, skipping all the children nodes of the pointed node, 340 * returns pointer to next node if exists, otherwise returns NULL. 341 * If passed 0 will return first node of the tree (root). 342 */ 343 void * 344 fdt_next_node(void *node) 345 { 346 u_int32_t *ptr; 347 348 if (!tree_inited) 349 return NULL; 350 351 ptr = node; 352 353 if (node == NULL) { 354 ptr = skip_nops((uint32_t *)tree.tree); 355 return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL; 356 } 357 358 if (betoh32(*ptr) != FDT_NODE_BEGIN) 359 return NULL; 360 361 ptr++; 362 363 ptr = skip_node_name(ptr); 364 ptr = skip_props(ptr); 365 366 /* skip children */ 367 while (betoh32(*ptr) == FDT_NODE_BEGIN) 368 ptr = skip_node(ptr); 369 370 if (betoh32(*ptr) != FDT_NODE_END) 371 return NULL; 372 373 ptr = skip_nops(ptr + 1); 374 375 if (betoh32(*ptr) != FDT_NODE_BEGIN) 376 return NULL; 377 378 return ptr; 379 } 380 381 int 382 fdt_next_property(void *node, char *name, char **nextname) 383 { 384 u_int32_t *ptr; 385 u_int32_t nameid; 386 387 if (!tree_inited) 388 return 0; 389 390 ptr = (u_int32_t *)node; 391 392 if (betoh32(*ptr) != FDT_NODE_BEGIN) 393 return 0; 394 395 ptr = skip_node_name(ptr + 1); 396 397 while (betoh32(*ptr) == FDT_PROPERTY) { 398 nameid = betoh32(*(ptr + 2)); /* id of name in strings table */ 399 if (strcmp(name, "") == 0) { 400 *nextname = fdt_get_str(nameid); 401 return 1; 402 } 403 if (strcmp(name, fdt_get_str(nameid)) == 0) { 404 ptr = skip_property(ptr); 405 if (betoh32(*ptr) != FDT_PROPERTY) 406 break; 407 nameid = betoh32(*(ptr + 2)); 408 *nextname = fdt_get_str(nameid); 409 return 1; 410 } 411 ptr = skip_property(ptr); 412 } 413 *nextname = ""; 414 return 1; 415 } 416 417 /* 418 * Retrieves node property as integers and puts them in the given 419 * integer array. 420 */ 421 int 422 fdt_node_property_ints(void *node, char *name, int *out, int outlen) 423 { 424 int *data; 425 int i, inlen; 426 427 inlen = fdt_node_property(node, name, (char **)&data) / sizeof(int); 428 if (inlen <= 0) 429 return -1; 430 431 for (i = 0; i < inlen && i < outlen; i++) 432 out[i] = betoh32(data[i]); 433 434 return i; 435 } 436 437 /* 438 * Retrieves node property as an integer. 439 */ 440 int 441 fdt_node_property_int(void *node, char *name, int *out) 442 { 443 return fdt_node_property_ints(node, name, out, 1); 444 } 445 446 /* 447 * Retrieves next node, skipping all the children nodes of the pointed node 448 */ 449 void * 450 fdt_child_node(void *node) 451 { 452 u_int32_t *ptr; 453 454 if (!tree_inited) 455 return NULL; 456 457 ptr = node; 458 459 if (betoh32(*ptr) != FDT_NODE_BEGIN) 460 return NULL; 461 462 ptr++; 463 464 ptr = skip_node_name(ptr); 465 ptr = skip_props(ptr); 466 /* check if there is a child node */ 467 return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL; 468 } 469 470 /* 471 * Retrieves node name. 472 */ 473 char * 474 fdt_node_name(void *node) 475 { 476 u_int32_t *ptr; 477 478 if (!tree_inited) 479 return NULL; 480 481 ptr = node; 482 483 if (betoh32(*ptr) != FDT_NODE_BEGIN) 484 return NULL; 485 486 return (char *)(ptr + 1); 487 } 488 489 void * 490 fdt_find_node(char *name) 491 { 492 void *node = fdt_next_node(0); 493 const char *p = name; 494 495 if (!tree_inited) 496 return NULL; 497 498 if (*p != '/') 499 return NULL; 500 501 while (*p) { 502 void *child; 503 const char *q; 504 505 while (*p == '/') 506 p++; 507 if (*p == 0) 508 return node; 509 q = strchr(p, '/'); 510 if (q == NULL) 511 q = p + strlen(p); 512 513 for (child = fdt_child_node(node); child; 514 child = fdt_next_node(child)) { 515 if (strncmp(p, fdt_node_name(child), q - p) == 0) { 516 node = child; 517 break; 518 } 519 } 520 521 if (child == NULL) 522 return NULL; /* No match found. */ 523 524 p = q; 525 } 526 527 return node; 528 } 529 530 void * 531 fdt_parent_node_recurse(void *pnode, void *child) 532 { 533 void *node = fdt_child_node(pnode); 534 void *tmp; 535 536 while (node && (node != child)) { 537 if ((tmp = fdt_parent_node_recurse(node, child))) 538 return tmp; 539 node = fdt_next_node(node); 540 } 541 return (node) ? pnode : NULL; 542 } 543 544 void * 545 fdt_parent_node(void *node) 546 { 547 void *pnode = fdt_next_node(0); 548 549 if (!tree_inited) 550 return NULL; 551 552 if (node == pnode) 553 return NULL; 554 555 return fdt_parent_node_recurse(pnode, node); 556 } 557 558 void * 559 fdt_find_phandle_recurse(void *node, uint32_t phandle) 560 { 561 void *child; 562 char *data; 563 void *tmp; 564 int len; 565 566 len = fdt_node_property(node, "phandle", &data); 567 if (len < 0) 568 len = fdt_node_property(node, "linux,phandle", &data); 569 570 if (len == sizeof(uint32_t) && bemtoh32(data) == phandle) 571 return node; 572 573 for (child = fdt_child_node(node); child; child = fdt_next_node(child)) 574 if ((tmp = fdt_find_phandle_recurse(child, phandle))) 575 return tmp; 576 577 return NULL; 578 } 579 580 void * 581 fdt_find_phandle(uint32_t phandle) 582 { 583 return fdt_find_phandle_recurse(fdt_next_node(0), phandle); 584 } 585 586 void 587 fdt_get_cells(void *node, int *ac, int *sc) 588 { 589 void *parent; 590 591 parent = fdt_parent_node(node); 592 if (parent == NULL) 593 *ac = *sc = 1; 594 else 595 fdt_get_cells(parent, ac, sc); 596 597 fdt_node_property_int(node, "#address-cells", ac); 598 fdt_node_property_int(node, "#size-cells", sc); 599 } 600 601 /* 602 * Translate memory address depending on parent's range. 603 * 604 * Ranges are a way of mapping one address to another. This ranges attribute 605 * is set on a node's parent. This means if a node does not have a parent, 606 * there's nothing to translate. If it does have a parent and the parent does 607 * not have a ranges attribute, there's nothing to translate either. 608 * 609 * If the parent has a ranges attribute and the attribute is not empty, the 610 * node's memory address has to be in one of the given ranges. This range is 611 * then used to translate the memory address. 612 * 613 * If the parent has a ranges attribute, but the attribute is empty, there's 614 * nothing to translate. But it's not a translation barrier. It can be treated 615 * as a simple 1:1 mapping. 616 * 617 * Translation does not end here. We need to check if the parent's parent also 618 * has a ranges attribute and ask the same questions again. 619 */ 620 int 621 fdt_translate_reg(void *node, struct fdt_reg *reg) 622 { 623 void *parent; 624 int pac, psc, ac, sc, rlen, rone, *range; 625 uint64_t from, to, size; 626 627 /* No parent, no translation. */ 628 parent = fdt_parent_node(node); 629 if (parent == NULL) 630 return 0; 631 632 /* Extract ranges property from node. */ 633 rlen = fdt_node_property(node, "ranges", (char **)&range) / sizeof(int); 634 635 /* No ranges means translation barrier. Translation stops here. */ 636 if (range == NULL) 637 return 0; 638 639 /* Empty ranges means 1:1 mapping. Continue translation on parent. */ 640 if (rlen <= 0) 641 return fdt_translate_reg(parent, reg); 642 643 /* 644 * Get parent address/size width. We only support 32-bit (1) 645 * and 64-bit (2) wide addresses and sizes here. 646 */ 647 fdt_get_cells(parent, &pac, &psc); 648 if (pac <= 0 || pac > 2 || psc <= 0 || psc > 2) 649 return EINVAL; 650 651 /* 652 * Get our own address/size width. Again, we only support 653 * 32-bit (1) and 64-bit (2) wide addresses and sizes here. 654 */ 655 fdt_get_cells(node, &ac, &sc); 656 if (ac <= 0 || ac > 2 || sc <= 0 || sc > 2) 657 return EINVAL; 658 659 /* Must have at least one range. */ 660 rone = pac + ac + sc; 661 if (rlen < rone) 662 return ESRCH; 663 664 /* For each range. */ 665 for (; rlen >= rone; rlen -= rone, range += rone) { 666 /* Extract from and size, so we can see if we fit. */ 667 from = betoh32(range[0]); 668 if (ac == 2) 669 from = (from << 32) + betoh32(range[1]); 670 size = betoh32(range[ac + pac]); 671 if (sc == 2) 672 size = (size << 32) + betoh32(range[ac + pac + 1]); 673 674 /* Try next, if we're not in the range. */ 675 if (reg->addr < from || (reg->addr + reg->size) > (from + size)) 676 continue; 677 678 /* All good, extract to address and translate. */ 679 to = betoh32(range[ac]); 680 if (pac == 2) 681 to = (to << 32) + betoh32(range[ac + 1]); 682 683 reg->addr -= from; 684 reg->addr += to; 685 return fdt_translate_reg(parent, reg); 686 } 687 688 /* To be successful, we must have returned in the for-loop. */ 689 return ESRCH; 690 } 691 692 /* 693 * Parse the memory address and size of a node. 694 */ 695 int 696 fdt_get_reg(void *node, int idx, struct fdt_reg *reg) 697 { 698 void *parent; 699 int ac, sc, off, *in, inlen; 700 701 if (node == NULL || reg == NULL) 702 return EINVAL; 703 704 parent = fdt_parent_node(node); 705 if (parent == NULL) 706 return EINVAL; 707 708 /* 709 * Get parent address/size width. We only support 32-bit (1) 710 * and 64-bit (2) wide addresses and sizes here. 711 */ 712 fdt_get_cells(parent, &ac, &sc); 713 if (ac <= 0 || ac > 2 || sc <= 0 || sc > 2) 714 return EINVAL; 715 716 inlen = fdt_node_property(node, "reg", (char **)&in) / sizeof(int); 717 if (inlen < ((idx + 1) * (ac + sc))) 718 return EINVAL; 719 720 off = idx * (ac + sc); 721 722 reg->addr = betoh32(in[off]); 723 if (ac == 2) 724 reg->addr = (reg->addr << 32) + betoh32(in[off + 1]); 725 726 reg->size = betoh32(in[off + ac]); 727 if (sc == 2) 728 reg->size = (reg->size << 32) + betoh32(in[off + ac + 1]); 729 730 return fdt_translate_reg(parent, reg); 731 } 732 733 int 734 fdt_is_compatible(void *node, const char *name) 735 { 736 char *data; 737 int len; 738 739 len = fdt_node_property(node, "compatible", &data); 740 while (len > 0) { 741 if (strcmp(data, name) == 0) 742 return 1; 743 len -= strlen(data) + 1; 744 data += strlen(data) + 1; 745 } 746 747 return 0; 748 } 749 750 #ifdef DEBUG 751 /* 752 * Debug methods for printing whole tree, particular odes and properies 753 */ 754 void * 755 fdt_print_property(void *node, int level) 756 { 757 u_int32_t *ptr; 758 char *tmp, *value; 759 int cnt; 760 u_int32_t nameid, size; 761 762 ptr = (u_int32_t *)node; 763 764 if (!tree_inited) 765 return NULL; 766 767 if (betoh32(*ptr) != FDT_PROPERTY) 768 return ptr; /* should never happen */ 769 770 /* extract property name_id and size */ 771 size = betoh32(*++ptr); 772 nameid = betoh32(*++ptr); 773 774 for (cnt = 0; cnt < level; cnt++) 775 printf("\t"); 776 777 tmp = fdt_get_str(nameid); 778 printf("\t%s : ", tmp ? tmp : "NO_NAME"); 779 780 ptr++; 781 value = (char *)ptr; 782 783 if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") || 784 !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") || 785 !strcmp(tmp, "linux,stdout-path")) { 786 printf("%s", value); 787 } else if (!strcmp(tmp, "clock-frequency") || 788 !strcmp(tmp, "timebase-frequency")) { 789 printf("%d", betoh32(*((unsigned int *)value))); 790 } else { 791 for (cnt = 0; cnt < size; cnt++) { 792 if ((cnt % sizeof(u_int32_t)) == 0) 793 printf(" "); 794 printf("%02x", value[cnt]); 795 } 796 } 797 ptr += roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t); 798 printf("\n"); 799 800 return ptr; 801 } 802 803 void 804 fdt_print_node(void *node, int level) 805 { 806 u_int32_t *ptr; 807 int cnt; 808 809 ptr = (u_int32_t *)node; 810 811 if (betoh32(*ptr) != FDT_NODE_BEGIN) 812 return; 813 814 ptr++; 815 816 for (cnt = 0; cnt < level; cnt++) 817 printf("\t"); 818 printf("%s :\n", fdt_node_name(node)); 819 ptr = skip_node_name(ptr); 820 821 while (betoh32(*ptr) == FDT_PROPERTY) 822 ptr = fdt_print_property(ptr, level); 823 } 824 825 void 826 fdt_print_node_recurse(void *node, int level) 827 { 828 void *child; 829 830 fdt_print_node(node, level); 831 for (child = fdt_child_node(node); child; child = fdt_next_node(child)) 832 fdt_print_node_recurse(child, level + 1); 833 } 834 835 void 836 fdt_print_tree(void) 837 { 838 fdt_print_node_recurse(fdt_next_node(0), 0); 839 } 840 #endif 841 842 int 843 OF_peer(int handle) 844 { 845 void *node = (char *)tree.header + handle; 846 847 if (handle == 0) 848 node = fdt_find_node("/"); 849 else 850 node = fdt_next_node(node); 851 return node ? ((char *)node - (char *)tree.header) : 0; 852 } 853 854 int 855 OF_child(int handle) 856 { 857 void *node = (char *)tree.header + handle; 858 859 node = fdt_child_node(node); 860 return node ? ((char *)node - (char *)tree.header) : 0; 861 } 862 863 int 864 OF_parent(int handle) 865 { 866 void *node = (char *)tree.header + handle; 867 868 node = fdt_parent_node(node); 869 return node ? ((char *)node - (char *)tree.header) : 0; 870 } 871 872 int 873 OF_finddevice(char *name) 874 { 875 void *node; 876 877 node = fdt_find_node(name); 878 return node ? ((char *)node - (char *)tree.header) : -1; 879 } 880 881 int 882 OF_getnodebyname(int handle, const char *name) 883 { 884 void *node = (char *)tree.header + handle; 885 886 if (handle == 0) 887 node = fdt_find_node("/"); 888 889 for (node = fdt_child_node(node); node; node = fdt_next_node(node)) { 890 if (strcmp(name, fdt_node_name(node)) == 0) 891 break; 892 } 893 894 return node ? ((char *)node - (char *)tree.header) : 0; 895 } 896 897 int 898 OF_getnodebyphandle(uint32_t phandle) 899 { 900 void *node; 901 902 node = fdt_find_phandle(phandle); 903 return node ? ((char *)node - (char *)tree.header) : 0; 904 } 905 906 int 907 OF_getproplen(int handle, char *prop) 908 { 909 void *node = (char *)tree.header + handle; 910 char *data, *name; 911 int len; 912 913 len = fdt_node_property(node, prop, &data); 914 915 /* 916 * The "name" property is optional since version 16 of the 917 * flattened device tree specification, so we synthesize one 918 * from the unit name of the node if it is missing. 919 */ 920 if (len < 0 && strcmp(prop, "name") == 0) { 921 name = fdt_node_name(node); 922 data = strchr(name, '@'); 923 if (data) 924 len = data - name; 925 else 926 len = strlen(name); 927 return len + 1; 928 } 929 930 return len; 931 } 932 933 int 934 OF_getprop(int handle, char *prop, void *buf, int buflen) 935 { 936 void *node = (char *)tree.header + handle; 937 char *data; 938 int len; 939 940 len = fdt_node_property(node, prop, &data); 941 942 /* 943 * The "name" property is optional since version 16 of the 944 * flattened device tree specification, so we synthesize one 945 * from the unit name of the node if it is missing. 946 */ 947 if (len < 0 && strcmp(prop, "name") == 0) { 948 data = fdt_node_name(node); 949 if (data) { 950 len = strlcpy(buf, data, buflen); 951 data = strchr(buf, '@'); 952 if (data) { 953 *data = 0; 954 len = data - (char *)buf; 955 } 956 return len + 1; 957 } 958 } 959 960 if (len > 0) 961 memcpy(buf, data, min(len, buflen)); 962 return len; 963 } 964 965 int 966 OF_getpropbool(int handle, char *prop) 967 { 968 void *node = (char *)tree.header + handle; 969 char *data; 970 971 return (fdt_node_property(node, prop, &data) >= 0); 972 } 973 974 uint32_t 975 OF_getpropint(int handle, char *prop, uint32_t defval) 976 { 977 uint32_t val; 978 int len; 979 980 len = OF_getprop(handle, prop, &val, sizeof(val)); 981 if (len != sizeof(val)) 982 return defval; 983 984 return betoh32(val); 985 } 986 987 int 988 OF_getpropintarray(int handle, char *prop, uint32_t *buf, int buflen) 989 { 990 int len; 991 int i; 992 993 len = OF_getprop(handle, prop, buf, buflen); 994 if (len < 0 || (len % sizeof(uint32_t))) 995 return -1; 996 997 for (i = 0; i < len / sizeof(uint32_t); i++) 998 buf[i] = betoh32(buf[i]); 999 1000 return len; 1001 } 1002 1003 uint64_t 1004 OF_getpropint64(int handle, char *prop, uint64_t defval) 1005 { 1006 uint64_t val; 1007 int len; 1008 1009 len = OF_getprop(handle, prop, &val, sizeof(val)); 1010 if (len != sizeof(val)) 1011 return defval; 1012 1013 return betoh64(val); 1014 } 1015 1016 int 1017 OF_getpropint64array(int handle, char *prop, uint64_t *buf, int buflen) 1018 { 1019 int len; 1020 int i; 1021 1022 len = OF_getprop(handle, prop, buf, buflen); 1023 if (len < 0 || (len % sizeof(uint64_t))) 1024 return -1; 1025 1026 for (i = 0; i < len / sizeof(uint64_t); i++) 1027 buf[i] = betoh64(buf[i]); 1028 1029 return len; 1030 } 1031 1032 int 1033 OF_nextprop(int handle, char *prop, void *nextprop) 1034 { 1035 void *node = (char *)tree.header + handle; 1036 char *data; 1037 1038 if (fdt_node_property(node, "name", &data) == -1) { 1039 if (strcmp(prop, "") == 0) 1040 return strlcpy(nextprop, "name", OFMAXPARAM); 1041 if (strcmp(prop, "name") == 0) 1042 prop = ""; 1043 } 1044 1045 if (fdt_next_property(node, prop, &data)) 1046 return strlcpy(nextprop, data, OFMAXPARAM); 1047 return -1; 1048 } 1049 1050 int 1051 OF_is_compatible(int handle, const char *name) 1052 { 1053 void *node = (char *)tree.header + handle; 1054 return (fdt_is_compatible(node, name)); 1055 } 1056 1057 int 1058 OF_getindex(int handle, const char *entry, const char *prop) 1059 { 1060 char *names; 1061 char *name; 1062 char *end; 1063 int idx = 0; 1064 int len; 1065 1066 if (entry == NULL) 1067 return 0; 1068 1069 len = OF_getproplen(handle, (char *)prop); 1070 if (len <= 0) 1071 return -1; 1072 1073 names = malloc(len, M_TEMP, M_WAITOK); 1074 OF_getprop(handle, (char *)prop, names, len); 1075 end = names + len; 1076 name = names; 1077 while (name < end) { 1078 if (strcmp(name, entry) == 0) { 1079 free(names, M_TEMP, len); 1080 return idx; 1081 } 1082 name += strlen(name) + 1; 1083 idx++; 1084 } 1085 free(names, M_TEMP, len); 1086 return -1; 1087 } 1088