blob: b76534057a5edabb7c2a5a7d2b93cfde906b563c [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
250 cb_err_t err = cbfs_walk(cbfs, NULL, NULL, &mhc->cbfs_hash,
251 CBFS_WALK_WRITEBACK_HASH);
252 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
Sol Boucher67a0a862015-03-18 12:36:27 -0700282static bool region_is_flashmap(const char *region)
283{
284 return partitioned_file_region_check_magic(param.image_file, region,
285 FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
286}
287
288/* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
289static bool region_is_modern_cbfs(const char *region)
290{
291 return partitioned_file_region_check_magic(param.image_file, region,
292 CBFS_FILE_MAGIC, strlen(CBFS_FILE_MAGIC));
293}
294
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800295/* This describes a window from the SPI flash address space into the host address space. */
296struct mmap_window {
297 struct region flash_space;
298 struct region host_space;
299};
300
301enum mmap_window_type {
302 X86_DEFAULT_DECODE_WINDOW, /* Decode window just below 4G boundary */
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800303 X86_EXTENDED_DECODE_WINDOW, /* Extended decode window for mapping greater than 16MiB
304 flash */
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800305 MMAP_MAX_WINDOWS,
306};
307
308/* Table of all the decode windows supported by the platform. */
309static struct mmap_window mmap_window_table[MMAP_MAX_WINDOWS];
310
311static void add_mmap_window(enum mmap_window_type idx, size_t flash_offset, size_t host_offset,
312 size_t window_size)
313{
314 if (idx >= MMAP_MAX_WINDOWS) {
315 ERROR("Incorrect mmap window index(%d)\n", idx);
316 return;
317 }
318
319 mmap_window_table[idx].flash_space.offset = flash_offset;
320 mmap_window_table[idx].host_space.offset = host_offset;
321 mmap_window_table[idx].flash_space.size = window_size;
322 mmap_window_table[idx].host_space.size = window_size;
323}
324
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800325#define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
326#define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
327
328static bool create_mmap_windows(void)
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800329{
330 static bool done;
331
332 if (done)
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800333 return done;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800334
335 const size_t image_size = partitioned_file_total_size(param.image_file);
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800336 const size_t std_window_size = MIN(DEFAULT_DECODE_WINDOW_MAX_SIZE, image_size);
337 const size_t std_window_flash_offset = image_size - std_window_size;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800338
339 /*
340 * Default decode window lives just below 4G boundary in host space and maps up to a
341 * maximum of 16MiB. If the window is smaller than 16MiB, the SPI flash window is mapped
342 * at the top of the host window just below 4G.
343 */
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800344 add_mmap_window(X86_DEFAULT_DECODE_WINDOW, std_window_flash_offset,
345 DEFAULT_DECODE_WINDOW_TOP - std_window_size, std_window_size);
346
347 if (param.ext_win_size && (image_size > DEFAULT_DECODE_WINDOW_MAX_SIZE)) {
348 /*
349 * If the platform supports extended window and the SPI flash size is greater
350 * than 16MiB, then create a mapping for the extended window as well.
351 * The assumptions here are:
352 * 1. Top 16MiB is still decoded in the fixed decode window just below 4G
353 * boundary.
354 * 2. Rest of the SPI flash below the top 16MiB is mapped at the top of extended
355 * window. Even though the platform might support a larger extended window, the
356 * SPI flash part used by the mainboard might not be large enough to be mapped
357 * in the entire window. In such cases, the mapping is assumed to be in the top
358 * part of the extended window with the bottom part remaining unused.
359 *
360 * Example:
361 * ext_win_base = 0xF8000000
362 * ext_win_size = 32 * MiB
363 * ext_win_limit = ext_win_base + ext_win_size - 1 = 0xF9FFFFFF
364 *
365 * If SPI flash is 32MiB, then top 16MiB is mapped from 0xFF000000 - 0xFFFFFFFF
366 * whereas the bottom 16MiB is mapped from 0xF9000000 - 0xF9FFFFFF. The extended
367 * window 0xF8000000 - 0xF8FFFFFF remains unused.
368 */
369 const size_t ext_window_mapped_size = MIN(param.ext_win_size,
370 image_size - std_window_size);
371 const size_t ext_window_top = param.ext_win_base + param.ext_win_size;
372 add_mmap_window(X86_EXTENDED_DECODE_WINDOW,
373 std_window_flash_offset - ext_window_mapped_size,
374 ext_window_top - ext_window_mapped_size,
375 ext_window_mapped_size);
376
377 if (region_overlap(&mmap_window_table[X86_EXTENDED_DECODE_WINDOW].host_space,
378 &mmap_window_table[X86_DEFAULT_DECODE_WINDOW].host_space)) {
379 const struct region *ext_region;
380
381 ext_region = &mmap_window_table[X86_EXTENDED_DECODE_WINDOW].host_space;
382 ERROR("Extended window(base=0x%zx, limit=0x%zx) overlaps with default window!\n",
383 region_offset(ext_region), region_end(ext_region));
384
385 return false;
386 }
387 }
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800388
389 done = true;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800390 return done;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800391}
392
393static unsigned int convert_address(const struct region *to, const struct region *from,
394 unsigned int addr)
395{
396 /*
397 * Calculate the offset in the "from" region and use that offset to calculate
398 * corresponding address in the "to" region.
399 */
400 size_t offset = addr - region_offset(from);
401 return region_offset(to) + offset;
402}
403
404enum mmap_addr_type {
405 HOST_SPACE_ADDR,
406 FLASH_SPACE_ADDR,
407};
408
409static int find_mmap_window(enum mmap_addr_type addr_type, unsigned int addr)
410{
411 size_t i;
412
413 for (i = 0; i < ARRAY_SIZE(mmap_window_table); i++) {
414 const struct region *reg;
415
416 if (addr_type == HOST_SPACE_ADDR)
417 reg = &mmap_window_table[i].host_space;
418 else
419 reg = &mmap_window_table[i].flash_space;
420
Kyösti Mälkkib57373b2021-02-13 01:57:14 +0200421 if (region_offset(reg) <= addr &&
422 ((uint64_t)region_offset(reg) + (uint64_t)region_sz(reg) - 1) >= addr)
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800423 return i;
424 }
425
426 return -1;
427}
428
429static unsigned int convert_host_to_flash(const struct buffer *region, unsigned int addr)
430{
431 int idx;
432 const struct region *to, *from;
433
434 idx = find_mmap_window(HOST_SPACE_ADDR, addr);
435 if (idx == -1) {
436 ERROR("Host address(%x) not in any mmap window!\n", addr);
437 return 0;
438 }
439
440 to = &mmap_window_table[idx].flash_space;
441 from = &mmap_window_table[idx].host_space;
442
443 /* region->offset is subtracted because caller expects offset in the given region. */
444 return convert_address(to, from, addr) - region->offset;
445}
446
447static unsigned int convert_flash_to_host(const struct buffer *region, unsigned int addr)
448{
449 int idx;
450 const struct region *to, *from;
451
452 /*
453 * region->offset is added because caller provides offset in the given region. This is
454 * converted to an absolute address in the SPI flash space. This is done before the
455 * conversion as opposed to after in convert_host_to_flash() above because the address
456 * is actually an offset within the region. So, it needs to be converted into an
457 * absolute address in the SPI flash space before converting into an address in host
458 * space.
459 */
460 addr += region->offset;
461 idx = find_mmap_window(FLASH_SPACE_ADDR, addr);
462
463 if (idx == -1) {
464 ERROR("SPI flash address(%x) not in any mmap window!\n", addr);
465 return 0;
466 }
467
468 to = &mmap_window_table[idx].host_space;
469 from = &mmap_window_table[idx].flash_space;
470
471 return convert_address(to, from, addr);
472}
473
474static unsigned int convert_addr_space(const struct buffer *region, unsigned int addr)
Aaron Durbinab00d772016-05-04 16:07:15 -0500475{
476 assert(region);
477
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800478 assert(create_mmap_windows());
Aaron Durbinab00d772016-05-04 16:07:15 -0500479
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800480 if (IS_HOST_SPACE_ADDRESS(addr))
481 return convert_host_to_flash(region, addr);
482 else
483 return convert_flash_to_host(region, addr);
Aaron Durbinab00d772016-05-04 16:07:15 -0500484}
485
486/*
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800487 * This function takes offset value which represents the offset from one end of the region and
488 * converts it to offset from the other end of the region. offset is expected to be positive.
489 */
490static int convert_region_offset(unsigned int offset, uint32_t *region_offset)
Sol Boucher67d59982015-05-07 02:39:22 -0700491{
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800492 size_t size;
Sol Boucher67d59982015-05-07 02:39:22 -0700493
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800494 if (param.size) {
495 size = param.size;
496 } else {
497 assert(param.image_region);
498 size = param.image_region->size;
Patrick Georgi97311192015-12-07 23:14:07 +0100499 }
500
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800501 if (size < offset) {
502 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size, offset);
503 return 1;
504 }
505
506 *region_offset = size - offset;
507 return 0;
Sol Boucher67d59982015-05-07 02:39:22 -0700508}
509
Julius Werner6ddacd62021-12-03 14:46:53 -0800510static int do_cbfs_locate(uint32_t *cbfs_addr, size_t data_size)
Aaron Durbin4be16742015-09-15 17:00:23 -0500511{
Julius Werner6ddacd62021-12-03 14:46:53 -0800512 uint32_t metadata_size = 0;
513
Aaron Durbin4be16742015-09-15 17:00:23 -0500514 if (!param.name) {
515 ERROR("You need to specify -n/--name.\n");
516 return 1;
517 }
518
519 struct cbfs_image image;
520 if (cbfs_image_from_buffer(&image, param.image_region,
521 param.headeroffset))
522 return 1;
523
524 if (cbfs_get_entry(&image, param.name))
525 WARN("'%s' already in CBFS.\n", param.name);
526
Aaron Durbinfacf1492017-12-18 14:50:22 -0700527 if (!data_size) {
Julius Werner772714d2021-12-13 10:16:44 -0800528 ERROR("File '%s' is empty?\n", param.name);
529 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -0500530 }
531
Arthur Heymansc78521b32021-06-16 17:16:05 +0200532 /* Compute required page size */
Kyösti Mälkkic36469e2020-05-28 23:12:02 +0300533 if (param.force_pow2_pagesize) {
Arthur Heymansc78521b32021-06-16 17:16:05 +0200534 param.pagesize = 1;
535 while (param.pagesize < data_size)
536 param.pagesize <<= 1;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +0300537 DEBUG("Page size is %d (0x%x)\n", param.pagesize, param.pagesize);
538 }
539
Aaron Durbin4be16742015-09-15 17:00:23 -0500540 /* Include cbfs_file size along with space for with name. */
541 metadata_size += cbfs_calculate_file_header_size(param.name);
Werner Zehe9995f12016-01-14 13:22:37 +0100542 /* Adjust metadata_size if additional attributes were added */
543 if (param.autogen_attr) {
544 if (param.alignment)
545 metadata_size += sizeof(struct cbfs_file_attr_align);
546 if (param.baseaddress_assigned || param.stage_xip)
547 metadata_size += sizeof(struct cbfs_file_attr_position);
548 }
Julius Wernerc24db002020-11-18 18:00:31 -0800549 if (param.precompression || param.compression != CBFS_COMPRESS_NONE)
550 metadata_size += sizeof(struct cbfs_file_attr_compression);
Julius Werner6ddacd62021-12-03 14:46:53 -0800551 if (param.type == CBFS_TYPE_STAGE)
552 metadata_size += sizeof(struct cbfs_file_attr_stageheader);
Aaron Durbin4be16742015-09-15 17:00:23 -0500553
Werner Zehc7b2b7c2016-01-22 19:43:01 +0100554 /* Take care of the hash attribute if it is used */
555 if (param.hash != VB2_HASH_INVALID)
Julius Wernerd4775652020-03-13 16:43:34 -0700556 metadata_size += cbfs_file_attr_hash_size(param.hash);
Werner Zehc7b2b7c2016-01-22 19:43:01 +0100557
Aaron Durbinfacf1492017-12-18 14:50:22 -0700558 int32_t address = cbfs_locate_entry(&image, data_size, param.pagesize,
Aaron Durbin4be16742015-09-15 17:00:23 -0500559 param.alignment, metadata_size);
Aaron Durbin4be16742015-09-15 17:00:23 -0500560
Julius Wernerc24db002020-11-18 18:00:31 -0800561 if (address < 0) {
Julius Werner772714d2021-12-13 10:16:44 -0800562 ERROR("'%s'(%u + %zu) can't fit in CBFS for page-size %#x, align %#x.\n",
563 param.name, metadata_size, data_size, param.pagesize, param.alignment);
Aaron Durbin4be16742015-09-15 17:00:23 -0500564 return 1;
565 }
566
Aaron Durbin4be16742015-09-15 17:00:23 -0500567 *cbfs_addr = address;
568 return 0;
569}
570
Patrick Georgif8204972015-08-11 15:16:24 +0200571typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +0200572 struct cbfs_file *header);
Stefan Reinauer63217582012-10-29 16:52:36 -0700573
Sol Bouchere3260a02015-03-25 13:40:08 -0700574static int cbfs_add_integer_component(const char *name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200575 uint64_t u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800576 uint32_t offset,
577 uint32_t headeroffset) {
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200578 struct cbfs_image image;
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200579 struct cbfs_file *header = NULL;
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200580 struct buffer buffer;
581 int i, ret = 1;
582
583 if (!name) {
584 ERROR("You need to specify -n/--name.\n");
585 return 1;
586 }
587
588 if (buffer_create(&buffer, 8, name) != 0)
589 return 1;
590
591 for (i = 0; i < 8; i++)
592 buffer.data[i] = (u64val >> i*8) & 0xff;
593
Sol Bouchere3260a02015-03-25 13:40:08 -0700594 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset)) {
595 ERROR("Selected image region is not a CBFS.\n");
596 goto done;
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200597 }
598
599 if (cbfs_get_entry(&image, name)) {
600 ERROR("'%s' already in ROM image.\n", name);
601 goto done;
602 }
603
Julius Wernerd4775652020-03-13 16:43:34 -0700604 header = cbfs_create_file_header(CBFS_TYPE_RAW,
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200605 buffer.size, name);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700606
607 enum vb2_hash_algorithm algo = get_mh_cache()->cbfs_hash.algo;
608 if (algo != VB2_HASH_INVALID)
609 if (cbfs_add_file_hash(header, &buffer, algo)) {
610 ERROR("couldn't add hash for '%s'\n", name);
611 goto done;
612 }
613
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100614 if (cbfs_add_entry(&image, &buffer, offset, header, 0) != 0) {
Sol Boucher0e539312015-03-05 15:38:03 -0800615 ERROR("Failed to add %llu into ROM image as '%s'.\n",
616 (long long unsigned)u64val, name);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200617 goto done;
618 }
619
Julius Werner4bfbabd2020-05-06 17:27:02 -0700620 ret = maybe_update_metadata_hash(&image);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200621
622done:
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200623 free(header);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200624 buffer_delete(&buffer);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200625 return ret;
626}
627
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530628static int is_valid_topswap(void)
629{
630 switch (param.topswap_size) {
631 case (64 * KiB):
632 case (128 * KiB):
633 case (256 * KiB):
634 case (512 * KiB):
635 case (1 * MiB):
636 break;
637 default:
638 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
639 param.topswap_size);
640 return 0;
641 }
642 return 1;
643}
644
645static void fill_header_offset(void *location, uint32_t offset)
646{
Joel Kitching672d5ad2018-10-11 17:01:30 +0800647 // TODO: When we have a BE target, we'll need to store this as BE
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530648 write_le32(location, offset);
649}
650
651static int update_master_header_loc_topswap(struct cbfs_image *image,
652 void *h_loc, uint32_t header_offset)
653{
654 struct cbfs_file *entry;
655 void *ts_h_loc = h_loc;
656
657 entry = cbfs_get_entry(image, "bootblock");
658 if (entry == NULL) {
659 ERROR("Bootblock not in ROM image?!?\n");
660 return 1;
661 }
662
663 /*
664 * Check if the existing topswap boundary matches with
665 * the one provided.
666 */
Alex James02001a382021-12-19 16:41:59 -0600667 if (param.topswap_size != be32toh(entry->len)/2) {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530668 ERROR("Top swap boundary does not match\n");
669 return 1;
670 }
671
672 ts_h_loc -= param.topswap_size;
673 fill_header_offset(ts_h_loc, header_offset);
674
675 return 0;
676}
677
Patrick Georgi59e52b92015-09-10 15:28:27 +0200678static int cbfs_add_master_header(void)
679{
680 const char * const name = "cbfs master header";
681 struct cbfs_image image;
682 struct cbfs_file *header = NULL;
683 struct buffer buffer;
684 int ret = 1;
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600685 size_t offset;
686 size_t size;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530687 void *h_loc;
Patrick Georgi59e52b92015-09-10 15:28:27 +0200688
689 if (cbfs_image_from_buffer(&image, param.image_region,
690 param.headeroffset)) {
691 ERROR("Selected image region is not a CBFS.\n");
692 return 1;
693 }
694
695 if (cbfs_get_entry(&image, name)) {
696 ERROR("'%s' already in ROM image.\n", name);
697 return 1;
698 }
699
700 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
701 return 1;
702
703 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
Alex James02001a382021-12-19 16:41:59 -0600704 h->magic = htobe32(CBFS_HEADER_MAGIC);
705 h->version = htobe32(CBFS_HEADER_VERSION);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200706 /* The 4 bytes are left out for two reasons:
707 * 1. the cbfs master header pointer resides there
708 * 2. some cbfs implementations assume that an image that resides
709 * below 4GB has a bootblock and get confused when the end of the
710 * image is at 4GB == 0.
711 */
Alex James02001a382021-12-19 16:41:59 -0600712 h->bootblocksize = htobe32(4);
713 h->align = htobe32(CBFS_ALIGNMENT);
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600714 /* The offset and romsize fields within the master header are absolute
715 * values within the boot media. As such, romsize needs to relfect
716 * the end 'offset' for a CBFS. To achieve that the current buffer
717 * representing the CBFS region's size is added to the offset of
718 * the region within a larger image.
Patrick Georgi59e52b92015-09-10 15:28:27 +0200719 */
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600720 offset = buffer_get(param.image_region) -
721 buffer_get_original_backing(param.image_region);
722 size = buffer_size(param.image_region);
Alex James02001a382021-12-19 16:41:59 -0600723 h->romsize = htobe32(size + offset);
724 h->offset = htobe32(offset);
725 h->architecture = htobe32(CBFS_ARCHITECTURE_UNKNOWN);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200726
Julius Werner4bfbabd2020-05-06 17:27:02 -0700727 /* Never add a hash attribute to the master header. */
Julius Wernerd4775652020-03-13 16:43:34 -0700728 header = cbfs_create_file_header(CBFS_TYPE_CBFSHEADER,
Patrick Georgi59e52b92015-09-10 15:28:27 +0200729 buffer_size(&buffer), name);
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100730 if (cbfs_add_entry(&image, &buffer, 0, header, 0) != 0) {
Patrick Georgi59e52b92015-09-10 15:28:27 +0200731 ERROR("Failed to add cbfs master header into ROM image.\n");
732 goto done;
733 }
734
735 struct cbfs_file *entry;
736 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
737 ERROR("'%s' not in ROM image?!?\n", name);
738 goto done;
739 }
740
741 uint32_t header_offset = CBFS_SUBHEADER(entry) -
742 buffer_get(&image.buffer);
743 header_offset = -(buffer_size(&image.buffer) - header_offset);
744
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530745 h_loc = (void *)(buffer_get(&image.buffer) +
746 buffer_size(&image.buffer) - 4);
747 fill_header_offset(h_loc, header_offset);
748 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800749 * If top swap present, update the header
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530750 * location in secondary bootblock
751 */
752 if (param.topswap_size) {
753 if (update_master_header_loc_topswap(&image, h_loc,
754 header_offset))
755 return 1;
756 }
Patrick Georgi59e52b92015-09-10 15:28:27 +0200757
Julius Werner4bfbabd2020-05-06 17:27:02 -0700758 ret = maybe_update_metadata_hash(&image);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200759
760done:
761 free(header);
762 buffer_delete(&buffer);
763 return ret;
764}
765
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530766static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
767{
768 size_t bb_buf_size = buffer_size(buffer);
769
770 if (bb_buf_size > param.topswap_size) {
771 ERROR("Bootblock bigger than the topswap boundary\n");
772 ERROR("size = %zd, ts = %d\n", bb_buf_size,
773 param.topswap_size);
774 return 1;
775 }
776
777 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800778 * Allocate topswap_size*2 bytes for bootblock to
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530779 * accommodate the second bootblock.
780 */
781 struct buffer new_bootblock, bb1, bb2;
782 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
783 buffer->name))
784 return 1;
785
786 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
787 bb_buf_size);
788 buffer_splice(&bb2, &new_bootblock,
789 buffer_size(&new_bootblock) - bb_buf_size,
790 bb_buf_size);
791
Joel Kitching672d5ad2018-10-11 17:01:30 +0800792 /* Copy to first bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530793 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
Joel Kitching672d5ad2018-10-11 17:01:30 +0800794 /* Copy to second bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530795 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
796
797 buffer_delete(buffer);
798 buffer_clone(buffer, &new_bootblock);
799
Joel Kitching672d5ad2018-10-11 17:01:30 +0800800 /* Update the location (offset) of bootblock in the region */
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800801 return convert_region_offset(buffer_size(buffer), offset);
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530802}
803
Sol Bouchere3260a02015-03-25 13:40:08 -0700804static int cbfs_add_component(const char *filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800805 const char *name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800806 uint32_t headeroffset,
Stefan Reinauer2dd161f2015-03-04 00:55:03 +0100807 convert_buffer_t convert)
808{
Julius Werner6ddacd62021-12-03 14:46:53 -0800809 /*
810 * The steps used to determine the final placement offset in CBFS, in order:
811 *
Julius Werner20ad3652021-12-09 13:20:59 -0800812 * 1. If --base-address was passed, that value is used. If it was passed in the host
813 * address space, convert it to flash address space. (After that, |*offset| is always
814 * in the flash address space.)
Julius Werner6ddacd62021-12-03 14:46:53 -0800815 *
816 * 2. The convert() function may write a location back to |offset|, usually by calling
817 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
818 * the CBFS file in its final form (after any compression and conversion).
819 *
820 * 3. If --align was passed and the offset is still undecided at this point,
821 * do_cbfs_locate() is called to find an appropriately aligned location.
822 *
823 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
824 * location that fits.
825 */
Julius Wernerc24db002020-11-18 18:00:31 -0800826 uint32_t offset = param.baseaddress_assigned ? param.baseaddress : 0;
Julius Werner6ddacd62021-12-03 14:46:53 -0800827 size_t len_align = 0;
Julius Wernerc24db002020-11-18 18:00:31 -0800828
829 if (param.alignment && param.baseaddress_assigned) {
830 ERROR("Cannot specify both alignment and base address\n");
831 return 1;
832 }
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100833
Julius Werner6ddacd62021-12-03 14:46:53 -0800834 if (param.stage_xip && param.compression != CBFS_COMPRESS_NONE) {
835 ERROR("Cannot specify compression for XIP.\n");
836 return 1;
837 }
838
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800839 if (!filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800840 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700841 return 1;
842 }
843
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800844 if (!name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800845 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700846 return 1;
847 }
848
Julius Werner6ddacd62021-12-03 14:46:53 -0800849 if (param.type == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800850 ERROR("You need to specify a valid -t/--type.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700851 return 1;
852 }
853
Sol Bouchere3260a02015-03-25 13:40:08 -0700854 struct cbfs_image image;
Sol Boucher67a0a862015-03-18 12:36:27 -0700855 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800856 return 1;
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800857
Patrick Georgi4e54bf92015-08-11 14:35:39 +0200858 if (cbfs_get_entry(&image, name)) {
859 ERROR("'%s' already in ROM image.\n", name);
860 return 1;
861 }
862
Sol Bouchere3260a02015-03-25 13:40:08 -0700863 struct buffer buffer;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800864 if (buffer_from_file(&buffer, filename) != 0) {
865 ERROR("Could not load file '%s'.\n", filename);
Stefan Reinauer63217582012-10-29 16:52:36 -0700866 return 1;
867 }
868
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200869 struct cbfs_file *header =
Julius Werner6ddacd62021-12-03 14:46:53 -0800870 cbfs_create_file_header(param.type, buffer.size, name);
Patrick Georgi4110abc2015-08-11 15:10:33 +0200871
Julius Werner4bfbabd2020-05-06 17:27:02 -0700872 /* Bootblock and CBFS header should never have file hashes. When adding
873 the bootblock it is important that we *don't* look up the metadata
874 hash yet (before it is added) or we'll cache an outdated result. */
Julius Werner6ddacd62021-12-03 14:46:53 -0800875 if (param.type != CBFS_TYPE_BOOTBLOCK && param.type != CBFS_TYPE_CBFSHEADER) {
Julius Werner4bfbabd2020-05-06 17:27:02 -0700876 enum vb2_hash_algorithm mh_algo = get_mh_cache()->cbfs_hash.algo;
877 if (mh_algo != VB2_HASH_INVALID && param.hash != mh_algo) {
878 if (param.hash == VB2_HASH_INVALID) {
879 param.hash = mh_algo;
880 } else {
881 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
882 vb2_get_hash_algorithm_name(param.hash),
883 vb2_get_hash_algorithm_name(mh_algo));
884 goto error;
885 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200886 }
Julius Wernerc24db002020-11-18 18:00:31 -0800887 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200888
Julius Wernerc24db002020-11-18 18:00:31 -0800889 /*
890 * Check if Intel CPU topswap is specified this will require a
891 * second bootblock to be added.
892 */
Julius Werner6ddacd62021-12-03 14:46:53 -0800893 if (param.type == CBFS_TYPE_BOOTBLOCK && param.topswap_size)
Julius Wernerc24db002020-11-18 18:00:31 -0800894 if (add_topswap_bootblock(&buffer, &offset))
895 goto error;
896
Julius Werner20ad3652021-12-09 13:20:59 -0800897 /* With --base-address we allow host space addresses -- if so, convert it here. */
898 if (IS_HOST_SPACE_ADDRESS(offset))
899 offset = convert_addr_space(param.image_region, offset);
900
Julius Wernerc24db002020-11-18 18:00:31 -0800901 if (convert && convert(&buffer, &offset, header) != 0) {
902 ERROR("Failed to parse file '%s'.\n", filename);
903 goto error;
904 }
905
Julius Werner6ddacd62021-12-03 14:46:53 -0800906 /* This needs to run after convert() to take compression into account. */
907 if (!offset && param.alignment)
Julius Werner772714d2021-12-13 10:16:44 -0800908 if (do_cbfs_locate(&offset, buffer_size(&buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -0800909 goto error;
910
911 /* This needs to run after convert() to hash the actual final file data. */
Julius Wernerc24db002020-11-18 18:00:31 -0800912 if (param.hash != VB2_HASH_INVALID &&
913 cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
914 ERROR("couldn't add hash for '%s'\n", name);
915 goto error;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700916 }
917
Werner Zehe9995f12016-01-14 13:22:37 +0100918 if (param.autogen_attr) {
919 /* Add position attribute if assigned */
920 if (param.baseaddress_assigned || param.stage_xip) {
921 struct cbfs_file_attr_position *attrs =
922 (struct cbfs_file_attr_position *)
923 cbfs_add_file_attr(header,
924 CBFS_FILE_ATTR_TAG_POSITION,
925 sizeof(struct cbfs_file_attr_position));
926 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800927 goto error;
Alex James02001a382021-12-19 16:41:59 -0600928 attrs->position = htobe32(offset);
Werner Zehe9995f12016-01-14 13:22:37 +0100929 }
930 /* Add alignment attribute if used */
931 if (param.alignment) {
932 struct cbfs_file_attr_align *attrs =
933 (struct cbfs_file_attr_align *)
934 cbfs_add_file_attr(header,
935 CBFS_FILE_ATTR_TAG_ALIGNMENT,
936 sizeof(struct cbfs_file_attr_align));
937 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800938 goto error;
Alex James02001a382021-12-19 16:41:59 -0600939 attrs->alignment = htobe32(param.alignment);
Werner Zehe9995f12016-01-14 13:22:37 +0100940 }
941 }
942
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100943 if (param.ibb) {
944 /* Mark as Initial Boot Block */
945 struct cbfs_file_attribute *attrs = cbfs_add_file_attr(header,
946 CBFS_FILE_ATTR_TAG_IBB,
947 sizeof(struct cbfs_file_attribute));
948 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800949 goto error;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100950 /* For Intel TXT minimum align is 16 */
951 len_align = 16;
952 }
953
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700954 if (param.padding) {
955 const uint32_t hs = sizeof(struct cbfs_file_attribute);
Julius Werner5779ca72020-11-20 16:12:40 -0800956 uint32_t size = ALIGN_UP(MAX(hs, param.padding),
957 CBFS_ATTRIBUTE_ALIGN);
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700958 INFO("Padding %d bytes\n", size);
959 struct cbfs_file_attribute *attr =
960 (struct cbfs_file_attribute *)cbfs_add_file_attr(
961 header, CBFS_FILE_ATTR_TAG_PADDING,
962 size);
963 if (attr == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800964 goto error;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700965 }
966
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100967 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800968 ERROR("Failed to add '%s' into ROM image.\n", filename);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700969 goto error;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800970 }
971
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200972 free(header);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800973 buffer_delete(&buffer);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700974
975 return maybe_update_metadata_hash(&image) || maybe_update_fmap_hash();
976
977error:
978 free(header);
979 buffer_delete(&buffer);
980 return 1;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +0000981}
982
Daisuke Nojiri8984a632015-07-09 15:07:45 -0700983static int cbfstool_convert_raw(struct buffer *buffer,
984 unused uint32_t *offset, struct cbfs_file *header)
985{
986 char *compressed;
Patrick Georgiecaa5702017-01-11 18:38:11 +0100987 int decompressed_size, compressed_size;
988 comp_func_ptr compress;
Daisuke Nojiri8984a632015-07-09 15:07:45 -0700989
Patrick Georgiecaa5702017-01-11 18:38:11 +0100990 decompressed_size = buffer->size;
991 if (param.precompression) {
Nico Huber607796a2017-01-17 13:01:25 +0100992 param.compression = read_le32(buffer->data);
993 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
Patrick Georgiecaa5702017-01-11 18:38:11 +0100994 compressed_size = buffer->size - 8;
995 compressed = malloc(compressed_size);
996 if (!compressed)
Daisuke Nojiri8984a632015-07-09 15:07:45 -0700997 return -1;
Patrick Georgiecaa5702017-01-11 18:38:11 +0100998 memcpy(compressed, buffer->data + 8, compressed_size);
999 } else {
Julius Werner105cdf52020-10-23 19:19:32 -07001000 if (param.compression == CBFS_COMPRESS_NONE)
1001 goto out;
1002
Patrick Georgiecaa5702017-01-11 18:38:11 +01001003 compress = compression_function(param.compression);
1004 if (!compress)
1005 return -1;
1006 compressed = calloc(buffer->size, 1);
1007 if (!compressed)
1008 return -1;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001009
Patrick Georgiecaa5702017-01-11 18:38:11 +01001010 if (compress(buffer->data, buffer->size,
1011 compressed, &compressed_size)) {
1012 WARN("Compression failed - disabled\n");
1013 free(compressed);
Julius Werner105cdf52020-10-23 19:19:32 -07001014 goto out;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001015 }
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001016 }
Patrick Georgiecaa5702017-01-11 18:38:11 +01001017
1018 struct cbfs_file_attr_compression *attrs =
1019 (struct cbfs_file_attr_compression *)
1020 cbfs_add_file_attr(header,
1021 CBFS_FILE_ATTR_TAG_COMPRESSION,
1022 sizeof(struct cbfs_file_attr_compression));
1023 if (attrs == NULL) {
1024 free(compressed);
1025 return -1;
1026 }
Alex James02001a382021-12-19 16:41:59 -06001027 attrs->compression = htobe32(param.compression);
1028 attrs->decompressed_size = htobe32(decompressed_size);
Patrick Georgiecaa5702017-01-11 18:38:11 +01001029
1030 free(buffer->data);
1031 buffer->data = compressed;
1032 buffer->size = compressed_size;
1033
Julius Werner105cdf52020-10-23 19:19:32 -07001034out:
Alex James02001a382021-12-19 16:41:59 -06001035 header->len = htobe32(buffer->size);
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001036 return 0;
1037}
1038
Aaron Durbin8a414a02015-10-01 17:02:45 -05001039static int cbfstool_convert_fsp(struct buffer *buffer,
1040 uint32_t *offset, struct cbfs_file *header)
1041{
1042 uint32_t address;
1043 struct buffer fsp;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001044
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001045 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001046 * There are 4 different cases here:
1047 *
1048 * 1. --xip and --base-address: we need to place the binary at the given base address
Julius Werner20ad3652021-12-09 13:20:59 -08001049 * in the CBFS image and relocate it to that address. *offset was already filled in,
1050 * but we need to convert it to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001051 *
1052 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1053 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
Julius Werner20ad3652021-12-09 13:20:59 -08001054 * This also needs to be converted to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001055 *
1056 * 3. No --xip but a --base-address: special case where --base-address does not have its
1057 * normal meaning, instead we use it as the relocation target address. We explicitly
1058 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1059 *
1060 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1061 * not be relocated. Just chain directly to convert_raw() for compression.
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001062 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001063
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001064 if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001065 if (!param.baseaddress_assigned) {
1066 param.alignment = 4*1024;
Julius Werner772714d2021-12-13 10:16:44 -08001067 if (do_cbfs_locate(offset, buffer_size(buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -08001068 return -1;
1069 }
Julius Werner20ad3652021-12-09 13:20:59 -08001070 assert(!IS_HOST_SPACE_ADDRESS(*offset));
1071 address = convert_addr_space(param.image_region, *offset);
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001072 } else {
Aaron Durbin493ec922016-05-16 15:18:45 -05001073 if (param.baseaddress_assigned == 0) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001074 INFO("Honoring pre-linked FSP module, no relocation.\n");
1075 return cbfstool_convert_raw(buffer, offset, header);
Aaron Durbin493ec922016-05-16 15:18:45 -05001076 } else {
1077 address = param.baseaddress;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001078 *offset = 0;
Furquan Shaikhc3bb6922020-06-09 21:20:27 -07001079 }
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001080 }
Aaron Durbin8a414a02015-10-01 17:02:45 -05001081
1082 /* Create a copy of the buffer to attempt relocation. */
1083 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
1084 return -1;
1085
1086 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
1087
1088 /* Replace the buffer contents w/ the relocated ones on success. */
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001089 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
1090 > 0) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001091 buffer_delete(buffer);
1092 buffer_clone(buffer, &fsp);
1093 } else {
1094 buffer_delete(&fsp);
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001095 WARN("Invalid FSP variant.\n");
Aaron Durbin8a414a02015-10-01 17:02:45 -05001096 }
1097
1098 /* Let the raw path handle all the cbfs metadata logic. */
1099 return cbfstool_convert_raw(buffer, offset, header);
1100}
1101
Patrick Georgif8204972015-08-11 15:16:24 +02001102static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001103 struct cbfs_file *header)
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001104{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001105 struct buffer output;
Julius Wernerc24db002020-11-18 18:00:31 -08001106 size_t data_size;
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001107 int ret;
Aaron Durbin4be16742015-09-15 17:00:23 -05001108
Julius Wernerc24db002020-11-18 18:00:31 -08001109 if (elf_program_file_size(buffer, &data_size) < 0) {
1110 ERROR("Could not obtain ELF size\n");
1111 return 1;
1112 }
1113
1114 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001115 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1116 * here. That is okay because XIP stages may not be compressed, so their size cannot
1117 * change anymore at a later point.
Julius Wernerc24db002020-11-18 18:00:31 -08001118 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001119 if (param.stage_xip &&
1120 do_cbfs_locate(offset, data_size)) {
Julius Wernerc24db002020-11-18 18:00:31 -08001121 ERROR("Could not find location for stage.\n");
1122 return 1;
1123 }
1124
Julius Werner81dc20e2020-10-15 17:37:57 -07001125 struct cbfs_file_attr_stageheader *stageheader = (void *)
1126 cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
1127 sizeof(struct cbfs_file_attr_stageheader));
1128 if (!stageheader)
1129 return -1;
1130
Aaron Durbin4be16742015-09-15 17:00:23 -05001131 if (param.stage_xip) {
Julius Werner20ad3652021-12-09 13:20:59 -08001132 uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
1133 assert(IS_HOST_SPACE_ADDRESS(host_space_address));
1134 ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
1135 param.ignore_section, stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001136 } else {
Julius Werner81dc20e2020-10-15 17:37:57 -07001137 ret = parse_elf_to_stage(buffer, &output, param.ignore_section,
1138 stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001139 }
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001140 if (ret != 0)
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001141 return -1;
Julius Werner81dc20e2020-10-15 17:37:57 -07001142
1143 /* Store a hash of original uncompressed stage to compare later. */
1144 size_t decmp_size = buffer_size(&output);
1145 uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
1146
1147 /* Chain to base conversion routine to handle compression. */
1148 ret = cbfstool_convert_raw(&output, offset, header);
1149 if (ret != 0)
1150 goto fail;
1151
1152 /* Special care must be taken for LZ4-compressed stages that the BSS is
1153 large enough to provide scratch space for in-place decompression. */
1154 if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
Alex James02001a382021-12-19 16:41:59 -06001155 size_t memlen = be32toh(stageheader->memlen);
Julius Werner81dc20e2020-10-15 17:37:57 -07001156 size_t compressed_size = buffer_size(&output);
1157 uint8_t *compare_buffer = malloc(memlen);
1158 uint8_t *start = compare_buffer + memlen - compressed_size;
1159 if (!compare_buffer) {
1160 ERROR("Out of memory\n");
1161 goto fail;
1162 }
1163 memcpy(start, buffer_get(&output), compressed_size);
1164 ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
1165 if (ret == 0) {
1166 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1167 free(compare_buffer);
1168 goto fail;
1169 } else if (ret != (int)decmp_size ||
1170 decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
1171 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1172 free(compare_buffer);
1173 goto fail;
1174 }
1175 free(compare_buffer);
1176 }
1177
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001178 buffer_delete(buffer);
Julius Werner81dc20e2020-10-15 17:37:57 -07001179 buffer_clone(buffer, &output);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001180 return 0;
Julius Werner81dc20e2020-10-15 17:37:57 -07001181
1182fail:
1183 buffer_delete(&output);
1184 return -1;
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001185}
1186
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001187static int cbfstool_convert_mkpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001188 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001189{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001190 struct buffer output;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001191 int ret;
Joel Kitching672d5ad2018-10-11 17:01:30 +08001192 /* Per default, try and see if payload is an ELF binary */
Sol Boucher65336712015-05-07 21:00:05 -07001193 ret = parse_elf_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001194
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001195 /* If it's not an ELF, see if it's a FIT */
1196 if (ret != 0) {
1197 ret = parse_fit_to_payload(buffer, &output, param.compression);
1198 if (ret == 0)
Alex James02001a382021-12-19 16:41:59 -06001199 header->type = htobe32(CBFS_TYPE_FIT);
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001200 }
1201
1202 /* If it's not an FIT, see if it's a UEFI FV */
Stefan Reinauer543a6822013-02-04 15:39:13 -08001203 if (ret != 0)
Sol Boucher65336712015-05-07 21:00:05 -07001204 ret = parse_fv_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001205
Patrick Georgide36d332013-08-27 20:22:21 +02001206 /* If it's neither ELF nor UEFI Fv, try bzImage */
1207 if (ret != 0)
1208 ret = parse_bzImage_to_payload(buffer, &output,
Sol Boucher65336712015-05-07 21:00:05 -07001209 param.initrd, param.cmdline, param.compression);
Patrick Georgide36d332013-08-27 20:22:21 +02001210
Stefan Reinauer543a6822013-02-04 15:39:13 -08001211 /* Not a supported payload type */
1212 if (ret != 0) {
1213 ERROR("Not a supported payload type (ELF / FV).\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001214 buffer_delete(buffer);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001215 return -1;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001216 }
1217
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001218 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001219 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001220 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001221 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001222 return 0;
1223}
1224
1225static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001226 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001227{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001228 struct buffer output;
1229 if (parse_flat_binary_to_payload(buffer, &output,
1230 param.loadaddress,
1231 param.entrypoint,
Sol Boucher65336712015-05-07 21:00:05 -07001232 param.compression) != 0) {
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001233 return -1;
1234 }
1235 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001236 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001237 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001238 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001239 return 0;
1240}
1241
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001242static int cbfs_add(void)
1243{
Julius Wernerc24db002020-11-18 18:00:31 -08001244 convert_buffer_t convert = cbfstool_convert_raw;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001245
Julius Wernerd4775652020-03-13 16:43:34 -07001246 if (param.type == CBFS_TYPE_FSP) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001247 convert = cbfstool_convert_fsp;
Julius Werner6ddacd62021-12-03 14:46:53 -08001248 } else if (param.type == CBFS_TYPE_STAGE) {
1249 ERROR("stages can only be added with cbfstool add-stage\n");
1250 return 1;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001251 } else if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001252 ERROR("cbfstool add supports xip only for FSP component type\n");
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001253 return 1;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001254 }
1255
Sol Bouchere3260a02015-03-25 13:40:08 -07001256 return cbfs_add_component(param.filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001257 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001258 param.headeroffset,
Aaron Durbin8a414a02015-10-01 17:02:45 -05001259 convert);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001260}
1261
Stefan Reinauer63217582012-10-29 16:52:36 -07001262static int cbfs_add_stage(void)
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001263{
Julius Werner6ddacd62021-12-03 14:46:53 -08001264 if (param.stage_xip && param.baseaddress_assigned) {
1265 ERROR("Cannot specify base address for XIP.\n");
1266 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -05001267 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001268 param.type = CBFS_TYPE_STAGE;
Aaron Durbin4be16742015-09-15 17:00:23 -05001269
Sol Bouchere3260a02015-03-25 13:40:08 -07001270 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001271 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001272 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001273 cbfstool_convert_mkstage);
1274}
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001275
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001276static int cbfs_add_payload(void)
1277{
Julius Werner6ddacd62021-12-03 14:46:53 -08001278 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001279 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001280 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001281 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001282 cbfstool_convert_mkpayload);
Stefan Reinauer63217582012-10-29 16:52:36 -07001283}
1284
1285static int cbfs_add_flat_binary(void)
1286{
Hung-Te Lind1739622013-01-28 14:23:49 +08001287 if (param.loadaddress == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001288 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001289 "-l/--load-address.\n");
1290 return 1;
1291 }
Hung-Te Lind1739622013-01-28 14:23:49 +08001292 if (param.entrypoint == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001293 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001294 "-e/--entry-point.\n");
1295 return 1;
1296 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001297 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001298 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001299 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001300 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001301 cbfstool_convert_mkflatpayload);
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001302}
1303
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001304static int cbfs_add_integer(void)
1305{
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01001306 if (!param.u64val_assigned) {
1307 ERROR("You need to specify a value to write.\n");
1308 return 1;
1309 }
Sol Bouchere3260a02015-03-25 13:40:08 -07001310 return cbfs_add_integer_component(param.name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001311 param.u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001312 param.baseaddress,
1313 param.headeroffset);
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001314}
1315
Stefan Reinauer63217582012-10-29 16:52:36 -07001316static int cbfs_remove(void)
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001317{
Hung-Te Lind1739622013-01-28 14:23:49 +08001318 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001319 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001320 return 1;
1321 }
1322
Sol Bouchere3260a02015-03-25 13:40:08 -07001323 struct cbfs_image image;
1324 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001325 param.headeroffset))
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001326 return 1;
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001327
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001328 if (cbfs_remove_entry(&image, param.name) != 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001329 ERROR("Removing file '%s' failed.\n",
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001330 param.name);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001331 return 1;
1332 }
1333
Julius Werner4bfbabd2020-05-06 17:27:02 -07001334 return maybe_update_metadata_hash(&image);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001335}
1336
Stefan Reinauer63217582012-10-29 16:52:36 -07001337static int cbfs_create(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001338{
Sol Boucher67a0a862015-03-18 12:36:27 -07001339 struct cbfs_image image;
1340 memset(&image, 0, sizeof(image));
1341 buffer_clone(&image.buffer, param.image_region);
1342
1343 if (param.fmap) {
1344 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
1345 param.baseaddress_assigned ||
1346 param.headeroffset_assigned ||
1347 param.cbfsoffset_assigned ||
Sol Boucher67a0a862015-03-18 12:36:27 -07001348 param.bootblock) {
Patrick Georgi45acb342015-07-14 22:18:23 +02001349 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
Sol Boucher67a0a862015-03-18 12:36:27 -07001350 return 1;
1351 }
1352
1353 return cbfs_image_create(&image, image.buffer.size);
1354 }
1355
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001356 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
Stefan Reinauer60a4a732013-03-28 16:46:07 -07001357 ERROR("You need to specify -m/--machine arch.\n");
David Hendricks90ca3b62012-11-16 14:48:22 -08001358 return 1;
1359 }
1360
Sol Bouchere3260a02015-03-25 13:40:08 -07001361 struct buffer bootblock;
Julius Wernerefcee762014-11-10 13:14:24 -08001362 if (!param.bootblock) {
1363 DEBUG("-B not given, creating image without bootblock.\n");
Patrick Georgi6b2d2db2016-12-14 16:11:58 +01001364 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
1365 return 1;
Julius Wernerefcee762014-11-10 13:14:24 -08001366 } else if (buffer_from_file(&bootblock, param.bootblock)) {
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001367 return 1;
1368 }
1369
Julius Wernerefcee762014-11-10 13:14:24 -08001370 if (!param.alignment)
Patrick Georgi45acb342015-07-14 22:18:23 +02001371 param.alignment = CBFS_ALIGNMENT;
Julius Wernerefcee762014-11-10 13:14:24 -08001372
1373 // Set default offsets. x86, as usual, needs to be a special snowflake.
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001374 if (!param.baseaddress_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001375 if (param.arch == CBFS_ARCHITECTURE_X86) {
1376 // Make sure there's at least enough room for rel_offset
Sol Boucher67a0a862015-03-18 12:36:27 -07001377 param.baseaddress = param.size -
1378 MAX(bootblock.size, sizeof(int32_t));
Julius Wernerefcee762014-11-10 13:14:24 -08001379 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1380 param.baseaddress);
1381 } else {
1382 param.baseaddress = 0;
1383 DEBUG("bootblock starts at address 0x0.\n");
1384 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001385 }
1386 if (!param.headeroffset_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001387 if (param.arch == CBFS_ARCHITECTURE_X86) {
1388 param.headeroffset = param.baseaddress -
1389 sizeof(struct cbfs_header);
1390 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1391 param.headeroffset);
1392 } else {
1393 param.headeroffset = align_up(param.baseaddress +
1394 bootblock.size, sizeof(uint32_t));
1395 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1396 param.headeroffset);
1397 }
1398 }
1399 if (!param.cbfsoffset_assigned) {
1400 if (param.arch == CBFS_ARCHITECTURE_X86) {
1401 param.cbfsoffset = 0;
1402 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1403 } else {
1404 param.cbfsoffset = align_up(param.headeroffset +
1405 sizeof(struct cbfs_header),
Patrick Georgi45acb342015-07-14 22:18:23 +02001406 CBFS_ALIGNMENT);
Julius Wernerefcee762014-11-10 13:14:24 -08001407 DEBUG("CBFS entries start beind master header (%#x).\n",
1408 param.cbfsoffset);
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001409 }
1410 }
1411
Sol Boucher67a0a862015-03-18 12:36:27 -07001412 int ret = cbfs_legacy_image_create(&image,
1413 param.arch,
Patrick Georgi45acb342015-07-14 22:18:23 +02001414 CBFS_ALIGNMENT,
Sol Boucher67a0a862015-03-18 12:36:27 -07001415 &bootblock,
1416 param.baseaddress,
1417 param.headeroffset,
1418 param.cbfsoffset);
Sol Bouchere3260a02015-03-25 13:40:08 -07001419 buffer_delete(&bootblock);
Sol Boucher67a0a862015-03-18 12:36:27 -07001420 return ret;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001421}
1422
Sol Boucher67a0a862015-03-18 12:36:27 -07001423static int cbfs_layout(void)
1424{
1425 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1426 if (!fmap) {
1427 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1428 return 1;
1429 }
1430
1431 printf("This image contains the following sections that can be %s with this tool:\n",
1432 param.show_immutable ? "accessed" : "manipulated");
1433 puts("");
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001434 for (unsigned i = 0; i < fmap->nareas; ++i) {
1435 const struct fmap_area *current = fmap->areas + i;
Sol Boucher67a0a862015-03-18 12:36:27 -07001436
1437 bool readonly = partitioned_file_fmap_count(param.image_file,
1438 partitioned_file_fmap_select_children_of, current) ||
1439 region_is_flashmap((const char *)current->name);
1440 if (!param.show_immutable && readonly)
1441 continue;
1442
1443 printf("'%s'", current->name);
1444
1445 // Detect consecutive sections that describe the same region and
1446 // show them as aliases. This cannot find equivalent entries
1447 // that aren't adjacent; however, fmaptool doesn't generate
1448 // FMAPs with such sections, so this convenience feature works
1449 // for all but the strangest manually created FMAP binaries.
1450 // TODO: This could be done by parsing the FMAP into some kind
1451 // of tree that had duplicate lists in addition to child lists,
1452 // which would allow covering that weird, unlikely case as well.
1453 unsigned lookahead;
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001454 for (lookahead = 1; i + lookahead < fmap->nareas;
Sol Boucher67a0a862015-03-18 12:36:27 -07001455 ++lookahead) {
1456 const struct fmap_area *consecutive =
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001457 fmap->areas + i + lookahead;
Sol Boucher67a0a862015-03-18 12:36:27 -07001458 if (consecutive->offset != current->offset ||
1459 consecutive->size != current->size)
1460 break;
1461 printf(", '%s'", consecutive->name);
1462 }
1463 if (lookahead > 1)
1464 fputs(" are aliases for the same region", stdout);
1465
1466 const char *qualifier = "";
1467 if (readonly)
1468 qualifier = "read-only, ";
1469 else if (region_is_modern_cbfs((const char *)current->name))
1470 qualifier = "CBFS, ";
Patrick Georgi848e30d2019-06-18 22:02:13 +02001471 else if (current->flags & FMAP_AREA_PRESERVE)
1472 qualifier = "preserve, ";
Werner Zeh7850b582018-04-26 09:26:38 +02001473 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1474 current->offset);
Sol Boucher67a0a862015-03-18 12:36:27 -07001475
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001476 i += lookahead - 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001477 }
1478 puts("");
1479
1480 if (param.show_immutable) {
1481 puts("It is at least possible to perform the read action on every section listed above.");
1482 } else {
1483 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1484 puts("To see the image's read-only sections as well, rerun with the -w option.");
1485 }
1486
1487 return 0;
1488}
1489
Stefan Reinauer63217582012-10-29 16:52:36 -07001490static int cbfs_print(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001491{
Hung-Te Lin3bb035b2013-01-29 02:15:49 +08001492 struct cbfs_image image;
Sol Bouchere3260a02015-03-25 13:40:08 -07001493 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001494 param.headeroffset))
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001495 return 1;
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001496 if (param.machine_parseable) {
1497 if (verbose)
1498 printf("[FMAP REGION]\t%s\n", param.region_name);
1499 cbfs_print_parseable_directory(&image);
1500 } else {
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001501 printf("FMAP REGION: %s\n", param.region_name);
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001502 cbfs_print_directory(&image);
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001503 }
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001504
Julius Werner4bfbabd2020-05-06 17:27:02 -07001505 if (verbose) {
1506 struct mh_cache *mhc = get_mh_cache();
1507 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1508 return 0;
1509
1510 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
1511 cb_err_t err = cbfs_walk(&image, NULL, NULL, &real_hash,
1512 CBFS_WALK_WRITEBACK_HASH);
1513 if (err != CB_CBFS_NOT_FOUND) {
1514 ERROR("Unexpected cbfs_walk() error %d\n", err);
1515 return 1;
1516 }
1517 char *hash_str = bintohex(real_hash.raw,
1518 vb2_digest_size(real_hash.algo));
1519 printf("[METADATA HASH]\t%s:%s",
1520 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1521 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1522 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
1523 vb2_digest_size(real_hash.algo)))
1524 printf(":valid");
1525 else
1526 printf(":invalid");
1527 }
1528 printf("\n");
1529 free(hash_str);
1530 }
1531
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001532 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001533}
1534
Stefan Reinauer63217582012-10-29 16:52:36 -07001535static int cbfs_extract(void)
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001536{
Hung-Te Lind1739622013-01-28 14:23:49 +08001537 if (!param.filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001538 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001539 return 1;
1540 }
1541
Hung-Te Lind1739622013-01-28 14:23:49 +08001542 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001543 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001544 return 1;
1545 }
1546
Sol Bouchere3260a02015-03-25 13:40:08 -07001547 struct cbfs_image image;
1548 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001549 param.headeroffset))
1550 return 1;
1551
Aaron Durbin17625022015-10-27 13:17:52 -05001552 return cbfs_export_entry(&image, param.name, param.filename,
Joel Kitching21fdd892018-08-09 17:49:52 +08001553 param.arch, !param.unprocessed);
Sol Boucher67a0a862015-03-18 12:36:27 -07001554}
1555
1556static int cbfs_write(void)
1557{
1558 if (!param.filename) {
1559 ERROR("You need to specify a valid input -f/--file.\n");
1560 return 1;
1561 }
1562 if (!partitioned_file_is_partitioned(param.image_file)) {
1563 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001564 return 1;
1565 }
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001566
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001567 if (!param.force && region_is_modern_cbfs(param.region_name)) {
Sol Boucher67a0a862015-03-18 12:36:27 -07001568 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1569 param.region_name);
1570 return 1;
1571 }
1572
1573 struct buffer new_content;
1574 if (buffer_from_file(&new_content, param.filename))
1575 return 1;
1576
1577 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1578 strlen(FMAP_SIGNATURE))) {
1579 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1580 param.filename);
1581 buffer_delete(&new_content);
1582 return 1;
1583 }
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001584 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
Sol Boucher67a0a862015-03-18 12:36:27 -07001585 strlen(CBFS_FILE_MAGIC))) {
1586 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1587 param.filename);
1588 buffer_delete(&new_content);
1589 return 1;
1590 }
1591
1592 unsigned offset = 0;
1593 if (param.fill_partial_upward && param.fill_partial_downward) {
1594 ERROR("You may only specify one of -u and -d.\n");
1595 buffer_delete(&new_content);
1596 return 1;
1597 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1598 if (new_content.size != param.image_region->size) {
1599 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",
1600 new_content.size, param.image_region->size);
1601 buffer_delete(&new_content);
1602 return 1;
1603 }
1604 } else {
1605 if (new_content.size > param.image_region->size) {
1606 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1607 new_content.size, param.image_region->size);
1608 buffer_delete(&new_content);
1609 return 1;
1610 }
Patrick Georgid9edb182016-12-06 18:55:26 +01001611 if (param.u64val == (uint64_t)-1) {
1612 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1613 param.fill_partial_upward ? "bottom" : "top");
1614 } else if (param.u64val > 0xff) {
1615 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1616 buffer_delete(&new_content);
1617 return 1;
1618 } else {
1619 memset(buffer_get(param.image_region),
1620 param.u64val & 0xff,
1621 buffer_size(param.image_region));
1622 }
Sol Boucher67a0a862015-03-18 12:36:27 -07001623 if (param.fill_partial_downward)
1624 offset = param.image_region->size - new_content.size;
1625 }
1626
1627 memcpy(param.image_region->data + offset, new_content.data,
1628 new_content.size);
1629 buffer_delete(&new_content);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001630
1631 return maybe_update_fmap_hash();
Sol Boucher67a0a862015-03-18 12:36:27 -07001632}
1633
1634static int cbfs_read(void)
1635{
1636 if (!param.filename) {
1637 ERROR("You need to specify a valid output -f/--file.\n");
1638 return 1;
1639 }
1640 if (!partitioned_file_is_partitioned(param.image_file)) {
1641 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1642 return 1;
1643 }
1644
1645 return buffer_write_file(param.image_region, param.filename);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001646}
1647
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001648static int cbfs_copy(void)
1649{
Patrick Georgi214e4af2015-11-20 19:22:50 +01001650 struct cbfs_image src_image;
Patrick Georgibd0bb232015-11-20 21:48:18 +01001651 struct buffer src_buf;
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001652
Patrick Georgibd0bb232015-11-20 21:48:18 +01001653 if (!param.source_region) {
1654 ERROR("You need to specify -R/--source-region.\n");
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001655 return 1;
Sol Bouchere3260a02015-03-25 13:40:08 -07001656 }
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001657
Patrick Georgibd0bb232015-11-20 21:48:18 +01001658 /* Obtain the source region and convert it to a cbfs_image. */
1659 if (!partitioned_file_read_region(&src_buf, param.image_file,
1660 param.source_region)) {
1661 ERROR("Region not found in image: %s\n", param.source_region);
1662 return 1;
1663 }
1664
1665 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1666 return 1;
1667
1668 return cbfs_copy_instance(&src_image, param.image_region);
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001669}
1670
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001671static int cbfs_compact(void)
1672{
1673 struct cbfs_image image;
1674 if (cbfs_image_from_buffer(&image, param.image_region,
1675 param.headeroffset))
1676 return 1;
1677 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1678 return cbfs_compact_instance(&image);
1679}
1680
Patrick Georgi5d982d72017-09-19 14:39:58 +02001681static int cbfs_expand(void)
1682{
1683 struct buffer src_buf;
1684
1685 /* Obtain the source region. */
1686 if (!partitioned_file_read_region(&src_buf, param.image_file,
1687 param.region_name)) {
1688 ERROR("Region not found in image: %s\n", param.source_region);
1689 return 1;
1690 }
1691
1692 return cbfs_expand_to_region(param.image_region);
1693}
1694
Patrick Georgi12631a42017-09-20 11:59:18 +02001695static int cbfs_truncate(void)
1696{
1697 struct buffer src_buf;
1698
1699 /* Obtain the source region. */
1700 if (!partitioned_file_read_region(&src_buf, param.image_file,
1701 param.region_name)) {
1702 ERROR("Region not found in image: %s\n", param.source_region);
1703 return 1;
1704 }
1705
1706 uint32_t size;
1707 int result = cbfs_truncate_space(param.image_region, &size);
1708 printf("0x%x\n", size);
1709 return result;
1710}
1711
Stefan Reinauera1e48242011-10-21 14:24:57 -07001712static const struct command commands[] = {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301713 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001714 {"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 +01001715 true, true},
Raul E Rangeldb280402021-06-29 13:26:41 -06001716 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
Werner Zehe9995f12016-01-14 13:22:37 +01001717 true, true},
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001718 {"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 +01001719 true, true},
1720 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301721 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001722 {"compact", "r:h?", cbfs_compact, true, true},
Patrick Georgibd0bb232015-11-20 21:48:18 +01001723 {"copy", "r:R:h?", cbfs_copy, true, true},
Patrick Georgi45acb342015-07-14 22:18:23 +02001724 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
Joel Kitching21fdd892018-08-09 17:49:52 +08001725 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001726 {"layout", "wvh?", cbfs_layout, false, false},
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001727 {"print", "H:r:vkh?", cbfs_print, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001728 {"read", "r:f:vh?", cbfs_read, true, false},
1729 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
Patrick Georgid9edb182016-12-06 18:55:26 +01001730 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
Patrick Georgi5d982d72017-09-19 14:39:58 +02001731 {"expand", "r:h?", cbfs_expand, true, true},
Patrick Georgi12631a42017-09-20 11:59:18 +02001732 {"truncate", "r:h?", cbfs_truncate, true, true},
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001733};
1734
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001735enum {
1736 /* begin after ASCII characters */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001737 LONGOPT_START = 256,
1738 LONGOPT_IBB = LONGOPT_START,
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001739 LONGOPT_EXT_WIN_BASE,
1740 LONGOPT_EXT_WIN_SIZE,
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001741 LONGOPT_END,
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001742};
1743
Stefan Reinauer63217582012-10-29 16:52:36 -07001744static struct option long_options[] = {
Julius Wernerefcee762014-11-10 13:14:24 -08001745 {"alignment", required_argument, 0, 'a' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001746 {"base-address", required_argument, 0, 'b' },
1747 {"bootblock", required_argument, 0, 'B' },
Julius Wernerefcee762014-11-10 13:14:24 -08001748 {"cmdline", required_argument, 0, 'C' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001749 {"compression", required_argument, 0, 'c' },
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301750 {"topswap-size", required_argument, 0, 'j' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001751 {"empty-fits", required_argument, 0, 'x' },
1752 {"entry-point", required_argument, 0, 'e' },
1753 {"file", required_argument, 0, 'f' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001754 {"fill-downward", no_argument, 0, 'd' },
1755 {"fill-upward", no_argument, 0, 'u' },
1756 {"flashmap", required_argument, 0, 'M' },
1757 {"fmap-regions", required_argument, 0, 'r' },
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001758 {"force", no_argument, 0, 'F' },
Patrick Georgibd0bb232015-11-20 21:48:18 +01001759 {"source-region", required_argument, 0, 'R' },
Patrick Georgi89f20342015-10-01 15:54:04 +02001760 {"hash-algorithm",required_argument, 0, 'A' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001761 {"header-offset", required_argument, 0, 'H' },
Julius Wernerefcee762014-11-10 13:14:24 -08001762 {"help", no_argument, 0, 'h' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001763 {"ignore-sec", required_argument, 0, 'S' },
1764 {"initrd", required_argument, 0, 'I' },
1765 {"int", required_argument, 0, 'i' },
1766 {"load-address", required_argument, 0, 'l' },
1767 {"machine", required_argument, 0, 'm' },
1768 {"name", required_argument, 0, 'n' },
1769 {"offset", required_argument, 0, 'o' },
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001770 {"padding", required_argument, 0, 'p' },
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001771 {"pow2page", no_argument, 0, 'Q' },
Rizwan Qureshic1072f22018-06-04 23:02:46 +05301772 {"ucode-region", required_argument, 0, 'q' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001773 {"size", required_argument, 0, 's' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001774 {"type", required_argument, 0, 't' },
1775 {"verbose", no_argument, 0, 'v' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001776 {"with-readonly", no_argument, 0, 'w' },
Aaron Durbin4be16742015-09-15 17:00:23 -05001777 {"xip", no_argument, 0, 'y' },
Werner Zehe9995f12016-01-14 13:22:37 +01001778 {"gen-attribute", no_argument, 0, 'g' },
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001779 {"mach-parseable",no_argument, 0, 'k' },
Joel Kitching21fdd892018-08-09 17:49:52 +08001780 {"unprocessed", no_argument, 0, 'U' },
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001781 {"ibb", no_argument, 0, LONGOPT_IBB },
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001782 {"ext-win-base", required_argument, 0, LONGOPT_EXT_WIN_BASE },
1783 {"ext-win-size", required_argument, 0, LONGOPT_EXT_WIN_SIZE },
Julius Wernerefcee762014-11-10 13:14:24 -08001784 {NULL, 0, 0, 0 }
Stefan Reinauer63217582012-10-29 16:52:36 -07001785};
1786
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001787static int get_region_offset(long long int offset, uint32_t *region_offset)
1788{
1789 /* If offset is not negative, no transformation required. */
1790 if (offset >= 0) {
1791 *region_offset = offset;
1792 return 0;
1793 }
1794
1795 /* Calculate offset from start of region. */
1796 return convert_region_offset(-offset, region_offset);
1797}
1798
1799static int calculate_region_offsets(void)
1800{
1801 int ret = 0;
1802
1803 if (param.baseaddress_assigned)
1804 ret |= get_region_offset(param.baseaddress_input, &param.baseaddress);
1805 if (param.headeroffset_assigned)
1806 ret |= get_region_offset(param.headeroffset_input, &param.headeroffset);
1807 if (param.cbfsoffset_assigned)
1808 ret |= get_region_offset(param.cbfsoffset_input, &param.cbfsoffset);
1809
1810 return ret;
1811}
1812
Sol Boucher67a0a862015-03-18 12:36:27 -07001813static int dispatch_command(struct command command)
1814{
1815 if (command.accesses_region) {
1816 assert(param.image_file);
1817
1818 if (partitioned_file_is_partitioned(param.image_file)) {
Patrick Georgi2ed72952016-12-16 14:51:53 +01001819 INFO("Performing operation on '%s' region...\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07001820 param.region_name);
1821 }
1822 if (!partitioned_file_read_region(param.image_region,
1823 param.image_file, param.region_name)) {
1824 ERROR("The image will be left unmodified.\n");
1825 return 1;
1826 }
1827
1828 if (command.modifies_region) {
1829 // We (intentionally) don't support overwriting the FMAP
1830 // section. If you find yourself wanting to do this,
1831 // consider creating a new image rather than performing
1832 // whatever hacky transformation you were planning.
1833 if (region_is_flashmap(param.region_name)) {
1834 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1835 param.region_name);
1836 ERROR("The image will be left unmodified.\n");
1837 return 1;
1838 }
1839 // We don't allow writing raw data to regions that
1840 // contain nested regions, since doing so would
1841 // overwrite all such subregions.
1842 if (partitioned_file_region_contains_nested(
1843 param.image_file, param.region_name)) {
1844 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1845 param.region_name);
1846 ERROR("The image will be left unmodified.\n");
1847 return 1;
1848 }
1849 }
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001850
1851 /*
1852 * Once image region is read, input offsets can be adjusted accordingly if the
1853 * inputs are provided as negative integers i.e. offsets from end of region.
1854 */
1855 if (calculate_region_offsets())
1856 return 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001857 }
1858
1859 if (command.function()) {
1860 if (partitioned_file_is_partitioned(param.image_file)) {
1861 ERROR("Failed while operating on '%s' region!\n",
1862 param.region_name);
1863 ERROR("The image will be left unmodified.\n");
1864 }
1865 return 1;
1866 }
1867
1868 return 0;
1869}
1870
Stefan Reinauer63217582012-10-29 16:52:36 -07001871static void usage(char *name)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001872{
1873 printf
Stefan Reinauer07040582010-04-24 21:24:06 +00001874 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
Stefan Reinauer63217582012-10-29 16:52:36 -07001875 "USAGE:\n" " %s [-h]\n"
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001876 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001877 " -H header_offset Do not search for header; use this offset*\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001878 " -T Output top-aligned memory address\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001879 " -u Accept short data; fill upward/from bottom\n"
1880 " -d Accept short data; fill downward/from top\n"
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001881 " -F Force action\n"
Patrick Georgi5b6bdcc2016-10-13 18:24:10 +02001882 " -g Generate position and alignment arguments\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001883 " -U Unprocessed; don't decompress or make ELF\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001884 " -v Provide verbose output\n"
1885 " -h Display this help message\n\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001886 " --ext-win-base Base of extended decode window in host address\n"
1887 " space(x86 only)\n"
1888 " --ext-win-size Size of extended decode window in host address\n"
1889 " space(x86 only)\n"
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001890 "COMMANDs:\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001891 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001892 " [-c compression] [-b base-address | -a alignment] \\\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301893 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001894 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1895 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001896 "Add a component\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301897 " "
1898 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001899 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001900 " [-c compression] [-b base-address] \\\n"
1901 " (linux specific: [-C cmdline] [-I initrd]) "
Stefan Reinauer63217582012-10-29 16:52:36 -07001902 "Add a payload to the ROM\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001903 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001904 " [-c compression] [-b base] [-S section-to-ignore] \\\n"
Arthur Heymansc78521b32021-06-16 17:16:05 +02001905 " [-a alignment] [-Q|--pow2page] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001906 " [-y|--xip] [--ibb] \\\n"
1907 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001908 "Add a stage to the ROM\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001909 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1910 " [-A hash] -l load-address -e entry-point \\\n"
1911 " [-c compression] [-b base] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001912 "Add a 32bit flat mode binary\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001913 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001914 "Add a raw 64-bit integer value\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301915 " add-master-header [-r image,regions] \\ \n"
1916 " [-j topswap-size] (Intel CPUs only) "
Patrick Georgi59e52b92015-09-10 15:28:27 +02001917 "Add a legacy CBFS master header\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001918 " remove [-r image,regions] -n NAME "
Stefan Reinauer63217582012-10-29 16:52:36 -07001919 "Remove a component\n"
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001920 " compact -r image,regions "
1921 "Defragment CBFS image.\n"
Patrick Georgibd0bb232015-11-20 21:48:18 +01001922 " copy -r image,regions -R source-region "
1923 "Create a copy (duplicate) cbfs instance in fmap\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001924 " create -m ARCH -s size [-b bootblock offset] \\\n"
Patrick Georgi83c2d122015-08-26 10:40:00 +02001925 " [-o CBFS offset] [-H header offset] [-B bootblock] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001926 "Create a legacy ROM file with CBFS master header*\n"
1927 " create -M flashmap [-r list,of,regions,containing,cbfses] "
1928 "Create a new-style partitioned firmware image\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001929 " layout [-w] "
1930 "List mutable (or, with -w, readable) image regions\n"
Julius Werner4bfbabd2020-05-06 17:27:02 -07001931 " print [-r image,regions] [-k] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001932 "Show the contents of the ROM\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001933 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
1934 "Extracts a file from ROM\n"
Patrick Georgid9edb182016-12-06 18:55:26 +01001935 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001936 "Write file into same-size [or larger] raw region\n"
1937 " read [-r fmap-region] -f file "
1938 "Extract raw region contents into binary file\n"
Patrick Georgi12631a42017-09-20 11:59:18 +02001939 " truncate [-r fmap-region] "
1940 "Truncate CBFS and print new size on stdout\n"
Patrick Georgi5d982d72017-09-19 14:39:58 +02001941 " expand [-r fmap-region] "
1942 "Expand CBFS to span entire region\n"
Sol Boucher0e539312015-03-05 15:38:03 -08001943 "OFFSETs:\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001944 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
1945 " in two possible formats: if their value is greater than\n"
Sol Boucher0e539312015-03-05 15:38:03 -08001946 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
1947 " address; otherwise, they are treated as an offset into flash.\n"
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02001948 "ARCHes:\n", name, name
Stefan Reinauer63217582012-10-29 16:52:36 -07001949 );
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02001950 print_supported_architectures();
1951
1952 printf("TYPEs:\n");
Stefan Reinauer07040582010-04-24 21:24:06 +00001953 print_supported_filetypes();
Sol Boucher67a0a862015-03-18 12:36:27 -07001954 printf(
1955 "\n* Note that these actions and switches are only valid when\n"
1956 " working with legacy images whose structure is described\n"
1957 " primarily by a CBFS master header. New-style images, in\n"
1958 " contrast, exclusively make use of an FMAP to describe their\n"
1959 " layout: this must minimally contain an '%s' section\n"
1960 " specifying the location of this FMAP itself and a '%s'\n"
1961 " section describing the primary CBFS. It should also be noted\n"
1962 " that, when working with such images, the -F and -r switches\n"
Julius Werner0fd072d2021-12-13 10:28:29 -08001963 " default to '%s' for convenience, and the -b switch becomes\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001964 " relative to the selected CBFS region's lowest address.\n"
1965 " The one exception to this rule is the top-aligned address,\n"
1966 " which is always relative to the end of the entire image\n"
1967 " rather than relative to the local region; this is true for\n"
1968 " for both input (sufficiently large) and output (-T) data.\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07001969 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
1970 SECTION_NAME_PRIMARY_CBFS
1971 );
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001972}
1973
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001974static bool valid_opt(size_t i, int c)
1975{
1976 /* Check if it is one of the optstrings supported by the command. */
1977 if (strchr(commands[i].optstring, c))
1978 return true;
1979
1980 /*
1981 * Check if it is one of the non-ASCII characters. Currently, the
1982 * non-ASCII characters are only checked against the valid list
1983 * irrespective of the command.
1984 */
1985 if (c >= LONGOPT_START && c < LONGOPT_END)
1986 return true;
1987
1988 return false;
1989}
1990
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001991int main(int argc, char **argv)
1992{
Mathias Krause41c229c2012-07-17 21:17:15 +02001993 size_t i;
Stefan Reinauer63217582012-10-29 16:52:36 -07001994 int c;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001995
1996 if (argc < 3) {
Stefan Reinauer63217582012-10-29 16:52:36 -07001997 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001998 return 1;
1999 }
2000
Sol Bouchere3260a02015-03-25 13:40:08 -07002001 char *image_name = argv[1];
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002002 char *cmd = argv[2];
Stefan Reinauer63217582012-10-29 16:52:36 -07002003 optind += 2;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002004
2005 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2006 if (strcmp(cmd, commands[i].name) != 0)
2007 continue;
Stefan Reinauer63217582012-10-29 16:52:36 -07002008
2009 while (1) {
2010 char *suffix = NULL;
2011 int option_index = 0;
2012
2013 c = getopt_long(argc, argv, commands[i].optstring,
2014 long_options, &option_index);
Nico Huber54073102016-08-01 23:18:29 +02002015 if (c == -1) {
2016 if (optind < argc) {
2017 ERROR("%s: excessive argument -- '%s'"
2018 "\n", argv[0], argv[optind]);
2019 return 1;
2020 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002021 break;
Nico Huber54073102016-08-01 23:18:29 +02002022 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002023
Joel Kitching672d5ad2018-10-11 17:01:30 +08002024 /* Filter out illegal long options */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002025 if (!valid_opt(i, c)) {
2026 ERROR("%s: invalid option -- '%d'\n",
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002027 argv[0], c);
Stefan Reinauer63217582012-10-29 16:52:36 -07002028 c = '?';
2029 }
2030
2031 switch(c) {
2032 case 'n':
Hung-Te Lind1739622013-01-28 14:23:49 +08002033 param.name = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002034 break;
2035 case 't':
2036 if (intfiletype(optarg) != ((uint64_t) - 1))
Hung-Te Lind1739622013-01-28 14:23:49 +08002037 param.type = intfiletype(optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002038 else
Hung-Te Lind1739622013-01-28 14:23:49 +08002039 param.type = strtoul(optarg, NULL, 0);
2040 if (param.type == 0)
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002041 WARN("Unknown type '%s' ignored\n",
Stefan Reinauer63217582012-10-29 16:52:36 -07002042 optarg);
2043 break;
Sol Boucherec424862015-05-07 21:00:05 -07002044 case 'c': {
Patrick Georgiecaa5702017-01-11 18:38:11 +01002045 if (strcmp(optarg, "precompression") == 0) {
2046 param.precompression = 1;
2047 break;
2048 }
Sol Boucherec424862015-05-07 21:00:05 -07002049 int algo = cbfs_parse_comp_algo(optarg);
2050 if (algo >= 0)
2051 param.compression = algo;
Stefan Reinauer63217582012-10-29 16:52:36 -07002052 else
Sol Boucherec424862015-05-07 21:00:05 -07002053 WARN("Unknown compression '%s' ignored.\n",
2054 optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002055 break;
Sol Boucherec424862015-05-07 21:00:05 -07002056 }
Patrick Georgi89f20342015-10-01 15:54:04 +02002057 case 'A': {
Julius Wernerd4775652020-03-13 16:43:34 -07002058 if (!vb2_lookup_hash_alg(optarg, &param.hash)) {
Patrick Georgi89f20342015-10-01 15:54:04 +02002059 ERROR("Unknown hash algorithm '%s'.\n",
2060 optarg);
2061 return 1;
2062 }
2063 break;
2064 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002065 case 'M':
2066 param.fmap = optarg;
2067 break;
2068 case 'r':
2069 param.region_name = optarg;
2070 break;
Patrick Georgibd0bb232015-11-20 21:48:18 +01002071 case 'R':
2072 param.source_region = optarg;
2073 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002074 case 'b':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002075 param.baseaddress_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002076 if (!*optarg || (suffix && *suffix)) {
2077 ERROR("Invalid base address '%s'.\n",
2078 optarg);
2079 return 1;
2080 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002081 // baseaddress may be zero on non-x86, so we
2082 // need an explicit "baseaddress_assigned".
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002083 param.baseaddress_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002084 break;
2085 case 'l':
Nico Huber9ade7172016-08-01 21:37:42 +02002086 param.loadaddress = strtoul(optarg, &suffix, 0);
2087 if (!*optarg || (suffix && *suffix)) {
2088 ERROR("Invalid load address '%s'.\n",
2089 optarg);
2090 return 1;
2091 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002092 break;
2093 case 'e':
Nico Huber9ade7172016-08-01 21:37:42 +02002094 param.entrypoint = strtoul(optarg, &suffix, 0);
2095 if (!*optarg || (suffix && *suffix)) {
2096 ERROR("Invalid entry point '%s'.\n",
2097 optarg);
2098 return 1;
2099 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002100 break;
2101 case 's':
Hung-Te Lind1739622013-01-28 14:23:49 +08002102 param.size = strtoul(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002103 if (!*optarg) {
2104 ERROR("Empty size specified.\n");
2105 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002106 }
Nico Huber9ade7172016-08-01 21:37:42 +02002107 switch (tolower((int)suffix[0])) {
2108 case 'k':
2109 param.size *= 1024;
2110 break;
2111 case 'm':
Hung-Te Lind1739622013-01-28 14:23:49 +08002112 param.size *= 1024 * 1024;
Nico Huber9ade7172016-08-01 21:37:42 +02002113 break;
2114 case '\0':
2115 break;
2116 default:
2117 ERROR("Invalid suffix for size '%s'.\n",
2118 optarg);
2119 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002120 }
Patrick Georgie887ca52014-08-09 17:44:39 +02002121 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002122 case 'B':
Hung-Te Lind1739622013-01-28 14:23:49 +08002123 param.bootblock = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002124 break;
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002125 case 'H':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002126 param.headeroffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002127 if (!*optarg || (suffix && *suffix)) {
2128 ERROR("Invalid header offset '%s'.\n",
2129 optarg);
2130 return 1;
2131 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002132 param.headeroffset_assigned = 1;
2133 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002134 case 'a':
Nico Huber9ade7172016-08-01 21:37:42 +02002135 param.alignment = strtoul(optarg, &suffix, 0);
2136 if (!*optarg || (suffix && *suffix)) {
2137 ERROR("Invalid alignment '%s'.\n",
2138 optarg);
2139 return 1;
2140 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002141 break;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07002142 case 'p':
2143 param.padding = strtoul(optarg, &suffix, 0);
2144 if (!*optarg || (suffix && *suffix)) {
2145 ERROR("Invalid pad size '%s'.\n",
2146 optarg);
2147 return 1;
2148 }
2149 break;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03002150 case 'Q':
2151 param.force_pow2_pagesize = 1;
2152 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002153 case 'o':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002154 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002155 if (!*optarg || (suffix && *suffix)) {
2156 ERROR("Invalid cbfs offset '%s'.\n",
2157 optarg);
2158 return 1;
2159 }
Julius Wernerefcee762014-11-10 13:14:24 -08002160 param.cbfsoffset_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002161 break;
2162 case 'f':
Hung-Te Lind1739622013-01-28 14:23:49 +08002163 param.filename = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002164 break;
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02002165 case 'F':
2166 param.force = 1;
2167 break;
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002168 case 'i':
Nico Huber9ade7172016-08-01 21:37:42 +02002169 param.u64val = strtoull(optarg, &suffix, 0);
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01002170 param.u64val_assigned = 1;
Nico Huber9ade7172016-08-01 21:37:42 +02002171 if (!*optarg || (suffix && *suffix)) {
2172 ERROR("Invalid int parameter '%s'.\n",
2173 optarg);
2174 return 1;
2175 }
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002176 break;
Sol Boucher67a0a862015-03-18 12:36:27 -07002177 case 'u':
2178 param.fill_partial_upward = true;
2179 break;
2180 case 'd':
2181 param.fill_partial_downward = true;
2182 break;
2183 case 'w':
2184 param.show_immutable = true;
Hung-Te Lin215d1d72013-01-29 03:46:02 +08002185 break;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05302186 case 'j':
2187 param.topswap_size = strtol(optarg, NULL, 0);
2188 if (!is_valid_topswap())
2189 return 1;
2190 break;
Rizwan Qureshic1072f22018-06-04 23:02:46 +05302191 case 'q':
2192 param.ucode_region = optarg;
2193 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002194 case 'v':
2195 verbose++;
2196 break;
David Hendricks90ca3b62012-11-16 14:48:22 -08002197 case 'm':
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06002198 param.arch = string_to_arch(optarg);
David Hendricks90ca3b62012-11-16 14:48:22 -08002199 break;
Patrick Georgide36d332013-08-27 20:22:21 +02002200 case 'I':
2201 param.initrd = optarg;
2202 break;
2203 case 'C':
2204 param.cmdline = optarg;
2205 break;
Furquan Shaikh405304a2014-10-30 11:44:20 -07002206 case 'S':
2207 param.ignore_section = optarg;
2208 break;
Aaron Durbin4be16742015-09-15 17:00:23 -05002209 case 'y':
2210 param.stage_xip = true;
2211 break;
Werner Zehe9995f12016-01-14 13:22:37 +01002212 case 'g':
2213 param.autogen_attr = true;
2214 break;
Aaron Durbin5dc628a2016-01-26 15:35:34 -06002215 case 'k':
2216 param.machine_parseable = true;
2217 break;
Joel Kitching21fdd892018-08-09 17:49:52 +08002218 case 'U':
2219 param.unprocessed = true;
2220 break;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01002221 case LONGOPT_IBB:
2222 param.ibb = true;
2223 break;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08002224 case LONGOPT_EXT_WIN_BASE:
2225 param.ext_win_base = strtoul(optarg, &suffix, 0);
2226 if (!*optarg || (suffix && *suffix)) {
2227 ERROR("Invalid ext window base '%s'.\n", optarg);
2228 return 1;
2229 }
2230 break;
2231 case LONGOPT_EXT_WIN_SIZE:
2232 param.ext_win_size = strtoul(optarg, &suffix, 0);
2233 if (!*optarg || (suffix && *suffix)) {
2234 ERROR("Invalid ext window size '%s'.\n", optarg);
2235 return 1;
2236 }
2237 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002238 case 'h':
2239 case '?':
2240 usage(argv[0]);
2241 return 1;
2242 default:
2243 break;
2244 }
2245 }
2246
Sol Bouchere3260a02015-03-25 13:40:08 -07002247 if (commands[i].function == cbfs_create) {
Sol Boucher67a0a862015-03-18 12:36:27 -07002248 if (param.fmap) {
2249 struct buffer flashmap;
2250 if (buffer_from_file(&flashmap, param.fmap))
2251 return 1;
2252 param.image_file = partitioned_file_create(
2253 image_name, &flashmap);
2254 buffer_delete(&flashmap);
2255 } else if (param.size) {
2256 param.image_file = partitioned_file_create_flat(
2257 image_name, param.size);
2258 } else {
2259 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002260 return 1;
2261 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002262 } else {
Vadim Bendebury75599462015-12-09 09:39:31 -08002263 bool write_access = commands[i].modifies_region;
2264
Sol Bouchere3260a02015-03-25 13:40:08 -07002265 param.image_file =
Vadim Bendebury75599462015-12-09 09:39:31 -08002266 partitioned_file_reopen(image_name,
2267 write_access);
Sol Bouchere3260a02015-03-25 13:40:08 -07002268 }
2269 if (!param.image_file)
2270 return 1;
2271
Sol Boucher67a0a862015-03-18 12:36:27 -07002272 unsigned num_regions = 1;
2273 for (const char *list = strchr(param.region_name, ','); list;
2274 list = strchr(list + 1, ','))
2275 ++num_regions;
2276
Sol Bouchere3260a02015-03-25 13:40:08 -07002277 // If the action needs to read an image region, as indicated by
2278 // having accesses_region set in its command struct, that
2279 // region's buffer struct will be stored here and the client
2280 // will receive a pointer to it via param.image_region. It
2281 // need not write the buffer back to the image file itself,
2282 // since this behavior can be requested via its modifies_region
2283 // field. Additionally, it should never free the region buffer,
2284 // as that is performed automatically once it completes.
Sol Boucher67a0a862015-03-18 12:36:27 -07002285 struct buffer image_regions[num_regions];
2286 memset(image_regions, 0, sizeof(image_regions));
Sol Bouchere3260a02015-03-25 13:40:08 -07002287
Sol Boucher67a0a862015-03-18 12:36:27 -07002288 bool seen_primary_cbfs = false;
2289 char region_name_scratch[strlen(param.region_name) + 1];
2290 strcpy(region_name_scratch, param.region_name);
2291 param.region_name = strtok(region_name_scratch, ",");
2292 for (unsigned region = 0; region < num_regions; ++region) {
2293 if (!param.region_name) {
2294 ERROR("Encountered illegal degenerate region name in -r list\n");
2295 ERROR("The image will be left unmodified.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002296 partitioned_file_close(param.image_file);
2297 return 1;
2298 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002299
Sol Boucher67a0a862015-03-18 12:36:27 -07002300 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2301 == 0)
2302 seen_primary_cbfs = true;
Sol Bouchere3260a02015-03-25 13:40:08 -07002303
Sol Boucher67a0a862015-03-18 12:36:27 -07002304 param.image_region = image_regions + region;
2305 if (dispatch_command(commands[i])) {
Sol Bouchere3260a02015-03-25 13:40:08 -07002306 partitioned_file_close(param.image_file);
2307 return 1;
2308 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002309
2310 param.region_name = strtok(NULL, ",");
2311 }
2312
2313 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2314 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2315 SECTION_NAME_PRIMARY_CBFS);
2316 ERROR("The image will be left unmodified.\n");
2317 partitioned_file_close(param.image_file);
2318 return 1;
2319 }
2320
2321 if (commands[i].modifies_region) {
2322 assert(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002323 for (unsigned region = 0; region < num_regions;
2324 ++region) {
2325
2326 if (!partitioned_file_write_region(
2327 param.image_file,
2328 image_regions + region)) {
2329 partitioned_file_close(
2330 param.image_file);
2331 return 1;
2332 }
2333 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002334 }
2335
2336 partitioned_file_close(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002337 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002338 }
2339
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002340 ERROR("Unknown command '%s'.\n", cmd);
Stefan Reinauer63217582012-10-29 16:52:36 -07002341 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002342 return 1;
2343}