blob: 71c8911ede21b6bf16942ce6a7b7f8c0006ce9a9 [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
Stefan Reinauer63217582012-10-29 16:52:36 -07001502static int cbfs_print(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001503{
Hung-Te Lin3bb035b2013-01-29 02:15:49 +08001504 struct cbfs_image image;
Sol Bouchere3260a02015-03-25 13:40:08 -07001505 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001506 param.headeroffset))
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001507 return 1;
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001508 if (param.machine_parseable) {
1509 if (verbose)
1510 printf("[FMAP REGION]\t%s\n", param.region_name);
1511 cbfs_print_parseable_directory(&image);
1512 } else {
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001513 printf("FMAP REGION: %s\n", param.region_name);
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001514 cbfs_print_directory(&image);
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001515 }
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001516
Julius Werner4bfbabd2020-05-06 17:27:02 -07001517 if (verbose) {
1518 struct mh_cache *mhc = get_mh_cache();
1519 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1520 return 0;
1521
1522 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
Julius Werner69cc5572022-03-04 17:49:56 -08001523 enum cb_err err = cbfs_walk(&image, NULL, NULL, &real_hash,
1524 CBFS_WALK_WRITEBACK_HASH);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001525 if (err != CB_CBFS_NOT_FOUND) {
1526 ERROR("Unexpected cbfs_walk() error %d\n", err);
1527 return 1;
1528 }
1529 char *hash_str = bintohex(real_hash.raw,
1530 vb2_digest_size(real_hash.algo));
1531 printf("[METADATA HASH]\t%s:%s",
1532 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1533 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1534 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
1535 vb2_digest_size(real_hash.algo)))
1536 printf(":valid");
1537 else
1538 printf(":invalid");
1539 }
1540 printf("\n");
1541 free(hash_str);
1542 }
1543
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001544 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001545}
1546
Stefan Reinauer63217582012-10-29 16:52:36 -07001547static int cbfs_extract(void)
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001548{
Hung-Te Lind1739622013-01-28 14:23:49 +08001549 if (!param.filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001550 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001551 return 1;
1552 }
1553
Hung-Te Lind1739622013-01-28 14:23:49 +08001554 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001555 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001556 return 1;
1557 }
1558
Sol Bouchere3260a02015-03-25 13:40:08 -07001559 struct cbfs_image image;
1560 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001561 param.headeroffset))
1562 return 1;
1563
Aaron Durbin17625022015-10-27 13:17:52 -05001564 return cbfs_export_entry(&image, param.name, param.filename,
Joel Kitching21fdd892018-08-09 17:49:52 +08001565 param.arch, !param.unprocessed);
Sol Boucher67a0a862015-03-18 12:36:27 -07001566}
1567
1568static int cbfs_write(void)
1569{
1570 if (!param.filename) {
1571 ERROR("You need to specify a valid input -f/--file.\n");
1572 return 1;
1573 }
1574 if (!partitioned_file_is_partitioned(param.image_file)) {
1575 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001576 return 1;
1577 }
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001578
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001579 if (!param.force && region_is_modern_cbfs(param.region_name)) {
Sol Boucher67a0a862015-03-18 12:36:27 -07001580 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1581 param.region_name);
1582 return 1;
1583 }
1584
1585 struct buffer new_content;
1586 if (buffer_from_file(&new_content, param.filename))
1587 return 1;
1588
1589 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1590 strlen(FMAP_SIGNATURE))) {
1591 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1592 param.filename);
1593 buffer_delete(&new_content);
1594 return 1;
1595 }
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001596 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
Sol Boucher67a0a862015-03-18 12:36:27 -07001597 strlen(CBFS_FILE_MAGIC))) {
1598 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1599 param.filename);
1600 buffer_delete(&new_content);
1601 return 1;
1602 }
1603
1604 unsigned offset = 0;
1605 if (param.fill_partial_upward && param.fill_partial_downward) {
1606 ERROR("You may only specify one of -u and -d.\n");
1607 buffer_delete(&new_content);
1608 return 1;
1609 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1610 if (new_content.size != param.image_region->size) {
1611 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",
1612 new_content.size, param.image_region->size);
1613 buffer_delete(&new_content);
1614 return 1;
1615 }
1616 } else {
1617 if (new_content.size > param.image_region->size) {
1618 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1619 new_content.size, param.image_region->size);
1620 buffer_delete(&new_content);
1621 return 1;
1622 }
Patrick Georgid9edb182016-12-06 18:55:26 +01001623 if (param.u64val == (uint64_t)-1) {
1624 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1625 param.fill_partial_upward ? "bottom" : "top");
1626 } else if (param.u64val > 0xff) {
1627 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1628 buffer_delete(&new_content);
1629 return 1;
1630 } else {
1631 memset(buffer_get(param.image_region),
1632 param.u64val & 0xff,
1633 buffer_size(param.image_region));
1634 }
Sol Boucher67a0a862015-03-18 12:36:27 -07001635 if (param.fill_partial_downward)
1636 offset = param.image_region->size - new_content.size;
1637 }
1638
1639 memcpy(param.image_region->data + offset, new_content.data,
1640 new_content.size);
1641 buffer_delete(&new_content);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001642
1643 return maybe_update_fmap_hash();
Sol Boucher67a0a862015-03-18 12:36:27 -07001644}
1645
1646static int cbfs_read(void)
1647{
1648 if (!param.filename) {
1649 ERROR("You need to specify a valid output -f/--file.\n");
1650 return 1;
1651 }
1652 if (!partitioned_file_is_partitioned(param.image_file)) {
1653 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1654 return 1;
1655 }
1656
1657 return buffer_write_file(param.image_region, param.filename);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001658}
1659
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001660static int cbfs_copy(void)
1661{
Patrick Georgi214e4af2015-11-20 19:22:50 +01001662 struct cbfs_image src_image;
Patrick Georgibd0bb232015-11-20 21:48:18 +01001663 struct buffer src_buf;
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001664
Patrick Georgibd0bb232015-11-20 21:48:18 +01001665 if (!param.source_region) {
1666 ERROR("You need to specify -R/--source-region.\n");
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001667 return 1;
Sol Bouchere3260a02015-03-25 13:40:08 -07001668 }
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001669
Patrick Georgibd0bb232015-11-20 21:48:18 +01001670 /* Obtain the source region and convert it to a cbfs_image. */
1671 if (!partitioned_file_read_region(&src_buf, param.image_file,
1672 param.source_region)) {
1673 ERROR("Region not found in image: %s\n", param.source_region);
1674 return 1;
1675 }
1676
1677 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1678 return 1;
1679
1680 return cbfs_copy_instance(&src_image, param.image_region);
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001681}
1682
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001683static int cbfs_compact(void)
1684{
1685 struct cbfs_image image;
1686 if (cbfs_image_from_buffer(&image, param.image_region,
1687 param.headeroffset))
1688 return 1;
1689 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1690 return cbfs_compact_instance(&image);
1691}
1692
Patrick Georgi5d982d72017-09-19 14:39:58 +02001693static int cbfs_expand(void)
1694{
1695 struct buffer src_buf;
1696
1697 /* Obtain the source region. */
1698 if (!partitioned_file_read_region(&src_buf, param.image_file,
1699 param.region_name)) {
1700 ERROR("Region not found in image: %s\n", param.source_region);
1701 return 1;
1702 }
1703
1704 return cbfs_expand_to_region(param.image_region);
1705}
1706
Patrick Georgi12631a42017-09-20 11:59:18 +02001707static int cbfs_truncate(void)
1708{
1709 struct buffer src_buf;
1710
1711 /* Obtain the source region. */
1712 if (!partitioned_file_read_region(&src_buf, param.image_file,
1713 param.region_name)) {
1714 ERROR("Region not found in image: %s\n", param.source_region);
1715 return 1;
1716 }
1717
1718 uint32_t size;
1719 int result = cbfs_truncate_space(param.image_region, &size);
1720 printf("0x%x\n", size);
1721 return result;
1722}
1723
Stefan Reinauera1e48242011-10-21 14:24:57 -07001724static const struct command commands[] = {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301725 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001726 {"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 +01001727 true, true},
Raul E Rangeldb280402021-06-29 13:26:41 -06001728 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
Werner Zehe9995f12016-01-14 13:22:37 +01001729 true, true},
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001730 {"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 +01001731 true, true},
1732 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301733 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001734 {"compact", "r:h?", cbfs_compact, true, true},
Patrick Georgibd0bb232015-11-20 21:48:18 +01001735 {"copy", "r:R:h?", cbfs_copy, true, true},
Patrick Georgi45acb342015-07-14 22:18:23 +02001736 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
Joel Kitching21fdd892018-08-09 17:49:52 +08001737 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001738 {"layout", "wvh?", cbfs_layout, false, false},
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001739 {"print", "H:r:vkh?", cbfs_print, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001740 {"read", "r:f:vh?", cbfs_read, true, false},
1741 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
Patrick Georgid9edb182016-12-06 18:55:26 +01001742 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
Patrick Georgi5d982d72017-09-19 14:39:58 +02001743 {"expand", "r:h?", cbfs_expand, true, true},
Patrick Georgi12631a42017-09-20 11:59:18 +02001744 {"truncate", "r:h?", cbfs_truncate, true, true},
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001745};
1746
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001747enum {
1748 /* begin after ASCII characters */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001749 LONGOPT_START = 256,
1750 LONGOPT_IBB = LONGOPT_START,
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001751 LONGOPT_EXT_WIN_BASE,
1752 LONGOPT_EXT_WIN_SIZE,
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001753 LONGOPT_END,
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001754};
1755
Stefan Reinauer63217582012-10-29 16:52:36 -07001756static struct option long_options[] = {
Julius Wernerefcee762014-11-10 13:14:24 -08001757 {"alignment", required_argument, 0, 'a' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001758 {"base-address", required_argument, 0, 'b' },
1759 {"bootblock", required_argument, 0, 'B' },
Julius Wernerefcee762014-11-10 13:14:24 -08001760 {"cmdline", required_argument, 0, 'C' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001761 {"compression", required_argument, 0, 'c' },
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301762 {"topswap-size", required_argument, 0, 'j' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001763 {"empty-fits", required_argument, 0, 'x' },
1764 {"entry-point", required_argument, 0, 'e' },
1765 {"file", required_argument, 0, 'f' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001766 {"fill-downward", no_argument, 0, 'd' },
1767 {"fill-upward", no_argument, 0, 'u' },
1768 {"flashmap", required_argument, 0, 'M' },
1769 {"fmap-regions", required_argument, 0, 'r' },
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001770 {"force", no_argument, 0, 'F' },
Patrick Georgibd0bb232015-11-20 21:48:18 +01001771 {"source-region", required_argument, 0, 'R' },
Patrick Georgi89f20342015-10-01 15:54:04 +02001772 {"hash-algorithm",required_argument, 0, 'A' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001773 {"header-offset", required_argument, 0, 'H' },
Julius Wernerefcee762014-11-10 13:14:24 -08001774 {"help", no_argument, 0, 'h' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001775 {"ignore-sec", required_argument, 0, 'S' },
1776 {"initrd", required_argument, 0, 'I' },
1777 {"int", required_argument, 0, 'i' },
1778 {"load-address", required_argument, 0, 'l' },
1779 {"machine", required_argument, 0, 'm' },
1780 {"name", required_argument, 0, 'n' },
1781 {"offset", required_argument, 0, 'o' },
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001782 {"padding", required_argument, 0, 'p' },
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001783 {"pow2page", no_argument, 0, 'Q' },
Rizwan Qureshic1072f22018-06-04 23:02:46 +05301784 {"ucode-region", required_argument, 0, 'q' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001785 {"size", required_argument, 0, 's' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001786 {"type", required_argument, 0, 't' },
1787 {"verbose", no_argument, 0, 'v' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001788 {"with-readonly", no_argument, 0, 'w' },
Aaron Durbin4be16742015-09-15 17:00:23 -05001789 {"xip", no_argument, 0, 'y' },
Werner Zehe9995f12016-01-14 13:22:37 +01001790 {"gen-attribute", no_argument, 0, 'g' },
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001791 {"mach-parseable",no_argument, 0, 'k' },
Joel Kitching21fdd892018-08-09 17:49:52 +08001792 {"unprocessed", no_argument, 0, 'U' },
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001793 {"ibb", no_argument, 0, LONGOPT_IBB },
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001794 {"ext-win-base", required_argument, 0, LONGOPT_EXT_WIN_BASE },
1795 {"ext-win-size", required_argument, 0, LONGOPT_EXT_WIN_SIZE },
Julius Wernerefcee762014-11-10 13:14:24 -08001796 {NULL, 0, 0, 0 }
Stefan Reinauer63217582012-10-29 16:52:36 -07001797};
1798
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001799static int get_region_offset(long long int offset, uint32_t *region_offset)
1800{
1801 /* If offset is not negative, no transformation required. */
1802 if (offset >= 0) {
1803 *region_offset = offset;
1804 return 0;
1805 }
1806
1807 /* Calculate offset from start of region. */
1808 return convert_region_offset(-offset, region_offset);
1809}
1810
1811static int calculate_region_offsets(void)
1812{
1813 int ret = 0;
1814
1815 if (param.baseaddress_assigned)
1816 ret |= get_region_offset(param.baseaddress_input, &param.baseaddress);
1817 if (param.headeroffset_assigned)
1818 ret |= get_region_offset(param.headeroffset_input, &param.headeroffset);
1819 if (param.cbfsoffset_assigned)
1820 ret |= get_region_offset(param.cbfsoffset_input, &param.cbfsoffset);
1821
1822 return ret;
1823}
1824
Sol Boucher67a0a862015-03-18 12:36:27 -07001825static int dispatch_command(struct command command)
1826{
1827 if (command.accesses_region) {
1828 assert(param.image_file);
1829
1830 if (partitioned_file_is_partitioned(param.image_file)) {
Patrick Georgi2ed72952016-12-16 14:51:53 +01001831 INFO("Performing operation on '%s' region...\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07001832 param.region_name);
1833 }
1834 if (!partitioned_file_read_region(param.image_region,
1835 param.image_file, param.region_name)) {
1836 ERROR("The image will be left unmodified.\n");
1837 return 1;
1838 }
1839
1840 if (command.modifies_region) {
1841 // We (intentionally) don't support overwriting the FMAP
1842 // section. If you find yourself wanting to do this,
1843 // consider creating a new image rather than performing
1844 // whatever hacky transformation you were planning.
1845 if (region_is_flashmap(param.region_name)) {
1846 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1847 param.region_name);
1848 ERROR("The image will be left unmodified.\n");
1849 return 1;
1850 }
1851 // We don't allow writing raw data to regions that
1852 // contain nested regions, since doing so would
1853 // overwrite all such subregions.
1854 if (partitioned_file_region_contains_nested(
1855 param.image_file, param.region_name)) {
1856 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1857 param.region_name);
1858 ERROR("The image will be left unmodified.\n");
1859 return 1;
1860 }
1861 }
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001862
1863 /*
1864 * Once image region is read, input offsets can be adjusted accordingly if the
1865 * inputs are provided as negative integers i.e. offsets from end of region.
1866 */
1867 if (calculate_region_offsets())
1868 return 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001869 }
1870
1871 if (command.function()) {
1872 if (partitioned_file_is_partitioned(param.image_file)) {
1873 ERROR("Failed while operating on '%s' region!\n",
1874 param.region_name);
1875 ERROR("The image will be left unmodified.\n");
1876 }
1877 return 1;
1878 }
1879
1880 return 0;
1881}
1882
Stefan Reinauer63217582012-10-29 16:52:36 -07001883static void usage(char *name)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001884{
1885 printf
Stefan Reinauer07040582010-04-24 21:24:06 +00001886 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
Stefan Reinauer63217582012-10-29 16:52:36 -07001887 "USAGE:\n" " %s [-h]\n"
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001888 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001889 " -H header_offset Do not search for header; use this offset*\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001890 " -T Output top-aligned memory address\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001891 " -u Accept short data; fill upward/from bottom\n"
1892 " -d Accept short data; fill downward/from top\n"
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001893 " -F Force action\n"
Patrick Georgi5b6bdcc2016-10-13 18:24:10 +02001894 " -g Generate position and alignment arguments\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001895 " -U Unprocessed; don't decompress or make ELF\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001896 " -v Provide verbose output\n"
1897 " -h Display this help message\n\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001898 " --ext-win-base Base of extended decode window in host address\n"
1899 " space(x86 only)\n"
1900 " --ext-win-size Size of extended decode window in host address\n"
1901 " space(x86 only)\n"
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001902 "COMMANDs:\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001903 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001904 " [-c compression] [-b base-address | -a alignment] \\\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301905 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001906 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1907 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001908 "Add a component\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301909 " "
1910 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001911 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001912 " [-c compression] [-b base-address] \\\n"
1913 " (linux specific: [-C cmdline] [-I initrd]) "
Stefan Reinauer63217582012-10-29 16:52:36 -07001914 "Add a payload to the ROM\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001915 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001916 " [-c compression] [-b base] [-S section-to-ignore] \\\n"
Arthur Heymansc78521b32021-06-16 17:16:05 +02001917 " [-a alignment] [-Q|--pow2page] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001918 " [-y|--xip] [--ibb] \\\n"
1919 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001920 "Add a stage to the ROM\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001921 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1922 " [-A hash] -l load-address -e entry-point \\\n"
1923 " [-c compression] [-b base] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001924 "Add a 32bit flat mode binary\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001925 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001926 "Add a raw 64-bit integer value\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301927 " add-master-header [-r image,regions] \\ \n"
1928 " [-j topswap-size] (Intel CPUs only) "
Patrick Georgi59e52b92015-09-10 15:28:27 +02001929 "Add a legacy CBFS master header\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001930 " remove [-r image,regions] -n NAME "
Stefan Reinauer63217582012-10-29 16:52:36 -07001931 "Remove a component\n"
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001932 " compact -r image,regions "
1933 "Defragment CBFS image.\n"
Patrick Georgibd0bb232015-11-20 21:48:18 +01001934 " copy -r image,regions -R source-region "
1935 "Create a copy (duplicate) cbfs instance in fmap\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001936 " create -m ARCH -s size [-b bootblock offset] \\\n"
Patrick Georgi83c2d122015-08-26 10:40:00 +02001937 " [-o CBFS offset] [-H header offset] [-B bootblock] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001938 "Create a legacy ROM file with CBFS master header*\n"
1939 " create -M flashmap [-r list,of,regions,containing,cbfses] "
1940 "Create a new-style partitioned firmware image\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001941 " layout [-w] "
1942 "List mutable (or, with -w, readable) image regions\n"
Julius Werner4bfbabd2020-05-06 17:27:02 -07001943 " print [-r image,regions] [-k] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001944 "Show the contents of the ROM\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001945 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
1946 "Extracts a file from ROM\n"
Patrick Georgid9edb182016-12-06 18:55:26 +01001947 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001948 "Write file into same-size [or larger] raw region\n"
1949 " read [-r fmap-region] -f file "
1950 "Extract raw region contents into binary file\n"
Patrick Georgi12631a42017-09-20 11:59:18 +02001951 " truncate [-r fmap-region] "
1952 "Truncate CBFS and print new size on stdout\n"
Patrick Georgi5d982d72017-09-19 14:39:58 +02001953 " expand [-r fmap-region] "
1954 "Expand CBFS to span entire region\n"
Sol Boucher0e539312015-03-05 15:38:03 -08001955 "OFFSETs:\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001956 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
1957 " in two possible formats: if their value is greater than\n"
Sol Boucher0e539312015-03-05 15:38:03 -08001958 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
1959 " address; otherwise, they are treated as an offset into flash.\n"
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02001960 "ARCHes:\n", name, name
Stefan Reinauer63217582012-10-29 16:52:36 -07001961 );
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02001962 print_supported_architectures();
1963
1964 printf("TYPEs:\n");
Stefan Reinauer07040582010-04-24 21:24:06 +00001965 print_supported_filetypes();
Sol Boucher67a0a862015-03-18 12:36:27 -07001966 printf(
1967 "\n* Note that these actions and switches are only valid when\n"
1968 " working with legacy images whose structure is described\n"
1969 " primarily by a CBFS master header. New-style images, in\n"
1970 " contrast, exclusively make use of an FMAP to describe their\n"
1971 " layout: this must minimally contain an '%s' section\n"
1972 " specifying the location of this FMAP itself and a '%s'\n"
1973 " section describing the primary CBFS. It should also be noted\n"
1974 " that, when working with such images, the -F and -r switches\n"
Julius Werner0fd072d2021-12-13 10:28:29 -08001975 " default to '%s' for convenience, and the -b switch becomes\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001976 " relative to the selected CBFS region's lowest address.\n"
1977 " The one exception to this rule is the top-aligned address,\n"
1978 " which is always relative to the end of the entire image\n"
1979 " rather than relative to the local region; this is true for\n"
1980 " for both input (sufficiently large) and output (-T) data.\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07001981 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
1982 SECTION_NAME_PRIMARY_CBFS
1983 );
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001984}
1985
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001986static bool valid_opt(size_t i, int c)
1987{
1988 /* Check if it is one of the optstrings supported by the command. */
1989 if (strchr(commands[i].optstring, c))
1990 return true;
1991
1992 /*
1993 * Check if it is one of the non-ASCII characters. Currently, the
1994 * non-ASCII characters are only checked against the valid list
1995 * irrespective of the command.
1996 */
1997 if (c >= LONGOPT_START && c < LONGOPT_END)
1998 return true;
1999
2000 return false;
2001}
2002
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002003int main(int argc, char **argv)
2004{
Mathias Krause41c229c2012-07-17 21:17:15 +02002005 size_t i;
Stefan Reinauer63217582012-10-29 16:52:36 -07002006 int c;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002007
2008 if (argc < 3) {
Stefan Reinauer63217582012-10-29 16:52:36 -07002009 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002010 return 1;
2011 }
2012
Sol Bouchere3260a02015-03-25 13:40:08 -07002013 char *image_name = argv[1];
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002014 char *cmd = argv[2];
Stefan Reinauer63217582012-10-29 16:52:36 -07002015 optind += 2;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002016
2017 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2018 if (strcmp(cmd, commands[i].name) != 0)
2019 continue;
Stefan Reinauer63217582012-10-29 16:52:36 -07002020
2021 while (1) {
2022 char *suffix = NULL;
2023 int option_index = 0;
2024
2025 c = getopt_long(argc, argv, commands[i].optstring,
2026 long_options, &option_index);
Nico Huber54073102016-08-01 23:18:29 +02002027 if (c == -1) {
2028 if (optind < argc) {
2029 ERROR("%s: excessive argument -- '%s'"
2030 "\n", argv[0], argv[optind]);
2031 return 1;
2032 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002033 break;
Nico Huber54073102016-08-01 23:18:29 +02002034 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002035
Joel Kitching672d5ad2018-10-11 17:01:30 +08002036 /* Filter out illegal long options */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002037 if (!valid_opt(i, c)) {
2038 ERROR("%s: invalid option -- '%d'\n",
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002039 argv[0], c);
Stefan Reinauer63217582012-10-29 16:52:36 -07002040 c = '?';
2041 }
2042
2043 switch(c) {
2044 case 'n':
Hung-Te Lind1739622013-01-28 14:23:49 +08002045 param.name = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002046 break;
2047 case 't':
2048 if (intfiletype(optarg) != ((uint64_t) - 1))
Hung-Te Lind1739622013-01-28 14:23:49 +08002049 param.type = intfiletype(optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002050 else
Hung-Te Lind1739622013-01-28 14:23:49 +08002051 param.type = strtoul(optarg, NULL, 0);
2052 if (param.type == 0)
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002053 WARN("Unknown type '%s' ignored\n",
Stefan Reinauer63217582012-10-29 16:52:36 -07002054 optarg);
2055 break;
Sol Boucherec424862015-05-07 21:00:05 -07002056 case 'c': {
Patrick Georgiecaa5702017-01-11 18:38:11 +01002057 if (strcmp(optarg, "precompression") == 0) {
2058 param.precompression = 1;
2059 break;
2060 }
Sol Boucherec424862015-05-07 21:00:05 -07002061 int algo = cbfs_parse_comp_algo(optarg);
2062 if (algo >= 0)
2063 param.compression = algo;
Stefan Reinauer63217582012-10-29 16:52:36 -07002064 else
Sol Boucherec424862015-05-07 21:00:05 -07002065 WARN("Unknown compression '%s' ignored.\n",
2066 optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002067 break;
Sol Boucherec424862015-05-07 21:00:05 -07002068 }
Patrick Georgi89f20342015-10-01 15:54:04 +02002069 case 'A': {
Julius Wernerd4775652020-03-13 16:43:34 -07002070 if (!vb2_lookup_hash_alg(optarg, &param.hash)) {
Patrick Georgi89f20342015-10-01 15:54:04 +02002071 ERROR("Unknown hash algorithm '%s'.\n",
2072 optarg);
2073 return 1;
2074 }
2075 break;
2076 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002077 case 'M':
2078 param.fmap = optarg;
2079 break;
2080 case 'r':
2081 param.region_name = optarg;
2082 break;
Patrick Georgibd0bb232015-11-20 21:48:18 +01002083 case 'R':
2084 param.source_region = optarg;
2085 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002086 case 'b':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002087 param.baseaddress_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002088 if (!*optarg || (suffix && *suffix)) {
2089 ERROR("Invalid base address '%s'.\n",
2090 optarg);
2091 return 1;
2092 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002093 // baseaddress may be zero on non-x86, so we
2094 // need an explicit "baseaddress_assigned".
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002095 param.baseaddress_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002096 break;
2097 case 'l':
Nico Huber9ade7172016-08-01 21:37:42 +02002098 param.loadaddress = strtoul(optarg, &suffix, 0);
2099 if (!*optarg || (suffix && *suffix)) {
2100 ERROR("Invalid load address '%s'.\n",
2101 optarg);
2102 return 1;
2103 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002104 break;
2105 case 'e':
Nico Huber9ade7172016-08-01 21:37:42 +02002106 param.entrypoint = strtoul(optarg, &suffix, 0);
2107 if (!*optarg || (suffix && *suffix)) {
2108 ERROR("Invalid entry point '%s'.\n",
2109 optarg);
2110 return 1;
2111 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002112 break;
2113 case 's':
Hung-Te Lind1739622013-01-28 14:23:49 +08002114 param.size = strtoul(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002115 if (!*optarg) {
2116 ERROR("Empty size specified.\n");
2117 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002118 }
Nico Huber9ade7172016-08-01 21:37:42 +02002119 switch (tolower((int)suffix[0])) {
2120 case 'k':
2121 param.size *= 1024;
2122 break;
2123 case 'm':
Hung-Te Lind1739622013-01-28 14:23:49 +08002124 param.size *= 1024 * 1024;
Nico Huber9ade7172016-08-01 21:37:42 +02002125 break;
2126 case '\0':
2127 break;
2128 default:
2129 ERROR("Invalid suffix for size '%s'.\n",
2130 optarg);
2131 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002132 }
Patrick Georgie887ca52014-08-09 17:44:39 +02002133 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002134 case 'B':
Hung-Te Lind1739622013-01-28 14:23:49 +08002135 param.bootblock = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002136 break;
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002137 case 'H':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002138 param.headeroffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002139 if (!*optarg || (suffix && *suffix)) {
2140 ERROR("Invalid header offset '%s'.\n",
2141 optarg);
2142 return 1;
2143 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002144 param.headeroffset_assigned = 1;
2145 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002146 case 'a':
Nico Huber9ade7172016-08-01 21:37:42 +02002147 param.alignment = strtoul(optarg, &suffix, 0);
2148 if (!*optarg || (suffix && *suffix)) {
2149 ERROR("Invalid alignment '%s'.\n",
2150 optarg);
2151 return 1;
2152 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002153 break;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07002154 case 'p':
2155 param.padding = strtoul(optarg, &suffix, 0);
2156 if (!*optarg || (suffix && *suffix)) {
2157 ERROR("Invalid pad size '%s'.\n",
2158 optarg);
2159 return 1;
2160 }
2161 break;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03002162 case 'Q':
2163 param.force_pow2_pagesize = 1;
2164 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002165 case 'o':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002166 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002167 if (!*optarg || (suffix && *suffix)) {
2168 ERROR("Invalid cbfs offset '%s'.\n",
2169 optarg);
2170 return 1;
2171 }
Julius Wernerefcee762014-11-10 13:14:24 -08002172 param.cbfsoffset_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002173 break;
2174 case 'f':
Hung-Te Lind1739622013-01-28 14:23:49 +08002175 param.filename = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002176 break;
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02002177 case 'F':
2178 param.force = 1;
2179 break;
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002180 case 'i':
Nico Huber9ade7172016-08-01 21:37:42 +02002181 param.u64val = strtoull(optarg, &suffix, 0);
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01002182 param.u64val_assigned = 1;
Nico Huber9ade7172016-08-01 21:37:42 +02002183 if (!*optarg || (suffix && *suffix)) {
2184 ERROR("Invalid int parameter '%s'.\n",
2185 optarg);
2186 return 1;
2187 }
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002188 break;
Sol Boucher67a0a862015-03-18 12:36:27 -07002189 case 'u':
2190 param.fill_partial_upward = true;
2191 break;
2192 case 'd':
2193 param.fill_partial_downward = true;
2194 break;
2195 case 'w':
2196 param.show_immutable = true;
Hung-Te Lin215d1d72013-01-29 03:46:02 +08002197 break;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05302198 case 'j':
2199 param.topswap_size = strtol(optarg, NULL, 0);
2200 if (!is_valid_topswap())
2201 return 1;
2202 break;
Rizwan Qureshic1072f22018-06-04 23:02:46 +05302203 case 'q':
2204 param.ucode_region = optarg;
2205 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002206 case 'v':
2207 verbose++;
2208 break;
David Hendricks90ca3b62012-11-16 14:48:22 -08002209 case 'm':
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06002210 param.arch = string_to_arch(optarg);
David Hendricks90ca3b62012-11-16 14:48:22 -08002211 break;
Patrick Georgide36d332013-08-27 20:22:21 +02002212 case 'I':
2213 param.initrd = optarg;
2214 break;
2215 case 'C':
2216 param.cmdline = optarg;
2217 break;
Furquan Shaikh405304a2014-10-30 11:44:20 -07002218 case 'S':
2219 param.ignore_section = optarg;
2220 break;
Aaron Durbin4be16742015-09-15 17:00:23 -05002221 case 'y':
2222 param.stage_xip = true;
2223 break;
Werner Zehe9995f12016-01-14 13:22:37 +01002224 case 'g':
2225 param.autogen_attr = true;
2226 break;
Aaron Durbin5dc628a2016-01-26 15:35:34 -06002227 case 'k':
2228 param.machine_parseable = true;
2229 break;
Joel Kitching21fdd892018-08-09 17:49:52 +08002230 case 'U':
2231 param.unprocessed = true;
2232 break;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01002233 case LONGOPT_IBB:
2234 param.ibb = true;
2235 break;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08002236 case LONGOPT_EXT_WIN_BASE:
2237 param.ext_win_base = strtoul(optarg, &suffix, 0);
2238 if (!*optarg || (suffix && *suffix)) {
2239 ERROR("Invalid ext window base '%s'.\n", optarg);
2240 return 1;
2241 }
2242 break;
2243 case LONGOPT_EXT_WIN_SIZE:
2244 param.ext_win_size = strtoul(optarg, &suffix, 0);
2245 if (!*optarg || (suffix && *suffix)) {
2246 ERROR("Invalid ext window size '%s'.\n", optarg);
2247 return 1;
2248 }
2249 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002250 case 'h':
2251 case '?':
2252 usage(argv[0]);
2253 return 1;
2254 default:
2255 break;
2256 }
2257 }
2258
Sol Bouchere3260a02015-03-25 13:40:08 -07002259 if (commands[i].function == cbfs_create) {
Sol Boucher67a0a862015-03-18 12:36:27 -07002260 if (param.fmap) {
2261 struct buffer flashmap;
2262 if (buffer_from_file(&flashmap, param.fmap))
2263 return 1;
2264 param.image_file = partitioned_file_create(
2265 image_name, &flashmap);
2266 buffer_delete(&flashmap);
2267 } else if (param.size) {
2268 param.image_file = partitioned_file_create_flat(
2269 image_name, param.size);
2270 } else {
2271 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002272 return 1;
2273 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002274 } else {
Vadim Bendebury75599462015-12-09 09:39:31 -08002275 bool write_access = commands[i].modifies_region;
2276
Sol Bouchere3260a02015-03-25 13:40:08 -07002277 param.image_file =
Vadim Bendebury75599462015-12-09 09:39:31 -08002278 partitioned_file_reopen(image_name,
2279 write_access);
Sol Bouchere3260a02015-03-25 13:40:08 -07002280 }
2281 if (!param.image_file)
2282 return 1;
2283
Sol Boucher67a0a862015-03-18 12:36:27 -07002284 unsigned num_regions = 1;
2285 for (const char *list = strchr(param.region_name, ','); list;
2286 list = strchr(list + 1, ','))
2287 ++num_regions;
2288
Sol Bouchere3260a02015-03-25 13:40:08 -07002289 // If the action needs to read an image region, as indicated by
2290 // having accesses_region set in its command struct, that
2291 // region's buffer struct will be stored here and the client
2292 // will receive a pointer to it via param.image_region. It
2293 // need not write the buffer back to the image file itself,
2294 // since this behavior can be requested via its modifies_region
2295 // field. Additionally, it should never free the region buffer,
2296 // as that is performed automatically once it completes.
Sol Boucher67a0a862015-03-18 12:36:27 -07002297 struct buffer image_regions[num_regions];
2298 memset(image_regions, 0, sizeof(image_regions));
Sol Bouchere3260a02015-03-25 13:40:08 -07002299
Sol Boucher67a0a862015-03-18 12:36:27 -07002300 bool seen_primary_cbfs = false;
2301 char region_name_scratch[strlen(param.region_name) + 1];
2302 strcpy(region_name_scratch, param.region_name);
2303 param.region_name = strtok(region_name_scratch, ",");
2304 for (unsigned region = 0; region < num_regions; ++region) {
2305 if (!param.region_name) {
2306 ERROR("Encountered illegal degenerate region name in -r list\n");
2307 ERROR("The image will be left unmodified.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002308 partitioned_file_close(param.image_file);
2309 return 1;
2310 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002311
Sol Boucher67a0a862015-03-18 12:36:27 -07002312 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2313 == 0)
2314 seen_primary_cbfs = true;
Sol Bouchere3260a02015-03-25 13:40:08 -07002315
Sol Boucher67a0a862015-03-18 12:36:27 -07002316 param.image_region = image_regions + region;
2317 if (dispatch_command(commands[i])) {
Sol Bouchere3260a02015-03-25 13:40:08 -07002318 partitioned_file_close(param.image_file);
2319 return 1;
2320 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002321
2322 param.region_name = strtok(NULL, ",");
2323 }
2324
2325 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2326 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2327 SECTION_NAME_PRIMARY_CBFS);
2328 ERROR("The image will be left unmodified.\n");
2329 partitioned_file_close(param.image_file);
2330 return 1;
2331 }
2332
2333 if (commands[i].modifies_region) {
2334 assert(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002335 for (unsigned region = 0; region < num_regions;
2336 ++region) {
2337
2338 if (!partitioned_file_write_region(
2339 param.image_file,
2340 image_regions + region)) {
2341 partitioned_file_close(
2342 param.image_file);
2343 return 1;
2344 }
2345 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002346 }
2347
2348 partitioned_file_close(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002349 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002350 }
2351
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002352 ERROR("Unknown command '%s'.\n", cmd);
Stefan Reinauer63217582012-10-29 16:52:36 -07002353 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002354 return 1;
2355}