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 <commonlib/fsp.h>
18 #include <commonlib/endian.h>
19 #include <commonlib/helpers.h>
21 #define SECTION_WITH_FIT_TABLE "BOOTBLOCK"
25 const char *optstring
;
26 int (*function
) (void);
27 // Whether to populate param.image_region before invoking function
29 // This set to true means two things:
30 // - in case of a command operating on a region, the region's contents
31 // will be written back to image_file at the end
32 // - write access to the file is required
37 partitioned_file_t
*image_file
;
38 struct buffer
*image_region
;
42 const char *region_name
;
43 const char *source_region
;
44 const char *bootblock
;
45 const char *ignore_section
;
46 const char *ucode_region
;
50 uint32_t baseaddress_assigned
;
52 uint32_t headeroffset
;
53 uint32_t headeroffset_assigned
;
59 uint32_t cbfsoffset_assigned
;
62 uint32_t topswap_size
;
64 bool fill_partial_upward
;
65 bool fill_partial_downward
;
69 bool machine_parseable
;
72 enum comp_algo compression
;
74 enum vb2_hash_algorithm hash
;
75 /* For linux payloads */
80 /* All variables not listed are initialized as zero. */
81 .arch
= CBFS_ARCHITECTURE_UNKNOWN
,
82 .compression
= CBFS_COMPRESS_NONE
,
83 .hash
= VB2_HASH_INVALID
,
85 .region_name
= SECTION_NAME_PRIMARY_CBFS
,
89 static bool region_is_flashmap(const char *region
)
91 return partitioned_file_region_check_magic(param
.image_file
, region
,
92 FMAP_SIGNATURE
, strlen(FMAP_SIGNATURE
));
95 /* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
96 static bool region_is_modern_cbfs(const char *region
)
98 return partitioned_file_region_check_magic(param
.image_file
, region
,
99 CBFS_FILE_MAGIC
, strlen(CBFS_FILE_MAGIC
));
103 * Converts between offsets from the start of the specified image region and
104 * "top-aligned" offsets from the top of the entire boot media. See comment
105 * below for convert_to_from_top_aligned() about forming addresses.
107 static unsigned convert_to_from_absolute_top_aligned(
108 const struct buffer
*region
, unsigned offset
)
112 size_t image_size
= partitioned_file_total_size(param
.image_file
);
114 return image_size
- region
->offset
- offset
;
118 * Converts between offsets from the start of the specified image region and
119 * "top-aligned" offsets from the top of the image region. Works in either
120 * direction: pass in one type of offset and receive the other type.
121 * N.B. A top-aligned offset is always a positive number, and should not be
122 * confused with a top-aligned *address*, which is its arithmetic inverse. */
123 static unsigned convert_to_from_top_aligned(const struct buffer
*region
,
128 /* Cover the situation where a negative base address is given by the
129 * user. Callers of this function negate it, so it'll be a positive
130 * number smaller than the region.
132 if ((offset
> 0) && (offset
< region
->size
)) {
133 return region
->size
- offset
;
136 return convert_to_from_absolute_top_aligned(region
, offset
);
139 static int do_cbfs_locate(int32_t *cbfs_addr
, size_t metadata_size
,
142 if (!param
.filename
) {
143 ERROR("You need to specify -f/--filename.\n");
148 ERROR("You need to specify -n/--name.\n");
152 struct cbfs_image image
;
153 if (cbfs_image_from_buffer(&image
, param
.image_region
,
157 if (cbfs_get_entry(&image
, param
.name
))
158 WARN("'%s' already in CBFS.\n", param
.name
);
161 struct buffer buffer
;
162 if (buffer_from_file(&buffer
, param
.filename
) != 0) {
163 ERROR("Cannot load %s.\n", param
.filename
);
166 data_size
= buffer
.size
;
167 buffer_delete(&buffer
);
170 DEBUG("File size is %zd (0x%zx)\n", data_size
, data_size
);
172 /* Include cbfs_file size along with space for with name. */
173 metadata_size
+= cbfs_calculate_file_header_size(param
.name
);
174 /* Adjust metadata_size if additional attributes were added */
175 if (param
.autogen_attr
) {
177 metadata_size
+= sizeof(struct cbfs_file_attr_align
);
178 if (param
.baseaddress_assigned
|| param
.stage_xip
)
179 metadata_size
+= sizeof(struct cbfs_file_attr_position
);
182 /* Take care of the hash attribute if it is used */
183 if (param
.hash
!= VB2_HASH_INVALID
)
184 metadata_size
+= sizeof(struct cbfs_file_attr_hash
);
186 int32_t address
= cbfs_locate_entry(&image
, data_size
, param
.pagesize
,
187 param
.alignment
, metadata_size
);
190 ERROR("'%s' can't fit in CBFS for page-size %#x, align %#x.\n",
191 param
.name
, param
.pagesize
, param
.alignment
);
195 *cbfs_addr
= address
;
199 typedef int (*convert_buffer_t
)(struct buffer
*buffer
, uint32_t *offset
,
200 struct cbfs_file
*header
);
202 static int cbfs_add_integer_component(const char *name
,
205 uint32_t headeroffset
) {
206 struct cbfs_image image
;
207 struct cbfs_file
*header
= NULL
;
208 struct buffer buffer
;
212 ERROR("You need to specify -n/--name.\n");
216 if (buffer_create(&buffer
, 8, name
) != 0)
219 for (i
= 0; i
< 8; i
++)
220 buffer
.data
[i
] = (u64val
>> i
*8) & 0xff;
222 if (cbfs_image_from_buffer(&image
, param
.image_region
, headeroffset
)) {
223 ERROR("Selected image region is not a CBFS.\n");
227 if (cbfs_get_entry(&image
, name
)) {
228 ERROR("'%s' already in ROM image.\n", name
);
232 if (IS_TOP_ALIGNED_ADDRESS(offset
))
233 offset
= convert_to_from_top_aligned(param
.image_region
,
236 header
= cbfs_create_file_header(CBFS_COMPONENT_RAW
,
238 if (cbfs_add_entry(&image
, &buffer
, offset
, header
, 0) != 0) {
239 ERROR("Failed to add %llu into ROM image as '%s'.\n",
240 (long long unsigned)u64val
, name
);
248 buffer_delete(&buffer
);
252 static int is_valid_topswap(void)
254 switch (param
.topswap_size
) {
262 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
269 static void fill_header_offset(void *location
, uint32_t offset
)
271 // TODO: When we have a BE target, we'll need to store this as BE
272 write_le32(location
, offset
);
275 static int update_master_header_loc_topswap(struct cbfs_image
*image
,
276 void *h_loc
, uint32_t header_offset
)
278 struct cbfs_file
*entry
;
279 void *ts_h_loc
= h_loc
;
281 entry
= cbfs_get_entry(image
, "bootblock");
283 ERROR("Bootblock not in ROM image?!?\n");
288 * Check if the existing topswap boundary matches with
291 if (param
.topswap_size
!= ntohl(entry
->len
)/2) {
292 ERROR("Top swap boundary does not match\n");
296 ts_h_loc
-= param
.topswap_size
;
297 fill_header_offset(ts_h_loc
, header_offset
);
302 static int cbfs_add_master_header(void)
304 const char * const name
= "cbfs master header";
305 struct cbfs_image image
;
306 struct cbfs_file
*header
= NULL
;
307 struct buffer buffer
;
313 if (cbfs_image_from_buffer(&image
, param
.image_region
,
314 param
.headeroffset
)) {
315 ERROR("Selected image region is not a CBFS.\n");
319 if (cbfs_get_entry(&image
, name
)) {
320 ERROR("'%s' already in ROM image.\n", name
);
324 if (buffer_create(&buffer
, sizeof(struct cbfs_header
), name
) != 0)
327 struct cbfs_header
*h
= (struct cbfs_header
*)buffer
.data
;
328 h
->magic
= htonl(CBFS_HEADER_MAGIC
);
329 h
->version
= htonl(CBFS_HEADER_VERSION
);
330 /* The 4 bytes are left out for two reasons:
331 * 1. the cbfs master header pointer resides there
332 * 2. some cbfs implementations assume that an image that resides
333 * below 4GB has a bootblock and get confused when the end of the
334 * image is at 4GB == 0.
336 h
->bootblocksize
= htonl(4);
337 h
->align
= htonl(CBFS_ENTRY_ALIGNMENT
);
338 /* The offset and romsize fields within the master header are absolute
339 * values within the boot media. As such, romsize needs to relfect
340 * the end 'offset' for a CBFS. To achieve that the current buffer
341 * representing the CBFS region's size is added to the offset of
342 * the region within a larger image.
344 offset
= buffer_get(param
.image_region
) -
345 buffer_get_original_backing(param
.image_region
);
346 size
= buffer_size(param
.image_region
);
347 h
->romsize
= htonl(size
+ offset
);
348 h
->offset
= htonl(offset
);
349 h
->architecture
= htonl(CBFS_ARCHITECTURE_UNKNOWN
);
351 header
= cbfs_create_file_header(CBFS_COMPONENT_CBFSHEADER
,
352 buffer_size(&buffer
), name
);
353 if (cbfs_add_entry(&image
, &buffer
, 0, header
, 0) != 0) {
354 ERROR("Failed to add cbfs master header into ROM image.\n");
358 struct cbfs_file
*entry
;
359 if ((entry
= cbfs_get_entry(&image
, name
)) == NULL
) {
360 ERROR("'%s' not in ROM image?!?\n", name
);
364 uint32_t header_offset
= CBFS_SUBHEADER(entry
) -
365 buffer_get(&image
.buffer
);
366 header_offset
= -(buffer_size(&image
.buffer
) - header_offset
);
368 h_loc
= (void *)(buffer_get(&image
.buffer
) +
369 buffer_size(&image
.buffer
) - 4);
370 fill_header_offset(h_loc
, header_offset
);
372 * If top swap present, update the header
373 * location in secondary bootblock
375 if (param
.topswap_size
) {
376 if (update_master_header_loc_topswap(&image
, h_loc
,
385 buffer_delete(&buffer
);
389 static int add_topswap_bootblock(struct buffer
*buffer
, uint32_t *offset
)
391 size_t bb_buf_size
= buffer_size(buffer
);
393 if (bb_buf_size
> param
.topswap_size
) {
394 ERROR("Bootblock bigger than the topswap boundary\n");
395 ERROR("size = %zd, ts = %d\n", bb_buf_size
,
401 * Allocate topswap_size*2 bytes for bootblock to
402 * accommodate the second bootblock.
404 struct buffer new_bootblock
, bb1
, bb2
;
405 if (buffer_create(&new_bootblock
, 2 * param
.topswap_size
,
409 buffer_splice(&bb1
, &new_bootblock
, param
.topswap_size
- bb_buf_size
,
411 buffer_splice(&bb2
, &new_bootblock
,
412 buffer_size(&new_bootblock
) - bb_buf_size
,
415 /* Copy to first bootblock */
416 memcpy(buffer_get(&bb1
), buffer_get(buffer
), bb_buf_size
);
417 /* Copy to second bootblock */
418 memcpy(buffer_get(&bb2
), buffer_get(buffer
), bb_buf_size
);
420 buffer_delete(buffer
);
421 buffer_clone(buffer
, &new_bootblock
);
423 /* Update the location (offset) of bootblock in the region */
424 *offset
= convert_to_from_top_aligned(param
.image_region
,
425 buffer_size(buffer
));
430 static int cbfs_add_component(const char *filename
,
434 uint32_t headeroffset
,
435 convert_buffer_t convert
)
437 size_t len_align
= 0;
440 ERROR("You need to specify -f/--filename.\n");
445 ERROR("You need to specify -n/--name.\n");
450 ERROR("You need to specify a valid -t/--type.\n");
454 struct cbfs_image image
;
455 if (cbfs_image_from_buffer(&image
, param
.image_region
, headeroffset
))
458 if (cbfs_get_entry(&image
, name
)) {
459 ERROR("'%s' already in ROM image.\n", name
);
463 struct buffer buffer
;
464 if (buffer_from_file(&buffer
, filename
) != 0) {
465 ERROR("Could not load file '%s'.\n", filename
);
470 * Check if Intel CPU topswap is specified this will require a
471 * second bootblock to be added.
473 if (type
== CBFS_COMPONENT_BOOTBLOCK
&& param
.topswap_size
)
474 if (add_topswap_bootblock(&buffer
, &offset
))
477 struct cbfs_file
*header
=
478 cbfs_create_file_header(type
, buffer
.size
, name
);
480 if (convert
&& convert(&buffer
, &offset
, header
) != 0) {
481 ERROR("Failed to parse file '%s'.\n", filename
);
482 buffer_delete(&buffer
);
486 if (param
.hash
!= VB2_HASH_INVALID
)
487 if (cbfs_add_file_hash(header
, &buffer
, param
.hash
) == -1) {
488 ERROR("couldn't add hash for '%s'\n", name
);
490 buffer_delete(&buffer
);
494 if (param
.autogen_attr
) {
495 /* Add position attribute if assigned */
496 if (param
.baseaddress_assigned
|| param
.stage_xip
) {
497 struct cbfs_file_attr_position
*attrs
=
498 (struct cbfs_file_attr_position
*)
499 cbfs_add_file_attr(header
,
500 CBFS_FILE_ATTR_TAG_POSITION
,
501 sizeof(struct cbfs_file_attr_position
));
504 /* If we add a stage or a payload, we need to take */
505 /* care about the additional metadata that is added */
506 /* to the cbfs file and therefore set the position */
507 /* the real beginning of the data. */
508 if (type
== CBFS_COMPONENT_STAGE
)
509 attrs
->position
= htonl(offset
+
510 sizeof(struct cbfs_stage
));
511 else if (type
== CBFS_COMPONENT_SELF
)
512 attrs
->position
= htonl(offset
+
513 sizeof(struct cbfs_payload
));
515 attrs
->position
= htonl(offset
);
517 /* Add alignment attribute if used */
518 if (param
.alignment
) {
519 struct cbfs_file_attr_align
*attrs
=
520 (struct cbfs_file_attr_align
*)
521 cbfs_add_file_attr(header
,
522 CBFS_FILE_ATTR_TAG_ALIGNMENT
,
523 sizeof(struct cbfs_file_attr_align
));
526 attrs
->alignment
= htonl(param
.alignment
);
531 /* Mark as Initial Boot Block */
532 struct cbfs_file_attribute
*attrs
= cbfs_add_file_attr(header
,
533 CBFS_FILE_ATTR_TAG_IBB
,
534 sizeof(struct cbfs_file_attribute
));
537 /* For Intel TXT minimum align is 16 */
542 const uint32_t hs
= sizeof(struct cbfs_file_attribute
);
543 uint32_t size
= MAX(hs
, param
.padding
);
544 INFO("Padding %d bytes\n", size
);
545 struct cbfs_file_attribute
*attr
=
546 (struct cbfs_file_attribute
*)cbfs_add_file_attr(
547 header
, CBFS_FILE_ATTR_TAG_PADDING
,
553 if (IS_TOP_ALIGNED_ADDRESS(offset
))
554 offset
= convert_to_from_top_aligned(param
.image_region
,
556 if (cbfs_add_entry(&image
, &buffer
, offset
, header
, len_align
) != 0) {
557 ERROR("Failed to add '%s' into ROM image.\n", filename
);
559 buffer_delete(&buffer
);
564 buffer_delete(&buffer
);
568 static int cbfstool_convert_raw(struct buffer
*buffer
,
569 unused
uint32_t *offset
, struct cbfs_file
*header
)
572 int decompressed_size
, compressed_size
;
573 comp_func_ptr compress
;
575 decompressed_size
= buffer
->size
;
576 if (param
.precompression
) {
577 param
.compression
= read_le32(buffer
->data
);
578 decompressed_size
= read_le32(buffer
->data
+ sizeof(uint32_t));
579 compressed_size
= buffer
->size
- 8;
580 compressed
= malloc(compressed_size
);
583 memcpy(compressed
, buffer
->data
+ 8, compressed_size
);
585 compress
= compression_function(param
.compression
);
588 compressed
= calloc(buffer
->size
, 1);
592 if (compress(buffer
->data
, buffer
->size
,
593 compressed
, &compressed_size
)) {
594 WARN("Compression failed - disabled\n");
600 struct cbfs_file_attr_compression
*attrs
=
601 (struct cbfs_file_attr_compression
*)
602 cbfs_add_file_attr(header
,
603 CBFS_FILE_ATTR_TAG_COMPRESSION
,
604 sizeof(struct cbfs_file_attr_compression
));
609 attrs
->compression
= htonl(param
.compression
);
610 attrs
->decompressed_size
= htonl(decompressed_size
);
613 buffer
->data
= compressed
;
614 buffer
->size
= compressed_size
;
616 header
->len
= htonl(buffer
->size
);
620 static int cbfstool_convert_fsp(struct buffer
*buffer
,
621 uint32_t *offset
, struct cbfs_file
*header
)
625 int do_relocation
= 1;
630 * If the FSP component is xip, then ensure that the address is a memory
632 * If the FSP component is not xip, then use param.baseaddress that is
633 * passed in by the caller.
635 if (param
.stage_xip
) {
636 if (!IS_TOP_ALIGNED_ADDRESS(address
))
637 address
= -convert_to_from_absolute_top_aligned(
638 param
.image_region
, address
);
640 if (param
.baseaddress_assigned
== 0) {
641 INFO("Honoring pre-linked FSP module.\n");
644 address
= param
.baseaddress
;
646 * *offset should either be 0 or the value returned by
647 * do_cbfs_locate. do_cbfs_locate is called only when param.baseaddress
648 * is not provided by user. Thus, set *offset to 0 if user provides
649 * a baseaddress i.e. params.baseaddress_assigned is set. The only
650 * requirement in this case is that the binary should be relocated to
651 * the base address that is requested. There is no requirement on where
652 * the file ends up in the cbfs.
659 * Nothing left to do if relocation is not being attempted. Just add
663 return cbfstool_convert_raw(buffer
, offset
, header
);
665 /* Create a copy of the buffer to attempt relocation. */
666 if (buffer_create(&fsp
, buffer_size(buffer
), "fsp"))
669 memcpy(buffer_get(&fsp
), buffer_get(buffer
), buffer_size(buffer
));
671 /* Replace the buffer contents w/ the relocated ones on success. */
672 if (fsp_component_relocate(address
, buffer_get(&fsp
), buffer_size(&fsp
))
674 buffer_delete(buffer
);
675 buffer_clone(buffer
, &fsp
);
678 WARN("Invalid FSP variant.\n");
681 /* Let the raw path handle all the cbfs metadata logic. */
682 return cbfstool_convert_raw(buffer
, offset
, header
);
685 static int cbfstool_convert_mkstage(struct buffer
*buffer
, uint32_t *offset
,
686 struct cbfs_file
*header
)
688 struct buffer output
;
691 if (param
.stage_xip
) {
695 if (elf_program_file_size(buffer
, &data_size
) < 0) {
696 ERROR("Could not obtain ELF size\n");
700 if (do_cbfs_locate(&address
, sizeof(struct cbfs_stage
),
702 ERROR("Could not find location for XIP stage.\n");
707 * Ensure the address is a memory mapped one. This assumes
708 * x86 semantics about the boot media being directly mapped
709 * below 4GiB in the CPU address space.
711 address
= -convert_to_from_absolute_top_aligned(
712 param
.image_region
, address
);
715 ret
= parse_elf_to_xip_stage(buffer
, &output
, offset
,
716 param
.ignore_section
);
718 ret
= parse_elf_to_stage(buffer
, &output
, param
.compression
,
719 offset
, param
.ignore_section
);
723 buffer_delete(buffer
);
724 // Direct assign, no dupe.
725 memcpy(buffer
, &output
, sizeof(*buffer
));
726 header
->len
= htonl(output
.size
);
730 static int cbfstool_convert_mkpayload(struct buffer
*buffer
,
731 unused
uint32_t *offset
, struct cbfs_file
*header
)
733 struct buffer output
;
735 /* Per default, try and see if payload is an ELF binary */
736 ret
= parse_elf_to_payload(buffer
, &output
, param
.compression
);
738 /* If it's not an ELF, see if it's a FIT */
740 ret
= parse_fit_to_payload(buffer
, &output
, param
.compression
);
742 header
->type
= htonl(CBFS_COMPONENT_FIT
);
745 /* If it's not an FIT, see if it's a UEFI FV */
747 ret
= parse_fv_to_payload(buffer
, &output
, param
.compression
);
749 /* If it's neither ELF nor UEFI Fv, try bzImage */
751 ret
= parse_bzImage_to_payload(buffer
, &output
,
752 param
.initrd
, param
.cmdline
, param
.compression
);
754 /* Not a supported payload type */
756 ERROR("Not a supported payload type (ELF / FV).\n");
757 buffer_delete(buffer
);
761 buffer_delete(buffer
);
762 // Direct assign, no dupe.
763 memcpy(buffer
, &output
, sizeof(*buffer
));
764 header
->len
= htonl(output
.size
);
768 static int cbfstool_convert_mkflatpayload(struct buffer
*buffer
,
769 unused
uint32_t *offset
, struct cbfs_file
*header
)
771 struct buffer output
;
772 if (parse_flat_binary_to_payload(buffer
, &output
,
775 param
.compression
) != 0) {
778 buffer_delete(buffer
);
779 // Direct assign, no dupe.
780 memcpy(buffer
, &output
, sizeof(*buffer
));
781 header
->len
= htonl(output
.size
);
785 static int cbfs_add(void)
788 convert_buffer_t convert
;
789 uint32_t local_baseaddress
= param
.baseaddress
;
791 if (param
.alignment
&& param
.baseaddress
) {
792 ERROR("Cannot specify both alignment and base address\n");
796 convert
= cbfstool_convert_raw
;
798 /* Set the alignment to 4KiB minimum for FSP blobs when no base address
799 * is provided so that relocation can occur. */
800 if (param
.type
== CBFS_COMPONENT_FSP
) {
801 if (!param
.baseaddress_assigned
)
802 param
.alignment
= 4*1024;
803 convert
= cbfstool_convert_fsp
;
804 } else if (param
.stage_xip
) {
805 ERROR("cbfs add supports xip only for FSP component type\n");
809 if (param
.alignment
) {
810 /* CBFS compression file attribute is unconditionally added. */
811 size_t metadata_sz
= sizeof(struct cbfs_file_attr_compression
);
812 if (do_cbfs_locate(&address
, metadata_sz
, 0))
814 local_baseaddress
= address
;
817 return cbfs_add_component(param
.filename
,
825 static int cbfs_add_stage(void)
827 if (param
.stage_xip
) {
828 if (param
.baseaddress_assigned
) {
829 ERROR("Cannot specify base address for XIP.\n");
833 if (param
.compression
!= CBFS_COMPRESS_NONE
) {
834 ERROR("Cannot specify compression for XIP.\n");
839 return cbfs_add_component(param
.filename
,
841 CBFS_COMPONENT_STAGE
,
844 cbfstool_convert_mkstage
);
847 static int cbfs_add_payload(void)
849 return cbfs_add_component(param
.filename
,
854 cbfstool_convert_mkpayload
);
857 static int cbfs_add_flat_binary(void)
859 if (param
.loadaddress
== 0) {
860 ERROR("You need to specify a valid "
861 "-l/--load-address.\n");
864 if (param
.entrypoint
== 0) {
865 ERROR("You need to specify a valid "
866 "-e/--entry-point.\n");
869 return cbfs_add_component(param
.filename
,
874 cbfstool_convert_mkflatpayload
);
877 static int cbfs_add_integer(void)
879 if (!param
.u64val_assigned
) {
880 ERROR("You need to specify a value to write.\n");
883 return cbfs_add_integer_component(param
.name
,
889 static int cbfs_remove(void)
892 ERROR("You need to specify -n/--name.\n");
896 struct cbfs_image image
;
897 if (cbfs_image_from_buffer(&image
, param
.image_region
,
901 if (cbfs_remove_entry(&image
, param
.name
) != 0) {
902 ERROR("Removing file '%s' failed.\n",
910 static int cbfs_create(void)
912 struct cbfs_image image
;
913 memset(&image
, 0, sizeof(image
));
914 buffer_clone(&image
.buffer
, param
.image_region
);
917 if (param
.arch
!= CBFS_ARCHITECTURE_UNKNOWN
|| param
.size
||
918 param
.baseaddress_assigned
||
919 param
.headeroffset_assigned
||
920 param
.cbfsoffset_assigned
||
922 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
926 return cbfs_image_create(&image
, image
.buffer
.size
);
929 if (param
.arch
== CBFS_ARCHITECTURE_UNKNOWN
) {
930 ERROR("You need to specify -m/--machine arch.\n");
934 struct buffer bootblock
;
935 if (!param
.bootblock
) {
936 DEBUG("-B not given, creating image without bootblock.\n");
937 if (buffer_create(&bootblock
, 0, "(dummy)") != 0)
939 } else if (buffer_from_file(&bootblock
, param
.bootblock
)) {
943 if (!param
.alignment
)
944 param
.alignment
= CBFS_ALIGNMENT
;
946 // Set default offsets. x86, as usual, needs to be a special snowflake.
947 if (!param
.baseaddress_assigned
) {
948 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
949 // Make sure there's at least enough room for rel_offset
950 param
.baseaddress
= param
.size
-
951 MAX(bootblock
.size
, sizeof(int32_t));
952 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
955 param
.baseaddress
= 0;
956 DEBUG("bootblock starts at address 0x0.\n");
959 if (!param
.headeroffset_assigned
) {
960 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
961 param
.headeroffset
= param
.baseaddress
-
962 sizeof(struct cbfs_header
);
963 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
966 param
.headeroffset
= align_up(param
.baseaddress
+
967 bootblock
.size
, sizeof(uint32_t));
968 DEBUG("CBFS header placed behind bootblock (%#x).\n",
972 if (!param
.cbfsoffset_assigned
) {
973 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
974 param
.cbfsoffset
= 0;
975 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
977 param
.cbfsoffset
= align_up(param
.headeroffset
+
978 sizeof(struct cbfs_header
),
980 DEBUG("CBFS entries start beind master header (%#x).\n",
985 int ret
= cbfs_legacy_image_create(&image
,
992 buffer_delete(&bootblock
);
996 static int cbfs_layout(void)
998 const struct fmap
*fmap
= partitioned_file_get_fmap(param
.image_file
);
1000 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1004 printf("This image contains the following sections that can be %s with this tool:\n",
1005 param
.show_immutable
? "accessed" : "manipulated");
1007 for (unsigned i
= 0; i
< fmap
->nareas
; ++i
) {
1008 const struct fmap_area
*current
= fmap
->areas
+ i
;
1010 bool readonly
= partitioned_file_fmap_count(param
.image_file
,
1011 partitioned_file_fmap_select_children_of
, current
) ||
1012 region_is_flashmap((const char *)current
->name
);
1013 if (!param
.show_immutable
&& readonly
)
1016 printf("'%s'", current
->name
);
1018 // Detect consecutive sections that describe the same region and
1019 // show them as aliases. This cannot find equivalent entries
1020 // that aren't adjacent; however, fmaptool doesn't generate
1021 // FMAPs with such sections, so this convenience feature works
1022 // for all but the strangest manually created FMAP binaries.
1023 // TODO: This could be done by parsing the FMAP into some kind
1024 // of tree that had duplicate lists in addition to child lists,
1025 // which would allow covering that weird, unlikely case as well.
1027 for (lookahead
= 1; i
+ lookahead
< fmap
->nareas
;
1029 const struct fmap_area
*consecutive
=
1030 fmap
->areas
+ i
+ lookahead
;
1031 if (consecutive
->offset
!= current
->offset
||
1032 consecutive
->size
!= current
->size
)
1034 printf(", '%s'", consecutive
->name
);
1037 fputs(" are aliases for the same region", stdout
);
1039 const char *qualifier
= "";
1041 qualifier
= "read-only, ";
1042 else if (region_is_modern_cbfs((const char *)current
->name
))
1043 qualifier
= "CBFS, ";
1044 else if (current
->flags
& FMAP_AREA_PRESERVE
)
1045 qualifier
= "preserve, ";
1046 printf(" (%ssize %u, offset %u)\n", qualifier
, current
->size
,
1053 if (param
.show_immutable
) {
1054 puts("It is at least possible to perform the read action on every section listed above.");
1056 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1057 puts("To see the image's read-only sections as well, rerun with the -w option.");
1063 static int cbfs_print(void)
1065 struct cbfs_image image
;
1066 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1067 param
.headeroffset
))
1069 if (param
.machine_parseable
)
1070 return cbfs_print_parseable_directory(&image
);
1072 printf("FMAP REGION: %s\n", param
.region_name
);
1073 return cbfs_print_directory(&image
);
1077 static int cbfs_extract(void)
1079 if (!param
.filename
) {
1080 ERROR("You need to specify -f/--filename.\n");
1085 ERROR("You need to specify -n/--name.\n");
1089 struct cbfs_image image
;
1090 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1091 param
.headeroffset
))
1094 return cbfs_export_entry(&image
, param
.name
, param
.filename
,
1095 param
.arch
, !param
.unprocessed
);
1098 static int cbfs_write(void)
1100 if (!param
.filename
) {
1101 ERROR("You need to specify a valid input -f/--file.\n");
1104 if (!partitioned_file_is_partitioned(param
.image_file
)) {
1105 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1109 if (!param
.force
&& region_is_modern_cbfs(param
.region_name
)) {
1110 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1115 struct buffer new_content
;
1116 if (buffer_from_file(&new_content
, param
.filename
))
1119 if (buffer_check_magic(&new_content
, FMAP_SIGNATURE
,
1120 strlen(FMAP_SIGNATURE
))) {
1121 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1123 buffer_delete(&new_content
);
1126 if (!param
.force
&& buffer_check_magic(&new_content
, CBFS_FILE_MAGIC
,
1127 strlen(CBFS_FILE_MAGIC
))) {
1128 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1130 buffer_delete(&new_content
);
1134 unsigned offset
= 0;
1135 if (param
.fill_partial_upward
&& param
.fill_partial_downward
) {
1136 ERROR("You may only specify one of -u and -d.\n");
1137 buffer_delete(&new_content
);
1139 } else if (!param
.fill_partial_upward
&& !param
.fill_partial_downward
) {
1140 if (new_content
.size
!= param
.image_region
->size
) {
1141 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",
1142 new_content
.size
, param
.image_region
->size
);
1143 buffer_delete(&new_content
);
1147 if (new_content
.size
> param
.image_region
->size
) {
1148 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1149 new_content
.size
, param
.image_region
->size
);
1150 buffer_delete(&new_content
);
1153 if (param
.u64val
== (uint64_t)-1) {
1154 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1155 param
.fill_partial_upward
? "bottom" : "top");
1156 } else if (param
.u64val
> 0xff) {
1157 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param
.u64val
& 0xff));
1158 buffer_delete(&new_content
);
1161 memset(buffer_get(param
.image_region
),
1162 param
.u64val
& 0xff,
1163 buffer_size(param
.image_region
));
1165 if (param
.fill_partial_downward
)
1166 offset
= param
.image_region
->size
- new_content
.size
;
1169 memcpy(param
.image_region
->data
+ offset
, new_content
.data
,
1171 buffer_delete(&new_content
);
1175 static int cbfs_read(void)
1177 if (!param
.filename
) {
1178 ERROR("You need to specify a valid output -f/--file.\n");
1181 if (!partitioned_file_is_partitioned(param
.image_file
)) {
1182 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1186 return buffer_write_file(param
.image_region
, param
.filename
);
1189 static int cbfs_copy(void)
1191 struct cbfs_image src_image
;
1192 struct buffer src_buf
;
1194 if (!param
.source_region
) {
1195 ERROR("You need to specify -R/--source-region.\n");
1199 /* Obtain the source region and convert it to a cbfs_image. */
1200 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1201 param
.source_region
)) {
1202 ERROR("Region not found in image: %s\n", param
.source_region
);
1206 if (cbfs_image_from_buffer(&src_image
, &src_buf
, param
.headeroffset
))
1209 return cbfs_copy_instance(&src_image
, param
.image_region
);
1212 static int cbfs_compact(void)
1214 struct cbfs_image image
;
1215 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1216 param
.headeroffset
))
1218 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1219 return cbfs_compact_instance(&image
);
1222 static int cbfs_expand(void)
1224 struct buffer src_buf
;
1226 /* Obtain the source region. */
1227 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1228 param
.region_name
)) {
1229 ERROR("Region not found in image: %s\n", param
.source_region
);
1233 return cbfs_expand_to_region(param
.image_region
);
1236 static int cbfs_truncate(void)
1238 struct buffer src_buf
;
1240 /* Obtain the source region. */
1241 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1242 param
.region_name
)) {
1243 ERROR("Region not found in image: %s\n", param
.source_region
);
1248 int result
= cbfs_truncate_space(param
.image_region
, &size
);
1249 printf("0x%x\n", size
);
1253 static const struct command commands
[] = {
1254 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add
, true, true},
1255 {"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary
,
1257 {"add-payload", "H:r:f:n:c:b:C:I:p:vA:gh?", cbfs_add_payload
,
1259 {"add-stage", "a:H:r:f:n:t:c:b:P:S:p:yvA:gh?", cbfs_add_stage
,
1261 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer
, true, true},
1262 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header
, true, true},
1263 {"compact", "r:h?", cbfs_compact
, true, true},
1264 {"copy", "r:R:h?", cbfs_copy
, true, true},
1265 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create
, true, true},
1266 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract
, true, false},
1267 {"layout", "wvh?", cbfs_layout
, false, false},
1268 {"print", "H:r:vkh?", cbfs_print
, true, false},
1269 {"read", "r:f:vh?", cbfs_read
, true, false},
1270 {"remove", "H:r:n:vh?", cbfs_remove
, true, true},
1271 {"write", "r:f:i:Fudvh?", cbfs_write
, true, true},
1272 {"expand", "r:h?", cbfs_expand
, true, true},
1273 {"truncate", "r:h?", cbfs_truncate
, true, true},
1277 /* begin after ASCII characters */
1278 LONGOPT_START
= 256,
1279 LONGOPT_IBB
= LONGOPT_START
,
1283 static struct option long_options
[] = {
1284 {"alignment", required_argument
, 0, 'a' },
1285 {"base-address", required_argument
, 0, 'b' },
1286 {"bootblock", required_argument
, 0, 'B' },
1287 {"cmdline", required_argument
, 0, 'C' },
1288 {"compression", required_argument
, 0, 'c' },
1289 {"topswap-size", required_argument
, 0, 'j' },
1290 {"empty-fits", required_argument
, 0, 'x' },
1291 {"entry-point", required_argument
, 0, 'e' },
1292 {"file", required_argument
, 0, 'f' },
1293 {"fill-downward", no_argument
, 0, 'd' },
1294 {"fill-upward", no_argument
, 0, 'u' },
1295 {"flashmap", required_argument
, 0, 'M' },
1296 {"fmap-regions", required_argument
, 0, 'r' },
1297 {"force", no_argument
, 0, 'F' },
1298 {"source-region", required_argument
, 0, 'R' },
1299 {"hash-algorithm",required_argument
, 0, 'A' },
1300 {"header-offset", required_argument
, 0, 'H' },
1301 {"help", no_argument
, 0, 'h' },
1302 {"ignore-sec", required_argument
, 0, 'S' },
1303 {"initrd", required_argument
, 0, 'I' },
1304 {"int", required_argument
, 0, 'i' },
1305 {"load-address", required_argument
, 0, 'l' },
1306 {"machine", required_argument
, 0, 'm' },
1307 {"name", required_argument
, 0, 'n' },
1308 {"offset", required_argument
, 0, 'o' },
1309 {"padding", required_argument
, 0, 'p' },
1310 {"page-size", required_argument
, 0, 'P' },
1311 {"ucode-region", required_argument
, 0, 'q' },
1312 {"size", required_argument
, 0, 's' },
1313 {"top-aligned", required_argument
, 0, 'T' },
1314 {"type", required_argument
, 0, 't' },
1315 {"verbose", no_argument
, 0, 'v' },
1316 {"with-readonly", no_argument
, 0, 'w' },
1317 {"xip", no_argument
, 0, 'y' },
1318 {"gen-attribute", no_argument
, 0, 'g' },
1319 {"mach-parseable",no_argument
, 0, 'k' },
1320 {"unprocessed", no_argument
, 0, 'U' },
1321 {"ibb", no_argument
, 0, LONGOPT_IBB
},
1325 static int dispatch_command(struct command command
)
1327 if (command
.accesses_region
) {
1328 assert(param
.image_file
);
1330 if (partitioned_file_is_partitioned(param
.image_file
)) {
1331 INFO("Performing operation on '%s' region...\n",
1334 if (!partitioned_file_read_region(param
.image_region
,
1335 param
.image_file
, param
.region_name
)) {
1336 ERROR("The image will be left unmodified.\n");
1340 if (command
.modifies_region
) {
1341 // We (intentionally) don't support overwriting the FMAP
1342 // section. If you find yourself wanting to do this,
1343 // consider creating a new image rather than performing
1344 // whatever hacky transformation you were planning.
1345 if (region_is_flashmap(param
.region_name
)) {
1346 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1348 ERROR("The image will be left unmodified.\n");
1351 // We don't allow writing raw data to regions that
1352 // contain nested regions, since doing so would
1353 // overwrite all such subregions.
1354 if (partitioned_file_region_contains_nested(
1355 param
.image_file
, param
.region_name
)) {
1356 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1358 ERROR("The image will be left unmodified.\n");
1364 if (command
.function()) {
1365 if (partitioned_file_is_partitioned(param
.image_file
)) {
1366 ERROR("Failed while operating on '%s' region!\n",
1368 ERROR("The image will be left unmodified.\n");
1376 static void usage(char *name
)
1379 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
1380 "USAGE:\n" " %s [-h]\n"
1381 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
1382 " -H header_offset Do not search for header; use this offset*\n"
1383 " -T Output top-aligned memory address\n"
1384 " -u Accept short data; fill upward/from bottom\n"
1385 " -d Accept short data; fill downward/from top\n"
1386 " -F Force action\n"
1387 " -g Generate position and alignment arguments\n"
1388 " -U Unprocessed; don't decompress or make ELF\n"
1389 " -v Provide verbose output\n"
1390 " -h Display this help message\n\n"
1392 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
1393 " [-c compression] [-b base-address | -a alignment] \\\n"
1394 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
1395 " [-j topswap-size] (Intel CPUs only) [--ibb] "
1398 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
1399 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1400 " [-c compression] [-b base-address] \\\n"
1401 " (linux specific: [-C cmdline] [-I initrd]) "
1402 "Add a payload to the ROM\n"
1403 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1404 " [-c compression] [-b base] [-S section-to-ignore] \\\n"
1405 " [-a alignment] [-y|--xip] [-P page-size] [--ibb] "
1406 "Add a stage to the ROM\n"
1407 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1408 " [-A hash] -l load-address -e entry-point \\\n"
1409 " [-c compression] [-b base] "
1410 "Add a 32bit flat mode binary\n"
1411 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
1412 "Add a raw 64-bit integer value\n"
1413 " add-master-header [-r image,regions] \\ \n"
1414 " [-j topswap-size] (Intel CPUs only) "
1415 "Add a legacy CBFS master header\n"
1416 " remove [-r image,regions] -n NAME "
1417 "Remove a component\n"
1418 " compact -r image,regions "
1419 "Defragment CBFS image.\n"
1420 " copy -r image,regions -R source-region "
1421 "Create a copy (duplicate) cbfs instance in fmap\n"
1422 " create -m ARCH -s size [-b bootblock offset] \\\n"
1423 " [-o CBFS offset] [-H header offset] [-B bootblock] "
1424 "Create a legacy ROM file with CBFS master header*\n"
1425 " create -M flashmap [-r list,of,regions,containing,cbfses] "
1426 "Create a new-style partitioned firmware image\n"
1427 " locate [-r image,regions] -f FILE -n NAME [-P page-size] \\\n"
1429 "Find a place for a file of that size\n"
1431 "List mutable (or, with -w, readable) image regions\n"
1432 " print [-r image,regions] "
1433 "Show the contents of the ROM\n"
1434 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
1435 "Extracts a file from ROM\n"
1436 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
1437 "Write file into same-size [or larger] raw region\n"
1438 " read [-r fmap-region] -f file "
1439 "Extract raw region contents into binary file\n"
1440 " truncate [-r fmap-region] "
1441 "Truncate CBFS and print new size on stdout\n"
1442 " expand [-r fmap-region] "
1443 "Expand CBFS to span entire region\n"
1445 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
1446 " in two possible formats: if their value is greater than\n"
1447 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
1448 " address; otherwise, they are treated as an offset into flash.\n"
1449 "ARCHes:\n", name
, name
1451 print_supported_architectures();
1454 print_supported_filetypes();
1456 "\n* Note that these actions and switches are only valid when\n"
1457 " working with legacy images whose structure is described\n"
1458 " primarily by a CBFS master header. New-style images, in\n"
1459 " contrast, exclusively make use of an FMAP to describe their\n"
1460 " layout: this must minimally contain an '%s' section\n"
1461 " specifying the location of this FMAP itself and a '%s'\n"
1462 " section describing the primary CBFS. It should also be noted\n"
1463 " that, when working with such images, the -F and -r switches\n"
1464 " default to '%s' for convenience, and both the -b switch to\n"
1465 " CBFS operations and the output of the locate action become\n"
1466 " relative to the selected CBFS region's lowest address.\n"
1467 " The one exception to this rule is the top-aligned address,\n"
1468 " which is always relative to the end of the entire image\n"
1469 " rather than relative to the local region; this is true for\n"
1470 " for both input (sufficiently large) and output (-T) data.\n",
1471 SECTION_NAME_FMAP
, SECTION_NAME_PRIMARY_CBFS
,
1472 SECTION_NAME_PRIMARY_CBFS
1476 static bool valid_opt(size_t i
, int c
)
1478 /* Check if it is one of the optstrings supported by the command. */
1479 if (strchr(commands
[i
].optstring
, c
))
1483 * Check if it is one of the non-ASCII characters. Currently, the
1484 * non-ASCII characters are only checked against the valid list
1485 * irrespective of the command.
1487 if (c
>= LONGOPT_START
&& c
< LONGOPT_END
)
1493 int main(int argc
, char **argv
)
1503 char *image_name
= argv
[1];
1504 char *cmd
= argv
[2];
1507 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
1508 if (strcmp(cmd
, commands
[i
].name
) != 0)
1512 char *suffix
= NULL
;
1513 int option_index
= 0;
1515 c
= getopt_long(argc
, argv
, commands
[i
].optstring
,
1516 long_options
, &option_index
);
1518 if (optind
< argc
) {
1519 ERROR("%s: excessive argument -- '%s'"
1520 "\n", argv
[0], argv
[optind
]);
1526 /* Filter out illegal long options */
1527 if (!valid_opt(i
, c
)) {
1528 ERROR("%s: invalid option -- '%d'\n",
1535 param
.name
= optarg
;
1538 if (intfiletype(optarg
) != ((uint64_t) - 1))
1539 param
.type
= intfiletype(optarg
);
1541 param
.type
= strtoul(optarg
, NULL
, 0);
1542 if (param
.type
== 0)
1543 WARN("Unknown type '%s' ignored\n",
1547 if (strcmp(optarg
, "precompression") == 0) {
1548 param
.precompression
= 1;
1551 int algo
= cbfs_parse_comp_algo(optarg
);
1553 param
.compression
= algo
;
1555 WARN("Unknown compression '%s' ignored.\n",
1560 int algo
= cbfs_parse_hash_algo(optarg
);
1564 ERROR("Unknown hash algorithm '%s'.\n",
1571 param
.fmap
= optarg
;
1574 param
.region_name
= optarg
;
1577 param
.source_region
= optarg
;
1580 param
.baseaddress
= strtoul(optarg
, &suffix
, 0);
1581 if (!*optarg
|| (suffix
&& *suffix
)) {
1582 ERROR("Invalid base address '%s'.\n",
1586 // baseaddress may be zero on non-x86, so we
1587 // need an explicit "baseaddress_assigned".
1588 param
.baseaddress_assigned
= 1;
1591 param
.loadaddress
= strtoul(optarg
, &suffix
, 0);
1592 if (!*optarg
|| (suffix
&& *suffix
)) {
1593 ERROR("Invalid load address '%s'.\n",
1599 param
.entrypoint
= strtoul(optarg
, &suffix
, 0);
1600 if (!*optarg
|| (suffix
&& *suffix
)) {
1601 ERROR("Invalid entry point '%s'.\n",
1607 param
.size
= strtoul(optarg
, &suffix
, 0);
1609 ERROR("Empty size specified.\n");
1612 switch (tolower((int)suffix
[0])) {
1617 param
.size
*= 1024 * 1024;
1622 ERROR("Invalid suffix for size '%s'.\n",
1628 param
.bootblock
= optarg
;
1631 param
.headeroffset
= strtoul(
1632 optarg
, &suffix
, 0);
1633 if (!*optarg
|| (suffix
&& *suffix
)) {
1634 ERROR("Invalid header offset '%s'.\n",
1638 param
.headeroffset_assigned
= 1;
1641 param
.alignment
= strtoul(optarg
, &suffix
, 0);
1642 if (!*optarg
|| (suffix
&& *suffix
)) {
1643 ERROR("Invalid alignment '%s'.\n",
1649 param
.padding
= strtoul(optarg
, &suffix
, 0);
1650 if (!*optarg
|| (suffix
&& *suffix
)) {
1651 ERROR("Invalid pad size '%s'.\n",
1657 param
.pagesize
= strtoul(optarg
, &suffix
, 0);
1658 if (!*optarg
|| (suffix
&& *suffix
)) {
1659 ERROR("Invalid page size '%s'.\n",
1665 param
.cbfsoffset
= strtoul(optarg
, &suffix
, 0);
1666 if (!*optarg
|| (suffix
&& *suffix
)) {
1667 ERROR("Invalid cbfs offset '%s'.\n",
1671 param
.cbfsoffset_assigned
= 1;
1674 param
.filename
= optarg
;
1680 param
.u64val
= strtoull(optarg
, &suffix
, 0);
1681 param
.u64val_assigned
= 1;
1682 if (!*optarg
|| (suffix
&& *suffix
)) {
1683 ERROR("Invalid int parameter '%s'.\n",
1689 param
.fill_partial_upward
= true;
1692 param
.fill_partial_downward
= true;
1695 param
.show_immutable
= true;
1698 param
.topswap_size
= strtol(optarg
, NULL
, 0);
1699 if (!is_valid_topswap())
1703 param
.ucode_region
= optarg
;
1709 param
.arch
= string_to_arch(optarg
);
1712 param
.initrd
= optarg
;
1715 param
.cmdline
= optarg
;
1718 param
.ignore_section
= optarg
;
1721 param
.stage_xip
= true;
1724 param
.autogen_attr
= true;
1727 param
.machine_parseable
= true;
1730 param
.unprocessed
= true;
1744 if (commands
[i
].function
== cbfs_create
) {
1746 struct buffer flashmap
;
1747 if (buffer_from_file(&flashmap
, param
.fmap
))
1749 param
.image_file
= partitioned_file_create(
1750 image_name
, &flashmap
);
1751 buffer_delete(&flashmap
);
1752 } else if (param
.size
) {
1753 param
.image_file
= partitioned_file_create_flat(
1754 image_name
, param
.size
);
1756 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
1760 bool write_access
= commands
[i
].modifies_region
;
1763 partitioned_file_reopen(image_name
,
1766 if (!param
.image_file
)
1769 unsigned num_regions
= 1;
1770 for (const char *list
= strchr(param
.region_name
, ','); list
;
1771 list
= strchr(list
+ 1, ','))
1774 // If the action needs to read an image region, as indicated by
1775 // having accesses_region set in its command struct, that
1776 // region's buffer struct will be stored here and the client
1777 // will receive a pointer to it via param.image_region. It
1778 // need not write the buffer back to the image file itself,
1779 // since this behavior can be requested via its modifies_region
1780 // field. Additionally, it should never free the region buffer,
1781 // as that is performed automatically once it completes.
1782 struct buffer image_regions
[num_regions
];
1783 memset(image_regions
, 0, sizeof(image_regions
));
1785 bool seen_primary_cbfs
= false;
1786 char region_name_scratch
[strlen(param
.region_name
) + 1];
1787 strcpy(region_name_scratch
, param
.region_name
);
1788 param
.region_name
= strtok(region_name_scratch
, ",");
1789 for (unsigned region
= 0; region
< num_regions
; ++region
) {
1790 if (!param
.region_name
) {
1791 ERROR("Encountered illegal degenerate region name in -r list\n");
1792 ERROR("The image will be left unmodified.\n");
1793 partitioned_file_close(param
.image_file
);
1797 if (strcmp(param
.region_name
, SECTION_NAME_PRIMARY_CBFS
)
1799 seen_primary_cbfs
= true;
1801 param
.image_region
= image_regions
+ region
;
1802 if (dispatch_command(commands
[i
])) {
1803 partitioned_file_close(param
.image_file
);
1807 param
.region_name
= strtok(NULL
, ",");
1810 if (commands
[i
].function
== cbfs_create
&& !seen_primary_cbfs
) {
1811 ERROR("The creation -r list must include the mandatory '%s' section.\n",
1812 SECTION_NAME_PRIMARY_CBFS
);
1813 ERROR("The image will be left unmodified.\n");
1814 partitioned_file_close(param
.image_file
);
1818 if (commands
[i
].modifies_region
) {
1819 assert(param
.image_file
);
1820 for (unsigned region
= 0; region
< num_regions
;
1823 if (!partitioned_file_write_region(
1825 image_regions
+ region
)) {
1826 partitioned_file_close(
1833 partitioned_file_close(param
.image_file
);
1837 ERROR("Unknown command '%s'.\n", cmd
);