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