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:cAaDvhF?";
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 {"set-fit-pointer", no_argument
, 0, 'F' },
27 {"fit-type", required_argument
, 0, 't' },
28 {"cbfs-filename", required_argument
, 0, 'n' },
29 {"max-table-size", required_argument
, 0, 's' },
30 {"topswap-size", required_argument
, 0, 'j' },
31 {"dump", no_argument
, 0, 'D' },
32 {"verbose", no_argument
, 0, 'v' },
33 {"help", no_argument
, 0, 'h' },
34 {"header-offset", required_argument
, 0, 'H' },
38 static void usage(const char *name
)
41 "ifittool: utility for modifying Intel Firmware Interface Table\n\n"
42 "USAGE: %s [-h] [-H] [-v] [-D] [-c] <-f|--file name> <-s|--max-table-size size> <-r|--region fmap region> OPERATION\n"
44 "\t\t-a|--add-entry : Add a CBFS file as new entry to FIT\n"
45 "\t\t-A|--add-region : Add region as new entry to FIT (for microcodes)\n"
46 "\t\t-d|--del-entry number : Delete existing <number> entry\n"
47 "\t\t-F|--set-fit-pointer : Set the FIT pointer to a CBFS file\n"
48 "\t\t-t|--fit-type : Type of new entry\n"
49 "\t\t-n|--name : The CBFS filename or region to add to table\n"
50 "\tOPTIONAL ARGUMENTS:\n"
51 "\t\t-h|--help : Display this text\n"
52 "\t\t-H|--header-offset : Do not search for header, use this offset\n"
53 "\t\t-v|--verbose : Be verbose (-v=INFO -vv=DEBUG output)\n"
54 "\t\t-D|--dump : Dump FIT table (at end of operation)\n"
55 "\t\t-c|--clear-table : Remove all existing entries (do not update)\n"
56 "\t\t-j|--topswap-size : Use second FIT table if non zero\n"
57 "\tREQUIRED ARGUMENTS:\n"
58 "\t\t-f|--file name : The file containing the CBFS\n"
59 "\t\t-s|--max-table-size : The number of possible FIT entries in table\n"
60 "\t\t-r|--region : The FMAP region to operate on\n"
64 static int is_valid_topswap(size_t topswap_size
)
66 switch (topswap_size
) {
74 ERROR("Invalid topswap_size %zd\n", topswap_size
);
75 ERROR("topswap can be 64K|128K|256K|512K|1M\n");
82 * Converts between offsets from the start of the specified image region and
83 * "top-aligned" offsets from the top of the entire boot media. See comment
84 * below for convert_to_from_top_aligned() about forming addresses.
86 static unsigned int convert_to_from_absolute_top_aligned(
87 const struct buffer
*region
, unsigned int offset
)
91 size_t image_size
= partitioned_file_total_size(image_file
);
93 return image_size
- region
->offset
- offset
;
97 * Converts between offsets from the start of the specified image region and
98 * "top-aligned" offsets from the top of the image region. Works in either
99 * direction: pass in one type of offset and receive the other type.
100 * N.B. A top-aligned offset is always a positive number, and should not be
101 * confused with a top-aligned *address*, which is its arithmetic inverse. */
102 static unsigned int convert_to_from_top_aligned(const struct buffer
*region
,
107 /* Cover the situation where a negative base address is given by the
108 * user. Callers of this function negate it, so it'll be a positive
109 * number smaller than the region.
111 if ((offset
> 0) && (offset
< region
->size
))
112 return region
->size
- offset
;
114 return convert_to_from_absolute_top_aligned(region
, offset
);
118 * Get a pointer from an offset. This function assumes the ROM is located
119 * in the host address space at [4G - romsize -> 4G). It also assume all
120 * pointers have values within this address range.
122 static inline uint32_t offset_to_ptr(fit_offset_converter_t helper
,
123 const struct buffer
*region
, int offset
)
125 return -helper(region
, offset
);
136 int main(int argc
, char *argv
[])
139 const char *input_file
= NULL
;
140 const char *name
= NULL
;
141 const char *region_name
= NULL
;
142 enum fit_operation op
= NO_OP
;
143 bool dump
= false, clear_table
= false;
144 size_t max_table_size
= 0;
145 size_t table_entry
= 0;
147 size_t topswap_size
= 0;
148 enum fit_type fit_type
= 0;
149 uint32_t headeroffset
= HEADER_OFFSET_UNKNOWN
;
162 c
= getopt_long(argc
, argv
, optstring
, long_options
, &optindex
);
173 ERROR("specified multiple actions at once\n");
181 ERROR("specified multiple actions at once\n");
192 ERROR("specified multiple actions at once\n");
197 table_entry
= atoi(optarg
);
207 ERROR("specified multiple actions at once\n");
214 headeroffset
= strtoul(optarg
, &suffix
, 0);
215 if (!*optarg
|| (suffix
&& *suffix
)) {
216 ERROR("Invalid header offset '%s'.\n", optarg
);
221 topswap_size
= strtol(optarg
, NULL
, 0);
222 if (!is_valid_topswap(topswap_size
))
229 region_name
= optarg
;
232 max_table_size
= atoi(optarg
);
235 fit_type
= atoi(optarg
);
245 if (input_file
== NULL
) {
246 ERROR("No input file given\n");
251 if (op
== ADD_CBFS_OP
|| op
== ADD_REGI_OP
) {
253 ERROR("Adding FIT entry, but no type given\n");
256 } else if (name
== NULL
) {
257 ERROR("Adding FIT entry, but no name set\n");
260 } else if (max_table_size
== 0) {
261 ERROR("Maximum table size not given\n");
267 if (op
== SET_FIT_PTR_OP
) {
269 ERROR("Adding FIT entry, but no name set\n");
276 ERROR("Region not given\n");
281 image_file
= partitioned_file_reopen(input_file
,
282 op
!= NO_OP
|| clear_table
);
284 struct buffer image_region
;
286 if (!partitioned_file_read_region(&image_region
, image_file
,
288 partitioned_file_close(image_file
);
289 ERROR("The image will be left unmodified.\n");
293 struct buffer bootblock
;
294 // The bootblock is part of the CBFS on x86
295 buffer_clone(&bootblock
, &image_region
);
297 struct cbfs_image image
;
298 if (cbfs_image_from_buffer(&image
, &image_region
, headeroffset
)) {
299 partitioned_file_close(image_file
);
303 struct fit_table
*fit
= NULL
;
304 if (op
!= SET_FIT_PTR_OP
) {
305 fit
= fit_get_table(&bootblock
, convert_to_from_top_aligned
, topswap_size
);
307 partitioned_file_close(image_file
);
308 ERROR("FIT not found.\n");
312 if (fit_clear_table(fit
)) {
313 partitioned_file_close(image_file
);
314 ERROR("Failed to clear table.\n");
323 struct buffer region
;
325 if (partitioned_file_read_region(®ion
, image_file
, name
)) {
326 addr
= -convert_to_from_top_aligned(®ion
, 0);
328 partitioned_file_close(image_file
);
332 if (fit_add_entry(fit
, addr
, 0, fit_type
, max_table_size
)) {
333 partitioned_file_close(image_file
);
334 ERROR("Adding type %u FIT entry\n", fit_type
);
341 if (fit_type
== FIT_TYPE_MICROCODE
) {
342 if (fit_add_microcode_file(fit
, &image
, name
,
343 convert_to_from_top_aligned
,
348 uint32_t offset
, len
;
349 struct cbfs_file
*cbfs_file
;
351 cbfs_file
= cbfs_get_entry(&image
, name
);
353 partitioned_file_close(image_file
);
354 ERROR("%s not found in CBFS.\n", name
);
358 len
= be32toh(cbfs_file
->len
);
359 offset
= offset_to_ptr(convert_to_from_top_aligned
,
361 cbfs_get_entry_addr(&image
, cbfs_file
) +
362 be32toh(cbfs_file
->offset
));
365 if (fit_add_entry(fit
, offset
, len
, fit_type
,
367 partitioned_file_close(image_file
);
368 ERROR("Adding type %u FIT entry\n", fit_type
);
376 uint32_t fit_address
;
377 struct cbfs_file
*cbfs_file
= cbfs_get_entry(&image
, name
);
379 partitioned_file_close(image_file
);
380 ERROR("%s not found in CBFS.\n", name
);
384 fit_address
= offset_to_ptr(convert_to_from_top_aligned
, &image
.buffer
,
385 cbfs_get_entry_addr(&image
, cbfs_file
)
386 + be32toh(cbfs_file
->offset
));
389 if (set_fit_pointer(&bootblock
, fit_address
, convert_to_from_top_aligned
,
391 partitioned_file_close(image_file
);
392 ERROR("%s is not a FIT table\n", name
);
395 fit
= fit_get_table(&bootblock
, convert_to_from_top_aligned
, topswap_size
);
398 if (fit_clear_table(fit
)) {
399 partitioned_file_close(image_file
);
400 ERROR("Failed to clear table.\n");
409 if (fit_delete_entry(fit
, table_entry
)) {
410 partitioned_file_close(image_file
);
411 ERROR("Deleting FIT entry %zu failed\n", table_entry
);
421 if (op
!= NO_OP
|| clear_table
) {
422 if (!partitioned_file_write_region(image_file
, &bootblock
)) {
423 ERROR("Failed to write changes to disk.\n");
424 partitioned_file_close(image_file
);
431 partitioned_file_close(image_file
);
436 partitioned_file_close(image_file
);