soc/intel/xeon_sp: Align resources to 4K
[coreboot2.git] / util / cbfstool / cbfstool.c
blob88bf22b6493d214889473db21beca08e20918125
1 /* cbfstool, CLI utility for CBFS file manipulation */
2 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <strings.h>
8 #include <ctype.h>
9 #include <unistd.h>
10 #include <getopt.h>
11 #include "common.h"
12 #include "cbfs.h"
13 #include "cbfs_image.h"
14 #include "cbfs_sections.h"
15 #include "elfparsing.h"
16 #include "partitioned_file.h"
17 #include "lz4/lib/xxhash.h"
18 #include <commonlib/bsd/cbfs_private.h>
19 #include <commonlib/bsd/compression.h>
20 #include <commonlib/bsd/metadata_hash.h>
21 #include <commonlib/fsp.h>
22 #include <commonlib/endian.h>
23 #include <commonlib/helpers.h>
24 #include <commonlib/region.h>
25 #include <vboot_host.h>
27 struct command {
28 const char *name;
29 const char *optstring;
30 int (*function) (void);
31 // Whether to populate param.image_region before invoking function
32 bool accesses_region;
33 // This set to true means two things:
34 // - in case of a command operating on a region, the region's contents
35 // will be written back to image_file at the end
36 // - write access to the file is required
37 bool modifies_region;
40 static struct param {
41 partitioned_file_t *image_file;
42 struct buffer *image_region;
43 const char *name;
44 const char *filename;
45 const char *fmap;
46 const char *region_name;
47 const char *source_region;
48 const char *bootblock;
49 const char *ignore_sections;
50 const char *ucode_region;
51 uint64_t u64val;
52 uint32_t type;
53 uint32_t baseaddress;
55 * Input can be negative. It will be transformed to offset from start of region (if
56 * negative) and stored in baseaddress.
58 long long int baseaddress_input;
59 uint32_t baseaddress_assigned;
60 uint64_t loadaddress;
61 uint32_t headeroffset;
63 * Input can be negative. It will be transformed to offset from start of region (if
64 * negative) and stored in baseaddress.
66 long long int headeroffset_input;
67 uint32_t headeroffset_assigned;
68 uint64_t entrypoint;
69 uint32_t size;
70 uint32_t alignment;
71 uint32_t pagesize;
72 uint32_t cbfsoffset;
74 * Input can be negative. It will be transformed to corresponding region offset (if
75 * negative) and stored in baseaddress.
77 long long int cbfsoffset_input;
78 uint32_t cbfsoffset_assigned;
79 uint32_t arch;
80 uint32_t padding;
81 uint32_t topswap_size;
82 bool u64val_assigned;
83 bool fill_partial_upward;
84 bool fill_partial_downward;
85 bool show_immutable;
86 bool stage_xip;
87 bool force_pow2_pagesize;
88 bool autogen_attr;
89 bool machine_parseable;
90 bool unprocessed;
91 bool ibb;
92 enum cbfs_compression compression;
93 int precompression;
94 enum vb2_hash_algorithm hash;
95 /* For linux payloads */
96 char *initrd;
97 char *cmdline;
98 int force;
100 * Base and size of extended window for decoding SPI flash greater than 16MiB in host
101 * address space on x86 platforms. The assumptions here are:
102 * 1. Top 16MiB is still decoded in the fixed decode window just below 4G boundary.
103 * 2. Rest of the SPI flash below the top 16MiB is mapped at the top of extended
104 * window. Even though the platform might support a larger extended window, the SPI
105 * flash part used by the mainboard might not be large enough to be mapped in the entire
106 * window. In such cases, the mapping is assumed to be in the top part of the extended
107 * window with the bottom part remaining unused.
109 uint32_t ext_win_base;
110 uint32_t ext_win_size;
111 } param = {
112 /* All variables not listed are initialized as zero. */
113 .arch = CBFS_ARCHITECTURE_UNKNOWN,
114 .compression = CBFS_COMPRESS_NONE,
115 .hash = VB2_HASH_INVALID,
116 .headeroffset = HEADER_OFFSET_UNKNOWN,
117 .region_name = SECTION_NAME_PRIMARY_CBFS,
118 .u64val = -1,
122 * This "metadata_hash cache" caches the value and location of the CBFS metadata
123 * hash embedded in the bootblock when CBFS verification is enabled. The first
124 * call to get_mh_cache() searches for the cache by scanning the whole bootblock
125 * for its 8-byte signature, later calls will just return the previously found
126 * information again. If the cbfs_hash.algo member in the result is
127 * VB2_HASH_INVALID, that means no metadata hash was found and this image does
128 * not use CBFS verification.
130 struct mh_cache {
131 const char *region;
132 size_t offset;
133 struct vb2_hash cbfs_hash;
134 platform_fixup_func fixup;
135 bool initialized;
138 static struct mh_cache *get_mh_cache(void)
140 static struct mh_cache mhc;
142 if (mhc.initialized)
143 return &mhc;
145 mhc.initialized = true;
147 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
148 if (!fmap)
149 goto no_metadata_hash;
151 /* Find the metadata_hash container. If there is a "BOOTBLOCK" FMAP section, it's
152 there. If not, it's a normal file in the primary CBFS section. */
153 size_t offset, size;
154 struct buffer buffer;
155 if (fmap_find_area(fmap, SECTION_NAME_BOOTBLOCK)) {
156 if (!partitioned_file_read_region(&buffer, param.image_file,
157 SECTION_NAME_BOOTBLOCK))
158 goto no_metadata_hash;
159 mhc.region = SECTION_NAME_BOOTBLOCK;
160 offset = 0;
161 size = buffer.size;
162 } else {
163 struct cbfs_image cbfs;
164 struct cbfs_file *mh_container;
165 if (!partitioned_file_read_region(&buffer, param.image_file,
166 SECTION_NAME_PRIMARY_CBFS))
167 goto no_metadata_hash;
168 mhc.region = SECTION_NAME_PRIMARY_CBFS;
169 if (cbfs_image_from_buffer(&cbfs, &buffer, param.headeroffset))
170 goto no_metadata_hash;
171 mh_container = cbfs_get_entry(&cbfs, "bootblock");
172 if (!mh_container || be32toh(mh_container->type) != CBFS_TYPE_BOOTBLOCK) {
173 /* Check for apu/amdfw file */
174 mh_container = cbfs_get_entry(&cbfs, "apu/amdfw");
175 if (!mh_container || be32toh(mh_container->type) != CBFS_TYPE_AMDFW)
176 goto no_metadata_hash;
179 offset = (void *)mh_container + be32toh(mh_container->offset) -
180 buffer_get(&cbfs.buffer);
181 size = be32toh(mh_container->len);
184 /* Find and validate the metadata hash anchor inside the containing file and
185 record its exact byte offset from the start of the FMAP region. */
186 struct metadata_hash_anchor *anchor = memmem(buffer_get(&buffer) + offset,
187 size, METADATA_HASH_ANCHOR_MAGIC, sizeof(anchor->magic));
188 if (anchor) {
189 if (!vb2_digest_size(anchor->cbfs_hash.algo)) {
190 ERROR("Unknown CBFS metadata hash type: %d\n",
191 anchor->cbfs_hash.algo);
192 goto no_metadata_hash;
194 mhc.cbfs_hash = anchor->cbfs_hash;
195 mhc.offset = (void *)anchor - buffer_get(&buffer);
196 mhc.fixup = platform_fixups_probe(&buffer, mhc.offset,
197 mhc.region);
198 return &mhc;
201 no_metadata_hash:
202 mhc.cbfs_hash.algo = VB2_HASH_INVALID;
203 return &mhc;
206 static void update_and_info(const char *name, void *dst, void *src, size_t size)
208 if (!memcmp(dst, src, size))
209 return;
210 char *src_str = bintohex(src, size);
211 char *dst_str = bintohex(dst, size);
212 INFO("Updating %s from %s to %s\n", name, dst_str, src_str);
213 memcpy(dst, src, size);
214 free(src_str);
215 free(dst_str);
218 static int update_anchor(struct mh_cache *mhc, uint8_t *fmap_hash)
220 struct buffer buffer;
221 if (!partitioned_file_read_region(&buffer, param.image_file,
222 mhc->region))
223 return -1;
224 struct metadata_hash_anchor *anchor = buffer_get(&buffer) + mhc->offset;
225 /* The metadata hash anchor should always still be where we left it. */
226 assert(!memcmp(anchor->magic, METADATA_HASH_ANCHOR_MAGIC,
227 sizeof(anchor->magic)) &&
228 anchor->cbfs_hash.algo == mhc->cbfs_hash.algo);
229 update_and_info("CBFS metadata hash", anchor->cbfs_hash.raw,
230 mhc->cbfs_hash.raw, vb2_digest_size(anchor->cbfs_hash.algo));
231 if (fmap_hash) {
232 update_and_info("FMAP hash",
233 metadata_hash_anchor_fmap_hash(anchor), fmap_hash,
234 vb2_digest_size(anchor->cbfs_hash.algo));
236 if (mhc->fixup && mhc->fixup(&buffer, mhc->offset) != 0)
237 return -1;
238 if (!partitioned_file_write_region(param.image_file, &buffer))
239 return -1;
240 return 0;
244 /* This should be called after every time CBFS metadata might have changed. It
245 will recalculate and update the metadata hash in the bootblock if needed. */
246 static int maybe_update_metadata_hash(struct cbfs_image *cbfs)
248 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS))
249 return 0; /* Metadata hash only embedded in primary CBFS. */
251 struct mh_cache *mhc = get_mh_cache();
252 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
253 return 0;
255 enum cb_err err = cbfs_walk(cbfs, NULL, NULL, &mhc->cbfs_hash,
256 CBFS_WALK_WRITEBACK_HASH);
257 if (err != CB_CBFS_NOT_FOUND) {
258 ERROR("Unexpected cbfs_walk() error %d\n", err);
259 return -1;
262 return update_anchor(mhc, NULL);
265 /* This should be called after every time the FMAP or the bootblock itself might
266 have changed, and will write the new FMAP hash into the metadata hash anchor
267 in the bootblock if required (usually when the bootblock is first added). */
268 static int maybe_update_fmap_hash(void)
270 if (strcmp(param.region_name, SECTION_NAME_BOOTBLOCK) &&
271 strcmp(param.region_name, SECTION_NAME_FMAP) &&
272 param.type != CBFS_TYPE_BOOTBLOCK &&
273 param.type != CBFS_TYPE_AMDFW)
274 return 0; /* FMAP and bootblock didn't change. */
276 struct mh_cache *mhc = get_mh_cache();
277 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
278 return 0;
280 struct vb2_hash fmap_hash;
281 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
282 if (!fmap || vb2_hash_calculate(false, fmap, fmap_size(fmap),
283 mhc->cbfs_hash.algo, &fmap_hash))
284 return -1;
285 return update_anchor(mhc, fmap_hash.raw);
288 static bool verification_exclude(enum cbfs_type type)
290 switch (type) {
291 case CBFS_TYPE_BOOTBLOCK:
292 case CBFS_TYPE_CBFSHEADER:
293 case CBFS_TYPE_INTEL_FIT:
294 case CBFS_TYPE_AMDFW:
295 return true;
296 default:
297 return false;
301 static bool region_is_flashmap(const char *region)
303 return partitioned_file_region_check_magic(param.image_file, region,
304 FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
307 /* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
308 static bool region_is_modern_cbfs(const char *region)
310 return partitioned_file_region_check_magic(param.image_file, region,
311 CBFS_FILE_MAGIC, strlen(CBFS_FILE_MAGIC));
314 /* This describes a window from the SPI flash address space into the host address space. */
315 struct mmap_window {
316 struct region flash_space;
317 struct region host_space;
320 /* Should be enough for now */
321 #define MMAP_MAX_WINDOWS 3
323 /* Table of all the decode windows supported by the platform. */
324 static int mmap_window_table_size;
325 static struct mmap_window mmap_window_table[MMAP_MAX_WINDOWS];
327 static void add_mmap_window(size_t flash_offset, size_t host_offset,
328 size_t window_size)
330 if (mmap_window_table_size >= MMAP_MAX_WINDOWS) {
331 ERROR("Too many memory map windows\n");
332 return;
335 mmap_window_table[mmap_window_table_size].flash_space.offset = flash_offset;
336 mmap_window_table[mmap_window_table_size].host_space.offset = host_offset;
337 mmap_window_table[mmap_window_table_size].flash_space.size = window_size;
338 mmap_window_table[mmap_window_table_size].host_space.size = window_size;
339 mmap_window_table_size++;
343 static int decode_mmap_arg(char *arg)
345 if (arg == NULL)
346 return 1;
348 union {
349 unsigned long int array[3];
350 struct {
351 unsigned long int flash_base;
352 unsigned long int mmap_base;
353 unsigned long int mmap_size;
355 } mmap_args;
356 char *suffix = NULL;
357 char *substring = strtok(arg, ":");
358 for (size_t i = 0; i < ARRAY_SIZE(mmap_args.array); i++) {
359 if (!substring) {
360 ERROR("Invalid mmap arguments '%s'.\n",
361 arg);
362 return 1;
364 mmap_args.array[i] = strtol(substring, &suffix, 0);
365 if (suffix && *suffix) {
366 ERROR("Invalid mmap arguments '%s'.\n",
367 arg);
368 return 1;
370 substring = strtok(NULL, ":");
373 if (substring != NULL) {
374 ERROR("Invalid argument, too many substrings '%s'.\n",
375 arg);
377 return 1;
380 add_mmap_window(mmap_args.flash_base, mmap_args.mmap_base, mmap_args.mmap_size);
381 return 0;
384 #define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
385 #define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
387 static bool create_mmap_windows(void)
389 static bool done;
391 if (done)
392 return done;
394 // No memory map provided, use a default one
395 if (mmap_window_table_size == 0) {
396 const size_t image_size = partitioned_file_total_size(param.image_file);
397 printf("Image SIZE %zu\n", image_size);
398 const size_t std_window_size = MIN(DEFAULT_DECODE_WINDOW_MAX_SIZE, image_size);
399 const size_t std_window_flash_offset = image_size - std_window_size;
402 * Default decode window lives just below 4G boundary in host space and maps up to a
403 * maximum of 16MiB. If the window is smaller than 16MiB, the SPI flash window is mapped
404 * at the top of the host window just below 4G.
406 add_mmap_window(std_window_flash_offset, DEFAULT_DECODE_WINDOW_TOP - std_window_size, std_window_size);
407 } else {
409 * Check provided memory map
411 for (int i = 0; i < mmap_window_table_size; i++) {
412 for (int j = i + 1; j < mmap_window_table_size; j++) {
413 if (region_overlap(&mmap_window_table[i].flash_space,
414 &mmap_window_table[j].flash_space)) {
415 ERROR("Flash space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
416 region_offset(&mmap_window_table[i].flash_space),
417 region_end(&mmap_window_table[i].flash_space),
418 region_offset(&mmap_window_table[j].flash_space),
419 region_end(&mmap_window_table[j].flash_space));
420 return false;
423 if (region_overlap(&mmap_window_table[i].host_space,
424 &mmap_window_table[j].host_space)) {
425 ERROR("Host space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
426 region_offset(&mmap_window_table[i].flash_space),
427 region_end(&mmap_window_table[i].flash_space),
428 region_offset(&mmap_window_table[j].flash_space),
429 region_end(&mmap_window_table[j].flash_space));
430 return false;
436 done = true;
437 return done;
440 static unsigned int convert_address(const struct region *to, const struct region *from,
441 unsigned int addr)
444 * Calculate the offset in the "from" region and use that offset to calculate
445 * corresponding address in the "to" region.
447 size_t offset = addr - region_offset(from);
448 return region_offset(to) + offset;
451 enum mmap_addr_type {
452 HOST_SPACE_ADDR,
453 FLASH_SPACE_ADDR,
456 static int find_mmap_window(enum mmap_addr_type addr_type, unsigned int addr)
458 size_t i;
460 for (i = 0; i < ARRAY_SIZE(mmap_window_table); i++) {
461 const struct region *reg;
463 if (addr_type == HOST_SPACE_ADDR)
464 reg = &mmap_window_table[i].host_space;
465 else
466 reg = &mmap_window_table[i].flash_space;
468 if (region_offset(reg) <= addr &&
469 ((uint64_t)region_offset(reg) + (uint64_t)region_sz(reg) - 1) >= addr)
470 return i;
473 return -1;
476 static unsigned int convert_host_to_flash(const struct buffer *region, unsigned int addr)
478 int idx;
479 const struct region *to, *from;
481 idx = find_mmap_window(HOST_SPACE_ADDR, addr);
482 if (idx == -1) {
483 ERROR("Host address(%x) not in any mmap window!\n", addr);
484 return 0;
487 to = &mmap_window_table[idx].flash_space;
488 from = &mmap_window_table[idx].host_space;
490 /* region->offset is subtracted because caller expects offset in the given region. */
491 return convert_address(to, from, addr) - region->offset;
494 static unsigned int convert_flash_to_host(const struct buffer *region, unsigned int addr)
496 int idx;
497 const struct region *to, *from;
500 * region->offset is added because caller provides offset in the given region. This is
501 * converted to an absolute address in the SPI flash space. This is done before the
502 * conversion as opposed to after in convert_host_to_flash() above because the address
503 * is actually an offset within the region. So, it needs to be converted into an
504 * absolute address in the SPI flash space before converting into an address in host
505 * space.
507 addr += region->offset;
508 idx = find_mmap_window(FLASH_SPACE_ADDR, addr);
510 if (idx == -1) {
511 ERROR("SPI flash address(%x) not in any mmap window!\n", addr);
512 return 0;
515 to = &mmap_window_table[idx].host_space;
516 from = &mmap_window_table[idx].flash_space;
518 return convert_address(to, from, addr);
521 static unsigned int convert_addr_space(const struct buffer *region, unsigned int addr)
523 assert(region);
525 assert(create_mmap_windows());
527 if (IS_HOST_SPACE_ADDRESS(addr))
528 return convert_host_to_flash(region, addr);
529 else
530 return convert_flash_to_host(region, addr);
534 * This function takes offset value which represents the offset from one end of the region and
535 * converts it to offset from the other end of the region. offset is expected to be positive.
537 static int convert_region_offset(unsigned int offset, uint32_t *region_offset)
539 size_t size;
541 if (param.size) {
542 size = param.size;
543 } else {
544 assert(param.image_region);
545 size = param.image_region->size;
548 if (size < offset) {
549 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size, offset);
550 return 1;
553 *region_offset = size - offset;
554 return 0;
557 static int do_cbfs_locate(uint32_t *cbfs_addr, size_t data_size)
559 uint32_t metadata_size = 0;
561 if (!param.name) {
562 ERROR("You need to specify -n/--name.\n");
563 return 1;
566 struct cbfs_image image;
567 if (cbfs_image_from_buffer(&image, param.image_region,
568 param.headeroffset))
569 return 1;
571 if (cbfs_get_entry(&image, param.name))
572 WARN("'%s' already in CBFS.\n", param.name);
574 if (!data_size) {
575 ERROR("File '%s' is empty?\n", param.name);
576 return 1;
579 /* Compute required page size */
580 if (param.force_pow2_pagesize) {
581 param.pagesize = 1;
582 while (param.pagesize < data_size)
583 param.pagesize <<= 1;
584 DEBUG("Page size is %d (0x%x)\n", param.pagesize, param.pagesize);
587 /* Include cbfs_file size along with space for with name. */
588 metadata_size += cbfs_calculate_file_header_size(param.name);
589 /* Adjust metadata_size if additional attributes were added */
590 if (param.autogen_attr) {
591 if (param.alignment)
592 metadata_size += sizeof(struct cbfs_file_attr_align);
593 if (param.baseaddress_assigned || param.stage_xip)
594 metadata_size += sizeof(struct cbfs_file_attr_position);
596 if (param.precompression || param.compression != CBFS_COMPRESS_NONE)
597 metadata_size += sizeof(struct cbfs_file_attr_compression);
598 if (param.type == CBFS_TYPE_STAGE)
599 metadata_size += sizeof(struct cbfs_file_attr_stageheader);
601 /* Take care of the hash attribute if it is used */
602 if (param.hash != VB2_HASH_INVALID)
603 metadata_size += cbfs_file_attr_hash_size(param.hash);
605 int32_t address = cbfs_locate_entry(&image, data_size, param.pagesize,
606 param.alignment, metadata_size);
608 if (address < 0) {
609 ERROR("'%s'(%u + %zu) can't fit in CBFS for page-size %#x, align %#x.\n",
610 param.name, metadata_size, data_size, param.pagesize, param.alignment);
611 return 1;
614 *cbfs_addr = address;
615 return 0;
618 typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
619 struct cbfs_file *header);
621 static int cbfs_add_integer_component(const char *name,
622 uint64_t u64val,
623 uint32_t offset,
624 uint32_t headeroffset) {
625 struct cbfs_image image;
626 struct cbfs_file *header = NULL;
627 struct buffer buffer;
628 int i, ret = 1;
630 if (!name) {
631 ERROR("You need to specify -n/--name.\n");
632 return 1;
635 if (buffer_create(&buffer, 8, name) != 0)
636 return 1;
638 for (i = 0; i < 8; i++)
639 buffer.data[i] = (u64val >> i*8) & 0xff;
641 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset)) {
642 ERROR("Selected image region is not a CBFS.\n");
643 goto done;
646 if (cbfs_get_entry(&image, name)) {
647 ERROR("'%s' already in ROM image.\n", name);
648 goto done;
651 header = cbfs_create_file_header(CBFS_TYPE_RAW,
652 buffer.size, name);
653 if (!header)
654 goto done;
656 enum vb2_hash_algorithm algo = get_mh_cache()->cbfs_hash.algo;
657 if (algo != VB2_HASH_INVALID)
658 if (cbfs_add_file_hash(header, &buffer, algo)) {
659 ERROR("couldn't add hash for '%s'\n", name);
660 goto done;
663 if (cbfs_add_entry(&image, &buffer, offset, header, 0) != 0) {
664 ERROR("Failed to add %llu into ROM image as '%s'.\n",
665 (long long unsigned)u64val, name);
666 goto done;
669 ret = maybe_update_metadata_hash(&image);
671 done:
672 free(header);
673 buffer_delete(&buffer);
674 return ret;
677 static int is_valid_topswap(void)
679 switch (param.topswap_size) {
680 case (64 * KiB):
681 case (128 * KiB):
682 case (256 * KiB):
683 case (512 * KiB):
684 case (1 * MiB):
685 break;
686 default:
687 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
688 param.topswap_size);
689 return 0;
691 return 1;
694 static void fill_header_offset(void *location, uint32_t offset)
696 // TODO: When we have a BE target, we'll need to store this as BE
697 write_le32(location, offset);
700 static int update_master_header_loc_topswap(struct cbfs_image *image,
701 void *h_loc, uint32_t header_offset)
703 struct cbfs_file *entry;
704 void *ts_h_loc = h_loc;
706 entry = cbfs_get_entry(image, "bootblock");
707 if (entry == NULL) {
708 ERROR("Bootblock not in ROM image?!?\n");
709 return 1;
713 * Check if the existing topswap boundary matches with
714 * the one provided.
716 if (param.topswap_size != be32toh(entry->len)/2) {
717 ERROR("Top swap boundary does not match\n");
718 return 1;
721 ts_h_loc -= param.topswap_size;
722 fill_header_offset(ts_h_loc, header_offset);
724 return 0;
727 static int cbfs_add_master_header(void)
729 const char * const name = "cbfs master header";
730 struct cbfs_image image;
731 struct cbfs_file *header = NULL;
732 struct buffer buffer;
733 int ret = 1;
734 size_t offset;
735 size_t size;
736 void *h_loc;
738 if (cbfs_image_from_buffer(&image, param.image_region,
739 param.headeroffset)) {
740 ERROR("Selected image region is not a CBFS.\n");
741 return 1;
744 if (cbfs_get_entry(&image, name)) {
745 ERROR("'%s' already in ROM image.\n", name);
746 return 1;
749 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
750 return 1;
752 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
753 h->magic = htobe32(CBFS_HEADER_MAGIC);
754 h->version = htobe32(CBFS_HEADER_VERSION);
755 /* The 4 bytes are left out for two reasons:
756 * 1. the cbfs master header pointer resides there
757 * 2. some cbfs implementations assume that an image that resides
758 * below 4GB has a bootblock and get confused when the end of the
759 * image is at 4GB == 0.
761 h->bootblocksize = htobe32(4);
762 h->align = htobe32(CBFS_ALIGNMENT);
763 /* The offset and romsize fields within the master header are absolute
764 * values within the boot media. As such, romsize needs to relfect
765 * the end 'offset' for a CBFS. To achieve that the current buffer
766 * representing the CBFS region's size is added to the offset of
767 * the region within a larger image.
769 offset = buffer_get(param.image_region) -
770 buffer_get_original_backing(param.image_region);
771 size = buffer_size(param.image_region);
772 h->romsize = htobe32(size + offset);
773 h->offset = htobe32(offset);
774 h->architecture = htobe32(CBFS_ARCHITECTURE_UNKNOWN);
776 /* Never add a hash attribute to the master header. */
777 header = cbfs_create_file_header(CBFS_TYPE_CBFSHEADER,
778 buffer_size(&buffer), name);
779 if (!header)
780 goto done;
781 if (cbfs_add_entry(&image, &buffer, 0, header, 0) != 0) {
782 ERROR("Failed to add cbfs master header into ROM image.\n");
783 goto done;
786 struct cbfs_file *entry;
787 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
788 ERROR("'%s' not in ROM image?!?\n", name);
789 goto done;
792 uint32_t header_offset = CBFS_SUBHEADER(entry) -
793 buffer_get(&image.buffer);
794 header_offset = -(buffer_size(&image.buffer) - header_offset);
796 h_loc = (void *)(buffer_get(&image.buffer) +
797 buffer_size(&image.buffer) - 4);
798 fill_header_offset(h_loc, header_offset);
800 * If top swap present, update the header
801 * location in secondary bootblock
803 if (param.topswap_size) {
804 if (update_master_header_loc_topswap(&image, h_loc,
805 header_offset))
806 goto done;
809 ret = maybe_update_metadata_hash(&image);
811 done:
812 free(header);
813 buffer_delete(&buffer);
814 return ret;
817 static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
819 size_t bb_buf_size = buffer_size(buffer);
821 if (bb_buf_size > param.topswap_size) {
822 ERROR("Bootblock bigger than the topswap boundary\n");
823 ERROR("size = %zd, ts = %d\n", bb_buf_size,
824 param.topswap_size);
825 return 1;
829 * Allocate topswap_size*2 bytes for bootblock to
830 * accommodate the second bootblock.
832 struct buffer new_bootblock, bb1, bb2;
833 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
834 buffer->name))
835 return 1;
837 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
838 bb_buf_size);
839 buffer_splice(&bb2, &new_bootblock,
840 buffer_size(&new_bootblock) - bb_buf_size,
841 bb_buf_size);
843 /* Copy to first bootblock */
844 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
845 /* Copy to second bootblock */
846 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
848 buffer_delete(buffer);
849 buffer_clone(buffer, &new_bootblock);
851 /* Update the location (offset) of bootblock in the region */
852 return convert_region_offset(buffer_size(buffer), offset);
855 static int cbfs_add_component(const char *filename,
856 const char *name,
857 uint32_t headeroffset,
858 convert_buffer_t convert)
861 * The steps used to determine the final placement offset in CBFS, in order:
863 * 1. If --base-address was passed, that value is used. If it was passed in the host
864 * address space, convert it to flash address space. (After that, |*offset| is always
865 * in the flash address space.)
867 * 2. The convert() function may write a location back to |offset|, usually by calling
868 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
869 * the CBFS file in its final form (after any compression and conversion).
871 * 3. If --align was passed and the offset is still undecided at this point,
872 * do_cbfs_locate() is called to find an appropriately aligned location.
874 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
875 * location that fits.
877 uint32_t offset = param.baseaddress_assigned ? param.baseaddress : 0;
878 size_t len_align = 0;
880 if (param.alignment && param.baseaddress_assigned) {
881 ERROR("Cannot specify both alignment and base address\n");
882 return 1;
885 if (param.stage_xip && param.compression != CBFS_COMPRESS_NONE) {
886 ERROR("Cannot specify compression for XIP.\n");
887 return 1;
890 if (!filename) {
891 ERROR("You need to specify -f/--filename.\n");
892 return 1;
895 if (!name) {
896 ERROR("You need to specify -n/--name.\n");
897 return 1;
900 if (param.type == 0) {
901 ERROR("You need to specify a valid -t/--type.\n");
902 return 1;
905 struct cbfs_image image;
906 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
907 return 1;
909 if (cbfs_get_entry(&image, name)) {
910 ERROR("'%s' already in ROM image.\n", name);
911 return 1;
914 struct buffer buffer;
915 if (buffer_from_file(&buffer, filename) != 0) {
916 ERROR("Could not load file '%s'.\n", filename);
917 return 1;
920 struct cbfs_file *header =
921 cbfs_create_file_header(param.type, buffer.size, name);
922 if (!header)
923 goto error;
925 /* Bootblock and CBFS header should never have file hashes. When adding
926 the bootblock it is important that we *don't* look up the metadata
927 hash yet (before it is added) or we'll cache an outdated result. */
928 if (!verification_exclude(param.type)) {
929 enum vb2_hash_algorithm mh_algo = get_mh_cache()->cbfs_hash.algo;
930 if (mh_algo != VB2_HASH_INVALID && param.hash != mh_algo) {
931 if (param.hash == VB2_HASH_INVALID) {
932 param.hash = mh_algo;
933 } else {
934 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
935 vb2_get_hash_algorithm_name(param.hash),
936 vb2_get_hash_algorithm_name(mh_algo));
937 goto error;
943 * Check if Intel CPU topswap is specified this will require a
944 * second bootblock to be added.
946 if (param.type == CBFS_TYPE_BOOTBLOCK && param.topswap_size)
947 if (add_topswap_bootblock(&buffer, &offset))
948 goto error;
950 /* With --base-address we allow host space addresses -- if so, convert it here. */
951 if (IS_HOST_SPACE_ADDRESS(offset))
952 offset = convert_addr_space(param.image_region, offset);
954 if (convert && convert(&buffer, &offset, header) != 0) {
955 ERROR("Failed to parse file '%s'.\n", filename);
956 goto error;
959 /* This needs to run after convert() to take compression into account. */
960 if (!offset && param.alignment)
961 if (do_cbfs_locate(&offset, buffer_size(&buffer)))
962 goto error;
964 /* This needs to run after convert() to hash the actual final file data. */
965 if (param.hash != VB2_HASH_INVALID &&
966 cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
967 ERROR("couldn't add hash for '%s'\n", name);
968 goto error;
971 if (param.autogen_attr) {
972 /* Add position attribute if assigned */
973 if (param.baseaddress_assigned || param.stage_xip) {
974 struct cbfs_file_attr_position *attrs =
975 (struct cbfs_file_attr_position *)
976 cbfs_add_file_attr(header,
977 CBFS_FILE_ATTR_TAG_POSITION,
978 sizeof(struct cbfs_file_attr_position));
979 if (attrs == NULL)
980 goto error;
981 attrs->position = htobe32(offset);
983 /* Add alignment attribute if used */
984 if (param.alignment) {
985 struct cbfs_file_attr_align *attrs =
986 (struct cbfs_file_attr_align *)
987 cbfs_add_file_attr(header,
988 CBFS_FILE_ATTR_TAG_ALIGNMENT,
989 sizeof(struct cbfs_file_attr_align));
990 if (attrs == NULL)
991 goto error;
992 attrs->alignment = htobe32(param.alignment);
996 if (param.ibb) {
997 /* Mark as Initial Boot Block */
998 struct cbfs_file_attribute *attrs = cbfs_add_file_attr(header,
999 CBFS_FILE_ATTR_TAG_IBB,
1000 sizeof(struct cbfs_file_attribute));
1001 if (attrs == NULL)
1002 goto error;
1003 /* For Intel TXT minimum align is 16 */
1004 len_align = 16;
1007 if (param.padding) {
1008 const uint32_t hs = sizeof(struct cbfs_file_attribute);
1009 uint32_t size = ALIGN_UP(MAX(hs, param.padding),
1010 CBFS_ATTRIBUTE_ALIGN);
1011 INFO("Padding %d bytes\n", size);
1012 struct cbfs_file_attribute *attr =
1013 (struct cbfs_file_attribute *)cbfs_add_file_attr(
1014 header, CBFS_FILE_ATTR_TAG_PADDING,
1015 size);
1016 if (attr == NULL)
1017 goto error;
1020 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
1021 ERROR("Failed to add '%s' into ROM image.\n", filename);
1022 goto error;
1025 free(header);
1026 buffer_delete(&buffer);
1028 return maybe_update_metadata_hash(&image) || maybe_update_fmap_hash();
1030 error:
1031 free(header);
1032 buffer_delete(&buffer);
1033 return 1;
1036 static int cbfstool_convert_raw(struct buffer *buffer,
1037 unused uint32_t *offset, struct cbfs_file *header)
1039 char *compressed;
1040 int decompressed_size, compressed_size;
1041 comp_func_ptr compress;
1043 decompressed_size = buffer->size;
1044 if (param.precompression) {
1045 param.compression = read_le32(buffer->data);
1046 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
1047 compressed_size = buffer->size - 8;
1048 compressed = malloc(compressed_size);
1049 if (!compressed)
1050 return -1;
1051 memcpy(compressed, buffer->data + 8, compressed_size);
1052 } else {
1053 if (param.compression == CBFS_COMPRESS_NONE)
1054 goto out;
1056 compress = compression_function(param.compression);
1057 if (!compress)
1058 return -1;
1059 compressed = calloc(buffer->size, 1);
1060 if (!compressed)
1061 return -1;
1063 if (compress(buffer->data, buffer->size,
1064 compressed, &compressed_size)) {
1065 WARN("Compression failed - disabled\n");
1066 free(compressed);
1067 goto out;
1071 struct cbfs_file_attr_compression *attrs =
1072 (struct cbfs_file_attr_compression *)
1073 cbfs_add_file_attr(header,
1074 CBFS_FILE_ATTR_TAG_COMPRESSION,
1075 sizeof(struct cbfs_file_attr_compression));
1076 if (attrs == NULL) {
1077 free(compressed);
1078 return -1;
1080 attrs->compression = htobe32(param.compression);
1081 attrs->decompressed_size = htobe32(decompressed_size);
1083 free(buffer->data);
1084 buffer->data = compressed;
1085 buffer->size = compressed_size;
1087 out:
1088 header->len = htobe32(buffer->size);
1089 return 0;
1092 static int cbfstool_convert_fsp(struct buffer *buffer,
1093 uint32_t *offset, struct cbfs_file *header)
1095 uint32_t address;
1096 struct buffer fsp;
1099 * There are 4 different cases here:
1101 * 1. --xip and --base-address: we need to place the binary at the given base address
1102 * in the CBFS image and relocate it to that address. *offset was already filled in,
1103 * but we need to convert it to the host address space for relocation.
1105 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1106 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
1107 * This also needs to be converted to the host address space for relocation.
1109 * 3. No --xip but a --base-address: special case where --base-address does not have its
1110 * normal meaning, instead we use it as the relocation target address. We explicitly
1111 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1113 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1114 * not be relocated. Just chain directly to convert_raw() for compression.
1117 if (param.stage_xip) {
1118 if (!param.baseaddress_assigned) {
1119 param.alignment = 4*1024;
1120 if (do_cbfs_locate(offset, buffer_size(buffer)))
1121 return -1;
1123 assert(!IS_HOST_SPACE_ADDRESS(*offset));
1124 address = convert_addr_space(param.image_region, *offset);
1125 } else {
1126 if (param.baseaddress_assigned == 0) {
1127 INFO("Honoring pre-linked FSP module, no relocation.\n");
1128 return cbfstool_convert_raw(buffer, offset, header);
1129 } else {
1130 address = param.baseaddress;
1131 *offset = 0;
1135 /* Create a copy of the buffer to attempt relocation. */
1136 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
1137 return -1;
1139 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
1141 /* Replace the buffer contents w/ the relocated ones on success. */
1142 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
1143 > 0) {
1144 buffer_delete(buffer);
1145 buffer_clone(buffer, &fsp);
1146 } else {
1147 buffer_delete(&fsp);
1148 WARN("Invalid FSP variant.\n");
1151 /* Let the raw path handle all the cbfs metadata logic. */
1152 return cbfstool_convert_raw(buffer, offset, header);
1155 static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
1156 struct cbfs_file *header)
1158 struct buffer output;
1159 size_t data_size;
1160 int ret;
1162 if (elf_program_file_size(buffer, &data_size) < 0) {
1163 ERROR("Could not obtain ELF size\n");
1164 return 1;
1168 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1169 * here. That is okay because XIP stages may not be compressed, so their size cannot
1170 * change anymore at a later point.
1172 if (param.stage_xip &&
1173 do_cbfs_locate(offset, data_size)) {
1174 ERROR("Could not find location for stage.\n");
1175 return 1;
1178 struct cbfs_file_attr_stageheader *stageheader = (void *)
1179 cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
1180 sizeof(struct cbfs_file_attr_stageheader));
1181 if (!stageheader)
1182 return -1;
1184 if (param.stage_xip) {
1185 uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
1186 assert(IS_HOST_SPACE_ADDRESS(host_space_address));
1187 ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
1188 param.ignore_sections, stageheader);
1189 } else {
1190 ret = parse_elf_to_stage(buffer, &output, param.ignore_sections,
1191 stageheader);
1193 if (ret != 0)
1194 return -1;
1196 /* Store a hash of original uncompressed stage to compare later. */
1197 size_t decmp_size = buffer_size(&output);
1198 uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
1200 /* Chain to base conversion routine to handle compression. */
1201 ret = cbfstool_convert_raw(&output, offset, header);
1202 if (ret != 0)
1203 goto fail;
1205 /* Special care must be taken for LZ4-compressed stages that the BSS is
1206 large enough to provide scratch space for in-place decompression. */
1207 if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
1208 size_t memlen = be32toh(stageheader->memlen);
1209 size_t compressed_size = buffer_size(&output);
1210 uint8_t *compare_buffer = malloc(memlen);
1211 uint8_t *start = compare_buffer + memlen - compressed_size;
1212 if (!compare_buffer) {
1213 ERROR("Out of memory\n");
1214 goto fail;
1216 memcpy(start, buffer_get(&output), compressed_size);
1217 ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
1218 if (ret == 0) {
1219 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1220 free(compare_buffer);
1221 goto fail;
1222 } else if (ret != (int)decmp_size ||
1223 decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
1224 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1225 free(compare_buffer);
1226 goto fail;
1228 free(compare_buffer);
1231 buffer_delete(buffer);
1232 buffer_clone(buffer, &output);
1233 return 0;
1235 fail:
1236 buffer_delete(&output);
1237 return -1;
1240 static int cbfstool_convert_mkpayload(struct buffer *buffer,
1241 unused uint32_t *offset, struct cbfs_file *header)
1243 struct buffer output;
1244 int ret;
1245 /* Per default, try and see if payload is an ELF binary */
1246 ret = parse_elf_to_payload(buffer, &output, param.compression);
1248 /* If it's not an ELF, see if it's a FIT */
1249 if (ret != 0) {
1250 ret = parse_fit_to_payload(buffer, &output, param.compression);
1251 if (ret == 0)
1252 header->type = htobe32(CBFS_TYPE_FIT_PAYLOAD);
1255 /* If it's not an FIT, see if it's a UEFI FV */
1256 if (ret != 0)
1257 ret = parse_fv_to_payload(buffer, &output, param.compression);
1259 /* If it's neither ELF nor UEFI Fv, try bzImage */
1260 if (ret != 0)
1261 ret = parse_bzImage_to_payload(buffer, &output,
1262 param.initrd, param.cmdline, param.compression);
1264 /* Not a supported payload type */
1265 if (ret != 0) {
1266 ERROR("Not a supported payload type (ELF / FV).\n");
1267 buffer_delete(buffer);
1268 return -1;
1271 buffer_delete(buffer);
1272 // Direct assign, no dupe.
1273 memcpy(buffer, &output, sizeof(*buffer));
1274 header->len = htobe32(output.size);
1275 return 0;
1278 static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
1279 unused uint32_t *offset, struct cbfs_file *header)
1281 struct buffer output;
1282 if (parse_flat_binary_to_payload(buffer, &output,
1283 param.loadaddress,
1284 param.entrypoint,
1285 param.compression) != 0) {
1286 return -1;
1288 buffer_delete(buffer);
1289 // Direct assign, no dupe.
1290 memcpy(buffer, &output, sizeof(*buffer));
1291 header->len = htobe32(output.size);
1292 return 0;
1295 static int cbfs_add(void)
1297 convert_buffer_t convert = cbfstool_convert_raw;
1299 if (param.type == CBFS_TYPE_FSP) {
1300 convert = cbfstool_convert_fsp;
1301 } else if (param.type == CBFS_TYPE_STAGE) {
1302 ERROR("stages can only be added with cbfstool add-stage\n");
1303 return 1;
1304 } else if (param.stage_xip) {
1305 ERROR("cbfstool add supports xip only for FSP component type\n");
1306 return 1;
1309 return cbfs_add_component(param.filename,
1310 param.name,
1311 param.headeroffset,
1312 convert);
1315 static int cbfs_add_stage(void)
1317 if (param.stage_xip && param.baseaddress_assigned) {
1318 ERROR("Cannot specify base address for XIP.\n");
1319 return 1;
1321 param.type = CBFS_TYPE_STAGE;
1323 return cbfs_add_component(param.filename,
1324 param.name,
1325 param.headeroffset,
1326 cbfstool_convert_mkstage);
1329 static int cbfs_add_payload(void)
1331 param.type = CBFS_TYPE_SELF;
1332 return cbfs_add_component(param.filename,
1333 param.name,
1334 param.headeroffset,
1335 cbfstool_convert_mkpayload);
1338 static int cbfs_add_flat_binary(void)
1340 if (param.loadaddress == 0) {
1341 ERROR("You need to specify a valid "
1342 "-l/--load-address.\n");
1343 return 1;
1345 if (param.entrypoint == 0) {
1346 ERROR("You need to specify a valid "
1347 "-e/--entry-point.\n");
1348 return 1;
1350 param.type = CBFS_TYPE_SELF;
1351 return cbfs_add_component(param.filename,
1352 param.name,
1353 param.headeroffset,
1354 cbfstool_convert_mkflatpayload);
1357 static int cbfs_add_integer(void)
1359 if (!param.u64val_assigned) {
1360 ERROR("You need to specify a value to write.\n");
1361 return 1;
1363 return cbfs_add_integer_component(param.name,
1364 param.u64val,
1365 param.baseaddress,
1366 param.headeroffset);
1369 static int cbfs_remove(void)
1371 if (!param.name) {
1372 ERROR("You need to specify -n/--name.\n");
1373 return 1;
1376 struct cbfs_image image;
1377 if (cbfs_image_from_buffer(&image, param.image_region,
1378 param.headeroffset))
1379 return 1;
1381 if (cbfs_remove_entry(&image, param.name) != 0) {
1382 ERROR("Removing file '%s' failed.\n",
1383 param.name);
1384 return 1;
1387 return maybe_update_metadata_hash(&image);
1390 static int cbfs_create(void)
1392 struct cbfs_image image;
1393 memset(&image, 0, sizeof(image));
1394 buffer_clone(&image.buffer, param.image_region);
1396 if (param.fmap) {
1397 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
1398 param.baseaddress_assigned ||
1399 param.headeroffset_assigned ||
1400 param.cbfsoffset_assigned ||
1401 param.bootblock) {
1402 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
1403 return 1;
1406 return cbfs_image_create(&image, image.buffer.size);
1409 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
1410 ERROR("You need to specify -m/--machine arch.\n");
1411 return 1;
1414 struct buffer bootblock;
1415 if (!param.bootblock) {
1416 DEBUG("-B not given, creating image without bootblock.\n");
1417 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
1418 return 1;
1419 } else if (buffer_from_file(&bootblock, param.bootblock)) {
1420 return 1;
1423 if (!param.alignment)
1424 param.alignment = CBFS_ALIGNMENT;
1426 // Set default offsets. x86, as usual, needs to be a special snowflake.
1427 if (!param.baseaddress_assigned) {
1428 if (param.arch == CBFS_ARCHITECTURE_X86) {
1429 // Make sure there's at least enough room for rel_offset
1430 param.baseaddress = param.size -
1431 MAX(bootblock.size, sizeof(int32_t));
1432 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1433 param.baseaddress);
1434 } else {
1435 param.baseaddress = 0;
1436 DEBUG("bootblock starts at address 0x0.\n");
1439 if (!param.headeroffset_assigned) {
1440 if (param.arch == CBFS_ARCHITECTURE_X86) {
1441 param.headeroffset = param.baseaddress -
1442 sizeof(struct cbfs_header);
1443 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1444 param.headeroffset);
1445 } else {
1446 param.headeroffset = align_up(param.baseaddress +
1447 bootblock.size, sizeof(uint32_t));
1448 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1449 param.headeroffset);
1452 if (!param.cbfsoffset_assigned) {
1453 if (param.arch == CBFS_ARCHITECTURE_X86) {
1454 param.cbfsoffset = 0;
1455 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1456 } else {
1457 param.cbfsoffset = align_up(param.headeroffset +
1458 sizeof(struct cbfs_header),
1459 CBFS_ALIGNMENT);
1460 DEBUG("CBFS entries start beind master header (%#x).\n",
1461 param.cbfsoffset);
1465 int ret = cbfs_legacy_image_create(&image,
1466 param.arch,
1467 CBFS_ALIGNMENT,
1468 &bootblock,
1469 param.baseaddress,
1470 param.headeroffset,
1471 param.cbfsoffset);
1472 buffer_delete(&bootblock);
1473 return ret;
1476 static int cbfs_layout(void)
1478 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1479 if (!fmap) {
1480 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1481 return 1;
1484 printf("This image contains the following sections that can be %s with this tool:\n",
1485 param.show_immutable ? "accessed" : "manipulated");
1486 puts("");
1487 for (unsigned i = 0; i < fmap->nareas; ++i) {
1488 const struct fmap_area *current = fmap->areas + i;
1490 bool readonly = partitioned_file_fmap_count(param.image_file,
1491 partitioned_file_fmap_select_children_of, current) ||
1492 region_is_flashmap((const char *)current->name);
1493 if (!param.show_immutable && readonly)
1494 continue;
1496 printf("'%s'", current->name);
1498 // Detect consecutive sections that describe the same region and
1499 // show them as aliases. This cannot find equivalent entries
1500 // that aren't adjacent; however, fmaptool doesn't generate
1501 // FMAPs with such sections, so this convenience feature works
1502 // for all but the strangest manually created FMAP binaries.
1503 // TODO: This could be done by parsing the FMAP into some kind
1504 // of tree that had duplicate lists in addition to child lists,
1505 // which would allow covering that weird, unlikely case as well.
1506 unsigned lookahead;
1507 for (lookahead = 1; i + lookahead < fmap->nareas;
1508 ++lookahead) {
1509 const struct fmap_area *consecutive =
1510 fmap->areas + i + lookahead;
1511 if (consecutive->offset != current->offset ||
1512 consecutive->size != current->size)
1513 break;
1514 printf(", '%s'", consecutive->name);
1516 if (lookahead > 1)
1517 fputs(" are aliases for the same region", stdout);
1519 const char *qualifier = "";
1520 if (readonly)
1521 qualifier = "read-only, ";
1522 else if (region_is_modern_cbfs((const char *)current->name))
1523 qualifier = "CBFS, ";
1524 else if (current->flags & FMAP_AREA_PRESERVE)
1525 qualifier = "preserve, ";
1526 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1527 current->offset);
1529 i += lookahead - 1;
1531 puts("");
1533 if (param.show_immutable) {
1534 puts("It is at least possible to perform the read action on every section listed above.");
1535 } else {
1536 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1537 puts("To see the image's read-only sections as well, rerun with the -w option.");
1540 return 0;
1543 static enum cb_err verify_walker(__always_unused cbfs_dev_t dev, size_t offset,
1544 const union cbfs_mdata *mdata, size_t already_read, void *arg)
1546 uint32_t type = be32toh(mdata->h.type);
1547 uint32_t data_offset = be32toh(mdata->h.offset);
1548 if (verification_exclude(type))
1549 return CB_CBFS_NOT_FOUND;
1550 assert(already_read == data_offset);
1551 const struct vb2_hash *hash = cbfs_file_hash(mdata);
1552 if (!hash)
1553 return CB_ERR;
1554 void *file_data = arg + offset + data_offset;
1555 if (vb2_hash_verify(false, file_data, be32toh(mdata->h.len), hash) != VB2_SUCCESS)
1556 return CB_CBFS_HASH_MISMATCH;
1557 return CB_CBFS_NOT_FOUND;
1560 static int cbfs_print(void)
1562 struct cbfs_image image;
1563 if (cbfs_image_from_buffer(&image, param.image_region,
1564 param.headeroffset))
1565 return 1;
1566 if (param.machine_parseable) {
1567 if (verbose)
1568 printf("[FMAP REGION]\t%s\n", param.region_name);
1569 cbfs_print_parseable_directory(&image);
1570 } else {
1571 printf("FMAP REGION: %s\n", param.region_name);
1572 cbfs_print_directory(&image);
1575 if (verbose) {
1576 const char *verification_state = "fully valid";
1577 struct mh_cache *mhc = get_mh_cache();
1578 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1579 return 0;
1581 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
1582 enum cb_err err = cbfs_walk(&image, verify_walker, buffer_get(&image.buffer),
1583 &real_hash, CBFS_WALK_WRITEBACK_HASH);
1584 if (err == CB_CBFS_HASH_MISMATCH)
1585 verification_state = "invalid file hashes";
1586 else if (err != CB_CBFS_NOT_FOUND)
1587 verification_state = "missing file hashes";
1588 char *hash_str = bintohex(real_hash.raw,
1589 vb2_digest_size(real_hash.algo));
1590 printf("[METADATA HASH]\t%s:%s",
1591 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1592 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1593 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
1594 vb2_digest_size(real_hash.algo))) {
1595 printf(":valid");
1596 } else {
1597 printf(":invalid");
1598 verification_state = "invalid metadata hash";
1601 printf("\n");
1602 printf("[CBFS VERIFICATION (%s)]\t%s\n", param.region_name, verification_state);
1603 free(hash_str);
1606 return 0;
1609 static int cbfs_extract(void)
1611 if (!param.filename) {
1612 ERROR("You need to specify -f/--filename.\n");
1613 return 1;
1616 if (!param.name) {
1617 ERROR("You need to specify -n/--name.\n");
1618 return 1;
1621 struct cbfs_image image;
1622 if (cbfs_image_from_buffer(&image, param.image_region,
1623 param.headeroffset))
1624 return 1;
1626 return cbfs_export_entry(&image, param.name, param.filename,
1627 param.arch, !param.unprocessed);
1630 static int cbfs_write(void)
1632 if (!param.filename) {
1633 ERROR("You need to specify a valid input -f/--file.\n");
1634 return 1;
1636 if (!partitioned_file_is_partitioned(param.image_file)) {
1637 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1638 return 1;
1641 if (!param.force && region_is_modern_cbfs(param.region_name)) {
1642 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1643 param.region_name);
1644 return 1;
1647 struct buffer new_content;
1648 if (buffer_from_file(&new_content, param.filename))
1649 return 1;
1651 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1652 strlen(FMAP_SIGNATURE))) {
1653 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1654 param.filename);
1655 buffer_delete(&new_content);
1656 return 1;
1658 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
1659 strlen(CBFS_FILE_MAGIC))) {
1660 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1661 param.filename);
1662 buffer_delete(&new_content);
1663 return 1;
1666 unsigned offset = 0;
1667 if (param.fill_partial_upward && param.fill_partial_downward) {
1668 ERROR("You may only specify one of -u and -d.\n");
1669 buffer_delete(&new_content);
1670 return 1;
1671 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1672 if (new_content.size != param.image_region->size) {
1673 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",
1674 new_content.size, param.image_region->size);
1675 buffer_delete(&new_content);
1676 return 1;
1678 } else {
1679 if (new_content.size > param.image_region->size) {
1680 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1681 new_content.size, param.image_region->size);
1682 buffer_delete(&new_content);
1683 return 1;
1685 if (param.u64val == (uint64_t)-1) {
1686 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1687 param.fill_partial_upward ? "bottom" : "top");
1688 } else if (param.u64val > 0xff) {
1689 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1690 buffer_delete(&new_content);
1691 return 1;
1692 } else {
1693 memset(buffer_get(param.image_region),
1694 param.u64val & 0xff,
1695 buffer_size(param.image_region));
1697 if (param.fill_partial_downward)
1698 offset = param.image_region->size - new_content.size;
1701 memcpy(param.image_region->data + offset, new_content.data,
1702 new_content.size);
1703 buffer_delete(&new_content);
1705 return maybe_update_fmap_hash();
1708 static int cbfs_read(void)
1710 if (!param.filename) {
1711 ERROR("You need to specify a valid output -f/--file.\n");
1712 return 1;
1714 if (!partitioned_file_is_partitioned(param.image_file)) {
1715 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1716 return 1;
1719 return buffer_write_file(param.image_region, param.filename);
1722 static int cbfs_copy(void)
1724 struct cbfs_image src_image;
1725 struct buffer src_buf;
1727 if (!param.source_region) {
1728 ERROR("You need to specify -R/--source-region.\n");
1729 return 1;
1732 /* Obtain the source region and convert it to a cbfs_image. */
1733 if (!partitioned_file_read_region(&src_buf, param.image_file,
1734 param.source_region)) {
1735 ERROR("Region not found in image: %s\n", param.source_region);
1736 return 1;
1739 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1740 return 1;
1742 return cbfs_copy_instance(&src_image, param.image_region);
1745 static int cbfs_compact(void)
1747 struct cbfs_image image;
1748 if (cbfs_image_from_buffer(&image, param.image_region,
1749 param.headeroffset))
1750 return 1;
1751 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1752 return cbfs_compact_instance(&image);
1755 static int cbfs_expand(void)
1757 struct buffer src_buf;
1759 /* Obtain the source region. */
1760 if (!partitioned_file_read_region(&src_buf, param.image_file,
1761 param.region_name)) {
1762 ERROR("Region not found in image: %s\n", param.source_region);
1763 return 1;
1766 return cbfs_expand_to_region(param.image_region);
1769 static int cbfs_truncate(void)
1771 struct buffer src_buf;
1773 /* Obtain the source region. */
1774 if (!partitioned_file_read_region(&src_buf, param.image_file,
1775 param.region_name)) {
1776 ERROR("Region not found in image: %s\n", param.source_region);
1777 return 1;
1780 uint32_t size;
1781 int result = cbfs_truncate_space(param.image_region, &size);
1782 if (!result)
1783 printf("0x%x\n", size);
1784 return result;
1787 static const struct command commands[] = {
1788 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
1789 {"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary,
1790 true, true},
1791 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
1792 true, true},
1793 {"add-stage", "a:H:r:f:n:t:c:b:P:QS:p:yvA:gh?", cbfs_add_stage,
1794 true, true},
1795 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
1796 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
1797 {"compact", "r:h?", cbfs_compact, true, true},
1798 {"copy", "r:R:h?", cbfs_copy, true, true},
1799 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
1800 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
1801 {"layout", "wvh?", cbfs_layout, false, false},
1802 {"print", "H:r:vkh?", cbfs_print, true, false},
1803 {"read", "r:f:vh?", cbfs_read, true, false},
1804 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
1805 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
1806 {"expand", "r:h?", cbfs_expand, true, true},
1807 {"truncate", "r:h?", cbfs_truncate, true, true},
1810 enum {
1811 /* begin after ASCII characters */
1812 LONGOPT_START = 256,
1813 LONGOPT_IBB = LONGOPT_START,
1814 LONGOPT_MMAP,
1815 LONGOPT_END,
1818 static struct option long_options[] = {
1819 {"alignment", required_argument, 0, 'a' },
1820 {"base-address", required_argument, 0, 'b' },
1821 {"bootblock", required_argument, 0, 'B' },
1822 {"cmdline", required_argument, 0, 'C' },
1823 {"compression", required_argument, 0, 'c' },
1824 {"topswap-size", required_argument, 0, 'j' },
1825 {"empty-fits", required_argument, 0, 'x' },
1826 {"entry-point", required_argument, 0, 'e' },
1827 {"file", required_argument, 0, 'f' },
1828 {"fill-downward", no_argument, 0, 'd' },
1829 {"fill-upward", no_argument, 0, 'u' },
1830 {"flashmap", required_argument, 0, 'M' },
1831 {"fmap-regions", required_argument, 0, 'r' },
1832 {"force", no_argument, 0, 'F' },
1833 {"source-region", required_argument, 0, 'R' },
1834 {"hash-algorithm",required_argument, 0, 'A' },
1835 {"header-offset", required_argument, 0, 'H' },
1836 {"help", no_argument, 0, 'h' },
1837 {"ignore-sec", required_argument, 0, 'S' },
1838 {"initrd", required_argument, 0, 'I' },
1839 {"int", required_argument, 0, 'i' },
1840 {"load-address", required_argument, 0, 'l' },
1841 {"machine", required_argument, 0, 'm' },
1842 {"name", required_argument, 0, 'n' },
1843 {"offset", required_argument, 0, 'o' },
1844 {"padding", required_argument, 0, 'p' },
1845 {"pow2page", no_argument, 0, 'Q' },
1846 {"ucode-region", required_argument, 0, 'q' },
1847 {"size", required_argument, 0, 's' },
1848 {"type", required_argument, 0, 't' },
1849 {"verbose", no_argument, 0, 'v' },
1850 {"with-readonly", no_argument, 0, 'w' },
1851 {"xip", no_argument, 0, 'y' },
1852 {"gen-attribute", no_argument, 0, 'g' },
1853 {"mach-parseable",no_argument, 0, 'k' },
1854 {"unprocessed", no_argument, 0, 'U' },
1855 {"ibb", no_argument, 0, LONGOPT_IBB },
1856 {"mmap", required_argument, 0, LONGOPT_MMAP },
1857 {NULL, 0, 0, 0 }
1860 static int get_region_offset(long long int offset, uint32_t *region_offset)
1862 /* If offset is not negative, no transformation required. */
1863 if (offset >= 0) {
1864 *region_offset = offset;
1865 return 0;
1868 /* Calculate offset from start of region. */
1869 return convert_region_offset(-offset, region_offset);
1872 static int calculate_region_offsets(void)
1874 int ret = 0;
1876 if (param.baseaddress_assigned)
1877 ret |= get_region_offset(param.baseaddress_input, &param.baseaddress);
1878 if (param.headeroffset_assigned)
1879 ret |= get_region_offset(param.headeroffset_input, &param.headeroffset);
1880 if (param.cbfsoffset_assigned)
1881 ret |= get_region_offset(param.cbfsoffset_input, &param.cbfsoffset);
1883 return ret;
1886 static int dispatch_command(struct command command)
1888 if (command.accesses_region) {
1889 assert(param.image_file);
1891 if (partitioned_file_is_partitioned(param.image_file)) {
1892 INFO("Performing operation on '%s' region...\n",
1893 param.region_name);
1895 if (!partitioned_file_read_region(param.image_region,
1896 param.image_file, param.region_name)) {
1897 ERROR("The image will be left unmodified.\n");
1898 return 1;
1901 if (command.modifies_region) {
1902 // We (intentionally) don't support overwriting the FMAP
1903 // section. If you find yourself wanting to do this,
1904 // consider creating a new image rather than performing
1905 // whatever hacky transformation you were planning.
1906 if (region_is_flashmap(param.region_name)) {
1907 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1908 param.region_name);
1909 ERROR("The image will be left unmodified.\n");
1910 return 1;
1912 // We don't allow writing raw data to regions that
1913 // contain nested regions, since doing so would
1914 // overwrite all such subregions.
1915 if (partitioned_file_region_contains_nested(
1916 param.image_file, param.region_name)) {
1917 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1918 param.region_name);
1919 ERROR("The image will be left unmodified.\n");
1920 return 1;
1925 * Once image region is read, input offsets can be adjusted accordingly if the
1926 * inputs are provided as negative integers i.e. offsets from end of region.
1928 if (calculate_region_offsets())
1929 return 1;
1932 if (command.function()) {
1933 if (partitioned_file_is_partitioned(param.image_file)) {
1934 ERROR("Failed while operating on '%s' region!\n",
1935 param.region_name);
1936 ERROR("The image will be left unmodified.\n");
1938 return 1;
1941 return 0;
1944 static void usage(char *name)
1946 printf
1947 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
1948 "USAGE:\n" " %s [-h]\n"
1949 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
1950 " -H header_offset Do not search for header; use this offset*\n"
1951 " -T Output top-aligned memory address\n"
1952 " -u Accept short data; fill upward/from bottom\n"
1953 " -d Accept short data; fill downward/from top\n"
1954 " -F Force action\n"
1955 " -g Generate position and alignment arguments\n"
1956 " -U Unprocessed; don't decompress or make ELF\n"
1957 " -v Provide verbose output (-v=INFO -vv=DEBUG output)\n"
1958 " -h Display this help message\n\n"
1959 " --ext-win-base Base of extended decode window in host address\n"
1960 " space(x86 only)\n"
1961 " --ext-win-size Size of extended decode window in host address\n"
1962 " space(x86 only)\n"
1963 "COMMANDs:\n"
1964 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
1965 " [-c compression] [-b base-address | -a alignment] \\\n"
1966 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
1967 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1968 " [--ext-win-base win-base --ext-win-size win-size] "
1969 "Add a component\n"
1971 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
1972 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1973 " [-c compression] [-b base-address] \\\n"
1974 " (linux specific: [-C cmdline] [-I initrd]) "
1975 "Add a payload to the ROM\n"
1976 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1977 " [-c compression] [-b base] \\\n"
1978 " [-S comma-separated-section(s)-to-ignore] \\\n"
1979 " [-a alignment] [-Q|--pow2page] \\\n"
1980 " [-y|--xip] [--ibb] \\\n"
1981 " [--ext-win-base win-base --ext-win-size win-size] "
1982 "Add a stage to the ROM\n"
1983 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1984 " [-A hash] -l load-address -e entry-point \\\n"
1985 " [-c compression] [-b base] "
1986 "Add a 32bit flat mode binary\n"
1987 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
1988 "Add a raw 64-bit integer value\n"
1989 " add-master-header [-r image,regions] \\ \n"
1990 " [-j topswap-size] (Intel CPUs only) "
1991 "Add a legacy CBFS master header\n"
1992 " remove [-r image,regions] -n NAME "
1993 "Remove a component\n"
1994 " compact -r image,regions "
1995 "Defragment CBFS image.\n"
1996 " copy -r image,regions -R source-region "
1997 "Create a copy (duplicate) cbfs instance in fmap\n"
1998 " create -m ARCH -s size [-b bootblock offset] \\\n"
1999 " [-o CBFS offset] [-H header offset] [-B bootblock] "
2000 "Create a legacy ROM file with CBFS master header*\n"
2001 " create -M flashmap [-r list,of,regions,containing,cbfses] "
2002 "Create a new-style partitioned firmware image\n"
2003 " layout [-w] "
2004 "List mutable (or, with -w, readable) image regions\n"
2005 " print [-r image,regions] [-k] "
2006 "Show the contents of the ROM\n"
2007 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
2008 "Extracts a file from ROM\n"
2009 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
2010 "Write file into same-size [or larger] raw region\n"
2011 " read [-r fmap-region] -f file "
2012 "Extract raw region contents into binary file\n"
2013 " truncate [-r fmap-region] "
2014 "Truncate CBFS and print new size on stdout\n"
2015 " expand [-r fmap-region] "
2016 "Expand CBFS to span entire region\n"
2017 "OFFSETs:\n"
2018 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
2019 " in two possible formats: if their value is greater than\n"
2020 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
2021 " address; otherwise, they are treated as an offset into flash.\n"
2022 "ARCHes:\n", name, name
2024 print_supported_architectures();
2026 printf("TYPEs:\n");
2027 print_supported_filetypes();
2028 printf(
2029 "\n* Note that these actions and switches are only valid when\n"
2030 " working with legacy images whose structure is described\n"
2031 " primarily by a CBFS master header. New-style images, in\n"
2032 " contrast, exclusively make use of an FMAP to describe their\n"
2033 " layout: this must minimally contain an '%s' section\n"
2034 " specifying the location of this FMAP itself and a '%s'\n"
2035 " section describing the primary CBFS. It should also be noted\n"
2036 " that, when working with such images, the -F and -r switches\n"
2037 " default to '%s' for convenience, and the -b switch becomes\n"
2038 " relative to the selected CBFS region's lowest address.\n"
2039 " The one exception to this rule is the top-aligned address,\n"
2040 " which is always relative to the end of the entire image\n"
2041 " rather than relative to the local region; this is true for\n"
2042 " for both input (sufficiently large) and output (-T) data.\n",
2043 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
2044 SECTION_NAME_PRIMARY_CBFS
2048 static bool valid_opt(size_t i, int c)
2050 /* Check if it is one of the optstrings supported by the command. */
2051 if (strchr(commands[i].optstring, c))
2052 return true;
2055 * Check if it is one of the non-ASCII characters. Currently, the
2056 * non-ASCII characters are only checked against the valid list
2057 * irrespective of the command.
2059 if (c >= LONGOPT_START && c < LONGOPT_END)
2060 return true;
2062 return false;
2065 int main(int argc, char **argv)
2067 size_t i;
2068 int c;
2070 if (argc < 3) {
2071 usage(argv[0]);
2072 return 1;
2075 char *image_name = argv[1];
2076 char *cmd = argv[2];
2077 optind += 2;
2079 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2080 if (strcmp(cmd, commands[i].name) != 0)
2081 continue;
2083 while (1) {
2084 char *suffix = NULL;
2085 int option_index = 0;
2087 c = getopt_long(argc, argv, commands[i].optstring,
2088 long_options, &option_index);
2089 if (c == -1) {
2090 if (optind < argc) {
2091 ERROR("%s: excessive argument -- '%s'"
2092 "\n", argv[0], argv[optind]);
2093 return 1;
2095 break;
2098 /* Filter out illegal long options */
2099 if (!valid_opt(i, c)) {
2100 ERROR("%s: invalid option -- '%d'\n",
2101 argv[0], c);
2102 c = '?';
2105 switch(c) {
2106 case 'n':
2107 param.name = optarg;
2108 break;
2109 case 't':
2110 if (intfiletype(optarg) != ((uint64_t) - 1))
2111 param.type = intfiletype(optarg);
2112 else
2113 param.type = strtoul(optarg, NULL, 0);
2114 if (param.type == 0)
2115 WARN("Unknown type '%s' ignored\n",
2116 optarg);
2117 break;
2118 case 'c': {
2119 if (strcmp(optarg, "precompression") == 0) {
2120 param.precompression = 1;
2121 break;
2123 int algo = cbfs_parse_comp_algo(optarg);
2124 if (algo >= 0)
2125 param.compression = algo;
2126 else
2127 WARN("Unknown compression '%s' ignored.\n",
2128 optarg);
2129 break;
2131 case 'A': {
2132 if (!vb2_lookup_hash_alg(optarg, &param.hash)) {
2133 ERROR("Unknown hash algorithm '%s'.\n",
2134 optarg);
2135 return 1;
2137 break;
2139 case 'M':
2140 param.fmap = optarg;
2141 break;
2142 case 'r':
2143 param.region_name = optarg;
2144 break;
2145 case 'R':
2146 param.source_region = optarg;
2147 break;
2148 case 'b':
2149 param.baseaddress_input = strtoll(optarg, &suffix, 0);
2150 if (!*optarg || (suffix && *suffix)) {
2151 ERROR("Invalid base address '%s'.\n",
2152 optarg);
2153 return 1;
2155 // baseaddress may be zero on non-x86, so we
2156 // need an explicit "baseaddress_assigned".
2157 param.baseaddress_assigned = 1;
2158 break;
2159 case 'l':
2160 param.loadaddress = strtoull(optarg, &suffix, 0);
2161 if (!*optarg || (suffix && *suffix)) {
2162 ERROR("Invalid load address '%s'.\n",
2163 optarg);
2164 return 1;
2166 break;
2167 case 'e':
2168 param.entrypoint = strtoull(optarg, &suffix, 0);
2169 if (!*optarg || (suffix && *suffix)) {
2170 ERROR("Invalid entry point '%s'.\n",
2171 optarg);
2172 return 1;
2174 break;
2175 case 's':
2176 param.size = strtoul(optarg, &suffix, 0);
2177 if (!*optarg) {
2178 ERROR("Empty size specified.\n");
2179 return 1;
2181 switch (tolower((int)suffix[0])) {
2182 case 'k':
2183 param.size *= 1024;
2184 break;
2185 case 'm':
2186 param.size *= 1024 * 1024;
2187 break;
2188 case '\0':
2189 break;
2190 default:
2191 ERROR("Invalid suffix for size '%s'.\n",
2192 optarg);
2193 return 1;
2195 break;
2196 case 'B':
2197 param.bootblock = optarg;
2198 break;
2199 case 'H':
2200 param.headeroffset_input = strtoll(optarg, &suffix, 0);
2201 if (!*optarg || (suffix && *suffix)) {
2202 ERROR("Invalid header offset '%s'.\n",
2203 optarg);
2204 return 1;
2206 param.headeroffset_assigned = 1;
2207 break;
2208 case 'a':
2209 param.alignment = strtoul(optarg, &suffix, 0);
2210 if (!*optarg || (suffix && *suffix)) {
2211 ERROR("Invalid alignment '%s'.\n",
2212 optarg);
2213 return 1;
2215 break;
2216 case 'p':
2217 param.padding = strtoul(optarg, &suffix, 0);
2218 if (!*optarg || (suffix && *suffix)) {
2219 ERROR("Invalid pad size '%s'.\n",
2220 optarg);
2221 return 1;
2223 break;
2224 case 'Q':
2225 param.force_pow2_pagesize = 1;
2226 break;
2227 case 'o':
2228 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
2229 if (!*optarg || (suffix && *suffix)) {
2230 ERROR("Invalid cbfs offset '%s'.\n",
2231 optarg);
2232 return 1;
2234 param.cbfsoffset_assigned = 1;
2235 break;
2236 case 'f':
2237 param.filename = optarg;
2238 break;
2239 case 'F':
2240 param.force = 1;
2241 break;
2242 case 'i':
2243 param.u64val = strtoull(optarg, &suffix, 0);
2244 param.u64val_assigned = 1;
2245 if (!*optarg || (suffix && *suffix)) {
2246 ERROR("Invalid int parameter '%s'.\n",
2247 optarg);
2248 return 1;
2250 break;
2251 case 'u':
2252 param.fill_partial_upward = true;
2253 break;
2254 case 'd':
2255 param.fill_partial_downward = true;
2256 break;
2257 case 'w':
2258 param.show_immutable = true;
2259 break;
2260 case 'j':
2261 param.topswap_size = strtol(optarg, NULL, 0);
2262 if (!is_valid_topswap())
2263 return 1;
2264 break;
2265 case 'q':
2266 param.ucode_region = optarg;
2267 break;
2268 case 'v':
2269 verbose++;
2270 break;
2271 case 'm':
2272 param.arch = string_to_arch(optarg);
2273 break;
2274 case 'I':
2275 param.initrd = optarg;
2276 break;
2277 case 'C':
2278 param.cmdline = optarg;
2279 break;
2280 case 'S':
2281 param.ignore_sections = optarg;
2282 break;
2283 case 'y':
2284 param.stage_xip = true;
2285 break;
2286 case 'g':
2287 param.autogen_attr = true;
2288 break;
2289 case 'k':
2290 param.machine_parseable = true;
2291 break;
2292 case 'U':
2293 param.unprocessed = true;
2294 break;
2295 case LONGOPT_IBB:
2296 param.ibb = true;
2297 break;
2298 case LONGOPT_MMAP:
2299 if (decode_mmap_arg(optarg))
2300 return 1;
2301 break;
2302 case 'h':
2303 case '?':
2304 usage(argv[0]);
2305 return 1;
2306 default:
2307 break;
2311 if (commands[i].function == cbfs_create) {
2312 if (param.fmap) {
2313 struct buffer flashmap;
2314 if (buffer_from_file(&flashmap, param.fmap))
2315 return 1;
2316 param.image_file = partitioned_file_create(
2317 image_name, &flashmap);
2318 buffer_delete(&flashmap);
2319 } else if (param.size) {
2320 param.image_file = partitioned_file_create_flat(
2321 image_name, param.size);
2322 } else {
2323 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
2324 return 1;
2326 } else {
2327 bool write_access = commands[i].modifies_region;
2329 param.image_file =
2330 partitioned_file_reopen(image_name,
2331 write_access);
2333 if (!param.image_file)
2334 return 1;
2336 unsigned num_regions = 1;
2337 for (const char *list = strchr(param.region_name, ','); list;
2338 list = strchr(list + 1, ','))
2339 ++num_regions;
2341 // If the action needs to read an image region, as indicated by
2342 // having accesses_region set in its command struct, that
2343 // region's buffer struct will be stored here and the client
2344 // will receive a pointer to it via param.image_region. It
2345 // need not write the buffer back to the image file itself,
2346 // since this behavior can be requested via its modifies_region
2347 // field. Additionally, it should never free the region buffer,
2348 // as that is performed automatically once it completes.
2349 struct buffer image_regions[num_regions];
2350 memset(image_regions, 0, sizeof(image_regions));
2352 bool seen_primary_cbfs = false;
2353 char region_name_scratch[strlen(param.region_name) + 1];
2354 strcpy(region_name_scratch, param.region_name);
2355 param.region_name = strtok(region_name_scratch, ",");
2356 for (unsigned region = 0; region < num_regions; ++region) {
2357 if (!param.region_name) {
2358 ERROR("Encountered illegal degenerate region name in -r list\n");
2359 ERROR("The image will be left unmodified.\n");
2360 partitioned_file_close(param.image_file);
2361 return 1;
2364 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2365 == 0)
2366 seen_primary_cbfs = true;
2368 param.image_region = image_regions + region;
2369 if (dispatch_command(commands[i])) {
2370 partitioned_file_close(param.image_file);
2371 return 1;
2374 param.region_name = strtok(NULL, ",");
2377 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2378 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2379 SECTION_NAME_PRIMARY_CBFS);
2380 ERROR("The image will be left unmodified.\n");
2381 partitioned_file_close(param.image_file);
2382 return 1;
2385 if (commands[i].modifies_region) {
2386 assert(param.image_file);
2387 for (unsigned region = 0; region < num_regions;
2388 ++region) {
2390 if (!partitioned_file_write_region(
2391 param.image_file,
2392 image_regions + region)) {
2393 partitioned_file_close(
2394 param.image_file);
2395 return 1;
2400 partitioned_file_close(param.image_file);
2401 return 0;
2404 ERROR("Unknown command '%s'.\n", cmd);
2405 usage(argv[0]);
2406 return 1;