cbfstool: fix option parsing
[coreboot.git] / util / cbfstool / cbfstool.c
blobf236d8b68984d7123ad33dd97d49b084b3be1da7
1 /*
2 * cbfstool, CLI utility for CBFS file manipulation
4 * Copyright (C) 2009 coresystems GmbH
5 * written by Patrick Georgi <patrick.georgi@coresystems.de>
6 * Copyright (C) 2012 Google, Inc.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <unistd.h>
27 #include <getopt.h>
28 #include "common.h"
29 #include "cbfs.h"
30 #include "cbfs_image.h"
31 #include "fit.h"
33 struct command {
34 const char *name;
35 const char *optstring;
36 int (*function) (void);
39 static struct param {
40 char *cbfs_name;
41 char *name;
42 char *filename;
43 char *bootblock;
44 uint64_t u64val;
45 uint32_t type;
46 uint32_t baseaddress;
47 uint32_t baseaddress_assigned;
48 uint32_t loadaddress;
49 uint32_t headeroffset;
50 uint32_t headeroffset_assigned;
51 uint32_t entrypoint;
52 uint32_t size;
53 uint32_t alignment;
54 uint32_t pagesize;
55 uint32_t offset;
56 uint32_t top_aligned;
57 uint32_t arch;
58 int fit_empty_entries;
59 comp_algo algo;
60 /* for linux payloads */
61 char *initrd;
62 char *cmdline;
63 } param = {
64 /* All variables not listed are initialized as zero. */
65 .arch = CBFS_ARCHITECTURE_UNKNOWN,
66 .algo = CBFS_COMPRESS_NONE,
69 typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset);
71 static int cbfs_add_integer_component(const char *cbfs_name,
72 const char *name,
73 uint64_t u64val,
74 uint32_t offset) {
75 struct cbfs_image image;
76 struct buffer buffer;
77 int i, ret = 1;
79 if (!name) {
80 ERROR("You need to specify -n/--name.\n");
81 return 1;
84 if (buffer_create(&buffer, 8, name) != 0)
85 return 1;
87 for (i = 0; i < 8; i++)
88 buffer.data[i] = (u64val >> i*8) & 0xff;
90 if (cbfs_image_from_file(&image, cbfs_name) != 0) {
91 ERROR("Could not load ROM image '%s'.\n", cbfs_name);
92 buffer_delete(&buffer);
93 return 1;
96 if (cbfs_get_entry(&image, name)) {
97 ERROR("'%s' already in ROM image.\n", name);
98 goto done;
101 if (cbfs_add_entry(&image, &buffer, name, CBFS_COMPONENT_RAW, param.baseaddress) != 0) {
102 ERROR("Failed to add %llu into ROM image as '%s'.\n", (long long unsigned)u64val, name);
103 goto done;
106 if (cbfs_image_write_file(&image, cbfs_name) == 0)
107 ret = 0;
109 done:
110 buffer_delete(&buffer);
111 cbfs_image_delete(&image);
112 return ret;
115 static int cbfs_add_component(const char *cbfs_name,
116 const char *filename,
117 const char *name,
118 uint32_t type,
119 uint32_t offset,
120 convert_buffer_t convert) {
121 struct cbfs_image image;
122 struct buffer buffer;
124 if (!filename) {
125 ERROR("You need to specify -f/--filename.\n");
126 return 1;
129 if (!name) {
130 ERROR("You need to specify -n/--name.\n");
131 return 1;
134 if (type == 0) {
135 ERROR("You need to specify a valid -t/--type.\n");
136 return 1;
139 if (buffer_from_file(&buffer, filename) != 0) {
140 ERROR("Could not load file '%s'.\n", filename);
141 return 1;
144 if (convert && convert(&buffer, &offset) != 0) {
145 ERROR("Failed to parse file '%s'.\n", filename);
146 buffer_delete(&buffer);
147 return 1;
150 if (cbfs_image_from_file(&image, cbfs_name) != 0) {
151 ERROR("Could not load ROM image '%s'.\n", cbfs_name);
152 buffer_delete(&buffer);
153 return 1;
156 if (cbfs_get_entry(&image, name)) {
157 ERROR("'%s' already in ROM image.\n", name);
158 buffer_delete(&buffer);
159 cbfs_image_delete(&image);
160 return 1;
163 if (cbfs_add_entry(&image, &buffer, name, type, offset) != 0) {
164 ERROR("Failed to add '%s' into ROM image.\n", filename);
165 buffer_delete(&buffer);
166 cbfs_image_delete(&image);
167 return 1;
170 if (cbfs_image_write_file(&image, cbfs_name) != 0) {
171 buffer_delete(&buffer);
172 cbfs_image_delete(&image);
173 return 1;
176 buffer_delete(&buffer);
177 cbfs_image_delete(&image);
178 return 0;
181 static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset)
183 struct buffer output;
184 int ret;
185 ret = parse_elf_to_stage(buffer, &output, param.arch, param.algo,
186 offset);
187 if (ret != 0)
188 return -1;
189 buffer_delete(buffer);
190 // direct assign, no dupe.
191 memcpy(buffer, &output, sizeof(*buffer));
192 return 0;
195 static int cbfstool_convert_mkpayload(struct buffer *buffer, uint32_t *offset) {
196 struct buffer output;
197 int ret;
198 /* per default, try and see if payload is an ELF binary */
199 ret = parse_elf_to_payload(buffer, &output, param.arch, param.algo);
201 /* If it's not an ELF, see if it's a UEFI FV */
202 if (ret != 0)
203 ret = parse_fv_to_payload(buffer, &output, param.algo);
205 /* If it's neither ELF nor UEFI Fv, try bzImage */
206 if (ret != 0)
207 ret = parse_bzImage_to_payload(buffer, &output,
208 param.initrd, param.cmdline, param.algo);
210 /* Not a supported payload type */
211 if (ret != 0) {
212 ERROR("Not a supported payload type (ELF / FV).\n");
213 return -1;
216 buffer_delete(buffer);
217 // direct assign, no dupe.
218 memcpy(buffer, &output, sizeof(*buffer));
219 return 0;
222 static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
223 uint32_t *offset) {
224 struct buffer output;
225 if (parse_flat_binary_to_payload(buffer, &output,
226 param.loadaddress,
227 param.entrypoint,
228 param.algo) != 0) {
229 return -1;
231 buffer_delete(buffer);
232 // direct assign, no dupe.
233 memcpy(buffer, &output, sizeof(*buffer));
234 return 0;
238 static int cbfs_add(void)
240 return cbfs_add_component(param.cbfs_name,
241 param.filename,
242 param.name,
243 param.type,
244 param.baseaddress,
245 NULL);
248 static int cbfs_add_stage(void)
250 return cbfs_add_component(param.cbfs_name,
251 param.filename,
252 param.name,
253 CBFS_COMPONENT_STAGE,
254 param.baseaddress,
255 cbfstool_convert_mkstage);
258 static int cbfs_add_payload(void)
260 return cbfs_add_component(param.cbfs_name,
261 param.filename,
262 param.name,
263 CBFS_COMPONENT_PAYLOAD,
264 param.baseaddress,
265 cbfstool_convert_mkpayload);
268 static int cbfs_add_flat_binary(void)
270 if (param.loadaddress == 0) {
271 ERROR("You need to specify a valid "
272 "-l/--load-address.\n");
273 return 1;
275 if (param.entrypoint == 0) {
276 ERROR("You need to specify a valid "
277 "-e/--entry-point.\n");
278 return 1;
280 return cbfs_add_component(param.cbfs_name,
281 param.filename,
282 param.name,
283 CBFS_COMPONENT_PAYLOAD,
284 param.baseaddress,
285 cbfstool_convert_mkflatpayload);
288 static int cbfs_add_integer(void)
290 return cbfs_add_integer_component(param.cbfs_name,
291 param.name,
292 param.u64val,
293 param.baseaddress);
296 static int cbfs_remove(void)
298 struct cbfs_image image;
300 if (!param.name) {
301 ERROR("You need to specify -n/--name.\n");
302 return 1;
305 if (cbfs_image_from_file(&image, param.cbfs_name) != 0) {
306 ERROR("Could not load ROM image '%s'.\n",
307 param.cbfs_name);
308 return 1;
311 if (cbfs_remove_entry(&image, param.name) != 0) {
312 ERROR("Removing file '%s' failed.\n",
313 param.name);
314 cbfs_image_delete(&image);
315 return 1;
317 if (cbfs_image_write_file(&image, param.cbfs_name) != 0) {
318 cbfs_image_delete(&image);
319 return 1;
322 cbfs_image_delete(&image);
323 return 0;
326 static int cbfs_create(void)
328 struct cbfs_image image;
329 struct buffer bootblock;
331 if (param.size == 0) {
332 ERROR("You need to specify a valid -s/--size.\n");
333 return 1;
336 if (!param.bootblock) {
337 ERROR("You need to specify -B/--bootblock.\n");
338 return 1;
341 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
342 ERROR("You need to specify -m/--machine arch.\n");
343 return 1;
346 if (buffer_from_file(&bootblock, param.bootblock) != 0) {
347 return 1;
350 // Setup default boot offset and header offset.
351 if (!param.baseaddress_assigned) {
352 // put boot block before end of ROM.
353 param.baseaddress = param.size - bootblock.size;
354 DEBUG("bootblock in end of ROM.\n");
356 if (!param.headeroffset_assigned) {
357 // Put header before bootblock, and make a reference in end of
358 // bootblock.
359 param.headeroffset = (
360 param.baseaddress -
361 sizeof(struct cbfs_header));
362 if (bootblock.size >= sizeof(uint32_t)) {
363 // TODO this only works for 32b top-aligned system now...
364 uint32_t ptr = param.headeroffset - param.size;
365 uint32_t *sig = (uint32_t *)(bootblock.data +
366 bootblock.size -
367 sizeof(ptr));
368 *sig = ptr;
369 DEBUG("CBFS header reference in end of bootblock.\n");
373 if (cbfs_image_create(&image,
374 param.arch,
375 param.size,
376 param.alignment,
377 &bootblock,
378 param.baseaddress,
379 param.headeroffset,
380 param.offset) != 0) {
381 ERROR("Failed to create %s.\n", param.cbfs_name);
382 return 1;
384 buffer_delete(&bootblock);
386 if (cbfs_image_write_file(&image, param.cbfs_name) != 0) {
387 ERROR("Failed to write %s.\n", param.cbfs_name);
388 cbfs_image_delete(&image);
389 return 1;
391 cbfs_image_delete(&image);
392 return 0;
395 static int cbfs_locate(void)
397 struct cbfs_image image;
398 struct buffer buffer;
399 int32_t address;
401 if (!param.filename) {
402 ERROR("You need to specify -f/--filename.\n");
403 return 1;
406 if (!param.name) {
407 ERROR("You need to specify -n/--name.\n");
408 return 1;
411 if (cbfs_image_from_file(&image, param.cbfs_name) != 0) {
412 ERROR("Failed to load %s.\n", param.cbfs_name);
413 return 1;
416 if (cbfs_get_entry(&image, param.name))
417 WARN("'%s' already in CBFS.\n", param.name);
419 if (buffer_from_file(&buffer, param.filename) != 0) {
420 ERROR("Cannot load %s.\n", param.filename);
421 cbfs_image_delete(&image);
422 return 1;
425 address = cbfs_locate_entry(&image, param.name, buffer.size,
426 param.pagesize, param.alignment);
427 buffer_delete(&buffer);
429 if (address == -1) {
430 ERROR("'%s' can't fit in CBFS for page-size %#x, align %#x.\n",
431 param.name, param.pagesize, param.alignment);
432 cbfs_image_delete(&image);
433 return 1;
436 if (param.top_aligned)
437 address = address - image.header->romsize;
439 cbfs_image_delete(&image);
440 printf("0x%x\n", address);
441 return 0;
444 static int cbfs_print(void)
446 struct cbfs_image image;
447 if (cbfs_image_from_file(&image, param.cbfs_name) != 0) {
448 ERROR("Could not load ROM image '%s'.\n",
449 param.cbfs_name);
450 return 1;
452 cbfs_print_directory(&image);
453 cbfs_image_delete(&image);
454 return 0;
457 static int cbfs_extract(void)
459 int result = 0;
460 struct cbfs_image image;
462 if (!param.filename) {
463 ERROR("You need to specify -f/--filename.\n");
464 return 1;
467 if (!param.name) {
468 ERROR("You need to specify -n/--name.\n");
469 return 1;
472 if (cbfs_image_from_file(&image, param.cbfs_name) != 0) {
473 ERROR("Could not load ROM image '%s'.\n",
474 param.cbfs_name);
475 result = 1;
476 } else if (cbfs_export_entry(&image, param.name,
477 param.filename) != 0) {
478 result = 1;
481 cbfs_image_delete(&image);
482 return result;
485 static int cbfs_update_fit(void)
487 int ret = 0;
488 struct cbfs_image image;
490 if (!param.name) {
491 ERROR("You need to specify -n/--name.\n");
492 return 1;
495 if (param.fit_empty_entries <= 0) {
496 ERROR("Invalid number of fit entries "
497 "(-x/--empty-fits): %d\n", param.fit_empty_entries);
498 return 1;
501 if (cbfs_image_from_file(&image, param.cbfs_name) != 0) {
502 ERROR("Could not load ROM image '%s'.\n",
503 param.cbfs_name);
504 return 1;
507 ret = fit_update_table(&image, param.fit_empty_entries, param.name);
508 if (!ret)
509 ret = cbfs_image_write_file(&image, param.cbfs_name);
511 cbfs_image_delete(&image);
512 return ret;
515 static const struct command commands[] = {
516 {"add", "f:n:t:b:vh?", cbfs_add},
517 {"add-payload", "f:n:t:c:b:vh?C:I:", cbfs_add_payload},
518 {"add-stage", "f:n:t:c:b:vh?", cbfs_add_stage},
519 {"add-flat-binary", "f:n:l:e:c:b:vh?", cbfs_add_flat_binary},
520 {"add-int", "i:n:b:vh?", cbfs_add_integer},
521 {"remove", "n:vh?", cbfs_remove},
522 {"create", "s:B:b:H:a:o:m:vh?", cbfs_create},
523 {"locate", "f:n:P:a:Tvh?", cbfs_locate},
524 {"print", "vh?", cbfs_print},
525 {"extract", "n:f:vh?", cbfs_extract},
526 {"update-fit", "n:x:vh?", cbfs_update_fit},
529 static struct option long_options[] = {
530 {"name", required_argument, 0, 'n' },
531 {"type", required_argument, 0, 't' },
532 {"compression", required_argument, 0, 'c' },
533 {"base-address", required_argument, 0, 'b' },
534 {"load-address", required_argument, 0, 'l' },
535 {"top-aligned", required_argument, 0, 'T' },
536 {"entry-point", required_argument, 0, 'e' },
537 {"size", required_argument, 0, 's' },
538 {"bootblock", required_argument, 0, 'B' },
539 {"alignment", required_argument, 0, 'a' },
540 {"page-size", required_argument, 0, 'P' },
541 {"offset", required_argument, 0, 'o' },
542 {"file", required_argument, 0, 'f' },
543 {"int", required_argument, 0, 'i' },
544 {"machine", required_argument, 0, 'm' },
545 {"empty-fits", required_argument, 0, 'x' },
546 {"initrd", required_argument, 0, 'I' },
547 {"cmdline", required_argument, 0, 'C' },
548 {"verbose", no_argument, 0, 'v' },
549 {"help", no_argument, 0, 'h' },
550 {NULL, 0, 0, 0 }
553 static void usage(char *name)
555 printf
556 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
557 "USAGE:\n" " %s [-h]\n"
558 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
559 " -T Output top-aligned memory address\n"
560 " -v Provide verbose output\n"
561 " -h Display this help message\n\n"
562 "COMMANDs:\n"
563 " add -f FILE -n NAME -t TYPE [-b base-address] "
564 "Add a component\n"
565 " add-payload -f FILE -n NAME [-c compression] [-b base] "
566 "Add a payload to the ROM\n"
567 " (linux specific: [-C cmdline] [-I initrd])\n"
568 " add-stage -f FILE -n NAME [-c compression] [-b base] "
569 "Add a stage to the ROM\n"
570 " add-flat-binary -f FILE -n NAME -l load-address \\\n"
571 " -e entry-point [-c compression] [-b base] "
572 "Add a 32bit flat mode binary\n"
573 " add-int -i INTEGER -n NAME [-b base] "
574 "Add a raw 64-bit integer value\n"
575 " remove -n NAME "
576 "Remove a component\n"
577 " create -s size -B bootblock -m ARCH [-a align] [-o offset] "
578 "Create a ROM file\n"
579 " locate -f FILE -n NAME [-P page-size] [-a align] [-T] "
580 "Find a place for a file of that size\n"
581 " print "
582 "Show the contents of the ROM\n"
583 " extract -n NAME -f FILE "
584 "Extracts a raw payload from ROM\n"
585 " update-fit -n MICROCODE_BLOB_NAME -x EMTPY_FIT_ENTRIES\n "
586 "Updates the FIT table with microcode entries\n"
587 "\n"
588 "ARCHes:\n"
589 " armv7, x86\n"
590 "TYPEs:\n", name, name
592 print_supported_filetypes();
595 int main(int argc, char **argv)
597 size_t i;
598 int c;
600 if (argc < 3) {
601 usage(argv[0]);
602 return 1;
605 param.cbfs_name = argv[1];
606 char *cmd = argv[2];
607 optind += 2;
609 for (i = 0; i < ARRAY_SIZE(commands); i++) {
610 if (strcmp(cmd, commands[i].name) != 0)
611 continue;
613 while (1) {
614 char *suffix = NULL;
615 int option_index = 0;
617 c = getopt_long(argc, argv, commands[i].optstring,
618 long_options, &option_index);
619 if (c == -1)
620 break;
622 /* filter out illegal long options */
623 if (strchr(commands[i].optstring, c) == NULL) {
624 /* TODO maybe print actual long option instead */
625 ERROR("%s: invalid option -- '%c'\n",
626 argv[0], c);
627 c = '?';
630 switch(c) {
631 case 'n':
632 param.name = optarg;
633 break;
634 case 't':
635 if (intfiletype(optarg) != ((uint64_t) - 1))
636 param.type = intfiletype(optarg);
637 else
638 param.type = strtoul(optarg, NULL, 0);
639 if (param.type == 0)
640 WARN("Unknown type '%s' ignored\n",
641 optarg);
642 break;
643 case 'c':
644 if (!strncasecmp(optarg, "lzma", 5))
645 param.algo = CBFS_COMPRESS_LZMA;
646 else if (!strncasecmp(optarg, "none", 5))
647 param.algo = CBFS_COMPRESS_NONE;
648 else
649 WARN("Unknown compression '%s'"
650 " ignored.\n", optarg);
651 break;
652 case 'b':
653 param.baseaddress = strtoul(optarg, NULL, 0);
654 // baseaddress may be zero on non-x86, so we
655 // need an explicit "baseaddress_assigned".
656 param.baseaddress = strtoul(optarg, NULL, 0);
657 param.baseaddress_assigned = 1;
658 break;
659 case 'l':
660 param.loadaddress = strtoul(optarg, NULL, 0);
662 break;
663 case 'e':
664 param.entrypoint = strtoul(optarg, NULL, 0);
665 break;
666 case 's':
667 param.size = strtoul(optarg, &suffix, 0);
668 if (tolower(suffix[0])=='k') {
669 param.size *= 1024;
671 if (tolower(suffix[0])=='m') {
672 param.size *= 1024 * 1024;
674 break;
675 case 'B':
676 param.bootblock = optarg;
677 break;
678 case 'H':
679 param.headeroffset = strtoul(
680 optarg, NULL, 0);
681 param.headeroffset_assigned = 1;
682 break;
683 case 'a':
684 param.alignment = strtoul(optarg, NULL, 0);
685 break;
686 case 'P':
687 param.pagesize = strtoul(optarg, NULL, 0);
688 break;
689 case 'o':
690 param.offset = strtoul(optarg, NULL, 0);
691 break;
692 case 'f':
693 param.filename = optarg;
694 break;
695 case 'i':
696 param.u64val = strtoull(optarg, NULL, 0);
697 break;
698 case 'T':
699 param.top_aligned = 1;
700 break;
701 case 'x':
702 param.fit_empty_entries = strtol(optarg, NULL, 0);
703 break;
704 case 'v':
705 verbose++;
706 break;
707 case 'm':
708 param.arch = string_to_arch(optarg);
709 break;
710 case 'I':
711 param.initrd = optarg;
712 break;
713 case 'C':
714 param.cmdline = optarg;
715 break;
716 case 'h':
717 case '?':
718 usage(argv[0]);
719 return 1;
720 default:
721 break;
725 return commands[i].function();
728 ERROR("Unknown command '%s'.\n", cmd);
729 usage(argv[0]);
730 return 1;