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; 264261215Simp bool seen_name_prop = false; 265204431Sraj 266261215Simp if (tree->deleted) 267261215Simp return; 268261215Simp 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")) 282261215Simp 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 369318102Sgonzovoid dt_to_blob(FILE *f, struct dt_info *dti, 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 387318102Sgonzo flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi); 388204431Sraj bin_emit_cell(&dtbuf, FDT_END); 389204431Sraj 390318102Sgonzo reservebuf = flatten_reserve_list(dti->reservelist, vi); 391204431Sraj 392204431Sraj /* Make header */ 393204431Sraj make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len, 394318102Sgonzo dti->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); 401318102Sgonzo if (padlen < 0) { 402318102Sgonzo padlen = 0; 403318102Sgonzo if (quiet < 1) 404318102Sgonzo fprintf(stderr, 405318102Sgonzo "Warning: blob size %d >= minimum size %d\n", 406318102Sgonzo fdt32_to_cpu(fdt.totalsize), minsize); 407318102Sgonzo } 408204431Sraj } 409204431Sraj 410204431Sraj if (padsize > 0) 411204431Sraj padlen = padsize; 412204431Sraj 413318102Sgonzo if (alignsize > 0) 414318102Sgonzo padlen = ALIGN(fdt32_to_cpu(fdt.totalsize) + padlen, alignsize) 415318102Sgonzo - fdt32_to_cpu(fdt.totalsize); 416318102Sgonzo 417204431Sraj if (padlen > 0) { 418204431Sraj int tsize = fdt32_to_cpu(fdt.totalsize); 419204431Sraj tsize += padlen; 420204431Sraj fdt.totalsize = cpu_to_fdt32(tsize); 421204431Sraj } 422204431Sraj 423204431Sraj /* 424204431Sraj * Assemble the blob: start with the header, add with alignment 425204431Sraj * the reserve buffer, add the reserve map terminating zeroes, 426204431Sraj * the device tree itself, and finally the strings. 427204431Sraj */ 428204431Sraj blob = data_append_data(blob, &fdt, vi->hdr_size); 429204431Sraj blob = data_append_align(blob, 8); 430204431Sraj blob = data_merge(blob, reservebuf); 431204431Sraj blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry)); 432204431Sraj blob = data_merge(blob, dtbuf); 433204431Sraj blob = data_merge(blob, strbuf); 434204431Sraj 435204431Sraj /* 436204431Sraj * If the user asked for more space than is used, pad out the blob. 437204431Sraj */ 438204431Sraj if (padlen > 0) 439204431Sraj blob = data_append_zeroes(blob, padlen); 440204431Sraj 441204433Sraj if (fwrite(blob.val, blob.len, 1, f) != 1) { 442204433Sraj if (ferror(f)) 443204433Sraj die("Error writing device tree blob: %s\n", 444204433Sraj strerror(errno)); 445204433Sraj else 446204433Sraj die("Short write on device tree blob\n"); 447204433Sraj } 448204431Sraj 449204431Sraj /* 450204431Sraj * data_merge() frees the right-hand element so only the blob 451204431Sraj * remains to be freed. 452204431Sraj */ 453204431Sraj data_free(blob); 454204431Sraj} 455204431Sraj 456204431Srajstatic void dump_stringtable_asm(FILE *f, struct data strbuf) 457204431Sraj{ 458204431Sraj const char *p; 459204431Sraj int len; 460204431Sraj 461204431Sraj p = strbuf.val; 462204431Sraj 463204431Sraj while (p < (strbuf.val + strbuf.len)) { 464204431Sraj len = strlen(p); 465204431Sraj fprintf(f, "\t.string \"%s\"\n", p); 466204431Sraj p += len+1; 467204431Sraj } 468204431Sraj} 469204431Sraj 470318102Sgonzovoid dt_to_asm(FILE *f, struct dt_info *dti, int version) 471204431Sraj{ 472204431Sraj struct version_info *vi = NULL; 473204431Sraj int i; 474204431Sraj struct data strbuf = empty_data; 475204431Sraj struct reserve_info *re; 476204431Sraj const char *symprefix = "dt"; 477204431Sraj 478204431Sraj for (i = 0; i < ARRAY_SIZE(version_table); i++) { 479204431Sraj if (version_table[i].version == version) 480204431Sraj vi = &version_table[i]; 481204431Sraj } 482204431Sraj if (!vi) 483204431Sraj die("Unknown device tree blob version %d\n", version); 484204431Sraj 485204431Sraj fprintf(f, "/* autogenerated by dtc, do not edit */\n\n"); 486204431Sraj 487204431Sraj emit_label(f, symprefix, "blob_start"); 488204431Sraj emit_label(f, symprefix, "header"); 489204433Sraj fprintf(f, "\t/* magic */\n"); 490204433Sraj asm_emit_cell(f, FDT_MAGIC); 491204433Sraj fprintf(f, "\t/* totalsize */\n"); 492204433Sraj ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start", 493204433Sraj symprefix, symprefix); 494204433Sraj fprintf(f, "\t/* off_dt_struct */\n"); 495204433Sraj ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start", 496204431Sraj symprefix, symprefix); 497204433Sraj fprintf(f, "\t/* off_dt_strings */\n"); 498204433Sraj ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start", 499204431Sraj symprefix, symprefix); 500204433Sraj fprintf(f, "\t/* off_mem_rsvmap */\n"); 501204433Sraj ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start", 502204431Sraj symprefix, symprefix); 503204433Sraj fprintf(f, "\t/* version */\n"); 504204433Sraj asm_emit_cell(f, vi->version); 505204433Sraj fprintf(f, "\t/* last_comp_version */\n"); 506204433Sraj asm_emit_cell(f, vi->last_comp_version); 507204431Sraj 508204433Sraj if (vi->flags & FTF_BOOTCPUID) { 509204433Sraj fprintf(f, "\t/* boot_cpuid_phys */\n"); 510318102Sgonzo asm_emit_cell(f, dti->boot_cpuid_phys); 511204433Sraj } 512204431Sraj 513204433Sraj if (vi->flags & FTF_STRTABSIZE) { 514204433Sraj fprintf(f, "\t/* size_dt_strings */\n"); 515204433Sraj ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start", 516204433Sraj symprefix, symprefix); 517204433Sraj } 518204431Sraj 519204433Sraj if (vi->flags & FTF_STRUCTSIZE) { 520204433Sraj fprintf(f, "\t/* size_dt_struct */\n"); 521204433Sraj ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start", 522204431Sraj symprefix, symprefix); 523204433Sraj } 524204431Sraj 525204431Sraj /* 526204431Sraj * Reserve map entries. 527204431Sraj * Align the reserve map to a doubleword boundary. 528204431Sraj * Each entry is an (address, size) pair of u64 values. 529204431Sraj * Always supply a zero-sized temination entry. 530204431Sraj */ 531204431Sraj asm_emit_align(f, 8); 532204431Sraj emit_label(f, symprefix, "reserve_map"); 533204431Sraj 534204431Sraj fprintf(f, "/* Memory reserve map from source file */\n"); 535204431Sraj 536204431Sraj /* 537204431Sraj * Use .long on high and low halfs of u64s to avoid .quad 538204431Sraj * as it appears .quad isn't available in some assemblers. 539204431Sraj */ 540318102Sgonzo for (re = dti->reservelist; re; re = re->next) { 541238742Simp struct label *l; 542238742Simp 543238742Simp for_each_label(re->labels, l) { 544238742Simp fprintf(f, "\t.globl\t%s\n", l->label); 545238742Simp fprintf(f, "%s:\n", l->label); 546204431Sraj } 547204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.address >> 32)); 548204433Sraj ASM_EMIT_BELONG(f, "0x%08x", 549204433Sraj (unsigned int)(re->re.address & 0xffffffff)); 550204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size >> 32)); 551204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size & 0xffffffff)); 552204431Sraj } 553204431Sraj for (i = 0; i < reservenum; i++) { 554204431Sraj fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); 555204431Sraj } 556204431Sraj 557204431Sraj fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); 558204431Sraj 559204431Sraj emit_label(f, symprefix, "struct_start"); 560318102Sgonzo flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi); 561204433Sraj 562204433Sraj fprintf(f, "\t/* FDT_END */\n"); 563204433Sraj asm_emit_cell(f, FDT_END); 564204431Sraj emit_label(f, symprefix, "struct_end"); 565204431Sraj 566204431Sraj emit_label(f, symprefix, "strings_start"); 567204431Sraj dump_stringtable_asm(f, strbuf); 568204431Sraj emit_label(f, symprefix, "strings_end"); 569204431Sraj 570204431Sraj emit_label(f, symprefix, "blob_end"); 571204431Sraj 572204431Sraj /* 573204431Sraj * If the user asked for more space than is used, pad it out. 574204431Sraj */ 575204431Sraj if (minsize > 0) { 576204431Sraj fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n", 577204431Sraj minsize, symprefix, symprefix); 578204431Sraj } 579204431Sraj if (padsize > 0) { 580204431Sraj fprintf(f, "\t.space\t%d, 0\n", padsize); 581204431Sraj } 582318102Sgonzo if (alignsize > 0) 583318102Sgonzo asm_emit_align(f, alignsize); 584204431Sraj emit_label(f, symprefix, "blob_abs_end"); 585204431Sraj 586204431Sraj data_free(strbuf); 587204431Sraj} 588204431Sraj 589204431Srajstruct inbuf { 590204431Sraj char *base, *limit, *ptr; 591204431Sraj}; 592204431Sraj 593204431Srajstatic void inbuf_init(struct inbuf *inb, void *base, void *limit) 594204431Sraj{ 595204431Sraj inb->base = base; 596204431Sraj inb->limit = limit; 597204431Sraj inb->ptr = inb->base; 598204431Sraj} 599204431Sraj 600204431Srajstatic void flat_read_chunk(struct inbuf *inb, void *p, int len) 601204431Sraj{ 602204431Sraj if ((inb->ptr + len) > inb->limit) 603204431Sraj die("Premature end of data parsing flat device tree\n"); 604204431Sraj 605204431Sraj memcpy(p, inb->ptr, len); 606204431Sraj 607204431Sraj inb->ptr += len; 608204431Sraj} 609204431Sraj 610204431Srajstatic uint32_t flat_read_word(struct inbuf *inb) 611204431Sraj{ 612204431Sraj uint32_t val; 613204431Sraj 614204431Sraj assert(((inb->ptr - inb->base) % sizeof(val)) == 0); 615204431Sraj 616204431Sraj flat_read_chunk(inb, &val, sizeof(val)); 617204431Sraj 618204431Sraj return fdt32_to_cpu(val); 619204431Sraj} 620204431Sraj 621204431Srajstatic void flat_realign(struct inbuf *inb, int align) 622204431Sraj{ 623204431Sraj int off = inb->ptr - inb->base; 624204431Sraj 625204431Sraj inb->ptr = inb->base + ALIGN(off, align); 626204431Sraj if (inb->ptr > inb->limit) 627204431Sraj die("Premature end of data parsing flat device tree\n"); 628204431Sraj} 629204431Sraj 630204431Srajstatic char *flat_read_string(struct inbuf *inb) 631204431Sraj{ 632204431Sraj int len = 0; 633204431Sraj const char *p = inb->ptr; 634204431Sraj char *str; 635204431Sraj 636204431Sraj do { 637204431Sraj if (p >= inb->limit) 638204431Sraj die("Premature end of data parsing flat device tree\n"); 639204431Sraj len++; 640204431Sraj } while ((*p++) != '\0'); 641204431Sraj 642204433Sraj str = xstrdup(inb->ptr); 643204431Sraj 644204431Sraj inb->ptr += len; 645204431Sraj 646204431Sraj flat_realign(inb, sizeof(uint32_t)); 647204431Sraj 648204431Sraj return str; 649204431Sraj} 650204431Sraj 651204431Srajstatic struct data flat_read_data(struct inbuf *inb, int len) 652204431Sraj{ 653204431Sraj struct data d = empty_data; 654204431Sraj 655204431Sraj if (len == 0) 656204431Sraj return empty_data; 657204431Sraj 658204431Sraj d = data_grow_for(d, len); 659204431Sraj d.len = len; 660204431Sraj 661204431Sraj flat_read_chunk(inb, d.val, len); 662204431Sraj 663204431Sraj flat_realign(inb, sizeof(uint32_t)); 664204431Sraj 665204431Sraj return d; 666204431Sraj} 667204431Sraj 668204431Srajstatic char *flat_read_stringtable(struct inbuf *inb, int offset) 669204431Sraj{ 670204431Sraj const char *p; 671204431Sraj 672204431Sraj p = inb->base + offset; 673204431Sraj while (1) { 674204431Sraj if (p >= inb->limit || p < inb->base) 675204431Sraj die("String offset %d overruns string table\n", 676204431Sraj offset); 677204431Sraj 678204431Sraj if (*p == '\0') 679204431Sraj break; 680204431Sraj 681204431Sraj p++; 682204431Sraj } 683204431Sraj 684204433Sraj return xstrdup(inb->base + offset); 685204431Sraj} 686204431Sraj 687204431Srajstatic struct property *flat_read_property(struct inbuf *dtbuf, 688204431Sraj struct inbuf *strbuf, int flags) 689204431Sraj{ 690204431Sraj uint32_t proplen, stroff; 691204431Sraj char *name; 692204431Sraj struct data val; 693204431Sraj 694204431Sraj proplen = flat_read_word(dtbuf); 695204431Sraj stroff = flat_read_word(dtbuf); 696204431Sraj 697204431Sraj name = flat_read_stringtable(strbuf, stroff); 698204431Sraj 699204431Sraj if ((flags & FTF_VARALIGN) && (proplen >= 8)) 700204431Sraj flat_realign(dtbuf, 8); 701204431Sraj 702204431Sraj val = flat_read_data(dtbuf, proplen); 703204431Sraj 704238742Simp return build_property(name, val); 705204431Sraj} 706204431Sraj 707204431Sraj 708204431Srajstatic struct reserve_info *flat_read_mem_reserve(struct inbuf *inb) 709204431Sraj{ 710204431Sraj struct reserve_info *reservelist = NULL; 711204431Sraj struct reserve_info *new; 712204431Sraj struct fdt_reserve_entry re; 713204431Sraj 714204431Sraj /* 715204431Sraj * Each entry is a pair of u64 (addr, size) values for 4 cell_t's. 716204431Sraj * List terminates at an entry with size equal to zero. 717204431Sraj * 718204431Sraj * First pass, count entries. 719204431Sraj */ 720204431Sraj while (1) { 721204431Sraj flat_read_chunk(inb, &re, sizeof(re)); 722204431Sraj re.address = fdt64_to_cpu(re.address); 723204431Sraj re.size = fdt64_to_cpu(re.size); 724204431Sraj if (re.size == 0) 725204431Sraj break; 726204431Sraj 727238742Simp new = build_reserve_entry(re.address, re.size); 728204431Sraj reservelist = add_reserve_entry(reservelist, new); 729204431Sraj } 730204431Sraj 731204431Sraj return reservelist; 732204431Sraj} 733204431Sraj 734204431Sraj 735204431Srajstatic char *nodename_from_path(const char *ppath, const char *cpath) 736204431Sraj{ 737204431Sraj int plen; 738204431Sraj 739204431Sraj plen = strlen(ppath); 740204431Sraj 741204431Sraj if (!strneq(ppath, cpath, plen)) 742204431Sraj die("Path \"%s\" is not valid as a child of \"%s\"\n", 743204431Sraj cpath, ppath); 744204431Sraj 745204431Sraj /* root node is a special case */ 746204431Sraj if (!streq(ppath, "/")) 747204431Sraj plen++; 748204431Sraj 749204433Sraj return xstrdup(cpath + plen); 750204431Sraj} 751204431Sraj 752204431Srajstatic struct node *unflatten_tree(struct inbuf *dtbuf, 753204431Sraj struct inbuf *strbuf, 754204431Sraj const char *parent_flatname, int flags) 755204431Sraj{ 756204431Sraj struct node *node; 757204431Sraj char *flatname; 758204431Sraj uint32_t val; 759204431Sraj 760204431Sraj node = build_node(NULL, NULL); 761204431Sraj 762204431Sraj flatname = flat_read_string(dtbuf); 763204431Sraj 764204431Sraj if (flags & FTF_FULLPATH) 765204431Sraj node->name = nodename_from_path(parent_flatname, flatname); 766204431Sraj else 767204431Sraj node->name = flatname; 768204431Sraj 769204431Sraj do { 770204431Sraj struct property *prop; 771204431Sraj struct node *child; 772204431Sraj 773204431Sraj val = flat_read_word(dtbuf); 774204431Sraj switch (val) { 775204431Sraj case FDT_PROP: 776204431Sraj if (node->children) 777204431Sraj fprintf(stderr, "Warning: Flat tree input has " 778204431Sraj "subnodes preceding a property.\n"); 779204431Sraj prop = flat_read_property(dtbuf, strbuf, flags); 780204431Sraj add_property(node, prop); 781204431Sraj break; 782204431Sraj 783204431Sraj case FDT_BEGIN_NODE: 784204431Sraj child = unflatten_tree(dtbuf,strbuf, flatname, flags); 785204431Sraj add_child(node, child); 786204431Sraj break; 787204431Sraj 788204431Sraj case FDT_END_NODE: 789204431Sraj break; 790204431Sraj 791204431Sraj case FDT_END: 792204431Sraj die("Premature FDT_END in device tree blob\n"); 793204431Sraj break; 794204431Sraj 795204431Sraj case FDT_NOP: 796204431Sraj if (!(flags & FTF_NOPS)) 797204431Sraj fprintf(stderr, "Warning: NOP tag found in flat tree" 798204431Sraj " version <16\n"); 799204431Sraj 800204431Sraj /* Ignore */ 801204431Sraj break; 802204431Sraj 803204431Sraj default: 804204431Sraj die("Invalid opcode word %08x in device tree blob\n", 805204431Sraj val); 806204431Sraj } 807204431Sraj } while (val != FDT_END_NODE); 808204431Sraj 809318102Sgonzo if (node->name != flatname) { 810318102Sgonzo free(flatname); 811318102Sgonzo } 812318102Sgonzo 813204431Sraj return node; 814204431Sraj} 815204431Sraj 816204431Sraj 817318102Sgonzostruct dt_info *dt_from_blob(const char *fname) 818204431Sraj{ 819238742Simp FILE *f; 820204431Sraj uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys; 821204431Sraj uint32_t off_dt, off_str, off_mem_rsvmap; 822204431Sraj int rc; 823204431Sraj char *blob; 824204431Sraj struct fdt_header *fdt; 825204431Sraj char *p; 826204431Sraj struct inbuf dtbuf, strbuf; 827204431Sraj struct inbuf memresvbuf; 828204431Sraj int sizeleft; 829204431Sraj struct reserve_info *reservelist; 830204431Sraj struct node *tree; 831204431Sraj uint32_t val; 832204431Sraj int flags = 0; 833204431Sraj 834238742Simp f = srcfile_relative_open(fname, NULL); 835204431Sraj 836238742Simp rc = fread(&magic, sizeof(magic), 1, f); 837238742Simp if (ferror(f)) 838204431Sraj die("Error reading DT blob magic number: %s\n", 839204431Sraj strerror(errno)); 840204431Sraj if (rc < 1) { 841238742Simp if (feof(f)) 842204431Sraj die("EOF reading DT blob magic number\n"); 843204431Sraj else 844204431Sraj die("Mysterious short read reading magic number\n"); 845204431Sraj } 846204431Sraj 847204431Sraj magic = fdt32_to_cpu(magic); 848204431Sraj if (magic != FDT_MAGIC) 849204431Sraj die("Blob has incorrect magic number\n"); 850204431Sraj 851238742Simp rc = fread(&totalsize, sizeof(totalsize), 1, f); 852238742Simp if (ferror(f)) 853204431Sraj die("Error reading DT blob size: %s\n", strerror(errno)); 854204431Sraj if (rc < 1) { 855238742Simp if (feof(f)) 856204431Sraj die("EOF reading DT blob size\n"); 857204431Sraj else 858204431Sraj die("Mysterious short read reading blob size\n"); 859204431Sraj } 860204431Sraj 861204431Sraj totalsize = fdt32_to_cpu(totalsize); 862204431Sraj if (totalsize < FDT_V1_SIZE) 863204431Sraj die("DT blob size (%d) is too small\n", totalsize); 864204431Sraj 865204431Sraj blob = xmalloc(totalsize); 866204431Sraj 867204431Sraj fdt = (struct fdt_header *)blob; 868204431Sraj fdt->magic = cpu_to_fdt32(magic); 869204431Sraj fdt->totalsize = cpu_to_fdt32(totalsize); 870204431Sraj 871204431Sraj sizeleft = totalsize - sizeof(magic) - sizeof(totalsize); 872204431Sraj p = blob + sizeof(magic) + sizeof(totalsize); 873204431Sraj 874204431Sraj while (sizeleft) { 875238742Simp if (feof(f)) 876204431Sraj die("EOF before reading %d bytes of DT blob\n", 877204431Sraj totalsize); 878204431Sraj 879238742Simp rc = fread(p, 1, sizeleft, f); 880238742Simp if (ferror(f)) 881204431Sraj die("Error reading DT blob: %s\n", 882204431Sraj strerror(errno)); 883204431Sraj 884204431Sraj sizeleft -= rc; 885204431Sraj p += rc; 886204431Sraj } 887204431Sraj 888204431Sraj off_dt = fdt32_to_cpu(fdt->off_dt_struct); 889204431Sraj off_str = fdt32_to_cpu(fdt->off_dt_strings); 890204431Sraj off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap); 891204431Sraj version = fdt32_to_cpu(fdt->version); 892204431Sraj boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys); 893204431Sraj 894204431Sraj if (off_mem_rsvmap >= totalsize) 895204431Sraj die("Mem Reserve structure offset exceeds total size\n"); 896204431Sraj 897204431Sraj if (off_dt >= totalsize) 898204431Sraj die("DT structure offset exceeds total size\n"); 899204431Sraj 900204431Sraj if (off_str > totalsize) 901204431Sraj die("String table offset exceeds total size\n"); 902204431Sraj 903204431Sraj if (version >= 3) { 904204431Sraj uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings); 905318102Sgonzo if ((off_str+size_str < off_str) || (off_str+size_str > totalsize)) 906204431Sraj die("String table extends past total size\n"); 907204431Sraj inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str); 908204431Sraj } else { 909204431Sraj inbuf_init(&strbuf, blob + off_str, blob + totalsize); 910204431Sraj } 911204431Sraj 912204431Sraj if (version >= 17) { 913204431Sraj size_dt = fdt32_to_cpu(fdt->size_dt_struct); 914318102Sgonzo if ((off_dt+size_dt < off_dt) || (off_dt+size_dt > totalsize)) 915204431Sraj die("Structure block extends past total size\n"); 916204431Sraj } 917204431Sraj 918204431Sraj if (version < 16) { 919204431Sraj flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN; 920204431Sraj } else { 921204431Sraj flags |= FTF_NOPS; 922204431Sraj } 923204431Sraj 924204431Sraj inbuf_init(&memresvbuf, 925204431Sraj blob + off_mem_rsvmap, blob + totalsize); 926204431Sraj inbuf_init(&dtbuf, blob + off_dt, blob + totalsize); 927204431Sraj 928204431Sraj reservelist = flat_read_mem_reserve(&memresvbuf); 929204431Sraj 930204431Sraj val = flat_read_word(&dtbuf); 931204431Sraj 932204431Sraj if (val != FDT_BEGIN_NODE) 933204431Sraj die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val); 934204431Sraj 935204431Sraj tree = unflatten_tree(&dtbuf, &strbuf, "", flags); 936204431Sraj 937204431Sraj val = flat_read_word(&dtbuf); 938204431Sraj if (val != FDT_END) 939204431Sraj die("Device tree blob doesn't end with FDT_END\n"); 940204431Sraj 941204431Sraj free(blob); 942204431Sraj 943238742Simp fclose(f); 944204431Sraj 945318102Sgonzo return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys); 946204431Sraj} 947