blob: d26c4186121e9883b588c1419627f04c7a2e1b05 [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,
Jakub Czapigaaa415632022-08-01 16:01:28 +0200116 .headeroffset = HEADER_OFFSET_UNKNOWN,
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
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600151 /* Find the metadata_hash container. If there is a "BOOTBLOCK" FMAP section, it's
Julius Werner4bfbabd2020-05-06 17:27:02 -0700152 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;
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600164 struct cbfs_file *mh_container;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700165 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;
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600171 mh_container = cbfs_get_entry(&cbfs, "bootblock");
172 if (!mh_container || be32toh(mh_container->type) != CBFS_TYPE_BOOTBLOCK) {
173 /* Check for apu/amdfw file */
174 mh_container = cbfs_get_entry(&cbfs, "apu/amdfw");
175 if (!mh_container || be32toh(mh_container->type) != CBFS_TYPE_AMDFW)
176 goto no_metadata_hash;
177 }
178
179 offset = (void *)mh_container + be32toh(mh_container->offset) -
Julius Werner4bfbabd2020-05-06 17:27:02 -0700180 buffer_get(&cbfs.buffer);
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600181 size = be32toh(mh_container->len);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700182 }
183
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600184 /* Find and validate the metadata hash anchor inside the containing file and
Julius Werner4bfbabd2020-05-06 17:27:02 -0700185 record its exact byte offset from the start of the FMAP region. */
186 struct metadata_hash_anchor *anchor = memmem(buffer_get(&buffer) + offset,
187 size, METADATA_HASH_ANCHOR_MAGIC, sizeof(anchor->magic));
188 if (anchor) {
189 if (!vb2_digest_size(anchor->cbfs_hash.algo)) {
190 ERROR("Unknown CBFS metadata hash type: %d\n",
191 anchor->cbfs_hash.algo);
192 goto no_metadata_hash;
193 }
194 mhc.cbfs_hash = anchor->cbfs_hash;
195 mhc.offset = (void *)anchor - buffer_get(&buffer);
Julius Werner76dab5f2020-03-19 21:09:35 -0700196 mhc.fixup = platform_fixups_probe(&buffer, mhc.offset,
197 mhc.region);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700198 return &mhc;
199 }
200
201no_metadata_hash:
202 mhc.cbfs_hash.algo = VB2_HASH_INVALID;
203 return &mhc;
204}
205
206static void update_and_info(const char *name, void *dst, void *src, size_t size)
207{
208 if (!memcmp(dst, src, size))
209 return;
210 char *src_str = bintohex(src, size);
211 char *dst_str = bintohex(dst, size);
212 INFO("Updating %s from %s to %s\n", name, dst_str, src_str);
213 memcpy(dst, src, size);
214 free(src_str);
215 free(dst_str);
216}
217
218static int update_anchor(struct mh_cache *mhc, uint8_t *fmap_hash)
219{
220 struct buffer buffer;
221 if (!partitioned_file_read_region(&buffer, param.image_file,
222 mhc->region))
223 return -1;
224 struct metadata_hash_anchor *anchor = buffer_get(&buffer) + mhc->offset;
225 /* The metadata hash anchor should always still be where we left it. */
226 assert(!memcmp(anchor->magic, METADATA_HASH_ANCHOR_MAGIC,
227 sizeof(anchor->magic)) &&
228 anchor->cbfs_hash.algo == mhc->cbfs_hash.algo);
229 update_and_info("CBFS metadata hash", anchor->cbfs_hash.raw,
230 mhc->cbfs_hash.raw, vb2_digest_size(anchor->cbfs_hash.algo));
231 if (fmap_hash) {
232 update_and_info("FMAP hash",
233 metadata_hash_anchor_fmap_hash(anchor), fmap_hash,
234 vb2_digest_size(anchor->cbfs_hash.algo));
235 }
Julius Werner76dab5f2020-03-19 21:09:35 -0700236 if (mhc->fixup && mhc->fixup(&buffer, mhc->offset) != 0)
237 return -1;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700238 if (!partitioned_file_write_region(param.image_file, &buffer))
239 return -1;
240 return 0;
241
242}
243
244/* This should be called after every time CBFS metadata might have changed. It
245 will recalculate and update the metadata hash in the bootblock if needed. */
246static int maybe_update_metadata_hash(struct cbfs_image *cbfs)
247{
248 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS))
249 return 0; /* Metadata hash only embedded in primary CBFS. */
250
251 struct mh_cache *mhc = get_mh_cache();
252 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
253 return 0;
254
Julius Werner69cc5572022-03-04 17:49:56 -0800255 enum cb_err err = cbfs_walk(cbfs, NULL, NULL, &mhc->cbfs_hash,
256 CBFS_WALK_WRITEBACK_HASH);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700257 if (err != CB_CBFS_NOT_FOUND) {
258 ERROR("Unexpected cbfs_walk() error %d\n", err);
259 return -1;
260 }
261
262 return update_anchor(mhc, NULL);
263}
264
265/* This should be called after every time the FMAP or the bootblock itself might
266 have changed, and will write the new FMAP hash into the metadata hash anchor
267 in the bootblock if required (usually when the bootblock is first added). */
268static int maybe_update_fmap_hash(void)
269{
270 if (strcmp(param.region_name, SECTION_NAME_BOOTBLOCK) &&
271 strcmp(param.region_name, SECTION_NAME_FMAP) &&
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600272 param.type != CBFS_TYPE_BOOTBLOCK &&
273 param.type != CBFS_TYPE_AMDFW)
Julius Werner4bfbabd2020-05-06 17:27:02 -0700274 return 0; /* FMAP and bootblock didn't change. */
275
276 struct mh_cache *mhc = get_mh_cache();
277 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
278 return 0;
279
Julius Wernerd96ca242022-08-08 18:08:35 -0700280 struct vb2_hash fmap_hash;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700281 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
Julius Wernerd96ca242022-08-08 18:08:35 -0700282 if (!fmap || vb2_hash_calculate(false, fmap, fmap_size(fmap),
283 mhc->cbfs_hash.algo, &fmap_hash))
Julius Werner4bfbabd2020-05-06 17:27:02 -0700284 return -1;
Julius Wernerd96ca242022-08-08 18:08:35 -0700285 return update_anchor(mhc, fmap_hash.raw);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700286}
287
Julius Werneraf20fd72022-05-27 18:33:15 -0700288static bool verification_exclude(enum cbfs_type type)
289{
290 switch (type) {
291 case CBFS_TYPE_BOOTBLOCK:
292 case CBFS_TYPE_CBFSHEADER:
293 case CBFS_TYPE_INTEL_FIT:
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600294 case CBFS_TYPE_AMDFW:
Julius Werneraf20fd72022-05-27 18:33:15 -0700295 return true;
296 default:
297 return false;
298 }
299}
300
Sol Boucher67a0a862015-03-18 12:36:27 -0700301static bool region_is_flashmap(const char *region)
302{
303 return partitioned_file_region_check_magic(param.image_file, region,
304 FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
305}
306
307/* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
308static bool region_is_modern_cbfs(const char *region)
309{
310 return partitioned_file_region_check_magic(param.image_file, region,
311 CBFS_FILE_MAGIC, strlen(CBFS_FILE_MAGIC));
312}
313
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800314/* This describes a window from the SPI flash address space into the host address space. */
315struct mmap_window {
316 struct region flash_space;
317 struct region host_space;
318};
319
320enum mmap_window_type {
321 X86_DEFAULT_DECODE_WINDOW, /* Decode window just below 4G boundary */
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800322 X86_EXTENDED_DECODE_WINDOW, /* Extended decode window for mapping greater than 16MiB
323 flash */
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800324 MMAP_MAX_WINDOWS,
325};
326
327/* Table of all the decode windows supported by the platform. */
328static struct mmap_window mmap_window_table[MMAP_MAX_WINDOWS];
329
330static void add_mmap_window(enum mmap_window_type idx, size_t flash_offset, size_t host_offset,
331 size_t window_size)
332{
333 if (idx >= MMAP_MAX_WINDOWS) {
334 ERROR("Incorrect mmap window index(%d)\n", idx);
335 return;
336 }
337
338 mmap_window_table[idx].flash_space.offset = flash_offset;
339 mmap_window_table[idx].host_space.offset = host_offset;
340 mmap_window_table[idx].flash_space.size = window_size;
341 mmap_window_table[idx].host_space.size = window_size;
342}
343
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800344#define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
345#define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
346
347static bool create_mmap_windows(void)
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800348{
349 static bool done;
350
351 if (done)
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800352 return done;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800353
354 const size_t image_size = partitioned_file_total_size(param.image_file);
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800355 const size_t std_window_size = MIN(DEFAULT_DECODE_WINDOW_MAX_SIZE, image_size);
356 const size_t std_window_flash_offset = image_size - std_window_size;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800357
358 /*
359 * Default decode window lives just below 4G boundary in host space and maps up to a
360 * maximum of 16MiB. If the window is smaller than 16MiB, the SPI flash window is mapped
361 * at the top of the host window just below 4G.
362 */
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800363 add_mmap_window(X86_DEFAULT_DECODE_WINDOW, std_window_flash_offset,
364 DEFAULT_DECODE_WINDOW_TOP - std_window_size, std_window_size);
365
366 if (param.ext_win_size && (image_size > DEFAULT_DECODE_WINDOW_MAX_SIZE)) {
367 /*
368 * If the platform supports extended window and the SPI flash size is greater
369 * than 16MiB, then create a mapping for the extended window as well.
370 * The assumptions here are:
371 * 1. Top 16MiB is still decoded in the fixed decode window just below 4G
372 * boundary.
373 * 2. Rest of the SPI flash below the top 16MiB is mapped at the top of extended
374 * window. Even though the platform might support a larger extended window, the
375 * SPI flash part used by the mainboard might not be large enough to be mapped
376 * in the entire window. In such cases, the mapping is assumed to be in the top
377 * part of the extended window with the bottom part remaining unused.
378 *
379 * Example:
380 * ext_win_base = 0xF8000000
381 * ext_win_size = 32 * MiB
382 * ext_win_limit = ext_win_base + ext_win_size - 1 = 0xF9FFFFFF
383 *
384 * If SPI flash is 32MiB, then top 16MiB is mapped from 0xFF000000 - 0xFFFFFFFF
385 * whereas the bottom 16MiB is mapped from 0xF9000000 - 0xF9FFFFFF. The extended
386 * window 0xF8000000 - 0xF8FFFFFF remains unused.
387 */
388 const size_t ext_window_mapped_size = MIN(param.ext_win_size,
389 image_size - std_window_size);
390 const size_t ext_window_top = param.ext_win_base + param.ext_win_size;
391 add_mmap_window(X86_EXTENDED_DECODE_WINDOW,
392 std_window_flash_offset - ext_window_mapped_size,
393 ext_window_top - ext_window_mapped_size,
394 ext_window_mapped_size);
395
396 if (region_overlap(&mmap_window_table[X86_EXTENDED_DECODE_WINDOW].host_space,
397 &mmap_window_table[X86_DEFAULT_DECODE_WINDOW].host_space)) {
398 const struct region *ext_region;
399
400 ext_region = &mmap_window_table[X86_EXTENDED_DECODE_WINDOW].host_space;
401 ERROR("Extended window(base=0x%zx, limit=0x%zx) overlaps with default window!\n",
402 region_offset(ext_region), region_end(ext_region));
403
404 return false;
405 }
406 }
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800407
408 done = true;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800409 return done;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800410}
411
412static unsigned int convert_address(const struct region *to, const struct region *from,
413 unsigned int addr)
414{
415 /*
416 * Calculate the offset in the "from" region and use that offset to calculate
417 * corresponding address in the "to" region.
418 */
419 size_t offset = addr - region_offset(from);
420 return region_offset(to) + offset;
421}
422
423enum mmap_addr_type {
424 HOST_SPACE_ADDR,
425 FLASH_SPACE_ADDR,
426};
427
428static int find_mmap_window(enum mmap_addr_type addr_type, unsigned int addr)
429{
430 size_t i;
431
432 for (i = 0; i < ARRAY_SIZE(mmap_window_table); i++) {
433 const struct region *reg;
434
435 if (addr_type == HOST_SPACE_ADDR)
436 reg = &mmap_window_table[i].host_space;
437 else
438 reg = &mmap_window_table[i].flash_space;
439
Kyösti Mälkkib57373b2021-02-13 01:57:14 +0200440 if (region_offset(reg) <= addr &&
441 ((uint64_t)region_offset(reg) + (uint64_t)region_sz(reg) - 1) >= addr)
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800442 return i;
443 }
444
445 return -1;
446}
447
448static unsigned int convert_host_to_flash(const struct buffer *region, unsigned int addr)
449{
450 int idx;
451 const struct region *to, *from;
452
453 idx = find_mmap_window(HOST_SPACE_ADDR, addr);
454 if (idx == -1) {
455 ERROR("Host address(%x) not in any mmap window!\n", addr);
456 return 0;
457 }
458
459 to = &mmap_window_table[idx].flash_space;
460 from = &mmap_window_table[idx].host_space;
461
462 /* region->offset is subtracted because caller expects offset in the given region. */
463 return convert_address(to, from, addr) - region->offset;
464}
465
466static unsigned int convert_flash_to_host(const struct buffer *region, unsigned int addr)
467{
468 int idx;
469 const struct region *to, *from;
470
471 /*
472 * region->offset is added because caller provides offset in the given region. This is
473 * converted to an absolute address in the SPI flash space. This is done before the
474 * conversion as opposed to after in convert_host_to_flash() above because the address
475 * is actually an offset within the region. So, it needs to be converted into an
476 * absolute address in the SPI flash space before converting into an address in host
477 * space.
478 */
479 addr += region->offset;
480 idx = find_mmap_window(FLASH_SPACE_ADDR, addr);
481
482 if (idx == -1) {
483 ERROR("SPI flash address(%x) not in any mmap window!\n", addr);
484 return 0;
485 }
486
487 to = &mmap_window_table[idx].host_space;
488 from = &mmap_window_table[idx].flash_space;
489
490 return convert_address(to, from, addr);
491}
492
493static unsigned int convert_addr_space(const struct buffer *region, unsigned int addr)
Aaron Durbinab00d772016-05-04 16:07:15 -0500494{
495 assert(region);
496
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800497 assert(create_mmap_windows());
Aaron Durbinab00d772016-05-04 16:07:15 -0500498
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800499 if (IS_HOST_SPACE_ADDRESS(addr))
500 return convert_host_to_flash(region, addr);
501 else
502 return convert_flash_to_host(region, addr);
Aaron Durbinab00d772016-05-04 16:07:15 -0500503}
504
505/*
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800506 * This function takes offset value which represents the offset from one end of the region and
507 * converts it to offset from the other end of the region. offset is expected to be positive.
508 */
509static int convert_region_offset(unsigned int offset, uint32_t *region_offset)
Sol Boucher67d59982015-05-07 02:39:22 -0700510{
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800511 size_t size;
Sol Boucher67d59982015-05-07 02:39:22 -0700512
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800513 if (param.size) {
514 size = param.size;
515 } else {
516 assert(param.image_region);
517 size = param.image_region->size;
Patrick Georgi97311192015-12-07 23:14:07 +0100518 }
519
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800520 if (size < offset) {
521 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size, offset);
522 return 1;
523 }
524
525 *region_offset = size - offset;
526 return 0;
Sol Boucher67d59982015-05-07 02:39:22 -0700527}
528
Julius Werner6ddacd62021-12-03 14:46:53 -0800529static int do_cbfs_locate(uint32_t *cbfs_addr, size_t data_size)
Aaron Durbin4be16742015-09-15 17:00:23 -0500530{
Julius Werner6ddacd62021-12-03 14:46:53 -0800531 uint32_t metadata_size = 0;
532
Aaron Durbin4be16742015-09-15 17:00:23 -0500533 if (!param.name) {
534 ERROR("You need to specify -n/--name.\n");
535 return 1;
536 }
537
538 struct cbfs_image image;
539 if (cbfs_image_from_buffer(&image, param.image_region,
540 param.headeroffset))
541 return 1;
542
543 if (cbfs_get_entry(&image, param.name))
544 WARN("'%s' already in CBFS.\n", param.name);
545
Aaron Durbinfacf1492017-12-18 14:50:22 -0700546 if (!data_size) {
Julius Werner772714d2021-12-13 10:16:44 -0800547 ERROR("File '%s' is empty?\n", param.name);
548 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -0500549 }
550
Arthur Heymansc78521b32021-06-16 17:16:05 +0200551 /* Compute required page size */
Kyösti Mälkkic36469e2020-05-28 23:12:02 +0300552 if (param.force_pow2_pagesize) {
Arthur Heymansc78521b32021-06-16 17:16:05 +0200553 param.pagesize = 1;
554 while (param.pagesize < data_size)
555 param.pagesize <<= 1;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +0300556 DEBUG("Page size is %d (0x%x)\n", param.pagesize, param.pagesize);
557 }
558
Aaron Durbin4be16742015-09-15 17:00:23 -0500559 /* Include cbfs_file size along with space for with name. */
560 metadata_size += cbfs_calculate_file_header_size(param.name);
Werner Zehe9995f12016-01-14 13:22:37 +0100561 /* Adjust metadata_size if additional attributes were added */
562 if (param.autogen_attr) {
563 if (param.alignment)
564 metadata_size += sizeof(struct cbfs_file_attr_align);
565 if (param.baseaddress_assigned || param.stage_xip)
566 metadata_size += sizeof(struct cbfs_file_attr_position);
567 }
Julius Wernerc24db002020-11-18 18:00:31 -0800568 if (param.precompression || param.compression != CBFS_COMPRESS_NONE)
569 metadata_size += sizeof(struct cbfs_file_attr_compression);
Julius Werner6ddacd62021-12-03 14:46:53 -0800570 if (param.type == CBFS_TYPE_STAGE)
571 metadata_size += sizeof(struct cbfs_file_attr_stageheader);
Aaron Durbin4be16742015-09-15 17:00:23 -0500572
Werner Zehc7b2b7c2016-01-22 19:43:01 +0100573 /* Take care of the hash attribute if it is used */
574 if (param.hash != VB2_HASH_INVALID)
Julius Wernerd4775652020-03-13 16:43:34 -0700575 metadata_size += cbfs_file_attr_hash_size(param.hash);
Werner Zehc7b2b7c2016-01-22 19:43:01 +0100576
Aaron Durbinfacf1492017-12-18 14:50:22 -0700577 int32_t address = cbfs_locate_entry(&image, data_size, param.pagesize,
Aaron Durbin4be16742015-09-15 17:00:23 -0500578 param.alignment, metadata_size);
Aaron Durbin4be16742015-09-15 17:00:23 -0500579
Julius Wernerc24db002020-11-18 18:00:31 -0800580 if (address < 0) {
Julius Werner772714d2021-12-13 10:16:44 -0800581 ERROR("'%s'(%u + %zu) can't fit in CBFS for page-size %#x, align %#x.\n",
582 param.name, metadata_size, data_size, param.pagesize, param.alignment);
Aaron Durbin4be16742015-09-15 17:00:23 -0500583 return 1;
584 }
585
Aaron Durbin4be16742015-09-15 17:00:23 -0500586 *cbfs_addr = address;
587 return 0;
588}
589
Patrick Georgif8204972015-08-11 15:16:24 +0200590typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +0200591 struct cbfs_file *header);
Stefan Reinauer63217582012-10-29 16:52:36 -0700592
Sol Bouchere3260a02015-03-25 13:40:08 -0700593static int cbfs_add_integer_component(const char *name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200594 uint64_t u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800595 uint32_t offset,
596 uint32_t headeroffset) {
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200597 struct cbfs_image image;
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200598 struct cbfs_file *header = NULL;
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200599 struct buffer buffer;
600 int i, ret = 1;
601
602 if (!name) {
603 ERROR("You need to specify -n/--name.\n");
604 return 1;
605 }
606
607 if (buffer_create(&buffer, 8, name) != 0)
608 return 1;
609
610 for (i = 0; i < 8; i++)
611 buffer.data[i] = (u64val >> i*8) & 0xff;
612
Sol Bouchere3260a02015-03-25 13:40:08 -0700613 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset)) {
614 ERROR("Selected image region is not a CBFS.\n");
615 goto done;
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200616 }
617
618 if (cbfs_get_entry(&image, name)) {
619 ERROR("'%s' already in ROM image.\n", name);
620 goto done;
621 }
622
Julius Wernerd4775652020-03-13 16:43:34 -0700623 header = cbfs_create_file_header(CBFS_TYPE_RAW,
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200624 buffer.size, name);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700625
626 enum vb2_hash_algorithm algo = get_mh_cache()->cbfs_hash.algo;
627 if (algo != VB2_HASH_INVALID)
628 if (cbfs_add_file_hash(header, &buffer, algo)) {
629 ERROR("couldn't add hash for '%s'\n", name);
630 goto done;
631 }
632
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100633 if (cbfs_add_entry(&image, &buffer, offset, header, 0) != 0) {
Sol Boucher0e539312015-03-05 15:38:03 -0800634 ERROR("Failed to add %llu into ROM image as '%s'.\n",
635 (long long unsigned)u64val, name);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200636 goto done;
637 }
638
Julius Werner4bfbabd2020-05-06 17:27:02 -0700639 ret = maybe_update_metadata_hash(&image);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200640
641done:
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200642 free(header);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200643 buffer_delete(&buffer);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200644 return ret;
645}
646
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530647static int is_valid_topswap(void)
648{
649 switch (param.topswap_size) {
650 case (64 * KiB):
651 case (128 * KiB):
652 case (256 * KiB):
653 case (512 * KiB):
654 case (1 * MiB):
655 break;
656 default:
657 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
658 param.topswap_size);
659 return 0;
660 }
661 return 1;
662}
663
664static void fill_header_offset(void *location, uint32_t offset)
665{
Joel Kitching672d5ad2018-10-11 17:01:30 +0800666 // TODO: When we have a BE target, we'll need to store this as BE
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530667 write_le32(location, offset);
668}
669
670static int update_master_header_loc_topswap(struct cbfs_image *image,
671 void *h_loc, uint32_t header_offset)
672{
673 struct cbfs_file *entry;
674 void *ts_h_loc = h_loc;
675
676 entry = cbfs_get_entry(image, "bootblock");
677 if (entry == NULL) {
678 ERROR("Bootblock not in ROM image?!?\n");
679 return 1;
680 }
681
682 /*
683 * Check if the existing topswap boundary matches with
684 * the one provided.
685 */
Alex James02001a382021-12-19 16:41:59 -0600686 if (param.topswap_size != be32toh(entry->len)/2) {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530687 ERROR("Top swap boundary does not match\n");
688 return 1;
689 }
690
691 ts_h_loc -= param.topswap_size;
692 fill_header_offset(ts_h_loc, header_offset);
693
694 return 0;
695}
696
Patrick Georgi59e52b92015-09-10 15:28:27 +0200697static int cbfs_add_master_header(void)
698{
699 const char * const name = "cbfs master header";
700 struct cbfs_image image;
701 struct cbfs_file *header = NULL;
702 struct buffer buffer;
703 int ret = 1;
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600704 size_t offset;
705 size_t size;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530706 void *h_loc;
Patrick Georgi59e52b92015-09-10 15:28:27 +0200707
708 if (cbfs_image_from_buffer(&image, param.image_region,
709 param.headeroffset)) {
710 ERROR("Selected image region is not a CBFS.\n");
711 return 1;
712 }
713
714 if (cbfs_get_entry(&image, name)) {
715 ERROR("'%s' already in ROM image.\n", name);
716 return 1;
717 }
718
719 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
720 return 1;
721
722 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
Alex James02001a382021-12-19 16:41:59 -0600723 h->magic = htobe32(CBFS_HEADER_MAGIC);
724 h->version = htobe32(CBFS_HEADER_VERSION);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200725 /* The 4 bytes are left out for two reasons:
726 * 1. the cbfs master header pointer resides there
727 * 2. some cbfs implementations assume that an image that resides
728 * below 4GB has a bootblock and get confused when the end of the
729 * image is at 4GB == 0.
730 */
Alex James02001a382021-12-19 16:41:59 -0600731 h->bootblocksize = htobe32(4);
732 h->align = htobe32(CBFS_ALIGNMENT);
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600733 /* The offset and romsize fields within the master header are absolute
734 * values within the boot media. As such, romsize needs to relfect
735 * the end 'offset' for a CBFS. To achieve that the current buffer
736 * representing the CBFS region's size is added to the offset of
737 * the region within a larger image.
Patrick Georgi59e52b92015-09-10 15:28:27 +0200738 */
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600739 offset = buffer_get(param.image_region) -
740 buffer_get_original_backing(param.image_region);
741 size = buffer_size(param.image_region);
Alex James02001a382021-12-19 16:41:59 -0600742 h->romsize = htobe32(size + offset);
743 h->offset = htobe32(offset);
744 h->architecture = htobe32(CBFS_ARCHITECTURE_UNKNOWN);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200745
Julius Werner4bfbabd2020-05-06 17:27:02 -0700746 /* Never add a hash attribute to the master header. */
Julius Wernerd4775652020-03-13 16:43:34 -0700747 header = cbfs_create_file_header(CBFS_TYPE_CBFSHEADER,
Patrick Georgi59e52b92015-09-10 15:28:27 +0200748 buffer_size(&buffer), name);
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100749 if (cbfs_add_entry(&image, &buffer, 0, header, 0) != 0) {
Patrick Georgi59e52b92015-09-10 15:28:27 +0200750 ERROR("Failed to add cbfs master header into ROM image.\n");
751 goto done;
752 }
753
754 struct cbfs_file *entry;
755 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
756 ERROR("'%s' not in ROM image?!?\n", name);
757 goto done;
758 }
759
760 uint32_t header_offset = CBFS_SUBHEADER(entry) -
761 buffer_get(&image.buffer);
762 header_offset = -(buffer_size(&image.buffer) - header_offset);
763
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530764 h_loc = (void *)(buffer_get(&image.buffer) +
765 buffer_size(&image.buffer) - 4);
766 fill_header_offset(h_loc, header_offset);
767 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800768 * If top swap present, update the header
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530769 * location in secondary bootblock
770 */
771 if (param.topswap_size) {
772 if (update_master_header_loc_topswap(&image, h_loc,
Shaik Shahina425413c2022-11-05 01:07:06 +0530773 header_offset))
774 goto done;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530775 }
Patrick Georgi59e52b92015-09-10 15:28:27 +0200776
Julius Werner4bfbabd2020-05-06 17:27:02 -0700777 ret = maybe_update_metadata_hash(&image);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200778
779done:
780 free(header);
781 buffer_delete(&buffer);
782 return ret;
783}
784
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530785static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
786{
787 size_t bb_buf_size = buffer_size(buffer);
788
789 if (bb_buf_size > param.topswap_size) {
790 ERROR("Bootblock bigger than the topswap boundary\n");
791 ERROR("size = %zd, ts = %d\n", bb_buf_size,
792 param.topswap_size);
793 return 1;
794 }
795
796 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800797 * Allocate topswap_size*2 bytes for bootblock to
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530798 * accommodate the second bootblock.
799 */
800 struct buffer new_bootblock, bb1, bb2;
801 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
802 buffer->name))
803 return 1;
804
805 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
806 bb_buf_size);
807 buffer_splice(&bb2, &new_bootblock,
808 buffer_size(&new_bootblock) - bb_buf_size,
809 bb_buf_size);
810
Joel Kitching672d5ad2018-10-11 17:01:30 +0800811 /* Copy to first bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530812 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
Joel Kitching672d5ad2018-10-11 17:01:30 +0800813 /* Copy to second bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530814 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
815
816 buffer_delete(buffer);
817 buffer_clone(buffer, &new_bootblock);
818
Joel Kitching672d5ad2018-10-11 17:01:30 +0800819 /* Update the location (offset) of bootblock in the region */
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800820 return convert_region_offset(buffer_size(buffer), offset);
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530821}
822
Sol Bouchere3260a02015-03-25 13:40:08 -0700823static int cbfs_add_component(const char *filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800824 const char *name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800825 uint32_t headeroffset,
Stefan Reinauer2dd161f2015-03-04 00:55:03 +0100826 convert_buffer_t convert)
827{
Julius Werner6ddacd62021-12-03 14:46:53 -0800828 /*
829 * The steps used to determine the final placement offset in CBFS, in order:
830 *
Julius Werner20ad3652021-12-09 13:20:59 -0800831 * 1. If --base-address was passed, that value is used. If it was passed in the host
832 * address space, convert it to flash address space. (After that, |*offset| is always
833 * in the flash address space.)
Julius Werner6ddacd62021-12-03 14:46:53 -0800834 *
835 * 2. The convert() function may write a location back to |offset|, usually by calling
836 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
837 * the CBFS file in its final form (after any compression and conversion).
838 *
839 * 3. If --align was passed and the offset is still undecided at this point,
840 * do_cbfs_locate() is called to find an appropriately aligned location.
841 *
842 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
843 * location that fits.
844 */
Julius Wernerc24db002020-11-18 18:00:31 -0800845 uint32_t offset = param.baseaddress_assigned ? param.baseaddress : 0;
Julius Werner6ddacd62021-12-03 14:46:53 -0800846 size_t len_align = 0;
Julius Wernerc24db002020-11-18 18:00:31 -0800847
848 if (param.alignment && param.baseaddress_assigned) {
849 ERROR("Cannot specify both alignment and base address\n");
850 return 1;
851 }
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100852
Julius Werner6ddacd62021-12-03 14:46:53 -0800853 if (param.stage_xip && param.compression != CBFS_COMPRESS_NONE) {
854 ERROR("Cannot specify compression for XIP.\n");
855 return 1;
856 }
857
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800858 if (!filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800859 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700860 return 1;
861 }
862
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800863 if (!name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800864 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700865 return 1;
866 }
867
Julius Werner6ddacd62021-12-03 14:46:53 -0800868 if (param.type == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800869 ERROR("You need to specify a valid -t/--type.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700870 return 1;
871 }
872
Sol Bouchere3260a02015-03-25 13:40:08 -0700873 struct cbfs_image image;
Sol Boucher67a0a862015-03-18 12:36:27 -0700874 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800875 return 1;
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800876
Patrick Georgi4e54bf92015-08-11 14:35:39 +0200877 if (cbfs_get_entry(&image, name)) {
878 ERROR("'%s' already in ROM image.\n", name);
879 return 1;
880 }
881
Sol Bouchere3260a02015-03-25 13:40:08 -0700882 struct buffer buffer;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800883 if (buffer_from_file(&buffer, filename) != 0) {
884 ERROR("Could not load file '%s'.\n", filename);
Stefan Reinauer63217582012-10-29 16:52:36 -0700885 return 1;
886 }
887
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200888 struct cbfs_file *header =
Julius Werner6ddacd62021-12-03 14:46:53 -0800889 cbfs_create_file_header(param.type, buffer.size, name);
Patrick Georgi4110abc2015-08-11 15:10:33 +0200890
Julius Werner4bfbabd2020-05-06 17:27:02 -0700891 /* Bootblock and CBFS header should never have file hashes. When adding
892 the bootblock it is important that we *don't* look up the metadata
893 hash yet (before it is added) or we'll cache an outdated result. */
Julius Werneraf20fd72022-05-27 18:33:15 -0700894 if (!verification_exclude(param.type)) {
Julius Werner4bfbabd2020-05-06 17:27:02 -0700895 enum vb2_hash_algorithm mh_algo = get_mh_cache()->cbfs_hash.algo;
896 if (mh_algo != VB2_HASH_INVALID && param.hash != mh_algo) {
897 if (param.hash == VB2_HASH_INVALID) {
898 param.hash = mh_algo;
899 } else {
900 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
901 vb2_get_hash_algorithm_name(param.hash),
902 vb2_get_hash_algorithm_name(mh_algo));
903 goto error;
904 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200905 }
Julius Wernerc24db002020-11-18 18:00:31 -0800906 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200907
Julius Wernerc24db002020-11-18 18:00:31 -0800908 /*
909 * Check if Intel CPU topswap is specified this will require a
910 * second bootblock to be added.
911 */
Julius Werner6ddacd62021-12-03 14:46:53 -0800912 if (param.type == CBFS_TYPE_BOOTBLOCK && param.topswap_size)
Julius Wernerc24db002020-11-18 18:00:31 -0800913 if (add_topswap_bootblock(&buffer, &offset))
914 goto error;
915
Julius Werner20ad3652021-12-09 13:20:59 -0800916 /* With --base-address we allow host space addresses -- if so, convert it here. */
917 if (IS_HOST_SPACE_ADDRESS(offset))
918 offset = convert_addr_space(param.image_region, offset);
919
Julius Wernerc24db002020-11-18 18:00:31 -0800920 if (convert && convert(&buffer, &offset, header) != 0) {
921 ERROR("Failed to parse file '%s'.\n", filename);
922 goto error;
923 }
924
Julius Werner6ddacd62021-12-03 14:46:53 -0800925 /* This needs to run after convert() to take compression into account. */
926 if (!offset && param.alignment)
Julius Werner772714d2021-12-13 10:16:44 -0800927 if (do_cbfs_locate(&offset, buffer_size(&buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -0800928 goto error;
929
930 /* This needs to run after convert() to hash the actual final file data. */
Julius Wernerc24db002020-11-18 18:00:31 -0800931 if (param.hash != VB2_HASH_INVALID &&
932 cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
933 ERROR("couldn't add hash for '%s'\n", name);
934 goto error;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700935 }
936
Werner Zehe9995f12016-01-14 13:22:37 +0100937 if (param.autogen_attr) {
938 /* Add position attribute if assigned */
939 if (param.baseaddress_assigned || param.stage_xip) {
940 struct cbfs_file_attr_position *attrs =
941 (struct cbfs_file_attr_position *)
942 cbfs_add_file_attr(header,
943 CBFS_FILE_ATTR_TAG_POSITION,
944 sizeof(struct cbfs_file_attr_position));
945 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800946 goto error;
Alex James02001a382021-12-19 16:41:59 -0600947 attrs->position = htobe32(offset);
Werner Zehe9995f12016-01-14 13:22:37 +0100948 }
949 /* Add alignment attribute if used */
950 if (param.alignment) {
951 struct cbfs_file_attr_align *attrs =
952 (struct cbfs_file_attr_align *)
953 cbfs_add_file_attr(header,
954 CBFS_FILE_ATTR_TAG_ALIGNMENT,
955 sizeof(struct cbfs_file_attr_align));
956 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800957 goto error;
Alex James02001a382021-12-19 16:41:59 -0600958 attrs->alignment = htobe32(param.alignment);
Werner Zehe9995f12016-01-14 13:22:37 +0100959 }
960 }
961
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100962 if (param.ibb) {
963 /* Mark as Initial Boot Block */
964 struct cbfs_file_attribute *attrs = cbfs_add_file_attr(header,
965 CBFS_FILE_ATTR_TAG_IBB,
966 sizeof(struct cbfs_file_attribute));
967 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800968 goto error;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100969 /* For Intel TXT minimum align is 16 */
970 len_align = 16;
971 }
972
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700973 if (param.padding) {
974 const uint32_t hs = sizeof(struct cbfs_file_attribute);
Julius Werner5779ca72020-11-20 16:12:40 -0800975 uint32_t size = ALIGN_UP(MAX(hs, param.padding),
976 CBFS_ATTRIBUTE_ALIGN);
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700977 INFO("Padding %d bytes\n", size);
978 struct cbfs_file_attribute *attr =
979 (struct cbfs_file_attribute *)cbfs_add_file_attr(
980 header, CBFS_FILE_ATTR_TAG_PADDING,
981 size);
982 if (attr == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800983 goto error;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -0700984 }
985
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100986 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800987 ERROR("Failed to add '%s' into ROM image.\n", filename);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700988 goto error;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800989 }
990
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200991 free(header);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800992 buffer_delete(&buffer);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700993
994 return maybe_update_metadata_hash(&image) || maybe_update_fmap_hash();
995
996error:
997 free(header);
998 buffer_delete(&buffer);
999 return 1;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001000}
1001
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001002static int cbfstool_convert_raw(struct buffer *buffer,
1003 unused uint32_t *offset, struct cbfs_file *header)
1004{
1005 char *compressed;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001006 int decompressed_size, compressed_size;
1007 comp_func_ptr compress;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001008
Patrick Georgiecaa5702017-01-11 18:38:11 +01001009 decompressed_size = buffer->size;
1010 if (param.precompression) {
Nico Huber607796a2017-01-17 13:01:25 +01001011 param.compression = read_le32(buffer->data);
1012 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
Patrick Georgiecaa5702017-01-11 18:38:11 +01001013 compressed_size = buffer->size - 8;
1014 compressed = malloc(compressed_size);
1015 if (!compressed)
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001016 return -1;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001017 memcpy(compressed, buffer->data + 8, compressed_size);
1018 } else {
Julius Werner105cdf52020-10-23 19:19:32 -07001019 if (param.compression == CBFS_COMPRESS_NONE)
1020 goto out;
1021
Patrick Georgiecaa5702017-01-11 18:38:11 +01001022 compress = compression_function(param.compression);
1023 if (!compress)
1024 return -1;
1025 compressed = calloc(buffer->size, 1);
1026 if (!compressed)
1027 return -1;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001028
Patrick Georgiecaa5702017-01-11 18:38:11 +01001029 if (compress(buffer->data, buffer->size,
1030 compressed, &compressed_size)) {
1031 WARN("Compression failed - disabled\n");
1032 free(compressed);
Julius Werner105cdf52020-10-23 19:19:32 -07001033 goto out;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001034 }
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001035 }
Patrick Georgiecaa5702017-01-11 18:38:11 +01001036
1037 struct cbfs_file_attr_compression *attrs =
1038 (struct cbfs_file_attr_compression *)
1039 cbfs_add_file_attr(header,
1040 CBFS_FILE_ATTR_TAG_COMPRESSION,
1041 sizeof(struct cbfs_file_attr_compression));
1042 if (attrs == NULL) {
1043 free(compressed);
1044 return -1;
1045 }
Alex James02001a382021-12-19 16:41:59 -06001046 attrs->compression = htobe32(param.compression);
1047 attrs->decompressed_size = htobe32(decompressed_size);
Patrick Georgiecaa5702017-01-11 18:38:11 +01001048
1049 free(buffer->data);
1050 buffer->data = compressed;
1051 buffer->size = compressed_size;
1052
Julius Werner105cdf52020-10-23 19:19:32 -07001053out:
Alex James02001a382021-12-19 16:41:59 -06001054 header->len = htobe32(buffer->size);
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001055 return 0;
1056}
1057
Aaron Durbin8a414a02015-10-01 17:02:45 -05001058static int cbfstool_convert_fsp(struct buffer *buffer,
1059 uint32_t *offset, struct cbfs_file *header)
1060{
1061 uint32_t address;
1062 struct buffer fsp;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001063
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001064 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001065 * There are 4 different cases here:
1066 *
1067 * 1. --xip and --base-address: we need to place the binary at the given base address
Julius Werner20ad3652021-12-09 13:20:59 -08001068 * in the CBFS image and relocate it to that address. *offset was already filled in,
1069 * but we need to convert it to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001070 *
1071 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1072 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
Julius Werner20ad3652021-12-09 13:20:59 -08001073 * This also needs to be converted to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001074 *
1075 * 3. No --xip but a --base-address: special case where --base-address does not have its
1076 * normal meaning, instead we use it as the relocation target address. We explicitly
1077 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1078 *
1079 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1080 * not be relocated. Just chain directly to convert_raw() for compression.
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001081 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001082
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001083 if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001084 if (!param.baseaddress_assigned) {
1085 param.alignment = 4*1024;
Julius Werner772714d2021-12-13 10:16:44 -08001086 if (do_cbfs_locate(offset, buffer_size(buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -08001087 return -1;
1088 }
Julius Werner20ad3652021-12-09 13:20:59 -08001089 assert(!IS_HOST_SPACE_ADDRESS(*offset));
1090 address = convert_addr_space(param.image_region, *offset);
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001091 } else {
Aaron Durbin493ec922016-05-16 15:18:45 -05001092 if (param.baseaddress_assigned == 0) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001093 INFO("Honoring pre-linked FSP module, no relocation.\n");
1094 return cbfstool_convert_raw(buffer, offset, header);
Aaron Durbin493ec922016-05-16 15:18:45 -05001095 } else {
1096 address = param.baseaddress;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001097 *offset = 0;
Furquan Shaikhc3bb6922020-06-09 21:20:27 -07001098 }
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001099 }
Aaron Durbin8a414a02015-10-01 17:02:45 -05001100
1101 /* Create a copy of the buffer to attempt relocation. */
1102 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
1103 return -1;
1104
1105 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
1106
1107 /* Replace the buffer contents w/ the relocated ones on success. */
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001108 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
1109 > 0) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001110 buffer_delete(buffer);
1111 buffer_clone(buffer, &fsp);
1112 } else {
1113 buffer_delete(&fsp);
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001114 WARN("Invalid FSP variant.\n");
Aaron Durbin8a414a02015-10-01 17:02:45 -05001115 }
1116
1117 /* Let the raw path handle all the cbfs metadata logic. */
1118 return cbfstool_convert_raw(buffer, offset, header);
1119}
1120
Patrick Georgif8204972015-08-11 15:16:24 +02001121static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001122 struct cbfs_file *header)
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001123{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001124 struct buffer output;
Julius Wernerc24db002020-11-18 18:00:31 -08001125 size_t data_size;
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001126 int ret;
Aaron Durbin4be16742015-09-15 17:00:23 -05001127
Julius Wernerc24db002020-11-18 18:00:31 -08001128 if (elf_program_file_size(buffer, &data_size) < 0) {
1129 ERROR("Could not obtain ELF size\n");
1130 return 1;
1131 }
1132
1133 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001134 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1135 * here. That is okay because XIP stages may not be compressed, so their size cannot
1136 * change anymore at a later point.
Julius Wernerc24db002020-11-18 18:00:31 -08001137 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001138 if (param.stage_xip &&
1139 do_cbfs_locate(offset, data_size)) {
Julius Wernerc24db002020-11-18 18:00:31 -08001140 ERROR("Could not find location for stage.\n");
1141 return 1;
1142 }
1143
Julius Werner81dc20e2020-10-15 17:37:57 -07001144 struct cbfs_file_attr_stageheader *stageheader = (void *)
1145 cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
1146 sizeof(struct cbfs_file_attr_stageheader));
1147 if (!stageheader)
1148 return -1;
1149
Aaron Durbin4be16742015-09-15 17:00:23 -05001150 if (param.stage_xip) {
Julius Werner20ad3652021-12-09 13:20:59 -08001151 uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
1152 assert(IS_HOST_SPACE_ADDRESS(host_space_address));
1153 ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
1154 param.ignore_section, stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001155 } else {
Julius Werner81dc20e2020-10-15 17:37:57 -07001156 ret = parse_elf_to_stage(buffer, &output, param.ignore_section,
1157 stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001158 }
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001159 if (ret != 0)
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001160 return -1;
Julius Werner81dc20e2020-10-15 17:37:57 -07001161
1162 /* Store a hash of original uncompressed stage to compare later. */
1163 size_t decmp_size = buffer_size(&output);
1164 uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
1165
1166 /* Chain to base conversion routine to handle compression. */
1167 ret = cbfstool_convert_raw(&output, offset, header);
1168 if (ret != 0)
1169 goto fail;
1170
1171 /* Special care must be taken for LZ4-compressed stages that the BSS is
1172 large enough to provide scratch space for in-place decompression. */
1173 if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
Alex James02001a382021-12-19 16:41:59 -06001174 size_t memlen = be32toh(stageheader->memlen);
Julius Werner81dc20e2020-10-15 17:37:57 -07001175 size_t compressed_size = buffer_size(&output);
1176 uint8_t *compare_buffer = malloc(memlen);
1177 uint8_t *start = compare_buffer + memlen - compressed_size;
1178 if (!compare_buffer) {
1179 ERROR("Out of memory\n");
1180 goto fail;
1181 }
1182 memcpy(start, buffer_get(&output), compressed_size);
1183 ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
1184 if (ret == 0) {
1185 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1186 free(compare_buffer);
1187 goto fail;
1188 } else if (ret != (int)decmp_size ||
1189 decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
1190 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1191 free(compare_buffer);
1192 goto fail;
1193 }
1194 free(compare_buffer);
1195 }
1196
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001197 buffer_delete(buffer);
Julius Werner81dc20e2020-10-15 17:37:57 -07001198 buffer_clone(buffer, &output);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001199 return 0;
Julius Werner81dc20e2020-10-15 17:37:57 -07001200
1201fail:
1202 buffer_delete(&output);
1203 return -1;
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001204}
1205
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001206static int cbfstool_convert_mkpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001207 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001208{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001209 struct buffer output;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001210 int ret;
Joel Kitching672d5ad2018-10-11 17:01:30 +08001211 /* Per default, try and see if payload is an ELF binary */
Sol Boucher65336712015-05-07 21:00:05 -07001212 ret = parse_elf_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001213
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001214 /* If it's not an ELF, see if it's a FIT */
1215 if (ret != 0) {
1216 ret = parse_fit_to_payload(buffer, &output, param.compression);
1217 if (ret == 0)
Julius Werner00572622022-05-26 20:29:42 -07001218 header->type = htobe32(CBFS_TYPE_FIT_PAYLOAD);
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001219 }
1220
1221 /* If it's not an FIT, see if it's a UEFI FV */
Stefan Reinauer543a6822013-02-04 15:39:13 -08001222 if (ret != 0)
Sol Boucher65336712015-05-07 21:00:05 -07001223 ret = parse_fv_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001224
Patrick Georgide36d332013-08-27 20:22:21 +02001225 /* If it's neither ELF nor UEFI Fv, try bzImage */
1226 if (ret != 0)
1227 ret = parse_bzImage_to_payload(buffer, &output,
Sol Boucher65336712015-05-07 21:00:05 -07001228 param.initrd, param.cmdline, param.compression);
Patrick Georgide36d332013-08-27 20:22:21 +02001229
Stefan Reinauer543a6822013-02-04 15:39:13 -08001230 /* Not a supported payload type */
1231 if (ret != 0) {
1232 ERROR("Not a supported payload type (ELF / FV).\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001233 buffer_delete(buffer);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001234 return -1;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001235 }
1236
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001237 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001238 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001239 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001240 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001241 return 0;
1242}
1243
1244static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001245 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001246{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001247 struct buffer output;
1248 if (parse_flat_binary_to_payload(buffer, &output,
1249 param.loadaddress,
1250 param.entrypoint,
Sol Boucher65336712015-05-07 21:00:05 -07001251 param.compression) != 0) {
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001252 return -1;
1253 }
1254 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001255 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001256 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001257 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001258 return 0;
1259}
1260
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001261static int cbfs_add(void)
1262{
Julius Wernerc24db002020-11-18 18:00:31 -08001263 convert_buffer_t convert = cbfstool_convert_raw;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001264
Julius Wernerd4775652020-03-13 16:43:34 -07001265 if (param.type == CBFS_TYPE_FSP) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001266 convert = cbfstool_convert_fsp;
Julius Werner6ddacd62021-12-03 14:46:53 -08001267 } else if (param.type == CBFS_TYPE_STAGE) {
1268 ERROR("stages can only be added with cbfstool add-stage\n");
1269 return 1;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001270 } else if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001271 ERROR("cbfstool add supports xip only for FSP component type\n");
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001272 return 1;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001273 }
1274
Sol Bouchere3260a02015-03-25 13:40:08 -07001275 return cbfs_add_component(param.filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001276 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001277 param.headeroffset,
Aaron Durbin8a414a02015-10-01 17:02:45 -05001278 convert);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001279}
1280
Stefan Reinauer63217582012-10-29 16:52:36 -07001281static int cbfs_add_stage(void)
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001282{
Julius Werner6ddacd62021-12-03 14:46:53 -08001283 if (param.stage_xip && param.baseaddress_assigned) {
1284 ERROR("Cannot specify base address for XIP.\n");
1285 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -05001286 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001287 param.type = CBFS_TYPE_STAGE;
Aaron Durbin4be16742015-09-15 17:00:23 -05001288
Sol Bouchere3260a02015-03-25 13:40:08 -07001289 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001290 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001291 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001292 cbfstool_convert_mkstage);
1293}
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001294
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001295static int cbfs_add_payload(void)
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_mkpayload);
Stefan Reinauer63217582012-10-29 16:52:36 -07001302}
1303
1304static int cbfs_add_flat_binary(void)
1305{
Hung-Te Lind1739622013-01-28 14:23:49 +08001306 if (param.loadaddress == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001307 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001308 "-l/--load-address.\n");
1309 return 1;
1310 }
Hung-Te Lind1739622013-01-28 14:23:49 +08001311 if (param.entrypoint == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001312 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001313 "-e/--entry-point.\n");
1314 return 1;
1315 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001316 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001317 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001318 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001319 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001320 cbfstool_convert_mkflatpayload);
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001321}
1322
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001323static int cbfs_add_integer(void)
1324{
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01001325 if (!param.u64val_assigned) {
1326 ERROR("You need to specify a value to write.\n");
1327 return 1;
1328 }
Sol Bouchere3260a02015-03-25 13:40:08 -07001329 return cbfs_add_integer_component(param.name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001330 param.u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001331 param.baseaddress,
1332 param.headeroffset);
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001333}
1334
Stefan Reinauer63217582012-10-29 16:52:36 -07001335static int cbfs_remove(void)
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001336{
Hung-Te Lind1739622013-01-28 14:23:49 +08001337 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001338 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001339 return 1;
1340 }
1341
Sol Bouchere3260a02015-03-25 13:40:08 -07001342 struct cbfs_image image;
1343 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001344 param.headeroffset))
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001345 return 1;
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001346
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001347 if (cbfs_remove_entry(&image, param.name) != 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001348 ERROR("Removing file '%s' failed.\n",
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001349 param.name);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001350 return 1;
1351 }
1352
Julius Werner4bfbabd2020-05-06 17:27:02 -07001353 return maybe_update_metadata_hash(&image);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001354}
1355
Stefan Reinauer63217582012-10-29 16:52:36 -07001356static int cbfs_create(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001357{
Sol Boucher67a0a862015-03-18 12:36:27 -07001358 struct cbfs_image image;
1359 memset(&image, 0, sizeof(image));
1360 buffer_clone(&image.buffer, param.image_region);
1361
1362 if (param.fmap) {
1363 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
1364 param.baseaddress_assigned ||
1365 param.headeroffset_assigned ||
1366 param.cbfsoffset_assigned ||
Sol Boucher67a0a862015-03-18 12:36:27 -07001367 param.bootblock) {
Patrick Georgi45acb342015-07-14 22:18:23 +02001368 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
Sol Boucher67a0a862015-03-18 12:36:27 -07001369 return 1;
1370 }
1371
1372 return cbfs_image_create(&image, image.buffer.size);
1373 }
1374
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001375 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
Stefan Reinauer60a4a732013-03-28 16:46:07 -07001376 ERROR("You need to specify -m/--machine arch.\n");
David Hendricks90ca3b62012-11-16 14:48:22 -08001377 return 1;
1378 }
1379
Sol Bouchere3260a02015-03-25 13:40:08 -07001380 struct buffer bootblock;
Julius Wernerefcee762014-11-10 13:14:24 -08001381 if (!param.bootblock) {
1382 DEBUG("-B not given, creating image without bootblock.\n");
Patrick Georgi6b2d2db2016-12-14 16:11:58 +01001383 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
1384 return 1;
Julius Wernerefcee762014-11-10 13:14:24 -08001385 } else if (buffer_from_file(&bootblock, param.bootblock)) {
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001386 return 1;
1387 }
1388
Julius Wernerefcee762014-11-10 13:14:24 -08001389 if (!param.alignment)
Patrick Georgi45acb342015-07-14 22:18:23 +02001390 param.alignment = CBFS_ALIGNMENT;
Julius Wernerefcee762014-11-10 13:14:24 -08001391
1392 // Set default offsets. x86, as usual, needs to be a special snowflake.
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001393 if (!param.baseaddress_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001394 if (param.arch == CBFS_ARCHITECTURE_X86) {
1395 // Make sure there's at least enough room for rel_offset
Sol Boucher67a0a862015-03-18 12:36:27 -07001396 param.baseaddress = param.size -
1397 MAX(bootblock.size, sizeof(int32_t));
Julius Wernerefcee762014-11-10 13:14:24 -08001398 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1399 param.baseaddress);
1400 } else {
1401 param.baseaddress = 0;
1402 DEBUG("bootblock starts at address 0x0.\n");
1403 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001404 }
1405 if (!param.headeroffset_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001406 if (param.arch == CBFS_ARCHITECTURE_X86) {
1407 param.headeroffset = param.baseaddress -
1408 sizeof(struct cbfs_header);
1409 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1410 param.headeroffset);
1411 } else {
1412 param.headeroffset = align_up(param.baseaddress +
1413 bootblock.size, sizeof(uint32_t));
1414 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1415 param.headeroffset);
1416 }
1417 }
1418 if (!param.cbfsoffset_assigned) {
1419 if (param.arch == CBFS_ARCHITECTURE_X86) {
1420 param.cbfsoffset = 0;
1421 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1422 } else {
1423 param.cbfsoffset = align_up(param.headeroffset +
1424 sizeof(struct cbfs_header),
Patrick Georgi45acb342015-07-14 22:18:23 +02001425 CBFS_ALIGNMENT);
Julius Wernerefcee762014-11-10 13:14:24 -08001426 DEBUG("CBFS entries start beind master header (%#x).\n",
1427 param.cbfsoffset);
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001428 }
1429 }
1430
Sol Boucher67a0a862015-03-18 12:36:27 -07001431 int ret = cbfs_legacy_image_create(&image,
1432 param.arch,
Patrick Georgi45acb342015-07-14 22:18:23 +02001433 CBFS_ALIGNMENT,
Sol Boucher67a0a862015-03-18 12:36:27 -07001434 &bootblock,
1435 param.baseaddress,
1436 param.headeroffset,
1437 param.cbfsoffset);
Sol Bouchere3260a02015-03-25 13:40:08 -07001438 buffer_delete(&bootblock);
Sol Boucher67a0a862015-03-18 12:36:27 -07001439 return ret;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001440}
1441
Sol Boucher67a0a862015-03-18 12:36:27 -07001442static int cbfs_layout(void)
1443{
1444 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1445 if (!fmap) {
1446 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1447 return 1;
1448 }
1449
1450 printf("This image contains the following sections that can be %s with this tool:\n",
1451 param.show_immutable ? "accessed" : "manipulated");
1452 puts("");
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001453 for (unsigned i = 0; i < fmap->nareas; ++i) {
1454 const struct fmap_area *current = fmap->areas + i;
Sol Boucher67a0a862015-03-18 12:36:27 -07001455
1456 bool readonly = partitioned_file_fmap_count(param.image_file,
1457 partitioned_file_fmap_select_children_of, current) ||
1458 region_is_flashmap((const char *)current->name);
1459 if (!param.show_immutable && readonly)
1460 continue;
1461
1462 printf("'%s'", current->name);
1463
1464 // Detect consecutive sections that describe the same region and
1465 // show them as aliases. This cannot find equivalent entries
1466 // that aren't adjacent; however, fmaptool doesn't generate
1467 // FMAPs with such sections, so this convenience feature works
1468 // for all but the strangest manually created FMAP binaries.
1469 // TODO: This could be done by parsing the FMAP into some kind
1470 // of tree that had duplicate lists in addition to child lists,
1471 // which would allow covering that weird, unlikely case as well.
1472 unsigned lookahead;
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001473 for (lookahead = 1; i + lookahead < fmap->nareas;
Sol Boucher67a0a862015-03-18 12:36:27 -07001474 ++lookahead) {
1475 const struct fmap_area *consecutive =
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001476 fmap->areas + i + lookahead;
Sol Boucher67a0a862015-03-18 12:36:27 -07001477 if (consecutive->offset != current->offset ||
1478 consecutive->size != current->size)
1479 break;
1480 printf(", '%s'", consecutive->name);
1481 }
1482 if (lookahead > 1)
1483 fputs(" are aliases for the same region", stdout);
1484
1485 const char *qualifier = "";
1486 if (readonly)
1487 qualifier = "read-only, ";
1488 else if (region_is_modern_cbfs((const char *)current->name))
1489 qualifier = "CBFS, ";
Patrick Georgi848e30d2019-06-18 22:02:13 +02001490 else if (current->flags & FMAP_AREA_PRESERVE)
1491 qualifier = "preserve, ";
Werner Zeh7850b582018-04-26 09:26:38 +02001492 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1493 current->offset);
Sol Boucher67a0a862015-03-18 12:36:27 -07001494
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001495 i += lookahead - 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001496 }
1497 puts("");
1498
1499 if (param.show_immutable) {
1500 puts("It is at least possible to perform the read action on every section listed above.");
1501 } else {
1502 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1503 puts("To see the image's read-only sections as well, rerun with the -w option.");
1504 }
1505
1506 return 0;
1507}
1508
Bill XIEac1362502022-07-08 16:53:21 +08001509static enum cb_err verify_walker(__always_unused cbfs_dev_t dev, size_t offset,
Julius Werner423cd062022-05-24 18:18:46 -07001510 const union cbfs_mdata *mdata, size_t already_read, void *arg)
1511{
1512 uint32_t type = be32toh(mdata->h.type);
1513 uint32_t data_offset = be32toh(mdata->h.offset);
1514 if (verification_exclude(type))
1515 return CB_CBFS_NOT_FOUND;
1516 assert(already_read == data_offset);
1517 const struct vb2_hash *hash = cbfs_file_hash(mdata);
1518 if (!hash)
1519 return CB_ERR;
1520 void *file_data = arg + offset + data_offset;
Julius Wernerd96ca242022-08-08 18:08:35 -07001521 if (vb2_hash_verify(false, file_data, be32toh(mdata->h.len), hash) != VB2_SUCCESS)
Julius Werner423cd062022-05-24 18:18:46 -07001522 return CB_CBFS_HASH_MISMATCH;
1523 return CB_CBFS_NOT_FOUND;
1524}
1525
Stefan Reinauer63217582012-10-29 16:52:36 -07001526static int cbfs_print(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001527{
Hung-Te Lin3bb035b2013-01-29 02:15:49 +08001528 struct cbfs_image image;
Sol Bouchere3260a02015-03-25 13:40:08 -07001529 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001530 param.headeroffset))
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001531 return 1;
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001532 if (param.machine_parseable) {
1533 if (verbose)
1534 printf("[FMAP REGION]\t%s\n", param.region_name);
1535 cbfs_print_parseable_directory(&image);
1536 } else {
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001537 printf("FMAP REGION: %s\n", param.region_name);
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001538 cbfs_print_directory(&image);
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001539 }
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001540
Julius Werner4bfbabd2020-05-06 17:27:02 -07001541 if (verbose) {
Julius Werner423cd062022-05-24 18:18:46 -07001542 const char *verification_state = "fully valid";
Julius Werner4bfbabd2020-05-06 17:27:02 -07001543 struct mh_cache *mhc = get_mh_cache();
1544 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1545 return 0;
1546
1547 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
Julius Werner423cd062022-05-24 18:18:46 -07001548 enum cb_err err = cbfs_walk(&image, verify_walker, buffer_get(&image.buffer),
1549 &real_hash, CBFS_WALK_WRITEBACK_HASH);
1550 if (err == CB_CBFS_HASH_MISMATCH)
1551 verification_state = "invalid file hashes";
1552 else if (err != CB_CBFS_NOT_FOUND)
1553 verification_state = "missing file hashes";
Julius Werner4bfbabd2020-05-06 17:27:02 -07001554 char *hash_str = bintohex(real_hash.raw,
1555 vb2_digest_size(real_hash.algo));
1556 printf("[METADATA HASH]\t%s:%s",
1557 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1558 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1559 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
Julius Werner423cd062022-05-24 18:18:46 -07001560 vb2_digest_size(real_hash.algo))) {
Julius Werner4bfbabd2020-05-06 17:27:02 -07001561 printf(":valid");
Julius Werner423cd062022-05-24 18:18:46 -07001562 } else {
Julius Werner4bfbabd2020-05-06 17:27:02 -07001563 printf(":invalid");
Julius Werner423cd062022-05-24 18:18:46 -07001564 verification_state = "invalid metadata hash";
1565 }
Julius Werner4bfbabd2020-05-06 17:27:02 -07001566 }
1567 printf("\n");
Julius Werner423cd062022-05-24 18:18:46 -07001568 printf("[CBFS VERIFICATION (%s)]\t%s\n", param.region_name, verification_state);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001569 free(hash_str);
1570 }
1571
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001572 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001573}
1574
Stefan Reinauer63217582012-10-29 16:52:36 -07001575static int cbfs_extract(void)
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001576{
Hung-Te Lind1739622013-01-28 14:23:49 +08001577 if (!param.filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001578 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001579 return 1;
1580 }
1581
Hung-Te Lind1739622013-01-28 14:23:49 +08001582 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001583 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001584 return 1;
1585 }
1586
Sol Bouchere3260a02015-03-25 13:40:08 -07001587 struct cbfs_image image;
1588 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001589 param.headeroffset))
1590 return 1;
1591
Aaron Durbin17625022015-10-27 13:17:52 -05001592 return cbfs_export_entry(&image, param.name, param.filename,
Joel Kitching21fdd892018-08-09 17:49:52 +08001593 param.arch, !param.unprocessed);
Sol Boucher67a0a862015-03-18 12:36:27 -07001594}
1595
1596static int cbfs_write(void)
1597{
1598 if (!param.filename) {
1599 ERROR("You need to specify a valid input -f/--file.\n");
1600 return 1;
1601 }
1602 if (!partitioned_file_is_partitioned(param.image_file)) {
1603 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001604 return 1;
1605 }
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001606
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001607 if (!param.force && region_is_modern_cbfs(param.region_name)) {
Sol Boucher67a0a862015-03-18 12:36:27 -07001608 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1609 param.region_name);
1610 return 1;
1611 }
1612
1613 struct buffer new_content;
1614 if (buffer_from_file(&new_content, param.filename))
1615 return 1;
1616
1617 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1618 strlen(FMAP_SIGNATURE))) {
1619 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1620 param.filename);
1621 buffer_delete(&new_content);
1622 return 1;
1623 }
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001624 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
Sol Boucher67a0a862015-03-18 12:36:27 -07001625 strlen(CBFS_FILE_MAGIC))) {
1626 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1627 param.filename);
1628 buffer_delete(&new_content);
1629 return 1;
1630 }
1631
1632 unsigned offset = 0;
1633 if (param.fill_partial_upward && param.fill_partial_downward) {
1634 ERROR("You may only specify one of -u and -d.\n");
1635 buffer_delete(&new_content);
1636 return 1;
1637 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1638 if (new_content.size != param.image_region->size) {
1639 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",
1640 new_content.size, param.image_region->size);
1641 buffer_delete(&new_content);
1642 return 1;
1643 }
1644 } else {
1645 if (new_content.size > param.image_region->size) {
1646 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1647 new_content.size, param.image_region->size);
1648 buffer_delete(&new_content);
1649 return 1;
1650 }
Patrick Georgid9edb182016-12-06 18:55:26 +01001651 if (param.u64val == (uint64_t)-1) {
1652 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1653 param.fill_partial_upward ? "bottom" : "top");
1654 } else if (param.u64val > 0xff) {
1655 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1656 buffer_delete(&new_content);
1657 return 1;
1658 } else {
1659 memset(buffer_get(param.image_region),
1660 param.u64val & 0xff,
1661 buffer_size(param.image_region));
1662 }
Sol Boucher67a0a862015-03-18 12:36:27 -07001663 if (param.fill_partial_downward)
1664 offset = param.image_region->size - new_content.size;
1665 }
1666
1667 memcpy(param.image_region->data + offset, new_content.data,
1668 new_content.size);
1669 buffer_delete(&new_content);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001670
1671 return maybe_update_fmap_hash();
Sol Boucher67a0a862015-03-18 12:36:27 -07001672}
1673
1674static int cbfs_read(void)
1675{
1676 if (!param.filename) {
1677 ERROR("You need to specify a valid output -f/--file.\n");
1678 return 1;
1679 }
1680 if (!partitioned_file_is_partitioned(param.image_file)) {
1681 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1682 return 1;
1683 }
1684
1685 return buffer_write_file(param.image_region, param.filename);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001686}
1687
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001688static int cbfs_copy(void)
1689{
Patrick Georgi214e4af2015-11-20 19:22:50 +01001690 struct cbfs_image src_image;
Patrick Georgibd0bb232015-11-20 21:48:18 +01001691 struct buffer src_buf;
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001692
Patrick Georgibd0bb232015-11-20 21:48:18 +01001693 if (!param.source_region) {
1694 ERROR("You need to specify -R/--source-region.\n");
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001695 return 1;
Sol Bouchere3260a02015-03-25 13:40:08 -07001696 }
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001697
Patrick Georgibd0bb232015-11-20 21:48:18 +01001698 /* Obtain the source region and convert it to a cbfs_image. */
1699 if (!partitioned_file_read_region(&src_buf, param.image_file,
1700 param.source_region)) {
1701 ERROR("Region not found in image: %s\n", param.source_region);
1702 return 1;
1703 }
1704
1705 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1706 return 1;
1707
1708 return cbfs_copy_instance(&src_image, param.image_region);
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001709}
1710
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001711static int cbfs_compact(void)
1712{
1713 struct cbfs_image image;
1714 if (cbfs_image_from_buffer(&image, param.image_region,
1715 param.headeroffset))
1716 return 1;
1717 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1718 return cbfs_compact_instance(&image);
1719}
1720
Patrick Georgi5d982d72017-09-19 14:39:58 +02001721static int cbfs_expand(void)
1722{
1723 struct buffer src_buf;
1724
1725 /* Obtain the source region. */
1726 if (!partitioned_file_read_region(&src_buf, param.image_file,
1727 param.region_name)) {
1728 ERROR("Region not found in image: %s\n", param.source_region);
1729 return 1;
1730 }
1731
1732 return cbfs_expand_to_region(param.image_region);
1733}
1734
Patrick Georgi12631a42017-09-20 11:59:18 +02001735static int cbfs_truncate(void)
1736{
1737 struct buffer src_buf;
1738
1739 /* Obtain the source region. */
1740 if (!partitioned_file_read_region(&src_buf, param.image_file,
1741 param.region_name)) {
1742 ERROR("Region not found in image: %s\n", param.source_region);
1743 return 1;
1744 }
1745
1746 uint32_t size;
1747 int result = cbfs_truncate_space(param.image_region, &size);
Jakub Czapigaaa415632022-08-01 16:01:28 +02001748 if (!result)
1749 printf("0x%x\n", size);
Patrick Georgi12631a42017-09-20 11:59:18 +02001750 return result;
1751}
1752
Stefan Reinauera1e48242011-10-21 14:24:57 -07001753static const struct command commands[] = {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301754 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001755 {"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 +01001756 true, true},
Raul E Rangeldb280402021-06-29 13:26:41 -06001757 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
Werner Zehe9995f12016-01-14 13:22:37 +01001758 true, true},
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001759 {"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 +01001760 true, true},
1761 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301762 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001763 {"compact", "r:h?", cbfs_compact, true, true},
Patrick Georgibd0bb232015-11-20 21:48:18 +01001764 {"copy", "r:R:h?", cbfs_copy, true, true},
Patrick Georgi45acb342015-07-14 22:18:23 +02001765 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
Joel Kitching21fdd892018-08-09 17:49:52 +08001766 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001767 {"layout", "wvh?", cbfs_layout, false, false},
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001768 {"print", "H:r:vkh?", cbfs_print, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001769 {"read", "r:f:vh?", cbfs_read, true, false},
1770 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
Patrick Georgid9edb182016-12-06 18:55:26 +01001771 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
Patrick Georgi5d982d72017-09-19 14:39:58 +02001772 {"expand", "r:h?", cbfs_expand, true, true},
Patrick Georgi12631a42017-09-20 11:59:18 +02001773 {"truncate", "r:h?", cbfs_truncate, true, true},
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001774};
1775
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001776enum {
1777 /* begin after ASCII characters */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001778 LONGOPT_START = 256,
1779 LONGOPT_IBB = LONGOPT_START,
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001780 LONGOPT_EXT_WIN_BASE,
1781 LONGOPT_EXT_WIN_SIZE,
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001782 LONGOPT_END,
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001783};
1784
Stefan Reinauer63217582012-10-29 16:52:36 -07001785static struct option long_options[] = {
Julius Wernerefcee762014-11-10 13:14:24 -08001786 {"alignment", required_argument, 0, 'a' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001787 {"base-address", required_argument, 0, 'b' },
1788 {"bootblock", required_argument, 0, 'B' },
Julius Wernerefcee762014-11-10 13:14:24 -08001789 {"cmdline", required_argument, 0, 'C' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001790 {"compression", required_argument, 0, 'c' },
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301791 {"topswap-size", required_argument, 0, 'j' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001792 {"empty-fits", required_argument, 0, 'x' },
1793 {"entry-point", required_argument, 0, 'e' },
1794 {"file", required_argument, 0, 'f' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001795 {"fill-downward", no_argument, 0, 'd' },
1796 {"fill-upward", no_argument, 0, 'u' },
1797 {"flashmap", required_argument, 0, 'M' },
1798 {"fmap-regions", required_argument, 0, 'r' },
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001799 {"force", no_argument, 0, 'F' },
Patrick Georgibd0bb232015-11-20 21:48:18 +01001800 {"source-region", required_argument, 0, 'R' },
Patrick Georgi89f20342015-10-01 15:54:04 +02001801 {"hash-algorithm",required_argument, 0, 'A' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001802 {"header-offset", required_argument, 0, 'H' },
Julius Wernerefcee762014-11-10 13:14:24 -08001803 {"help", no_argument, 0, 'h' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001804 {"ignore-sec", required_argument, 0, 'S' },
1805 {"initrd", required_argument, 0, 'I' },
1806 {"int", required_argument, 0, 'i' },
1807 {"load-address", required_argument, 0, 'l' },
1808 {"machine", required_argument, 0, 'm' },
1809 {"name", required_argument, 0, 'n' },
1810 {"offset", required_argument, 0, 'o' },
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001811 {"padding", required_argument, 0, 'p' },
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001812 {"pow2page", no_argument, 0, 'Q' },
Rizwan Qureshic1072f22018-06-04 23:02:46 +05301813 {"ucode-region", required_argument, 0, 'q' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001814 {"size", required_argument, 0, 's' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001815 {"type", required_argument, 0, 't' },
1816 {"verbose", no_argument, 0, 'v' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001817 {"with-readonly", no_argument, 0, 'w' },
Aaron Durbin4be16742015-09-15 17:00:23 -05001818 {"xip", no_argument, 0, 'y' },
Werner Zehe9995f12016-01-14 13:22:37 +01001819 {"gen-attribute", no_argument, 0, 'g' },
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001820 {"mach-parseable",no_argument, 0, 'k' },
Joel Kitching21fdd892018-08-09 17:49:52 +08001821 {"unprocessed", no_argument, 0, 'U' },
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001822 {"ibb", no_argument, 0, LONGOPT_IBB },
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001823 {"ext-win-base", required_argument, 0, LONGOPT_EXT_WIN_BASE },
1824 {"ext-win-size", required_argument, 0, LONGOPT_EXT_WIN_SIZE },
Julius Wernerefcee762014-11-10 13:14:24 -08001825 {NULL, 0, 0, 0 }
Stefan Reinauer63217582012-10-29 16:52:36 -07001826};
1827
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001828static int get_region_offset(long long int offset, uint32_t *region_offset)
1829{
1830 /* If offset is not negative, no transformation required. */
1831 if (offset >= 0) {
1832 *region_offset = offset;
1833 return 0;
1834 }
1835
1836 /* Calculate offset from start of region. */
1837 return convert_region_offset(-offset, region_offset);
1838}
1839
1840static int calculate_region_offsets(void)
1841{
1842 int ret = 0;
1843
1844 if (param.baseaddress_assigned)
1845 ret |= get_region_offset(param.baseaddress_input, &param.baseaddress);
1846 if (param.headeroffset_assigned)
1847 ret |= get_region_offset(param.headeroffset_input, &param.headeroffset);
1848 if (param.cbfsoffset_assigned)
1849 ret |= get_region_offset(param.cbfsoffset_input, &param.cbfsoffset);
1850
1851 return ret;
1852}
1853
Sol Boucher67a0a862015-03-18 12:36:27 -07001854static int dispatch_command(struct command command)
1855{
1856 if (command.accesses_region) {
1857 assert(param.image_file);
1858
1859 if (partitioned_file_is_partitioned(param.image_file)) {
Patrick Georgi2ed72952016-12-16 14:51:53 +01001860 INFO("Performing operation on '%s' region...\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07001861 param.region_name);
1862 }
1863 if (!partitioned_file_read_region(param.image_region,
1864 param.image_file, param.region_name)) {
1865 ERROR("The image will be left unmodified.\n");
1866 return 1;
1867 }
1868
1869 if (command.modifies_region) {
1870 // We (intentionally) don't support overwriting the FMAP
1871 // section. If you find yourself wanting to do this,
1872 // consider creating a new image rather than performing
1873 // whatever hacky transformation you were planning.
1874 if (region_is_flashmap(param.region_name)) {
1875 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1876 param.region_name);
1877 ERROR("The image will be left unmodified.\n");
1878 return 1;
1879 }
1880 // We don't allow writing raw data to regions that
1881 // contain nested regions, since doing so would
1882 // overwrite all such subregions.
1883 if (partitioned_file_region_contains_nested(
1884 param.image_file, param.region_name)) {
1885 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1886 param.region_name);
1887 ERROR("The image will be left unmodified.\n");
1888 return 1;
1889 }
1890 }
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001891
1892 /*
1893 * Once image region is read, input offsets can be adjusted accordingly if the
1894 * inputs are provided as negative integers i.e. offsets from end of region.
1895 */
1896 if (calculate_region_offsets())
1897 return 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001898 }
1899
1900 if (command.function()) {
1901 if (partitioned_file_is_partitioned(param.image_file)) {
1902 ERROR("Failed while operating on '%s' region!\n",
1903 param.region_name);
1904 ERROR("The image will be left unmodified.\n");
1905 }
1906 return 1;
1907 }
1908
1909 return 0;
1910}
1911
Stefan Reinauer63217582012-10-29 16:52:36 -07001912static void usage(char *name)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001913{
1914 printf
Stefan Reinauer07040582010-04-24 21:24:06 +00001915 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
Stefan Reinauer63217582012-10-29 16:52:36 -07001916 "USAGE:\n" " %s [-h]\n"
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001917 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001918 " -H header_offset Do not search for header; use this offset*\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001919 " -T Output top-aligned memory address\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001920 " -u Accept short data; fill upward/from bottom\n"
1921 " -d Accept short data; fill downward/from top\n"
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001922 " -F Force action\n"
Patrick Georgi5b6bdcc2016-10-13 18:24:10 +02001923 " -g Generate position and alignment arguments\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001924 " -U Unprocessed; don't decompress or make ELF\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001925 " -v Provide verbose output\n"
1926 " -h Display this help message\n\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001927 " --ext-win-base Base of extended decode window in host address\n"
1928 " space(x86 only)\n"
1929 " --ext-win-size Size of extended decode window in host address\n"
1930 " space(x86 only)\n"
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001931 "COMMANDs:\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001932 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001933 " [-c compression] [-b base-address | -a alignment] \\\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301934 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001935 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1936 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001937 "Add a component\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301938 " "
1939 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001940 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001941 " [-c compression] [-b base-address] \\\n"
1942 " (linux specific: [-C cmdline] [-I initrd]) "
Stefan Reinauer63217582012-10-29 16:52:36 -07001943 "Add a payload to the ROM\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001944 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001945 " [-c compression] [-b base] [-S section-to-ignore] \\\n"
Arthur Heymansc78521b32021-06-16 17:16:05 +02001946 " [-a alignment] [-Q|--pow2page] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001947 " [-y|--xip] [--ibb] \\\n"
1948 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001949 "Add a stage to the ROM\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001950 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1951 " [-A hash] -l load-address -e entry-point \\\n"
1952 " [-c compression] [-b base] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001953 "Add a 32bit flat mode binary\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001954 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001955 "Add a raw 64-bit integer value\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301956 " add-master-header [-r image,regions] \\ \n"
1957 " [-j topswap-size] (Intel CPUs only) "
Patrick Georgi59e52b92015-09-10 15:28:27 +02001958 "Add a legacy CBFS master header\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001959 " remove [-r image,regions] -n NAME "
Stefan Reinauer63217582012-10-29 16:52:36 -07001960 "Remove a component\n"
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001961 " compact -r image,regions "
1962 "Defragment CBFS image.\n"
Patrick Georgibd0bb232015-11-20 21:48:18 +01001963 " copy -r image,regions -R source-region "
1964 "Create a copy (duplicate) cbfs instance in fmap\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001965 " create -m ARCH -s size [-b bootblock offset] \\\n"
Patrick Georgi83c2d122015-08-26 10:40:00 +02001966 " [-o CBFS offset] [-H header offset] [-B bootblock] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001967 "Create a legacy ROM file with CBFS master header*\n"
1968 " create -M flashmap [-r list,of,regions,containing,cbfses] "
1969 "Create a new-style partitioned firmware image\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001970 " layout [-w] "
1971 "List mutable (or, with -w, readable) image regions\n"
Julius Werner4bfbabd2020-05-06 17:27:02 -07001972 " print [-r image,regions] [-k] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001973 "Show the contents of the ROM\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001974 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
1975 "Extracts a file from ROM\n"
Patrick Georgid9edb182016-12-06 18:55:26 +01001976 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001977 "Write file into same-size [or larger] raw region\n"
1978 " read [-r fmap-region] -f file "
1979 "Extract raw region contents into binary file\n"
Patrick Georgi12631a42017-09-20 11:59:18 +02001980 " truncate [-r fmap-region] "
1981 "Truncate CBFS and print new size on stdout\n"
Patrick Georgi5d982d72017-09-19 14:39:58 +02001982 " expand [-r fmap-region] "
1983 "Expand CBFS to span entire region\n"
Sol Boucher0e539312015-03-05 15:38:03 -08001984 "OFFSETs:\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001985 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
1986 " in two possible formats: if their value is greater than\n"
Sol Boucher0e539312015-03-05 15:38:03 -08001987 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
1988 " address; otherwise, they are treated as an offset into flash.\n"
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02001989 "ARCHes:\n", name, name
Stefan Reinauer63217582012-10-29 16:52:36 -07001990 );
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02001991 print_supported_architectures();
1992
1993 printf("TYPEs:\n");
Stefan Reinauer07040582010-04-24 21:24:06 +00001994 print_supported_filetypes();
Sol Boucher67a0a862015-03-18 12:36:27 -07001995 printf(
1996 "\n* Note that these actions and switches are only valid when\n"
1997 " working with legacy images whose structure is described\n"
1998 " primarily by a CBFS master header. New-style images, in\n"
1999 " contrast, exclusively make use of an FMAP to describe their\n"
2000 " layout: this must minimally contain an '%s' section\n"
2001 " specifying the location of this FMAP itself and a '%s'\n"
2002 " section describing the primary CBFS. It should also be noted\n"
2003 " that, when working with such images, the -F and -r switches\n"
Julius Werner0fd072d2021-12-13 10:28:29 -08002004 " default to '%s' for convenience, and the -b switch becomes\n"
Sol Boucher67d59982015-05-07 02:39:22 -07002005 " relative to the selected CBFS region's lowest address.\n"
2006 " The one exception to this rule is the top-aligned address,\n"
2007 " which is always relative to the end of the entire image\n"
2008 " rather than relative to the local region; this is true for\n"
2009 " for both input (sufficiently large) and output (-T) data.\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07002010 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
2011 SECTION_NAME_PRIMARY_CBFS
2012 );
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002013}
2014
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002015static bool valid_opt(size_t i, int c)
2016{
2017 /* Check if it is one of the optstrings supported by the command. */
2018 if (strchr(commands[i].optstring, c))
2019 return true;
2020
2021 /*
2022 * Check if it is one of the non-ASCII characters. Currently, the
2023 * non-ASCII characters are only checked against the valid list
2024 * irrespective of the command.
2025 */
2026 if (c >= LONGOPT_START && c < LONGOPT_END)
2027 return true;
2028
2029 return false;
2030}
2031
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002032int main(int argc, char **argv)
2033{
Mathias Krause41c229c2012-07-17 21:17:15 +02002034 size_t i;
Stefan Reinauer63217582012-10-29 16:52:36 -07002035 int c;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002036
2037 if (argc < 3) {
Stefan Reinauer63217582012-10-29 16:52:36 -07002038 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002039 return 1;
2040 }
2041
Sol Bouchere3260a02015-03-25 13:40:08 -07002042 char *image_name = argv[1];
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002043 char *cmd = argv[2];
Stefan Reinauer63217582012-10-29 16:52:36 -07002044 optind += 2;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002045
2046 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2047 if (strcmp(cmd, commands[i].name) != 0)
2048 continue;
Stefan Reinauer63217582012-10-29 16:52:36 -07002049
2050 while (1) {
2051 char *suffix = NULL;
2052 int option_index = 0;
2053
2054 c = getopt_long(argc, argv, commands[i].optstring,
2055 long_options, &option_index);
Nico Huber54073102016-08-01 23:18:29 +02002056 if (c == -1) {
2057 if (optind < argc) {
2058 ERROR("%s: excessive argument -- '%s'"
2059 "\n", argv[0], argv[optind]);
2060 return 1;
2061 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002062 break;
Nico Huber54073102016-08-01 23:18:29 +02002063 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002064
Joel Kitching672d5ad2018-10-11 17:01:30 +08002065 /* Filter out illegal long options */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002066 if (!valid_opt(i, c)) {
2067 ERROR("%s: invalid option -- '%d'\n",
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002068 argv[0], c);
Stefan Reinauer63217582012-10-29 16:52:36 -07002069 c = '?';
2070 }
2071
2072 switch(c) {
2073 case 'n':
Hung-Te Lind1739622013-01-28 14:23:49 +08002074 param.name = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002075 break;
2076 case 't':
2077 if (intfiletype(optarg) != ((uint64_t) - 1))
Hung-Te Lind1739622013-01-28 14:23:49 +08002078 param.type = intfiletype(optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002079 else
Hung-Te Lind1739622013-01-28 14:23:49 +08002080 param.type = strtoul(optarg, NULL, 0);
2081 if (param.type == 0)
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002082 WARN("Unknown type '%s' ignored\n",
Stefan Reinauer63217582012-10-29 16:52:36 -07002083 optarg);
2084 break;
Sol Boucherec424862015-05-07 21:00:05 -07002085 case 'c': {
Patrick Georgiecaa5702017-01-11 18:38:11 +01002086 if (strcmp(optarg, "precompression") == 0) {
2087 param.precompression = 1;
2088 break;
2089 }
Sol Boucherec424862015-05-07 21:00:05 -07002090 int algo = cbfs_parse_comp_algo(optarg);
2091 if (algo >= 0)
2092 param.compression = algo;
Stefan Reinauer63217582012-10-29 16:52:36 -07002093 else
Sol Boucherec424862015-05-07 21:00:05 -07002094 WARN("Unknown compression '%s' ignored.\n",
2095 optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002096 break;
Sol Boucherec424862015-05-07 21:00:05 -07002097 }
Patrick Georgi89f20342015-10-01 15:54:04 +02002098 case 'A': {
Julius Wernerd4775652020-03-13 16:43:34 -07002099 if (!vb2_lookup_hash_alg(optarg, &param.hash)) {
Patrick Georgi89f20342015-10-01 15:54:04 +02002100 ERROR("Unknown hash algorithm '%s'.\n",
2101 optarg);
2102 return 1;
2103 }
2104 break;
2105 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002106 case 'M':
2107 param.fmap = optarg;
2108 break;
2109 case 'r':
2110 param.region_name = optarg;
2111 break;
Patrick Georgibd0bb232015-11-20 21:48:18 +01002112 case 'R':
2113 param.source_region = optarg;
2114 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002115 case 'b':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002116 param.baseaddress_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002117 if (!*optarg || (suffix && *suffix)) {
2118 ERROR("Invalid base address '%s'.\n",
2119 optarg);
2120 return 1;
2121 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002122 // baseaddress may be zero on non-x86, so we
2123 // need an explicit "baseaddress_assigned".
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002124 param.baseaddress_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002125 break;
2126 case 'l':
Nico Huber9ade7172016-08-01 21:37:42 +02002127 param.loadaddress = strtoul(optarg, &suffix, 0);
2128 if (!*optarg || (suffix && *suffix)) {
2129 ERROR("Invalid load address '%s'.\n",
2130 optarg);
2131 return 1;
2132 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002133 break;
2134 case 'e':
Nico Huber9ade7172016-08-01 21:37:42 +02002135 param.entrypoint = strtoul(optarg, &suffix, 0);
2136 if (!*optarg || (suffix && *suffix)) {
2137 ERROR("Invalid entry point '%s'.\n",
2138 optarg);
2139 return 1;
2140 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002141 break;
2142 case 's':
Hung-Te Lind1739622013-01-28 14:23:49 +08002143 param.size = strtoul(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002144 if (!*optarg) {
2145 ERROR("Empty size specified.\n");
2146 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002147 }
Nico Huber9ade7172016-08-01 21:37:42 +02002148 switch (tolower((int)suffix[0])) {
2149 case 'k':
2150 param.size *= 1024;
2151 break;
2152 case 'm':
Hung-Te Lind1739622013-01-28 14:23:49 +08002153 param.size *= 1024 * 1024;
Nico Huber9ade7172016-08-01 21:37:42 +02002154 break;
2155 case '\0':
2156 break;
2157 default:
2158 ERROR("Invalid suffix for size '%s'.\n",
2159 optarg);
2160 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002161 }
Patrick Georgie887ca52014-08-09 17:44:39 +02002162 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002163 case 'B':
Hung-Te Lind1739622013-01-28 14:23:49 +08002164 param.bootblock = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002165 break;
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002166 case 'H':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002167 param.headeroffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002168 if (!*optarg || (suffix && *suffix)) {
2169 ERROR("Invalid header offset '%s'.\n",
2170 optarg);
2171 return 1;
2172 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002173 param.headeroffset_assigned = 1;
2174 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002175 case 'a':
Nico Huber9ade7172016-08-01 21:37:42 +02002176 param.alignment = strtoul(optarg, &suffix, 0);
2177 if (!*optarg || (suffix && *suffix)) {
2178 ERROR("Invalid alignment '%s'.\n",
2179 optarg);
2180 return 1;
2181 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002182 break;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07002183 case 'p':
2184 param.padding = strtoul(optarg, &suffix, 0);
2185 if (!*optarg || (suffix && *suffix)) {
2186 ERROR("Invalid pad size '%s'.\n",
2187 optarg);
2188 return 1;
2189 }
2190 break;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03002191 case 'Q':
2192 param.force_pow2_pagesize = 1;
2193 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002194 case 'o':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002195 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002196 if (!*optarg || (suffix && *suffix)) {
2197 ERROR("Invalid cbfs offset '%s'.\n",
2198 optarg);
2199 return 1;
2200 }
Julius Wernerefcee762014-11-10 13:14:24 -08002201 param.cbfsoffset_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002202 break;
2203 case 'f':
Hung-Te Lind1739622013-01-28 14:23:49 +08002204 param.filename = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002205 break;
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02002206 case 'F':
2207 param.force = 1;
2208 break;
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002209 case 'i':
Nico Huber9ade7172016-08-01 21:37:42 +02002210 param.u64val = strtoull(optarg, &suffix, 0);
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01002211 param.u64val_assigned = 1;
Nico Huber9ade7172016-08-01 21:37:42 +02002212 if (!*optarg || (suffix && *suffix)) {
2213 ERROR("Invalid int parameter '%s'.\n",
2214 optarg);
2215 return 1;
2216 }
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002217 break;
Sol Boucher67a0a862015-03-18 12:36:27 -07002218 case 'u':
2219 param.fill_partial_upward = true;
2220 break;
2221 case 'd':
2222 param.fill_partial_downward = true;
2223 break;
2224 case 'w':
2225 param.show_immutable = true;
Hung-Te Lin215d1d72013-01-29 03:46:02 +08002226 break;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05302227 case 'j':
2228 param.topswap_size = strtol(optarg, NULL, 0);
2229 if (!is_valid_topswap())
2230 return 1;
2231 break;
Rizwan Qureshic1072f22018-06-04 23:02:46 +05302232 case 'q':
2233 param.ucode_region = optarg;
2234 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002235 case 'v':
2236 verbose++;
2237 break;
David Hendricks90ca3b62012-11-16 14:48:22 -08002238 case 'm':
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06002239 param.arch = string_to_arch(optarg);
David Hendricks90ca3b62012-11-16 14:48:22 -08002240 break;
Patrick Georgide36d332013-08-27 20:22:21 +02002241 case 'I':
2242 param.initrd = optarg;
2243 break;
2244 case 'C':
2245 param.cmdline = optarg;
2246 break;
Furquan Shaikh405304a2014-10-30 11:44:20 -07002247 case 'S':
2248 param.ignore_section = optarg;
2249 break;
Aaron Durbin4be16742015-09-15 17:00:23 -05002250 case 'y':
2251 param.stage_xip = true;
2252 break;
Werner Zehe9995f12016-01-14 13:22:37 +01002253 case 'g':
2254 param.autogen_attr = true;
2255 break;
Aaron Durbin5dc628a2016-01-26 15:35:34 -06002256 case 'k':
2257 param.machine_parseable = true;
2258 break;
Joel Kitching21fdd892018-08-09 17:49:52 +08002259 case 'U':
2260 param.unprocessed = true;
2261 break;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01002262 case LONGOPT_IBB:
2263 param.ibb = true;
2264 break;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08002265 case LONGOPT_EXT_WIN_BASE:
2266 param.ext_win_base = strtoul(optarg, &suffix, 0);
2267 if (!*optarg || (suffix && *suffix)) {
2268 ERROR("Invalid ext window base '%s'.\n", optarg);
2269 return 1;
2270 }
2271 break;
2272 case LONGOPT_EXT_WIN_SIZE:
2273 param.ext_win_size = strtoul(optarg, &suffix, 0);
2274 if (!*optarg || (suffix && *suffix)) {
2275 ERROR("Invalid ext window size '%s'.\n", optarg);
2276 return 1;
2277 }
2278 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002279 case 'h':
2280 case '?':
2281 usage(argv[0]);
2282 return 1;
2283 default:
2284 break;
2285 }
2286 }
2287
Sol Bouchere3260a02015-03-25 13:40:08 -07002288 if (commands[i].function == cbfs_create) {
Sol Boucher67a0a862015-03-18 12:36:27 -07002289 if (param.fmap) {
2290 struct buffer flashmap;
2291 if (buffer_from_file(&flashmap, param.fmap))
2292 return 1;
2293 param.image_file = partitioned_file_create(
2294 image_name, &flashmap);
2295 buffer_delete(&flashmap);
2296 } else if (param.size) {
2297 param.image_file = partitioned_file_create_flat(
2298 image_name, param.size);
2299 } else {
2300 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002301 return 1;
2302 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002303 } else {
Vadim Bendebury75599462015-12-09 09:39:31 -08002304 bool write_access = commands[i].modifies_region;
2305
Sol Bouchere3260a02015-03-25 13:40:08 -07002306 param.image_file =
Vadim Bendebury75599462015-12-09 09:39:31 -08002307 partitioned_file_reopen(image_name,
2308 write_access);
Sol Bouchere3260a02015-03-25 13:40:08 -07002309 }
2310 if (!param.image_file)
2311 return 1;
2312
Sol Boucher67a0a862015-03-18 12:36:27 -07002313 unsigned num_regions = 1;
2314 for (const char *list = strchr(param.region_name, ','); list;
2315 list = strchr(list + 1, ','))
2316 ++num_regions;
2317
Sol Bouchere3260a02015-03-25 13:40:08 -07002318 // If the action needs to read an image region, as indicated by
2319 // having accesses_region set in its command struct, that
2320 // region's buffer struct will be stored here and the client
2321 // will receive a pointer to it via param.image_region. It
2322 // need not write the buffer back to the image file itself,
2323 // since this behavior can be requested via its modifies_region
2324 // field. Additionally, it should never free the region buffer,
2325 // as that is performed automatically once it completes.
Sol Boucher67a0a862015-03-18 12:36:27 -07002326 struct buffer image_regions[num_regions];
2327 memset(image_regions, 0, sizeof(image_regions));
Sol Bouchere3260a02015-03-25 13:40:08 -07002328
Sol Boucher67a0a862015-03-18 12:36:27 -07002329 bool seen_primary_cbfs = false;
2330 char region_name_scratch[strlen(param.region_name) + 1];
2331 strcpy(region_name_scratch, param.region_name);
2332 param.region_name = strtok(region_name_scratch, ",");
2333 for (unsigned region = 0; region < num_regions; ++region) {
2334 if (!param.region_name) {
2335 ERROR("Encountered illegal degenerate region name in -r list\n");
2336 ERROR("The image will be left unmodified.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002337 partitioned_file_close(param.image_file);
2338 return 1;
2339 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002340
Sol Boucher67a0a862015-03-18 12:36:27 -07002341 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2342 == 0)
2343 seen_primary_cbfs = true;
Sol Bouchere3260a02015-03-25 13:40:08 -07002344
Sol Boucher67a0a862015-03-18 12:36:27 -07002345 param.image_region = image_regions + region;
2346 if (dispatch_command(commands[i])) {
Sol Bouchere3260a02015-03-25 13:40:08 -07002347 partitioned_file_close(param.image_file);
2348 return 1;
2349 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002350
2351 param.region_name = strtok(NULL, ",");
2352 }
2353
2354 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2355 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2356 SECTION_NAME_PRIMARY_CBFS);
2357 ERROR("The image will be left unmodified.\n");
2358 partitioned_file_close(param.image_file);
2359 return 1;
2360 }
2361
2362 if (commands[i].modifies_region) {
2363 assert(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002364 for (unsigned region = 0; region < num_regions;
2365 ++region) {
2366
2367 if (!partitioned_file_write_region(
2368 param.image_file,
2369 image_regions + region)) {
2370 partitioned_file_close(
2371 param.image_file);
2372 return 1;
2373 }
2374 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002375 }
2376
2377 partitioned_file_close(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002378 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002379 }
2380
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002381 ERROR("Unknown command '%s'.\n", cmd);
Stefan Reinauer63217582012-10-29 16:52:36 -07002382 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002383 return 1;
2384}