2 * cbfstool, CLI utility for CBFS file manipulation
4 * Copyright (C) 2009 coresystems GmbH
5 * written by Patrick Georgi <patrick.georgi@coresystems.de>
6 * Copyright (C) 2012 Google, Inc.
7 * Copyright (C) 2016 Siemens AG
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
28 #include "cbfs_image.h"
29 #include "cbfs_sections.h"
30 #include "elfparsing.h"
32 #include "partitioned_file.h"
33 #include <commonlib/fsp.h>
34 #include <commonlib/endian.h>
35 #include <commonlib/helpers.h>
37 #define SECTION_WITH_FIT_TABLE "BOOTBLOCK"
41 const char *optstring
;
42 int (*function
) (void);
43 // Whether to populate param.image_region before invoking function
45 // This set to true means two things:
46 // - in case of a command operating on a region, the region's contents
47 // will be written back to image_file at the end
48 // - write access to the file is required
53 partitioned_file_t
*image_file
;
54 struct buffer
*image_region
;
58 const char *region_name
;
59 const char *source_region
;
60 const char *bootblock
;
61 const char *ignore_section
;
62 const char *ucode_region
;
66 uint32_t baseaddress_assigned
;
68 uint32_t headeroffset
;
69 uint32_t headeroffset_assigned
;
75 uint32_t cbfsoffset_assigned
;
78 uint32_t topswap_size
;
80 bool fill_partial_upward
;
81 bool fill_partial_downward
;
85 bool machine_parseable
;
87 int fit_empty_entries
;
88 enum comp_algo compression
;
90 enum vb2_hash_algorithm hash
;
91 /* For linux payloads */
96 /* All variables not listed are initialized as zero. */
97 .arch
= CBFS_ARCHITECTURE_UNKNOWN
,
98 .compression
= CBFS_COMPRESS_NONE
,
99 .hash
= VB2_HASH_INVALID
,
101 .region_name
= SECTION_NAME_PRIMARY_CBFS
,
105 static bool region_is_flashmap(const char *region
)
107 return partitioned_file_region_check_magic(param
.image_file
, region
,
108 FMAP_SIGNATURE
, strlen(FMAP_SIGNATURE
));
111 /* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
112 static bool region_is_modern_cbfs(const char *region
)
114 return partitioned_file_region_check_magic(param
.image_file
, region
,
115 CBFS_FILE_MAGIC
, strlen(CBFS_FILE_MAGIC
));
119 * Converts between offsets from the start of the specified image region and
120 * "top-aligned" offsets from the top of the entire boot media. See comment
121 * below for convert_to_from_top_aligned() about forming addresses.
123 static unsigned convert_to_from_absolute_top_aligned(
124 const struct buffer
*region
, unsigned offset
)
128 size_t image_size
= partitioned_file_total_size(param
.image_file
);
130 return image_size
- region
->offset
- offset
;
134 * Converts between offsets from the start of the specified image region and
135 * "top-aligned" offsets from the top of the image region. Works in either
136 * direction: pass in one type of offset and receive the other type.
137 * N.B. A top-aligned offset is always a positive number, and should not be
138 * confused with a top-aliged *address*, which is its arithmetic inverse. */
139 static unsigned convert_to_from_top_aligned(const struct buffer
*region
,
144 /* Cover the situation where a negative base address is given by the
145 * user. Callers of this function negate it, so it'll be a positive
146 * number smaller than the region.
148 if ((offset
> 0) && (offset
< region
->size
)) {
149 return region
->size
- offset
;
152 return convert_to_from_absolute_top_aligned(region
, offset
);
155 static int do_cbfs_locate(int32_t *cbfs_addr
, size_t metadata_size
,
158 if (!param
.filename
) {
159 ERROR("You need to specify -f/--filename.\n");
164 ERROR("You need to specify -n/--name.\n");
168 struct cbfs_image image
;
169 if (cbfs_image_from_buffer(&image
, param
.image_region
,
173 if (cbfs_get_entry(&image
, param
.name
))
174 WARN("'%s' already in CBFS.\n", param
.name
);
177 struct buffer buffer
;
178 if (buffer_from_file(&buffer
, param
.filename
) != 0) {
179 ERROR("Cannot load %s.\n", param
.filename
);
182 data_size
= buffer
.size
;
183 buffer_delete(&buffer
);
186 DEBUG("File size is %zd (0x%zx)\n", data_size
, data_size
);
188 /* Include cbfs_file size along with space for with name. */
189 metadata_size
+= cbfs_calculate_file_header_size(param
.name
);
190 /* Adjust metadata_size if additional attributes were added */
191 if (param
.autogen_attr
) {
193 metadata_size
+= sizeof(struct cbfs_file_attr_align
);
194 if (param
.baseaddress_assigned
|| param
.stage_xip
)
195 metadata_size
+= sizeof(struct cbfs_file_attr_position
);
198 /* Take care of the hash attribute if it is used */
199 if (param
.hash
!= VB2_HASH_INVALID
)
200 metadata_size
+= sizeof(struct cbfs_file_attr_hash
);
202 int32_t address
= cbfs_locate_entry(&image
, data_size
, param
.pagesize
,
203 param
.alignment
, metadata_size
);
206 ERROR("'%s' can't fit in CBFS for page-size %#x, align %#x.\n",
207 param
.name
, param
.pagesize
, param
.alignment
);
211 *cbfs_addr
= address
;
215 typedef int (*convert_buffer_t
)(struct buffer
*buffer
, uint32_t *offset
,
216 struct cbfs_file
*header
);
218 static int cbfs_add_integer_component(const char *name
,
221 uint32_t headeroffset
) {
222 struct cbfs_image image
;
223 struct cbfs_file
*header
= NULL
;
224 struct buffer buffer
;
228 ERROR("You need to specify -n/--name.\n");
232 if (buffer_create(&buffer
, 8, name
) != 0)
235 for (i
= 0; i
< 8; i
++)
236 buffer
.data
[i
] = (u64val
>> i
*8) & 0xff;
238 if (cbfs_image_from_buffer(&image
, param
.image_region
, headeroffset
)) {
239 ERROR("Selected image region is not a CBFS.\n");
243 if (cbfs_get_entry(&image
, name
)) {
244 ERROR("'%s' already in ROM image.\n", name
);
248 if (IS_TOP_ALIGNED_ADDRESS(offset
))
249 offset
= convert_to_from_top_aligned(param
.image_region
,
252 header
= cbfs_create_file_header(CBFS_COMPONENT_RAW
,
254 if (cbfs_add_entry(&image
, &buffer
, offset
, header
) != 0) {
255 ERROR("Failed to add %llu into ROM image as '%s'.\n",
256 (long long unsigned)u64val
, name
);
264 buffer_delete(&buffer
);
268 static int is_valid_topswap(void)
270 switch (param
.topswap_size
) {
278 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
285 static void fill_header_offset(void *location
, uint32_t offset
)
287 // TODO: When we have a BE target, we'll need to store this as BE
288 write_le32(location
, offset
);
291 static int update_master_header_loc_topswap(struct cbfs_image
*image
,
292 void *h_loc
, uint32_t header_offset
)
294 struct cbfs_file
*entry
;
295 void *ts_h_loc
= h_loc
;
297 entry
= cbfs_get_entry(image
, "bootblock");
299 ERROR("Bootblock not in ROM image?!?\n");
304 * Check if the existing topswap boundary matches with
307 if (param
.topswap_size
!= ntohl(entry
->len
)/2) {
308 ERROR("Top swap boundary does not match\n");
312 ts_h_loc
-= param
.topswap_size
;
313 fill_header_offset(ts_h_loc
, header_offset
);
318 static int cbfs_add_master_header(void)
320 const char * const name
= "cbfs master header";
321 struct cbfs_image image
;
322 struct cbfs_file
*header
= NULL
;
323 struct buffer buffer
;
329 if (cbfs_image_from_buffer(&image
, param
.image_region
,
330 param
.headeroffset
)) {
331 ERROR("Selected image region is not a CBFS.\n");
335 if (cbfs_get_entry(&image
, name
)) {
336 ERROR("'%s' already in ROM image.\n", name
);
340 if (buffer_create(&buffer
, sizeof(struct cbfs_header
), name
) != 0)
343 struct cbfs_header
*h
= (struct cbfs_header
*)buffer
.data
;
344 h
->magic
= htonl(CBFS_HEADER_MAGIC
);
345 h
->version
= htonl(CBFS_HEADER_VERSION
);
346 /* The 4 bytes are left out for two reasons:
347 * 1. the cbfs master header pointer resides there
348 * 2. some cbfs implementations assume that an image that resides
349 * below 4GB has a bootblock and get confused when the end of the
350 * image is at 4GB == 0.
352 h
->bootblocksize
= htonl(4);
353 h
->align
= htonl(CBFS_ENTRY_ALIGNMENT
);
354 /* The offset and romsize fields within the master header are absolute
355 * values within the boot media. As such, romsize needs to relfect
356 * the end 'offset' for a CBFS. To achieve that the current buffer
357 * representing the CBFS region's size is added to the offset of
358 * the region within a larger image.
360 offset
= buffer_get(param
.image_region
) -
361 buffer_get_original_backing(param
.image_region
);
362 size
= buffer_size(param
.image_region
);
363 h
->romsize
= htonl(size
+ offset
);
364 h
->offset
= htonl(offset
);
365 h
->architecture
= htonl(CBFS_ARCHITECTURE_UNKNOWN
);
367 header
= cbfs_create_file_header(CBFS_COMPONENT_CBFSHEADER
,
368 buffer_size(&buffer
), name
);
369 if (cbfs_add_entry(&image
, &buffer
, 0, header
) != 0) {
370 ERROR("Failed to add cbfs master header into ROM image.\n");
374 struct cbfs_file
*entry
;
375 if ((entry
= cbfs_get_entry(&image
, name
)) == NULL
) {
376 ERROR("'%s' not in ROM image?!?\n", name
);
380 uint32_t header_offset
= CBFS_SUBHEADER(entry
) -
381 buffer_get(&image
.buffer
);
382 header_offset
= -(buffer_size(&image
.buffer
) - header_offset
);
384 h_loc
= (void *)(buffer_get(&image
.buffer
) +
385 buffer_size(&image
.buffer
) - 4);
386 fill_header_offset(h_loc
, header_offset
);
388 * If top swap present, update the header
389 * location in secondary bootblock
391 if (param
.topswap_size
) {
392 if (update_master_header_loc_topswap(&image
, h_loc
,
401 buffer_delete(&buffer
);
405 static int add_topswap_bootblock(struct buffer
*buffer
, uint32_t *offset
)
407 size_t bb_buf_size
= buffer_size(buffer
);
409 if (bb_buf_size
> param
.topswap_size
) {
410 ERROR("Bootblock bigger than the topswap boundary\n");
411 ERROR("size = %zd, ts = %d\n", bb_buf_size
,
417 * Allocate topswap_size*2 bytes for bootblock to
418 * accommodate the second bootblock.
420 struct buffer new_bootblock
, bb1
, bb2
;
421 if (buffer_create(&new_bootblock
, 2 * param
.topswap_size
,
425 buffer_splice(&bb1
, &new_bootblock
, param
.topswap_size
- bb_buf_size
,
427 buffer_splice(&bb2
, &new_bootblock
,
428 buffer_size(&new_bootblock
) - bb_buf_size
,
431 /* Copy to first bootblock */
432 memcpy(buffer_get(&bb1
), buffer_get(buffer
), bb_buf_size
);
433 /* Copy to second bootblock */
434 memcpy(buffer_get(&bb2
), buffer_get(buffer
), bb_buf_size
);
436 buffer_delete(buffer
);
437 buffer_clone(buffer
, &new_bootblock
);
439 /* Update the location (offset) of bootblock in the region */
440 *offset
= convert_to_from_top_aligned(param
.image_region
,
441 buffer_size(buffer
));
446 static int cbfs_add_component(const char *filename
,
450 uint32_t headeroffset
,
451 convert_buffer_t convert
)
454 ERROR("You need to specify -f/--filename.\n");
459 ERROR("You need to specify -n/--name.\n");
464 ERROR("You need to specify a valid -t/--type.\n");
468 struct cbfs_image image
;
469 if (cbfs_image_from_buffer(&image
, param
.image_region
, headeroffset
))
472 if (cbfs_get_entry(&image
, name
)) {
473 ERROR("'%s' already in ROM image.\n", name
);
477 struct buffer buffer
;
478 if (buffer_from_file(&buffer
, filename
) != 0) {
479 ERROR("Could not load file '%s'.\n", filename
);
484 * Check if Intel CPU topswap is specified this will require a
485 * second bootblock to be added.
487 if (type
== CBFS_COMPONENT_BOOTBLOCK
&& param
.topswap_size
)
488 if (add_topswap_bootblock(&buffer
, &offset
))
491 struct cbfs_file
*header
=
492 cbfs_create_file_header(type
, buffer
.size
, name
);
494 if (convert
&& convert(&buffer
, &offset
, header
) != 0) {
495 ERROR("Failed to parse file '%s'.\n", filename
);
496 buffer_delete(&buffer
);
500 if (param
.hash
!= VB2_HASH_INVALID
)
501 if (cbfs_add_file_hash(header
, &buffer
, param
.hash
) == -1) {
502 ERROR("couldn't add hash for '%s'\n", name
);
504 buffer_delete(&buffer
);
508 if (param
.autogen_attr
) {
509 /* Add position attribute if assigned */
510 if (param
.baseaddress_assigned
|| param
.stage_xip
) {
511 struct cbfs_file_attr_position
*attrs
=
512 (struct cbfs_file_attr_position
*)
513 cbfs_add_file_attr(header
,
514 CBFS_FILE_ATTR_TAG_POSITION
,
515 sizeof(struct cbfs_file_attr_position
));
518 /* If we add a stage or a payload, we need to take */
519 /* care about the additional metadata that is added */
520 /* to the cbfs file and therefore set the position */
521 /* the real beginning of the data. */
522 if (type
== CBFS_COMPONENT_STAGE
)
523 attrs
->position
= htonl(offset
+
524 sizeof(struct cbfs_stage
));
525 else if (type
== CBFS_COMPONENT_SELF
)
526 attrs
->position
= htonl(offset
+
527 sizeof(struct cbfs_payload
));
529 attrs
->position
= htonl(offset
);
531 /* Add alignment attribute if used */
532 if (param
.alignment
) {
533 struct cbfs_file_attr_align
*attrs
=
534 (struct cbfs_file_attr_align
*)
535 cbfs_add_file_attr(header
,
536 CBFS_FILE_ATTR_TAG_ALIGNMENT
,
537 sizeof(struct cbfs_file_attr_align
));
540 attrs
->alignment
= htonl(param
.alignment
);
545 const uint32_t hs
= sizeof(struct cbfs_file_attribute
);
546 uint32_t size
= MAX(hs
, param
.padding
);
547 INFO("Padding %d bytes\n", size
);
548 struct cbfs_file_attribute
*attr
=
549 (struct cbfs_file_attribute
*)cbfs_add_file_attr(
550 header
, CBFS_FILE_ATTR_TAG_PADDING
,
556 if (IS_TOP_ALIGNED_ADDRESS(offset
))
557 offset
= convert_to_from_top_aligned(param
.image_region
,
559 if (cbfs_add_entry(&image
, &buffer
, offset
, header
) != 0) {
560 ERROR("Failed to add '%s' into ROM image.\n", filename
);
562 buffer_delete(&buffer
);
567 buffer_delete(&buffer
);
571 static int cbfstool_convert_raw(struct buffer
*buffer
,
572 unused
uint32_t *offset
, struct cbfs_file
*header
)
575 int decompressed_size
, compressed_size
;
576 comp_func_ptr compress
;
578 decompressed_size
= buffer
->size
;
579 if (param
.precompression
) {
580 param
.compression
= read_le32(buffer
->data
);
581 decompressed_size
= read_le32(buffer
->data
+ sizeof(uint32_t));
582 compressed_size
= buffer
->size
- 8;
583 compressed
= malloc(compressed_size
);
586 memcpy(compressed
, buffer
->data
+ 8, compressed_size
);
588 compress
= compression_function(param
.compression
);
591 compressed
= calloc(buffer
->size
, 1);
595 if (compress(buffer
->data
, buffer
->size
,
596 compressed
, &compressed_size
)) {
597 WARN("Compression failed - disabled\n");
603 struct cbfs_file_attr_compression
*attrs
=
604 (struct cbfs_file_attr_compression
*)
605 cbfs_add_file_attr(header
,
606 CBFS_FILE_ATTR_TAG_COMPRESSION
,
607 sizeof(struct cbfs_file_attr_compression
));
612 attrs
->compression
= htonl(param
.compression
);
613 attrs
->decompressed_size
= htonl(decompressed_size
);
616 buffer
->data
= compressed
;
617 buffer
->size
= compressed_size
;
619 header
->len
= htonl(buffer
->size
);
623 static int cbfstool_convert_fsp(struct buffer
*buffer
,
624 uint32_t *offset
, struct cbfs_file
*header
)
628 int do_relocation
= 1;
633 * If the FSP component is xip, then ensure that the address is a memory
635 * If the FSP component is not xip, then use param.baseaddress that is
636 * passed in by the caller.
638 if (param
.stage_xip
) {
639 if (!IS_TOP_ALIGNED_ADDRESS(address
))
640 address
= -convert_to_from_absolute_top_aligned(
641 param
.image_region
, address
);
643 if (param
.baseaddress_assigned
== 0) {
644 INFO("Honoring pre-linked FSP module.\n");
647 address
= param
.baseaddress
;
651 * *offset should either be 0 or the value returned by
652 * do_cbfs_locate. do_cbfs_locate should not ever return a value
653 * that is TOP_ALIGNED_ADDRESS. Thus, if *offset contains a top
654 * aligned address, set it to 0.
656 * The only requirement in this case is that the binary should
657 * be relocated to the base address that is requested. There is
658 * no requirement on where the file ends up in the cbfs.
660 if (IS_TOP_ALIGNED_ADDRESS(*offset
))
665 * Nothing left to do if relocation is not being attempted. Just add
669 return cbfstool_convert_raw(buffer
, offset
, header
);
671 /* Create a copy of the buffer to attempt relocation. */
672 if (buffer_create(&fsp
, buffer_size(buffer
), "fsp"))
675 memcpy(buffer_get(&fsp
), buffer_get(buffer
), buffer_size(buffer
));
677 /* Replace the buffer contents w/ the relocated ones on success. */
678 if (fsp_component_relocate(address
, buffer_get(&fsp
), buffer_size(&fsp
))
680 buffer_delete(buffer
);
681 buffer_clone(buffer
, &fsp
);
684 WARN("Invalid FSP variant.\n");
687 /* Let the raw path handle all the cbfs metadata logic. */
688 return cbfstool_convert_raw(buffer
, offset
, header
);
691 static int cbfstool_convert_mkstage(struct buffer
*buffer
, uint32_t *offset
,
692 struct cbfs_file
*header
)
694 struct buffer output
;
697 if (param
.stage_xip
) {
701 if (elf_program_file_size(buffer
, &data_size
) < 0) {
702 ERROR("Could not obtain ELF size\n");
706 if (do_cbfs_locate(&address
, sizeof(struct cbfs_stage
),
708 ERROR("Could not find location for XIP stage.\n");
713 * Ensure the address is a memory mapped one. This assumes
714 * x86 semantics about the boot media being directly mapped
715 * below 4GiB in the CPU address space.
717 address
= -convert_to_from_absolute_top_aligned(
718 param
.image_region
, address
);
721 ret
= parse_elf_to_xip_stage(buffer
, &output
, offset
,
722 param
.ignore_section
);
724 ret
= parse_elf_to_stage(buffer
, &output
, param
.compression
,
725 offset
, param
.ignore_section
);
729 buffer_delete(buffer
);
730 // Direct assign, no dupe.
731 memcpy(buffer
, &output
, sizeof(*buffer
));
732 header
->len
= htonl(output
.size
);
736 static int cbfstool_convert_mkpayload(struct buffer
*buffer
,
737 unused
uint32_t *offset
, struct cbfs_file
*header
)
739 struct buffer output
;
741 /* Per default, try and see if payload is an ELF binary */
742 ret
= parse_elf_to_payload(buffer
, &output
, param
.compression
);
744 /* If it's not an ELF, see if it's a FIT */
746 ret
= parse_fit_to_payload(buffer
, &output
, param
.compression
);
748 header
->type
= htonl(CBFS_COMPONENT_FIT
);
751 /* If it's not an FIT, see if it's a UEFI FV */
753 ret
= parse_fv_to_payload(buffer
, &output
, param
.compression
);
755 /* If it's neither ELF nor UEFI Fv, try bzImage */
757 ret
= parse_bzImage_to_payload(buffer
, &output
,
758 param
.initrd
, param
.cmdline
, param
.compression
);
760 /* Not a supported payload type */
762 ERROR("Not a supported payload type (ELF / FV).\n");
763 buffer_delete(buffer
);
767 buffer_delete(buffer
);
768 // Direct assign, no dupe.
769 memcpy(buffer
, &output
, sizeof(*buffer
));
770 header
->len
= htonl(output
.size
);
774 static int cbfstool_convert_mkflatpayload(struct buffer
*buffer
,
775 unused
uint32_t *offset
, struct cbfs_file
*header
)
777 struct buffer output
;
778 if (parse_flat_binary_to_payload(buffer
, &output
,
781 param
.compression
) != 0) {
784 buffer_delete(buffer
);
785 // Direct assign, no dupe.
786 memcpy(buffer
, &output
, sizeof(*buffer
));
787 header
->len
= htonl(output
.size
);
791 static int cbfs_add(void)
794 convert_buffer_t convert
;
795 uint32_t local_baseaddress
= param
.baseaddress
;
797 if (param
.alignment
&& param
.baseaddress
) {
798 ERROR("Cannot specify both alignment and base address\n");
802 convert
= cbfstool_convert_raw
;
804 /* Set the alignment to 4KiB minimum for FSP blobs when no base address
805 * is provided so that relocation can occur. */
806 if (param
.type
== CBFS_COMPONENT_FSP
) {
807 if (!param
.baseaddress_assigned
)
808 param
.alignment
= 4*1024;
809 convert
= cbfstool_convert_fsp
;
810 } else if (param
.stage_xip
) {
811 ERROR("cbfs add supports xip only for FSP component type\n");
815 if (param
.alignment
) {
816 /* CBFS compression file attribute is unconditionally added. */
817 size_t metadata_sz
= sizeof(struct cbfs_file_attr_compression
);
818 if (do_cbfs_locate(&address
, metadata_sz
, 0))
820 local_baseaddress
= address
;
823 return cbfs_add_component(param
.filename
,
831 static int cbfs_add_stage(void)
833 if (param
.stage_xip
) {
834 if (param
.baseaddress_assigned
) {
835 ERROR("Cannot specify base address for XIP.\n");
839 if (param
.compression
!= CBFS_COMPRESS_NONE
) {
840 ERROR("Cannot specify compression for XIP.\n");
845 return cbfs_add_component(param
.filename
,
847 CBFS_COMPONENT_STAGE
,
850 cbfstool_convert_mkstage
);
853 static int cbfs_add_payload(void)
855 return cbfs_add_component(param
.filename
,
860 cbfstool_convert_mkpayload
);
863 static int cbfs_add_flat_binary(void)
865 if (param
.loadaddress
== 0) {
866 ERROR("You need to specify a valid "
867 "-l/--load-address.\n");
870 if (param
.entrypoint
== 0) {
871 ERROR("You need to specify a valid "
872 "-e/--entry-point.\n");
875 return cbfs_add_component(param
.filename
,
880 cbfstool_convert_mkflatpayload
);
883 static int cbfs_add_integer(void)
885 if (!param
.u64val_assigned
) {
886 ERROR("You need to specify a value to write.\n");
889 return cbfs_add_integer_component(param
.name
,
895 static int cbfs_remove(void)
898 ERROR("You need to specify -n/--name.\n");
902 struct cbfs_image image
;
903 if (cbfs_image_from_buffer(&image
, param
.image_region
,
907 if (cbfs_remove_entry(&image
, param
.name
) != 0) {
908 ERROR("Removing file '%s' failed.\n",
916 static int cbfs_create(void)
918 struct cbfs_image image
;
919 memset(&image
, 0, sizeof(image
));
920 buffer_clone(&image
.buffer
, param
.image_region
);
923 if (param
.arch
!= CBFS_ARCHITECTURE_UNKNOWN
|| param
.size
||
924 param
.baseaddress_assigned
||
925 param
.headeroffset_assigned
||
926 param
.cbfsoffset_assigned
||
928 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
932 return cbfs_image_create(&image
, image
.buffer
.size
);
935 if (param
.arch
== CBFS_ARCHITECTURE_UNKNOWN
) {
936 ERROR("You need to specify -m/--machine arch.\n");
940 struct buffer bootblock
;
941 if (!param
.bootblock
) {
942 DEBUG("-B not given, creating image without bootblock.\n");
943 if (buffer_create(&bootblock
, 0, "(dummy)") != 0)
945 } else if (buffer_from_file(&bootblock
, param
.bootblock
)) {
949 if (!param
.alignment
)
950 param
.alignment
= CBFS_ALIGNMENT
;
952 // Set default offsets. x86, as usual, needs to be a special snowflake.
953 if (!param
.baseaddress_assigned
) {
954 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
955 // Make sure there's at least enough room for rel_offset
956 param
.baseaddress
= param
.size
-
957 MAX(bootblock
.size
, sizeof(int32_t));
958 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
961 param
.baseaddress
= 0;
962 DEBUG("bootblock starts at address 0x0.\n");
965 if (!param
.headeroffset_assigned
) {
966 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
967 param
.headeroffset
= param
.baseaddress
-
968 sizeof(struct cbfs_header
);
969 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
972 param
.headeroffset
= align_up(param
.baseaddress
+
973 bootblock
.size
, sizeof(uint32_t));
974 DEBUG("CBFS header placed behind bootblock (%#x).\n",
978 if (!param
.cbfsoffset_assigned
) {
979 if (param
.arch
== CBFS_ARCHITECTURE_X86
) {
980 param
.cbfsoffset
= 0;
981 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
983 param
.cbfsoffset
= align_up(param
.headeroffset
+
984 sizeof(struct cbfs_header
),
986 DEBUG("CBFS entries start beind master header (%#x).\n",
991 int ret
= cbfs_legacy_image_create(&image
,
998 buffer_delete(&bootblock
);
1002 static int cbfs_layout(void)
1004 const struct fmap
*fmap
= partitioned_file_get_fmap(param
.image_file
);
1006 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1010 printf("This image contains the following sections that can be %s with this tool:\n",
1011 param
.show_immutable
? "accessed" : "manipulated");
1013 for (unsigned i
= 0; i
< fmap
->nareas
; ++i
) {
1014 const struct fmap_area
*current
= fmap
->areas
+ i
;
1016 bool readonly
= partitioned_file_fmap_count(param
.image_file
,
1017 partitioned_file_fmap_select_children_of
, current
) ||
1018 region_is_flashmap((const char *)current
->name
);
1019 if (!param
.show_immutable
&& readonly
)
1022 printf("'%s'", current
->name
);
1024 // Detect consecutive sections that describe the same region and
1025 // show them as aliases. This cannot find equivalent entries
1026 // that aren't adjacent; however, fmaptool doesn't generate
1027 // FMAPs with such sections, so this convenience feature works
1028 // for all but the strangest manually created FMAP binaries.
1029 // TODO: This could be done by parsing the FMAP into some kind
1030 // of tree that had duplicate lists in addition to child lists,
1031 // which would allow covering that weird, unlikely case as well.
1033 for (lookahead
= 1; i
+ lookahead
< fmap
->nareas
;
1035 const struct fmap_area
*consecutive
=
1036 fmap
->areas
+ i
+ lookahead
;
1037 if (consecutive
->offset
!= current
->offset
||
1038 consecutive
->size
!= current
->size
)
1040 printf(", '%s'", consecutive
->name
);
1043 fputs(" are aliases for the same region", stdout
);
1045 const char *qualifier
= "";
1047 qualifier
= "read-only, ";
1048 else if (region_is_modern_cbfs((const char *)current
->name
))
1049 qualifier
= "CBFS, ";
1050 printf(" (%ssize %u, offset %u)\n", qualifier
, current
->size
,
1057 if (param
.show_immutable
) {
1058 puts("It is at least possible to perform the read action on every section listed above.");
1060 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1061 puts("To see the image's read-only sections as well, rerun with the -w option.");
1067 static int cbfs_print(void)
1069 struct cbfs_image image
;
1070 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1071 param
.headeroffset
))
1073 if (param
.machine_parseable
)
1074 return cbfs_print_parseable_directory(&image
);
1076 printf("FMAP REGION: %s\n", param
.region_name
);
1077 return cbfs_print_directory(&image
);
1081 static int cbfs_extract(void)
1083 if (!param
.filename
) {
1084 ERROR("You need to specify -f/--filename.\n");
1089 ERROR("You need to specify -n/--name.\n");
1093 struct cbfs_image image
;
1094 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1095 param
.headeroffset
))
1098 return cbfs_export_entry(&image
, param
.name
, param
.filename
,
1099 param
.arch
, !param
.unprocessed
);
1102 static int cbfs_write(void)
1104 if (!param
.filename
) {
1105 ERROR("You need to specify a valid input -f/--file.\n");
1108 if (!partitioned_file_is_partitioned(param
.image_file
)) {
1109 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1113 if (!param
.force
&& region_is_modern_cbfs(param
.region_name
)) {
1114 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1119 struct buffer new_content
;
1120 if (buffer_from_file(&new_content
, param
.filename
))
1123 if (buffer_check_magic(&new_content
, FMAP_SIGNATURE
,
1124 strlen(FMAP_SIGNATURE
))) {
1125 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1127 buffer_delete(&new_content
);
1130 if (!param
.force
&& buffer_check_magic(&new_content
, CBFS_FILE_MAGIC
,
1131 strlen(CBFS_FILE_MAGIC
))) {
1132 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1134 buffer_delete(&new_content
);
1138 unsigned offset
= 0;
1139 if (param
.fill_partial_upward
&& param
.fill_partial_downward
) {
1140 ERROR("You may only specify one of -u and -d.\n");
1141 buffer_delete(&new_content
);
1143 } else if (!param
.fill_partial_upward
&& !param
.fill_partial_downward
) {
1144 if (new_content
.size
!= param
.image_region
->size
) {
1145 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",
1146 new_content
.size
, param
.image_region
->size
);
1147 buffer_delete(&new_content
);
1151 if (new_content
.size
> param
.image_region
->size
) {
1152 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1153 new_content
.size
, param
.image_region
->size
);
1154 buffer_delete(&new_content
);
1157 if (param
.u64val
== (uint64_t)-1) {
1158 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1159 param
.fill_partial_upward
? "bottom" : "top");
1160 } else if (param
.u64val
> 0xff) {
1161 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param
.u64val
& 0xff));
1162 buffer_delete(&new_content
);
1165 memset(buffer_get(param
.image_region
),
1166 param
.u64val
& 0xff,
1167 buffer_size(param
.image_region
));
1169 if (param
.fill_partial_downward
)
1170 offset
= param
.image_region
->size
- new_content
.size
;
1173 memcpy(param
.image_region
->data
+ offset
, new_content
.data
,
1175 buffer_delete(&new_content
);
1179 static int cbfs_read(void)
1181 if (!param
.filename
) {
1182 ERROR("You need to specify a valid output -f/--file.\n");
1185 if (!partitioned_file_is_partitioned(param
.image_file
)) {
1186 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1190 return buffer_write_file(param
.image_region
, param
.filename
);
1193 static int cbfs_update_fit(void)
1196 ERROR("You need to specify -n/--name.\n");
1200 if (param
.fit_empty_entries
<= 0) {
1201 ERROR("Invalid number of fit entries "
1202 "(-x/--empty-fits): %d\n", param
.fit_empty_entries
);
1206 struct buffer bootblock
;
1207 // The bootblock is part of the CBFS on x86
1208 buffer_clone(&bootblock
, param
.image_region
);
1210 struct cbfs_image image
;
1211 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1212 param
.headeroffset
))
1218 * Get the address of provided region for first row.
1220 if (param
.ucode_region
) {
1221 struct buffer ucode
;
1223 if (partitioned_file_read_region(&ucode
,
1224 param
.image_file
, param
.ucode_region
))
1225 addr
= -convert_to_from_top_aligned(&ucode
, 0);
1231 if (fit_update_table(&bootblock
, &image
, param
.name
,
1232 param
.fit_empty_entries
, convert_to_from_top_aligned
,
1233 param
.topswap_size
, addr
))
1236 // The region to be written depends on the type of image, so we write it
1237 // here rather than having main() write the CBFS region back as usual.
1238 return !partitioned_file_write_region(param
.image_file
, &bootblock
);
1241 static int cbfs_copy(void)
1243 struct cbfs_image src_image
;
1244 struct buffer src_buf
;
1246 if (!param
.source_region
) {
1247 ERROR("You need to specify -R/--source-region.\n");
1251 /* Obtain the source region and convert it to a cbfs_image. */
1252 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1253 param
.source_region
)) {
1254 ERROR("Region not found in image: %s\n", param
.source_region
);
1258 if (cbfs_image_from_buffer(&src_image
, &src_buf
, param
.headeroffset
))
1261 return cbfs_copy_instance(&src_image
, param
.image_region
);
1264 static int cbfs_compact(void)
1266 struct cbfs_image image
;
1267 if (cbfs_image_from_buffer(&image
, param
.image_region
,
1268 param
.headeroffset
))
1270 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1271 return cbfs_compact_instance(&image
);
1274 static int cbfs_expand(void)
1276 struct buffer src_buf
;
1278 /* Obtain the source region. */
1279 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1280 param
.region_name
)) {
1281 ERROR("Region not found in image: %s\n", param
.source_region
);
1285 return cbfs_expand_to_region(param
.image_region
);
1288 static int cbfs_truncate(void)
1290 struct buffer src_buf
;
1292 /* Obtain the source region. */
1293 if (!partitioned_file_read_region(&src_buf
, param
.image_file
,
1294 param
.region_name
)) {
1295 ERROR("Region not found in image: %s\n", param
.source_region
);
1300 int result
= cbfs_truncate_space(param
.image_region
, &size
);
1301 printf("0x%x\n", size
);
1305 static const struct command commands
[] = {
1306 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add
, true, true},
1307 {"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary
,
1309 {"add-payload", "H:r:f:n:c:b:C:I:p:vA:gh?", cbfs_add_payload
,
1311 {"add-stage", "a:H:r:f:n:t:c:b:P:S:p:yvA:gh?", cbfs_add_stage
,
1313 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer
, true, true},
1314 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header
, true, true},
1315 {"compact", "r:h?", cbfs_compact
, true, true},
1316 {"copy", "r:R:h?", cbfs_copy
, true, true},
1317 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create
, true, true},
1318 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract
, true, false},
1319 {"layout", "wvh?", cbfs_layout
, false, false},
1320 {"print", "H:r:vkh?", cbfs_print
, true, false},
1321 {"read", "r:f:vh?", cbfs_read
, true, false},
1322 {"remove", "H:r:n:vh?", cbfs_remove
, true, true},
1323 {"update-fit", "H:r:n:x:vh?j:q:", cbfs_update_fit
, true, true},
1324 {"write", "r:f:i:Fudvh?", cbfs_write
, true, true},
1325 {"expand", "r:h?", cbfs_expand
, true, true},
1326 {"truncate", "r:h?", cbfs_truncate
, true, true},
1329 static struct option long_options
[] = {
1330 {"alignment", required_argument
, 0, 'a' },
1331 {"base-address", required_argument
, 0, 'b' },
1332 {"bootblock", required_argument
, 0, 'B' },
1333 {"cmdline", required_argument
, 0, 'C' },
1334 {"compression", required_argument
, 0, 'c' },
1335 {"topswap-size", required_argument
, 0, 'j' },
1336 {"empty-fits", required_argument
, 0, 'x' },
1337 {"entry-point", required_argument
, 0, 'e' },
1338 {"file", required_argument
, 0, 'f' },
1339 {"fill-downward", no_argument
, 0, 'd' },
1340 {"fill-upward", no_argument
, 0, 'u' },
1341 {"flashmap", required_argument
, 0, 'M' },
1342 {"fmap-regions", required_argument
, 0, 'r' },
1343 {"force", no_argument
, 0, 'F' },
1344 {"source-region", required_argument
, 0, 'R' },
1345 {"hash-algorithm",required_argument
, 0, 'A' },
1346 {"header-offset", required_argument
, 0, 'H' },
1347 {"help", no_argument
, 0, 'h' },
1348 {"ignore-sec", required_argument
, 0, 'S' },
1349 {"initrd", required_argument
, 0, 'I' },
1350 {"int", required_argument
, 0, 'i' },
1351 {"load-address", required_argument
, 0, 'l' },
1352 {"machine", required_argument
, 0, 'm' },
1353 {"name", required_argument
, 0, 'n' },
1354 {"offset", required_argument
, 0, 'o' },
1355 {"padding", required_argument
, 0, 'p' },
1356 {"page-size", required_argument
, 0, 'P' },
1357 {"ucode-region", required_argument
, 0, 'q' },
1358 {"size", required_argument
, 0, 's' },
1359 {"top-aligned", required_argument
, 0, 'T' },
1360 {"type", required_argument
, 0, 't' },
1361 {"verbose", no_argument
, 0, 'v' },
1362 {"with-readonly", no_argument
, 0, 'w' },
1363 {"xip", no_argument
, 0, 'y' },
1364 {"gen-attribute", no_argument
, 0, 'g' },
1365 {"mach-parseable",no_argument
, 0, 'k' },
1366 {"unprocessed", no_argument
, 0, 'U' },
1370 static int dispatch_command(struct command command
)
1372 if (command
.accesses_region
) {
1373 assert(param
.image_file
);
1375 if (partitioned_file_is_partitioned(param
.image_file
)) {
1376 INFO("Performing operation on '%s' region...\n",
1379 if (!partitioned_file_read_region(param
.image_region
,
1380 param
.image_file
, param
.region_name
)) {
1381 ERROR("The image will be left unmodified.\n");
1385 if (command
.modifies_region
) {
1386 // We (intentionally) don't support overwriting the FMAP
1387 // section. If you find yourself wanting to do this,
1388 // consider creating a new image rather than performing
1389 // whatever hacky transformation you were planning.
1390 if (region_is_flashmap(param
.region_name
)) {
1391 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1393 ERROR("The image will be left unmodified.\n");
1396 // We don't allow writing raw data to regions that
1397 // contain nested regions, since doing so would
1398 // overwrite all such subregions.
1399 if (partitioned_file_region_contains_nested(
1400 param
.image_file
, param
.region_name
)) {
1401 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1403 ERROR("The image will be left unmodified.\n");
1409 if (command
.function()) {
1410 if (partitioned_file_is_partitioned(param
.image_file
)) {
1411 ERROR("Failed while operating on '%s' region!\n",
1413 ERROR("The image will be left unmodified.\n");
1421 static void usage(char *name
)
1424 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
1425 "USAGE:\n" " %s [-h]\n"
1426 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
1427 " -H header_offset Do not search for header; use this offset*\n"
1428 " -T Output top-aligned memory address\n"
1429 " -u Accept short data; fill upward/from bottom\n"
1430 " -d Accept short data; fill downward/from top\n"
1431 " -F Force action\n"
1432 " -g Generate position and alignment arguments\n"
1433 " -U Unprocessed; don't decompress or make ELF\n"
1434 " -v Provide verbose output\n"
1435 " -h Display this help message\n\n"
1437 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
1438 " [-c compression] [-b base-address | -a alignment] \\\n"
1439 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
1440 " [-j topswap-size] (Intel CPUs only) "
1443 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
1444 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1445 " [-c compression] [-b base-address] \\\n"
1446 " (linux specific: [-C cmdline] [-I initrd]) "
1447 "Add a payload to the ROM\n"
1448 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1449 " [-c compression] [-b base] [-S section-to-ignore] \\\n"
1450 " [-a alignment] [-y|--xip] [-P page-size] "
1451 "Add a stage to the ROM\n"
1452 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1453 " [-A hash] -l load-address -e entry-point \\\n"
1454 " [-c compression] [-b base] "
1455 "Add a 32bit flat mode binary\n"
1456 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
1457 "Add a raw 64-bit integer value\n"
1458 " add-master-header [-r image,regions] \\ \n"
1459 " [-j topswap-size] (Intel CPUs only) "
1460 "Add a legacy CBFS master header\n"
1461 " remove [-r image,regions] -n NAME "
1462 "Remove a component\n"
1463 " compact -r image,regions "
1464 "Defragment CBFS image.\n"
1465 " copy -r image,regions -R source-region "
1466 "Create a copy (duplicate) cbfs instance in fmap\n"
1467 " create -m ARCH -s size [-b bootblock offset] \\\n"
1468 " [-o CBFS offset] [-H header offset] [-B bootblock] "
1469 "Create a legacy ROM file with CBFS master header*\n"
1470 " create -M flashmap [-r list,of,regions,containing,cbfses] "
1471 "Create a new-style partitioned firmware image\n"
1472 " locate [-r image,regions] -f FILE -n NAME [-P page-size] \\\n"
1474 "Find a place for a file of that size\n"
1476 "List mutable (or, with -w, readable) image regions\n"
1477 " print [-r image,regions] "
1478 "Show the contents of the ROM\n"
1479 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
1480 "Extracts a file from ROM\n"
1481 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
1482 "Write file into same-size [or larger] raw region\n"
1483 " read [-r fmap-region] -f file "
1484 "Extract raw region contents into binary file\n"
1485 " truncate [-r fmap-region] "
1486 "Truncate CBFS and print new size on stdout\n"
1487 " expand [-r fmap-region] "
1488 "Expand CBFS to span entire region\n"
1489 " update-fit [-r image,regions] -n MICROCODE_BLOB_NAME \\\n"
1490 " -x EMTPY_FIT_ENTRIES \\ \n"
1491 " [-j topswap-size [-q ucode-region](Intel CPUs only)] "
1492 "Updates the FIT table with microcode entries.\n"
1494 " ucode-region is a region in the FMAP, its address is \n"
1496 " inserted as the first entry in the topswap FIT. \n"
1499 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
1500 " in two possible formats: if their value is greater than\n"
1501 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
1502 " address; otherwise, they are treated as an offset into flash.\n"
1503 "ARCHes:\n", name
, name
1505 print_supported_architectures();
1508 print_supported_filetypes();
1510 "\n* Note that these actions and switches are only valid when\n"
1511 " working with legacy images whose structure is described\n"
1512 " primarily by a CBFS master header. New-style images, in\n"
1513 " contrast, exclusively make use of an FMAP to describe their\n"
1514 " layout: this must minimally contain an '%s' section\n"
1515 " specifying the location of this FMAP itself and a '%s'\n"
1516 " section describing the primary CBFS. It should also be noted\n"
1517 " that, when working with such images, the -F and -r switches\n"
1518 " default to '%s' for convenience, and both the -b switch to\n"
1519 " CBFS operations and the output of the locate action become\n"
1520 " relative to the selected CBFS region's lowest address.\n"
1521 " The one exception to this rule is the top-aligned address,\n"
1522 " which is always relative to the end of the entire image\n"
1523 " rather than relative to the local region; this is true for\n"
1524 " for both input (sufficiently large) and output (-T) data.\n",
1525 SECTION_NAME_FMAP
, SECTION_NAME_PRIMARY_CBFS
,
1526 SECTION_NAME_PRIMARY_CBFS
1530 int main(int argc
, char **argv
)
1540 char *image_name
= argv
[1];
1541 char *cmd
= argv
[2];
1544 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
1545 if (strcmp(cmd
, commands
[i
].name
) != 0)
1549 char *suffix
= NULL
;
1550 int option_index
= 0;
1552 c
= getopt_long(argc
, argv
, commands
[i
].optstring
,
1553 long_options
, &option_index
);
1555 if (optind
< argc
) {
1556 ERROR("%s: excessive argument -- '%s'"
1557 "\n", argv
[0], argv
[optind
]);
1563 /* Filter out illegal long options */
1564 if (strchr(commands
[i
].optstring
, c
) == NULL
) {
1565 /* TODO maybe print actual long option instead */
1566 ERROR("%s: invalid option -- '%c'\n",
1573 param
.name
= optarg
;
1576 if (intfiletype(optarg
) != ((uint64_t) - 1))
1577 param
.type
= intfiletype(optarg
);
1579 param
.type
= strtoul(optarg
, NULL
, 0);
1580 if (param
.type
== 0)
1581 WARN("Unknown type '%s' ignored\n",
1585 if (strcmp(optarg
, "precompression") == 0) {
1586 param
.precompression
= 1;
1589 int algo
= cbfs_parse_comp_algo(optarg
);
1591 param
.compression
= algo
;
1593 WARN("Unknown compression '%s' ignored.\n",
1598 int algo
= cbfs_parse_hash_algo(optarg
);
1602 ERROR("Unknown hash algorithm '%s'.\n",
1609 param
.fmap
= optarg
;
1612 param
.region_name
= optarg
;
1615 param
.source_region
= optarg
;
1618 param
.baseaddress
= strtoul(optarg
, &suffix
, 0);
1619 if (!*optarg
|| (suffix
&& *suffix
)) {
1620 ERROR("Invalid base address '%s'.\n",
1624 // baseaddress may be zero on non-x86, so we
1625 // need an explicit "baseaddress_assigned".
1626 param
.baseaddress_assigned
= 1;
1629 param
.loadaddress
= strtoul(optarg
, &suffix
, 0);
1630 if (!*optarg
|| (suffix
&& *suffix
)) {
1631 ERROR("Invalid load address '%s'.\n",
1637 param
.entrypoint
= strtoul(optarg
, &suffix
, 0);
1638 if (!*optarg
|| (suffix
&& *suffix
)) {
1639 ERROR("Invalid entry point '%s'.\n",
1645 param
.size
= strtoul(optarg
, &suffix
, 0);
1647 ERROR("Empty size specified.\n");
1650 switch (tolower((int)suffix
[0])) {
1655 param
.size
*= 1024 * 1024;
1660 ERROR("Invalid suffix for size '%s'.\n",
1666 param
.bootblock
= optarg
;
1669 param
.headeroffset
= strtoul(
1670 optarg
, &suffix
, 0);
1671 if (!*optarg
|| (suffix
&& *suffix
)) {
1672 ERROR("Invalid header offset '%s'.\n",
1676 param
.headeroffset_assigned
= 1;
1679 param
.alignment
= strtoul(optarg
, &suffix
, 0);
1680 if (!*optarg
|| (suffix
&& *suffix
)) {
1681 ERROR("Invalid alignment '%s'.\n",
1687 param
.padding
= strtoul(optarg
, &suffix
, 0);
1688 if (!*optarg
|| (suffix
&& *suffix
)) {
1689 ERROR("Invalid pad size '%s'.\n",
1695 param
.pagesize
= strtoul(optarg
, &suffix
, 0);
1696 if (!*optarg
|| (suffix
&& *suffix
)) {
1697 ERROR("Invalid page size '%s'.\n",
1703 param
.cbfsoffset
= strtoul(optarg
, &suffix
, 0);
1704 if (!*optarg
|| (suffix
&& *suffix
)) {
1705 ERROR("Invalid cbfs offset '%s'.\n",
1709 param
.cbfsoffset_assigned
= 1;
1712 param
.filename
= optarg
;
1718 param
.u64val
= strtoull(optarg
, &suffix
, 0);
1719 param
.u64val_assigned
= 1;
1720 if (!*optarg
|| (suffix
&& *suffix
)) {
1721 ERROR("Invalid int parameter '%s'.\n",
1727 param
.fill_partial_upward
= true;
1730 param
.fill_partial_downward
= true;
1733 param
.show_immutable
= true;
1736 param
.fit_empty_entries
= strtol(
1737 optarg
, &suffix
, 0);
1738 if (!*optarg
|| (suffix
&& *suffix
)) {
1739 ERROR("Invalid number of fit entries "
1745 param
.topswap_size
= strtol(optarg
, NULL
, 0);
1746 if (!is_valid_topswap())
1750 param
.ucode_region
= optarg
;
1756 param
.arch
= string_to_arch(optarg
);
1759 param
.initrd
= optarg
;
1762 param
.cmdline
= optarg
;
1765 param
.ignore_section
= optarg
;
1768 param
.stage_xip
= true;
1771 param
.autogen_attr
= true;
1774 param
.machine_parseable
= true;
1777 param
.unprocessed
= true;
1788 if (commands
[i
].function
== cbfs_create
) {
1790 struct buffer flashmap
;
1791 if (buffer_from_file(&flashmap
, param
.fmap
))
1793 param
.image_file
= partitioned_file_create(
1794 image_name
, &flashmap
);
1795 buffer_delete(&flashmap
);
1796 } else if (param
.size
) {
1797 param
.image_file
= partitioned_file_create_flat(
1798 image_name
, param
.size
);
1800 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
1804 bool write_access
= commands
[i
].modifies_region
;
1807 partitioned_file_reopen(image_name
,
1810 if (!param
.image_file
)
1813 unsigned num_regions
= 1;
1814 for (const char *list
= strchr(param
.region_name
, ','); list
;
1815 list
= strchr(list
+ 1, ','))
1818 // If the action needs to read an image region, as indicated by
1819 // having accesses_region set in its command struct, that
1820 // region's buffer struct will be stored here and the client
1821 // will receive a pointer to it via param.image_region. It
1822 // need not write the buffer back to the image file itself,
1823 // since this behavior can be requested via its modifies_region
1824 // field. Additionally, it should never free the region buffer,
1825 // as that is performed automatically once it completes.
1826 struct buffer image_regions
[num_regions
];
1827 memset(image_regions
, 0, sizeof(image_regions
));
1829 bool seen_primary_cbfs
= false;
1830 char region_name_scratch
[strlen(param
.region_name
) + 1];
1831 strcpy(region_name_scratch
, param
.region_name
);
1832 param
.region_name
= strtok(region_name_scratch
, ",");
1833 for (unsigned region
= 0; region
< num_regions
; ++region
) {
1834 if (!param
.region_name
) {
1835 ERROR("Encountered illegal degenerate region name in -r list\n");
1836 ERROR("The image will be left unmodified.\n");
1837 partitioned_file_close(param
.image_file
);
1841 if (strcmp(param
.region_name
, SECTION_NAME_PRIMARY_CBFS
)
1843 seen_primary_cbfs
= true;
1845 param
.image_region
= image_regions
+ region
;
1846 if (dispatch_command(commands
[i
])) {
1847 partitioned_file_close(param
.image_file
);
1851 param
.region_name
= strtok(NULL
, ",");
1854 if (commands
[i
].function
== cbfs_create
&& !seen_primary_cbfs
) {
1855 ERROR("The creation -r list must include the mandatory '%s' section.\n",
1856 SECTION_NAME_PRIMARY_CBFS
);
1857 ERROR("The image will be left unmodified.\n");
1858 partitioned_file_close(param
.image_file
);
1862 if (commands
[i
].modifies_region
) {
1863 assert(param
.image_file
);
1864 for (unsigned region
= 0; region
< num_regions
;
1867 if (!partitioned_file_write_region(
1869 image_regions
+ region
)) {
1870 partitioned_file_close(
1877 partitioned_file_close(param
.image_file
);
1881 ERROR("Unknown command '%s'.\n", cmd
);