acpi: Add IORT helper functions
[coreboot2.git] / util / cbfstool / cbfstool.c
blob25f48478d2ef1fef5651248d7e7880c980c2a033
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 int add_mmap_window(unsigned long flash_offset, unsigned long host_offset, unsigned long window_size)
329 if (mmap_window_table_size >= MMAP_MAX_WINDOWS) {
330 ERROR("Too many memory map windows\n");
331 return 1;
334 if (region_create_untrusted(
335 &mmap_window_table[mmap_window_table_size].flash_space,
336 flash_offset, window_size) != CB_SUCCESS ||
337 region_create_untrusted(
338 &mmap_window_table[mmap_window_table_size].host_space,
339 host_offset, window_size) != CB_SUCCESS) {
340 ERROR("Invalid mmap window size %lu.\n", window_size);
341 return 1;
344 mmap_window_table_size++;
345 return 0;
349 static int decode_mmap_arg(char *arg)
351 if (arg == NULL)
352 return 1;
354 union {
355 unsigned long int array[3];
356 struct {
357 unsigned long int flash_base;
358 unsigned long int mmap_base;
359 unsigned long int mmap_size;
361 } mmap_args;
362 char *suffix = NULL;
363 char *substring = strtok(arg, ":");
364 for (size_t i = 0; i < ARRAY_SIZE(mmap_args.array); i++) {
365 if (!substring) {
366 ERROR("Invalid mmap arguments '%s'.\n",
367 arg);
368 return 1;
370 mmap_args.array[i] = strtol(substring, &suffix, 0);
371 if (suffix && *suffix) {
372 ERROR("Invalid mmap arguments '%s'.\n",
373 arg);
374 return 1;
376 substring = strtok(NULL, ":");
379 if (substring != NULL) {
380 ERROR("Invalid argument, too many substrings '%s'.\n",
381 arg);
383 return 1;
386 if (add_mmap_window(mmap_args.flash_base, mmap_args.mmap_base, mmap_args.mmap_size))
387 return 1;
389 return 0;
392 #define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
393 #define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
395 static bool create_mmap_windows(void)
397 static bool done;
399 if (done)
400 return done;
402 // No memory map provided, use a default one
403 if (mmap_window_table_size == 0) {
404 const size_t image_size = partitioned_file_total_size(param.image_file);
405 printf("Image SIZE %zu\n", image_size);
406 const size_t std_window_size = MIN(DEFAULT_DECODE_WINDOW_MAX_SIZE, image_size);
407 const size_t std_window_flash_offset = image_size - std_window_size;
410 * Default decode window lives just below 4G boundary in host space and maps up to a
411 * maximum of 16MiB. If the window is smaller than 16MiB, the SPI flash window is mapped
412 * at the top of the host window just below 4G.
414 if (add_mmap_window(std_window_flash_offset, DEFAULT_DECODE_WINDOW_TOP - std_window_size, std_window_size))
415 return false;
416 } else {
418 * Check provided memory map
420 for (int i = 0; i < mmap_window_table_size; i++) {
421 for (int j = i + 1; j < mmap_window_table_size; j++) {
422 if (region_overlap(&mmap_window_table[i].flash_space,
423 &mmap_window_table[j].flash_space)) {
424 ERROR("Flash space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
425 region_offset(&mmap_window_table[i].flash_space),
426 region_last(&mmap_window_table[i].flash_space),
427 region_offset(&mmap_window_table[j].flash_space),
428 region_last(&mmap_window_table[j].flash_space));
429 return false;
432 if (region_overlap(&mmap_window_table[i].host_space,
433 &mmap_window_table[j].host_space)) {
434 ERROR("Host space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
435 region_offset(&mmap_window_table[i].flash_space),
436 region_last(&mmap_window_table[i].flash_space),
437 region_offset(&mmap_window_table[j].flash_space),
438 region_last(&mmap_window_table[j].flash_space));
439 return false;
445 done = true;
446 return done;
449 static unsigned int convert_address(const struct region *to, const struct region *from,
450 unsigned int addr)
453 * Calculate the offset in the "from" region and use that offset to calculate
454 * corresponding address in the "to" region.
456 size_t offset = addr - region_offset(from);
457 return region_offset(to) + offset;
460 enum mmap_addr_type {
461 HOST_SPACE_ADDR,
462 FLASH_SPACE_ADDR,
465 static int find_mmap_window(enum mmap_addr_type addr_type, unsigned int addr)
467 size_t i;
469 for (i = 0; i < ARRAY_SIZE(mmap_window_table); i++) {
470 const struct region *reg;
472 if (addr_type == HOST_SPACE_ADDR)
473 reg = &mmap_window_table[i].host_space;
474 else
475 reg = &mmap_window_table[i].flash_space;
477 if (region_offset(reg) <= addr &&
478 ((uint64_t)region_offset(reg) + (uint64_t)region_sz(reg) - 1) >= addr)
479 return i;
482 return -1;
485 static unsigned int convert_host_to_flash(const struct buffer *region, unsigned int addr)
487 int idx;
488 const struct region *to, *from;
490 idx = find_mmap_window(HOST_SPACE_ADDR, addr);
491 if (idx == -1) {
492 ERROR("Host address(%x) not in any mmap window!\n", addr);
493 return 0;
496 to = &mmap_window_table[idx].flash_space;
497 from = &mmap_window_table[idx].host_space;
499 /* region->offset is subtracted because caller expects offset in the given region. */
500 return convert_address(to, from, addr) - region->offset;
503 static unsigned int convert_flash_to_host(const struct buffer *region, unsigned int addr)
505 int idx;
506 const struct region *to, *from;
509 * region->offset is added because caller provides offset in the given region. This is
510 * converted to an absolute address in the SPI flash space. This is done before the
511 * conversion as opposed to after in convert_host_to_flash() above because the address
512 * is actually an offset within the region. So, it needs to be converted into an
513 * absolute address in the SPI flash space before converting into an address in host
514 * space.
516 addr += region->offset;
517 idx = find_mmap_window(FLASH_SPACE_ADDR, addr);
519 if (idx == -1) {
520 ERROR("SPI flash address(%x) not in any mmap window!\n", addr);
521 return 0;
524 to = &mmap_window_table[idx].host_space;
525 from = &mmap_window_table[idx].flash_space;
527 return convert_address(to, from, addr);
530 static unsigned int convert_addr_space(const struct buffer *region, unsigned int addr)
532 assert(region);
534 assert(create_mmap_windows());
536 if (IS_HOST_SPACE_ADDRESS(addr))
537 return convert_host_to_flash(region, addr);
538 else
539 return convert_flash_to_host(region, addr);
543 * This function takes offset value which represents the offset from one end of the region and
544 * converts it to offset from the other end of the region. offset is expected to be positive.
546 static int convert_region_offset(unsigned int offset, uint32_t *region_offset)
548 size_t size;
550 if (param.size) {
551 size = param.size;
552 } else {
553 assert(param.image_region);
554 size = param.image_region->size;
557 if (size < offset) {
558 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size, offset);
559 return 1;
562 *region_offset = size - offset;
563 return 0;
566 static int do_cbfs_locate(uint32_t *cbfs_addr, size_t data_size)
568 uint32_t metadata_size = 0;
570 if (!param.name) {
571 ERROR("You need to specify -n/--name.\n");
572 return 1;
575 struct cbfs_image image;
576 if (cbfs_image_from_buffer(&image, param.image_region,
577 param.headeroffset))
578 return 1;
580 if (cbfs_get_entry(&image, param.name))
581 WARN("'%s' already in CBFS.\n", param.name);
583 if (!data_size) {
584 ERROR("File '%s' is empty?\n", param.name);
585 return 1;
588 /* Compute required page size */
589 if (param.force_pow2_pagesize) {
590 param.pagesize = 1;
591 while (param.pagesize < data_size)
592 param.pagesize <<= 1;
593 DEBUG("Page size is %d (0x%x)\n", param.pagesize, param.pagesize);
596 /* Include cbfs_file size along with space for with name. */
597 metadata_size += cbfs_calculate_file_header_size(param.name);
598 /* Adjust metadata_size if additional attributes were added */
599 if (param.autogen_attr) {
600 if (param.alignment)
601 metadata_size += sizeof(struct cbfs_file_attr_align);
602 if (param.baseaddress_assigned || param.stage_xip)
603 metadata_size += sizeof(struct cbfs_file_attr_position);
605 if (param.precompression || param.compression != CBFS_COMPRESS_NONE)
606 metadata_size += sizeof(struct cbfs_file_attr_compression);
607 if (param.type == CBFS_TYPE_STAGE)
608 metadata_size += sizeof(struct cbfs_file_attr_stageheader);
610 /* Take care of the hash attribute if it is used */
611 if (param.hash != VB2_HASH_INVALID)
612 metadata_size += cbfs_file_attr_hash_size(param.hash);
614 int32_t address = cbfs_locate_entry(&image, data_size, param.pagesize,
615 param.alignment, metadata_size);
617 if (address < 0) {
618 ERROR("'%s'(%u + %zu) can't fit in CBFS for page-size %#x, align %#x.\n",
619 param.name, metadata_size, data_size, param.pagesize, param.alignment);
620 return 1;
623 *cbfs_addr = address;
624 return 0;
627 typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
628 struct cbfs_file *header);
630 static int cbfs_add_integer_component(const char *name,
631 uint64_t u64val,
632 uint32_t offset,
633 uint32_t headeroffset) {
634 struct cbfs_image image;
635 struct cbfs_file *header = NULL;
636 struct buffer buffer;
637 int i, ret = 1;
639 if (!name) {
640 ERROR("You need to specify -n/--name.\n");
641 return 1;
644 if (buffer_create(&buffer, 8, name) != 0)
645 return 1;
647 for (i = 0; i < 8; i++)
648 buffer.data[i] = (u64val >> i*8) & 0xff;
650 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset)) {
651 ERROR("Selected image region is not a CBFS.\n");
652 goto done;
655 if (cbfs_get_entry(&image, name)) {
656 ERROR("'%s' already in ROM image.\n", name);
657 goto done;
660 header = cbfs_create_file_header(CBFS_TYPE_RAW,
661 buffer.size, name);
662 if (!header)
663 goto done;
665 enum vb2_hash_algorithm algo = get_mh_cache()->cbfs_hash.algo;
666 if (algo != VB2_HASH_INVALID)
667 if (cbfs_add_file_hash(header, &buffer, algo)) {
668 ERROR("couldn't add hash for '%s'\n", name);
669 goto done;
672 if (cbfs_add_entry(&image, &buffer, offset, header, 0) != 0) {
673 ERROR("Failed to add %llu into ROM image as '%s'.\n",
674 (long long unsigned)u64val, name);
675 goto done;
678 ret = maybe_update_metadata_hash(&image);
680 done:
681 free(header);
682 buffer_delete(&buffer);
683 return ret;
686 static int is_valid_topswap(void)
688 switch (param.topswap_size) {
689 case (64 * KiB):
690 case (128 * KiB):
691 case (256 * KiB):
692 case (512 * KiB):
693 case (1 * MiB):
694 break;
695 default:
696 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
697 param.topswap_size);
698 return 0;
700 return 1;
703 static void fill_header_offset(void *location, uint32_t offset)
705 // TODO: When we have a BE target, we'll need to store this as BE
706 write_le32(location, offset);
709 static int update_master_header_loc_topswap(struct cbfs_image *image,
710 void *h_loc, uint32_t header_offset)
712 struct cbfs_file *entry;
713 void *ts_h_loc = h_loc;
715 entry = cbfs_get_entry(image, "bootblock");
716 if (entry == NULL) {
717 ERROR("Bootblock not in ROM image?!?\n");
718 return 1;
722 * Check if the existing topswap boundary matches with
723 * the one provided.
725 if (param.topswap_size != be32toh(entry->len)/2) {
726 ERROR("Top swap boundary does not match\n");
727 return 1;
730 ts_h_loc -= param.topswap_size;
731 fill_header_offset(ts_h_loc, header_offset);
733 return 0;
736 static int cbfs_add_master_header(void)
738 const char * const name = "cbfs master header";
739 struct cbfs_image image;
740 struct cbfs_file *header = NULL;
741 struct buffer buffer;
742 int ret = 1;
743 size_t offset;
744 size_t size;
745 void *h_loc;
747 if (cbfs_image_from_buffer(&image, param.image_region,
748 param.headeroffset)) {
749 ERROR("Selected image region is not a CBFS.\n");
750 return 1;
753 if (cbfs_get_entry(&image, name)) {
754 ERROR("'%s' already in ROM image.\n", name);
755 return 1;
758 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
759 return 1;
761 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
762 h->magic = htobe32(CBFS_HEADER_MAGIC);
763 h->version = htobe32(CBFS_HEADER_VERSION);
764 /* The 4 bytes are left out for two reasons:
765 * 1. the cbfs master header pointer resides there
766 * 2. some cbfs implementations assume that an image that resides
767 * below 4GB has a bootblock and get confused when the end of the
768 * image is at 4GB == 0.
770 h->bootblocksize = htobe32(4);
771 h->align = htobe32(CBFS_ALIGNMENT);
772 /* The offset and romsize fields within the master header are absolute
773 * values within the boot media. As such, romsize needs to relfect
774 * the end 'offset' for a CBFS. To achieve that the current buffer
775 * representing the CBFS region's size is added to the offset of
776 * the region within a larger image.
778 offset = buffer_get(param.image_region) -
779 buffer_get_original_backing(param.image_region);
780 size = buffer_size(param.image_region);
781 h->romsize = htobe32(size + offset);
782 h->offset = htobe32(offset);
783 h->architecture = htobe32(CBFS_ARCHITECTURE_UNKNOWN);
785 /* Never add a hash attribute to the master header. */
786 header = cbfs_create_file_header(CBFS_TYPE_CBFSHEADER,
787 buffer_size(&buffer), name);
788 if (!header)
789 goto done;
790 if (cbfs_add_entry(&image, &buffer, 0, header, 0) != 0) {
791 ERROR("Failed to add cbfs master header into ROM image.\n");
792 goto done;
795 struct cbfs_file *entry;
796 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
797 ERROR("'%s' not in ROM image?!?\n", name);
798 goto done;
801 uint32_t header_offset = CBFS_SUBHEADER(entry) -
802 buffer_get(&image.buffer);
803 header_offset = -(buffer_size(&image.buffer) - header_offset);
805 h_loc = (void *)(buffer_get(&image.buffer) +
806 buffer_size(&image.buffer) - 4);
807 fill_header_offset(h_loc, header_offset);
809 * If top swap present, update the header
810 * location in secondary bootblock
812 if (param.topswap_size) {
813 if (update_master_header_loc_topswap(&image, h_loc,
814 header_offset))
815 goto done;
818 ret = maybe_update_metadata_hash(&image);
820 done:
821 free(header);
822 buffer_delete(&buffer);
823 return ret;
826 static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
828 size_t bb_buf_size = buffer_size(buffer);
830 if (bb_buf_size > param.topswap_size) {
831 ERROR("Bootblock bigger than the topswap boundary\n");
832 ERROR("size = %zd, ts = %d\n", bb_buf_size,
833 param.topswap_size);
834 return 1;
838 * Allocate topswap_size*2 bytes for bootblock to
839 * accommodate the second bootblock.
841 struct buffer new_bootblock, bb1, bb2;
842 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
843 buffer->name))
844 return 1;
846 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
847 bb_buf_size);
848 buffer_splice(&bb2, &new_bootblock,
849 buffer_size(&new_bootblock) - bb_buf_size,
850 bb_buf_size);
852 /* Copy to first bootblock */
853 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
854 /* Copy to second bootblock */
855 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
857 buffer_delete(buffer);
858 buffer_clone(buffer, &new_bootblock);
860 /* Update the location (offset) of bootblock in the region */
861 return convert_region_offset(buffer_size(buffer), offset);
864 static int cbfs_add_component(const char *filename,
865 const char *name,
866 uint32_t headeroffset,
867 convert_buffer_t convert)
870 * The steps used to determine the final placement offset in CBFS, in order:
872 * 1. If --base-address was passed, that value is used. If it was passed in the host
873 * address space, convert it to flash address space. (After that, |*offset| is always
874 * in the flash address space.)
876 * 2. The convert() function may write a location back to |offset|, usually by calling
877 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
878 * the CBFS file in its final form (after any compression and conversion).
880 * 3. If --align was passed and the offset is still undecided at this point,
881 * do_cbfs_locate() is called to find an appropriately aligned location.
883 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
884 * location that fits.
886 uint32_t offset = param.baseaddress_assigned ? param.baseaddress : 0;
887 size_t len_align = 0;
889 if (param.alignment && param.baseaddress_assigned) {
890 ERROR("Cannot specify both alignment and base address\n");
891 return 1;
894 if (param.stage_xip && param.compression != CBFS_COMPRESS_NONE) {
895 ERROR("Cannot specify compression for XIP.\n");
896 return 1;
899 if (!filename) {
900 ERROR("You need to specify -f/--filename.\n");
901 return 1;
904 if (!name) {
905 ERROR("You need to specify -n/--name.\n");
906 return 1;
909 if (param.type == 0) {
910 ERROR("You need to specify a valid -t/--type.\n");
911 return 1;
914 struct cbfs_image image;
915 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
916 return 1;
918 if (cbfs_get_entry(&image, name)) {
919 ERROR("'%s' already in ROM image.\n", name);
920 return 1;
923 struct buffer buffer;
924 if (buffer_from_file(&buffer, filename) != 0) {
925 ERROR("Could not load file '%s'.\n", filename);
926 return 1;
929 struct cbfs_file *header =
930 cbfs_create_file_header(param.type, buffer.size, name);
931 if (!header)
932 goto error;
934 /* Bootblock and CBFS header should never have file hashes. When adding
935 the bootblock it is important that we *don't* look up the metadata
936 hash yet (before it is added) or we'll cache an outdated result. */
937 if (!verification_exclude(param.type)) {
938 enum vb2_hash_algorithm mh_algo = get_mh_cache()->cbfs_hash.algo;
939 if (mh_algo != VB2_HASH_INVALID && param.hash != mh_algo) {
940 if (param.hash == VB2_HASH_INVALID) {
941 param.hash = mh_algo;
942 } else {
943 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
944 vb2_get_hash_algorithm_name(param.hash),
945 vb2_get_hash_algorithm_name(mh_algo));
946 goto error;
952 * Check if Intel CPU topswap is specified this will require a
953 * second bootblock to be added.
955 if (param.type == CBFS_TYPE_BOOTBLOCK && param.topswap_size)
956 if (add_topswap_bootblock(&buffer, &offset))
957 goto error;
959 /* With --base-address we allow host space addresses -- if so, convert it here. */
960 if (IS_HOST_SPACE_ADDRESS(offset))
961 offset = convert_addr_space(param.image_region, offset);
963 if (convert && convert(&buffer, &offset, header) != 0) {
964 ERROR("Failed to parse file '%s'.\n", filename);
965 goto error;
968 /* This needs to run after convert() to take compression into account. */
969 if (!offset && param.alignment)
970 if (do_cbfs_locate(&offset, buffer_size(&buffer)))
971 goto error;
973 /* This needs to run after convert() to hash the actual final file data. */
974 if (param.hash != VB2_HASH_INVALID &&
975 cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
976 ERROR("couldn't add hash for '%s'\n", name);
977 goto error;
980 if (param.autogen_attr) {
981 /* Add position attribute if assigned */
982 if (param.baseaddress_assigned || param.stage_xip) {
983 struct cbfs_file_attr_position *attrs =
984 (struct cbfs_file_attr_position *)
985 cbfs_add_file_attr(header,
986 CBFS_FILE_ATTR_TAG_POSITION,
987 sizeof(struct cbfs_file_attr_position));
988 if (attrs == NULL)
989 goto error;
990 attrs->position = htobe32(offset);
992 /* Add alignment attribute if used */
993 if (param.alignment) {
994 struct cbfs_file_attr_align *attrs =
995 (struct cbfs_file_attr_align *)
996 cbfs_add_file_attr(header,
997 CBFS_FILE_ATTR_TAG_ALIGNMENT,
998 sizeof(struct cbfs_file_attr_align));
999 if (attrs == NULL)
1000 goto error;
1001 attrs->alignment = htobe32(param.alignment);
1005 if (param.ibb) {
1006 /* Mark as Initial Boot Block */
1007 struct cbfs_file_attribute *attrs = cbfs_add_file_attr(header,
1008 CBFS_FILE_ATTR_TAG_IBB,
1009 sizeof(struct cbfs_file_attribute));
1010 if (attrs == NULL)
1011 goto error;
1012 /* For Intel TXT minimum align is 16 */
1013 len_align = 16;
1016 if (param.padding) {
1017 const uint32_t hs = sizeof(struct cbfs_file_attribute);
1018 uint32_t size = ALIGN_UP(MAX(hs, param.padding),
1019 CBFS_ATTRIBUTE_ALIGN);
1020 INFO("Padding %d bytes\n", size);
1021 struct cbfs_file_attribute *attr =
1022 (struct cbfs_file_attribute *)cbfs_add_file_attr(
1023 header, CBFS_FILE_ATTR_TAG_PADDING,
1024 size);
1025 if (attr == NULL)
1026 goto error;
1029 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
1030 ERROR("Failed to add '%s' into ROM image.\n", filename);
1031 goto error;
1034 free(header);
1035 buffer_delete(&buffer);
1037 return maybe_update_metadata_hash(&image) || maybe_update_fmap_hash();
1039 error:
1040 free(header);
1041 buffer_delete(&buffer);
1042 return 1;
1045 static int cbfstool_convert_raw(struct buffer *buffer,
1046 unused uint32_t *offset, struct cbfs_file *header)
1048 char *compressed;
1049 int decompressed_size, compressed_size;
1050 comp_func_ptr compress;
1052 decompressed_size = buffer->size;
1053 if (param.precompression) {
1054 param.compression = read_le32(buffer->data);
1055 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
1056 compressed_size = buffer->size - 8;
1057 compressed = malloc(compressed_size);
1058 if (!compressed)
1059 return -1;
1060 memcpy(compressed, buffer->data + 8, compressed_size);
1061 } else {
1062 if (param.compression == CBFS_COMPRESS_NONE)
1063 goto out;
1065 compress = compression_function(param.compression);
1066 if (!compress)
1067 return -1;
1068 compressed = calloc(buffer->size, 1);
1069 if (!compressed)
1070 return -1;
1072 if (compress(buffer->data, buffer->size,
1073 compressed, &compressed_size)) {
1074 WARN("Compression failed - disabled\n");
1075 free(compressed);
1076 goto out;
1080 struct cbfs_file_attr_compression *attrs =
1081 (struct cbfs_file_attr_compression *)
1082 cbfs_add_file_attr(header,
1083 CBFS_FILE_ATTR_TAG_COMPRESSION,
1084 sizeof(struct cbfs_file_attr_compression));
1085 if (attrs == NULL) {
1086 free(compressed);
1087 return -1;
1089 attrs->compression = htobe32(param.compression);
1090 attrs->decompressed_size = htobe32(decompressed_size);
1092 free(buffer->data);
1093 buffer->data = compressed;
1094 buffer->size = compressed_size;
1096 out:
1097 header->len = htobe32(buffer->size);
1098 return 0;
1101 static int cbfstool_convert_fsp(struct buffer *buffer,
1102 uint32_t *offset, struct cbfs_file *header)
1104 uint32_t address;
1105 struct buffer fsp;
1108 * There are 4 different cases here:
1110 * 1. --xip and --base-address: we need to place the binary at the given base address
1111 * in the CBFS image and relocate it to that address. *offset was already filled in,
1112 * but we need to convert it to the host address space for relocation.
1114 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1115 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
1116 * This also needs to be converted to the host address space for relocation.
1118 * 3. No --xip but a --base-address: special case where --base-address does not have its
1119 * normal meaning, instead we use it as the relocation target address. We explicitly
1120 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1122 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1123 * not be relocated. Just chain directly to convert_raw() for compression.
1126 if (param.stage_xip) {
1127 if (!param.baseaddress_assigned) {
1128 param.alignment = 4*1024;
1129 if (do_cbfs_locate(offset, buffer_size(buffer)))
1130 return -1;
1132 assert(!IS_HOST_SPACE_ADDRESS(*offset));
1133 address = convert_addr_space(param.image_region, *offset);
1134 } else {
1135 if (param.baseaddress_assigned == 0) {
1136 INFO("Honoring pre-linked FSP module, no relocation.\n");
1137 return cbfstool_convert_raw(buffer, offset, header);
1138 } else {
1139 address = param.baseaddress;
1140 *offset = 0;
1144 /* Create a copy of the buffer to attempt relocation. */
1145 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
1146 return -1;
1148 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
1150 /* Replace the buffer contents w/ the relocated ones on success. */
1151 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
1152 > 0) {
1153 buffer_delete(buffer);
1154 buffer_clone(buffer, &fsp);
1155 } else {
1156 buffer_delete(&fsp);
1157 WARN("Invalid FSP variant.\n");
1160 /* Let the raw path handle all the cbfs metadata logic. */
1161 return cbfstool_convert_raw(buffer, offset, header);
1164 static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
1165 struct cbfs_file *header)
1167 struct buffer output;
1168 int ret;
1171 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1172 * here. That is okay because XIP stages may not be compressed, so their size cannot
1173 * change anymore at a later point.
1175 if (param.stage_xip) {
1176 size_t data_size, alignment;
1177 if (elf_program_file_size_align(buffer, &data_size, &alignment) < 0) {
1178 ERROR("Could not obtain ELF size & alignment\n");
1179 return 1;
1182 param.alignment = MAX(alignment, param.alignment);
1184 if (do_cbfs_locate(offset, data_size)) {
1185 ERROR("Could not find location for stage.\n");
1186 return 1;
1190 struct cbfs_file_attr_stageheader *stageheader = (void *)
1191 cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
1192 sizeof(struct cbfs_file_attr_stageheader));
1193 if (!stageheader)
1194 return -1;
1196 if (param.stage_xip) {
1197 uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
1198 assert(IS_HOST_SPACE_ADDRESS(host_space_address));
1199 ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
1200 param.ignore_sections, stageheader);
1201 } else {
1202 ret = parse_elf_to_stage(buffer, &output, param.ignore_sections,
1203 stageheader);
1205 if (ret != 0)
1206 return -1;
1208 /* Store a hash of original uncompressed stage to compare later. */
1209 size_t decmp_size = buffer_size(&output);
1210 uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
1212 /* Chain to base conversion routine to handle compression. */
1213 ret = cbfstool_convert_raw(&output, offset, header);
1214 if (ret != 0)
1215 goto fail;
1217 /* Special care must be taken for LZ4-compressed stages that the BSS is
1218 large enough to provide scratch space for in-place decompression. */
1219 if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
1220 size_t memlen = be32toh(stageheader->memlen);
1221 size_t compressed_size = buffer_size(&output);
1222 uint8_t *compare_buffer = malloc(memlen);
1223 uint8_t *start = compare_buffer + memlen - compressed_size;
1224 if (!compare_buffer) {
1225 ERROR("Out of memory\n");
1226 goto fail;
1228 memcpy(start, buffer_get(&output), compressed_size);
1229 ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
1230 if (ret == 0) {
1231 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1232 free(compare_buffer);
1233 goto fail;
1234 } else if (ret != (int)decmp_size ||
1235 decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
1236 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1237 free(compare_buffer);
1238 goto fail;
1240 free(compare_buffer);
1243 buffer_delete(buffer);
1244 buffer_clone(buffer, &output);
1245 return 0;
1247 fail:
1248 buffer_delete(&output);
1249 return -1;
1252 static int cbfstool_convert_mkpayload(struct buffer *buffer,
1253 unused uint32_t *offset, struct cbfs_file *header)
1255 struct buffer output;
1256 int ret;
1257 /* Per default, try and see if payload is an ELF binary */
1258 ret = parse_elf_to_payload(buffer, &output, param.compression);
1260 /* If it's not an ELF, see if it's a FIT */
1261 if (ret != 0) {
1262 ret = parse_fit_to_payload(buffer, &output, param.compression);
1263 if (ret == 0)
1264 header->type = htobe32(CBFS_TYPE_FIT_PAYLOAD);
1267 /* If it's not an FIT, see if it's a UEFI FV */
1268 if (ret != 0)
1269 ret = parse_fv_to_payload(buffer, &output, param.compression);
1271 /* If it's neither ELF nor UEFI Fv, try bzImage */
1272 if (ret != 0)
1273 ret = parse_bzImage_to_payload(buffer, &output,
1274 param.initrd, param.cmdline, param.compression);
1276 /* Not a supported payload type */
1277 if (ret != 0) {
1278 ERROR("Not a supported payload type (ELF / FV).\n");
1279 buffer_delete(buffer);
1280 return -1;
1283 buffer_delete(buffer);
1284 // Direct assign, no dupe.
1285 memcpy(buffer, &output, sizeof(*buffer));
1286 header->len = htobe32(output.size);
1287 return 0;
1290 static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
1291 unused uint32_t *offset, struct cbfs_file *header)
1293 struct buffer output;
1294 if (parse_flat_binary_to_payload(buffer, &output,
1295 param.loadaddress,
1296 param.entrypoint,
1297 param.compression) != 0) {
1298 return -1;
1300 buffer_delete(buffer);
1301 // Direct assign, no dupe.
1302 memcpy(buffer, &output, sizeof(*buffer));
1303 header->len = htobe32(output.size);
1304 return 0;
1307 static int cbfs_add(void)
1309 convert_buffer_t convert = cbfstool_convert_raw;
1311 if (param.type == CBFS_TYPE_FSP) {
1312 convert = cbfstool_convert_fsp;
1313 } else if (param.type == CBFS_TYPE_STAGE) {
1314 ERROR("stages can only be added with cbfstool add-stage\n");
1315 return 1;
1316 } else if (param.stage_xip) {
1317 ERROR("cbfstool add supports xip only for FSP component type\n");
1318 return 1;
1321 return cbfs_add_component(param.filename,
1322 param.name,
1323 param.headeroffset,
1324 convert);
1327 static int cbfs_add_stage(void)
1329 if (param.stage_xip && param.baseaddress_assigned) {
1330 ERROR("Cannot specify base address for XIP.\n");
1331 return 1;
1333 param.type = CBFS_TYPE_STAGE;
1335 return cbfs_add_component(param.filename,
1336 param.name,
1337 param.headeroffset,
1338 cbfstool_convert_mkstage);
1341 static int cbfs_add_payload(void)
1343 param.type = CBFS_TYPE_SELF;
1344 return cbfs_add_component(param.filename,
1345 param.name,
1346 param.headeroffset,
1347 cbfstool_convert_mkpayload);
1350 static int cbfs_add_flat_binary(void)
1352 if (param.loadaddress == 0) {
1353 ERROR("You need to specify a valid "
1354 "-l/--load-address.\n");
1355 return 1;
1357 if (param.entrypoint == 0) {
1358 ERROR("You need to specify a valid "
1359 "-e/--entry-point.\n");
1360 return 1;
1362 param.type = CBFS_TYPE_SELF;
1363 return cbfs_add_component(param.filename,
1364 param.name,
1365 param.headeroffset,
1366 cbfstool_convert_mkflatpayload);
1369 static int cbfs_add_integer(void)
1371 if (!param.u64val_assigned) {
1372 ERROR("You need to specify a value to write.\n");
1373 return 1;
1375 return cbfs_add_integer_component(param.name,
1376 param.u64val,
1377 param.baseaddress,
1378 param.headeroffset);
1381 static int cbfs_remove(void)
1383 if (!param.name) {
1384 ERROR("You need to specify -n/--name.\n");
1385 return 1;
1388 struct cbfs_image image;
1389 if (cbfs_image_from_buffer(&image, param.image_region,
1390 param.headeroffset))
1391 return 1;
1393 if (cbfs_remove_entry(&image, param.name) != 0) {
1394 ERROR("Removing file '%s' failed.\n",
1395 param.name);
1396 return 1;
1399 return maybe_update_metadata_hash(&image);
1402 static int cbfs_create(void)
1404 struct cbfs_image image;
1405 memset(&image, 0, sizeof(image));
1406 buffer_clone(&image.buffer, param.image_region);
1408 if (param.fmap) {
1409 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
1410 param.baseaddress_assigned ||
1411 param.headeroffset_assigned ||
1412 param.cbfsoffset_assigned ||
1413 param.bootblock) {
1414 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
1415 return 1;
1418 return cbfs_image_create(&image, image.buffer.size);
1421 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
1422 ERROR("You need to specify -m/--machine arch.\n");
1423 return 1;
1426 struct buffer bootblock;
1427 if (!param.bootblock) {
1428 DEBUG("-B not given, creating image without bootblock.\n");
1429 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
1430 return 1;
1431 } else if (buffer_from_file(&bootblock, param.bootblock)) {
1432 return 1;
1435 if (!param.alignment)
1436 param.alignment = CBFS_ALIGNMENT;
1438 // Set default offsets. x86, as usual, needs to be a special snowflake.
1439 if (!param.baseaddress_assigned) {
1440 if (param.arch == CBFS_ARCHITECTURE_X86) {
1441 // Make sure there's at least enough room for rel_offset
1442 param.baseaddress = param.size -
1443 MAX(bootblock.size, sizeof(int32_t));
1444 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1445 param.baseaddress);
1446 } else {
1447 param.baseaddress = 0;
1448 DEBUG("bootblock starts at address 0x0.\n");
1451 if (!param.headeroffset_assigned) {
1452 if (param.arch == CBFS_ARCHITECTURE_X86) {
1453 param.headeroffset = param.baseaddress -
1454 sizeof(struct cbfs_header);
1455 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1456 param.headeroffset);
1457 } else {
1458 param.headeroffset = align_up(param.baseaddress +
1459 bootblock.size, sizeof(uint32_t));
1460 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1461 param.headeroffset);
1464 if (!param.cbfsoffset_assigned) {
1465 if (param.arch == CBFS_ARCHITECTURE_X86) {
1466 param.cbfsoffset = 0;
1467 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1468 } else {
1469 param.cbfsoffset = align_up(param.headeroffset +
1470 sizeof(struct cbfs_header),
1471 CBFS_ALIGNMENT);
1472 DEBUG("CBFS entries start beind master header (%#x).\n",
1473 param.cbfsoffset);
1477 int ret = cbfs_legacy_image_create(&image,
1478 param.arch,
1479 CBFS_ALIGNMENT,
1480 &bootblock,
1481 param.baseaddress,
1482 param.headeroffset,
1483 param.cbfsoffset);
1484 buffer_delete(&bootblock);
1485 return ret;
1488 static int cbfs_layout(void)
1490 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1491 if (!fmap) {
1492 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1493 return 1;
1496 printf("This image contains the following sections that can be %s with this tool:\n",
1497 param.show_immutable ? "accessed" : "manipulated");
1498 puts("");
1499 for (unsigned i = 0; i < fmap->nareas; ++i) {
1500 const struct fmap_area *current = fmap->areas + i;
1502 bool readonly = partitioned_file_fmap_count(param.image_file,
1503 partitioned_file_fmap_select_children_of, current) ||
1504 region_is_flashmap((const char *)current->name);
1505 if (!param.show_immutable && readonly)
1506 continue;
1508 printf("'%s'", current->name);
1510 // Detect consecutive sections that describe the same region and
1511 // show them as aliases. This cannot find equivalent entries
1512 // that aren't adjacent; however, fmaptool doesn't generate
1513 // FMAPs with such sections, so this convenience feature works
1514 // for all but the strangest manually created FMAP binaries.
1515 // TODO: This could be done by parsing the FMAP into some kind
1516 // of tree that had duplicate lists in addition to child lists,
1517 // which would allow covering that weird, unlikely case as well.
1518 unsigned lookahead;
1519 for (lookahead = 1; i + lookahead < fmap->nareas;
1520 ++lookahead) {
1521 const struct fmap_area *consecutive =
1522 fmap->areas + i + lookahead;
1523 if (consecutive->offset != current->offset ||
1524 consecutive->size != current->size)
1525 break;
1526 printf(", '%s'", consecutive->name);
1528 if (lookahead > 1)
1529 fputs(" are aliases for the same region", stdout);
1531 const char *qualifier = "";
1532 if (readonly)
1533 qualifier = "read-only, ";
1534 else if (region_is_modern_cbfs((const char *)current->name))
1535 qualifier = "CBFS, ";
1536 else if (current->flags & FMAP_AREA_PRESERVE)
1537 qualifier = "preserve, ";
1538 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1539 current->offset);
1541 i += lookahead - 1;
1543 puts("");
1545 if (param.show_immutable) {
1546 puts("It is at least possible to perform the read action on every section listed above.");
1547 } else {
1548 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1549 puts("To see the image's read-only sections as well, rerun with the -w option.");
1552 return 0;
1555 static enum cb_err verify_walker(__always_unused cbfs_dev_t dev, size_t offset,
1556 const union cbfs_mdata *mdata, size_t already_read, void *arg)
1558 uint32_t type = be32toh(mdata->h.type);
1559 uint32_t data_offset = be32toh(mdata->h.offset);
1560 if (verification_exclude(type))
1561 return CB_CBFS_NOT_FOUND;
1562 assert(already_read == data_offset);
1563 const struct vb2_hash *hash = cbfs_file_hash(mdata);
1564 if (!hash)
1565 return CB_ERR;
1566 void *file_data = arg + offset + data_offset;
1567 if (vb2_hash_verify(false, file_data, be32toh(mdata->h.len), hash) != VB2_SUCCESS)
1568 return CB_CBFS_HASH_MISMATCH;
1569 return CB_CBFS_NOT_FOUND;
1572 static int cbfs_print(void)
1574 struct cbfs_image image;
1575 if (cbfs_image_from_buffer(&image, param.image_region,
1576 param.headeroffset))
1577 return 1;
1578 if (param.machine_parseable) {
1579 if (verbose)
1580 printf("[FMAP REGION]\t%s\n", param.region_name);
1581 cbfs_print_parseable_directory(&image);
1582 } else {
1583 printf("FMAP REGION: %s\n", param.region_name);
1584 cbfs_print_directory(&image);
1587 if (verbose) {
1588 const char *verification_state = "fully valid";
1589 struct mh_cache *mhc = get_mh_cache();
1590 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1591 return 0;
1593 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
1594 enum cb_err err = cbfs_walk(&image, verify_walker, buffer_get(&image.buffer),
1595 &real_hash, CBFS_WALK_WRITEBACK_HASH);
1596 if (err == CB_CBFS_HASH_MISMATCH)
1597 verification_state = "invalid file hashes";
1598 else if (err != CB_CBFS_NOT_FOUND)
1599 verification_state = "missing file hashes";
1600 char *hash_str = bintohex(real_hash.raw,
1601 vb2_digest_size(real_hash.algo));
1602 printf("[METADATA HASH]\t%s:%s",
1603 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1604 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1605 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
1606 vb2_digest_size(real_hash.algo))) {
1607 printf(":valid");
1608 } else {
1609 printf(":invalid");
1610 verification_state = "invalid metadata hash";
1613 printf("\n");
1614 printf("[CBFS VERIFICATION (%s)]\t%s\n", param.region_name, verification_state);
1615 free(hash_str);
1618 return 0;
1621 static int cbfs_extract(void)
1623 if (!param.filename) {
1624 ERROR("You need to specify -f/--filename.\n");
1625 return 1;
1628 if (!param.name) {
1629 ERROR("You need to specify -n/--name.\n");
1630 return 1;
1633 struct cbfs_image image;
1634 if (cbfs_image_from_buffer(&image, param.image_region,
1635 param.headeroffset))
1636 return 1;
1638 return cbfs_export_entry(&image, param.name, param.filename,
1639 param.arch, !param.unprocessed);
1642 static int cbfs_write(void)
1644 if (!param.filename) {
1645 ERROR("You need to specify a valid input -f/--file.\n");
1646 return 1;
1648 if (!partitioned_file_is_partitioned(param.image_file)) {
1649 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1650 return 1;
1653 if (!param.force && region_is_modern_cbfs(param.region_name)) {
1654 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1655 param.region_name);
1656 return 1;
1659 struct buffer new_content;
1660 if (buffer_from_file(&new_content, param.filename))
1661 return 1;
1663 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1664 strlen(FMAP_SIGNATURE))) {
1665 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1666 param.filename);
1667 buffer_delete(&new_content);
1668 return 1;
1670 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
1671 strlen(CBFS_FILE_MAGIC))) {
1672 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1673 param.filename);
1674 buffer_delete(&new_content);
1675 return 1;
1678 unsigned offset = 0;
1679 if (param.fill_partial_upward && param.fill_partial_downward) {
1680 ERROR("You may only specify one of -u and -d.\n");
1681 buffer_delete(&new_content);
1682 return 1;
1683 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1684 if (new_content.size != param.image_region->size) {
1685 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",
1686 new_content.size, param.image_region->size);
1687 buffer_delete(&new_content);
1688 return 1;
1690 } else {
1691 if (new_content.size > param.image_region->size) {
1692 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1693 new_content.size, param.image_region->size);
1694 buffer_delete(&new_content);
1695 return 1;
1697 if (param.u64val == (uint64_t)-1) {
1698 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1699 param.fill_partial_upward ? "bottom" : "top");
1700 } else if (param.u64val > 0xff) {
1701 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1702 buffer_delete(&new_content);
1703 return 1;
1704 } else {
1705 memset(buffer_get(param.image_region),
1706 param.u64val & 0xff,
1707 buffer_size(param.image_region));
1709 if (param.fill_partial_downward)
1710 offset = param.image_region->size - new_content.size;
1713 memcpy(param.image_region->data + offset, new_content.data,
1714 new_content.size);
1715 buffer_delete(&new_content);
1717 return maybe_update_fmap_hash();
1720 static int cbfs_read(void)
1722 if (!param.filename) {
1723 ERROR("You need to specify a valid output -f/--file.\n");
1724 return 1;
1726 if (!partitioned_file_is_partitioned(param.image_file)) {
1727 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1728 return 1;
1731 return buffer_write_file(param.image_region, param.filename);
1734 static int cbfs_copy(void)
1736 struct cbfs_image src_image;
1737 struct buffer src_buf;
1739 if (!param.source_region) {
1740 ERROR("You need to specify -R/--source-region.\n");
1741 return 1;
1744 /* Obtain the source region and convert it to a cbfs_image. */
1745 if (!partitioned_file_read_region(&src_buf, param.image_file,
1746 param.source_region)) {
1747 ERROR("Region not found in image: %s\n", param.source_region);
1748 return 1;
1751 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1752 return 1;
1754 return cbfs_copy_instance(&src_image, param.image_region);
1757 static int cbfs_compact(void)
1759 struct cbfs_image image;
1760 if (cbfs_image_from_buffer(&image, param.image_region,
1761 param.headeroffset))
1762 return 1;
1763 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1764 return cbfs_compact_instance(&image);
1767 static int cbfs_expand(void)
1769 struct buffer src_buf;
1771 /* Obtain the source region. */
1772 if (!partitioned_file_read_region(&src_buf, param.image_file,
1773 param.region_name)) {
1774 ERROR("Region not found in image: %s\n", param.source_region);
1775 return 1;
1778 return cbfs_expand_to_region(param.image_region);
1781 static int cbfs_truncate(void)
1783 struct buffer src_buf;
1785 /* Obtain the source region. */
1786 if (!partitioned_file_read_region(&src_buf, param.image_file,
1787 param.region_name)) {
1788 ERROR("Region not found in image: %s\n", param.source_region);
1789 return 1;
1792 uint32_t size;
1793 int result = cbfs_truncate_space(param.image_region, &size);
1794 if (!result)
1795 printf("0x%x\n", size);
1796 return result;
1799 static const struct command commands[] = {
1800 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
1801 {"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary,
1802 true, true},
1803 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
1804 true, true},
1805 {"add-stage", "a:H:r:f:n:t:c:b:P:QS:p:yvA:gh?", cbfs_add_stage,
1806 true, true},
1807 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
1808 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
1809 {"compact", "r:h?", cbfs_compact, true, true},
1810 {"copy", "r:R:h?", cbfs_copy, true, true},
1811 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
1812 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
1813 {"layout", "wvh?", cbfs_layout, false, false},
1814 {"print", "H:r:vkh?", cbfs_print, true, false},
1815 {"read", "r:f:vh?", cbfs_read, true, false},
1816 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
1817 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
1818 {"expand", "r:h?", cbfs_expand, true, true},
1819 {"truncate", "r:h?", cbfs_truncate, true, true},
1822 enum {
1823 /* begin after ASCII characters */
1824 LONGOPT_START = 256,
1825 LONGOPT_IBB = LONGOPT_START,
1826 LONGOPT_MMAP,
1827 LONGOPT_END,
1830 static struct option long_options[] = {
1831 {"alignment", required_argument, 0, 'a' },
1832 {"base-address", required_argument, 0, 'b' },
1833 {"bootblock", required_argument, 0, 'B' },
1834 {"cmdline", required_argument, 0, 'C' },
1835 {"compression", required_argument, 0, 'c' },
1836 {"topswap-size", required_argument, 0, 'j' },
1837 {"empty-fits", required_argument, 0, 'x' },
1838 {"entry-point", required_argument, 0, 'e' },
1839 {"file", required_argument, 0, 'f' },
1840 {"fill-downward", no_argument, 0, 'd' },
1841 {"fill-upward", no_argument, 0, 'u' },
1842 {"flashmap", required_argument, 0, 'M' },
1843 {"fmap-regions", required_argument, 0, 'r' },
1844 {"force", no_argument, 0, 'F' },
1845 {"source-region", required_argument, 0, 'R' },
1846 {"hash-algorithm",required_argument, 0, 'A' },
1847 {"header-offset", required_argument, 0, 'H' },
1848 {"help", no_argument, 0, 'h' },
1849 {"ignore-sec", required_argument, 0, 'S' },
1850 {"initrd", required_argument, 0, 'I' },
1851 {"int", required_argument, 0, 'i' },
1852 {"load-address", required_argument, 0, 'l' },
1853 {"machine", required_argument, 0, 'm' },
1854 {"name", required_argument, 0, 'n' },
1855 {"offset", required_argument, 0, 'o' },
1856 {"padding", required_argument, 0, 'p' },
1857 {"pow2page", no_argument, 0, 'Q' },
1858 {"ucode-region", required_argument, 0, 'q' },
1859 {"size", required_argument, 0, 's' },
1860 {"type", required_argument, 0, 't' },
1861 {"verbose", no_argument, 0, 'v' },
1862 {"with-readonly", no_argument, 0, 'w' },
1863 {"xip", no_argument, 0, 'y' },
1864 {"gen-attribute", no_argument, 0, 'g' },
1865 {"mach-parseable",no_argument, 0, 'k' },
1866 {"unprocessed", no_argument, 0, 'U' },
1867 {"ibb", no_argument, 0, LONGOPT_IBB },
1868 {"mmap", required_argument, 0, LONGOPT_MMAP },
1869 {NULL, 0, 0, 0 }
1872 static int get_region_offset(long long int offset, uint32_t *region_offset)
1874 /* If offset is not negative, no transformation required. */
1875 if (offset >= 0) {
1876 *region_offset = offset;
1877 return 0;
1880 /* Calculate offset from start of region. */
1881 return convert_region_offset(-offset, region_offset);
1884 static int calculate_region_offsets(void)
1886 int ret = 0;
1888 if (param.baseaddress_assigned)
1889 ret |= get_region_offset(param.baseaddress_input, &param.baseaddress);
1890 if (param.headeroffset_assigned)
1891 ret |= get_region_offset(param.headeroffset_input, &param.headeroffset);
1892 if (param.cbfsoffset_assigned)
1893 ret |= get_region_offset(param.cbfsoffset_input, &param.cbfsoffset);
1895 return ret;
1898 static int dispatch_command(struct command command)
1900 if (command.accesses_region) {
1901 assert(param.image_file);
1903 if (partitioned_file_is_partitioned(param.image_file)) {
1904 INFO("Performing operation on '%s' region...\n",
1905 param.region_name);
1907 if (!partitioned_file_read_region(param.image_region,
1908 param.image_file, param.region_name)) {
1909 ERROR("The image will be left unmodified.\n");
1910 return 1;
1913 if (command.modifies_region) {
1914 // We (intentionally) don't support overwriting the FMAP
1915 // section. If you find yourself wanting to do this,
1916 // consider creating a new image rather than performing
1917 // whatever hacky transformation you were planning.
1918 if (region_is_flashmap(param.region_name)) {
1919 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1920 param.region_name);
1921 ERROR("The image will be left unmodified.\n");
1922 return 1;
1924 // We don't allow writing raw data to regions that
1925 // contain nested regions, since doing so would
1926 // overwrite all such subregions.
1927 if (partitioned_file_region_contains_nested(
1928 param.image_file, param.region_name)) {
1929 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1930 param.region_name);
1931 ERROR("The image will be left unmodified.\n");
1932 return 1;
1937 * Once image region is read, input offsets can be adjusted accordingly if the
1938 * inputs are provided as negative integers i.e. offsets from end of region.
1940 if (calculate_region_offsets())
1941 return 1;
1944 if (command.function()) {
1945 if (partitioned_file_is_partitioned(param.image_file)) {
1946 ERROR("Failed while operating on '%s' region!\n",
1947 param.region_name);
1948 ERROR("The image will be left unmodified.\n");
1950 return 1;
1953 return 0;
1956 static void usage(char *name)
1958 printf
1959 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
1960 "USAGE:\n" " %s [-h]\n"
1961 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
1962 " -H header_offset Do not search for header; use this offset*\n"
1963 " -T Output top-aligned memory address\n"
1964 " -u Accept short data; fill upward/from bottom\n"
1965 " -d Accept short data; fill downward/from top\n"
1966 " -F Force action\n"
1967 " -g Generate position and alignment arguments\n"
1968 " -U Unprocessed; don't decompress or make ELF\n"
1969 " -v Provide verbose output (-v=INFO -vv=DEBUG output)\n"
1970 " -h Display this help message\n\n"
1971 " --ext-win-base Base of extended decode window in host address\n"
1972 " space(x86 only)\n"
1973 " --ext-win-size Size of extended decode window in host address\n"
1974 " space(x86 only)\n"
1975 "COMMANDs:\n"
1976 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
1977 " [-c compression] [-b base-address | -a alignment] \\\n"
1978 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
1979 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1980 " [--ext-win-base win-base --ext-win-size win-size] "
1981 "Add a component\n"
1983 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
1984 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1985 " [-c compression] [-b base-address] \\\n"
1986 " (linux specific: [-C cmdline] [-I initrd]) "
1987 "Add a payload to the ROM\n"
1988 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1989 " [-c compression] [-b base] \\\n"
1990 " [-S comma-separated-section(s)-to-ignore] \\\n"
1991 " [-a alignment] [-Q|--pow2page] \\\n"
1992 " [-y|--xip] [--ibb] \\\n"
1993 " [--ext-win-base win-base --ext-win-size win-size] "
1994 "Add a stage to the ROM\n"
1995 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1996 " [-A hash] -l load-address -e entry-point \\\n"
1997 " [-c compression] [-b base] "
1998 "Add a 32bit flat mode binary\n"
1999 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
2000 "Add a raw 64-bit integer value\n"
2001 " add-master-header [-r image,regions] \\ \n"
2002 " [-j topswap-size] (Intel CPUs only) "
2003 "Add a legacy CBFS master header\n"
2004 " remove [-r image,regions] -n NAME "
2005 "Remove a component\n"
2006 " compact -r image,regions "
2007 "Defragment CBFS image.\n"
2008 " copy -r image,regions -R source-region "
2009 "Create a copy (duplicate) cbfs instance in fmap\n"
2010 " create -m ARCH -s size [-b bootblock offset] \\\n"
2011 " [-o CBFS offset] [-H header offset] [-B bootblock] "
2012 "Create a legacy ROM file with CBFS master header*\n"
2013 " create -M flashmap [-r list,of,regions,containing,cbfses] "
2014 "Create a new-style partitioned firmware image\n"
2015 " layout [-w] "
2016 "List mutable (or, with -w, readable) image regions\n"
2017 " print [-r image,regions] [-k] "
2018 "Show the contents of the ROM\n"
2019 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
2020 "Extracts a file from ROM\n"
2021 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
2022 "Write file into same-size [or larger] raw region\n"
2023 " read [-r fmap-region] -f file "
2024 "Extract raw region contents into binary file\n"
2025 " truncate [-r fmap-region] "
2026 "Truncate CBFS and print new size on stdout\n"
2027 " expand [-r fmap-region] "
2028 "Expand CBFS to span entire region\n"
2029 "OFFSETs:\n"
2030 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
2031 " in two possible formats: if their value is greater than\n"
2032 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
2033 " address; otherwise, they are treated as an offset into flash.\n"
2034 "ARCHes:\n", name, name
2036 print_supported_architectures();
2038 printf("TYPEs:\n");
2039 print_supported_filetypes();
2040 printf(
2041 "\n* Note that these actions and switches are only valid when\n"
2042 " working with legacy images whose structure is described\n"
2043 " primarily by a CBFS master header. New-style images, in\n"
2044 " contrast, exclusively make use of an FMAP to describe their\n"
2045 " layout: this must minimally contain an '%s' section\n"
2046 " specifying the location of this FMAP itself and a '%s'\n"
2047 " section describing the primary CBFS. It should also be noted\n"
2048 " that, when working with such images, the -F and -r switches\n"
2049 " default to '%s' for convenience, and the -b switch becomes\n"
2050 " relative to the selected CBFS region's lowest address.\n"
2051 " The one exception to this rule is the top-aligned address,\n"
2052 " which is always relative to the end of the entire image\n"
2053 " rather than relative to the local region; this is true for\n"
2054 " for both input (sufficiently large) and output (-T) data.\n",
2055 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
2056 SECTION_NAME_PRIMARY_CBFS
2060 static bool valid_opt(size_t i, int c)
2062 /* Check if it is one of the optstrings supported by the command. */
2063 if (strchr(commands[i].optstring, c))
2064 return true;
2067 * Check if it is one of the non-ASCII characters. Currently, the
2068 * non-ASCII characters are only checked against the valid list
2069 * irrespective of the command.
2071 if (c >= LONGOPT_START && c < LONGOPT_END)
2072 return true;
2074 return false;
2077 int main(int argc, char **argv)
2079 size_t i;
2080 int c;
2082 if (argc < 3) {
2083 usage(argv[0]);
2084 return 1;
2087 char *image_name = argv[1];
2088 char *cmd = argv[2];
2089 optind += 2;
2091 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2092 if (strcmp(cmd, commands[i].name) != 0)
2093 continue;
2095 while (1) {
2096 char *suffix = NULL;
2097 int option_index = 0;
2099 c = getopt_long(argc, argv, commands[i].optstring,
2100 long_options, &option_index);
2101 if (c == -1) {
2102 if (optind < argc) {
2103 ERROR("%s: excessive argument -- '%s'"
2104 "\n", argv[0], argv[optind]);
2105 return 1;
2107 break;
2110 /* Filter out illegal long options */
2111 if (!valid_opt(i, c)) {
2112 ERROR("%s: invalid option -- '%d'\n",
2113 argv[0], c);
2114 c = '?';
2117 switch(c) {
2118 case 'n':
2119 param.name = optarg;
2120 break;
2121 case 't':
2122 if (intfiletype(optarg) != ((uint64_t) - 1))
2123 param.type = intfiletype(optarg);
2124 else
2125 param.type = strtoul(optarg, NULL, 0);
2126 if (param.type == 0)
2127 WARN("Unknown type '%s' ignored\n",
2128 optarg);
2129 break;
2130 case 'c': {
2131 if (strcmp(optarg, "precompression") == 0) {
2132 param.precompression = 1;
2133 break;
2135 int algo = cbfs_parse_comp_algo(optarg);
2136 if (algo >= 0)
2137 param.compression = algo;
2138 else
2139 WARN("Unknown compression '%s' ignored.\n",
2140 optarg);
2141 break;
2143 case 'A': {
2144 if (!vb2_lookup_hash_alg(optarg, &param.hash)) {
2145 ERROR("Unknown hash algorithm '%s'.\n",
2146 optarg);
2147 return 1;
2149 break;
2151 case 'M':
2152 param.fmap = optarg;
2153 break;
2154 case 'r':
2155 param.region_name = optarg;
2156 break;
2157 case 'R':
2158 param.source_region = optarg;
2159 break;
2160 case 'b':
2161 param.baseaddress_input = strtoll(optarg, &suffix, 0);
2162 if (!*optarg || (suffix && *suffix)) {
2163 ERROR("Invalid base address '%s'.\n",
2164 optarg);
2165 return 1;
2167 // baseaddress may be zero on non-x86, so we
2168 // need an explicit "baseaddress_assigned".
2169 param.baseaddress_assigned = 1;
2170 break;
2171 case 'l':
2172 param.loadaddress = strtoull(optarg, &suffix, 0);
2173 if (!*optarg || (suffix && *suffix)) {
2174 ERROR("Invalid load address '%s'.\n",
2175 optarg);
2176 return 1;
2178 break;
2179 case 'e':
2180 param.entrypoint = strtoull(optarg, &suffix, 0);
2181 if (!*optarg || (suffix && *suffix)) {
2182 ERROR("Invalid entry point '%s'.\n",
2183 optarg);
2184 return 1;
2186 break;
2187 case 's':
2188 param.size = strtoul(optarg, &suffix, 0);
2189 if (!*optarg) {
2190 ERROR("Empty size specified.\n");
2191 return 1;
2193 switch (tolower((int)suffix[0])) {
2194 case 'k':
2195 param.size *= 1024;
2196 break;
2197 case 'm':
2198 param.size *= 1024 * 1024;
2199 break;
2200 case '\0':
2201 break;
2202 default:
2203 ERROR("Invalid suffix for size '%s'.\n",
2204 optarg);
2205 return 1;
2207 break;
2208 case 'B':
2209 param.bootblock = optarg;
2210 break;
2211 case 'H':
2212 param.headeroffset_input = strtoll(optarg, &suffix, 0);
2213 if (!*optarg || (suffix && *suffix)) {
2214 ERROR("Invalid header offset '%s'.\n",
2215 optarg);
2216 return 1;
2218 param.headeroffset_assigned = 1;
2219 break;
2220 case 'a':
2221 param.alignment = strtoul(optarg, &suffix, 0);
2222 if (!*optarg || (suffix && *suffix)) {
2223 ERROR("Invalid alignment '%s'.\n",
2224 optarg);
2225 return 1;
2227 break;
2228 case 'p':
2229 param.padding = strtoul(optarg, &suffix, 0);
2230 if (!*optarg || (suffix && *suffix)) {
2231 ERROR("Invalid pad size '%s'.\n",
2232 optarg);
2233 return 1;
2235 break;
2236 case 'Q':
2237 param.force_pow2_pagesize = 1;
2238 break;
2239 case 'o':
2240 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
2241 if (!*optarg || (suffix && *suffix)) {
2242 ERROR("Invalid cbfs offset '%s'.\n",
2243 optarg);
2244 return 1;
2246 param.cbfsoffset_assigned = 1;
2247 break;
2248 case 'f':
2249 param.filename = optarg;
2250 break;
2251 case 'F':
2252 param.force = 1;
2253 break;
2254 case 'i':
2255 param.u64val = strtoull(optarg, &suffix, 0);
2256 param.u64val_assigned = 1;
2257 if (!*optarg || (suffix && *suffix)) {
2258 ERROR("Invalid int parameter '%s'.\n",
2259 optarg);
2260 return 1;
2262 break;
2263 case 'u':
2264 param.fill_partial_upward = true;
2265 break;
2266 case 'd':
2267 param.fill_partial_downward = true;
2268 break;
2269 case 'w':
2270 param.show_immutable = true;
2271 break;
2272 case 'j':
2273 param.topswap_size = strtol(optarg, NULL, 0);
2274 if (!is_valid_topswap())
2275 return 1;
2276 break;
2277 case 'q':
2278 param.ucode_region = optarg;
2279 break;
2280 case 'v':
2281 verbose++;
2282 break;
2283 case 'm':
2284 param.arch = string_to_arch(optarg);
2285 break;
2286 case 'I':
2287 param.initrd = optarg;
2288 break;
2289 case 'C':
2290 param.cmdline = optarg;
2291 break;
2292 case 'S':
2293 param.ignore_sections = optarg;
2294 break;
2295 case 'y':
2296 param.stage_xip = true;
2297 break;
2298 case 'g':
2299 param.autogen_attr = true;
2300 break;
2301 case 'k':
2302 param.machine_parseable = true;
2303 break;
2304 case 'U':
2305 param.unprocessed = true;
2306 break;
2307 case LONGOPT_IBB:
2308 param.ibb = true;
2309 break;
2310 case LONGOPT_MMAP:
2311 if (decode_mmap_arg(optarg))
2312 return 1;
2313 break;
2314 case 'h':
2315 case '?':
2316 usage(argv[0]);
2317 return 1;
2318 default:
2319 break;
2323 if (commands[i].function == cbfs_create) {
2324 if (param.fmap) {
2325 struct buffer flashmap;
2326 if (buffer_from_file(&flashmap, param.fmap))
2327 return 1;
2328 param.image_file = partitioned_file_create(
2329 image_name, &flashmap);
2330 buffer_delete(&flashmap);
2331 } else if (param.size) {
2332 param.image_file = partitioned_file_create_flat(
2333 image_name, param.size);
2334 } else {
2335 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
2336 return 1;
2338 } else {
2339 bool write_access = commands[i].modifies_region;
2341 param.image_file =
2342 partitioned_file_reopen(image_name,
2343 write_access);
2345 if (!param.image_file)
2346 return 1;
2348 unsigned num_regions = 1;
2349 for (const char *list = strchr(param.region_name, ','); list;
2350 list = strchr(list + 1, ','))
2351 ++num_regions;
2353 // If the action needs to read an image region, as indicated by
2354 // having accesses_region set in its command struct, that
2355 // region's buffer struct will be stored here and the client
2356 // will receive a pointer to it via param.image_region. It
2357 // need not write the buffer back to the image file itself,
2358 // since this behavior can be requested via its modifies_region
2359 // field. Additionally, it should never free the region buffer,
2360 // as that is performed automatically once it completes.
2361 struct buffer image_regions[num_regions];
2362 memset(image_regions, 0, sizeof(image_regions));
2364 bool seen_primary_cbfs = false;
2365 char region_name_scratch[strlen(param.region_name) + 1];
2366 strcpy(region_name_scratch, param.region_name);
2367 param.region_name = strtok(region_name_scratch, ",");
2368 for (unsigned region = 0; region < num_regions; ++region) {
2369 if (!param.region_name) {
2370 ERROR("Encountered illegal degenerate region name in -r list\n");
2371 ERROR("The image will be left unmodified.\n");
2372 partitioned_file_close(param.image_file);
2373 return 1;
2376 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2377 == 0)
2378 seen_primary_cbfs = true;
2380 param.image_region = image_regions + region;
2381 if (dispatch_command(commands[i])) {
2382 partitioned_file_close(param.image_file);
2383 return 1;
2386 param.region_name = strtok(NULL, ",");
2389 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2390 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2391 SECTION_NAME_PRIMARY_CBFS);
2392 ERROR("The image will be left unmodified.\n");
2393 partitioned_file_close(param.image_file);
2394 return 1;
2397 if (commands[i].modifies_region) {
2398 assert(param.image_file);
2399 for (unsigned region = 0; region < num_regions;
2400 ++region) {
2402 if (!partitioned_file_write_region(
2403 param.image_file,
2404 image_regions + region)) {
2405 partitioned_file_close(
2406 param.image_file);
2407 return 1;
2412 partitioned_file_close(param.image_file);
2413 return 0;
2416 ERROR("Unknown command '%s'.\n", cmd);
2417 usage(argv[0]);
2418 return 1;