blob: c7817c60586c4c4a636649194a20a10046d13088 [file] [log] [blame]
David Gibsonfc14dad2005-06-08 17:18:34 +10001/*
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"
David Gibsonab870ca2005-06-23 15:45:13 +100022#include "flat_dt.h"
David Gibsonfc14dad2005-06-08 17:18:34 +100023
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
David Gibson46c88df2007-03-14 11:02:40 +110029#define FTF_STRUCTSIZE 0x20
Milton Millerce243222007-06-09 23:21:31 -050030#define FTF_NOPS 0x40
David Gibsonfc14dad2005-06-08 17:18:34 +100031
David Gibson230f2532005-08-29 12:48:02 +100032static struct version_info {
David Gibsonfc14dad2005-06-08 17:18:34 +100033 int version;
34 int last_comp_version;
35 int hdr_size;
36 int flags;
37} version_table[] = {
38 {1, 1, BPH_V1_SIZE,
39 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS},
40 {2, 1, BPH_V2_SIZE,
41 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID},
42 {3, 1, BPH_V3_SIZE,
43 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE},
David Gibson46c88df2007-03-14 11:02:40 +110044 {16, 16, BPH_V3_SIZE,
Milton Millerce243222007-06-09 23:21:31 -050045 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_NOPS},
David Gibson46c88df2007-03-14 11:02:40 +110046 {17, 16, BPH_V17_SIZE,
Milton Millerce243222007-06-09 23:21:31 -050047 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS},
David Gibsonfc14dad2005-06-08 17:18:34 +100048};
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);
David Gibson4102d842005-06-16 14:36:37 +100055 void (*beginnode)(void *, char *);
56 void (*endnode)(void *, char *);
57 void (*property)(void *, char *);
David Gibsonfc14dad2005-06-08 17:18:34 +100058};
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
David Gibson4102d842005-06-16 14:36:37 +100092static void bin_emit_beginnode(void *e, char *label)
David Gibsonfc14dad2005-06-08 17:18:34 +100093{
94 bin_emit_cell(e, OF_DT_BEGIN_NODE);
95}
96
David Gibson4102d842005-06-16 14:36:37 +100097static void bin_emit_endnode(void *e, char *label)
David Gibsonfc14dad2005-06-08 17:18:34 +100098{
99 bin_emit_cell(e, OF_DT_END_NODE);
100}
101
David Gibson4102d842005-06-16 14:36:37 +1000102static void bin_emit_property(void *e, char *label)
David Gibsonfc14dad2005-06-08 17:18:34 +1000103{
104 bin_emit_cell(e, OF_DT_PROP);
105}
106
David Gibson230f2532005-08-29 12:48:02 +1000107static struct emitter bin_emitter = {
David Gibsonfc14dad2005-06-08 17:18:34 +1000108 .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
David Gibson230f2532005-08-29 12:48:02 +1000117static void emit_label(FILE *f, char *prefix, char *label)
David Gibson4102d842005-06-16 14:36:37 +1000118{
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
Milton Miller6a99b132007-07-07 01:18:51 -0500124static void emit_offset_label(FILE *f, 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
David Gibsonfc14dad2005-06-08 17:18:34 +1000130static void asm_emit_cell(void *e, cell_t val)
131{
132 FILE *f = e;
133
Mark A. Greer7a9f6632006-03-15 18:59:24 -0700134 fprintf(f, "\t.long\t0x%x\n", val);
David Gibsonfc14dad2005-06-08 17:18:34 +1000135}
136
137static void asm_emit_string(void *e, char *str, int len)
138{
139 FILE *f = e;
140 char c;
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;
Milton Miller6a99b132007-07-07 01:18:51 -0500166 struct fixup *l;
167
168 l = d.labels;
169 while (l) {
170 emit_offset_label(f, l->ref, l->offset);
171 l = l->next;
172 }
David Gibsonfc14dad2005-06-08 17:18:34 +1000173
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
David Gibson4102d842005-06-16 14:36:37 +1000194static void asm_emit_beginnode(void *e, char *label)
David Gibsonfc14dad2005-06-08 17:18:34 +1000195{
196 FILE *f = e;
197
David Gibson4102d842005-06-16 14:36:37 +1000198 if (label) {
199 fprintf(f, "\t.globl\t%s\n", label);
200 fprintf(f, "%s:\n", label);
201 }
David Gibsonfc14dad2005-06-08 17:18:34 +1000202 fprintf(f, "\t.long\tOF_DT_BEGIN_NODE\n");
203}
204
David Gibson4102d842005-06-16 14:36:37 +1000205static void asm_emit_endnode(void *e, char *label)
David Gibsonfc14dad2005-06-08 17:18:34 +1000206{
207 FILE *f = e;
208
209 fprintf(f, "\t.long\tOF_DT_END_NODE\n");
David Gibson4102d842005-06-16 14:36:37 +1000210 if (label) {
211 fprintf(f, "\t.globl\t%s_end\n", label);
212 fprintf(f, "%s_end:\n", label);
213 }
David Gibsonfc14dad2005-06-08 17:18:34 +1000214}
215
David Gibson4102d842005-06-16 14:36:37 +1000216static void asm_emit_property(void *e, char *label)
David Gibsonfc14dad2005-06-08 17:18:34 +1000217{
218 FILE *f = e;
219
David Gibson4102d842005-06-16 14:36:37 +1000220 if (label) {
221 fprintf(f, "\t.globl\t%s\n", label);
222 fprintf(f, "%s:\n", label);
223 }
David Gibsonfc14dad2005-06-08 17:18:34 +1000224 fprintf(f, "\t.long\tOF_DT_PROP\n");
225}
226
David Gibson230f2532005-08-29 12:48:02 +1000227static struct emitter asm_emitter = {
David Gibsonfc14dad2005-06-08 17:18:34 +1000228 .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, 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);
David Gibsona6c69572005-07-11 17:09:42 +1000249 return i;
David Gibsonfc14dad2005-06-08 17:18:34 +1000250}
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
David Gibson4102d842005-06-16 14:36:37 +1000260 emit->beginnode(etarget, tree->label);
David Gibsonfc14dad2005-06-08 17:18:34 +1000261
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
David Gibson4102d842005-06-16 14:36:37 +1000277 emit->property(etarget, prop->label);
David Gibsonfc14dad2005-06-08 17:18:34 +1000278 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) {
David Gibson4102d842005-06-16 14:36:37 +1000289 emit->property(etarget, NULL);
David Gibsonfc14dad2005-06-08 17:18:34 +1000290 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);
David Gibson41916132005-08-25 15:39:09 +1000297 emit->align(etarget, sizeof(cell_t));
David Gibsonfc14dad2005-06-08 17:18:34 +1000298 }
299
300 for_each_child(tree, child) {
301 flatten_tree(child, emit, etarget, strbuf, vi);
302 }
303
David Gibson4102d842005-06-16 14:36:37 +1000304 emit->endnode(etarget, tree->label);
David Gibsonfc14dad2005-06-08 17:18:34 +1000305}
306
David Gibsonf040d952005-10-24 18:18:38 +1000307static 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;
Jerry Van Baren4384b232007-04-04 22:04:33 -0400312 static struct reserve_entry null_re = {0,0};
313 int j;
David Gibsonf040d952005-10-24 18:18:38 +1000314
315 for (re = reservelist; re; re = re->next) {
316 d = data_append_re(d, &re->re);
317 }
Jerry Van Baren4384b232007-04-04 22:04:33 -0400318 /*
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 }
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400324
David Gibsonf040d952005-10-24 18:18:38 +1000325 return d;
326}
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400327
David Gibsonfc14dad2005-06-08 17:18:34 +1000328static void make_bph(struct boot_param_header *bph,
David Gibsonf0517db2005-07-15 17:14:24 +1000329 struct version_info *vi,
Michael Neuling38e8f8f2006-05-31 08:31:51 +1000330 int reservesize, int dtsize, int strsize,
331 int boot_cpuid_phys)
David Gibsonfc14dad2005-06-08 17:18:34 +1000332{
David Gibson47f23de2005-07-11 17:19:26 +1000333 int reserve_off;
David Gibsonf040d952005-10-24 18:18:38 +1000334
335 reservesize += sizeof(struct reserve_entry);
David Gibsonfc14dad2005-06-08 17:18:34 +1000336
337 memset(bph, 0xff, sizeof(*bph));
338
339 bph->magic = cpu_to_be32(OF_DT_HEADER);
David Gibsondffc2a82005-08-25 14:47:20 +1000340 bph->version = cpu_to_be32(vi->version);
341 bph->last_comp_version = cpu_to_be32(vi->last_comp_version);
David Gibsonfc14dad2005-06-08 17:18:34 +1000342
David Gibson47f23de2005-07-11 17:19:26 +1000343 /* Reserve map should be doubleword aligned */
344 reserve_off = ALIGN(vi->hdr_size, 8);
345
346 bph->off_mem_rsvmap = cpu_to_be32(reserve_off);
347 bph->off_dt_struct = cpu_to_be32(reserve_off + reservesize);
348 bph->off_dt_strings = cpu_to_be32(reserve_off + reservesize
David Gibsonfc14dad2005-06-08 17:18:34 +1000349 + dtsize);
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400350 bph->totalsize = cpu_to_be32(reserve_off + reservesize + dtsize + strsize);
Jerry Van Baren4384b232007-04-04 22:04:33 -0400351
David Gibsonfc14dad2005-06-08 17:18:34 +1000352 if (vi->flags & FTF_BOOTCPUID)
Michael Neuling38e8f8f2006-05-31 08:31:51 +1000353 bph->boot_cpuid_phys = cpu_to_be32(boot_cpuid_phys);
David Gibsonfc14dad2005-06-08 17:18:34 +1000354 if (vi->flags & FTF_STRTABSIZE)
355 bph->size_dt_strings = cpu_to_be32(strsize);
David Gibson46c88df2007-03-14 11:02:40 +1100356 if (vi->flags & FTF_STRUCTSIZE)
357 bph->size_dt_struct = cpu_to_be32(dtsize);
David Gibsonfc14dad2005-06-08 17:18:34 +1000358}
359
Michael Neuling38e8f8f2006-05-31 08:31:51 +1000360void dt_to_blob(FILE *f, struct boot_info *bi, int version,
361 int boot_cpuid_phys)
David Gibsonfc14dad2005-06-08 17:18:34 +1000362{
363 struct version_info *vi = NULL;
364 int i;
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400365 struct data blob = empty_data;
366 struct data reservebuf = empty_data;
367 struct data dtbuf = empty_data;
368 struct data strbuf = empty_data;
David Gibsonfc14dad2005-06-08 17:18:34 +1000369 struct boot_param_header bph;
Jerry Van Baren7ea144f2007-04-19 22:22:35 -0400370 int padlen;
David Gibsonfc14dad2005-06-08 17:18:34 +1000371
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
David Gibsonf0517db2005-07-15 17:14:24 +1000379 flatten_tree(bi->dt, &bin_emitter, &dtbuf, &strbuf, vi);
David Gibsonfc14dad2005-06-08 17:18:34 +1000380 bin_emit_cell(&dtbuf, OF_DT_END);
381
David Gibsonf040d952005-10-24 18:18:38 +1000382 reservebuf = flatten_reserve_list(bi->reservelist, vi);
383
David Gibsonf0517db2005-07-15 17:14:24 +1000384 /* Make header */
Michael Neuling38e8f8f2006-05-31 08:31:51 +1000385 make_bph(&bph, vi, reservebuf.len, dtbuf.len, strbuf.len,
386 boot_cpuid_phys);
David Gibsonfc14dad2005-06-08 17:18:34 +1000387
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400388 /*
Jerry Van Baren7ea144f2007-04-19 22:22:35 -0400389 * If the user asked for more space than is used, adjust the totalsize.
390 */
391 padlen = minsize - be32_to_cpu(bph.totalsize);
392 if (padlen > 0) {
393 bph.totalsize = cpu_to_be32(minsize);
394 } else {
395 if ((minsize > 0) && (quiet < 1))
396 fprintf(stderr,
397 "Warning: blob size %d >= minimum size %d\n",
398 be32_to_cpu(bph.totalsize), minsize);
399 }
400
401 /*
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400402 * Assemble the blob: start with the header, add with alignment
403 * the reserve buffer, add the reserve map terminating zeroes,
404 * the device tree itself, and finally the strings.
405 */
406 blob = data_append_data(blob, &bph, sizeof(bph));
407 blob = data_append_align(blob, 8);
408 blob = data_merge(blob, reservebuf);
409 blob = data_append_zeroes(blob, sizeof(struct reserve_entry));
410 blob = data_merge(blob, dtbuf);
411 blob = data_merge(blob, strbuf);
David Gibson47f23de2005-07-11 17:19:26 +1000412
David Gibsonf0517db2005-07-15 17:14:24 +1000413 /*
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400414 * If the user asked for more space than is used, pad out the blob.
David Gibsonf0517db2005-07-15 17:14:24 +1000415 */
Jerry Van Baren7ea144f2007-04-19 22:22:35 -0400416 if (padlen > 0) {
417 blob = data_append_zeroes(blob, padlen);
418 bph.totalsize = cpu_to_be32(minsize);
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400419 }
420
421 fwrite(blob.val, blob.len, 1, f);
David Gibsonfc14dad2005-06-08 17:18:34 +1000422
423 if (ferror(f))
424 die("Error writing device tree blob: %s\n", strerror(errno));
425
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400426 /*
427 * data_merge() frees the right-hand element so only the blob
428 * remains to be freed.
429 */
430 data_free(blob);
David Gibsonfc14dad2005-06-08 17:18:34 +1000431}
432
David Gibson230f2532005-08-29 12:48:02 +1000433static void dump_stringtable_asm(FILE *f, struct data strbuf)
David Gibsonfc14dad2005-06-08 17:18:34 +1000434{
435 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
Michael Neuling38e8f8f2006-05-31 08:31:51 +1000447void dt_to_asm(FILE *f, struct boot_info *bi, int version, int boot_cpuid_phys)
David Gibsonfc14dad2005-06-08 17:18:34 +1000448{
449 struct version_info *vi = NULL;
450 int i;
451 struct data strbuf = empty_data;
David Gibsonf040d952005-10-24 18:18:38 +1000452 struct reserve_info *re;
David Gibsonfc14dad2005-06-08 17:18:34 +1000453 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 OF_DT_HEADER 0x%x\n", OF_DT_HEADER);
464 fprintf(f, "#define OF_DT_BEGIN_NODE 0x%x\n", OF_DT_BEGIN_NODE);
465 fprintf(f, "#define OF_DT_END_NODE 0x%x\n", OF_DT_END_NODE);
466 fprintf(f, "#define OF_DT_PROP 0x%x\n", OF_DT_PROP);
467 fprintf(f, "#define OF_DT_END 0x%x\n", OF_DT_END);
468 fprintf(f, "\n");
469
470 emit_label(f, symprefix, "blob_start");
471 emit_label(f, symprefix, "header");
472 fprintf(f, "\t.long\tOF_DT_HEADER /* magic */\n");
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400473 fprintf(f, "\t.long\t_%s_blob_abs_end - _%s_blob_start /* totalsize */\n",
David Gibsonfc14dad2005-06-08 17:18:34 +1000474 symprefix, symprefix);
475 fprintf(f, "\t.long\t_%s_struct_start - _%s_blob_start /* off_dt_struct */\n",
476 symprefix, symprefix);
477 fprintf(f, "\t.long\t_%s_strings_start - _%s_blob_start /* off_dt_strings */\n",
478 symprefix, symprefix);
479 fprintf(f, "\t.long\t_%s_reserve_map - _%s_blob_start /* off_dt_strings */\n",
480 symprefix, symprefix);
481 fprintf(f, "\t.long\t%d /* version */\n", vi->version);
482 fprintf(f, "\t.long\t%d /* last_comp_version */\n",
483 vi->last_comp_version);
484
485 if (vi->flags & FTF_BOOTCPUID)
Michael Neuling38e8f8f2006-05-31 08:31:51 +1000486 fprintf(f, "\t.long\t%i\t/*boot_cpuid_phys*/\n",
487 boot_cpuid_phys);
David Gibsonfc14dad2005-06-08 17:18:34 +1000488
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
Milton Miller81fda8a2007-07-07 01:18:47 -0500493 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
David Gibsonf0517db2005-07-15 17:14:24 +1000497 /*
498 * Reserve map entries.
499 * Align the reserve map to a doubleword boundary.
500 * Each entry is an (address, size) pair of u64 values.
David Gibsonf0517db2005-07-15 17:14:24 +1000501 * Always supply a zero-sized temination entry.
502 */
David Gibson47f23de2005-07-11 17:19:26 +1000503 asm_emit_align(f, 8);
David Gibsonfc14dad2005-06-08 17:18:34 +1000504 emit_label(f, symprefix, "reserve_map");
David Gibsonf0517db2005-07-15 17:14:24 +1000505
David Gibsonf040d952005-10-24 18:18:38 +1000506 fprintf(f, "/* Memory reserve map from source file */\n");
Jon Loeliger05ae3d82006-04-19 11:58:45 -0500507
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 */
David Gibsonf040d952005-10-24 18:18:38 +1000512 for (re = bi->reservelist; re; re = re->next) {
Milton Millerd4290332007-07-07 01:18:49 -0500513 if (re->label) {
514 fprintf(f, "\t.globl\t%s\n", re->label);
515 fprintf(f, "%s:\n", re->label);
516 }
Jon Loeliger05ae3d82006-04-19 11:58:45 -0500517 fprintf(f, "\t.long\t0x%08x\n\t.long\t0x%08x\n",
518 (unsigned int)(re->re.address >> 32),
519 (unsigned int)(re->re.address & 0xffffffff));
520 fprintf(f, "\t.long\t0x%08x\n\t.long\t0x%08x\n",
521 (unsigned int)(re->re.size >> 32),
522 (unsigned int)(re->re.size & 0xffffffff));
David Gibsonfc14dad2005-06-08 17:18:34 +1000523 }
Jerry Van Barenca25e542007-04-17 18:14:41 -0400524 for (i = 0; i < reservenum; i++) {
525 fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
526 }
David Gibsonfc14dad2005-06-08 17:18:34 +1000527
Jon Loeliger05ae3d82006-04-19 11:58:45 -0500528 fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
David Gibsonf0517db2005-07-15 17:14:24 +1000529
David Gibsonfc14dad2005-06-08 17:18:34 +1000530 emit_label(f, symprefix, "struct_start");
David Gibsonf0517db2005-07-15 17:14:24 +1000531 flatten_tree(bi->dt, &asm_emitter, f, &strbuf, vi);
David Gibsonfc14dad2005-06-08 17:18:34 +1000532 fprintf(f, "\t.long\tOF_DT_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
Jerry Van Baren86c01ee2007-04-18 21:59:51 -0400541 /*
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 emit_label(f, symprefix, "blob_abs_end");
549
David Gibsonfc14dad2005-06-08 17:18:34 +1000550 data_free(strbuf);
551}
552
553struct inbuf {
554 char *base, *limit, *ptr;
555};
556
557static void inbuf_init(struct inbuf *inb, void *base, void *limit)
558{
559 inb->base = base;
560 inb->limit = limit;
561 inb->ptr = inb->base;
562}
563
564static void flat_read_chunk(struct inbuf *inb, void *p, int len)
565{
566 if ((inb->ptr + len) > inb->limit)
567 die("Premature end of data parsing flat device tree\n");
568
569 memcpy(p, inb->ptr, len);
570
571 inb->ptr += len;
572}
573
574static u32 flat_read_word(struct inbuf *inb)
575{
576 u32 val;
577
578 assert(((inb->ptr - inb->base) % sizeof(val)) == 0);
579
580 flat_read_chunk(inb, &val, sizeof(val));
581
582 return be32_to_cpu(val);
583}
584
585static void flat_realign(struct inbuf *inb, int align)
586{
587 int off = inb->ptr - inb->base;
588
589 inb->ptr = inb->base + ALIGN(off, align);
590 if (inb->ptr > inb->limit)
591 die("Premature end of data parsing flat device tree\n");
592}
593
594static char *flat_read_string(struct inbuf *inb)
595{
596 int len = 0;
597 char *p = inb->ptr;
598 char *str;
599
600 do {
601 if (p >= inb->limit)
602 die("Premature end of data parsing flat device tree\n");
603 len++;
604 } while ((*p++) != '\0');
605
606 str = strdup(inb->ptr);
607
608 inb->ptr += len;
609
610 flat_realign(inb, sizeof(u32));
611
612 return str;
613}
614
615static struct data flat_read_data(struct inbuf *inb, int len)
616{
617 struct data d = empty_data;
618
619 if (len == 0)
620 return empty_data;
621
622 d = data_grow_for(d, len);
623 d.len = len;
624
625 flat_read_chunk(inb, d.val, len);
626
627 flat_realign(inb, sizeof(u32));
628
629 return d;
630}
631
632static char *flat_read_stringtable(struct inbuf *inb, int offset)
633{
634 char *p;
635
636 p = inb->base + offset;
637 while (1) {
David Gibson4ddf7c02005-08-19 16:11:11 +1000638 if (p >= inb->limit || p < inb->base)
David Gibson7ee3ffd2005-07-11 16:45:57 +1000639 die("String offset %d overruns string table\n",
640 offset);
David Gibsonfc14dad2005-06-08 17:18:34 +1000641
642 if (*p == '\0')
643 break;
644
645 p++;
646 }
647
648 return strdup(inb->base + offset);
649}
650
David Gibson230f2532005-08-29 12:48:02 +1000651static struct property *flat_read_property(struct inbuf *dtbuf,
652 struct inbuf *strbuf, int flags)
David Gibsonfc14dad2005-06-08 17:18:34 +1000653{
654 u32 proplen, stroff;
655 char *name;
656 struct data val;
657
658 proplen = flat_read_word(dtbuf);
659 stroff = flat_read_word(dtbuf);
660
661 name = flat_read_stringtable(strbuf, stroff);
662
663 if ((flags & FTF_VARALIGN) && (proplen >= 8))
664 flat_realign(dtbuf, 8);
665
666 val = flat_read_data(dtbuf, proplen);
667
David Gibson4102d842005-06-16 14:36:37 +1000668 return build_property(name, val, NULL);
David Gibsonfc14dad2005-06-08 17:18:34 +1000669}
670
David Gibsonf0517db2005-07-15 17:14:24 +1000671
David Gibsonf040d952005-10-24 18:18:38 +1000672static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
David Gibsonf0517db2005-07-15 17:14:24 +1000673{
David Gibsonf040d952005-10-24 18:18:38 +1000674 struct reserve_info *reservelist = NULL;
675 struct reserve_info *new;
David Gibsonf0517db2005-07-15 17:14:24 +1000676 char *p;
David Gibson6c0f3672005-08-29 13:36:15 +1000677 struct reserve_entry re;
David Gibsonf0517db2005-07-15 17:14:24 +1000678
679 /*
680 * Each entry is a pair of u64 (addr, size) values for 4 cell_t's.
681 * List terminates at an entry with size equal to zero.
682 *
683 * First pass, count entries.
684 */
685 p = inb->ptr;
David Gibson6c0f3672005-08-29 13:36:15 +1000686 while (1) {
687 flat_read_chunk(inb, &re, sizeof(re));
Michael Neuling332c5362006-07-07 23:28:10 +1000688 re.address = be64_to_cpu(re.address);
689 re.size = be64_to_cpu(re.size);
David Gibson6c0f3672005-08-29 13:36:15 +1000690 if (re.size == 0)
691 break;
David Gibsonf0517db2005-07-15 17:14:24 +1000692
David Gibsonf040d952005-10-24 18:18:38 +1000693 new = build_reserve_entry(re.address, re.size, NULL);
694 reservelist = add_reserve_entry(reservelist, new);
David Gibson6c0f3672005-08-29 13:36:15 +1000695 }
David Gibsonf0517db2005-07-15 17:14:24 +1000696
David Gibsonf040d952005-10-24 18:18:38 +1000697 return reservelist;
David Gibsonf0517db2005-07-15 17:14:24 +1000698}
699
700
David Gibsonfc14dad2005-06-08 17:18:34 +1000701static char *nodename_from_path(char *ppath, char *cpath)
702{
703 char *lslash;
704 int plen;
705
706 lslash = strrchr(cpath, '/');
707 if (! lslash)
708 return NULL;
709
710 plen = lslash - cpath;
711
712 if (streq(cpath, "/") && streq(ppath, ""))
713 return "";
714
715 if ((plen == 0) && streq(ppath, "/"))
716 return strdup(lslash+1);
717
David Gibson81f2e892005-06-16 17:04:00 +1000718 if (! strneq(ppath, cpath, plen))
David Gibsonfc14dad2005-06-08 17:18:34 +1000719 return NULL;
720
721 return strdup(lslash+1);
722}
723
724static const char PROPCHAR[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,._+*#?-";
725static const char UNITCHAR[] = "0123456789abcdef,";
726
727static int check_node_name(char *name)
728{
729 char *atpos;
730 int basenamelen;
731
732 atpos = strrchr(name, '@');
733
734 if (atpos)
735 basenamelen = atpos - name;
736 else
737 basenamelen = strlen(name);
738
739 if (strspn(name, PROPCHAR) < basenamelen)
740 return -1;
741
742 if (atpos
743 && ((basenamelen + 1 + strspn(atpos+1, UNITCHAR)) < strlen(name)))
744 return -1;
745
746 return basenamelen;
747}
748
749static struct node *unflatten_tree(struct inbuf *dtbuf,
750 struct inbuf *strbuf,
751 char *parent_path, int flags)
752{
753 struct node *node;
754 u32 val;
755
756 node = build_node(NULL, NULL);
757
758 if (flags & FTF_FULLPATH) {
759 node->fullpath = flat_read_string(dtbuf);
760 node->name = nodename_from_path(parent_path, node->fullpath);
761
762 if (! node->name)
763 die("Path \"%s\" is not valid as a child of \"%s\"\n",
764 node->fullpath, parent_path);
765 } else {
766 node->name = flat_read_string(dtbuf);
767 node->fullpath = join_path(parent_path, node->name);
768 }
769
770 node->basenamelen = check_node_name(node->name);
771 if (node->basenamelen < 0) {
772 fprintf(stderr, "Warning \"%s\" has incorrect format\n", node->name);
773 }
774
775 do {
776 struct property *prop;
777 struct node *child;
778
779 val = flat_read_word(dtbuf);
780 switch (val) {
781 case OF_DT_PROP:
782 prop = flat_read_property(dtbuf, strbuf, flags);
783 add_property(node, prop);
784 break;
785
786 case OF_DT_BEGIN_NODE:
787 child = unflatten_tree(dtbuf,strbuf, node->fullpath,
788 flags);
789 add_child(node, child);
790 break;
791
792 case OF_DT_END_NODE:
793 break;
794
795 case OF_DT_END:
796 die("Premature OF_DT_END in device tree blob\n");
797 break;
798
Milton Millerce243222007-06-09 23:21:31 -0500799 case OF_DT_NOP:
David Gibson07387742007-06-26 11:30:47 +1000800 if (!(flags & FTF_NOPS))
801 fprintf(stderr, "Warning: NOP tag found in flat tree"
802 " version <16\n");
Milton Millerce243222007-06-09 23:21:31 -0500803
David Gibson07387742007-06-26 11:30:47 +1000804 /* Ignore */
Milton Millerce243222007-06-09 23:21:31 -0500805 break;
806
David Gibsonfc14dad2005-06-08 17:18:34 +1000807 default:
808 die("Invalid opcode word %08x in device tree blob\n",
809 val);
810 }
811 } while (val != OF_DT_END_NODE);
812
813 return node;
814}
815
David Gibsonf0517db2005-07-15 17:14:24 +1000816
817struct boot_info *dt_from_blob(FILE *f)
David Gibsonfc14dad2005-06-08 17:18:34 +1000818{
David Gibson46c88df2007-03-14 11:02:40 +1100819 u32 magic, totalsize, version, size_str, size_dt;
David Gibsonf0517db2005-07-15 17:14:24 +1000820 u32 off_dt, off_str, off_mem_rsvmap;
David Gibsonfc14dad2005-06-08 17:18:34 +1000821 int rc;
822 char *blob;
823 struct boot_param_header *bph;
824 char *p;
825 struct inbuf dtbuf, strbuf;
David Gibsonf0517db2005-07-15 17:14:24 +1000826 struct inbuf memresvbuf;
David Gibsonfc14dad2005-06-08 17:18:34 +1000827 int sizeleft;
David Gibsonf040d952005-10-24 18:18:38 +1000828 struct reserve_info *reservelist;
David Gibsonfc14dad2005-06-08 17:18:34 +1000829 struct node *tree;
830 u32 val;
831 int flags = 0;
832
833 rc = fread(&magic, sizeof(magic), 1, f);
834 if (ferror(f))
835 die("Error reading DT blob magic number: %s\n",
836 strerror(errno));
837 if (rc < 1) {
838 if (feof(f))
839 die("EOF reading DT blob magic number\n");
840 else
841 die("Mysterious short read reading magic number\n");
842 }
843
844 magic = be32_to_cpu(magic);
845 if (magic != OF_DT_HEADER)
846 die("Blob has incorrect magic number\n");
847
848 rc = fread(&totalsize, sizeof(totalsize), 1, f);
849 if (ferror(f))
850 die("Error reading DT blob size: %s\n", strerror(errno));
851 if (rc < 1) {
852 if (feof(f))
853 die("EOF reading DT blob size\n");
854 else
855 die("Mysterious short read reading blob size\n");
856 }
857
858 totalsize = be32_to_cpu(totalsize);
859 if (totalsize < BPH_V1_SIZE)
860 die("DT blob size (%d) is too small\n", totalsize);
861
862 blob = xmalloc(totalsize);
863
864 bph = (struct boot_param_header *)blob;
865 bph->magic = cpu_to_be32(magic);
866 bph->totalsize = cpu_to_be32(totalsize);
867
868 sizeleft = totalsize - sizeof(magic) - sizeof(totalsize);
869 p = blob + sizeof(magic) + sizeof(totalsize);
870
871 while (sizeleft) {
872 if (feof(f))
873 die("EOF before reading %d bytes of DT blob\n",
874 totalsize);
875
876 rc = fread(p, 1, sizeleft, f);
877 if (ferror(f))
878 die("Error reading DT blob: %s\n",
879 strerror(errno));
880
881 sizeleft -= rc;
882 p += rc;
883 }
884
885 off_dt = be32_to_cpu(bph->off_dt_struct);
886 off_str = be32_to_cpu(bph->off_dt_strings);
David Gibsonf0517db2005-07-15 17:14:24 +1000887 off_mem_rsvmap = be32_to_cpu(bph->off_mem_rsvmap);
David Gibsonfc14dad2005-06-08 17:18:34 +1000888 version = be32_to_cpu(bph->version);
889
890 fprintf(stderr, "\tmagic:\t\t\t0x%x\n", magic);
891 fprintf(stderr, "\ttotalsize:\t\t%d\n", totalsize);
892 fprintf(stderr, "\toff_dt_struct:\t\t0x%x\n", off_dt);
893 fprintf(stderr, "\toff_dt_strings:\t\t0x%x\n", off_str);
David Gibsonf0517db2005-07-15 17:14:24 +1000894 fprintf(stderr, "\toff_mem_rsvmap:\t\t0x%x\n", off_mem_rsvmap);
David Gibsonfc14dad2005-06-08 17:18:34 +1000895 fprintf(stderr, "\tversion:\t\t0x%x\n", version );
896 fprintf(stderr, "\tlast_comp_version:\t0x%x\n",
897 be32_to_cpu(bph->last_comp_version));
898
David Gibsonf0517db2005-07-15 17:14:24 +1000899 if (off_mem_rsvmap >= totalsize)
900 die("Mem Reserve structure offset exceeds total size\n");
901
David Gibsonfc14dad2005-06-08 17:18:34 +1000902 if (off_dt >= totalsize)
903 die("DT structure offset exceeds total size\n");
904
905 if (off_str > totalsize)
906 die("String table offset exceeds total size\n");
907
908 if (version >= 2)
909 fprintf(stderr, "\tboot_cpuid_phys:\t0x%x\n",
910 be32_to_cpu(bph->boot_cpuid_phys));
911
912 if (version >= 3) {
913 size_str = be32_to_cpu(bph->size_dt_strings);
914 fprintf(stderr, "\tsize_dt_strings:\t%d\n", size_str);
915 if (off_str+size_str > totalsize)
916 die("String table extends past total size\n");
917 }
David Gibson46c88df2007-03-14 11:02:40 +1100918
919 if (version >= 17) {
920 size_dt = be32_to_cpu(bph->size_dt_struct);
921 fprintf(stderr, "\tsize_dt_struct:\t\t%d\n", size_dt);
922 if (off_dt+size_dt > totalsize)
923 die("Structure block extends past total size\n");
924 }
David Gibsonfc14dad2005-06-08 17:18:34 +1000925
David Gibson46c88df2007-03-14 11:02:40 +1100926 if (version < 16) {
David Gibsonfc14dad2005-06-08 17:18:34 +1000927 flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN;
Milton Millerce243222007-06-09 23:21:31 -0500928 } else {
929 flags |= FTF_NOPS;
David Gibsonfc14dad2005-06-08 17:18:34 +1000930 }
931
David Gibsonf0517db2005-07-15 17:14:24 +1000932 inbuf_init(&memresvbuf,
933 blob + off_mem_rsvmap, blob + totalsize);
David Gibsonfc14dad2005-06-08 17:18:34 +1000934 inbuf_init(&dtbuf, blob + off_dt, blob + totalsize);
935 inbuf_init(&strbuf, blob + off_str, blob + totalsize);
936
937 if (version >= 3)
938 strbuf.limit = strbuf.base + size_str;
939
David Gibsonf040d952005-10-24 18:18:38 +1000940 reservelist = flat_read_mem_reserve(&memresvbuf);
David Gibsonf0517db2005-07-15 17:14:24 +1000941
David Gibsonfc14dad2005-06-08 17:18:34 +1000942 val = flat_read_word(&dtbuf);
943
944 if (val != OF_DT_BEGIN_NODE)
David Gibson41916132005-08-25 15:39:09 +1000945 die("Device tree blob doesn't begin with OF_DT_BEGIN_NODE (begins with 0x%08x)\n", val);
David Gibsonfc14dad2005-06-08 17:18:34 +1000946
947 tree = unflatten_tree(&dtbuf, &strbuf, "", flags);
948
949 val = flat_read_word(&dtbuf);
950 if (val != OF_DT_END)
951 die("Device tree blob doesn't end with OF_DT_END\n");
952
953 free(blob);
954
David Gibsonf040d952005-10-24 18:18:38 +1000955 return build_boot_info(reservelist, tree);
David Gibsonfc14dad2005-06-08 17:18:34 +1000956}