]>
Commit | Line | Data |
---|---|---|
6915dcf3 AG |
1 | /* |
2 | * Copyright (C) 2018 Alexander Graf <[email protected]> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include "imagetool.h" | |
8 | #include "mkimage.h" | |
9 | #include "zynqmpimage.h" | |
10 | #include <elf.h> | |
11 | #include <image.h> | |
12 | ||
13 | struct bif_entry { | |
14 | const char *filename; | |
15 | uint64_t flags; | |
16 | uint64_t dest_cpu; | |
17 | uint64_t exp_lvl; | |
18 | uint64_t dest_dev; | |
19 | uint64_t load; | |
20 | uint64_t entry; | |
21 | size_t offset; | |
22 | }; | |
23 | ||
24 | enum bif_flag { | |
25 | BIF_FLAG_AESKEYFILE, | |
26 | BIF_FLAG_INIT, | |
27 | BIF_FLAG_UDF_BH, | |
28 | BIF_FLAG_HEADERSIGNATURE, | |
29 | BIF_FLAG_PPKFILE, | |
30 | BIF_FLAG_PSKFILE, | |
31 | BIF_FLAG_SPKFILE, | |
32 | BIF_FLAG_SSKFILE, | |
33 | BIF_FLAG_SPKSIGNATURE, | |
34 | BIF_FLAG_FSBL_CONFIG, | |
35 | BIF_FLAG_AUTH_PARAMS, | |
36 | BIF_FLAG_KEYSRC_ENCRYPTION, | |
37 | BIF_FLAG_PMUFW_IMAGE, | |
38 | BIF_FLAG_BOOTLOADER, | |
39 | BIF_FLAG_TZ, | |
40 | BIF_FLAG_BH_KEY_IV, | |
41 | BIF_FLAG_BH_KEYFILE, | |
42 | BIF_FLAG_PUF_FILE, | |
43 | BIF_FLAG_AARCH32, | |
44 | BIF_FLAG_PART_OWNER_UBOOT, | |
45 | ||
46 | /* Internal flags */ | |
47 | BIF_FLAG_BIT_FILE, | |
48 | BIF_FLAG_ELF_FILE, | |
49 | BIF_FLAG_BIN_FILE, | |
50 | }; | |
51 | ||
52 | struct bif_flags { | |
53 | const char name[32]; | |
54 | uint64_t flag; | |
55 | char *(*parse)(char *line, struct bif_entry *bf); | |
56 | }; | |
57 | ||
58 | struct bif_file_type { | |
59 | const char name[32]; | |
60 | uint32_t header; | |
61 | int (*add)(struct bif_entry *bf); | |
62 | }; | |
63 | ||
64 | struct bif_output { | |
65 | size_t data_len; | |
66 | char *data; | |
67 | struct image_header_table *imgheader; | |
68 | struct zynqmp_header *header; | |
69 | struct partition_header *last_part; | |
70 | }; | |
71 | ||
72 | struct bif_output bif_output; | |
73 | ||
74 | static uint32_t zynqmp_csum(void *start, void *end) | |
75 | { | |
76 | uint32_t checksum = 0; | |
77 | uint32_t *ptr32 = start; | |
78 | ||
79 | while (ptr32 != end) { | |
80 | checksum += le32_to_cpu(*ptr32); | |
81 | ptr32++; | |
82 | } | |
83 | ||
84 | return ~checksum; | |
85 | } | |
86 | ||
87 | static int zynqmpbif_check_params(struct image_tool_params *params) | |
88 | { | |
89 | if (!params) | |
90 | return 0; | |
91 | ||
92 | if (params->addr != 0x0) { | |
93 | fprintf(stderr, "Error: Load Address can not be specified.\n"); | |
94 | return -1; | |
95 | } | |
96 | ||
97 | if (params->eflag) { | |
98 | fprintf(stderr, "Error: Entry Point can not be specified.\n"); | |
99 | return -1; | |
100 | } | |
101 | ||
102 | return !(params->lflag || params->dflag); | |
103 | } | |
104 | ||
105 | static int zynqmpbif_check_image_types(uint8_t type) | |
106 | { | |
107 | return (type == IH_TYPE_ZYNQMPBIF) ? EXIT_SUCCESS : EXIT_FAILURE; | |
108 | } | |
109 | ||
110 | static char *parse_dest_cpu(char *line, struct bif_entry *bf) | |
111 | { | |
112 | uint64_t i; | |
113 | ||
114 | for (i = 0; i < ARRAY_SIZE(dest_cpus); i++) { | |
115 | if (!strncmp(line, dest_cpus[i], strlen(dest_cpus[i]))) { | |
116 | bf->dest_cpu = i << PART_ATTR_DEST_CPU_SHIFT; | |
117 | return line + strlen(dest_cpus[i]); | |
118 | } | |
119 | ||
120 | /* a5x can also be written as a53 */ | |
121 | if (!strncmp(dest_cpus[i], "a5x", 3)) { | |
122 | char a53[] = "a53-X"; | |
123 | ||
124 | a53[4] = dest_cpus[i][4]; | |
125 | if (!strncmp(line, a53, strlen(a53))) { | |
126 | bf->dest_cpu = i << PART_ATTR_DEST_CPU_SHIFT; | |
127 | return line + strlen(a53); | |
128 | } | |
129 | } | |
130 | } | |
131 | ||
132 | return line; | |
133 | } | |
134 | ||
135 | static char *parse_el(char *line, struct bif_entry *bf) | |
136 | { | |
137 | const char *dest_els[] = { "none", "el-0", "el-1", "el-2", "el-3" }; | |
138 | int i; | |
139 | ||
140 | for (i = 0; i < ARRAY_SIZE(dest_els); i++) { | |
141 | if (!strncmp(line, dest_els[i], strlen(dest_els[i]))) { | |
142 | bf->exp_lvl = i; | |
143 | return line + strlen(dest_els[i]); | |
144 | } | |
145 | } | |
146 | ||
147 | return line; | |
148 | } | |
149 | ||
150 | static char *parse_load(char *line, struct bif_entry *bf) | |
151 | { | |
152 | char *endptr; | |
153 | ||
154 | bf->load = strtoll(line, &endptr, 0); | |
155 | ||
156 | return endptr; | |
157 | } | |
158 | ||
159 | static char *parse_entry(char *line, struct bif_entry *bf) | |
160 | { | |
161 | char *endptr; | |
162 | ||
163 | bf->entry = strtoll(line, &endptr, 0); | |
164 | ||
165 | return endptr; | |
166 | } | |
167 | ||
168 | static char *parse_offset(char *line, struct bif_entry *bf) | |
169 | { | |
170 | char *endptr; | |
171 | ||
172 | bf->offset = strtoll(line, &endptr, 0); | |
173 | ||
174 | return endptr; | |
175 | } | |
176 | ||
177 | static char *parse_partition_owner(char *line, struct bif_entry *bf) | |
178 | { | |
179 | char *endptr = NULL; | |
180 | ||
181 | if (!strncmp(line, "fsbl", 4)) { | |
182 | endptr = line + 4; | |
183 | } else if (!strncmp(line, "uboot", 5)) { | |
184 | bf->flags |= 1ULL << BIF_FLAG_PART_OWNER_UBOOT; | |
185 | endptr = line + 5; | |
186 | } else { | |
187 | printf("ERROR: Unknown partition type '%s'\n", line); | |
188 | } | |
189 | ||
190 | return endptr; | |
191 | } | |
192 | ||
193 | static const struct bif_flags bif_flags[] = { | |
194 | { "fsbl_config", BIF_FLAG_FSBL_CONFIG }, | |
195 | { "trustzone", BIF_FLAG_TZ }, | |
196 | { "pmufw_image", BIF_FLAG_PMUFW_IMAGE }, | |
197 | { "bootloader", BIF_FLAG_BOOTLOADER }, | |
198 | { "destination_cpu=", 0, parse_dest_cpu }, | |
199 | { "exception_level=", 0, parse_el }, | |
200 | { "load=", 0, parse_load }, | |
201 | { "startup=", 0, parse_entry }, | |
202 | { "offset=", 0, parse_offset }, | |
203 | { "partition_owner=", 0, parse_partition_owner }, | |
204 | }; | |
205 | ||
206 | static char *read_full_file(const char *filename, size_t *size) | |
207 | { | |
208 | char *buf, *bufp; | |
209 | struct stat sbuf; | |
210 | int len = 0, r, fd; | |
211 | ||
212 | fd = open(filename, O_RDONLY); | |
213 | if (fd < 0) | |
214 | return NULL; | |
215 | ||
216 | if (fstat(fd, &sbuf) < 0) | |
217 | return NULL; | |
218 | ||
219 | if (size) | |
220 | *size = sbuf.st_size; | |
221 | ||
222 | buf = malloc(sbuf.st_size); | |
223 | if (!buf) | |
224 | return NULL; | |
225 | ||
226 | bufp = buf; | |
227 | while (len < sbuf.st_size) { | |
228 | r = read(fd, bufp, sbuf.st_size - len); | |
229 | if (r < 0) | |
230 | return NULL; | |
231 | len += r; | |
232 | bufp += r; | |
233 | } | |
234 | ||
235 | close(fd); | |
236 | ||
237 | return buf; | |
238 | } | |
239 | ||
240 | static int bif_add_blob(const void *data, size_t len, size_t *offset) | |
241 | { | |
242 | size_t new_size; | |
243 | uintptr_t header_off; | |
244 | uintptr_t last_part_off; | |
245 | uintptr_t imgheader_off; | |
246 | uintptr_t old_data = (uintptr_t)bif_output.data; | |
247 | void *new_data; | |
248 | ||
249 | header_off = (uintptr_t)bif_output.header - old_data; | |
250 | last_part_off = (uintptr_t)bif_output.last_part - old_data; | |
251 | imgheader_off = (uintptr_t)bif_output.imgheader - old_data; | |
252 | ||
253 | if (offset && *offset) { | |
254 | /* Pad to a given offset */ | |
255 | if (bif_output.data_len > *offset) { | |
256 | printf("Can not pad to offset %zx\n", *offset); | |
257 | return -1; | |
258 | } | |
259 | ||
260 | bif_output.data_len = *offset; | |
261 | } | |
262 | ||
263 | new_size = ROUND(bif_output.data_len + len, 64); | |
264 | new_data = realloc(bif_output.data, new_size); | |
265 | memcpy(new_data + bif_output.data_len, data, len); | |
266 | if (offset) | |
267 | *offset = bif_output.data_len; | |
268 | bif_output.data = new_data; | |
269 | bif_output.data_len = new_size; | |
270 | ||
271 | /* Readjust internal pointers */ | |
272 | if (bif_output.header) | |
273 | bif_output.header = new_data + header_off; | |
274 | if (bif_output.last_part) | |
275 | bif_output.last_part = new_data + last_part_off; | |
276 | if (bif_output.imgheader) | |
277 | bif_output.imgheader = new_data + imgheader_off; | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | static int bif_init(void) | |
283 | { | |
284 | struct zynqmp_header header = { { 0 } }; | |
285 | int r; | |
286 | ||
287 | zynqmpimage_default_header(&header); | |
288 | ||
289 | r = bif_add_blob(&header, sizeof(header), NULL); | |
290 | if (r) | |
291 | return r; | |
292 | ||
293 | bif_output.header = (void *)bif_output.data; | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | static int bif_add_pmufw(struct bif_entry *bf, const char *data, size_t len) | |
299 | { | |
300 | int r; | |
301 | ||
302 | if (bif_output.header->image_offset) { | |
303 | printf("PMUFW expected before bootloader in your .bif file!\n"); | |
304 | return -1; | |
305 | } | |
306 | ||
307 | r = bif_add_blob(data, len, &bf->offset); | |
308 | if (r) | |
309 | return r; | |
310 | ||
311 | len = ROUND(len, 64); | |
312 | bif_output.header->pfw_image_length = cpu_to_le32(len); | |
313 | bif_output.header->total_pfw_image_length = cpu_to_le32(len); | |
314 | bif_output.header->image_offset = cpu_to_le32(bf->offset); | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | static int bif_add_part(struct bif_entry *bf, const char *data, size_t len) | |
320 | { | |
321 | size_t parthdr_offset = 0; | |
322 | struct partition_header parthdr = { | |
323 | .len_enc = cpu_to_le32(len / 4), | |
324 | .len_unenc = cpu_to_le32(len / 4), | |
325 | .len = cpu_to_le32(len / 4), | |
326 | .entry_point = cpu_to_le64(bf->entry), | |
327 | .load_address = cpu_to_le64(bf->load), | |
328 | }; | |
329 | int r; | |
330 | uint32_t csum; | |
331 | ||
332 | if (bf->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) | |
333 | return bif_add_pmufw(bf, data, len); | |
334 | ||
335 | r = bif_add_blob(data, len, &bf->offset); | |
336 | if (r) | |
337 | return r; | |
338 | ||
339 | parthdr.offset = cpu_to_le32(bf->offset / 4); | |
340 | ||
341 | if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { | |
342 | if (bif_output.last_part) { | |
343 | printf("ERROR: Bootloader expected before others\n"); | |
344 | return -1; | |
345 | } | |
346 | ||
347 | parthdr.offset = cpu_to_le32(bif_output.header->image_offset); | |
348 | parthdr.len = cpu_to_le32((bf->offset + len - | |
349 | bif_output.header->image_offset) / 4); | |
350 | parthdr.len_enc = parthdr.len; | |
351 | parthdr.len_unenc = parthdr.len; | |
352 | } | |
353 | ||
354 | /* Normalize EL */ | |
355 | bf->exp_lvl = bf->exp_lvl ? bf->exp_lvl - 1 : 3; | |
356 | parthdr.attributes |= bf->exp_lvl << PART_ATTR_TARGET_EL_SHIFT; | |
357 | parthdr.attributes |= bf->dest_dev; | |
358 | parthdr.attributes |= bf->dest_cpu; | |
359 | if (bf->flags & (1ULL << BIF_FLAG_TZ)) | |
360 | parthdr.attributes |= PART_ATTR_TZ_SECURE; | |
361 | if (bf->flags & (1ULL << BIF_FLAG_PART_OWNER_UBOOT)) | |
362 | parthdr.attributes |= PART_ATTR_PART_OWNER_UBOOT; | |
363 | switch (bf->dest_cpu) { | |
364 | case PART_ATTR_DEST_CPU_NONE: | |
365 | case PART_ATTR_DEST_CPU_A53_0: | |
366 | case PART_ATTR_DEST_CPU_A53_1: | |
367 | case PART_ATTR_DEST_CPU_A53_2: | |
368 | case PART_ATTR_DEST_CPU_A53_3: | |
369 | if (bf->flags & (1ULL << BIF_FLAG_AARCH32)) | |
370 | parthdr.attributes |= PART_ATTR_A53_EXEC_AARCH32; | |
371 | } | |
372 | ||
373 | csum = zynqmp_csum(&parthdr, &parthdr.checksum); | |
374 | parthdr.checksum = cpu_to_le32(csum); | |
375 | ||
376 | r = bif_add_blob(&parthdr, sizeof(parthdr), &parthdr_offset); | |
377 | if (r) | |
378 | return r; | |
379 | ||
380 | /* Add image header table if not there yet */ | |
381 | if (!bif_output.imgheader) { | |
382 | size_t imghdr_off = 0; | |
383 | struct image_header_table imghdr = { | |
384 | .version = cpu_to_le32(0x01020000), | |
385 | .nr_parts = 0, | |
386 | }; | |
387 | ||
388 | r = bif_add_blob(&imghdr, sizeof(imghdr), &imghdr_off); | |
389 | if (r) | |
390 | return r; | |
391 | ||
392 | bif_output.header->image_header_table_offset = imghdr_off; | |
393 | bif_output.imgheader = (void *)(bif_output.data + imghdr_off); | |
394 | } | |
395 | ||
396 | bif_output.imgheader->nr_parts = cpu_to_le32(le32_to_cpu( | |
397 | bif_output.imgheader->nr_parts) + 1); | |
398 | ||
399 | /* Link to this partition header */ | |
400 | if (bif_output.last_part) { | |
401 | bif_output.last_part->next_partition_offset = | |
402 | cpu_to_le32(parthdr_offset / 4); | |
403 | ||
404 | /* Recalc checksum of last_part */ | |
405 | csum = zynqmp_csum(bif_output.last_part, | |
406 | &bif_output.last_part->checksum); | |
407 | bif_output.last_part->checksum = cpu_to_le32(csum); | |
408 | } else { | |
409 | bif_output.imgheader->partition_header_offset = | |
410 | cpu_to_le32(parthdr_offset / 4); | |
411 | } | |
412 | bif_output.last_part = (void *)(bif_output.data + parthdr_offset); | |
413 | ||
414 | if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { | |
415 | bif_output.header->image_load = cpu_to_le32(bf->load); | |
416 | if (!bif_output.header->image_offset) | |
417 | bif_output.header->image_offset = | |
418 | cpu_to_le32(bf->offset); | |
419 | bif_output.header->image_size = cpu_to_le32(len); | |
420 | bif_output.header->image_stored_size = cpu_to_le32(len); | |
421 | ||
422 | bif_output.header->image_attributes &= ~HEADER_CPU_SELECT_MASK; | |
423 | switch (bf->dest_cpu) { | |
424 | default: | |
425 | case PART_ATTR_DEST_CPU_A53_0: | |
426 | if (bf->flags & BIF_FLAG_AARCH32) | |
427 | bif_output.header->image_attributes |= | |
428 | HEADER_CPU_SELECT_A53_32BIT; | |
429 | else | |
430 | bif_output.header->image_attributes |= | |
431 | HEADER_CPU_SELECT_A53_64BIT; | |
432 | break; | |
433 | case PART_ATTR_DEST_CPU_R5_0: | |
434 | bif_output.header->image_attributes |= | |
435 | HEADER_CPU_SELECT_R5_SINGLE; | |
436 | break; | |
437 | case PART_ATTR_DEST_CPU_R5_L: | |
438 | bif_output.header->image_attributes |= | |
439 | HEADER_CPU_SELECT_R5_DUAL; | |
440 | break; | |
441 | } | |
442 | } | |
443 | ||
444 | return 0; | |
445 | } | |
446 | ||
447 | /* Add .bit bitstream */ | |
448 | static int bif_add_bit(struct bif_entry *bf) | |
449 | { | |
450 | char *bit = read_full_file(bf->filename, NULL); | |
451 | char *bitbin; | |
452 | uint8_t initial_header[] = { 0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, | |
453 | 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x61 }; | |
454 | uint16_t len; | |
455 | uint32_t bitlen; | |
456 | int i; | |
457 | ||
458 | if (!bit) | |
459 | return -1; | |
460 | ||
461 | /* Skip initial header */ | |
462 | if (memcmp(bit, initial_header, sizeof(initial_header))) | |
463 | return -1; | |
464 | ||
465 | bit += sizeof(initial_header); | |
466 | ||
467 | /* Design name */ | |
468 | len = be16_to_cpu(*(uint16_t *)bit); | |
469 | bit += sizeof(uint16_t); | |
470 | debug("Design: %s\n", bit); | |
471 | bit += len; | |
472 | ||
473 | /* Device identifier */ | |
474 | if (*bit != 'b') | |
475 | return -1; | |
476 | bit++; | |
477 | len = be16_to_cpu(*(uint16_t *)bit); | |
478 | bit += sizeof(uint16_t); | |
479 | debug("Device: %s\n", bit); | |
480 | bit += len; | |
481 | ||
482 | /* Date */ | |
483 | if (*bit != 'c') | |
484 | return -1; | |
485 | bit++; | |
486 | len = be16_to_cpu(*(uint16_t *)bit); | |
487 | bit += sizeof(uint16_t); | |
488 | debug("Date: %s\n", bit); | |
489 | bit += len; | |
490 | ||
491 | /* Time */ | |
492 | if (*bit != 'd') | |
493 | return -1; | |
494 | bit++; | |
495 | len = be16_to_cpu(*(uint16_t *)bit); | |
496 | bit += sizeof(uint16_t); | |
497 | debug("Time: %s\n", bit); | |
498 | bit += len; | |
499 | ||
500 | /* Bitstream length */ | |
501 | if (*bit != 'e') | |
502 | return -1; | |
503 | bit++; | |
504 | bitlen = be32_to_cpu(*(uint32_t *)bit); | |
505 | bit += sizeof(uint32_t); | |
506 | bitbin = bit; | |
507 | ||
508 | debug("Bitstream Length: 0x%x\n", bitlen); | |
509 | for (i = 0; i < bitlen; i += sizeof(uint32_t)) { | |
510 | uint32_t *bitbin32 = (uint32_t *)&bitbin[i]; | |
511 | *bitbin32 = __swab32(*bitbin32); | |
512 | } | |
513 | ||
514 | if (!bf->dest_dev) | |
515 | bf->dest_dev = PART_ATTR_DEST_DEVICE_PL; | |
516 | ||
517 | bf->load = 0xffffffff; | |
518 | bf->entry = 0; | |
519 | ||
520 | bf->flags |= 1ULL << BIF_FLAG_BIT_FILE; | |
521 | return bif_add_part(bf, bit, bitlen); | |
522 | } | |
523 | ||
524 | /* Add .bin bitstream */ | |
525 | static int bif_add_bin(struct bif_entry *bf) | |
526 | { | |
527 | size_t size; | |
528 | char *bin = read_full_file(bf->filename, &size); | |
529 | ||
530 | if (!bf->dest_dev) | |
531 | bf->dest_dev = PART_ATTR_DEST_DEVICE_PS; | |
532 | ||
533 | bf->flags |= 1ULL << BIF_FLAG_BIN_FILE; | |
534 | return bif_add_part(bf, bin, size); | |
535 | } | |
536 | ||
537 | /* Add elf file */ | |
538 | static char *elf2flat64(char *elf, size_t *flat_size, size_t *load_addr) | |
539 | { | |
540 | Elf64_Ehdr *ehdr; | |
541 | Elf64_Shdr *shdr; | |
542 | size_t min_addr = -1, max_addr = 0; | |
543 | char *flat; | |
544 | int i; | |
545 | ||
546 | ehdr = (void *)elf; | |
547 | shdr = (void *)(elf + le64_to_cpu(ehdr->e_shoff)); | |
548 | ||
549 | /* Look for smallest / biggest address */ | |
550 | for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++, shdr++) { | |
551 | if (!shdr->sh_size || !shdr->sh_addr || | |
552 | !(shdr->sh_flags & SHF_ALLOC) || | |
553 | (shdr->sh_type == SHT_NOBITS)) | |
554 | continue; | |
555 | ||
556 | if (le64_to_cpu(shdr->sh_addr) < min_addr) | |
557 | min_addr = le64_to_cpu(shdr->sh_addr); | |
558 | if ((le64_to_cpu(shdr->sh_addr) + le64_to_cpu(shdr->sh_size)) > | |
559 | max_addr) | |
560 | max_addr = le64_to_cpu(shdr->sh_addr) + | |
561 | le64_to_cpu(shdr->sh_size); | |
562 | } | |
563 | ||
564 | *load_addr = min_addr; | |
565 | *flat_size = max_addr - min_addr; | |
566 | flat = calloc(1, *flat_size); | |
567 | if (!flat) | |
568 | return NULL; | |
569 | ||
570 | shdr = (void *)(elf + le64_to_cpu(ehdr->e_shoff)); | |
571 | for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++, shdr++) { | |
572 | char *dst = flat + le64_to_cpu(shdr->sh_addr) - min_addr; | |
573 | char *src = elf + le64_to_cpu(shdr->sh_offset); | |
574 | ||
575 | if (!shdr->sh_size || !shdr->sh_addr || | |
576 | !(shdr->sh_flags & SHF_ALLOC)) | |
577 | continue; | |
578 | ||
579 | if (shdr->sh_type != SHT_NOBITS) | |
580 | memcpy(dst, src, le64_to_cpu(shdr->sh_size)); | |
581 | } | |
582 | ||
583 | return flat; | |
584 | } | |
585 | ||
586 | static char *elf2flat32(char *elf, size_t *flat_size, size_t *load_addr) | |
587 | { | |
588 | Elf32_Ehdr *ehdr; | |
589 | Elf32_Shdr *shdr; | |
590 | size_t min_addr = -1, max_addr = 0; | |
591 | char *flat; | |
592 | int i; | |
593 | ||
594 | ehdr = (void *)elf; | |
595 | shdr = (void *)(elf + le32_to_cpu(ehdr->e_shoff)); | |
596 | ||
597 | /* Look for smallest / biggest address */ | |
598 | for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++, shdr++) { | |
599 | if (!shdr->sh_size || !shdr->sh_addr || | |
600 | !(shdr->sh_flags & SHF_ALLOC) || | |
601 | (shdr->sh_type == SHT_NOBITS)) | |
602 | continue; | |
603 | ||
604 | if (le32_to_cpu(shdr->sh_addr) < min_addr) | |
605 | min_addr = le32_to_cpu(shdr->sh_addr); | |
606 | if ((le32_to_cpu(shdr->sh_addr) + le32_to_cpu(shdr->sh_size)) > | |
607 | max_addr) | |
608 | max_addr = le32_to_cpu(shdr->sh_addr) + | |
609 | le32_to_cpu(shdr->sh_size); | |
610 | } | |
611 | ||
612 | *load_addr = min_addr; | |
613 | *flat_size = max_addr - min_addr; | |
614 | flat = calloc(1, *flat_size); | |
615 | if (!flat) | |
616 | return NULL; | |
617 | ||
618 | shdr = (void *)(elf + le32_to_cpu(ehdr->e_shoff)); | |
619 | for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++, shdr++) { | |
620 | char *dst = flat + le32_to_cpu(shdr->sh_addr) - min_addr; | |
621 | char *src = elf + le32_to_cpu(shdr->sh_offset); | |
622 | ||
623 | if (!shdr->sh_size || !shdr->sh_addr || | |
624 | !(shdr->sh_flags & SHF_ALLOC)) | |
625 | continue; | |
626 | ||
627 | if (shdr->sh_type != SHT_NOBITS) | |
628 | memcpy(dst, src, le32_to_cpu(shdr->sh_size)); | |
629 | } | |
630 | ||
631 | return flat; | |
632 | } | |
633 | ||
634 | static int bif_add_elf(struct bif_entry *bf) | |
635 | { | |
636 | size_t size; | |
637 | size_t elf_size; | |
638 | char *elf; | |
639 | char *flat; | |
640 | size_t load_addr; | |
641 | Elf32_Ehdr *ehdr32; | |
642 | Elf64_Ehdr *ehdr64; | |
643 | ||
644 | elf = read_full_file(bf->filename, &elf_size); | |
645 | if (!elf) | |
646 | return -1; | |
647 | ||
648 | ehdr32 = (void *)elf; | |
649 | ehdr64 = (void *)elf; | |
650 | ||
651 | switch (ehdr32->e_ident[EI_CLASS]) { | |
652 | case ELFCLASS32: | |
653 | flat = elf2flat32(elf, &size, &load_addr); | |
654 | bf->entry = le32_to_cpu(ehdr32->e_entry); | |
655 | bf->flags |= 1ULL << BIF_FLAG_AARCH32; | |
656 | break; | |
657 | case ELFCLASS64: | |
658 | flat = elf2flat64(elf, &size, &load_addr); | |
659 | bf->entry = le64_to_cpu(ehdr64->e_entry); | |
660 | break; | |
661 | default: | |
662 | printf("Unknown ELF class: %d\n", ehdr32->e_ident[EI_CLASS]); | |
663 | return -1; | |
664 | } | |
665 | ||
666 | if (!flat) | |
667 | return -1; | |
668 | ||
669 | bf->load = load_addr; | |
670 | if (!bf->dest_dev) | |
671 | bf->dest_dev = PART_ATTR_DEST_DEVICE_PS; | |
672 | ||
673 | bf->flags |= 1ULL << BIF_FLAG_ELF_FILE; | |
674 | return bif_add_part(bf, flat, size); | |
675 | } | |
676 | ||
677 | static const struct bif_file_type bif_file_types[] = { | |
678 | { | |
679 | .name = "bitstream (.bit)", | |
680 | .header = 0x00090ff0, | |
681 | .add = bif_add_bit, | |
682 | }, | |
683 | ||
684 | { | |
685 | .name = "ELF", | |
686 | .header = 0x7f454c46, | |
687 | .add = bif_add_elf, | |
688 | }, | |
689 | ||
690 | /* Anything else is a .bin file */ | |
691 | { | |
692 | .name = ".bin", | |
693 | .add = bif_add_bin, | |
694 | }, | |
695 | }; | |
696 | ||
697 | static int bif_fsbl_config(struct bif_entry *fsbl_config, | |
698 | struct bif_entry *entries, int nr_entries) | |
699 | { | |
700 | int i; | |
701 | int config_set = 0; | |
702 | struct { | |
703 | const char *name; | |
704 | uint64_t flags; | |
705 | uint64_t dest_cpu; | |
706 | } configs[] = { | |
707 | { .name = "a5x_x64", .dest_cpu = PART_ATTR_DEST_CPU_A53_0 }, | |
708 | { .name = "a53_x64", .dest_cpu = PART_ATTR_DEST_CPU_A53_0 }, | |
709 | { .name = "a5x_x32", .dest_cpu = PART_ATTR_DEST_CPU_A53_0, | |
710 | .flags = 1ULL << BIF_FLAG_AARCH32 }, | |
711 | { .name = "a53_x32", .dest_cpu = PART_ATTR_DEST_CPU_A53_0, | |
712 | .flags = 1ULL << BIF_FLAG_AARCH32 }, | |
713 | { .name = "r5_single", .dest_cpu = PART_ATTR_DEST_CPU_R5_0 }, | |
714 | { .name = "r5_dual", .dest_cpu = PART_ATTR_DEST_CPU_R5_L }, | |
715 | }; | |
716 | ||
717 | /* Set target CPU of bootloader entry */ | |
718 | for (i = 0; i < nr_entries; i++) { | |
719 | struct bif_entry *b = &entries[i]; | |
720 | const char *config_attr = fsbl_config->filename; | |
721 | int j; | |
722 | ||
723 | if (!(b->flags & (1ULL << BIF_FLAG_BOOTLOADER))) | |
724 | continue; | |
725 | ||
726 | for (j = 0; j < ARRAY_SIZE(configs); j++) { | |
727 | if (!strncmp(config_attr, configs[j].name, | |
728 | strlen(configs[j].name))) { | |
729 | b->dest_cpu = configs[j].dest_cpu; | |
730 | b->flags |= configs[j].flags; | |
731 | config_set = 1; | |
732 | } | |
733 | } | |
734 | ||
735 | if (!config_set) { | |
736 | printf("ERROR: Unsupported fsbl_config: %s\n", | |
737 | config_attr); | |
738 | return -1; | |
739 | } | |
740 | } | |
741 | ||
742 | if (!config_set) { | |
743 | printf("ERROR: fsbl_config w/o bootloader\n"); | |
744 | return -1; | |
745 | } | |
746 | ||
747 | return 0; | |
748 | } | |
749 | ||
750 | static const struct bif_flags *find_flag(char *str) | |
751 | { | |
752 | const struct bif_flags *bf; | |
753 | int i; | |
754 | ||
755 | for (i = 0; i < ARRAY_SIZE(bif_flags); i++) { | |
756 | bf = &bif_flags[i]; | |
757 | if (!strncmp(bf->name, str, strlen(bf->name))) | |
758 | return bf; | |
759 | } | |
760 | ||
761 | printf("ERROR: Flag '%s' not found\n", str); | |
762 | ||
763 | return NULL; | |
764 | } | |
765 | ||
766 | static int bif_open_file(struct bif_entry *entry) | |
767 | { | |
768 | int fd = open(entry->filename, O_RDONLY); | |
769 | ||
770 | if (fd < 0) | |
771 | printf("Error opening file %s\n", entry->filename); | |
772 | ||
773 | return fd; | |
774 | } | |
775 | ||
776 | static const struct bif_file_type *get_file_type(struct bif_entry *entry) | |
777 | { | |
778 | int fd = bif_open_file(entry); | |
779 | uint32_t header; | |
780 | int i; | |
781 | ||
782 | if (fd < 0) | |
783 | return NULL; | |
784 | ||
785 | if (read(fd, &header, sizeof(header)) != sizeof(header)) { | |
786 | printf("Error reading file %s", entry->filename); | |
787 | return NULL; | |
788 | } | |
789 | ||
790 | close(fd); | |
791 | ||
792 | for (i = 0; i < ARRAY_SIZE(bif_file_types); i++) { | |
793 | const struct bif_file_type *type = &bif_file_types[i]; | |
794 | ||
795 | if (!type->header) | |
796 | return type; | |
797 | if (type->header == be32_to_cpu(header)) | |
798 | return type; | |
799 | } | |
800 | ||
801 | return NULL; | |
802 | } | |
803 | ||
804 | #define NEXT_CHAR(str, chr) ({ \ | |
805 | char *_n = strchr(str, chr); \ | |
806 | if (!_n) \ | |
807 | goto err; \ | |
808 | _n; \ | |
809 | }) | |
810 | ||
811 | static char *skip_whitespace(char *str) | |
812 | { | |
813 | while (*str == ' ' || *str == '\t') | |
814 | str++; | |
815 | ||
816 | return str; | |
817 | } | |
818 | ||
819 | int zynqmpbif_copy_image(int outfd, struct image_tool_params *mparams) | |
820 | { | |
821 | char *bif, *bifp, *bifpn; | |
822 | char *line; | |
823 | struct bif_entry entries[32] = { { 0 } }; | |
824 | int nr_entries = 0; | |
825 | struct bif_entry *entry = entries; | |
826 | size_t len; | |
827 | int i; | |
828 | uint32_t csum; | |
829 | int bldr = -1; | |
830 | ||
831 | bif_init(); | |
832 | ||
833 | /* Read .bif input file */ | |
834 | bif = read_full_file(mparams->datafile, NULL); | |
835 | if (!bif) | |
836 | goto err; | |
837 | ||
838 | /* Interpret .bif file */ | |
839 | bifp = bif; | |
840 | ||
841 | /* A bif description starts with a { section */ | |
842 | bifp = NEXT_CHAR(bifp, '{') + 1; | |
843 | ||
844 | /* Read every line */ | |
845 | while (1) { | |
846 | bifpn = NEXT_CHAR(bifp, '\n'); | |
847 | ||
848 | if (bifpn[-1] == '\r') | |
849 | bifpn[-1] = '\0'; | |
850 | ||
851 | *bifpn = '\0'; | |
852 | bifpn++; | |
853 | line = bifp; | |
854 | ||
855 | line = skip_whitespace(line); | |
856 | ||
857 | /* Attributes? */ | |
858 | if (*line == '[') { | |
859 | line++; | |
860 | while (1) { | |
861 | const struct bif_flags *bf; | |
862 | ||
863 | line = skip_whitespace(line); | |
864 | bf = find_flag(line); | |
865 | if (!bf) | |
866 | goto err; | |
867 | ||
868 | line += strlen(bf->name); | |
869 | if (bf->parse) | |
870 | line = bf->parse(line, entry); | |
871 | else | |
872 | entry->flags |= 1ULL << bf->flag; | |
873 | ||
874 | if (!line) | |
875 | goto err; | |
876 | ||
877 | /* Go to next attribute or quit */ | |
878 | if (*line == ']') { | |
879 | line++; | |
880 | break; | |
881 | } | |
882 | if (*line == ',') | |
883 | line++; | |
884 | } | |
885 | } | |
886 | ||
887 | /* End of image description */ | |
888 | if (*line == '}') | |
889 | break; | |
890 | ||
891 | if (*line) { | |
892 | line = skip_whitespace(line); | |
893 | entry->filename = line; | |
894 | nr_entries++; | |
895 | entry++; | |
896 | } | |
897 | ||
898 | /* Use next line */ | |
899 | bifp = bifpn; | |
900 | } | |
901 | ||
902 | for (i = 0; i < nr_entries; i++) { | |
903 | debug("Entry flags=%#lx name=%s\n", entries[i].flags, | |
904 | entries[i].filename); | |
905 | } | |
906 | ||
907 | /* | |
908 | * Some entries are actually configuration option for other ones, | |
909 | * let's apply them in an intermediate step. | |
910 | */ | |
911 | for (i = 0; i < nr_entries; i++) { | |
912 | struct bif_entry *entry = &entries[i]; | |
913 | ||
914 | if (entry->flags & (1ULL << BIF_FLAG_FSBL_CONFIG)) | |
915 | if (bif_fsbl_config(entry, entries, nr_entries)) | |
916 | goto err; | |
917 | } | |
918 | ||
919 | /* Make sure PMUFW comes before bootloader */ | |
920 | for (i = 0; i < nr_entries; i++) { | |
921 | struct bif_entry *entry = &entries[i]; | |
922 | ||
923 | if (entry->flags & (1ULL << BIF_FLAG_BOOTLOADER)) | |
924 | bldr = i; | |
925 | if (entry->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) { | |
926 | if (bldr >= 0) { | |
927 | struct bif_entry tmp = *entry; | |
928 | ||
929 | *entry = entries[bldr]; | |
930 | entries[bldr] = tmp; | |
931 | } | |
932 | } | |
933 | } | |
934 | ||
935 | for (i = 0; i < nr_entries; i++) { | |
936 | struct bif_entry *entry = &entries[i]; | |
937 | const struct bif_file_type *type; | |
938 | int r; | |
939 | ||
940 | if (entry->flags & (1ULL << BIF_FLAG_FSBL_CONFIG)) | |
941 | continue; | |
942 | ||
943 | type = get_file_type(entry); | |
944 | if (!type) | |
945 | goto err; | |
946 | ||
947 | debug("type=%s file=%s\n", type->name, entry->filename); | |
948 | r = type->add(entry); | |
949 | if (r) | |
950 | goto err; | |
951 | } | |
952 | ||
953 | /* Calculate checksums */ | |
954 | csum = zynqmp_csum(&bif_output.header->width_detection, | |
955 | &bif_output.header->checksum); | |
956 | bif_output.header->checksum = cpu_to_le32(csum); | |
957 | ||
958 | if (bif_output.imgheader) { | |
959 | csum = zynqmp_csum(bif_output.imgheader, | |
960 | &bif_output.imgheader->checksum); | |
961 | bif_output.imgheader->checksum = cpu_to_le32(csum); | |
962 | } | |
963 | ||
964 | /* Write headers and components */ | |
965 | if (lseek(outfd, 0, SEEK_SET) != 0) | |
966 | goto err; | |
967 | ||
968 | len = bif_output.data_len; | |
969 | bifp = bif_output.data; | |
970 | while (len) { | |
971 | int r; | |
972 | ||
973 | r = write(outfd, bifp, len); | |
974 | if (r < 0) | |
975 | goto err; | |
976 | len -= r; | |
977 | bifp += r; | |
978 | } | |
979 | ||
980 | return 0; | |
981 | ||
982 | err: | |
983 | fprintf(stderr, "Error: Failed to create image.\n"); | |
984 | return -1; | |
985 | } | |
986 | ||
987 | /* Needs to be stubbed out so we can print after creation */ | |
988 | static void zynqmpbif_set_header(void *ptr, struct stat *sbuf, int ifd, | |
989 | struct image_tool_params *params) | |
990 | { | |
991 | } | |
992 | ||
993 | static struct zynqmp_header zynqmpimage_header; | |
994 | ||
995 | U_BOOT_IMAGE_TYPE( | |
996 | zynqmpbif, | |
997 | "Xilinx ZynqMP Boot Image support (bif)", | |
998 | sizeof(struct zynqmp_header), | |
999 | (void *)&zynqmpimage_header, | |
1000 | zynqmpbif_check_params, | |
1001 | NULL, | |
1002 | zynqmpimage_print_header, | |
1003 | zynqmpbif_set_header, | |
1004 | NULL, | |
1005 | zynqmpbif_check_image_types, | |
1006 | NULL, | |
1007 | NULL | |
1008 | ); |