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