]>
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; | |
16c78cba MT |
322 | size_t len_padded = ROUND(len, 4); |
323 | ||
6915dcf3 | 324 | struct partition_header parthdr = { |
16c78cba MT |
325 | .len_enc = cpu_to_le32(len_padded / 4), |
326 | .len_unenc = cpu_to_le32(len_padded / 4), | |
327 | .len = cpu_to_le32(len_padded / 4), | |
6915dcf3 AG |
328 | .entry_point = cpu_to_le64(bf->entry), |
329 | .load_address = cpu_to_le64(bf->load), | |
330 | }; | |
331 | int r; | |
332 | uint32_t csum; | |
333 | ||
16c78cba MT |
334 | if (len < len_padded) { |
335 | char *newdata = malloc(len_padded); | |
336 | memcpy(newdata, data, len); | |
337 | memset(newdata + len, 0, len_padded - len); | |
338 | data = newdata; | |
339 | } | |
340 | ||
6915dcf3 AG |
341 | if (bf->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) |
342 | return bif_add_pmufw(bf, data, len); | |
343 | ||
344 | r = bif_add_blob(data, len, &bf->offset); | |
345 | if (r) | |
346 | return r; | |
347 | ||
348 | parthdr.offset = cpu_to_le32(bf->offset / 4); | |
349 | ||
350 | if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { | |
351 | if (bif_output.last_part) { | |
352 | printf("ERROR: Bootloader expected before others\n"); | |
353 | return -1; | |
354 | } | |
355 | ||
356 | parthdr.offset = cpu_to_le32(bif_output.header->image_offset); | |
357 | parthdr.len = cpu_to_le32((bf->offset + len - | |
358 | bif_output.header->image_offset) / 4); | |
359 | parthdr.len_enc = parthdr.len; | |
360 | parthdr.len_unenc = parthdr.len; | |
361 | } | |
362 | ||
363 | /* Normalize EL */ | |
364 | bf->exp_lvl = bf->exp_lvl ? bf->exp_lvl - 1 : 3; | |
365 | parthdr.attributes |= bf->exp_lvl << PART_ATTR_TARGET_EL_SHIFT; | |
366 | parthdr.attributes |= bf->dest_dev; | |
367 | parthdr.attributes |= bf->dest_cpu; | |
368 | if (bf->flags & (1ULL << BIF_FLAG_TZ)) | |
369 | parthdr.attributes |= PART_ATTR_TZ_SECURE; | |
370 | if (bf->flags & (1ULL << BIF_FLAG_PART_OWNER_UBOOT)) | |
371 | parthdr.attributes |= PART_ATTR_PART_OWNER_UBOOT; | |
372 | switch (bf->dest_cpu) { | |
373 | case PART_ATTR_DEST_CPU_NONE: | |
374 | case PART_ATTR_DEST_CPU_A53_0: | |
375 | case PART_ATTR_DEST_CPU_A53_1: | |
376 | case PART_ATTR_DEST_CPU_A53_2: | |
377 | case PART_ATTR_DEST_CPU_A53_3: | |
378 | if (bf->flags & (1ULL << BIF_FLAG_AARCH32)) | |
379 | parthdr.attributes |= PART_ATTR_A53_EXEC_AARCH32; | |
380 | } | |
381 | ||
382 | csum = zynqmp_csum(&parthdr, &parthdr.checksum); | |
383 | parthdr.checksum = cpu_to_le32(csum); | |
384 | ||
385 | r = bif_add_blob(&parthdr, sizeof(parthdr), &parthdr_offset); | |
386 | if (r) | |
387 | return r; | |
388 | ||
389 | /* Add image header table if not there yet */ | |
390 | if (!bif_output.imgheader) { | |
391 | size_t imghdr_off = 0; | |
392 | struct image_header_table imghdr = { | |
393 | .version = cpu_to_le32(0x01020000), | |
394 | .nr_parts = 0, | |
395 | }; | |
396 | ||
397 | r = bif_add_blob(&imghdr, sizeof(imghdr), &imghdr_off); | |
398 | if (r) | |
399 | return r; | |
400 | ||
401 | bif_output.header->image_header_table_offset = imghdr_off; | |
402 | bif_output.imgheader = (void *)(bif_output.data + imghdr_off); | |
403 | } | |
404 | ||
405 | bif_output.imgheader->nr_parts = cpu_to_le32(le32_to_cpu( | |
406 | bif_output.imgheader->nr_parts) + 1); | |
407 | ||
408 | /* Link to this partition header */ | |
409 | if (bif_output.last_part) { | |
410 | bif_output.last_part->next_partition_offset = | |
411 | cpu_to_le32(parthdr_offset / 4); | |
412 | ||
413 | /* Recalc checksum of last_part */ | |
414 | csum = zynqmp_csum(bif_output.last_part, | |
415 | &bif_output.last_part->checksum); | |
416 | bif_output.last_part->checksum = cpu_to_le32(csum); | |
417 | } else { | |
418 | bif_output.imgheader->partition_header_offset = | |
419 | cpu_to_le32(parthdr_offset / 4); | |
420 | } | |
421 | bif_output.last_part = (void *)(bif_output.data + parthdr_offset); | |
422 | ||
423 | if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { | |
424 | bif_output.header->image_load = cpu_to_le32(bf->load); | |
425 | if (!bif_output.header->image_offset) | |
426 | bif_output.header->image_offset = | |
427 | cpu_to_le32(bf->offset); | |
775ed87a MS |
428 | bif_output.header->image_size = cpu_to_le32(len_padded); |
429 | bif_output.header->image_stored_size = cpu_to_le32(len_padded); | |
6915dcf3 AG |
430 | |
431 | bif_output.header->image_attributes &= ~HEADER_CPU_SELECT_MASK; | |
432 | switch (bf->dest_cpu) { | |
433 | default: | |
434 | case PART_ATTR_DEST_CPU_A53_0: | |
435 | if (bf->flags & BIF_FLAG_AARCH32) | |
436 | bif_output.header->image_attributes |= | |
437 | HEADER_CPU_SELECT_A53_32BIT; | |
438 | else | |
439 | bif_output.header->image_attributes |= | |
440 | HEADER_CPU_SELECT_A53_64BIT; | |
441 | break; | |
442 | case PART_ATTR_DEST_CPU_R5_0: | |
443 | bif_output.header->image_attributes |= | |
444 | HEADER_CPU_SELECT_R5_SINGLE; | |
445 | break; | |
446 | case PART_ATTR_DEST_CPU_R5_L: | |
447 | bif_output.header->image_attributes |= | |
448 | HEADER_CPU_SELECT_R5_DUAL; | |
449 | break; | |
450 | } | |
451 | } | |
452 | ||
453 | return 0; | |
454 | } | |
455 | ||
456 | /* Add .bit bitstream */ | |
457 | static int bif_add_bit(struct bif_entry *bf) | |
458 | { | |
459 | char *bit = read_full_file(bf->filename, NULL); | |
460 | char *bitbin; | |
461 | uint8_t initial_header[] = { 0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, | |
462 | 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x61 }; | |
463 | uint16_t len; | |
464 | uint32_t bitlen; | |
465 | int i; | |
466 | ||
467 | if (!bit) | |
468 | return -1; | |
469 | ||
470 | /* Skip initial header */ | |
471 | if (memcmp(bit, initial_header, sizeof(initial_header))) | |
472 | return -1; | |
473 | ||
474 | bit += sizeof(initial_header); | |
475 | ||
476 | /* Design name */ | |
477 | len = be16_to_cpu(*(uint16_t *)bit); | |
478 | bit += sizeof(uint16_t); | |
479 | debug("Design: %s\n", bit); | |
480 | bit += len; | |
481 | ||
482 | /* Device identifier */ | |
483 | if (*bit != 'b') | |
484 | return -1; | |
485 | bit++; | |
486 | len = be16_to_cpu(*(uint16_t *)bit); | |
487 | bit += sizeof(uint16_t); | |
488 | debug("Device: %s\n", bit); | |
489 | bit += len; | |
490 | ||
491 | /* Date */ | |
492 | if (*bit != 'c') | |
493 | return -1; | |
494 | bit++; | |
495 | len = be16_to_cpu(*(uint16_t *)bit); | |
496 | bit += sizeof(uint16_t); | |
497 | debug("Date: %s\n", bit); | |
498 | bit += len; | |
499 | ||
500 | /* Time */ | |
501 | if (*bit != 'd') | |
502 | return -1; | |
503 | bit++; | |
504 | len = be16_to_cpu(*(uint16_t *)bit); | |
505 | bit += sizeof(uint16_t); | |
506 | debug("Time: %s\n", bit); | |
507 | bit += len; | |
508 | ||
509 | /* Bitstream length */ | |
510 | if (*bit != 'e') | |
511 | return -1; | |
512 | bit++; | |
513 | bitlen = be32_to_cpu(*(uint32_t *)bit); | |
514 | bit += sizeof(uint32_t); | |
515 | bitbin = bit; | |
516 | ||
517 | debug("Bitstream Length: 0x%x\n", bitlen); | |
518 | for (i = 0; i < bitlen; i += sizeof(uint32_t)) { | |
519 | uint32_t *bitbin32 = (uint32_t *)&bitbin[i]; | |
afd3bf43 | 520 | *bitbin32 = __builtin_bswap32(*bitbin32); |
6915dcf3 AG |
521 | } |
522 | ||
523 | if (!bf->dest_dev) | |
524 | bf->dest_dev = PART_ATTR_DEST_DEVICE_PL; | |
525 | ||
526 | bf->load = 0xffffffff; | |
527 | bf->entry = 0; | |
528 | ||
529 | bf->flags |= 1ULL << BIF_FLAG_BIT_FILE; | |
530 | return bif_add_part(bf, bit, bitlen); | |
531 | } | |
532 | ||
533 | /* Add .bin bitstream */ | |
534 | static int bif_add_bin(struct bif_entry *bf) | |
535 | { | |
536 | size_t size; | |
537 | char *bin = read_full_file(bf->filename, &size); | |
538 | ||
539 | if (!bf->dest_dev) | |
540 | bf->dest_dev = PART_ATTR_DEST_DEVICE_PS; | |
541 | ||
542 | bf->flags |= 1ULL << BIF_FLAG_BIN_FILE; | |
543 | return bif_add_part(bf, bin, size); | |
544 | } | |
545 | ||
546 | /* Add elf file */ | |
547 | static char *elf2flat64(char *elf, size_t *flat_size, size_t *load_addr) | |
548 | { | |
549 | Elf64_Ehdr *ehdr; | |
550 | Elf64_Shdr *shdr; | |
551 | size_t min_addr = -1, max_addr = 0; | |
552 | char *flat; | |
553 | int i; | |
554 | ||
555 | ehdr = (void *)elf; | |
556 | shdr = (void *)(elf + le64_to_cpu(ehdr->e_shoff)); | |
557 | ||
558 | /* Look for smallest / biggest address */ | |
559 | for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++, shdr++) { | |
560 | if (!shdr->sh_size || !shdr->sh_addr || | |
561 | !(shdr->sh_flags & SHF_ALLOC) || | |
562 | (shdr->sh_type == SHT_NOBITS)) | |
563 | continue; | |
564 | ||
565 | if (le64_to_cpu(shdr->sh_addr) < min_addr) | |
566 | min_addr = le64_to_cpu(shdr->sh_addr); | |
567 | if ((le64_to_cpu(shdr->sh_addr) + le64_to_cpu(shdr->sh_size)) > | |
568 | max_addr) | |
569 | max_addr = le64_to_cpu(shdr->sh_addr) + | |
570 | le64_to_cpu(shdr->sh_size); | |
571 | } | |
572 | ||
573 | *load_addr = min_addr; | |
574 | *flat_size = max_addr - min_addr; | |
575 | flat = calloc(1, *flat_size); | |
576 | if (!flat) | |
577 | return NULL; | |
578 | ||
579 | shdr = (void *)(elf + le64_to_cpu(ehdr->e_shoff)); | |
580 | for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++, shdr++) { | |
581 | char *dst = flat + le64_to_cpu(shdr->sh_addr) - min_addr; | |
582 | char *src = elf + le64_to_cpu(shdr->sh_offset); | |
583 | ||
584 | if (!shdr->sh_size || !shdr->sh_addr || | |
585 | !(shdr->sh_flags & SHF_ALLOC)) | |
586 | continue; | |
587 | ||
588 | if (shdr->sh_type != SHT_NOBITS) | |
589 | memcpy(dst, src, le64_to_cpu(shdr->sh_size)); | |
590 | } | |
591 | ||
592 | return flat; | |
593 | } | |
594 | ||
595 | static char *elf2flat32(char *elf, size_t *flat_size, size_t *load_addr) | |
596 | { | |
597 | Elf32_Ehdr *ehdr; | |
598 | Elf32_Shdr *shdr; | |
599 | size_t min_addr = -1, max_addr = 0; | |
600 | char *flat; | |
601 | int i; | |
602 | ||
603 | ehdr = (void *)elf; | |
604 | shdr = (void *)(elf + le32_to_cpu(ehdr->e_shoff)); | |
605 | ||
606 | /* Look for smallest / biggest address */ | |
607 | for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++, shdr++) { | |
608 | if (!shdr->sh_size || !shdr->sh_addr || | |
609 | !(shdr->sh_flags & SHF_ALLOC) || | |
610 | (shdr->sh_type == SHT_NOBITS)) | |
611 | continue; | |
612 | ||
613 | if (le32_to_cpu(shdr->sh_addr) < min_addr) | |
614 | min_addr = le32_to_cpu(shdr->sh_addr); | |
615 | if ((le32_to_cpu(shdr->sh_addr) + le32_to_cpu(shdr->sh_size)) > | |
616 | max_addr) | |
617 | max_addr = le32_to_cpu(shdr->sh_addr) + | |
618 | le32_to_cpu(shdr->sh_size); | |
619 | } | |
620 | ||
621 | *load_addr = min_addr; | |
622 | *flat_size = max_addr - min_addr; | |
623 | flat = calloc(1, *flat_size); | |
624 | if (!flat) | |
625 | return NULL; | |
626 | ||
627 | shdr = (void *)(elf + le32_to_cpu(ehdr->e_shoff)); | |
628 | for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++, shdr++) { | |
629 | char *dst = flat + le32_to_cpu(shdr->sh_addr) - min_addr; | |
630 | char *src = elf + le32_to_cpu(shdr->sh_offset); | |
631 | ||
632 | if (!shdr->sh_size || !shdr->sh_addr || | |
633 | !(shdr->sh_flags & SHF_ALLOC)) | |
634 | continue; | |
635 | ||
636 | if (shdr->sh_type != SHT_NOBITS) | |
637 | memcpy(dst, src, le32_to_cpu(shdr->sh_size)); | |
638 | } | |
639 | ||
640 | return flat; | |
641 | } | |
642 | ||
643 | static int bif_add_elf(struct bif_entry *bf) | |
644 | { | |
645 | size_t size; | |
646 | size_t elf_size; | |
647 | char *elf; | |
648 | char *flat; | |
649 | size_t load_addr; | |
650 | Elf32_Ehdr *ehdr32; | |
651 | Elf64_Ehdr *ehdr64; | |
652 | ||
653 | elf = read_full_file(bf->filename, &elf_size); | |
654 | if (!elf) | |
655 | return -1; | |
656 | ||
657 | ehdr32 = (void *)elf; | |
658 | ehdr64 = (void *)elf; | |
659 | ||
660 | switch (ehdr32->e_ident[EI_CLASS]) { | |
661 | case ELFCLASS32: | |
662 | flat = elf2flat32(elf, &size, &load_addr); | |
663 | bf->entry = le32_to_cpu(ehdr32->e_entry); | |
664 | bf->flags |= 1ULL << BIF_FLAG_AARCH32; | |
665 | break; | |
666 | case ELFCLASS64: | |
667 | flat = elf2flat64(elf, &size, &load_addr); | |
668 | bf->entry = le64_to_cpu(ehdr64->e_entry); | |
669 | break; | |
670 | default: | |
671 | printf("Unknown ELF class: %d\n", ehdr32->e_ident[EI_CLASS]); | |
672 | return -1; | |
673 | } | |
674 | ||
675 | if (!flat) | |
676 | return -1; | |
677 | ||
678 | bf->load = load_addr; | |
679 | if (!bf->dest_dev) | |
680 | bf->dest_dev = PART_ATTR_DEST_DEVICE_PS; | |
681 | ||
682 | bf->flags |= 1ULL << BIF_FLAG_ELF_FILE; | |
683 | return bif_add_part(bf, flat, size); | |
684 | } | |
685 | ||
686 | static const struct bif_file_type bif_file_types[] = { | |
687 | { | |
688 | .name = "bitstream (.bit)", | |
689 | .header = 0x00090ff0, | |
690 | .add = bif_add_bit, | |
691 | }, | |
692 | ||
693 | { | |
694 | .name = "ELF", | |
695 | .header = 0x7f454c46, | |
696 | .add = bif_add_elf, | |
697 | }, | |
698 | ||
699 | /* Anything else is a .bin file */ | |
700 | { | |
701 | .name = ".bin", | |
702 | .add = bif_add_bin, | |
703 | }, | |
704 | }; | |
705 | ||
706 | static int bif_fsbl_config(struct bif_entry *fsbl_config, | |
707 | struct bif_entry *entries, int nr_entries) | |
708 | { | |
709 | int i; | |
710 | int config_set = 0; | |
711 | struct { | |
712 | const char *name; | |
713 | uint64_t flags; | |
714 | uint64_t dest_cpu; | |
715 | } configs[] = { | |
716 | { .name = "a5x_x64", .dest_cpu = PART_ATTR_DEST_CPU_A53_0 }, | |
717 | { .name = "a53_x64", .dest_cpu = PART_ATTR_DEST_CPU_A53_0 }, | |
718 | { .name = "a5x_x32", .dest_cpu = PART_ATTR_DEST_CPU_A53_0, | |
719 | .flags = 1ULL << BIF_FLAG_AARCH32 }, | |
720 | { .name = "a53_x32", .dest_cpu = PART_ATTR_DEST_CPU_A53_0, | |
721 | .flags = 1ULL << BIF_FLAG_AARCH32 }, | |
722 | { .name = "r5_single", .dest_cpu = PART_ATTR_DEST_CPU_R5_0 }, | |
723 | { .name = "r5_dual", .dest_cpu = PART_ATTR_DEST_CPU_R5_L }, | |
724 | }; | |
725 | ||
726 | /* Set target CPU of bootloader entry */ | |
727 | for (i = 0; i < nr_entries; i++) { | |
728 | struct bif_entry *b = &entries[i]; | |
729 | const char *config_attr = fsbl_config->filename; | |
730 | int j; | |
731 | ||
732 | if (!(b->flags & (1ULL << BIF_FLAG_BOOTLOADER))) | |
733 | continue; | |
734 | ||
735 | for (j = 0; j < ARRAY_SIZE(configs); j++) { | |
736 | if (!strncmp(config_attr, configs[j].name, | |
737 | strlen(configs[j].name))) { | |
738 | b->dest_cpu = configs[j].dest_cpu; | |
739 | b->flags |= configs[j].flags; | |
740 | config_set = 1; | |
741 | } | |
742 | } | |
743 | ||
744 | if (!config_set) { | |
745 | printf("ERROR: Unsupported fsbl_config: %s\n", | |
746 | config_attr); | |
747 | return -1; | |
748 | } | |
749 | } | |
750 | ||
751 | if (!config_set) { | |
752 | printf("ERROR: fsbl_config w/o bootloader\n"); | |
753 | return -1; | |
754 | } | |
755 | ||
756 | return 0; | |
757 | } | |
758 | ||
759 | static const struct bif_flags *find_flag(char *str) | |
760 | { | |
761 | const struct bif_flags *bf; | |
762 | int i; | |
763 | ||
764 | for (i = 0; i < ARRAY_SIZE(bif_flags); i++) { | |
765 | bf = &bif_flags[i]; | |
766 | if (!strncmp(bf->name, str, strlen(bf->name))) | |
767 | return bf; | |
768 | } | |
769 | ||
770 | printf("ERROR: Flag '%s' not found\n", str); | |
771 | ||
772 | return NULL; | |
773 | } | |
774 | ||
775 | static int bif_open_file(struct bif_entry *entry) | |
776 | { | |
777 | int fd = open(entry->filename, O_RDONLY); | |
778 | ||
779 | if (fd < 0) | |
780 | printf("Error opening file %s\n", entry->filename); | |
781 | ||
782 | return fd; | |
783 | } | |
784 | ||
785 | static const struct bif_file_type *get_file_type(struct bif_entry *entry) | |
786 | { | |
787 | int fd = bif_open_file(entry); | |
788 | uint32_t header; | |
789 | int i; | |
790 | ||
791 | if (fd < 0) | |
792 | return NULL; | |
793 | ||
794 | if (read(fd, &header, sizeof(header)) != sizeof(header)) { | |
795 | printf("Error reading file %s", entry->filename); | |
796 | return NULL; | |
797 | } | |
798 | ||
799 | close(fd); | |
800 | ||
801 | for (i = 0; i < ARRAY_SIZE(bif_file_types); i++) { | |
802 | const struct bif_file_type *type = &bif_file_types[i]; | |
803 | ||
804 | if (!type->header) | |
805 | return type; | |
806 | if (type->header == be32_to_cpu(header)) | |
807 | return type; | |
808 | } | |
809 | ||
810 | return NULL; | |
811 | } | |
812 | ||
813 | #define NEXT_CHAR(str, chr) ({ \ | |
814 | char *_n = strchr(str, chr); \ | |
815 | if (!_n) \ | |
816 | goto err; \ | |
817 | _n; \ | |
818 | }) | |
819 | ||
820 | static char *skip_whitespace(char *str) | |
821 | { | |
822 | while (*str == ' ' || *str == '\t') | |
823 | str++; | |
824 | ||
825 | return str; | |
826 | } | |
827 | ||
828 | int zynqmpbif_copy_image(int outfd, struct image_tool_params *mparams) | |
829 | { | |
830 | char *bif, *bifp, *bifpn; | |
831 | char *line; | |
832 | struct bif_entry entries[32] = { { 0 } }; | |
833 | int nr_entries = 0; | |
834 | struct bif_entry *entry = entries; | |
835 | size_t len; | |
836 | int i; | |
837 | uint32_t csum; | |
838 | int bldr = -1; | |
839 | ||
840 | bif_init(); | |
841 | ||
842 | /* Read .bif input file */ | |
843 | bif = read_full_file(mparams->datafile, NULL); | |
844 | if (!bif) | |
845 | goto err; | |
846 | ||
847 | /* Interpret .bif file */ | |
848 | bifp = bif; | |
849 | ||
850 | /* A bif description starts with a { section */ | |
851 | bifp = NEXT_CHAR(bifp, '{') + 1; | |
852 | ||
853 | /* Read every line */ | |
854 | while (1) { | |
855 | bifpn = NEXT_CHAR(bifp, '\n'); | |
856 | ||
857 | if (bifpn[-1] == '\r') | |
858 | bifpn[-1] = '\0'; | |
859 | ||
860 | *bifpn = '\0'; | |
861 | bifpn++; | |
862 | line = bifp; | |
863 | ||
864 | line = skip_whitespace(line); | |
865 | ||
866 | /* Attributes? */ | |
867 | if (*line == '[') { | |
868 | line++; | |
869 | while (1) { | |
870 | const struct bif_flags *bf; | |
871 | ||
872 | line = skip_whitespace(line); | |
873 | bf = find_flag(line); | |
874 | if (!bf) | |
875 | goto err; | |
876 | ||
877 | line += strlen(bf->name); | |
878 | if (bf->parse) | |
879 | line = bf->parse(line, entry); | |
880 | else | |
881 | entry->flags |= 1ULL << bf->flag; | |
882 | ||
883 | if (!line) | |
884 | goto err; | |
885 | ||
886 | /* Go to next attribute or quit */ | |
887 | if (*line == ']') { | |
888 | line++; | |
889 | break; | |
890 | } | |
891 | if (*line == ',') | |
892 | line++; | |
893 | } | |
894 | } | |
895 | ||
896 | /* End of image description */ | |
897 | if (*line == '}') | |
898 | break; | |
899 | ||
900 | if (*line) { | |
901 | line = skip_whitespace(line); | |
902 | entry->filename = line; | |
903 | nr_entries++; | |
904 | entry++; | |
905 | } | |
906 | ||
907 | /* Use next line */ | |
908 | bifp = bifpn; | |
909 | } | |
910 | ||
911 | for (i = 0; i < nr_entries; i++) { | |
912 | debug("Entry flags=%#lx name=%s\n", entries[i].flags, | |
913 | entries[i].filename); | |
914 | } | |
915 | ||
916 | /* | |
917 | * Some entries are actually configuration option for other ones, | |
918 | * let's apply them in an intermediate step. | |
919 | */ | |
920 | for (i = 0; i < nr_entries; i++) { | |
921 | struct bif_entry *entry = &entries[i]; | |
922 | ||
923 | if (entry->flags & (1ULL << BIF_FLAG_FSBL_CONFIG)) | |
924 | if (bif_fsbl_config(entry, entries, nr_entries)) | |
925 | goto err; | |
926 | } | |
927 | ||
928 | /* Make sure PMUFW comes before bootloader */ | |
929 | for (i = 0; i < nr_entries; i++) { | |
930 | struct bif_entry *entry = &entries[i]; | |
931 | ||
932 | if (entry->flags & (1ULL << BIF_FLAG_BOOTLOADER)) | |
933 | bldr = i; | |
934 | if (entry->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) { | |
935 | if (bldr >= 0) { | |
936 | struct bif_entry tmp = *entry; | |
937 | ||
938 | *entry = entries[bldr]; | |
939 | entries[bldr] = tmp; | |
940 | } | |
941 | } | |
942 | } | |
943 | ||
944 | for (i = 0; i < nr_entries; i++) { | |
945 | struct bif_entry *entry = &entries[i]; | |
946 | const struct bif_file_type *type; | |
947 | int r; | |
948 | ||
949 | if (entry->flags & (1ULL << BIF_FLAG_FSBL_CONFIG)) | |
950 | continue; | |
951 | ||
952 | type = get_file_type(entry); | |
953 | if (!type) | |
954 | goto err; | |
955 | ||
956 | debug("type=%s file=%s\n", type->name, entry->filename); | |
957 | r = type->add(entry); | |
958 | if (r) | |
959 | goto err; | |
960 | } | |
961 | ||
962 | /* Calculate checksums */ | |
963 | csum = zynqmp_csum(&bif_output.header->width_detection, | |
964 | &bif_output.header->checksum); | |
965 | bif_output.header->checksum = cpu_to_le32(csum); | |
966 | ||
967 | if (bif_output.imgheader) { | |
968 | csum = zynqmp_csum(bif_output.imgheader, | |
969 | &bif_output.imgheader->checksum); | |
970 | bif_output.imgheader->checksum = cpu_to_le32(csum); | |
971 | } | |
972 | ||
973 | /* Write headers and components */ | |
974 | if (lseek(outfd, 0, SEEK_SET) != 0) | |
975 | goto err; | |
976 | ||
977 | len = bif_output.data_len; | |
978 | bifp = bif_output.data; | |
979 | while (len) { | |
980 | int r; | |
981 | ||
982 | r = write(outfd, bifp, len); | |
983 | if (r < 0) | |
984 | goto err; | |
985 | len -= r; | |
986 | bifp += r; | |
987 | } | |
988 | ||
989 | return 0; | |
990 | ||
991 | err: | |
992 | fprintf(stderr, "Error: Failed to create image.\n"); | |
993 | return -1; | |
994 | } | |
995 | ||
996 | /* Needs to be stubbed out so we can print after creation */ | |
997 | static void zynqmpbif_set_header(void *ptr, struct stat *sbuf, int ifd, | |
998 | struct image_tool_params *params) | |
999 | { | |
1000 | } | |
1001 | ||
1002 | static struct zynqmp_header zynqmpimage_header; | |
1003 | ||
1004 | U_BOOT_IMAGE_TYPE( | |
1005 | zynqmpbif, | |
1006 | "Xilinx ZynqMP Boot Image support (bif)", | |
1007 | sizeof(struct zynqmp_header), | |
1008 | (void *)&zynqmpimage_header, | |
1009 | zynqmpbif_check_params, | |
1010 | NULL, | |
1011 | zynqmpimage_print_header, | |
1012 | zynqmpbif_set_header, | |
1013 | NULL, | |
1014 | zynqmpbif_check_image_types, | |
1015 | NULL, | |
1016 | NULL | |
1017 | ); |