blob: e11cfbc1264123f51e6aedb0d45e3186ef323210 [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;
Jeremy Compostellac9cae532023-08-30 10:25:33 -070049 const char *ignore_sections;
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
Arthur Heymans34a7e662022-10-06 09:42:15 +0200320/* Should be enough for now */
321#define MMAP_MAX_WINDOWS 3
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800322
323/* Table of all the decode windows supported by the platform. */
Arthur Heymans34a7e662022-10-06 09:42:15 +0200324static int mmap_window_table_size;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800325static struct mmap_window mmap_window_table[MMAP_MAX_WINDOWS];
326
Arthur Heymans34a7e662022-10-06 09:42:15 +0200327static void add_mmap_window(size_t flash_offset, size_t host_offset,
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800328 size_t window_size)
329{
Arthur Heymans34a7e662022-10-06 09:42:15 +0200330 if (mmap_window_table_size >= MMAP_MAX_WINDOWS) {
331 ERROR("Too many memory map windows\n");
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800332 return;
333 }
334
Arthur Heymans34a7e662022-10-06 09:42:15 +0200335 mmap_window_table[mmap_window_table_size].flash_space.offset = flash_offset;
336 mmap_window_table[mmap_window_table_size].host_space.offset = host_offset;
337 mmap_window_table[mmap_window_table_size].flash_space.size = window_size;
338 mmap_window_table[mmap_window_table_size].host_space.size = window_size;
339 mmap_window_table_size++;
340}
341
342
Arthur Heymans8f95f742022-12-08 20:24:44 +0100343static int decode_mmap_arg(char *arg)
Arthur Heymans34a7e662022-10-06 09:42:15 +0200344{
Arthur Heymans8f95f742022-12-08 20:24:44 +0100345 if (arg == NULL)
Arthur Heymans34a7e662022-10-06 09:42:15 +0200346 return 1;
347
348 union {
349 unsigned long int array[3];
350 struct {
351 unsigned long int flash_base;
352 unsigned long int mmap_base;
353 unsigned long int mmap_size;
354 };
355 } mmap_args;
356 char *suffix = NULL;
Arthur Heymans8f95f742022-12-08 20:24:44 +0100357 char *substring = strtok(arg, ":");
Arthur Heymans34a7e662022-10-06 09:42:15 +0200358 for (size_t i = 0; i < ARRAY_SIZE(mmap_args.array); i++) {
359 if (!substring) {
360 ERROR("Invalid mmap arguments '%s'.\n",
Arthur Heymans8f95f742022-12-08 20:24:44 +0100361 arg);
Arthur Heymans34a7e662022-10-06 09:42:15 +0200362 return 1;
363 }
364 mmap_args.array[i] = strtol(substring, &suffix, 0);
365 if (suffix && *suffix) {
366 ERROR("Invalid mmap arguments '%s'.\n",
Arthur Heymans8f95f742022-12-08 20:24:44 +0100367 arg);
Arthur Heymans34a7e662022-10-06 09:42:15 +0200368 return 1;
369 }
370 substring = strtok(NULL, ":");
371 }
372
373 if (substring != NULL) {
374 ERROR("Invalid argument, too many substrings '%s'.\n",
Arthur Heymans8f95f742022-12-08 20:24:44 +0100375 arg);
Arthur Heymans34a7e662022-10-06 09:42:15 +0200376
377 return 1;
378 }
379
380 add_mmap_window(mmap_args.flash_base, mmap_args.mmap_base, mmap_args.mmap_size);
381 return 0;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800382}
383
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800384#define DEFAULT_DECODE_WINDOW_TOP (4ULL * GiB)
385#define DEFAULT_DECODE_WINDOW_MAX_SIZE (16 * MiB)
386
387static bool create_mmap_windows(void)
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800388{
389 static bool done;
390
391 if (done)
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800392 return done;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800393
Arthur Heymans34a7e662022-10-06 09:42:15 +0200394 // No memory map provided, use a default one
395 if (mmap_window_table_size == 0) {
396 const size_t image_size = partitioned_file_total_size(param.image_file);
Reka Normanf49fcc62022-12-16 17:03:46 +1100397 printf("Image SIZE %zu\n", image_size);
Arthur Heymans34a7e662022-10-06 09:42:15 +0200398 const size_t std_window_size = MIN(DEFAULT_DECODE_WINDOW_MAX_SIZE, image_size);
399 const size_t std_window_flash_offset = image_size - std_window_size;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800400
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800401 /*
Arthur Heymans34a7e662022-10-06 09:42:15 +0200402 * Default decode window lives just below 4G boundary in host space and maps up to a
403 * maximum of 16MiB. If the window is smaller than 16MiB, the SPI flash window is mapped
404 * at the top of the host window just below 4G.
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800405 */
Arthur Heymans34a7e662022-10-06 09:42:15 +0200406 add_mmap_window(std_window_flash_offset, DEFAULT_DECODE_WINDOW_TOP - std_window_size, std_window_size);
407 } else {
408 /*
409 * Check provided memory map
410 */
411 for (int i = 0; i < mmap_window_table_size; i++) {
412 for (int j = i + 1; j < mmap_window_table_size; j++) {
413 if (region_overlap(&mmap_window_table[i].flash_space,
414 &mmap_window_table[j].flash_space)) {
415 ERROR("Flash space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
416 region_offset(&mmap_window_table[i].flash_space),
417 region_end(&mmap_window_table[i].flash_space),
418 region_offset(&mmap_window_table[j].flash_space),
419 region_end(&mmap_window_table[j].flash_space));
420 return false;
421 }
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800422
Arthur Heymans34a7e662022-10-06 09:42:15 +0200423 if (region_overlap(&mmap_window_table[i].host_space,
424 &mmap_window_table[j].host_space)) {
425 ERROR("Host space windows (base=0x%zx, limit=0x%zx) and (base=0x%zx, limit=0x%zx) overlap!\n",
426 region_offset(&mmap_window_table[i].flash_space),
427 region_end(&mmap_window_table[i].flash_space),
428 region_offset(&mmap_window_table[j].flash_space),
429 region_end(&mmap_window_table[j].flash_space));
430 return false;
431 }
432 }
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800433 }
434 }
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800435
436 done = true;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800437 return done;
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800438}
439
440static unsigned int convert_address(const struct region *to, const struct region *from,
441 unsigned int addr)
442{
443 /*
444 * Calculate the offset in the "from" region and use that offset to calculate
445 * corresponding address in the "to" region.
446 */
447 size_t offset = addr - region_offset(from);
448 return region_offset(to) + offset;
449}
450
451enum mmap_addr_type {
452 HOST_SPACE_ADDR,
453 FLASH_SPACE_ADDR,
454};
455
456static int find_mmap_window(enum mmap_addr_type addr_type, unsigned int addr)
457{
458 size_t i;
459
460 for (i = 0; i < ARRAY_SIZE(mmap_window_table); i++) {
461 const struct region *reg;
462
463 if (addr_type == HOST_SPACE_ADDR)
464 reg = &mmap_window_table[i].host_space;
465 else
466 reg = &mmap_window_table[i].flash_space;
467
Kyösti Mälkkib57373b2021-02-13 01:57:14 +0200468 if (region_offset(reg) <= addr &&
469 ((uint64_t)region_offset(reg) + (uint64_t)region_sz(reg) - 1) >= addr)
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800470 return i;
471 }
472
473 return -1;
474}
475
476static unsigned int convert_host_to_flash(const struct buffer *region, unsigned int addr)
477{
478 int idx;
479 const struct region *to, *from;
480
481 idx = find_mmap_window(HOST_SPACE_ADDR, addr);
482 if (idx == -1) {
483 ERROR("Host address(%x) not in any mmap window!\n", addr);
484 return 0;
485 }
486
487 to = &mmap_window_table[idx].flash_space;
488 from = &mmap_window_table[idx].host_space;
489
490 /* region->offset is subtracted because caller expects offset in the given region. */
491 return convert_address(to, from, addr) - region->offset;
492}
493
494static unsigned int convert_flash_to_host(const struct buffer *region, unsigned int addr)
495{
496 int idx;
497 const struct region *to, *from;
498
499 /*
500 * region->offset is added because caller provides offset in the given region. This is
501 * converted to an absolute address in the SPI flash space. This is done before the
502 * conversion as opposed to after in convert_host_to_flash() above because the address
503 * is actually an offset within the region. So, it needs to be converted into an
504 * absolute address in the SPI flash space before converting into an address in host
505 * space.
506 */
507 addr += region->offset;
508 idx = find_mmap_window(FLASH_SPACE_ADDR, addr);
509
510 if (idx == -1) {
511 ERROR("SPI flash address(%x) not in any mmap window!\n", addr);
512 return 0;
513 }
514
515 to = &mmap_window_table[idx].host_space;
516 from = &mmap_window_table[idx].flash_space;
517
518 return convert_address(to, from, addr);
519}
520
521static unsigned int convert_addr_space(const struct buffer *region, unsigned int addr)
Aaron Durbinab00d772016-05-04 16:07:15 -0500522{
523 assert(region);
524
Furquan Shaikh0ae389c2020-11-21 10:35:10 -0800525 assert(create_mmap_windows());
Aaron Durbinab00d772016-05-04 16:07:15 -0500526
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800527 if (IS_HOST_SPACE_ADDRESS(addr))
528 return convert_host_to_flash(region, addr);
529 else
530 return convert_flash_to_host(region, addr);
Aaron Durbinab00d772016-05-04 16:07:15 -0500531}
532
533/*
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800534 * This function takes offset value which represents the offset from one end of the region and
535 * converts it to offset from the other end of the region. offset is expected to be positive.
536 */
537static int convert_region_offset(unsigned int offset, uint32_t *region_offset)
Sol Boucher67d59982015-05-07 02:39:22 -0700538{
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800539 size_t size;
Sol Boucher67d59982015-05-07 02:39:22 -0700540
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800541 if (param.size) {
542 size = param.size;
543 } else {
544 assert(param.image_region);
545 size = param.image_region->size;
Patrick Georgi97311192015-12-07 23:14:07 +0100546 }
547
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800548 if (size < offset) {
549 ERROR("Cannot convert region offset (size=0x%zx, offset=0x%x)\n", size, offset);
550 return 1;
551 }
552
553 *region_offset = size - offset;
554 return 0;
Sol Boucher67d59982015-05-07 02:39:22 -0700555}
556
Julius Werner6ddacd62021-12-03 14:46:53 -0800557static int do_cbfs_locate(uint32_t *cbfs_addr, size_t data_size)
Aaron Durbin4be16742015-09-15 17:00:23 -0500558{
Julius Werner6ddacd62021-12-03 14:46:53 -0800559 uint32_t metadata_size = 0;
560
Aaron Durbin4be16742015-09-15 17:00:23 -0500561 if (!param.name) {
562 ERROR("You need to specify -n/--name.\n");
563 return 1;
564 }
565
566 struct cbfs_image image;
567 if (cbfs_image_from_buffer(&image, param.image_region,
568 param.headeroffset))
569 return 1;
570
571 if (cbfs_get_entry(&image, param.name))
572 WARN("'%s' already in CBFS.\n", param.name);
573
Aaron Durbinfacf1492017-12-18 14:50:22 -0700574 if (!data_size) {
Julius Werner772714d2021-12-13 10:16:44 -0800575 ERROR("File '%s' is empty?\n", param.name);
576 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -0500577 }
578
Arthur Heymansc78521b32021-06-16 17:16:05 +0200579 /* Compute required page size */
Kyösti Mälkkic36469e2020-05-28 23:12:02 +0300580 if (param.force_pow2_pagesize) {
Arthur Heymansc78521b32021-06-16 17:16:05 +0200581 param.pagesize = 1;
582 while (param.pagesize < data_size)
583 param.pagesize <<= 1;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +0300584 DEBUG("Page size is %d (0x%x)\n", param.pagesize, param.pagesize);
585 }
586
Aaron Durbin4be16742015-09-15 17:00:23 -0500587 /* Include cbfs_file size along with space for with name. */
588 metadata_size += cbfs_calculate_file_header_size(param.name);
Werner Zehe9995f12016-01-14 13:22:37 +0100589 /* Adjust metadata_size if additional attributes were added */
590 if (param.autogen_attr) {
591 if (param.alignment)
592 metadata_size += sizeof(struct cbfs_file_attr_align);
593 if (param.baseaddress_assigned || param.stage_xip)
594 metadata_size += sizeof(struct cbfs_file_attr_position);
595 }
Julius Wernerc24db002020-11-18 18:00:31 -0800596 if (param.precompression || param.compression != CBFS_COMPRESS_NONE)
597 metadata_size += sizeof(struct cbfs_file_attr_compression);
Julius Werner6ddacd62021-12-03 14:46:53 -0800598 if (param.type == CBFS_TYPE_STAGE)
599 metadata_size += sizeof(struct cbfs_file_attr_stageheader);
Aaron Durbin4be16742015-09-15 17:00:23 -0500600
Werner Zehc7b2b7c2016-01-22 19:43:01 +0100601 /* Take care of the hash attribute if it is used */
602 if (param.hash != VB2_HASH_INVALID)
Julius Wernerd4775652020-03-13 16:43:34 -0700603 metadata_size += cbfs_file_attr_hash_size(param.hash);
Werner Zehc7b2b7c2016-01-22 19:43:01 +0100604
Aaron Durbinfacf1492017-12-18 14:50:22 -0700605 int32_t address = cbfs_locate_entry(&image, data_size, param.pagesize,
Aaron Durbin4be16742015-09-15 17:00:23 -0500606 param.alignment, metadata_size);
Aaron Durbin4be16742015-09-15 17:00:23 -0500607
Julius Wernerc24db002020-11-18 18:00:31 -0800608 if (address < 0) {
Julius Werner772714d2021-12-13 10:16:44 -0800609 ERROR("'%s'(%u + %zu) can't fit in CBFS for page-size %#x, align %#x.\n",
610 param.name, metadata_size, data_size, param.pagesize, param.alignment);
Aaron Durbin4be16742015-09-15 17:00:23 -0500611 return 1;
612 }
613
Aaron Durbin4be16742015-09-15 17:00:23 -0500614 *cbfs_addr = address;
615 return 0;
616}
617
Patrick Georgif8204972015-08-11 15:16:24 +0200618typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +0200619 struct cbfs_file *header);
Stefan Reinauer63217582012-10-29 16:52:36 -0700620
Sol Bouchere3260a02015-03-25 13:40:08 -0700621static int cbfs_add_integer_component(const char *name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200622 uint64_t u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800623 uint32_t offset,
624 uint32_t headeroffset) {
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200625 struct cbfs_image image;
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200626 struct cbfs_file *header = NULL;
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200627 struct buffer buffer;
628 int i, ret = 1;
629
630 if (!name) {
631 ERROR("You need to specify -n/--name.\n");
632 return 1;
633 }
634
635 if (buffer_create(&buffer, 8, name) != 0)
636 return 1;
637
638 for (i = 0; i < 8; i++)
639 buffer.data[i] = (u64val >> i*8) & 0xff;
640
Sol Bouchere3260a02015-03-25 13:40:08 -0700641 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset)) {
642 ERROR("Selected image region is not a CBFS.\n");
643 goto done;
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200644 }
645
646 if (cbfs_get_entry(&image, name)) {
647 ERROR("'%s' already in ROM image.\n", name);
648 goto done;
649 }
650
Julius Wernerd4775652020-03-13 16:43:34 -0700651 header = cbfs_create_file_header(CBFS_TYPE_RAW,
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200652 buffer.size, name);
Jeremy Compostella66df1002023-10-23 13:00:33 -0700653 if (!header)
654 goto done;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700655
656 enum vb2_hash_algorithm algo = get_mh_cache()->cbfs_hash.algo;
657 if (algo != VB2_HASH_INVALID)
658 if (cbfs_add_file_hash(header, &buffer, algo)) {
659 ERROR("couldn't add hash for '%s'\n", name);
660 goto done;
661 }
662
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100663 if (cbfs_add_entry(&image, &buffer, offset, header, 0) != 0) {
Sol Boucher0e539312015-03-05 15:38:03 -0800664 ERROR("Failed to add %llu into ROM image as '%s'.\n",
665 (long long unsigned)u64val, name);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200666 goto done;
667 }
668
Julius Werner4bfbabd2020-05-06 17:27:02 -0700669 ret = maybe_update_metadata_hash(&image);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200670
671done:
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200672 free(header);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200673 buffer_delete(&buffer);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200674 return ret;
675}
676
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530677static int is_valid_topswap(void)
678{
679 switch (param.topswap_size) {
680 case (64 * KiB):
681 case (128 * KiB):
682 case (256 * KiB):
683 case (512 * KiB):
684 case (1 * MiB):
685 break;
686 default:
687 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
688 param.topswap_size);
689 return 0;
690 }
691 return 1;
692}
693
694static void fill_header_offset(void *location, uint32_t offset)
695{
Joel Kitching672d5ad2018-10-11 17:01:30 +0800696 // TODO: When we have a BE target, we'll need to store this as BE
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530697 write_le32(location, offset);
698}
699
700static int update_master_header_loc_topswap(struct cbfs_image *image,
701 void *h_loc, uint32_t header_offset)
702{
703 struct cbfs_file *entry;
704 void *ts_h_loc = h_loc;
705
706 entry = cbfs_get_entry(image, "bootblock");
707 if (entry == NULL) {
708 ERROR("Bootblock not in ROM image?!?\n");
709 return 1;
710 }
711
712 /*
713 * Check if the existing topswap boundary matches with
714 * the one provided.
715 */
Alex James02001a382021-12-19 16:41:59 -0600716 if (param.topswap_size != be32toh(entry->len)/2) {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530717 ERROR("Top swap boundary does not match\n");
718 return 1;
719 }
720
721 ts_h_loc -= param.topswap_size;
722 fill_header_offset(ts_h_loc, header_offset);
723
724 return 0;
725}
726
Patrick Georgi59e52b92015-09-10 15:28:27 +0200727static int cbfs_add_master_header(void)
728{
729 const char * const name = "cbfs master header";
730 struct cbfs_image image;
731 struct cbfs_file *header = NULL;
732 struct buffer buffer;
733 int ret = 1;
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600734 size_t offset;
735 size_t size;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530736 void *h_loc;
Patrick Georgi59e52b92015-09-10 15:28:27 +0200737
738 if (cbfs_image_from_buffer(&image, param.image_region,
739 param.headeroffset)) {
740 ERROR("Selected image region is not a CBFS.\n");
741 return 1;
742 }
743
744 if (cbfs_get_entry(&image, name)) {
745 ERROR("'%s' already in ROM image.\n", name);
746 return 1;
747 }
748
749 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
750 return 1;
751
752 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
Alex James02001a382021-12-19 16:41:59 -0600753 h->magic = htobe32(CBFS_HEADER_MAGIC);
754 h->version = htobe32(CBFS_HEADER_VERSION);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200755 /* The 4 bytes are left out for two reasons:
756 * 1. the cbfs master header pointer resides there
757 * 2. some cbfs implementations assume that an image that resides
758 * below 4GB has a bootblock and get confused when the end of the
759 * image is at 4GB == 0.
760 */
Alex James02001a382021-12-19 16:41:59 -0600761 h->bootblocksize = htobe32(4);
762 h->align = htobe32(CBFS_ALIGNMENT);
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600763 /* The offset and romsize fields within the master header are absolute
764 * values within the boot media. As such, romsize needs to relfect
765 * the end 'offset' for a CBFS. To achieve that the current buffer
766 * representing the CBFS region's size is added to the offset of
767 * the region within a larger image.
Patrick Georgi59e52b92015-09-10 15:28:27 +0200768 */
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600769 offset = buffer_get(param.image_region) -
770 buffer_get_original_backing(param.image_region);
771 size = buffer_size(param.image_region);
Alex James02001a382021-12-19 16:41:59 -0600772 h->romsize = htobe32(size + offset);
773 h->offset = htobe32(offset);
774 h->architecture = htobe32(CBFS_ARCHITECTURE_UNKNOWN);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200775
Julius Werner4bfbabd2020-05-06 17:27:02 -0700776 /* Never add a hash attribute to the master header. */
Julius Wernerd4775652020-03-13 16:43:34 -0700777 header = cbfs_create_file_header(CBFS_TYPE_CBFSHEADER,
Patrick Georgi59e52b92015-09-10 15:28:27 +0200778 buffer_size(&buffer), name);
Jeremy Compostella66df1002023-10-23 13:00:33 -0700779 if (!header)
780 goto done;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100781 if (cbfs_add_entry(&image, &buffer, 0, header, 0) != 0) {
Patrick Georgi59e52b92015-09-10 15:28:27 +0200782 ERROR("Failed to add cbfs master header into ROM image.\n");
783 goto done;
784 }
785
786 struct cbfs_file *entry;
787 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
788 ERROR("'%s' not in ROM image?!?\n", name);
789 goto done;
790 }
791
792 uint32_t header_offset = CBFS_SUBHEADER(entry) -
793 buffer_get(&image.buffer);
794 header_offset = -(buffer_size(&image.buffer) - header_offset);
795
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530796 h_loc = (void *)(buffer_get(&image.buffer) +
797 buffer_size(&image.buffer) - 4);
798 fill_header_offset(h_loc, header_offset);
799 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800800 * If top swap present, update the header
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530801 * location in secondary bootblock
802 */
803 if (param.topswap_size) {
804 if (update_master_header_loc_topswap(&image, h_loc,
Shaik Shahina425413c2022-11-05 01:07:06 +0530805 header_offset))
806 goto done;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530807 }
Patrick Georgi59e52b92015-09-10 15:28:27 +0200808
Julius Werner4bfbabd2020-05-06 17:27:02 -0700809 ret = maybe_update_metadata_hash(&image);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200810
811done:
812 free(header);
813 buffer_delete(&buffer);
814 return ret;
815}
816
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530817static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
818{
819 size_t bb_buf_size = buffer_size(buffer);
820
821 if (bb_buf_size > param.topswap_size) {
822 ERROR("Bootblock bigger than the topswap boundary\n");
823 ERROR("size = %zd, ts = %d\n", bb_buf_size,
824 param.topswap_size);
825 return 1;
826 }
827
828 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800829 * Allocate topswap_size*2 bytes for bootblock to
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530830 * accommodate the second bootblock.
831 */
832 struct buffer new_bootblock, bb1, bb2;
833 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
834 buffer->name))
835 return 1;
836
837 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
838 bb_buf_size);
839 buffer_splice(&bb2, &new_bootblock,
840 buffer_size(&new_bootblock) - bb_buf_size,
841 bb_buf_size);
842
Joel Kitching672d5ad2018-10-11 17:01:30 +0800843 /* Copy to first bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530844 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
Joel Kitching672d5ad2018-10-11 17:01:30 +0800845 /* Copy to second bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530846 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
847
848 buffer_delete(buffer);
849 buffer_clone(buffer, &new_bootblock);
850
Joel Kitching672d5ad2018-10-11 17:01:30 +0800851 /* Update the location (offset) of bootblock in the region */
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800852 return convert_region_offset(buffer_size(buffer), offset);
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530853}
854
Sol Bouchere3260a02015-03-25 13:40:08 -0700855static int cbfs_add_component(const char *filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800856 const char *name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800857 uint32_t headeroffset,
Stefan Reinauer2dd161f2015-03-04 00:55:03 +0100858 convert_buffer_t convert)
859{
Julius Werner6ddacd62021-12-03 14:46:53 -0800860 /*
861 * The steps used to determine the final placement offset in CBFS, in order:
862 *
Julius Werner20ad3652021-12-09 13:20:59 -0800863 * 1. If --base-address was passed, that value is used. If it was passed in the host
864 * address space, convert it to flash address space. (After that, |*offset| is always
865 * in the flash address space.)
Julius Werner6ddacd62021-12-03 14:46:53 -0800866 *
867 * 2. The convert() function may write a location back to |offset|, usually by calling
868 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
869 * the CBFS file in its final form (after any compression and conversion).
870 *
871 * 3. If --align was passed and the offset is still undecided at this point,
872 * do_cbfs_locate() is called to find an appropriately aligned location.
873 *
874 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
875 * location that fits.
876 */
Julius Wernerc24db002020-11-18 18:00:31 -0800877 uint32_t offset = param.baseaddress_assigned ? param.baseaddress : 0;
Julius Werner6ddacd62021-12-03 14:46:53 -0800878 size_t len_align = 0;
Julius Wernerc24db002020-11-18 18:00:31 -0800879
880 if (param.alignment && param.baseaddress_assigned) {
881 ERROR("Cannot specify both alignment and base address\n");
882 return 1;
883 }
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100884
Julius Werner6ddacd62021-12-03 14:46:53 -0800885 if (param.stage_xip && param.compression != CBFS_COMPRESS_NONE) {
886 ERROR("Cannot specify compression for XIP.\n");
887 return 1;
888 }
889
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800890 if (!filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800891 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700892 return 1;
893 }
894
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800895 if (!name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800896 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700897 return 1;
898 }
899
Julius Werner6ddacd62021-12-03 14:46:53 -0800900 if (param.type == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800901 ERROR("You need to specify a valid -t/--type.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700902 return 1;
903 }
904
Sol Bouchere3260a02015-03-25 13:40:08 -0700905 struct cbfs_image image;
Sol Boucher67a0a862015-03-18 12:36:27 -0700906 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800907 return 1;
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800908
Patrick Georgi4e54bf92015-08-11 14:35:39 +0200909 if (cbfs_get_entry(&image, name)) {
910 ERROR("'%s' already in ROM image.\n", name);
911 return 1;
912 }
913
Sol Bouchere3260a02015-03-25 13:40:08 -0700914 struct buffer buffer;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800915 if (buffer_from_file(&buffer, filename) != 0) {
916 ERROR("Could not load file '%s'.\n", filename);
Stefan Reinauer63217582012-10-29 16:52:36 -0700917 return 1;
918 }
919
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200920 struct cbfs_file *header =
Julius Werner6ddacd62021-12-03 14:46:53 -0800921 cbfs_create_file_header(param.type, buffer.size, name);
Jeremy Compostella66df1002023-10-23 13:00:33 -0700922 if (!header)
923 goto error;
Patrick Georgi4110abc2015-08-11 15:10:33 +0200924
Julius Werner4bfbabd2020-05-06 17:27:02 -0700925 /* Bootblock and CBFS header should never have file hashes. When adding
926 the bootblock it is important that we *don't* look up the metadata
927 hash yet (before it is added) or we'll cache an outdated result. */
Julius Werneraf20fd72022-05-27 18:33:15 -0700928 if (!verification_exclude(param.type)) {
Julius Werner4bfbabd2020-05-06 17:27:02 -0700929 enum vb2_hash_algorithm mh_algo = get_mh_cache()->cbfs_hash.algo;
930 if (mh_algo != VB2_HASH_INVALID && param.hash != mh_algo) {
931 if (param.hash == VB2_HASH_INVALID) {
932 param.hash = mh_algo;
933 } else {
934 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
935 vb2_get_hash_algorithm_name(param.hash),
936 vb2_get_hash_algorithm_name(mh_algo));
937 goto error;
938 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200939 }
Julius Wernerc24db002020-11-18 18:00:31 -0800940 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200941
Julius Wernerc24db002020-11-18 18:00:31 -0800942 /*
943 * Check if Intel CPU topswap is specified this will require a
944 * second bootblock to be added.
945 */
Julius Werner6ddacd62021-12-03 14:46:53 -0800946 if (param.type == CBFS_TYPE_BOOTBLOCK && param.topswap_size)
Julius Wernerc24db002020-11-18 18:00:31 -0800947 if (add_topswap_bootblock(&buffer, &offset))
948 goto error;
949
Julius Werner20ad3652021-12-09 13:20:59 -0800950 /* With --base-address we allow host space addresses -- if so, convert it here. */
951 if (IS_HOST_SPACE_ADDRESS(offset))
952 offset = convert_addr_space(param.image_region, offset);
953
Julius Wernerc24db002020-11-18 18:00:31 -0800954 if (convert && convert(&buffer, &offset, header) != 0) {
955 ERROR("Failed to parse file '%s'.\n", filename);
956 goto error;
957 }
958
Julius Werner6ddacd62021-12-03 14:46:53 -0800959 /* This needs to run after convert() to take compression into account. */
960 if (!offset && param.alignment)
Julius Werner772714d2021-12-13 10:16:44 -0800961 if (do_cbfs_locate(&offset, buffer_size(&buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -0800962 goto error;
963
964 /* This needs to run after convert() to hash the actual final file data. */
Julius Wernerc24db002020-11-18 18:00:31 -0800965 if (param.hash != VB2_HASH_INVALID &&
966 cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
967 ERROR("couldn't add hash for '%s'\n", name);
968 goto error;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700969 }
970
Werner Zehe9995f12016-01-14 13:22:37 +0100971 if (param.autogen_attr) {
972 /* Add position attribute if assigned */
973 if (param.baseaddress_assigned || param.stage_xip) {
974 struct cbfs_file_attr_position *attrs =
975 (struct cbfs_file_attr_position *)
976 cbfs_add_file_attr(header,
977 CBFS_FILE_ATTR_TAG_POSITION,
978 sizeof(struct cbfs_file_attr_position));
979 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800980 goto error;
Alex James02001a382021-12-19 16:41:59 -0600981 attrs->position = htobe32(offset);
Werner Zehe9995f12016-01-14 13:22:37 +0100982 }
983 /* Add alignment attribute if used */
984 if (param.alignment) {
985 struct cbfs_file_attr_align *attrs =
986 (struct cbfs_file_attr_align *)
987 cbfs_add_file_attr(header,
988 CBFS_FILE_ATTR_TAG_ALIGNMENT,
989 sizeof(struct cbfs_file_attr_align));
990 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800991 goto error;
Alex James02001a382021-12-19 16:41:59 -0600992 attrs->alignment = htobe32(param.alignment);
Werner Zehe9995f12016-01-14 13:22:37 +0100993 }
994 }
995
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100996 if (param.ibb) {
997 /* Mark as Initial Boot Block */
998 struct cbfs_file_attribute *attrs = cbfs_add_file_attr(header,
999 CBFS_FILE_ATTR_TAG_IBB,
1000 sizeof(struct cbfs_file_attribute));
1001 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -08001002 goto error;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001003 /* For Intel TXT minimum align is 16 */
1004 len_align = 16;
1005 }
1006
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001007 if (param.padding) {
1008 const uint32_t hs = sizeof(struct cbfs_file_attribute);
Julius Werner5779ca72020-11-20 16:12:40 -08001009 uint32_t size = ALIGN_UP(MAX(hs, param.padding),
1010 CBFS_ATTRIBUTE_ALIGN);
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001011 INFO("Padding %d bytes\n", size);
1012 struct cbfs_file_attribute *attr =
1013 (struct cbfs_file_attribute *)cbfs_add_file_attr(
1014 header, CBFS_FILE_ATTR_TAG_PADDING,
1015 size);
1016 if (attr == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -08001017 goto error;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001018 }
1019
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001020 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001021 ERROR("Failed to add '%s' into ROM image.\n", filename);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001022 goto error;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001023 }
1024
Patrick Georgi3ba501b2015-08-25 13:48:10 +02001025 free(header);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001026 buffer_delete(&buffer);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001027
1028 return maybe_update_metadata_hash(&image) || maybe_update_fmap_hash();
1029
1030error:
1031 free(header);
1032 buffer_delete(&buffer);
1033 return 1;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001034}
1035
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001036static int cbfstool_convert_raw(struct buffer *buffer,
1037 unused uint32_t *offset, struct cbfs_file *header)
1038{
1039 char *compressed;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001040 int decompressed_size, compressed_size;
1041 comp_func_ptr compress;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001042
Patrick Georgiecaa5702017-01-11 18:38:11 +01001043 decompressed_size = buffer->size;
1044 if (param.precompression) {
Nico Huber607796a2017-01-17 13:01:25 +01001045 param.compression = read_le32(buffer->data);
1046 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
Patrick Georgiecaa5702017-01-11 18:38:11 +01001047 compressed_size = buffer->size - 8;
1048 compressed = malloc(compressed_size);
1049 if (!compressed)
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001050 return -1;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001051 memcpy(compressed, buffer->data + 8, compressed_size);
1052 } else {
Julius Werner105cdf52020-10-23 19:19:32 -07001053 if (param.compression == CBFS_COMPRESS_NONE)
1054 goto out;
1055
Patrick Georgiecaa5702017-01-11 18:38:11 +01001056 compress = compression_function(param.compression);
1057 if (!compress)
1058 return -1;
1059 compressed = calloc(buffer->size, 1);
1060 if (!compressed)
1061 return -1;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001062
Patrick Georgiecaa5702017-01-11 18:38:11 +01001063 if (compress(buffer->data, buffer->size,
1064 compressed, &compressed_size)) {
1065 WARN("Compression failed - disabled\n");
1066 free(compressed);
Julius Werner105cdf52020-10-23 19:19:32 -07001067 goto out;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001068 }
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001069 }
Patrick Georgiecaa5702017-01-11 18:38:11 +01001070
1071 struct cbfs_file_attr_compression *attrs =
1072 (struct cbfs_file_attr_compression *)
1073 cbfs_add_file_attr(header,
1074 CBFS_FILE_ATTR_TAG_COMPRESSION,
1075 sizeof(struct cbfs_file_attr_compression));
1076 if (attrs == NULL) {
1077 free(compressed);
1078 return -1;
1079 }
Alex James02001a382021-12-19 16:41:59 -06001080 attrs->compression = htobe32(param.compression);
1081 attrs->decompressed_size = htobe32(decompressed_size);
Patrick Georgiecaa5702017-01-11 18:38:11 +01001082
1083 free(buffer->data);
1084 buffer->data = compressed;
1085 buffer->size = compressed_size;
1086
Julius Werner105cdf52020-10-23 19:19:32 -07001087out:
Alex James02001a382021-12-19 16:41:59 -06001088 header->len = htobe32(buffer->size);
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001089 return 0;
1090}
1091
Aaron Durbin8a414a02015-10-01 17:02:45 -05001092static int cbfstool_convert_fsp(struct buffer *buffer,
1093 uint32_t *offset, struct cbfs_file *header)
1094{
1095 uint32_t address;
1096 struct buffer fsp;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001097
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001098 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001099 * There are 4 different cases here:
1100 *
1101 * 1. --xip and --base-address: we need to place the binary at the given base address
Julius Werner20ad3652021-12-09 13:20:59 -08001102 * in the CBFS image and relocate it to that address. *offset was already filled in,
1103 * but we need to convert it to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001104 *
1105 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1106 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
Julius Werner20ad3652021-12-09 13:20:59 -08001107 * This also needs to be converted to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001108 *
1109 * 3. No --xip but a --base-address: special case where --base-address does not have its
1110 * normal meaning, instead we use it as the relocation target address. We explicitly
1111 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1112 *
1113 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1114 * not be relocated. Just chain directly to convert_raw() for compression.
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001115 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001116
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001117 if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001118 if (!param.baseaddress_assigned) {
1119 param.alignment = 4*1024;
Julius Werner772714d2021-12-13 10:16:44 -08001120 if (do_cbfs_locate(offset, buffer_size(buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -08001121 return -1;
1122 }
Julius Werner20ad3652021-12-09 13:20:59 -08001123 assert(!IS_HOST_SPACE_ADDRESS(*offset));
1124 address = convert_addr_space(param.image_region, *offset);
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001125 } else {
Aaron Durbin493ec922016-05-16 15:18:45 -05001126 if (param.baseaddress_assigned == 0) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001127 INFO("Honoring pre-linked FSP module, no relocation.\n");
1128 return cbfstool_convert_raw(buffer, offset, header);
Aaron Durbin493ec922016-05-16 15:18:45 -05001129 } else {
1130 address = param.baseaddress;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001131 *offset = 0;
Furquan Shaikhc3bb6922020-06-09 21:20:27 -07001132 }
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001133 }
Aaron Durbin8a414a02015-10-01 17:02:45 -05001134
1135 /* Create a copy of the buffer to attempt relocation. */
1136 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
1137 return -1;
1138
1139 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
1140
1141 /* Replace the buffer contents w/ the relocated ones on success. */
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001142 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
1143 > 0) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001144 buffer_delete(buffer);
1145 buffer_clone(buffer, &fsp);
1146 } else {
1147 buffer_delete(&fsp);
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001148 WARN("Invalid FSP variant.\n");
Aaron Durbin8a414a02015-10-01 17:02:45 -05001149 }
1150
1151 /* Let the raw path handle all the cbfs metadata logic. */
1152 return cbfstool_convert_raw(buffer, offset, header);
1153}
1154
Patrick Georgif8204972015-08-11 15:16:24 +02001155static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001156 struct cbfs_file *header)
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001157{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001158 struct buffer output;
Julius Wernerc24db002020-11-18 18:00:31 -08001159 size_t data_size;
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001160 int ret;
Aaron Durbin4be16742015-09-15 17:00:23 -05001161
Julius Wernerc24db002020-11-18 18:00:31 -08001162 if (elf_program_file_size(buffer, &data_size) < 0) {
1163 ERROR("Could not obtain ELF size\n");
1164 return 1;
1165 }
1166
1167 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001168 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1169 * here. That is okay because XIP stages may not be compressed, so their size cannot
1170 * change anymore at a later point.
Julius Wernerc24db002020-11-18 18:00:31 -08001171 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001172 if (param.stage_xip &&
1173 do_cbfs_locate(offset, data_size)) {
Julius Wernerc24db002020-11-18 18:00:31 -08001174 ERROR("Could not find location for stage.\n");
1175 return 1;
1176 }
1177
Julius Werner81dc20e2020-10-15 17:37:57 -07001178 struct cbfs_file_attr_stageheader *stageheader = (void *)
1179 cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
1180 sizeof(struct cbfs_file_attr_stageheader));
1181 if (!stageheader)
1182 return -1;
1183
Aaron Durbin4be16742015-09-15 17:00:23 -05001184 if (param.stage_xip) {
Julius Werner20ad3652021-12-09 13:20:59 -08001185 uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
1186 assert(IS_HOST_SPACE_ADDRESS(host_space_address));
1187 ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
Jeremy Compostellac9cae532023-08-30 10:25:33 -07001188 param.ignore_sections, stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001189 } else {
Jeremy Compostellac9cae532023-08-30 10:25:33 -07001190 ret = parse_elf_to_stage(buffer, &output, param.ignore_sections,
Julius Werner81dc20e2020-10-15 17:37:57 -07001191 stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001192 }
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001193 if (ret != 0)
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001194 return -1;
Julius Werner81dc20e2020-10-15 17:37:57 -07001195
1196 /* Store a hash of original uncompressed stage to compare later. */
1197 size_t decmp_size = buffer_size(&output);
1198 uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
1199
1200 /* Chain to base conversion routine to handle compression. */
1201 ret = cbfstool_convert_raw(&output, offset, header);
1202 if (ret != 0)
1203 goto fail;
1204
1205 /* Special care must be taken for LZ4-compressed stages that the BSS is
1206 large enough to provide scratch space for in-place decompression. */
1207 if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
Alex James02001a382021-12-19 16:41:59 -06001208 size_t memlen = be32toh(stageheader->memlen);
Julius Werner81dc20e2020-10-15 17:37:57 -07001209 size_t compressed_size = buffer_size(&output);
1210 uint8_t *compare_buffer = malloc(memlen);
1211 uint8_t *start = compare_buffer + memlen - compressed_size;
1212 if (!compare_buffer) {
1213 ERROR("Out of memory\n");
1214 goto fail;
1215 }
1216 memcpy(start, buffer_get(&output), compressed_size);
1217 ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
1218 if (ret == 0) {
1219 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1220 free(compare_buffer);
1221 goto fail;
1222 } else if (ret != (int)decmp_size ||
1223 decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
1224 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1225 free(compare_buffer);
1226 goto fail;
1227 }
1228 free(compare_buffer);
1229 }
1230
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001231 buffer_delete(buffer);
Julius Werner81dc20e2020-10-15 17:37:57 -07001232 buffer_clone(buffer, &output);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001233 return 0;
Julius Werner81dc20e2020-10-15 17:37:57 -07001234
1235fail:
1236 buffer_delete(&output);
1237 return -1;
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001238}
1239
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001240static int cbfstool_convert_mkpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001241 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001242{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001243 struct buffer output;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001244 int ret;
Joel Kitching672d5ad2018-10-11 17:01:30 +08001245 /* Per default, try and see if payload is an ELF binary */
Sol Boucher65336712015-05-07 21:00:05 -07001246 ret = parse_elf_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001247
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001248 /* If it's not an ELF, see if it's a FIT */
1249 if (ret != 0) {
1250 ret = parse_fit_to_payload(buffer, &output, param.compression);
1251 if (ret == 0)
Julius Werner00572622022-05-26 20:29:42 -07001252 header->type = htobe32(CBFS_TYPE_FIT_PAYLOAD);
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001253 }
1254
1255 /* If it's not an FIT, see if it's a UEFI FV */
Stefan Reinauer543a6822013-02-04 15:39:13 -08001256 if (ret != 0)
Sol Boucher65336712015-05-07 21:00:05 -07001257 ret = parse_fv_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001258
Patrick Georgide36d332013-08-27 20:22:21 +02001259 /* If it's neither ELF nor UEFI Fv, try bzImage */
1260 if (ret != 0)
1261 ret = parse_bzImage_to_payload(buffer, &output,
Sol Boucher65336712015-05-07 21:00:05 -07001262 param.initrd, param.cmdline, param.compression);
Patrick Georgide36d332013-08-27 20:22:21 +02001263
Stefan Reinauer543a6822013-02-04 15:39:13 -08001264 /* Not a supported payload type */
1265 if (ret != 0) {
1266 ERROR("Not a supported payload type (ELF / FV).\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001267 buffer_delete(buffer);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001268 return -1;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001269 }
1270
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001271 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001272 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001273 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001274 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001275 return 0;
1276}
1277
1278static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001279 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001280{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001281 struct buffer output;
1282 if (parse_flat_binary_to_payload(buffer, &output,
1283 param.loadaddress,
1284 param.entrypoint,
Sol Boucher65336712015-05-07 21:00:05 -07001285 param.compression) != 0) {
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001286 return -1;
1287 }
1288 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001289 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001290 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001291 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001292 return 0;
1293}
1294
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001295static int cbfs_add(void)
1296{
Julius Wernerc24db002020-11-18 18:00:31 -08001297 convert_buffer_t convert = cbfstool_convert_raw;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001298
Julius Wernerd4775652020-03-13 16:43:34 -07001299 if (param.type == CBFS_TYPE_FSP) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001300 convert = cbfstool_convert_fsp;
Julius Werner6ddacd62021-12-03 14:46:53 -08001301 } else if (param.type == CBFS_TYPE_STAGE) {
1302 ERROR("stages can only be added with cbfstool add-stage\n");
1303 return 1;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001304 } else if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001305 ERROR("cbfstool add supports xip only for FSP component type\n");
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001306 return 1;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001307 }
1308
Sol Bouchere3260a02015-03-25 13:40:08 -07001309 return cbfs_add_component(param.filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001310 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001311 param.headeroffset,
Aaron Durbin8a414a02015-10-01 17:02:45 -05001312 convert);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001313}
1314
Stefan Reinauer63217582012-10-29 16:52:36 -07001315static int cbfs_add_stage(void)
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001316{
Julius Werner6ddacd62021-12-03 14:46:53 -08001317 if (param.stage_xip && param.baseaddress_assigned) {
1318 ERROR("Cannot specify base address for XIP.\n");
1319 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -05001320 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001321 param.type = CBFS_TYPE_STAGE;
Aaron Durbin4be16742015-09-15 17:00:23 -05001322
Sol Bouchere3260a02015-03-25 13:40:08 -07001323 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001324 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001325 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001326 cbfstool_convert_mkstage);
1327}
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001328
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001329static int cbfs_add_payload(void)
1330{
Julius Werner6ddacd62021-12-03 14:46:53 -08001331 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001332 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001333 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001334 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001335 cbfstool_convert_mkpayload);
Stefan Reinauer63217582012-10-29 16:52:36 -07001336}
1337
1338static int cbfs_add_flat_binary(void)
1339{
Hung-Te Lind1739622013-01-28 14:23:49 +08001340 if (param.loadaddress == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001341 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001342 "-l/--load-address.\n");
1343 return 1;
1344 }
Hung-Te Lind1739622013-01-28 14:23:49 +08001345 if (param.entrypoint == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001346 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001347 "-e/--entry-point.\n");
1348 return 1;
1349 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001350 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001351 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001352 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001353 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001354 cbfstool_convert_mkflatpayload);
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001355}
1356
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001357static int cbfs_add_integer(void)
1358{
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01001359 if (!param.u64val_assigned) {
1360 ERROR("You need to specify a value to write.\n");
1361 return 1;
1362 }
Sol Bouchere3260a02015-03-25 13:40:08 -07001363 return cbfs_add_integer_component(param.name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001364 param.u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001365 param.baseaddress,
1366 param.headeroffset);
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001367}
1368
Stefan Reinauer63217582012-10-29 16:52:36 -07001369static int cbfs_remove(void)
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001370{
Hung-Te Lind1739622013-01-28 14:23:49 +08001371 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001372 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001373 return 1;
1374 }
1375
Sol Bouchere3260a02015-03-25 13:40:08 -07001376 struct cbfs_image image;
1377 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001378 param.headeroffset))
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001379 return 1;
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001380
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001381 if (cbfs_remove_entry(&image, param.name) != 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001382 ERROR("Removing file '%s' failed.\n",
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001383 param.name);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001384 return 1;
1385 }
1386
Julius Werner4bfbabd2020-05-06 17:27:02 -07001387 return maybe_update_metadata_hash(&image);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001388}
1389
Stefan Reinauer63217582012-10-29 16:52:36 -07001390static int cbfs_create(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001391{
Sol Boucher67a0a862015-03-18 12:36:27 -07001392 struct cbfs_image image;
1393 memset(&image, 0, sizeof(image));
1394 buffer_clone(&image.buffer, param.image_region);
1395
1396 if (param.fmap) {
1397 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
1398 param.baseaddress_assigned ||
1399 param.headeroffset_assigned ||
1400 param.cbfsoffset_assigned ||
Sol Boucher67a0a862015-03-18 12:36:27 -07001401 param.bootblock) {
Patrick Georgi45acb342015-07-14 22:18:23 +02001402 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
Sol Boucher67a0a862015-03-18 12:36:27 -07001403 return 1;
1404 }
1405
1406 return cbfs_image_create(&image, image.buffer.size);
1407 }
1408
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001409 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
Stefan Reinauer60a4a732013-03-28 16:46:07 -07001410 ERROR("You need to specify -m/--machine arch.\n");
David Hendricks90ca3b62012-11-16 14:48:22 -08001411 return 1;
1412 }
1413
Sol Bouchere3260a02015-03-25 13:40:08 -07001414 struct buffer bootblock;
Julius Wernerefcee762014-11-10 13:14:24 -08001415 if (!param.bootblock) {
1416 DEBUG("-B not given, creating image without bootblock.\n");
Patrick Georgi6b2d2db2016-12-14 16:11:58 +01001417 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
1418 return 1;
Julius Wernerefcee762014-11-10 13:14:24 -08001419 } else if (buffer_from_file(&bootblock, param.bootblock)) {
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001420 return 1;
1421 }
1422
Julius Wernerefcee762014-11-10 13:14:24 -08001423 if (!param.alignment)
Patrick Georgi45acb342015-07-14 22:18:23 +02001424 param.alignment = CBFS_ALIGNMENT;
Julius Wernerefcee762014-11-10 13:14:24 -08001425
1426 // Set default offsets. x86, as usual, needs to be a special snowflake.
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001427 if (!param.baseaddress_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001428 if (param.arch == CBFS_ARCHITECTURE_X86) {
1429 // Make sure there's at least enough room for rel_offset
Sol Boucher67a0a862015-03-18 12:36:27 -07001430 param.baseaddress = param.size -
1431 MAX(bootblock.size, sizeof(int32_t));
Julius Wernerefcee762014-11-10 13:14:24 -08001432 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1433 param.baseaddress);
1434 } else {
1435 param.baseaddress = 0;
1436 DEBUG("bootblock starts at address 0x0.\n");
1437 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001438 }
1439 if (!param.headeroffset_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001440 if (param.arch == CBFS_ARCHITECTURE_X86) {
1441 param.headeroffset = param.baseaddress -
1442 sizeof(struct cbfs_header);
1443 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1444 param.headeroffset);
1445 } else {
1446 param.headeroffset = align_up(param.baseaddress +
1447 bootblock.size, sizeof(uint32_t));
1448 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1449 param.headeroffset);
1450 }
1451 }
1452 if (!param.cbfsoffset_assigned) {
1453 if (param.arch == CBFS_ARCHITECTURE_X86) {
1454 param.cbfsoffset = 0;
1455 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1456 } else {
1457 param.cbfsoffset = align_up(param.headeroffset +
1458 sizeof(struct cbfs_header),
Patrick Georgi45acb342015-07-14 22:18:23 +02001459 CBFS_ALIGNMENT);
Julius Wernerefcee762014-11-10 13:14:24 -08001460 DEBUG("CBFS entries start beind master header (%#x).\n",
1461 param.cbfsoffset);
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001462 }
1463 }
1464
Sol Boucher67a0a862015-03-18 12:36:27 -07001465 int ret = cbfs_legacy_image_create(&image,
1466 param.arch,
Patrick Georgi45acb342015-07-14 22:18:23 +02001467 CBFS_ALIGNMENT,
Sol Boucher67a0a862015-03-18 12:36:27 -07001468 &bootblock,
1469 param.baseaddress,
1470 param.headeroffset,
1471 param.cbfsoffset);
Sol Bouchere3260a02015-03-25 13:40:08 -07001472 buffer_delete(&bootblock);
Sol Boucher67a0a862015-03-18 12:36:27 -07001473 return ret;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001474}
1475
Sol Boucher67a0a862015-03-18 12:36:27 -07001476static int cbfs_layout(void)
1477{
1478 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1479 if (!fmap) {
1480 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1481 return 1;
1482 }
1483
1484 printf("This image contains the following sections that can be %s with this tool:\n",
1485 param.show_immutable ? "accessed" : "manipulated");
1486 puts("");
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001487 for (unsigned i = 0; i < fmap->nareas; ++i) {
1488 const struct fmap_area *current = fmap->areas + i;
Sol Boucher67a0a862015-03-18 12:36:27 -07001489
1490 bool readonly = partitioned_file_fmap_count(param.image_file,
1491 partitioned_file_fmap_select_children_of, current) ||
1492 region_is_flashmap((const char *)current->name);
1493 if (!param.show_immutable && readonly)
1494 continue;
1495
1496 printf("'%s'", current->name);
1497
1498 // Detect consecutive sections that describe the same region and
1499 // show them as aliases. This cannot find equivalent entries
1500 // that aren't adjacent; however, fmaptool doesn't generate
1501 // FMAPs with such sections, so this convenience feature works
1502 // for all but the strangest manually created FMAP binaries.
1503 // TODO: This could be done by parsing the FMAP into some kind
1504 // of tree that had duplicate lists in addition to child lists,
1505 // which would allow covering that weird, unlikely case as well.
1506 unsigned lookahead;
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001507 for (lookahead = 1; i + lookahead < fmap->nareas;
Sol Boucher67a0a862015-03-18 12:36:27 -07001508 ++lookahead) {
1509 const struct fmap_area *consecutive =
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001510 fmap->areas + i + lookahead;
Sol Boucher67a0a862015-03-18 12:36:27 -07001511 if (consecutive->offset != current->offset ||
1512 consecutive->size != current->size)
1513 break;
1514 printf(", '%s'", consecutive->name);
1515 }
1516 if (lookahead > 1)
1517 fputs(" are aliases for the same region", stdout);
1518
1519 const char *qualifier = "";
1520 if (readonly)
1521 qualifier = "read-only, ";
1522 else if (region_is_modern_cbfs((const char *)current->name))
1523 qualifier = "CBFS, ";
Patrick Georgi848e30d2019-06-18 22:02:13 +02001524 else if (current->flags & FMAP_AREA_PRESERVE)
1525 qualifier = "preserve, ";
Werner Zeh7850b582018-04-26 09:26:38 +02001526 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1527 current->offset);
Sol Boucher67a0a862015-03-18 12:36:27 -07001528
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001529 i += lookahead - 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001530 }
1531 puts("");
1532
1533 if (param.show_immutable) {
1534 puts("It is at least possible to perform the read action on every section listed above.");
1535 } else {
1536 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1537 puts("To see the image's read-only sections as well, rerun with the -w option.");
1538 }
1539
1540 return 0;
1541}
1542
Bill XIEac1362502022-07-08 16:53:21 +08001543static enum cb_err verify_walker(__always_unused cbfs_dev_t dev, size_t offset,
Julius Werner423cd062022-05-24 18:18:46 -07001544 const union cbfs_mdata *mdata, size_t already_read, void *arg)
1545{
1546 uint32_t type = be32toh(mdata->h.type);
1547 uint32_t data_offset = be32toh(mdata->h.offset);
1548 if (verification_exclude(type))
1549 return CB_CBFS_NOT_FOUND;
1550 assert(already_read == data_offset);
1551 const struct vb2_hash *hash = cbfs_file_hash(mdata);
1552 if (!hash)
1553 return CB_ERR;
1554 void *file_data = arg + offset + data_offset;
Julius Wernerd96ca242022-08-08 18:08:35 -07001555 if (vb2_hash_verify(false, file_data, be32toh(mdata->h.len), hash) != VB2_SUCCESS)
Julius Werner423cd062022-05-24 18:18:46 -07001556 return CB_CBFS_HASH_MISMATCH;
1557 return CB_CBFS_NOT_FOUND;
1558}
1559
Stefan Reinauer63217582012-10-29 16:52:36 -07001560static int cbfs_print(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001561{
Hung-Te Lin3bb035b2013-01-29 02:15:49 +08001562 struct cbfs_image image;
Sol Bouchere3260a02015-03-25 13:40:08 -07001563 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001564 param.headeroffset))
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001565 return 1;
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001566 if (param.machine_parseable) {
1567 if (verbose)
1568 printf("[FMAP REGION]\t%s\n", param.region_name);
1569 cbfs_print_parseable_directory(&image);
1570 } else {
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001571 printf("FMAP REGION: %s\n", param.region_name);
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001572 cbfs_print_directory(&image);
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001573 }
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001574
Julius Werner4bfbabd2020-05-06 17:27:02 -07001575 if (verbose) {
Julius Werner423cd062022-05-24 18:18:46 -07001576 const char *verification_state = "fully valid";
Julius Werner4bfbabd2020-05-06 17:27:02 -07001577 struct mh_cache *mhc = get_mh_cache();
1578 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1579 return 0;
1580
1581 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
Julius Werner423cd062022-05-24 18:18:46 -07001582 enum cb_err err = cbfs_walk(&image, verify_walker, buffer_get(&image.buffer),
1583 &real_hash, CBFS_WALK_WRITEBACK_HASH);
1584 if (err == CB_CBFS_HASH_MISMATCH)
1585 verification_state = "invalid file hashes";
1586 else if (err != CB_CBFS_NOT_FOUND)
1587 verification_state = "missing file hashes";
Julius Werner4bfbabd2020-05-06 17:27:02 -07001588 char *hash_str = bintohex(real_hash.raw,
1589 vb2_digest_size(real_hash.algo));
1590 printf("[METADATA HASH]\t%s:%s",
1591 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1592 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1593 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
Julius Werner423cd062022-05-24 18:18:46 -07001594 vb2_digest_size(real_hash.algo))) {
Julius Werner4bfbabd2020-05-06 17:27:02 -07001595 printf(":valid");
Julius Werner423cd062022-05-24 18:18:46 -07001596 } else {
Julius Werner4bfbabd2020-05-06 17:27:02 -07001597 printf(":invalid");
Julius Werner423cd062022-05-24 18:18:46 -07001598 verification_state = "invalid metadata hash";
1599 }
Julius Werner4bfbabd2020-05-06 17:27:02 -07001600 }
1601 printf("\n");
Julius Werner423cd062022-05-24 18:18:46 -07001602 printf("[CBFS VERIFICATION (%s)]\t%s\n", param.region_name, verification_state);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001603 free(hash_str);
1604 }
1605
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001606 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001607}
1608
Stefan Reinauer63217582012-10-29 16:52:36 -07001609static int cbfs_extract(void)
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001610{
Hung-Te Lind1739622013-01-28 14:23:49 +08001611 if (!param.filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001612 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001613 return 1;
1614 }
1615
Hung-Te Lind1739622013-01-28 14:23:49 +08001616 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001617 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001618 return 1;
1619 }
1620
Sol Bouchere3260a02015-03-25 13:40:08 -07001621 struct cbfs_image image;
1622 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001623 param.headeroffset))
1624 return 1;
1625
Aaron Durbin17625022015-10-27 13:17:52 -05001626 return cbfs_export_entry(&image, param.name, param.filename,
Joel Kitching21fdd892018-08-09 17:49:52 +08001627 param.arch, !param.unprocessed);
Sol Boucher67a0a862015-03-18 12:36:27 -07001628}
1629
1630static int cbfs_write(void)
1631{
1632 if (!param.filename) {
1633 ERROR("You need to specify a valid input -f/--file.\n");
1634 return 1;
1635 }
1636 if (!partitioned_file_is_partitioned(param.image_file)) {
1637 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001638 return 1;
1639 }
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001640
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001641 if (!param.force && region_is_modern_cbfs(param.region_name)) {
Sol Boucher67a0a862015-03-18 12:36:27 -07001642 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1643 param.region_name);
1644 return 1;
1645 }
1646
1647 struct buffer new_content;
1648 if (buffer_from_file(&new_content, param.filename))
1649 return 1;
1650
1651 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1652 strlen(FMAP_SIGNATURE))) {
1653 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1654 param.filename);
1655 buffer_delete(&new_content);
1656 return 1;
1657 }
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001658 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
Sol Boucher67a0a862015-03-18 12:36:27 -07001659 strlen(CBFS_FILE_MAGIC))) {
1660 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1661 param.filename);
1662 buffer_delete(&new_content);
1663 return 1;
1664 }
1665
1666 unsigned offset = 0;
1667 if (param.fill_partial_upward && param.fill_partial_downward) {
1668 ERROR("You may only specify one of -u and -d.\n");
1669 buffer_delete(&new_content);
1670 return 1;
1671 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1672 if (new_content.size != param.image_region->size) {
1673 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",
1674 new_content.size, param.image_region->size);
1675 buffer_delete(&new_content);
1676 return 1;
1677 }
1678 } else {
1679 if (new_content.size > param.image_region->size) {
1680 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1681 new_content.size, param.image_region->size);
1682 buffer_delete(&new_content);
1683 return 1;
1684 }
Patrick Georgid9edb182016-12-06 18:55:26 +01001685 if (param.u64val == (uint64_t)-1) {
1686 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1687 param.fill_partial_upward ? "bottom" : "top");
1688 } else if (param.u64val > 0xff) {
1689 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1690 buffer_delete(&new_content);
1691 return 1;
1692 } else {
1693 memset(buffer_get(param.image_region),
1694 param.u64val & 0xff,
1695 buffer_size(param.image_region));
1696 }
Sol Boucher67a0a862015-03-18 12:36:27 -07001697 if (param.fill_partial_downward)
1698 offset = param.image_region->size - new_content.size;
1699 }
1700
1701 memcpy(param.image_region->data + offset, new_content.data,
1702 new_content.size);
1703 buffer_delete(&new_content);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001704
1705 return maybe_update_fmap_hash();
Sol Boucher67a0a862015-03-18 12:36:27 -07001706}
1707
1708static int cbfs_read(void)
1709{
1710 if (!param.filename) {
1711 ERROR("You need to specify a valid output -f/--file.\n");
1712 return 1;
1713 }
1714 if (!partitioned_file_is_partitioned(param.image_file)) {
1715 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1716 return 1;
1717 }
1718
1719 return buffer_write_file(param.image_region, param.filename);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001720}
1721
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001722static int cbfs_copy(void)
1723{
Patrick Georgi214e4af2015-11-20 19:22:50 +01001724 struct cbfs_image src_image;
Patrick Georgibd0bb232015-11-20 21:48:18 +01001725 struct buffer src_buf;
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001726
Patrick Georgibd0bb232015-11-20 21:48:18 +01001727 if (!param.source_region) {
1728 ERROR("You need to specify -R/--source-region.\n");
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001729 return 1;
Sol Bouchere3260a02015-03-25 13:40:08 -07001730 }
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001731
Patrick Georgibd0bb232015-11-20 21:48:18 +01001732 /* Obtain the source region and convert it to a cbfs_image. */
1733 if (!partitioned_file_read_region(&src_buf, param.image_file,
1734 param.source_region)) {
1735 ERROR("Region not found in image: %s\n", param.source_region);
1736 return 1;
1737 }
1738
1739 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1740 return 1;
1741
1742 return cbfs_copy_instance(&src_image, param.image_region);
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001743}
1744
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001745static int cbfs_compact(void)
1746{
1747 struct cbfs_image image;
1748 if (cbfs_image_from_buffer(&image, param.image_region,
1749 param.headeroffset))
1750 return 1;
1751 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1752 return cbfs_compact_instance(&image);
1753}
1754
Patrick Georgi5d982d72017-09-19 14:39:58 +02001755static int cbfs_expand(void)
1756{
1757 struct buffer src_buf;
1758
1759 /* Obtain the source region. */
1760 if (!partitioned_file_read_region(&src_buf, param.image_file,
1761 param.region_name)) {
1762 ERROR("Region not found in image: %s\n", param.source_region);
1763 return 1;
1764 }
1765
1766 return cbfs_expand_to_region(param.image_region);
1767}
1768
Patrick Georgi12631a42017-09-20 11:59:18 +02001769static int cbfs_truncate(void)
1770{
1771 struct buffer src_buf;
1772
1773 /* Obtain the source region. */
1774 if (!partitioned_file_read_region(&src_buf, param.image_file,
1775 param.region_name)) {
1776 ERROR("Region not found in image: %s\n", param.source_region);
1777 return 1;
1778 }
1779
1780 uint32_t size;
1781 int result = cbfs_truncate_space(param.image_region, &size);
Jakub Czapigaaa415632022-08-01 16:01:28 +02001782 if (!result)
1783 printf("0x%x\n", size);
Patrick Georgi12631a42017-09-20 11:59:18 +02001784 return result;
1785}
1786
Stefan Reinauera1e48242011-10-21 14:24:57 -07001787static const struct command commands[] = {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301788 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001789 {"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 +01001790 true, true},
Raul E Rangeldb280402021-06-29 13:26:41 -06001791 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
Werner Zehe9995f12016-01-14 13:22:37 +01001792 true, true},
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001793 {"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 +01001794 true, true},
1795 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301796 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001797 {"compact", "r:h?", cbfs_compact, true, true},
Patrick Georgibd0bb232015-11-20 21:48:18 +01001798 {"copy", "r:R:h?", cbfs_copy, true, true},
Patrick Georgi45acb342015-07-14 22:18:23 +02001799 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
Joel Kitching21fdd892018-08-09 17:49:52 +08001800 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001801 {"layout", "wvh?", cbfs_layout, false, false},
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001802 {"print", "H:r:vkh?", cbfs_print, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001803 {"read", "r:f:vh?", cbfs_read, true, false},
1804 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
Patrick Georgid9edb182016-12-06 18:55:26 +01001805 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
Patrick Georgi5d982d72017-09-19 14:39:58 +02001806 {"expand", "r:h?", cbfs_expand, true, true},
Patrick Georgi12631a42017-09-20 11:59:18 +02001807 {"truncate", "r:h?", cbfs_truncate, true, true},
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001808};
1809
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001810enum {
1811 /* begin after ASCII characters */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001812 LONGOPT_START = 256,
1813 LONGOPT_IBB = LONGOPT_START,
Arthur Heymans34a7e662022-10-06 09:42:15 +02001814 LONGOPT_MMAP,
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001815 LONGOPT_END,
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001816};
1817
Stefan Reinauer63217582012-10-29 16:52:36 -07001818static struct option long_options[] = {
Julius Wernerefcee762014-11-10 13:14:24 -08001819 {"alignment", required_argument, 0, 'a' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001820 {"base-address", required_argument, 0, 'b' },
1821 {"bootblock", required_argument, 0, 'B' },
Julius Wernerefcee762014-11-10 13:14:24 -08001822 {"cmdline", required_argument, 0, 'C' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001823 {"compression", required_argument, 0, 'c' },
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301824 {"topswap-size", required_argument, 0, 'j' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001825 {"empty-fits", required_argument, 0, 'x' },
1826 {"entry-point", required_argument, 0, 'e' },
1827 {"file", required_argument, 0, 'f' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001828 {"fill-downward", no_argument, 0, 'd' },
1829 {"fill-upward", no_argument, 0, 'u' },
1830 {"flashmap", required_argument, 0, 'M' },
1831 {"fmap-regions", required_argument, 0, 'r' },
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001832 {"force", no_argument, 0, 'F' },
Patrick Georgibd0bb232015-11-20 21:48:18 +01001833 {"source-region", required_argument, 0, 'R' },
Patrick Georgi89f20342015-10-01 15:54:04 +02001834 {"hash-algorithm",required_argument, 0, 'A' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001835 {"header-offset", required_argument, 0, 'H' },
Julius Wernerefcee762014-11-10 13:14:24 -08001836 {"help", no_argument, 0, 'h' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001837 {"ignore-sec", required_argument, 0, 'S' },
1838 {"initrd", required_argument, 0, 'I' },
1839 {"int", required_argument, 0, 'i' },
1840 {"load-address", required_argument, 0, 'l' },
1841 {"machine", required_argument, 0, 'm' },
1842 {"name", required_argument, 0, 'n' },
1843 {"offset", required_argument, 0, 'o' },
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001844 {"padding", required_argument, 0, 'p' },
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001845 {"pow2page", no_argument, 0, 'Q' },
Rizwan Qureshic1072f22018-06-04 23:02:46 +05301846 {"ucode-region", required_argument, 0, 'q' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001847 {"size", required_argument, 0, 's' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001848 {"type", required_argument, 0, 't' },
1849 {"verbose", no_argument, 0, 'v' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001850 {"with-readonly", no_argument, 0, 'w' },
Aaron Durbin4be16742015-09-15 17:00:23 -05001851 {"xip", no_argument, 0, 'y' },
Werner Zehe9995f12016-01-14 13:22:37 +01001852 {"gen-attribute", no_argument, 0, 'g' },
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001853 {"mach-parseable",no_argument, 0, 'k' },
Joel Kitching21fdd892018-08-09 17:49:52 +08001854 {"unprocessed", no_argument, 0, 'U' },
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001855 {"ibb", no_argument, 0, LONGOPT_IBB },
Arthur Heymans34a7e662022-10-06 09:42:15 +02001856 {"mmap", required_argument, 0, LONGOPT_MMAP },
Julius Wernerefcee762014-11-10 13:14:24 -08001857 {NULL, 0, 0, 0 }
Stefan Reinauer63217582012-10-29 16:52:36 -07001858};
1859
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001860static int get_region_offset(long long int offset, uint32_t *region_offset)
1861{
1862 /* If offset is not negative, no transformation required. */
1863 if (offset >= 0) {
1864 *region_offset = offset;
1865 return 0;
1866 }
1867
1868 /* Calculate offset from start of region. */
1869 return convert_region_offset(-offset, region_offset);
1870}
1871
1872static int calculate_region_offsets(void)
1873{
1874 int ret = 0;
1875
1876 if (param.baseaddress_assigned)
1877 ret |= get_region_offset(param.baseaddress_input, &param.baseaddress);
1878 if (param.headeroffset_assigned)
1879 ret |= get_region_offset(param.headeroffset_input, &param.headeroffset);
1880 if (param.cbfsoffset_assigned)
1881 ret |= get_region_offset(param.cbfsoffset_input, &param.cbfsoffset);
1882
1883 return ret;
1884}
1885
Sol Boucher67a0a862015-03-18 12:36:27 -07001886static int dispatch_command(struct command command)
1887{
1888 if (command.accesses_region) {
1889 assert(param.image_file);
1890
1891 if (partitioned_file_is_partitioned(param.image_file)) {
Patrick Georgi2ed72952016-12-16 14:51:53 +01001892 INFO("Performing operation on '%s' region...\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07001893 param.region_name);
1894 }
1895 if (!partitioned_file_read_region(param.image_region,
1896 param.image_file, param.region_name)) {
1897 ERROR("The image will be left unmodified.\n");
1898 return 1;
1899 }
1900
1901 if (command.modifies_region) {
1902 // We (intentionally) don't support overwriting the FMAP
1903 // section. If you find yourself wanting to do this,
1904 // consider creating a new image rather than performing
1905 // whatever hacky transformation you were planning.
1906 if (region_is_flashmap(param.region_name)) {
1907 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1908 param.region_name);
1909 ERROR("The image will be left unmodified.\n");
1910 return 1;
1911 }
1912 // We don't allow writing raw data to regions that
1913 // contain nested regions, since doing so would
1914 // overwrite all such subregions.
1915 if (partitioned_file_region_contains_nested(
1916 param.image_file, param.region_name)) {
1917 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1918 param.region_name);
1919 ERROR("The image will be left unmodified.\n");
1920 return 1;
1921 }
1922 }
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001923
1924 /*
1925 * Once image region is read, input offsets can be adjusted accordingly if the
1926 * inputs are provided as negative integers i.e. offsets from end of region.
1927 */
1928 if (calculate_region_offsets())
1929 return 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001930 }
1931
1932 if (command.function()) {
1933 if (partitioned_file_is_partitioned(param.image_file)) {
1934 ERROR("Failed while operating on '%s' region!\n",
1935 param.region_name);
1936 ERROR("The image will be left unmodified.\n");
1937 }
1938 return 1;
1939 }
1940
1941 return 0;
1942}
1943
Stefan Reinauer63217582012-10-29 16:52:36 -07001944static void usage(char *name)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001945{
1946 printf
Stefan Reinauer07040582010-04-24 21:24:06 +00001947 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
Stefan Reinauer63217582012-10-29 16:52:36 -07001948 "USAGE:\n" " %s [-h]\n"
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001949 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001950 " -H header_offset Do not search for header; use this offset*\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001951 " -T Output top-aligned memory address\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001952 " -u Accept short data; fill upward/from bottom\n"
1953 " -d Accept short data; fill downward/from top\n"
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001954 " -F Force action\n"
Patrick Georgi5b6bdcc2016-10-13 18:24:10 +02001955 " -g Generate position and alignment arguments\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001956 " -U Unprocessed; don't decompress or make ELF\n"
Maximilian Brune60c6a5a2023-03-06 02:40:58 +01001957 " -v Provide verbose output (-v=INFO -vv=DEBUG output)\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001958 " -h Display this help message\n\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001959 " --ext-win-base Base of extended decode window in host address\n"
1960 " space(x86 only)\n"
1961 " --ext-win-size Size of extended decode window in host address\n"
1962 " space(x86 only)\n"
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001963 "COMMANDs:\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001964 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001965 " [-c compression] [-b base-address | -a alignment] \\\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301966 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001967 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1968 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001969 "Add a component\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301970 " "
1971 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001972 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001973 " [-c compression] [-b base-address] \\\n"
1974 " (linux specific: [-C cmdline] [-I initrd]) "
Stefan Reinauer63217582012-10-29 16:52:36 -07001975 "Add a payload to the ROM\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001976 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Jeremy Compostellac9cae532023-08-30 10:25:33 -07001977 " [-c compression] [-b base] \\\n"
1978 " [-S comma-separated-section(s)-to-ignore] \\\n"
Arthur Heymansc78521b32021-06-16 17:16:05 +02001979 " [-a alignment] [-Q|--pow2page] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001980 " [-y|--xip] [--ibb] \\\n"
1981 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001982 "Add a stage to the ROM\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001983 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1984 " [-A hash] -l load-address -e entry-point \\\n"
1985 " [-c compression] [-b base] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001986 "Add a 32bit flat mode binary\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001987 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001988 "Add a raw 64-bit integer value\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301989 " add-master-header [-r image,regions] \\ \n"
1990 " [-j topswap-size] (Intel CPUs only) "
Patrick Georgi59e52b92015-09-10 15:28:27 +02001991 "Add a legacy CBFS master header\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001992 " remove [-r image,regions] -n NAME "
Stefan Reinauer63217582012-10-29 16:52:36 -07001993 "Remove a component\n"
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001994 " compact -r image,regions "
1995 "Defragment CBFS image.\n"
Patrick Georgibd0bb232015-11-20 21:48:18 +01001996 " copy -r image,regions -R source-region "
1997 "Create a copy (duplicate) cbfs instance in fmap\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001998 " create -m ARCH -s size [-b bootblock offset] \\\n"
Patrick Georgi83c2d122015-08-26 10:40:00 +02001999 " [-o CBFS offset] [-H header offset] [-B bootblock] "
Sol Boucher67a0a862015-03-18 12:36:27 -07002000 "Create a legacy ROM file with CBFS master header*\n"
2001 " create -M flashmap [-r list,of,regions,containing,cbfses] "
2002 "Create a new-style partitioned firmware image\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07002003 " layout [-w] "
2004 "List mutable (or, with -w, readable) image regions\n"
Julius Werner4bfbabd2020-05-06 17:27:02 -07002005 " print [-r image,regions] [-k] "
Stefan Reinauer63217582012-10-29 16:52:36 -07002006 "Show the contents of the ROM\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08002007 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
2008 "Extracts a file from ROM\n"
Patrick Georgid9edb182016-12-06 18:55:26 +01002009 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
Sol Boucher67a0a862015-03-18 12:36:27 -07002010 "Write file into same-size [or larger] raw region\n"
2011 " read [-r fmap-region] -f file "
2012 "Extract raw region contents into binary file\n"
Patrick Georgi12631a42017-09-20 11:59:18 +02002013 " truncate [-r fmap-region] "
2014 "Truncate CBFS and print new size on stdout\n"
Patrick Georgi5d982d72017-09-19 14:39:58 +02002015 " expand [-r fmap-region] "
2016 "Expand CBFS to span entire region\n"
Sol Boucher0e539312015-03-05 15:38:03 -08002017 "OFFSETs:\n"
Sol Boucher67d59982015-05-07 02:39:22 -07002018 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
2019 " in two possible formats: if their value is greater than\n"
Sol Boucher0e539312015-03-05 15:38:03 -08002020 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
2021 " address; otherwise, they are treated as an offset into flash.\n"
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02002022 "ARCHes:\n", name, name
Stefan Reinauer63217582012-10-29 16:52:36 -07002023 );
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02002024 print_supported_architectures();
2025
2026 printf("TYPEs:\n");
Stefan Reinauer07040582010-04-24 21:24:06 +00002027 print_supported_filetypes();
Sol Boucher67a0a862015-03-18 12:36:27 -07002028 printf(
2029 "\n* Note that these actions and switches are only valid when\n"
2030 " working with legacy images whose structure is described\n"
2031 " primarily by a CBFS master header. New-style images, in\n"
2032 " contrast, exclusively make use of an FMAP to describe their\n"
2033 " layout: this must minimally contain an '%s' section\n"
2034 " specifying the location of this FMAP itself and a '%s'\n"
2035 " section describing the primary CBFS. It should also be noted\n"
2036 " that, when working with such images, the -F and -r switches\n"
Julius Werner0fd072d2021-12-13 10:28:29 -08002037 " default to '%s' for convenience, and the -b switch becomes\n"
Sol Boucher67d59982015-05-07 02:39:22 -07002038 " relative to the selected CBFS region's lowest address.\n"
2039 " The one exception to this rule is the top-aligned address,\n"
2040 " which is always relative to the end of the entire image\n"
2041 " rather than relative to the local region; this is true for\n"
2042 " for both input (sufficiently large) and output (-T) data.\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07002043 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
2044 SECTION_NAME_PRIMARY_CBFS
2045 );
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002046}
2047
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002048static bool valid_opt(size_t i, int c)
2049{
2050 /* Check if it is one of the optstrings supported by the command. */
2051 if (strchr(commands[i].optstring, c))
2052 return true;
2053
2054 /*
2055 * Check if it is one of the non-ASCII characters. Currently, the
2056 * non-ASCII characters are only checked against the valid list
2057 * irrespective of the command.
2058 */
2059 if (c >= LONGOPT_START && c < LONGOPT_END)
2060 return true;
2061
2062 return false;
2063}
2064
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002065int main(int argc, char **argv)
2066{
Mathias Krause41c229c2012-07-17 21:17:15 +02002067 size_t i;
Stefan Reinauer63217582012-10-29 16:52:36 -07002068 int c;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002069
2070 if (argc < 3) {
Stefan Reinauer63217582012-10-29 16:52:36 -07002071 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002072 return 1;
2073 }
2074
Sol Bouchere3260a02015-03-25 13:40:08 -07002075 char *image_name = argv[1];
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002076 char *cmd = argv[2];
Stefan Reinauer63217582012-10-29 16:52:36 -07002077 optind += 2;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002078
2079 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2080 if (strcmp(cmd, commands[i].name) != 0)
2081 continue;
Stefan Reinauer63217582012-10-29 16:52:36 -07002082
2083 while (1) {
2084 char *suffix = NULL;
2085 int option_index = 0;
2086
2087 c = getopt_long(argc, argv, commands[i].optstring,
2088 long_options, &option_index);
Nico Huber54073102016-08-01 23:18:29 +02002089 if (c == -1) {
2090 if (optind < argc) {
2091 ERROR("%s: excessive argument -- '%s'"
2092 "\n", argv[0], argv[optind]);
2093 return 1;
2094 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002095 break;
Nico Huber54073102016-08-01 23:18:29 +02002096 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002097
Joel Kitching672d5ad2018-10-11 17:01:30 +08002098 /* Filter out illegal long options */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002099 if (!valid_opt(i, c)) {
2100 ERROR("%s: invalid option -- '%d'\n",
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002101 argv[0], c);
Stefan Reinauer63217582012-10-29 16:52:36 -07002102 c = '?';
2103 }
2104
2105 switch(c) {
2106 case 'n':
Hung-Te Lind1739622013-01-28 14:23:49 +08002107 param.name = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002108 break;
2109 case 't':
2110 if (intfiletype(optarg) != ((uint64_t) - 1))
Hung-Te Lind1739622013-01-28 14:23:49 +08002111 param.type = intfiletype(optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002112 else
Hung-Te Lind1739622013-01-28 14:23:49 +08002113 param.type = strtoul(optarg, NULL, 0);
2114 if (param.type == 0)
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002115 WARN("Unknown type '%s' ignored\n",
Stefan Reinauer63217582012-10-29 16:52:36 -07002116 optarg);
2117 break;
Sol Boucherec424862015-05-07 21:00:05 -07002118 case 'c': {
Patrick Georgiecaa5702017-01-11 18:38:11 +01002119 if (strcmp(optarg, "precompression") == 0) {
2120 param.precompression = 1;
2121 break;
2122 }
Sol Boucherec424862015-05-07 21:00:05 -07002123 int algo = cbfs_parse_comp_algo(optarg);
2124 if (algo >= 0)
2125 param.compression = algo;
Stefan Reinauer63217582012-10-29 16:52:36 -07002126 else
Sol Boucherec424862015-05-07 21:00:05 -07002127 WARN("Unknown compression '%s' ignored.\n",
2128 optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002129 break;
Sol Boucherec424862015-05-07 21:00:05 -07002130 }
Patrick Georgi89f20342015-10-01 15:54:04 +02002131 case 'A': {
Julius Wernerd4775652020-03-13 16:43:34 -07002132 if (!vb2_lookup_hash_alg(optarg, &param.hash)) {
Patrick Georgi89f20342015-10-01 15:54:04 +02002133 ERROR("Unknown hash algorithm '%s'.\n",
2134 optarg);
2135 return 1;
2136 }
2137 break;
2138 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002139 case 'M':
2140 param.fmap = optarg;
2141 break;
2142 case 'r':
2143 param.region_name = optarg;
2144 break;
Patrick Georgibd0bb232015-11-20 21:48:18 +01002145 case 'R':
2146 param.source_region = optarg;
2147 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002148 case 'b':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002149 param.baseaddress_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002150 if (!*optarg || (suffix && *suffix)) {
2151 ERROR("Invalid base address '%s'.\n",
2152 optarg);
2153 return 1;
2154 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002155 // baseaddress may be zero on non-x86, so we
2156 // need an explicit "baseaddress_assigned".
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002157 param.baseaddress_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002158 break;
2159 case 'l':
Nico Huber9ade7172016-08-01 21:37:42 +02002160 param.loadaddress = strtoul(optarg, &suffix, 0);
2161 if (!*optarg || (suffix && *suffix)) {
2162 ERROR("Invalid load address '%s'.\n",
2163 optarg);
2164 return 1;
2165 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002166 break;
2167 case 'e':
Nico Huber9ade7172016-08-01 21:37:42 +02002168 param.entrypoint = strtoul(optarg, &suffix, 0);
2169 if (!*optarg || (suffix && *suffix)) {
2170 ERROR("Invalid entry point '%s'.\n",
2171 optarg);
2172 return 1;
2173 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002174 break;
2175 case 's':
Hung-Te Lind1739622013-01-28 14:23:49 +08002176 param.size = strtoul(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002177 if (!*optarg) {
2178 ERROR("Empty size specified.\n");
2179 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002180 }
Nico Huber9ade7172016-08-01 21:37:42 +02002181 switch (tolower((int)suffix[0])) {
2182 case 'k':
2183 param.size *= 1024;
2184 break;
2185 case 'm':
Hung-Te Lind1739622013-01-28 14:23:49 +08002186 param.size *= 1024 * 1024;
Nico Huber9ade7172016-08-01 21:37:42 +02002187 break;
2188 case '\0':
2189 break;
2190 default:
2191 ERROR("Invalid suffix for size '%s'.\n",
2192 optarg);
2193 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002194 }
Patrick Georgie887ca52014-08-09 17:44:39 +02002195 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002196 case 'B':
Hung-Te Lind1739622013-01-28 14:23:49 +08002197 param.bootblock = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002198 break;
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002199 case 'H':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002200 param.headeroffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002201 if (!*optarg || (suffix && *suffix)) {
2202 ERROR("Invalid header offset '%s'.\n",
2203 optarg);
2204 return 1;
2205 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002206 param.headeroffset_assigned = 1;
2207 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002208 case 'a':
Nico Huber9ade7172016-08-01 21:37:42 +02002209 param.alignment = strtoul(optarg, &suffix, 0);
2210 if (!*optarg || (suffix && *suffix)) {
2211 ERROR("Invalid alignment '%s'.\n",
2212 optarg);
2213 return 1;
2214 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002215 break;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07002216 case 'p':
2217 param.padding = strtoul(optarg, &suffix, 0);
2218 if (!*optarg || (suffix && *suffix)) {
2219 ERROR("Invalid pad size '%s'.\n",
2220 optarg);
2221 return 1;
2222 }
2223 break;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03002224 case 'Q':
2225 param.force_pow2_pagesize = 1;
2226 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002227 case 'o':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002228 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002229 if (!*optarg || (suffix && *suffix)) {
2230 ERROR("Invalid cbfs offset '%s'.\n",
2231 optarg);
2232 return 1;
2233 }
Julius Wernerefcee762014-11-10 13:14:24 -08002234 param.cbfsoffset_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002235 break;
2236 case 'f':
Hung-Te Lind1739622013-01-28 14:23:49 +08002237 param.filename = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002238 break;
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02002239 case 'F':
2240 param.force = 1;
2241 break;
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002242 case 'i':
Nico Huber9ade7172016-08-01 21:37:42 +02002243 param.u64val = strtoull(optarg, &suffix, 0);
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01002244 param.u64val_assigned = 1;
Nico Huber9ade7172016-08-01 21:37:42 +02002245 if (!*optarg || (suffix && *suffix)) {
2246 ERROR("Invalid int parameter '%s'.\n",
2247 optarg);
2248 return 1;
2249 }
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002250 break;
Sol Boucher67a0a862015-03-18 12:36:27 -07002251 case 'u':
2252 param.fill_partial_upward = true;
2253 break;
2254 case 'd':
2255 param.fill_partial_downward = true;
2256 break;
2257 case 'w':
2258 param.show_immutable = true;
Hung-Te Lin215d1d72013-01-29 03:46:02 +08002259 break;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05302260 case 'j':
2261 param.topswap_size = strtol(optarg, NULL, 0);
2262 if (!is_valid_topswap())
2263 return 1;
2264 break;
Rizwan Qureshic1072f22018-06-04 23:02:46 +05302265 case 'q':
2266 param.ucode_region = optarg;
2267 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002268 case 'v':
2269 verbose++;
2270 break;
David Hendricks90ca3b62012-11-16 14:48:22 -08002271 case 'm':
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06002272 param.arch = string_to_arch(optarg);
David Hendricks90ca3b62012-11-16 14:48:22 -08002273 break;
Patrick Georgide36d332013-08-27 20:22:21 +02002274 case 'I':
2275 param.initrd = optarg;
2276 break;
2277 case 'C':
2278 param.cmdline = optarg;
2279 break;
Furquan Shaikh405304a2014-10-30 11:44:20 -07002280 case 'S':
Jeremy Compostellac9cae532023-08-30 10:25:33 -07002281 param.ignore_sections = optarg;
Furquan Shaikh405304a2014-10-30 11:44:20 -07002282 break;
Aaron Durbin4be16742015-09-15 17:00:23 -05002283 case 'y':
2284 param.stage_xip = true;
2285 break;
Werner Zehe9995f12016-01-14 13:22:37 +01002286 case 'g':
2287 param.autogen_attr = true;
2288 break;
Aaron Durbin5dc628a2016-01-26 15:35:34 -06002289 case 'k':
2290 param.machine_parseable = true;
2291 break;
Joel Kitching21fdd892018-08-09 17:49:52 +08002292 case 'U':
2293 param.unprocessed = true;
2294 break;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01002295 case LONGOPT_IBB:
2296 param.ibb = true;
2297 break;
Arthur Heymans34a7e662022-10-06 09:42:15 +02002298 case LONGOPT_MMAP:
2299 if (decode_mmap_arg(optarg))
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08002300 return 1;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08002301 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002302 case 'h':
2303 case '?':
2304 usage(argv[0]);
2305 return 1;
2306 default:
2307 break;
2308 }
2309 }
2310
Sol Bouchere3260a02015-03-25 13:40:08 -07002311 if (commands[i].function == cbfs_create) {
Sol Boucher67a0a862015-03-18 12:36:27 -07002312 if (param.fmap) {
2313 struct buffer flashmap;
2314 if (buffer_from_file(&flashmap, param.fmap))
2315 return 1;
2316 param.image_file = partitioned_file_create(
2317 image_name, &flashmap);
2318 buffer_delete(&flashmap);
2319 } else if (param.size) {
2320 param.image_file = partitioned_file_create_flat(
2321 image_name, param.size);
2322 } else {
2323 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002324 return 1;
2325 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002326 } else {
Vadim Bendebury75599462015-12-09 09:39:31 -08002327 bool write_access = commands[i].modifies_region;
2328
Sol Bouchere3260a02015-03-25 13:40:08 -07002329 param.image_file =
Vadim Bendebury75599462015-12-09 09:39:31 -08002330 partitioned_file_reopen(image_name,
2331 write_access);
Sol Bouchere3260a02015-03-25 13:40:08 -07002332 }
2333 if (!param.image_file)
2334 return 1;
2335
Sol Boucher67a0a862015-03-18 12:36:27 -07002336 unsigned num_regions = 1;
2337 for (const char *list = strchr(param.region_name, ','); list;
2338 list = strchr(list + 1, ','))
2339 ++num_regions;
2340
Sol Bouchere3260a02015-03-25 13:40:08 -07002341 // If the action needs to read an image region, as indicated by
2342 // having accesses_region set in its command struct, that
2343 // region's buffer struct will be stored here and the client
2344 // will receive a pointer to it via param.image_region. It
2345 // need not write the buffer back to the image file itself,
2346 // since this behavior can be requested via its modifies_region
2347 // field. Additionally, it should never free the region buffer,
2348 // as that is performed automatically once it completes.
Sol Boucher67a0a862015-03-18 12:36:27 -07002349 struct buffer image_regions[num_regions];
2350 memset(image_regions, 0, sizeof(image_regions));
Sol Bouchere3260a02015-03-25 13:40:08 -07002351
Sol Boucher67a0a862015-03-18 12:36:27 -07002352 bool seen_primary_cbfs = false;
2353 char region_name_scratch[strlen(param.region_name) + 1];
2354 strcpy(region_name_scratch, param.region_name);
2355 param.region_name = strtok(region_name_scratch, ",");
2356 for (unsigned region = 0; region < num_regions; ++region) {
2357 if (!param.region_name) {
2358 ERROR("Encountered illegal degenerate region name in -r list\n");
2359 ERROR("The image will be left unmodified.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002360 partitioned_file_close(param.image_file);
2361 return 1;
2362 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002363
Sol Boucher67a0a862015-03-18 12:36:27 -07002364 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2365 == 0)
2366 seen_primary_cbfs = true;
Sol Bouchere3260a02015-03-25 13:40:08 -07002367
Sol Boucher67a0a862015-03-18 12:36:27 -07002368 param.image_region = image_regions + region;
2369 if (dispatch_command(commands[i])) {
Sol Bouchere3260a02015-03-25 13:40:08 -07002370 partitioned_file_close(param.image_file);
2371 return 1;
2372 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002373
2374 param.region_name = strtok(NULL, ",");
2375 }
2376
2377 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2378 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2379 SECTION_NAME_PRIMARY_CBFS);
2380 ERROR("The image will be left unmodified.\n");
2381 partitioned_file_close(param.image_file);
2382 return 1;
2383 }
2384
2385 if (commands[i].modifies_region) {
2386 assert(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002387 for (unsigned region = 0; region < num_regions;
2388 ++region) {
2389
2390 if (!partitioned_file_write_region(
2391 param.image_file,
2392 image_regions + region)) {
2393 partitioned_file_close(
2394 param.image_file);
2395 return 1;
2396 }
2397 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002398 }
2399
2400 partitioned_file_close(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002401 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002402 }
2403
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002404 ERROR("Unknown command '%s'.\n", cmd);
Stefan Reinauer63217582012-10-29 16:52:36 -07002405 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002406 return 1;
2407}