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