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