1 /* $OpenBSD: fdt.c,v 1.4 2016/05/19 19:32:07 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 memcpy(end, name, len); 152 153 return (end - tree.strings); 154 } 155 156 /* 157 * Utility functions for skipping parts of tree. 158 */ 159 void * 160 skip_property(uint32_t *ptr) 161 { 162 uint32_t size; 163 164 size = betoh32(*(ptr + 1)); 165 /* move forward by magic + size + nameid + rounded up property size */ 166 ptr += 3 + roundup(size, sizeof(uint32_t)) / sizeof(uint32_t); 167 168 return ptr; 169 } 170 171 void * 172 skip_props(uint32_t *ptr) 173 { 174 while (betoh32(*ptr) == FDT_PROPERTY) { 175 ptr = skip_property(ptr); 176 } 177 return ptr; 178 } 179 180 void * 181 skip_node_name(uint32_t *ptr) 182 { 183 /* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */ 184 return ptr + roundup(strlen((char *)ptr) + 1, 185 sizeof(uint32_t)) / sizeof(uint32_t); 186 } 187 188 /* 189 * Retrieves node property, the returned pointer is inside the fdt tree, 190 * so we should not modify content pointed by it directly. 191 */ 192 int 193 fdt_node_property(void *node, char *name, char **out) 194 { 195 uint32_t *ptr; 196 uint32_t nameid; 197 char *tmp; 198 199 if (!tree_inited) 200 return 0; 201 202 ptr = (uint32_t *)node; 203 204 if (betoh32(*ptr) != FDT_NODE_BEGIN) 205 return 0; 206 207 ptr = skip_node_name(ptr + 1); 208 209 while (betoh32(*ptr) == FDT_PROPERTY) { 210 nameid = betoh32(*(ptr + 2)); /* id of name in strings table */ 211 tmp = fdt_get_str(nameid); 212 if (!strcmp(name, tmp)) { 213 *out = (char *)(ptr + 3); /* beginning of the value */ 214 return betoh32(*(ptr + 1)); /* size of value */ 215 } 216 ptr = skip_property(ptr); 217 } 218 return 0; 219 } 220 221 int 222 fdt_node_set_property(void *node, char *name, char *data, int len) 223 { 224 uint32_t *ptr, *next; 225 uint32_t nameid; 226 uint32_t curlen; 227 size_t delta; 228 char *tmp; 229 230 if (!tree_inited) 231 return 0; 232 233 ptr = (uint32_t *)node; 234 235 if (betoh32(*ptr) != FDT_NODE_BEGIN) 236 return 0; 237 238 ptr = skip_node_name(ptr + 1); 239 240 while (betoh32(*ptr) == FDT_PROPERTY) { 241 nameid = betoh32(*(ptr + 2)); /* id of name in strings table */ 242 tmp = fdt_get_str(nameid); 243 next = skip_property(ptr); 244 if (!strcmp(name, tmp)) { 245 curlen = betoh32(*(ptr + 1)); 246 delta = roundup(len, sizeof(uint32_t)) - 247 roundup(curlen, sizeof(uint32_t)); 248 memmove((char *)next + delta, next, 249 tree.end - (char *)next); 250 tree.struct_size += delta; 251 if (tree.strings > tree.tree) 252 tree.strings += delta; 253 if (tree.memory > tree.tree) 254 tree.memory += delta; 255 tree.end += delta; 256 *(ptr + 1) = htobe32(len); 257 memcpy(ptr + 3, data, len); 258 return 1; 259 } 260 ptr = next; 261 } 262 return 0; 263 } 264 265 int 266 fdt_node_add_property(void *node, char *name, char *data, int len) 267 { 268 char *dummy; 269 270 if (!tree_inited) 271 return 0; 272 273 if (!fdt_node_property(node, name, &dummy)) { 274 uint32_t *ptr = (uint32_t *)node; 275 276 if (betoh32(*ptr) != FDT_NODE_BEGIN) 277 return 0; 278 279 ptr = skip_node_name(ptr + 1); 280 281 memmove(ptr + 3, ptr, tree.end - (char *)ptr); 282 tree.struct_size += 3 * sizeof(uint32_t); 283 if (tree.strings > tree.tree) 284 tree.strings += 3 * sizeof(uint32_t); 285 if (tree.memory > tree.tree) 286 tree.memory += 3 * sizeof(uint32_t); 287 tree.end += 3 * sizeof(uint32_t); 288 *ptr++ = htobe32(FDT_PROPERTY); 289 *ptr++ = htobe32(0); 290 *ptr++ = htobe32(fdt_add_str(name)); 291 } 292 293 return fdt_node_set_property(node, name, data, len); 294 } 295 296 /* 297 * Retrieves next node, skipping all the children nodes of the pointed node, 298 * returns pointer to next node, no matter if it exists or not. 299 */ 300 void * 301 skip_node(void *node) 302 { 303 uint32_t *ptr = node; 304 305 ptr++; 306 307 ptr = skip_node_name(ptr); 308 ptr = skip_props(ptr); 309 310 /* skip children */ 311 while (betoh32(*ptr) == FDT_NODE_BEGIN) 312 ptr = skip_node(ptr); 313 314 return (ptr + 1); 315 } 316 317 /* 318 * Retrieves next node, skipping all the children nodes of the pointed node, 319 * returns pointer to next node if exists, otherwise returns NULL. 320 * If passed 0 will return first node of the tree (root). 321 */ 322 void * 323 fdt_next_node(void *node) 324 { 325 uint32_t *ptr; 326 327 if (!tree_inited) 328 return NULL; 329 330 ptr = node; 331 332 if (node == NULL) { 333 ptr = (uint32_t *)tree.tree; 334 return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL; 335 } 336 337 if (betoh32(*ptr) != FDT_NODE_BEGIN) 338 return NULL; 339 340 ptr++; 341 342 ptr = skip_node_name(ptr); 343 ptr = skip_props(ptr); 344 345 /* skip children */ 346 while (betoh32(*ptr) == FDT_NODE_BEGIN) 347 ptr = skip_node(ptr); 348 349 if (betoh32(*ptr) != FDT_NODE_END) 350 return NULL; 351 352 if (betoh32(*(ptr + 1)) != FDT_NODE_BEGIN) 353 return NULL; 354 355 return (ptr + 1); 356 } 357 358 /* 359 * Retrieves next node, skipping all the children nodes of the pointed node 360 */ 361 void * 362 fdt_child_node(void *node) 363 { 364 uint32_t *ptr; 365 366 if (!tree_inited) 367 return NULL; 368 369 ptr = node; 370 371 if (betoh32(*ptr) != FDT_NODE_BEGIN) 372 return NULL; 373 374 ptr++; 375 376 ptr = skip_node_name(ptr); 377 ptr = skip_props(ptr); 378 /* check if there is a child node */ 379 return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL; 380 } 381 382 /* 383 * Retrieves node name. 384 */ 385 char * 386 fdt_node_name(void *node) 387 { 388 uint32_t *ptr; 389 390 if (!tree_inited) 391 return NULL; 392 393 ptr = node; 394 395 if (betoh32(*ptr) != FDT_NODE_BEGIN) 396 return NULL; 397 398 return (char *)(ptr + 1); 399 } 400 401 void * 402 fdt_find_node(char *name) 403 { 404 void *node = fdt_next_node(0); 405 const char *p = name; 406 407 if (!tree_inited) 408 return NULL; 409 410 if (*p != '/') 411 return NULL; 412 413 while (*p) { 414 void *child; 415 const char *q; 416 417 while (*p == '/') 418 p++; 419 if (*p == 0) 420 return node; 421 q = strchr(p, '/'); 422 if (q == NULL) 423 q = p + strlen(p); 424 425 for (child = fdt_child_node(node); child; 426 child = fdt_next_node(child)) { 427 if (strncmp(p, fdt_node_name(child), q - p) == 0) { 428 node = child; 429 break; 430 } 431 } 432 433 p = q; 434 } 435 436 return node; 437 } 438 439 void * 440 fdt_parent_node_recurse(void *pnode, void *child) 441 { 442 void *node = fdt_child_node(pnode); 443 void *tmp; 444 445 while (node && (node != child)) { 446 if ((tmp = fdt_parent_node_recurse(node, child))) 447 return tmp; 448 node = fdt_next_node(node); 449 } 450 return (node) ? pnode : NULL; 451 } 452 453 void * 454 fdt_parent_node(void *node) 455 { 456 void *pnode = fdt_next_node(0); 457 458 if (!tree_inited) 459 return NULL; 460 461 if (node == pnode) 462 return NULL; 463 464 return fdt_parent_node_recurse(pnode, node); 465 } 466 467 int 468 fdt_node_is_compatible(void *node, const char *name) 469 { 470 char *data; 471 int len; 472 473 len = fdt_node_property(node, "compatible", &data); 474 while (len > 0) { 475 if (strcmp(data, name) == 0) 476 return 1; 477 len -= strlen(data) + 1; 478 data += strlen(data) + 1; 479 } 480 481 return 0; 482 } 483 484 #ifdef DEBUG 485 /* 486 * Debug methods for printing whole tree, particular odes and properies 487 */ 488 void * 489 fdt_print_property(void *node, int level) 490 { 491 uint32_t *ptr; 492 char *tmp, *value; 493 int cnt; 494 uint32_t nameid, size; 495 496 ptr = (uint32_t *)node; 497 498 if (!tree_inited) 499 return NULL; 500 501 if (betoh32(*ptr) != FDT_PROPERTY) 502 return ptr; /* should never happen */ 503 504 /* extract property name_id and size */ 505 size = betoh32(*++ptr); 506 nameid = betoh32(*++ptr); 507 508 for (cnt = 0; cnt < level; cnt++) 509 printf("\t"); 510 511 tmp = fdt_get_str(nameid); 512 printf("\t%s : ", tmp ? tmp : "NO_NAME"); 513 514 ptr++; 515 value = (char *)ptr; 516 517 if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") || 518 !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") || 519 !strcmp(tmp, "linux,stdout-path")) { 520 printf("%s", value); 521 } else if (!strcmp(tmp, "clock-frequency") || 522 !strcmp(tmp, "timebase-frequency")) { 523 printf("%d", betoh32(*((unsigned int *)value))); 524 } else { 525 for (cnt = 0; cnt < size; cnt++) { 526 if ((cnt % sizeof(uint32_t)) == 0) 527 printf(" "); 528 printf("%x%x", value[cnt] >> 4, value[cnt] & 0xf); 529 } 530 } 531 ptr += roundup(size, sizeof(uint32_t)) / sizeof(uint32_t); 532 printf("\n"); 533 534 return ptr; 535 } 536 537 void 538 fdt_print_node(void *node, int level) 539 { 540 uint32_t *ptr; 541 int cnt; 542 543 ptr = (uint32_t *)node; 544 545 if (betoh32(*ptr) != FDT_NODE_BEGIN) 546 return; 547 548 ptr++; 549 550 for (cnt = 0; cnt < level; cnt++) 551 printf("\t"); 552 printf("%s :\n", fdt_node_name(node)); 553 ptr = skip_node_name(ptr); 554 555 while (betoh32(*ptr) == FDT_PROPERTY) 556 ptr = fdt_print_property(ptr, level); 557 } 558 559 void 560 fdt_print_node_recurse(void *node, int level) 561 { 562 void *child; 563 564 fdt_print_node(node, level); 565 for (child = fdt_child_node(node); child; child = fdt_next_node(child)) 566 fdt_print_node_recurse(child, level + 1); 567 } 568 569 void 570 fdt_print_tree(void) 571 { 572 fdt_print_node_recurse(fdt_next_node(0), 0); 573 } 574 #endif 575