1/* $NetBSD: flattree.c,v 1.5 2019/12/22 12:38:24 skrll Exp $ */ 2 3// SPDX-License-Identifier: GPL-2.0-or-later 4/* 5 * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. 6 */ 7 8#include "dtc.h" 9#include "srcpos.h" 10 11#define FTF_FULLPATH 0x1 12#define FTF_VARALIGN 0x2 13#define FTF_NAMEPROPS 0x4 14#define FTF_BOOTCPUID 0x8 15#define FTF_STRTABSIZE 0x10 16#define FTF_STRUCTSIZE 0x20 17#define FTF_NOPS 0x40 18 19static struct version_info { 20 int version; 21 int last_comp_version; 22 int hdr_size; 23 int flags; 24} version_table[] = { 25 {1, 1, FDT_V1_SIZE, 26 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS}, 27 {2, 1, FDT_V2_SIZE, 28 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID}, 29 {3, 1, FDT_V3_SIZE, 30 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE}, 31 {16, 16, FDT_V3_SIZE, 32 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_NOPS}, 33 {17, 16, FDT_V17_SIZE, 34 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS}, 35}; 36 37struct emitter { 38 void (*cell)(void *, cell_t); 39 void (*string)(void *, const char *, int); 40 void (*align)(void *, int); 41 void (*data)(void *, struct data); 42 void (*beginnode)(void *, struct label *labels); 43 void (*endnode)(void *, struct label *labels); 44 void (*property)(void *, struct label *labels); 45}; 46 47static void bin_emit_cell(void *e, cell_t val) 48{ 49 struct data *dtbuf = e; 50 51 *dtbuf = data_append_cell(*dtbuf, val); 52} 53 54static void bin_emit_string(void *e, const char *str, int len) 55{ 56 struct data *dtbuf = e; 57 58 if (len == 0) 59 len = strlen(str); 60 61 *dtbuf = data_append_data(*dtbuf, str, len); 62 *dtbuf = data_append_byte(*dtbuf, '\0'); 63} 64 65static void bin_emit_align(void *e, int a) 66{ 67 struct data *dtbuf = e; 68 69 *dtbuf = data_append_align(*dtbuf, a); 70} 71 72static void bin_emit_data(void *e, struct data d) 73{ 74 struct data *dtbuf = e; 75 76 *dtbuf = data_append_data(*dtbuf, d.val, d.len); 77} 78 79static void bin_emit_beginnode(void *e, struct label *labels) 80{ 81 bin_emit_cell(e, FDT_BEGIN_NODE); 82} 83 84static void bin_emit_endnode(void *e, struct label *labels) 85{ 86 bin_emit_cell(e, FDT_END_NODE); 87} 88 89static void bin_emit_property(void *e, struct label *labels) 90{ 91 bin_emit_cell(e, FDT_PROP); 92} 93 94static struct emitter bin_emitter = { 95 .cell = bin_emit_cell, 96 .string = bin_emit_string, 97 .align = bin_emit_align, 98 .data = bin_emit_data, 99 .beginnode = bin_emit_beginnode, 100 .endnode = bin_emit_endnode, 101 .property = bin_emit_property, 102}; 103 104static void emit_label(FILE *f, const char *prefix, const char *label) 105{ 106 fprintf(f, "\t.globl\t%s_%s\n", prefix, label); 107 fprintf(f, "%s_%s:\n", prefix, label); 108 fprintf(f, "_%s_%s:\n", prefix, label); 109} 110 111static void emit_offset_label(FILE *f, const char *label, int offset) 112{ 113 fprintf(f, "\t.globl\t%s\n", label); 114 fprintf(f, "%s\t= . + %d\n", label, offset); 115} 116 117#define ASM_EMIT_BELONG(f, fmt, ...) \ 118 { \ 119 fprintf((f), "\t.byte\t((" fmt ") >> 24) & 0xff\n", __VA_ARGS__); \ 120 fprintf((f), "\t.byte\t((" fmt ") >> 16) & 0xff\n", __VA_ARGS__); \ 121 fprintf((f), "\t.byte\t((" fmt ") >> 8) & 0xff\n", __VA_ARGS__); \ 122 fprintf((f), "\t.byte\t(" fmt ") & 0xff\n", __VA_ARGS__); \ 123 } 124 125static void asm_emit_cell(void *e, cell_t val) 126{ 127 FILE *f = e; 128 129 fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n", 130 (val >> 24) & 0xff, (val >> 16) & 0xff, 131 (val >> 8) & 0xff, val & 0xff); 132} 133 134static void asm_emit_string(void *e, const char *str, int len) 135{ 136 FILE *f = e; 137 138 if (len != 0) 139 fprintf(f, "\t.string\t\"%.*s\"\n", len, str); 140 else 141 fprintf(f, "\t.string\t\"%s\"\n", str); 142} 143 144static void asm_emit_align(void *e, int a) 145{ 146 FILE *f = e; 147 148 fprintf(f, "\t.balign\t%d, 0\n", a); 149} 150 151static void asm_emit_data(void *e, struct data d) 152{ 153 FILE *f = e; 154 int off = 0; 155 struct marker *m = d.markers; 156 157 for_each_marker_of_type(m, LABEL) 158 emit_offset_label(f, m->ref, m->offset); 159 160 while ((d.len - off) >= sizeof(uint32_t)) { 161 asm_emit_cell(e, fdt32_to_cpu(*((fdt32_t *)(d.val+off)))); 162 off += sizeof(uint32_t); 163 } 164 165 while ((d.len - off) >= 1) { 166 fprintf(f, "\t.byte\t0x%hhx\n", d.val[off]); 167 off += 1; 168 } 169 170 assert(off == d.len); 171} 172 173static void asm_emit_beginnode(void *e, struct label *labels) 174{ 175 FILE *f = e; 176 struct label *l; 177 178 for_each_label(labels, l) { 179 fprintf(f, "\t.globl\t%s\n", l->label); 180 fprintf(f, "%s:\n", l->label); 181 } 182 fprintf(f, "\t/* FDT_BEGIN_NODE */\n"); 183 asm_emit_cell(e, FDT_BEGIN_NODE); 184} 185 186static void asm_emit_endnode(void *e, struct label *labels) 187{ 188 FILE *f = e; 189 struct label *l; 190 191 fprintf(f, "\t/* FDT_END_NODE */\n"); 192 asm_emit_cell(e, FDT_END_NODE); 193 for_each_label(labels, l) { 194 fprintf(f, "\t.globl\t%s_end\n", l->label); 195 fprintf(f, "%s_end:\n", l->label); 196 } 197} 198 199static void asm_emit_property(void *e, struct label *labels) 200{ 201 FILE *f = e; 202 struct label *l; 203 204 for_each_label(labels, l) { 205 fprintf(f, "\t.globl\t%s\n", l->label); 206 fprintf(f, "%s:\n", l->label); 207 } 208 fprintf(f, "\t/* FDT_PROP */\n"); 209 asm_emit_cell(e, FDT_PROP); 210} 211 212static struct emitter asm_emitter = { 213 .cell = asm_emit_cell, 214 .string = asm_emit_string, 215 .align = asm_emit_align, 216 .data = asm_emit_data, 217 .beginnode = asm_emit_beginnode, 218 .endnode = asm_emit_endnode, 219 .property = asm_emit_property, 220}; 221 222static int stringtable_insert(struct data *d, const char *str) 223{ 224 int i; 225 226 /* FIXME: do this more efficiently? */ 227 228 for (i = 0; i < d->len; i++) { 229 if (streq(str, d->val + i)) 230 return i; 231 } 232 233 *d = data_append_data(*d, str, strlen(str)+1); 234 return i; 235} 236 237static void flatten_tree(struct node *tree, struct emitter *emit, 238 void *etarget, struct data *strbuf, 239 struct version_info *vi) 240{ 241 struct property *prop; 242 struct node *child; 243 bool seen_name_prop = false; 244 245 if (tree->deleted) 246 return; 247 248 emit->beginnode(etarget, tree->labels); 249 250 if (vi->flags & FTF_FULLPATH) 251 emit->string(etarget, tree->fullpath, 0); 252 else 253 emit->string(etarget, tree->name, 0); 254 255 emit->align(etarget, sizeof(cell_t)); 256 257 for_each_property(tree, prop) { 258 int nameoff; 259 260 if (streq(prop->name, "name")) 261 seen_name_prop = true; 262 263 nameoff = stringtable_insert(strbuf, prop->name); 264 265 emit->property(etarget, prop->labels); 266 emit->cell(etarget, prop->val.len); 267 emit->cell(etarget, nameoff); 268 269 if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8)) 270 emit->align(etarget, 8); 271 272 emit->data(etarget, prop->val); 273 emit->align(etarget, sizeof(cell_t)); 274 } 275 276 if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) { 277 emit->property(etarget, NULL); 278 emit->cell(etarget, tree->basenamelen+1); 279 emit->cell(etarget, stringtable_insert(strbuf, "name")); 280 281 if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8)) 282 emit->align(etarget, 8); 283 284 emit->string(etarget, tree->name, tree->basenamelen); 285 emit->align(etarget, sizeof(cell_t)); 286 } 287 288 for_each_child(tree, child) { 289 flatten_tree(child, emit, etarget, strbuf, vi); 290 } 291 292 emit->endnode(etarget, tree->labels); 293} 294 295static struct data flatten_reserve_list(struct reserve_info *reservelist, 296 struct version_info *vi) 297{ 298 struct reserve_info *re; 299 struct data d = empty_data; 300 int j; 301 302 for (re = reservelist; re; re = re->next) { 303 d = data_append_re(d, re->address, re->size); 304 } 305 /* 306 * Add additional reserved slots if the user asked for them. 307 */ 308 for (j = 0; j < reservenum; j++) { 309 d = data_append_re(d, 0, 0); 310 } 311 312 return d; 313} 314 315static void make_fdt_header(struct fdt_header *fdt, 316 struct version_info *vi, 317 int reservesize, int dtsize, int strsize, 318 int boot_cpuid_phys) 319{ 320 int reserve_off; 321 322 reservesize += sizeof(struct fdt_reserve_entry); 323 324 memset(fdt, 0xff, sizeof(*fdt)); 325 326 fdt->magic = cpu_to_fdt32(FDT_MAGIC); 327 fdt->version = cpu_to_fdt32(vi->version); 328 fdt->last_comp_version = cpu_to_fdt32(vi->last_comp_version); 329 330 /* Reserve map should be doubleword aligned */ 331 reserve_off = FDTALIGN2(vi->hdr_size, 8); 332 333 fdt->off_mem_rsvmap = cpu_to_fdt32(reserve_off); 334 fdt->off_dt_struct = cpu_to_fdt32(reserve_off + reservesize); 335 fdt->off_dt_strings = cpu_to_fdt32(reserve_off + reservesize 336 + dtsize); 337 fdt->totalsize = cpu_to_fdt32(reserve_off + reservesize + dtsize + strsize); 338 339 if (vi->flags & FTF_BOOTCPUID) 340 fdt->boot_cpuid_phys = cpu_to_fdt32(boot_cpuid_phys); 341 if (vi->flags & FTF_STRTABSIZE) 342 fdt->size_dt_strings = cpu_to_fdt32(strsize); 343 if (vi->flags & FTF_STRUCTSIZE) 344 fdt->size_dt_struct = cpu_to_fdt32(dtsize); 345} 346 347void dt_to_blob(FILE *f, struct dt_info *dti, int version) 348{ 349 struct version_info *vi = NULL; 350 int i; 351 struct data blob = empty_data; 352 struct data reservebuf = empty_data; 353 struct data dtbuf = empty_data; 354 struct data strbuf = empty_data; 355 struct fdt_header fdt; 356 int padlen = 0; 357 358 for (i = 0; i < ARRAY_SIZE(version_table); i++) { 359 if (version_table[i].version == version) 360 vi = &version_table[i]; 361 } 362 if (!vi) 363 die("Unknown device tree blob version %d\n", version); 364 365 flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi); 366 bin_emit_cell(&dtbuf, FDT_END); 367 368 reservebuf = flatten_reserve_list(dti->reservelist, vi); 369 370 /* Make header */ 371 make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len, 372 dti->boot_cpuid_phys); 373 374 /* 375 * If the user asked for more space than is used, adjust the totalsize. 376 */ 377 if (minsize > 0) { 378 padlen = minsize - fdt32_to_cpu(fdt.totalsize); 379 if (padlen < 0) { 380 padlen = 0; 381 if (quiet < 1) 382 fprintf(stderr, 383 "Warning: blob size %"PRIu32" >= minimum size %d\n", 384 fdt32_to_cpu(fdt.totalsize), minsize); 385 } 386 } 387 388 if (padsize > 0) 389 padlen = padsize; 390 391 if (alignsize > 0) 392 padlen = FDTALIGN2(fdt32_to_cpu(fdt.totalsize) + padlen, alignsize) 393 - fdt32_to_cpu(fdt.totalsize); 394 395 if (padlen > 0) { 396 int tsize = fdt32_to_cpu(fdt.totalsize); 397 tsize += padlen; 398 fdt.totalsize = cpu_to_fdt32(tsize); 399 } 400 401 /* 402 * Assemble the blob: start with the header, add with alignment 403 * the reserve buffer, add the reserve map terminating zeroes, 404 * the device tree itself, and finally the strings. 405 */ 406 blob = data_append_data(blob, &fdt, vi->hdr_size); 407 blob = data_append_align(blob, 8); 408 blob = data_merge(blob, reservebuf); 409 blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry)); 410 blob = data_merge(blob, dtbuf); 411 blob = data_merge(blob, strbuf); 412 413 /* 414 * If the user asked for more space than is used, pad out the blob. 415 */ 416 if (padlen > 0) 417 blob = data_append_zeroes(blob, padlen); 418 419 if (fwrite(blob.val, blob.len, 1, f) != 1) { 420 if (ferror(f)) 421 die("Error writing device tree blob: %s\n", 422 strerror(errno)); 423 else 424 die("Short write on device tree blob\n"); 425 } 426 427 /* 428 * data_merge() frees the right-hand element so only the blob 429 * remains to be freed. 430 */ 431 data_free(blob); 432} 433 434static void dump_stringtable_asm(FILE *f, struct data strbuf) 435{ 436 const char *p; 437 int len; 438 439 p = strbuf.val; 440 441 while (p < (strbuf.val + strbuf.len)) { 442 len = strlen(p); 443 fprintf(f, "\t.string \"%s\"\n", p); 444 p += len+1; 445 } 446} 447 448void dt_to_asm(FILE *f, struct dt_info *dti, int version) 449{ 450 struct version_info *vi = NULL; 451 int i; 452 struct data strbuf = empty_data; 453 struct reserve_info *re; 454 const char *symprefix = "dt"; 455 456 for (i = 0; i < ARRAY_SIZE(version_table); i++) { 457 if (version_table[i].version == version) 458 vi = &version_table[i]; 459 } 460 if (!vi) 461 die("Unknown device tree blob version %d\n", version); 462 463 fprintf(f, "/* autogenerated by dtc, do not edit */\n\n"); 464 465 emit_label(f, symprefix, "blob_start"); 466 emit_label(f, symprefix, "header"); 467 fprintf(f, "\t/* magic */\n"); 468 asm_emit_cell(f, FDT_MAGIC); 469 fprintf(f, "\t/* totalsize */\n"); 470 ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start", 471 symprefix, symprefix); 472 fprintf(f, "\t/* off_dt_struct */\n"); 473 ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start", 474 symprefix, symprefix); 475 fprintf(f, "\t/* off_dt_strings */\n"); 476 ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start", 477 symprefix, symprefix); 478 fprintf(f, "\t/* off_mem_rsvmap */\n"); 479 ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start", 480 symprefix, symprefix); 481 fprintf(f, "\t/* version */\n"); 482 asm_emit_cell(f, vi->version); 483 fprintf(f, "\t/* last_comp_version */\n"); 484 asm_emit_cell(f, vi->last_comp_version); 485 486 if (vi->flags & FTF_BOOTCPUID) { 487 fprintf(f, "\t/* boot_cpuid_phys */\n"); 488 asm_emit_cell(f, dti->boot_cpuid_phys); 489 } 490 491 if (vi->flags & FTF_STRTABSIZE) { 492 fprintf(f, "\t/* size_dt_strings */\n"); 493 ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start", 494 symprefix, symprefix); 495 } 496 497 if (vi->flags & FTF_STRUCTSIZE) { 498 fprintf(f, "\t/* size_dt_struct */\n"); 499 ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start", 500 symprefix, symprefix); 501 } 502 503 /* 504 * Reserve map entries. 505 * Align the reserve map to a doubleword boundary. 506 * Each entry is an (address, size) pair of u64 values. 507 * Always supply a zero-sized temination entry. 508 */ 509 asm_emit_align(f, 8); 510 emit_label(f, symprefix, "reserve_map"); 511 512 fprintf(f, "/* Memory reserve map from source file */\n"); 513 514 /* 515 * Use .long on high and low halves of u64s to avoid .quad 516 * as it appears .quad isn't available in some assemblers. 517 */ 518 for (re = dti->reservelist; re; re = re->next) { 519 struct label *l; 520 521 for_each_label(re->labels, l) { 522 fprintf(f, "\t.globl\t%s\n", l->label); 523 fprintf(f, "%s:\n", l->label); 524 } 525 ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->address >> 32)); 526 ASM_EMIT_BELONG(f, "0x%08x", 527 (unsigned int)(re->address & 0xffffffff)); 528 ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->size >> 32)); 529 ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->size & 0xffffffff)); 530 } 531 for (i = 0; i < reservenum; i++) { 532 fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); 533 } 534 535 fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); 536 537 emit_label(f, symprefix, "struct_start"); 538 flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi); 539 540 fprintf(f, "\t/* FDT_END */\n"); 541 asm_emit_cell(f, FDT_END); 542 emit_label(f, symprefix, "struct_end"); 543 544 emit_label(f, symprefix, "strings_start"); 545 dump_stringtable_asm(f, strbuf); 546 emit_label(f, symprefix, "strings_end"); 547 548 emit_label(f, symprefix, "blob_end"); 549 550 /* 551 * If the user asked for more space than is used, pad it out. 552 */ 553 if (minsize > 0) { 554 fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n", 555 minsize, symprefix, symprefix); 556 } 557 if (padsize > 0) { 558 fprintf(f, "\t.space\t%d, 0\n", padsize); 559 } 560 if (alignsize > 0) 561 asm_emit_align(f, alignsize); 562 emit_label(f, symprefix, "blob_abs_end"); 563 564 data_free(strbuf); 565} 566 567struct inbuf { 568 char *base, *limit, *ptr; 569}; 570 571static void inbuf_init(struct inbuf *inb, void *base, void *limit) 572{ 573 inb->base = base; 574 inb->limit = limit; 575 inb->ptr = inb->base; 576} 577 578static void flat_read_chunk(struct inbuf *inb, void *p, int len) 579{ 580 if ((inb->ptr + len) > inb->limit) 581 die("Premature end of data parsing flat device tree\n"); 582 583 memcpy(p, inb->ptr, len); 584 585 inb->ptr += len; 586} 587 588static uint32_t flat_read_word(struct inbuf *inb) 589{ 590 fdt32_t val; 591 592 assert(((inb->ptr - inb->base) % sizeof(val)) == 0); 593 594 flat_read_chunk(inb, &val, sizeof(val)); 595 596 return fdt32_to_cpu(val); 597} 598 599static void flat_realign(struct inbuf *inb, int align) 600{ 601 int off = inb->ptr - inb->base; 602 603 inb->ptr = inb->base + FDTALIGN2(off, align); 604 if (inb->ptr > inb->limit) 605 die("Premature end of data parsing flat device tree\n"); 606} 607 608static char *flat_read_string(struct inbuf *inb) 609{ 610 int len = 0; 611 const char *p = inb->ptr; 612 char *str; 613 614 do { 615 if (p >= inb->limit) 616 die("Premature end of data parsing flat device tree\n"); 617 len++; 618 } while ((*p++) != '\0'); 619 620 str = xstrdup(inb->ptr); 621 622 inb->ptr += len; 623 624 flat_realign(inb, sizeof(uint32_t)); 625 626 return str; 627} 628 629static struct data flat_read_data(struct inbuf *inb, int len) 630{ 631 struct data d = empty_data; 632 633 if (len == 0) 634 return empty_data; 635 636 d = data_grow_for(d, len); 637 d.len = len; 638 639 flat_read_chunk(inb, d.val, len); 640 641 flat_realign(inb, sizeof(uint32_t)); 642 643 return d; 644} 645 646static char *flat_read_stringtable(struct inbuf *inb, int offset) 647{ 648 const char *p; 649 650 p = inb->base + offset; 651 while (1) { 652 if (p >= inb->limit || p < inb->base) 653 die("String offset %d overruns string table\n", 654 offset); 655 656 if (*p == '\0') 657 break; 658 659 p++; 660 } 661 662 return xstrdup(inb->base + offset); 663} 664 665static struct property *flat_read_property(struct inbuf *dtbuf, 666 struct inbuf *strbuf, int flags) 667{ 668 uint32_t proplen, stroff; 669 char *name; 670 struct data val; 671 672 proplen = flat_read_word(dtbuf); 673 stroff = flat_read_word(dtbuf); 674 675 name = flat_read_stringtable(strbuf, stroff); 676 677 if ((flags & FTF_VARALIGN) && (proplen >= 8)) 678 flat_realign(dtbuf, 8); 679 680 val = flat_read_data(dtbuf, proplen); 681 682 return build_property(name, val, NULL); 683} 684 685 686static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb) 687{ 688 struct reserve_info *reservelist = NULL; 689 struct reserve_info *new; 690 struct fdt_reserve_entry re; 691 692 /* 693 * Each entry is a pair of u64 (addr, size) values for 4 cell_t's. 694 * List terminates at an entry with size equal to zero. 695 * 696 * First pass, count entries. 697 */ 698 while (1) { 699 uint64_t address, size; 700 701 flat_read_chunk(inb, &re, sizeof(re)); 702 address = fdt64_to_cpu(re.address); 703 size = fdt64_to_cpu(re.size); 704 if (size == 0) 705 break; 706 707 new = build_reserve_entry(address, size); 708 reservelist = add_reserve_entry(reservelist, new); 709 } 710 711 return reservelist; 712} 713 714 715static char *nodename_from_path(const char *ppath, const char *cpath) 716{ 717 int plen; 718 719 plen = strlen(ppath); 720 721 if (!strstarts(cpath, ppath)) 722 die("Path \"%s\" is not valid as a child of \"%s\"\n", 723 cpath, ppath); 724 725 /* root node is a special case */ 726 if (!streq(ppath, "/")) 727 plen++; 728 729 return xstrdup(cpath + plen); 730} 731 732static struct node *unflatten_tree(struct inbuf *dtbuf, 733 struct inbuf *strbuf, 734 const char *parent_flatname, int flags) 735{ 736 struct node *node; 737 char *flatname; 738 uint32_t val; 739 740 node = build_node(NULL, NULL, NULL); 741 742 flatname = flat_read_string(dtbuf); 743 744 if (flags & FTF_FULLPATH) 745 node->name = nodename_from_path(parent_flatname, flatname); 746 else 747 node->name = flatname; 748 749 do { 750 struct property *prop; 751 struct node *child; 752 753 val = flat_read_word(dtbuf); 754 switch (val) { 755 case FDT_PROP: 756 if (node->children) 757 fprintf(stderr, "Warning: Flat tree input has " 758 "subnodes preceding a property.\n"); 759 prop = flat_read_property(dtbuf, strbuf, flags); 760 add_property(node, prop); 761 break; 762 763 case FDT_BEGIN_NODE: 764 child = unflatten_tree(dtbuf,strbuf, flatname, flags); 765 add_child(node, child); 766 break; 767 768 case FDT_END_NODE: 769 break; 770 771 case FDT_END: 772 die("Premature FDT_END in device tree blob\n"); 773 break; 774 775 case FDT_NOP: 776 if (!(flags & FTF_NOPS)) 777 fprintf(stderr, "Warning: NOP tag found in flat tree" 778 " version <16\n"); 779 780 /* Ignore */ 781 break; 782 783 default: 784 die("Invalid opcode word %08x in device tree blob\n", 785 val); 786 } 787 } while (val != FDT_END_NODE); 788 789 if (node->name != flatname) { 790 free(flatname); 791 } 792 793 return node; 794} 795 796 797struct dt_info *dt_from_blob(const char *fname) 798{ 799 FILE *f; 800 fdt32_t magic_buf, totalsize_buf; 801 uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys; 802 uint32_t off_dt, off_str, off_mem_rsvmap; 803 int rc; 804 char *blob; 805 struct fdt_header *fdt; 806 char *p; 807 struct inbuf dtbuf, strbuf; 808 struct inbuf memresvbuf; 809 int sizeleft; 810 struct reserve_info *reservelist; 811 struct node *tree; 812 uint32_t val; 813 int flags = 0; 814 815 f = srcfile_relative_open(fname, NULL); 816 817 rc = fread(&magic_buf, sizeof(magic_buf), 1, f); 818 if (ferror(f)) 819 die("Error reading DT blob magic number: %s\n", 820 strerror(errno)); 821 if (rc < 1) { 822 if (feof(f)) 823 die("EOF reading DT blob magic number\n"); 824 else 825 die("Mysterious short read reading magic number\n"); 826 } 827 828 magic = fdt32_to_cpu(magic_buf); 829 if (magic != FDT_MAGIC) 830 die("Blob has incorrect magic number\n"); 831 832 rc = fread(&totalsize_buf, sizeof(totalsize_buf), 1, f); 833 if (ferror(f)) 834 die("Error reading DT blob size: %s\n", strerror(errno)); 835 if (rc < 1) { 836 if (feof(f)) 837 die("EOF reading DT blob size\n"); 838 else 839 die("Mysterious short read reading blob size\n"); 840 } 841 842 totalsize = fdt32_to_cpu(totalsize_buf); 843 if (totalsize < FDT_V1_SIZE) 844 die("DT blob size (%d) is too small\n", totalsize); 845 846 blob = xmalloc(totalsize); 847 848 fdt = (struct fdt_header *)blob; 849 fdt->magic = cpu_to_fdt32(magic); 850 fdt->totalsize = cpu_to_fdt32(totalsize); 851 852 sizeleft = totalsize - sizeof(magic) - sizeof(totalsize); 853 p = blob + sizeof(magic) + sizeof(totalsize); 854 855 while (sizeleft) { 856 if (feof(f)) 857 die("EOF before reading %d bytes of DT blob\n", 858 totalsize); 859 860 rc = fread(p, 1, sizeleft, f); 861 if (ferror(f)) 862 die("Error reading DT blob: %s\n", 863 strerror(errno)); 864 865 sizeleft -= rc; 866 p += rc; 867 } 868 869 off_dt = fdt32_to_cpu(fdt->off_dt_struct); 870 off_str = fdt32_to_cpu(fdt->off_dt_strings); 871 off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap); 872 version = fdt32_to_cpu(fdt->version); 873 boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys); 874 875 if (off_mem_rsvmap >= totalsize) 876 die("Mem Reserve structure offset exceeds total size\n"); 877 878 if (off_dt >= totalsize) 879 die("DT structure offset exceeds total size\n"); 880 881 if (off_str > totalsize) 882 die("String table offset exceeds total size\n"); 883 884 if (version >= 3) { 885 uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings); 886 if ((off_str+size_str < off_str) || (off_str+size_str > totalsize)) 887 die("String table extends past total size\n"); 888 inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str); 889 } else { 890 inbuf_init(&strbuf, blob + off_str, blob + totalsize); 891 } 892 893 if (version >= 17) { 894 size_dt = fdt32_to_cpu(fdt->size_dt_struct); 895 if ((off_dt+size_dt < off_dt) || (off_dt+size_dt > totalsize)) 896 die("Structure block extends past total size\n"); 897 } 898 899 if (version < 16) { 900 flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN; 901 } else { 902 flags |= FTF_NOPS; 903 } 904 905 inbuf_init(&memresvbuf, 906 blob + off_mem_rsvmap, blob + totalsize); 907 inbuf_init(&dtbuf, blob + off_dt, blob + totalsize); 908 909 reservelist = flat_read_mem_reserve(&memresvbuf); 910 911 val = flat_read_word(&dtbuf); 912 913 if (val != FDT_BEGIN_NODE) 914 die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val); 915 916 tree = unflatten_tree(&dtbuf, &strbuf, "", flags); 917 918 val = flat_read_word(&dtbuf); 919 if (val != FDT_END) 920 die("Device tree blob doesn't end with FDT_END\n"); 921 922 free(blob); 923 924 fclose(f); 925 926 return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys); 927} 928