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 void add_mmap_window(size_t flash_offset
, size_t host_offset
,
330 if (mmap_window_table_size
>= MMAP_MAX_WINDOWS
) {
331 ERROR("Too many memory map windows\n");
335 mmap_window_table
[mmap_window_table_size
].flash_space
.offset
= flash_offset
;
336 mmap_window_table
[mmap_window_table_size
].host_space
.offset
= host_offset
;
337 mmap_window_table
[mmap_window_table_size
].flash_space
.size
= window_size
;
338 mmap_window_table
[mmap_window_table_size
].host_space
.size
= window_size
;
339 mmap_window_table_size
++;
343 static int decode_mmap_arg(char *arg
)
349 unsigned long int array
[3];
351 unsigned long int flash_base
;
352 unsigned long int mmap_base
;
353 unsigned long int mmap_size
;
357 char *substring
= strtok(arg
, ":");
358 for (size_t i
= 0; i
< ARRAY_SIZE(mmap_args
.array
); i
++) {
360 ERROR("Invalid mmap arguments '%s'.\n",
364 mmap_args
.array
[i
] = strtol(substring
, &suffix
, 0);
365 if (suffix
&& *suffix
) {
366 ERROR("Invalid mmap arguments '%s'.\n",
370 substring
= strtok(NULL
, ":");
373 if (substring
!= NULL
) {
374 ERROR("Invalid argument, too many substrings '%s'.\n",
380 add_mmap_window(mmap_args
.flash_base
, mmap_args
.mmap_base
, mmap_args
.mmap_size
);
384 #define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
385 #define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
387 static bool create_mmap_windows(void)
394 // No memory map provided, use a default one
395 if (mmap_window_table_size
== 0) {
396 const size_t image_size
= partitioned_file_total_size(param
.image_file
);
397 printf("Image SIZE %zu\n", image_size
);
398 const size_t std_window_size
= MIN(DEFAULT_DECODE_WINDOW_MAX_SIZE
, image_size
);
399 const size_t std_window_flash_offset
= image_size
- std_window_size
;
402 * Default decode window lives just below 4G boundary in host space and maps up to a
403 * maximum of 16MiB. If the window is smaller than 16MiB, the SPI flash window is mapped
404 * at the top of the host window just below 4G.
406 add_mmap_window(std_window_flash_offset
, DEFAULT_DECODE_WINDOW_TOP
- std_window_size
, std_window_size
);
409 * Check provided memory map
411 for (int i
= 0; i
< mmap_window_table_size
; i
++) {
412 for (int j
= i
+ 1; j
< mmap_window_table_size
; j
++) {
413 if (region_overlap(&mmap_window_table
[i
].flash_space
,
414 &mmap_window_table
[j
].flash_space
)) {
415 ERROR("Flash space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
416 region_offset(&mmap_window_table
[i
].flash_space
),
417 region_end(&mmap_window_table
[i
].flash_space
),
418 region_offset(&mmap_window_table
[j
].flash_space
),
419 region_end(&mmap_window_table
[j
].flash_space
));
423 if (region_overlap(&mmap_window_table
[i
].host_space
,
424 &mmap_window_table
[j
].host_space
)) {
425 ERROR("Host space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
426 region_offset(&mmap_window_table
[i
].flash_space
),
427 region_end(&mmap_window_table
[i
].flash_space
),
428 region_offset(&mmap_window_table
[j
].flash_space
),
429 region_end(&mmap_window_table
[j
].flash_space
));
440 static unsigned int convert_address(const struct region
*to
, const struct region
*from
,
444 * Calculate the offset in the "from" region and use that offset to calculate
445 * corresponding address in the "to" region.
447 size_t offset
= addr
- region_offset(from
);
448 return region_offset(to
) + offset
;
451 enum mmap_addr_type
{
456 static int find_mmap_window(enum mmap_addr_type addr_type
, unsigned int addr
)
460 for (i
= 0; i
< ARRAY_SIZE(mmap_window_table
); i
++) {
461 const struct region
*reg
;
463 if (addr_type
== HOST_SPACE_ADDR
)
464 reg
= &mmap_window_table
[i
].host_space
;
466 reg
= &mmap_window_table
[i
].flash_space
;
468 if (region_offset(reg
) <= addr
&&
469 ((uint64_t)region_offset(reg
) + (uint64_t)region_sz(reg
) - 1) >= addr
)
476 static unsigned int convert_host_to_flash(const struct buffer
*region
, unsigned int addr
)
479 const struct region
*to
, *from
;
481 idx
= find_mmap_window(HOST_SPACE_ADDR
, addr
);
483 ERROR("Host address(%x) not in any mmap window!\n", addr
);
487 to
= &mmap_window_table
[idx
].flash_space
;
488 from
= &mmap_window_table
[idx
].host_space
;
490 /* region->offset is subtracted because caller expects offset in the given region. */
491 return convert_address(to
, from
, addr
) - region
->offset
;
494 static unsigned int convert_flash_to_host(const struct buffer
*region
, unsigned int addr
)
497 const struct region
*to
, *from
;
500 * region->offset is added because caller provides offset in the given region. This is
501 * converted to an absolute address in the SPI flash space. This is done before the
502 * conversion as opposed to after in convert_host_to_flash() above because the address
503 * is actually an offset within the region. So, it needs to be converted into an
504 * absolute address in the SPI flash space before converting into an address in host
507 addr
+= region
->offset
;
508 idx
= find_mmap_window(FLASH_SPACE_ADDR
, addr
);
511 ERROR("SPI flash address(%x) not in any mmap window!\n", addr
);
515 to
= &mmap_window_table
[idx
].host_space
;
516 from
= &mmap_window_table
[idx
].flash_space
;
518 return convert_address(to
, from
, addr
);
521 static unsigned int convert_addr_space(const struct buffer
*region
, unsigned int addr
)
525 assert(create_mmap_windows());
527 if (IS_HOST_SPACE_ADDRESS(addr
))
528 return convert_host_to_flash(region
, addr
);
530 return convert_flash_to_host(region
, addr
);
534 * This function takes offset value which represents the offset from one end of the region and
535 * converts it to offset from the other end of the region. offset is expected to be positive.
537 static int convert_region_offset(unsigned int offset
, uint32_t *region_offset
)
544 assert(param
.image_region
);
545 size
= param
.image_region
->size
;
549 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size
, offset
);
553 *region_offset
= size
- offset
;
557 static int do_cbfs_locate(uint32_t *cbfs_addr
, size_t data_size
)
559 uint32_t metadata_size
= 0;
562 ERROR("You need to specify -n/--name.\n");
566 struct cbfs_image image
;
567 if (cbfs_image_from_buffer(&image
, param
.image_region
,
571 if (cbfs_get_entry(&image
, param
.name
))
572 WARN("'%s' already in CBFS.\n", param
.name
);
575 ERROR("File '%s' is empty?\n", param
.name
);
579 /* Compute required page size */
580 if (param
.force_pow2_pagesize
) {
582 while (param
.pagesize
< data_size
)
583 param
.pagesize
<<= 1;
584 DEBUG("Page size is %d (0x%x)\n", param
.pagesize
, param
.pagesize
);
587 /* Include cbfs_file size along with space for with name. */
588 metadata_size
+= cbfs_calculate_file_header_size(param
.name
);
589 /* Adjust metadata_size if additional attributes were added */
590 if (param
.autogen_attr
) {
592 metadata_size
+= sizeof(struct cbfs_file_attr_align
);
593 if (param
.baseaddress_assigned
|| param
.stage_xip
)
594 metadata_size
+= sizeof(struct cbfs_file_attr_position
);
596 if (param
.precompression
|| param
.compression
!= CBFS_COMPRESS_NONE
)
597 metadata_size
+= sizeof(struct cbfs_file_attr_compression
);
598 if (param
.type
== CBFS_TYPE_STAGE
)
599 metadata_size
+= sizeof(struct cbfs_file_attr_stageheader
);
601 /* Take care of the hash attribute if it is used */
602 if (param
.hash
!= VB2_HASH_INVALID
)
603 metadata_size
+= cbfs_file_attr_hash_size(param
.hash
);
605 int32_t address
= cbfs_locate_entry(&image
, data_size
, param
.pagesize
,
606 param
.alignment
, metadata_size
);
609 ERROR("'%s'(%u + %zu) can't fit in CBFS for page-size %#x, align %#x.\n",
610 param
.name
, metadata_size
, data_size
, param
.pagesize
, param
.alignment
);
614 *cbfs_addr
= address
;
618 typedef int (*convert_buffer_t
)(struct buffer
*buffer
, uint32_t *offset
,
619 struct cbfs_file
*header
);
621 static int cbfs_add_integer_component(const char *name
,
624 uint32_t headeroffset
) {
625 struct cbfs_image image
;
626 struct cbfs_file
*header
= NULL
;
627 struct buffer buffer
;
631 ERROR("You need to specify -n/--name.\n");
635 if (buffer_create(&buffer
, 8, name
) != 0)
638 for (i
= 0; i
< 8; i
++)
639 buffer
.data
[i
] = (u64val
>> i
*8) & 0xff;
641 if (cbfs_image_from_buffer(&image
, param
.image_region
, headeroffset
)) {
642 ERROR("Selected image region is not a CBFS.\n");
646 if (cbfs_get_entry(&image
, name
)) {
647 ERROR("'%s' already in ROM image.\n", name
);
651 header
= cbfs_create_file_header(CBFS_TYPE_RAW
,
656 enum vb2_hash_algorithm algo
= get_mh_cache()->cbfs_hash
.algo
;
657 if (algo
!= VB2_HASH_INVALID
)
658 if (cbfs_add_file_hash(header
, &buffer
, algo
)) {
659 ERROR("couldn't add hash for '%s'\n", name
);
663 if (cbfs_add_entry(&image
, &buffer
, offset
, header
, 0) != 0) {
664 ERROR("Failed to add %llu into ROM image as '%s'.\n",
665 (long long unsigned)u64val
, name
);
669 ret
= maybe_update_metadata_hash(&image
);
673 buffer_delete(&buffer
);
677 static int is_valid_topswap(void)
679 switch (param
.topswap_size
) {
687 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
694 static void fill_header_offset(void *location
, uint32_t offset
)
696 // TODO: When we have a BE target, we'll need to store this as BE
697 write_le32(location
, offset
);
700 static int update_master_header_loc_topswap(struct cbfs_image
*image
,
701 void *h_loc
, uint32_t header_offset
)
703 struct cbfs_file
*entry
;
704 void *ts_h_loc
= h_loc
;
706 entry
= cbfs_get_entry(image
, "bootblock");
708 ERROR("Bootblock not in ROM image?!?\n");
713 * Check if the existing topswap boundary matches with
716 if (param
.topswap_size
!= be32toh(entry
->len
)/2) {
717 ERROR("Top swap boundary does not match\n");
721 ts_h_loc
-= param
.topswap_size
;
722 fill_header_offset(ts_h_loc
, header_offset
);
727 static int cbfs_add_master_header(void)
729 const char * const name
= "cbfs master header";
730 struct cbfs_image image
;
731 struct cbfs_file
*header
= NULL
;
732 struct buffer buffer
;
738 if (cbfs_image_from_buffer(&image
, param
.image_region
,
739 param
.headeroffset
)) {
740 ERROR("Selected image region is not a CBFS.\n");
744 if (cbfs_get_entry(&image
, name
)) {
745 ERROR("'%s' already in ROM image.\n", name
);
749 if (buffer_create(&buffer
, sizeof(struct cbfs_header
), name
) != 0)
752 struct cbfs_header
*h
= (struct cbfs_header
*)buffer
.data
;
753 h
->magic
= htobe32(CBFS_HEADER_MAGIC
);
754 h
->version
= htobe32(CBFS_HEADER_VERSION
);
755 /* The 4 bytes are left out for two reasons:
756 * 1. the cbfs master header pointer resides there
757 * 2. some cbfs implementations assume that an image that resides
758 * below 4GB has a bootblock and get confused when the end of the
759 * image is at 4GB == 0.
761 h
->bootblocksize
= htobe32(4);
762 h
->align
= htobe32(CBFS_ALIGNMENT
);
763 /* The offset and romsize fields within the master header are absolute
764 * values within the boot media. As such, romsize needs to relfect
765 * the end 'offset' for a CBFS. To achieve that the current buffer
766 * representing the CBFS region's size is added to the offset of
767 * the region within a larger image.
769 offset
= buffer_get(param
.image_region
) -
770 buffer_get_original_backing(param
.image_region
);
771 size
= buffer_size(param
.image_region
);
772 h
->romsize
= htobe32(size
+ offset
);
773 h
->offset
= htobe32(offset
);
774 h
->architecture
= htobe32(CBFS_ARCHITECTURE_UNKNOWN
);
776 /* Never add a hash attribute to the master header. */
777 header
= cbfs_create_file_header(CBFS_TYPE_CBFSHEADER
,
778 buffer_size(&buffer
), name
);
781 if (cbfs_add_entry(&image
, &buffer
, 0, header
, 0) != 0) {
782 ERROR("Failed to add cbfs master header into ROM image.\n");
786 struct cbfs_file
*entry
;
787 if ((entry
= cbfs_get_entry(&image
, name
)) == NULL
) {
788 ERROR("'%s' not in ROM image?!?\n", name
);
792 uint32_t header_offset
= CBFS_SUBHEADER(entry
) -
793 buffer_get(&image
.buffer
);
794 header_offset
= -(buffer_size(&image
.buffer
) - header_offset
);
796 h_loc
= (void *)(buffer_get(&image
.buffer
) +
797 buffer_size(&image
.buffer
) - 4);
798 fill_header_offset(h_loc
, header_offset
);
800 * If top swap present, update the header
801 * location in secondary bootblock
803 if (param
.topswap_size
) {
804 if (update_master_header_loc_topswap(&image
, h_loc
,
809 ret
= maybe_update_metadata_hash(&image
);
813 buffer_delete(&buffer
);
817 static int add_topswap_bootblock(struct buffer
*buffer
, uint32_t *offset
)
819 size_t bb_buf_size
= buffer_size(buffer
);
821 if (bb_buf_size
> param
.topswap_size
) {
822 ERROR("Bootblock bigger than the topswap boundary\n");
823 ERROR("size = %zd, ts = %d\n", bb_buf_size
,
829 * Allocate topswap_size*2 bytes for bootblock to
830 * accommodate the second bootblock.
832 struct buffer new_bootblock
, bb1
, bb2
;
833 if (buffer_create(&new_bootblock
, 2 * param
.topswap_size
,
837 buffer_splice(&bb1
, &new_bootblock
, param
.topswap_size
- bb_buf_size
,
839 buffer_splice(&bb2
, &new_bootblock
,
840 buffer_size(&new_bootblock
) - bb_buf_size
,
843 /* Copy to first bootblock */
844 memcpy(buffer_get(&bb1
), buffer_get(buffer
), bb_buf_size
);
845 /* Copy to second bootblock */
846 memcpy(buffer_get(&bb2
), buffer_get(buffer
), bb_buf_size
);
848 buffer_delete(buffer
);
849 buffer_clone(buffer
, &new_bootblock
);
851 /* Update the location (offset) of bootblock in the region */
852 return convert_region_offset(buffer_size(buffer
), offset
);
855 static int cbfs_add_component(const char *filename
,
857 uint32_t headeroffset
,
858 convert_buffer_t convert
)
861 * The steps used to determine the final placement offset in CBFS, in order:
863 * 1. If --base-address was passed, that value is used. If it was passed in the host
864 * address space, convert it to flash address space. (After that, |*offset| is always
865 * in the flash address space.)
867 * 2. The convert() function may write a location back to |offset|, usually by calling
868 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
869 * the CBFS file in its final form (after any compression and conversion).
871 * 3. If --align was passed and the offset is still undecided at this point,
872 * do_cbfs_locate() is called to find an appropriately aligned location.
874 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
875 * location that fits.
877 uint32_t offset
= param
.baseaddress_assigned
? param
.baseaddress
: 0;
878 size_t len_align
= 0;
880 if (param
.alignment
&& param
.baseaddress_assigned
) {
881 ERROR("Cannot specify both alignment and base address\n");
885 if (param
.stage_xip
&& param
.compression
!= CBFS_COMPRESS_NONE
) {
886 ERROR("Cannot specify compression for XIP.\n");
891 ERROR("You need to specify -f/--filename.\n");
896 ERROR("You need to specify -n/--name.\n");
900 if (param
.type
== 0) {
901 ERROR("You need to specify a valid -t/--type.\n");
905 struct cbfs_image image
;
906 if (cbfs_image_from_buffer(&image
, param
.image_region
, headeroffset
))
909 if (cbfs_get_entry(&image
, name
)) {
910 ERROR("'%s' already in ROM image.\n", name
);
914 struct buffer buffer
;
915 if (buffer_from_file(&buffer
, filename
) != 0) {
916 ERROR("Could not load file '%s'.\n", filename
);
920 struct cbfs_file
*header
=
921 cbfs_create_file_header(param
.type
, buffer
.size
, name
);
925 /* Bootblock and CBFS header should never have file hashes. When adding
926 the bootblock it is important that we *don't* look up the metadata
927 hash yet (before it is added) or we'll cache an outdated result. */
928 if (!verification_exclude(param
.type
)) {
929 enum vb2_hash_algorithm mh_algo
= get_mh_cache()->cbfs_hash
.algo
;
930 if (mh_algo
!= VB2_HASH_INVALID
&& param
.hash
!= mh_algo
) {
931 if (param
.hash
== VB2_HASH_INVALID
) {
932 param
.hash
= mh_algo
;
934 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
935 vb2_get_hash_algorithm_name(param
.hash
),
936 vb2_get_hash_algorithm_name(mh_algo
));
943 * Check if Intel CPU topswap is specified this will require a
944 * second bootblock to be added.
946 if (param
.type
== CBFS_TYPE_BOOTBLOCK
&& param
.topswap_size
)
947 if (add_topswap_bootblock(&buffer
, &offset
))
950 /* With --base-address we allow host space addresses -- if so, convert it here. */
951 if (IS_HOST_SPACE_ADDRESS(offset
))
952 offset
= convert_addr_space(param
.image_region
, offset
);
954 if (convert
&& convert(&buffer
, &offset
, header
) != 0) {
955 ERROR("Failed to parse file '%s'.\n", filename
);
959 /* This needs to run after convert() to take compression into account. */
960 if (!offset
&& param
.alignment
)
961 if (do_cbfs_locate(&offset
, buffer_size(&buffer
)))
964 /* This needs to run after convert() to hash the actual final file data. */
965 if (param
.hash
!= VB2_HASH_INVALID
&&
966 cbfs_add_file_hash(header
, &buffer
, param
.hash
) == -1) {
967 ERROR("couldn't add hash for '%s'\n", name
);
971 if (param
.autogen_attr
) {
972 /* Add position attribute if assigned */
973 if (param
.baseaddress_assigned
|| param
.stage_xip
) {
974 struct cbfs_file_attr_position
*attrs
=
975 (struct cbfs_file_attr_position
*)
976 cbfs_add_file_attr(header
,
977 CBFS_FILE_ATTR_TAG_POSITION
,
978 sizeof(struct cbfs_file_attr_position
));
981 attrs
->position
= htobe32(offset
);
983 /* Add alignment attribute if used */
984 if (param
.alignment
) {
985 struct cbfs_file_attr_align
*attrs
=
986 (struct cbfs_file_attr_align
*)
987 cbfs_add_file_attr(header
,
988 CBFS_FILE_ATTR_TAG_ALIGNMENT
,
989 sizeof(struct cbfs_file_attr_align
));
992 attrs
->alignment
= htobe32(param
.alignment
);
997 /* Mark as Initial Boot Block */
998 struct cbfs_file_attribute
*attrs
= cbfs_add_file_attr(header
,
999 CBFS_FILE_ATTR_TAG_IBB
,
1000 sizeof(struct cbfs_file_attribute
));
1003 /* For Intel TXT minimum align is 16 */
1007 if (param
.padding
) {
1008 const uint32_t hs
= sizeof(struct cbfs_file_attribute
);
1009 uint32_t size
= ALIGN_UP(MAX(hs
, param
.padding
),
1010 CBFS_ATTRIBUTE_ALIGN
);
1011 INFO("Padding %d bytes\n", size
);
1012 struct cbfs_file_attribute
*attr
=
1013 (struct cbfs_file_attribute
*)cbfs_add_file_attr(
1014 header
, CBFS_FILE_ATTR_TAG_PADDING
,
1020 if (cbfs_add_entry(&image
, &buffer
, offset
, header
, len_align
) != 0) {
1021 ERROR("Failed to add '%s' into ROM image.\n", filename
);
1026 buffer_delete(&buffer
);
1028 return maybe_update_metadata_hash(&image
) || maybe_update_fmap_hash();
1032 buffer_delete(&buffer
);
1036 static int cbfstool_convert_raw(struct buffer
*buffer
,
1037 unused
uint32_t *offset
, struct cbfs_file
*header
)
1040 int decompressed_size
, compressed_size
;
1041 comp_func_ptr compress
;
1043 decompressed_size
= buffer
->size
;
1044 if (param
.precompression
) {
1045 param
.compression
= read_le32(buffer
->data
);
1046 decompressed_size
= read_le32(buffer
->data
+ sizeof(uint32_t));
1047 compressed_size
= buffer
->size
- 8;
1048 compressed
= malloc(compressed_size
);
1051 memcpy(compressed
, buffer
->data
+ 8, compressed_size
);
1053 if (param
.compression
== CBFS_COMPRESS_NONE
)
1056 compress
= compression_function(param
.compression
);
1059 compressed
= calloc(buffer
->size
, 1);
1063 if (compress(buffer
->data
, buffer
->size
,
1064 compressed
, &compressed_size
)) {
1065 WARN("Compression failed - disabled\n");
1071 struct cbfs_file_attr_compression
*attrs
=
1072 (struct cbfs_file_attr_compression
*)
1073 cbfs_add_file_attr(header
,
1074 CBFS_FILE_ATTR_TAG_COMPRESSION
,
1075 sizeof(struct cbfs_file_attr_compression
));
1076 if (attrs
== NULL
) {
1080 attrs
->compression
= htobe32(param
.compression
);
1081 attrs
->decompressed_size
= htobe32(decompressed_size
);
1084 buffer
->data
= compressed
;
1085 buffer
->size
= compressed_size
;
1088 header
->len
= htobe32(buffer
->size
);
1092 static int cbfstool_convert_fsp(struct buffer
*buffer
,
1093 uint32_t *offset
, struct cbfs_file
*header
)
1099 * There are 4 different cases here:
1101 * 1. --xip and --base-address: we need to place the binary at the given base address
1102 * in the CBFS image and relocate it to that address. *offset was already filled in,
1103 * but we need to convert it to the host address space for relocation.
1105 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1106 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
1107 * This also needs to be converted to the host address space for relocation.
1109 * 3. No --xip but a --base-address: special case where --base-address does not have its
1110 * normal meaning, instead we use it as the relocation target address. We explicitly
1111 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1113 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1114 * not be relocated. Just chain directly to convert_raw() for compression.
1117 if (param
.stage_xip
) {
1118 if (!param
.baseaddress_assigned
) {
1119 param
.alignment
= 4*1024;
1120 if (do_cbfs_locate(offset
, buffer_size(buffer
)))
1123 assert(!IS_HOST_SPACE_ADDRESS(*offset
));
1124 address
= convert_addr_space(param
.image_region
, *offset
);
1126 if (param
.baseaddress_assigned
== 0) {
1127 INFO("Honoring pre-linked FSP module, no relocation.\n");
1128 return cbfstool_convert_raw(buffer
, offset
, header
);
1130 address
= param
.baseaddress
;
1135 /* Create a copy of the buffer to attempt relocation. */
1136 if (buffer_create(&fsp
, buffer_size(buffer
), "fsp"))
1139 memcpy(buffer_get(&fsp
), buffer_get(buffer
), buffer_size(buffer
));
1141 /* Replace the buffer contents w/ the relocated ones on success. */
1142 if (fsp_component_relocate(address
, buffer_get(&fsp
), buffer_size(&fsp
))
1144 buffer_delete(buffer
);
1145 buffer_clone(buffer
, &fsp
);
1147 buffer_delete(&fsp
);
1148 WARN("Invalid FSP variant.\n");
1151 /* Let the raw path handle all the cbfs metadata logic. */
1152 return cbfstool_convert_raw(buffer
, offset
, header
);
1155 static int cbfstool_convert_mkstage(struct buffer
*buffer
, uint32_t *offset
,
1156 struct cbfs_file
*header
)
1158 struct buffer output
;
1162 if (elf_program_file_size(buffer
, &data_size
) < 0) {
1163 ERROR("Could not obtain ELF size\n");
1168 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1169 * here. That is okay because XIP stages may not be compressed, so their size cannot
1170 * change anymore at a later point.
1172 if (param
.stage_xip
&&
1173 do_cbfs_locate(offset
, data_size
)) {
1174 ERROR("Could not find location for stage.\n");
1178 struct cbfs_file_attr_stageheader
*stageheader
= (void *)
1179 cbfs_add_file_attr(header
, CBFS_FILE_ATTR_TAG_STAGEHEADER
,
1180 sizeof(struct cbfs_file_attr_stageheader
));
1184 if (param
.stage_xip
) {
1185 uint32_t host_space_address
= convert_addr_space(param
.image_region
, *offset
);
1186 assert(IS_HOST_SPACE_ADDRESS(host_space_address
));
1187 ret
= parse_elf_to_xip_stage(buffer
, &output
, host_space_address
,
1188 param
.ignore_sections
, stageheader
);
1190 ret
= parse_elf_to_stage(buffer
, &output
, param
.ignore_sections
,
1196 /* Store a hash of original uncompressed stage to compare later. */
1197 size_t decmp_size
= buffer_size(&output
);
1198 uint32_t decmp_hash
= XXH32(buffer_get(&output
), decmp_size
, 0);
1200 /* Chain to base conversion routine to handle compression. */
1201 ret
= cbfstool_convert_raw(&output
, offset
, header
);
1205 /* Special care must be taken for LZ4-compressed stages that the BSS is
1206 large enough to provide scratch space for in-place decompression. */
1207 if (!param
.precompression
&& param
.compression
== CBFS_COMPRESS_LZ4
) {
1208 size_t memlen
= be32toh(stageheader
->memlen
);
1209 size_t compressed_size
= buffer_size(&output
);
1210 uint8_t *compare_buffer
= malloc(memlen
);
1211 uint8_t *start
= compare_buffer
+ memlen
- compressed_size
;
1212 if (!compare_buffer
) {
1213 ERROR("Out of memory\n");
1216 memcpy(start
, buffer_get(&output
), compressed_size
);
1217 ret
= ulz4fn(start
, compressed_size
, compare_buffer
, memlen
);
1219 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1220 free(compare_buffer
);
1222 } else if (ret
!= (int)decmp_size
||
1223 decmp_hash
!= XXH32(compare_buffer
, decmp_size
, 0)) {
1224 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1225 free(compare_buffer
);
1228 free(compare_buffer
);
1231 buffer_delete(buffer
);
1232 buffer_clone(buffer
, &output
);
1236 buffer_delete(&output
);
1240 static int cbfstool_convert_mkpayload(struct buffer
*buffer
,
1241 unused
uint32_t *offset
, struct cbfs_file
*header
)
1243 struct buffer output
;
1245 /* Per default, try and see if payload is an ELF binary */
1246 ret
= parse_elf_to_payload(buffer
, &output
, param
.compression
);
1248 /* If it's not an ELF, see if it's a FIT */
1250 ret
= parse_fit_to_payload(buffer
, &output
, param
.compression
);
1252 header
->type
= htobe32(CBFS_TYPE_FIT_PAYLOAD
);
1255 /* If it's not an FIT, see if it's a UEFI FV */
1257 ret
= parse_fv_to_payload(buffer
, &output
, param
.compression
);
1259 /* If it's neither ELF nor UEFI Fv, try bzImage */
1261 ret
= parse_bzImage_to_payload(buffer
, &output
,
1262 param
.initrd
, param
.cmdline
, param
.compression
);
1264 /* Not a supported payload type */
1266 ERROR("Not a supported payload type (ELF / FV).\n");
1267 buffer_delete(buffer
);
1271 buffer_delete(buffer
);
1272 // Direct assign, no dupe.
1273 memcpy(buffer
, &output
, sizeof(*buffer
));
1274 header
->len
= htobe32(output
.size
);
1278 static int cbfstool_convert_mkflatpayload(struct buffer
*buffer
,
1279 unused
uint32_t *offset
, struct cbfs_file
*header
)
1281 struct buffer output
;
1282 if (parse_flat_binary_to_payload(buffer
, &output
,
1285 param
.compression
) != 0) {
1288 buffer_delete(buffer
);
1289 // Direct assign, no dupe.
1290 memcpy(buffer
, &output
, sizeof(*buffer
));
1291 header
->len
= htobe32(output
.size
);
1295 static int cbfs_add(void)
1297 convert_buffer_t convert
= cbfstool_convert_raw
;
1299 if (param
.type
== CBFS_TYPE_FSP
) {
1300 convert
= cbfstool_convert_fsp
;
1301 } else if (param
.type
== CBFS_TYPE_STAGE
) {
1302 ERROR("stages can only be added with cbfstool add-stage\n");
1304 } else if (param
.stage_xip
) {
1305 ERROR("cbfstool add supports xip only for FSP component type\n");
1309 return cbfs_add_component(param
.filename
,
1315 static int cbfs_add_stage(void)
1317 if (param
.stage_xip
&& param
.baseaddress_assigned
) {
1318 ERROR("Cannot specify base address for XIP.\n");
1321 param
.type
= CBFS_TYPE_STAGE
;
1323 return cbfs_add_component(param
.filename
,
1326 cbfstool_convert_mkstage
);
1329 static int cbfs_add_payload(void)
1331 param
.type
= CBFS_TYPE_SELF
;
1332 return cbfs_add_component(param
.filename
,
1335 cbfstool_convert_mkpayload
);
1338 static int cbfs_add_flat_binary(void)
1340 if (param
.loadaddress
== 0) {
1341 ERROR("You need to specify a valid "
1342 "-l/--load-address.\n");
1345 if (param
.entrypoint
== 0) {
1346 ERROR("You need to specify a valid "
1347 "-e/--entry-point.\n");
1350 param
.type
= CBFS_TYPE_SELF
;
1351 return cbfs_add_component(param
.filename
,
1354 cbfstool_convert_mkflatpayload
);
1357 static int cbfs_add_integer(void)
1359 if (!param
.u64val_assigned
) {
1360 ERROR("You need to specify a value to write.\n");
1363 return cbfs_add_integer_component(param
.name
,
1366 param
.headeroffset
);
1369 static int cbfs_remove(void)
1372 ERROR("You need to specify -n/--name.\n");
1376 struct cbfs_image image
;
1377 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1378 param
.headeroffset
))
1381 if (cbfs_remove_entry(&image
, param
.name
) != 0) {
1382 ERROR("Removing file '%s' failed.\n",
1387 return maybe_update_metadata_hash(&image
);
1390 static int cbfs_create(void)
1392 struct cbfs_image image
;
1393 memset(&image
, 0, sizeof(image
));
1394 buffer_clone(&image
.buffer
, param
.image_region
);
1397 if (param
.arch
!= CBFS_ARCHITECTURE_UNKNOWN
|| param
.size
||
1398 param
.baseaddress_assigned
||
1399 param
.headeroffset_assigned
||
1400 param
.cbfsoffset_assigned
||
1402 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
1406 return cbfs_image_create(&image
, image
.buffer
.size
);
1409 if (param
.arch
== CBFS_ARCHITECTURE_UNKNOWN
) {
1410 ERROR("You need to specify -m/--machine arch.\n");
1414 struct buffer bootblock
;
1415 if (!param
.bootblock
) {
1416 DEBUG("-B not given, creating image without bootblock.\n");
1417 if (buffer_create(&bootblock
, 0, "(dummy)") != 0)
1419 } else if (buffer_from_file(&bootblock
, param
.bootblock
)) {
1423 if (!param
.alignment
)
1424 param
.alignment
= CBFS_ALIGNMENT
;
1426 // Set default offsets. x86, as usual, needs to be a special snowflake.
1427 if (!param
.baseaddress_assigned
) {
1428 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
1429 // Make sure there's at least enough room for rel_offset
1430 param
.baseaddress
= param
.size
-
1431 MAX(bootblock
.size
, sizeof(int32_t));
1432 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1435 param
.baseaddress
= 0;
1436 DEBUG("bootblock starts at address 0x0.\n");
1439 if (!param
.headeroffset_assigned
) {
1440 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
1441 param
.headeroffset
= param
.baseaddress
-
1442 sizeof(struct cbfs_header
);
1443 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1444 param
.headeroffset
);
1446 param
.headeroffset
= align_up(param
.baseaddress
+
1447 bootblock
.size
, sizeof(uint32_t));
1448 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1449 param
.headeroffset
);
1452 if (!param
.cbfsoffset_assigned
) {
1453 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
1454 param
.cbfsoffset
= 0;
1455 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1457 param
.cbfsoffset
= align_up(param
.headeroffset
+
1458 sizeof(struct cbfs_header
),
1460 DEBUG("CBFS entries start beind master header (%#x).\n",
1465 int ret
= cbfs_legacy_image_create(&image
,
1472 buffer_delete(&bootblock
);
1476 static int cbfs_layout(void)
1478 const struct fmap
*fmap
= partitioned_file_get_fmap(param
.image_file
);
1480 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1484 printf("This image contains the following sections that can be %s with this tool:\n",
1485 param
.show_immutable
? "accessed" : "manipulated");
1487 for (unsigned i
= 0; i
< fmap
->nareas
; ++i
) {
1488 const struct fmap_area
*current
= fmap
->areas
+ i
;
1490 bool readonly
= partitioned_file_fmap_count(param
.image_file
,
1491 partitioned_file_fmap_select_children_of
, current
) ||
1492 region_is_flashmap((const char *)current
->name
);
1493 if (!param
.show_immutable
&& readonly
)
1496 printf("'%s'", current
->name
);
1498 // Detect consecutive sections that describe the same region and
1499 // show them as aliases. This cannot find equivalent entries
1500 // that aren't adjacent; however, fmaptool doesn't generate
1501 // FMAPs with such sections, so this convenience feature works
1502 // for all but the strangest manually created FMAP binaries.
1503 // TODO: This could be done by parsing the FMAP into some kind
1504 // of tree that had duplicate lists in addition to child lists,
1505 // which would allow covering that weird, unlikely case as well.
1507 for (lookahead
= 1; i
+ lookahead
< fmap
->nareas
;
1509 const struct fmap_area
*consecutive
=
1510 fmap
->areas
+ i
+ lookahead
;
1511 if (consecutive
->offset
!= current
->offset
||
1512 consecutive
->size
!= current
->size
)
1514 printf(", '%s'", consecutive
->name
);
1517 fputs(" are aliases for the same region", stdout
);
1519 const char *qualifier
= "";
1521 qualifier
= "read-only, ";
1522 else if (region_is_modern_cbfs((const char *)current
->name
))
1523 qualifier
= "CBFS, ";
1524 else if (current
->flags
& FMAP_AREA_PRESERVE
)
1525 qualifier
= "preserve, ";
1526 printf(" (%ssize %u, offset %u)\n", qualifier
, current
->size
,
1533 if (param
.show_immutable
) {
1534 puts("It is at least possible to perform the read action on every section listed above.");
1536 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1537 puts("To see the image's read-only sections as well, rerun with the -w option.");
1543 static enum cb_err
verify_walker(__always_unused cbfs_dev_t dev
, size_t offset
,
1544 const union cbfs_mdata
*mdata
, size_t already_read
, void *arg
)
1546 uint32_t type
= be32toh(mdata
->h
.type
);
1547 uint32_t data_offset
= be32toh(mdata
->h
.offset
);
1548 if (verification_exclude(type
))
1549 return CB_CBFS_NOT_FOUND
;
1550 assert(already_read
== data_offset
);
1551 const struct vb2_hash
*hash
= cbfs_file_hash(mdata
);
1554 void *file_data
= arg
+ offset
+ data_offset
;
1555 if (vb2_hash_verify(false, file_data
, be32toh(mdata
->h
.len
), hash
) != VB2_SUCCESS
)
1556 return CB_CBFS_HASH_MISMATCH
;
1557 return CB_CBFS_NOT_FOUND
;
1560 static int cbfs_print(void)
1562 struct cbfs_image image
;
1563 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1564 param
.headeroffset
))
1566 if (param
.machine_parseable
) {
1568 printf("[FMAP REGION]\t%s\n", param
.region_name
);
1569 cbfs_print_parseable_directory(&image
);
1571 printf("FMAP REGION: %s\n", param
.region_name
);
1572 cbfs_print_directory(&image
);
1576 const char *verification_state
= "fully valid";
1577 struct mh_cache
*mhc
= get_mh_cache();
1578 if (mhc
->cbfs_hash
.algo
== VB2_HASH_INVALID
)
1581 struct vb2_hash real_hash
= { .algo
= mhc
->cbfs_hash
.algo
};
1582 enum cb_err err
= cbfs_walk(&image
, verify_walker
, buffer_get(&image
.buffer
),
1583 &real_hash
, CBFS_WALK_WRITEBACK_HASH
);
1584 if (err
== CB_CBFS_HASH_MISMATCH
)
1585 verification_state
= "invalid file hashes";
1586 else if (err
!= CB_CBFS_NOT_FOUND
)
1587 verification_state
= "missing file hashes";
1588 char *hash_str
= bintohex(real_hash
.raw
,
1589 vb2_digest_size(real_hash
.algo
));
1590 printf("[METADATA HASH]\t%s:%s",
1591 vb2_get_hash_algorithm_name(real_hash
.algo
), hash_str
);
1592 if (!strcmp(param
.region_name
, SECTION_NAME_PRIMARY_CBFS
)) {
1593 if (!memcmp(mhc
->cbfs_hash
.raw
, real_hash
.raw
,
1594 vb2_digest_size(real_hash
.algo
))) {
1598 verification_state
= "invalid metadata hash";
1602 printf("[CBFS VERIFICATION (%s)]\t%s\n", param
.region_name
, verification_state
);
1609 static int cbfs_extract(void)
1611 if (!param
.filename
) {
1612 ERROR("You need to specify -f/--filename.\n");
1617 ERROR("You need to specify -n/--name.\n");
1621 struct cbfs_image image
;
1622 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1623 param
.headeroffset
))
1626 return cbfs_export_entry(&image
, param
.name
, param
.filename
,
1627 param
.arch
, !param
.unprocessed
);
1630 static int cbfs_write(void)
1632 if (!param
.filename
) {
1633 ERROR("You need to specify a valid input -f/--file.\n");
1636 if (!partitioned_file_is_partitioned(param
.image_file
)) {
1637 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1641 if (!param
.force
&& region_is_modern_cbfs(param
.region_name
)) {
1642 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1647 struct buffer new_content
;
1648 if (buffer_from_file(&new_content
, param
.filename
))
1651 if (buffer_check_magic(&new_content
, FMAP_SIGNATURE
,
1652 strlen(FMAP_SIGNATURE
))) {
1653 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1655 buffer_delete(&new_content
);
1658 if (!param
.force
&& buffer_check_magic(&new_content
, CBFS_FILE_MAGIC
,
1659 strlen(CBFS_FILE_MAGIC
))) {
1660 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1662 buffer_delete(&new_content
);
1666 unsigned offset
= 0;
1667 if (param
.fill_partial_upward
&& param
.fill_partial_downward
) {
1668 ERROR("You may only specify one of -u and -d.\n");
1669 buffer_delete(&new_content
);
1671 } else if (!param
.fill_partial_upward
&& !param
.fill_partial_downward
) {
1672 if (new_content
.size
!= param
.image_region
->size
) {
1673 ERROR("File to add is %zu bytes and would not fill %zu-byte target region (did you mean to pass either -u or -d?)\n",
1674 new_content
.size
, param
.image_region
->size
);
1675 buffer_delete(&new_content
);
1679 if (new_content
.size
> param
.image_region
->size
) {
1680 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1681 new_content
.size
, param
.image_region
->size
);
1682 buffer_delete(&new_content
);
1685 if (param
.u64val
== (uint64_t)-1) {
1686 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1687 param
.fill_partial_upward
? "bottom" : "top");
1688 } else if (param
.u64val
> 0xff) {
1689 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param
.u64val
& 0xff));
1690 buffer_delete(&new_content
);
1693 memset(buffer_get(param
.image_region
),
1694 param
.u64val
& 0xff,
1695 buffer_size(param
.image_region
));
1697 if (param
.fill_partial_downward
)
1698 offset
= param
.image_region
->size
- new_content
.size
;
1701 memcpy(param
.image_region
->data
+ offset
, new_content
.data
,
1703 buffer_delete(&new_content
);
1705 return maybe_update_fmap_hash();
1708 static int cbfs_read(void)
1710 if (!param
.filename
) {
1711 ERROR("You need to specify a valid output -f/--file.\n");
1714 if (!partitioned_file_is_partitioned(param
.image_file
)) {
1715 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1719 return buffer_write_file(param
.image_region
, param
.filename
);
1722 static int cbfs_copy(void)
1724 struct cbfs_image src_image
;
1725 struct buffer src_buf
;
1727 if (!param
.source_region
) {
1728 ERROR("You need to specify -R/--source-region.\n");
1732 /* Obtain the source region and convert it to a cbfs_image. */
1733 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1734 param
.source_region
)) {
1735 ERROR("Region not found in image: %s\n", param
.source_region
);
1739 if (cbfs_image_from_buffer(&src_image
, &src_buf
, param
.headeroffset
))
1742 return cbfs_copy_instance(&src_image
, param
.image_region
);
1745 static int cbfs_compact(void)
1747 struct cbfs_image image
;
1748 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1749 param
.headeroffset
))
1751 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1752 return cbfs_compact_instance(&image
);
1755 static int cbfs_expand(void)
1757 struct buffer src_buf
;
1759 /* Obtain the source region. */
1760 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1761 param
.region_name
)) {
1762 ERROR("Region not found in image: %s\n", param
.source_region
);
1766 return cbfs_expand_to_region(param
.image_region
);
1769 static int cbfs_truncate(void)
1771 struct buffer src_buf
;
1773 /* Obtain the source region. */
1774 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1775 param
.region_name
)) {
1776 ERROR("Region not found in image: %s\n", param
.source_region
);
1781 int result
= cbfs_truncate_space(param
.image_region
, &size
);
1783 printf("0x%x\n", size
);
1787 static const struct command commands
[] = {
1788 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add
, true, true},
1789 {"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary
,
1791 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload
,
1793 {"add-stage", "a:H:r:f:n:t:c:b:P:QS:p:yvA:gh?", cbfs_add_stage
,
1795 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer
, true, true},
1796 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header
, true, true},
1797 {"compact", "r:h?", cbfs_compact
, true, true},
1798 {"copy", "r:R:h?", cbfs_copy
, true, true},
1799 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create
, true, true},
1800 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract
, true, false},
1801 {"layout", "wvh?", cbfs_layout
, false, false},
1802 {"print", "H:r:vkh?", cbfs_print
, true, false},
1803 {"read", "r:f:vh?", cbfs_read
, true, false},
1804 {"remove", "H:r:n:vh?", cbfs_remove
, true, true},
1805 {"write", "r:f:i:Fudvh?", cbfs_write
, true, true},
1806 {"expand", "r:h?", cbfs_expand
, true, true},
1807 {"truncate", "r:h?", cbfs_truncate
, true, true},
1811 /* begin after ASCII characters */
1812 LONGOPT_START
= 256,
1813 LONGOPT_IBB
= LONGOPT_START
,
1818 static struct option long_options
[] = {
1819 {"alignment", required_argument
, 0, 'a' },
1820 {"base-address", required_argument
, 0, 'b' },
1821 {"bootblock", required_argument
, 0, 'B' },
1822 {"cmdline", required_argument
, 0, 'C' },
1823 {"compression", required_argument
, 0, 'c' },
1824 {"topswap-size", required_argument
, 0, 'j' },
1825 {"empty-fits", required_argument
, 0, 'x' },
1826 {"entry-point", required_argument
, 0, 'e' },
1827 {"file", required_argument
, 0, 'f' },
1828 {"fill-downward", no_argument
, 0, 'd' },
1829 {"fill-upward", no_argument
, 0, 'u' },
1830 {"flashmap", required_argument
, 0, 'M' },
1831 {"fmap-regions", required_argument
, 0, 'r' },
1832 {"force", no_argument
, 0, 'F' },
1833 {"source-region", required_argument
, 0, 'R' },
1834 {"hash-algorithm",required_argument
, 0, 'A' },
1835 {"header-offset", required_argument
, 0, 'H' },
1836 {"help", no_argument
, 0, 'h' },
1837 {"ignore-sec", required_argument
, 0, 'S' },
1838 {"initrd", required_argument
, 0, 'I' },
1839 {"int", required_argument
, 0, 'i' },
1840 {"load-address", required_argument
, 0, 'l' },
1841 {"machine", required_argument
, 0, 'm' },
1842 {"name", required_argument
, 0, 'n' },
1843 {"offset", required_argument
, 0, 'o' },
1844 {"padding", required_argument
, 0, 'p' },
1845 {"pow2page", no_argument
, 0, 'Q' },
1846 {"ucode-region", required_argument
, 0, 'q' },
1847 {"size", required_argument
, 0, 's' },
1848 {"type", required_argument
, 0, 't' },
1849 {"verbose", no_argument
, 0, 'v' },
1850 {"with-readonly", no_argument
, 0, 'w' },
1851 {"xip", no_argument
, 0, 'y' },
1852 {"gen-attribute", no_argument
, 0, 'g' },
1853 {"mach-parseable",no_argument
, 0, 'k' },
1854 {"unprocessed", no_argument
, 0, 'U' },
1855 {"ibb", no_argument
, 0, LONGOPT_IBB
},
1856 {"mmap", required_argument
, 0, LONGOPT_MMAP
},
1860 static int get_region_offset(long long int offset
, uint32_t *region_offset
)
1862 /* If offset is not negative, no transformation required. */
1864 *region_offset
= offset
;
1868 /* Calculate offset from start of region. */
1869 return convert_region_offset(-offset
, region_offset
);
1872 static int calculate_region_offsets(void)
1876 if (param
.baseaddress_assigned
)
1877 ret
|= get_region_offset(param
.baseaddress_input
, ¶m
.baseaddress
);
1878 if (param
.headeroffset_assigned
)
1879 ret
|= get_region_offset(param
.headeroffset_input
, ¶m
.headeroffset
);
1880 if (param
.cbfsoffset_assigned
)
1881 ret
|= get_region_offset(param
.cbfsoffset_input
, ¶m
.cbfsoffset
);
1886 static int dispatch_command(struct command command
)
1888 if (command
.accesses_region
) {
1889 assert(param
.image_file
);
1891 if (partitioned_file_is_partitioned(param
.image_file
)) {
1892 INFO("Performing operation on '%s' region...\n",
1895 if (!partitioned_file_read_region(param
.image_region
,
1896 param
.image_file
, param
.region_name
)) {
1897 ERROR("The image will be left unmodified.\n");
1901 if (command
.modifies_region
) {
1902 // We (intentionally) don't support overwriting the FMAP
1903 // section. If you find yourself wanting to do this,
1904 // consider creating a new image rather than performing
1905 // whatever hacky transformation you were planning.
1906 if (region_is_flashmap(param
.region_name
)) {
1907 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1909 ERROR("The image will be left unmodified.\n");
1912 // We don't allow writing raw data to regions that
1913 // contain nested regions, since doing so would
1914 // overwrite all such subregions.
1915 if (partitioned_file_region_contains_nested(
1916 param
.image_file
, param
.region_name
)) {
1917 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1919 ERROR("The image will be left unmodified.\n");
1925 * Once image region is read, input offsets can be adjusted accordingly if the
1926 * inputs are provided as negative integers i.e. offsets from end of region.
1928 if (calculate_region_offsets())
1932 if (command
.function()) {
1933 if (partitioned_file_is_partitioned(param
.image_file
)) {
1934 ERROR("Failed while operating on '%s' region!\n",
1936 ERROR("The image will be left unmodified.\n");
1944 static void usage(char *name
)
1947 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
1948 "USAGE:\n" " %s [-h]\n"
1949 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
1950 " -H header_offset Do not search for header; use this offset*\n"
1951 " -T Output top-aligned memory address\n"
1952 " -u Accept short data; fill upward/from bottom\n"
1953 " -d Accept short data; fill downward/from top\n"
1954 " -F Force action\n"
1955 " -g Generate position and alignment arguments\n"
1956 " -U Unprocessed; don't decompress or make ELF\n"
1957 " -v Provide verbose output (-v=INFO -vv=DEBUG output)\n"
1958 " -h Display this help message\n\n"
1959 " --ext-win-base Base of extended decode window in host address\n"
1960 " space(x86 only)\n"
1961 " --ext-win-size Size of extended decode window in host address\n"
1962 " space(x86 only)\n"
1964 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
1965 " [-c compression] [-b base-address | -a alignment] \\\n"
1966 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
1967 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1968 " [--ext-win-base win-base --ext-win-size win-size] "
1971 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
1972 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1973 " [-c compression] [-b base-address] \\\n"
1974 " (linux specific: [-C cmdline] [-I initrd]) "
1975 "Add a payload to the ROM\n"
1976 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1977 " [-c compression] [-b base] \\\n"
1978 " [-S comma-separated-section(s)-to-ignore] \\\n"
1979 " [-a alignment] [-Q|--pow2page] \\\n"
1980 " [-y|--xip] [--ibb] \\\n"
1981 " [--ext-win-base win-base --ext-win-size win-size] "
1982 "Add a stage to the ROM\n"
1983 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1984 " [-A hash] -l load-address -e entry-point \\\n"
1985 " [-c compression] [-b base] "
1986 "Add a 32bit flat mode binary\n"
1987 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
1988 "Add a raw 64-bit integer value\n"
1989 " add-master-header [-r image,regions] \\ \n"
1990 " [-j topswap-size] (Intel CPUs only) "
1991 "Add a legacy CBFS master header\n"
1992 " remove [-r image,regions] -n NAME "
1993 "Remove a component\n"
1994 " compact -r image,regions "
1995 "Defragment CBFS image.\n"
1996 " copy -r image,regions -R source-region "
1997 "Create a copy (duplicate) cbfs instance in fmap\n"
1998 " create -m ARCH -s size [-b bootblock offset] \\\n"
1999 " [-o CBFS offset] [-H header offset] [-B bootblock] "
2000 "Create a legacy ROM file with CBFS master header*\n"
2001 " create -M flashmap [-r list,of,regions,containing,cbfses] "
2002 "Create a new-style partitioned firmware image\n"
2004 "List mutable (or, with -w, readable) image regions\n"
2005 " print [-r image,regions] [-k] "
2006 "Show the contents of the ROM\n"
2007 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
2008 "Extracts a file from ROM\n"
2009 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
2010 "Write file into same-size [or larger] raw region\n"
2011 " read [-r fmap-region] -f file "
2012 "Extract raw region contents into binary file\n"
2013 " truncate [-r fmap-region] "
2014 "Truncate CBFS and print new size on stdout\n"
2015 " expand [-r fmap-region] "
2016 "Expand CBFS to span entire region\n"
2018 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
2019 " in two possible formats: if their value is greater than\n"
2020 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
2021 " address; otherwise, they are treated as an offset into flash.\n"
2022 "ARCHes:\n", name
, name
2024 print_supported_architectures();
2027 print_supported_filetypes();
2029 "\n* Note that these actions and switches are only valid when\n"
2030 " working with legacy images whose structure is described\n"
2031 " primarily by a CBFS master header. New-style images, in\n"
2032 " contrast, exclusively make use of an FMAP to describe their\n"
2033 " layout: this must minimally contain an '%s' section\n"
2034 " specifying the location of this FMAP itself and a '%s'\n"
2035 " section describing the primary CBFS. It should also be noted\n"
2036 " that, when working with such images, the -F and -r switches\n"
2037 " default to '%s' for convenience, and the -b switch becomes\n"
2038 " relative to the selected CBFS region's lowest address.\n"
2039 " The one exception to this rule is the top-aligned address,\n"
2040 " which is always relative to the end of the entire image\n"
2041 " rather than relative to the local region; this is true for\n"
2042 " for both input (sufficiently large) and output (-T) data.\n",
2043 SECTION_NAME_FMAP
, SECTION_NAME_PRIMARY_CBFS
,
2044 SECTION_NAME_PRIMARY_CBFS
2048 static bool valid_opt(size_t i
, int c
)
2050 /* Check if it is one of the optstrings supported by the command. */
2051 if (strchr(commands
[i
].optstring
, c
))
2055 * Check if it is one of the non-ASCII characters. Currently, the
2056 * non-ASCII characters are only checked against the valid list
2057 * irrespective of the command.
2059 if (c
>= LONGOPT_START
&& c
< LONGOPT_END
)
2065 int main(int argc
, char **argv
)
2075 char *image_name
= argv
[1];
2076 char *cmd
= argv
[2];
2079 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
2080 if (strcmp(cmd
, commands
[i
].name
) != 0)
2084 char *suffix
= NULL
;
2085 int option_index
= 0;
2087 c
= getopt_long(argc
, argv
, commands
[i
].optstring
,
2088 long_options
, &option_index
);
2090 if (optind
< argc
) {
2091 ERROR("%s: excessive argument -- '%s'"
2092 "\n", argv
[0], argv
[optind
]);
2098 /* Filter out illegal long options */
2099 if (!valid_opt(i
, c
)) {
2100 ERROR("%s: invalid option -- '%d'\n",
2107 param
.name
= optarg
;
2110 if (intfiletype(optarg
) != ((uint64_t) - 1))
2111 param
.type
= intfiletype(optarg
);
2113 param
.type
= strtoul(optarg
, NULL
, 0);
2114 if (param
.type
== 0)
2115 WARN("Unknown type '%s' ignored\n",
2119 if (strcmp(optarg
, "precompression") == 0) {
2120 param
.precompression
= 1;
2123 int algo
= cbfs_parse_comp_algo(optarg
);
2125 param
.compression
= algo
;
2127 WARN("Unknown compression '%s' ignored.\n",
2132 if (!vb2_lookup_hash_alg(optarg
, ¶m
.hash
)) {
2133 ERROR("Unknown hash algorithm '%s'.\n",
2140 param
.fmap
= optarg
;
2143 param
.region_name
= optarg
;
2146 param
.source_region
= optarg
;
2149 param
.baseaddress_input
= strtoll(optarg
, &suffix
, 0);
2150 if (!*optarg
|| (suffix
&& *suffix
)) {
2151 ERROR("Invalid base address '%s'.\n",
2155 // baseaddress may be zero on non-x86, so we
2156 // need an explicit "baseaddress_assigned".
2157 param
.baseaddress_assigned
= 1;
2160 param
.loadaddress
= strtoull(optarg
, &suffix
, 0);
2161 if (!*optarg
|| (suffix
&& *suffix
)) {
2162 ERROR("Invalid load address '%s'.\n",
2168 param
.entrypoint
= strtoull(optarg
, &suffix
, 0);
2169 if (!*optarg
|| (suffix
&& *suffix
)) {
2170 ERROR("Invalid entry point '%s'.\n",
2176 param
.size
= strtoul(optarg
, &suffix
, 0);
2178 ERROR("Empty size specified.\n");
2181 switch (tolower((int)suffix
[0])) {
2186 param
.size
*= 1024 * 1024;
2191 ERROR("Invalid suffix for size '%s'.\n",
2197 param
.bootblock
= optarg
;
2200 param
.headeroffset_input
= strtoll(optarg
, &suffix
, 0);
2201 if (!*optarg
|| (suffix
&& *suffix
)) {
2202 ERROR("Invalid header offset '%s'.\n",
2206 param
.headeroffset_assigned
= 1;
2209 param
.alignment
= strtoul(optarg
, &suffix
, 0);
2210 if (!*optarg
|| (suffix
&& *suffix
)) {
2211 ERROR("Invalid alignment '%s'.\n",
2217 param
.padding
= strtoul(optarg
, &suffix
, 0);
2218 if (!*optarg
|| (suffix
&& *suffix
)) {
2219 ERROR("Invalid pad size '%s'.\n",
2225 param
.force_pow2_pagesize
= 1;
2228 param
.cbfsoffset_input
= strtoll(optarg
, &suffix
, 0);
2229 if (!*optarg
|| (suffix
&& *suffix
)) {
2230 ERROR("Invalid cbfs offset '%s'.\n",
2234 param
.cbfsoffset_assigned
= 1;
2237 param
.filename
= optarg
;
2243 param
.u64val
= strtoull(optarg
, &suffix
, 0);
2244 param
.u64val_assigned
= 1;
2245 if (!*optarg
|| (suffix
&& *suffix
)) {
2246 ERROR("Invalid int parameter '%s'.\n",
2252 param
.fill_partial_upward
= true;
2255 param
.fill_partial_downward
= true;
2258 param
.show_immutable
= true;
2261 param
.topswap_size
= strtol(optarg
, NULL
, 0);
2262 if (!is_valid_topswap())
2266 param
.ucode_region
= optarg
;
2272 param
.arch
= string_to_arch(optarg
);
2275 param
.initrd
= optarg
;
2278 param
.cmdline
= optarg
;
2281 param
.ignore_sections
= optarg
;
2284 param
.stage_xip
= true;
2287 param
.autogen_attr
= true;
2290 param
.machine_parseable
= true;
2293 param
.unprocessed
= true;
2299 if (decode_mmap_arg(optarg
))
2311 if (commands
[i
].function
== cbfs_create
) {
2313 struct buffer flashmap
;
2314 if (buffer_from_file(&flashmap
, param
.fmap
))
2316 param
.image_file
= partitioned_file_create(
2317 image_name
, &flashmap
);
2318 buffer_delete(&flashmap
);
2319 } else if (param
.size
) {
2320 param
.image_file
= partitioned_file_create_flat(
2321 image_name
, param
.size
);
2323 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
2327 bool write_access
= commands
[i
].modifies_region
;
2330 partitioned_file_reopen(image_name
,
2333 if (!param
.image_file
)
2336 unsigned num_regions
= 1;
2337 for (const char *list
= strchr(param
.region_name
, ','); list
;
2338 list
= strchr(list
+ 1, ','))
2341 // If the action needs to read an image region, as indicated by
2342 // having accesses_region set in its command struct, that
2343 // region's buffer struct will be stored here and the client
2344 // will receive a pointer to it via param.image_region. It
2345 // need not write the buffer back to the image file itself,
2346 // since this behavior can be requested via its modifies_region
2347 // field. Additionally, it should never free the region buffer,
2348 // as that is performed automatically once it completes.
2349 struct buffer image_regions
[num_regions
];
2350 memset(image_regions
, 0, sizeof(image_regions
));
2352 bool seen_primary_cbfs
= false;
2353 char region_name_scratch
[strlen(param
.region_name
) + 1];
2354 strcpy(region_name_scratch
, param
.region_name
);
2355 param
.region_name
= strtok(region_name_scratch
, ",");
2356 for (unsigned region
= 0; region
< num_regions
; ++region
) {
2357 if (!param
.region_name
) {
2358 ERROR("Encountered illegal degenerate region name in -r list\n");
2359 ERROR("The image will be left unmodified.\n");
2360 partitioned_file_close(param
.image_file
);
2364 if (strcmp(param
.region_name
, SECTION_NAME_PRIMARY_CBFS
)
2366 seen_primary_cbfs
= true;
2368 param
.image_region
= image_regions
+ region
;
2369 if (dispatch_command(commands
[i
])) {
2370 partitioned_file_close(param
.image_file
);
2374 param
.region_name
= strtok(NULL
, ",");
2377 if (commands
[i
].function
== cbfs_create
&& !seen_primary_cbfs
) {
2378 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2379 SECTION_NAME_PRIMARY_CBFS
);
2380 ERROR("The image will be left unmodified.\n");
2381 partitioned_file_close(param
.image_file
);
2385 if (commands
[i
].modifies_region
) {
2386 assert(param
.image_file
);
2387 for (unsigned region
= 0; region
< num_regions
;
2390 if (!partitioned_file_write_region(
2392 image_regions
+ region
)) {
2393 partitioned_file_close(
2400 partitioned_file_close(param
.image_file
);
2404 ERROR("Unknown command '%s'.\n", cmd
);