flattree.c revision 261215
1198956Srrs/*
2198956Srrs * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
3198956Srrs *
4198956Srrs *
5198956Srrs * This program is free software; you can redistribute it and/or
6198956Srrs * modify it under the terms of the GNU General Public License as
7198956Srrs * published by the Free Software Foundation; either version 2 of the
8198956Srrs * License, or (at your option) any later version.
9198956Srrs *
10198956Srrs *  This program is distributed in the hope that it will be useful,
11198956Srrs *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12198956Srrs *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13198956Srrs *  General Public License for more details.
14198956Srrs *
15198956Srrs *  You should have received a copy of the GNU General Public License
16198956Srrs *  along with this program; if not, write to the Free Software
17198956Srrs *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
18198956Srrs *                                                                   USA
19198956Srrs */
20198956Srrs
21198956Srrs#include "dtc.h"
22198956Srrs#include "srcpos.h"
23198956Srrs
24198956Srrs#define FTF_FULLPATH	0x1
25198956Srrs#define FTF_VARALIGN	0x2
26198956Srrs#define FTF_NAMEPROPS	0x4
27198956Srrs#define FTF_BOOTCPUID	0x8
28198956Srrs#define FTF_STRTABSIZE	0x10
29198956Srrs#define FTF_STRUCTSIZE	0x20
30198956Srrs#define FTF_NOPS	0x40
31198956Srrs
32198956Srrsstatic struct version_info {
33198956Srrs	int version;
34198956Srrs	int last_comp_version;
35204131Srrs	int hdr_size;
36198956Srrs	int flags;
37198956Srrs} version_table[] = {
38198956Srrs	{1, 1, FDT_V1_SIZE,
39198956Srrs	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS},
40198956Srrs	{2, 1, FDT_V2_SIZE,
41198956Srrs	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID},
42198956Srrs	{3, 1, FDT_V3_SIZE,
43198956Srrs	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE},
44198956Srrs	{16, 16, FDT_V3_SIZE,
45198956Srrs	 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_NOPS},
46198956Srrs	{17, 16, FDT_V17_SIZE,
47198957Srrs	 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS},
48198957Srrs};
49198957Srrs
50198957Srrsstruct emitter {
51198956Srrs	void (*cell)(void *, cell_t);
52198957Srrs	void (*string)(void *, char *, int);
53198957Srrs	void (*align)(void *, int);
54198957Srrs	void (*data)(void *, struct data);
55198956Srrs	void (*beginnode)(void *, struct label *labels);
56198957Srrs	void (*endnode)(void *, struct label *labels);
57198957Srrs	void (*property)(void *, struct label *labels);
58198957Srrs};
59198957Srrs
60198957Srrsstatic void bin_emit_cell(void *e, cell_t val)
61198956Srrs{
62198957Srrs	struct data *dtbuf = e;
63198957Srrs
64198957Srrs	*dtbuf = data_append_cell(*dtbuf, val);
65198957Srrs}
66198956Srrs
67198957Srrsstatic void bin_emit_string(void *e, char *str, int len)
68198957Srrs{
69198957Srrs	struct data *dtbuf = e;
70198957Srrs
71198956Srrs	if (len == 0)
72198957Srrs		len = strlen(str);
73198957Srrs
74198957Srrs	*dtbuf = data_append_data(*dtbuf, str, len);
75198957Srrs	*dtbuf = data_append_byte(*dtbuf, '\0');
76198956Srrs}
77198957Srrs
78198957Srrsstatic void bin_emit_align(void *e, int a)
79198957Srrs{
80198957Srrs	struct data *dtbuf = e;
81198957Srrs
82198956Srrs	*dtbuf = data_append_align(*dtbuf, a);
83198957Srrs}
84198957Srrs
85198957Srrsstatic void bin_emit_data(void *e, struct data d)
86198957Srrs{
87198957Srrs	struct data *dtbuf = e;
88198956Srrs
89198957Srrs	*dtbuf = data_append_data(*dtbuf, d.val, d.len);
90198957Srrs}
91198957Srrs
92198957Srrsstatic void bin_emit_beginnode(void *e, struct label *labels)
93198957Srrs{
94198956Srrs	bin_emit_cell(e, FDT_BEGIN_NODE);
95198957Srrs}
96198957Srrs
97198957Srrsstatic void bin_emit_endnode(void *e, struct label *labels)
98198957Srrs{
99198957Srrs	bin_emit_cell(e, FDT_END_NODE);
100198956Srrs}
101198957Srrs
102198957Srrsstatic void bin_emit_property(void *e, struct label *labels)
103198957Srrs{
104198957Srrs	bin_emit_cell(e, FDT_PROP);
105198957Srrs}
106198956Srrs
107198957Srrsstatic struct emitter bin_emitter = {
108198957Srrs	.cell = bin_emit_cell,
109198957Srrs	.string = bin_emit_string,
110198957Srrs	.align = bin_emit_align,
111198957Srrs	.data = bin_emit_data,
112198956Srrs	.beginnode = bin_emit_beginnode,
113198957Srrs	.endnode = bin_emit_endnode,
114198957Srrs	.property = bin_emit_property,
115198957Srrs};
116198957Srrs
117198956Srrsstatic void emit_label(FILE *f, const char *prefix, const char *label)
118198957Srrs{
119198957Srrs	fprintf(f, "\t.globl\t%s_%s\n", prefix, label);
120198957Srrs	fprintf(f, "%s_%s:\n", prefix, label);
121198957Srrs	fprintf(f, "_%s_%s:\n", prefix, label);
122198956Srrs}
123198957Srrs
124198957Srrsstatic void emit_offset_label(FILE *f, const char *label, int offset)
125198957Srrs{
126198957Srrs	fprintf(f, "\t.globl\t%s\n", label);
127198956Srrs	fprintf(f, "%s\t= . + %d\n", label, offset);
128198957Srrs}
129198957Srrs
130198957Srrs#define ASM_EMIT_BELONG(f, fmt, ...) \
131198957Srrs	{ \
132198957Srrs		fprintf((f), "\t.byte\t((" fmt ") >> 24) & 0xff\n", __VA_ARGS__); \
133198957Srrs		fprintf((f), "\t.byte\t((" fmt ") >> 16) & 0xff\n", __VA_ARGS__); \
134204131Srrs		fprintf((f), "\t.byte\t((" fmt ") >> 8) & 0xff\n", __VA_ARGS__); \
135198957Srrs		fprintf((f), "\t.byte\t(" fmt ") & 0xff\n", __VA_ARGS__); \
136198957Srrs	}
137198957Srrs
138198957Srrsstatic void asm_emit_cell(void *e, cell_t val)
139198957Srrs{
140198957Srrs	FILE *f = e;
141198956Srrs
142198957Srrs	fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n",
143198957Srrs		(val >> 24) & 0xff, (val >> 16) & 0xff,
144198957Srrs		(val >> 8) & 0xff, val & 0xff);
145198957Srrs}
146198957Srrs
147198957Srrsstatic void asm_emit_string(void *e, char *str, int len)
148198956Srrs{
149198957Srrs	FILE *f = e;
150198957Srrs	char c = 0;
151198957Srrs
152198957Srrs	if (len != 0) {
153198957Srrs		/* XXX: ewww */
154198957Srrs		c = str[len];
155198956Srrs		str[len] = '\0';
156198957Srrs	}
157198957Srrs
158198957Srrs	fprintf(f, "\t.string\t\"%s\"\n", str);
159198957Srrs
160198957Srrs	if (len != 0) {
161198957Srrs		str[len] = c;
162198956Srrs	}
163198956Srrs}
164198957Srrs
165198957Srrsstatic void asm_emit_align(void *e, int a)
166198957Srrs{
167198957Srrs	FILE *f = e;
168198957Srrs
169198957Srrs	fprintf(f, "\t.balign\t%d, 0\n", a);
170198957Srrs}
171198957Srrs
172198957Srrsstatic void asm_emit_data(void *e, struct data d)
173198957Srrs{
174198956Srrs	FILE *f = e;
175198957Srrs	int off = 0;
176198957Srrs	struct marker *m = d.markers;
177198957Srrs
178198956Srrs	for_each_marker_of_type(m, LABEL)
179198957Srrs		emit_offset_label(f, m->ref, m->offset);
180198957Srrs
181198957Srrs	while ((d.len - off) >= sizeof(uint32_t)) {
182198957Srrs		asm_emit_cell(e, fdt32_to_cpu(*((uint32_t *)(d.val+off))));
183198957Srrs		off += sizeof(uint32_t);
184198957Srrs	}
185198956Srrs
186198957Srrs	while ((d.len - off) >= 1) {
187198957Srrs		fprintf(f, "\t.byte\t0x%hhx\n", d.val[off]);
188198957Srrs		off += 1;
189198956Srrs	}
190198957Srrs
191198957Srrs	assert(off == d.len);
192198957Srrs}
193198956Srrs
194198957Srrsstatic void asm_emit_beginnode(void *e, struct label *labels)
195198957Srrs{
196198957Srrs	FILE *f = e;
197198957Srrs	struct label *l;
198198957Srrs
199198957Srrs	for_each_label(labels, l) {
200198957Srrs		fprintf(f, "\t.globl\t%s\n", l->label);
201198957Srrs		fprintf(f, "%s:\n", l->label);
202198956Srrs	}
203198957Srrs	fprintf(f, "\t/* FDT_BEGIN_NODE */\n");
204198957Srrs	asm_emit_cell(e, FDT_BEGIN_NODE);
205198957Srrs}
206198957Srrs
207198957Srrsstatic void asm_emit_endnode(void *e, struct label *labels)
208198956Srrs{
209198957Srrs	FILE *f = e;
210198957Srrs	struct label *l;
211198957Srrs
212198957Srrs	fprintf(f, "\t/* FDT_END_NODE */\n");
213198957Srrs	asm_emit_cell(e, FDT_END_NODE);
214198956Srrs	for_each_label(labels, l) {
215198957Srrs		fprintf(f, "\t.globl\t%s_end\n", l->label);
216198957Srrs		fprintf(f, "%s_end:\n", l->label);
217198957Srrs	}
218198957Srrs}
219198957Srrs
220198957Srrsstatic void asm_emit_property(void *e, struct label *labels)
221198956Srrs{
222198957Srrs	FILE *f = e;
223198957Srrs	struct label *l;
224198957Srrs
225198956Srrs	for_each_label(labels, l) {
226198957Srrs		fprintf(f, "\t.globl\t%s\n", l->label);
227198957Srrs		fprintf(f, "%s:\n", l->label);
228198957Srrs	}
229198957Srrs	fprintf(f, "\t/* FDT_PROP */\n");
230198957Srrs	asm_emit_cell(e, FDT_PROP);
231198957Srrs}
232198957Srrs
233198957Srrsstatic struct emitter asm_emitter = {
234198957Srrs	.cell = asm_emit_cell,
235198957Srrs	.string = asm_emit_string,
236198957Srrs	.align = asm_emit_align,
237198957Srrs	.data = asm_emit_data,
238198956Srrs	.beginnode = asm_emit_beginnode,
239198957Srrs	.endnode = asm_emit_endnode,
240198957Srrs	.property = asm_emit_property,
241198957Srrs};
242198957Srrs
243198957Srrsstatic int stringtable_insert(struct data *d, const char *str)
244198957Srrs{
245198956Srrs	int i;
246204131Srrs
247204131Srrs	/* FIXME: do this more efficiently? */
248198956Srrs
249198956Srrs	for (i = 0; i < d->len; i++) {
250198957Srrs		if (streq(str, d->val + i))
251198956Srrs			return i;
252198956Srrs	}
253198956Srrs
254198956Srrs	*d = data_append_data(*d, str, strlen(str)+1);
255198956Srrs	return i;
256198956Srrs}
257198956Srrs
258198956Srrsstatic void flatten_tree(struct node *tree, struct emitter *emit,
259198956Srrs			 void *etarget, struct data *strbuf,
260198956Srrs			 struct version_info *vi)
261198956Srrs{
262198957Srrs	struct property *prop;
263198956Srrs	struct node *child;
264198956Srrs	bool seen_name_prop = false;
265198956Srrs
266198956Srrs	if (tree->deleted)
267198956Srrs		return;
268198956Srrs
269198956Srrs	emit->beginnode(etarget, tree->labels);
270198956Srrs
271198956Srrs	if (vi->flags & FTF_FULLPATH)
272198956Srrs		emit->string(etarget, tree->fullpath, 0);
273198956Srrs	else
274198956Srrs		emit->string(etarget, tree->name, 0);
275198956Srrs
276198956Srrs	emit->align(etarget, sizeof(cell_t));
277198956Srrs
278198956Srrs	for_each_property(tree, prop) {
279198956Srrs		int nameoff;
280198956Srrs
281198956Srrs		if (streq(prop->name, "name"))
282198956Srrs			seen_name_prop = true;
283198956Srrs
284198956Srrs		nameoff = stringtable_insert(strbuf, prop->name);
285198956Srrs
286198956Srrs		emit->property(etarget, prop->labels);
287198956Srrs		emit->cell(etarget, prop->val.len);
288198956Srrs		emit->cell(etarget, nameoff);
289198956Srrs
290198956Srrs		if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8))
291198956Srrs			emit->align(etarget, 8);
292198956Srrs
293198956Srrs		emit->data(etarget, prop->val);
294198956Srrs		emit->align(etarget, sizeof(cell_t));
295198956Srrs	}
296198956Srrs
297198956Srrs	if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) {
298198956Srrs		emit->property(etarget, NULL);
299198956Srrs		emit->cell(etarget, tree->basenamelen+1);
300198956Srrs		emit->cell(etarget, stringtable_insert(strbuf, "name"));
301198956Srrs
302198956Srrs		if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8))
303198956Srrs			emit->align(etarget, 8);
304198956Srrs
305198956Srrs		emit->string(etarget, tree->name, tree->basenamelen);
306198956Srrs		emit->align(etarget, sizeof(cell_t));
307198956Srrs	}
308198956Srrs
309198956Srrs	for_each_child(tree, child) {
310198956Srrs		flatten_tree(child, emit, etarget, strbuf, vi);
311198956Srrs	}
312198956Srrs
313198956Srrs	emit->endnode(etarget, tree->labels);
314198956Srrs}
315198956Srrs
316198956Srrsstatic struct data flatten_reserve_list(struct reserve_info *reservelist,
317198956Srrs				 struct version_info *vi)
318198956Srrs{
319198956Srrs	struct reserve_info *re;
320198956Srrs	struct data d = empty_data;
321198956Srrs	static struct fdt_reserve_entry null_re = {0,0};
322198956Srrs	int    j;
323198956Srrs
324198956Srrs	for (re = reservelist; re; re = re->next) {
325198956Srrs		d = data_append_re(d, &re->re);
326198956Srrs	}
327198956Srrs	/*
328198956Srrs	 * Add additional reserved slots if the user asked for them.
329198956Srrs	 */
330198956Srrs	for (j = 0; j < reservenum; j++) {
331198956Srrs		d = data_append_re(d, &null_re);
332198956Srrs	}
333198956Srrs
334198956Srrs	return d;
335198956Srrs}
336198956Srrs
337198956Srrsstatic void make_fdt_header(struct fdt_header *fdt,
338198956Srrs			    struct version_info *vi,
339198956Srrs			    int reservesize, int dtsize, int strsize,
340198956Srrs			    int boot_cpuid_phys)
341198956Srrs{
342198956Srrs	int reserve_off;
343198956Srrs
344198956Srrs	reservesize += sizeof(struct fdt_reserve_entry);
345198956Srrs
346198956Srrs	memset(fdt, 0xff, sizeof(*fdt));
347198956Srrs
348198956Srrs	fdt->magic = cpu_to_fdt32(FDT_MAGIC);
349198956Srrs	fdt->version = cpu_to_fdt32(vi->version);
350198956Srrs	fdt->last_comp_version = cpu_to_fdt32(vi->last_comp_version);
351198956Srrs
352198956Srrs	/* Reserve map should be doubleword aligned */
353198956Srrs	reserve_off = ALIGN(vi->hdr_size, 8);
354198956Srrs
355198956Srrs	fdt->off_mem_rsvmap = cpu_to_fdt32(reserve_off);
356198956Srrs	fdt->off_dt_struct = cpu_to_fdt32(reserve_off + reservesize);
357198956Srrs	fdt->off_dt_strings = cpu_to_fdt32(reserve_off + reservesize
358198956Srrs					  + dtsize);
359198956Srrs	fdt->totalsize = cpu_to_fdt32(reserve_off + reservesize + dtsize + strsize);
360198956Srrs
361198956Srrs	if (vi->flags & FTF_BOOTCPUID)
362198956Srrs		fdt->boot_cpuid_phys = cpu_to_fdt32(boot_cpuid_phys);
363198957Srrs	if (vi->flags & FTF_STRTABSIZE)
364198957Srrs		fdt->size_dt_strings = cpu_to_fdt32(strsize);
365198956Srrs	if (vi->flags & FTF_STRUCTSIZE)
366198956Srrs		fdt->size_dt_struct = cpu_to_fdt32(dtsize);
367198956Srrs}
368198956Srrs
369198956Srrsvoid dt_to_blob(FILE *f, struct boot_info *bi, int version)
370198956Srrs{
371198956Srrs	struct version_info *vi = NULL;
372198956Srrs	int i;
373198956Srrs	struct data blob       = empty_data;
374198956Srrs	struct data reservebuf = empty_data;
375198956Srrs	struct data dtbuf      = empty_data;
376198957Srrs	struct data strbuf     = empty_data;
377198956Srrs	struct fdt_header fdt;
378198956Srrs	int padlen = 0;
379198956Srrs
380198956Srrs	for (i = 0; i < ARRAY_SIZE(version_table); i++) {
381198956Srrs		if (version_table[i].version == version)
382198956Srrs			vi = &version_table[i];
383198956Srrs	}
384198956Srrs	if (!vi)
385198956Srrs		die("Unknown device tree blob version %d\n", version);
386198957Srrs
387198957Srrs	flatten_tree(bi->dt, &bin_emitter, &dtbuf, &strbuf, vi);
388198956Srrs	bin_emit_cell(&dtbuf, FDT_END);
389198956Srrs
390198956Srrs	reservebuf = flatten_reserve_list(bi->reservelist, vi);
391198956Srrs
392198956Srrs	/* Make header */
393198956Srrs	make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len,
394198956Srrs			bi->boot_cpuid_phys);
395198956Srrs
396198956Srrs	/*
397198956Srrs	 * If the user asked for more space than is used, adjust the totalsize.
398198956Srrs	 */
399198956Srrs	if (minsize > 0) {
400198957Srrs		padlen = minsize - fdt32_to_cpu(fdt.totalsize);
401198956Srrs		if ((padlen < 0) && (quiet < 1))
402204131Srrs			fprintf(stderr,
403198956Srrs				"Warning: blob size %d >= minimum size %d\n",
404198956Srrs				fdt32_to_cpu(fdt.totalsize), minsize);
405198956Srrs	}
406198956Srrs
407198957Srrs	if (padsize > 0)
408198956Srrs		padlen = padsize;
409204131Srrs
410198956Srrs	if (padlen > 0) {
411198956Srrs		int tsize = fdt32_to_cpu(fdt.totalsize);
412198956Srrs		tsize += padlen;
413198956Srrs		fdt.totalsize = cpu_to_fdt32(tsize);
414198957Srrs	}
415198956Srrs
416204131Srrs	/*
417198956Srrs	 * Assemble the blob: start with the header, add with alignment
418198956Srrs	 * the reserve buffer, add the reserve map terminating zeroes,
419198956Srrs	 * the device tree itself, and finally the strings.
420198956Srrs	 */
421198956Srrs	blob = data_append_data(blob, &fdt, vi->hdr_size);
422198956Srrs	blob = data_append_align(blob, 8);
423198956Srrs	blob = data_merge(blob, reservebuf);
424198956Srrs	blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry));
425198956Srrs	blob = data_merge(blob, dtbuf);
426198957Srrs	blob = data_merge(blob, strbuf);
427198956Srrs
428204131Srrs	/*
429198956Srrs	 * If the user asked for more space than is used, pad out the blob.
430198956Srrs	 */
431198956Srrs	if (padlen > 0)
432198956Srrs		blob = data_append_zeroes(blob, padlen);
433198957Srrs
434198956Srrs	if (fwrite(blob.val, blob.len, 1, f) != 1) {
435204131Srrs		if (ferror(f))
436198956Srrs			die("Error writing device tree blob: %s\n",
437198956Srrs			    strerror(errno));
438198956Srrs		else
439198956Srrs			die("Short write on device tree blob\n");
440198957Srrs	}
441198956Srrs
442204131Srrs	/*
443198956Srrs	 * data_merge() frees the right-hand element so only the blob
444198956Srrs	 * remains to be freed.
445198956Srrs	 */
446198956Srrs	data_free(blob);
447198956Srrs}
448198956Srrs
449198956Srrsstatic void dump_stringtable_asm(FILE *f, struct data strbuf)
450198956Srrs{
451198956Srrs	const char *p;
452198957Srrs	int len;
453198956Srrs
454204131Srrs	p = strbuf.val;
455198956Srrs
456198956Srrs	while (p < (strbuf.val + strbuf.len)) {
457198956Srrs		len = strlen(p);
458198956Srrs		fprintf(f, "\t.string \"%s\"\n", p);
459198957Srrs		p += len+1;
460198956Srrs	}
461204131Srrs}
462198956Srrs
463198956Srrsvoid dt_to_asm(FILE *f, struct boot_info *bi, int version)
464198956Srrs{
465198956Srrs	struct version_info *vi = NULL;
466198957Srrs	int i;
467198956Srrs	struct data strbuf = empty_data;
468204131Srrs	struct reserve_info *re;
469198956Srrs	const char *symprefix = "dt";
470198956Srrs
471198956Srrs	for (i = 0; i < ARRAY_SIZE(version_table); i++) {
472198956Srrs		if (version_table[i].version == version)
473198956Srrs			vi = &version_table[i];
474198956Srrs	}
475198956Srrs	if (!vi)
476198956Srrs		die("Unknown device tree blob version %d\n", version);
477198956Srrs
478198956Srrs	fprintf(f, "/* autogenerated by dtc, do not edit */\n\n");
479198956Srrs
480198957Srrs	emit_label(f, symprefix, "blob_start");
481198956Srrs	emit_label(f, symprefix, "header");
482204131Srrs	fprintf(f, "\t/* magic */\n");
483198956Srrs	asm_emit_cell(f, FDT_MAGIC);
484198956Srrs	fprintf(f, "\t/* totalsize */\n");
485198956Srrs	ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start",
486198956Srrs			symprefix, symprefix);
487198957Srrs	fprintf(f, "\t/* off_dt_struct */\n");
488198956Srrs	ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start",
489204131Srrs		symprefix, symprefix);
490198956Srrs	fprintf(f, "\t/* off_dt_strings */\n");
491198956Srrs	ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start",
492198956Srrs		symprefix, symprefix);
493198956Srrs	fprintf(f, "\t/* off_mem_rsvmap */\n");
494198957Srrs	ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start",
495198956Srrs		symprefix, symprefix);
496204131Srrs	fprintf(f, "\t/* version */\n");
497198956Srrs	asm_emit_cell(f, vi->version);
498198956Srrs	fprintf(f, "\t/* last_comp_version */\n");
499198956Srrs	asm_emit_cell(f, vi->last_comp_version);
500198956Srrs
501198956Srrs	if (vi->flags & FTF_BOOTCPUID) {
502198956Srrs		fprintf(f, "\t/* boot_cpuid_phys */\n");
503198956Srrs		asm_emit_cell(f, bi->boot_cpuid_phys);
504198956Srrs	}
505198956Srrs
506198957Srrs	if (vi->flags & FTF_STRTABSIZE) {
507198956Srrs		fprintf(f, "\t/* size_dt_strings */\n");
508198956Srrs		ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start",
509198956Srrs				symprefix, symprefix);
510198956Srrs	}
511204131Srrs
512198956Srrs	if (vi->flags & FTF_STRUCTSIZE) {
513198956Srrs		fprintf(f, "\t/* size_dt_struct */\n");
514198956Srrs		ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start",
515198956Srrs			symprefix, symprefix);
516198957Srrs	}
517198956Srrs
518198956Srrs	/*
519198956Srrs	 * Reserve map entries.
520198956Srrs	 * Align the reserve map to a doubleword boundary.
521198956Srrs	 * Each entry is an (address, size) pair of u64 values.
522198956Srrs	 * Always supply a zero-sized temination entry.
523198956Srrs	 */
524198956Srrs	asm_emit_align(f, 8);
525198956Srrs	emit_label(f, symprefix, "reserve_map");
526198956Srrs
527198956Srrs	fprintf(f, "/* Memory reserve map from source file */\n");
528198956Srrs
529198956Srrs	/*
530198956Srrs	 * Use .long on high and low halfs of u64s to avoid .quad
531198957Srrs	 * as it appears .quad isn't available in some assemblers.
532198957Srrs	 */
533198956Srrs	for (re = bi->reservelist; re; re = re->next) {
534198956Srrs		struct label *l;
535198956Srrs
536198956Srrs		for_each_label(re->labels, l) {
537198956Srrs			fprintf(f, "\t.globl\t%s\n", l->label);
538198956Srrs			fprintf(f, "%s:\n", l->label);
539198956Srrs		}
540198956Srrs		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.address >> 32));
541198956Srrs		ASM_EMIT_BELONG(f, "0x%08x",
542198956Srrs				(unsigned int)(re->re.address & 0xffffffff));
543198956Srrs		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size >> 32));
544198956Srrs		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size & 0xffffffff));
545198956Srrs	}
546198956Srrs	for (i = 0; i < reservenum; i++) {
547198956Srrs		fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
548198956Srrs	}
549198956Srrs
550198956Srrs	fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
551198956Srrs
552198956Srrs	emit_label(f, symprefix, "struct_start");
553198957Srrs	flatten_tree(bi->dt, &asm_emitter, f, &strbuf, vi);
554198956Srrs
555198957Srrs	fprintf(f, "\t/* FDT_END */\n");
556198956Srrs	asm_emit_cell(f, FDT_END);
557198956Srrs	emit_label(f, symprefix, "struct_end");
558198956Srrs
559198956Srrs	emit_label(f, symprefix, "strings_start");
560198956Srrs	dump_stringtable_asm(f, strbuf);
561198957Srrs	emit_label(f, symprefix, "strings_end");
562198956Srrs
563198957Srrs	emit_label(f, symprefix, "blob_end");
564198956Srrs
565198956Srrs	/*
566198956Srrs	 * If the user asked for more space than is used, pad it out.
567198956Srrs	 */
568198956Srrs	if (minsize > 0) {
569198957Srrs		fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n",
570198956Srrs			minsize, symprefix, symprefix);
571204131Srrs	}
572198956Srrs	if (padsize > 0) {
573198956Srrs		fprintf(f, "\t.space\t%d, 0\n", padsize);
574198956Srrs	}
575198956Srrs	emit_label(f, symprefix, "blob_abs_end");
576198957Srrs
577198956Srrs	data_free(strbuf);
578204131Srrs}
579198956Srrs
580198956Srrsstruct inbuf {
581198956Srrs	char *base, *limit, *ptr;
582198956Srrs};
583198957Srrs
584198956Srrsstatic void inbuf_init(struct inbuf *inb, void *base, void *limit)
585204131Srrs{
586198956Srrs	inb->base = base;
587198956Srrs	inb->limit = limit;
588198956Srrs	inb->ptr = inb->base;
589198956Srrs}
590198956Srrs
591198956Srrsstatic void flat_read_chunk(struct inbuf *inb, void *p, int len)
592198956Srrs{
593198956Srrs	if ((inb->ptr + len) > inb->limit)
594198956Srrs		die("Premature end of data parsing flat device tree\n");
595198956Srrs
596198957Srrs	memcpy(p, inb->ptr, len);
597198956Srrs
598204131Srrs	inb->ptr += len;
599198956Srrs}
600198956Srrs
601198956Srrsstatic uint32_t flat_read_word(struct inbuf *inb)
602198956Srrs{
603198957Srrs	uint32_t val;
604198956Srrs
605204131Srrs	assert(((inb->ptr - inb->base) % sizeof(val)) == 0);
606198956Srrs
607198956Srrs	flat_read_chunk(inb, &val, sizeof(val));
608198956Srrs
609198956Srrs	return fdt32_to_cpu(val);
610198957Srrs}
611198956Srrs
612198956Srrsstatic void flat_realign(struct inbuf *inb, int align)
613198956Srrs{
614198956Srrs	int off = inb->ptr - inb->base;
615198956Srrs
616198956Srrs	inb->ptr = inb->base + ALIGN(off, align);
617198956Srrs	if (inb->ptr > inb->limit)
618198956Srrs		die("Premature end of data parsing flat device tree\n");
619198956Srrs}
620198956Srrs
621198956Srrsstatic char *flat_read_string(struct inbuf *inb)
622198956Srrs{
623198956Srrs	int len = 0;
624204131Srrs	const char *p = inb->ptr;
625198956Srrs	char *str;
626198956Srrs
627198956Srrs	do {
628198956Srrs		if (p >= inb->limit)
629198956Srrs			die("Premature end of data parsing flat device tree\n");
630198957Srrs		len++;
631198956Srrs	} while ((*p++) != '\0');
632204131Srrs
633198956Srrs	str = xstrdup(inb->ptr);
634198956Srrs
635198956Srrs	inb->ptr += len;
636198956Srrs
637198956Srrs	flat_realign(inb, sizeof(uint32_t));
638198957Srrs
639198956Srrs	return str;
640204131Srrs}
641198956Srrs
642198956Srrsstatic struct data flat_read_data(struct inbuf *inb, int len)
643198956Srrs{
644198956Srrs	struct data d = empty_data;
645198956Srrs
646198957Srrs	if (len == 0)
647198956Srrs		return empty_data;
648204131Srrs
649198956Srrs	d = data_grow_for(d, len);
650198956Srrs	d.len = len;
651198956Srrs
652198956Srrs	flat_read_chunk(inb, d.val, len);
653198957Srrs
654198956Srrs	flat_realign(inb, sizeof(uint32_t));
655204131Srrs
656198956Srrs	return d;
657198956Srrs}
658198956Srrs
659198956Srrsstatic char *flat_read_stringtable(struct inbuf *inb, int offset)
660198957Srrs{
661198956Srrs	const char *p;
662204131Srrs
663198956Srrs	p = inb->base + offset;
664198956Srrs	while (1) {
665198956Srrs		if (p >= inb->limit || p < inb->base)
666198956Srrs			die("String offset %d overruns string table\n",
667198957Srrs			    offset);
668198957Srrs
669198957Srrs		if (*p == '\0')
670198957Srrs			break;
671198956Srrs
672204131Srrs		p++;
673198956Srrs	}
674198956Srrs
675198956Srrs	return xstrdup(inb->base + offset);
676198956Srrs}
677198957Srrs
678198956Srrsstatic struct property *flat_read_property(struct inbuf *dtbuf,
679204131Srrs					   struct inbuf *strbuf, int flags)
680198956Srrs{
681198956Srrs	uint32_t proplen, stroff;
682198956Srrs	char *name;
683198956Srrs	struct data val;
684198957Srrs
685198956Srrs	proplen = flat_read_word(dtbuf);
686198956Srrs	stroff = flat_read_word(dtbuf);
687
688	name = flat_read_stringtable(strbuf, stroff);
689
690	if ((flags & FTF_VARALIGN) && (proplen >= 8))
691		flat_realign(dtbuf, 8);
692
693	val = flat_read_data(dtbuf, proplen);
694
695	return build_property(name, val);
696}
697
698
699static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
700{
701	struct reserve_info *reservelist = NULL;
702	struct reserve_info *new;
703	struct fdt_reserve_entry re;
704
705	/*
706	 * Each entry is a pair of u64 (addr, size) values for 4 cell_t's.
707	 * List terminates at an entry with size equal to zero.
708	 *
709	 * First pass, count entries.
710	 */
711	while (1) {
712		flat_read_chunk(inb, &re, sizeof(re));
713		re.address  = fdt64_to_cpu(re.address);
714		re.size = fdt64_to_cpu(re.size);
715		if (re.size == 0)
716			break;
717
718		new = build_reserve_entry(re.address, re.size);
719		reservelist = add_reserve_entry(reservelist, new);
720	}
721
722	return reservelist;
723}
724
725
726static char *nodename_from_path(const char *ppath, const char *cpath)
727{
728	int plen;
729
730	plen = strlen(ppath);
731
732	if (!strneq(ppath, cpath, plen))
733		die("Path \"%s\" is not valid as a child of \"%s\"\n",
734		    cpath, ppath);
735
736	/* root node is a special case */
737	if (!streq(ppath, "/"))
738		plen++;
739
740	return xstrdup(cpath + plen);
741}
742
743static struct node *unflatten_tree(struct inbuf *dtbuf,
744				   struct inbuf *strbuf,
745				   const char *parent_flatname, int flags)
746{
747	struct node *node;
748	char *flatname;
749	uint32_t val;
750
751	node = build_node(NULL, NULL);
752
753	flatname = flat_read_string(dtbuf);
754
755	if (flags & FTF_FULLPATH)
756		node->name = nodename_from_path(parent_flatname, flatname);
757	else
758		node->name = flatname;
759
760	do {
761		struct property *prop;
762		struct node *child;
763
764		val = flat_read_word(dtbuf);
765		switch (val) {
766		case FDT_PROP:
767			if (node->children)
768				fprintf(stderr, "Warning: Flat tree input has "
769					"subnodes preceding a property.\n");
770			prop = flat_read_property(dtbuf, strbuf, flags);
771			add_property(node, prop);
772			break;
773
774		case FDT_BEGIN_NODE:
775			child = unflatten_tree(dtbuf,strbuf, flatname, flags);
776			add_child(node, child);
777			break;
778
779		case FDT_END_NODE:
780			break;
781
782		case FDT_END:
783			die("Premature FDT_END in device tree blob\n");
784			break;
785
786		case FDT_NOP:
787			if (!(flags & FTF_NOPS))
788				fprintf(stderr, "Warning: NOP tag found in flat tree"
789					" version <16\n");
790
791			/* Ignore */
792			break;
793
794		default:
795			die("Invalid opcode word %08x in device tree blob\n",
796			    val);
797		}
798	} while (val != FDT_END_NODE);
799
800	return node;
801}
802
803
804struct boot_info *dt_from_blob(const char *fname)
805{
806	FILE *f;
807	uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys;
808	uint32_t off_dt, off_str, off_mem_rsvmap;
809	int rc;
810	char *blob;
811	struct fdt_header *fdt;
812	char *p;
813	struct inbuf dtbuf, strbuf;
814	struct inbuf memresvbuf;
815	int sizeleft;
816	struct reserve_info *reservelist;
817	struct node *tree;
818	uint32_t val;
819	int flags = 0;
820
821	f = srcfile_relative_open(fname, NULL);
822
823	rc = fread(&magic, sizeof(magic), 1, f);
824	if (ferror(f))
825		die("Error reading DT blob magic number: %s\n",
826		    strerror(errno));
827	if (rc < 1) {
828		if (feof(f))
829			die("EOF reading DT blob magic number\n");
830		else
831			die("Mysterious short read reading magic number\n");
832	}
833
834	magic = fdt32_to_cpu(magic);
835	if (magic != FDT_MAGIC)
836		die("Blob has incorrect magic number\n");
837
838	rc = fread(&totalsize, sizeof(totalsize), 1, f);
839	if (ferror(f))
840		die("Error reading DT blob size: %s\n", strerror(errno));
841	if (rc < 1) {
842		if (feof(f))
843			die("EOF reading DT blob size\n");
844		else
845			die("Mysterious short read reading blob size\n");
846	}
847
848	totalsize = fdt32_to_cpu(totalsize);
849	if (totalsize < FDT_V1_SIZE)
850		die("DT blob size (%d) is too small\n", totalsize);
851
852	blob = xmalloc(totalsize);
853
854	fdt = (struct fdt_header *)blob;
855	fdt->magic = cpu_to_fdt32(magic);
856	fdt->totalsize = cpu_to_fdt32(totalsize);
857
858	sizeleft = totalsize - sizeof(magic) - sizeof(totalsize);
859	p = blob + sizeof(magic)  + sizeof(totalsize);
860
861	while (sizeleft) {
862		if (feof(f))
863			die("EOF before reading %d bytes of DT blob\n",
864			    totalsize);
865
866		rc = fread(p, 1, sizeleft, f);
867		if (ferror(f))
868			die("Error reading DT blob: %s\n",
869			    strerror(errno));
870
871		sizeleft -= rc;
872		p += rc;
873	}
874
875	off_dt = fdt32_to_cpu(fdt->off_dt_struct);
876	off_str = fdt32_to_cpu(fdt->off_dt_strings);
877	off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap);
878	version = fdt32_to_cpu(fdt->version);
879	boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys);
880
881	if (off_mem_rsvmap >= totalsize)
882		die("Mem Reserve structure offset exceeds total size\n");
883
884	if (off_dt >= totalsize)
885		die("DT structure offset exceeds total size\n");
886
887	if (off_str > totalsize)
888		die("String table offset exceeds total size\n");
889
890	if (version >= 3) {
891		uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings);
892		if (off_str+size_str > totalsize)
893			die("String table extends past total size\n");
894		inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str);
895	} else {
896		inbuf_init(&strbuf, blob + off_str, blob + totalsize);
897	}
898
899	if (version >= 17) {
900		size_dt = fdt32_to_cpu(fdt->size_dt_struct);
901		if (off_dt+size_dt > totalsize)
902			die("Structure block extends past total size\n");
903	}
904
905	if (version < 16) {
906		flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN;
907	} else {
908		flags |= FTF_NOPS;
909	}
910
911	inbuf_init(&memresvbuf,
912		   blob + off_mem_rsvmap, blob + totalsize);
913	inbuf_init(&dtbuf, blob + off_dt, blob + totalsize);
914
915	reservelist = flat_read_mem_reserve(&memresvbuf);
916
917	val = flat_read_word(&dtbuf);
918
919	if (val != FDT_BEGIN_NODE)
920		die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val);
921
922	tree = unflatten_tree(&dtbuf, &strbuf, "", flags);
923
924	val = flat_read_word(&dtbuf);
925	if (val != FDT_END)
926		die("Device tree blob doesn't end with FDT_END\n");
927
928	free(blob);
929
930	fclose(f);
931
932	return build_boot_info(reservelist, tree, boot_cpuid_phys);
933}
934