lib/cbfs: deserialize cbfs_stage objects correctly
[coreboot.git] / util / cbfstool / ifittool.c
blob7e99d4fd4320f9cc3d4feb0961d5f30bca7ebae8
1 /* cbfstool, CLI utility for creating rmodules */
2 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <getopt.h>
10 #include "common.h"
11 #include "cbfs_image.h"
12 #include "partitioned_file.h"
13 #include "fit.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' },
34 {NULL, 0, 0, 0 }
37 static void usage(const char *name)
39 printf(
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"
42 "\tOPERATION:\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"
59 , name);
62 static int is_valid_topswap(size_t topswap_size)
64 switch (topswap_size) {
65 case (64 * KiB):
66 case (128 * KiB):
67 case (256 * KiB):
68 case (512 * KiB):
69 case (1 * MiB):
70 break;
71 default:
72 ERROR("Invalid topswap_size %zd\n", topswap_size);
73 ERROR("topswap can be 64K|128K|256K|512K|1M\n");
74 return 0;
76 return 1;
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)
87 assert(region);
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,
101 unsigned int offset)
103 assert(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);
126 enum fit_operation {
127 NO_OP = 0,
128 ADD_CBFS_OP,
129 ADD_REGI_OP,
130 ADD_ADDR_OP,
131 DEL_OP
134 int main(int argc, char *argv[])
136 int c;
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;
144 uint32_t addr = 0;
145 size_t topswap_size = 0;
146 enum fit_type fit_type = 0;
147 uint32_t headeroffset = ~0u;
149 verbose = 0;
151 if (argc < 4) {
152 usage(argv[0]);
153 return 1;
156 while (1) {
157 int optindex = 0;
158 char *suffix = NULL;
160 c = getopt_long(argc, argv, optstring, long_options, &optindex);
162 if (c == -1)
163 break;
165 switch (c) {
166 case 'h':
167 usage(argv[0]);
168 return 1;
169 case 'a':
170 if (op != NO_OP) {
171 ERROR("specified multiple actions at once\n");
172 usage(argv[0]);
173 return 1;
175 op = ADD_CBFS_OP;
176 break;
177 case 'A':
178 if (op != NO_OP) {
179 ERROR("specified multiple actions at once\n");
180 usage(argv[0]);
181 return 1;
183 op = ADD_REGI_OP;
184 break;
185 case 'x':
186 if (op != NO_OP) {
187 ERROR("specified multiple actions at once\n");
188 usage(argv[0]);
189 return 1;
191 op = ADD_ADDR_OP;
192 addr = atoll(optarg);
193 break;
194 case 'c':
195 clear_table = true;
196 break;
197 case 'd':
198 if (op != NO_OP) {
199 ERROR("specified multiple actions at once\n");
200 usage(argv[0]);
201 return 1;
203 op = DEL_OP;
204 table_entry = atoi(optarg);
205 break;
206 case 'D':
207 dump = true;
208 break;
209 case 'f':
210 input_file = optarg;
211 break;
212 case 'H':
213 headeroffset = strtoul(optarg, &suffix, 0);
214 if (!*optarg || (suffix && *suffix)) {
215 ERROR("Invalid header offset '%s'.\n", optarg);
216 return 1;
218 break;
219 case 'j':
220 topswap_size = strtol(optarg, NULL, 0);
221 if (!is_valid_topswap(topswap_size))
222 return 1;
223 break;
224 case 'n':
225 name = optarg;
226 break;
227 case 'r':
228 region_name = optarg;
229 break;
230 case 's':
231 max_table_size = atoi(optarg);
232 break;
233 case 't':
234 fit_type = atoi(optarg);
235 break;
236 case 'v':
237 verbose++;
238 break;
239 default:
240 break;
244 if (input_file == NULL) {
245 ERROR("No input file given\n");
246 usage(argv[0]);
247 return 1;
250 if (op == ADD_CBFS_OP || op == ADD_REGI_OP) {
251 if (fit_type == 0) {
252 ERROR("Adding FIT entry, but no type given\n");
253 usage(argv[0]);
254 return 1;
255 } else if (name == NULL) {
256 ERROR("Adding FIT entry, but no name set\n");
257 usage(argv[0]);
258 return 1;
259 } else if (max_table_size == 0) {
260 ERROR("Maximum table size not given\n");
261 usage(argv[0]);
262 return 1;
265 if (op == ADD_ADDR_OP) {
266 if (fit_type == 0) {
267 ERROR("Adding FIT entry, but no type given\n");
268 usage(argv[0]);
269 return 1;
270 } else if (max_table_size == 0) {
271 ERROR("Maximum table size not given\n");
272 usage(argv[0]);
273 return 1;
277 if (!region_name) {
278 ERROR("Region not given\n");
279 usage(argv[0]);
280 return 1;
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,
289 region_name)) {
290 partitioned_file_close(image_file);
291 ERROR("The image will be left unmodified.\n");
292 return 1;
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);
302 return 1;
305 struct fit_table *fit = fit_get_table(&bootblock,
306 convert_to_from_top_aligned,
307 topswap_size);
308 if (!fit) {
309 partitioned_file_close(image_file);
310 ERROR("FIT not found.\n");
311 return 1;
314 if (clear_table) {
315 if (fit_clear_table(fit)) {
316 partitioned_file_close(image_file);
317 ERROR("Failed to clear table.\n");
318 return 1;
322 switch (op) {
323 case ADD_REGI_OP:
325 struct buffer region;
327 if (partitioned_file_read_region(&region, image_file, name)) {
328 addr = -convert_to_from_top_aligned(&region, 0);
329 } else {
330 partitioned_file_close(image_file);
331 return 1;
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);
337 return 1;
339 break;
341 case ADD_CBFS_OP:
343 if (fit_type == FIT_TYPE_MICROCODE) {
344 if (fit_add_microcode_file(fit, &image, name,
345 convert_to_from_top_aligned,
346 max_table_size)) {
347 return 1;
349 } else {
350 uint32_t offset, len;
351 struct cbfs_file *cbfs_file;
353 cbfs_file = cbfs_get_entry(&image, name);
354 if (!cbfs_file) {
355 partitioned_file_close(image_file);
356 ERROR("%s not found in CBFS.\n", name);
357 return 1;
360 len = ntohl(cbfs_file->len);
361 offset = offset_to_ptr(convert_to_from_top_aligned,
362 &image.buffer,
363 cbfs_get_entry_addr(&image, cbfs_file) +
364 ntohl(cbfs_file->offset));
367 if (fit_add_entry(fit, offset, len, fit_type,
368 max_table_size)) {
369 partitioned_file_close(image_file);
370 ERROR("Adding type %u FIT entry\n", fit_type);
371 return 1;
374 break;
376 case ADD_ADDR_OP:
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);
381 return 1;
384 break;
385 case DEL_OP:
387 if (fit_delete_entry(fit, table_entry)) {
388 partitioned_file_close(image_file);
389 ERROR("Deleting FIT entry %zu failed\n", table_entry);
390 return 1;
392 break;
394 case NO_OP:
395 default:
396 break;
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);
403 return 1;
407 if (dump) {
408 if (fit_dump(fit)) {
409 partitioned_file_close(image_file);
410 return 1;
414 partitioned_file_close(image_file);
416 return 0;