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