blob: 71ee1de93224a5590879dc9715526a7d5193bcfd [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
Sandrine Bailleux29e54422014-03-19 13:09:54 +000041/* Values returned by getopt() as part of the command line parsing */
42#define OPT_TOC_ENTRY 0
43#define OPT_DUMP 1
Sandrine Bailleux8c60fee2014-03-19 13:21:42 +000044#define OPT_HELP 2
Sandrine Bailleux29e54422014-03-19 13:09:54 +000045
Harry Liebelf58ad362014-01-10 18:00:33 +000046file_info files[MAX_FILES];
47unsigned file_info_count = 0;
48uuid_t uuid_null = {0};
49
50/*
51 * TODO: Add ability to specify and flag different file types.
52 * Add flags to the toc_entry?
53 * const char* format_type_str[] = { "RAW", "ELF", "PIC" };
54 */
55
56/* Currently only BL2 and BL31 images are supported. */
57static entry_lookup_list toc_entry_lookup_list[] = {
58 { "Trusted Boot Firmware BL2", UUID_TRUSTED_BOOT_FIRMWARE_BL2,
59 "bl2", NULL, FLAG_FILENAME },
60 { "SCP Firmware BL3-0", UUID_SCP_FIRMWARE_BL30,
61 "bl30", NULL, FLAG_FILENAME},
62 { "EL3 Runtime Firmware BL3-1", UUID_EL3_RUNTIME_FIRMWARE_BL31,
63 "bl31", NULL, FLAG_FILENAME},
64 { "Secure Payload BL3-2 (Trusted OS)", UUID_SECURE_PAYLOAD_BL32,
65 "bl32", NULL, FLAG_FILENAME},
66 { "Non-Trusted Firmware BL3-3", UUID_NON_TRUSTED_FIRMWARE_BL33,
67 "bl33", NULL, FLAG_FILENAME},
68 { NULL, {0}, 0 }
69};
70
71
72/* Return 0 for equal uuids */
73static inline int compare_uuids(const uuid_t *uuid1, const uuid_t *uuid2)
74{
75 return memcmp(uuid1, uuid2, sizeof(uuid_t));
76}
77
78
79static inline void copy_uuid(uuid_t *to_uuid, const uuid_t *from_uuid)
80{
81 memcpy(to_uuid, from_uuid, sizeof(uuid_t));
82}
83
84
85static void print_usage(void)
86{
87 entry_lookup_list *entry = toc_entry_lookup_list;
88
Sandrine Bailleux8c60fee2014-03-19 13:21:42 +000089 printf("Usage: fip_create [options] FIP_FILENAME\n\n");
Harry Liebelf58ad362014-01-10 18:00:33 +000090 printf("\tThis tool is used to create a Firmware Image Package.\n\n");
Sandrine Bailleux8c60fee2014-03-19 13:21:42 +000091 printf("Options:\n");
92 printf("\t--help: Print this help message and exit\n");
93 printf("\t--dump: Print contents of FIP\n\n");
Harry Liebelf58ad362014-01-10 18:00:33 +000094 printf("\tComponents that can be added/updated:\n");
95 for (; entry->command_line_name != NULL; entry++) {
Sandrine Bailleux8c60fee2014-03-19 13:21:42 +000096 printf("\t--%s%s\t\t%s",
97 entry->command_line_name,
98 (entry->flags & FLAG_FILENAME) ? " FILENAME" : "",
99 entry->name);
100 printf("\n");
Harry Liebelf58ad362014-01-10 18:00:33 +0000101 }
102}
103
104
105static entry_lookup_list *get_entry_lookup_from_uuid(const uuid_t *uuid)
106{
107 unsigned int lookup_index = 0;
108
109 while (toc_entry_lookup_list[lookup_index].command_line_name != NULL) {
110 if (compare_uuids(&toc_entry_lookup_list[lookup_index].name_uuid,
111 uuid) == 0) {
112 return &toc_entry_lookup_list[lookup_index];
113 }
114 lookup_index++;
115 }
116 return NULL;
117}
118
119
120static file_info *find_file_info_from_uuid(const uuid_t *uuid)
121{
122 int index;
123
124 for (index = 0; index < file_info_count; index++) {
125 if (compare_uuids(&files[index].name_uuid, uuid) == 0) {
126 return &files[index];
127 }
128 }
129 return NULL;
130}
131
132
133static int add_file_info_entry(entry_lookup_list *lookup_entry, char *filename)
134{
135 file_info *file_info_entry;
136 int error;
137 struct stat file_status;
138 bool is_new_entry = false;
139
140 /* Check if the file already exists in the array */
141 file_info_entry = find_file_info_from_uuid(&lookup_entry->name_uuid);
142 if (file_info_entry == NULL) {
143 /* The file does not exist in the current list; take the next
144 * one available in the file_info list. 'file_info_count' is
145 * incremented in case of successful update at the end of the
146 * function.
147 */
148 file_info_entry = &files[file_info_count];
149 is_new_entry = true;
150
151 /* Copy the uuid for the new entry */
152 copy_uuid(&file_info_entry->name_uuid,
153 &lookup_entry->name_uuid);
154 }
155
156 /* Get the file information for entry */
157 error = stat(filename, &file_status);
158 if (error != 0) {
159 printf("Error: Cannot get information for file \"%s\": %s\n",
160 filename, strerror(errno));
161 return errno;
162 }
163 file_info_entry->filename = filename;
164 file_info_entry->size = (unsigned int)file_status.st_size;
165 file_info_entry->entry = lookup_entry;
166
167 /* Increment the file_info counter on success if it is new file entry */
168 if (is_new_entry) {
169 file_info_count++;
170
171 /* Ensure we do not overflow */
172 if (file_info_count > MAX_FILES) {
173 printf("ERROR: Too many files in Package\n");
174 return 1;
175 }
176 }
177
178 return 0;
179}
180
181
182static int write_memory_to_file(const uint8_t *start, const char *filename,
183 unsigned int size)
184{
185 FILE *stream;
186 unsigned int bytes_written;
187
188 /* Write the packed file out to the filesystem */
189 stream = fopen(filename, "r+");
190 if (stream == NULL) {
191 stream = fopen(filename, "w");
192 if (stream == NULL) {
193 printf("Error: Cannot create output file \"%s\": %s\n",
194 filename, strerror(errno));
195 return errno;
196 } else {
197 printf("Creating \"%s\"\n", filename);
198 }
199 } else {
200 printf("Updating \"%s\"\n", filename);
201 }
202
203 bytes_written = fwrite(start, sizeof(uint8_t), size, stream);
204 fclose(stream);
205
206 if (bytes_written != size) {
207 printf("Error: Incorrect write for file \"%s\": Size=%u,"
208 "Written=%u bytes.\n", filename, size, bytes_written);
209 return EIO;
210 }
211
212 return 0;
213}
214
215
216static int read_file_to_memory(void *memory, const file_info *info)
217{
218 FILE *stream;
219 unsigned int bytes_read;
220
221 /* If the file_info is defined by its filename we need to load it */
222 if (info->filename) {
223 /* Read image from filesystem */
224 stream = fopen(info->filename, "r");
225 if (stream == NULL) {
226 printf("Error: Cannot open file \"%s\": %s\n",
227 info->filename, strerror(errno));
228 return errno;
229 }
230
231 bytes_read = (unsigned int)fread(memory, sizeof(uint8_t),
232 info->size, stream);
233 fclose(stream);
234 if (bytes_read != info->size) {
235 printf("Error: Incomplete read for file \"%s\":"
236 "Size=%u, Read=%u bytes.\n", info->filename,
237 info->size, bytes_read);
238 return EIO;
239 }
240 } else {
241 if (info->image_buffer == NULL) {
242 printf("ERROR: info->image_buffer = NULL\n");
243 return EIO;
244 }
245 /* Copy the file_info buffer (extracted from the existing
246 * image package) into the new buffer.
247 */
248 memcpy(memory, info->image_buffer, info->size);
249 }
250
251 return 0;
252}
253
254
255/* Create the image package file */
256static int pack_images(const char *fip_filename)
257{
258 int status;
259 uint8_t *fip_base_address;
260 void *entry_address;
261 fip_toc_header *toc_header;
262 fip_toc_entry *toc_entry;
263 unsigned int entry_index;
264 unsigned int toc_size;
265 unsigned int fip_size;
266 unsigned int entry_offset_address;
267 unsigned int payload_size = 0;
268
269 /* Validate filename */
270 if ((fip_filename == NULL) || (strcmp(fip_filename, "") == 0)) {
271 return EINVAL;
272 }
273
274 /* Payload size calculation */
275 for (entry_index = 0; entry_index < file_info_count; entry_index++) {
276 payload_size += files[entry_index].size;
277 }
278
279 /* Allocate memory for entire package, including the final null entry */
280 toc_size = (sizeof(fip_toc_header) +
281 (sizeof(fip_toc_entry) * (file_info_count + 1)));
282 fip_size = toc_size + payload_size;
283 fip_base_address = malloc(fip_size);
284 if (fip_base_address == NULL) {
285 printf("Error: Can't allocate enough memory to create package."
286 "Process aborted.\n");
287 return ENOMEM;
288 }
289 memset(fip_base_address, 0, fip_size);
290
291 /* Create ToC Header */
292 toc_header = (fip_toc_header *)fip_base_address;
293 toc_header->name = TOC_HEADER_NAME;
294 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
295 toc_header->flags = 0;
296
297 toc_entry = (fip_toc_entry *)(fip_base_address +
298 sizeof(fip_toc_header));
299
300 /* Calculate the starting address of the first image, right after the
301 * toc header.
302 */
303 entry_offset_address = toc_size;
304 entry_index = 0;
305
306 /* Create the package in memory. */
307 for (entry_index = 0; entry_index < file_info_count; entry_index++) {
308 entry_address = (fip_base_address + entry_offset_address);
309 status = read_file_to_memory(entry_address,
310 &files[entry_index]);
311 if (status != 0) {
312 printf("Error: While reading \"%s\" from filesystem.\n",
313 files[entry_index].filename);
314 return status;
315 }
316
317 copy_uuid(&toc_entry->uuid, &files[entry_index].name_uuid);
318 toc_entry->offset_address = entry_offset_address;
319 toc_entry->size = files[entry_index].size;
320 toc_entry->flags = 0;
321 entry_offset_address += toc_entry->size;
322 toc_entry++;
323 }
324
325 /* Add a null uuid entry to mark the end of toc entries */
326 copy_uuid(&toc_entry->uuid, &uuid_null);
327 toc_entry->offset_address = entry_offset_address;
328 toc_entry->size = 0;
329 toc_entry->flags = 0;
330
331 /* Save the package to file */
332 status = write_memory_to_file(fip_base_address, fip_filename, fip_size);
333 if (status != 0) {
334 printf("Error: Failed while writing package to file \"%s\" "
335 "with status=%d.\n", fip_filename, status);
336 return status;
337 }
338 return 0;
339}
340
341
342static void dump_toc(void)
343{
344 unsigned int index = 0;
345 unsigned int image_offset;
346 unsigned int image_size = 0;
347
348 image_offset = sizeof(fip_toc_header) +
349 (sizeof(fip_toc_entry) * (file_info_count + 1));
350
351 printf("Firmware Image Package ToC:\n");
352 printf("---------------------------\n");
353 for (index = 0; index < file_info_count; index++) {
354 if (files[index].entry) {
355 printf("- %s: ", files[index].entry->name);
356 } else {
357 printf("- Unknown entry: ");
358 }
359 image_size = files[index].size;
360
361 printf("offset=0x%X, size=0x%X\n", image_offset, image_size);
362 image_offset += image_size;
363
364 if (files[index].filename) {
365 printf(" file: '%s'\n", files[index].filename);
366 }
367 }
368 printf("---------------------------\n");
369}
370
371
372/* Read and load existing package into memory. */
373static int parse_fip(const char *fip_filename)
374{
375 FILE *fip;
376 char *fip_buffer;
377 char *fip_buffer_end;
378 int fip_size, read_fip_size;
379 fip_toc_header *toc_header;
380 fip_toc_entry *toc_entry;
381 bool found_last_toc_entry = false;
382 file_info *file_info_entry;
383 int status = -1;
384 struct stat st;
385
386 fip = fopen(fip_filename, "r");
387 if (fip == NULL) {
388 /* If the fip does not exist just return, it should not be
389 * considered as an error. The package will be created later
390 */
391 status = 0;
392 goto parse_fip_return;
393 }
394
395 if (stat(fip_filename, &st) != 0) {
396 status = errno;
397 goto parse_fip_fclose;
398 } else {
399 fip_size = (int)st.st_size;
400 }
401
402 /* Allocate a buffer to read the package */
403 fip_buffer = (char *)malloc(fip_size);
404 if (fip_buffer == NULL) {
405 printf("ERROR: Cannot allocate %d bytes.\n", fip_size);
406 status = errno;
407 goto parse_fip_fclose;
408 }
409 fip_buffer_end = fip_buffer + fip_size;
410
411 /* Read the file */
412 read_fip_size = fread(fip_buffer, sizeof(char), fip_size, fip);
413 if (read_fip_size != fip_size) {
414 printf("ERROR: Cannot read the FIP.\n");
415 status = EIO;
416 goto parse_fip_free;
417 }
418 fclose(fip);
419 fip = NULL;
420
421 /* The package must at least contain the ToC Header */
422 if (fip_size < sizeof(fip_toc_header)) {
423 printf("ERROR: Given FIP is smaller than the ToC header.\n");
424 status = EINVAL;
425 goto parse_fip_free;
426 }
427 /* Set the ToC Header at the base of the buffer */
428 toc_header = (fip_toc_header *)fip_buffer;
429 /* The first toc entry should be just after the ToC header */
430 toc_entry = (fip_toc_entry *)(toc_header + 1);
431
432 /* While the ToC entry is contained into the buffer */
433 int cnt = 0;
434 while (((char *)toc_entry + sizeof(fip_toc_entry)) < fip_buffer_end) {
435 cnt++;
436 /* Check if the ToC Entry is the last one */
437 if (compare_uuids(&toc_entry->uuid, &uuid_null) == 0) {
438 found_last_toc_entry = true;
439 status = 0;
440 break;
441 }
442
443 /* Add the entry into file_info */
444
445 /* Get the new entry in the array and clear it */
446 file_info_entry = &files[file_info_count++];
447 memset(file_info_entry, 0, sizeof(file_info));
448
449 /* Copy the info from the ToC entry */
450 copy_uuid(&file_info_entry->name_uuid, &toc_entry->uuid);
451 file_info_entry->image_buffer = fip_buffer +
452 toc_entry->offset_address;
453 file_info_entry->size = toc_entry->size;
454
455 /* Check if there is a corresponding entry in lookup table */
456 file_info_entry->entry =
457 get_entry_lookup_from_uuid(&toc_entry->uuid);
458
459 /* Go to the next ToC entry */
460 toc_entry++;
461 }
462
463 if (!found_last_toc_entry) {
464 printf("ERROR: Given FIP does not have an end ToC entry.\n");
465 status = EINVAL;
466 goto parse_fip_free;
467 } else {
468 /* All is well, we should not free any of the loaded images */
469 goto parse_fip_fclose;
470 }
471
472 parse_fip_free:
473 if (fip_buffer != NULL) {
474 free(fip_buffer);
475 fip_buffer = NULL;
476 }
477
478 parse_fip_fclose:
479 if (fip != NULL) {
480 fclose(fip);
481 }
482
483 parse_fip_return:
484 return status;
485}
486
487
488/* Parse all command-line options and return the FIP name if present. */
489static char *get_filename(int argc, char **argv, struct option *options)
490{
491 int c;
492 char *filename = NULL;
493
494 /* Reset option pointer so we parse all args. starts at 1.
495 * The filename is the only argument that does not have an option flag.
496 */
497 optind = 1;
498 while (1) {
499 c = getopt_long(argc, argv, "", options, NULL);
500 if (c == -1)
501 break;
502
503 if (c == '?') {
504 /* Failed to parse an option. Fail. */
505 return NULL;
506 }
507 }
508
509 /* Only one argument left then it is the filename.
510 * We dont expect any other options
511 */
Sandrine Bailleuxfce7a7d2014-03-19 13:39:52 +0000512 if (optind + 1 == argc)
Harry Liebelf58ad362014-01-10 18:00:33 +0000513 filename = argv[optind];
Harry Liebelf58ad362014-01-10 18:00:33 +0000514
515 return filename;
516}
517
518
519/* Work through command-line options */
520static int parse_cmdline(int argc, char **argv, struct option *options,
521 int *do_pack)
522{
523 int c;
524 int status = 0;
525 int option_index = 0;
526 entry_lookup_list *lookup_entry;
527 int do_dump = 0;
528
529 /* restart parse to process all options. starts at 1. */
530 optind = 1;
531 while (1) {
532 c = getopt_long(argc, argv, "", options, &option_index);
533 if (c == -1)
534 break;
535
536 switch (c) {
Sandrine Bailleux29e54422014-03-19 13:09:54 +0000537 case OPT_TOC_ENTRY:
Harry Liebelf58ad362014-01-10 18:00:33 +0000538 if (optarg) {
539 /* Does the option expect a filename. */
540 lookup_entry = &toc_entry_lookup_list[option_index];
541 if (lookup_entry->flags & FLAG_FILENAME) {
542 status = add_file_info_entry(lookup_entry, optarg);
543 if (status != 0) {
544 printf("Failed to process %s\n",
545 options[option_index].name);
546 break;
547 } else {
548 /* Update package */
549 *do_pack = 1;
550 }
551 }
552 }
553 break;
Sandrine Bailleux29e54422014-03-19 13:09:54 +0000554
555 case OPT_DUMP:
556 do_dump = 1;
557 continue;
558
Sandrine Bailleux8c60fee2014-03-19 13:21:42 +0000559 case OPT_HELP:
560 print_usage();
561 exit(0);
562
Harry Liebelf58ad362014-01-10 18:00:33 +0000563 default:
564 /* Unrecognised options are caught in get_filename() */
565 break;
566 }
567 }
568
569
570 /* Do not dump toc if we have an error as it could hide the error */
571 if ((status == 0) && (do_dump)) {
572 dump_toc();
573 }
574
575 return status;
576
577}
578
579int main(int argc, char **argv)
580{
581 int i;
582 int status;
583 char *fip_filename;
584 int do_pack = 0;
585
586 /* Clear file list table. */
587 memset(files, 0, sizeof(files));
588
589 /* Initialise for getopt_long().
590 * Use image table as defined at top of file to get options.
Sandrine Bailleux8c60fee2014-03-19 13:21:42 +0000591 * Add 'dump' option, 'help' option and end marker.
Harry Liebelf58ad362014-01-10 18:00:33 +0000592 */
593 static struct option long_options[(sizeof(toc_entry_lookup_list)/
Sandrine Bailleux8c60fee2014-03-19 13:21:42 +0000594 sizeof(entry_lookup_list)) + 2];
Harry Liebelf58ad362014-01-10 18:00:33 +0000595
596 for (i = 0;
597 /* -1 because we dont want to process end marker in toc table */
598 i < sizeof(toc_entry_lookup_list)/sizeof(entry_lookup_list) - 1;
599 i++) {
600 long_options[i].name = toc_entry_lookup_list[i].command_line_name;
601 /* The only flag defined at the moment is for a FILENAME */
602 long_options[i].has_arg = toc_entry_lookup_list[i].flags ? 1 : 0;
603 long_options[i].flag = 0;
Sandrine Bailleux29e54422014-03-19 13:09:54 +0000604 long_options[i].val = OPT_TOC_ENTRY;
Harry Liebelf58ad362014-01-10 18:00:33 +0000605 }
606
607 /* Add '--dump' option */
608 long_options[i].name = "dump";
609 long_options[i].has_arg = 0;
610 long_options[i].flag = 0;
Sandrine Bailleux29e54422014-03-19 13:09:54 +0000611 long_options[i].val = OPT_DUMP;
Harry Liebelf58ad362014-01-10 18:00:33 +0000612
Sandrine Bailleux8c60fee2014-03-19 13:21:42 +0000613 /* Add '--help' option */
614 long_options[++i].name = "help";
615 long_options[i].has_arg = 0;
616 long_options[i].flag = 0;
617 long_options[i].val = OPT_HELP;
618
Harry Liebelf58ad362014-01-10 18:00:33 +0000619 /* Zero the last entry (required) */
620 long_options[++i].name = 0;
621 long_options[i].has_arg = 0;
622 long_options[i].flag = 0;
623 long_options[i].val = 0;
624
625#ifdef DEBUG
626 /* Print all supported options */
627 for (i = 0; i < sizeof(long_options)/sizeof(struct option); i++) {
628 printf("long opt (%d) : name = %s\n", i, long_options[i].name);
629 }
630#endif /* DEBUG */
631
632 /* As the package may already exist and is to be updated we need to get
633 * the filename from the arguments and load from it.
634 * NOTE: As this is the first function to look at the program arguments
635 * it causes a failure if bad options were provided.
636 */
637 fip_filename = get_filename(argc, argv, long_options);
Harry Liebelf58ad362014-01-10 18:00:33 +0000638
639 /* Try to open the file and load it into memory */
Sandrine Bailleuxfce7a7d2014-03-19 13:39:52 +0000640 if (fip_filename != NULL) {
641 status = parse_fip(fip_filename);
642 if (status != 0) {
643 return status;
644 }
Harry Liebelf58ad362014-01-10 18:00:33 +0000645 }
646
647 /* Work through provided program arguments and perform actions */
648 status = parse_cmdline(argc, argv, long_options, &do_pack);
649 if (status != 0) {
650 return status;
651 };
652
Sandrine Bailleuxfce7a7d2014-03-19 13:39:52 +0000653 if (fip_filename == NULL) {
654 printf("ERROR: Missing FIP filename\n");
655 print_usage();
656 return 0;
657 }
658
Harry Liebelf58ad362014-01-10 18:00:33 +0000659 /* Processed all command line options. Create/update the package if
660 * required.
661 */
662 if (do_pack) {
663 status = pack_images(fip_filename);
664 if (status != 0) {
665 printf("Failed to create package (status = %d).\n",
666 status);
667 }
668 }
669
670 return status;
671}