util/cbfstool: Drop IS_TOP_ALIGNED_ADDRESS() check in cbfstool_convert_fsp
[coreboot.git] / util / cbfstool / cbfstool.c
blobdecbd051f63808fa479c7ab8add437cd939ed1a3
1 /* cbfstool, CLI utility for CBFS file manipulation */
2 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <strings.h>
8 #include <ctype.h>
9 #include <unistd.h>
10 #include <getopt.h>
11 #include "common.h"
12 #include "cbfs.h"
13 #include "cbfs_image.h"
14 #include "cbfs_sections.h"
15 #include "elfparsing.h"
16 #include "partitioned_file.h"
17 #include <commonlib/fsp.h>
18 #include <commonlib/endian.h>
19 #include <commonlib/helpers.h>
21 #define SECTION_WITH_FIT_TABLE "BOOTBLOCK"
23 struct command {
24 const char *name;
25 const char *optstring;
26 int (*function) (void);
27 // Whether to populate param.image_region before invoking function
28 bool accesses_region;
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
33 bool modifies_region;
36 static struct param {
37 partitioned_file_t *image_file;
38 struct buffer *image_region;
39 const char *name;
40 const char *filename;
41 const char *fmap;
42 const char *region_name;
43 const char *source_region;
44 const char *bootblock;
45 const char *ignore_section;
46 const char *ucode_region;
47 uint64_t u64val;
48 uint32_t type;
49 uint32_t baseaddress;
50 uint32_t baseaddress_assigned;
51 uint32_t loadaddress;
52 uint32_t headeroffset;
53 uint32_t headeroffset_assigned;
54 uint32_t entrypoint;
55 uint32_t size;
56 uint32_t alignment;
57 uint32_t pagesize;
58 uint32_t cbfsoffset;
59 uint32_t cbfsoffset_assigned;
60 uint32_t arch;
61 uint32_t padding;
62 uint32_t topswap_size;
63 bool u64val_assigned;
64 bool fill_partial_upward;
65 bool fill_partial_downward;
66 bool show_immutable;
67 bool stage_xip;
68 bool autogen_attr;
69 bool machine_parseable;
70 bool unprocessed;
71 bool ibb;
72 enum comp_algo compression;
73 int precompression;
74 enum vb2_hash_algorithm hash;
75 /* For linux payloads */
76 char *initrd;
77 char *cmdline;
78 int force;
79 } param = {
80 /* All variables not listed are initialized as zero. */
81 .arch = CBFS_ARCHITECTURE_UNKNOWN,
82 .compression = CBFS_COMPRESS_NONE,
83 .hash = VB2_HASH_INVALID,
84 .headeroffset = ~0,
85 .region_name = SECTION_NAME_PRIMARY_CBFS,
86 .u64val = -1,
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)
110 assert(region);
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,
124 unsigned offset)
126 assert(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,
140 size_t data_size)
142 if (!param.filename) {
143 ERROR("You need to specify -f/--filename.\n");
144 return 1;
147 if (!param.name) {
148 ERROR("You need to specify -n/--name.\n");
149 return 1;
152 struct cbfs_image image;
153 if (cbfs_image_from_buffer(&image, param.image_region,
154 param.headeroffset))
155 return 1;
157 if (cbfs_get_entry(&image, param.name))
158 WARN("'%s' already in CBFS.\n", param.name);
160 if (!data_size) {
161 struct buffer buffer;
162 if (buffer_from_file(&buffer, param.filename) != 0) {
163 ERROR("Cannot load %s.\n", param.filename);
164 return 1;
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) {
176 if (param.alignment)
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);
189 if (address == -1) {
190 ERROR("'%s' can't fit in CBFS for page-size %#x, align %#x.\n",
191 param.name, param.pagesize, param.alignment);
192 return 1;
195 *cbfs_addr = address;
196 return 0;
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,
203 uint64_t u64val,
204 uint32_t offset,
205 uint32_t headeroffset) {
206 struct cbfs_image image;
207 struct cbfs_file *header = NULL;
208 struct buffer buffer;
209 int i, ret = 1;
211 if (!name) {
212 ERROR("You need to specify -n/--name.\n");
213 return 1;
216 if (buffer_create(&buffer, 8, name) != 0)
217 return 1;
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");
224 goto done;
227 if (cbfs_get_entry(&image, name)) {
228 ERROR("'%s' already in ROM image.\n", name);
229 goto done;
232 if (IS_TOP_ALIGNED_ADDRESS(offset))
233 offset = convert_to_from_top_aligned(param.image_region,
234 -offset);
236 header = cbfs_create_file_header(CBFS_COMPONENT_RAW,
237 buffer.size, name);
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);
241 goto done;
244 ret = 0;
246 done:
247 free(header);
248 buffer_delete(&buffer);
249 return ret;
252 static int is_valid_topswap(void)
254 switch (param.topswap_size) {
255 case (64 * KiB):
256 case (128 * KiB):
257 case (256 * KiB):
258 case (512 * KiB):
259 case (1 * MiB):
260 break;
261 default:
262 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
263 param.topswap_size);
264 return 0;
266 return 1;
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");
282 if (entry == NULL) {
283 ERROR("Bootblock not in ROM image?!?\n");
284 return 1;
288 * Check if the existing topswap boundary matches with
289 * the one provided.
291 if (param.topswap_size != ntohl(entry->len)/2) {
292 ERROR("Top swap boundary does not match\n");
293 return 1;
296 ts_h_loc -= param.topswap_size;
297 fill_header_offset(ts_h_loc, header_offset);
299 return 0;
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;
308 int ret = 1;
309 size_t offset;
310 size_t size;
311 void *h_loc;
313 if (cbfs_image_from_buffer(&image, param.image_region,
314 param.headeroffset)) {
315 ERROR("Selected image region is not a CBFS.\n");
316 return 1;
319 if (cbfs_get_entry(&image, name)) {
320 ERROR("'%s' already in ROM image.\n", name);
321 return 1;
324 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
325 return 1;
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");
355 goto done;
358 struct cbfs_file *entry;
359 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
360 ERROR("'%s' not in ROM image?!?\n", name);
361 goto done;
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,
377 header_offset))
378 return 1;
381 ret = 0;
383 done:
384 free(header);
385 buffer_delete(&buffer);
386 return ret;
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,
396 param.topswap_size);
397 return 1;
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,
406 buffer->name))
407 return 1;
409 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
410 bb_buf_size);
411 buffer_splice(&bb2, &new_bootblock,
412 buffer_size(&new_bootblock) - bb_buf_size,
413 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));
427 return 0;
430 static int cbfs_add_component(const char *filename,
431 const char *name,
432 uint32_t type,
433 uint32_t offset,
434 uint32_t headeroffset,
435 convert_buffer_t convert)
437 size_t len_align = 0;
439 if (!filename) {
440 ERROR("You need to specify -f/--filename.\n");
441 return 1;
444 if (!name) {
445 ERROR("You need to specify -n/--name.\n");
446 return 1;
449 if (type == 0) {
450 ERROR("You need to specify a valid -t/--type.\n");
451 return 1;
454 struct cbfs_image image;
455 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
456 return 1;
458 if (cbfs_get_entry(&image, name)) {
459 ERROR("'%s' already in ROM image.\n", name);
460 return 1;
463 struct buffer buffer;
464 if (buffer_from_file(&buffer, filename) != 0) {
465 ERROR("Could not load file '%s'.\n", filename);
466 return 1;
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))
475 return 1;
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);
483 return 1;
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);
489 free(header);
490 buffer_delete(&buffer);
491 return 1;
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));
502 if (attrs == NULL)
503 return -1;
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));
514 else
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));
524 if (attrs == NULL)
525 return -1;
526 attrs->alignment = htonl(param.alignment);
530 if (param.ibb) {
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));
535 if (attrs == NULL)
536 return -1;
537 /* For Intel TXT minimum align is 16 */
538 len_align = 16;
541 if (param.padding) {
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,
548 size);
549 if (attr == NULL)
550 return -1;
553 if (IS_TOP_ALIGNED_ADDRESS(offset))
554 offset = convert_to_from_top_aligned(param.image_region,
555 -offset);
556 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
557 ERROR("Failed to add '%s' into ROM image.\n", filename);
558 free(header);
559 buffer_delete(&buffer);
560 return 1;
563 free(header);
564 buffer_delete(&buffer);
565 return 0;
568 static int cbfstool_convert_raw(struct buffer *buffer,
569 unused uint32_t *offset, struct cbfs_file *header)
571 char *compressed;
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);
581 if (!compressed)
582 return -1;
583 memcpy(compressed, buffer->data + 8, compressed_size);
584 } else {
585 compress = compression_function(param.compression);
586 if (!compress)
587 return -1;
588 compressed = calloc(buffer->size, 1);
589 if (!compressed)
590 return -1;
592 if (compress(buffer->data, buffer->size,
593 compressed, &compressed_size)) {
594 WARN("Compression failed - disabled\n");
595 free(compressed);
596 return 0;
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));
605 if (attrs == NULL) {
606 free(compressed);
607 return -1;
609 attrs->compression = htonl(param.compression);
610 attrs->decompressed_size = htonl(decompressed_size);
612 free(buffer->data);
613 buffer->data = compressed;
614 buffer->size = compressed_size;
616 header->len = htonl(buffer->size);
617 return 0;
620 static int cbfstool_convert_fsp(struct buffer *buffer,
621 uint32_t *offset, struct cbfs_file *header)
623 uint32_t address;
624 struct buffer fsp;
625 int do_relocation = 1;
627 address = *offset;
630 * If the FSP component is xip, then ensure that the address is a memory
631 * mapped one.
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);
639 } else {
640 if (param.baseaddress_assigned == 0) {
641 INFO("Honoring pre-linked FSP module.\n");
642 do_relocation = 0;
643 } else {
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.
654 *offset = 0;
659 * Nothing left to do if relocation is not being attempted. Just add
660 * the file.
662 if (!do_relocation)
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"))
667 return -1;
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))
673 > 0) {
674 buffer_delete(buffer);
675 buffer_clone(buffer, &fsp);
676 } else {
677 buffer_delete(&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;
689 int ret;
691 if (param.stage_xip) {
692 int32_t address;
693 size_t data_size;
695 if (elf_program_file_size(buffer, &data_size) < 0) {
696 ERROR("Could not obtain ELF size\n");
697 return 1;
700 if (do_cbfs_locate(&address, sizeof(struct cbfs_stage),
701 data_size)) {
702 ERROR("Could not find location for XIP stage.\n");
703 return 1;
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);
713 *offset = address;
715 ret = parse_elf_to_xip_stage(buffer, &output, offset,
716 param.ignore_section);
717 } else
718 ret = parse_elf_to_stage(buffer, &output, param.compression,
719 offset, param.ignore_section);
721 if (ret != 0)
722 return -1;
723 buffer_delete(buffer);
724 // Direct assign, no dupe.
725 memcpy(buffer, &output, sizeof(*buffer));
726 header->len = htonl(output.size);
727 return 0;
730 static int cbfstool_convert_mkpayload(struct buffer *buffer,
731 unused uint32_t *offset, struct cbfs_file *header)
733 struct buffer output;
734 int ret;
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 */
739 if (ret != 0) {
740 ret = parse_fit_to_payload(buffer, &output, param.compression);
741 if (ret == 0)
742 header->type = htonl(CBFS_COMPONENT_FIT);
745 /* If it's not an FIT, see if it's a UEFI FV */
746 if (ret != 0)
747 ret = parse_fv_to_payload(buffer, &output, param.compression);
749 /* If it's neither ELF nor UEFI Fv, try bzImage */
750 if (ret != 0)
751 ret = parse_bzImage_to_payload(buffer, &output,
752 param.initrd, param.cmdline, param.compression);
754 /* Not a supported payload type */
755 if (ret != 0) {
756 ERROR("Not a supported payload type (ELF / FV).\n");
757 buffer_delete(buffer);
758 return -1;
761 buffer_delete(buffer);
762 // Direct assign, no dupe.
763 memcpy(buffer, &output, sizeof(*buffer));
764 header->len = htonl(output.size);
765 return 0;
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,
773 param.loadaddress,
774 param.entrypoint,
775 param.compression) != 0) {
776 return -1;
778 buffer_delete(buffer);
779 // Direct assign, no dupe.
780 memcpy(buffer, &output, sizeof(*buffer));
781 header->len = htonl(output.size);
782 return 0;
785 static int cbfs_add(void)
787 int32_t address;
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");
793 return 1;
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");
806 return 1;
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))
813 return 1;
814 local_baseaddress = address;
817 return cbfs_add_component(param.filename,
818 param.name,
819 param.type,
820 local_baseaddress,
821 param.headeroffset,
822 convert);
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");
830 return 1;
833 if (param.compression != CBFS_COMPRESS_NONE) {
834 ERROR("Cannot specify compression for XIP.\n");
835 return 1;
839 return cbfs_add_component(param.filename,
840 param.name,
841 CBFS_COMPONENT_STAGE,
842 param.baseaddress,
843 param.headeroffset,
844 cbfstool_convert_mkstage);
847 static int cbfs_add_payload(void)
849 return cbfs_add_component(param.filename,
850 param.name,
851 CBFS_COMPONENT_SELF,
852 param.baseaddress,
853 param.headeroffset,
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");
862 return 1;
864 if (param.entrypoint == 0) {
865 ERROR("You need to specify a valid "
866 "-e/--entry-point.\n");
867 return 1;
869 return cbfs_add_component(param.filename,
870 param.name,
871 CBFS_COMPONENT_SELF,
872 param.baseaddress,
873 param.headeroffset,
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");
881 return 1;
883 return cbfs_add_integer_component(param.name,
884 param.u64val,
885 param.baseaddress,
886 param.headeroffset);
889 static int cbfs_remove(void)
891 if (!param.name) {
892 ERROR("You need to specify -n/--name.\n");
893 return 1;
896 struct cbfs_image image;
897 if (cbfs_image_from_buffer(&image, param.image_region,
898 param.headeroffset))
899 return 1;
901 if (cbfs_remove_entry(&image, param.name) != 0) {
902 ERROR("Removing file '%s' failed.\n",
903 param.name);
904 return 1;
907 return 0;
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);
916 if (param.fmap) {
917 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
918 param.baseaddress_assigned ||
919 param.headeroffset_assigned ||
920 param.cbfsoffset_assigned ||
921 param.bootblock) {
922 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
923 return 1;
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");
931 return 1;
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)
938 return 1;
939 } else if (buffer_from_file(&bootblock, param.bootblock)) {
940 return 1;
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",
953 param.baseaddress);
954 } else {
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",
964 param.headeroffset);
965 } else {
966 param.headeroffset = align_up(param.baseaddress +
967 bootblock.size, sizeof(uint32_t));
968 DEBUG("CBFS header placed behind bootblock (%#x).\n",
969 param.headeroffset);
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");
976 } else {
977 param.cbfsoffset = align_up(param.headeroffset +
978 sizeof(struct cbfs_header),
979 CBFS_ALIGNMENT);
980 DEBUG("CBFS entries start beind master header (%#x).\n",
981 param.cbfsoffset);
985 int ret = cbfs_legacy_image_create(&image,
986 param.arch,
987 CBFS_ALIGNMENT,
988 &bootblock,
989 param.baseaddress,
990 param.headeroffset,
991 param.cbfsoffset);
992 buffer_delete(&bootblock);
993 return ret;
996 static int cbfs_layout(void)
998 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
999 if (!fmap) {
1000 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1001 return 1;
1004 printf("This image contains the following sections that can be %s with this tool:\n",
1005 param.show_immutable ? "accessed" : "manipulated");
1006 puts("");
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)
1014 continue;
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.
1026 unsigned lookahead;
1027 for (lookahead = 1; i + lookahead < fmap->nareas;
1028 ++lookahead) {
1029 const struct fmap_area *consecutive =
1030 fmap->areas + i + lookahead;
1031 if (consecutive->offset != current->offset ||
1032 consecutive->size != current->size)
1033 break;
1034 printf(", '%s'", consecutive->name);
1036 if (lookahead > 1)
1037 fputs(" are aliases for the same region", stdout);
1039 const char *qualifier = "";
1040 if (readonly)
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,
1047 current->offset);
1049 i += lookahead - 1;
1051 puts("");
1053 if (param.show_immutable) {
1054 puts("It is at least possible to perform the read action on every section listed above.");
1055 } else {
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.");
1060 return 0;
1063 static int cbfs_print(void)
1065 struct cbfs_image image;
1066 if (cbfs_image_from_buffer(&image, param.image_region,
1067 param.headeroffset))
1068 return 1;
1069 if (param.machine_parseable)
1070 return cbfs_print_parseable_directory(&image);
1071 else {
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");
1081 return 1;
1084 if (!param.name) {
1085 ERROR("You need to specify -n/--name.\n");
1086 return 1;
1089 struct cbfs_image image;
1090 if (cbfs_image_from_buffer(&image, param.image_region,
1091 param.headeroffset))
1092 return 1;
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");
1102 return 1;
1104 if (!partitioned_file_is_partitioned(param.image_file)) {
1105 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1106 return 1;
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",
1111 param.region_name);
1112 return 1;
1115 struct buffer new_content;
1116 if (buffer_from_file(&new_content, param.filename))
1117 return 1;
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",
1122 param.filename);
1123 buffer_delete(&new_content);
1124 return 1;
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",
1129 param.filename);
1130 buffer_delete(&new_content);
1131 return 1;
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);
1138 return 1;
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);
1144 return 1;
1146 } else {
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);
1151 return 1;
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);
1159 return 1;
1160 } else {
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,
1170 new_content.size);
1171 buffer_delete(&new_content);
1172 return 0;
1175 static int cbfs_read(void)
1177 if (!param.filename) {
1178 ERROR("You need to specify a valid output -f/--file.\n");
1179 return 1;
1181 if (!partitioned_file_is_partitioned(param.image_file)) {
1182 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1183 return 1;
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");
1196 return 1;
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);
1203 return 1;
1206 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1207 return 1;
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))
1217 return 1;
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);
1230 return 1;
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);
1244 return 1;
1247 uint32_t size;
1248 int result = cbfs_truncate_space(param.image_region, &size);
1249 printf("0x%x\n", size);
1250 return result;
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,
1256 true, true},
1257 {"add-payload", "H:r:f:n:c:b:C:I:p:vA:gh?", cbfs_add_payload,
1258 true, true},
1259 {"add-stage", "a:H:r:f:n:t:c:b:P:S:p:yvA:gh?", cbfs_add_stage,
1260 true, true},
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},
1276 enum {
1277 /* begin after ASCII characters */
1278 LONGOPT_START = 256,
1279 LONGOPT_IBB = LONGOPT_START,
1280 LONGOPT_END,
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 },
1322 {NULL, 0, 0, 0 }
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",
1332 param.region_name);
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");
1337 return 1;
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",
1347 param.region_name);
1348 ERROR("The image will be left unmodified.\n");
1349 return 1;
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",
1357 param.region_name);
1358 ERROR("The image will be left unmodified.\n");
1359 return 1;
1364 if (command.function()) {
1365 if (partitioned_file_is_partitioned(param.image_file)) {
1366 ERROR("Failed while operating on '%s' region!\n",
1367 param.region_name);
1368 ERROR("The image will be left unmodified.\n");
1370 return 1;
1373 return 0;
1376 static void usage(char *name)
1378 printf
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"
1391 "COMMANDs:\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] "
1396 "Add a component\n"
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"
1428 " [-a align] [-T] "
1429 "Find a place for a file of that size\n"
1430 " layout [-w] "
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"
1444 "OFFSETs:\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();
1453 printf("TYPEs:\n");
1454 print_supported_filetypes();
1455 printf(
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))
1480 return true;
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)
1488 return true;
1490 return false;
1493 int main(int argc, char **argv)
1495 size_t i;
1496 int c;
1498 if (argc < 3) {
1499 usage(argv[0]);
1500 return 1;
1503 char *image_name = argv[1];
1504 char *cmd = argv[2];
1505 optind += 2;
1507 for (i = 0; i < ARRAY_SIZE(commands); i++) {
1508 if (strcmp(cmd, commands[i].name) != 0)
1509 continue;
1511 while (1) {
1512 char *suffix = NULL;
1513 int option_index = 0;
1515 c = getopt_long(argc, argv, commands[i].optstring,
1516 long_options, &option_index);
1517 if (c == -1) {
1518 if (optind < argc) {
1519 ERROR("%s: excessive argument -- '%s'"
1520 "\n", argv[0], argv[optind]);
1521 return 1;
1523 break;
1526 /* Filter out illegal long options */
1527 if (!valid_opt(i, c)) {
1528 ERROR("%s: invalid option -- '%d'\n",
1529 argv[0], c);
1530 c = '?';
1533 switch(c) {
1534 case 'n':
1535 param.name = optarg;
1536 break;
1537 case 't':
1538 if (intfiletype(optarg) != ((uint64_t) - 1))
1539 param.type = intfiletype(optarg);
1540 else
1541 param.type = strtoul(optarg, NULL, 0);
1542 if (param.type == 0)
1543 WARN("Unknown type '%s' ignored\n",
1544 optarg);
1545 break;
1546 case 'c': {
1547 if (strcmp(optarg, "precompression") == 0) {
1548 param.precompression = 1;
1549 break;
1551 int algo = cbfs_parse_comp_algo(optarg);
1552 if (algo >= 0)
1553 param.compression = algo;
1554 else
1555 WARN("Unknown compression '%s' ignored.\n",
1556 optarg);
1557 break;
1559 case 'A': {
1560 int algo = cbfs_parse_hash_algo(optarg);
1561 if (algo >= 0)
1562 param.hash = algo;
1563 else {
1564 ERROR("Unknown hash algorithm '%s'.\n",
1565 optarg);
1566 return 1;
1568 break;
1570 case 'M':
1571 param.fmap = optarg;
1572 break;
1573 case 'r':
1574 param.region_name = optarg;
1575 break;
1576 case 'R':
1577 param.source_region = optarg;
1578 break;
1579 case 'b':
1580 param.baseaddress = strtoul(optarg, &suffix, 0);
1581 if (!*optarg || (suffix && *suffix)) {
1582 ERROR("Invalid base address '%s'.\n",
1583 optarg);
1584 return 1;
1586 // baseaddress may be zero on non-x86, so we
1587 // need an explicit "baseaddress_assigned".
1588 param.baseaddress_assigned = 1;
1589 break;
1590 case 'l':
1591 param.loadaddress = strtoul(optarg, &suffix, 0);
1592 if (!*optarg || (suffix && *suffix)) {
1593 ERROR("Invalid load address '%s'.\n",
1594 optarg);
1595 return 1;
1597 break;
1598 case 'e':
1599 param.entrypoint = strtoul(optarg, &suffix, 0);
1600 if (!*optarg || (suffix && *suffix)) {
1601 ERROR("Invalid entry point '%s'.\n",
1602 optarg);
1603 return 1;
1605 break;
1606 case 's':
1607 param.size = strtoul(optarg, &suffix, 0);
1608 if (!*optarg) {
1609 ERROR("Empty size specified.\n");
1610 return 1;
1612 switch (tolower((int)suffix[0])) {
1613 case 'k':
1614 param.size *= 1024;
1615 break;
1616 case 'm':
1617 param.size *= 1024 * 1024;
1618 break;
1619 case '\0':
1620 break;
1621 default:
1622 ERROR("Invalid suffix for size '%s'.\n",
1623 optarg);
1624 return 1;
1626 break;
1627 case 'B':
1628 param.bootblock = optarg;
1629 break;
1630 case 'H':
1631 param.headeroffset = strtoul(
1632 optarg, &suffix, 0);
1633 if (!*optarg || (suffix && *suffix)) {
1634 ERROR("Invalid header offset '%s'.\n",
1635 optarg);
1636 return 1;
1638 param.headeroffset_assigned = 1;
1639 break;
1640 case 'a':
1641 param.alignment = strtoul(optarg, &suffix, 0);
1642 if (!*optarg || (suffix && *suffix)) {
1643 ERROR("Invalid alignment '%s'.\n",
1644 optarg);
1645 return 1;
1647 break;
1648 case 'p':
1649 param.padding = strtoul(optarg, &suffix, 0);
1650 if (!*optarg || (suffix && *suffix)) {
1651 ERROR("Invalid pad size '%s'.\n",
1652 optarg);
1653 return 1;
1655 break;
1656 case 'P':
1657 param.pagesize = strtoul(optarg, &suffix, 0);
1658 if (!*optarg || (suffix && *suffix)) {
1659 ERROR("Invalid page size '%s'.\n",
1660 optarg);
1661 return 1;
1663 break;
1664 case 'o':
1665 param.cbfsoffset = strtoul(optarg, &suffix, 0);
1666 if (!*optarg || (suffix && *suffix)) {
1667 ERROR("Invalid cbfs offset '%s'.\n",
1668 optarg);
1669 return 1;
1671 param.cbfsoffset_assigned = 1;
1672 break;
1673 case 'f':
1674 param.filename = optarg;
1675 break;
1676 case 'F':
1677 param.force = 1;
1678 break;
1679 case 'i':
1680 param.u64val = strtoull(optarg, &suffix, 0);
1681 param.u64val_assigned = 1;
1682 if (!*optarg || (suffix && *suffix)) {
1683 ERROR("Invalid int parameter '%s'.\n",
1684 optarg);
1685 return 1;
1687 break;
1688 case 'u':
1689 param.fill_partial_upward = true;
1690 break;
1691 case 'd':
1692 param.fill_partial_downward = true;
1693 break;
1694 case 'w':
1695 param.show_immutable = true;
1696 break;
1697 case 'j':
1698 param.topswap_size = strtol(optarg, NULL, 0);
1699 if (!is_valid_topswap())
1700 return 1;
1701 break;
1702 case 'q':
1703 param.ucode_region = optarg;
1704 break;
1705 case 'v':
1706 verbose++;
1707 break;
1708 case 'm':
1709 param.arch = string_to_arch(optarg);
1710 break;
1711 case 'I':
1712 param.initrd = optarg;
1713 break;
1714 case 'C':
1715 param.cmdline = optarg;
1716 break;
1717 case 'S':
1718 param.ignore_section = optarg;
1719 break;
1720 case 'y':
1721 param.stage_xip = true;
1722 break;
1723 case 'g':
1724 param.autogen_attr = true;
1725 break;
1726 case 'k':
1727 param.machine_parseable = true;
1728 break;
1729 case 'U':
1730 param.unprocessed = true;
1731 break;
1732 case LONGOPT_IBB:
1733 param.ibb = true;
1734 break;
1735 case 'h':
1736 case '?':
1737 usage(argv[0]);
1738 return 1;
1739 default:
1740 break;
1744 if (commands[i].function == cbfs_create) {
1745 if (param.fmap) {
1746 struct buffer flashmap;
1747 if (buffer_from_file(&flashmap, param.fmap))
1748 return 1;
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);
1755 } else {
1756 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
1757 return 1;
1759 } else {
1760 bool write_access = commands[i].modifies_region;
1762 param.image_file =
1763 partitioned_file_reopen(image_name,
1764 write_access);
1766 if (!param.image_file)
1767 return 1;
1769 unsigned num_regions = 1;
1770 for (const char *list = strchr(param.region_name, ','); list;
1771 list = strchr(list + 1, ','))
1772 ++num_regions;
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);
1794 return 1;
1797 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
1798 == 0)
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);
1804 return 1;
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);
1815 return 1;
1818 if (commands[i].modifies_region) {
1819 assert(param.image_file);
1820 for (unsigned region = 0; region < num_regions;
1821 ++region) {
1823 if (!partitioned_file_write_region(
1824 param.image_file,
1825 image_regions + region)) {
1826 partitioned_file_close(
1827 param.image_file);
1828 return 1;
1833 partitioned_file_close(param.image_file);
1834 return 0;
1837 ERROR("Unknown command '%s'.\n", cmd);
1838 usage(argv[0]);
1839 return 1;