blob: c825a13c72223af4f971d1e0e009426e0d41741b [file] [log] [blame]
Patrick Georgiea063cb2020-05-08 19:28:13 +02001/* cbfstool, CLI utility for CBFS file manipulation */
Patrick Georgi7333a112020-05-08 20:48:04 +02002/* SPDX-License-Identifier: GPL-2.0-only */
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +00003
Patrick Georgib7b56dd82009-09-14 13:29:27 +00004#include <stdio.h>
Stefan Reinauera1e48242011-10-21 14:24:57 -07005#include <stdlib.h>
Stefan Reinauer336daa72009-12-21 15:09:01 +00006#include <string.h>
Sol Boucher0e539312015-03-05 15:38:03 -08007#include <strings.h>
Stefan Reinauera1e48242011-10-21 14:24:57 -07008#include <ctype.h>
Stefan Reinauer63217582012-10-29 16:52:36 -07009#include <unistd.h>
10#include <getopt.h>
Patrick Georgib7b56dd82009-09-14 13:29:27 +000011#include "common.h"
12#include "cbfs.h"
Hung-Te Lin3bb035b2013-01-29 02:15:49 +080013#include "cbfs_image.h"
Sol Bouchere3260a02015-03-25 13:40:08 -070014#include "cbfs_sections.h"
Aaron Durbinfacf1492017-12-18 14:50:22 -070015#include "elfparsing.h"
Sol Bouchere3260a02015-03-25 13:40:08 -070016#include "partitioned_file.h"
Julius Werner81dc20e2020-10-15 17:37:57 -070017#include "lz4/lib/xxhash.h"
Julius Werner4bfbabd2020-05-06 17:27:02 -070018#include <commonlib/bsd/cbfs_private.h>
Julius Werner81dc20e2020-10-15 17:37:57 -070019#include <commonlib/bsd/compression.h>
Julius Werner4bfbabd2020-05-06 17:27:02 -070020#include <commonlib/bsd/metadata_hash.h>
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -070021#include <commonlib/fsp.h>
Nico Huber607796a2017-01-17 13:01:25 +010022#include <commonlib/endian.h>
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +053023#include <commonlib/helpers.h>
Furquan Shaikh0dcc0662020-11-20 22:51:17 -080024#include <commonlib/region.h>
Julius Wernerd4775652020-03-13 16:43:34 -070025#include <vboot_host.h>
Ronald G. Minnich5d01ec02009-03-31 11:57:36 +000026
Stefan Reinauer3fec29c2009-09-22 15:58:19 +000027struct command {
Stefan Reinauer3fec29c2009-09-22 15:58:19 +000028 const char *name;
Stefan Reinauer63217582012-10-29 16:52:36 -070029 const char *optstring;
30 int (*function) (void);
Sol Bouchere3260a02015-03-25 13:40:08 -070031 // Whether to populate param.image_region before invoking function
32 bool accesses_region;
Vadim Bendebury75599462015-12-09 09:39:31 -080033 // This set to true means two things:
34 // - in case of a command operating on a region, the region's contents
35 // will be written back to image_file at the end
36 // - write access to the file is required
Sol Bouchere3260a02015-03-25 13:40:08 -070037 bool modifies_region;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +000038};
39
Hung-Te Lind1739622013-01-28 14:23:49 +080040static struct param {
Sol Bouchere3260a02015-03-25 13:40:08 -070041 partitioned_file_t *image_file;
42 struct buffer *image_region;
Sol Boucher67a0a862015-03-18 12:36:27 -070043 const char *name;
44 const char *filename;
45 const char *fmap;
46 const char *region_name;
Patrick Georgibd0bb232015-11-20 21:48:18 +010047 const char *source_region;
Sol Boucher67a0a862015-03-18 12:36:27 -070048 const char *bootblock;
49 const char *ignore_section;
Rizwan Qureshic1072f22018-06-04 23:02:46 +053050 const char *ucode_region;
Peter Stuge3bfd5b82013-07-09 19:39:13 +020051 uint64_t u64val;
Hung-Te Lind1739622013-01-28 14:23:49 +080052 uint32_t type;
53 uint32_t baseaddress;
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -080054 /*
55 * Input can be negative. It will be transformed to offset from start of region (if
56 * negative) and stored in baseaddress.
57 */
58 long long int baseaddress_input;
Hung-Te Linf56c73f2013-01-29 09:45:12 +080059 uint32_t baseaddress_assigned;
Hung-Te Lind1739622013-01-28 14:23:49 +080060 uint32_t loadaddress;
Hung-Te Linf56c73f2013-01-29 09:45:12 +080061 uint32_t headeroffset;
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -080062 /*
63 * Input can be negative. It will be transformed to offset from start of region (if
64 * negative) and stored in baseaddress.
65 */
66 long long int headeroffset_input;
Hung-Te Linf56c73f2013-01-29 09:45:12 +080067 uint32_t headeroffset_assigned;
Hung-Te Lind1739622013-01-28 14:23:49 +080068 uint32_t entrypoint;
69 uint32_t size;
70 uint32_t alignment;
Hung-Te Line9198372013-03-19 12:17:12 +080071 uint32_t pagesize;
Julius Wernerefcee762014-11-10 13:14:24 -080072 uint32_t cbfsoffset;
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -080073 /*
74 * Input can be negative. It will be transformed to corresponding region offset (if
75 * negative) and stored in baseaddress.
76 */
77 long long int cbfsoffset_input;
Julius Wernerefcee762014-11-10 13:14:24 -080078 uint32_t cbfsoffset_assigned;
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -060079 uint32_t arch;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -070080 uint32_t padding;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +053081 uint32_t topswap_size;
Patrick Georgi16b3e4b2016-12-12 18:38:01 +010082 bool u64val_assigned;
Sol Boucher67a0a862015-03-18 12:36:27 -070083 bool fill_partial_upward;
84 bool fill_partial_downward;
85 bool show_immutable;
Aaron Durbin4be16742015-09-15 17:00:23 -050086 bool stage_xip;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +030087 bool force_pow2_pagesize;
Werner Zehe9995f12016-01-14 13:22:37 +010088 bool autogen_attr;
Aaron Durbin5dc628a2016-01-26 15:35:34 -060089 bool machine_parseable;
Joel Kitching21fdd892018-08-09 17:49:52 +080090 bool unprocessed;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +010091 bool ibb;
Julius Wernerd4775652020-03-13 16:43:34 -070092 enum cbfs_compression compression;
Patrick Georgiecaa5702017-01-11 18:38:11 +010093 int precompression;
Patrick Georgi89f20342015-10-01 15:54:04 +020094 enum vb2_hash_algorithm hash;
Joel Kitching672d5ad2018-10-11 17:01:30 +080095 /* For linux payloads */
Patrick Georgide36d332013-08-27 20:22:21 +020096 char *initrd;
97 char *cmdline;
Patrick Georgi01fbc3a2016-10-12 16:46:13 +020098 int force;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -080099 /*
100 * Base and size of extended window for decoding SPI flash greater than 16MiB in host
101 * address space on x86 platforms. The assumptions here are:
102 * 1. Top 16MiB is still decoded in the fixed decode window just below 4G boundary.
103 * 2. Rest of the SPI flash below the top 16MiB is mapped at the top of extended
104 * window. Even though the platform might support a larger extended window, the SPI
105 * flash part used by the mainboard might not be large enough to be mapped in the entire
106 * window. In such cases, the mapping is assumed to be in the top part of the extended
107 * window with the bottom part remaining unused.
108 */
109 uint32_t ext_win_base;
110 uint32_t ext_win_size;
Hung-Te Lind1739622013-01-28 14:23:49 +0800111} param = {
112 /* All variables not listed are initialized as zero. */
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -0600113 .arch = CBFS_ARCHITECTURE_UNKNOWN,
Sol Boucher65336712015-05-07 21:00:05 -0700114 .compression = CBFS_COMPRESS_NONE,
Patrick Georgi89f20342015-10-01 15:54:04 +0200115 .hash = VB2_HASH_INVALID,
Jakub Czapigaaa415632022-08-01 16:01:28 +0200116 .headeroffset = HEADER_OFFSET_UNKNOWN,
Sol Boucher67a0a862015-03-18 12:36:27 -0700117 .region_name = SECTION_NAME_PRIMARY_CBFS,
Patrick Georgid9edb182016-12-06 18:55:26 +0100118 .u64val = -1,
Hung-Te Lind1739622013-01-28 14:23:49 +0800119};
Stefan Reinauer63217582012-10-29 16:52:36 -0700120
Julius Werner4bfbabd2020-05-06 17:27:02 -0700121/*
122 * This "metadata_hash cache" caches the value and location of the CBFS metadata
123 * hash embedded in the bootblock when CBFS verification is enabled. The first
124 * call to get_mh_cache() searches for the cache by scanning the whole bootblock
125 * for its 8-byte signature, later calls will just return the previously found
126 * information again. If the cbfs_hash.algo member in the result is
127 * VB2_HASH_INVALID, that means no metadata hash was found and this image does
128 * not use CBFS verification.
129 */
130struct mh_cache {
131 const char *region;
132 size_t offset;
133 struct vb2_hash cbfs_hash;
Julius Werner76dab5f2020-03-19 21:09:35 -0700134 platform_fixup_func fixup;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700135 bool initialized;
136};
137
138static struct mh_cache *get_mh_cache(void)
139{
140 static struct mh_cache mhc;
141
142 if (mhc.initialized)
143 return &mhc;
144
145 mhc.initialized = true;
146
147 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
148 if (!fmap)
149 goto no_metadata_hash;
150
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600151 /* Find the metadata_hash container. If there is a "BOOTBLOCK" FMAP section, it's
Julius Werner4bfbabd2020-05-06 17:27:02 -0700152 there. If not, it's a normal file in the primary CBFS section. */
153 size_t offset, size;
154 struct buffer buffer;
155 if (fmap_find_area(fmap, SECTION_NAME_BOOTBLOCK)) {
156 if (!partitioned_file_read_region(&buffer, param.image_file,
157 SECTION_NAME_BOOTBLOCK))
158 goto no_metadata_hash;
159 mhc.region = SECTION_NAME_BOOTBLOCK;
160 offset = 0;
161 size = buffer.size;
162 } else {
163 struct cbfs_image cbfs;
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600164 struct cbfs_file *mh_container;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700165 if (!partitioned_file_read_region(&buffer, param.image_file,
166 SECTION_NAME_PRIMARY_CBFS))
167 goto no_metadata_hash;
168 mhc.region = SECTION_NAME_PRIMARY_CBFS;
169 if (cbfs_image_from_buffer(&cbfs, &buffer, param.headeroffset))
170 goto no_metadata_hash;
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600171 mh_container = cbfs_get_entry(&cbfs, "bootblock");
172 if (!mh_container || be32toh(mh_container->type) != CBFS_TYPE_BOOTBLOCK) {
173 /* Check for apu/amdfw file */
174 mh_container = cbfs_get_entry(&cbfs, "apu/amdfw");
175 if (!mh_container || be32toh(mh_container->type) != CBFS_TYPE_AMDFW)
176 goto no_metadata_hash;
177 }
178
179 offset = (void *)mh_container + be32toh(mh_container->offset) -
Julius Werner4bfbabd2020-05-06 17:27:02 -0700180 buffer_get(&cbfs.buffer);
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600181 size = be32toh(mh_container->len);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700182 }
183
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600184 /* Find and validate the metadata hash anchor inside the containing file and
Julius Werner4bfbabd2020-05-06 17:27:02 -0700185 record its exact byte offset from the start of the FMAP region. */
186 struct metadata_hash_anchor *anchor = memmem(buffer_get(&buffer) + offset,
187 size, METADATA_HASH_ANCHOR_MAGIC, sizeof(anchor->magic));
188 if (anchor) {
189 if (!vb2_digest_size(anchor->cbfs_hash.algo)) {
190 ERROR("Unknown CBFS metadata hash type: %d\n",
191 anchor->cbfs_hash.algo);
192 goto no_metadata_hash;
193 }
194 mhc.cbfs_hash = anchor->cbfs_hash;
195 mhc.offset = (void *)anchor - buffer_get(&buffer);
Julius Werner76dab5f2020-03-19 21:09:35 -0700196 mhc.fixup = platform_fixups_probe(&buffer, mhc.offset,
197 mhc.region);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700198 return &mhc;
199 }
200
201no_metadata_hash:
202 mhc.cbfs_hash.algo = VB2_HASH_INVALID;
203 return &mhc;
204}
205
206static void update_and_info(const char *name, void *dst, void *src, size_t size)
207{
208 if (!memcmp(dst, src, size))
209 return;
210 char *src_str = bintohex(src, size);
211 char *dst_str = bintohex(dst, size);
212 INFO("Updating %s from %s to %s\n", name, dst_str, src_str);
213 memcpy(dst, src, size);
214 free(src_str);
215 free(dst_str);
216}
217
218static int update_anchor(struct mh_cache *mhc, uint8_t *fmap_hash)
219{
220 struct buffer buffer;
221 if (!partitioned_file_read_region(&buffer, param.image_file,
222 mhc->region))
223 return -1;
224 struct metadata_hash_anchor *anchor = buffer_get(&buffer) + mhc->offset;
225 /* The metadata hash anchor should always still be where we left it. */
226 assert(!memcmp(anchor->magic, METADATA_HASH_ANCHOR_MAGIC,
227 sizeof(anchor->magic)) &&
228 anchor->cbfs_hash.algo == mhc->cbfs_hash.algo);
229 update_and_info("CBFS metadata hash", anchor->cbfs_hash.raw,
230 mhc->cbfs_hash.raw, vb2_digest_size(anchor->cbfs_hash.algo));
231 if (fmap_hash) {
232 update_and_info("FMAP hash",
233 metadata_hash_anchor_fmap_hash(anchor), fmap_hash,
234 vb2_digest_size(anchor->cbfs_hash.algo));
235 }
Julius Werner76dab5f2020-03-19 21:09:35 -0700236 if (mhc->fixup && mhc->fixup(&buffer, mhc->offset) != 0)
237 return -1;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700238 if (!partitioned_file_write_region(param.image_file, &buffer))
239 return -1;
240 return 0;
241
242}
243
244/* This should be called after every time CBFS metadata might have changed. It
245 will recalculate and update the metadata hash in the bootblock if needed. */
246static int maybe_update_metadata_hash(struct cbfs_image *cbfs)
247{
248 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS))
249 return 0; /* Metadata hash only embedded in primary CBFS. */
250
251 struct mh_cache *mhc = get_mh_cache();
252 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
253 return 0;
254
Julius Werner69cc5572022-03-04 17:49:56 -0800255 enum cb_err err = cbfs_walk(cbfs, NULL, NULL, &mhc->cbfs_hash,
256 CBFS_WALK_WRITEBACK_HASH);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700257 if (err != CB_CBFS_NOT_FOUND) {
258 ERROR("Unexpected cbfs_walk() error %d\n", err);
259 return -1;
260 }
261
262 return update_anchor(mhc, NULL);
263}
264
265/* This should be called after every time the FMAP or the bootblock itself might
266 have changed, and will write the new FMAP hash into the metadata hash anchor
267 in the bootblock if required (usually when the bootblock is first added). */
268static int maybe_update_fmap_hash(void)
269{
270 if (strcmp(param.region_name, SECTION_NAME_BOOTBLOCK) &&
271 strcmp(param.region_name, SECTION_NAME_FMAP) &&
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600272 param.type != CBFS_TYPE_BOOTBLOCK &&
273 param.type != CBFS_TYPE_AMDFW)
Julius Werner4bfbabd2020-05-06 17:27:02 -0700274 return 0; /* FMAP and bootblock didn't change. */
275
276 struct mh_cache *mhc = get_mh_cache();
277 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
278 return 0;
279
Julius Wernerd96ca242022-08-08 18:08:35 -0700280 struct vb2_hash fmap_hash;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700281 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
Julius Wernerd96ca242022-08-08 18:08:35 -0700282 if (!fmap || vb2_hash_calculate(false, fmap, fmap_size(fmap),
283 mhc->cbfs_hash.algo, &fmap_hash))
Julius Werner4bfbabd2020-05-06 17:27:02 -0700284 return -1;
Julius Wernerd96ca242022-08-08 18:08:35 -0700285 return update_anchor(mhc, fmap_hash.raw);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700286}
287
Julius Werneraf20fd72022-05-27 18:33:15 -0700288static bool verification_exclude(enum cbfs_type type)
289{
290 switch (type) {
291 case CBFS_TYPE_BOOTBLOCK:
292 case CBFS_TYPE_CBFSHEADER:
293 case CBFS_TYPE_INTEL_FIT:
Karthikeyan Ramasubramanianda5d0252022-09-20 23:00:00 -0600294 case CBFS_TYPE_AMDFW:
Julius Werneraf20fd72022-05-27 18:33:15 -0700295 return true;
296 default:
297 return false;
298 }
299}
300
Sol Boucher67a0a862015-03-18 12:36:27 -0700301static bool region_is_flashmap(const char *region)
302{
303 return partitioned_file_region_check_magic(param.image_file, region,
304 FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
305}
306
307/* @return Same as cbfs_is_valid_cbfs(), but for a named region. */
308static bool region_is_modern_cbfs(const char *region)
309{
310 return partitioned_file_region_check_magic(param.image_file, region,
311 CBFS_FILE_MAGIC, strlen(CBFS_FILE_MAGIC));
312}
313
Furquan Shaikh0dcc0662020-11-20 22:51:17 -0800314/* This describes a window from the SPI flash address space into the host address space. */
315struct mmap_window {
316 struct region flash_space;
317 struct region host_space;
318};
319
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);
Julius Werner4bfbabd2020-05-06 17:27:02 -0700653
654 enum vb2_hash_algorithm algo = get_mh_cache()->cbfs_hash.algo;
655 if (algo != VB2_HASH_INVALID)
656 if (cbfs_add_file_hash(header, &buffer, algo)) {
657 ERROR("couldn't add hash for '%s'\n", name);
658 goto done;
659 }
660
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100661 if (cbfs_add_entry(&image, &buffer, offset, header, 0) != 0) {
Sol Boucher0e539312015-03-05 15:38:03 -0800662 ERROR("Failed to add %llu into ROM image as '%s'.\n",
663 (long long unsigned)u64val, name);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200664 goto done;
665 }
666
Julius Werner4bfbabd2020-05-06 17:27:02 -0700667 ret = maybe_update_metadata_hash(&image);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200668
669done:
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200670 free(header);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200671 buffer_delete(&buffer);
Peter Stuge3bfd5b82013-07-09 19:39:13 +0200672 return ret;
673}
674
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530675static int is_valid_topswap(void)
676{
677 switch (param.topswap_size) {
678 case (64 * KiB):
679 case (128 * KiB):
680 case (256 * KiB):
681 case (512 * KiB):
682 case (1 * MiB):
683 break;
684 default:
685 ERROR("Invalid topswap_size %d, topswap can be 64K|128K|256K|512K|1M\n",
686 param.topswap_size);
687 return 0;
688 }
689 return 1;
690}
691
692static void fill_header_offset(void *location, uint32_t offset)
693{
Joel Kitching672d5ad2018-10-11 17:01:30 +0800694 // TODO: When we have a BE target, we'll need to store this as BE
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530695 write_le32(location, offset);
696}
697
698static int update_master_header_loc_topswap(struct cbfs_image *image,
699 void *h_loc, uint32_t header_offset)
700{
701 struct cbfs_file *entry;
702 void *ts_h_loc = h_loc;
703
704 entry = cbfs_get_entry(image, "bootblock");
705 if (entry == NULL) {
706 ERROR("Bootblock not in ROM image?!?\n");
707 return 1;
708 }
709
710 /*
711 * Check if the existing topswap boundary matches with
712 * the one provided.
713 */
Alex James02001a382021-12-19 16:41:59 -0600714 if (param.topswap_size != be32toh(entry->len)/2) {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530715 ERROR("Top swap boundary does not match\n");
716 return 1;
717 }
718
719 ts_h_loc -= param.topswap_size;
720 fill_header_offset(ts_h_loc, header_offset);
721
722 return 0;
723}
724
Patrick Georgi59e52b92015-09-10 15:28:27 +0200725static int cbfs_add_master_header(void)
726{
727 const char * const name = "cbfs master header";
728 struct cbfs_image image;
729 struct cbfs_file *header = NULL;
730 struct buffer buffer;
731 int ret = 1;
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600732 size_t offset;
733 size_t size;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530734 void *h_loc;
Patrick Georgi59e52b92015-09-10 15:28:27 +0200735
736 if (cbfs_image_from_buffer(&image, param.image_region,
737 param.headeroffset)) {
738 ERROR("Selected image region is not a CBFS.\n");
739 return 1;
740 }
741
742 if (cbfs_get_entry(&image, name)) {
743 ERROR("'%s' already in ROM image.\n", name);
744 return 1;
745 }
746
747 if (buffer_create(&buffer, sizeof(struct cbfs_header), name) != 0)
748 return 1;
749
750 struct cbfs_header *h = (struct cbfs_header *)buffer.data;
Alex James02001a382021-12-19 16:41:59 -0600751 h->magic = htobe32(CBFS_HEADER_MAGIC);
752 h->version = htobe32(CBFS_HEADER_VERSION);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200753 /* The 4 bytes are left out for two reasons:
754 * 1. the cbfs master header pointer resides there
755 * 2. some cbfs implementations assume that an image that resides
756 * below 4GB has a bootblock and get confused when the end of the
757 * image is at 4GB == 0.
758 */
Alex James02001a382021-12-19 16:41:59 -0600759 h->bootblocksize = htobe32(4);
760 h->align = htobe32(CBFS_ALIGNMENT);
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600761 /* The offset and romsize fields within the master header are absolute
762 * values within the boot media. As such, romsize needs to relfect
763 * the end 'offset' for a CBFS. To achieve that the current buffer
764 * representing the CBFS region's size is added to the offset of
765 * the region within a larger image.
Patrick Georgi59e52b92015-09-10 15:28:27 +0200766 */
Aaron Durbinbb826ef2016-01-04 13:56:00 -0600767 offset = buffer_get(param.image_region) -
768 buffer_get_original_backing(param.image_region);
769 size = buffer_size(param.image_region);
Alex James02001a382021-12-19 16:41:59 -0600770 h->romsize = htobe32(size + offset);
771 h->offset = htobe32(offset);
772 h->architecture = htobe32(CBFS_ARCHITECTURE_UNKNOWN);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200773
Julius Werner4bfbabd2020-05-06 17:27:02 -0700774 /* Never add a hash attribute to the master header. */
Julius Wernerd4775652020-03-13 16:43:34 -0700775 header = cbfs_create_file_header(CBFS_TYPE_CBFSHEADER,
Patrick Georgi59e52b92015-09-10 15:28:27 +0200776 buffer_size(&buffer), name);
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100777 if (cbfs_add_entry(&image, &buffer, 0, header, 0) != 0) {
Patrick Georgi59e52b92015-09-10 15:28:27 +0200778 ERROR("Failed to add cbfs master header into ROM image.\n");
779 goto done;
780 }
781
782 struct cbfs_file *entry;
783 if ((entry = cbfs_get_entry(&image, name)) == NULL) {
784 ERROR("'%s' not in ROM image?!?\n", name);
785 goto done;
786 }
787
788 uint32_t header_offset = CBFS_SUBHEADER(entry) -
789 buffer_get(&image.buffer);
790 header_offset = -(buffer_size(&image.buffer) - header_offset);
791
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530792 h_loc = (void *)(buffer_get(&image.buffer) +
793 buffer_size(&image.buffer) - 4);
794 fill_header_offset(h_loc, header_offset);
795 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800796 * If top swap present, update the header
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530797 * location in secondary bootblock
798 */
799 if (param.topswap_size) {
800 if (update_master_header_loc_topswap(&image, h_loc,
Shaik Shahina425413c2022-11-05 01:07:06 +0530801 header_offset))
802 goto done;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530803 }
Patrick Georgi59e52b92015-09-10 15:28:27 +0200804
Julius Werner4bfbabd2020-05-06 17:27:02 -0700805 ret = maybe_update_metadata_hash(&image);
Patrick Georgi59e52b92015-09-10 15:28:27 +0200806
807done:
808 free(header);
809 buffer_delete(&buffer);
810 return ret;
811}
812
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530813static int add_topswap_bootblock(struct buffer *buffer, uint32_t *offset)
814{
815 size_t bb_buf_size = buffer_size(buffer);
816
817 if (bb_buf_size > param.topswap_size) {
818 ERROR("Bootblock bigger than the topswap boundary\n");
819 ERROR("size = %zd, ts = %d\n", bb_buf_size,
820 param.topswap_size);
821 return 1;
822 }
823
824 /*
Joel Kitching672d5ad2018-10-11 17:01:30 +0800825 * Allocate topswap_size*2 bytes for bootblock to
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530826 * accommodate the second bootblock.
827 */
828 struct buffer new_bootblock, bb1, bb2;
829 if (buffer_create(&new_bootblock, 2 * param.topswap_size,
830 buffer->name))
831 return 1;
832
833 buffer_splice(&bb1, &new_bootblock, param.topswap_size - bb_buf_size,
834 bb_buf_size);
835 buffer_splice(&bb2, &new_bootblock,
836 buffer_size(&new_bootblock) - bb_buf_size,
837 bb_buf_size);
838
Joel Kitching672d5ad2018-10-11 17:01:30 +0800839 /* Copy to first bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530840 memcpy(buffer_get(&bb1), buffer_get(buffer), bb_buf_size);
Joel Kitching672d5ad2018-10-11 17:01:30 +0800841 /* Copy to second bootblock */
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530842 memcpy(buffer_get(&bb2), buffer_get(buffer), bb_buf_size);
843
844 buffer_delete(buffer);
845 buffer_clone(buffer, &new_bootblock);
846
Joel Kitching672d5ad2018-10-11 17:01:30 +0800847 /* Update the location (offset) of bootblock in the region */
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -0800848 return convert_region_offset(buffer_size(buffer), offset);
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +0530849}
850
Sol Bouchere3260a02015-03-25 13:40:08 -0700851static int cbfs_add_component(const char *filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800852 const char *name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -0800853 uint32_t headeroffset,
Stefan Reinauer2dd161f2015-03-04 00:55:03 +0100854 convert_buffer_t convert)
855{
Julius Werner6ddacd62021-12-03 14:46:53 -0800856 /*
857 * The steps used to determine the final placement offset in CBFS, in order:
858 *
Julius Werner20ad3652021-12-09 13:20:59 -0800859 * 1. If --base-address was passed, that value is used. If it was passed in the host
860 * address space, convert it to flash address space. (After that, |*offset| is always
861 * in the flash address space.)
Julius Werner6ddacd62021-12-03 14:46:53 -0800862 *
863 * 2. The convert() function may write a location back to |offset|, usually by calling
864 * do_cbfs_locate(). In this case, it needs to ensure that the location found can fit
865 * the CBFS file in its final form (after any compression and conversion).
866 *
867 * 3. If --align was passed and the offset is still undecided at this point,
868 * do_cbfs_locate() is called to find an appropriately aligned location.
869 *
870 * 4. If |offset| is still 0 at the end, cbfs_add_entry() will find the first available
871 * location that fits.
872 */
Julius Wernerc24db002020-11-18 18:00:31 -0800873 uint32_t offset = param.baseaddress_assigned ? param.baseaddress : 0;
Julius Werner6ddacd62021-12-03 14:46:53 -0800874 size_t len_align = 0;
Julius Wernerc24db002020-11-18 18:00:31 -0800875
876 if (param.alignment && param.baseaddress_assigned) {
877 ERROR("Cannot specify both alignment and base address\n");
878 return 1;
879 }
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100880
Julius Werner6ddacd62021-12-03 14:46:53 -0800881 if (param.stage_xip && param.compression != CBFS_COMPRESS_NONE) {
882 ERROR("Cannot specify compression for XIP.\n");
883 return 1;
884 }
885
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800886 if (!filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800887 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700888 return 1;
889 }
890
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800891 if (!name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800892 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700893 return 1;
894 }
895
Julius Werner6ddacd62021-12-03 14:46:53 -0800896 if (param.type == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800897 ERROR("You need to specify a valid -t/--type.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -0700898 return 1;
899 }
900
Sol Bouchere3260a02015-03-25 13:40:08 -0700901 struct cbfs_image image;
Sol Boucher67a0a862015-03-18 12:36:27 -0700902 if (cbfs_image_from_buffer(&image, param.image_region, headeroffset))
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800903 return 1;
Stefan Reinauer8f50e532013-11-13 14:34:57 -0800904
Patrick Georgi4e54bf92015-08-11 14:35:39 +0200905 if (cbfs_get_entry(&image, name)) {
906 ERROR("'%s' already in ROM image.\n", name);
907 return 1;
908 }
909
Sol Bouchere3260a02015-03-25 13:40:08 -0700910 struct buffer buffer;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +0800911 if (buffer_from_file(&buffer, filename) != 0) {
912 ERROR("Could not load file '%s'.\n", filename);
Stefan Reinauer63217582012-10-29 16:52:36 -0700913 return 1;
914 }
915
Patrick Georgi3ba501b2015-08-25 13:48:10 +0200916 struct cbfs_file *header =
Julius Werner6ddacd62021-12-03 14:46:53 -0800917 cbfs_create_file_header(param.type, buffer.size, name);
Patrick Georgi4110abc2015-08-11 15:10:33 +0200918
Julius Werner4bfbabd2020-05-06 17:27:02 -0700919 /* Bootblock and CBFS header should never have file hashes. When adding
920 the bootblock it is important that we *don't* look up the metadata
921 hash yet (before it is added) or we'll cache an outdated result. */
Julius Werneraf20fd72022-05-27 18:33:15 -0700922 if (!verification_exclude(param.type)) {
Julius Werner4bfbabd2020-05-06 17:27:02 -0700923 enum vb2_hash_algorithm mh_algo = get_mh_cache()->cbfs_hash.algo;
924 if (mh_algo != VB2_HASH_INVALID && param.hash != mh_algo) {
925 if (param.hash == VB2_HASH_INVALID) {
926 param.hash = mh_algo;
927 } else {
928 ERROR("Cannot specify hash %s that's different from metadata hash algorithm %s\n",
929 vb2_get_hash_algorithm_name(param.hash),
930 vb2_get_hash_algorithm_name(mh_algo));
931 goto error;
932 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200933 }
Julius Wernerc24db002020-11-18 18:00:31 -0800934 }
Patrick Georgi89f20342015-10-01 15:54:04 +0200935
Julius Wernerc24db002020-11-18 18:00:31 -0800936 /*
937 * Check if Intel CPU topswap is specified this will require a
938 * second bootblock to be added.
939 */
Julius Werner6ddacd62021-12-03 14:46:53 -0800940 if (param.type == CBFS_TYPE_BOOTBLOCK && param.topswap_size)
Julius Wernerc24db002020-11-18 18:00:31 -0800941 if (add_topswap_bootblock(&buffer, &offset))
942 goto error;
943
Julius Werner20ad3652021-12-09 13:20:59 -0800944 /* With --base-address we allow host space addresses -- if so, convert it here. */
945 if (IS_HOST_SPACE_ADDRESS(offset))
946 offset = convert_addr_space(param.image_region, offset);
947
Julius Wernerc24db002020-11-18 18:00:31 -0800948 if (convert && convert(&buffer, &offset, header) != 0) {
949 ERROR("Failed to parse file '%s'.\n", filename);
950 goto error;
951 }
952
Julius Werner6ddacd62021-12-03 14:46:53 -0800953 /* This needs to run after convert() to take compression into account. */
954 if (!offset && param.alignment)
Julius Werner772714d2021-12-13 10:16:44 -0800955 if (do_cbfs_locate(&offset, buffer_size(&buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -0800956 goto error;
957
958 /* This needs to run after convert() to hash the actual final file data. */
Julius Wernerc24db002020-11-18 18:00:31 -0800959 if (param.hash != VB2_HASH_INVALID &&
960 cbfs_add_file_hash(header, &buffer, param.hash) == -1) {
961 ERROR("couldn't add hash for '%s'\n", name);
962 goto error;
Julius Werner4bfbabd2020-05-06 17:27:02 -0700963 }
964
Werner Zehe9995f12016-01-14 13:22:37 +0100965 if (param.autogen_attr) {
966 /* Add position attribute if assigned */
967 if (param.baseaddress_assigned || param.stage_xip) {
968 struct cbfs_file_attr_position *attrs =
969 (struct cbfs_file_attr_position *)
970 cbfs_add_file_attr(header,
971 CBFS_FILE_ATTR_TAG_POSITION,
972 sizeof(struct cbfs_file_attr_position));
973 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800974 goto error;
Alex James02001a382021-12-19 16:41:59 -0600975 attrs->position = htobe32(offset);
Werner Zehe9995f12016-01-14 13:22:37 +0100976 }
977 /* Add alignment attribute if used */
978 if (param.alignment) {
979 struct cbfs_file_attr_align *attrs =
980 (struct cbfs_file_attr_align *)
981 cbfs_add_file_attr(header,
982 CBFS_FILE_ATTR_TAG_ALIGNMENT,
983 sizeof(struct cbfs_file_attr_align));
984 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800985 goto error;
Alex James02001a382021-12-19 16:41:59 -0600986 attrs->alignment = htobe32(param.alignment);
Werner Zehe9995f12016-01-14 13:22:37 +0100987 }
988 }
989
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100990 if (param.ibb) {
991 /* Mark as Initial Boot Block */
992 struct cbfs_file_attribute *attrs = cbfs_add_file_attr(header,
993 CBFS_FILE_ATTR_TAG_IBB,
994 sizeof(struct cbfs_file_attribute));
995 if (attrs == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -0800996 goto error;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +0100997 /* For Intel TXT minimum align is 16 */
998 len_align = 16;
999 }
1000
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001001 if (param.padding) {
1002 const uint32_t hs = sizeof(struct cbfs_file_attribute);
Julius Werner5779ca72020-11-20 16:12:40 -08001003 uint32_t size = ALIGN_UP(MAX(hs, param.padding),
1004 CBFS_ATTRIBUTE_ALIGN);
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001005 INFO("Padding %d bytes\n", size);
1006 struct cbfs_file_attribute *attr =
1007 (struct cbfs_file_attribute *)cbfs_add_file_attr(
1008 header, CBFS_FILE_ATTR_TAG_PADDING,
1009 size);
1010 if (attr == NULL)
Julius Wernerc24db002020-11-18 18:00:31 -08001011 goto error;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001012 }
1013
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001014 if (cbfs_add_entry(&image, &buffer, offset, header, len_align) != 0) {
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001015 ERROR("Failed to add '%s' into ROM image.\n", filename);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001016 goto error;
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001017 }
1018
Patrick Georgi3ba501b2015-08-25 13:48:10 +02001019 free(header);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001020 buffer_delete(&buffer);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001021
1022 return maybe_update_metadata_hash(&image) || maybe_update_fmap_hash();
1023
1024error:
1025 free(header);
1026 buffer_delete(&buffer);
1027 return 1;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001028}
1029
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001030static int cbfstool_convert_raw(struct buffer *buffer,
1031 unused uint32_t *offset, struct cbfs_file *header)
1032{
1033 char *compressed;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001034 int decompressed_size, compressed_size;
1035 comp_func_ptr compress;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001036
Patrick Georgiecaa5702017-01-11 18:38:11 +01001037 decompressed_size = buffer->size;
1038 if (param.precompression) {
Nico Huber607796a2017-01-17 13:01:25 +01001039 param.compression = read_le32(buffer->data);
1040 decompressed_size = read_le32(buffer->data + sizeof(uint32_t));
Patrick Georgiecaa5702017-01-11 18:38:11 +01001041 compressed_size = buffer->size - 8;
1042 compressed = malloc(compressed_size);
1043 if (!compressed)
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001044 return -1;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001045 memcpy(compressed, buffer->data + 8, compressed_size);
1046 } else {
Julius Werner105cdf52020-10-23 19:19:32 -07001047 if (param.compression == CBFS_COMPRESS_NONE)
1048 goto out;
1049
Patrick Georgiecaa5702017-01-11 18:38:11 +01001050 compress = compression_function(param.compression);
1051 if (!compress)
1052 return -1;
1053 compressed = calloc(buffer->size, 1);
1054 if (!compressed)
1055 return -1;
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001056
Patrick Georgiecaa5702017-01-11 18:38:11 +01001057 if (compress(buffer->data, buffer->size,
1058 compressed, &compressed_size)) {
1059 WARN("Compression failed - disabled\n");
1060 free(compressed);
Julius Werner105cdf52020-10-23 19:19:32 -07001061 goto out;
Patrick Georgiecaa5702017-01-11 18:38:11 +01001062 }
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001063 }
Patrick Georgiecaa5702017-01-11 18:38:11 +01001064
1065 struct cbfs_file_attr_compression *attrs =
1066 (struct cbfs_file_attr_compression *)
1067 cbfs_add_file_attr(header,
1068 CBFS_FILE_ATTR_TAG_COMPRESSION,
1069 sizeof(struct cbfs_file_attr_compression));
1070 if (attrs == NULL) {
1071 free(compressed);
1072 return -1;
1073 }
Alex James02001a382021-12-19 16:41:59 -06001074 attrs->compression = htobe32(param.compression);
1075 attrs->decompressed_size = htobe32(decompressed_size);
Patrick Georgiecaa5702017-01-11 18:38:11 +01001076
1077 free(buffer->data);
1078 buffer->data = compressed;
1079 buffer->size = compressed_size;
1080
Julius Werner105cdf52020-10-23 19:19:32 -07001081out:
Alex James02001a382021-12-19 16:41:59 -06001082 header->len = htobe32(buffer->size);
Daisuke Nojiri8984a632015-07-09 15:07:45 -07001083 return 0;
1084}
1085
Aaron Durbin8a414a02015-10-01 17:02:45 -05001086static int cbfstool_convert_fsp(struct buffer *buffer,
1087 uint32_t *offset, struct cbfs_file *header)
1088{
1089 uint32_t address;
1090 struct buffer fsp;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001091
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001092 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001093 * There are 4 different cases here:
1094 *
1095 * 1. --xip and --base-address: we need to place the binary at the given base address
Julius Werner20ad3652021-12-09 13:20:59 -08001096 * in the CBFS image and relocate it to that address. *offset was already filled in,
1097 * but we need to convert it to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001098 *
1099 * 2. --xip but no --base-address: we implicitly force a 4K minimum alignment so that
1100 * relocation can occur. Call do_cbfs_locate() here to find an appropriate *offset.
Julius Werner20ad3652021-12-09 13:20:59 -08001101 * This also needs to be converted to the host address space for relocation.
Julius Werner6ddacd62021-12-03 14:46:53 -08001102 *
1103 * 3. No --xip but a --base-address: special case where --base-address does not have its
1104 * normal meaning, instead we use it as the relocation target address. We explicitly
1105 * reset *offset to 0 so that the file will be placed wherever it fits in CBFS.
1106 *
1107 * 4. No --xip and no --base-address: this means that the FSP was pre-linked and should
1108 * not be relocated. Just chain directly to convert_raw() for compression.
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001109 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001110
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001111 if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001112 if (!param.baseaddress_assigned) {
1113 param.alignment = 4*1024;
Julius Werner772714d2021-12-13 10:16:44 -08001114 if (do_cbfs_locate(offset, buffer_size(buffer)))
Julius Werner6ddacd62021-12-03 14:46:53 -08001115 return -1;
1116 }
Julius Werner20ad3652021-12-09 13:20:59 -08001117 assert(!IS_HOST_SPACE_ADDRESS(*offset));
1118 address = convert_addr_space(param.image_region, *offset);
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001119 } else {
Aaron Durbin493ec922016-05-16 15:18:45 -05001120 if (param.baseaddress_assigned == 0) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001121 INFO("Honoring pre-linked FSP module, no relocation.\n");
1122 return cbfstool_convert_raw(buffer, offset, header);
Aaron Durbin493ec922016-05-16 15:18:45 -05001123 } else {
1124 address = param.baseaddress;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001125 *offset = 0;
Furquan Shaikhc3bb6922020-06-09 21:20:27 -07001126 }
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001127 }
Aaron Durbin8a414a02015-10-01 17:02:45 -05001128
1129 /* Create a copy of the buffer to attempt relocation. */
1130 if (buffer_create(&fsp, buffer_size(buffer), "fsp"))
1131 return -1;
1132
1133 memcpy(buffer_get(&fsp), buffer_get(buffer), buffer_size(buffer));
1134
1135 /* Replace the buffer contents w/ the relocated ones on success. */
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001136 if (fsp_component_relocate(address, buffer_get(&fsp), buffer_size(&fsp))
1137 > 0) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001138 buffer_delete(buffer);
1139 buffer_clone(buffer, &fsp);
1140 } else {
1141 buffer_delete(&fsp);
Furquan Shaikhb0c2fe02016-05-09 12:23:01 -07001142 WARN("Invalid FSP variant.\n");
Aaron Durbin8a414a02015-10-01 17:02:45 -05001143 }
1144
1145 /* Let the raw path handle all the cbfs metadata logic. */
1146 return cbfstool_convert_raw(buffer, offset, header);
1147}
1148
Patrick Georgif8204972015-08-11 15:16:24 +02001149static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001150 struct cbfs_file *header)
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001151{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001152 struct buffer output;
Julius Wernerc24db002020-11-18 18:00:31 -08001153 size_t data_size;
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001154 int ret;
Aaron Durbin4be16742015-09-15 17:00:23 -05001155
Julius Wernerc24db002020-11-18 18:00:31 -08001156 if (elf_program_file_size(buffer, &data_size) < 0) {
1157 ERROR("Could not obtain ELF size\n");
1158 return 1;
1159 }
1160
1161 /*
Julius Werner6ddacd62021-12-03 14:46:53 -08001162 * We need a final location for XIP parsing, so we need to call do_cbfs_locate() early
1163 * here. That is okay because XIP stages may not be compressed, so their size cannot
1164 * change anymore at a later point.
Julius Wernerc24db002020-11-18 18:00:31 -08001165 */
Julius Werner6ddacd62021-12-03 14:46:53 -08001166 if (param.stage_xip &&
1167 do_cbfs_locate(offset, data_size)) {
Julius Wernerc24db002020-11-18 18:00:31 -08001168 ERROR("Could not find location for stage.\n");
1169 return 1;
1170 }
1171
Julius Werner81dc20e2020-10-15 17:37:57 -07001172 struct cbfs_file_attr_stageheader *stageheader = (void *)
1173 cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
1174 sizeof(struct cbfs_file_attr_stageheader));
1175 if (!stageheader)
1176 return -1;
1177
Aaron Durbin4be16742015-09-15 17:00:23 -05001178 if (param.stage_xip) {
Julius Werner20ad3652021-12-09 13:20:59 -08001179 uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
1180 assert(IS_HOST_SPACE_ADDRESS(host_space_address));
1181 ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
1182 param.ignore_section, stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001183 } else {
Julius Werner81dc20e2020-10-15 17:37:57 -07001184 ret = parse_elf_to_stage(buffer, &output, param.ignore_section,
1185 stageheader);
Julius Wernerff61a392021-01-12 15:21:03 -08001186 }
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001187 if (ret != 0)
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001188 return -1;
Julius Werner81dc20e2020-10-15 17:37:57 -07001189
1190 /* Store a hash of original uncompressed stage to compare later. */
1191 size_t decmp_size = buffer_size(&output);
1192 uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
1193
1194 /* Chain to base conversion routine to handle compression. */
1195 ret = cbfstool_convert_raw(&output, offset, header);
1196 if (ret != 0)
1197 goto fail;
1198
1199 /* Special care must be taken for LZ4-compressed stages that the BSS is
1200 large enough to provide scratch space for in-place decompression. */
1201 if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
Alex James02001a382021-12-19 16:41:59 -06001202 size_t memlen = be32toh(stageheader->memlen);
Julius Werner81dc20e2020-10-15 17:37:57 -07001203 size_t compressed_size = buffer_size(&output);
1204 uint8_t *compare_buffer = malloc(memlen);
1205 uint8_t *start = compare_buffer + memlen - compressed_size;
1206 if (!compare_buffer) {
1207 ERROR("Out of memory\n");
1208 goto fail;
1209 }
1210 memcpy(start, buffer_get(&output), compressed_size);
1211 ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
1212 if (ret == 0) {
1213 ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
1214 free(compare_buffer);
1215 goto fail;
1216 } else if (ret != (int)decmp_size ||
1217 decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
1218 ERROR("LZ4 compression BUG! Report to mailing list.\n");
1219 free(compare_buffer);
1220 goto fail;
1221 }
1222 free(compare_buffer);
1223 }
1224
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001225 buffer_delete(buffer);
Julius Werner81dc20e2020-10-15 17:37:57 -07001226 buffer_clone(buffer, &output);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001227 return 0;
Julius Werner81dc20e2020-10-15 17:37:57 -07001228
1229fail:
1230 buffer_delete(&output);
1231 return -1;
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001232}
1233
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001234static int cbfstool_convert_mkpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001235 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001236{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001237 struct buffer output;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001238 int ret;
Joel Kitching672d5ad2018-10-11 17:01:30 +08001239 /* Per default, try and see if payload is an ELF binary */
Sol Boucher65336712015-05-07 21:00:05 -07001240 ret = parse_elf_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001241
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001242 /* If it's not an ELF, see if it's a FIT */
1243 if (ret != 0) {
1244 ret = parse_fit_to_payload(buffer, &output, param.compression);
1245 if (ret == 0)
Julius Werner00572622022-05-26 20:29:42 -07001246 header->type = htobe32(CBFS_TYPE_FIT_PAYLOAD);
Patrick Rudolph7ee05ed2018-04-26 09:35:13 +02001247 }
1248
1249 /* If it's not an FIT, see if it's a UEFI FV */
Stefan Reinauer543a6822013-02-04 15:39:13 -08001250 if (ret != 0)
Sol Boucher65336712015-05-07 21:00:05 -07001251 ret = parse_fv_to_payload(buffer, &output, param.compression);
Stefan Reinauer543a6822013-02-04 15:39:13 -08001252
Patrick Georgide36d332013-08-27 20:22:21 +02001253 /* If it's neither ELF nor UEFI Fv, try bzImage */
1254 if (ret != 0)
1255 ret = parse_bzImage_to_payload(buffer, &output,
Sol Boucher65336712015-05-07 21:00:05 -07001256 param.initrd, param.cmdline, param.compression);
Patrick Georgide36d332013-08-27 20:22:21 +02001257
Stefan Reinauer543a6822013-02-04 15:39:13 -08001258 /* Not a supported payload type */
1259 if (ret != 0) {
1260 ERROR("Not a supported payload type (ELF / FV).\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001261 buffer_delete(buffer);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001262 return -1;
Stefan Reinauer543a6822013-02-04 15:39:13 -08001263 }
1264
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001265 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001266 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001267 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001268 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001269 return 0;
1270}
1271
1272static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
Patrick Georgi056f6a12015-08-25 15:53:52 +02001273 unused uint32_t *offset, struct cbfs_file *header)
Stefan Reinauer2dd161f2015-03-04 00:55:03 +01001274{
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001275 struct buffer output;
1276 if (parse_flat_binary_to_payload(buffer, &output,
1277 param.loadaddress,
1278 param.entrypoint,
Sol Boucher65336712015-05-07 21:00:05 -07001279 param.compression) != 0) {
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001280 return -1;
1281 }
1282 buffer_delete(buffer);
Joel Kitching672d5ad2018-10-11 17:01:30 +08001283 // Direct assign, no dupe.
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001284 memcpy(buffer, &output, sizeof(*buffer));
Alex James02001a382021-12-19 16:41:59 -06001285 header->len = htobe32(output.size);
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001286 return 0;
1287}
1288
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001289static int cbfs_add(void)
1290{
Julius Wernerc24db002020-11-18 18:00:31 -08001291 convert_buffer_t convert = cbfstool_convert_raw;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001292
Julius Wernerd4775652020-03-13 16:43:34 -07001293 if (param.type == CBFS_TYPE_FSP) {
Aaron Durbin8a414a02015-10-01 17:02:45 -05001294 convert = cbfstool_convert_fsp;
Julius Werner6ddacd62021-12-03 14:46:53 -08001295 } else if (param.type == CBFS_TYPE_STAGE) {
1296 ERROR("stages can only be added with cbfstool add-stage\n");
1297 return 1;
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001298 } else if (param.stage_xip) {
Julius Werner6ddacd62021-12-03 14:46:53 -08001299 ERROR("cbfstool add supports xip only for FSP component type\n");
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001300 return 1;
Aaron Durbin8a414a02015-10-01 17:02:45 -05001301 }
1302
Sol Bouchere3260a02015-03-25 13:40:08 -07001303 return cbfs_add_component(param.filename,
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001304 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001305 param.headeroffset,
Aaron Durbin8a414a02015-10-01 17:02:45 -05001306 convert);
Hung-Te Lin5f3eb262013-01-29 10:24:00 +08001307}
1308
Stefan Reinauer63217582012-10-29 16:52:36 -07001309static int cbfs_add_stage(void)
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001310{
Julius Werner6ddacd62021-12-03 14:46:53 -08001311 if (param.stage_xip && param.baseaddress_assigned) {
1312 ERROR("Cannot specify base address for XIP.\n");
1313 return 1;
Aaron Durbin4be16742015-09-15 17:00:23 -05001314 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001315 param.type = CBFS_TYPE_STAGE;
Aaron Durbin4be16742015-09-15 17:00:23 -05001316
Sol Bouchere3260a02015-03-25 13:40:08 -07001317 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001318 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001319 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001320 cbfstool_convert_mkstage);
1321}
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001322
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001323static int cbfs_add_payload(void)
1324{
Julius Werner6ddacd62021-12-03 14:46:53 -08001325 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001326 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001327 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001328 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001329 cbfstool_convert_mkpayload);
Stefan Reinauer63217582012-10-29 16:52:36 -07001330}
1331
1332static int cbfs_add_flat_binary(void)
1333{
Hung-Te Lind1739622013-01-28 14:23:49 +08001334 if (param.loadaddress == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001335 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001336 "-l/--load-address.\n");
1337 return 1;
1338 }
Hung-Te Lind1739622013-01-28 14:23:49 +08001339 if (param.entrypoint == 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001340 ERROR("You need to specify a valid "
Stefan Reinauer63217582012-10-29 16:52:36 -07001341 "-e/--entry-point.\n");
1342 return 1;
1343 }
Julius Werner6ddacd62021-12-03 14:46:53 -08001344 param.type = CBFS_TYPE_SELF;
Sol Bouchere3260a02015-03-25 13:40:08 -07001345 return cbfs_add_component(param.filename,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001346 param.name,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001347 param.headeroffset,
Hung-Te Linc13e4bf2013-01-29 15:22:11 +08001348 cbfstool_convert_mkflatpayload);
Stefan Reinauer20848ee2012-10-22 16:04:13 -07001349}
1350
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001351static int cbfs_add_integer(void)
1352{
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01001353 if (!param.u64val_assigned) {
1354 ERROR("You need to specify a value to write.\n");
1355 return 1;
1356 }
Sol Bouchere3260a02015-03-25 13:40:08 -07001357 return cbfs_add_integer_component(param.name,
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001358 param.u64val,
Vadim Bendebury458a12e2014-12-23 15:10:12 -08001359 param.baseaddress,
1360 param.headeroffset);
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001361}
1362
Stefan Reinauer63217582012-10-29 16:52:36 -07001363static int cbfs_remove(void)
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001364{
Hung-Te Lind1739622013-01-28 14:23:49 +08001365 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001366 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001367 return 1;
1368 }
1369
Sol Bouchere3260a02015-03-25 13:40:08 -07001370 struct cbfs_image image;
1371 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001372 param.headeroffset))
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001373 return 1;
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001374
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001375 if (cbfs_remove_entry(&image, param.name) != 0) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001376 ERROR("Removing file '%s' failed.\n",
Hung-Te Linc03d9b02013-01-29 02:38:40 +08001377 param.name);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001378 return 1;
1379 }
1380
Julius Werner4bfbabd2020-05-06 17:27:02 -07001381 return maybe_update_metadata_hash(&image);
Gabe Blacke1bb49e2012-01-27 00:33:47 -08001382}
1383
Stefan Reinauer63217582012-10-29 16:52:36 -07001384static int cbfs_create(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001385{
Sol Boucher67a0a862015-03-18 12:36:27 -07001386 struct cbfs_image image;
1387 memset(&image, 0, sizeof(image));
1388 buffer_clone(&image.buffer, param.image_region);
1389
1390 if (param.fmap) {
1391 if (param.arch != CBFS_ARCHITECTURE_UNKNOWN || param.size ||
1392 param.baseaddress_assigned ||
1393 param.headeroffset_assigned ||
1394 param.cbfsoffset_assigned ||
Sol Boucher67a0a862015-03-18 12:36:27 -07001395 param.bootblock) {
Patrick Georgi45acb342015-07-14 22:18:23 +02001396 ERROR("Since -M was provided, -m, -s, -b, -o, -H, and -B should be omitted\n");
Sol Boucher67a0a862015-03-18 12:36:27 -07001397 return 1;
1398 }
1399
1400 return cbfs_image_create(&image, image.buffer.size);
1401 }
1402
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06001403 if (param.arch == CBFS_ARCHITECTURE_UNKNOWN) {
Stefan Reinauer60a4a732013-03-28 16:46:07 -07001404 ERROR("You need to specify -m/--machine arch.\n");
David Hendricks90ca3b62012-11-16 14:48:22 -08001405 return 1;
1406 }
1407
Sol Bouchere3260a02015-03-25 13:40:08 -07001408 struct buffer bootblock;
Julius Wernerefcee762014-11-10 13:14:24 -08001409 if (!param.bootblock) {
1410 DEBUG("-B not given, creating image without bootblock.\n");
Patrick Georgi6b2d2db2016-12-14 16:11:58 +01001411 if (buffer_create(&bootblock, 0, "(dummy)") != 0)
1412 return 1;
Julius Wernerefcee762014-11-10 13:14:24 -08001413 } else if (buffer_from_file(&bootblock, param.bootblock)) {
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001414 return 1;
1415 }
1416
Julius Wernerefcee762014-11-10 13:14:24 -08001417 if (!param.alignment)
Patrick Georgi45acb342015-07-14 22:18:23 +02001418 param.alignment = CBFS_ALIGNMENT;
Julius Wernerefcee762014-11-10 13:14:24 -08001419
1420 // Set default offsets. x86, as usual, needs to be a special snowflake.
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001421 if (!param.baseaddress_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001422 if (param.arch == CBFS_ARCHITECTURE_X86) {
1423 // Make sure there's at least enough room for rel_offset
Sol Boucher67a0a862015-03-18 12:36:27 -07001424 param.baseaddress = param.size -
1425 MAX(bootblock.size, sizeof(int32_t));
Julius Wernerefcee762014-11-10 13:14:24 -08001426 DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n",
1427 param.baseaddress);
1428 } else {
1429 param.baseaddress = 0;
1430 DEBUG("bootblock starts at address 0x0.\n");
1431 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001432 }
1433 if (!param.headeroffset_assigned) {
Julius Wernerefcee762014-11-10 13:14:24 -08001434 if (param.arch == CBFS_ARCHITECTURE_X86) {
1435 param.headeroffset = param.baseaddress -
1436 sizeof(struct cbfs_header);
1437 DEBUG("x86 -> CBFS header before bootblock (%#x).\n",
1438 param.headeroffset);
1439 } else {
1440 param.headeroffset = align_up(param.baseaddress +
1441 bootblock.size, sizeof(uint32_t));
1442 DEBUG("CBFS header placed behind bootblock (%#x).\n",
1443 param.headeroffset);
1444 }
1445 }
1446 if (!param.cbfsoffset_assigned) {
1447 if (param.arch == CBFS_ARCHITECTURE_X86) {
1448 param.cbfsoffset = 0;
1449 DEBUG("x86 -> CBFS entries start at address 0x0.\n");
1450 } else {
1451 param.cbfsoffset = align_up(param.headeroffset +
1452 sizeof(struct cbfs_header),
Patrick Georgi45acb342015-07-14 22:18:23 +02001453 CBFS_ALIGNMENT);
Julius Wernerefcee762014-11-10 13:14:24 -08001454 DEBUG("CBFS entries start beind master header (%#x).\n",
1455 param.cbfsoffset);
Hung-Te Linf56c73f2013-01-29 09:45:12 +08001456 }
1457 }
1458
Sol Boucher67a0a862015-03-18 12:36:27 -07001459 int ret = cbfs_legacy_image_create(&image,
1460 param.arch,
Patrick Georgi45acb342015-07-14 22:18:23 +02001461 CBFS_ALIGNMENT,
Sol Boucher67a0a862015-03-18 12:36:27 -07001462 &bootblock,
1463 param.baseaddress,
1464 param.headeroffset,
1465 param.cbfsoffset);
Sol Bouchere3260a02015-03-25 13:40:08 -07001466 buffer_delete(&bootblock);
Sol Boucher67a0a862015-03-18 12:36:27 -07001467 return ret;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001468}
1469
Sol Boucher67a0a862015-03-18 12:36:27 -07001470static int cbfs_layout(void)
1471{
1472 const struct fmap *fmap = partitioned_file_get_fmap(param.image_file);
1473 if (!fmap) {
1474 LOG("This is a legacy image composed entirely of a single CBFS.\n");
1475 return 1;
1476 }
1477
1478 printf("This image contains the following sections that can be %s with this tool:\n",
1479 param.show_immutable ? "accessed" : "manipulated");
1480 puts("");
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001481 for (unsigned i = 0; i < fmap->nareas; ++i) {
1482 const struct fmap_area *current = fmap->areas + i;
Sol Boucher67a0a862015-03-18 12:36:27 -07001483
1484 bool readonly = partitioned_file_fmap_count(param.image_file,
1485 partitioned_file_fmap_select_children_of, current) ||
1486 region_is_flashmap((const char *)current->name);
1487 if (!param.show_immutable && readonly)
1488 continue;
1489
1490 printf("'%s'", current->name);
1491
1492 // Detect consecutive sections that describe the same region and
1493 // show them as aliases. This cannot find equivalent entries
1494 // that aren't adjacent; however, fmaptool doesn't generate
1495 // FMAPs with such sections, so this convenience feature works
1496 // for all but the strangest manually created FMAP binaries.
1497 // TODO: This could be done by parsing the FMAP into some kind
1498 // of tree that had duplicate lists in addition to child lists,
1499 // which would allow covering that weird, unlikely case as well.
1500 unsigned lookahead;
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001501 for (lookahead = 1; i + lookahead < fmap->nareas;
Sol Boucher67a0a862015-03-18 12:36:27 -07001502 ++lookahead) {
1503 const struct fmap_area *consecutive =
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001504 fmap->areas + i + lookahead;
Sol Boucher67a0a862015-03-18 12:36:27 -07001505 if (consecutive->offset != current->offset ||
1506 consecutive->size != current->size)
1507 break;
1508 printf(", '%s'", consecutive->name);
1509 }
1510 if (lookahead > 1)
1511 fputs(" are aliases for the same region", stdout);
1512
1513 const char *qualifier = "";
1514 if (readonly)
1515 qualifier = "read-only, ";
1516 else if (region_is_modern_cbfs((const char *)current->name))
1517 qualifier = "CBFS, ";
Patrick Georgi848e30d2019-06-18 22:02:13 +02001518 else if (current->flags & FMAP_AREA_PRESERVE)
1519 qualifier = "preserve, ";
Werner Zeh7850b582018-04-26 09:26:38 +02001520 printf(" (%ssize %u, offset %u)\n", qualifier, current->size,
1521 current->offset);
Sol Boucher67a0a862015-03-18 12:36:27 -07001522
Kyösti Mälkki2e042592015-05-15 09:14:09 +03001523 i += lookahead - 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001524 }
1525 puts("");
1526
1527 if (param.show_immutable) {
1528 puts("It is at least possible to perform the read action on every section listed above.");
1529 } else {
1530 puts("It is possible to perform either the write action or the CBFS add/remove actions on every section listed above.");
1531 puts("To see the image's read-only sections as well, rerun with the -w option.");
1532 }
1533
1534 return 0;
1535}
1536
Bill XIEac1362502022-07-08 16:53:21 +08001537static enum cb_err verify_walker(__always_unused cbfs_dev_t dev, size_t offset,
Julius Werner423cd062022-05-24 18:18:46 -07001538 const union cbfs_mdata *mdata, size_t already_read, void *arg)
1539{
1540 uint32_t type = be32toh(mdata->h.type);
1541 uint32_t data_offset = be32toh(mdata->h.offset);
1542 if (verification_exclude(type))
1543 return CB_CBFS_NOT_FOUND;
1544 assert(already_read == data_offset);
1545 const struct vb2_hash *hash = cbfs_file_hash(mdata);
1546 if (!hash)
1547 return CB_ERR;
1548 void *file_data = arg + offset + data_offset;
Julius Wernerd96ca242022-08-08 18:08:35 -07001549 if (vb2_hash_verify(false, file_data, be32toh(mdata->h.len), hash) != VB2_SUCCESS)
Julius Werner423cd062022-05-24 18:18:46 -07001550 return CB_CBFS_HASH_MISMATCH;
1551 return CB_CBFS_NOT_FOUND;
1552}
1553
Stefan Reinauer63217582012-10-29 16:52:36 -07001554static int cbfs_print(void)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001555{
Hung-Te Lin3bb035b2013-01-29 02:15:49 +08001556 struct cbfs_image image;
Sol Bouchere3260a02015-03-25 13:40:08 -07001557 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001558 param.headeroffset))
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001559 return 1;
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001560 if (param.machine_parseable) {
1561 if (verbose)
1562 printf("[FMAP REGION]\t%s\n", param.region_name);
1563 cbfs_print_parseable_directory(&image);
1564 } else {
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001565 printf("FMAP REGION: %s\n", param.region_name);
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001566 cbfs_print_directory(&image);
Patrick Rudolphfb87e412018-06-05 15:11:01 +02001567 }
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001568
Julius Werner4bfbabd2020-05-06 17:27:02 -07001569 if (verbose) {
Julius Werner423cd062022-05-24 18:18:46 -07001570 const char *verification_state = "fully valid";
Julius Werner4bfbabd2020-05-06 17:27:02 -07001571 struct mh_cache *mhc = get_mh_cache();
1572 if (mhc->cbfs_hash.algo == VB2_HASH_INVALID)
1573 return 0;
1574
1575 struct vb2_hash real_hash = { .algo = mhc->cbfs_hash.algo };
Julius Werner423cd062022-05-24 18:18:46 -07001576 enum cb_err err = cbfs_walk(&image, verify_walker, buffer_get(&image.buffer),
1577 &real_hash, CBFS_WALK_WRITEBACK_HASH);
1578 if (err == CB_CBFS_HASH_MISMATCH)
1579 verification_state = "invalid file hashes";
1580 else if (err != CB_CBFS_NOT_FOUND)
1581 verification_state = "missing file hashes";
Julius Werner4bfbabd2020-05-06 17:27:02 -07001582 char *hash_str = bintohex(real_hash.raw,
1583 vb2_digest_size(real_hash.algo));
1584 printf("[METADATA HASH]\t%s:%s",
1585 vb2_get_hash_algorithm_name(real_hash.algo), hash_str);
1586 if (!strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)) {
1587 if (!memcmp(mhc->cbfs_hash.raw, real_hash.raw,
Julius Werner423cd062022-05-24 18:18:46 -07001588 vb2_digest_size(real_hash.algo))) {
Julius Werner4bfbabd2020-05-06 17:27:02 -07001589 printf(":valid");
Julius Werner423cd062022-05-24 18:18:46 -07001590 } else {
Julius Werner4bfbabd2020-05-06 17:27:02 -07001591 printf(":invalid");
Julius Werner423cd062022-05-24 18:18:46 -07001592 verification_state = "invalid metadata hash";
1593 }
Julius Werner4bfbabd2020-05-06 17:27:02 -07001594 }
1595 printf("\n");
Julius Werner423cd062022-05-24 18:18:46 -07001596 printf("[CBFS VERIFICATION (%s)]\t%s\n", param.region_name, verification_state);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001597 free(hash_str);
1598 }
1599
Julius Wernerc4ee28c2020-04-27 19:31:03 -07001600 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001601}
1602
Stefan Reinauer63217582012-10-29 16:52:36 -07001603static int cbfs_extract(void)
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001604{
Hung-Te Lind1739622013-01-28 14:23:49 +08001605 if (!param.filename) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001606 ERROR("You need to specify -f/--filename.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001607 return 1;
1608 }
1609
Hung-Te Lind1739622013-01-28 14:23:49 +08001610 if (!param.name) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001611 ERROR("You need to specify -n/--name.\n");
Stefan Reinauer63217582012-10-29 16:52:36 -07001612 return 1;
1613 }
1614
Sol Bouchere3260a02015-03-25 13:40:08 -07001615 struct cbfs_image image;
1616 if (cbfs_image_from_buffer(&image, param.image_region,
Sol Boucher67a0a862015-03-18 12:36:27 -07001617 param.headeroffset))
1618 return 1;
1619
Aaron Durbin17625022015-10-27 13:17:52 -05001620 return cbfs_export_entry(&image, param.name, param.filename,
Joel Kitching21fdd892018-08-09 17:49:52 +08001621 param.arch, !param.unprocessed);
Sol Boucher67a0a862015-03-18 12:36:27 -07001622}
1623
1624static int cbfs_write(void)
1625{
1626 if (!param.filename) {
1627 ERROR("You need to specify a valid input -f/--file.\n");
1628 return 1;
1629 }
1630 if (!partitioned_file_is_partitioned(param.image_file)) {
1631 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07001632 return 1;
1633 }
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001634
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001635 if (!param.force && region_is_modern_cbfs(param.region_name)) {
Sol Boucher67a0a862015-03-18 12:36:27 -07001636 ERROR("Target image region '%s' is a CBFS and must be manipulated using add and remove\n",
1637 param.region_name);
1638 return 1;
1639 }
1640
1641 struct buffer new_content;
1642 if (buffer_from_file(&new_content, param.filename))
1643 return 1;
1644
1645 if (buffer_check_magic(&new_content, FMAP_SIGNATURE,
1646 strlen(FMAP_SIGNATURE))) {
1647 ERROR("File '%s' appears to be an FMAP and cannot be added to an existing image\n",
1648 param.filename);
1649 buffer_delete(&new_content);
1650 return 1;
1651 }
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001652 if (!param.force && buffer_check_magic(&new_content, CBFS_FILE_MAGIC,
Sol Boucher67a0a862015-03-18 12:36:27 -07001653 strlen(CBFS_FILE_MAGIC))) {
1654 ERROR("File '%s' appears to be a CBFS and cannot be inserted into a raw region\n",
1655 param.filename);
1656 buffer_delete(&new_content);
1657 return 1;
1658 }
1659
1660 unsigned offset = 0;
1661 if (param.fill_partial_upward && param.fill_partial_downward) {
1662 ERROR("You may only specify one of -u and -d.\n");
1663 buffer_delete(&new_content);
1664 return 1;
1665 } else if (!param.fill_partial_upward && !param.fill_partial_downward) {
1666 if (new_content.size != param.image_region->size) {
1667 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",
1668 new_content.size, param.image_region->size);
1669 buffer_delete(&new_content);
1670 return 1;
1671 }
1672 } else {
1673 if (new_content.size > param.image_region->size) {
1674 ERROR("File to add is %zu bytes and would overflow %zu-byte target region\n",
1675 new_content.size, param.image_region->size);
1676 buffer_delete(&new_content);
1677 return 1;
1678 }
Patrick Georgid9edb182016-12-06 18:55:26 +01001679 if (param.u64val == (uint64_t)-1) {
1680 WARN("Written area will abut %s of target region: any unused space will keep its current contents\n",
1681 param.fill_partial_upward ? "bottom" : "top");
1682 } else if (param.u64val > 0xff) {
1683 ERROR("given fill value (%x) is larger than a byte\n", (unsigned)(param.u64val & 0xff));
1684 buffer_delete(&new_content);
1685 return 1;
1686 } else {
1687 memset(buffer_get(param.image_region),
1688 param.u64val & 0xff,
1689 buffer_size(param.image_region));
1690 }
Sol Boucher67a0a862015-03-18 12:36:27 -07001691 if (param.fill_partial_downward)
1692 offset = param.image_region->size - new_content.size;
1693 }
1694
1695 memcpy(param.image_region->data + offset, new_content.data,
1696 new_content.size);
1697 buffer_delete(&new_content);
Julius Werner4bfbabd2020-05-06 17:27:02 -07001698
1699 return maybe_update_fmap_hash();
Sol Boucher67a0a862015-03-18 12:36:27 -07001700}
1701
1702static int cbfs_read(void)
1703{
1704 if (!param.filename) {
1705 ERROR("You need to specify a valid output -f/--file.\n");
1706 return 1;
1707 }
1708 if (!partitioned_file_is_partitioned(param.image_file)) {
1709 ERROR("This operation isn't valid on legacy images having CBFS master headers\n");
1710 return 1;
1711 }
1712
1713 return buffer_write_file(param.image_region, param.filename);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +00001714}
1715
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001716static int cbfs_copy(void)
1717{
Patrick Georgi214e4af2015-11-20 19:22:50 +01001718 struct cbfs_image src_image;
Patrick Georgibd0bb232015-11-20 21:48:18 +01001719 struct buffer src_buf;
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001720
Patrick Georgibd0bb232015-11-20 21:48:18 +01001721 if (!param.source_region) {
1722 ERROR("You need to specify -R/--source-region.\n");
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001723 return 1;
Sol Bouchere3260a02015-03-25 13:40:08 -07001724 }
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001725
Patrick Georgibd0bb232015-11-20 21:48:18 +01001726 /* Obtain the source region and convert it to a cbfs_image. */
1727 if (!partitioned_file_read_region(&src_buf, param.image_file,
1728 param.source_region)) {
1729 ERROR("Region not found in image: %s\n", param.source_region);
1730 return 1;
1731 }
1732
1733 if (cbfs_image_from_buffer(&src_image, &src_buf, param.headeroffset))
1734 return 1;
1735
1736 return cbfs_copy_instance(&src_image, param.image_region);
Vadim Bendebury5e273a42014-12-23 19:26:54 -08001737}
1738
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001739static int cbfs_compact(void)
1740{
1741 struct cbfs_image image;
1742 if (cbfs_image_from_buffer(&image, param.image_region,
1743 param.headeroffset))
1744 return 1;
1745 WARN("Compacting a CBFS doesn't honor alignment or fixed addresses!\n");
1746 return cbfs_compact_instance(&image);
1747}
1748
Patrick Georgi5d982d72017-09-19 14:39:58 +02001749static int cbfs_expand(void)
1750{
1751 struct buffer src_buf;
1752
1753 /* Obtain the source region. */
1754 if (!partitioned_file_read_region(&src_buf, param.image_file,
1755 param.region_name)) {
1756 ERROR("Region not found in image: %s\n", param.source_region);
1757 return 1;
1758 }
1759
1760 return cbfs_expand_to_region(param.image_region);
1761}
1762
Patrick Georgi12631a42017-09-20 11:59:18 +02001763static int cbfs_truncate(void)
1764{
1765 struct buffer src_buf;
1766
1767 /* Obtain the source region. */
1768 if (!partitioned_file_read_region(&src_buf, param.image_file,
1769 param.region_name)) {
1770 ERROR("Region not found in image: %s\n", param.source_region);
1771 return 1;
1772 }
1773
1774 uint32_t size;
1775 int result = cbfs_truncate_space(param.image_region, &size);
Jakub Czapigaaa415632022-08-01 16:01:28 +02001776 if (!result)
1777 printf("0x%x\n", size);
Patrick Georgi12631a42017-09-20 11:59:18 +02001778 return result;
1779}
1780
Stefan Reinauera1e48242011-10-21 14:24:57 -07001781static const struct command commands[] = {
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301782 {"add", "H:r:f:n:t:c:b:a:p:yvA:j:gh?", cbfs_add, true, true},
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001783 {"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 +01001784 true, true},
Raul E Rangeldb280402021-06-29 13:26:41 -06001785 {"add-payload", "H:r:f:n:c:b:a:C:I:p:vA:gh?", cbfs_add_payload,
Werner Zehe9995f12016-01-14 13:22:37 +01001786 true, true},
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001787 {"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 +01001788 true, true},
1789 {"add-int", "H:r:i:n:b:vgh?", cbfs_add_integer, true, true},
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301790 {"add-master-header", "H:r:vh?j:", cbfs_add_master_header, true, true},
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001791 {"compact", "r:h?", cbfs_compact, true, true},
Patrick Georgibd0bb232015-11-20 21:48:18 +01001792 {"copy", "r:R:h?", cbfs_copy, true, true},
Patrick Georgi45acb342015-07-14 22:18:23 +02001793 {"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
Joel Kitching21fdd892018-08-09 17:49:52 +08001794 {"extract", "H:r:m:n:f:Uvh?", cbfs_extract, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001795 {"layout", "wvh?", cbfs_layout, false, false},
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001796 {"print", "H:r:vkh?", cbfs_print, true, false},
Sol Boucher67a0a862015-03-18 12:36:27 -07001797 {"read", "r:f:vh?", cbfs_read, true, false},
1798 {"remove", "H:r:n:vh?", cbfs_remove, true, true},
Patrick Georgid9edb182016-12-06 18:55:26 +01001799 {"write", "r:f:i:Fudvh?", cbfs_write, true, true},
Patrick Georgi5d982d72017-09-19 14:39:58 +02001800 {"expand", "r:h?", cbfs_expand, true, true},
Patrick Georgi12631a42017-09-20 11:59:18 +02001801 {"truncate", "r:h?", cbfs_truncate, true, true},
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001802};
1803
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001804enum {
1805 /* begin after ASCII characters */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001806 LONGOPT_START = 256,
1807 LONGOPT_IBB = LONGOPT_START,
Arthur Heymans34a7e662022-10-06 09:42:15 +02001808 LONGOPT_MMAP,
Furquan Shaikhc42cf912020-04-14 00:14:44 -07001809 LONGOPT_END,
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001810};
1811
Stefan Reinauer63217582012-10-29 16:52:36 -07001812static struct option long_options[] = {
Julius Wernerefcee762014-11-10 13:14:24 -08001813 {"alignment", required_argument, 0, 'a' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001814 {"base-address", required_argument, 0, 'b' },
1815 {"bootblock", required_argument, 0, 'B' },
Julius Wernerefcee762014-11-10 13:14:24 -08001816 {"cmdline", required_argument, 0, 'C' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001817 {"compression", required_argument, 0, 'c' },
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301818 {"topswap-size", required_argument, 0, 'j' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001819 {"empty-fits", required_argument, 0, 'x' },
1820 {"entry-point", required_argument, 0, 'e' },
1821 {"file", required_argument, 0, 'f' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001822 {"fill-downward", no_argument, 0, 'd' },
1823 {"fill-upward", no_argument, 0, 'u' },
1824 {"flashmap", required_argument, 0, 'M' },
1825 {"fmap-regions", required_argument, 0, 'r' },
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001826 {"force", no_argument, 0, 'F' },
Patrick Georgibd0bb232015-11-20 21:48:18 +01001827 {"source-region", required_argument, 0, 'R' },
Patrick Georgi89f20342015-10-01 15:54:04 +02001828 {"hash-algorithm",required_argument, 0, 'A' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001829 {"header-offset", required_argument, 0, 'H' },
Julius Wernerefcee762014-11-10 13:14:24 -08001830 {"help", no_argument, 0, 'h' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001831 {"ignore-sec", required_argument, 0, 'S' },
1832 {"initrd", required_argument, 0, 'I' },
1833 {"int", required_argument, 0, 'i' },
1834 {"load-address", required_argument, 0, 'l' },
1835 {"machine", required_argument, 0, 'm' },
1836 {"name", required_argument, 0, 'n' },
1837 {"offset", required_argument, 0, 'o' },
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07001838 {"padding", required_argument, 0, 'p' },
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03001839 {"pow2page", no_argument, 0, 'Q' },
Rizwan Qureshic1072f22018-06-04 23:02:46 +05301840 {"ucode-region", required_argument, 0, 'q' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001841 {"size", required_argument, 0, 's' },
Vadim Bendebury45e59972014-12-23 15:59:57 -08001842 {"type", required_argument, 0, 't' },
1843 {"verbose", no_argument, 0, 'v' },
Sol Boucher67a0a862015-03-18 12:36:27 -07001844 {"with-readonly", no_argument, 0, 'w' },
Aaron Durbin4be16742015-09-15 17:00:23 -05001845 {"xip", no_argument, 0, 'y' },
Werner Zehe9995f12016-01-14 13:22:37 +01001846 {"gen-attribute", no_argument, 0, 'g' },
Aaron Durbin5dc628a2016-01-26 15:35:34 -06001847 {"mach-parseable",no_argument, 0, 'k' },
Joel Kitching21fdd892018-08-09 17:49:52 +08001848 {"unprocessed", no_argument, 0, 'U' },
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01001849 {"ibb", no_argument, 0, LONGOPT_IBB },
Arthur Heymans34a7e662022-10-06 09:42:15 +02001850 {"mmap", required_argument, 0, LONGOPT_MMAP },
Julius Wernerefcee762014-11-10 13:14:24 -08001851 {NULL, 0, 0, 0 }
Stefan Reinauer63217582012-10-29 16:52:36 -07001852};
1853
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001854static int get_region_offset(long long int offset, uint32_t *region_offset)
1855{
1856 /* If offset is not negative, no transformation required. */
1857 if (offset >= 0) {
1858 *region_offset = offset;
1859 return 0;
1860 }
1861
1862 /* Calculate offset from start of region. */
1863 return convert_region_offset(-offset, region_offset);
1864}
1865
1866static int calculate_region_offsets(void)
1867{
1868 int ret = 0;
1869
1870 if (param.baseaddress_assigned)
1871 ret |= get_region_offset(param.baseaddress_input, &param.baseaddress);
1872 if (param.headeroffset_assigned)
1873 ret |= get_region_offset(param.headeroffset_input, &param.headeroffset);
1874 if (param.cbfsoffset_assigned)
1875 ret |= get_region_offset(param.cbfsoffset_input, &param.cbfsoffset);
1876
1877 return ret;
1878}
1879
Sol Boucher67a0a862015-03-18 12:36:27 -07001880static int dispatch_command(struct command command)
1881{
1882 if (command.accesses_region) {
1883 assert(param.image_file);
1884
1885 if (partitioned_file_is_partitioned(param.image_file)) {
Patrick Georgi2ed72952016-12-16 14:51:53 +01001886 INFO("Performing operation on '%s' region...\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07001887 param.region_name);
1888 }
1889 if (!partitioned_file_read_region(param.image_region,
1890 param.image_file, param.region_name)) {
1891 ERROR("The image will be left unmodified.\n");
1892 return 1;
1893 }
1894
1895 if (command.modifies_region) {
1896 // We (intentionally) don't support overwriting the FMAP
1897 // section. If you find yourself wanting to do this,
1898 // consider creating a new image rather than performing
1899 // whatever hacky transformation you were planning.
1900 if (region_is_flashmap(param.region_name)) {
1901 ERROR("Image region '%s' is read-only because it contains the FMAP.\n",
1902 param.region_name);
1903 ERROR("The image will be left unmodified.\n");
1904 return 1;
1905 }
1906 // We don't allow writing raw data to regions that
1907 // contain nested regions, since doing so would
1908 // overwrite all such subregions.
1909 if (partitioned_file_region_contains_nested(
1910 param.image_file, param.region_name)) {
1911 ERROR("Image region '%s' is read-only because it contains nested regions.\n",
1912 param.region_name);
1913 ERROR("The image will be left unmodified.\n");
1914 return 1;
1915 }
1916 }
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08001917
1918 /*
1919 * Once image region is read, input offsets can be adjusted accordingly if the
1920 * inputs are provided as negative integers i.e. offsets from end of region.
1921 */
1922 if (calculate_region_offsets())
1923 return 1;
Sol Boucher67a0a862015-03-18 12:36:27 -07001924 }
1925
1926 if (command.function()) {
1927 if (partitioned_file_is_partitioned(param.image_file)) {
1928 ERROR("Failed while operating on '%s' region!\n",
1929 param.region_name);
1930 ERROR("The image will be left unmodified.\n");
1931 }
1932 return 1;
1933 }
1934
1935 return 0;
1936}
1937
Stefan Reinauer63217582012-10-29 16:52:36 -07001938static void usage(char *name)
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001939{
1940 printf
Stefan Reinauer07040582010-04-24 21:24:06 +00001941 ("cbfstool: Management utility for CBFS formatted ROM images\n\n"
Stefan Reinauer63217582012-10-29 16:52:36 -07001942 "USAGE:\n" " %s [-h]\n"
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08001943 " %s FILE COMMAND [-v] [PARAMETERS]...\n\n" "OPTIONs:\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001944 " -H header_offset Do not search for header; use this offset*\n"
Sol Boucher67d59982015-05-07 02:39:22 -07001945 " -T Output top-aligned memory address\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001946 " -u Accept short data; fill upward/from bottom\n"
1947 " -d Accept short data; fill downward/from top\n"
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02001948 " -F Force action\n"
Patrick Georgi5b6bdcc2016-10-13 18:24:10 +02001949 " -g Generate position and alignment arguments\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08001950 " -U Unprocessed; don't decompress or make ELF\n"
Maximilian Brune60c6a5a2023-03-06 02:40:58 +01001951 " -v Provide verbose output (-v=INFO -vv=DEBUG output)\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001952 " -h Display this help message\n\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001953 " --ext-win-base Base of extended decode window in host address\n"
1954 " space(x86 only)\n"
1955 " --ext-win-size Size of extended decode window in host address\n"
1956 " space(x86 only)\n"
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00001957 "COMMANDs:\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001958 " add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \\\n"
Furquan Shaikh61c1a052016-05-09 11:53:34 -07001959 " [-c compression] [-b base-address | -a alignment] \\\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301960 " [-p padding size] [-y|--xip if TYPE is FSP] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001961 " [-j topswap-size] (Intel CPUs only) [--ibb] \\\n"
1962 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001963 "Add a component\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301964 " "
1965 " -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 \n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001966 " add-payload [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001967 " [-c compression] [-b base-address] \\\n"
1968 " (linux specific: [-C cmdline] [-I initrd]) "
Stefan Reinauer63217582012-10-29 16:52:36 -07001969 "Add a payload to the ROM\n"
Patrick Georgi89f20342015-10-01 15:54:04 +02001970 " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001971 " [-c compression] [-b base] [-S section-to-ignore] \\\n"
Arthur Heymansc78521b32021-06-16 17:16:05 +02001972 " [-a alignment] [-Q|--pow2page] \\\n"
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08001973 " [-y|--xip] [--ibb] \\\n"
1974 " [--ext-win-base win-base --ext-win-size win-size] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001975 "Add a stage to the ROM\n"
Martin Roth0eae3642015-12-04 11:27:11 -07001976 " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
1977 " [-A hash] -l load-address -e entry-point \\\n"
1978 " [-c compression] [-b base] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001979 "Add a 32bit flat mode binary\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001980 " add-int [-r image,regions] -i INTEGER -n NAME [-b base] "
Peter Stuge3bfd5b82013-07-09 19:39:13 +02001981 "Add a raw 64-bit integer value\n"
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05301982 " add-master-header [-r image,regions] \\ \n"
1983 " [-j topswap-size] (Intel CPUs only) "
Patrick Georgi59e52b92015-09-10 15:28:27 +02001984 "Add a legacy CBFS master header\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001985 " remove [-r image,regions] -n NAME "
Stefan Reinauer63217582012-10-29 16:52:36 -07001986 "Remove a component\n"
Aaron Durbin71c60ca2016-01-26 17:08:56 -06001987 " compact -r image,regions "
1988 "Defragment CBFS image.\n"
Patrick Georgibd0bb232015-11-20 21:48:18 +01001989 " copy -r image,regions -R source-region "
1990 "Create a copy (duplicate) cbfs instance in fmap\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001991 " create -m ARCH -s size [-b bootblock offset] \\\n"
Patrick Georgi83c2d122015-08-26 10:40:00 +02001992 " [-o CBFS offset] [-H header offset] [-B bootblock] "
Sol Boucher67a0a862015-03-18 12:36:27 -07001993 "Create a legacy ROM file with CBFS master header*\n"
1994 " create -M flashmap [-r list,of,regions,containing,cbfses] "
1995 "Create a new-style partitioned firmware image\n"
Sol Boucher67a0a862015-03-18 12:36:27 -07001996 " layout [-w] "
1997 "List mutable (or, with -w, readable) image regions\n"
Julius Werner4bfbabd2020-05-06 17:27:02 -07001998 " print [-r image,regions] [-k] "
Stefan Reinauer63217582012-10-29 16:52:36 -07001999 "Show the contents of the ROM\n"
Joel Kitching21fdd892018-08-09 17:49:52 +08002000 " extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] "
2001 "Extracts a file from ROM\n"
Patrick Georgid9edb182016-12-06 18:55:26 +01002002 " write [-F] -r image,regions -f file [-u | -d] [-i int] "
Sol Boucher67a0a862015-03-18 12:36:27 -07002003 "Write file into same-size [or larger] raw region\n"
2004 " read [-r fmap-region] -f file "
2005 "Extract raw region contents into binary file\n"
Patrick Georgi12631a42017-09-20 11:59:18 +02002006 " truncate [-r fmap-region] "
2007 "Truncate CBFS and print new size on stdout\n"
Patrick Georgi5d982d72017-09-19 14:39:58 +02002008 " expand [-r fmap-region] "
2009 "Expand CBFS to span entire region\n"
Sol Boucher0e539312015-03-05 15:38:03 -08002010 "OFFSETs:\n"
Sol Boucher67d59982015-05-07 02:39:22 -07002011 " Numbers accompanying -b, -H, and -o switches* may be provided\n"
2012 " in two possible formats: if their value is greater than\n"
Sol Boucher0e539312015-03-05 15:38:03 -08002013 " 0x80000000, they are interpreted as a top-aligned x86 memory\n"
2014 " address; otherwise, they are treated as an offset into flash.\n"
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02002015 "ARCHes:\n", name, name
Stefan Reinauer63217582012-10-29 16:52:36 -07002016 );
Jonathan Neuschäferfbc66b92018-04-08 15:05:09 +02002017 print_supported_architectures();
2018
2019 printf("TYPEs:\n");
Stefan Reinauer07040582010-04-24 21:24:06 +00002020 print_supported_filetypes();
Sol Boucher67a0a862015-03-18 12:36:27 -07002021 printf(
2022 "\n* Note that these actions and switches are only valid when\n"
2023 " working with legacy images whose structure is described\n"
2024 " primarily by a CBFS master header. New-style images, in\n"
2025 " contrast, exclusively make use of an FMAP to describe their\n"
2026 " layout: this must minimally contain an '%s' section\n"
2027 " specifying the location of this FMAP itself and a '%s'\n"
2028 " section describing the primary CBFS. It should also be noted\n"
2029 " that, when working with such images, the -F and -r switches\n"
Julius Werner0fd072d2021-12-13 10:28:29 -08002030 " default to '%s' for convenience, and the -b switch becomes\n"
Sol Boucher67d59982015-05-07 02:39:22 -07002031 " relative to the selected CBFS region's lowest address.\n"
2032 " The one exception to this rule is the top-aligned address,\n"
2033 " which is always relative to the end of the entire image\n"
2034 " rather than relative to the local region; this is true for\n"
2035 " for both input (sufficiently large) and output (-T) data.\n",
Sol Boucher67a0a862015-03-18 12:36:27 -07002036 SECTION_NAME_FMAP, SECTION_NAME_PRIMARY_CBFS,
2037 SECTION_NAME_PRIMARY_CBFS
2038 );
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002039}
2040
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002041static bool valid_opt(size_t i, int c)
2042{
2043 /* Check if it is one of the optstrings supported by the command. */
2044 if (strchr(commands[i].optstring, c))
2045 return true;
2046
2047 /*
2048 * Check if it is one of the non-ASCII characters. Currently, the
2049 * non-ASCII characters are only checked against the valid list
2050 * irrespective of the command.
2051 */
2052 if (c >= LONGOPT_START && c < LONGOPT_END)
2053 return true;
2054
2055 return false;
2056}
2057
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002058int main(int argc, char **argv)
2059{
Mathias Krause41c229c2012-07-17 21:17:15 +02002060 size_t i;
Stefan Reinauer63217582012-10-29 16:52:36 -07002061 int c;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002062
2063 if (argc < 3) {
Stefan Reinauer63217582012-10-29 16:52:36 -07002064 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002065 return 1;
2066 }
2067
Sol Bouchere3260a02015-03-25 13:40:08 -07002068 char *image_name = argv[1];
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002069 char *cmd = argv[2];
Stefan Reinauer63217582012-10-29 16:52:36 -07002070 optind += 2;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002071
2072 for (i = 0; i < ARRAY_SIZE(commands); i++) {
2073 if (strcmp(cmd, commands[i].name) != 0)
2074 continue;
Stefan Reinauer63217582012-10-29 16:52:36 -07002075
2076 while (1) {
2077 char *suffix = NULL;
2078 int option_index = 0;
2079
2080 c = getopt_long(argc, argv, commands[i].optstring,
2081 long_options, &option_index);
Nico Huber54073102016-08-01 23:18:29 +02002082 if (c == -1) {
2083 if (optind < argc) {
2084 ERROR("%s: excessive argument -- '%s'"
2085 "\n", argv[0], argv[optind]);
2086 return 1;
2087 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002088 break;
Nico Huber54073102016-08-01 23:18:29 +02002089 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002090
Joel Kitching672d5ad2018-10-11 17:01:30 +08002091 /* Filter out illegal long options */
Furquan Shaikhc42cf912020-04-14 00:14:44 -07002092 if (!valid_opt(i, c)) {
2093 ERROR("%s: invalid option -- '%d'\n",
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002094 argv[0], c);
Stefan Reinauer63217582012-10-29 16:52:36 -07002095 c = '?';
2096 }
2097
2098 switch(c) {
2099 case 'n':
Hung-Te Lind1739622013-01-28 14:23:49 +08002100 param.name = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002101 break;
2102 case 't':
2103 if (intfiletype(optarg) != ((uint64_t) - 1))
Hung-Te Lind1739622013-01-28 14:23:49 +08002104 param.type = intfiletype(optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002105 else
Hung-Te Lind1739622013-01-28 14:23:49 +08002106 param.type = strtoul(optarg, NULL, 0);
2107 if (param.type == 0)
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002108 WARN("Unknown type '%s' ignored\n",
Stefan Reinauer63217582012-10-29 16:52:36 -07002109 optarg);
2110 break;
Sol Boucherec424862015-05-07 21:00:05 -07002111 case 'c': {
Patrick Georgiecaa5702017-01-11 18:38:11 +01002112 if (strcmp(optarg, "precompression") == 0) {
2113 param.precompression = 1;
2114 break;
2115 }
Sol Boucherec424862015-05-07 21:00:05 -07002116 int algo = cbfs_parse_comp_algo(optarg);
2117 if (algo >= 0)
2118 param.compression = algo;
Stefan Reinauer63217582012-10-29 16:52:36 -07002119 else
Sol Boucherec424862015-05-07 21:00:05 -07002120 WARN("Unknown compression '%s' ignored.\n",
2121 optarg);
Stefan Reinauer63217582012-10-29 16:52:36 -07002122 break;
Sol Boucherec424862015-05-07 21:00:05 -07002123 }
Patrick Georgi89f20342015-10-01 15:54:04 +02002124 case 'A': {
Julius Wernerd4775652020-03-13 16:43:34 -07002125 if (!vb2_lookup_hash_alg(optarg, &param.hash)) {
Patrick Georgi89f20342015-10-01 15:54:04 +02002126 ERROR("Unknown hash algorithm '%s'.\n",
2127 optarg);
2128 return 1;
2129 }
2130 break;
2131 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002132 case 'M':
2133 param.fmap = optarg;
2134 break;
2135 case 'r':
2136 param.region_name = optarg;
2137 break;
Patrick Georgibd0bb232015-11-20 21:48:18 +01002138 case 'R':
2139 param.source_region = optarg;
2140 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002141 case 'b':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002142 param.baseaddress_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002143 if (!*optarg || (suffix && *suffix)) {
2144 ERROR("Invalid base address '%s'.\n",
2145 optarg);
2146 return 1;
2147 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002148 // baseaddress may be zero on non-x86, so we
2149 // need an explicit "baseaddress_assigned".
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002150 param.baseaddress_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002151 break;
2152 case 'l':
Nico Huber9ade7172016-08-01 21:37:42 +02002153 param.loadaddress = strtoul(optarg, &suffix, 0);
2154 if (!*optarg || (suffix && *suffix)) {
2155 ERROR("Invalid load address '%s'.\n",
2156 optarg);
2157 return 1;
2158 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002159 break;
2160 case 'e':
Nico Huber9ade7172016-08-01 21:37:42 +02002161 param.entrypoint = strtoul(optarg, &suffix, 0);
2162 if (!*optarg || (suffix && *suffix)) {
2163 ERROR("Invalid entry point '%s'.\n",
2164 optarg);
2165 return 1;
2166 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002167 break;
2168 case 's':
Hung-Te Lind1739622013-01-28 14:23:49 +08002169 param.size = strtoul(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002170 if (!*optarg) {
2171 ERROR("Empty size specified.\n");
2172 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002173 }
Nico Huber9ade7172016-08-01 21:37:42 +02002174 switch (tolower((int)suffix[0])) {
2175 case 'k':
2176 param.size *= 1024;
2177 break;
2178 case 'm':
Hung-Te Lind1739622013-01-28 14:23:49 +08002179 param.size *= 1024 * 1024;
Nico Huber9ade7172016-08-01 21:37:42 +02002180 break;
2181 case '\0':
2182 break;
2183 default:
2184 ERROR("Invalid suffix for size '%s'.\n",
2185 optarg);
2186 return 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002187 }
Patrick Georgie887ca52014-08-09 17:44:39 +02002188 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002189 case 'B':
Hung-Te Lind1739622013-01-28 14:23:49 +08002190 param.bootblock = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002191 break;
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002192 case 'H':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002193 param.headeroffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002194 if (!*optarg || (suffix && *suffix)) {
2195 ERROR("Invalid header offset '%s'.\n",
2196 optarg);
2197 return 1;
2198 }
Hung-Te Linf56c73f2013-01-29 09:45:12 +08002199 param.headeroffset_assigned = 1;
2200 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002201 case 'a':
Nico Huber9ade7172016-08-01 21:37:42 +02002202 param.alignment = strtoul(optarg, &suffix, 0);
2203 if (!*optarg || (suffix && *suffix)) {
2204 ERROR("Invalid alignment '%s'.\n",
2205 optarg);
2206 return 1;
2207 }
Stefan Reinauer63217582012-10-29 16:52:36 -07002208 break;
Daisuke Nojiriff906fb2017-10-30 17:38:04 -07002209 case 'p':
2210 param.padding = strtoul(optarg, &suffix, 0);
2211 if (!*optarg || (suffix && *suffix)) {
2212 ERROR("Invalid pad size '%s'.\n",
2213 optarg);
2214 return 1;
2215 }
2216 break;
Kyösti Mälkkic36469e2020-05-28 23:12:02 +03002217 case 'Q':
2218 param.force_pow2_pagesize = 1;
2219 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002220 case 'o':
Furquan Shaikh6b6e9b52020-11-20 16:52:03 -08002221 param.cbfsoffset_input = strtoll(optarg, &suffix, 0);
Nico Huber9ade7172016-08-01 21:37:42 +02002222 if (!*optarg || (suffix && *suffix)) {
2223 ERROR("Invalid cbfs offset '%s'.\n",
2224 optarg);
2225 return 1;
2226 }
Julius Wernerefcee762014-11-10 13:14:24 -08002227 param.cbfsoffset_assigned = 1;
Stefan Reinauer63217582012-10-29 16:52:36 -07002228 break;
2229 case 'f':
Hung-Te Lind1739622013-01-28 14:23:49 +08002230 param.filename = optarg;
Stefan Reinauer63217582012-10-29 16:52:36 -07002231 break;
Patrick Georgi01fbc3a2016-10-12 16:46:13 +02002232 case 'F':
2233 param.force = 1;
2234 break;
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002235 case 'i':
Nico Huber9ade7172016-08-01 21:37:42 +02002236 param.u64val = strtoull(optarg, &suffix, 0);
Patrick Georgi16b3e4b2016-12-12 18:38:01 +01002237 param.u64val_assigned = 1;
Nico Huber9ade7172016-08-01 21:37:42 +02002238 if (!*optarg || (suffix && *suffix)) {
2239 ERROR("Invalid int parameter '%s'.\n",
2240 optarg);
2241 return 1;
2242 }
Peter Stuge3bfd5b82013-07-09 19:39:13 +02002243 break;
Sol Boucher67a0a862015-03-18 12:36:27 -07002244 case 'u':
2245 param.fill_partial_upward = true;
2246 break;
2247 case 'd':
2248 param.fill_partial_downward = true;
2249 break;
2250 case 'w':
2251 param.show_immutable = true;
Hung-Te Lin215d1d72013-01-29 03:46:02 +08002252 break;
Rizwan Qureshi1dc188f2018-05-29 14:48:14 +05302253 case 'j':
2254 param.topswap_size = strtol(optarg, NULL, 0);
2255 if (!is_valid_topswap())
2256 return 1;
2257 break;
Rizwan Qureshic1072f22018-06-04 23:02:46 +05302258 case 'q':
2259 param.ucode_region = optarg;
2260 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002261 case 'v':
2262 verbose++;
2263 break;
David Hendricks90ca3b62012-11-16 14:48:22 -08002264 case 'm':
Alexandru Gagniuc35850ae2014-02-02 22:37:28 -06002265 param.arch = string_to_arch(optarg);
David Hendricks90ca3b62012-11-16 14:48:22 -08002266 break;
Patrick Georgide36d332013-08-27 20:22:21 +02002267 case 'I':
2268 param.initrd = optarg;
2269 break;
2270 case 'C':
2271 param.cmdline = optarg;
2272 break;
Furquan Shaikh405304a2014-10-30 11:44:20 -07002273 case 'S':
2274 param.ignore_section = optarg;
2275 break;
Aaron Durbin4be16742015-09-15 17:00:23 -05002276 case 'y':
2277 param.stage_xip = true;
2278 break;
Werner Zehe9995f12016-01-14 13:22:37 +01002279 case 'g':
2280 param.autogen_attr = true;
2281 break;
Aaron Durbin5dc628a2016-01-26 15:35:34 -06002282 case 'k':
2283 param.machine_parseable = true;
2284 break;
Joel Kitching21fdd892018-08-09 17:49:52 +08002285 case 'U':
2286 param.unprocessed = true;
2287 break;
Philipp Deppenwiese7ba58712018-11-20 13:54:49 +01002288 case LONGOPT_IBB:
2289 param.ibb = true;
2290 break;
Arthur Heymans34a7e662022-10-06 09:42:15 +02002291 case LONGOPT_MMAP:
2292 if (decode_mmap_arg(optarg))
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08002293 return 1;
Furquan Shaikh0ae389c2020-11-21 10:35:10 -08002294 break;
Stefan Reinauer63217582012-10-29 16:52:36 -07002295 case 'h':
2296 case '?':
2297 usage(argv[0]);
2298 return 1;
2299 default:
2300 break;
2301 }
2302 }
2303
Sol Bouchere3260a02015-03-25 13:40:08 -07002304 if (commands[i].function == cbfs_create) {
Sol Boucher67a0a862015-03-18 12:36:27 -07002305 if (param.fmap) {
2306 struct buffer flashmap;
2307 if (buffer_from_file(&flashmap, param.fmap))
2308 return 1;
2309 param.image_file = partitioned_file_create(
2310 image_name, &flashmap);
2311 buffer_delete(&flashmap);
2312 } else if (param.size) {
2313 param.image_file = partitioned_file_create_flat(
2314 image_name, param.size);
2315 } else {
2316 ERROR("You need to specify a valid -M/--flashmap or -s/--size.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002317 return 1;
2318 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002319 } else {
Vadim Bendebury75599462015-12-09 09:39:31 -08002320 bool write_access = commands[i].modifies_region;
2321
Sol Bouchere3260a02015-03-25 13:40:08 -07002322 param.image_file =
Vadim Bendebury75599462015-12-09 09:39:31 -08002323 partitioned_file_reopen(image_name,
2324 write_access);
Sol Bouchere3260a02015-03-25 13:40:08 -07002325 }
2326 if (!param.image_file)
2327 return 1;
2328
Sol Boucher67a0a862015-03-18 12:36:27 -07002329 unsigned num_regions = 1;
2330 for (const char *list = strchr(param.region_name, ','); list;
2331 list = strchr(list + 1, ','))
2332 ++num_regions;
2333
Sol Bouchere3260a02015-03-25 13:40:08 -07002334 // If the action needs to read an image region, as indicated by
2335 // having accesses_region set in its command struct, that
2336 // region's buffer struct will be stored here and the client
2337 // will receive a pointer to it via param.image_region. It
2338 // need not write the buffer back to the image file itself,
2339 // since this behavior can be requested via its modifies_region
2340 // field. Additionally, it should never free the region buffer,
2341 // as that is performed automatically once it completes.
Sol Boucher67a0a862015-03-18 12:36:27 -07002342 struct buffer image_regions[num_regions];
2343 memset(image_regions, 0, sizeof(image_regions));
Sol Bouchere3260a02015-03-25 13:40:08 -07002344
Sol Boucher67a0a862015-03-18 12:36:27 -07002345 bool seen_primary_cbfs = false;
2346 char region_name_scratch[strlen(param.region_name) + 1];
2347 strcpy(region_name_scratch, param.region_name);
2348 param.region_name = strtok(region_name_scratch, ",");
2349 for (unsigned region = 0; region < num_regions; ++region) {
2350 if (!param.region_name) {
2351 ERROR("Encountered illegal degenerate region name in -r list\n");
2352 ERROR("The image will be left unmodified.\n");
Sol Bouchere3260a02015-03-25 13:40:08 -07002353 partitioned_file_close(param.image_file);
2354 return 1;
2355 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002356
Sol Boucher67a0a862015-03-18 12:36:27 -07002357 if (strcmp(param.region_name, SECTION_NAME_PRIMARY_CBFS)
2358 == 0)
2359 seen_primary_cbfs = true;
Sol Bouchere3260a02015-03-25 13:40:08 -07002360
Sol Boucher67a0a862015-03-18 12:36:27 -07002361 param.image_region = image_regions + region;
2362 if (dispatch_command(commands[i])) {
Sol Bouchere3260a02015-03-25 13:40:08 -07002363 partitioned_file_close(param.image_file);
2364 return 1;
2365 }
Sol Boucher67a0a862015-03-18 12:36:27 -07002366
2367 param.region_name = strtok(NULL, ",");
2368 }
2369
2370 if (commands[i].function == cbfs_create && !seen_primary_cbfs) {
2371 ERROR("The creation -r list must include the mandatory '%s' section.\n",
2372 SECTION_NAME_PRIMARY_CBFS);
2373 ERROR("The image will be left unmodified.\n");
2374 partitioned_file_close(param.image_file);
2375 return 1;
2376 }
2377
2378 if (commands[i].modifies_region) {
2379 assert(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002380 for (unsigned region = 0; region < num_regions;
2381 ++region) {
2382
2383 if (!partitioned_file_write_region(
2384 param.image_file,
2385 image_regions + region)) {
2386 partitioned_file_close(
2387 param.image_file);
2388 return 1;
2389 }
2390 }
Sol Bouchere3260a02015-03-25 13:40:08 -07002391 }
2392
2393 partitioned_file_close(param.image_file);
Sol Boucher67a0a862015-03-18 12:36:27 -07002394 return 0;
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002395 }
2396
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +08002397 ERROR("Unknown command '%s'.\n", cmd);
Stefan Reinauer63217582012-10-29 16:52:36 -07002398 usage(argv[0]);
Stefan Reinauer3fec29c2009-09-22 15:58:19 +00002399 return 1;
2400}