1 /* cbfstool, CLI utility for creating rmodules */
2 /* SPDX-License-Identifier: GPL-2.0-only */
11 #include "cbfs_image.h"
12 #include "partitioned_file.h"
15 /* Global variables */
16 partitioned_file_t
*image_file
;
18 static const char *optstring
= "H:j:f:r:d:t:n:s:cAaDvh?";
19 static struct option long_options
[] = {
20 {"file", required_argument
, 0, 'f' },
21 {"region", required_argument
, 0, 'r' },
22 {"add-cbfs-entry", no_argument
, 0, 'a' },
23 {"add-region", no_argument
, 0, 'A' },
24 {"del-entry", required_argument
, 0, 'd' },
25 {"clear-table", no_argument
, 0, 'c' },
26 {"fit-type", required_argument
, 0, 't' },
27 {"cbfs-filename", required_argument
, 0, 'n' },
28 {"max-table-size", required_argument
, 0, 's' },
29 {"topswap-size", required_argument
, 0, 'j' },
30 {"dump", no_argument
, 0, 'D' },
31 {"verbose", no_argument
, 0, 'v' },
32 {"help", no_argument
, 0, 'h' },
33 {"header-offset", required_argument
, 0, 'H' },
37 static void usage(const char *name
)
40 "ifittool: utility for modifying Intel Firmware Interface Table\n\n"
41 "USAGE: %s [-h] [-H] [-v] [-D] [-c] <-f|--file name> <-s|--max-table-size size> <-r|--region fmap region> OPERATION\n"
43 "\t\t-a|--add-entry : Add a CBFS file as new entry to FIT\n"
44 "\t\t-A|--add-region : Add region as new entry to FIT (for microcodes)\n"
45 "\t\t-d|--del-entry number : Delete existing <number> entry\n"
46 "\t\t-t|--fit-type : Type of new entry\n"
47 "\t\t-n|--name : The CBFS filename or region to add to table\n"
48 "\tOPTIONAL ARGUMENTS:\n"
49 "\t\t-h|--help : Display this text\n"
50 "\t\t-H|--header-offset : Do not search for header, use this offset\n"
51 "\t\t-v|--verbose : Be verbose\n"
52 "\t\t-D|--dump : Dump FIT table (at end of operation)\n"
53 "\t\t-c|--clear-table : Remove all existing entries (do not update)\n"
54 "\t\t-j|--topswap-size : Use second FIT table if non zero\n"
55 "\tREQUIRED ARGUMENTS:\n"
56 "\t\t-f|--file name : The file containing the CBFS\n"
57 "\t\t-s|--max-table-size : The number of possible FIT entries in table\n"
58 "\t\t-r|--region : The FMAP region to operate on\n"
62 static int is_valid_topswap(size_t topswap_size
)
64 switch (topswap_size
) {
72 ERROR("Invalid topswap_size %zd\n", topswap_size
);
73 ERROR("topswap can be 64K|128K|256K|512K|1M\n");
80 * Converts between offsets from the start of the specified image region and
81 * "top-aligned" offsets from the top of the entire boot media. See comment
82 * below for convert_to_from_top_aligned() about forming addresses.
84 static unsigned int convert_to_from_absolute_top_aligned(
85 const struct buffer
*region
, unsigned int offset
)
89 size_t image_size
= partitioned_file_total_size(image_file
);
91 return image_size
- region
->offset
- offset
;
95 * Converts between offsets from the start of the specified image region and
96 * "top-aligned" offsets from the top of the image region. Works in either
97 * direction: pass in one type of offset and receive the other type.
98 * N.B. A top-aligned offset is always a positive number, and should not be
99 * confused with a top-aligned *address*, which is its arithmetic inverse. */
100 static unsigned int convert_to_from_top_aligned(const struct buffer
*region
,
105 /* Cover the situation where a negative base address is given by the
106 * user. Callers of this function negate it, so it'll be a positive
107 * number smaller than the region.
109 if ((offset
> 0) && (offset
< region
->size
))
110 return region
->size
- offset
;
112 return convert_to_from_absolute_top_aligned(region
, offset
);
116 * Get a pointer from an offset. This function assumes the ROM is located
117 * in the host address space at [4G - romsize -> 4G). It also assume all
118 * pointers have values within this address range.
120 static inline uint32_t offset_to_ptr(fit_offset_converter_t helper
,
121 const struct buffer
*region
, int offset
)
123 return -helper(region
, offset
);
134 int main(int argc
, char *argv
[])
137 const char *input_file
= NULL
;
138 const char *name
= NULL
;
139 const char *region_name
= NULL
;
140 enum fit_operation op
= NO_OP
;
141 bool dump
= false, clear_table
= false;
142 size_t max_table_size
= 0;
143 size_t table_entry
= 0;
145 size_t topswap_size
= 0;
146 enum fit_type fit_type
= 0;
147 uint32_t headeroffset
= ~0u;
160 c
= getopt_long(argc
, argv
, optstring
, long_options
, &optindex
);
171 ERROR("specified multiple actions at once\n");
179 ERROR("specified multiple actions at once\n");
187 ERROR("specified multiple actions at once\n");
192 addr
= atoll(optarg
);
199 ERROR("specified multiple actions at once\n");
204 table_entry
= atoi(optarg
);
213 headeroffset
= strtoul(optarg
, &suffix
, 0);
214 if (!*optarg
|| (suffix
&& *suffix
)) {
215 ERROR("Invalid header offset '%s'.\n", optarg
);
220 topswap_size
= strtol(optarg
, NULL
, 0);
221 if (!is_valid_topswap(topswap_size
))
228 region_name
= optarg
;
231 max_table_size
= atoi(optarg
);
234 fit_type
= atoi(optarg
);
244 if (input_file
== NULL
) {
245 ERROR("No input file given\n");
250 if (op
== ADD_CBFS_OP
|| op
== ADD_REGI_OP
) {
252 ERROR("Adding FIT entry, but no type given\n");
255 } else if (name
== NULL
) {
256 ERROR("Adding FIT entry, but no name set\n");
259 } else if (max_table_size
== 0) {
260 ERROR("Maximum table size not given\n");
265 if (op
== ADD_ADDR_OP
) {
267 ERROR("Adding FIT entry, but no type given\n");
270 } else if (max_table_size
== 0) {
271 ERROR("Maximum table size not given\n");
278 ERROR("Region not given\n");
283 image_file
= partitioned_file_reopen(input_file
,
284 op
!= NO_OP
|| clear_table
);
286 struct buffer image_region
;
288 if (!partitioned_file_read_region(&image_region
, image_file
,
290 partitioned_file_close(image_file
);
291 ERROR("The image will be left unmodified.\n");
295 struct buffer bootblock
;
296 // The bootblock is part of the CBFS on x86
297 buffer_clone(&bootblock
, &image_region
);
299 struct cbfs_image image
;
300 if (cbfs_image_from_buffer(&image
, &image_region
, headeroffset
)) {
301 partitioned_file_close(image_file
);
305 struct fit_table
*fit
= fit_get_table(&bootblock
,
306 convert_to_from_top_aligned
,
309 partitioned_file_close(image_file
);
310 ERROR("FIT not found.\n");
315 if (fit_clear_table(fit
)) {
316 partitioned_file_close(image_file
);
317 ERROR("Failed to clear table.\n");
325 struct buffer region
;
327 if (partitioned_file_read_region(®ion
, image_file
, name
)) {
328 addr
= -convert_to_from_top_aligned(®ion
, 0);
330 partitioned_file_close(image_file
);
334 if (fit_add_entry(fit
, addr
, 0, fit_type
, max_table_size
)) {
335 partitioned_file_close(image_file
);
336 ERROR("Adding type %u FIT entry\n", fit_type
);
343 if (fit_type
== FIT_TYPE_MICROCODE
) {
344 if (fit_add_microcode_file(fit
, &image
, name
,
345 convert_to_from_top_aligned
,
350 uint32_t offset
, len
;
351 struct cbfs_file
*cbfs_file
;
353 cbfs_file
= cbfs_get_entry(&image
, name
);
355 partitioned_file_close(image_file
);
356 ERROR("%s not found in CBFS.\n", name
);
360 len
= ntohl(cbfs_file
->len
);
361 offset
= offset_to_ptr(convert_to_from_top_aligned
,
363 cbfs_get_entry_addr(&image
, cbfs_file
) +
364 ntohl(cbfs_file
->offset
));
367 if (fit_add_entry(fit
, offset
, len
, fit_type
,
369 partitioned_file_close(image_file
);
370 ERROR("Adding type %u FIT entry\n", fit_type
);
378 if (fit_add_entry(fit
, addr
, 0, fit_type
, max_table_size
)) {
379 partitioned_file_close(image_file
);
380 ERROR("Adding type %u FIT entry\n", fit_type
);
387 if (fit_delete_entry(fit
, table_entry
)) {
388 partitioned_file_close(image_file
);
389 ERROR("Deleting FIT entry %zu failed\n", table_entry
);
399 if (op
!= NO_OP
|| clear_table
) {
400 if (!partitioned_file_write_region(image_file
, &bootblock
)) {
401 ERROR("Failed to write changes to disk.\n");
402 partitioned_file_close(image_file
);
409 partitioned_file_close(image_file
);
414 partitioned_file_close(image_file
);