mb: Set coreboot as DSDT's manufacturer model ID
[coreboot.git] / util / cbfstool / cbfstool.c
blobf0fbf5ae4fb825758948532b002c5b86785cc048
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.
7 * Copyright (C) 2016 Siemens AG
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <strings.h>
23 #include <ctype.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include "common.h"
27 #include "cbfs.h"
28 #include "cbfs_image.h"
29 #include "cbfs_sections.h"
30 #include "elfparsing.h"
31 #include "fit.h"
32 #include "partitioned_file.h"
33 #include <commonlib/fsp.h>
34 #include <commonlib/endian.h>
35 #include <commonlib/helpers.h>
37 #define SECTION_WITH_FIT_TABLE "BOOTBLOCK"
39 struct command {
40 const char *name;
41 const char *optstring;
42 int (*function) (void);
43 // Whether to populate param.image_region before invoking function
44 bool accesses_region;
45 // This set to true means two things:
46 // - in case of a command operating on a region, the region's contents
47 // will be written back to image_file at the end
48 // - write access to the file is required
49 bool modifies_region;
52 static struct param {
53 partitioned_file_t *image_file;
54 struct buffer *image_region;
55 const char *name;
56 const char *filename;
57 const char *fmap;
58 const char *region_name;
59 const char *source_region;
60 const char *bootblock;
61 const char *ignore_section;
62 const char *ucode_region;
63 uint64_t u64val;
64 uint32_t type;
65 uint32_t baseaddress;
66 uint32_t baseaddress_assigned;
67 uint32_t loadaddress;
68 uint32_t headeroffset;
69 uint32_t headeroffset_assigned;
70 uint32_t entrypoint;
71 uint32_t size;
72 uint32_t alignment;
73 uint32_t pagesize;
74 uint32_t cbfsoffset;
75 uint32_t cbfsoffset_assigned;
76 uint32_t arch;
77 uint32_t padding;
78 uint32_t topswap_size;
79 bool u64val_assigned;
80 bool fill_partial_upward;
81 bool fill_partial_downward;
82 bool show_immutable;
83 bool stage_xip;
84 bool autogen_attr;
85 bool machine_parseable;
86 bool unprocessed;
87 int fit_empty_entries;
88 enum comp_algo compression;
89 int precompression;
90 enum vb2_hash_algorithm hash;
91 /* For linux payloads */
92 char *initrd;
93 char *cmdline;
94 int force;
95 } param = {
96 /* All variables not listed are initialized as zero. */
97 .arch = CBFS_ARCHITECTURE_UNKNOWN,
98 .compression = CBFS_COMPRESS_NONE,
99 .hash = VB2_HASH_INVALID,
100 .headeroffset = ~0,
101 .region_name = SECTION_NAME_PRIMARY_CBFS,
102 .u64val = -1,
105 static bool region_is_flashmap(const char *region)
107 return partitioned_file_region_check_magic(param.image_file, region,
108 FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
111 /* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
112 static bool region_is_modern_cbfs(const char *region)
114 return partitioned_file_region_check_magic(param.image_file, region,
115 CBFS_FILE_MAGIC, strlen(CBFS_FILE_MAGIC));
119 * Converts between offsets from the start of the specified image region and
120 * "top-aligned" offsets from the top of the entire boot media. See comment
121 * below for convert_to_from_top_aligned() about forming addresses.
123 static unsigned convert_to_from_absolute_top_aligned(
124 const struct buffer *region, unsigned offset)
126 assert(region);
128 size_t image_size = partitioned_file_total_size(param.image_file);
130 return image_size - region->offset - offset;
134 * Converts between offsets from the start of the specified image region and
135 * "top-aligned" offsets from the top of the image region. Works in either
136 * direction: pass in one type of offset and receive the other type.
137 * N.B. A top-aligned offset is always a positive number, and should not be
138 * confused with a top-aliged *address*, which is its arithmetic inverse. */
139 static unsigned convert_to_from_top_aligned(const struct buffer *region,
140 unsigned offset)
142 assert(region);
144 /* Cover the situation where a negative base address is given by the
145 * user. Callers of this function negate it, so it'll be a positive
146 * number smaller than the region.
148 if ((offset > 0) && (offset < region->size)) {
149 return region->size - offset;
152 return convert_to_from_absolute_top_aligned(region, offset);
155 static int do_cbfs_locate(int32_t *cbfs_addr, size_t metadata_size,
156 size_t data_size)
158 if (!param.filename) {
159 ERROR("You need to specify -f/--filename.\n");
160 return 1;
163 if (!param.name) {
164 ERROR("You need to specify -n/--name.\n");
165 return 1;
168 struct cbfs_image image;
169 if (cbfs_image_from_buffer(&image, param.image_region,
170 param.headeroffset))
171 return 1;
173 if (cbfs_get_entry(&image, param.name))
174 WARN("'%s' already in CBFS.\n", param.name);
176 if (!data_size) {
177 struct buffer buffer;
178 if (buffer_from_file(&buffer, param.filename) != 0) {
179 ERROR("Cannot load %s.\n", param.filename);
180 return 1;
182 data_size = buffer.size;
183 buffer_delete(&buffer);
186 DEBUG("File size is %zd (0x%zx)\n", data_size, data_size);
188 /* Include cbfs_file size along with space for with name. */
189 metadata_size += cbfs_calculate_file_header_size(param.name);
190 /* Adjust metadata_size if additional attributes were added */
191 if (param.autogen_attr) {
192 if (param.alignment)
193 metadata_size += sizeof(struct cbfs_file_attr_align);
194 if (param.baseaddress_assigned || param.stage_xip)
195 metadata_size += sizeof(struct cbfs_file_attr_position);
198 /* Take care of the hash attribute if it is used */
199 if (param.hash != VB2_HASH_INVALID)
200 metadata_size += sizeof(struct cbfs_file_attr_hash);
202 int32_t address = cbfs_locate_entry(&image, data_size, param.pagesize,
203 param.alignment, metadata_size);
205 if (address == -1) {
206 ERROR("'%s' can't fit in CBFS for page-size %#x, align %#x.\n",
207 param.name, param.pagesize, param.alignment);
208 return 1;
211 *cbfs_addr = address;
212 return 0;
215 typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
216 struct cbfs_file *header);
218 static int cbfs_add_integer_component(const char *name,
219 uint64_t u64val,
220 uint32_t offset,
221 uint32_t headeroffset) {
222 struct cbfs_image image;
223 struct cbfs_file *header = NULL;
224 struct buffer buffer;
225 int i, ret = 1;
227 if (!name) {
228 ERROR("You need to specify -n/--name.\n");
229 return 1;
232 if (buffer_create(&buffer, 8, name) != 0)
233 return 1;
235 for (i = 0; i < 8; i++)
236 buffer.data[i] = (u64val >> i*8) & 0xff;
238 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset)) {
239 ERROR("Selected image region is not a CBFS.\n");
240 goto done;
243 if (cbfs_get_entry(&image, name)) {
244 ERROR("'%s' already in ROM image.\n", name);
245 goto done;
248 if (IS_TOP_ALIGNED_ADDRESS(offset))
249 offset = convert_to_from_top_aligned(param.image_region,
250 -offset);
252 header = cbfs_create_file_header(CBFS_COMPONENT_RAW,
253 buffer.size, name);
254 if (cbfs_add_entry(&image, &buffer, offset, header) != 0) {
255 ERROR("Failed to add %llu into ROM image as '%s'.\n",
256 (long long unsigned)u64val, name);
257 goto done;
260 ret = 0;
262 done:
263 free(header);
264 buffer_delete(&buffer);
265 return ret;
268 static int is_valid_topswap(void)
270 switch (param.topswap_size) {
271 case (64 * KiB):
272 case (128 * KiB):
273 case (256 * KiB):
274 case (512 * KiB):
275 case (1 * MiB):
276 break;
277 default:
278 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
279 param.topswap_size);
280 return 0;
282 return 1;
285 static void fill_header_offset(void *location, uint32_t offset)
287 // TODO: When we have a BE target, we'll need to store this as BE
288 write_le32(location, offset);
291 static int update_master_header_loc_topswap(struct cbfs_image *image,
292 void *h_loc, uint32_t header_offset)
294 struct cbfs_file *entry;
295 void *ts_h_loc = h_loc;
297 entry = cbfs_get_entry(image, "bootblock");
298 if (entry == NULL) {
299 ERROR("Bootblock not in ROM image?!?\n");
300 return 1;
304 * Check if the existing topswap boundary matches with
305 * the one provided.
307 if (param.topswap_size != ntohl(entry->len)/2) {
308 ERROR("Top swap boundary does not match\n");
309 return 1;
312 ts_h_loc -= param.topswap_size;
313 fill_header_offset(ts_h_loc, header_offset);
315 return 0;
318 static int cbfs_add_master_header(void)
320 const char * const name = "cbfs master header";
321 struct cbfs_image image;
322 struct cbfs_file *header = NULL;
323 struct buffer buffer;
324 int ret = 1;
325 size_t offset;
326 size_t size;
327 void *h_loc;
329 if (cbfs_image_from_buffer(&image, param.image_region,
330 param.headeroffset)) {
331 ERROR("Selected image region is not a CBFS.\n");
332 return 1;
335 if (cbfs_get_entry(&image, name)) {
336 ERROR("'%s' already in ROM image.\n", name);
337 return 1;
340 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
341 return 1;
343 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
344 h->magic = htonl(CBFS_HEADER_MAGIC);
345 h->version = htonl(CBFS_HEADER_VERSION);
346 /* The 4 bytes are left out for two reasons:
347 * 1. the cbfs master header pointer resides there
348 * 2. some cbfs implementations assume that an image that resides
349 * below 4GB has a bootblock and get confused when the end of the
350 * image is at 4GB == 0.
352 h->bootblocksize = htonl(4);
353 h->align = htonl(CBFS_ENTRY_ALIGNMENT);
354 /* The offset and romsize fields within the master header are absolute
355 * values within the boot media. As such, romsize needs to relfect
356 * the end 'offset' for a CBFS. To achieve that the current buffer
357 * representing the CBFS region's size is added to the offset of
358 * the region within a larger image.
360 offset = buffer_get(param.image_region) -
361 buffer_get_original_backing(param.image_region);
362 size = buffer_size(param.image_region);
363 h->romsize = htonl(size + offset);
364 h->offset = htonl(offset);
365 h->architecture = htonl(CBFS_ARCHITECTURE_UNKNOWN);
367 header = cbfs_create_file_header(CBFS_COMPONENT_CBFSHEADER,
368 buffer_size(&buffer), name);
369 if (cbfs_add_entry(&image, &buffer, 0, header) != 0) {
370 ERROR("Failed to add cbfs master header into ROM image.\n");
371 goto done;
374 struct cbfs_file *entry;
375 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
376 ERROR("'%s' not in ROM image?!?\n", name);
377 goto done;
380 uint32_t header_offset = CBFS_SUBHEADER(entry) -
381 buffer_get(&image.buffer);
382 header_offset = -(buffer_size(&image.buffer) - header_offset);
384 h_loc = (void *)(buffer_get(&image.buffer) +
385 buffer_size(&image.buffer) - 4);
386 fill_header_offset(h_loc, header_offset);
388 * If top swap present, update the header
389 * location in secondary bootblock
391 if (param.topswap_size) {
392 if (update_master_header_loc_topswap(&image, h_loc,
393 header_offset))
394 return 1;
397 ret = 0;
399 done:
400 free(header);
401 buffer_delete(&buffer);
402 return ret;
405 static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
407 size_t bb_buf_size = buffer_size(buffer);
409 if (bb_buf_size > param.topswap_size) {
410 ERROR("Bootblock bigger than the topswap boundary\n");
411 ERROR("size = %zd, ts = %d\n", bb_buf_size,
412 param.topswap_size);
413 return 1;
417 * Allocate topswap_size*2 bytes for bootblock to
418 * accommodate the second bootblock.
420 struct buffer new_bootblock, bb1, bb2;
421 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
422 buffer->name))
423 return 1;
425 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
426 bb_buf_size);
427 buffer_splice(&bb2, &new_bootblock,
428 buffer_size(&new_bootblock) - bb_buf_size,
429 bb_buf_size);
431 /* Copy to first bootblock */
432 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
433 /* Copy to second bootblock */
434 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
436 buffer_delete(buffer);
437 buffer_clone(buffer, &new_bootblock);
439 /* Update the location (offset) of bootblock in the region */
440 *offset = convert_to_from_top_aligned(param.image_region,
441 buffer_size(buffer));
443 return 0;
446 static int cbfs_add_component(const char *filename,
447 const char *name,
448 uint32_t type,
449 uint32_t offset,
450 uint32_t headeroffset,
451 convert_buffer_t convert)
453 if (!filename) {
454 ERROR("You need to specify -f/--filename.\n");
455 return 1;
458 if (!name) {
459 ERROR("You need to specify -n/--name.\n");
460 return 1;
463 if (type == 0) {
464 ERROR("You need to specify a valid -t/--type.\n");
465 return 1;
468 struct cbfs_image image;
469 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
470 return 1;
472 if (cbfs_get_entry(&image, name)) {
473 ERROR("'%s' already in ROM image.\n", name);
474 return 1;
477 struct buffer buffer;
478 if (buffer_from_file(&buffer, filename) != 0) {
479 ERROR("Could not load file '%s'.\n", filename);
480 return 1;
484 * Check if Intel CPU topswap is specified this will require a
485 * second bootblock to be added.
487 if (type == CBFS_COMPONENT_BOOTBLOCK && param.topswap_size)
488 if (add_topswap_bootblock(&buffer, &offset))
489 return 1;
491 struct cbfs_file *header =
492 cbfs_create_file_header(type, buffer.size, name);
494 if (convert && convert(&buffer, &offset, header) != 0) {
495 ERROR("Failed to parse file '%s'.\n", filename);
496 buffer_delete(&buffer);
497 return 1;
500 if (param.hash != VB2_HASH_INVALID)
501 if (cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
502 ERROR("couldn't add hash for '%s'\n", name);
503 free(header);
504 buffer_delete(&buffer);
505 return 1;
508 if (param.autogen_attr) {
509 /* Add position attribute if assigned */
510 if (param.baseaddress_assigned || param.stage_xip) {
511 struct cbfs_file_attr_position *attrs =
512 (struct cbfs_file_attr_position *)
513 cbfs_add_file_attr(header,
514 CBFS_FILE_ATTR_TAG_POSITION,
515 sizeof(struct cbfs_file_attr_position));
516 if (attrs == NULL)
517 return -1;
518 /* If we add a stage or a payload, we need to take */
519 /* care about the additional metadata that is added */
520 /* to the cbfs file and therefore set the position */
521 /* the real beginning of the data. */
522 if (type == CBFS_COMPONENT_STAGE)
523 attrs->position = htonl(offset +
524 sizeof(struct cbfs_stage));
525 else if (type == CBFS_COMPONENT_SELF)
526 attrs->position = htonl(offset +
527 sizeof(struct cbfs_payload));
528 else
529 attrs->position = htonl(offset);
531 /* Add alignment attribute if used */
532 if (param.alignment) {
533 struct cbfs_file_attr_align *attrs =
534 (struct cbfs_file_attr_align *)
535 cbfs_add_file_attr(header,
536 CBFS_FILE_ATTR_TAG_ALIGNMENT,
537 sizeof(struct cbfs_file_attr_align));
538 if (attrs == NULL)
539 return -1;
540 attrs->alignment = htonl(param.alignment);
544 if (param.padding) {
545 const uint32_t hs = sizeof(struct cbfs_file_attribute);
546 uint32_t size = MAX(hs, param.padding);
547 INFO("Padding %d bytes\n", size);
548 struct cbfs_file_attribute *attr =
549 (struct cbfs_file_attribute *)cbfs_add_file_attr(
550 header, CBFS_FILE_ATTR_TAG_PADDING,
551 size);
552 if (attr == NULL)
553 return -1;
556 if (IS_TOP_ALIGNED_ADDRESS(offset))
557 offset = convert_to_from_top_aligned(param.image_region,
558 -offset);
559 if (cbfs_add_entry(&image, &buffer, offset, header) != 0) {
560 ERROR("Failed to add '%s' into ROM image.\n", filename);
561 free(header);
562 buffer_delete(&buffer);
563 return 1;
566 free(header);
567 buffer_delete(&buffer);
568 return 0;
571 static int cbfstool_convert_raw(struct buffer *buffer,
572 unused uint32_t *offset, struct cbfs_file *header)
574 char *compressed;
575 int decompressed_size, compressed_size;
576 comp_func_ptr compress;
578 decompressed_size = buffer->size;
579 if (param.precompression) {
580 param.compression = read_le32(buffer->data);
581 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
582 compressed_size = buffer->size - 8;
583 compressed = malloc(compressed_size);
584 if (!compressed)
585 return -1;
586 memcpy(compressed, buffer->data + 8, compressed_size);
587 } else {
588 compress = compression_function(param.compression);
589 if (!compress)
590 return -1;
591 compressed = calloc(buffer->size, 1);
592 if (!compressed)
593 return -1;
595 if (compress(buffer->data, buffer->size,
596 compressed, &compressed_size)) {
597 WARN("Compression failed - disabled\n");
598 free(compressed);
599 return 0;
603 struct cbfs_file_attr_compression *attrs =
604 (struct cbfs_file_attr_compression *)
605 cbfs_add_file_attr(header,
606 CBFS_FILE_ATTR_TAG_COMPRESSION,
607 sizeof(struct cbfs_file_attr_compression));
608 if (attrs == NULL) {
609 free(compressed);
610 return -1;
612 attrs->compression = htonl(param.compression);
613 attrs->decompressed_size = htonl(decompressed_size);
615 free(buffer->data);
616 buffer->data = compressed;
617 buffer->size = compressed_size;
619 header->len = htonl(buffer->size);
620 return 0;
623 static int cbfstool_convert_fsp(struct buffer *buffer,
624 uint32_t *offset, struct cbfs_file *header)
626 uint32_t address;
627 struct buffer fsp;
628 int do_relocation = 1;
630 address = *offset;
633 * If the FSP component is xip, then ensure that the address is a memory
634 * mapped one.
635 * If the FSP component is not xip, then use param.baseaddress that is
636 * passed in by the caller.
638 if (param.stage_xip) {
639 if (!IS_TOP_ALIGNED_ADDRESS(address))
640 address = -convert_to_from_absolute_top_aligned(
641 param.image_region, address);
642 } else {
643 if (param.baseaddress_assigned == 0) {
644 INFO("Honoring pre-linked FSP module.\n");
645 do_relocation = 0;
646 } else {
647 address = param.baseaddress;
651 * *offset should either be 0 or the value returned by
652 * do_cbfs_locate. do_cbfs_locate should not ever return a value
653 * that is TOP_ALIGNED_ADDRESS. Thus, if *offset contains a top
654 * aligned address, set it to 0.
656 * The only requirement in this case is that the binary should
657 * be relocated to the base address that is requested. There is
658 * no requirement on where the file ends up in the cbfs.
660 if (IS_TOP_ALIGNED_ADDRESS(*offset))
661 *offset = 0;
665 * Nothing left to do if relocation is not being attempted. Just add
666 * the file.
668 if (!do_relocation)
669 return cbfstool_convert_raw(buffer, offset, header);
671 /* Create a copy of the buffer to attempt relocation. */
672 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
673 return -1;
675 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
677 /* Replace the buffer contents w/ the relocated ones on success. */
678 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
679 > 0) {
680 buffer_delete(buffer);
681 buffer_clone(buffer, &fsp);
682 } else {
683 buffer_delete(&fsp);
684 WARN("Invalid FSP variant.\n");
687 /* Let the raw path handle all the cbfs metadata logic. */
688 return cbfstool_convert_raw(buffer, offset, header);
691 static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
692 struct cbfs_file *header)
694 struct buffer output;
695 int ret;
697 if (param.stage_xip) {
698 int32_t address;
699 size_t data_size;
701 if (elf_program_file_size(buffer, &data_size) < 0) {
702 ERROR("Could not obtain ELF size\n");
703 return 1;
706 if (do_cbfs_locate(&address, sizeof(struct cbfs_stage),
707 data_size)) {
708 ERROR("Could not find location for XIP stage.\n");
709 return 1;
713 * Ensure the address is a memory mapped one. This assumes
714 * x86 semantics about the boot media being directly mapped
715 * below 4GiB in the CPU address space.
717 address = -convert_to_from_absolute_top_aligned(
718 param.image_region, address);
719 *offset = address;
721 ret = parse_elf_to_xip_stage(buffer, &output, offset,
722 param.ignore_section);
723 } else
724 ret = parse_elf_to_stage(buffer, &output, param.compression,
725 offset, param.ignore_section);
727 if (ret != 0)
728 return -1;
729 buffer_delete(buffer);
730 // Direct assign, no dupe.
731 memcpy(buffer, &output, sizeof(*buffer));
732 header->len = htonl(output.size);
733 return 0;
736 static int cbfstool_convert_mkpayload(struct buffer *buffer,
737 unused uint32_t *offset, struct cbfs_file *header)
739 struct buffer output;
740 int ret;
741 /* Per default, try and see if payload is an ELF binary */
742 ret = parse_elf_to_payload(buffer, &output, param.compression);
744 /* If it's not an ELF, see if it's a FIT */
745 if (ret != 0) {
746 ret = parse_fit_to_payload(buffer, &output, param.compression);
747 if (ret == 0)
748 header->type = htonl(CBFS_COMPONENT_FIT);
751 /* If it's not an FIT, see if it's a UEFI FV */
752 if (ret != 0)
753 ret = parse_fv_to_payload(buffer, &output, param.compression);
755 /* If it's neither ELF nor UEFI Fv, try bzImage */
756 if (ret != 0)
757 ret = parse_bzImage_to_payload(buffer, &output,
758 param.initrd, param.cmdline, param.compression);
760 /* Not a supported payload type */
761 if (ret != 0) {
762 ERROR("Not a supported payload type (ELF / FV).\n");
763 buffer_delete(buffer);
764 return -1;
767 buffer_delete(buffer);
768 // Direct assign, no dupe.
769 memcpy(buffer, &output, sizeof(*buffer));
770 header->len = htonl(output.size);
771 return 0;
774 static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
775 unused uint32_t *offset, struct cbfs_file *header)
777 struct buffer output;
778 if (parse_flat_binary_to_payload(buffer, &output,
779 param.loadaddress,
780 param.entrypoint,
781 param.compression) != 0) {
782 return -1;
784 buffer_delete(buffer);
785 // Direct assign, no dupe.
786 memcpy(buffer, &output, sizeof(*buffer));
787 header->len = htonl(output.size);
788 return 0;
791 static int cbfs_add(void)
793 int32_t address;
794 convert_buffer_t convert;
795 uint32_t local_baseaddress = param.baseaddress;
797 if (param.alignment && param.baseaddress) {
798 ERROR("Cannot specify both alignment and base address\n");
799 return 1;
802 convert = cbfstool_convert_raw;
804 /* Set the alignment to 4KiB minimum for FSP blobs when no base address
805 * is provided so that relocation can occur. */
806 if (param.type == CBFS_COMPONENT_FSP) {
807 if (!param.baseaddress_assigned)
808 param.alignment = 4*1024;
809 convert = cbfstool_convert_fsp;
810 } else if (param.stage_xip) {
811 ERROR("cbfs add supports xip only for FSP component type\n");
812 return 1;
815 if (param.alignment) {
816 /* CBFS compression file attribute is unconditionally added. */
817 size_t metadata_sz = sizeof(struct cbfs_file_attr_compression);
818 if (do_cbfs_locate(&address, metadata_sz, 0))
819 return 1;
820 local_baseaddress = address;
823 return cbfs_add_component(param.filename,
824 param.name,
825 param.type,
826 local_baseaddress,
827 param.headeroffset,
828 convert);
831 static int cbfs_add_stage(void)
833 if (param.stage_xip) {
834 if (param.baseaddress_assigned) {
835 ERROR("Cannot specify base address for XIP.\n");
836 return 1;
839 if (param.compression != CBFS_COMPRESS_NONE) {
840 ERROR("Cannot specify compression for XIP.\n");
841 return 1;
845 return cbfs_add_component(param.filename,
846 param.name,
847 CBFS_COMPONENT_STAGE,
848 param.baseaddress,
849 param.headeroffset,
850 cbfstool_convert_mkstage);
853 static int cbfs_add_payload(void)
855 return cbfs_add_component(param.filename,
856 param.name,
857 CBFS_COMPONENT_SELF,
858 param.baseaddress,
859 param.headeroffset,
860 cbfstool_convert_mkpayload);
863 static int cbfs_add_flat_binary(void)
865 if (param.loadaddress == 0) {
866 ERROR("You need to specify a valid "
867 "-l/--load-address.\n");
868 return 1;
870 if (param.entrypoint == 0) {
871 ERROR("You need to specify a valid "
872 "-e/--entry-point.\n");
873 return 1;
875 return cbfs_add_component(param.filename,
876 param.name,
877 CBFS_COMPONENT_SELF,
878 param.baseaddress,
879 param.headeroffset,
880 cbfstool_convert_mkflatpayload);
883 static int cbfs_add_integer(void)
885 if (!param.u64val_assigned) {
886 ERROR("You need to specify a value to write.\n");
887 return 1;
889 return cbfs_add_integer_component(param.name,
890 param.u64val,
891 param.baseaddress,
892 param.headeroffset);
895 static int cbfs_remove(void)
897 if (!param.name) {
898 ERROR("You need to specify -n/--name.\n");
899 return 1;
902 struct cbfs_image image;
903 if (cbfs_image_from_buffer(&image, param.image_region,
904 param.headeroffset))
905 return 1;
907 if (cbfs_remove_entry(&image, param.name) != 0) {
908 ERROR("Removing file '%s' failed.\n",
909 param.name);
910 return 1;
913 return 0;
916 static int cbfs_create(void)
918 struct cbfs_image image;
919 memset(&image, 0, sizeof(image));
920 buffer_clone(&image.buffer, param.image_region);
922 if (param.fmap) {
923 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
924 param.baseaddress_assigned ||
925 param.headeroffset_assigned ||
926 param.cbfsoffset_assigned ||
927 param.bootblock) {
928 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
929 return 1;
932 return cbfs_image_create(&image, image.buffer.size);
935 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
936 ERROR("You need to specify -m/--machine arch.\n");
937 return 1;
940 struct buffer bootblock;
941 if (!param.bootblock) {
942 DEBUG("-B not given, creating image without bootblock.\n");
943 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
944 return 1;
945 } else if (buffer_from_file(&bootblock, param.bootblock)) {
946 return 1;
949 if (!param.alignment)
950 param.alignment = CBFS_ALIGNMENT;
952 // Set default offsets. x86, as usual, needs to be a special snowflake.
953 if (!param.baseaddress_assigned) {
954 if (param.arch == CBFS_ARCHITECTURE_X86) {
955 // Make sure there's at least enough room for rel_offset
956 param.baseaddress = param.size -
957 MAX(bootblock.size, sizeof(int32_t));
958 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
959 param.baseaddress);
960 } else {
961 param.baseaddress = 0;
962 DEBUG("bootblock starts at address 0x0.\n");
965 if (!param.headeroffset_assigned) {
966 if (param.arch == CBFS_ARCHITECTURE_X86) {
967 param.headeroffset = param.baseaddress -
968 sizeof(struct cbfs_header);
969 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
970 param.headeroffset);
971 } else {
972 param.headeroffset = align_up(param.baseaddress +
973 bootblock.size, sizeof(uint32_t));
974 DEBUG("CBFS header placed behind bootblock (%#x).\n",
975 param.headeroffset);
978 if (!param.cbfsoffset_assigned) {
979 if (param.arch == CBFS_ARCHITECTURE_X86) {
980 param.cbfsoffset = 0;
981 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
982 } else {
983 param.cbfsoffset = align_up(param.headeroffset +
984 sizeof(struct cbfs_header),
985 CBFS_ALIGNMENT);
986 DEBUG("CBFS entries start beind master header (%#x).\n",
987 param.cbfsoffset);
991 int ret = cbfs_legacy_image_create(&image,
992 param.arch,
993 CBFS_ALIGNMENT,
994 &bootblock,
995 param.baseaddress,
996 param.headeroffset,
997 param.cbfsoffset);
998 buffer_delete(&bootblock);
999 return ret;
1002 static int cbfs_layout(void)
1004 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1005 if (!fmap) {
1006 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1007 return 1;
1010 printf("This image contains the following sections that can be %s with this tool:\n",
1011 param.show_immutable ? "accessed" : "manipulated");
1012 puts("");
1013 for (unsigned i = 0; i < fmap->nareas; ++i) {
1014 const struct fmap_area *current = fmap->areas + i;
1016 bool readonly = partitioned_file_fmap_count(param.image_file,
1017 partitioned_file_fmap_select_children_of, current) ||
1018 region_is_flashmap((const char *)current->name);
1019 if (!param.show_immutable && readonly)
1020 continue;
1022 printf("'%s'", current->name);
1024 // Detect consecutive sections that describe the same region and
1025 // show them as aliases. This cannot find equivalent entries
1026 // that aren't adjacent; however, fmaptool doesn't generate
1027 // FMAPs with such sections, so this convenience feature works
1028 // for all but the strangest manually created FMAP binaries.
1029 // TODO: This could be done by parsing the FMAP into some kind
1030 // of tree that had duplicate lists in addition to child lists,
1031 // which would allow covering that weird, unlikely case as well.
1032 unsigned lookahead;
1033 for (lookahead = 1; i + lookahead < fmap->nareas;
1034 ++lookahead) {
1035 const struct fmap_area *consecutive =
1036 fmap->areas + i + lookahead;
1037 if (consecutive->offset != current->offset ||
1038 consecutive->size != current->size)
1039 break;
1040 printf(", '%s'", consecutive->name);
1042 if (lookahead > 1)
1043 fputs(" are aliases for the same region", stdout);
1045 const char *qualifier = "";
1046 if (readonly)
1047 qualifier = "read-only, ";
1048 else if (region_is_modern_cbfs((const char *)current->name))
1049 qualifier = "CBFS, ";
1050 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1051 current->offset);
1053 i += lookahead - 1;
1055 puts("");
1057 if (param.show_immutable) {
1058 puts("It is at least possible to perform the read action on every section listed above.");
1059 } else {
1060 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1061 puts("To see the image's read-only sections as well, rerun with the -w option.");
1064 return 0;
1067 static int cbfs_print(void)
1069 struct cbfs_image image;
1070 if (cbfs_image_from_buffer(&image, param.image_region,
1071 param.headeroffset))
1072 return 1;
1073 if (param.machine_parseable)
1074 return cbfs_print_parseable_directory(&image);
1075 else {
1076 printf("FMAP REGION: %s\n", param.region_name);
1077 return cbfs_print_directory(&image);
1081 static int cbfs_extract(void)
1083 if (!param.filename) {
1084 ERROR("You need to specify -f/--filename.\n");
1085 return 1;
1088 if (!param.name) {
1089 ERROR("You need to specify -n/--name.\n");
1090 return 1;
1093 struct cbfs_image image;
1094 if (cbfs_image_from_buffer(&image, param.image_region,
1095 param.headeroffset))
1096 return 1;
1098 return cbfs_export_entry(&image, param.name, param.filename,
1099 param.arch, !param.unprocessed);
1102 static int cbfs_write(void)
1104 if (!param.filename) {
1105 ERROR("You need to specify a valid input -f/--file.\n");
1106 return 1;
1108 if (!partitioned_file_is_partitioned(param.image_file)) {
1109 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1110 return 1;
1113 if (!param.force && region_is_modern_cbfs(param.region_name)) {
1114 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1115 param.region_name);
1116 return 1;
1119 struct buffer new_content;
1120 if (buffer_from_file(&new_content, param.filename))
1121 return 1;
1123 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1124 strlen(FMAP_SIGNATURE))) {
1125 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1126 param.filename);
1127 buffer_delete(&new_content);
1128 return 1;
1130 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
1131 strlen(CBFS_FILE_MAGIC))) {
1132 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1133 param.filename);
1134 buffer_delete(&new_content);
1135 return 1;
1138 unsigned offset = 0;
1139 if (param.fill_partial_upward && param.fill_partial_downward) {
1140 ERROR("You may only specify one of -u and -d.\n");
1141 buffer_delete(&new_content);
1142 return 1;
1143 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1144 if (new_content.size != param.image_region->size) {
1145 ERROR("File to add is %zu bytes and would not fill %zu-byte target region (did you mean to pass either -u or -d?)\n",
1146 new_content.size, param.image_region->size);
1147 buffer_delete(&new_content);
1148 return 1;
1150 } else {
1151 if (new_content.size > param.image_region->size) {
1152 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1153 new_content.size, param.image_region->size);
1154 buffer_delete(&new_content);
1155 return 1;
1157 if (param.u64val == (uint64_t)-1) {
1158 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1159 param.fill_partial_upward ? "bottom" : "top");
1160 } else if (param.u64val > 0xff) {
1161 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1162 buffer_delete(&new_content);
1163 return 1;
1164 } else {
1165 memset(buffer_get(param.image_region),
1166 param.u64val & 0xff,
1167 buffer_size(param.image_region));
1169 if (param.fill_partial_downward)
1170 offset = param.image_region->size - new_content.size;
1173 memcpy(param.image_region->data + offset, new_content.data,
1174 new_content.size);
1175 buffer_delete(&new_content);
1176 return 0;
1179 static int cbfs_read(void)
1181 if (!param.filename) {
1182 ERROR("You need to specify a valid output -f/--file.\n");
1183 return 1;
1185 if (!partitioned_file_is_partitioned(param.image_file)) {
1186 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1187 return 1;
1190 return buffer_write_file(param.image_region, param.filename);
1193 static int cbfs_update_fit(void)
1195 if (!param.name) {
1196 ERROR("You need to specify -n/--name.\n");
1197 return 1;
1200 if (param.fit_empty_entries <= 0) {
1201 ERROR("Invalid number of fit entries "
1202 "(-x/--empty-fits): %d\n", param.fit_empty_entries);
1203 return 1;
1206 struct buffer bootblock;
1207 // The bootblock is part of the CBFS on x86
1208 buffer_clone(&bootblock, param.image_region);
1210 struct cbfs_image image;
1211 if (cbfs_image_from_buffer(&image, param.image_region,
1212 param.headeroffset))
1213 return 1;
1215 uint32_t addr = 0;
1218 * Get the address of provided region for first row.
1220 if (param.ucode_region) {
1221 struct buffer ucode;
1223 if (partitioned_file_read_region(&ucode,
1224 param.image_file, param.ucode_region))
1225 addr = -convert_to_from_top_aligned(&ucode, 0);
1226 else
1227 return 1;
1231 if (fit_update_table(&bootblock, &image, param.name,
1232 param.fit_empty_entries, convert_to_from_top_aligned,
1233 param.topswap_size, addr))
1234 return 1;
1236 // The region to be written depends on the type of image, so we write it
1237 // here rather than having main() write the CBFS region back as usual.
1238 return !partitioned_file_write_region(param.image_file, &bootblock);
1241 static int cbfs_copy(void)
1243 struct cbfs_image src_image;
1244 struct buffer src_buf;
1246 if (!param.source_region) {
1247 ERROR("You need to specify -R/--source-region.\n");
1248 return 1;
1251 /* Obtain the source region and convert it to a cbfs_image. */
1252 if (!partitioned_file_read_region(&src_buf, param.image_file,
1253 param.source_region)) {
1254 ERROR("Region not found in image: %s\n", param.source_region);
1255 return 1;
1258 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1259 return 1;
1261 return cbfs_copy_instance(&src_image, param.image_region);
1264 static int cbfs_compact(void)
1266 struct cbfs_image image;
1267 if (cbfs_image_from_buffer(&image, param.image_region,
1268 param.headeroffset))
1269 return 1;
1270 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1271 return cbfs_compact_instance(&image);
1274 static int cbfs_expand(void)
1276 struct buffer src_buf;
1278 /* Obtain the source region. */
1279 if (!partitioned_file_read_region(&src_buf, param.image_file,
1280 param.region_name)) {
1281 ERROR("Region not found in image: %s\n", param.source_region);
1282 return 1;
1285 return cbfs_expand_to_region(param.image_region);
1288 static int cbfs_truncate(void)
1290 struct buffer src_buf;
1292 /* Obtain the source region. */
1293 if (!partitioned_file_read_region(&src_buf, param.image_file,
1294 param.region_name)) {
1295 ERROR("Region not found in image: %s\n", param.source_region);
1296 return 1;
1299 uint32_t size;
1300 int result = cbfs_truncate_space(param.image_region, &size);
1301 printf("0x%x\n", size);
1302 return result;
1305 static const struct command commands[] = {
1306 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
1307 {"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary,
1308 true, true},
1309 {"add-payload", "H:r:f:n:c:b:C:I:p:vA:gh?", cbfs_add_payload,
1310 true, true},
1311 {"add-stage", "a:H:r:f:n:t:c:b:P:S:p:yvA:gh?", cbfs_add_stage,
1312 true, true},
1313 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
1314 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
1315 {"compact", "r:h?", cbfs_compact, true, true},
1316 {"copy", "r:R:h?", cbfs_copy, true, true},
1317 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
1318 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
1319 {"layout", "wvh?", cbfs_layout, false, false},
1320 {"print", "H:r:vkh?", cbfs_print, true, false},
1321 {"read", "r:f:vh?", cbfs_read, true, false},
1322 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
1323 {"update-fit", "H:r:n:x:vh?j:q:", cbfs_update_fit, true, true},
1324 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
1325 {"expand", "r:h?", cbfs_expand, true, true},
1326 {"truncate", "r:h?", cbfs_truncate, true, true},
1329 static struct option long_options[] = {
1330 {"alignment", required_argument, 0, 'a' },
1331 {"base-address", required_argument, 0, 'b' },
1332 {"bootblock", required_argument, 0, 'B' },
1333 {"cmdline", required_argument, 0, 'C' },
1334 {"compression", required_argument, 0, 'c' },
1335 {"topswap-size", required_argument, 0, 'j' },
1336 {"empty-fits", required_argument, 0, 'x' },
1337 {"entry-point", required_argument, 0, 'e' },
1338 {"file", required_argument, 0, 'f' },
1339 {"fill-downward", no_argument, 0, 'd' },
1340 {"fill-upward", no_argument, 0, 'u' },
1341 {"flashmap", required_argument, 0, 'M' },
1342 {"fmap-regions", required_argument, 0, 'r' },
1343 {"force", no_argument, 0, 'F' },
1344 {"source-region", required_argument, 0, 'R' },
1345 {"hash-algorithm",required_argument, 0, 'A' },
1346 {"header-offset", required_argument, 0, 'H' },
1347 {"help", no_argument, 0, 'h' },
1348 {"ignore-sec", required_argument, 0, 'S' },
1349 {"initrd", required_argument, 0, 'I' },
1350 {"int", required_argument, 0, 'i' },
1351 {"load-address", required_argument, 0, 'l' },
1352 {"machine", required_argument, 0, 'm' },
1353 {"name", required_argument, 0, 'n' },
1354 {"offset", required_argument, 0, 'o' },
1355 {"padding", required_argument, 0, 'p' },
1356 {"page-size", required_argument, 0, 'P' },
1357 {"ucode-region", required_argument, 0, 'q' },
1358 {"size", required_argument, 0, 's' },
1359 {"top-aligned", required_argument, 0, 'T' },
1360 {"type", required_argument, 0, 't' },
1361 {"verbose", no_argument, 0, 'v' },
1362 {"with-readonly", no_argument, 0, 'w' },
1363 {"xip", no_argument, 0, 'y' },
1364 {"gen-attribute", no_argument, 0, 'g' },
1365 {"mach-parseable",no_argument, 0, 'k' },
1366 {"unprocessed", no_argument, 0, 'U' },
1367 {NULL, 0, 0, 0 }
1370 static int dispatch_command(struct command command)
1372 if (command.accesses_region) {
1373 assert(param.image_file);
1375 if (partitioned_file_is_partitioned(param.image_file)) {
1376 INFO("Performing operation on '%s' region...\n",
1377 param.region_name);
1379 if (!partitioned_file_read_region(param.image_region,
1380 param.image_file, param.region_name)) {
1381 ERROR("The image will be left unmodified.\n");
1382 return 1;
1385 if (command.modifies_region) {
1386 // We (intentionally) don't support overwriting the FMAP
1387 // section. If you find yourself wanting to do this,
1388 // consider creating a new image rather than performing
1389 // whatever hacky transformation you were planning.
1390 if (region_is_flashmap(param.region_name)) {
1391 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1392 param.region_name);
1393 ERROR("The image will be left unmodified.\n");
1394 return 1;
1396 // We don't allow writing raw data to regions that
1397 // contain nested regions, since doing so would
1398 // overwrite all such subregions.
1399 if (partitioned_file_region_contains_nested(
1400 param.image_file, param.region_name)) {
1401 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1402 param.region_name);
1403 ERROR("The image will be left unmodified.\n");
1404 return 1;
1409 if (command.function()) {
1410 if (partitioned_file_is_partitioned(param.image_file)) {
1411 ERROR("Failed while operating on '%s' region!\n",
1412 param.region_name);
1413 ERROR("The image will be left unmodified.\n");
1415 return 1;
1418 return 0;
1421 static void usage(char *name)
1423 printf
1424 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
1425 "USAGE:\n" " %s [-h]\n"
1426 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
1427 " -H header_offset Do not search for header; use this offset*\n"
1428 " -T Output top-aligned memory address\n"
1429 " -u Accept short data; fill upward/from bottom\n"
1430 " -d Accept short data; fill downward/from top\n"
1431 " -F Force action\n"
1432 " -g Generate position and alignment arguments\n"
1433 " -U Unprocessed; don't decompress or make ELF\n"
1434 " -v Provide verbose output\n"
1435 " -h Display this help message\n\n"
1436 "COMMANDs:\n"
1437 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
1438 " [-c compression] [-b base-address | -a alignment] \\\n"
1439 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
1440 " [-j topswap-size] (Intel CPUs only) "
1441 "Add a component\n"
1443 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
1444 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1445 " [-c compression] [-b base-address] \\\n"
1446 " (linux specific: [-C cmdline] [-I initrd]) "
1447 "Add a payload to the ROM\n"
1448 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1449 " [-c compression] [-b base] [-S section-to-ignore] \\\n"
1450 " [-a alignment] [-y|--xip] [-P page-size] "
1451 "Add a stage to the ROM\n"
1452 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1453 " [-A hash] -l load-address -e entry-point \\\n"
1454 " [-c compression] [-b base] "
1455 "Add a 32bit flat mode binary\n"
1456 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
1457 "Add a raw 64-bit integer value\n"
1458 " add-master-header [-r image,regions] \\ \n"
1459 " [-j topswap-size] (Intel CPUs only) "
1460 "Add a legacy CBFS master header\n"
1461 " remove [-r image,regions] -n NAME "
1462 "Remove a component\n"
1463 " compact -r image,regions "
1464 "Defragment CBFS image.\n"
1465 " copy -r image,regions -R source-region "
1466 "Create a copy (duplicate) cbfs instance in fmap\n"
1467 " create -m ARCH -s size [-b bootblock offset] \\\n"
1468 " [-o CBFS offset] [-H header offset] [-B bootblock] "
1469 "Create a legacy ROM file with CBFS master header*\n"
1470 " create -M flashmap [-r list,of,regions,containing,cbfses] "
1471 "Create a new-style partitioned firmware image\n"
1472 " locate [-r image,regions] -f FILE -n NAME [-P page-size] \\\n"
1473 " [-a align] [-T] "
1474 "Find a place for a file of that size\n"
1475 " layout [-w] "
1476 "List mutable (or, with -w, readable) image regions\n"
1477 " print [-r image,regions] "
1478 "Show the contents of the ROM\n"
1479 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
1480 "Extracts a file from ROM\n"
1481 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
1482 "Write file into same-size [or larger] raw region\n"
1483 " read [-r fmap-region] -f file "
1484 "Extract raw region contents into binary file\n"
1485 " truncate [-r fmap-region] "
1486 "Truncate CBFS and print new size on stdout\n"
1487 " expand [-r fmap-region] "
1488 "Expand CBFS to span entire region\n"
1489 " update-fit [-r image,regions] -n MICROCODE_BLOB_NAME \\\n"
1490 " -x EMTPY_FIT_ENTRIES \\ \n"
1491 " [-j topswap-size [-q ucode-region](Intel CPUs only)] "
1492 "Updates the FIT table with microcode entries.\n"
1494 " ucode-region is a region in the FMAP, its address is \n"
1496 " inserted as the first entry in the topswap FIT. \n"
1497 "\n"
1498 "OFFSETs:\n"
1499 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
1500 " in two possible formats: if their value is greater than\n"
1501 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
1502 " address; otherwise, they are treated as an offset into flash.\n"
1503 "ARCHes:\n", name, name
1505 print_supported_architectures();
1507 printf("TYPEs:\n");
1508 print_supported_filetypes();
1509 printf(
1510 "\n* Note that these actions and switches are only valid when\n"
1511 " working with legacy images whose structure is described\n"
1512 " primarily by a CBFS master header. New-style images, in\n"
1513 " contrast, exclusively make use of an FMAP to describe their\n"
1514 " layout: this must minimally contain an '%s' section\n"
1515 " specifying the location of this FMAP itself and a '%s'\n"
1516 " section describing the primary CBFS. It should also be noted\n"
1517 " that, when working with such images, the -F and -r switches\n"
1518 " default to '%s' for convenience, and both the -b switch to\n"
1519 " CBFS operations and the output of the locate action become\n"
1520 " relative to the selected CBFS region's lowest address.\n"
1521 " The one exception to this rule is the top-aligned address,\n"
1522 " which is always relative to the end of the entire image\n"
1523 " rather than relative to the local region; this is true for\n"
1524 " for both input (sufficiently large) and output (-T) data.\n",
1525 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
1526 SECTION_NAME_PRIMARY_CBFS
1530 int main(int argc, char **argv)
1532 size_t i;
1533 int c;
1535 if (argc < 3) {
1536 usage(argv[0]);
1537 return 1;
1540 char *image_name = argv[1];
1541 char *cmd = argv[2];
1542 optind += 2;
1544 for (i = 0; i < ARRAY_SIZE(commands); i++) {
1545 if (strcmp(cmd, commands[i].name) != 0)
1546 continue;
1548 while (1) {
1549 char *suffix = NULL;
1550 int option_index = 0;
1552 c = getopt_long(argc, argv, commands[i].optstring,
1553 long_options, &option_index);
1554 if (c == -1) {
1555 if (optind < argc) {
1556 ERROR("%s: excessive argument -- '%s'"
1557 "\n", argv[0], argv[optind]);
1558 return 1;
1560 break;
1563 /* Filter out illegal long options */
1564 if (strchr(commands[i].optstring, c) == NULL) {
1565 /* TODO maybe print actual long option instead */
1566 ERROR("%s: invalid option -- '%c'\n",
1567 argv[0], c);
1568 c = '?';
1571 switch(c) {
1572 case 'n':
1573 param.name = optarg;
1574 break;
1575 case 't':
1576 if (intfiletype(optarg) != ((uint64_t) - 1))
1577 param.type = intfiletype(optarg);
1578 else
1579 param.type = strtoul(optarg, NULL, 0);
1580 if (param.type == 0)
1581 WARN("Unknown type '%s' ignored\n",
1582 optarg);
1583 break;
1584 case 'c': {
1585 if (strcmp(optarg, "precompression") == 0) {
1586 param.precompression = 1;
1587 break;
1589 int algo = cbfs_parse_comp_algo(optarg);
1590 if (algo >= 0)
1591 param.compression = algo;
1592 else
1593 WARN("Unknown compression '%s' ignored.\n",
1594 optarg);
1595 break;
1597 case 'A': {
1598 int algo = cbfs_parse_hash_algo(optarg);
1599 if (algo >= 0)
1600 param.hash = algo;
1601 else {
1602 ERROR("Unknown hash algorithm '%s'.\n",
1603 optarg);
1604 return 1;
1606 break;
1608 case 'M':
1609 param.fmap = optarg;
1610 break;
1611 case 'r':
1612 param.region_name = optarg;
1613 break;
1614 case 'R':
1615 param.source_region = optarg;
1616 break;
1617 case 'b':
1618 param.baseaddress = strtoul(optarg, &suffix, 0);
1619 if (!*optarg || (suffix && *suffix)) {
1620 ERROR("Invalid base address '%s'.\n",
1621 optarg);
1622 return 1;
1624 // baseaddress may be zero on non-x86, so we
1625 // need an explicit "baseaddress_assigned".
1626 param.baseaddress_assigned = 1;
1627 break;
1628 case 'l':
1629 param.loadaddress = strtoul(optarg, &suffix, 0);
1630 if (!*optarg || (suffix && *suffix)) {
1631 ERROR("Invalid load address '%s'.\n",
1632 optarg);
1633 return 1;
1635 break;
1636 case 'e':
1637 param.entrypoint = strtoul(optarg, &suffix, 0);
1638 if (!*optarg || (suffix && *suffix)) {
1639 ERROR("Invalid entry point '%s'.\n",
1640 optarg);
1641 return 1;
1643 break;
1644 case 's':
1645 param.size = strtoul(optarg, &suffix, 0);
1646 if (!*optarg) {
1647 ERROR("Empty size specified.\n");
1648 return 1;
1650 switch (tolower((int)suffix[0])) {
1651 case 'k':
1652 param.size *= 1024;
1653 break;
1654 case 'm':
1655 param.size *= 1024 * 1024;
1656 break;
1657 case '\0':
1658 break;
1659 default:
1660 ERROR("Invalid suffix for size '%s'.\n",
1661 optarg);
1662 return 1;
1664 break;
1665 case 'B':
1666 param.bootblock = optarg;
1667 break;
1668 case 'H':
1669 param.headeroffset = strtoul(
1670 optarg, &suffix, 0);
1671 if (!*optarg || (suffix && *suffix)) {
1672 ERROR("Invalid header offset '%s'.\n",
1673 optarg);
1674 return 1;
1676 param.headeroffset_assigned = 1;
1677 break;
1678 case 'a':
1679 param.alignment = strtoul(optarg, &suffix, 0);
1680 if (!*optarg || (suffix && *suffix)) {
1681 ERROR("Invalid alignment '%s'.\n",
1682 optarg);
1683 return 1;
1685 break;
1686 case 'p':
1687 param.padding = strtoul(optarg, &suffix, 0);
1688 if (!*optarg || (suffix && *suffix)) {
1689 ERROR("Invalid pad size '%s'.\n",
1690 optarg);
1691 return 1;
1693 break;
1694 case 'P':
1695 param.pagesize = strtoul(optarg, &suffix, 0);
1696 if (!*optarg || (suffix && *suffix)) {
1697 ERROR("Invalid page size '%s'.\n",
1698 optarg);
1699 return 1;
1701 break;
1702 case 'o':
1703 param.cbfsoffset = strtoul(optarg, &suffix, 0);
1704 if (!*optarg || (suffix && *suffix)) {
1705 ERROR("Invalid cbfs offset '%s'.\n",
1706 optarg);
1707 return 1;
1709 param.cbfsoffset_assigned = 1;
1710 break;
1711 case 'f':
1712 param.filename = optarg;
1713 break;
1714 case 'F':
1715 param.force = 1;
1716 break;
1717 case 'i':
1718 param.u64val = strtoull(optarg, &suffix, 0);
1719 param.u64val_assigned = 1;
1720 if (!*optarg || (suffix && *suffix)) {
1721 ERROR("Invalid int parameter '%s'.\n",
1722 optarg);
1723 return 1;
1725 break;
1726 case 'u':
1727 param.fill_partial_upward = true;
1728 break;
1729 case 'd':
1730 param.fill_partial_downward = true;
1731 break;
1732 case 'w':
1733 param.show_immutable = true;
1734 break;
1735 case 'x':
1736 param.fit_empty_entries = strtol(
1737 optarg, &suffix, 0);
1738 if (!*optarg || (suffix && *suffix)) {
1739 ERROR("Invalid number of fit entries "
1740 "'%s'.\n", optarg);
1741 return 1;
1743 break;
1744 case 'j':
1745 param.topswap_size = strtol(optarg, NULL, 0);
1746 if (!is_valid_topswap())
1747 return 1;
1748 break;
1749 case 'q':
1750 param.ucode_region = optarg;
1751 break;
1752 case 'v':
1753 verbose++;
1754 break;
1755 case 'm':
1756 param.arch = string_to_arch(optarg);
1757 break;
1758 case 'I':
1759 param.initrd = optarg;
1760 break;
1761 case 'C':
1762 param.cmdline = optarg;
1763 break;
1764 case 'S':
1765 param.ignore_section = optarg;
1766 break;
1767 case 'y':
1768 param.stage_xip = true;
1769 break;
1770 case 'g':
1771 param.autogen_attr = true;
1772 break;
1773 case 'k':
1774 param.machine_parseable = true;
1775 break;
1776 case 'U':
1777 param.unprocessed = true;
1778 break;
1779 case 'h':
1780 case '?':
1781 usage(argv[0]);
1782 return 1;
1783 default:
1784 break;
1788 if (commands[i].function == cbfs_create) {
1789 if (param.fmap) {
1790 struct buffer flashmap;
1791 if (buffer_from_file(&flashmap, param.fmap))
1792 return 1;
1793 param.image_file = partitioned_file_create(
1794 image_name, &flashmap);
1795 buffer_delete(&flashmap);
1796 } else if (param.size) {
1797 param.image_file = partitioned_file_create_flat(
1798 image_name, param.size);
1799 } else {
1800 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
1801 return 1;
1803 } else {
1804 bool write_access = commands[i].modifies_region;
1806 param.image_file =
1807 partitioned_file_reopen(image_name,
1808 write_access);
1810 if (!param.image_file)
1811 return 1;
1813 unsigned num_regions = 1;
1814 for (const char *list = strchr(param.region_name, ','); list;
1815 list = strchr(list + 1, ','))
1816 ++num_regions;
1818 // If the action needs to read an image region, as indicated by
1819 // having accesses_region set in its command struct, that
1820 // region's buffer struct will be stored here and the client
1821 // will receive a pointer to it via param.image_region. It
1822 // need not write the buffer back to the image file itself,
1823 // since this behavior can be requested via its modifies_region
1824 // field. Additionally, it should never free the region buffer,
1825 // as that is performed automatically once it completes.
1826 struct buffer image_regions[num_regions];
1827 memset(image_regions, 0, sizeof(image_regions));
1829 bool seen_primary_cbfs = false;
1830 char region_name_scratch[strlen(param.region_name) + 1];
1831 strcpy(region_name_scratch, param.region_name);
1832 param.region_name = strtok(region_name_scratch, ",");
1833 for (unsigned region = 0; region < num_regions; ++region) {
1834 if (!param.region_name) {
1835 ERROR("Encountered illegal degenerate region name in -r list\n");
1836 ERROR("The image will be left unmodified.\n");
1837 partitioned_file_close(param.image_file);
1838 return 1;
1841 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
1842 == 0)
1843 seen_primary_cbfs = true;
1845 param.image_region = image_regions + region;
1846 if (dispatch_command(commands[i])) {
1847 partitioned_file_close(param.image_file);
1848 return 1;
1851 param.region_name = strtok(NULL, ",");
1854 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
1855 ERROR("The creation -r list must include the mandatory '%s' section.\n",
1856 SECTION_NAME_PRIMARY_CBFS);
1857 ERROR("The image will be left unmodified.\n");
1858 partitioned_file_close(param.image_file);
1859 return 1;
1862 if (commands[i].modifies_region) {
1863 assert(param.image_file);
1864 for (unsigned region = 0; region < num_regions;
1865 ++region) {
1867 if (!partitioned_file_write_region(
1868 param.image_file,
1869 image_regions + region)) {
1870 partitioned_file_close(
1871 param.image_file);
1872 return 1;
1877 partitioned_file_close(param.image_file);
1878 return 0;
1881 ERROR("Unknown command '%s'.\n", cmd);
1882 usage(argv[0]);
1883 return 1;