xref: /netbsd-src/external/gpl2/dtc/dist/flattree.c (revision 7cdf52c4e116cff01c811ade809106cc07d84795)
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 
19 static 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 
37 struct 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 
bin_emit_cell(void * e,cell_t val)47 static 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 
bin_emit_string(void * e,const char * str,int len)54 static 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 
bin_emit_align(void * e,int a)65 static void bin_emit_align(void *e, int a)
66 {
67 	struct data *dtbuf = e;
68 
69 	*dtbuf = data_append_align(*dtbuf, a);
70 }
71 
bin_emit_data(void * e,struct data d)72 static 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 
bin_emit_beginnode(void * e,struct label * labels)79 static void bin_emit_beginnode(void *e, struct label *labels)
80 {
81 	bin_emit_cell(e, FDT_BEGIN_NODE);
82 }
83 
bin_emit_endnode(void * e,struct label * labels)84 static void bin_emit_endnode(void *e, struct label *labels)
85 {
86 	bin_emit_cell(e, FDT_END_NODE);
87 }
88 
bin_emit_property(void * e,struct label * labels)89 static void bin_emit_property(void *e, struct label *labels)
90 {
91 	bin_emit_cell(e, FDT_PROP);
92 }
93 
94 static 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 
emit_label(FILE * f,const char * prefix,const char * label)104 static 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 
emit_offset_label(FILE * f,const char * label,int offset)111 static 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 
asm_emit_cell(void * e,cell_t val)125 static 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 
asm_emit_string(void * e,const char * str,int len)134 static 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 
asm_emit_align(void * e,int a)144 static 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 
asm_emit_data(void * e,struct data d)151 static 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 
asm_emit_beginnode(void * e,struct label * labels)173 static 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 
asm_emit_endnode(void * e,struct label * labels)186 static 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 
asm_emit_property(void * e,struct label * labels)199 static 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 
212 static 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 
stringtable_insert(struct data * d,const char * str)222 static 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 
flatten_tree(struct node * tree,struct emitter * emit,void * etarget,struct data * strbuf,struct version_info * vi)237 static 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 
flatten_reserve_list(struct reserve_info * reservelist,struct version_info * vi)295 static 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 
make_fdt_header(struct fdt_header * fdt,struct version_info * vi,int reservesize,int dtsize,int strsize,int boot_cpuid_phys)315 static 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 
dt_to_blob(FILE * f,struct dt_info * dti,int version)347 void 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 
dump_stringtable_asm(FILE * f,struct data strbuf)434 static 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 
dt_to_asm(FILE * f,struct dt_info * dti,int version)448 void 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 
567 struct inbuf {
568 	char *base, *limit, *ptr;
569 };
570 
inbuf_init(struct inbuf * inb,void * base,void * limit)571 static 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 
flat_read_chunk(struct inbuf * inb,void * p,int len)578 static 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 
flat_read_word(struct inbuf * inb)588 static 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 
flat_realign(struct inbuf * inb,int align)599 static 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 
flat_read_string(struct inbuf * inb)608 static 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 
flat_read_data(struct inbuf * inb,int len)629 static 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 
flat_read_stringtable(struct inbuf * inb,int offset)646 static 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 
flat_read_property(struct inbuf * dtbuf,struct inbuf * strbuf,int flags)665 static 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 
flat_read_mem_reserve(struct inbuf * inb)686 static 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 
nodename_from_path(const char * ppath,const char * cpath)715 static 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 
unflatten_tree(struct inbuf * dtbuf,struct inbuf * strbuf,const char * parent_flatname,int flags)732 static 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 
dt_from_blob(const char * fname)797 struct 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