AUTHORS, util/: Drop individual copyright notices
[coreboot.git] / util / cbfstool / ifittool.c
blob3a9c37e09724962645326fcb421ab631b92409be
1 /* cbfstool, CLI utility for creating rmodules */
2 /*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; version 2 of the License.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <getopt.h>
19 #include "common.h"
20 #include "cbfs_image.h"
21 #include "partitioned_file.h"
22 #include "fit.h"
24 /* Global variables */
25 partitioned_file_t *image_file;
27 static const char *optstring = "H:j:f:r:d:t:n:s:cAaDvh?";
28 static struct option long_options[] = {
29 {"file", required_argument, 0, 'f' },
30 {"region", required_argument, 0, 'r' },
31 {"add-cbfs-entry", no_argument, 0, 'a' },
32 {"add-region", no_argument, 0, 'A' },
33 {"del-entry", required_argument, 0, 'd' },
34 {"clear-table", no_argument, 0, 'c' },
35 {"fit-type", required_argument, 0, 't' },
36 {"cbfs-filename", required_argument, 0, 'n' },
37 {"max-table-size", required_argument, 0, 's' },
38 {"topswap-size", required_argument, 0, 'j' },
39 {"dump", no_argument, 0, 'D' },
40 {"verbose", no_argument, 0, 'v' },
41 {"help", no_argument, 0, 'h' },
42 {"header-offset", required_argument, 0, 'H' },
43 {NULL, 0, 0, 0 }
46 static void usage(const char *name)
48 printf(
49 "ifittool: utility for modifying Intel Firmware Interface Table\n\n"
50 "USAGE: %s [-h] [-H] [-v] [-D] [-c] <-f|--file name> <-s|--max-table-size size> <-r|--region fmap region> OPERATION\n"
51 "\tOPERATION:\n"
52 "\t\t-a|--add-entry : Add a CBFS file as new entry to FIT\n"
53 "\t\t-A|--add-region : Add region as new entry to FIT (for microcodes)\n"
54 "\t\t-d|--del-entry number : Delete existing <number> entry\n"
55 "\t\t-t|--fit-type : Type of new entry\n"
56 "\t\t-n|--name : The CBFS filename or region to add to table\n"
57 "\tOPTIONAL ARGUMENTS:\n"
58 "\t\t-h|--help : Display this text\n"
59 "\t\t-H|--header-offset : Do not search for header, use this offset\n"
60 "\t\t-v|--verbose : Be verbose\n"
61 "\t\t-D|--dump : Dump FIT table (at end of operation)\n"
62 "\t\t-c|--clear-table : Remove all existing entries (do not update)\n"
63 "\t\t-j|--topswap-size : Use second FIT table if non zero\n"
64 "\tREQUIRED ARGUMENTS:\n"
65 "\t\t-f|--file name : The file containing the CBFS\n"
66 "\t\t-s|--max-table-size : The number of possible FIT entries in table\n"
67 "\t\t-r|--region : The FMAP region to operate on\n"
68 , name);
71 static int is_valid_topswap(size_t topswap_size)
73 switch (topswap_size) {
74 case (64 * KiB):
75 case (128 * KiB):
76 case (256 * KiB):
77 case (512 * KiB):
78 case (1 * MiB):
79 break;
80 default:
81 ERROR("Invalid topswap_size %zd\n", topswap_size);
82 ERROR("topswap can be 64K|128K|256K|512K|1M\n");
83 return 0;
85 return 1;
89 * Converts between offsets from the start of the specified image region and
90 * "top-aligned" offsets from the top of the entire boot media. See comment
91 * below for convert_to_from_top_aligned() about forming addresses.
93 static unsigned int convert_to_from_absolute_top_aligned(
94 const struct buffer *region, unsigned int offset)
96 assert(region);
98 size_t image_size = partitioned_file_total_size(image_file);
100 return image_size - region->offset - offset;
104 * Converts between offsets from the start of the specified image region and
105 * "top-aligned" offsets from the top of the image region. Works in either
106 * direction: pass in one type of offset and receive the other type.
107 * N.B. A top-aligned offset is always a positive number, and should not be
108 * confused with a top-aligned *address*, which is its arithmetic inverse. */
109 static unsigned int convert_to_from_top_aligned(const struct buffer *region,
110 unsigned int offset)
112 assert(region);
114 /* Cover the situation where a negative base address is given by the
115 * user. Callers of this function negate it, so it'll be a positive
116 * number smaller than the region.
118 if ((offset > 0) && (offset < region->size))
119 return region->size - offset;
121 return convert_to_from_absolute_top_aligned(region, offset);
125 * Get a pointer from an offset. This function assumes the ROM is located
126 * in the host address space at [4G - romsize -> 4G). It also assume all
127 * pointers have values within this address range.
129 static inline uint32_t offset_to_ptr(fit_offset_converter_t helper,
130 const struct buffer *region, int offset)
132 return -helper(region, offset);
135 enum fit_operation {
136 NO_OP = 0,
137 ADD_CBFS_OP,
138 ADD_REGI_OP,
139 ADD_ADDR_OP,
140 DEL_OP
143 int main(int argc, char *argv[])
145 int c;
146 const char *input_file = NULL;
147 const char *name = NULL;
148 const char *region_name = NULL;
149 enum fit_operation op = NO_OP;
150 bool dump = false, clear_table = false;
151 size_t max_table_size = 0;
152 size_t table_entry = 0;
153 uint32_t addr = 0;
154 size_t topswap_size = 0;
155 enum fit_type fit_type = 0;
156 uint32_t headeroffset = ~0u;
158 verbose = 0;
160 if (argc < 4) {
161 usage(argv[0]);
162 return 1;
165 while (1) {
166 int optindex = 0;
167 char *suffix = NULL;
169 c = getopt_long(argc, argv, optstring, long_options, &optindex);
171 if (c == -1)
172 break;
174 switch (c) {
175 case 'h':
176 usage(argv[0]);
177 return 1;
178 case 'a':
179 if (op != NO_OP) {
180 ERROR("specified multiple actions at once\n");
181 usage(argv[0]);
182 return 1;
184 op = ADD_CBFS_OP;
185 break;
186 case 'A':
187 if (op != NO_OP) {
188 ERROR("specified multiple actions at once\n");
189 usage(argv[0]);
190 return 1;
192 op = ADD_REGI_OP;
193 break;
194 case 'x':
195 if (op != NO_OP) {
196 ERROR("specified multiple actions at once\n");
197 usage(argv[0]);
198 return 1;
200 op = ADD_ADDR_OP;
201 addr = atoll(optarg);
202 break;
203 case 'c':
204 clear_table = true;
205 break;
206 case 'd':
207 if (op != NO_OP) {
208 ERROR("specified multiple actions at once\n");
209 usage(argv[0]);
210 return 1;
212 op = DEL_OP;
213 table_entry = atoi(optarg);
214 break;
215 case 'D':
216 dump = true;
217 break;
218 case 'f':
219 input_file = optarg;
220 break;
221 case 'H':
222 headeroffset = strtoul(optarg, &suffix, 0);
223 if (!*optarg || (suffix && *suffix)) {
224 ERROR("Invalid header offset '%s'.\n", optarg);
225 return 1;
227 break;
228 case 'j':
229 topswap_size = strtol(optarg, NULL, 0);
230 if (!is_valid_topswap(topswap_size))
231 return 1;
232 break;
233 case 'n':
234 name = optarg;
235 break;
236 case 'r':
237 region_name = optarg;
238 break;
239 case 's':
240 max_table_size = atoi(optarg);
241 break;
242 case 't':
243 fit_type = atoi(optarg);
244 break;
245 case 'v':
246 verbose++;
247 break;
248 default:
249 break;
253 if (input_file == NULL) {
254 ERROR("No input file given\n");
255 usage(argv[0]);
256 return 1;
259 if (op == ADD_CBFS_OP || op == ADD_REGI_OP) {
260 if (fit_type == 0) {
261 ERROR("Adding FIT entry, but no type given\n");
262 usage(argv[0]);
263 return 1;
264 } else if (name == NULL) {
265 ERROR("Adding FIT entry, but no name set\n");
266 usage(argv[0]);
267 return 1;
268 } else if (max_table_size == 0) {
269 ERROR("Maximum table size not given\n");
270 usage(argv[0]);
271 return 1;
274 if (op == ADD_ADDR_OP) {
275 if (fit_type == 0) {
276 ERROR("Adding FIT entry, but no type given\n");
277 usage(argv[0]);
278 return 1;
279 } else if (max_table_size == 0) {
280 ERROR("Maximum table size not given\n");
281 usage(argv[0]);
282 return 1;
286 if (!region_name) {
287 ERROR("Region not given\n");
288 usage(argv[0]);
289 return 1;
292 image_file = partitioned_file_reopen(input_file,
293 op != NO_OP || clear_table);
295 struct buffer image_region;
297 if (!partitioned_file_read_region(&image_region, image_file,
298 region_name)) {
299 partitioned_file_close(image_file);
300 ERROR("The image will be left unmodified.\n");
301 return 1;
304 struct buffer bootblock;
305 // The bootblock is part of the CBFS on x86
306 buffer_clone(&bootblock, &image_region);
308 struct cbfs_image image;
309 if (cbfs_image_from_buffer(&image, &image_region, headeroffset)) {
310 partitioned_file_close(image_file);
311 return 1;
314 struct fit_table *fit = fit_get_table(&bootblock,
315 convert_to_from_top_aligned,
316 topswap_size);
317 if (!fit) {
318 partitioned_file_close(image_file);
319 ERROR("FIT not found.\n");
320 return 1;
323 if (clear_table) {
324 if (fit_clear_table(fit)) {
325 partitioned_file_close(image_file);
326 ERROR("Failed to clear table.\n");
327 return 1;
331 switch (op) {
332 case ADD_REGI_OP:
334 struct buffer region;
336 if (partitioned_file_read_region(&region, image_file, name)) {
337 addr = -convert_to_from_top_aligned(&region, 0);
338 } else {
339 partitioned_file_close(image_file);
340 return 1;
343 if (fit_add_entry(fit, addr, 0, fit_type, max_table_size)) {
344 partitioned_file_close(image_file);
345 ERROR("Adding type %u FIT entry\n", fit_type);
346 return 1;
348 break;
350 case ADD_CBFS_OP:
352 if (fit_type == FIT_TYPE_MICROCODE) {
353 if (fit_add_microcode_file(fit, &image, name,
354 convert_to_from_top_aligned,
355 max_table_size)) {
356 return 1;
358 } else {
359 uint32_t offset, len;
360 struct cbfs_file *cbfs_file;
362 cbfs_file = cbfs_get_entry(&image, name);
363 if (!cbfs_file) {
364 partitioned_file_close(image_file);
365 ERROR("%s not found in CBFS.\n", name);
366 return 1;
369 len = ntohl(cbfs_file->len);
370 offset = offset_to_ptr(convert_to_from_top_aligned,
371 &image.buffer,
372 cbfs_get_entry_addr(&image, cbfs_file) +
373 ntohl(cbfs_file->offset));
376 if (fit_add_entry(fit, offset, len, fit_type,
377 max_table_size)) {
378 partitioned_file_close(image_file);
379 ERROR("Adding type %u FIT entry\n", fit_type);
380 return 1;
383 break;
385 case ADD_ADDR_OP:
387 if (fit_add_entry(fit, addr, 0, fit_type, max_table_size)) {
388 partitioned_file_close(image_file);
389 ERROR("Adding type %u FIT entry\n", fit_type);
390 return 1;
393 break;
394 case DEL_OP:
396 if (fit_delete_entry(fit, table_entry)) {
397 partitioned_file_close(image_file);
398 ERROR("Deleting FIT entry %zu failed\n", table_entry);
399 return 1;
401 break;
403 case NO_OP:
404 default:
405 break;
408 if (op != NO_OP || clear_table) {
409 if (!partitioned_file_write_region(image_file, &bootblock)) {
410 ERROR("Failed to write changes to disk.\n");
411 partitioned_file_close(image_file);
412 return 1;
416 if (dump) {
417 if (fit_dump(fit)) {
418 partitioned_file_close(image_file);
419 return 1;
423 partitioned_file_close(image_file);
425 return 0;