1 /* cbfstool, CLI utility for CBFS file manipulation */
2 /* SPDX-License-Identifier: GPL-2.0-only */
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>
29 const char *optstring
;
30 int (*function
) (void);
31 // Whether to populate param.image_region before invoking function
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
41 partitioned_file_t
*image_file
;
42 struct buffer
*image_region
;
46 const char *region_name
;
47 const char *source_region
;
48 const char *bootblock
;
49 const char *ignore_sections
;
50 const char *ucode_region
;
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
;
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
;
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
;
81 uint32_t topswap_size
;
83 bool fill_partial_upward
;
84 bool fill_partial_downward
;
87 bool force_pow2_pagesize
;
89 bool machine_parseable
;
92 enum cbfs_compression compression
;
94 enum vb2_hash_algorithm hash
;
95 /* For linux payloads */
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
;
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
,
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.
133 struct vb2_hash cbfs_hash
;
134 platform_fixup_func fixup
;
138 static struct mh_cache
*get_mh_cache(void)
140 static struct mh_cache mhc
;
145 mhc
.initialized
= true;
147 const struct fmap
*fmap
= partitioned_file_get_fmap(param
.image_file
);
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. */
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
;
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
));
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
,
202 mhc
.cbfs_hash
.algo
= VB2_HASH_INVALID
;
206 static void update_and_info(const char *name
, void *dst
, void *src
, size_t size
)
208 if (!memcmp(dst
, src
, size
))
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
);
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
,
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
));
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)
238 if (!partitioned_file_write_region(param
.image_file
, &buffer
))
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
)
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
);
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
)
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
))
285 return update_anchor(mhc
, fmap_hash
.raw
);
288 static bool verification_exclude(enum cbfs_type type
)
291 case CBFS_TYPE_BOOTBLOCK
:
292 case CBFS_TYPE_CBFSHEADER
:
293 case CBFS_TYPE_INTEL_FIT
:
294 case CBFS_TYPE_AMDFW
:
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. */
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");
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
);
344 mmap_window_table_size
++;
349 static int decode_mmap_arg(char *arg
)
355 unsigned long int array
[3];
357 unsigned long int flash_base
;
358 unsigned long int mmap_base
;
359 unsigned long int mmap_size
;
363 char *substring
= strtok(arg
, ":");
364 for (size_t i
= 0; i
< ARRAY_SIZE(mmap_args
.array
); i
++) {
366 ERROR("Invalid mmap arguments '%s'.\n",
370 mmap_args
.array
[i
] = strtol(substring
, &suffix
, 0);
371 if (suffix
&& *suffix
) {
372 ERROR("Invalid mmap arguments '%s'.\n",
376 substring
= strtok(NULL
, ":");
379 if (substring
!= NULL
) {
380 ERROR("Invalid argument, too many substrings '%s'.\n",
386 if (add_mmap_window(mmap_args
.flash_base
, mmap_args
.mmap_base
, mmap_args
.mmap_size
))
392 #define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
393 #define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
395 static bool create_mmap_windows(void)
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
))
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
));
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
));
449 static unsigned int convert_address(const struct region
*to
, const struct region
*from
,
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
{
465 static int find_mmap_window(enum mmap_addr_type addr_type
, unsigned int addr
)
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
;
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
)
485 static unsigned int convert_host_to_flash(const struct buffer
*region
, unsigned int addr
)
488 const struct region
*to
, *from
;
490 idx
= find_mmap_window(HOST_SPACE_ADDR
, addr
);
492 ERROR("Host address(%x) not in any mmap window!\n", addr
);
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
)
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
516 addr
+= region
->offset
;
517 idx
= find_mmap_window(FLASH_SPACE_ADDR
, addr
);
520 ERROR("SPI flash address(%x) not in any mmap window!\n", addr
);
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
)
534 assert(create_mmap_windows());
536 if (IS_HOST_SPACE_ADDRESS(addr
))
537 return convert_host_to_flash(region
, addr
);
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
)
553 assert(param
.image_region
);
554 size
= param
.image_region
->size
;
558 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size
, offset
);
562 *region_offset
= size
- offset
;
566 static int do_cbfs_locate(uint32_t *cbfs_addr
, size_t data_size
)
568 uint32_t metadata_size
= 0;
571 ERROR("You need to specify -n/--name.\n");
575 struct cbfs_image image
;
576 if (cbfs_image_from_buffer(&image
, param
.image_region
,
580 if (cbfs_get_entry(&image
, param
.name
))
581 WARN("'%s' already in CBFS.\n", param
.name
);
584 ERROR("File '%s' is empty?\n", param
.name
);
588 /* Compute required page size */
589 if (param
.force_pow2_pagesize
) {
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
) {
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
);
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
);
623 *cbfs_addr
= address
;
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
,
633 uint32_t headeroffset
) {
634 struct cbfs_image image
;
635 struct cbfs_file
*header
= NULL
;
636 struct buffer buffer
;
640 ERROR("You need to specify -n/--name.\n");
644 if (buffer_create(&buffer
, 8, name
) != 0)
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");
655 if (cbfs_get_entry(&image
, name
)) {
656 ERROR("'%s' already in ROM image.\n", name
);
660 header
= cbfs_create_file_header(CBFS_TYPE_RAW
,
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
);
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
);
678 ret
= maybe_update_metadata_hash(&image
);
682 buffer_delete(&buffer
);
686 static int is_valid_topswap(void)
688 switch (param
.topswap_size
) {
696 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
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");
717 ERROR("Bootblock not in ROM image?!?\n");
722 * Check if the existing topswap boundary matches with
725 if (param
.topswap_size
!= be32toh(entry
->len
)/2) {
726 ERROR("Top swap boundary does not match\n");
730 ts_h_loc
-= param
.topswap_size
;
731 fill_header_offset(ts_h_loc
, header_offset
);
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
;
747 if (cbfs_image_from_buffer(&image
, param
.image_region
,
748 param
.headeroffset
)) {
749 ERROR("Selected image region is not a CBFS.\n");
753 if (cbfs_get_entry(&image
, name
)) {
754 ERROR("'%s' already in ROM image.\n", name
);
758 if (buffer_create(&buffer
, sizeof(struct cbfs_header
), name
) != 0)
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
);
790 if (cbfs_add_entry(&image
, &buffer
, 0, header
, 0) != 0) {
791 ERROR("Failed to add cbfs master header into ROM image.\n");
795 struct cbfs_file
*entry
;
796 if ((entry
= cbfs_get_entry(&image
, name
)) == NULL
) {
797 ERROR("'%s' not in ROM image?!?\n", name
);
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
,
818 ret
= maybe_update_metadata_hash(&image
);
822 buffer_delete(&buffer
);
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
,
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
,
846 buffer_splice(&bb1
, &new_bootblock
, param
.topswap_size
- bb_buf_size
,
848 buffer_splice(&bb2
, &new_bootblock
,
849 buffer_size(&new_bootblock
) - 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
,
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");
894 if (param
.stage_xip
&& param
.compression
!= CBFS_COMPRESS_NONE
) {
895 ERROR("Cannot specify compression for XIP.\n");
900 ERROR("You need to specify -f/--filename.\n");
905 ERROR("You need to specify -n/--name.\n");
909 if (param
.type
== 0) {
910 ERROR("You need to specify a valid -t/--type.\n");
914 struct cbfs_image image
;
915 if (cbfs_image_from_buffer(&image
, param
.image_region
, headeroffset
))
918 if (cbfs_get_entry(&image
, name
)) {
919 ERROR("'%s' already in ROM image.\n", name
);
923 struct buffer buffer
;
924 if (buffer_from_file(&buffer
, filename
) != 0) {
925 ERROR("Could not load file '%s'.\n", filename
);
929 struct cbfs_file
*header
=
930 cbfs_create_file_header(param
.type
, buffer
.size
, name
);
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
;
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
));
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
))
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
);
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
)))
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
);
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
));
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
));
1001 attrs
->alignment
= htobe32(param
.alignment
);
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
));
1012 /* For Intel TXT minimum align is 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
,
1029 if (cbfs_add_entry(&image
, &buffer
, offset
, header
, len_align
) != 0) {
1030 ERROR("Failed to add '%s' into ROM image.\n", filename
);
1035 buffer_delete(&buffer
);
1037 return maybe_update_metadata_hash(&image
) || maybe_update_fmap_hash();
1041 buffer_delete(&buffer
);
1045 static int cbfstool_convert_raw(struct buffer
*buffer
,
1046 unused
uint32_t *offset
, struct cbfs_file
*header
)
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
);
1060 memcpy(compressed
, buffer
->data
+ 8, compressed_size
);
1062 if (param
.compression
== CBFS_COMPRESS_NONE
)
1065 compress
= compression_function(param
.compression
);
1068 compressed
= calloc(buffer
->size
, 1);
1072 if (compress(buffer
->data
, buffer
->size
,
1073 compressed
, &compressed_size
)) {
1074 WARN("Compression failed - disabled\n");
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
) {
1089 attrs
->compression
= htobe32(param
.compression
);
1090 attrs
->decompressed_size
= htobe32(decompressed_size
);
1093 buffer
->data
= compressed
;
1094 buffer
->size
= compressed_size
;
1097 header
->len
= htobe32(buffer
->size
);
1101 static int cbfstool_convert_fsp(struct buffer
*buffer
,
1102 uint32_t *offset
, struct cbfs_file
*header
)
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
)))
1132 assert(!IS_HOST_SPACE_ADDRESS(*offset
));
1133 address
= convert_addr_space(param
.image_region
, *offset
);
1135 if (param
.baseaddress_assigned
== 0) {
1136 INFO("Honoring pre-linked FSP module, no relocation.\n");
1137 return cbfstool_convert_raw(buffer
, offset
, header
);
1139 address
= param
.baseaddress
;
1144 /* Create a copy of the buffer to attempt relocation. */
1145 if (buffer_create(&fsp
, buffer_size(buffer
), "fsp"))
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
))
1153 buffer_delete(buffer
);
1154 buffer_clone(buffer
, &fsp
);
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
;
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");
1182 param
.alignment
= MAX(alignment
, param
.alignment
);
1184 if (do_cbfs_locate(offset
, data_size
)) {
1185 ERROR("Could not find location for stage.\n");
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
));
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
);
1202 ret
= parse_elf_to_stage(buffer
, &output
, param
.ignore_sections
,
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
);
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");
1228 memcpy(start
, buffer_get(&output
), compressed_size
);
1229 ret
= ulz4fn(start
, compressed_size
, compare_buffer
, memlen
);
1231 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1232 free(compare_buffer
);
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
);
1240 free(compare_buffer
);
1243 buffer_delete(buffer
);
1244 buffer_clone(buffer
, &output
);
1248 buffer_delete(&output
);
1252 static int cbfstool_convert_mkpayload(struct buffer
*buffer
,
1253 unused
uint32_t *offset
, struct cbfs_file
*header
)
1255 struct buffer output
;
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 */
1262 ret
= parse_fit_to_payload(buffer
, &output
, param
.compression
);
1264 header
->type
= htobe32(CBFS_TYPE_FIT_PAYLOAD
);
1267 /* If it's not an FIT, see if it's a UEFI FV */
1269 ret
= parse_fv_to_payload(buffer
, &output
, param
.compression
);
1271 /* If it's neither ELF nor UEFI Fv, try bzImage */
1273 ret
= parse_bzImage_to_payload(buffer
, &output
,
1274 param
.initrd
, param
.cmdline
, param
.compression
);
1276 /* Not a supported payload type */
1278 ERROR("Not a supported payload type (ELF / FV).\n");
1279 buffer_delete(buffer
);
1283 buffer_delete(buffer
);
1284 // Direct assign, no dupe.
1285 memcpy(buffer
, &output
, sizeof(*buffer
));
1286 header
->len
= htobe32(output
.size
);
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
,
1297 param
.compression
) != 0) {
1300 buffer_delete(buffer
);
1301 // Direct assign, no dupe.
1302 memcpy(buffer
, &output
, sizeof(*buffer
));
1303 header
->len
= htobe32(output
.size
);
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");
1316 } else if (param
.stage_xip
) {
1317 ERROR("cbfstool add supports xip only for FSP component type\n");
1321 return cbfs_add_component(param
.filename
,
1327 static int cbfs_add_stage(void)
1329 if (param
.stage_xip
&& param
.baseaddress_assigned
) {
1330 ERROR("Cannot specify base address for XIP.\n");
1333 param
.type
= CBFS_TYPE_STAGE
;
1335 return cbfs_add_component(param
.filename
,
1338 cbfstool_convert_mkstage
);
1341 static int cbfs_add_payload(void)
1343 param
.type
= CBFS_TYPE_SELF
;
1344 return cbfs_add_component(param
.filename
,
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");
1357 if (param
.entrypoint
== 0) {
1358 ERROR("You need to specify a valid "
1359 "-e/--entry-point.\n");
1362 param
.type
= CBFS_TYPE_SELF
;
1363 return cbfs_add_component(param
.filename
,
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");
1375 return cbfs_add_integer_component(param
.name
,
1378 param
.headeroffset
);
1381 static int cbfs_remove(void)
1384 ERROR("You need to specify -n/--name.\n");
1388 struct cbfs_image image
;
1389 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1390 param
.headeroffset
))
1393 if (cbfs_remove_entry(&image
, param
.name
) != 0) {
1394 ERROR("Removing file '%s' failed.\n",
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
);
1409 if (param
.arch
!= CBFS_ARCHITECTURE_UNKNOWN
|| param
.size
||
1410 param
.baseaddress_assigned
||
1411 param
.headeroffset_assigned
||
1412 param
.cbfsoffset_assigned
||
1414 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
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");
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)
1431 } else if (buffer_from_file(&bootblock
, param
.bootblock
)) {
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",
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
);
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");
1469 param
.cbfsoffset
= align_up(param
.headeroffset
+
1470 sizeof(struct cbfs_header
),
1472 DEBUG("CBFS entries start beind master header (%#x).\n",
1477 int ret
= cbfs_legacy_image_create(&image
,
1484 buffer_delete(&bootblock
);
1488 static int cbfs_layout(void)
1490 const struct fmap
*fmap
= partitioned_file_get_fmap(param
.image_file
);
1492 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1496 printf("This image contains the following sections that can be %s with this tool:\n",
1497 param
.show_immutable
? "accessed" : "manipulated");
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
)
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.
1519 for (lookahead
= 1; i
+ lookahead
< fmap
->nareas
;
1521 const struct fmap_area
*consecutive
=
1522 fmap
->areas
+ i
+ lookahead
;
1523 if (consecutive
->offset
!= current
->offset
||
1524 consecutive
->size
!= current
->size
)
1526 printf(", '%s'", consecutive
->name
);
1529 fputs(" are aliases for the same region", stdout
);
1531 const char *qualifier
= "";
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
,
1545 if (param
.show_immutable
) {
1546 puts("It is at least possible to perform the read action on every section listed above.");
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.");
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
);
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
))
1578 if (param
.machine_parseable
) {
1580 printf("[FMAP REGION]\t%s\n", param
.region_name
);
1581 cbfs_print_parseable_directory(&image
);
1583 printf("FMAP REGION: %s\n", param
.region_name
);
1584 cbfs_print_directory(&image
);
1588 const char *verification_state
= "fully valid";
1589 struct mh_cache
*mhc
= get_mh_cache();
1590 if (mhc
->cbfs_hash
.algo
== VB2_HASH_INVALID
)
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
))) {
1610 verification_state
= "invalid metadata hash";
1614 printf("[CBFS VERIFICATION (%s)]\t%s\n", param
.region_name
, verification_state
);
1621 static int cbfs_extract(void)
1623 if (!param
.filename
) {
1624 ERROR("You need to specify -f/--filename.\n");
1629 ERROR("You need to specify -n/--name.\n");
1633 struct cbfs_image image
;
1634 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1635 param
.headeroffset
))
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");
1648 if (!partitioned_file_is_partitioned(param
.image_file
)) {
1649 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
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",
1659 struct buffer new_content
;
1660 if (buffer_from_file(&new_content
, param
.filename
))
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",
1667 buffer_delete(&new_content
);
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",
1674 buffer_delete(&new_content
);
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
);
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
);
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
);
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
);
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
,
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");
1726 if (!partitioned_file_is_partitioned(param
.image_file
)) {
1727 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
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");
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
);
1751 if (cbfs_image_from_buffer(&src_image
, &src_buf
, param
.headeroffset
))
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
))
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
);
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
);
1793 int result
= cbfs_truncate_space(param
.image_region
, &size
);
1795 printf("0x%x\n", size
);
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
,
1803 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload
,
1805 {"add-stage", "a:H:r:f:n:t:c:b:P:QS:p:yvA:gh?", cbfs_add_stage
,
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},
1823 /* begin after ASCII characters */
1824 LONGOPT_START
= 256,
1825 LONGOPT_IBB
= LONGOPT_START
,
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
},
1872 static int get_region_offset(long long int offset
, uint32_t *region_offset
)
1874 /* If offset is not negative, no transformation required. */
1876 *region_offset
= offset
;
1880 /* Calculate offset from start of region. */
1881 return convert_region_offset(-offset
, region_offset
);
1884 static int calculate_region_offsets(void)
1888 if (param
.baseaddress_assigned
)
1889 ret
|= get_region_offset(param
.baseaddress_input
, ¶m
.baseaddress
);
1890 if (param
.headeroffset_assigned
)
1891 ret
|= get_region_offset(param
.headeroffset_input
, ¶m
.headeroffset
);
1892 if (param
.cbfsoffset_assigned
)
1893 ret
|= get_region_offset(param
.cbfsoffset_input
, ¶m
.cbfsoffset
);
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",
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");
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",
1921 ERROR("The image will be left unmodified.\n");
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",
1931 ERROR("The image will be left unmodified.\n");
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())
1944 if (command
.function()) {
1945 if (partitioned_file_is_partitioned(param
.image_file
)) {
1946 ERROR("Failed while operating on '%s' region!\n",
1948 ERROR("The image will be left unmodified.\n");
1956 static void usage(char *name
)
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"
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] "
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"
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"
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();
2039 print_supported_filetypes();
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
))
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
)
2077 int main(int argc
, char **argv
)
2087 char *image_name
= argv
[1];
2088 char *cmd
= argv
[2];
2091 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
2092 if (strcmp(cmd
, commands
[i
].name
) != 0)
2096 char *suffix
= NULL
;
2097 int option_index
= 0;
2099 c
= getopt_long(argc
, argv
, commands
[i
].optstring
,
2100 long_options
, &option_index
);
2102 if (optind
< argc
) {
2103 ERROR("%s: excessive argument -- '%s'"
2104 "\n", argv
[0], argv
[optind
]);
2110 /* Filter out illegal long options */
2111 if (!valid_opt(i
, c
)) {
2112 ERROR("%s: invalid option -- '%d'\n",
2119 param
.name
= optarg
;
2122 if (intfiletype(optarg
) != ((uint64_t) - 1))
2123 param
.type
= intfiletype(optarg
);
2125 param
.type
= strtoul(optarg
, NULL
, 0);
2126 if (param
.type
== 0)
2127 WARN("Unknown type '%s' ignored\n",
2131 if (strcmp(optarg
, "precompression") == 0) {
2132 param
.precompression
= 1;
2135 int algo
= cbfs_parse_comp_algo(optarg
);
2137 param
.compression
= algo
;
2139 WARN("Unknown compression '%s' ignored.\n",
2144 if (!vb2_lookup_hash_alg(optarg
, ¶m
.hash
)) {
2145 ERROR("Unknown hash algorithm '%s'.\n",
2152 param
.fmap
= optarg
;
2155 param
.region_name
= optarg
;
2158 param
.source_region
= optarg
;
2161 param
.baseaddress_input
= strtoll(optarg
, &suffix
, 0);
2162 if (!*optarg
|| (suffix
&& *suffix
)) {
2163 ERROR("Invalid base address '%s'.\n",
2167 // baseaddress may be zero on non-x86, so we
2168 // need an explicit "baseaddress_assigned".
2169 param
.baseaddress_assigned
= 1;
2172 param
.loadaddress
= strtoull(optarg
, &suffix
, 0);
2173 if (!*optarg
|| (suffix
&& *suffix
)) {
2174 ERROR("Invalid load address '%s'.\n",
2180 param
.entrypoint
= strtoull(optarg
, &suffix
, 0);
2181 if (!*optarg
|| (suffix
&& *suffix
)) {
2182 ERROR("Invalid entry point '%s'.\n",
2188 param
.size
= strtoul(optarg
, &suffix
, 0);
2190 ERROR("Empty size specified.\n");
2193 switch (tolower((int)suffix
[0])) {
2198 param
.size
*= 1024 * 1024;
2203 ERROR("Invalid suffix for size '%s'.\n",
2209 param
.bootblock
= optarg
;
2212 param
.headeroffset_input
= strtoll(optarg
, &suffix
, 0);
2213 if (!*optarg
|| (suffix
&& *suffix
)) {
2214 ERROR("Invalid header offset '%s'.\n",
2218 param
.headeroffset_assigned
= 1;
2221 param
.alignment
= strtoul(optarg
, &suffix
, 0);
2222 if (!*optarg
|| (suffix
&& *suffix
)) {
2223 ERROR("Invalid alignment '%s'.\n",
2229 param
.padding
= strtoul(optarg
, &suffix
, 0);
2230 if (!*optarg
|| (suffix
&& *suffix
)) {
2231 ERROR("Invalid pad size '%s'.\n",
2237 param
.force_pow2_pagesize
= 1;
2240 param
.cbfsoffset_input
= strtoll(optarg
, &suffix
, 0);
2241 if (!*optarg
|| (suffix
&& *suffix
)) {
2242 ERROR("Invalid cbfs offset '%s'.\n",
2246 param
.cbfsoffset_assigned
= 1;
2249 param
.filename
= optarg
;
2255 param
.u64val
= strtoull(optarg
, &suffix
, 0);
2256 param
.u64val_assigned
= 1;
2257 if (!*optarg
|| (suffix
&& *suffix
)) {
2258 ERROR("Invalid int parameter '%s'.\n",
2264 param
.fill_partial_upward
= true;
2267 param
.fill_partial_downward
= true;
2270 param
.show_immutable
= true;
2273 param
.topswap_size
= strtol(optarg
, NULL
, 0);
2274 if (!is_valid_topswap())
2278 param
.ucode_region
= optarg
;
2284 param
.arch
= string_to_arch(optarg
);
2287 param
.initrd
= optarg
;
2290 param
.cmdline
= optarg
;
2293 param
.ignore_sections
= optarg
;
2296 param
.stage_xip
= true;
2299 param
.autogen_attr
= true;
2302 param
.machine_parseable
= true;
2305 param
.unprocessed
= true;
2311 if (decode_mmap_arg(optarg
))
2323 if (commands
[i
].function
== cbfs_create
) {
2325 struct buffer flashmap
;
2326 if (buffer_from_file(&flashmap
, param
.fmap
))
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
);
2335 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
2339 bool write_access
= commands
[i
].modifies_region
;
2342 partitioned_file_reopen(image_name
,
2345 if (!param
.image_file
)
2348 unsigned num_regions
= 1;
2349 for (const char *list
= strchr(param
.region_name
, ','); list
;
2350 list
= strchr(list
+ 1, ','))
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
);
2376 if (strcmp(param
.region_name
, SECTION_NAME_PRIMARY_CBFS
)
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
);
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
);
2397 if (commands
[i
].modifies_region
) {
2398 assert(param
.image_file
);
2399 for (unsigned region
= 0; region
< num_regions
;
2402 if (!partitioned_file_write_region(
2404 image_regions
+ region
)) {
2405 partitioned_file_close(
2412 partitioned_file_close(param
.image_file
);
2416 ERROR("Unknown command '%s'.\n", cmd
);