1204431Sraj/* 2204431Sraj * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. 3204431Sraj * 4204431Sraj * 5204431Sraj * This program is free software; you can redistribute it and/or 6204431Sraj * modify it under the terms of the GNU General Public License as 7204431Sraj * published by the Free Software Foundation; either version 2 of the 8204431Sraj * License, or (at your option) any later version. 9204431Sraj * 10204431Sraj * This program is distributed in the hope that it will be useful, 11204431Sraj * but WITHOUT ANY WARRANTY; without even the implied warranty of 12204431Sraj * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13204431Sraj * General Public License for more details. 14204431Sraj * 15204431Sraj * You should have received a copy of the GNU General Public License 16204431Sraj * along with this program; if not, write to the Free Software 17204431Sraj * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 18204431Sraj * USA 19204431Sraj */ 20204431Sraj 21204431Sraj#include "dtc.h" 22204431Sraj#include "srcpos.h" 23204431Sraj 24204431Sraj#define FTF_FULLPATH 0x1 25204431Sraj#define FTF_VARALIGN 0x2 26204431Sraj#define FTF_NAMEPROPS 0x4 27204431Sraj#define FTF_BOOTCPUID 0x8 28204431Sraj#define FTF_STRTABSIZE 0x10 29204431Sraj#define FTF_STRUCTSIZE 0x20 30204431Sraj#define FTF_NOPS 0x40 31204431Sraj 32204431Srajstatic struct version_info { 33204431Sraj int version; 34204431Sraj int last_comp_version; 35204431Sraj int hdr_size; 36204431Sraj int flags; 37204431Sraj} version_table[] = { 38204431Sraj {1, 1, FDT_V1_SIZE, 39204431Sraj FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS}, 40204431Sraj {2, 1, FDT_V2_SIZE, 41204431Sraj FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID}, 42204431Sraj {3, 1, FDT_V3_SIZE, 43204431Sraj FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE}, 44204431Sraj {16, 16, FDT_V3_SIZE, 45204431Sraj FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_NOPS}, 46204431Sraj {17, 16, FDT_V17_SIZE, 47204431Sraj FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS}, 48204431Sraj}; 49204431Sraj 50204431Srajstruct emitter { 51204431Sraj void (*cell)(void *, cell_t); 52204431Sraj void (*string)(void *, char *, int); 53204431Sraj void (*align)(void *, int); 54204431Sraj void (*data)(void *, struct data); 55238742Simp void (*beginnode)(void *, struct label *labels); 56238742Simp void (*endnode)(void *, struct label *labels); 57238742Simp void (*property)(void *, struct label *labels); 58204431Sraj}; 59204431Sraj 60204431Srajstatic void bin_emit_cell(void *e, cell_t val) 61204431Sraj{ 62204431Sraj struct data *dtbuf = e; 63204431Sraj 64204431Sraj *dtbuf = data_append_cell(*dtbuf, val); 65204431Sraj} 66204431Sraj 67204431Srajstatic void bin_emit_string(void *e, char *str, int len) 68204431Sraj{ 69204431Sraj struct data *dtbuf = e; 70204431Sraj 71204431Sraj if (len == 0) 72204431Sraj len = strlen(str); 73204431Sraj 74204431Sraj *dtbuf = data_append_data(*dtbuf, str, len); 75204431Sraj *dtbuf = data_append_byte(*dtbuf, '\0'); 76204431Sraj} 77204431Sraj 78204431Srajstatic void bin_emit_align(void *e, int a) 79204431Sraj{ 80204431Sraj struct data *dtbuf = e; 81204431Sraj 82204431Sraj *dtbuf = data_append_align(*dtbuf, a); 83204431Sraj} 84204431Sraj 85204431Srajstatic void bin_emit_data(void *e, struct data d) 86204431Sraj{ 87204431Sraj struct data *dtbuf = e; 88204431Sraj 89204431Sraj *dtbuf = data_append_data(*dtbuf, d.val, d.len); 90204431Sraj} 91204431Sraj 92238742Simpstatic void bin_emit_beginnode(void *e, struct label *labels) 93204431Sraj{ 94204431Sraj bin_emit_cell(e, FDT_BEGIN_NODE); 95204431Sraj} 96204431Sraj 97238742Simpstatic void bin_emit_endnode(void *e, struct label *labels) 98204431Sraj{ 99204431Sraj bin_emit_cell(e, FDT_END_NODE); 100204431Sraj} 101204431Sraj 102238742Simpstatic void bin_emit_property(void *e, struct label *labels) 103204431Sraj{ 104204431Sraj bin_emit_cell(e, FDT_PROP); 105204431Sraj} 106204431Sraj 107204431Srajstatic struct emitter bin_emitter = { 108204431Sraj .cell = bin_emit_cell, 109204431Sraj .string = bin_emit_string, 110204431Sraj .align = bin_emit_align, 111204431Sraj .data = bin_emit_data, 112204431Sraj .beginnode = bin_emit_beginnode, 113204431Sraj .endnode = bin_emit_endnode, 114204431Sraj .property = bin_emit_property, 115204431Sraj}; 116204431Sraj 117204431Srajstatic void emit_label(FILE *f, const char *prefix, const char *label) 118204431Sraj{ 119204431Sraj fprintf(f, "\t.globl\t%s_%s\n", prefix, label); 120204431Sraj fprintf(f, "%s_%s:\n", prefix, label); 121204431Sraj fprintf(f, "_%s_%s:\n", prefix, label); 122204431Sraj} 123204431Sraj 124204431Srajstatic void emit_offset_label(FILE *f, const char *label, int offset) 125204431Sraj{ 126204431Sraj fprintf(f, "\t.globl\t%s\n", label); 127204431Sraj fprintf(f, "%s\t= . + %d\n", label, offset); 128204431Sraj} 129204431Sraj 130204433Sraj#define ASM_EMIT_BELONG(f, fmt, ...) \ 131204433Sraj { \ 132204433Sraj fprintf((f), "\t.byte\t((" fmt ") >> 24) & 0xff\n", __VA_ARGS__); \ 133204433Sraj fprintf((f), "\t.byte\t((" fmt ") >> 16) & 0xff\n", __VA_ARGS__); \ 134204433Sraj fprintf((f), "\t.byte\t((" fmt ") >> 8) & 0xff\n", __VA_ARGS__); \ 135204433Sraj fprintf((f), "\t.byte\t(" fmt ") & 0xff\n", __VA_ARGS__); \ 136204433Sraj } 137204433Sraj 138204431Srajstatic void asm_emit_cell(void *e, cell_t val) 139204431Sraj{ 140204431Sraj FILE *f = e; 141204431Sraj 142204433Sraj fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n", 143204433Sraj (val >> 24) & 0xff, (val >> 16) & 0xff, 144204433Sraj (val >> 8) & 0xff, val & 0xff); 145204431Sraj} 146204431Sraj 147204431Srajstatic void asm_emit_string(void *e, char *str, int len) 148204431Sraj{ 149204431Sraj FILE *f = e; 150204431Sraj char c = 0; 151204431Sraj 152204431Sraj if (len != 0) { 153204431Sraj /* XXX: ewww */ 154204431Sraj c = str[len]; 155204431Sraj str[len] = '\0'; 156204431Sraj } 157204431Sraj 158204431Sraj fprintf(f, "\t.string\t\"%s\"\n", str); 159204431Sraj 160204431Sraj if (len != 0) { 161204431Sraj str[len] = c; 162204431Sraj } 163204431Sraj} 164204431Sraj 165204431Srajstatic void asm_emit_align(void *e, int a) 166204431Sraj{ 167204431Sraj FILE *f = e; 168204431Sraj 169204433Sraj fprintf(f, "\t.balign\t%d, 0\n", a); 170204431Sraj} 171204431Sraj 172204431Srajstatic void asm_emit_data(void *e, struct data d) 173204431Sraj{ 174204431Sraj FILE *f = e; 175204431Sraj int off = 0; 176204431Sraj struct marker *m = d.markers; 177204431Sraj 178204431Sraj for_each_marker_of_type(m, LABEL) 179204431Sraj emit_offset_label(f, m->ref, m->offset); 180204431Sraj 181204431Sraj while ((d.len - off) >= sizeof(uint32_t)) { 182204433Sraj asm_emit_cell(e, fdt32_to_cpu(*((uint32_t *)(d.val+off)))); 183204431Sraj off += sizeof(uint32_t); 184204431Sraj } 185204431Sraj 186204431Sraj while ((d.len - off) >= 1) { 187204431Sraj fprintf(f, "\t.byte\t0x%hhx\n", d.val[off]); 188204431Sraj off += 1; 189204431Sraj } 190204431Sraj 191204431Sraj assert(off == d.len); 192204431Sraj} 193204431Sraj 194238742Simpstatic void asm_emit_beginnode(void *e, struct label *labels) 195204431Sraj{ 196204431Sraj FILE *f = e; 197238742Simp struct label *l; 198204431Sraj 199238742Simp for_each_label(labels, l) { 200238742Simp fprintf(f, "\t.globl\t%s\n", l->label); 201238742Simp fprintf(f, "%s:\n", l->label); 202204431Sraj } 203204433Sraj fprintf(f, "\t/* FDT_BEGIN_NODE */\n"); 204204433Sraj asm_emit_cell(e, FDT_BEGIN_NODE); 205204431Sraj} 206204431Sraj 207238742Simpstatic void asm_emit_endnode(void *e, struct label *labels) 208204431Sraj{ 209204431Sraj FILE *f = e; 210238742Simp struct label *l; 211204431Sraj 212204433Sraj fprintf(f, "\t/* FDT_END_NODE */\n"); 213204433Sraj asm_emit_cell(e, FDT_END_NODE); 214238742Simp for_each_label(labels, l) { 215238742Simp fprintf(f, "\t.globl\t%s_end\n", l->label); 216238742Simp fprintf(f, "%s_end:\n", l->label); 217204431Sraj } 218204431Sraj} 219204431Sraj 220238742Simpstatic void asm_emit_property(void *e, struct label *labels) 221204431Sraj{ 222204431Sraj FILE *f = e; 223238742Simp struct label *l; 224204431Sraj 225238742Simp for_each_label(labels, l) { 226238742Simp fprintf(f, "\t.globl\t%s\n", l->label); 227238742Simp fprintf(f, "%s:\n", l->label); 228204431Sraj } 229204433Sraj fprintf(f, "\t/* FDT_PROP */\n"); 230204433Sraj asm_emit_cell(e, FDT_PROP); 231204431Sraj} 232204431Sraj 233204431Srajstatic struct emitter asm_emitter = { 234204431Sraj .cell = asm_emit_cell, 235204431Sraj .string = asm_emit_string, 236204431Sraj .align = asm_emit_align, 237204431Sraj .data = asm_emit_data, 238204431Sraj .beginnode = asm_emit_beginnode, 239204431Sraj .endnode = asm_emit_endnode, 240204431Sraj .property = asm_emit_property, 241204431Sraj}; 242204431Sraj 243204431Srajstatic int stringtable_insert(struct data *d, const char *str) 244204431Sraj{ 245204431Sraj int i; 246204431Sraj 247204431Sraj /* FIXME: do this more efficiently? */ 248204431Sraj 249204431Sraj for (i = 0; i < d->len; i++) { 250204431Sraj if (streq(str, d->val + i)) 251204431Sraj return i; 252204431Sraj } 253204431Sraj 254204431Sraj *d = data_append_data(*d, str, strlen(str)+1); 255204431Sraj return i; 256204431Sraj} 257204431Sraj 258204431Srajstatic void flatten_tree(struct node *tree, struct emitter *emit, 259204431Sraj void *etarget, struct data *strbuf, 260204431Sraj struct version_info *vi) 261204431Sraj{ 262204431Sraj struct property *prop; 263204431Sraj struct node *child; 264266130Sian bool seen_name_prop = false; 265204431Sraj 266266130Sian if (tree->deleted) 267266130Sian return; 268266130Sian 269238742Simp emit->beginnode(etarget, tree->labels); 270204431Sraj 271204431Sraj if (vi->flags & FTF_FULLPATH) 272204431Sraj emit->string(etarget, tree->fullpath, 0); 273204431Sraj else 274204431Sraj emit->string(etarget, tree->name, 0); 275204431Sraj 276204431Sraj emit->align(etarget, sizeof(cell_t)); 277204431Sraj 278204431Sraj for_each_property(tree, prop) { 279204431Sraj int nameoff; 280204431Sraj 281204431Sraj if (streq(prop->name, "name")) 282266130Sian seen_name_prop = true; 283204431Sraj 284204431Sraj nameoff = stringtable_insert(strbuf, prop->name); 285204431Sraj 286238742Simp emit->property(etarget, prop->labels); 287204431Sraj emit->cell(etarget, prop->val.len); 288204431Sraj emit->cell(etarget, nameoff); 289204431Sraj 290204431Sraj if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8)) 291204431Sraj emit->align(etarget, 8); 292204431Sraj 293204431Sraj emit->data(etarget, prop->val); 294204431Sraj emit->align(etarget, sizeof(cell_t)); 295204431Sraj } 296204431Sraj 297204431Sraj if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) { 298204431Sraj emit->property(etarget, NULL); 299204431Sraj emit->cell(etarget, tree->basenamelen+1); 300204431Sraj emit->cell(etarget, stringtable_insert(strbuf, "name")); 301204431Sraj 302204431Sraj if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8)) 303204431Sraj emit->align(etarget, 8); 304204431Sraj 305204431Sraj emit->string(etarget, tree->name, tree->basenamelen); 306204431Sraj emit->align(etarget, sizeof(cell_t)); 307204431Sraj } 308204431Sraj 309204431Sraj for_each_child(tree, child) { 310204431Sraj flatten_tree(child, emit, etarget, strbuf, vi); 311204431Sraj } 312204431Sraj 313238742Simp emit->endnode(etarget, tree->labels); 314204431Sraj} 315204431Sraj 316204431Srajstatic struct data flatten_reserve_list(struct reserve_info *reservelist, 317204431Sraj struct version_info *vi) 318204431Sraj{ 319204431Sraj struct reserve_info *re; 320204431Sraj struct data d = empty_data; 321204431Sraj static struct fdt_reserve_entry null_re = {0,0}; 322204431Sraj int j; 323204431Sraj 324204431Sraj for (re = reservelist; re; re = re->next) { 325204431Sraj d = data_append_re(d, &re->re); 326204431Sraj } 327204431Sraj /* 328204431Sraj * Add additional reserved slots if the user asked for them. 329204431Sraj */ 330204431Sraj for (j = 0; j < reservenum; j++) { 331204431Sraj d = data_append_re(d, &null_re); 332204431Sraj } 333204431Sraj 334204431Sraj return d; 335204431Sraj} 336204431Sraj 337204431Srajstatic void make_fdt_header(struct fdt_header *fdt, 338204431Sraj struct version_info *vi, 339204431Sraj int reservesize, int dtsize, int strsize, 340204431Sraj int boot_cpuid_phys) 341204431Sraj{ 342204431Sraj int reserve_off; 343204431Sraj 344204431Sraj reservesize += sizeof(struct fdt_reserve_entry); 345204431Sraj 346204431Sraj memset(fdt, 0xff, sizeof(*fdt)); 347204431Sraj 348204431Sraj fdt->magic = cpu_to_fdt32(FDT_MAGIC); 349204431Sraj fdt->version = cpu_to_fdt32(vi->version); 350204431Sraj fdt->last_comp_version = cpu_to_fdt32(vi->last_comp_version); 351204431Sraj 352204431Sraj /* Reserve map should be doubleword aligned */ 353204431Sraj reserve_off = ALIGN(vi->hdr_size, 8); 354204431Sraj 355204431Sraj fdt->off_mem_rsvmap = cpu_to_fdt32(reserve_off); 356204431Sraj fdt->off_dt_struct = cpu_to_fdt32(reserve_off + reservesize); 357204431Sraj fdt->off_dt_strings = cpu_to_fdt32(reserve_off + reservesize 358204431Sraj + dtsize); 359204431Sraj fdt->totalsize = cpu_to_fdt32(reserve_off + reservesize + dtsize + strsize); 360204431Sraj 361204431Sraj if (vi->flags & FTF_BOOTCPUID) 362204431Sraj fdt->boot_cpuid_phys = cpu_to_fdt32(boot_cpuid_phys); 363204431Sraj if (vi->flags & FTF_STRTABSIZE) 364204431Sraj fdt->size_dt_strings = cpu_to_fdt32(strsize); 365204431Sraj if (vi->flags & FTF_STRUCTSIZE) 366204431Sraj fdt->size_dt_struct = cpu_to_fdt32(dtsize); 367204431Sraj} 368204431Sraj 369204431Srajvoid dt_to_blob(FILE *f, struct boot_info *bi, int version) 370204431Sraj{ 371204431Sraj struct version_info *vi = NULL; 372204431Sraj int i; 373204431Sraj struct data blob = empty_data; 374204431Sraj struct data reservebuf = empty_data; 375204431Sraj struct data dtbuf = empty_data; 376204431Sraj struct data strbuf = empty_data; 377204431Sraj struct fdt_header fdt; 378204431Sraj int padlen = 0; 379204431Sraj 380204431Sraj for (i = 0; i < ARRAY_SIZE(version_table); i++) { 381204431Sraj if (version_table[i].version == version) 382204431Sraj vi = &version_table[i]; 383204431Sraj } 384204431Sraj if (!vi) 385204431Sraj die("Unknown device tree blob version %d\n", version); 386204431Sraj 387204431Sraj flatten_tree(bi->dt, &bin_emitter, &dtbuf, &strbuf, vi); 388204431Sraj bin_emit_cell(&dtbuf, FDT_END); 389204431Sraj 390204431Sraj reservebuf = flatten_reserve_list(bi->reservelist, vi); 391204431Sraj 392204431Sraj /* Make header */ 393204431Sraj make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len, 394204431Sraj bi->boot_cpuid_phys); 395204431Sraj 396204431Sraj /* 397204431Sraj * If the user asked for more space than is used, adjust the totalsize. 398204431Sraj */ 399204431Sraj if (minsize > 0) { 400204431Sraj padlen = minsize - fdt32_to_cpu(fdt.totalsize); 401204431Sraj if ((padlen < 0) && (quiet < 1)) 402204431Sraj fprintf(stderr, 403204431Sraj "Warning: blob size %d >= minimum size %d\n", 404204431Sraj fdt32_to_cpu(fdt.totalsize), minsize); 405204431Sraj } 406204431Sraj 407204431Sraj if (padsize > 0) 408204431Sraj padlen = padsize; 409204431Sraj 410204431Sraj if (padlen > 0) { 411204431Sraj int tsize = fdt32_to_cpu(fdt.totalsize); 412204431Sraj tsize += padlen; 413204431Sraj fdt.totalsize = cpu_to_fdt32(tsize); 414204431Sraj } 415204431Sraj 416204431Sraj /* 417204431Sraj * Assemble the blob: start with the header, add with alignment 418204431Sraj * the reserve buffer, add the reserve map terminating zeroes, 419204431Sraj * the device tree itself, and finally the strings. 420204431Sraj */ 421204431Sraj blob = data_append_data(blob, &fdt, vi->hdr_size); 422204431Sraj blob = data_append_align(blob, 8); 423204431Sraj blob = data_merge(blob, reservebuf); 424204431Sraj blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry)); 425204431Sraj blob = data_merge(blob, dtbuf); 426204431Sraj blob = data_merge(blob, strbuf); 427204431Sraj 428204431Sraj /* 429204431Sraj * If the user asked for more space than is used, pad out the blob. 430204431Sraj */ 431204431Sraj if (padlen > 0) 432204431Sraj blob = data_append_zeroes(blob, padlen); 433204431Sraj 434204433Sraj if (fwrite(blob.val, blob.len, 1, f) != 1) { 435204433Sraj if (ferror(f)) 436204433Sraj die("Error writing device tree blob: %s\n", 437204433Sraj strerror(errno)); 438204433Sraj else 439204433Sraj die("Short write on device tree blob\n"); 440204433Sraj } 441204431Sraj 442204431Sraj /* 443204431Sraj * data_merge() frees the right-hand element so only the blob 444204431Sraj * remains to be freed. 445204431Sraj */ 446204431Sraj data_free(blob); 447204431Sraj} 448204431Sraj 449204431Srajstatic void dump_stringtable_asm(FILE *f, struct data strbuf) 450204431Sraj{ 451204431Sraj const char *p; 452204431Sraj int len; 453204431Sraj 454204431Sraj p = strbuf.val; 455204431Sraj 456204431Sraj while (p < (strbuf.val + strbuf.len)) { 457204431Sraj len = strlen(p); 458204431Sraj fprintf(f, "\t.string \"%s\"\n", p); 459204431Sraj p += len+1; 460204431Sraj } 461204431Sraj} 462204431Sraj 463204431Srajvoid dt_to_asm(FILE *f, struct boot_info *bi, int version) 464204431Sraj{ 465204431Sraj struct version_info *vi = NULL; 466204431Sraj int i; 467204431Sraj struct data strbuf = empty_data; 468204431Sraj struct reserve_info *re; 469204431Sraj const char *symprefix = "dt"; 470204431Sraj 471204431Sraj for (i = 0; i < ARRAY_SIZE(version_table); i++) { 472204431Sraj if (version_table[i].version == version) 473204431Sraj vi = &version_table[i]; 474204431Sraj } 475204431Sraj if (!vi) 476204431Sraj die("Unknown device tree blob version %d\n", version); 477204431Sraj 478204431Sraj fprintf(f, "/* autogenerated by dtc, do not edit */\n\n"); 479204431Sraj 480204431Sraj emit_label(f, symprefix, "blob_start"); 481204431Sraj emit_label(f, symprefix, "header"); 482204433Sraj fprintf(f, "\t/* magic */\n"); 483204433Sraj asm_emit_cell(f, FDT_MAGIC); 484204433Sraj fprintf(f, "\t/* totalsize */\n"); 485204433Sraj ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start", 486204433Sraj symprefix, symprefix); 487204433Sraj fprintf(f, "\t/* off_dt_struct */\n"); 488204433Sraj ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start", 489204431Sraj symprefix, symprefix); 490204433Sraj fprintf(f, "\t/* off_dt_strings */\n"); 491204433Sraj ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start", 492204431Sraj symprefix, symprefix); 493204433Sraj fprintf(f, "\t/* off_mem_rsvmap */\n"); 494204433Sraj ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start", 495204431Sraj symprefix, symprefix); 496204433Sraj fprintf(f, "\t/* version */\n"); 497204433Sraj asm_emit_cell(f, vi->version); 498204433Sraj fprintf(f, "\t/* last_comp_version */\n"); 499204433Sraj asm_emit_cell(f, vi->last_comp_version); 500204431Sraj 501204433Sraj if (vi->flags & FTF_BOOTCPUID) { 502204433Sraj fprintf(f, "\t/* boot_cpuid_phys */\n"); 503204433Sraj asm_emit_cell(f, bi->boot_cpuid_phys); 504204433Sraj } 505204431Sraj 506204433Sraj if (vi->flags & FTF_STRTABSIZE) { 507204433Sraj fprintf(f, "\t/* size_dt_strings */\n"); 508204433Sraj ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start", 509204433Sraj symprefix, symprefix); 510204433Sraj } 511204431Sraj 512204433Sraj if (vi->flags & FTF_STRUCTSIZE) { 513204433Sraj fprintf(f, "\t/* size_dt_struct */\n"); 514204433Sraj ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start", 515204431Sraj symprefix, symprefix); 516204433Sraj } 517204431Sraj 518204431Sraj /* 519204431Sraj * Reserve map entries. 520204431Sraj * Align the reserve map to a doubleword boundary. 521204431Sraj * Each entry is an (address, size) pair of u64 values. 522204431Sraj * Always supply a zero-sized temination entry. 523204431Sraj */ 524204431Sraj asm_emit_align(f, 8); 525204431Sraj emit_label(f, symprefix, "reserve_map"); 526204431Sraj 527204431Sraj fprintf(f, "/* Memory reserve map from source file */\n"); 528204431Sraj 529204431Sraj /* 530204431Sraj * Use .long on high and low halfs of u64s to avoid .quad 531204431Sraj * as it appears .quad isn't available in some assemblers. 532204431Sraj */ 533204431Sraj for (re = bi->reservelist; re; re = re->next) { 534238742Simp struct label *l; 535238742Simp 536238742Simp for_each_label(re->labels, l) { 537238742Simp fprintf(f, "\t.globl\t%s\n", l->label); 538238742Simp fprintf(f, "%s:\n", l->label); 539204431Sraj } 540204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.address >> 32)); 541204433Sraj ASM_EMIT_BELONG(f, "0x%08x", 542204433Sraj (unsigned int)(re->re.address & 0xffffffff)); 543204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size >> 32)); 544204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size & 0xffffffff)); 545204431Sraj } 546204431Sraj for (i = 0; i < reservenum; i++) { 547204431Sraj fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); 548204431Sraj } 549204431Sraj 550204431Sraj fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); 551204431Sraj 552204431Sraj emit_label(f, symprefix, "struct_start"); 553204431Sraj flatten_tree(bi->dt, &asm_emitter, f, &strbuf, vi); 554204433Sraj 555204433Sraj fprintf(f, "\t/* FDT_END */\n"); 556204433Sraj asm_emit_cell(f, FDT_END); 557204431Sraj emit_label(f, symprefix, "struct_end"); 558204431Sraj 559204431Sraj emit_label(f, symprefix, "strings_start"); 560204431Sraj dump_stringtable_asm(f, strbuf); 561204431Sraj emit_label(f, symprefix, "strings_end"); 562204431Sraj 563204431Sraj emit_label(f, symprefix, "blob_end"); 564204431Sraj 565204431Sraj /* 566204431Sraj * If the user asked for more space than is used, pad it out. 567204431Sraj */ 568204431Sraj if (minsize > 0) { 569204431Sraj fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n", 570204431Sraj minsize, symprefix, symprefix); 571204431Sraj } 572204431Sraj if (padsize > 0) { 573204431Sraj fprintf(f, "\t.space\t%d, 0\n", padsize); 574204431Sraj } 575204431Sraj emit_label(f, symprefix, "blob_abs_end"); 576204431Sraj 577204431Sraj data_free(strbuf); 578204431Sraj} 579204431Sraj 580204431Srajstruct inbuf { 581204431Sraj char *base, *limit, *ptr; 582204431Sraj}; 583204431Sraj 584204431Srajstatic void inbuf_init(struct inbuf *inb, void *base, void *limit) 585204431Sraj{ 586204431Sraj inb->base = base; 587204431Sraj inb->limit = limit; 588204431Sraj inb->ptr = inb->base; 589204431Sraj} 590204431Sraj 591204431Srajstatic void flat_read_chunk(struct inbuf *inb, void *p, int len) 592204431Sraj{ 593204431Sraj if ((inb->ptr + len) > inb->limit) 594204431Sraj die("Premature end of data parsing flat device tree\n"); 595204431Sraj 596204431Sraj memcpy(p, inb->ptr, len); 597204431Sraj 598204431Sraj inb->ptr += len; 599204431Sraj} 600204431Sraj 601204431Srajstatic uint32_t flat_read_word(struct inbuf *inb) 602204431Sraj{ 603204431Sraj uint32_t val; 604204431Sraj 605204431Sraj assert(((inb->ptr - inb->base) % sizeof(val)) == 0); 606204431Sraj 607204431Sraj flat_read_chunk(inb, &val, sizeof(val)); 608204431Sraj 609204431Sraj return fdt32_to_cpu(val); 610204431Sraj} 611204431Sraj 612204431Srajstatic void flat_realign(struct inbuf *inb, int align) 613204431Sraj{ 614204431Sraj int off = inb->ptr - inb->base; 615204431Sraj 616204431Sraj inb->ptr = inb->base + ALIGN(off, align); 617204431Sraj if (inb->ptr > inb->limit) 618204431Sraj die("Premature end of data parsing flat device tree\n"); 619204431Sraj} 620204431Sraj 621204431Srajstatic char *flat_read_string(struct inbuf *inb) 622204431Sraj{ 623204431Sraj int len = 0; 624204431Sraj const char *p = inb->ptr; 625204431Sraj char *str; 626204431Sraj 627204431Sraj do { 628204431Sraj if (p >= inb->limit) 629204431Sraj die("Premature end of data parsing flat device tree\n"); 630204431Sraj len++; 631204431Sraj } while ((*p++) != '\0'); 632204431Sraj 633204433Sraj str = xstrdup(inb->ptr); 634204431Sraj 635204431Sraj inb->ptr += len; 636204431Sraj 637204431Sraj flat_realign(inb, sizeof(uint32_t)); 638204431Sraj 639204431Sraj return str; 640204431Sraj} 641204431Sraj 642204431Srajstatic struct data flat_read_data(struct inbuf *inb, int len) 643204431Sraj{ 644204431Sraj struct data d = empty_data; 645204431Sraj 646204431Sraj if (len == 0) 647204431Sraj return empty_data; 648204431Sraj 649204431Sraj d = data_grow_for(d, len); 650204431Sraj d.len = len; 651204431Sraj 652204431Sraj flat_read_chunk(inb, d.val, len); 653204431Sraj 654204431Sraj flat_realign(inb, sizeof(uint32_t)); 655204431Sraj 656204431Sraj return d; 657204431Sraj} 658204431Sraj 659204431Srajstatic char *flat_read_stringtable(struct inbuf *inb, int offset) 660204431Sraj{ 661204431Sraj const char *p; 662204431Sraj 663204431Sraj p = inb->base + offset; 664204431Sraj while (1) { 665204431Sraj if (p >= inb->limit || p < inb->base) 666204431Sraj die("String offset %d overruns string table\n", 667204431Sraj offset); 668204431Sraj 669204431Sraj if (*p == '\0') 670204431Sraj break; 671204431Sraj 672204431Sraj p++; 673204431Sraj } 674204431Sraj 675204433Sraj return xstrdup(inb->base + offset); 676204431Sraj} 677204431Sraj 678204431Srajstatic struct property *flat_read_property(struct inbuf *dtbuf, 679204431Sraj struct inbuf *strbuf, int flags) 680204431Sraj{ 681204431Sraj uint32_t proplen, stroff; 682204431Sraj char *name; 683204431Sraj struct data val; 684204431Sraj 685204431Sraj proplen = flat_read_word(dtbuf); 686204431Sraj stroff = flat_read_word(dtbuf); 687204431Sraj 688204431Sraj name = flat_read_stringtable(strbuf, stroff); 689204431Sraj 690204431Sraj if ((flags & FTF_VARALIGN) && (proplen >= 8)) 691204431Sraj flat_realign(dtbuf, 8); 692204431Sraj 693204431Sraj val = flat_read_data(dtbuf, proplen); 694204431Sraj 695238742Simp return build_property(name, val); 696204431Sraj} 697204431Sraj 698204431Sraj 699204431Srajstatic struct reserve_info *flat_read_mem_reserve(struct inbuf *inb) 700204431Sraj{ 701204431Sraj struct reserve_info *reservelist = NULL; 702204431Sraj struct reserve_info *new; 703204431Sraj struct fdt_reserve_entry re; 704204431Sraj 705204431Sraj /* 706204431Sraj * Each entry is a pair of u64 (addr, size) values for 4 cell_t's. 707204431Sraj * List terminates at an entry with size equal to zero. 708204431Sraj * 709204431Sraj * First pass, count entries. 710204431Sraj */ 711204431Sraj while (1) { 712204431Sraj flat_read_chunk(inb, &re, sizeof(re)); 713204431Sraj re.address = fdt64_to_cpu(re.address); 714204431Sraj re.size = fdt64_to_cpu(re.size); 715204431Sraj if (re.size == 0) 716204431Sraj break; 717204431Sraj 718238742Simp new = build_reserve_entry(re.address, re.size); 719204431Sraj reservelist = add_reserve_entry(reservelist, new); 720204431Sraj } 721204431Sraj 722204431Sraj return reservelist; 723204431Sraj} 724204431Sraj 725204431Sraj 726204431Srajstatic char *nodename_from_path(const char *ppath, const char *cpath) 727204431Sraj{ 728204431Sraj int plen; 729204431Sraj 730204431Sraj plen = strlen(ppath); 731204431Sraj 732204431Sraj if (!strneq(ppath, cpath, plen)) 733204431Sraj die("Path \"%s\" is not valid as a child of \"%s\"\n", 734204431Sraj cpath, ppath); 735204431Sraj 736204431Sraj /* root node is a special case */ 737204431Sraj if (!streq(ppath, "/")) 738204431Sraj plen++; 739204431Sraj 740204433Sraj return xstrdup(cpath + plen); 741204431Sraj} 742204431Sraj 743204431Srajstatic struct node *unflatten_tree(struct inbuf *dtbuf, 744204431Sraj struct inbuf *strbuf, 745204431Sraj const char *parent_flatname, int flags) 746204431Sraj{ 747204431Sraj struct node *node; 748204431Sraj char *flatname; 749204431Sraj uint32_t val; 750204431Sraj 751204431Sraj node = build_node(NULL, NULL); 752204431Sraj 753204431Sraj flatname = flat_read_string(dtbuf); 754204431Sraj 755204431Sraj if (flags & FTF_FULLPATH) 756204431Sraj node->name = nodename_from_path(parent_flatname, flatname); 757204431Sraj else 758204431Sraj node->name = flatname; 759204431Sraj 760204431Sraj do { 761204431Sraj struct property *prop; 762204431Sraj struct node *child; 763204431Sraj 764204431Sraj val = flat_read_word(dtbuf); 765204431Sraj switch (val) { 766204431Sraj case FDT_PROP: 767204431Sraj if (node->children) 768204431Sraj fprintf(stderr, "Warning: Flat tree input has " 769204431Sraj "subnodes preceding a property.\n"); 770204431Sraj prop = flat_read_property(dtbuf, strbuf, flags); 771204431Sraj add_property(node, prop); 772204431Sraj break; 773204431Sraj 774204431Sraj case FDT_BEGIN_NODE: 775204431Sraj child = unflatten_tree(dtbuf,strbuf, flatname, flags); 776204431Sraj add_child(node, child); 777204431Sraj break; 778204431Sraj 779204431Sraj case FDT_END_NODE: 780204431Sraj break; 781204431Sraj 782204431Sraj case FDT_END: 783204431Sraj die("Premature FDT_END in device tree blob\n"); 784204431Sraj break; 785204431Sraj 786204431Sraj case FDT_NOP: 787204431Sraj if (!(flags & FTF_NOPS)) 788204431Sraj fprintf(stderr, "Warning: NOP tag found in flat tree" 789204431Sraj " version <16\n"); 790204431Sraj 791204431Sraj /* Ignore */ 792204431Sraj break; 793204431Sraj 794204431Sraj default: 795204431Sraj die("Invalid opcode word %08x in device tree blob\n", 796204431Sraj val); 797204431Sraj } 798204431Sraj } while (val != FDT_END_NODE); 799204431Sraj 800204431Sraj return node; 801204431Sraj} 802204431Sraj 803204431Sraj 804204431Srajstruct boot_info *dt_from_blob(const char *fname) 805204431Sraj{ 806238742Simp FILE *f; 807204431Sraj uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys; 808204431Sraj uint32_t off_dt, off_str, off_mem_rsvmap; 809204431Sraj int rc; 810204431Sraj char *blob; 811204431Sraj struct fdt_header *fdt; 812204431Sraj char *p; 813204431Sraj struct inbuf dtbuf, strbuf; 814204431Sraj struct inbuf memresvbuf; 815204431Sraj int sizeleft; 816204431Sraj struct reserve_info *reservelist; 817204431Sraj struct node *tree; 818204431Sraj uint32_t val; 819204431Sraj int flags = 0; 820204431Sraj 821238742Simp f = srcfile_relative_open(fname, NULL); 822204431Sraj 823238742Simp rc = fread(&magic, sizeof(magic), 1, f); 824238742Simp if (ferror(f)) 825204431Sraj die("Error reading DT blob magic number: %s\n", 826204431Sraj strerror(errno)); 827204431Sraj if (rc < 1) { 828238742Simp if (feof(f)) 829204431Sraj die("EOF reading DT blob magic number\n"); 830204431Sraj else 831204431Sraj die("Mysterious short read reading magic number\n"); 832204431Sraj } 833204431Sraj 834204431Sraj magic = fdt32_to_cpu(magic); 835204431Sraj if (magic != FDT_MAGIC) 836204431Sraj die("Blob has incorrect magic number\n"); 837204431Sraj 838238742Simp rc = fread(&totalsize, sizeof(totalsize), 1, f); 839238742Simp if (ferror(f)) 840204431Sraj die("Error reading DT blob size: %s\n", strerror(errno)); 841204431Sraj if (rc < 1) { 842238742Simp if (feof(f)) 843204431Sraj die("EOF reading DT blob size\n"); 844204431Sraj else 845204431Sraj die("Mysterious short read reading blob size\n"); 846204431Sraj } 847204431Sraj 848204431Sraj totalsize = fdt32_to_cpu(totalsize); 849204431Sraj if (totalsize < FDT_V1_SIZE) 850204431Sraj die("DT blob size (%d) is too small\n", totalsize); 851204431Sraj 852204431Sraj blob = xmalloc(totalsize); 853204431Sraj 854204431Sraj fdt = (struct fdt_header *)blob; 855204431Sraj fdt->magic = cpu_to_fdt32(magic); 856204431Sraj fdt->totalsize = cpu_to_fdt32(totalsize); 857204431Sraj 858204431Sraj sizeleft = totalsize - sizeof(magic) - sizeof(totalsize); 859204431Sraj p = blob + sizeof(magic) + sizeof(totalsize); 860204431Sraj 861204431Sraj while (sizeleft) { 862238742Simp if (feof(f)) 863204431Sraj die("EOF before reading %d bytes of DT blob\n", 864204431Sraj totalsize); 865204431Sraj 866238742Simp rc = fread(p, 1, sizeleft, f); 867238742Simp if (ferror(f)) 868204431Sraj die("Error reading DT blob: %s\n", 869204431Sraj strerror(errno)); 870204431Sraj 871204431Sraj sizeleft -= rc; 872204431Sraj p += rc; 873204431Sraj } 874204431Sraj 875204431Sraj off_dt = fdt32_to_cpu(fdt->off_dt_struct); 876204431Sraj off_str = fdt32_to_cpu(fdt->off_dt_strings); 877204431Sraj off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap); 878204431Sraj version = fdt32_to_cpu(fdt->version); 879204431Sraj boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys); 880204431Sraj 881204431Sraj if (off_mem_rsvmap >= totalsize) 882204431Sraj die("Mem Reserve structure offset exceeds total size\n"); 883204431Sraj 884204431Sraj if (off_dt >= totalsize) 885204431Sraj die("DT structure offset exceeds total size\n"); 886204431Sraj 887204431Sraj if (off_str > totalsize) 888204431Sraj die("String table offset exceeds total size\n"); 889204431Sraj 890204431Sraj if (version >= 3) { 891204431Sraj uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings); 892204431Sraj if (off_str+size_str > totalsize) 893204431Sraj die("String table extends past total size\n"); 894204431Sraj inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str); 895204431Sraj } else { 896204431Sraj inbuf_init(&strbuf, blob + off_str, blob + totalsize); 897204431Sraj } 898204431Sraj 899204431Sraj if (version >= 17) { 900204431Sraj size_dt = fdt32_to_cpu(fdt->size_dt_struct); 901204431Sraj if (off_dt+size_dt > totalsize) 902204431Sraj die("Structure block extends past total size\n"); 903204431Sraj } 904204431Sraj 905204431Sraj if (version < 16) { 906204431Sraj flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN; 907204431Sraj } else { 908204431Sraj flags |= FTF_NOPS; 909204431Sraj } 910204431Sraj 911204431Sraj inbuf_init(&memresvbuf, 912204431Sraj blob + off_mem_rsvmap, blob + totalsize); 913204431Sraj inbuf_init(&dtbuf, blob + off_dt, blob + totalsize); 914204431Sraj 915204431Sraj reservelist = flat_read_mem_reserve(&memresvbuf); 916204431Sraj 917204431Sraj val = flat_read_word(&dtbuf); 918204431Sraj 919204431Sraj if (val != FDT_BEGIN_NODE) 920204431Sraj die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val); 921204431Sraj 922204431Sraj tree = unflatten_tree(&dtbuf, &strbuf, "", flags); 923204431Sraj 924204431Sraj val = flat_read_word(&dtbuf); 925204431Sraj if (val != FDT_END) 926204431Sraj die("Device tree blob doesn't end with FDT_END\n"); 927204431Sraj 928204431Sraj free(blob); 929204431Sraj 930238742Simp fclose(f); 931204431Sraj 932204431Sraj return build_boot_info(reservelist, tree, boot_cpuid_phys); 933204431Sraj} 934