blob: d6bfdd9f8d572e604a9ef65a60217f738a3f6513 [file] [log] [blame]
Harry Liebelf58ad362014-01-10 18:00:33 +00001/*
2 * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <errno.h>
32#include <getopt.h> /* getopt_long() is a GNU extention */
33#include <stdbool.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/stat.h>
38#include "fip_create.h"
39#include "firmware_image_package.h"
40
41file_info files[MAX_FILES];
42unsigned file_info_count = 0;
43uuid_t uuid_null = {0};
44
45/*
46 * TODO: Add ability to specify and flag different file types.
47 * Add flags to the toc_entry?
48 * const char* format_type_str[] = { "RAW", "ELF", "PIC" };
49 */
50
51/* Currently only BL2 and BL31 images are supported. */
52static entry_lookup_list toc_entry_lookup_list[] = {
53 { "Trusted Boot Firmware BL2", UUID_TRUSTED_BOOT_FIRMWARE_BL2,
54 "bl2", NULL, FLAG_FILENAME },
55 { "SCP Firmware BL3-0", UUID_SCP_FIRMWARE_BL30,
56 "bl30", NULL, FLAG_FILENAME},
57 { "EL3 Runtime Firmware BL3-1", UUID_EL3_RUNTIME_FIRMWARE_BL31,
58 "bl31", NULL, FLAG_FILENAME},
59 { "Secure Payload BL3-2 (Trusted OS)", UUID_SECURE_PAYLOAD_BL32,
60 "bl32", NULL, FLAG_FILENAME},
61 { "Non-Trusted Firmware BL3-3", UUID_NON_TRUSTED_FIRMWARE_BL33,
62 "bl33", NULL, FLAG_FILENAME},
63 { NULL, {0}, 0 }
64};
65
66
67/* Return 0 for equal uuids */
68static inline int compare_uuids(const uuid_t *uuid1, const uuid_t *uuid2)
69{
70 return memcmp(uuid1, uuid2, sizeof(uuid_t));
71}
72
73
74static inline void copy_uuid(uuid_t *to_uuid, const uuid_t *from_uuid)
75{
76 memcpy(to_uuid, from_uuid, sizeof(uuid_t));
77}
78
79
80static void print_usage(void)
81{
82 entry_lookup_list *entry = toc_entry_lookup_list;
83
84 printf("fip_create FIP_FILENAME\n\n");
85 printf("\tThis tool is used to create a Firmware Image Package.\n\n");
86 printf("\t--help: this help\n");
87 printf("\t--dump: print contents of FIP\n\n");
88 printf("\tComponents that can be added/updated:\n");
89 for (; entry->command_line_name != NULL; entry++) {
90 printf("\t %s:\n\t --%s ",
91 entry->name, entry->command_line_name);
92 if (entry->flags & FLAG_FILENAME) {
93 printf("FILENAME");
94 }
95 printf("\n");
96 }
97}
98
99
100static entry_lookup_list *get_entry_lookup_from_uuid(const uuid_t *uuid)
101{
102 unsigned int lookup_index = 0;
103
104 while (toc_entry_lookup_list[lookup_index].command_line_name != NULL) {
105 if (compare_uuids(&toc_entry_lookup_list[lookup_index].name_uuid,
106 uuid) == 0) {
107 return &toc_entry_lookup_list[lookup_index];
108 }
109 lookup_index++;
110 }
111 return NULL;
112}
113
114
115static file_info *find_file_info_from_uuid(const uuid_t *uuid)
116{
117 int index;
118
119 for (index = 0; index < file_info_count; index++) {
120 if (compare_uuids(&files[index].name_uuid, uuid) == 0) {
121 return &files[index];
122 }
123 }
124 return NULL;
125}
126
127
128static int add_file_info_entry(entry_lookup_list *lookup_entry, char *filename)
129{
130 file_info *file_info_entry;
131 int error;
132 struct stat file_status;
133 bool is_new_entry = false;
134
135 /* Check if the file already exists in the array */
136 file_info_entry = find_file_info_from_uuid(&lookup_entry->name_uuid);
137 if (file_info_entry == NULL) {
138 /* The file does not exist in the current list; take the next
139 * one available in the file_info list. 'file_info_count' is
140 * incremented in case of successful update at the end of the
141 * function.
142 */
143 file_info_entry = &files[file_info_count];
144 is_new_entry = true;
145
146 /* Copy the uuid for the new entry */
147 copy_uuid(&file_info_entry->name_uuid,
148 &lookup_entry->name_uuid);
149 }
150
151 /* Get the file information for entry */
152 error = stat(filename, &file_status);
153 if (error != 0) {
154 printf("Error: Cannot get information for file \"%s\": %s\n",
155 filename, strerror(errno));
156 return errno;
157 }
158 file_info_entry->filename = filename;
159 file_info_entry->size = (unsigned int)file_status.st_size;
160 file_info_entry->entry = lookup_entry;
161
162 /* Increment the file_info counter on success if it is new file entry */
163 if (is_new_entry) {
164 file_info_count++;
165
166 /* Ensure we do not overflow */
167 if (file_info_count > MAX_FILES) {
168 printf("ERROR: Too many files in Package\n");
169 return 1;
170 }
171 }
172
173 return 0;
174}
175
176
177static int write_memory_to_file(const uint8_t *start, const char *filename,
178 unsigned int size)
179{
180 FILE *stream;
181 unsigned int bytes_written;
182
183 /* Write the packed file out to the filesystem */
184 stream = fopen(filename, "r+");
185 if (stream == NULL) {
186 stream = fopen(filename, "w");
187 if (stream == NULL) {
188 printf("Error: Cannot create output file \"%s\": %s\n",
189 filename, strerror(errno));
190 return errno;
191 } else {
192 printf("Creating \"%s\"\n", filename);
193 }
194 } else {
195 printf("Updating \"%s\"\n", filename);
196 }
197
198 bytes_written = fwrite(start, sizeof(uint8_t), size, stream);
199 fclose(stream);
200
201 if (bytes_written != size) {
202 printf("Error: Incorrect write for file \"%s\": Size=%u,"
203 "Written=%u bytes.\n", filename, size, bytes_written);
204 return EIO;
205 }
206
207 return 0;
208}
209
210
211static int read_file_to_memory(void *memory, const file_info *info)
212{
213 FILE *stream;
214 unsigned int bytes_read;
215
216 /* If the file_info is defined by its filename we need to load it */
217 if (info->filename) {
218 /* Read image from filesystem */
219 stream = fopen(info->filename, "r");
220 if (stream == NULL) {
221 printf("Error: Cannot open file \"%s\": %s\n",
222 info->filename, strerror(errno));
223 return errno;
224 }
225
226 bytes_read = (unsigned int)fread(memory, sizeof(uint8_t),
227 info->size, stream);
228 fclose(stream);
229 if (bytes_read != info->size) {
230 printf("Error: Incomplete read for file \"%s\":"
231 "Size=%u, Read=%u bytes.\n", info->filename,
232 info->size, bytes_read);
233 return EIO;
234 }
235 } else {
236 if (info->image_buffer == NULL) {
237 printf("ERROR: info->image_buffer = NULL\n");
238 return EIO;
239 }
240 /* Copy the file_info buffer (extracted from the existing
241 * image package) into the new buffer.
242 */
243 memcpy(memory, info->image_buffer, info->size);
244 }
245
246 return 0;
247}
248
249
250/* Create the image package file */
251static int pack_images(const char *fip_filename)
252{
253 int status;
254 uint8_t *fip_base_address;
255 void *entry_address;
256 fip_toc_header *toc_header;
257 fip_toc_entry *toc_entry;
258 unsigned int entry_index;
259 unsigned int toc_size;
260 unsigned int fip_size;
261 unsigned int entry_offset_address;
262 unsigned int payload_size = 0;
263
264 /* Validate filename */
265 if ((fip_filename == NULL) || (strcmp(fip_filename, "") == 0)) {
266 return EINVAL;
267 }
268
269 /* Payload size calculation */
270 for (entry_index = 0; entry_index < file_info_count; entry_index++) {
271 payload_size += files[entry_index].size;
272 }
273
274 /* Allocate memory for entire package, including the final null entry */
275 toc_size = (sizeof(fip_toc_header) +
276 (sizeof(fip_toc_entry) * (file_info_count + 1)));
277 fip_size = toc_size + payload_size;
278 fip_base_address = malloc(fip_size);
279 if (fip_base_address == NULL) {
280 printf("Error: Can't allocate enough memory to create package."
281 "Process aborted.\n");
282 return ENOMEM;
283 }
284 memset(fip_base_address, 0, fip_size);
285
286 /* Create ToC Header */
287 toc_header = (fip_toc_header *)fip_base_address;
288 toc_header->name = TOC_HEADER_NAME;
289 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
290 toc_header->flags = 0;
291
292 toc_entry = (fip_toc_entry *)(fip_base_address +
293 sizeof(fip_toc_header));
294
295 /* Calculate the starting address of the first image, right after the
296 * toc header.
297 */
298 entry_offset_address = toc_size;
299 entry_index = 0;
300
301 /* Create the package in memory. */
302 for (entry_index = 0; entry_index < file_info_count; entry_index++) {
303 entry_address = (fip_base_address + entry_offset_address);
304 status = read_file_to_memory(entry_address,
305 &files[entry_index]);
306 if (status != 0) {
307 printf("Error: While reading \"%s\" from filesystem.\n",
308 files[entry_index].filename);
309 return status;
310 }
311
312 copy_uuid(&toc_entry->uuid, &files[entry_index].name_uuid);
313 toc_entry->offset_address = entry_offset_address;
314 toc_entry->size = files[entry_index].size;
315 toc_entry->flags = 0;
316 entry_offset_address += toc_entry->size;
317 toc_entry++;
318 }
319
320 /* Add a null uuid entry to mark the end of toc entries */
321 copy_uuid(&toc_entry->uuid, &uuid_null);
322 toc_entry->offset_address = entry_offset_address;
323 toc_entry->size = 0;
324 toc_entry->flags = 0;
325
326 /* Save the package to file */
327 status = write_memory_to_file(fip_base_address, fip_filename, fip_size);
328 if (status != 0) {
329 printf("Error: Failed while writing package to file \"%s\" "
330 "with status=%d.\n", fip_filename, status);
331 return status;
332 }
333 return 0;
334}
335
336
337static void dump_toc(void)
338{
339 unsigned int index = 0;
340 unsigned int image_offset;
341 unsigned int image_size = 0;
342
343 image_offset = sizeof(fip_toc_header) +
344 (sizeof(fip_toc_entry) * (file_info_count + 1));
345
346 printf("Firmware Image Package ToC:\n");
347 printf("---------------------------\n");
348 for (index = 0; index < file_info_count; index++) {
349 if (files[index].entry) {
350 printf("- %s: ", files[index].entry->name);
351 } else {
352 printf("- Unknown entry: ");
353 }
354 image_size = files[index].size;
355
356 printf("offset=0x%X, size=0x%X\n", image_offset, image_size);
357 image_offset += image_size;
358
359 if (files[index].filename) {
360 printf(" file: '%s'\n", files[index].filename);
361 }
362 }
363 printf("---------------------------\n");
364}
365
366
367/* Read and load existing package into memory. */
368static int parse_fip(const char *fip_filename)
369{
370 FILE *fip;
371 char *fip_buffer;
372 char *fip_buffer_end;
373 int fip_size, read_fip_size;
374 fip_toc_header *toc_header;
375 fip_toc_entry *toc_entry;
376 bool found_last_toc_entry = false;
377 file_info *file_info_entry;
378 int status = -1;
379 struct stat st;
380
381 fip = fopen(fip_filename, "r");
382 if (fip == NULL) {
383 /* If the fip does not exist just return, it should not be
384 * considered as an error. The package will be created later
385 */
386 status = 0;
387 goto parse_fip_return;
388 }
389
390 if (stat(fip_filename, &st) != 0) {
391 status = errno;
392 goto parse_fip_fclose;
393 } else {
394 fip_size = (int)st.st_size;
395 }
396
397 /* Allocate a buffer to read the package */
398 fip_buffer = (char *)malloc(fip_size);
399 if (fip_buffer == NULL) {
400 printf("ERROR: Cannot allocate %d bytes.\n", fip_size);
401 status = errno;
402 goto parse_fip_fclose;
403 }
404 fip_buffer_end = fip_buffer + fip_size;
405
406 /* Read the file */
407 read_fip_size = fread(fip_buffer, sizeof(char), fip_size, fip);
408 if (read_fip_size != fip_size) {
409 printf("ERROR: Cannot read the FIP.\n");
410 status = EIO;
411 goto parse_fip_free;
412 }
413 fclose(fip);
414 fip = NULL;
415
416 /* The package must at least contain the ToC Header */
417 if (fip_size < sizeof(fip_toc_header)) {
418 printf("ERROR: Given FIP is smaller than the ToC header.\n");
419 status = EINVAL;
420 goto parse_fip_free;
421 }
422 /* Set the ToC Header at the base of the buffer */
423 toc_header = (fip_toc_header *)fip_buffer;
424 /* The first toc entry should be just after the ToC header */
425 toc_entry = (fip_toc_entry *)(toc_header + 1);
426
427 /* While the ToC entry is contained into the buffer */
428 int cnt = 0;
429 while (((char *)toc_entry + sizeof(fip_toc_entry)) < fip_buffer_end) {
430 cnt++;
431 /* Check if the ToC Entry is the last one */
432 if (compare_uuids(&toc_entry->uuid, &uuid_null) == 0) {
433 found_last_toc_entry = true;
434 status = 0;
435 break;
436 }
437
438 /* Add the entry into file_info */
439
440 /* Get the new entry in the array and clear it */
441 file_info_entry = &files[file_info_count++];
442 memset(file_info_entry, 0, sizeof(file_info));
443
444 /* Copy the info from the ToC entry */
445 copy_uuid(&file_info_entry->name_uuid, &toc_entry->uuid);
446 file_info_entry->image_buffer = fip_buffer +
447 toc_entry->offset_address;
448 file_info_entry->size = toc_entry->size;
449
450 /* Check if there is a corresponding entry in lookup table */
451 file_info_entry->entry =
452 get_entry_lookup_from_uuid(&toc_entry->uuid);
453
454 /* Go to the next ToC entry */
455 toc_entry++;
456 }
457
458 if (!found_last_toc_entry) {
459 printf("ERROR: Given FIP does not have an end ToC entry.\n");
460 status = EINVAL;
461 goto parse_fip_free;
462 } else {
463 /* All is well, we should not free any of the loaded images */
464 goto parse_fip_fclose;
465 }
466
467 parse_fip_free:
468 if (fip_buffer != NULL) {
469 free(fip_buffer);
470 fip_buffer = NULL;
471 }
472
473 parse_fip_fclose:
474 if (fip != NULL) {
475 fclose(fip);
476 }
477
478 parse_fip_return:
479 return status;
480}
481
482
483/* Parse all command-line options and return the FIP name if present. */
484static char *get_filename(int argc, char **argv, struct option *options)
485{
486 int c;
487 char *filename = NULL;
488
489 /* Reset option pointer so we parse all args. starts at 1.
490 * The filename is the only argument that does not have an option flag.
491 */
492 optind = 1;
493 while (1) {
494 c = getopt_long(argc, argv, "", options, NULL);
495 if (c == -1)
496 break;
497
498 if (c == '?') {
499 /* Failed to parse an option. Fail. */
500 return NULL;
501 }
502 }
503
504 /* Only one argument left then it is the filename.
505 * We dont expect any other options
506 */
507 if (optind + 1 == argc) {
508 filename = argv[optind];
509 } else {
510 printf("ERROR: Too many arguments.\n");
511 }
512
513 return filename;
514}
515
516
517/* Work through command-line options */
518static int parse_cmdline(int argc, char **argv, struct option *options,
519 int *do_pack)
520{
521 int c;
522 int status = 0;
523 int option_index = 0;
524 entry_lookup_list *lookup_entry;
525 int do_dump = 0;
526
527 /* restart parse to process all options. starts at 1. */
528 optind = 1;
529 while (1) {
530 c = getopt_long(argc, argv, "", options, &option_index);
531 if (c == -1)
532 break;
533
534 switch (c) {
535 case 0:
536 /* if this is --dump, set action and continue */
537 if (strcmp(options[option_index].name, "dump") == 0) {
538 do_dump = 1;
539 continue;
540 }
541
542 if (optarg) {
543 /* Does the option expect a filename. */
544 lookup_entry = &toc_entry_lookup_list[option_index];
545 if (lookup_entry->flags & FLAG_FILENAME) {
546 status = add_file_info_entry(lookup_entry, optarg);
547 if (status != 0) {
548 printf("Failed to process %s\n",
549 options[option_index].name);
550 break;
551 } else {
552 /* Update package */
553 *do_pack = 1;
554 }
555 }
556 }
557 break;
558 default:
559 /* Unrecognised options are caught in get_filename() */
560 break;
561 }
562 }
563
564
565 /* Do not dump toc if we have an error as it could hide the error */
566 if ((status == 0) && (do_dump)) {
567 dump_toc();
568 }
569
570 return status;
571
572}
573
574int main(int argc, char **argv)
575{
576 int i;
577 int status;
578 char *fip_filename;
579 int do_pack = 0;
580
581 /* Clear file list table. */
582 memset(files, 0, sizeof(files));
583
584 /* Initialise for getopt_long().
585 * Use image table as defined at top of file to get options.
586 * Add 'dump' option and end marker.
587 */
588 static struct option long_options[(sizeof(toc_entry_lookup_list)/
589 sizeof(entry_lookup_list)) + 1];
590
591 for (i = 0;
592 /* -1 because we dont want to process end marker in toc table */
593 i < sizeof(toc_entry_lookup_list)/sizeof(entry_lookup_list) - 1;
594 i++) {
595 long_options[i].name = toc_entry_lookup_list[i].command_line_name;
596 /* The only flag defined at the moment is for a FILENAME */
597 long_options[i].has_arg = toc_entry_lookup_list[i].flags ? 1 : 0;
598 long_options[i].flag = 0;
599 long_options[i].val = 0;
600 }
601
602 /* Add '--dump' option */
603 long_options[i].name = "dump";
604 long_options[i].has_arg = 0;
605 long_options[i].flag = 0;
606 long_options[i].val = 0;
607
608 /* Zero the last entry (required) */
609 long_options[++i].name = 0;
610 long_options[i].has_arg = 0;
611 long_options[i].flag = 0;
612 long_options[i].val = 0;
613
614#ifdef DEBUG
615 /* Print all supported options */
616 for (i = 0; i < sizeof(long_options)/sizeof(struct option); i++) {
617 printf("long opt (%d) : name = %s\n", i, long_options[i].name);
618 }
619#endif /* DEBUG */
620
621 /* As the package may already exist and is to be updated we need to get
622 * the filename from the arguments and load from it.
623 * NOTE: As this is the first function to look at the program arguments
624 * it causes a failure if bad options were provided.
625 */
626 fip_filename = get_filename(argc, argv, long_options);
627 if (fip_filename == NULL) {
628 print_usage();
629 return 0;
630 }
631
632 /* Try to open the file and load it into memory */
633 status = parse_fip(fip_filename);
634 if (status != 0) {
635 return status;
636 }
637
638 /* Work through provided program arguments and perform actions */
639 status = parse_cmdline(argc, argv, long_options, &do_pack);
640 if (status != 0) {
641 return status;
642 };
643
644 /* Processed all command line options. Create/update the package if
645 * required.
646 */
647 if (do_pack) {
648 status = pack_images(fip_filename);
649 if (status != 0) {
650 printf("Failed to create package (status = %d).\n",
651 status);
652 }
653 }
654
655 return status;
656}