1 /* cbfstool, CLI utility for CBFS file manipulation */
2 /* SPDX-License-Identifier: GPL-2.0-only */
3
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 "lz4/lib/xxhash.h"
18 #include <commonlib/bsd/cbfs_private.h>
19 #include <commonlib/bsd/compression.h>
20 #include <commonlib/bsd/metadata_hash.h>
21 #include <commonlib/fsp.h>
22 #include <commonlib/endian.h>
23 #include <commonlib/helpers.h>
24 #include <commonlib/region.h>
25 #include <vboot_host.h>
26
27 struct command {
28 const char *name;
29 const char *optstring;
30 int (*function) (void);
31 // Whether to populate param.image_region before invoking function
32 bool accesses_region;
33 // This set to true means two things:
34 // - in case of a command operating on a region, the region's contents
35 // will be written back to image_file at the end
36 // - write access to the file is required
37 bool modifies_region;
38 };
39
40 static struct param {
41 partitioned_file_t *image_file;
42 struct buffer *image_region;
43 const char *name;
44 const char *filename;
45 const char *fmap;
46 const char *region_name;
47 const char *source_region;
48 const char *bootblock;
49 const char *ignore_sections;
50 const char *ucode_region;
51 uint64_t u64val;
52 uint32_t type;
53 uint32_t baseaddress;
54 /*
55 * Input can be negative. It will be transformed to offset from start of region (if
56 * negative) and stored in baseaddress.
57 */
58 long long int baseaddress_input;
59 uint32_t baseaddress_assigned;
60 uint64_t loadaddress;
61 uint32_t headeroffset;
62 /*
63 * Input can be negative. It will be transformed to offset from start of region (if
64 * negative) and stored in baseaddress.
65 */
66 long long int headeroffset_input;
67 uint32_t headeroffset_assigned;
68 uint64_t entrypoint;
69 uint32_t size;
70 uint32_t alignment;
71 uint32_t pagesize;
72 uint32_t cbfsoffset;
73 /*
74 * Input can be negative. It will be transformed to corresponding region offset (if
75 * negative) and stored in baseaddress.
76 */
77 long long int cbfsoffset_input;
78 uint32_t cbfsoffset_assigned;
79 uint32_t arch;
80 uint32_t padding;
81 uint32_t topswap_size;
82 bool u64val_assigned;
83 bool fill_partial_upward;
84 bool fill_partial_downward;
85 bool show_immutable;
86 bool stage_xip;
87 bool force_pow2_pagesize;
88 bool autogen_attr;
89 bool machine_parseable;
90 bool unprocessed;
91 bool ibb;
92 enum cbfs_compression compression;
93 int precompression;
94 enum vb2_hash_algorithm hash;
95 /* For linux payloads */
96 char *initrd;
97 char *cmdline;
98 int force;
99 /*
100 * Base and size of extended window for decoding SPI flash greater than 16MiB in host
101 * address space on x86 platforms. The assumptions here are:
102 * 1. Top 16MiB is still decoded in the fixed decode window just below 4G boundary.
103 * 2. Rest of the SPI flash below the top 16MiB is mapped at the top of extended
104 * window. Even though the platform might support a larger extended window, the SPI
105 * flash part used by the mainboard might not be large enough to be mapped in the entire
106 * window. In such cases, the mapping is assumed to be in the top part of the extended
107 * window with the bottom part remaining unused.
108 */
109 uint32_t ext_win_base;
110 uint32_t ext_win_size;
111 } param = {
112 /* All variables not listed are initialized as zero. */
113 .arch = CBFS_ARCHITECTURE_UNKNOWN,
114 .compression = CBFS_COMPRESS_NONE,
115 .hash = VB2_HASH_INVALID,
116 .headeroffset = HEADER_OFFSET_UNKNOWN,
117 .region_name = SECTION_NAME_PRIMARY_CBFS,
118 .u64val = -1,
119 };
120
121 /*
122 * This "metadata_hash cache" caches the value and location of the CBFS metadata
123 * hash embedded in the bootblock when CBFS verification is enabled. The first
124 * call to get_mh_cache() searches for the cache by scanning the whole bootblock
125 * for its 8-byte signature, later calls will just return the previously found
126 * information again. If the cbfs_hash.algo member in the result is
127 * VB2_HASH_INVALID, that means no metadata hash was found and this image does
128 * not use CBFS verification.
129 */
130 struct mh_cache {
131 const char *region;
132 size_t offset;
133 struct vb2_hash cbfs_hash;
134 platform_fixup_func fixup;
135 bool initialized;
136 };
137
get_mh_cache(void)138 static struct mh_cache *get_mh_cache(void)
139 {
140 static struct mh_cache mhc;
141
142 if (mhc.initialized)
143 return &mhc;
144
145 mhc.initialized = true;
146
147 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
148 if (!fmap)
149 goto no_metadata_hash;
150
151 /* Find the metadata_hash container. If there is a "BOOTBLOCK" FMAP section, it's
152 there. If not, it's a normal file in the primary CBFS section. */
153 size_t offset, size;
154 struct buffer buffer;
155 if (fmap_find_area(fmap, SECTION_NAME_BOOTBLOCK)) {
156 if (!partitioned_file_read_region(&buffer, param.image_file,
157 SECTION_NAME_BOOTBLOCK))
158 goto no_metadata_hash;
159 mhc.region = SECTION_NAME_BOOTBLOCK;
160 offset = 0;
161 size = buffer.size;
162 } else {
163 struct cbfs_image cbfs;
164 struct cbfs_file *mh_container;
165 if (!partitioned_file_read_region(&buffer, param.image_file,
166 SECTION_NAME_PRIMARY_CBFS))
167 goto no_metadata_hash;
168 mhc.region = SECTION_NAME_PRIMARY_CBFS;
169 if (cbfs_image_from_buffer(&cbfs, &buffer, param.headeroffset))
170 goto no_metadata_hash;
171 mh_container = cbfs_get_entry(&cbfs, "bootblock");
172 if (!mh_container || be32toh(mh_container->type) != CBFS_TYPE_BOOTBLOCK) {
173 /* Check for apu/amdfw file */
174 mh_container = cbfs_get_entry(&cbfs, "apu/amdfw");
175 if (!mh_container || be32toh(mh_container->type) != CBFS_TYPE_AMDFW)
176 goto no_metadata_hash;
177 }
178
179 offset = (void *)mh_container + be32toh(mh_container->offset) -
180 buffer_get(&cbfs.buffer);
181 size = be32toh(mh_container->len);
182 }
183
184 /* Find and validate the metadata hash anchor inside the containing file and
185 record its exact byte offset from the start of the FMAP region. */
186 struct metadata_hash_anchor *anchor = memmem(buffer_get(&buffer) + offset,
187 size, METADATA_HASH_ANCHOR_MAGIC, sizeof(anchor->magic));
188 if (anchor) {
189 if (!vb2_digest_size(anchor->cbfs_hash.algo)) {
190 ERROR("Unknown CBFS metadata hash type: %d\n",
191 anchor->cbfs_hash.algo);
192 goto no_metadata_hash;
193 }
194 mhc.cbfs_hash = anchor->cbfs_hash;
195 mhc.offset = (void *)anchor - buffer_get(&buffer);
196 mhc.fixup = platform_fixups_probe(&buffer, mhc.offset,
197 mhc.region);
198 return &mhc;
199 }
200
201 no_metadata_hash:
202 mhc.cbfs_hash.algo = VB2_HASH_INVALID;
203 return &mhc;
204 }
205
update_and_info(const char * name,void * dst,void * src,size_t size)206 static void update_and_info(const char *name, void *dst, void *src, size_t size)
207 {
208 if (!memcmp(dst, src, size))
209 return;
210 char *src_str = bintohex(src, size);
211 char *dst_str = bintohex(dst, size);
212 INFO("Updating %s from %s to %s\n", name, dst_str, src_str);
213 memcpy(dst, src, size);
214 free(src_str);
215 free(dst_str);
216 }
217
update_anchor(struct mh_cache * mhc,uint8_t * fmap_hash)218 static int update_anchor(struct mh_cache *mhc, uint8_t *fmap_hash)
219 {
220 struct buffer buffer;
221 if (!partitioned_file_read_region(&buffer, param.image_file,
222 mhc->region))
223 return -1;
224 struct metadata_hash_anchor *anchor = buffer_get(&buffer) + mhc->offset;
225 /* The metadata hash anchor should always still be where we left it. */
226 assert(!memcmp(anchor->magic, METADATA_HASH_ANCHOR_MAGIC,
227 sizeof(anchor->magic)) &&
228 anchor->cbfs_hash.algo == mhc->cbfs_hash.algo);
229 update_and_info("CBFS metadata hash", anchor->cbfs_hash.raw,
230 mhc->cbfs_hash.raw, vb2_digest_size(anchor->cbfs_hash.algo));
231 if (fmap_hash) {
232 update_and_info("FMAP hash",
233 metadata_hash_anchor_fmap_hash(anchor), fmap_hash,
234 vb2_digest_size(anchor->cbfs_hash.algo));
235 }
236 if (mhc->fixup && mhc->fixup(&buffer, mhc->offset) != 0)
237 return -1;
238 if (!partitioned_file_write_region(param.image_file, &buffer))
239 return -1;
240 return 0;
241
242 }
243
244 /* This should be called after every time CBFS metadata might have changed. It
245 will recalculate and update the metadata hash in the bootblock if needed. */
maybe_update_metadata_hash(struct cbfs_image * cbfs)246 static int maybe_update_metadata_hash(struct cbfs_image *cbfs)
247 {
248 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS))
249 return 0; /* Metadata hash only embedded in primary CBFS. */
250
251 struct mh_cache *mhc = get_mh_cache();
252 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
253 return 0;
254
255 enum cb_err err = cbfs_walk(cbfs, NULL, NULL, &mhc->cbfs_hash,
256 CBFS_WALK_WRITEBACK_HASH);
257 if (err != CB_CBFS_NOT_FOUND) {
258 ERROR("Unexpected cbfs_walk() error %d\n", err);
259 return -1;
260 }
261
262 return update_anchor(mhc, NULL);
263 }
264
265 /* This should be called after every time the FMAP or the bootblock itself might
266 have changed, and will write the new FMAP hash into the metadata hash anchor
267 in the bootblock if required (usually when the bootblock is first added). */
maybe_update_fmap_hash(void)268 static int maybe_update_fmap_hash(void)
269 {
270 if (strcmp(param.region_name, SECTION_NAME_BOOTBLOCK) &&
271 strcmp(param.region_name, SECTION_NAME_FMAP) &&
272 param.type != CBFS_TYPE_BOOTBLOCK &&
273 param.type != CBFS_TYPE_AMDFW)
274 return 0; /* FMAP and bootblock didn't change. */
275
276 struct mh_cache *mhc = get_mh_cache();
277 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
278 return 0;
279
280 struct vb2_hash fmap_hash;
281 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
282 if (!fmap || vb2_hash_calculate(false, fmap, fmap_size(fmap),
283 mhc->cbfs_hash.algo, &fmap_hash))
284 return -1;
285 return update_anchor(mhc, fmap_hash.raw);
286 }
287
verification_exclude(enum cbfs_type type)288 static bool verification_exclude(enum cbfs_type type)
289 {
290 switch (type) {
291 case CBFS_TYPE_BOOTBLOCK:
292 case CBFS_TYPE_CBFSHEADER:
293 case CBFS_TYPE_INTEL_FIT:
294 case CBFS_TYPE_AMDFW:
295 return true;
296 default:
297 return false;
298 }
299 }
300
region_is_flashmap(const char * region)301 static bool region_is_flashmap(const char *region)
302 {
303 return partitioned_file_region_check_magic(param.image_file, region,
304 FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
305 }
306
307 /* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
region_is_modern_cbfs(const char * region)308 static bool region_is_modern_cbfs(const char *region)
309 {
310 return partitioned_file_region_check_magic(param.image_file, region,
311 CBFS_FILE_MAGIC, strlen(CBFS_FILE_MAGIC));
312 }
313
314 /* This describes a window from the SPI flash address space into the host address space. */
315 struct mmap_window {
316 struct region flash_space;
317 struct region host_space;
318 };
319
320 /* Should be enough for now */
321 #define MMAP_MAX_WINDOWS 3
322
323 /* Table of all the decode windows supported by the platform. */
324 static int mmap_window_table_size;
325 static struct mmap_window mmap_window_table[MMAP_MAX_WINDOWS];
326
add_mmap_window(size_t flash_offset,size_t host_offset,size_t window_size)327 static void add_mmap_window(size_t flash_offset, size_t host_offset,
328 size_t window_size)
329 {
330 if (mmap_window_table_size >= MMAP_MAX_WINDOWS) {
331 ERROR("Too many memory map windows\n");
332 return;
333 }
334
335 mmap_window_table[mmap_window_table_size].flash_space.offset = flash_offset;
336 mmap_window_table[mmap_window_table_size].host_space.offset = host_offset;
337 mmap_window_table[mmap_window_table_size].flash_space.size = window_size;
338 mmap_window_table[mmap_window_table_size].host_space.size = window_size;
339 mmap_window_table_size++;
340 }
341
342
decode_mmap_arg(char * arg)343 static int decode_mmap_arg(char *arg)
344 {
345 if (arg == NULL)
346 return 1;
347
348 union {
349 unsigned long int array[3];
350 struct {
351 unsigned long int flash_base;
352 unsigned long int mmap_base;
353 unsigned long int mmap_size;
354 };
355 } mmap_args;
356 char *suffix = NULL;
357 char *substring = strtok(arg, ":");
358 for (size_t i = 0; i < ARRAY_SIZE(mmap_args.array); i++) {
359 if (!substring) {
360 ERROR("Invalid mmap arguments '%s'.\n",
361 arg);
362 return 1;
363 }
364 mmap_args.array[i] = strtol(substring, &suffix, 0);
365 if (suffix && *suffix) {
366 ERROR("Invalid mmap arguments '%s'.\n",
367 arg);
368 return 1;
369 }
370 substring = strtok(NULL, ":");
371 }
372
373 if (substring != NULL) {
374 ERROR("Invalid argument, too many substrings '%s'.\n",
375 arg);
376
377 return 1;
378 }
379
380 add_mmap_window(mmap_args.flash_base, mmap_args.mmap_base, mmap_args.mmap_size);
381 return 0;
382 }
383
384 #define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
385 #define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
386
create_mmap_windows(void)387 static bool create_mmap_windows(void)
388 {
389 static bool done;
390
391 if (done)
392 return done;
393
394 // No memory map provided, use a default one
395 if (mmap_window_table_size == 0) {
396 const size_t image_size = partitioned_file_total_size(param.image_file);
397 printf("Image SIZE %zu\n", image_size);
398 const size_t std_window_size = MIN(DEFAULT_DECODE_WINDOW_MAX_SIZE, image_size);
399 const size_t std_window_flash_offset = image_size - std_window_size;
400
401 /*
402 * Default decode window lives just below 4G boundary in host space and maps up to a
403 * maximum of 16MiB. If the window is smaller than 16MiB, the SPI flash window is mapped
404 * at the top of the host window just below 4G.
405 */
406 add_mmap_window(std_window_flash_offset, DEFAULT_DECODE_WINDOW_TOP - std_window_size, std_window_size);
407 } else {
408 /*
409 * Check provided memory map
410 */
411 for (int i = 0; i < mmap_window_table_size; i++) {
412 for (int j = i + 1; j < mmap_window_table_size; j++) {
413 if (region_overlap(&mmap_window_table[i].flash_space,
414 &mmap_window_table[j].flash_space)) {
415 ERROR("Flash space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
416 region_offset(&mmap_window_table[i].flash_space),
417 region_end(&mmap_window_table[i].flash_space),
418 region_offset(&mmap_window_table[j].flash_space),
419 region_end(&mmap_window_table[j].flash_space));
420 return false;
421 }
422
423 if (region_overlap(&mmap_window_table[i].host_space,
424 &mmap_window_table[j].host_space)) {
425 ERROR("Host space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
426 region_offset(&mmap_window_table[i].flash_space),
427 region_end(&mmap_window_table[i].flash_space),
428 region_offset(&mmap_window_table[j].flash_space),
429 region_end(&mmap_window_table[j].flash_space));
430 return false;
431 }
432 }
433 }
434 }
435
436 done = true;
437 return done;
438 }
439
convert_address(const struct region * to,const struct region * from,unsigned int addr)440 static unsigned int convert_address(const struct region *to, const struct region *from,
441 unsigned int addr)
442 {
443 /*
444 * Calculate the offset in the "from" region and use that offset to calculate
445 * corresponding address in the "to" region.
446 */
447 size_t offset = addr - region_offset(from);
448 return region_offset(to) + offset;
449 }
450
451 enum mmap_addr_type {
452 HOST_SPACE_ADDR,
453 FLASH_SPACE_ADDR,
454 };
455
find_mmap_window(enum mmap_addr_type addr_type,unsigned int addr)456 static int find_mmap_window(enum mmap_addr_type addr_type, unsigned int addr)
457 {
458 size_t i;
459
460 for (i = 0; i < ARRAY_SIZE(mmap_window_table); i++) {
461 const struct region *reg;
462
463 if (addr_type == HOST_SPACE_ADDR)
464 reg = &mmap_window_table[i].host_space;
465 else
466 reg = &mmap_window_table[i].flash_space;
467
468 if (region_offset(reg) <= addr &&
469 ((uint64_t)region_offset(reg) + (uint64_t)region_sz(reg) - 1) >= addr)
470 return i;
471 }
472
473 return -1;
474 }
475
convert_host_to_flash(const struct buffer * region,unsigned int addr)476 static unsigned int convert_host_to_flash(const struct buffer *region, unsigned int addr)
477 {
478 int idx;
479 const struct region *to, *from;
480
481 idx = find_mmap_window(HOST_SPACE_ADDR, addr);
482 if (idx == -1) {
483 ERROR("Host address(%x) not in any mmap window!\n", addr);
484 return 0;
485 }
486
487 to = &mmap_window_table[idx].flash_space;
488 from = &mmap_window_table[idx].host_space;
489
490 /* region->offset is subtracted because caller expects offset in the given region. */
491 return convert_address(to, from, addr) - region->offset;
492 }
493
convert_flash_to_host(const struct buffer * region,unsigned int addr)494 static unsigned int convert_flash_to_host(const struct buffer *region, unsigned int addr)
495 {
496 int idx;
497 const struct region *to, *from;
498
499 /*
500 * region->offset is added because caller provides offset in the given region. This is
501 * converted to an absolute address in the SPI flash space. This is done before the
502 * conversion as opposed to after in convert_host_to_flash() above because the address
503 * is actually an offset within the region. So, it needs to be converted into an
504 * absolute address in the SPI flash space before converting into an address in host
505 * space.
506 */
507 addr += region->offset;
508 idx = find_mmap_window(FLASH_SPACE_ADDR, addr);
509
510 if (idx == -1) {
511 ERROR("SPI flash address(%x) not in any mmap window!\n", addr);
512 return 0;
513 }
514
515 to = &mmap_window_table[idx].host_space;
516 from = &mmap_window_table[idx].flash_space;
517
518 return convert_address(to, from, addr);
519 }
520
convert_addr_space(const struct buffer * region,unsigned int addr)521 static unsigned int convert_addr_space(const struct buffer *region, unsigned int addr)
522 {
523 assert(region);
524
525 assert(create_mmap_windows());
526
527 if (IS_HOST_SPACE_ADDRESS(addr))
528 return convert_host_to_flash(region, addr);
529 else
530 return convert_flash_to_host(region, addr);
531 }
532
533 /*
534 * This function takes offset value which represents the offset from one end of the region and
535 * converts it to offset from the other end of the region. offset is expected to be positive.
536 */
convert_region_offset(unsigned int offset,uint32_t * region_offset)537 static int convert_region_offset(unsigned int offset, uint32_t *region_offset)
538 {
539 size_t size;
540
541 if (param.size) {
542 size = param.size;
543 } else {
544 assert(param.image_region);
545 size = param.image_region->size;
546 }
547
548 if (size < offset) {
549 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size, offset);
550 return 1;
551 }
552
553 *region_offset = size - offset;
554 return 0;
555 }
556
do_cbfs_locate(uint32_t * cbfs_addr,size_t data_size)557 static int do_cbfs_locate(uint32_t *cbfs_addr, size_t data_size)
558 {
559 uint32_t metadata_size = 0;
560
561 if (!param.name) {
562 ERROR("You need to specify -n/--name.\n");
563 return 1;
564 }
565
566 struct cbfs_image image;
567 if (cbfs_image_from_buffer(&image, param.image_region,
568 param.headeroffset))
569 return 1;
570
571 if (cbfs_get_entry(&image, param.name))
572 WARN("'%s' already in CBFS.\n", param.name);
573
574 if (!data_size) {
575 ERROR("File '%s' is empty?\n", param.name);
576 return 1;
577 }
578
579 /* Compute required page size */
580 if (param.force_pow2_pagesize) {
581 param.pagesize = 1;
582 while (param.pagesize < data_size)
583 param.pagesize <<= 1;
584 DEBUG("Page size is %d (0x%x)\n", param.pagesize, param.pagesize);
585 }
586
587 /* Include cbfs_file size along with space for with name. */
588 metadata_size += cbfs_calculate_file_header_size(param.name);
589 /* Adjust metadata_size if additional attributes were added */
590 if (param.autogen_attr) {
591 if (param.alignment)
592 metadata_size += sizeof(struct cbfs_file_attr_align);
593 if (param.baseaddress_assigned || param.stage_xip)
594 metadata_size += sizeof(struct cbfs_file_attr_position);
595 }
596 if (param.precompression || param.compression != CBFS_COMPRESS_NONE)
597 metadata_size += sizeof(struct cbfs_file_attr_compression);
598 if (param.type == CBFS_TYPE_STAGE)
599 metadata_size += sizeof(struct cbfs_file_attr_stageheader);
600
601 /* Take care of the hash attribute if it is used */
602 if (param.hash != VB2_HASH_INVALID)
603 metadata_size += cbfs_file_attr_hash_size(param.hash);
604
605 int32_t address = cbfs_locate_entry(&image, data_size, param.pagesize,
606 param.alignment, metadata_size);
607
608 if (address < 0) {
609 ERROR("'%s'(%u + %zu) can't fit in CBFS for page-size %#x, align %#x.\n",
610 param.name, metadata_size, data_size, param.pagesize, param.alignment);
611 return 1;
612 }
613
614 *cbfs_addr = address;
615 return 0;
616 }
617
618 typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
619 struct cbfs_file *header);
620
cbfs_add_integer_component(const char * name,uint64_t u64val,uint32_t offset,uint32_t headeroffset)621 static int cbfs_add_integer_component(const char *name,
622 uint64_t u64val,
623 uint32_t offset,
624 uint32_t headeroffset) {
625 struct cbfs_image image;
626 struct cbfs_file *header = NULL;
627 struct buffer buffer;
628 int i, ret = 1;
629
630 if (!name) {
631 ERROR("You need to specify -n/--name.\n");
632 return 1;
633 }
634
635 if (buffer_create(&buffer, 8, name) != 0)
636 return 1;
637
638 for (i = 0; i < 8; i++)
639 buffer.data[i] = (u64val >> i*8) & 0xff;
640
641 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset)) {
642 ERROR("Selected image region is not a CBFS.\n");
643 goto done;
644 }
645
646 if (cbfs_get_entry(&image, name)) {
647 ERROR("'%s' already in ROM image.\n", name);
648 goto done;
649 }
650
651 header = cbfs_create_file_header(CBFS_TYPE_RAW,
652 buffer.size, name);
653 if (!header)
654 goto done;
655
656 enum vb2_hash_algorithm algo = get_mh_cache()->cbfs_hash.algo;
657 if (algo != VB2_HASH_INVALID)
658 if (cbfs_add_file_hash(header, &buffer, algo)) {
659 ERROR("couldn't add hash for '%s'\n", name);
660 goto done;
661 }
662
663 if (cbfs_add_entry(&image, &buffer, offset, header, 0) != 0) {
664 ERROR("Failed to add %llu into ROM image as '%s'.\n",
665 (long long unsigned)u64val, name);
666 goto done;
667 }
668
669 ret = maybe_update_metadata_hash(&image);
670
671 done:
672 free(header);
673 buffer_delete(&buffer);
674 return ret;
675 }
676
is_valid_topswap(void)677 static int is_valid_topswap(void)
678 {
679 switch (param.topswap_size) {
680 case (64 * KiB):
681 case (128 * KiB):
682 case (256 * KiB):
683 case (512 * KiB):
684 case (1 * MiB):
685 break;
686 default:
687 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
688 param.topswap_size);
689 return 0;
690 }
691 return 1;
692 }
693
fill_header_offset(void * location,uint32_t offset)694 static void fill_header_offset(void *location, uint32_t offset)
695 {
696 // TODO: When we have a BE target, we'll need to store this as BE
697 write_le32(location, offset);
698 }
699
update_master_header_loc_topswap(struct cbfs_image * image,void * h_loc,uint32_t header_offset)700 static int update_master_header_loc_topswap(struct cbfs_image *image,
701 void *h_loc, uint32_t header_offset)
702 {
703 struct cbfs_file *entry;
704 void *ts_h_loc = h_loc;
705
706 entry = cbfs_get_entry(image, "bootblock");
707 if (entry == NULL) {
708 ERROR("Bootblock not in ROM image?!?\n");
709 return 1;
710 }
711
712 /*
713 * Check if the existing topswap boundary matches with
714 * the one provided.
715 */
716 if (param.topswap_size != be32toh(entry->len)/2) {
717 ERROR("Top swap boundary does not match\n");
718 return 1;
719 }
720
721 ts_h_loc -= param.topswap_size;
722 fill_header_offset(ts_h_loc, header_offset);
723
724 return 0;
725 }
726
cbfs_add_master_header(void)727 static int cbfs_add_master_header(void)
728 {
729 const char * const name = "cbfs master header";
730 struct cbfs_image image;
731 struct cbfs_file *header = NULL;
732 struct buffer buffer;
733 int ret = 1;
734 size_t offset;
735 size_t size;
736 void *h_loc;
737
738 if (cbfs_image_from_buffer(&image, param.image_region,
739 param.headeroffset)) {
740 ERROR("Selected image region is not a CBFS.\n");
741 return 1;
742 }
743
744 if (cbfs_get_entry(&image, name)) {
745 ERROR("'%s' already in ROM image.\n", name);
746 return 1;
747 }
748
749 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
750 return 1;
751
752 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
753 h->magic = htobe32(CBFS_HEADER_MAGIC);
754 h->version = htobe32(CBFS_HEADER_VERSION);
755 /* The 4 bytes are left out for two reasons:
756 * 1. the cbfs master header pointer resides there
757 * 2. some cbfs implementations assume that an image that resides
758 * below 4GB has a bootblock and get confused when the end of the
759 * image is at 4GB == 0.
760 */
761 h->bootblocksize = htobe32(4);
762 h->align = htobe32(CBFS_ALIGNMENT);
763 /* The offset and romsize fields within the master header are absolute
764 * values within the boot media. As such, romsize needs to relfect
765 * the end 'offset' for a CBFS. To achieve that the current buffer
766 * representing the CBFS region's size is added to the offset of
767 * the region within a larger image.
768 */
769 offset = buffer_get(param.image_region) -
770 buffer_get_original_backing(param.image_region);
771 size = buffer_size(param.image_region);
772 h->romsize = htobe32(size + offset);
773 h->offset = htobe32(offset);
774 h->architecture = htobe32(CBFS_ARCHITECTURE_UNKNOWN);
775
776 /* Never add a hash attribute to the master header. */
777 header = cbfs_create_file_header(CBFS_TYPE_CBFSHEADER,
778 buffer_size(&buffer), name);
779 if (!header)
780 goto done;
781 if (cbfs_add_entry(&image, &buffer, 0, header, 0) != 0) {
782 ERROR("Failed to add cbfs master header into ROM image.\n");
783 goto done;
784 }
785
786 struct cbfs_file *entry;
787 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
788 ERROR("'%s' not in ROM image?!?\n", name);
789 goto done;
790 }
791
792 uint32_t header_offset = CBFS_SUBHEADER(entry) -
793 buffer_get(&image.buffer);
794 header_offset = -(buffer_size(&image.buffer) - header_offset);
795
796 h_loc = (void *)(buffer_get(&image.buffer) +
797 buffer_size(&image.buffer) - 4);
798 fill_header_offset(h_loc, header_offset);
799 /*
800 * If top swap present, update the header
801 * location in secondary bootblock
802 */
803 if (param.topswap_size) {
804 if (update_master_header_loc_topswap(&image, h_loc,
805 header_offset))
806 goto done;
807 }
808
809 ret = maybe_update_metadata_hash(&image);
810
811 done:
812 free(header);
813 buffer_delete(&buffer);
814 return ret;
815 }
816
add_topswap_bootblock(struct buffer * buffer,uint32_t * offset)817 static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
818 {
819 size_t bb_buf_size = buffer_size(buffer);
820
821 if (bb_buf_size > param.topswap_size) {
822 ERROR("Bootblock bigger than the topswap boundary\n");
823 ERROR("size = %zd, ts = %d\n", bb_buf_size,
824 param.topswap_size);
825 return 1;
826 }
827
828 /*
829 * Allocate topswap_size*2 bytes for bootblock to
830 * accommodate the second bootblock.
831 */
832 struct buffer new_bootblock, bb1, bb2;
833 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
834 buffer->name))
835 return 1;
836
837 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
838 bb_buf_size);
839 buffer_splice(&bb2, &new_bootblock,
840 buffer_size(&new_bootblock) - bb_buf_size,
841 bb_buf_size);
842
843 /* Copy to first bootblock */
844 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
845 /* Copy to second bootblock */
846 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
847
848 buffer_delete(buffer);
849 buffer_clone(buffer, &new_bootblock);
850
851 /* Update the location (offset) of bootblock in the region */
852 return convert_region_offset(buffer_size(buffer), offset);
853 }
854
cbfs_add_component(const char * filename,const char * name,uint32_t headeroffset,convert_buffer_t convert)855 static int cbfs_add_component(const char *filename,
856 const char *name,
857 uint32_t headeroffset,
858 convert_buffer_t convert)
859 {
860 /*
861 * The steps used to determine the final placement offset in CBFS, in order:
862 *
863 * 1. If --base-address was passed, that value is used. If it was passed in the host
864 * address space, convert it to flash address space. (After that, |*offset| is always
865 * in the flash address space.)
866 *
867 * 2. The convert() function may write a location back to |offset|, usually by calling
868 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
869 * the CBFS file in its final form (after any compression and conversion).
870 *
871 * 3. If --align was passed and the offset is still undecided at this point,
872 * do_cbfs_locate() is called to find an appropriately aligned location.
873 *
874 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
875 * location that fits.
876 */
877 uint32_t offset = param.baseaddress_assigned ? param.baseaddress : 0;
878 size_t len_align = 0;
879
880 if (param.alignment && param.baseaddress_assigned) {
881 ERROR("Cannot specify both alignment and base address\n");
882 return 1;
883 }
884
885 if (param.stage_xip && param.compression != CBFS_COMPRESS_NONE) {
886 ERROR("Cannot specify compression for XIP.\n");
887 return 1;
888 }
889
890 if (!filename) {
891 ERROR("You need to specify -f/--filename.\n");
892 return 1;
893 }
894
895 if (!name) {
896 ERROR("You need to specify -n/--name.\n");
897 return 1;
898 }
899
900 if (param.type == 0) {
901 ERROR("You need to specify a valid -t/--type.\n");
902 return 1;
903 }
904
905 struct cbfs_image image;
906 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
907 return 1;
908
909 if (cbfs_get_entry(&image, name)) {
910 ERROR("'%s' already in ROM image.\n", name);
911 return 1;
912 }
913
914 struct buffer buffer;
915 if (buffer_from_file(&buffer, filename) != 0) {
916 ERROR("Could not load file '%s'.\n", filename);
917 return 1;
918 }
919
920 struct cbfs_file *header =
921 cbfs_create_file_header(param.type, buffer.size, name);
922 if (!header)
923 goto error;
924
925 /* Bootblock and CBFS header should never have file hashes. When adding
926 the bootblock it is important that we *don't* look up the metadata
927 hash yet (before it is added) or we'll cache an outdated result. */
928 if (!verification_exclude(param.type)) {
929 enum vb2_hash_algorithm mh_algo = get_mh_cache()->cbfs_hash.algo;
930 if (mh_algo != VB2_HASH_INVALID && param.hash != mh_algo) {
931 if (param.hash == VB2_HASH_INVALID) {
932 param.hash = mh_algo;
933 } else {
934 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
935 vb2_get_hash_algorithm_name(param.hash),
936 vb2_get_hash_algorithm_name(mh_algo));
937 goto error;
938 }
939 }
940 }
941
942 /*
943 * Check if Intel CPU topswap is specified this will require a
944 * second bootblock to be added.
945 */
946 if (param.type == CBFS_TYPE_BOOTBLOCK && param.topswap_size)
947 if (add_topswap_bootblock(&buffer, &offset))
948 goto error;
949
950 /* With --base-address we allow host space addresses -- if so, convert it here. */
951 if (IS_HOST_SPACE_ADDRESS(offset))
952 offset = convert_addr_space(param.image_region, offset);
953
954 if (convert && convert(&buffer, &offset, header) != 0) {
955 ERROR("Failed to parse file '%s'.\n", filename);
956 goto error;
957 }
958
959 /* This needs to run after convert() to take compression into account. */
960 if (!offset && param.alignment)
961 if (do_cbfs_locate(&offset, buffer_size(&buffer)))
962 goto error;
963
964 /* This needs to run after convert() to hash the actual final file data. */
965 if (param.hash != VB2_HASH_INVALID &&
966 cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
967 ERROR("couldn't add hash for '%s'\n", name);
968 goto error;
969 }
970
971 if (param.autogen_attr) {
972 /* Add position attribute if assigned */
973 if (param.baseaddress_assigned || param.stage_xip) {
974 struct cbfs_file_attr_position *attrs =
975 (struct cbfs_file_attr_position *)
976 cbfs_add_file_attr(header,
977 CBFS_FILE_ATTR_TAG_POSITION,
978 sizeof(struct cbfs_file_attr_position));
979 if (attrs == NULL)
980 goto error;
981 attrs->position = htobe32(offset);
982 }
983 /* Add alignment attribute if used */
984 if (param.alignment) {
985 struct cbfs_file_attr_align *attrs =
986 (struct cbfs_file_attr_align *)
987 cbfs_add_file_attr(header,
988 CBFS_FILE_ATTR_TAG_ALIGNMENT,
989 sizeof(struct cbfs_file_attr_align));
990 if (attrs == NULL)
991 goto error;
992 attrs->alignment = htobe32(param.alignment);
993 }
994 }
995
996 if (param.ibb) {
997 /* Mark as Initial Boot Block */
998 struct cbfs_file_attribute *attrs = cbfs_add_file_attr(header,
999 CBFS_FILE_ATTR_TAG_IBB,
1000 sizeof(struct cbfs_file_attribute));
1001 if (attrs == NULL)
1002 goto error;
1003 /* For Intel TXT minimum align is 16 */
1004 len_align = 16;
1005 }
1006
1007 if (param.padding) {
1008 const uint32_t hs = sizeof(struct cbfs_file_attribute);
1009 uint32_t size = ALIGN_UP(MAX(hs, param.padding),
1010 CBFS_ATTRIBUTE_ALIGN);
1011 INFO("Padding %d bytes\n", size);
1012 struct cbfs_file_attribute *attr =
1013 (struct cbfs_file_attribute *)cbfs_add_file_attr(
1014 header, CBFS_FILE_ATTR_TAG_PADDING,
1015 size);
1016 if (attr == NULL)
1017 goto error;
1018 }
1019
1020 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
1021 ERROR("Failed to add '%s' into ROM image.\n", filename);
1022 goto error;
1023 }
1024
1025 free(header);
1026 buffer_delete(&buffer);
1027
1028 return maybe_update_metadata_hash(&image) || maybe_update_fmap_hash();
1029
1030 error:
1031 free(header);
1032 buffer_delete(&buffer);
1033 return 1;
1034 }
1035
cbfstool_convert_raw(struct buffer * buffer,unused uint32_t * offset,struct cbfs_file * header)1036 static int cbfstool_convert_raw(struct buffer *buffer,
1037 unused uint32_t *offset, struct cbfs_file *header)
1038 {
1039 char *compressed;
1040 int decompressed_size, compressed_size;
1041 comp_func_ptr compress;
1042
1043 decompressed_size = buffer->size;
1044 if (param.precompression) {
1045 param.compression = read_le32(buffer->data);
1046 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
1047 compressed_size = buffer->size - 8;
1048 compressed = malloc(compressed_size);
1049 if (!compressed)
1050 return -1;
1051 memcpy(compressed, buffer->data + 8, compressed_size);
1052 } else {
1053 if (param.compression == CBFS_COMPRESS_NONE)
1054 goto out;
1055
1056 compress = compression_function(param.compression);
1057 if (!compress)
1058 return -1;
1059 compressed = calloc(buffer->size, 1);
1060 if (!compressed)
1061 return -1;
1062
1063 if (compress(buffer->data, buffer->size,
1064 compressed, &compressed_size)) {
1065 WARN("Compression failed - disabled\n");
1066 free(compressed);
1067 goto out;
1068 }
1069 }
1070
1071 struct cbfs_file_attr_compression *attrs =
1072 (struct cbfs_file_attr_compression *)
1073 cbfs_add_file_attr(header,
1074 CBFS_FILE_ATTR_TAG_COMPRESSION,
1075 sizeof(struct cbfs_file_attr_compression));
1076 if (attrs == NULL) {
1077 free(compressed);
1078 return -1;
1079 }
1080 attrs->compression = htobe32(param.compression);
1081 attrs->decompressed_size = htobe32(decompressed_size);
1082
1083 free(buffer->data);
1084 buffer->data = compressed;
1085 buffer->size = compressed_size;
1086
1087 out:
1088 header->len = htobe32(buffer->size);
1089 return 0;
1090 }
1091
cbfstool_convert_fsp(struct buffer * buffer,uint32_t * offset,struct cbfs_file * header)1092 static int cbfstool_convert_fsp(struct buffer *buffer,
1093 uint32_t *offset, struct cbfs_file *header)
1094 {
1095 uint32_t address;
1096 struct buffer fsp;
1097
1098 /*
1099 * There are 4 different cases here:
1100 *
1101 * 1. --xip and --base-address: we need to place the binary at the given base address
1102 * in the CBFS image and relocate it to that address. *offset was already filled in,
1103 * but we need to convert it to the host address space for relocation.
1104 *
1105 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1106 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
1107 * This also needs to be converted to the host address space for relocation.
1108 *
1109 * 3. No --xip but a --base-address: special case where --base-address does not have its
1110 * normal meaning, instead we use it as the relocation target address. We explicitly
1111 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1112 *
1113 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1114 * not be relocated. Just chain directly to convert_raw() for compression.
1115 */
1116
1117 if (param.stage_xip) {
1118 if (!param.baseaddress_assigned) {
1119 param.alignment = 4*1024;
1120 if (do_cbfs_locate(offset, buffer_size(buffer)))
1121 return -1;
1122 }
1123 assert(!IS_HOST_SPACE_ADDRESS(*offset));
1124 address = convert_addr_space(param.image_region, *offset);
1125 } else {
1126 if (param.baseaddress_assigned == 0) {
1127 INFO("Honoring pre-linked FSP module, no relocation.\n");
1128 return cbfstool_convert_raw(buffer, offset, header);
1129 } else {
1130 address = param.baseaddress;
1131 *offset = 0;
1132 }
1133 }
1134
1135 /* Create a copy of the buffer to attempt relocation. */
1136 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
1137 return -1;
1138
1139 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
1140
1141 /* Replace the buffer contents w/ the relocated ones on success. */
1142 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
1143 > 0) {
1144 buffer_delete(buffer);
1145 buffer_clone(buffer, &fsp);
1146 } else {
1147 buffer_delete(&fsp);
1148 WARN("Invalid FSP variant.\n");
1149 }
1150
1151 /* Let the raw path handle all the cbfs metadata logic. */
1152 return cbfstool_convert_raw(buffer, offset, header);
1153 }
1154
cbfstool_convert_mkstage(struct buffer * buffer,uint32_t * offset,struct cbfs_file * header)1155 static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
1156 struct cbfs_file *header)
1157 {
1158 struct buffer output;
1159 int ret;
1160
1161 /*
1162 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1163 * here. That is okay because XIP stages may not be compressed, so their size cannot
1164 * change anymore at a later point.
1165 */
1166 if (param.stage_xip) {
1167 size_t data_size, alignment;
1168 if (elf_program_file_size_align(buffer, &data_size, &alignment) < 0) {
1169 ERROR("Could not obtain ELF size & alignment\n");
1170 return 1;
1171 }
1172
1173 param.alignment = MAX(alignment, param.alignment);
1174
1175 if (do_cbfs_locate(offset, data_size)) {
1176 ERROR("Could not find location for stage.\n");
1177 return 1;
1178 }
1179 }
1180
1181 struct cbfs_file_attr_stageheader *stageheader = (void *)
1182 cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
1183 sizeof(struct cbfs_file_attr_stageheader));
1184 if (!stageheader)
1185 return -1;
1186
1187 if (param.stage_xip) {
1188 uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
1189 assert(IS_HOST_SPACE_ADDRESS(host_space_address));
1190 ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
1191 param.ignore_sections, stageheader);
1192 } else {
1193 ret = parse_elf_to_stage(buffer, &output, param.ignore_sections,
1194 stageheader);
1195 }
1196 if (ret != 0)
1197 return -1;
1198
1199 /* Store a hash of original uncompressed stage to compare later. */
1200 size_t decmp_size = buffer_size(&output);
1201 uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
1202
1203 /* Chain to base conversion routine to handle compression. */
1204 ret = cbfstool_convert_raw(&output, offset, header);
1205 if (ret != 0)
1206 goto fail;
1207
1208 /* Special care must be taken for LZ4-compressed stages that the BSS is
1209 large enough to provide scratch space for in-place decompression. */
1210 if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
1211 size_t memlen = be32toh(stageheader->memlen);
1212 size_t compressed_size = buffer_size(&output);
1213 uint8_t *compare_buffer = malloc(memlen);
1214 uint8_t *start = compare_buffer + memlen - compressed_size;
1215 if (!compare_buffer) {
1216 ERROR("Out of memory\n");
1217 goto fail;
1218 }
1219 memcpy(start, buffer_get(&output), compressed_size);
1220 ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
1221 if (ret == 0) {
1222 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1223 free(compare_buffer);
1224 goto fail;
1225 } else if (ret != (int)decmp_size ||
1226 decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
1227 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1228 free(compare_buffer);
1229 goto fail;
1230 }
1231 free(compare_buffer);
1232 }
1233
1234 buffer_delete(buffer);
1235 buffer_clone(buffer, &output);
1236 return 0;
1237
1238 fail:
1239 buffer_delete(&output);
1240 return -1;
1241 }
1242
cbfstool_convert_mkpayload(struct buffer * buffer,unused uint32_t * offset,struct cbfs_file * header)1243 static int cbfstool_convert_mkpayload(struct buffer *buffer,
1244 unused uint32_t *offset, struct cbfs_file *header)
1245 {
1246 struct buffer output;
1247 int ret;
1248 /* Per default, try and see if payload is an ELF binary */
1249 ret = parse_elf_to_payload(buffer, &output, param.compression);
1250
1251 /* If it's not an ELF, see if it's a FIT */
1252 if (ret != 0) {
1253 ret = parse_fit_to_payload(buffer, &output, param.compression);
1254 if (ret == 0)
1255 header->type = htobe32(CBFS_TYPE_FIT_PAYLOAD);
1256 }
1257
1258 /* If it's not an FIT, see if it's a UEFI FV */
1259 if (ret != 0)
1260 ret = parse_fv_to_payload(buffer, &output, param.compression);
1261
1262 /* If it's neither ELF nor UEFI Fv, try bzImage */
1263 if (ret != 0)
1264 ret = parse_bzImage_to_payload(buffer, &output,
1265 param.initrd, param.cmdline, param.compression);
1266
1267 /* Not a supported payload type */
1268 if (ret != 0) {
1269 ERROR("Not a supported payload type (ELF / FV).\n");
1270 buffer_delete(buffer);
1271 return -1;
1272 }
1273
1274 buffer_delete(buffer);
1275 // Direct assign, no dupe.
1276 memcpy(buffer, &output, sizeof(*buffer));
1277 header->len = htobe32(output.size);
1278 return 0;
1279 }
1280
cbfstool_convert_mkflatpayload(struct buffer * buffer,unused uint32_t * offset,struct cbfs_file * header)1281 static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
1282 unused uint32_t *offset, struct cbfs_file *header)
1283 {
1284 struct buffer output;
1285 if (parse_flat_binary_to_payload(buffer, &output,
1286 param.loadaddress,
1287 param.entrypoint,
1288 param.compression) != 0) {
1289 return -1;
1290 }
1291 buffer_delete(buffer);
1292 // Direct assign, no dupe.
1293 memcpy(buffer, &output, sizeof(*buffer));
1294 header->len = htobe32(output.size);
1295 return 0;
1296 }
1297
cbfs_add(void)1298 static int cbfs_add(void)
1299 {
1300 convert_buffer_t convert = cbfstool_convert_raw;
1301
1302 if (param.type == CBFS_TYPE_FSP) {
1303 convert = cbfstool_convert_fsp;
1304 } else if (param.type == CBFS_TYPE_STAGE) {
1305 ERROR("stages can only be added with cbfstool add-stage\n");
1306 return 1;
1307 } else if (param.stage_xip) {
1308 ERROR("cbfstool add supports xip only for FSP component type\n");
1309 return 1;
1310 }
1311
1312 return cbfs_add_component(param.filename,
1313 param.name,
1314 param.headeroffset,
1315 convert);
1316 }
1317
cbfs_add_stage(void)1318 static int cbfs_add_stage(void)
1319 {
1320 if (param.stage_xip && param.baseaddress_assigned) {
1321 ERROR("Cannot specify base address for XIP.\n");
1322 return 1;
1323 }
1324 param.type = CBFS_TYPE_STAGE;
1325
1326 return cbfs_add_component(param.filename,
1327 param.name,
1328 param.headeroffset,
1329 cbfstool_convert_mkstage);
1330 }
1331
cbfs_add_payload(void)1332 static int cbfs_add_payload(void)
1333 {
1334 param.type = CBFS_TYPE_SELF;
1335 return cbfs_add_component(param.filename,
1336 param.name,
1337 param.headeroffset,
1338 cbfstool_convert_mkpayload);
1339 }
1340
cbfs_add_flat_binary(void)1341 static int cbfs_add_flat_binary(void)
1342 {
1343 if (param.loadaddress == 0) {
1344 ERROR("You need to specify a valid "
1345 "-l/--load-address.\n");
1346 return 1;
1347 }
1348 if (param.entrypoint == 0) {
1349 ERROR("You need to specify a valid "
1350 "-e/--entry-point.\n");
1351 return 1;
1352 }
1353 param.type = CBFS_TYPE_SELF;
1354 return cbfs_add_component(param.filename,
1355 param.name,
1356 param.headeroffset,
1357 cbfstool_convert_mkflatpayload);
1358 }
1359
cbfs_add_integer(void)1360 static int cbfs_add_integer(void)
1361 {
1362 if (!param.u64val_assigned) {
1363 ERROR("You need to specify a value to write.\n");
1364 return 1;
1365 }
1366 return cbfs_add_integer_component(param.name,
1367 param.u64val,
1368 param.baseaddress,
1369 param.headeroffset);
1370 }
1371
cbfs_remove(void)1372 static int cbfs_remove(void)
1373 {
1374 if (!param.name) {
1375 ERROR("You need to specify -n/--name.\n");
1376 return 1;
1377 }
1378
1379 struct cbfs_image image;
1380 if (cbfs_image_from_buffer(&image, param.image_region,
1381 param.headeroffset))
1382 return 1;
1383
1384 if (cbfs_remove_entry(&image, param.name) != 0) {
1385 ERROR("Removing file '%s' failed.\n",
1386 param.name);
1387 return 1;
1388 }
1389
1390 return maybe_update_metadata_hash(&image);
1391 }
1392
cbfs_create(void)1393 static int cbfs_create(void)
1394 {
1395 struct cbfs_image image;
1396 memset(&image, 0, sizeof(image));
1397 buffer_clone(&image.buffer, param.image_region);
1398
1399 if (param.fmap) {
1400 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
1401 param.baseaddress_assigned ||
1402 param.headeroffset_assigned ||
1403 param.cbfsoffset_assigned ||
1404 param.bootblock) {
1405 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
1406 return 1;
1407 }
1408
1409 return cbfs_image_create(&image, image.buffer.size);
1410 }
1411
1412 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
1413 ERROR("You need to specify -m/--machine arch.\n");
1414 return 1;
1415 }
1416
1417 struct buffer bootblock;
1418 if (!param.bootblock) {
1419 DEBUG("-B not given, creating image without bootblock.\n");
1420 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
1421 return 1;
1422 } else if (buffer_from_file(&bootblock, param.bootblock)) {
1423 return 1;
1424 }
1425
1426 if (!param.alignment)
1427 param.alignment = CBFS_ALIGNMENT;
1428
1429 // Set default offsets. x86, as usual, needs to be a special snowflake.
1430 if (!param.baseaddress_assigned) {
1431 if (param.arch == CBFS_ARCHITECTURE_X86) {
1432 // Make sure there's at least enough room for rel_offset
1433 param.baseaddress = param.size -
1434 MAX(bootblock.size, sizeof(int32_t));
1435 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1436 param.baseaddress);
1437 } else {
1438 param.baseaddress = 0;
1439 DEBUG("bootblock starts at address 0x0.\n");
1440 }
1441 }
1442 if (!param.headeroffset_assigned) {
1443 if (param.arch == CBFS_ARCHITECTURE_X86) {
1444 param.headeroffset = param.baseaddress -
1445 sizeof(struct cbfs_header);
1446 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1447 param.headeroffset);
1448 } else {
1449 param.headeroffset = align_up(param.baseaddress +
1450 bootblock.size, sizeof(uint32_t));
1451 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1452 param.headeroffset);
1453 }
1454 }
1455 if (!param.cbfsoffset_assigned) {
1456 if (param.arch == CBFS_ARCHITECTURE_X86) {
1457 param.cbfsoffset = 0;
1458 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1459 } else {
1460 param.cbfsoffset = align_up(param.headeroffset +
1461 sizeof(struct cbfs_header),
1462 CBFS_ALIGNMENT);
1463 DEBUG("CBFS entries start beind master header (%#x).\n",
1464 param.cbfsoffset);
1465 }
1466 }
1467
1468 int ret = cbfs_legacy_image_create(&image,
1469 param.arch,
1470 CBFS_ALIGNMENT,
1471 &bootblock,
1472 param.baseaddress,
1473 param.headeroffset,
1474 param.cbfsoffset);
1475 buffer_delete(&bootblock);
1476 return ret;
1477 }
1478
cbfs_layout(void)1479 static int cbfs_layout(void)
1480 {
1481 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1482 if (!fmap) {
1483 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1484 return 1;
1485 }
1486
1487 printf("This image contains the following sections that can be %s with this tool:\n",
1488 param.show_immutable ? "accessed" : "manipulated");
1489 puts("");
1490 for (unsigned i = 0; i < fmap->nareas; ++i) {
1491 const struct fmap_area *current = fmap->areas + i;
1492
1493 bool readonly = partitioned_file_fmap_count(param.image_file,
1494 partitioned_file_fmap_select_children_of, current) ||
1495 region_is_flashmap((const char *)current->name);
1496 if (!param.show_immutable && readonly)
1497 continue;
1498
1499 printf("'%s'", current->name);
1500
1501 // Detect consecutive sections that describe the same region and
1502 // show them as aliases. This cannot find equivalent entries
1503 // that aren't adjacent; however, fmaptool doesn't generate
1504 // FMAPs with such sections, so this convenience feature works
1505 // for all but the strangest manually created FMAP binaries.
1506 // TODO: This could be done by parsing the FMAP into some kind
1507 // of tree that had duplicate lists in addition to child lists,
1508 // which would allow covering that weird, unlikely case as well.
1509 unsigned lookahead;
1510 for (lookahead = 1; i + lookahead < fmap->nareas;
1511 ++lookahead) {
1512 const struct fmap_area *consecutive =
1513 fmap->areas + i + lookahead;
1514 if (consecutive->offset != current->offset ||
1515 consecutive->size != current->size)
1516 break;
1517 printf(", '%s'", consecutive->name);
1518 }
1519 if (lookahead > 1)
1520 fputs(" are aliases for the same region", stdout);
1521
1522 const char *qualifier = "";
1523 if (readonly)
1524 qualifier = "read-only, ";
1525 else if (region_is_modern_cbfs((const char *)current->name))
1526 qualifier = "CBFS, ";
1527 else if (current->flags & FMAP_AREA_PRESERVE)
1528 qualifier = "preserve, ";
1529 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1530 current->offset);
1531
1532 i += lookahead - 1;
1533 }
1534 puts("");
1535
1536 if (param.show_immutable) {
1537 puts("It is at least possible to perform the read action on every section listed above.");
1538 } else {
1539 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1540 puts("To see the image's read-only sections as well, rerun with the -w option.");
1541 }
1542
1543 return 0;
1544 }
1545
verify_walker(__always_unused cbfs_dev_t dev,size_t offset,const union cbfs_mdata * mdata,size_t already_read,void * arg)1546 static enum cb_err verify_walker(__always_unused cbfs_dev_t dev, size_t offset,
1547 const union cbfs_mdata *mdata, size_t already_read, void *arg)
1548 {
1549 uint32_t type = be32toh(mdata->h.type);
1550 uint32_t data_offset = be32toh(mdata->h.offset);
1551 if (verification_exclude(type))
1552 return CB_CBFS_NOT_FOUND;
1553 assert(already_read == data_offset);
1554 const struct vb2_hash *hash = cbfs_file_hash(mdata);
1555 if (!hash)
1556 return CB_ERR;
1557 void *file_data = arg + offset + data_offset;
1558 if (vb2_hash_verify(false, file_data, be32toh(mdata->h.len), hash) != VB2_SUCCESS)
1559 return CB_CBFS_HASH_MISMATCH;
1560 return CB_CBFS_NOT_FOUND;
1561 }
1562
cbfs_print(void)1563 static int cbfs_print(void)
1564 {
1565 struct cbfs_image image;
1566 if (cbfs_image_from_buffer(&image, param.image_region,
1567 param.headeroffset))
1568 return 1;
1569 if (param.machine_parseable) {
1570 if (verbose)
1571 printf("[FMAP REGION]\t%s\n", param.region_name);
1572 cbfs_print_parseable_directory(&image);
1573 } else {
1574 printf("FMAP REGION: %s\n", param.region_name);
1575 cbfs_print_directory(&image);
1576 }
1577
1578 if (verbose) {
1579 const char *verification_state = "fully valid";
1580 struct mh_cache *mhc = get_mh_cache();
1581 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1582 return 0;
1583
1584 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
1585 enum cb_err err = cbfs_walk(&image, verify_walker, buffer_get(&image.buffer),
1586 &real_hash, CBFS_WALK_WRITEBACK_HASH);
1587 if (err == CB_CBFS_HASH_MISMATCH)
1588 verification_state = "invalid file hashes";
1589 else if (err != CB_CBFS_NOT_FOUND)
1590 verification_state = "missing file hashes";
1591 char *hash_str = bintohex(real_hash.raw,
1592 vb2_digest_size(real_hash.algo));
1593 printf("[METADATA HASH]\t%s:%s",
1594 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1595 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1596 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
1597 vb2_digest_size(real_hash.algo))) {
1598 printf(":valid");
1599 } else {
1600 printf(":invalid");
1601 verification_state = "invalid metadata hash";
1602 }
1603 }
1604 printf("\n");
1605 printf("[CBFS VERIFICATION (%s)]\t%s\n", param.region_name, verification_state);
1606 free(hash_str);
1607 }
1608
1609 return 0;
1610 }
1611
cbfs_extract(void)1612 static int cbfs_extract(void)
1613 {
1614 if (!param.filename) {
1615 ERROR("You need to specify -f/--filename.\n");
1616 return 1;
1617 }
1618
1619 if (!param.name) {
1620 ERROR("You need to specify -n/--name.\n");
1621 return 1;
1622 }
1623
1624 struct cbfs_image image;
1625 if (cbfs_image_from_buffer(&image, param.image_region,
1626 param.headeroffset))
1627 return 1;
1628
1629 return cbfs_export_entry(&image, param.name, param.filename,
1630 param.arch, !param.unprocessed);
1631 }
1632
cbfs_write(void)1633 static int cbfs_write(void)
1634 {
1635 if (!param.filename) {
1636 ERROR("You need to specify a valid input -f/--file.\n");
1637 return 1;
1638 }
1639 if (!partitioned_file_is_partitioned(param.image_file)) {
1640 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1641 return 1;
1642 }
1643
1644 if (!param.force && region_is_modern_cbfs(param.region_name)) {
1645 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1646 param.region_name);
1647 return 1;
1648 }
1649
1650 struct buffer new_content;
1651 if (buffer_from_file(&new_content, param.filename))
1652 return 1;
1653
1654 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1655 strlen(FMAP_SIGNATURE))) {
1656 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1657 param.filename);
1658 buffer_delete(&new_content);
1659 return 1;
1660 }
1661 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
1662 strlen(CBFS_FILE_MAGIC))) {
1663 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1664 param.filename);
1665 buffer_delete(&new_content);
1666 return 1;
1667 }
1668
1669 unsigned offset = 0;
1670 if (param.fill_partial_upward && param.fill_partial_downward) {
1671 ERROR("You may only specify one of -u and -d.\n");
1672 buffer_delete(&new_content);
1673 return 1;
1674 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1675 if (new_content.size != param.image_region->size) {
1676 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",
1677 new_content.size, param.image_region->size);
1678 buffer_delete(&new_content);
1679 return 1;
1680 }
1681 } else {
1682 if (new_content.size > param.image_region->size) {
1683 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1684 new_content.size, param.image_region->size);
1685 buffer_delete(&new_content);
1686 return 1;
1687 }
1688 if (param.u64val == (uint64_t)-1) {
1689 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1690 param.fill_partial_upward ? "bottom" : "top");
1691 } else if (param.u64val > 0xff) {
1692 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1693 buffer_delete(&new_content);
1694 return 1;
1695 } else {
1696 memset(buffer_get(param.image_region),
1697 param.u64val & 0xff,
1698 buffer_size(param.image_region));
1699 }
1700 if (param.fill_partial_downward)
1701 offset = param.image_region->size - new_content.size;
1702 }
1703
1704 memcpy(param.image_region->data + offset, new_content.data,
1705 new_content.size);
1706 buffer_delete(&new_content);
1707
1708 return maybe_update_fmap_hash();
1709 }
1710
cbfs_read(void)1711 static int cbfs_read(void)
1712 {
1713 if (!param.filename) {
1714 ERROR("You need to specify a valid output -f/--file.\n");
1715 return 1;
1716 }
1717 if (!partitioned_file_is_partitioned(param.image_file)) {
1718 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1719 return 1;
1720 }
1721
1722 return buffer_write_file(param.image_region, param.filename);
1723 }
1724
cbfs_copy(void)1725 static int cbfs_copy(void)
1726 {
1727 struct cbfs_image src_image;
1728 struct buffer src_buf;
1729
1730 if (!param.source_region) {
1731 ERROR("You need to specify -R/--source-region.\n");
1732 return 1;
1733 }
1734
1735 /* Obtain the source region and convert it to a cbfs_image. */
1736 if (!partitioned_file_read_region(&src_buf, param.image_file,
1737 param.source_region)) {
1738 ERROR("Region not found in image: %s\n", param.source_region);
1739 return 1;
1740 }
1741
1742 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1743 return 1;
1744
1745 return cbfs_copy_instance(&src_image, param.image_region);
1746 }
1747
cbfs_compact(void)1748 static int cbfs_compact(void)
1749 {
1750 struct cbfs_image image;
1751 if (cbfs_image_from_buffer(&image, param.image_region,
1752 param.headeroffset))
1753 return 1;
1754 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1755 return cbfs_compact_instance(&image);
1756 }
1757
cbfs_expand(void)1758 static int cbfs_expand(void)
1759 {
1760 struct buffer src_buf;
1761
1762 /* Obtain the source region. */
1763 if (!partitioned_file_read_region(&src_buf, param.image_file,
1764 param.region_name)) {
1765 ERROR("Region not found in image: %s\n", param.source_region);
1766 return 1;
1767 }
1768
1769 return cbfs_expand_to_region(param.image_region);
1770 }
1771
cbfs_truncate(void)1772 static int cbfs_truncate(void)
1773 {
1774 struct buffer src_buf;
1775
1776 /* Obtain the source region. */
1777 if (!partitioned_file_read_region(&src_buf, param.image_file,
1778 param.region_name)) {
1779 ERROR("Region not found in image: %s\n", param.source_region);
1780 return 1;
1781 }
1782
1783 uint32_t size;
1784 int result = cbfs_truncate_space(param.image_region, &size);
1785 if (!result)
1786 printf("0x%x\n", size);
1787 return result;
1788 }
1789
1790 static const struct command commands[] = {
1791 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
1792 {"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary,
1793 true, true},
1794 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
1795 true, true},
1796 {"add-stage", "a:H:r:f:n:t:c:b:P:QS:p:yvA:gh?", cbfs_add_stage,
1797 true, true},
1798 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
1799 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
1800 {"compact", "r:h?", cbfs_compact, true, true},
1801 {"copy", "r:R:h?", cbfs_copy, true, true},
1802 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
1803 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
1804 {"layout", "wvh?", cbfs_layout, false, false},
1805 {"print", "H:r:vkh?", cbfs_print, true, false},
1806 {"read", "r:f:vh?", cbfs_read, true, false},
1807 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
1808 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
1809 {"expand", "r:h?", cbfs_expand, true, true},
1810 {"truncate", "r:h?", cbfs_truncate, true, true},
1811 };
1812
1813 enum {
1814 /* begin after ASCII characters */
1815 LONGOPT_START = 256,
1816 LONGOPT_IBB = LONGOPT_START,
1817 LONGOPT_MMAP,
1818 LONGOPT_END,
1819 };
1820
1821 static struct option long_options[] = {
1822 {"alignment", required_argument, 0, 'a' },
1823 {"base-address", required_argument, 0, 'b' },
1824 {"bootblock", required_argument, 0, 'B' },
1825 {"cmdline", required_argument, 0, 'C' },
1826 {"compression", required_argument, 0, 'c' },
1827 {"topswap-size", required_argument, 0, 'j' },
1828 {"empty-fits", required_argument, 0, 'x' },
1829 {"entry-point", required_argument, 0, 'e' },
1830 {"file", required_argument, 0, 'f' },
1831 {"fill-downward", no_argument, 0, 'd' },
1832 {"fill-upward", no_argument, 0, 'u' },
1833 {"flashmap", required_argument, 0, 'M' },
1834 {"fmap-regions", required_argument, 0, 'r' },
1835 {"force", no_argument, 0, 'F' },
1836 {"source-region", required_argument, 0, 'R' },
1837 {"hash-algorithm",required_argument, 0, 'A' },
1838 {"header-offset", required_argument, 0, 'H' },
1839 {"help", no_argument, 0, 'h' },
1840 {"ignore-sec", required_argument, 0, 'S' },
1841 {"initrd", required_argument, 0, 'I' },
1842 {"int", required_argument, 0, 'i' },
1843 {"load-address", required_argument, 0, 'l' },
1844 {"machine", required_argument, 0, 'm' },
1845 {"name", required_argument, 0, 'n' },
1846 {"offset", required_argument, 0, 'o' },
1847 {"padding", required_argument, 0, 'p' },
1848 {"pow2page", no_argument, 0, 'Q' },
1849 {"ucode-region", required_argument, 0, 'q' },
1850 {"size", required_argument, 0, 's' },
1851 {"type", required_argument, 0, 't' },
1852 {"verbose", no_argument, 0, 'v' },
1853 {"with-readonly", no_argument, 0, 'w' },
1854 {"xip", no_argument, 0, 'y' },
1855 {"gen-attribute", no_argument, 0, 'g' },
1856 {"mach-parseable",no_argument, 0, 'k' },
1857 {"unprocessed", no_argument, 0, 'U' },
1858 {"ibb", no_argument, 0, LONGOPT_IBB },
1859 {"mmap", required_argument, 0, LONGOPT_MMAP },
1860 {NULL, 0, 0, 0 }
1861 };
1862
get_region_offset(long long int offset,uint32_t * region_offset)1863 static int get_region_offset(long long int offset, uint32_t *region_offset)
1864 {
1865 /* If offset is not negative, no transformation required. */
1866 if (offset >= 0) {
1867 *region_offset = offset;
1868 return 0;
1869 }
1870
1871 /* Calculate offset from start of region. */
1872 return convert_region_offset(-offset, region_offset);
1873 }
1874
calculate_region_offsets(void)1875 static int calculate_region_offsets(void)
1876 {
1877 int ret = 0;
1878
1879 if (param.baseaddress_assigned)
1880 ret |= get_region_offset(param.baseaddress_input, ¶m.baseaddress);
1881 if (param.headeroffset_assigned)
1882 ret |= get_region_offset(param.headeroffset_input, ¶m.headeroffset);
1883 if (param.cbfsoffset_assigned)
1884 ret |= get_region_offset(param.cbfsoffset_input, ¶m.cbfsoffset);
1885
1886 return ret;
1887 }
1888
dispatch_command(struct command command)1889 static int dispatch_command(struct command command)
1890 {
1891 if (command.accesses_region) {
1892 assert(param.image_file);
1893
1894 if (partitioned_file_is_partitioned(param.image_file)) {
1895 INFO("Performing operation on '%s' region...\n",
1896 param.region_name);
1897 }
1898 if (!partitioned_file_read_region(param.image_region,
1899 param.image_file, param.region_name)) {
1900 ERROR("The image will be left unmodified.\n");
1901 return 1;
1902 }
1903
1904 if (command.modifies_region) {
1905 // We (intentionally) don't support overwriting the FMAP
1906 // section. If you find yourself wanting to do this,
1907 // consider creating a new image rather than performing
1908 // whatever hacky transformation you were planning.
1909 if (region_is_flashmap(param.region_name)) {
1910 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1911 param.region_name);
1912 ERROR("The image will be left unmodified.\n");
1913 return 1;
1914 }
1915 // We don't allow writing raw data to regions that
1916 // contain nested regions, since doing so would
1917 // overwrite all such subregions.
1918 if (partitioned_file_region_contains_nested(
1919 param.image_file, param.region_name)) {
1920 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1921 param.region_name);
1922 ERROR("The image will be left unmodified.\n");
1923 return 1;
1924 }
1925 }
1926
1927 /*
1928 * Once image region is read, input offsets can be adjusted accordingly if the
1929 * inputs are provided as negative integers i.e. offsets from end of region.
1930 */
1931 if (calculate_region_offsets())
1932 return 1;
1933 }
1934
1935 if (command.function()) {
1936 if (partitioned_file_is_partitioned(param.image_file)) {
1937 ERROR("Failed while operating on '%s' region!\n",
1938 param.region_name);
1939 ERROR("The image will be left unmodified.\n");
1940 }
1941 return 1;
1942 }
1943
1944 return 0;
1945 }
1946
usage(char * name)1947 static void usage(char *name)
1948 {
1949 printf
1950 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
1951 "USAGE:\n" " %s [-h]\n"
1952 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
1953 " -H header_offset Do not search for header; use this offset*\n"
1954 " -T Output top-aligned memory address\n"
1955 " -u Accept short data; fill upward/from bottom\n"
1956 " -d Accept short data; fill downward/from top\n"
1957 " -F Force action\n"
1958 " -g Generate position and alignment arguments\n"
1959 " -U Unprocessed; don't decompress or make ELF\n"
1960 " -v Provide verbose output (-v=INFO -vv=DEBUG output)\n"
1961 " -h Display this help message\n\n"
1962 " --ext-win-base Base of extended decode window in host address\n"
1963 " space(x86 only)\n"
1964 " --ext-win-size Size of extended decode window in host address\n"
1965 " space(x86 only)\n"
1966 "COMMANDs:\n"
1967 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
1968 " [-c compression] [-b base-address | -a alignment] \\\n"
1969 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
1970 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1971 " [--ext-win-base win-base --ext-win-size win-size] "
1972 "Add a component\n"
1973 " "
1974 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
1975 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1976 " [-c compression] [-b base-address] \\\n"
1977 " (linux specific: [-C cmdline] [-I initrd]) "
1978 "Add a payload to the ROM\n"
1979 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
1980 " [-c compression] [-b base] \\\n"
1981 " [-S comma-separated-section(s)-to-ignore] \\\n"
1982 " [-a alignment] [-Q|--pow2page] \\\n"
1983 " [-y|--xip] [--ibb] \\\n"
1984 " [--ext-win-base win-base --ext-win-size win-size] "
1985 "Add a stage to the ROM\n"
1986 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1987 " [-A hash] -l load-address -e entry-point \\\n"
1988 " [-c compression] [-b base] "
1989 "Add a 32bit flat mode binary\n"
1990 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
1991 "Add a raw 64-bit integer value\n"
1992 " add-master-header [-r image,regions] \\ \n"
1993 " [-j topswap-size] (Intel CPUs only) "
1994 "Add a legacy CBFS master header\n"
1995 " remove [-r image,regions] -n NAME "
1996 "Remove a component\n"
1997 " compact -r image,regions "
1998 "Defragment CBFS image.\n"
1999 " copy -r image,regions -R source-region "
2000 "Create a copy (duplicate) cbfs instance in fmap\n"
2001 " create -m ARCH -s size [-b bootblock offset] \\\n"
2002 " [-o CBFS offset] [-H header offset] [-B bootblock] "
2003 "Create a legacy ROM file with CBFS master header*\n"
2004 " create -M flashmap [-r list,of,regions,containing,cbfses] "
2005 "Create a new-style partitioned firmware image\n"
2006 " layout [-w] "
2007 "List mutable (or, with -w, readable) image regions\n"
2008 " print [-r image,regions] [-k] "
2009 "Show the contents of the ROM\n"
2010 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
2011 "Extracts a file from ROM\n"
2012 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
2013 "Write file into same-size [or larger] raw region\n"
2014 " read [-r fmap-region] -f file "
2015 "Extract raw region contents into binary file\n"
2016 " truncate [-r fmap-region] "
2017 "Truncate CBFS and print new size on stdout\n"
2018 " expand [-r fmap-region] "
2019 "Expand CBFS to span entire region\n"
2020 "OFFSETs:\n"
2021 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
2022 " in two possible formats: if their value is greater than\n"
2023 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
2024 " address; otherwise, they are treated as an offset into flash.\n"
2025 "ARCHes:\n", name, name
2026 );
2027 print_supported_architectures();
2028
2029 printf("TYPEs:\n");
2030 print_supported_filetypes();
2031 printf(
2032 "\n* Note that these actions and switches are only valid when\n"
2033 " working with legacy images whose structure is described\n"
2034 " primarily by a CBFS master header. New-style images, in\n"
2035 " contrast, exclusively make use of an FMAP to describe their\n"
2036 " layout: this must minimally contain an '%s' section\n"
2037 " specifying the location of this FMAP itself and a '%s'\n"
2038 " section describing the primary CBFS. It should also be noted\n"
2039 " that, when working with such images, the -F and -r switches\n"
2040 " default to '%s' for convenience, and the -b switch becomes\n"
2041 " relative to the selected CBFS region's lowest address.\n"
2042 " The one exception to this rule is the top-aligned address,\n"
2043 " which is always relative to the end of the entire image\n"
2044 " rather than relative to the local region; this is true for\n"
2045 " for both input (sufficiently large) and output (-T) data.\n",
2046 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
2047 SECTION_NAME_PRIMARY_CBFS
2048 );
2049 }
2050
valid_opt(size_t i,int c)2051 static bool valid_opt(size_t i, int c)
2052 {
2053 /* Check if it is one of the optstrings supported by the command. */
2054 if (strchr(commands[i].optstring, c))
2055 return true;
2056
2057 /*
2058 * Check if it is one of the non-ASCII characters. Currently, the
2059 * non-ASCII characters are only checked against the valid list
2060 * irrespective of the command.
2061 */
2062 if (c >= LONGOPT_START && c < LONGOPT_END)
2063 return true;
2064
2065 return false;
2066 }
2067
main(int argc,char ** argv)2068 int main(int argc, char **argv)
2069 {
2070 size_t i;
2071 int c;
2072
2073 if (argc < 3) {
2074 usage(argv[0]);
2075 return 1;
2076 }
2077
2078 char *image_name = argv[1];
2079 char *cmd = argv[2];
2080 optind += 2;
2081
2082 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2083 if (strcmp(cmd, commands[i].name) != 0)
2084 continue;
2085
2086 while (1) {
2087 char *suffix = NULL;
2088 int option_index = 0;
2089
2090 c = getopt_long(argc, argv, commands[i].optstring,
2091 long_options, &option_index);
2092 if (c == -1) {
2093 if (optind < argc) {
2094 ERROR("%s: excessive argument -- '%s'"
2095 "\n", argv[0], argv[optind]);
2096 return 1;
2097 }
2098 break;
2099 }
2100
2101 /* Filter out illegal long options */
2102 if (!valid_opt(i, c)) {
2103 ERROR("%s: invalid option -- '%d'\n",
2104 argv[0], c);
2105 c = '?';
2106 }
2107
2108 switch(c) {
2109 case 'n':
2110 param.name = optarg;
2111 break;
2112 case 't':
2113 if (intfiletype(optarg) != ((uint64_t) - 1))
2114 param.type = intfiletype(optarg);
2115 else
2116 param.type = strtoul(optarg, NULL, 0);
2117 if (param.type == 0)
2118 WARN("Unknown type '%s' ignored\n",
2119 optarg);
2120 break;
2121 case 'c': {
2122 if (strcmp(optarg, "precompression") == 0) {
2123 param.precompression = 1;
2124 break;
2125 }
2126 int algo = cbfs_parse_comp_algo(optarg);
2127 if (algo >= 0)
2128 param.compression = algo;
2129 else
2130 WARN("Unknown compression '%s' ignored.\n",
2131 optarg);
2132 break;
2133 }
2134 case 'A': {
2135 if (!vb2_lookup_hash_alg(optarg, ¶m.hash)) {
2136 ERROR("Unknown hash algorithm '%s'.\n",
2137 optarg);
2138 return 1;
2139 }
2140 break;
2141 }
2142 case 'M':
2143 param.fmap = optarg;
2144 break;
2145 case 'r':
2146 param.region_name = optarg;
2147 break;
2148 case 'R':
2149 param.source_region = optarg;
2150 break;
2151 case 'b':
2152 param.baseaddress_input = strtoll(optarg, &suffix, 0);
2153 if (!*optarg || (suffix && *suffix)) {
2154 ERROR("Invalid base address '%s'.\n",
2155 optarg);
2156 return 1;
2157 }
2158 // baseaddress may be zero on non-x86, so we
2159 // need an explicit "baseaddress_assigned".
2160 param.baseaddress_assigned = 1;
2161 break;
2162 case 'l':
2163 param.loadaddress = strtoull(optarg, &suffix, 0);
2164 if (!*optarg || (suffix && *suffix)) {
2165 ERROR("Invalid load address '%s'.\n",
2166 optarg);
2167 return 1;
2168 }
2169 break;
2170 case 'e':
2171 param.entrypoint = strtoull(optarg, &suffix, 0);
2172 if (!*optarg || (suffix && *suffix)) {
2173 ERROR("Invalid entry point '%s'.\n",
2174 optarg);
2175 return 1;
2176 }
2177 break;
2178 case 's':
2179 param.size = strtoul(optarg, &suffix, 0);
2180 if (!*optarg) {
2181 ERROR("Empty size specified.\n");
2182 return 1;
2183 }
2184 switch (tolower((int)suffix[0])) {
2185 case 'k':
2186 param.size *= 1024;
2187 break;
2188 case 'm':
2189 param.size *= 1024 * 1024;
2190 break;
2191 case '\0':
2192 break;
2193 default:
2194 ERROR("Invalid suffix for size '%s'.\n",
2195 optarg);
2196 return 1;
2197 }
2198 break;
2199 case 'B':
2200 param.bootblock = optarg;
2201 break;
2202 case 'H':
2203 param.headeroffset_input = strtoll(optarg, &suffix, 0);
2204 if (!*optarg || (suffix && *suffix)) {
2205 ERROR("Invalid header offset '%s'.\n",
2206 optarg);
2207 return 1;
2208 }
2209 param.headeroffset_assigned = 1;
2210 break;
2211 case 'a':
2212 param.alignment = strtoul(optarg, &suffix, 0);
2213 if (!*optarg || (suffix && *suffix)) {
2214 ERROR("Invalid alignment '%s'.\n",
2215 optarg);
2216 return 1;
2217 }
2218 break;
2219 case 'p':
2220 param.padding = strtoul(optarg, &suffix, 0);
2221 if (!*optarg || (suffix && *suffix)) {
2222 ERROR("Invalid pad size '%s'.\n",
2223 optarg);
2224 return 1;
2225 }
2226 break;
2227 case 'Q':
2228 param.force_pow2_pagesize = 1;
2229 break;
2230 case 'o':
2231 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
2232 if (!*optarg || (suffix && *suffix)) {
2233 ERROR("Invalid cbfs offset '%s'.\n",
2234 optarg);
2235 return 1;
2236 }
2237 param.cbfsoffset_assigned = 1;
2238 break;
2239 case 'f':
2240 param.filename = optarg;
2241 break;
2242 case 'F':
2243 param.force = 1;
2244 break;
2245 case 'i':
2246 param.u64val = strtoull(optarg, &suffix, 0);
2247 param.u64val_assigned = 1;
2248 if (!*optarg || (suffix && *suffix)) {
2249 ERROR("Invalid int parameter '%s'.\n",
2250 optarg);
2251 return 1;
2252 }
2253 break;
2254 case 'u':
2255 param.fill_partial_upward = true;
2256 break;
2257 case 'd':
2258 param.fill_partial_downward = true;
2259 break;
2260 case 'w':
2261 param.show_immutable = true;
2262 break;
2263 case 'j':
2264 param.topswap_size = strtol(optarg, NULL, 0);
2265 if (!is_valid_topswap())
2266 return 1;
2267 break;
2268 case 'q':
2269 param.ucode_region = optarg;
2270 break;
2271 case 'v':
2272 verbose++;
2273 break;
2274 case 'm':
2275 param.arch = string_to_arch(optarg);
2276 break;
2277 case 'I':
2278 param.initrd = optarg;
2279 break;
2280 case 'C':
2281 param.cmdline = optarg;
2282 break;
2283 case 'S':
2284 param.ignore_sections = optarg;
2285 break;
2286 case 'y':
2287 param.stage_xip = true;
2288 break;
2289 case 'g':
2290 param.autogen_attr = true;
2291 break;
2292 case 'k':
2293 param.machine_parseable = true;
2294 break;
2295 case 'U':
2296 param.unprocessed = true;
2297 break;
2298 case LONGOPT_IBB:
2299 param.ibb = true;
2300 break;
2301 case LONGOPT_MMAP:
2302 if (decode_mmap_arg(optarg))
2303 return 1;
2304 break;
2305 case 'h':
2306 case '?':
2307 usage(argv[0]);
2308 return 1;
2309 default:
2310 break;
2311 }
2312 }
2313
2314 if (commands[i].function == cbfs_create) {
2315 if (param.fmap) {
2316 struct buffer flashmap;
2317 if (buffer_from_file(&flashmap, param.fmap))
2318 return 1;
2319 param.image_file = partitioned_file_create(
2320 image_name, &flashmap);
2321 buffer_delete(&flashmap);
2322 } else if (param.size) {
2323 param.image_file = partitioned_file_create_flat(
2324 image_name, param.size);
2325 } else {
2326 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
2327 return 1;
2328 }
2329 } else {
2330 bool write_access = commands[i].modifies_region;
2331
2332 param.image_file =
2333 partitioned_file_reopen(image_name,
2334 write_access);
2335 }
2336 if (!param.image_file)
2337 return 1;
2338
2339 unsigned num_regions = 1;
2340 for (const char *list = strchr(param.region_name, ','); list;
2341 list = strchr(list + 1, ','))
2342 ++num_regions;
2343
2344 // If the action needs to read an image region, as indicated by
2345 // having accesses_region set in its command struct, that
2346 // region's buffer struct will be stored here and the client
2347 // will receive a pointer to it via param.image_region. It
2348 // need not write the buffer back to the image file itself,
2349 // since this behavior can be requested via its modifies_region
2350 // field. Additionally, it should never free the region buffer,
2351 // as that is performed automatically once it completes.
2352 struct buffer image_regions[num_regions];
2353 memset(image_regions, 0, sizeof(image_regions));
2354
2355 bool seen_primary_cbfs = false;
2356 char region_name_scratch[strlen(param.region_name) + 1];
2357 strcpy(region_name_scratch, param.region_name);
2358 param.region_name = strtok(region_name_scratch, ",");
2359 for (unsigned region = 0; region < num_regions; ++region) {
2360 if (!param.region_name) {
2361 ERROR("Encountered illegal degenerate region name in -r list\n");
2362 ERROR("The image will be left unmodified.\n");
2363 partitioned_file_close(param.image_file);
2364 return 1;
2365 }
2366
2367 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2368 == 0)
2369 seen_primary_cbfs = true;
2370
2371 param.image_region = image_regions + region;
2372 if (dispatch_command(commands[i])) {
2373 partitioned_file_close(param.image_file);
2374 return 1;
2375 }
2376
2377 param.region_name = strtok(NULL, ",");
2378 }
2379
2380 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2381 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2382 SECTION_NAME_PRIMARY_CBFS);
2383 ERROR("The image will be left unmodified.\n");
2384 partitioned_file_close(param.image_file);
2385 return 1;
2386 }
2387
2388 if (commands[i].modifies_region) {
2389 assert(param.image_file);
2390 for (unsigned region = 0; region < num_regions;
2391 ++region) {
2392
2393 if (!partitioned_file_write_region(
2394 param.image_file,
2395 image_regions + region)) {
2396 partitioned_file_close(
2397 param.image_file);
2398 return 1;
2399 }
2400 }
2401 }
2402
2403 partitioned_file_close(param.image_file);
2404 return 0;
2405 }
2406
2407 ERROR("Unknown command '%s'.\n", cmd);
2408 usage(argv[0]);
2409 return 1;
2410 }
2411