cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

flattree.c (21995B)


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