blob: ff10131e4744ecd0af220b94cdfeb92b44128a76 [file] [log] [blame]
Patrick Georgiea063cb2020-05-08 19:28:13 +02001/* cbfstool, CLI utility for CBFS file manipulation */
Patrick Georgi7333a112020-05-08 20:48:04 +02002/* SPDX-License-Identifier: GPL-2.0-only */
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +00003
Patrick Georgib7b56dd82009-09-14 13:29:27 +00004#include <stdio.h>
Stefan Reinauera1e48242011-10-21 14:24:57 -07005#include <stdlib.h>
Stefan Reinauer336daa72009-12-21 15:09:01 +00006#include <string.h>
Sol Boucher0e539312015-03-05 15:38:03 -08007#include <strings.h>
Stefan Reinauera1e48242011-10-21 14:24:57 -07008#include <ctype.h>
Stefan Reinauer63217582012-10-29 16:52:36 -07009#include <unistd.h>
10#include <getopt.h>
Patrick Georgib7b56dd82009-09-14 13:29:27 +000011#include "common.h"
12#include "cbfs.h"
Hung-Te Lin3bb035b2013-01-29 02:15:49 +080013#include "cbfs_image.h"
Sol Bouchere3260a02015-03-25 13:40:08 -070014#include "cbfs_sections.h"
Aaron Durbinfacf1492017-12-18 14:50:22 -070015#include "elfparsing.h"
Sol Bouchere3260a02015-03-25 13:40:08 -070016#include "partitioned_file.h"
Julius Werner81dc20e2020-10-15 17:37:57 -070017#include "lz4/lib/xxhash.h"
Julius Werner4bfbabd2020-05-06 17:27:02 -070018#include <commonlib/bsd/cbfs_private.h>
Julius Werner81dc20e2020-10-15 17:37:57 -070019#include <commonlib/bsd/compression.h>
Julius Werner4bfbabd2020-05-06 17:27:02 -070020#include <commonlib/bsd/metadata_hash.h>
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -070021#include <commonlib/fsp.h>
Nico Huber607796a2017-01-17 13:01:25 +010022#include <commonlib/endian.h>
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +053023#include <commonlib/helpers.h>
Furquan Shaikh0dcc0662020-11-20 22:51:17 -080024#include <commonlib/region.h>
Julius Wernerd4775652020-03-13 16:43:34 -070025#include <vboot_host.h>
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +000026
Stefan Reinauer3fec29c2009-09-22 15:58:19 +000027struct command {
Stefan Reinauer3fec29c2009-09-22 15:58:19 +000028 const char *name;
Stefan Reinauer63217582012-10-29 16:52:36 -070029 const char *optstring;
30 int (*function) (void);
Sol Bouchere3260a02015-03-25 13:40:08 -070031 // Whether to populate param.image_region before invoking function
32 bool accesses_region;
Vadim Bendebury75599462015-12-09 09:39:31 -080033 // 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
Sol Bouchere3260a02015-03-25 13:40:08 -070037 bool modifies_region;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +000038};
39
Hung-Te Lind1739622013-01-28 14:23:49 +080040static struct param {
Sol Bouchere3260a02015-03-25 13:40:08 -070041 partitioned_file_t *image_file;
42 struct buffer *image_region;
Sol Boucher67a0a862015-03-18 12:36:27 -070043 const char *name;
44 const char *filename;
45 const char *fmap;
46 const char *region_name;
Patrick Georgibd0bb232015-11-20 21:48:18 +010047 const char *source_region;
Sol Boucher67a0a862015-03-18 12:36:27 -070048 const char *bootblock;
49 const char *ignore_section;
Rizwan Qureshic1072f22018-06-04 23:02:46 +053050 const char *ucode_region;
Peter Stuge3bfd5b82013-07-09 19:39:13 +020051 uint64_t u64val;
Hung-Te Lind1739622013-01-28 14:23:49 +080052 uint32_t type;
53 uint32_t baseaddress;
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -080054 /*
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;
Hung-Te Linf56c73f2013-01-29 09:45:12 +080059 uint32_t baseaddress_assigned;
Hung-Te Lind1739622013-01-28 14:23:49 +080060 uint32_t loadaddress;
Hung-Te Linf56c73f2013-01-29 09:45:12 +080061 uint32_t headeroffset;
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -080062 /*
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;
Hung-Te Linf56c73f2013-01-29 09:45:12 +080067 uint32_t headeroffset_assigned;
Hung-Te Lind1739622013-01-28 14:23:49 +080068 uint32_t entrypoint;
69 uint32_t size;
70 uint32_t alignment;
Hung-Te Line9198372013-03-19 12:17:12 +080071 uint32_t pagesize;
Julius Wernerefcee762014-11-10 13:14:24 -080072 uint32_t cbfsoffset;
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -080073 /*
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;
Julius Wernerefcee762014-11-10 13:14:24 -080078 uint32_t cbfsoffset_assigned;
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -060079 uint32_t arch;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -070080 uint32_t padding;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +053081 uint32_t topswap_size;
Patrick Georgi16b3e4b2016-12-12 18:38:01 +010082 bool u64val_assigned;
Sol Boucher67a0a862015-03-18 12:36:27 -070083 bool fill_partial_upward;
84 bool fill_partial_downward;
85 bool show_immutable;
Aaron Durbin4be16742015-09-15 17:00:23 -050086 bool stage_xip;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +030087 bool force_pow2_pagesize;
Werner Zehe9995f12016-01-14 13:22:37 +010088 bool autogen_attr;
Aaron Durbin5dc628a2016-01-26 15:35:34 -060089 bool machine_parseable;
Joel Kitching21fdd892018-08-09 17:49:52 +080090 bool unprocessed;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +010091 bool ibb;
Julius Wernerd4775652020-03-13 16:43:34 -070092 enum cbfs_compression compression;
Patrick Georgiecaa5702017-01-11 18:38:11 +010093 int precompression;
Patrick Georgi89f20342015-10-01 15:54:04 +020094 enum vb2_hash_algorithm hash;
Joel Kitching672d5ad2018-10-11 17:01:30 +080095 /* For linux payloads */
Patrick Georgide36d332013-08-27 20:22:21 +020096 char *initrd;
97 char *cmdline;
Patrick Georgi01fbc3a2016-10-12 16:46:13 +020098 int force;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -080099 /*
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;
Hung-Te Lind1739622013-01-28 14:23:49 +0800111} param = {
112 /* All variables not listed are initialized as zero. */
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -0600113 .arch = CBFS_ARCHITECTURE_UNKNOWN,
Sol Boucher65336712015-05-07 21:00:05 -0700114 .compression = CBFS_COMPRESS_NONE,
Patrick Georgi89f20342015-10-01 15:54:04 +0200115 .hash = VB2_HASH_INVALID,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800116 .headeroffset = ~0,
Sol Boucher67a0a862015-03-18 12:36:27 -0700117 .region_name = SECTION_NAME_PRIMARY_CBFS,
Patrick Georgid9edb182016-12-06 18:55:26 +0100118 .u64val = -1,
Hung-Te Lind1739622013-01-28 14:23:49 +0800119};
Stefan Reinauer63217582012-10-29 16:52:36 -0700120
Julius Werner4bfbabd2020-05-06 17:27:02 -0700121/*
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 */
130struct mh_cache {
131 const char *region;
132 size_t offset;
133 struct vb2_hash cbfs_hash;
Julius Werner76dab5f2020-03-19 21:09:35 -0700134 platform_fixup_func fixup;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700135 bool initialized;
136};
137
138static 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 bootblock. 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 *bootblock;
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 bootblock = cbfs_get_entry(&cbfs, "bootblock");
Alex James02001a382021-12-19 16:41:59 -0600172 if (!bootblock || be32toh(bootblock->type) != CBFS_TYPE_BOOTBLOCK)
Julius Werner4bfbabd2020-05-06 17:27:02 -0700173 goto no_metadata_hash;
Alex James02001a382021-12-19 16:41:59 -0600174 offset = (void *)bootblock + be32toh(bootblock->offset) -
Julius Werner4bfbabd2020-05-06 17:27:02 -0700175 buffer_get(&cbfs.buffer);
Alex James02001a382021-12-19 16:41:59 -0600176 size = be32toh(bootblock->len);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700177 }
178
179 /* Find and validate the metadata hash anchor inside the bootblock and
180 record its exact byte offset from the start of the FMAP region. */
181 struct metadata_hash_anchor *anchor = memmem(buffer_get(&buffer) + offset,
182 size, METADATA_HASH_ANCHOR_MAGIC, sizeof(anchor->magic));
183 if (anchor) {
184 if (!vb2_digest_size(anchor->cbfs_hash.algo)) {
185 ERROR("Unknown CBFS metadata hash type: %d\n",
186 anchor->cbfs_hash.algo);
187 goto no_metadata_hash;
188 }
189 mhc.cbfs_hash = anchor->cbfs_hash;
190 mhc.offset = (void *)anchor - buffer_get(&buffer);
Julius Werner76dab5f2020-03-19 21:09:35 -0700191 mhc.fixup = platform_fixups_probe(&buffer, mhc.offset,
192 mhc.region);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700193 return &mhc;
194 }
195
196no_metadata_hash:
197 mhc.cbfs_hash.algo = VB2_HASH_INVALID;
198 return &mhc;
199}
200
201static void update_and_info(const char *name, void *dst, void *src, size_t size)
202{
203 if (!memcmp(dst, src, size))
204 return;
205 char *src_str = bintohex(src, size);
206 char *dst_str = bintohex(dst, size);
207 INFO("Updating %s from %s to %s\n", name, dst_str, src_str);
208 memcpy(dst, src, size);
209 free(src_str);
210 free(dst_str);
211}
212
213static int update_anchor(struct mh_cache *mhc, uint8_t *fmap_hash)
214{
215 struct buffer buffer;
216 if (!partitioned_file_read_region(&buffer, param.image_file,
217 mhc->region))
218 return -1;
219 struct metadata_hash_anchor *anchor = buffer_get(&buffer) + mhc->offset;
220 /* The metadata hash anchor should always still be where we left it. */
221 assert(!memcmp(anchor->magic, METADATA_HASH_ANCHOR_MAGIC,
222 sizeof(anchor->magic)) &&
223 anchor->cbfs_hash.algo == mhc->cbfs_hash.algo);
224 update_and_info("CBFS metadata hash", anchor->cbfs_hash.raw,
225 mhc->cbfs_hash.raw, vb2_digest_size(anchor->cbfs_hash.algo));
226 if (fmap_hash) {
227 update_and_info("FMAP hash",
228 metadata_hash_anchor_fmap_hash(anchor), fmap_hash,
229 vb2_digest_size(anchor->cbfs_hash.algo));
230 }
Julius Werner76dab5f2020-03-19 21:09:35 -0700231 if (mhc->fixup && mhc->fixup(&buffer, mhc->offset) != 0)
232 return -1;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700233 if (!partitioned_file_write_region(param.image_file, &buffer))
234 return -1;
235 return 0;
236
237}
238
239/* This should be called after every time CBFS metadata might have changed. It
240 will recalculate and update the metadata hash in the bootblock if needed. */
241static int maybe_update_metadata_hash(struct cbfs_image *cbfs)
242{
243 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS))
244 return 0; /* Metadata hash only embedded in primary CBFS. */
245
246 struct mh_cache *mhc = get_mh_cache();
247 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
248 return 0;
249
Julius Werner69cc5572022-03-04 17:49:56 -0800250 enum cb_err err = cbfs_walk(cbfs, NULL, NULL, &mhc->cbfs_hash,
251 CBFS_WALK_WRITEBACK_HASH);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700252 if (err != CB_CBFS_NOT_FOUND) {
253 ERROR("Unexpected cbfs_walk() error %d\n", err);
254 return -1;
255 }
256
257 return update_anchor(mhc, NULL);
258}
259
260/* This should be called after every time the FMAP or the bootblock itself might
261 have changed, and will write the new FMAP hash into the metadata hash anchor
262 in the bootblock if required (usually when the bootblock is first added). */
263static int maybe_update_fmap_hash(void)
264{
265 if (strcmp(param.region_name, SECTION_NAME_BOOTBLOCK) &&
266 strcmp(param.region_name, SECTION_NAME_FMAP) &&
267 param.type != CBFS_TYPE_BOOTBLOCK)
268 return 0; /* FMAP and bootblock didn't change. */
269
270 struct mh_cache *mhc = get_mh_cache();
271 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
272 return 0;
273
274 uint8_t fmap_hash[VB2_MAX_DIGEST_SIZE];
275 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
276 if (!fmap || vb2_digest_buffer((const void *)fmap, fmap_size(fmap),
277 mhc->cbfs_hash.algo, fmap_hash, sizeof(fmap_hash)))
278 return -1;
279 return update_anchor(mhc, fmap_hash);
280}
281
Julius Werneraf20fd72022-05-27 18:33:15 -0700282static bool verification_exclude(enum cbfs_type type)
283{
284 switch (type) {
285 case CBFS_TYPE_BOOTBLOCK:
286 case CBFS_TYPE_CBFSHEADER:
287 case CBFS_TYPE_INTEL_FIT:
288 return true;
289 default:
290 return false;
291 }
292}
293
Sol Boucher67a0a862015-03-18 12:36:27 -0700294static bool region_is_flashmap(const char *region)
295{
296 return partitioned_file_region_check_magic(param.image_file, region,
297 FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
298}
299
300/* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
301static bool region_is_modern_cbfs(const char *region)
302{
303 return partitioned_file_region_check_magic(param.image_file, region,
304 CBFS_FILE_MAGIC, strlen(CBFS_FILE_MAGIC));
305}
306
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800307/* This describes a window from the SPI flash address space into the host address space. */
308struct mmap_window {
309 struct region flash_space;
310 struct region host_space;
311};
312
313enum mmap_window_type {
314 X86_DEFAULT_DECODE_WINDOW, /* Decode window just below 4G boundary */
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800315 X86_EXTENDED_DECODE_WINDOW, /* Extended decode window for mapping greater than 16MiB
316 flash */
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800317 MMAP_MAX_WINDOWS,
318};
319
320/* Table of all the decode windows supported by the platform. */
321static struct mmap_window mmap_window_table[MMAP_MAX_WINDOWS];
322
323static void add_mmap_window(enum mmap_window_type idx, size_t flash_offset, size_t host_offset,
324 size_t window_size)
325{
326 if (idx >= MMAP_MAX_WINDOWS) {
327 ERROR("Incorrect mmap window index(%d)\n", idx);
328 return;
329 }
330
331 mmap_window_table[idx].flash_space.offset = flash_offset;
332 mmap_window_table[idx].host_space.offset = host_offset;
333 mmap_window_table[idx].flash_space.size = window_size;
334 mmap_window_table[idx].host_space.size = window_size;
335}
336
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800337#define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
338#define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
339
340static bool create_mmap_windows(void)
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800341{
342 static bool done;
343
344 if (done)
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800345 return done;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800346
347 const size_t image_size = partitioned_file_total_size(param.image_file);
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800348 const size_t std_window_size = MIN(DEFAULT_DECODE_WINDOW_MAX_SIZE, image_size);
349 const size_t std_window_flash_offset = image_size - std_window_size;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800350
351 /*
352 * Default decode window lives just below 4G boundary in host space and maps up to a
353 * maximum of 16MiB. If the window is smaller than 16MiB, the SPI flash window is mapped
354 * at the top of the host window just below 4G.
355 */
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800356 add_mmap_window(X86_DEFAULT_DECODE_WINDOW, std_window_flash_offset,
357 DEFAULT_DECODE_WINDOW_TOP - std_window_size, std_window_size);
358
359 if (param.ext_win_size && (image_size > DEFAULT_DECODE_WINDOW_MAX_SIZE)) {
360 /*
361 * If the platform supports extended window and the SPI flash size is greater
362 * than 16MiB, then create a mapping for the extended window as well.
363 * The assumptions here are:
364 * 1. Top 16MiB is still decoded in the fixed decode window just below 4G
365 * boundary.
366 * 2. Rest of the SPI flash below the top 16MiB is mapped at the top of extended
367 * window. Even though the platform might support a larger extended window, the
368 * SPI flash part used by the mainboard might not be large enough to be mapped
369 * in the entire window. In such cases, the mapping is assumed to be in the top
370 * part of the extended window with the bottom part remaining unused.
371 *
372 * Example:
373 * ext_win_base = 0xF8000000
374 * ext_win_size = 32 * MiB
375 * ext_win_limit = ext_win_base + ext_win_size - 1 = 0xF9FFFFFF
376 *
377 * If SPI flash is 32MiB, then top 16MiB is mapped from 0xFF000000 - 0xFFFFFFFF
378 * whereas the bottom 16MiB is mapped from 0xF9000000 - 0xF9FFFFFF. The extended
379 * window 0xF8000000 - 0xF8FFFFFF remains unused.
380 */
381 const size_t ext_window_mapped_size = MIN(param.ext_win_size,
382 image_size - std_window_size);
383 const size_t ext_window_top = param.ext_win_base + param.ext_win_size;
384 add_mmap_window(X86_EXTENDED_DECODE_WINDOW,
385 std_window_flash_offset - ext_window_mapped_size,
386 ext_window_top - ext_window_mapped_size,
387 ext_window_mapped_size);
388
389 if (region_overlap(&mmap_window_table[X86_EXTENDED_DECODE_WINDOW].host_space,
390 &mmap_window_table[X86_DEFAULT_DECODE_WINDOW].host_space)) {
391 const struct region *ext_region;
392
393 ext_region = &mmap_window_table[X86_EXTENDED_DECODE_WINDOW].host_space;
394 ERROR("Extended window(base=0x%zx, limit=0x%zx) overlaps with default window!\n",
395 region_offset(ext_region), region_end(ext_region));
396
397 return false;
398 }
399 }
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800400
401 done = true;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800402 return done;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800403}
404
405static unsigned int convert_address(const struct region *to, const struct region *from,
406 unsigned int addr)
407{
408 /*
409 * Calculate the offset in the "from" region and use that offset to calculate
410 * corresponding address in the "to" region.
411 */
412 size_t offset = addr - region_offset(from);
413 return region_offset(to) + offset;
414}
415
416enum mmap_addr_type {
417 HOST_SPACE_ADDR,
418 FLASH_SPACE_ADDR,
419};
420
421static int find_mmap_window(enum mmap_addr_type addr_type, unsigned int addr)
422{
423 size_t i;
424
425 for (i = 0; i < ARRAY_SIZE(mmap_window_table); i++) {
426 const struct region *reg;
427
428 if (addr_type == HOST_SPACE_ADDR)
429 reg = &mmap_window_table[i].host_space;
430 else
431 reg = &mmap_window_table[i].flash_space;
432
Kyösti Mälkkib57373b2021-02-13 01:57:14 +0200433 if (region_offset(reg) <= addr &&
434 ((uint64_t)region_offset(reg) + (uint64_t)region_sz(reg) - 1) >= addr)
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800435 return i;
436 }
437
438 return -1;
439}
440
441static unsigned int convert_host_to_flash(const struct buffer *region, unsigned int addr)
442{
443 int idx;
444 const struct region *to, *from;
445
446 idx = find_mmap_window(HOST_SPACE_ADDR, addr);
447 if (idx == -1) {
448 ERROR("Host address(%x) not in any mmap window!\n", addr);
449 return 0;
450 }
451
452 to = &mmap_window_table[idx].flash_space;
453 from = &mmap_window_table[idx].host_space;
454
455 /* region->offset is subtracted because caller expects offset in the given region. */
456 return convert_address(to, from, addr) - region->offset;
457}
458
459static unsigned int convert_flash_to_host(const struct buffer *region, unsigned int addr)
460{
461 int idx;
462 const struct region *to, *from;
463
464 /*
465 * region->offset is added because caller provides offset in the given region. This is
466 * converted to an absolute address in the SPI flash space. This is done before the
467 * conversion as opposed to after in convert_host_to_flash() above because the address
468 * is actually an offset within the region. So, it needs to be converted into an
469 * absolute address in the SPI flash space before converting into an address in host
470 * space.
471 */
472 addr += region->offset;
473 idx = find_mmap_window(FLASH_SPACE_ADDR, addr);
474
475 if (idx == -1) {
476 ERROR("SPI flash address(%x) not in any mmap window!\n", addr);
477 return 0;
478 }
479
480 to = &mmap_window_table[idx].host_space;
481 from = &mmap_window_table[idx].flash_space;
482
483 return convert_address(to, from, addr);
484}
485
486static unsigned int convert_addr_space(const struct buffer *region, unsigned int addr)
Aaron Durbinab00d772016-05-04 16:07:15 -0500487{
488 assert(region);
489
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800490 assert(create_mmap_windows());
Aaron Durbinab00d772016-05-04 16:07:15 -0500491
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800492 if (IS_HOST_SPACE_ADDRESS(addr))
493 return convert_host_to_flash(region, addr);
494 else
495 return convert_flash_to_host(region, addr);
Aaron Durbinab00d772016-05-04 16:07:15 -0500496}
497
498/*
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800499 * This function takes offset value which represents the offset from one end of the region and
500 * converts it to offset from the other end of the region. offset is expected to be positive.
501 */
502static int convert_region_offset(unsigned int offset, uint32_t *region_offset)
Sol Boucher67d59982015-05-07 02:39:22 -0700503{
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800504 size_t size;
Sol Boucher67d59982015-05-07 02:39:22 -0700505
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800506 if (param.size) {
507 size = param.size;
508 } else {
509 assert(param.image_region);
510 size = param.image_region->size;
Patrick Georgi97311192015-12-07 23:14:07 +0100511 }
512
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800513 if (size < offset) {
514 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size, offset);
515 return 1;
516 }
517
518 *region_offset = size - offset;
519 return 0;
Sol Boucher67d59982015-05-07 02:39:22 -0700520}
521
Julius Werner6ddacd62021-12-03 14:46:53 -0800522static int do_cbfs_locate(uint32_t *cbfs_addr, size_t data_size)
Aaron Durbin4be16742015-09-15 17:00:23 -0500523{
Julius Werner6ddacd62021-12-03 14:46:53 -0800524 uint32_t metadata_size = 0;
525
Aaron Durbin4be16742015-09-15 17:00:23 -0500526 if (!param.name) {
527 ERROR("You need to specify -n/--name.\n");
528 return 1;
529 }
530
531 struct cbfs_image image;
532 if (cbfs_image_from_buffer(&image, param.image_region,
533 param.headeroffset))
534 return 1;
535
536 if (cbfs_get_entry(&image, param.name))
537 WARN("'%s' already in CBFS.\n", param.name);
538
Aaron Durbinfacf1492017-12-18 14:50:22 -0700539 if (!data_size) {
Julius Werner772714d2021-12-13 10:16:44 -0800540 ERROR("File '%s' is empty?\n", param.name);
541 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -0500542 }
543
Arthur Heymansc78521b32021-06-16 17:16:05 +0200544 /* Compute required page size */
Kyösti Mälkkic36469e2020-05-28 23:12:02 +0300545 if (param.force_pow2_pagesize) {
Arthur Heymansc78521b32021-06-16 17:16:05 +0200546 param.pagesize = 1;
547 while (param.pagesize < data_size)
548 param.pagesize <<= 1;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +0300549 DEBUG("Page size is %d (0x%x)\n", param.pagesize, param.pagesize);
550 }
551
Aaron Durbin4be16742015-09-15 17:00:23 -0500552 /* Include cbfs_file size along with space for with name. */
553 metadata_size += cbfs_calculate_file_header_size(param.name);
Werner Zehe9995f12016-01-14 13:22:37 +0100554 /* Adjust metadata_size if additional attributes were added */
555 if (param.autogen_attr) {
556 if (param.alignment)
557 metadata_size += sizeof(struct cbfs_file_attr_align);
558 if (param.baseaddress_assigned || param.stage_xip)
559 metadata_size += sizeof(struct cbfs_file_attr_position);
560 }
Julius Wernerc24db002020-11-18 18:00:31 -0800561 if (param.precompression || param.compression != CBFS_COMPRESS_NONE)
562 metadata_size += sizeof(struct cbfs_file_attr_compression);
Julius Werner6ddacd62021-12-03 14:46:53 -0800563 if (param.type == CBFS_TYPE_STAGE)
564 metadata_size += sizeof(struct cbfs_file_attr_stageheader);
Aaron Durbin4be16742015-09-15 17:00:23 -0500565
Werner Zehc7b2b7c2016-01-22 19:43:01 +0100566 /* Take care of the hash attribute if it is used */
567 if (param.hash != VB2_HASH_INVALID)
Julius Wernerd4775652020-03-13 16:43:34 -0700568 metadata_size += cbfs_file_attr_hash_size(param.hash);
Werner Zehc7b2b7c2016-01-22 19:43:01 +0100569
Aaron Durbinfacf1492017-12-18 14:50:22 -0700570 int32_t address = cbfs_locate_entry(&image, data_size, param.pagesize,
Aaron Durbin4be16742015-09-15 17:00:23 -0500571 param.alignment, metadata_size);
Aaron Durbin4be16742015-09-15 17:00:23 -0500572
Julius Wernerc24db002020-11-18 18:00:31 -0800573 if (address < 0) {
Julius Werner772714d2021-12-13 10:16:44 -0800574 ERROR("'%s'(%u + %zu) can't fit in CBFS for page-size %#x, align %#x.\n",
575 param.name, metadata_size, data_size, param.pagesize, param.alignment);
Aaron Durbin4be16742015-09-15 17:00:23 -0500576 return 1;
577 }
578
Aaron Durbin4be16742015-09-15 17:00:23 -0500579 *cbfs_addr = address;
580 return 0;
581}
582
Patrick Georgif8204972015-08-11 15:16:24 +0200583typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +0200584 struct cbfs_file *header);
Stefan Reinauer63217582012-10-29 16:52:36 -0700585
Sol Bouchere3260a02015-03-25 13:40:08 -0700586static int cbfs_add_integer_component(const char *name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200587 uint64_t u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800588 uint32_t offset,
589 uint32_t headeroffset) {
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200590 struct cbfs_image image;
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200591 struct cbfs_file *header = NULL;
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200592 struct buffer buffer;
593 int i, ret = 1;
594
595 if (!name) {
596 ERROR("You need to specify -n/--name.\n");
597 return 1;
598 }
599
600 if (buffer_create(&buffer, 8, name) != 0)
601 return 1;
602
603 for (i = 0; i < 8; i++)
604 buffer.data[i] = (u64val >> i*8) & 0xff;
605
Sol Bouchere3260a02015-03-25 13:40:08 -0700606 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset)) {
607 ERROR("Selected image region is not a CBFS.\n");
608 goto done;
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200609 }
610
611 if (cbfs_get_entry(&image, name)) {
612 ERROR("'%s' already in ROM image.\n", name);
613 goto done;
614 }
615
Julius Wernerd4775652020-03-13 16:43:34 -0700616 header = cbfs_create_file_header(CBFS_TYPE_RAW,
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200617 buffer.size, name);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700618
619 enum vb2_hash_algorithm algo = get_mh_cache()->cbfs_hash.algo;
620 if (algo != VB2_HASH_INVALID)
621 if (cbfs_add_file_hash(header, &buffer, algo)) {
622 ERROR("couldn't add hash for '%s'\n", name);
623 goto done;
624 }
625
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100626 if (cbfs_add_entry(&image, &buffer, offset, header, 0) != 0) {
Sol Boucher0e539312015-03-05 15:38:03 -0800627 ERROR("Failed to add %llu into ROM image as '%s'.\n",
628 (long long unsigned)u64val, name);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200629 goto done;
630 }
631
Julius Werner4bfbabd2020-05-06 17:27:02 -0700632 ret = maybe_update_metadata_hash(&image);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200633
634done:
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200635 free(header);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200636 buffer_delete(&buffer);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200637 return ret;
638}
639
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530640static int is_valid_topswap(void)
641{
642 switch (param.topswap_size) {
643 case (64 * KiB):
644 case (128 * KiB):
645 case (256 * KiB):
646 case (512 * KiB):
647 case (1 * MiB):
648 break;
649 default:
650 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
651 param.topswap_size);
652 return 0;
653 }
654 return 1;
655}
656
657static void fill_header_offset(void *location, uint32_t offset)
658{
Joel Kitching672d5ad2018-10-11 17:01:30 +0800659 // TODO: When we have a BE target, we'll need to store this as BE
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530660 write_le32(location, offset);
661}
662
663static int update_master_header_loc_topswap(struct cbfs_image *image,
664 void *h_loc, uint32_t header_offset)
665{
666 struct cbfs_file *entry;
667 void *ts_h_loc = h_loc;
668
669 entry = cbfs_get_entry(image, "bootblock");
670 if (entry == NULL) {
671 ERROR("Bootblock not in ROM image?!?\n");
672 return 1;
673 }
674
675 /*
676 * Check if the existing topswap boundary matches with
677 * the one provided.
678 */
Alex James02001a382021-12-19 16:41:59 -0600679 if (param.topswap_size != be32toh(entry->len)/2) {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530680 ERROR("Top swap boundary does not match\n");
681 return 1;
682 }
683
684 ts_h_loc -= param.topswap_size;
685 fill_header_offset(ts_h_loc, header_offset);
686
687 return 0;
688}
689
Patrick Georgi59e52b92015-09-10 15:28:27 +0200690static int cbfs_add_master_header(void)
691{
692 const char * const name = "cbfs master header";
693 struct cbfs_image image;
694 struct cbfs_file *header = NULL;
695 struct buffer buffer;
696 int ret = 1;
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600697 size_t offset;
698 size_t size;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530699 void *h_loc;
Patrick Georgi59e52b92015-09-10 15:28:27 +0200700
701 if (cbfs_image_from_buffer(&image, param.image_region,
702 param.headeroffset)) {
703 ERROR("Selected image region is not a CBFS.\n");
704 return 1;
705 }
706
707 if (cbfs_get_entry(&image, name)) {
708 ERROR("'%s' already in ROM image.\n", name);
709 return 1;
710 }
711
712 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
713 return 1;
714
715 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
Alex James02001a382021-12-19 16:41:59 -0600716 h->magic = htobe32(CBFS_HEADER_MAGIC);
717 h->version = htobe32(CBFS_HEADER_VERSION);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200718 /* The 4 bytes are left out for two reasons:
719 * 1. the cbfs master header pointer resides there
720 * 2. some cbfs implementations assume that an image that resides
721 * below 4GB has a bootblock and get confused when the end of the
722 * image is at 4GB == 0.
723 */
Alex James02001a382021-12-19 16:41:59 -0600724 h->bootblocksize = htobe32(4);
725 h->align = htobe32(CBFS_ALIGNMENT);
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600726 /* The offset and romsize fields within the master header are absolute
727 * values within the boot media. As such, romsize needs to relfect
728 * the end 'offset' for a CBFS. To achieve that the current buffer
729 * representing the CBFS region's size is added to the offset of
730 * the region within a larger image.
Patrick Georgi59e52b92015-09-10 15:28:27 +0200731 */
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600732 offset = buffer_get(param.image_region) -
733 buffer_get_original_backing(param.image_region);
734 size = buffer_size(param.image_region);
Alex James02001a382021-12-19 16:41:59 -0600735 h->romsize = htobe32(size + offset);
736 h->offset = htobe32(offset);
737 h->architecture = htobe32(CBFS_ARCHITECTURE_UNKNOWN);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200738
Julius Werner4bfbabd2020-05-06 17:27:02 -0700739 /* Never add a hash attribute to the master header. */
Julius Wernerd4775652020-03-13 16:43:34 -0700740 header = cbfs_create_file_header(CBFS_TYPE_CBFSHEADER,
Patrick Georgi59e52b92015-09-10 15:28:27 +0200741 buffer_size(&buffer), name);
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100742 if (cbfs_add_entry(&image, &buffer, 0, header, 0) != 0) {
Patrick Georgi59e52b92015-09-10 15:28:27 +0200743 ERROR("Failed to add cbfs master header into ROM image.\n");
744 goto done;
745 }
746
747 struct cbfs_file *entry;
748 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
749 ERROR("'%s' not in ROM image?!?\n", name);
750 goto done;
751 }
752
753 uint32_t header_offset = CBFS_SUBHEADER(entry) -
754 buffer_get(&image.buffer);
755 header_offset = -(buffer_size(&image.buffer) - header_offset);
756
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530757 h_loc = (void *)(buffer_get(&image.buffer) +
758 buffer_size(&image.buffer) - 4);
759 fill_header_offset(h_loc, header_offset);
760 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800761 * If top swap present, update the header
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530762 * location in secondary bootblock
763 */
764 if (param.topswap_size) {
765 if (update_master_header_loc_topswap(&image, h_loc,
766 header_offset))
767 return 1;
768 }
Patrick Georgi59e52b92015-09-10 15:28:27 +0200769
Julius Werner4bfbabd2020-05-06 17:27:02 -0700770 ret = maybe_update_metadata_hash(&image);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200771
772done:
773 free(header);
774 buffer_delete(&buffer);
775 return ret;
776}
777
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530778static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
779{
780 size_t bb_buf_size = buffer_size(buffer);
781
782 if (bb_buf_size > param.topswap_size) {
783 ERROR("Bootblock bigger than the topswap boundary\n");
784 ERROR("size = %zd, ts = %d\n", bb_buf_size,
785 param.topswap_size);
786 return 1;
787 }
788
789 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800790 * Allocate topswap_size*2 bytes for bootblock to
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530791 * accommodate the second bootblock.
792 */
793 struct buffer new_bootblock, bb1, bb2;
794 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
795 buffer->name))
796 return 1;
797
798 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
799 bb_buf_size);
800 buffer_splice(&bb2, &new_bootblock,
801 buffer_size(&new_bootblock) - bb_buf_size,
802 bb_buf_size);
803
Joel Kitching672d5ad2018-10-11 17:01:30 +0800804 /* Copy to first bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530805 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
Joel Kitching672d5ad2018-10-11 17:01:30 +0800806 /* Copy to second bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530807 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
808
809 buffer_delete(buffer);
810 buffer_clone(buffer, &new_bootblock);
811
Joel Kitching672d5ad2018-10-11 17:01:30 +0800812 /* Update the location (offset) of bootblock in the region */
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800813 return convert_region_offset(buffer_size(buffer), offset);
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530814}
815
Sol Bouchere3260a02015-03-25 13:40:08 -0700816static int cbfs_add_component(const char *filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800817 const char *name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800818 uint32_t headeroffset,
Stefan Reinauer2dd161f2015-03-04 00:55:03 +0100819 convert_buffer_t convert)
820{
Julius Werner6ddacd62021-12-03 14:46:53 -0800821 /*
822 * The steps used to determine the final placement offset in CBFS, in order:
823 *
Julius Werner20ad3652021-12-09 13:20:59 -0800824 * 1. If --base-address was passed, that value is used. If it was passed in the host
825 * address space, convert it to flash address space. (After that, |*offset| is always
826 * in the flash address space.)
Julius Werner6ddacd62021-12-03 14:46:53 -0800827 *
828 * 2. The convert() function may write a location back to |offset|, usually by calling
829 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
830 * the CBFS file in its final form (after any compression and conversion).
831 *
832 * 3. If --align was passed and the offset is still undecided at this point,
833 * do_cbfs_locate() is called to find an appropriately aligned location.
834 *
835 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
836 * location that fits.
837 */
Julius Wernerc24db002020-11-18 18:00:31 -0800838 uint32_t offset = param.baseaddress_assigned ? param.baseaddress : 0;
Julius Werner6ddacd62021-12-03 14:46:53 -0800839 size_t len_align = 0;
Julius Wernerc24db002020-11-18 18:00:31 -0800840
841 if (param.alignment && param.baseaddress_assigned) {
842 ERROR("Cannot specify both alignment and base address\n");
843 return 1;
844 }
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100845
Julius Werner6ddacd62021-12-03 14:46:53 -0800846 if (param.stage_xip && param.compression != CBFS_COMPRESS_NONE) {
847 ERROR("Cannot specify compression for XIP.\n");
848 return 1;
849 }
850
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800851 if (!filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800852 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700853 return 1;
854 }
855
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800856 if (!name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800857 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700858 return 1;
859 }
860
Julius Werner6ddacd62021-12-03 14:46:53 -0800861 if (param.type == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800862 ERROR("You need to specify a valid -t/--type.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700863 return 1;
864 }
865
Sol Bouchere3260a02015-03-25 13:40:08 -0700866 struct cbfs_image image;
Sol Boucher67a0a862015-03-18 12:36:27 -0700867 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800868 return 1;
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800869
Patrick Georgi4e54bf92015-08-11 14:35:39 +0200870 if (cbfs_get_entry(&image, name)) {
871 ERROR("'%s' already in ROM image.\n", name);
872 return 1;
873 }
874
Sol Bouchere3260a02015-03-25 13:40:08 -0700875 struct buffer buffer;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800876 if (buffer_from_file(&buffer, filename) != 0) {
877 ERROR("Could not load file '%s'.\n", filename);
Stefan Reinauer63217582012-10-29 16:52:36 -0700878 return 1;
879 }
880
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200881 struct cbfs_file *header =
Julius Werner6ddacd62021-12-03 14:46:53 -0800882 cbfs_create_file_header(param.type, buffer.size, name);
Patrick Georgi4110abc2015-08-11 15:10:33 +0200883
Julius Werner4bfbabd2020-05-06 17:27:02 -0700884 /* Bootblock and CBFS header should never have file hashes. When adding
885 the bootblock it is important that we *don't* look up the metadata
886 hash yet (before it is added) or we'll cache an outdated result. */
Julius Werneraf20fd72022-05-27 18:33:15 -0700887 if (!verification_exclude(param.type)) {
Julius Werner4bfbabd2020-05-06 17:27:02 -0700888 enum vb2_hash_algorithm mh_algo = get_mh_cache()->cbfs_hash.algo;
889 if (mh_algo != VB2_HASH_INVALID && param.hash != mh_algo) {
890 if (param.hash == VB2_HASH_INVALID) {
891 param.hash = mh_algo;
892 } else {
893 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
894 vb2_get_hash_algorithm_name(param.hash),
895 vb2_get_hash_algorithm_name(mh_algo));
896 goto error;
897 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200898 }
Julius Wernerc24db002020-11-18 18:00:31 -0800899 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200900
Julius Wernerc24db002020-11-18 18:00:31 -0800901 /*
902 * Check if Intel CPU topswap is specified this will require a
903 * second bootblock to be added.
904 */
Julius Werner6ddacd62021-12-03 14:46:53 -0800905 if (param.type == CBFS_TYPE_BOOTBLOCK && param.topswap_size)
Julius Wernerc24db002020-11-18 18:00:31 -0800906 if (add_topswap_bootblock(&buffer, &offset))
907 goto error;
908
Julius Werner20ad3652021-12-09 13:20:59 -0800909 /* With --base-address we allow host space addresses -- if so, convert it here. */
910 if (IS_HOST_SPACE_ADDRESS(offset))
911 offset = convert_addr_space(param.image_region, offset);
912
Julius Wernerc24db002020-11-18 18:00:31 -0800913 if (convert && convert(&buffer, &offset, header) != 0) {
914 ERROR("Failed to parse file '%s'.\n", filename);
915 goto error;
916 }
917
Julius Werner6ddacd62021-12-03 14:46:53 -0800918 /* This needs to run after convert() to take compression into account. */
919 if (!offset && param.alignment)
Julius Werner772714d2021-12-13 10:16:44 -0800920 if (do_cbfs_locate(&offset, buffer_size(&buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -0800921 goto error;
922
923 /* This needs to run after convert() to hash the actual final file data. */
Julius Wernerc24db002020-11-18 18:00:31 -0800924 if (param.hash != VB2_HASH_INVALID &&
925 cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
926 ERROR("couldn't add hash for '%s'\n", name);
927 goto error;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700928 }
929
Werner Zehe9995f12016-01-14 13:22:37 +0100930 if (param.autogen_attr) {
931 /* Add position attribute if assigned */
932 if (param.baseaddress_assigned || param.stage_xip) {
933 struct cbfs_file_attr_position *attrs =
934 (struct cbfs_file_attr_position *)
935 cbfs_add_file_attr(header,
936 CBFS_FILE_ATTR_TAG_POSITION,
937 sizeof(struct cbfs_file_attr_position));
938 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800939 goto error;
Alex James02001a382021-12-19 16:41:59 -0600940 attrs->position = htobe32(offset);
Werner Zehe9995f12016-01-14 13:22:37 +0100941 }
942 /* Add alignment attribute if used */
943 if (param.alignment) {
944 struct cbfs_file_attr_align *attrs =
945 (struct cbfs_file_attr_align *)
946 cbfs_add_file_attr(header,
947 CBFS_FILE_ATTR_TAG_ALIGNMENT,
948 sizeof(struct cbfs_file_attr_align));
949 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800950 goto error;
Alex James02001a382021-12-19 16:41:59 -0600951 attrs->alignment = htobe32(param.alignment);
Werner Zehe9995f12016-01-14 13:22:37 +0100952 }
953 }
954
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100955 if (param.ibb) {
956 /* Mark as Initial Boot Block */
957 struct cbfs_file_attribute *attrs = cbfs_add_file_attr(header,
958 CBFS_FILE_ATTR_TAG_IBB,
959 sizeof(struct cbfs_file_attribute));
960 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800961 goto error;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100962 /* For Intel TXT minimum align is 16 */
963 len_align = 16;
964 }
965
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700966 if (param.padding) {
967 const uint32_t hs = sizeof(struct cbfs_file_attribute);
Julius Werner5779ca72020-11-20 16:12:40 -0800968 uint32_t size = ALIGN_UP(MAX(hs, param.padding),
969 CBFS_ATTRIBUTE_ALIGN);
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700970 INFO("Padding %d bytes\n", size);
971 struct cbfs_file_attribute *attr =
972 (struct cbfs_file_attribute *)cbfs_add_file_attr(
973 header, CBFS_FILE_ATTR_TAG_PADDING,
974 size);
975 if (attr == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800976 goto error;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700977 }
978
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100979 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800980 ERROR("Failed to add '%s' into ROM image.\n", filename);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700981 goto error;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800982 }
983
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200984 free(header);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800985 buffer_delete(&buffer);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700986
987 return maybe_update_metadata_hash(&image) || maybe_update_fmap_hash();
988
989error:
990 free(header);
991 buffer_delete(&buffer);
992 return 1;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +0000993}
994
Daisuke Nojiri8984a632015-07-09 15:07:45 -0700995static int cbfstool_convert_raw(struct buffer *buffer,
996 unused uint32_t *offset, struct cbfs_file *header)
997{
998 char *compressed;
Patrick Georgiecaa5702017-01-11 18:38:11 +0100999 int decompressed_size, compressed_size;
1000 comp_func_ptr compress;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001001
Patrick Georgiecaa5702017-01-11 18:38:11 +01001002 decompressed_size = buffer->size;
1003 if (param.precompression) {
Nico Huber607796a2017-01-17 13:01:25 +01001004 param.compression = read_le32(buffer->data);
1005 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
Patrick Georgiecaa5702017-01-11 18:38:11 +01001006 compressed_size = buffer->size - 8;
1007 compressed = malloc(compressed_size);
1008 if (!compressed)
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001009 return -1;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001010 memcpy(compressed, buffer->data + 8, compressed_size);
1011 } else {
Julius Werner105cdf52020-10-23 19:19:32 -07001012 if (param.compression == CBFS_COMPRESS_NONE)
1013 goto out;
1014
Patrick Georgiecaa5702017-01-11 18:38:11 +01001015 compress = compression_function(param.compression);
1016 if (!compress)
1017 return -1;
1018 compressed = calloc(buffer->size, 1);
1019 if (!compressed)
1020 return -1;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001021
Patrick Georgiecaa5702017-01-11 18:38:11 +01001022 if (compress(buffer->data, buffer->size,
1023 compressed, &compressed_size)) {
1024 WARN("Compression failed - disabled\n");
1025 free(compressed);
Julius Werner105cdf52020-10-23 19:19:32 -07001026 goto out;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001027 }
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001028 }
Patrick Georgiecaa5702017-01-11 18:38:11 +01001029
1030 struct cbfs_file_attr_compression *attrs =
1031 (struct cbfs_file_attr_compression *)
1032 cbfs_add_file_attr(header,
1033 CBFS_FILE_ATTR_TAG_COMPRESSION,
1034 sizeof(struct cbfs_file_attr_compression));
1035 if (attrs == NULL) {
1036 free(compressed);
1037 return -1;
1038 }
Alex James02001a382021-12-19 16:41:59 -06001039 attrs->compression = htobe32(param.compression);
1040 attrs->decompressed_size = htobe32(decompressed_size);
Patrick Georgiecaa5702017-01-11 18:38:11 +01001041
1042 free(buffer->data);
1043 buffer->data = compressed;
1044 buffer->size = compressed_size;
1045
Julius Werner105cdf52020-10-23 19:19:32 -07001046out:
Alex James02001a382021-12-19 16:41:59 -06001047 header->len = htobe32(buffer->size);
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001048 return 0;
1049}
1050
Aaron Durbin8a414a02015-10-01 17:02:45 -05001051static int cbfstool_convert_fsp(struct buffer *buffer,
1052 uint32_t *offset, struct cbfs_file *header)
1053{
1054 uint32_t address;
1055 struct buffer fsp;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001056
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001057 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001058 * There are 4 different cases here:
1059 *
1060 * 1. --xip and --base-address: we need to place the binary at the given base address
Julius Werner20ad3652021-12-09 13:20:59 -08001061 * in the CBFS image and relocate it to that address. *offset was already filled in,
1062 * but we need to convert it to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001063 *
1064 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1065 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
Julius Werner20ad3652021-12-09 13:20:59 -08001066 * This also needs to be converted to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001067 *
1068 * 3. No --xip but a --base-address: special case where --base-address does not have its
1069 * normal meaning, instead we use it as the relocation target address. We explicitly
1070 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1071 *
1072 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1073 * not be relocated. Just chain directly to convert_raw() for compression.
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001074 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001075
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001076 if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001077 if (!param.baseaddress_assigned) {
1078 param.alignment = 4*1024;
Julius Werner772714d2021-12-13 10:16:44 -08001079 if (do_cbfs_locate(offset, buffer_size(buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -08001080 return -1;
1081 }
Julius Werner20ad3652021-12-09 13:20:59 -08001082 assert(!IS_HOST_SPACE_ADDRESS(*offset));
1083 address = convert_addr_space(param.image_region, *offset);
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001084 } else {
Aaron Durbin493ec922016-05-16 15:18:45 -05001085 if (param.baseaddress_assigned == 0) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001086 INFO("Honoring pre-linked FSP module, no relocation.\n");
1087 return cbfstool_convert_raw(buffer, offset, header);
Aaron Durbin493ec922016-05-16 15:18:45 -05001088 } else {
1089 address = param.baseaddress;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001090 *offset = 0;
Furquan Shaikhc3bb6922020-06-09 21:20:27 -07001091 }
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001092 }
Aaron Durbin8a414a02015-10-01 17:02:45 -05001093
1094 /* Create a copy of the buffer to attempt relocation. */
1095 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
1096 return -1;
1097
1098 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
1099
1100 /* Replace the buffer contents w/ the relocated ones on success. */
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001101 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
1102 > 0) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001103 buffer_delete(buffer);
1104 buffer_clone(buffer, &fsp);
1105 } else {
1106 buffer_delete(&fsp);
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001107 WARN("Invalid FSP variant.\n");
Aaron Durbin8a414a02015-10-01 17:02:45 -05001108 }
1109
1110 /* Let the raw path handle all the cbfs metadata logic. */
1111 return cbfstool_convert_raw(buffer, offset, header);
1112}
1113
Patrick Georgif8204972015-08-11 15:16:24 +02001114static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001115 struct cbfs_file *header)
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001116{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001117 struct buffer output;
Julius Wernerc24db002020-11-18 18:00:31 -08001118 size_t data_size;
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001119 int ret;
Aaron Durbin4be16742015-09-15 17:00:23 -05001120
Julius Wernerc24db002020-11-18 18:00:31 -08001121 if (elf_program_file_size(buffer, &data_size) < 0) {
1122 ERROR("Could not obtain ELF size\n");
1123 return 1;
1124 }
1125
1126 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001127 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1128 * here. That is okay because XIP stages may not be compressed, so their size cannot
1129 * change anymore at a later point.
Julius Wernerc24db002020-11-18 18:00:31 -08001130 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001131 if (param.stage_xip &&
1132 do_cbfs_locate(offset, data_size)) {
Julius Wernerc24db002020-11-18 18:00:31 -08001133 ERROR("Could not find location for stage.\n");
1134 return 1;
1135 }
1136
Julius Werner81dc20e2020-10-15 17:37:57 -07001137 struct cbfs_file_attr_stageheader *stageheader = (void *)
1138 cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
1139 sizeof(struct cbfs_file_attr_stageheader));
1140 if (!stageheader)
1141 return -1;
1142
Aaron Durbin4be16742015-09-15 17:00:23 -05001143 if (param.stage_xip) {
Julius Werner20ad3652021-12-09 13:20:59 -08001144 uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
1145 assert(IS_HOST_SPACE_ADDRESS(host_space_address));
1146 ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
1147 param.ignore_section, stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001148 } else {
Julius Werner81dc20e2020-10-15 17:37:57 -07001149 ret = parse_elf_to_stage(buffer, &output, param.ignore_section,
1150 stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001151 }
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001152 if (ret != 0)
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001153 return -1;
Julius Werner81dc20e2020-10-15 17:37:57 -07001154
1155 /* Store a hash of original uncompressed stage to compare later. */
1156 size_t decmp_size = buffer_size(&output);
1157 uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
1158
1159 /* Chain to base conversion routine to handle compression. */
1160 ret = cbfstool_convert_raw(&output, offset, header);
1161 if (ret != 0)
1162 goto fail;
1163
1164 /* Special care must be taken for LZ4-compressed stages that the BSS is
1165 large enough to provide scratch space for in-place decompression. */
1166 if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
Alex James02001a382021-12-19 16:41:59 -06001167 size_t memlen = be32toh(stageheader->memlen);
Julius Werner81dc20e2020-10-15 17:37:57 -07001168 size_t compressed_size = buffer_size(&output);
1169 uint8_t *compare_buffer = malloc(memlen);
1170 uint8_t *start = compare_buffer + memlen - compressed_size;
1171 if (!compare_buffer) {
1172 ERROR("Out of memory\n");
1173 goto fail;
1174 }
1175 memcpy(start, buffer_get(&output), compressed_size);
1176 ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
1177 if (ret == 0) {
1178 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1179 free(compare_buffer);
1180 goto fail;
1181 } else if (ret != (int)decmp_size ||
1182 decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
1183 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1184 free(compare_buffer);
1185 goto fail;
1186 }
1187 free(compare_buffer);
1188 }
1189
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001190 buffer_delete(buffer);
Julius Werner81dc20e2020-10-15 17:37:57 -07001191 buffer_clone(buffer, &output);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001192 return 0;
Julius Werner81dc20e2020-10-15 17:37:57 -07001193
1194fail:
1195 buffer_delete(&output);
1196 return -1;
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001197}
1198
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001199static int cbfstool_convert_mkpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001200 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001201{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001202 struct buffer output;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001203 int ret;
Joel Kitching672d5ad2018-10-11 17:01:30 +08001204 /* Per default, try and see if payload is an ELF binary */
Sol Boucher65336712015-05-07 21:00:05 -07001205 ret = parse_elf_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001206
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001207 /* If it's not an ELF, see if it's a FIT */
1208 if (ret != 0) {
1209 ret = parse_fit_to_payload(buffer, &output, param.compression);
1210 if (ret == 0)
Julius Werner00572622022-05-26 20:29:42 -07001211 header->type = htobe32(CBFS_TYPE_FIT_PAYLOAD);
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001212 }
1213
1214 /* If it's not an FIT, see if it's a UEFI FV */
Stefan Reinauer543a6822013-02-04 15:39:13 -08001215 if (ret != 0)
Sol Boucher65336712015-05-07 21:00:05 -07001216 ret = parse_fv_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001217
Patrick Georgide36d332013-08-27 20:22:21 +02001218 /* If it's neither ELF nor UEFI Fv, try bzImage */
1219 if (ret != 0)
1220 ret = parse_bzImage_to_payload(buffer, &output,
Sol Boucher65336712015-05-07 21:00:05 -07001221 param.initrd, param.cmdline, param.compression);
Patrick Georgide36d332013-08-27 20:22:21 +02001222
Stefan Reinauer543a6822013-02-04 15:39:13 -08001223 /* Not a supported payload type */
1224 if (ret != 0) {
1225 ERROR("Not a supported payload type (ELF / FV).\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001226 buffer_delete(buffer);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001227 return -1;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001228 }
1229
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001230 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001231 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001232 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001233 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001234 return 0;
1235}
1236
1237static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001238 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001239{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001240 struct buffer output;
1241 if (parse_flat_binary_to_payload(buffer, &output,
1242 param.loadaddress,
1243 param.entrypoint,
Sol Boucher65336712015-05-07 21:00:05 -07001244 param.compression) != 0) {
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001245 return -1;
1246 }
1247 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001248 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001249 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001250 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001251 return 0;
1252}
1253
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001254static int cbfs_add(void)
1255{
Julius Wernerc24db002020-11-18 18:00:31 -08001256 convert_buffer_t convert = cbfstool_convert_raw;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001257
Julius Wernerd4775652020-03-13 16:43:34 -07001258 if (param.type == CBFS_TYPE_FSP) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001259 convert = cbfstool_convert_fsp;
Julius Werner6ddacd62021-12-03 14:46:53 -08001260 } else if (param.type == CBFS_TYPE_STAGE) {
1261 ERROR("stages can only be added with cbfstool add-stage\n");
1262 return 1;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001263 } else if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001264 ERROR("cbfstool add supports xip only for FSP component type\n");
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001265 return 1;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001266 }
1267
Sol Bouchere3260a02015-03-25 13:40:08 -07001268 return cbfs_add_component(param.filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001269 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001270 param.headeroffset,
Aaron Durbin8a414a02015-10-01 17:02:45 -05001271 convert);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001272}
1273
Stefan Reinauer63217582012-10-29 16:52:36 -07001274static int cbfs_add_stage(void)
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001275{
Julius Werner6ddacd62021-12-03 14:46:53 -08001276 if (param.stage_xip && param.baseaddress_assigned) {
1277 ERROR("Cannot specify base address for XIP.\n");
1278 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -05001279 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001280 param.type = CBFS_TYPE_STAGE;
Aaron Durbin4be16742015-09-15 17:00:23 -05001281
Sol Bouchere3260a02015-03-25 13:40:08 -07001282 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001283 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001284 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001285 cbfstool_convert_mkstage);
1286}
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001287
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001288static int cbfs_add_payload(void)
1289{
Julius Werner6ddacd62021-12-03 14:46:53 -08001290 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001291 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001292 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001293 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001294 cbfstool_convert_mkpayload);
Stefan Reinauer63217582012-10-29 16:52:36 -07001295}
1296
1297static int cbfs_add_flat_binary(void)
1298{
Hung-Te Lind1739622013-01-28 14:23:49 +08001299 if (param.loadaddress == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001300 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001301 "-l/--load-address.\n");
1302 return 1;
1303 }
Hung-Te Lind1739622013-01-28 14:23:49 +08001304 if (param.entrypoint == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001305 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001306 "-e/--entry-point.\n");
1307 return 1;
1308 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001309 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001310 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001311 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001312 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001313 cbfstool_convert_mkflatpayload);
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001314}
1315
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001316static int cbfs_add_integer(void)
1317{
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01001318 if (!param.u64val_assigned) {
1319 ERROR("You need to specify a value to write.\n");
1320 return 1;
1321 }
Sol Bouchere3260a02015-03-25 13:40:08 -07001322 return cbfs_add_integer_component(param.name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001323 param.u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001324 param.baseaddress,
1325 param.headeroffset);
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001326}
1327
Stefan Reinauer63217582012-10-29 16:52:36 -07001328static int cbfs_remove(void)
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001329{
Hung-Te Lind1739622013-01-28 14:23:49 +08001330 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001331 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001332 return 1;
1333 }
1334
Sol Bouchere3260a02015-03-25 13:40:08 -07001335 struct cbfs_image image;
1336 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001337 param.headeroffset))
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001338 return 1;
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001339
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001340 if (cbfs_remove_entry(&image, param.name) != 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001341 ERROR("Removing file '%s' failed.\n",
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001342 param.name);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001343 return 1;
1344 }
1345
Julius Werner4bfbabd2020-05-06 17:27:02 -07001346 return maybe_update_metadata_hash(&image);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001347}
1348
Stefan Reinauer63217582012-10-29 16:52:36 -07001349static int cbfs_create(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001350{
Sol Boucher67a0a862015-03-18 12:36:27 -07001351 struct cbfs_image image;
1352 memset(&image, 0, sizeof(image));
1353 buffer_clone(&image.buffer, param.image_region);
1354
1355 if (param.fmap) {
1356 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
1357 param.baseaddress_assigned ||
1358 param.headeroffset_assigned ||
1359 param.cbfsoffset_assigned ||
Sol Boucher67a0a862015-03-18 12:36:27 -07001360 param.bootblock) {
Patrick Georgi45acb342015-07-14 22:18:23 +02001361 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
Sol Boucher67a0a862015-03-18 12:36:27 -07001362 return 1;
1363 }
1364
1365 return cbfs_image_create(&image, image.buffer.size);
1366 }
1367
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001368 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
Stefan Reinauer60a4a732013-03-28 16:46:07 -07001369 ERROR("You need to specify -m/--machine arch.\n");
David Hendricks90ca3b62012-11-16 14:48:22 -08001370 return 1;
1371 }
1372
Sol Bouchere3260a02015-03-25 13:40:08 -07001373 struct buffer bootblock;
Julius Wernerefcee762014-11-10 13:14:24 -08001374 if (!param.bootblock) {
1375 DEBUG("-B not given, creating image without bootblock.\n");
Patrick Georgi6b2d2db2016-12-14 16:11:58 +01001376 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
1377 return 1;
Julius Wernerefcee762014-11-10 13:14:24 -08001378 } else if (buffer_from_file(&bootblock, param.bootblock)) {
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001379 return 1;
1380 }
1381
Julius Wernerefcee762014-11-10 13:14:24 -08001382 if (!param.alignment)
Patrick Georgi45acb342015-07-14 22:18:23 +02001383 param.alignment = CBFS_ALIGNMENT;
Julius Wernerefcee762014-11-10 13:14:24 -08001384
1385 // Set default offsets. x86, as usual, needs to be a special snowflake.
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001386 if (!param.baseaddress_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001387 if (param.arch == CBFS_ARCHITECTURE_X86) {
1388 // Make sure there's at least enough room for rel_offset
Sol Boucher67a0a862015-03-18 12:36:27 -07001389 param.baseaddress = param.size -
1390 MAX(bootblock.size, sizeof(int32_t));
Julius Wernerefcee762014-11-10 13:14:24 -08001391 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1392 param.baseaddress);
1393 } else {
1394 param.baseaddress = 0;
1395 DEBUG("bootblock starts at address 0x0.\n");
1396 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001397 }
1398 if (!param.headeroffset_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001399 if (param.arch == CBFS_ARCHITECTURE_X86) {
1400 param.headeroffset = param.baseaddress -
1401 sizeof(struct cbfs_header);
1402 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1403 param.headeroffset);
1404 } else {
1405 param.headeroffset = align_up(param.baseaddress +
1406 bootblock.size, sizeof(uint32_t));
1407 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1408 param.headeroffset);
1409 }
1410 }
1411 if (!param.cbfsoffset_assigned) {
1412 if (param.arch == CBFS_ARCHITECTURE_X86) {
1413 param.cbfsoffset = 0;
1414 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1415 } else {
1416 param.cbfsoffset = align_up(param.headeroffset +
1417 sizeof(struct cbfs_header),
Patrick Georgi45acb342015-07-14 22:18:23 +02001418 CBFS_ALIGNMENT);
Julius Wernerefcee762014-11-10 13:14:24 -08001419 DEBUG("CBFS entries start beind master header (%#x).\n",
1420 param.cbfsoffset);
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001421 }
1422 }
1423
Sol Boucher67a0a862015-03-18 12:36:27 -07001424 int ret = cbfs_legacy_image_create(&image,
1425 param.arch,
Patrick Georgi45acb342015-07-14 22:18:23 +02001426 CBFS_ALIGNMENT,
Sol Boucher67a0a862015-03-18 12:36:27 -07001427 &bootblock,
1428 param.baseaddress,
1429 param.headeroffset,
1430 param.cbfsoffset);
Sol Bouchere3260a02015-03-25 13:40:08 -07001431 buffer_delete(&bootblock);
Sol Boucher67a0a862015-03-18 12:36:27 -07001432 return ret;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001433}
1434
Sol Boucher67a0a862015-03-18 12:36:27 -07001435static int cbfs_layout(void)
1436{
1437 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1438 if (!fmap) {
1439 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1440 return 1;
1441 }
1442
1443 printf("This image contains the following sections that can be %s with this tool:\n",
1444 param.show_immutable ? "accessed" : "manipulated");
1445 puts("");
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001446 for (unsigned i = 0; i < fmap->nareas; ++i) {
1447 const struct fmap_area *current = fmap->areas + i;
Sol Boucher67a0a862015-03-18 12:36:27 -07001448
1449 bool readonly = partitioned_file_fmap_count(param.image_file,
1450 partitioned_file_fmap_select_children_of, current) ||
1451 region_is_flashmap((const char *)current->name);
1452 if (!param.show_immutable && readonly)
1453 continue;
1454
1455 printf("'%s'", current->name);
1456
1457 // Detect consecutive sections that describe the same region and
1458 // show them as aliases. This cannot find equivalent entries
1459 // that aren't adjacent; however, fmaptool doesn't generate
1460 // FMAPs with such sections, so this convenience feature works
1461 // for all but the strangest manually created FMAP binaries.
1462 // TODO: This could be done by parsing the FMAP into some kind
1463 // of tree that had duplicate lists in addition to child lists,
1464 // which would allow covering that weird, unlikely case as well.
1465 unsigned lookahead;
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001466 for (lookahead = 1; i + lookahead < fmap->nareas;
Sol Boucher67a0a862015-03-18 12:36:27 -07001467 ++lookahead) {
1468 const struct fmap_area *consecutive =
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001469 fmap->areas + i + lookahead;
Sol Boucher67a0a862015-03-18 12:36:27 -07001470 if (consecutive->offset != current->offset ||
1471 consecutive->size != current->size)
1472 break;
1473 printf(", '%s'", consecutive->name);
1474 }
1475 if (lookahead > 1)
1476 fputs(" are aliases for the same region", stdout);
1477
1478 const char *qualifier = "";
1479 if (readonly)
1480 qualifier = "read-only, ";
1481 else if (region_is_modern_cbfs((const char *)current->name))
1482 qualifier = "CBFS, ";
Patrick Georgi848e30d2019-06-18 22:02:13 +02001483 else if (current->flags & FMAP_AREA_PRESERVE)
1484 qualifier = "preserve, ";
Werner Zeh7850b582018-04-26 09:26:38 +02001485 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1486 current->offset);
Sol Boucher67a0a862015-03-18 12:36:27 -07001487
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001488 i += lookahead - 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001489 }
1490 puts("");
1491
1492 if (param.show_immutable) {
1493 puts("It is at least possible to perform the read action on every section listed above.");
1494 } else {
1495 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1496 puts("To see the image's read-only sections as well, rerun with the -w option.");
1497 }
1498
1499 return 0;
1500}
1501
Julius Werner423cd062022-05-24 18:18:46 -07001502static enum cb_err verify_walker(__unused cbfs_dev_t dev, size_t offset,
1503 const union cbfs_mdata *mdata, size_t already_read, void *arg)
1504{
1505 uint32_t type = be32toh(mdata->h.type);
1506 uint32_t data_offset = be32toh(mdata->h.offset);
1507 if (verification_exclude(type))
1508 return CB_CBFS_NOT_FOUND;
1509 assert(already_read == data_offset);
1510 const struct vb2_hash *hash = cbfs_file_hash(mdata);
1511 if (!hash)
1512 return CB_ERR;
1513 void *file_data = arg + offset + data_offset;
1514 if (vb2_hash_verify(file_data, be32toh(mdata->h.len), hash) != VB2_SUCCESS)
1515 return CB_CBFS_HASH_MISMATCH;
1516 return CB_CBFS_NOT_FOUND;
1517}
1518
Stefan Reinauer63217582012-10-29 16:52:36 -07001519static int cbfs_print(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001520{
Hung-Te Lin3bb035b2013-01-29 02:15:49 +08001521 struct cbfs_image image;
Sol Bouchere3260a02015-03-25 13:40:08 -07001522 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001523 param.headeroffset))
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001524 return 1;
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001525 if (param.machine_parseable) {
1526 if (verbose)
1527 printf("[FMAP REGION]\t%s\n", param.region_name);
1528 cbfs_print_parseable_directory(&image);
1529 } else {
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001530 printf("FMAP REGION: %s\n", param.region_name);
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001531 cbfs_print_directory(&image);
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001532 }
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001533
Julius Werner4bfbabd2020-05-06 17:27:02 -07001534 if (verbose) {
Julius Werner423cd062022-05-24 18:18:46 -07001535 const char *verification_state = "fully valid";
Julius Werner4bfbabd2020-05-06 17:27:02 -07001536 struct mh_cache *mhc = get_mh_cache();
1537 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1538 return 0;
1539
1540 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
Julius Werner423cd062022-05-24 18:18:46 -07001541 enum cb_err err = cbfs_walk(&image, verify_walker, buffer_get(&image.buffer),
1542 &real_hash, CBFS_WALK_WRITEBACK_HASH);
1543 if (err == CB_CBFS_HASH_MISMATCH)
1544 verification_state = "invalid file hashes";
1545 else if (err != CB_CBFS_NOT_FOUND)
1546 verification_state = "missing file hashes";
Julius Werner4bfbabd2020-05-06 17:27:02 -07001547 char *hash_str = bintohex(real_hash.raw,
1548 vb2_digest_size(real_hash.algo));
1549 printf("[METADATA HASH]\t%s:%s",
1550 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1551 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1552 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
Julius Werner423cd062022-05-24 18:18:46 -07001553 vb2_digest_size(real_hash.algo))) {
Julius Werner4bfbabd2020-05-06 17:27:02 -07001554 printf(":valid");
Julius Werner423cd062022-05-24 18:18:46 -07001555 } else {
Julius Werner4bfbabd2020-05-06 17:27:02 -07001556 printf(":invalid");
Julius Werner423cd062022-05-24 18:18:46 -07001557 verification_state = "invalid metadata hash";
1558 }
Julius Werner4bfbabd2020-05-06 17:27:02 -07001559 }
1560 printf("\n");
Julius Werner423cd062022-05-24 18:18:46 -07001561 printf("[CBFS VERIFICATION (%s)]\t%s\n", param.region_name, verification_state);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001562 free(hash_str);
1563 }
1564
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001565 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001566}
1567
Stefan Reinauer63217582012-10-29 16:52:36 -07001568static int cbfs_extract(void)
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001569{
Hung-Te Lind1739622013-01-28 14:23:49 +08001570 if (!param.filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001571 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001572 return 1;
1573 }
1574
Hung-Te Lind1739622013-01-28 14:23:49 +08001575 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001576 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001577 return 1;
1578 }
1579
Sol Bouchere3260a02015-03-25 13:40:08 -07001580 struct cbfs_image image;
1581 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001582 param.headeroffset))
1583 return 1;
1584
Aaron Durbin17625022015-10-27 13:17:52 -05001585 return cbfs_export_entry(&image, param.name, param.filename,
Joel Kitching21fdd892018-08-09 17:49:52 +08001586 param.arch, !param.unprocessed);
Sol Boucher67a0a862015-03-18 12:36:27 -07001587}
1588
1589static int cbfs_write(void)
1590{
1591 if (!param.filename) {
1592 ERROR("You need to specify a valid input -f/--file.\n");
1593 return 1;
1594 }
1595 if (!partitioned_file_is_partitioned(param.image_file)) {
1596 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001597 return 1;
1598 }
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001599
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001600 if (!param.force && region_is_modern_cbfs(param.region_name)) {
Sol Boucher67a0a862015-03-18 12:36:27 -07001601 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1602 param.region_name);
1603 return 1;
1604 }
1605
1606 struct buffer new_content;
1607 if (buffer_from_file(&new_content, param.filename))
1608 return 1;
1609
1610 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1611 strlen(FMAP_SIGNATURE))) {
1612 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1613 param.filename);
1614 buffer_delete(&new_content);
1615 return 1;
1616 }
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001617 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
Sol Boucher67a0a862015-03-18 12:36:27 -07001618 strlen(CBFS_FILE_MAGIC))) {
1619 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1620 param.filename);
1621 buffer_delete(&new_content);
1622 return 1;
1623 }
1624
1625 unsigned offset = 0;
1626 if (param.fill_partial_upward && param.fill_partial_downward) {
1627 ERROR("You may only specify one of -u and -d.\n");
1628 buffer_delete(&new_content);
1629 return 1;
1630 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1631 if (new_content.size != param.image_region->size) {
1632 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",
1633 new_content.size, param.image_region->size);
1634 buffer_delete(&new_content);
1635 return 1;
1636 }
1637 } else {
1638 if (new_content.size > param.image_region->size) {
1639 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1640 new_content.size, param.image_region->size);
1641 buffer_delete(&new_content);
1642 return 1;
1643 }
Patrick Georgid9edb182016-12-06 18:55:26 +01001644 if (param.u64val == (uint64_t)-1) {
1645 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1646 param.fill_partial_upward ? "bottom" : "top");
1647 } else if (param.u64val > 0xff) {
1648 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1649 buffer_delete(&new_content);
1650 return 1;
1651 } else {
1652 memset(buffer_get(param.image_region),
1653 param.u64val & 0xff,
1654 buffer_size(param.image_region));
1655 }
Sol Boucher67a0a862015-03-18 12:36:27 -07001656 if (param.fill_partial_downward)
1657 offset = param.image_region->size - new_content.size;
1658 }
1659
1660 memcpy(param.image_region->data + offset, new_content.data,
1661 new_content.size);
1662 buffer_delete(&new_content);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001663
1664 return maybe_update_fmap_hash();
Sol Boucher67a0a862015-03-18 12:36:27 -07001665}
1666
1667static int cbfs_read(void)
1668{
1669 if (!param.filename) {
1670 ERROR("You need to specify a valid output -f/--file.\n");
1671 return 1;
1672 }
1673 if (!partitioned_file_is_partitioned(param.image_file)) {
1674 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1675 return 1;
1676 }
1677
1678 return buffer_write_file(param.image_region, param.filename);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001679}
1680
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001681static int cbfs_copy(void)
1682{
Patrick Georgi214e4af2015-11-20 19:22:50 +01001683 struct cbfs_image src_image;
Patrick Georgibd0bb232015-11-20 21:48:18 +01001684 struct buffer src_buf;
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001685
Patrick Georgibd0bb232015-11-20 21:48:18 +01001686 if (!param.source_region) {
1687 ERROR("You need to specify -R/--source-region.\n");
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001688 return 1;
Sol Bouchere3260a02015-03-25 13:40:08 -07001689 }
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001690
Patrick Georgibd0bb232015-11-20 21:48:18 +01001691 /* Obtain the source region and convert it to a cbfs_image. */
1692 if (!partitioned_file_read_region(&src_buf, param.image_file,
1693 param.source_region)) {
1694 ERROR("Region not found in image: %s\n", param.source_region);
1695 return 1;
1696 }
1697
1698 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1699 return 1;
1700
1701 return cbfs_copy_instance(&src_image, param.image_region);
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001702}
1703
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001704static int cbfs_compact(void)
1705{
1706 struct cbfs_image image;
1707 if (cbfs_image_from_buffer(&image, param.image_region,
1708 param.headeroffset))
1709 return 1;
1710 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1711 return cbfs_compact_instance(&image);
1712}
1713
Patrick Georgi5d982d72017-09-19 14:39:58 +02001714static int cbfs_expand(void)
1715{
1716 struct buffer src_buf;
1717
1718 /* Obtain the source region. */
1719 if (!partitioned_file_read_region(&src_buf, param.image_file,
1720 param.region_name)) {
1721 ERROR("Region not found in image: %s\n", param.source_region);
1722 return 1;
1723 }
1724
1725 return cbfs_expand_to_region(param.image_region);
1726}
1727
Patrick Georgi12631a42017-09-20 11:59:18 +02001728static int cbfs_truncate(void)
1729{
1730 struct buffer src_buf;
1731
1732 /* Obtain the source region. */
1733 if (!partitioned_file_read_region(&src_buf, param.image_file,
1734 param.region_name)) {
1735 ERROR("Region not found in image: %s\n", param.source_region);
1736 return 1;
1737 }
1738
1739 uint32_t size;
1740 int result = cbfs_truncate_space(param.image_region, &size);
1741 printf("0x%x\n", size);
1742 return result;
1743}
1744
Stefan Reinauera1e48242011-10-21 14:24:57 -07001745static const struct command commands[] = {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301746 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001747 {"add-flat-binary", "H:r:f:n:l:e:c:b:p:vA:gh?", cbfs_add_flat_binary,
Werner Zehe9995f12016-01-14 13:22:37 +01001748 true, true},
Raul E Rangeldb280402021-06-29 13:26:41 -06001749 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
Werner Zehe9995f12016-01-14 13:22:37 +01001750 true, true},
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001751 {"add-stage", "a:H:r:f:n:t:c:b:P:QS:p:yvA:gh?", cbfs_add_stage,
Werner Zehe9995f12016-01-14 13:22:37 +01001752 true, true},
1753 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301754 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001755 {"compact", "r:h?", cbfs_compact, true, true},
Patrick Georgibd0bb232015-11-20 21:48:18 +01001756 {"copy", "r:R:h?", cbfs_copy, true, true},
Patrick Georgi45acb342015-07-14 22:18:23 +02001757 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
Joel Kitching21fdd892018-08-09 17:49:52 +08001758 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001759 {"layout", "wvh?", cbfs_layout, false, false},
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001760 {"print", "H:r:vkh?", cbfs_print, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001761 {"read", "r:f:vh?", cbfs_read, true, false},
1762 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
Patrick Georgid9edb182016-12-06 18:55:26 +01001763 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
Patrick Georgi5d982d72017-09-19 14:39:58 +02001764 {"expand", "r:h?", cbfs_expand, true, true},
Patrick Georgi12631a42017-09-20 11:59:18 +02001765 {"truncate", "r:h?", cbfs_truncate, true, true},
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001766};
1767
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001768enum {
1769 /* begin after ASCII characters */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001770 LONGOPT_START = 256,
1771 LONGOPT_IBB = LONGOPT_START,
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001772 LONGOPT_EXT_WIN_BASE,
1773 LONGOPT_EXT_WIN_SIZE,
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001774 LONGOPT_END,
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001775};
1776
Stefan Reinauer63217582012-10-29 16:52:36 -07001777static struct option long_options[] = {
Julius Wernerefcee762014-11-10 13:14:24 -08001778 {"alignment", required_argument, 0, 'a' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001779 {"base-address", required_argument, 0, 'b' },
1780 {"bootblock", required_argument, 0, 'B' },
Julius Wernerefcee762014-11-10 13:14:24 -08001781 {"cmdline", required_argument, 0, 'C' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001782 {"compression", required_argument, 0, 'c' },
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301783 {"topswap-size", required_argument, 0, 'j' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001784 {"empty-fits", required_argument, 0, 'x' },
1785 {"entry-point", required_argument, 0, 'e' },
1786 {"file", required_argument, 0, 'f' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001787 {"fill-downward", no_argument, 0, 'd' },
1788 {"fill-upward", no_argument, 0, 'u' },
1789 {"flashmap", required_argument, 0, 'M' },
1790 {"fmap-regions", required_argument, 0, 'r' },
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001791 {"force", no_argument, 0, 'F' },
Patrick Georgibd0bb232015-11-20 21:48:18 +01001792 {"source-region", required_argument, 0, 'R' },
Patrick Georgi89f20342015-10-01 15:54:04 +02001793 {"hash-algorithm",required_argument, 0, 'A' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001794 {"header-offset", required_argument, 0, 'H' },
Julius Wernerefcee762014-11-10 13:14:24 -08001795 {"help", no_argument, 0, 'h' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001796 {"ignore-sec", required_argument, 0, 'S' },
1797 {"initrd", required_argument, 0, 'I' },
1798 {"int", required_argument, 0, 'i' },
1799 {"load-address", required_argument, 0, 'l' },
1800 {"machine", required_argument, 0, 'm' },
1801 {"name", required_argument, 0, 'n' },
1802 {"offset", required_argument, 0, 'o' },
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001803 {"padding", required_argument, 0, 'p' },
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001804 {"pow2page", no_argument, 0, 'Q' },
Rizwan Qureshic1072f22018-06-04 23:02:46 +05301805 {"ucode-region", required_argument, 0, 'q' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001806 {"size", required_argument, 0, 's' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001807 {"type", required_argument, 0, 't' },
1808 {"verbose", no_argument, 0, 'v' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001809 {"with-readonly", no_argument, 0, 'w' },
Aaron Durbin4be16742015-09-15 17:00:23 -05001810 {"xip", no_argument, 0, 'y' },
Werner Zehe9995f12016-01-14 13:22:37 +01001811 {"gen-attribute", no_argument, 0, 'g' },
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001812 {"mach-parseable",no_argument, 0, 'k' },
Joel Kitching21fdd892018-08-09 17:49:52 +08001813 {"unprocessed", no_argument, 0, 'U' },
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001814 {"ibb", no_argument, 0, LONGOPT_IBB },
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001815 {"ext-win-base", required_argument, 0, LONGOPT_EXT_WIN_BASE },
1816 {"ext-win-size", required_argument, 0, LONGOPT_EXT_WIN_SIZE },
Julius Wernerefcee762014-11-10 13:14:24 -08001817 {NULL, 0, 0, 0 }
Stefan Reinauer63217582012-10-29 16:52:36 -07001818};
1819
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001820static int get_region_offset(long long int offset, uint32_t *region_offset)
1821{
1822 /* If offset is not negative, no transformation required. */
1823 if (offset >= 0) {
1824 *region_offset = offset;
1825 return 0;
1826 }
1827
1828 /* Calculate offset from start of region. */
1829 return convert_region_offset(-offset, region_offset);
1830}
1831
1832static int calculate_region_offsets(void)
1833{
1834 int ret = 0;
1835
1836 if (param.baseaddress_assigned)
1837 ret |= get_region_offset(param.baseaddress_input, &param.baseaddress);
1838 if (param.headeroffset_assigned)
1839 ret |= get_region_offset(param.headeroffset_input, &param.headeroffset);
1840 if (param.cbfsoffset_assigned)
1841 ret |= get_region_offset(param.cbfsoffset_input, &param.cbfsoffset);
1842
1843 return ret;
1844}
1845
Sol Boucher67a0a862015-03-18 12:36:27 -07001846static int dispatch_command(struct command command)
1847{
1848 if (command.accesses_region) {
1849 assert(param.image_file);
1850
1851 if (partitioned_file_is_partitioned(param.image_file)) {
Patrick Georgi2ed72952016-12-16 14:51:53 +01001852 INFO("Performing operation on '%s' region...\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07001853 param.region_name);
1854 }
1855 if (!partitioned_file_read_region(param.image_region,
1856 param.image_file, param.region_name)) {
1857 ERROR("The image will be left unmodified.\n");
1858 return 1;
1859 }
1860
1861 if (command.modifies_region) {
1862 // We (intentionally) don't support overwriting the FMAP
1863 // section. If you find yourself wanting to do this,
1864 // consider creating a new image rather than performing
1865 // whatever hacky transformation you were planning.
1866 if (region_is_flashmap(param.region_name)) {
1867 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1868 param.region_name);
1869 ERROR("The image will be left unmodified.\n");
1870 return 1;
1871 }
1872 // We don't allow writing raw data to regions that
1873 // contain nested regions, since doing so would
1874 // overwrite all such subregions.
1875 if (partitioned_file_region_contains_nested(
1876 param.image_file, param.region_name)) {
1877 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1878 param.region_name);
1879 ERROR("The image will be left unmodified.\n");
1880 return 1;
1881 }
1882 }
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001883
1884 /*
1885 * Once image region is read, input offsets can be adjusted accordingly if the
1886 * inputs are provided as negative integers i.e. offsets from end of region.
1887 */
1888 if (calculate_region_offsets())
1889 return 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001890 }
1891
1892 if (command.function()) {
1893 if (partitioned_file_is_partitioned(param.image_file)) {
1894 ERROR("Failed while operating on '%s' region!\n",
1895 param.region_name);
1896 ERROR("The image will be left unmodified.\n");
1897 }
1898 return 1;
1899 }
1900
1901 return 0;
1902}
1903
Stefan Reinauer63217582012-10-29 16:52:36 -07001904static void usage(char *name)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001905{
1906 printf
Stefan Reinauer07040582010-04-24 21:24:06 +00001907 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
Stefan Reinauer63217582012-10-29 16:52:36 -07001908 "USAGE:\n" " %s [-h]\n"
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001909 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001910 " -H header_offset Do not search for header; use this offset*\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001911 " -T Output top-aligned memory address\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001912 " -u Accept short data; fill upward/from bottom\n"
1913 " -d Accept short data; fill downward/from top\n"
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001914 " -F Force action\n"
Patrick Georgi5b6bdcc2016-10-13 18:24:10 +02001915 " -g Generate position and alignment arguments\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001916 " -U Unprocessed; don't decompress or make ELF\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001917 " -v Provide verbose output\n"
1918 " -h Display this help message\n\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001919 " --ext-win-base Base of extended decode window in host address\n"
1920 " space(x86 only)\n"
1921 " --ext-win-size Size of extended decode window in host address\n"
1922 " space(x86 only)\n"
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001923 "COMMANDs:\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001924 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001925 " [-c compression] [-b base-address | -a alignment] \\\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301926 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001927 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1928 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001929 "Add a component\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301930 " "
1931 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001932 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001933 " [-c compression] [-b base-address] \\\n"
1934 " (linux specific: [-C cmdline] [-I initrd]) "
Stefan Reinauer63217582012-10-29 16:52:36 -07001935 "Add a payload to the ROM\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001936 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001937 " [-c compression] [-b base] [-S section-to-ignore] \\\n"
Arthur Heymansc78521b32021-06-16 17:16:05 +02001938 " [-a alignment] [-Q|--pow2page] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001939 " [-y|--xip] [--ibb] \\\n"
1940 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001941 "Add a stage to the ROM\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001942 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1943 " [-A hash] -l load-address -e entry-point \\\n"
1944 " [-c compression] [-b base] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001945 "Add a 32bit flat mode binary\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001946 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001947 "Add a raw 64-bit integer value\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301948 " add-master-header [-r image,regions] \\ \n"
1949 " [-j topswap-size] (Intel CPUs only) "
Patrick Georgi59e52b92015-09-10 15:28:27 +02001950 "Add a legacy CBFS master header\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001951 " remove [-r image,regions] -n NAME "
Stefan Reinauer63217582012-10-29 16:52:36 -07001952 "Remove a component\n"
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001953 " compact -r image,regions "
1954 "Defragment CBFS image.\n"
Patrick Georgibd0bb232015-11-20 21:48:18 +01001955 " copy -r image,regions -R source-region "
1956 "Create a copy (duplicate) cbfs instance in fmap\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001957 " create -m ARCH -s size [-b bootblock offset] \\\n"
Patrick Georgi83c2d122015-08-26 10:40:00 +02001958 " [-o CBFS offset] [-H header offset] [-B bootblock] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001959 "Create a legacy ROM file with CBFS master header*\n"
1960 " create -M flashmap [-r list,of,regions,containing,cbfses] "
1961 "Create a new-style partitioned firmware image\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001962 " layout [-w] "
1963 "List mutable (or, with -w, readable) image regions\n"
Julius Werner4bfbabd2020-05-06 17:27:02 -07001964 " print [-r image,regions] [-k] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001965 "Show the contents of the ROM\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001966 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
1967 "Extracts a file from ROM\n"
Patrick Georgid9edb182016-12-06 18:55:26 +01001968 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001969 "Write file into same-size [or larger] raw region\n"
1970 " read [-r fmap-region] -f file "
1971 "Extract raw region contents into binary file\n"
Patrick Georgi12631a42017-09-20 11:59:18 +02001972 " truncate [-r fmap-region] "
1973 "Truncate CBFS and print new size on stdout\n"
Patrick Georgi5d982d72017-09-19 14:39:58 +02001974 " expand [-r fmap-region] "
1975 "Expand CBFS to span entire region\n"
Sol Boucher0e539312015-03-05 15:38:03 -08001976 "OFFSETs:\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001977 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
1978 " in two possible formats: if their value is greater than\n"
Sol Boucher0e539312015-03-05 15:38:03 -08001979 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
1980 " address; otherwise, they are treated as an offset into flash.\n"
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02001981 "ARCHes:\n", name, name
Stefan Reinauer63217582012-10-29 16:52:36 -07001982 );
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02001983 print_supported_architectures();
1984
1985 printf("TYPEs:\n");
Stefan Reinauer07040582010-04-24 21:24:06 +00001986 print_supported_filetypes();
Sol Boucher67a0a862015-03-18 12:36:27 -07001987 printf(
1988 "\n* Note that these actions and switches are only valid when\n"
1989 " working with legacy images whose structure is described\n"
1990 " primarily by a CBFS master header. New-style images, in\n"
1991 " contrast, exclusively make use of an FMAP to describe their\n"
1992 " layout: this must minimally contain an '%s' section\n"
1993 " specifying the location of this FMAP itself and a '%s'\n"
1994 " section describing the primary CBFS. It should also be noted\n"
1995 " that, when working with such images, the -F and -r switches\n"
Julius Werner0fd072d2021-12-13 10:28:29 -08001996 " default to '%s' for convenience, and the -b switch becomes\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001997 " relative to the selected CBFS region's lowest address.\n"
1998 " The one exception to this rule is the top-aligned address,\n"
1999 " which is always relative to the end of the entire image\n"
2000 " rather than relative to the local region; this is true for\n"
2001 " for both input (sufficiently large) and output (-T) data.\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07002002 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
2003 SECTION_NAME_PRIMARY_CBFS
2004 );
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002005}
2006
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002007static bool valid_opt(size_t i, int c)
2008{
2009 /* Check if it is one of the optstrings supported by the command. */
2010 if (strchr(commands[i].optstring, c))
2011 return true;
2012
2013 /*
2014 * Check if it is one of the non-ASCII characters. Currently, the
2015 * non-ASCII characters are only checked against the valid list
2016 * irrespective of the command.
2017 */
2018 if (c >= LONGOPT_START && c < LONGOPT_END)
2019 return true;
2020
2021 return false;
2022}
2023
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002024int main(int argc, char **argv)
2025{
Mathias Krause41c229c2012-07-17 21:17:15 +02002026 size_t i;
Stefan Reinauer63217582012-10-29 16:52:36 -07002027 int c;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002028
2029 if (argc < 3) {
Stefan Reinauer63217582012-10-29 16:52:36 -07002030 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002031 return 1;
2032 }
2033
Sol Bouchere3260a02015-03-25 13:40:08 -07002034 char *image_name = argv[1];
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002035 char *cmd = argv[2];
Stefan Reinauer63217582012-10-29 16:52:36 -07002036 optind += 2;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002037
2038 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2039 if (strcmp(cmd, commands[i].name) != 0)
2040 continue;
Stefan Reinauer63217582012-10-29 16:52:36 -07002041
2042 while (1) {
2043 char *suffix = NULL;
2044 int option_index = 0;
2045
2046 c = getopt_long(argc, argv, commands[i].optstring,
2047 long_options, &option_index);
Nico Huber54073102016-08-01 23:18:29 +02002048 if (c == -1) {
2049 if (optind < argc) {
2050 ERROR("%s: excessive argument -- '%s'"
2051 "\n", argv[0], argv[optind]);
2052 return 1;
2053 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002054 break;
Nico Huber54073102016-08-01 23:18:29 +02002055 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002056
Joel Kitching672d5ad2018-10-11 17:01:30 +08002057 /* Filter out illegal long options */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002058 if (!valid_opt(i, c)) {
2059 ERROR("%s: invalid option -- '%d'\n",
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002060 argv[0], c);
Stefan Reinauer63217582012-10-29 16:52:36 -07002061 c = '?';
2062 }
2063
2064 switch(c) {
2065 case 'n':
Hung-Te Lind1739622013-01-28 14:23:49 +08002066 param.name = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002067 break;
2068 case 't':
2069 if (intfiletype(optarg) != ((uint64_t) - 1))
Hung-Te Lind1739622013-01-28 14:23:49 +08002070 param.type = intfiletype(optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002071 else
Hung-Te Lind1739622013-01-28 14:23:49 +08002072 param.type = strtoul(optarg, NULL, 0);
2073 if (param.type == 0)
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002074 WARN("Unknown type '%s' ignored\n",
Stefan Reinauer63217582012-10-29 16:52:36 -07002075 optarg);
2076 break;
Sol Boucherec424862015-05-07 21:00:05 -07002077 case 'c': {
Patrick Georgiecaa5702017-01-11 18:38:11 +01002078 if (strcmp(optarg, "precompression") == 0) {
2079 param.precompression = 1;
2080 break;
2081 }
Sol Boucherec424862015-05-07 21:00:05 -07002082 int algo = cbfs_parse_comp_algo(optarg);
2083 if (algo >= 0)
2084 param.compression = algo;
Stefan Reinauer63217582012-10-29 16:52:36 -07002085 else
Sol Boucherec424862015-05-07 21:00:05 -07002086 WARN("Unknown compression '%s' ignored.\n",
2087 optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002088 break;
Sol Boucherec424862015-05-07 21:00:05 -07002089 }
Patrick Georgi89f20342015-10-01 15:54:04 +02002090 case 'A': {
Julius Wernerd4775652020-03-13 16:43:34 -07002091 if (!vb2_lookup_hash_alg(optarg, &param.hash)) {
Patrick Georgi89f20342015-10-01 15:54:04 +02002092 ERROR("Unknown hash algorithm '%s'.\n",
2093 optarg);
2094 return 1;
2095 }
2096 break;
2097 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002098 case 'M':
2099 param.fmap = optarg;
2100 break;
2101 case 'r':
2102 param.region_name = optarg;
2103 break;
Patrick Georgibd0bb232015-11-20 21:48:18 +01002104 case 'R':
2105 param.source_region = optarg;
2106 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002107 case 'b':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002108 param.baseaddress_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002109 if (!*optarg || (suffix && *suffix)) {
2110 ERROR("Invalid base address '%s'.\n",
2111 optarg);
2112 return 1;
2113 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002114 // baseaddress may be zero on non-x86, so we
2115 // need an explicit "baseaddress_assigned".
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002116 param.baseaddress_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002117 break;
2118 case 'l':
Nico Huber9ade7172016-08-01 21:37:42 +02002119 param.loadaddress = strtoul(optarg, &suffix, 0);
2120 if (!*optarg || (suffix && *suffix)) {
2121 ERROR("Invalid load address '%s'.\n",
2122 optarg);
2123 return 1;
2124 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002125 break;
2126 case 'e':
Nico Huber9ade7172016-08-01 21:37:42 +02002127 param.entrypoint = strtoul(optarg, &suffix, 0);
2128 if (!*optarg || (suffix && *suffix)) {
2129 ERROR("Invalid entry point '%s'.\n",
2130 optarg);
2131 return 1;
2132 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002133 break;
2134 case 's':
Hung-Te Lind1739622013-01-28 14:23:49 +08002135 param.size = strtoul(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002136 if (!*optarg) {
2137 ERROR("Empty size specified.\n");
2138 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002139 }
Nico Huber9ade7172016-08-01 21:37:42 +02002140 switch (tolower((int)suffix[0])) {
2141 case 'k':
2142 param.size *= 1024;
2143 break;
2144 case 'm':
Hung-Te Lind1739622013-01-28 14:23:49 +08002145 param.size *= 1024 * 1024;
Nico Huber9ade7172016-08-01 21:37:42 +02002146 break;
2147 case '\0':
2148 break;
2149 default:
2150 ERROR("Invalid suffix for size '%s'.\n",
2151 optarg);
2152 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002153 }
Patrick Georgie887ca52014-08-09 17:44:39 +02002154 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002155 case 'B':
Hung-Te Lind1739622013-01-28 14:23:49 +08002156 param.bootblock = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002157 break;
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002158 case 'H':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002159 param.headeroffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002160 if (!*optarg || (suffix && *suffix)) {
2161 ERROR("Invalid header offset '%s'.\n",
2162 optarg);
2163 return 1;
2164 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002165 param.headeroffset_assigned = 1;
2166 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002167 case 'a':
Nico Huber9ade7172016-08-01 21:37:42 +02002168 param.alignment = strtoul(optarg, &suffix, 0);
2169 if (!*optarg || (suffix && *suffix)) {
2170 ERROR("Invalid alignment '%s'.\n",
2171 optarg);
2172 return 1;
2173 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002174 break;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07002175 case 'p':
2176 param.padding = strtoul(optarg, &suffix, 0);
2177 if (!*optarg || (suffix && *suffix)) {
2178 ERROR("Invalid pad size '%s'.\n",
2179 optarg);
2180 return 1;
2181 }
2182 break;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03002183 case 'Q':
2184 param.force_pow2_pagesize = 1;
2185 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002186 case 'o':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002187 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002188 if (!*optarg || (suffix && *suffix)) {
2189 ERROR("Invalid cbfs offset '%s'.\n",
2190 optarg);
2191 return 1;
2192 }
Julius Wernerefcee762014-11-10 13:14:24 -08002193 param.cbfsoffset_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002194 break;
2195 case 'f':
Hung-Te Lind1739622013-01-28 14:23:49 +08002196 param.filename = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002197 break;
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02002198 case 'F':
2199 param.force = 1;
2200 break;
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002201 case 'i':
Nico Huber9ade7172016-08-01 21:37:42 +02002202 param.u64val = strtoull(optarg, &suffix, 0);
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01002203 param.u64val_assigned = 1;
Nico Huber9ade7172016-08-01 21:37:42 +02002204 if (!*optarg || (suffix && *suffix)) {
2205 ERROR("Invalid int parameter '%s'.\n",
2206 optarg);
2207 return 1;
2208 }
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002209 break;
Sol Boucher67a0a862015-03-18 12:36:27 -07002210 case 'u':
2211 param.fill_partial_upward = true;
2212 break;
2213 case 'd':
2214 param.fill_partial_downward = true;
2215 break;
2216 case 'w':
2217 param.show_immutable = true;
Hung-Te Lin215d1d72013-01-29 03:46:02 +08002218 break;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05302219 case 'j':
2220 param.topswap_size = strtol(optarg, NULL, 0);
2221 if (!is_valid_topswap())
2222 return 1;
2223 break;
Rizwan Qureshic1072f22018-06-04 23:02:46 +05302224 case 'q':
2225 param.ucode_region = optarg;
2226 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002227 case 'v':
2228 verbose++;
2229 break;
David Hendricks90ca3b62012-11-16 14:48:22 -08002230 case 'm':
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06002231 param.arch = string_to_arch(optarg);
David Hendricks90ca3b62012-11-16 14:48:22 -08002232 break;
Patrick Georgide36d332013-08-27 20:22:21 +02002233 case 'I':
2234 param.initrd = optarg;
2235 break;
2236 case 'C':
2237 param.cmdline = optarg;
2238 break;
Furquan Shaikh405304a2014-10-30 11:44:20 -07002239 case 'S':
2240 param.ignore_section = optarg;
2241 break;
Aaron Durbin4be16742015-09-15 17:00:23 -05002242 case 'y':
2243 param.stage_xip = true;
2244 break;
Werner Zehe9995f12016-01-14 13:22:37 +01002245 case 'g':
2246 param.autogen_attr = true;
2247 break;
Aaron Durbin5dc628a2016-01-26 15:35:34 -06002248 case 'k':
2249 param.machine_parseable = true;
2250 break;
Joel Kitching21fdd892018-08-09 17:49:52 +08002251 case 'U':
2252 param.unprocessed = true;
2253 break;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01002254 case LONGOPT_IBB:
2255 param.ibb = true;
2256 break;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08002257 case LONGOPT_EXT_WIN_BASE:
2258 param.ext_win_base = strtoul(optarg, &suffix, 0);
2259 if (!*optarg || (suffix && *suffix)) {
2260 ERROR("Invalid ext window base '%s'.\n", optarg);
2261 return 1;
2262 }
2263 break;
2264 case LONGOPT_EXT_WIN_SIZE:
2265 param.ext_win_size = strtoul(optarg, &suffix, 0);
2266 if (!*optarg || (suffix && *suffix)) {
2267 ERROR("Invalid ext window size '%s'.\n", optarg);
2268 return 1;
2269 }
2270 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002271 case 'h':
2272 case '?':
2273 usage(argv[0]);
2274 return 1;
2275 default:
2276 break;
2277 }
2278 }
2279
Sol Bouchere3260a02015-03-25 13:40:08 -07002280 if (commands[i].function == cbfs_create) {
Sol Boucher67a0a862015-03-18 12:36:27 -07002281 if (param.fmap) {
2282 struct buffer flashmap;
2283 if (buffer_from_file(&flashmap, param.fmap))
2284 return 1;
2285 param.image_file = partitioned_file_create(
2286 image_name, &flashmap);
2287 buffer_delete(&flashmap);
2288 } else if (param.size) {
2289 param.image_file = partitioned_file_create_flat(
2290 image_name, param.size);
2291 } else {
2292 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002293 return 1;
2294 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002295 } else {
Vadim Bendebury75599462015-12-09 09:39:31 -08002296 bool write_access = commands[i].modifies_region;
2297
Sol Bouchere3260a02015-03-25 13:40:08 -07002298 param.image_file =
Vadim Bendebury75599462015-12-09 09:39:31 -08002299 partitioned_file_reopen(image_name,
2300 write_access);
Sol Bouchere3260a02015-03-25 13:40:08 -07002301 }
2302 if (!param.image_file)
2303 return 1;
2304
Sol Boucher67a0a862015-03-18 12:36:27 -07002305 unsigned num_regions = 1;
2306 for (const char *list = strchr(param.region_name, ','); list;
2307 list = strchr(list + 1, ','))
2308 ++num_regions;
2309
Sol Bouchere3260a02015-03-25 13:40:08 -07002310 // If the action needs to read an image region, as indicated by
2311 // having accesses_region set in its command struct, that
2312 // region's buffer struct will be stored here and the client
2313 // will receive a pointer to it via param.image_region. It
2314 // need not write the buffer back to the image file itself,
2315 // since this behavior can be requested via its modifies_region
2316 // field. Additionally, it should never free the region buffer,
2317 // as that is performed automatically once it completes.
Sol Boucher67a0a862015-03-18 12:36:27 -07002318 struct buffer image_regions[num_regions];
2319 memset(image_regions, 0, sizeof(image_regions));
Sol Bouchere3260a02015-03-25 13:40:08 -07002320
Sol Boucher67a0a862015-03-18 12:36:27 -07002321 bool seen_primary_cbfs = false;
2322 char region_name_scratch[strlen(param.region_name) + 1];
2323 strcpy(region_name_scratch, param.region_name);
2324 param.region_name = strtok(region_name_scratch, ",");
2325 for (unsigned region = 0; region < num_regions; ++region) {
2326 if (!param.region_name) {
2327 ERROR("Encountered illegal degenerate region name in -r list\n");
2328 ERROR("The image will be left unmodified.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002329 partitioned_file_close(param.image_file);
2330 return 1;
2331 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002332
Sol Boucher67a0a862015-03-18 12:36:27 -07002333 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2334 == 0)
2335 seen_primary_cbfs = true;
Sol Bouchere3260a02015-03-25 13:40:08 -07002336
Sol Boucher67a0a862015-03-18 12:36:27 -07002337 param.image_region = image_regions + region;
2338 if (dispatch_command(commands[i])) {
Sol Bouchere3260a02015-03-25 13:40:08 -07002339 partitioned_file_close(param.image_file);
2340 return 1;
2341 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002342
2343 param.region_name = strtok(NULL, ",");
2344 }
2345
2346 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2347 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2348 SECTION_NAME_PRIMARY_CBFS);
2349 ERROR("The image will be left unmodified.\n");
2350 partitioned_file_close(param.image_file);
2351 return 1;
2352 }
2353
2354 if (commands[i].modifies_region) {
2355 assert(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002356 for (unsigned region = 0; region < num_regions;
2357 ++region) {
2358
2359 if (!partitioned_file_write_region(
2360 param.image_file,
2361 image_regions + region)) {
2362 partitioned_file_close(
2363 param.image_file);
2364 return 1;
2365 }
2366 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002367 }
2368
2369 partitioned_file_close(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002370 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002371 }
2372
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002373 ERROR("Unknown command '%s'.\n", cmd);
Stefan Reinauer63217582012-10-29 16:52:36 -07002374 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002375 return 1;
2376}