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); 55204431Sraj void (*beginnode)(void *, const char *); 56204431Sraj void (*endnode)(void *, const char *); 57204431Sraj void (*property)(void *, const char *); 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 92204431Srajstatic void bin_emit_beginnode(void *e, const char *label) 93204431Sraj{ 94204431Sraj bin_emit_cell(e, FDT_BEGIN_NODE); 95204431Sraj} 96204431Sraj 97204431Srajstatic void bin_emit_endnode(void *e, const char *label) 98204431Sraj{ 99204431Sraj bin_emit_cell(e, FDT_END_NODE); 100204431Sraj} 101204431Sraj 102204431Srajstatic void bin_emit_property(void *e, const char *label) 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 194204431Srajstatic void asm_emit_beginnode(void *e, const char *label) 195204431Sraj{ 196204431Sraj FILE *f = e; 197204431Sraj 198204431Sraj if (label) { 199204431Sraj fprintf(f, "\t.globl\t%s\n", label); 200204431Sraj fprintf(f, "%s:\n", label); 201204431Sraj } 202204433Sraj fprintf(f, "\t/* FDT_BEGIN_NODE */\n"); 203204433Sraj asm_emit_cell(e, FDT_BEGIN_NODE); 204204431Sraj} 205204431Sraj 206204431Srajstatic void asm_emit_endnode(void *e, const char *label) 207204431Sraj{ 208204431Sraj FILE *f = e; 209204431Sraj 210204433Sraj fprintf(f, "\t/* FDT_END_NODE */\n"); 211204433Sraj asm_emit_cell(e, FDT_END_NODE); 212204431Sraj if (label) { 213204431Sraj fprintf(f, "\t.globl\t%s_end\n", label); 214204431Sraj fprintf(f, "%s_end:\n", label); 215204431Sraj } 216204431Sraj} 217204431Sraj 218204431Srajstatic void asm_emit_property(void *e, const char *label) 219204431Sraj{ 220204431Sraj FILE *f = e; 221204431Sraj 222204431Sraj if (label) { 223204431Sraj fprintf(f, "\t.globl\t%s\n", label); 224204431Sraj fprintf(f, "%s:\n", label); 225204431Sraj } 226204433Sraj fprintf(f, "\t/* FDT_PROP */\n"); 227204433Sraj asm_emit_cell(e, FDT_PROP); 228204431Sraj} 229204431Sraj 230204431Srajstatic struct emitter asm_emitter = { 231204431Sraj .cell = asm_emit_cell, 232204431Sraj .string = asm_emit_string, 233204431Sraj .align = asm_emit_align, 234204431Sraj .data = asm_emit_data, 235204431Sraj .beginnode = asm_emit_beginnode, 236204431Sraj .endnode = asm_emit_endnode, 237204431Sraj .property = asm_emit_property, 238204431Sraj}; 239204431Sraj 240204431Srajstatic int stringtable_insert(struct data *d, const char *str) 241204431Sraj{ 242204431Sraj int i; 243204431Sraj 244204431Sraj /* FIXME: do this more efficiently? */ 245204431Sraj 246204431Sraj for (i = 0; i < d->len; i++) { 247204431Sraj if (streq(str, d->val + i)) 248204431Sraj return i; 249204431Sraj } 250204431Sraj 251204431Sraj *d = data_append_data(*d, str, strlen(str)+1); 252204431Sraj return i; 253204431Sraj} 254204431Sraj 255204431Srajstatic void flatten_tree(struct node *tree, struct emitter *emit, 256204431Sraj void *etarget, struct data *strbuf, 257204431Sraj struct version_info *vi) 258204431Sraj{ 259204431Sraj struct property *prop; 260204431Sraj struct node *child; 261204431Sraj int seen_name_prop = 0; 262204431Sraj 263204431Sraj emit->beginnode(etarget, tree->label); 264204431Sraj 265204431Sraj if (vi->flags & FTF_FULLPATH) 266204431Sraj emit->string(etarget, tree->fullpath, 0); 267204431Sraj else 268204431Sraj emit->string(etarget, tree->name, 0); 269204431Sraj 270204431Sraj emit->align(etarget, sizeof(cell_t)); 271204431Sraj 272204431Sraj for_each_property(tree, prop) { 273204431Sraj int nameoff; 274204431Sraj 275204431Sraj if (streq(prop->name, "name")) 276204431Sraj seen_name_prop = 1; 277204431Sraj 278204431Sraj nameoff = stringtable_insert(strbuf, prop->name); 279204431Sraj 280204431Sraj emit->property(etarget, prop->label); 281204431Sraj emit->cell(etarget, prop->val.len); 282204431Sraj emit->cell(etarget, nameoff); 283204431Sraj 284204431Sraj if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8)) 285204431Sraj emit->align(etarget, 8); 286204431Sraj 287204431Sraj emit->data(etarget, prop->val); 288204431Sraj emit->align(etarget, sizeof(cell_t)); 289204431Sraj } 290204431Sraj 291204431Sraj if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) { 292204431Sraj emit->property(etarget, NULL); 293204431Sraj emit->cell(etarget, tree->basenamelen+1); 294204431Sraj emit->cell(etarget, stringtable_insert(strbuf, "name")); 295204431Sraj 296204431Sraj if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8)) 297204431Sraj emit->align(etarget, 8); 298204431Sraj 299204431Sraj emit->string(etarget, tree->name, tree->basenamelen); 300204431Sraj emit->align(etarget, sizeof(cell_t)); 301204431Sraj } 302204431Sraj 303204431Sraj for_each_child(tree, child) { 304204431Sraj flatten_tree(child, emit, etarget, strbuf, vi); 305204431Sraj } 306204431Sraj 307204431Sraj emit->endnode(etarget, tree->label); 308204431Sraj} 309204431Sraj 310204431Srajstatic struct data flatten_reserve_list(struct reserve_info *reservelist, 311204431Sraj struct version_info *vi) 312204431Sraj{ 313204431Sraj struct reserve_info *re; 314204431Sraj struct data d = empty_data; 315204431Sraj static struct fdt_reserve_entry null_re = {0,0}; 316204431Sraj int j; 317204431Sraj 318204431Sraj for (re = reservelist; re; re = re->next) { 319204431Sraj d = data_append_re(d, &re->re); 320204431Sraj } 321204431Sraj /* 322204431Sraj * Add additional reserved slots if the user asked for them. 323204431Sraj */ 324204431Sraj for (j = 0; j < reservenum; j++) { 325204431Sraj d = data_append_re(d, &null_re); 326204431Sraj } 327204431Sraj 328204431Sraj return d; 329204431Sraj} 330204431Sraj 331204431Srajstatic void make_fdt_header(struct fdt_header *fdt, 332204431Sraj struct version_info *vi, 333204431Sraj int reservesize, int dtsize, int strsize, 334204431Sraj int boot_cpuid_phys) 335204431Sraj{ 336204431Sraj int reserve_off; 337204431Sraj 338204431Sraj reservesize += sizeof(struct fdt_reserve_entry); 339204431Sraj 340204431Sraj memset(fdt, 0xff, sizeof(*fdt)); 341204431Sraj 342204431Sraj fdt->magic = cpu_to_fdt32(FDT_MAGIC); 343204431Sraj fdt->version = cpu_to_fdt32(vi->version); 344204431Sraj fdt->last_comp_version = cpu_to_fdt32(vi->last_comp_version); 345204431Sraj 346204431Sraj /* Reserve map should be doubleword aligned */ 347204431Sraj reserve_off = ALIGN(vi->hdr_size, 8); 348204431Sraj 349204431Sraj fdt->off_mem_rsvmap = cpu_to_fdt32(reserve_off); 350204431Sraj fdt->off_dt_struct = cpu_to_fdt32(reserve_off + reservesize); 351204431Sraj fdt->off_dt_strings = cpu_to_fdt32(reserve_off + reservesize 352204431Sraj + dtsize); 353204431Sraj fdt->totalsize = cpu_to_fdt32(reserve_off + reservesize + dtsize + strsize); 354204431Sraj 355204431Sraj if (vi->flags & FTF_BOOTCPUID) 356204431Sraj fdt->boot_cpuid_phys = cpu_to_fdt32(boot_cpuid_phys); 357204431Sraj if (vi->flags & FTF_STRTABSIZE) 358204431Sraj fdt->size_dt_strings = cpu_to_fdt32(strsize); 359204431Sraj if (vi->flags & FTF_STRUCTSIZE) 360204431Sraj fdt->size_dt_struct = cpu_to_fdt32(dtsize); 361204431Sraj} 362204431Sraj 363204431Srajvoid dt_to_blob(FILE *f, struct boot_info *bi, int version) 364204431Sraj{ 365204431Sraj struct version_info *vi = NULL; 366204431Sraj int i; 367204431Sraj struct data blob = empty_data; 368204431Sraj struct data reservebuf = empty_data; 369204431Sraj struct data dtbuf = empty_data; 370204431Sraj struct data strbuf = empty_data; 371204431Sraj struct fdt_header fdt; 372204431Sraj int padlen = 0; 373204431Sraj 374204431Sraj for (i = 0; i < ARRAY_SIZE(version_table); i++) { 375204431Sraj if (version_table[i].version == version) 376204431Sraj vi = &version_table[i]; 377204431Sraj } 378204431Sraj if (!vi) 379204431Sraj die("Unknown device tree blob version %d\n", version); 380204431Sraj 381204431Sraj flatten_tree(bi->dt, &bin_emitter, &dtbuf, &strbuf, vi); 382204431Sraj bin_emit_cell(&dtbuf, FDT_END); 383204431Sraj 384204431Sraj reservebuf = flatten_reserve_list(bi->reservelist, vi); 385204431Sraj 386204431Sraj /* Make header */ 387204431Sraj make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len, 388204431Sraj bi->boot_cpuid_phys); 389204431Sraj 390204431Sraj /* 391204431Sraj * If the user asked for more space than is used, adjust the totalsize. 392204431Sraj */ 393204431Sraj if (minsize > 0) { 394204431Sraj padlen = minsize - fdt32_to_cpu(fdt.totalsize); 395204431Sraj if ((padlen < 0) && (quiet < 1)) 396204431Sraj fprintf(stderr, 397204431Sraj "Warning: blob size %d >= minimum size %d\n", 398204431Sraj fdt32_to_cpu(fdt.totalsize), minsize); 399204431Sraj } 400204431Sraj 401204431Sraj if (padsize > 0) 402204431Sraj padlen = padsize; 403204431Sraj 404204431Sraj if (padlen > 0) { 405204431Sraj int tsize = fdt32_to_cpu(fdt.totalsize); 406204431Sraj tsize += padlen; 407204431Sraj fdt.totalsize = cpu_to_fdt32(tsize); 408204431Sraj } 409204431Sraj 410204431Sraj /* 411204431Sraj * Assemble the blob: start with the header, add with alignment 412204431Sraj * the reserve buffer, add the reserve map terminating zeroes, 413204431Sraj * the device tree itself, and finally the strings. 414204431Sraj */ 415204431Sraj blob = data_append_data(blob, &fdt, vi->hdr_size); 416204431Sraj blob = data_append_align(blob, 8); 417204431Sraj blob = data_merge(blob, reservebuf); 418204431Sraj blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry)); 419204431Sraj blob = data_merge(blob, dtbuf); 420204431Sraj blob = data_merge(blob, strbuf); 421204431Sraj 422204431Sraj /* 423204431Sraj * If the user asked for more space than is used, pad out the blob. 424204431Sraj */ 425204431Sraj if (padlen > 0) 426204431Sraj blob = data_append_zeroes(blob, padlen); 427204431Sraj 428204433Sraj if (fwrite(blob.val, blob.len, 1, f) != 1) { 429204433Sraj if (ferror(f)) 430204433Sraj die("Error writing device tree blob: %s\n", 431204433Sraj strerror(errno)); 432204433Sraj else 433204433Sraj die("Short write on device tree blob\n"); 434204433Sraj } 435204431Sraj 436204431Sraj /* 437204431Sraj * data_merge() frees the right-hand element so only the blob 438204431Sraj * remains to be freed. 439204431Sraj */ 440204431Sraj data_free(blob); 441204431Sraj} 442204431Sraj 443204431Srajstatic void dump_stringtable_asm(FILE *f, struct data strbuf) 444204431Sraj{ 445204431Sraj const char *p; 446204431Sraj int len; 447204431Sraj 448204431Sraj p = strbuf.val; 449204431Sraj 450204431Sraj while (p < (strbuf.val + strbuf.len)) { 451204431Sraj len = strlen(p); 452204431Sraj fprintf(f, "\t.string \"%s\"\n", p); 453204431Sraj p += len+1; 454204431Sraj } 455204431Sraj} 456204431Sraj 457204431Srajvoid dt_to_asm(FILE *f, struct boot_info *bi, int version) 458204431Sraj{ 459204431Sraj struct version_info *vi = NULL; 460204431Sraj int i; 461204431Sraj struct data strbuf = empty_data; 462204431Sraj struct reserve_info *re; 463204431Sraj const char *symprefix = "dt"; 464204431Sraj 465204431Sraj for (i = 0; i < ARRAY_SIZE(version_table); i++) { 466204431Sraj if (version_table[i].version == version) 467204431Sraj vi = &version_table[i]; 468204431Sraj } 469204431Sraj if (!vi) 470204431Sraj die("Unknown device tree blob version %d\n", version); 471204431Sraj 472204431Sraj fprintf(f, "/* autogenerated by dtc, do not edit */\n\n"); 473204431Sraj 474204431Sraj emit_label(f, symprefix, "blob_start"); 475204431Sraj emit_label(f, symprefix, "header"); 476204433Sraj fprintf(f, "\t/* magic */\n"); 477204433Sraj asm_emit_cell(f, FDT_MAGIC); 478204433Sraj fprintf(f, "\t/* totalsize */\n"); 479204433Sraj ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start", 480204433Sraj symprefix, symprefix); 481204433Sraj fprintf(f, "\t/* off_dt_struct */\n"); 482204433Sraj ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start", 483204431Sraj symprefix, symprefix); 484204433Sraj fprintf(f, "\t/* off_dt_strings */\n"); 485204433Sraj ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start", 486204431Sraj symprefix, symprefix); 487204433Sraj fprintf(f, "\t/* off_mem_rsvmap */\n"); 488204433Sraj ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start", 489204431Sraj symprefix, symprefix); 490204433Sraj fprintf(f, "\t/* version */\n"); 491204433Sraj asm_emit_cell(f, vi->version); 492204433Sraj fprintf(f, "\t/* last_comp_version */\n"); 493204433Sraj asm_emit_cell(f, vi->last_comp_version); 494204431Sraj 495204433Sraj if (vi->flags & FTF_BOOTCPUID) { 496204433Sraj fprintf(f, "\t/* boot_cpuid_phys */\n"); 497204433Sraj asm_emit_cell(f, bi->boot_cpuid_phys); 498204433Sraj } 499204431Sraj 500204433Sraj if (vi->flags & FTF_STRTABSIZE) { 501204433Sraj fprintf(f, "\t/* size_dt_strings */\n"); 502204433Sraj ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start", 503204433Sraj symprefix, symprefix); 504204433Sraj } 505204431Sraj 506204433Sraj if (vi->flags & FTF_STRUCTSIZE) { 507204433Sraj fprintf(f, "\t/* size_dt_struct */\n"); 508204433Sraj ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start", 509204431Sraj symprefix, symprefix); 510204433Sraj } 511204431Sraj 512204431Sraj /* 513204431Sraj * Reserve map entries. 514204431Sraj * Align the reserve map to a doubleword boundary. 515204431Sraj * Each entry is an (address, size) pair of u64 values. 516204431Sraj * Always supply a zero-sized temination entry. 517204431Sraj */ 518204431Sraj asm_emit_align(f, 8); 519204431Sraj emit_label(f, symprefix, "reserve_map"); 520204431Sraj 521204431Sraj fprintf(f, "/* Memory reserve map from source file */\n"); 522204431Sraj 523204431Sraj /* 524204431Sraj * Use .long on high and low halfs of u64s to avoid .quad 525204431Sraj * as it appears .quad isn't available in some assemblers. 526204431Sraj */ 527204431Sraj for (re = bi->reservelist; re; re = re->next) { 528204431Sraj if (re->label) { 529204431Sraj fprintf(f, "\t.globl\t%s\n", re->label); 530204431Sraj fprintf(f, "%s:\n", re->label); 531204431Sraj } 532204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.address >> 32)); 533204433Sraj ASM_EMIT_BELONG(f, "0x%08x", 534204433Sraj (unsigned int)(re->re.address & 0xffffffff)); 535204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size >> 32)); 536204433Sraj ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size & 0xffffffff)); 537204431Sraj } 538204431Sraj for (i = 0; i < reservenum; i++) { 539204431Sraj fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); 540204431Sraj } 541204431Sraj 542204431Sraj fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); 543204431Sraj 544204431Sraj emit_label(f, symprefix, "struct_start"); 545204431Sraj flatten_tree(bi->dt, &asm_emitter, f, &strbuf, vi); 546204433Sraj 547204433Sraj fprintf(f, "\t/* FDT_END */\n"); 548204433Sraj asm_emit_cell(f, FDT_END); 549204431Sraj emit_label(f, symprefix, "struct_end"); 550204431Sraj 551204431Sraj emit_label(f, symprefix, "strings_start"); 552204431Sraj dump_stringtable_asm(f, strbuf); 553204431Sraj emit_label(f, symprefix, "strings_end"); 554204431Sraj 555204431Sraj emit_label(f, symprefix, "blob_end"); 556204431Sraj 557204431Sraj /* 558204431Sraj * If the user asked for more space than is used, pad it out. 559204431Sraj */ 560204431Sraj if (minsize > 0) { 561204431Sraj fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n", 562204431Sraj minsize, symprefix, symprefix); 563204431Sraj } 564204431Sraj if (padsize > 0) { 565204431Sraj fprintf(f, "\t.space\t%d, 0\n", padsize); 566204431Sraj } 567204431Sraj emit_label(f, symprefix, "blob_abs_end"); 568204431Sraj 569204431Sraj data_free(strbuf); 570204431Sraj} 571204431Sraj 572204431Srajstruct inbuf { 573204431Sraj char *base, *limit, *ptr; 574204431Sraj}; 575204431Sraj 576204431Srajstatic void inbuf_init(struct inbuf *inb, void *base, void *limit) 577204431Sraj{ 578204431Sraj inb->base = base; 579204431Sraj inb->limit = limit; 580204431Sraj inb->ptr = inb->base; 581204431Sraj} 582204431Sraj 583204431Srajstatic void flat_read_chunk(struct inbuf *inb, void *p, int len) 584204431Sraj{ 585204431Sraj if ((inb->ptr + len) > inb->limit) 586204431Sraj die("Premature end of data parsing flat device tree\n"); 587204431Sraj 588204431Sraj memcpy(p, inb->ptr, len); 589204431Sraj 590204431Sraj inb->ptr += len; 591204431Sraj} 592204431Sraj 593204431Srajstatic uint32_t flat_read_word(struct inbuf *inb) 594204431Sraj{ 595204431Sraj uint32_t val; 596204431Sraj 597204431Sraj assert(((inb->ptr - inb->base) % sizeof(val)) == 0); 598204431Sraj 599204431Sraj flat_read_chunk(inb, &val, sizeof(val)); 600204431Sraj 601204431Sraj return fdt32_to_cpu(val); 602204431Sraj} 603204431Sraj 604204431Srajstatic void flat_realign(struct inbuf *inb, int align) 605204431Sraj{ 606204431Sraj int off = inb->ptr - inb->base; 607204431Sraj 608204431Sraj inb->ptr = inb->base + ALIGN(off, align); 609204431Sraj if (inb->ptr > inb->limit) 610204431Sraj die("Premature end of data parsing flat device tree\n"); 611204431Sraj} 612204431Sraj 613204431Srajstatic char *flat_read_string(struct inbuf *inb) 614204431Sraj{ 615204431Sraj int len = 0; 616204431Sraj const char *p = inb->ptr; 617204431Sraj char *str; 618204431Sraj 619204431Sraj do { 620204431Sraj if (p >= inb->limit) 621204431Sraj die("Premature end of data parsing flat device tree\n"); 622204431Sraj len++; 623204431Sraj } while ((*p++) != '\0'); 624204431Sraj 625204433Sraj str = xstrdup(inb->ptr); 626204431Sraj 627204431Sraj inb->ptr += len; 628204431Sraj 629204431Sraj flat_realign(inb, sizeof(uint32_t)); 630204431Sraj 631204431Sraj return str; 632204431Sraj} 633204431Sraj 634204431Srajstatic struct data flat_read_data(struct inbuf *inb, int len) 635204431Sraj{ 636204431Sraj struct data d = empty_data; 637204431Sraj 638204431Sraj if (len == 0) 639204431Sraj return empty_data; 640204431Sraj 641204431Sraj d = data_grow_for(d, len); 642204431Sraj d.len = len; 643204431Sraj 644204431Sraj flat_read_chunk(inb, d.val, len); 645204431Sraj 646204431Sraj flat_realign(inb, sizeof(uint32_t)); 647204431Sraj 648204431Sraj return d; 649204431Sraj} 650204431Sraj 651204431Srajstatic char *flat_read_stringtable(struct inbuf *inb, int offset) 652204431Sraj{ 653204431Sraj const char *p; 654204431Sraj 655204431Sraj p = inb->base + offset; 656204431Sraj while (1) { 657204431Sraj if (p >= inb->limit || p < inb->base) 658204431Sraj die("String offset %d overruns string table\n", 659204431Sraj offset); 660204431Sraj 661204431Sraj if (*p == '\0') 662204431Sraj break; 663204431Sraj 664204431Sraj p++; 665204431Sraj } 666204431Sraj 667204433Sraj return xstrdup(inb->base + offset); 668204431Sraj} 669204431Sraj 670204431Srajstatic struct property *flat_read_property(struct inbuf *dtbuf, 671204431Sraj struct inbuf *strbuf, int flags) 672204431Sraj{ 673204431Sraj uint32_t proplen, stroff; 674204431Sraj char *name; 675204431Sraj struct data val; 676204431Sraj 677204431Sraj proplen = flat_read_word(dtbuf); 678204431Sraj stroff = flat_read_word(dtbuf); 679204431Sraj 680204431Sraj name = flat_read_stringtable(strbuf, stroff); 681204431Sraj 682204431Sraj if ((flags & FTF_VARALIGN) && (proplen >= 8)) 683204431Sraj flat_realign(dtbuf, 8); 684204431Sraj 685204431Sraj val = flat_read_data(dtbuf, proplen); 686204431Sraj 687204431Sraj return build_property(name, val, NULL); 688204431Sraj} 689204431Sraj 690204431Sraj 691204431Srajstatic struct reserve_info *flat_read_mem_reserve(struct inbuf *inb) 692204431Sraj{ 693204431Sraj struct reserve_info *reservelist = NULL; 694204431Sraj struct reserve_info *new; 695204431Sraj const char *p; 696204431Sraj struct fdt_reserve_entry re; 697204431Sraj 698204431Sraj /* 699204431Sraj * Each entry is a pair of u64 (addr, size) values for 4 cell_t's. 700204431Sraj * List terminates at an entry with size equal to zero. 701204431Sraj * 702204431Sraj * First pass, count entries. 703204431Sraj */ 704204431Sraj p = inb->ptr; 705204431Sraj while (1) { 706204431Sraj flat_read_chunk(inb, &re, sizeof(re)); 707204431Sraj re.address = fdt64_to_cpu(re.address); 708204431Sraj re.size = fdt64_to_cpu(re.size); 709204431Sraj if (re.size == 0) 710204431Sraj break; 711204431Sraj 712204431Sraj new = build_reserve_entry(re.address, re.size, NULL); 713204431Sraj reservelist = add_reserve_entry(reservelist, new); 714204431Sraj } 715204431Sraj 716204431Sraj return reservelist; 717204431Sraj} 718204431Sraj 719204431Sraj 720204431Srajstatic char *nodename_from_path(const char *ppath, const char *cpath) 721204431Sraj{ 722204431Sraj int plen; 723204431Sraj 724204431Sraj plen = strlen(ppath); 725204431Sraj 726204431Sraj if (!strneq(ppath, cpath, plen)) 727204431Sraj die("Path \"%s\" is not valid as a child of \"%s\"\n", 728204431Sraj cpath, ppath); 729204431Sraj 730204431Sraj /* root node is a special case */ 731204431Sraj if (!streq(ppath, "/")) 732204431Sraj plen++; 733204431Sraj 734204433Sraj return xstrdup(cpath + plen); 735204431Sraj} 736204431Sraj 737204431Srajstatic struct node *unflatten_tree(struct inbuf *dtbuf, 738204431Sraj struct inbuf *strbuf, 739204431Sraj const char *parent_flatname, int flags) 740204431Sraj{ 741204431Sraj struct node *node; 742204431Sraj char *flatname; 743204431Sraj uint32_t val; 744204431Sraj 745204431Sraj node = build_node(NULL, NULL); 746204431Sraj 747204431Sraj flatname = flat_read_string(dtbuf); 748204431Sraj 749204431Sraj if (flags & FTF_FULLPATH) 750204431Sraj node->name = nodename_from_path(parent_flatname, flatname); 751204431Sraj else 752204431Sraj node->name = flatname; 753204431Sraj 754204431Sraj do { 755204431Sraj struct property *prop; 756204431Sraj struct node *child; 757204431Sraj 758204431Sraj val = flat_read_word(dtbuf); 759204431Sraj switch (val) { 760204431Sraj case FDT_PROP: 761204431Sraj if (node->children) 762204431Sraj fprintf(stderr, "Warning: Flat tree input has " 763204431Sraj "subnodes preceding a property.\n"); 764204431Sraj prop = flat_read_property(dtbuf, strbuf, flags); 765204431Sraj add_property(node, prop); 766204431Sraj break; 767204431Sraj 768204431Sraj case FDT_BEGIN_NODE: 769204431Sraj child = unflatten_tree(dtbuf,strbuf, flatname, flags); 770204431Sraj add_child(node, child); 771204431Sraj break; 772204431Sraj 773204431Sraj case FDT_END_NODE: 774204431Sraj break; 775204431Sraj 776204431Sraj case FDT_END: 777204431Sraj die("Premature FDT_END in device tree blob\n"); 778204431Sraj break; 779204431Sraj 780204431Sraj case FDT_NOP: 781204431Sraj if (!(flags & FTF_NOPS)) 782204431Sraj fprintf(stderr, "Warning: NOP tag found in flat tree" 783204431Sraj " version <16\n"); 784204431Sraj 785204431Sraj /* Ignore */ 786204431Sraj break; 787204431Sraj 788204431Sraj default: 789204431Sraj die("Invalid opcode word %08x in device tree blob\n", 790204431Sraj val); 791204431Sraj } 792204431Sraj } while (val != FDT_END_NODE); 793204431Sraj 794204431Sraj return node; 795204431Sraj} 796204431Sraj 797204431Sraj 798204431Srajstruct boot_info *dt_from_blob(const char *fname) 799204431Sraj{ 800204431Sraj struct dtc_file *dtcf; 801204431Sraj uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys; 802204431Sraj uint32_t off_dt, off_str, off_mem_rsvmap; 803204431Sraj int rc; 804204431Sraj char *blob; 805204431Sraj struct fdt_header *fdt; 806204431Sraj char *p; 807204431Sraj struct inbuf dtbuf, strbuf; 808204431Sraj struct inbuf memresvbuf; 809204431Sraj int sizeleft; 810204431Sraj struct reserve_info *reservelist; 811204431Sraj struct node *tree; 812204431Sraj uint32_t val; 813204431Sraj int flags = 0; 814204431Sraj 815204431Sraj dtcf = dtc_open_file(fname, NULL); 816204431Sraj 817204431Sraj rc = fread(&magic, sizeof(magic), 1, dtcf->file); 818204431Sraj if (ferror(dtcf->file)) 819204431Sraj die("Error reading DT blob magic number: %s\n", 820204431Sraj strerror(errno)); 821204431Sraj if (rc < 1) { 822204431Sraj if (feof(dtcf->file)) 823204431Sraj die("EOF reading DT blob magic number\n"); 824204431Sraj else 825204431Sraj die("Mysterious short read reading magic number\n"); 826204431Sraj } 827204431Sraj 828204431Sraj magic = fdt32_to_cpu(magic); 829204431Sraj if (magic != FDT_MAGIC) 830204431Sraj die("Blob has incorrect magic number\n"); 831204431Sraj 832204431Sraj rc = fread(&totalsize, sizeof(totalsize), 1, dtcf->file); 833204431Sraj if (ferror(dtcf->file)) 834204431Sraj die("Error reading DT blob size: %s\n", strerror(errno)); 835204431Sraj if (rc < 1) { 836204431Sraj if (feof(dtcf->file)) 837204431Sraj die("EOF reading DT blob size\n"); 838204431Sraj else 839204431Sraj die("Mysterious short read reading blob size\n"); 840204431Sraj } 841204431Sraj 842204431Sraj totalsize = fdt32_to_cpu(totalsize); 843204431Sraj if (totalsize < FDT_V1_SIZE) 844204431Sraj die("DT blob size (%d) is too small\n", totalsize); 845204431Sraj 846204431Sraj blob = xmalloc(totalsize); 847204431Sraj 848204431Sraj fdt = (struct fdt_header *)blob; 849204431Sraj fdt->magic = cpu_to_fdt32(magic); 850204431Sraj fdt->totalsize = cpu_to_fdt32(totalsize); 851204431Sraj 852204431Sraj sizeleft = totalsize - sizeof(magic) - sizeof(totalsize); 853204431Sraj p = blob + sizeof(magic) + sizeof(totalsize); 854204431Sraj 855204431Sraj while (sizeleft) { 856204431Sraj if (feof(dtcf->file)) 857204431Sraj die("EOF before reading %d bytes of DT blob\n", 858204431Sraj totalsize); 859204431Sraj 860204431Sraj rc = fread(p, 1, sizeleft, dtcf->file); 861204431Sraj if (ferror(dtcf->file)) 862204431Sraj die("Error reading DT blob: %s\n", 863204431Sraj strerror(errno)); 864204431Sraj 865204431Sraj sizeleft -= rc; 866204431Sraj p += rc; 867204431Sraj } 868204431Sraj 869204431Sraj off_dt = fdt32_to_cpu(fdt->off_dt_struct); 870204431Sraj off_str = fdt32_to_cpu(fdt->off_dt_strings); 871204431Sraj off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap); 872204431Sraj version = fdt32_to_cpu(fdt->version); 873204431Sraj boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys); 874204431Sraj 875204431Sraj if (off_mem_rsvmap >= totalsize) 876204431Sraj die("Mem Reserve structure offset exceeds total size\n"); 877204431Sraj 878204431Sraj if (off_dt >= totalsize) 879204431Sraj die("DT structure offset exceeds total size\n"); 880204431Sraj 881204431Sraj if (off_str > totalsize) 882204431Sraj die("String table offset exceeds total size\n"); 883204431Sraj 884204431Sraj if (version >= 3) { 885204431Sraj uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings); 886204431Sraj if (off_str+size_str > totalsize) 887204431Sraj die("String table extends past total size\n"); 888204431Sraj inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str); 889204431Sraj } else { 890204431Sraj inbuf_init(&strbuf, blob + off_str, blob + totalsize); 891204431Sraj } 892204431Sraj 893204431Sraj if (version >= 17) { 894204431Sraj size_dt = fdt32_to_cpu(fdt->size_dt_struct); 895204431Sraj if (off_dt+size_dt > totalsize) 896204431Sraj die("Structure block extends past total size\n"); 897204431Sraj } 898204431Sraj 899204431Sraj if (version < 16) { 900204431Sraj flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN; 901204431Sraj } else { 902204431Sraj flags |= FTF_NOPS; 903204431Sraj } 904204431Sraj 905204431Sraj inbuf_init(&memresvbuf, 906204431Sraj blob + off_mem_rsvmap, blob + totalsize); 907204431Sraj inbuf_init(&dtbuf, blob + off_dt, blob + totalsize); 908204431Sraj 909204431Sraj reservelist = flat_read_mem_reserve(&memresvbuf); 910204431Sraj 911204431Sraj val = flat_read_word(&dtbuf); 912204431Sraj 913204431Sraj if (val != FDT_BEGIN_NODE) 914204431Sraj die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val); 915204431Sraj 916204431Sraj tree = unflatten_tree(&dtbuf, &strbuf, "", flags); 917204431Sraj 918204431Sraj val = flat_read_word(&dtbuf); 919204431Sraj if (val != FDT_END) 920204431Sraj die("Device tree blob doesn't end with FDT_END\n"); 921204431Sraj 922204431Sraj free(blob); 923204431Sraj 924204431Sraj dtc_close_file(dtcf); 925204431Sraj 926204431Sraj return build_boot_info(reservelist, tree, boot_cpuid_phys); 927204431Sraj} 928