Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005.
0004  */
0005 
0006 #include "dtc.h"
0007 #include "srcpos.h"
0008 
0009 #define FTF_FULLPATH    0x1
0010 #define FTF_VARALIGN    0x2
0011 #define FTF_NAMEPROPS   0x4
0012 #define FTF_BOOTCPUID   0x8
0013 #define FTF_STRTABSIZE  0x10
0014 #define FTF_STRUCTSIZE  0x20
0015 #define FTF_NOPS    0x40
0016 
0017 static struct version_info {
0018     int version;
0019     int last_comp_version;
0020     int hdr_size;
0021     int flags;
0022 } version_table[] = {
0023     {1, 1, FDT_V1_SIZE,
0024      FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS},
0025     {2, 1, FDT_V2_SIZE,
0026      FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID},
0027     {3, 1, FDT_V3_SIZE,
0028      FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE},
0029     {16, 16, FDT_V3_SIZE,
0030      FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_NOPS},
0031     {17, 16, FDT_V17_SIZE,
0032      FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS},
0033 };
0034 
0035 struct emitter {
0036     void (*cell)(void *, cell_t);
0037     void (*string)(void *, const char *, int);
0038     void (*align)(void *, int);
0039     void (*data)(void *, struct data);
0040     void (*beginnode)(void *, struct label *labels);
0041     void (*endnode)(void *, struct label *labels);
0042     void (*property)(void *, struct label *labels);
0043 };
0044 
0045 static void bin_emit_cell(void *e, cell_t val)
0046 {
0047     struct data *dtbuf = e;
0048 
0049     *dtbuf = data_append_cell(*dtbuf, val);
0050 }
0051 
0052 static void bin_emit_string(void *e, const char *str, int len)
0053 {
0054     struct data *dtbuf = e;
0055 
0056     if (len == 0)
0057         len = strlen(str);
0058 
0059     *dtbuf = data_append_data(*dtbuf, str, len);
0060     *dtbuf = data_append_byte(*dtbuf, '\0');
0061 }
0062 
0063 static void bin_emit_align(void *e, int a)
0064 {
0065     struct data *dtbuf = e;
0066 
0067     *dtbuf = data_append_align(*dtbuf, a);
0068 }
0069 
0070 static void bin_emit_data(void *e, struct data d)
0071 {
0072     struct data *dtbuf = e;
0073 
0074     *dtbuf = data_append_data(*dtbuf, d.val, d.len);
0075 }
0076 
0077 static void bin_emit_beginnode(void *e, struct label *labels)
0078 {
0079     bin_emit_cell(e, FDT_BEGIN_NODE);
0080 }
0081 
0082 static void bin_emit_endnode(void *e, struct label *labels)
0083 {
0084     bin_emit_cell(e, FDT_END_NODE);
0085 }
0086 
0087 static void bin_emit_property(void *e, struct label *labels)
0088 {
0089     bin_emit_cell(e, FDT_PROP);
0090 }
0091 
0092 static struct emitter bin_emitter = {
0093     .cell = bin_emit_cell,
0094     .string = bin_emit_string,
0095     .align = bin_emit_align,
0096     .data = bin_emit_data,
0097     .beginnode = bin_emit_beginnode,
0098     .endnode = bin_emit_endnode,
0099     .property = bin_emit_property,
0100 };
0101 
0102 static void emit_label(FILE *f, const char *prefix, const char *label)
0103 {
0104     fprintf(f, "\t.globl\t%s_%s\n", prefix, label);
0105     fprintf(f, "%s_%s:\n", prefix, label);
0106     fprintf(f, "_%s_%s:\n", prefix, label);
0107 }
0108 
0109 static void emit_offset_label(FILE *f, const char *label, int offset)
0110 {
0111     fprintf(f, "\t.globl\t%s\n", label);
0112     fprintf(f, "%s\t= . + %d\n", label, offset);
0113 }
0114 
0115 #define ASM_EMIT_BELONG(f, fmt, ...) \
0116     { \
0117         fprintf((f), "\t.byte\t((" fmt ") >> 24) & 0xff\n", __VA_ARGS__); \
0118         fprintf((f), "\t.byte\t((" fmt ") >> 16) & 0xff\n", __VA_ARGS__); \
0119         fprintf((f), "\t.byte\t((" fmt ") >> 8) & 0xff\n", __VA_ARGS__); \
0120         fprintf((f), "\t.byte\t(" fmt ") & 0xff\n", __VA_ARGS__); \
0121     }
0122 
0123 static void asm_emit_cell(void *e, cell_t val)
0124 {
0125     FILE *f = e;
0126 
0127     fprintf(f, "\t.byte\t0x%02x\n" "\t.byte\t0x%02x\n"
0128         "\t.byte\t0x%02x\n" "\t.byte\t0x%02x\n",
0129         (val >> 24) & 0xff, (val >> 16) & 0xff,
0130         (val >> 8) & 0xff, val & 0xff);
0131 }
0132 
0133 static void asm_emit_string(void *e, const char *str, int len)
0134 {
0135     FILE *f = e;
0136 
0137     if (len != 0)
0138         fprintf(f, "\t.asciz\t\"%.*s\"\n", len, str);
0139     else
0140         fprintf(f, "\t.asciz\t\"%s\"\n", str);
0141 }
0142 
0143 static void asm_emit_align(void *e, int a)
0144 {
0145     FILE *f = e;
0146 
0147     fprintf(f, "\t.balign\t%d, 0\n", a);
0148 }
0149 
0150 static void asm_emit_data(void *e, struct data d)
0151 {
0152     FILE *f = e;
0153     unsigned int off = 0;
0154     struct marker *m = d.markers;
0155 
0156     for_each_marker_of_type(m, LABEL)
0157         emit_offset_label(f, m->ref, m->offset);
0158 
0159     while ((d.len - off) >= sizeof(uint32_t)) {
0160         asm_emit_cell(e, dtb_ld32(d.val + off));
0161         off += sizeof(uint32_t);
0162     }
0163 
0164     while ((d.len - off) >= 1) {
0165         fprintf(f, "\t.byte\t0x%hhx\n", d.val[off]);
0166         off += 1;
0167     }
0168 
0169     assert(off == d.len);
0170 }
0171 
0172 static void asm_emit_beginnode(void *e, struct label *labels)
0173 {
0174     FILE *f = e;
0175     struct label *l;
0176 
0177     for_each_label(labels, l) {
0178         fprintf(f, "\t.globl\t%s\n", l->label);
0179         fprintf(f, "%s:\n", l->label);
0180     }
0181     fprintf(f, "\t/* FDT_BEGIN_NODE */\n");
0182     asm_emit_cell(e, FDT_BEGIN_NODE);
0183 }
0184 
0185 static void asm_emit_endnode(void *e, struct label *labels)
0186 {
0187     FILE *f = e;
0188     struct label *l;
0189 
0190     fprintf(f, "\t/* FDT_END_NODE */\n");
0191     asm_emit_cell(e, FDT_END_NODE);
0192     for_each_label(labels, l) {
0193         fprintf(f, "\t.globl\t%s_end\n", l->label);
0194         fprintf(f, "%s_end:\n", l->label);
0195     }
0196 }
0197 
0198 static void asm_emit_property(void *e, struct label *labels)
0199 {
0200     FILE *f = e;
0201     struct label *l;
0202 
0203     for_each_label(labels, l) {
0204         fprintf(f, "\t.globl\t%s\n", l->label);
0205         fprintf(f, "%s:\n", l->label);
0206     }
0207     fprintf(f, "\t/* FDT_PROP */\n");
0208     asm_emit_cell(e, FDT_PROP);
0209 }
0210 
0211 static struct emitter asm_emitter = {
0212     .cell = asm_emit_cell,
0213     .string = asm_emit_string,
0214     .align = asm_emit_align,
0215     .data = asm_emit_data,
0216     .beginnode = asm_emit_beginnode,
0217     .endnode = asm_emit_endnode,
0218     .property = asm_emit_property,
0219 };
0220 
0221 static int stringtable_insert(struct data *d, const char *str)
0222 {
0223     unsigned int i;
0224 
0225     /* FIXME: do this more efficiently? */
0226 
0227     for (i = 0; i < d->len; i++) {
0228         if (streq(str, d->val + i))
0229             return i;
0230     }
0231 
0232     *d = data_append_data(*d, str, strlen(str)+1);
0233     return i;
0234 }
0235 
0236 static void flatten_tree(struct node *tree, struct emitter *emit,
0237              void *etarget, struct data *strbuf,
0238              struct version_info *vi)
0239 {
0240     struct property *prop;
0241     struct node *child;
0242     bool seen_name_prop = false;
0243 
0244     if (tree->deleted)
0245         return;
0246 
0247     emit->beginnode(etarget, tree->labels);
0248 
0249     if (vi->flags & FTF_FULLPATH)
0250         emit->string(etarget, tree->fullpath, 0);
0251     else
0252         emit->string(etarget, tree->name, 0);
0253 
0254     emit->align(etarget, sizeof(cell_t));
0255 
0256     for_each_property(tree, prop) {
0257         int nameoff;
0258 
0259         if (streq(prop->name, "name"))
0260             seen_name_prop = true;
0261 
0262         nameoff = stringtable_insert(strbuf, prop->name);
0263 
0264         emit->property(etarget, prop->labels);
0265         emit->cell(etarget, prop->val.len);
0266         emit->cell(etarget, nameoff);
0267 
0268         if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8))
0269             emit->align(etarget, 8);
0270 
0271         emit->data(etarget, prop->val);
0272         emit->align(etarget, sizeof(cell_t));
0273     }
0274 
0275     if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) {
0276         emit->property(etarget, NULL);
0277         emit->cell(etarget, tree->basenamelen+1);
0278         emit->cell(etarget, stringtable_insert(strbuf, "name"));
0279 
0280         if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8))
0281             emit->align(etarget, 8);
0282 
0283         emit->string(etarget, tree->name, tree->basenamelen);
0284         emit->align(etarget, sizeof(cell_t));
0285     }
0286 
0287     for_each_child(tree, child) {
0288         flatten_tree(child, emit, etarget, strbuf, vi);
0289     }
0290 
0291     emit->endnode(etarget, tree->labels);
0292 }
0293 
0294 static struct data flatten_reserve_list(struct reserve_info *reservelist,
0295                  struct version_info *vi)
0296 {
0297     struct reserve_info *re;
0298     struct data d = empty_data;
0299     unsigned int j;
0300 
0301     for (re = reservelist; re; re = re->next) {
0302         d = data_append_re(d, re->address, re->size);
0303     }
0304     /*
0305      * Add additional reserved slots if the user asked for them.
0306      */
0307     for (j = 0; j < reservenum; j++) {
0308         d = data_append_re(d, 0, 0);
0309     }
0310 
0311     return d;
0312 }
0313 
0314 static void make_fdt_header(struct fdt_header *fdt,
0315                 struct version_info *vi,
0316                 int reservesize, int dtsize, int strsize,
0317                 int boot_cpuid_phys)
0318 {
0319     int reserve_off;
0320 
0321     reservesize += sizeof(struct fdt_reserve_entry);
0322 
0323     memset(fdt, 0xff, sizeof(*fdt));
0324 
0325     fdt->magic = cpu_to_fdt32(FDT_MAGIC);
0326     fdt->version = cpu_to_fdt32(vi->version);
0327     fdt->last_comp_version = cpu_to_fdt32(vi->last_comp_version);
0328 
0329     /* Reserve map should be doubleword aligned */
0330     reserve_off = ALIGN(vi->hdr_size, 8);
0331 
0332     fdt->off_mem_rsvmap = cpu_to_fdt32(reserve_off);
0333     fdt->off_dt_struct = cpu_to_fdt32(reserve_off + reservesize);
0334     fdt->off_dt_strings = cpu_to_fdt32(reserve_off + reservesize
0335                       + dtsize);
0336     fdt->totalsize = cpu_to_fdt32(reserve_off + reservesize + dtsize + strsize);
0337 
0338     if (vi->flags & FTF_BOOTCPUID)
0339         fdt->boot_cpuid_phys = cpu_to_fdt32(boot_cpuid_phys);
0340     if (vi->flags & FTF_STRTABSIZE)
0341         fdt->size_dt_strings = cpu_to_fdt32(strsize);
0342     if (vi->flags & FTF_STRUCTSIZE)
0343         fdt->size_dt_struct = cpu_to_fdt32(dtsize);
0344 }
0345 
0346 void dt_to_blob(FILE *f, struct dt_info *dti, int version)
0347 {
0348     struct version_info *vi = NULL;
0349     unsigned int i;
0350     struct data blob       = empty_data;
0351     struct data reservebuf = empty_data;
0352     struct data dtbuf      = empty_data;
0353     struct data strbuf     = empty_data;
0354     struct fdt_header fdt;
0355     int padlen = 0;
0356 
0357     for (i = 0; i < ARRAY_SIZE(version_table); i++) {
0358         if (version_table[i].version == version)
0359             vi = &version_table[i];
0360     }
0361     if (!vi)
0362         die("Unknown device tree blob version %d\n", version);
0363 
0364     flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
0365     bin_emit_cell(&dtbuf, FDT_END);
0366 
0367     reservebuf = flatten_reserve_list(dti->reservelist, vi);
0368 
0369     /* Make header */
0370     make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len,
0371             dti->boot_cpuid_phys);
0372 
0373     /*
0374      * If the user asked for more space than is used, adjust the totalsize.
0375      */
0376     if (minsize > 0) {
0377         padlen = minsize - fdt32_to_cpu(fdt.totalsize);
0378         if (padlen < 0) {
0379             padlen = 0;
0380             if (quiet < 1)
0381                 fprintf(stderr,
0382                     "Warning: blob size %"PRIu32" >= minimum size %d\n",
0383                     fdt32_to_cpu(fdt.totalsize), minsize);
0384         }
0385     }
0386 
0387     if (padsize > 0)
0388         padlen = padsize;
0389 
0390     if (alignsize > 0)
0391         padlen = ALIGN(fdt32_to_cpu(fdt.totalsize) + padlen, alignsize)
0392             - fdt32_to_cpu(fdt.totalsize);
0393 
0394     if (padlen > 0) {
0395         int tsize = fdt32_to_cpu(fdt.totalsize);
0396         tsize += padlen;
0397         fdt.totalsize = cpu_to_fdt32(tsize);
0398     }
0399 
0400     /*
0401      * Assemble the blob: start with the header, add with alignment
0402      * the reserve buffer, add the reserve map terminating zeroes,
0403      * the device tree itself, and finally the strings.
0404      */
0405     blob = data_append_data(blob, &fdt, vi->hdr_size);
0406     blob = data_append_align(blob, 8);
0407     blob = data_merge(blob, reservebuf);
0408     blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry));
0409     blob = data_merge(blob, dtbuf);
0410     blob = data_merge(blob, strbuf);
0411 
0412     /*
0413      * If the user asked for more space than is used, pad out the blob.
0414      */
0415     if (padlen > 0)
0416         blob = data_append_zeroes(blob, padlen);
0417 
0418     if (fwrite(blob.val, blob.len, 1, f) != 1) {
0419         if (ferror(f))
0420             die("Error writing device tree blob: %s\n",
0421                 strerror(errno));
0422         else
0423             die("Short write on device tree blob\n");
0424     }
0425 
0426     /*
0427      * data_merge() frees the right-hand element so only the blob
0428      * remains to be freed.
0429      */
0430     data_free(blob);
0431 }
0432 
0433 static void dump_stringtable_asm(FILE *f, struct data strbuf)
0434 {
0435     const char *p;
0436     int len;
0437 
0438     p = strbuf.val;
0439 
0440     while (p < (strbuf.val + strbuf.len)) {
0441         len = strlen(p);
0442         fprintf(f, "\t.asciz \"%s\"\n", p);
0443         p += len+1;
0444     }
0445 }
0446 
0447 void dt_to_asm(FILE *f, struct dt_info *dti, int version)
0448 {
0449     struct version_info *vi = NULL;
0450     unsigned int i;
0451     struct data strbuf = empty_data;
0452     struct reserve_info *re;
0453     const char *symprefix = "dt";
0454 
0455     for (i = 0; i < ARRAY_SIZE(version_table); i++) {
0456         if (version_table[i].version == version)
0457             vi = &version_table[i];
0458     }
0459     if (!vi)
0460         die("Unknown device tree blob version %d\n", version);
0461 
0462     fprintf(f, "/* autogenerated by dtc, do not edit */\n\n");
0463 
0464     emit_label(f, symprefix, "blob_start");
0465     emit_label(f, symprefix, "header");
0466     fprintf(f, "\t/* magic */\n");
0467     asm_emit_cell(f, FDT_MAGIC);
0468     fprintf(f, "\t/* totalsize */\n");
0469     ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start",
0470             symprefix, symprefix);
0471     fprintf(f, "\t/* off_dt_struct */\n");
0472     ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start",
0473         symprefix, symprefix);
0474     fprintf(f, "\t/* off_dt_strings */\n");
0475     ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start",
0476         symprefix, symprefix);
0477     fprintf(f, "\t/* off_mem_rsvmap */\n");
0478     ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start",
0479         symprefix, symprefix);
0480     fprintf(f, "\t/* version */\n");
0481     asm_emit_cell(f, vi->version);
0482     fprintf(f, "\t/* last_comp_version */\n");
0483     asm_emit_cell(f, vi->last_comp_version);
0484 
0485     if (vi->flags & FTF_BOOTCPUID) {
0486         fprintf(f, "\t/* boot_cpuid_phys */\n");
0487         asm_emit_cell(f, dti->boot_cpuid_phys);
0488     }
0489 
0490     if (vi->flags & FTF_STRTABSIZE) {
0491         fprintf(f, "\t/* size_dt_strings */\n");
0492         ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start",
0493                 symprefix, symprefix);
0494     }
0495 
0496     if (vi->flags & FTF_STRUCTSIZE) {
0497         fprintf(f, "\t/* size_dt_struct */\n");
0498         ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start",
0499             symprefix, symprefix);
0500     }
0501 
0502     /*
0503      * Reserve map entries.
0504      * Align the reserve map to a doubleword boundary.
0505      * Each entry is an (address, size) pair of u64 values.
0506      * Always supply a zero-sized temination entry.
0507      */
0508     asm_emit_align(f, 8);
0509     emit_label(f, symprefix, "reserve_map");
0510 
0511     fprintf(f, "/* Memory reserve map from source file */\n");
0512 
0513     /*
0514      * Use .long on high and low halves of u64s to avoid .quad
0515      * as it appears .quad isn't available in some assemblers.
0516      */
0517     for (re = dti->reservelist; re; re = re->next) {
0518         struct label *l;
0519 
0520         for_each_label(re->labels, l) {
0521             fprintf(f, "\t.globl\t%s\n", l->label);
0522             fprintf(f, "%s:\n", l->label);
0523         }
0524         ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->address >> 32));
0525         ASM_EMIT_BELONG(f, "0x%08x",
0526                 (unsigned int)(re->address & 0xffffffff));
0527         ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->size >> 32));
0528         ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->size & 0xffffffff));
0529     }
0530     for (i = 0; i < reservenum; i++) {
0531         fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
0532     }
0533 
0534     fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
0535 
0536     emit_label(f, symprefix, "struct_start");
0537     flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi);
0538 
0539     fprintf(f, "\t/* FDT_END */\n");
0540     asm_emit_cell(f, FDT_END);
0541     emit_label(f, symprefix, "struct_end");
0542 
0543     emit_label(f, symprefix, "strings_start");
0544     dump_stringtable_asm(f, strbuf);
0545     emit_label(f, symprefix, "strings_end");
0546 
0547     emit_label(f, symprefix, "blob_end");
0548 
0549     /*
0550      * If the user asked for more space than is used, pad it out.
0551      */
0552     if (minsize > 0) {
0553         fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n",
0554             minsize, symprefix, symprefix);
0555     }
0556     if (padsize > 0) {
0557         fprintf(f, "\t.space\t%d, 0\n", padsize);
0558     }
0559     if (alignsize > 0)
0560         asm_emit_align(f, alignsize);
0561     emit_label(f, symprefix, "blob_abs_end");
0562 
0563     data_free(strbuf);
0564 }
0565 
0566 struct inbuf {
0567     char *base, *limit, *ptr;
0568 };
0569 
0570 static void inbuf_init(struct inbuf *inb, void *base, void *limit)
0571 {
0572     inb->base = base;
0573     inb->limit = limit;
0574     inb->ptr = inb->base;
0575 }
0576 
0577 static void flat_read_chunk(struct inbuf *inb, void *p, int len)
0578 {
0579     if ((inb->ptr + len) > inb->limit)
0580         die("Premature end of data parsing flat device tree\n");
0581 
0582     memcpy(p, inb->ptr, len);
0583 
0584     inb->ptr += len;
0585 }
0586 
0587 static uint32_t flat_read_word(struct inbuf *inb)
0588 {
0589     fdt32_t val;
0590 
0591     assert(((inb->ptr - inb->base) % sizeof(val)) == 0);
0592 
0593     flat_read_chunk(inb, &val, sizeof(val));
0594 
0595     return fdt32_to_cpu(val);
0596 }
0597 
0598 static void flat_realign(struct inbuf *inb, int align)
0599 {
0600     int off = inb->ptr - inb->base;
0601 
0602     inb->ptr = inb->base + ALIGN(off, align);
0603     if (inb->ptr > inb->limit)
0604         die("Premature end of data parsing flat device tree\n");
0605 }
0606 
0607 static char *flat_read_string(struct inbuf *inb)
0608 {
0609     int len = 0;
0610     const char *p = inb->ptr;
0611     char *str;
0612 
0613     do {
0614         if (p >= inb->limit)
0615             die("Premature end of data parsing flat device tree\n");
0616         len++;
0617     } while ((*p++) != '\0');
0618 
0619     str = xstrdup(inb->ptr);
0620 
0621     inb->ptr += len;
0622 
0623     flat_realign(inb, sizeof(uint32_t));
0624 
0625     return str;
0626 }
0627 
0628 static struct data flat_read_data(struct inbuf *inb, int len)
0629 {
0630     struct data d = empty_data;
0631 
0632     if (len == 0)
0633         return empty_data;
0634 
0635     d = data_grow_for(d, len);
0636     d.len = len;
0637 
0638     flat_read_chunk(inb, d.val, len);
0639 
0640     flat_realign(inb, sizeof(uint32_t));
0641 
0642     return d;
0643 }
0644 
0645 static char *flat_read_stringtable(struct inbuf *inb, int offset)
0646 {
0647     const char *p;
0648 
0649     p = inb->base + offset;
0650     while (1) {
0651         if (p >= inb->limit || p < inb->base)
0652             die("String offset %d overruns string table\n",
0653                 offset);
0654 
0655         if (*p == '\0')
0656             break;
0657 
0658         p++;
0659     }
0660 
0661     return xstrdup(inb->base + offset);
0662 }
0663 
0664 static struct property *flat_read_property(struct inbuf *dtbuf,
0665                        struct inbuf *strbuf, int flags)
0666 {
0667     uint32_t proplen, stroff;
0668     char *name;
0669     struct data val;
0670 
0671     proplen = flat_read_word(dtbuf);
0672     stroff = flat_read_word(dtbuf);
0673 
0674     name = flat_read_stringtable(strbuf, stroff);
0675 
0676     if ((flags & FTF_VARALIGN) && (proplen >= 8))
0677         flat_realign(dtbuf, 8);
0678 
0679     val = flat_read_data(dtbuf, proplen);
0680 
0681     return build_property(name, val, NULL);
0682 }
0683 
0684 
0685 static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
0686 {
0687     struct reserve_info *reservelist = NULL;
0688     struct reserve_info *new;
0689     struct fdt_reserve_entry re;
0690 
0691     /*
0692      * Each entry is a pair of u64 (addr, size) values for 4 cell_t's.
0693      * List terminates at an entry with size equal to zero.
0694      *
0695      * First pass, count entries.
0696      */
0697     while (1) {
0698         uint64_t address, size;
0699 
0700         flat_read_chunk(inb, &re, sizeof(re));
0701         address  = fdt64_to_cpu(re.address);
0702         size = fdt64_to_cpu(re.size);
0703         if (size == 0)
0704             break;
0705 
0706         new = build_reserve_entry(address, size);
0707         reservelist = add_reserve_entry(reservelist, new);
0708     }
0709 
0710     return reservelist;
0711 }
0712 
0713 
0714 static char *nodename_from_path(const char *ppath, const char *cpath)
0715 {
0716     int plen;
0717 
0718     plen = strlen(ppath);
0719 
0720     if (!strstarts(cpath, ppath))
0721         die("Path \"%s\" is not valid as a child of \"%s\"\n",
0722             cpath, ppath);
0723 
0724     /* root node is a special case */
0725     if (!streq(ppath, "/"))
0726         plen++;
0727 
0728     return xstrdup(cpath + plen);
0729 }
0730 
0731 static struct node *unflatten_tree(struct inbuf *dtbuf,
0732                    struct inbuf *strbuf,
0733                    const char *parent_flatname, int flags)
0734 {
0735     struct node *node;
0736     char *flatname;
0737     uint32_t val;
0738 
0739     node = build_node(NULL, NULL, NULL);
0740 
0741     flatname = flat_read_string(dtbuf);
0742 
0743     if (flags & FTF_FULLPATH)
0744         node->name = nodename_from_path(parent_flatname, flatname);
0745     else
0746         node->name = flatname;
0747 
0748     do {
0749         struct property *prop;
0750         struct node *child;
0751 
0752         val = flat_read_word(dtbuf);
0753         switch (val) {
0754         case FDT_PROP:
0755             if (node->children)
0756                 fprintf(stderr, "Warning: Flat tree input has "
0757                     "subnodes preceding a property.\n");
0758             prop = flat_read_property(dtbuf, strbuf, flags);
0759             add_property(node, prop);
0760             break;
0761 
0762         case FDT_BEGIN_NODE:
0763             child = unflatten_tree(dtbuf,strbuf, flatname, flags);
0764             add_child(node, child);
0765             break;
0766 
0767         case FDT_END_NODE:
0768             break;
0769 
0770         case FDT_END:
0771             die("Premature FDT_END in device tree blob\n");
0772             break;
0773 
0774         case FDT_NOP:
0775             if (!(flags & FTF_NOPS))
0776                 fprintf(stderr, "Warning: NOP tag found in flat tree"
0777                     " version <16\n");
0778 
0779             /* Ignore */
0780             break;
0781 
0782         default:
0783             die("Invalid opcode word %08x in device tree blob\n",
0784                 val);
0785         }
0786     } while (val != FDT_END_NODE);
0787 
0788     if (node->name != flatname) {
0789         free(flatname);
0790     }
0791 
0792     return node;
0793 }
0794 
0795 
0796 struct dt_info *dt_from_blob(const char *fname)
0797 {
0798     FILE *f;
0799     fdt32_t magic_buf, totalsize_buf;
0800     uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys;
0801     uint32_t off_dt, off_str, off_mem_rsvmap;
0802     int rc;
0803     char *blob;
0804     struct fdt_header *fdt;
0805     char *p;
0806     struct inbuf dtbuf, strbuf;
0807     struct inbuf memresvbuf;
0808     int sizeleft;
0809     struct reserve_info *reservelist;
0810     struct node *tree;
0811     uint32_t val;
0812     int flags = 0;
0813 
0814     f = srcfile_relative_open(fname, NULL);
0815 
0816     rc = fread(&magic_buf, sizeof(magic_buf), 1, f);
0817     if (ferror(f))
0818         die("Error reading DT blob magic number: %s\n",
0819             strerror(errno));
0820     if (rc < 1) {
0821         if (feof(f))
0822             die("EOF reading DT blob magic number\n");
0823         else
0824             die("Mysterious short read reading magic number\n");
0825     }
0826 
0827     magic = fdt32_to_cpu(magic_buf);
0828     if (magic != FDT_MAGIC)
0829         die("Blob has incorrect magic number\n");
0830 
0831     rc = fread(&totalsize_buf, sizeof(totalsize_buf), 1, f);
0832     if (ferror(f))
0833         die("Error reading DT blob size: %s\n", strerror(errno));
0834     if (rc < 1) {
0835         if (feof(f))
0836             die("EOF reading DT blob size\n");
0837         else
0838             die("Mysterious short read reading blob size\n");
0839     }
0840 
0841     totalsize = fdt32_to_cpu(totalsize_buf);
0842     if (totalsize < FDT_V1_SIZE)
0843         die("DT blob size (%d) is too small\n", totalsize);
0844 
0845     blob = xmalloc(totalsize);
0846 
0847     fdt = (struct fdt_header *)blob;
0848     fdt->magic = cpu_to_fdt32(magic);
0849     fdt->totalsize = cpu_to_fdt32(totalsize);
0850 
0851     sizeleft = totalsize - sizeof(magic) - sizeof(totalsize);
0852     p = blob + sizeof(magic)  + sizeof(totalsize);
0853 
0854     while (sizeleft) {
0855         if (feof(f))
0856             die("EOF before reading %d bytes of DT blob\n",
0857                 totalsize);
0858 
0859         rc = fread(p, 1, sizeleft, f);
0860         if (ferror(f))
0861             die("Error reading DT blob: %s\n",
0862                 strerror(errno));
0863 
0864         sizeleft -= rc;
0865         p += rc;
0866     }
0867 
0868     off_dt = fdt32_to_cpu(fdt->off_dt_struct);
0869     off_str = fdt32_to_cpu(fdt->off_dt_strings);
0870     off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap);
0871     version = fdt32_to_cpu(fdt->version);
0872     boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys);
0873 
0874     if (off_mem_rsvmap >= totalsize)
0875         die("Mem Reserve structure offset exceeds total size\n");
0876 
0877     if (off_dt >= totalsize)
0878         die("DT structure offset exceeds total size\n");
0879 
0880     if (off_str > totalsize)
0881         die("String table offset exceeds total size\n");
0882 
0883     if (version >= 3) {
0884         uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings);
0885         if ((off_str+size_str < off_str) || (off_str+size_str > totalsize))
0886             die("String table extends past total size\n");
0887         inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str);
0888     } else {
0889         inbuf_init(&strbuf, blob + off_str, blob + totalsize);
0890     }
0891 
0892     if (version >= 17) {
0893         size_dt = fdt32_to_cpu(fdt->size_dt_struct);
0894         if ((off_dt+size_dt < off_dt) || (off_dt+size_dt > totalsize))
0895             die("Structure block extends past total size\n");
0896     }
0897 
0898     if (version < 16) {
0899         flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN;
0900     } else {
0901         flags |= FTF_NOPS;
0902     }
0903 
0904     inbuf_init(&memresvbuf,
0905            blob + off_mem_rsvmap, blob + totalsize);
0906     inbuf_init(&dtbuf, blob + off_dt, blob + totalsize);
0907 
0908     reservelist = flat_read_mem_reserve(&memresvbuf);
0909 
0910     val = flat_read_word(&dtbuf);
0911 
0912     if (val != FDT_BEGIN_NODE)
0913         die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val);
0914 
0915     tree = unflatten_tree(&dtbuf, &strbuf, "", flags);
0916 
0917     val = flat_read_word(&dtbuf);
0918     if (val != FDT_END)
0919         die("Device tree blob doesn't end with FDT_END\n");
0920 
0921     free(blob);
0922 
0923     fclose(f);
0924 
0925     return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys);
0926 }