1 /* $OpenBSD: fdt.c,v 1.6 2019/10/25 10:06:40 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net> 5 * Copyright (c) 2009, 2016 Mark Kettenis <kettenis@openbsd.org> 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/param.h> 21 #include <sys/systm.h> 22 23 #include <lib/libkern/libkern.h> 24 25 #include "fdt.h" 26 27 unsigned int fdt_check_head(void *); 28 char *fdt_get_str(uint32_t); 29 void *skip_property(uint32_t *); 30 void *skip_props(uint32_t *); 31 void *skip_node_name(uint32_t *); 32 void *skip_node(void *); 33 void *fdt_parent_node_recurse(void *, void *); 34 35 static int tree_inited = 0; 36 static struct fdt tree; 37 38 unsigned int 39 fdt_check_head(void *fdt) 40 { 41 struct fdt_head *fh; 42 uint32_t *ptr; 43 44 fh = fdt; 45 ptr = (uint32_t *)fdt; 46 47 if (betoh32(fh->fh_magic) != FDT_MAGIC) 48 return 0; 49 50 if (betoh32(fh->fh_version) > FDT_CODE_VERSION) 51 return 0; 52 53 if (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4))) != 54 FDT_NODE_BEGIN) 55 return 0; 56 57 /* check for end signature on version 17 blob */ 58 if ((betoh32(fh->fh_version) >= 17) && 59 (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) + 60 (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END)) 61 return 0; 62 63 return betoh32(fh->fh_version); 64 } 65 66 /* 67 * Initializes internal structures of module. 68 * Has to be called once. 69 */ 70 int 71 fdt_init(void *fdt) 72 { 73 int version; 74 75 memset(&tree, 0, sizeof(struct fdt)); 76 tree_inited = 0; 77 78 if (!fdt) 79 return 0; 80 81 if (!(version = fdt_check_head(fdt))) 82 return 0; 83 84 tree.header = (struct fdt_head *)fdt; 85 tree.tree = (char *)fdt + betoh32(tree.header->fh_struct_off); 86 tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off); 87 tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off); 88 tree.end = (char *)fdt + betoh32(tree.header->fh_size); 89 tree.version = version; 90 tree.strings_size = betoh32(tree.header->fh_strings_size); 91 if (tree.version >= 17) 92 tree.struct_size = betoh32(tree.header->fh_struct_size); 93 tree_inited = 1; 94 95 return version; 96 } 97 98 void 99 fdt_finalize(void) 100 { 101 char *start = (char *)tree.header; 102 103 tree.header->fh_size = htobe32(tree.end - start); 104 tree.header->fh_struct_off = htobe32(tree.tree - start); 105 tree.header->fh_strings_off = htobe32(tree.strings - start); 106 tree.header->fh_reserve_off = htobe32(tree.memory - start); 107 tree.header->fh_strings_size = htobe32(tree.strings_size); 108 if (tree.version >= 17) 109 tree.header->fh_struct_size = htobe32(tree.struct_size); 110 } 111 112 /* 113 * Return the size of the FDT. 114 */ 115 size_t 116 fdt_get_size(void *fdt) 117 { 118 if (!fdt) 119 return 0; 120 121 if (!fdt_check_head(fdt)) 122 return 0; 123 124 return betoh32(((struct fdt_head *)fdt)->fh_size); 125 } 126 127 /* 128 * Retrieve string pointer from strings table. 129 */ 130 char * 131 fdt_get_str(uint32_t num) 132 { 133 if (num > tree.strings_size) 134 return NULL; 135 return (tree.strings) ? (tree.strings + num) : NULL; 136 } 137 138 int 139 fdt_add_str(char *name) 140 { 141 size_t len = roundup(strlen(name) + 1, sizeof(uint32_t)); 142 char *end = tree.strings + tree.strings_size; 143 144 memmove(end + len, end, tree.end - end); 145 tree.strings_size += len; 146 if (tree.tree > tree.strings) 147 tree.tree += len; 148 if (tree.memory > tree.strings) 149 tree.memory += len; 150 tree.end += len; 151 memset(end, 0, len); 152 memcpy(end, name, strlen(name)); 153 154 return (end - tree.strings); 155 } 156 157 /* 158 * Utility functions for skipping parts of tree. 159 */ 160 void * 161 skip_property(uint32_t *ptr) 162 { 163 uint32_t size; 164 165 size = betoh32(*(ptr + 1)); 166 /* move forward by magic + size + nameid + rounded up property size */ 167 ptr += 3 + roundup(size, sizeof(uint32_t)) / sizeof(uint32_t); 168 169 return ptr; 170 } 171 172 void * 173 skip_props(uint32_t *ptr) 174 { 175 while (betoh32(*ptr) == FDT_PROPERTY) { 176 ptr = skip_property(ptr); 177 } 178 return ptr; 179 } 180 181 void * 182 skip_node_name(uint32_t *ptr) 183 { 184 /* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */ 185 return ptr + roundup(strlen((char *)ptr) + 1, 186 sizeof(uint32_t)) / sizeof(uint32_t); 187 } 188 189 /* 190 * Retrieves node property, the returned pointer is inside the fdt tree, 191 * so we should not modify content pointed by it directly. 192 */ 193 int 194 fdt_node_property(void *node, char *name, char **out) 195 { 196 uint32_t *ptr; 197 uint32_t nameid; 198 char *tmp; 199 200 if (!tree_inited) 201 return 0; 202 203 ptr = (uint32_t *)node; 204 205 if (betoh32(*ptr) != FDT_NODE_BEGIN) 206 return 0; 207 208 ptr = skip_node_name(ptr + 1); 209 210 while (betoh32(*ptr) == FDT_PROPERTY) { 211 nameid = betoh32(*(ptr + 2)); /* id of name in strings table */ 212 tmp = fdt_get_str(nameid); 213 if (!strcmp(name, tmp)) { 214 *out = (char *)(ptr + 3); /* beginning of the value */ 215 return betoh32(*(ptr + 1)); /* size of value */ 216 } 217 ptr = skip_property(ptr); 218 } 219 return 0; 220 } 221 222 int 223 fdt_node_set_property(void *node, char *name, void *data, int len) 224 { 225 uint32_t *ptr, *next; 226 uint32_t nameid; 227 uint32_t curlen; 228 size_t delta; 229 char *tmp; 230 231 if (!tree_inited) 232 return 0; 233 234 ptr = (uint32_t *)node; 235 236 if (betoh32(*ptr) != FDT_NODE_BEGIN) 237 return 0; 238 239 ptr = skip_node_name(ptr + 1); 240 241 while (betoh32(*ptr) == FDT_PROPERTY) { 242 nameid = betoh32(*(ptr + 2)); /* id of name in strings table */ 243 tmp = fdt_get_str(nameid); 244 next = skip_property(ptr); 245 if (!strcmp(name, tmp)) { 246 curlen = betoh32(*(ptr + 1)); 247 delta = roundup(len, sizeof(uint32_t)) - 248 roundup(curlen, sizeof(uint32_t)); 249 memmove((char *)next + delta, next, 250 tree.end - (char *)next); 251 tree.struct_size += delta; 252 if (tree.strings > tree.tree) 253 tree.strings += delta; 254 if (tree.memory > tree.tree) 255 tree.memory += delta; 256 tree.end += delta; 257 *(ptr + 1) = htobe32(len); 258 memcpy(ptr + 3, data, len); 259 return 1; 260 } 261 ptr = next; 262 } 263 return 0; 264 } 265 266 int 267 fdt_node_add_property(void *node, char *name, void *data, int len) 268 { 269 char *dummy; 270 271 if (!tree_inited) 272 return 0; 273 274 if (!fdt_node_property(node, name, &dummy)) { 275 uint32_t *ptr = (uint32_t *)node; 276 277 if (betoh32(*ptr) != FDT_NODE_BEGIN) 278 return 0; 279 280 ptr = skip_node_name(ptr + 1); 281 282 memmove(ptr + 3, ptr, tree.end - (char *)ptr); 283 tree.struct_size += 3 * sizeof(uint32_t); 284 if (tree.strings > tree.tree) 285 tree.strings += 3 * sizeof(uint32_t); 286 if (tree.memory > tree.tree) 287 tree.memory += 3 * sizeof(uint32_t); 288 tree.end += 3 * sizeof(uint32_t); 289 *ptr++ = htobe32(FDT_PROPERTY); 290 *ptr++ = htobe32(0); 291 *ptr++ = htobe32(fdt_add_str(name)); 292 } 293 294 return fdt_node_set_property(node, name, data, len); 295 } 296 297 int 298 fdt_node_add_node(void *node, char *name, void **child) 299 { 300 size_t len = roundup(strlen(name) + 1, sizeof(uint32_t)) + 8; 301 uint32_t *ptr = (uint32_t *)node; 302 303 if (!tree_inited) 304 return 0; 305 306 if (betoh32(*ptr) != FDT_NODE_BEGIN) 307 return 0; 308 309 ptr = skip_node_name(ptr + 1); 310 ptr = skip_props(ptr); 311 312 /* skip children */ 313 while (betoh32(*ptr) == FDT_NODE_BEGIN) 314 ptr = skip_node(ptr); 315 316 memmove((char *)ptr + len, ptr, tree.end - (char *)ptr); 317 tree.struct_size += len; 318 if (tree.strings > tree.tree) 319 tree.strings += len; 320 if (tree.memory > tree.tree) 321 tree.memory += len; 322 tree.end += len; 323 324 *child = ptr; 325 *ptr++ = htobe32(FDT_NODE_BEGIN); 326 memset(ptr, 0, len - 8); 327 memcpy(ptr, name, strlen(name)); 328 ptr += (len - 8) / sizeof(uint32_t); 329 *ptr++ = htobe32(FDT_NODE_END); 330 331 return 1; 332 } 333 334 /* 335 * Retrieves next node, skipping all the children nodes of the pointed node, 336 * returns pointer to next node, no matter if it exists or not. 337 */ 338 void * 339 skip_node(void *node) 340 { 341 uint32_t *ptr = node; 342 343 ptr++; 344 345 ptr = skip_node_name(ptr); 346 ptr = skip_props(ptr); 347 348 /* skip children */ 349 while (betoh32(*ptr) == FDT_NODE_BEGIN) 350 ptr = skip_node(ptr); 351 352 return (ptr + 1); 353 } 354 355 /* 356 * Retrieves next node, skipping all the children nodes of the pointed node, 357 * returns pointer to next node if exists, otherwise returns NULL. 358 * If passed 0 will return first node of the tree (root). 359 */ 360 void * 361 fdt_next_node(void *node) 362 { 363 uint32_t *ptr; 364 365 if (!tree_inited) 366 return NULL; 367 368 ptr = node; 369 370 if (node == NULL) { 371 ptr = (uint32_t *)tree.tree; 372 return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL; 373 } 374 375 if (betoh32(*ptr) != FDT_NODE_BEGIN) 376 return NULL; 377 378 ptr++; 379 380 ptr = skip_node_name(ptr); 381 ptr = skip_props(ptr); 382 383 /* skip children */ 384 while (betoh32(*ptr) == FDT_NODE_BEGIN) 385 ptr = skip_node(ptr); 386 387 if (betoh32(*ptr) != FDT_NODE_END) 388 return NULL; 389 390 if (betoh32(*(ptr + 1)) != FDT_NODE_BEGIN) 391 return NULL; 392 393 return (ptr + 1); 394 } 395 396 /* 397 * Retrieves node property as integers and puts them in the given 398 * integer array. 399 */ 400 int 401 fdt_node_property_ints(void *node, char *name, int *out, int outlen) 402 { 403 int *data; 404 int i, inlen; 405 406 inlen = fdt_node_property(node, name, (char **)&data) / sizeof(int); 407 if (inlen <= 0) 408 return -1; 409 410 for (i = 0; i < inlen && i < outlen; i++) 411 out[i] = betoh32(data[i]); 412 413 return i; 414 } 415 416 /* 417 * Retrieves node property as an integer. 418 */ 419 int 420 fdt_node_property_int(void *node, char *name, int *out) 421 { 422 return fdt_node_property_ints(node, name, out, 1); 423 } 424 425 /* 426 * Retrieves next node, skipping all the children nodes of the pointed node 427 */ 428 void * 429 fdt_child_node(void *node) 430 { 431 uint32_t *ptr; 432 433 if (!tree_inited) 434 return NULL; 435 436 ptr = node; 437 438 if (betoh32(*ptr) != FDT_NODE_BEGIN) 439 return NULL; 440 441 ptr++; 442 443 ptr = skip_node_name(ptr); 444 ptr = skip_props(ptr); 445 /* check if there is a child node */ 446 return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL; 447 } 448 449 /* 450 * Retrieves node name. 451 */ 452 char * 453 fdt_node_name(void *node) 454 { 455 uint32_t *ptr; 456 457 if (!tree_inited) 458 return NULL; 459 460 ptr = node; 461 462 if (betoh32(*ptr) != FDT_NODE_BEGIN) 463 return NULL; 464 465 return (char *)(ptr + 1); 466 } 467 468 void * 469 fdt_find_node(char *name) 470 { 471 void *node = fdt_next_node(0); 472 const char *p = name; 473 474 if (!tree_inited) 475 return NULL; 476 477 if (*p != '/') 478 return NULL; 479 480 while (*p) { 481 void *child; 482 const char *q; 483 484 while (*p == '/') 485 p++; 486 if (*p == 0) 487 return node; 488 q = strchr(p, '/'); 489 if (q == NULL) 490 q = p + strlen(p); 491 492 for (child = fdt_child_node(node); child; 493 child = fdt_next_node(child)) { 494 if (strncmp(p, fdt_node_name(child), q - p) == 0) { 495 node = child; 496 break; 497 } 498 } 499 500 p = q; 501 } 502 503 return node; 504 } 505 506 void * 507 fdt_parent_node_recurse(void *pnode, void *child) 508 { 509 void *node = fdt_child_node(pnode); 510 void *tmp; 511 512 while (node && (node != child)) { 513 if ((tmp = fdt_parent_node_recurse(node, child))) 514 return tmp; 515 node = fdt_next_node(node); 516 } 517 return (node) ? pnode : NULL; 518 } 519 520 void * 521 fdt_parent_node(void *node) 522 { 523 void *pnode = fdt_next_node(0); 524 525 if (!tree_inited) 526 return NULL; 527 528 if (node == pnode) 529 return NULL; 530 531 return fdt_parent_node_recurse(pnode, node); 532 } 533 534 int 535 fdt_node_is_compatible(void *node, const char *name) 536 { 537 char *data; 538 int len; 539 540 len = fdt_node_property(node, "compatible", &data); 541 while (len > 0) { 542 if (strcmp(data, name) == 0) 543 return 1; 544 len -= strlen(data) + 1; 545 data += strlen(data) + 1; 546 } 547 548 return 0; 549 } 550 551 #ifdef DEBUG 552 /* 553 * Debug methods for printing whole tree, particular odes and properies 554 */ 555 void * 556 fdt_print_property(void *node, int level) 557 { 558 uint32_t *ptr; 559 char *tmp, *value; 560 int cnt; 561 uint32_t nameid, size; 562 563 ptr = (uint32_t *)node; 564 565 if (!tree_inited) 566 return NULL; 567 568 if (betoh32(*ptr) != FDT_PROPERTY) 569 return ptr; /* should never happen */ 570 571 /* extract property name_id and size */ 572 size = betoh32(*++ptr); 573 nameid = betoh32(*++ptr); 574 575 for (cnt = 0; cnt < level; cnt++) 576 printf("\t"); 577 578 tmp = fdt_get_str(nameid); 579 printf("\t%s : ", tmp ? tmp : "NO_NAME"); 580 581 ptr++; 582 value = (char *)ptr; 583 584 if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") || 585 !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") || 586 !strcmp(tmp, "linux,stdout-path")) { 587 printf("%s", value); 588 } else if (!strcmp(tmp, "clock-frequency") || 589 !strcmp(tmp, "timebase-frequency")) { 590 printf("%d", betoh32(*((unsigned int *)value))); 591 } else { 592 for (cnt = 0; cnt < size; cnt++) { 593 if ((cnt % sizeof(uint32_t)) == 0) 594 printf(" "); 595 printf("%x%x", value[cnt] >> 4, value[cnt] & 0xf); 596 } 597 } 598 ptr += roundup(size, sizeof(uint32_t)) / sizeof(uint32_t); 599 printf("\n"); 600 601 return ptr; 602 } 603 604 void 605 fdt_print_node(void *node, int level) 606 { 607 uint32_t *ptr; 608 int cnt; 609 610 ptr = (uint32_t *)node; 611 612 if (betoh32(*ptr) != FDT_NODE_BEGIN) 613 return; 614 615 ptr++; 616 617 for (cnt = 0; cnt < level; cnt++) 618 printf("\t"); 619 printf("%s :\n", fdt_node_name(node)); 620 ptr = skip_node_name(ptr); 621 622 while (betoh32(*ptr) == FDT_PROPERTY) 623 ptr = fdt_print_property(ptr, level); 624 } 625 626 void 627 fdt_print_node_recurse(void *node, int level) 628 { 629 void *child; 630 631 fdt_print_node(node, level); 632 for (child = fdt_child_node(node); child; child = fdt_next_node(child)) 633 fdt_print_node_recurse(child, level + 1); 634 } 635 636 void 637 fdt_print_tree(void) 638 { 639 fdt_print_node_recurse(fdt_next_node(0), 0); 640 } 641 #endif 642