summaryrefslogtreecommitdiffstats
path: root/util/cbfstool/ifittool.c
diff options
context:
space:
mode:
authorPhilipp Deppenwiese <zaolin.daisuki@gmail.com>2018-11-20 13:54:49 +0100
committerPhilipp Deppenwiese <zaolin.daisuki@gmail.com>2019-06-24 09:42:31 +0000
commit5ada0023d191eb6af1de6581f1b770b75b5554c4 (patch)
tree69e2b78e5230c3a8d5dd5692399db6c081b10113 /util/cbfstool/ifittool.c
parent42c44c2f8391a3b56acf9215044d2b2787061738 (diff)
downloadcoreboot-5ada0023d191eb6af1de6581f1b770b75b5554c4.tar.gz
coreboot-5ada0023d191eb6af1de6581f1b770b75b5554c4.tar.bz2
coreboot-5ada0023d191eb6af1de6581f1b770b75b5554c4.zip
cbfstool: Add ifittool
Add the IntelFirmwareInterfaceTable-tool to modify the FIT. As cbfstool is overloaded with arguments, introduce a new tool to only modify FIT, which brings it's own command line syntax. Provide clean interface to: * Clear FIT * Add entry to CBFS file * Add entry to REGION * Delete entries * Add support for types other than 1 * Add support to dump current table * Add support for top-swap * Sort entries by type Most code is reused from existing cbfstool and functionality of cbfstool is kept. It will be removed once the make system uses only ifittool. Based on "Intel Trusted Execution Technology (Intel TXT) LAB Handout" and https://github.com/slimbootloader/slimbootloader . Change-Id: I0fe8cd70611d58823aca1147d5b830722ed72bd5 Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/31493 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
Diffstat (limited to 'util/cbfstool/ifittool.c')
-rw-r--r--util/cbfstool/ifittool.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/util/cbfstool/ifittool.c b/util/cbfstool/ifittool.c
new file mode 100644
index 000000000000..a83fd96715bb
--- /dev/null
+++ b/util/cbfstool/ifittool.c
@@ -0,0 +1,431 @@
+/*
+ * cbfstool, CLI utility for creating rmodules
+ *
+ * Copyright (C) 2019 9elements Agency GmbH
+ * Copyright (C) 2019 Facebook Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "common.h"
+#include "cbfs_image.h"
+#include "partitioned_file.h"
+#include "fit.h"
+
+/* Global variables */
+partitioned_file_t *image_file;
+
+static const char *optstring = "H:j:f:r:d:t:n:s:caDvh?";
+static struct option long_options[] = {
+ {"file", required_argument, 0, 'f' },
+ {"region", required_argument, 0, 'r' },
+ {"add-cbfs-entry", no_argument, 0, 'a' },
+ {"add-region", no_argument, 0, 'A' },
+ {"del-entry", required_argument, 0, 'd' },
+ {"clear-table", no_argument, 0, 'c' },
+ {"fit-type", required_argument, 0, 't' },
+ {"cbfs-filename", required_argument, 0, 'n' },
+ {"max-table-size", required_argument, 0, 's' },
+ {"topswap-size", required_argument, 0, 'j' },
+ {"dump", no_argument, 0, 'D' },
+ {"verbose", no_argument, 0, 'v' },
+ {"help", no_argument, 0, 'h' },
+ {"header-offset", required_argument, 0, 'H' },
+ {NULL, 0, 0, 0 }
+};
+
+static void usage(const char *name)
+{
+ printf(
+ "ifittool: utility for modifying Intel Firmware Interface Table\n\n"
+ "USAGE: %s [-h] [-H] [-v] [-D] [-c] <-f|--file name> <-s|--max-table-size size> <-r|--region fmap region> OPERATION\n"
+ "\tOPERATION:\n"
+ "\t\t-a|--add-entry : Add a CBFS file as new entry to FIT\n"
+ "\t\t-A|--add-region : Add region as new entry to FIT (for microcodes)\n"
+ "\t\t-d|--del-entry number : Delete existing <number> entry\n"
+ "\t\t-t|--fit-type : Type of new entry\n"
+ "\t\t-n|--name : The CBFS filename or region to add to table\n"
+ "\tOPTIONAL ARGUMENTS:\n"
+ "\t\t-h|--help : Display this text\n"
+ "\t\t-H|--header-offset : Do not search for header, use this offset\n"
+ "\t\t-v|--verbose : Be verbose\n"
+ "\t\t-D|--dump : Dump FIT table (at end of operation)\n"
+ "\t\t-c|--clear-table : Remove all existing entries (do not update)\n"
+ "\t\t-j|--topswap-size : Use second FIT table if non zero\n"
+ "\tREQUIRED ARGUMENTS:\n"
+ "\t\t-f|--file name : The file containing the CBFS\n"
+ "\t\t-s|--max-table-size : The number of possible FIT entries in table\n"
+ "\t\t-r|--region : The FMAP region to operate on\n"
+ , name);
+}
+
+static int is_valid_topswap(size_t topswap_size)
+{
+ switch (topswap_size) {
+ case (64 * KiB):
+ case (128 * KiB):
+ case (256 * KiB):
+ case (512 * KiB):
+ case (1 * MiB):
+ break;
+ default:
+ ERROR("Invalid topswap_size %zd\n", topswap_size);
+ ERROR("topswap can be 64K|128K|256K|512K|1M\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Converts between offsets from the start of the specified image region and
+ * "top-aligned" offsets from the top of the entire boot media. See comment
+ * below for convert_to_from_top_aligned() about forming addresses.
+ */
+static unsigned int convert_to_from_absolute_top_aligned(
+ const struct buffer *region, unsigned int offset)
+{
+ assert(region);
+
+ size_t image_size = partitioned_file_total_size(image_file);
+
+ return image_size - region->offset - offset;
+}
+
+/*
+ * Converts between offsets from the start of the specified image region and
+ * "top-aligned" offsets from the top of the image region. Works in either
+ * direction: pass in one type of offset and receive the other type.
+ * N.B. A top-aligned offset is always a positive number, and should not be
+ * confused with a top-aligned *address*, which is its arithmetic inverse. */
+static unsigned int convert_to_from_top_aligned(const struct buffer *region,
+ unsigned int offset)
+{
+ assert(region);
+
+ /* Cover the situation where a negative base address is given by the
+ * user. Callers of this function negate it, so it'll be a positive
+ * number smaller than the region.
+ */
+ if ((offset > 0) && (offset < region->size))
+ return region->size - offset;
+
+ return convert_to_from_absolute_top_aligned(region, offset);
+}
+
+/*
+ * Get a pointer from an offset. This function assumes the ROM is located
+ * in the host address space at [4G - romsize -> 4G). It also assume all
+ * pointers have values within this address range.
+ */
+static inline uint32_t offset_to_ptr(fit_offset_converter_t helper,
+ const struct buffer *region, int offset)
+{
+ return -helper(region, offset);
+}
+
+enum fit_operation {
+ NO_OP = 0,
+ ADD_CBFS_OP,
+ ADD_REGI_OP,
+ ADD_ADDR_OP,
+ DEL_OP
+};
+
+int main(int argc, char *argv[])
+{
+ int c;
+ const char *input_file = NULL;
+ const char *name = NULL;
+ const char *region_name = NULL;
+ enum fit_operation op = NO_OP;
+ bool dump = false, clear_table = false;
+ size_t max_table_size = 0;
+ size_t table_entry = 0;
+ uint32_t addr = 0;
+ size_t topswap_size = 0;
+ enum fit_type fit_type = 0;
+ uint32_t headeroffset = ~0u;
+
+ verbose = 0;
+
+ if (argc < 4) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ while (1) {
+ int optindex = 0;
+ char *suffix = NULL;
+
+ c = getopt_long(argc, argv, optstring, long_options, &optindex);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ usage(argv[0]);
+ return 1;
+ case 'a':
+ if (op != NO_OP) {
+ ERROR("specified multiple actions at once\n");
+ usage(argv[0]);
+ return 1;
+ }
+ op = ADD_CBFS_OP;
+ break;
+ case 'A':
+ if (op != NO_OP) {
+ ERROR("specified multiple actions at once\n");
+ usage(argv[0]);
+ return 1;
+ }
+ op = ADD_REGI_OP;
+ break;
+ case 'x':
+ if (op != NO_OP) {
+ ERROR("specified multiple actions at once\n");
+ usage(argv[0]);
+ return 1;
+ }
+ op = ADD_ADDR_OP;
+ addr = atoll(optarg);
+ break;
+ case 'c':
+ clear_table = true;
+ break;
+ case 'd':
+ if (op != NO_OP) {
+ ERROR("specified multiple actions at once\n");
+ usage(argv[0]);
+ return 1;
+ }
+ op = DEL_OP;
+ table_entry = atoi(optarg);
+ break;
+ case 'D':
+ dump = true;
+ break;
+ case 'f':
+ input_file = optarg;
+ break;
+ case 'H':
+ headeroffset = strtoul(optarg, &suffix, 0);
+ if (!*optarg || (suffix && *suffix)) {
+ ERROR("Invalid header offset '%s'.\n", optarg);
+ return 1;
+ }
+ break;
+ case 'j':
+ topswap_size = atoi(optarg);
+ if (!is_valid_topswap(topswap_size))
+ return 1;
+ break;
+ case 'n':
+ name = optarg;
+ break;
+ case 'r':
+ region_name = optarg;
+ break;
+ case 's':
+ max_table_size = atoi(optarg);
+ break;
+ case 't':
+ fit_type = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (input_file == NULL) {
+ ERROR("No input file given\n");
+ usage(argv[0]);
+ return 1;
+ }
+
+ if (op == ADD_CBFS_OP || op == ADD_REGI_OP) {
+ if (fit_type == 0) {
+ ERROR("Adding FIT entry, but no type given\n");
+ usage(argv[0]);
+ return 1;
+ } else if (name == NULL) {
+ ERROR("Adding FIT entry, but no name set\n");
+ usage(argv[0]);
+ return 1;
+ } else if (max_table_size == 0) {
+ ERROR("Maximum table size not given\n");
+ usage(argv[0]);
+ return 1;
+ }
+ }
+ if (op == ADD_ADDR_OP) {
+ if (fit_type == 0) {
+ ERROR("Adding FIT entry, but no type given\n");
+ usage(argv[0]);
+ return 1;
+ } else if (max_table_size == 0) {
+ ERROR("Maximum table size not given\n");
+ usage(argv[0]);
+ return 1;
+ }
+ }
+
+ if (!region_name) {
+ ERROR("Region not given\n");
+ usage(argv[0]);
+ return 1;
+ }
+
+ image_file = partitioned_file_reopen(input_file,
+ op != NO_OP || clear_table);
+
+ struct buffer image_region;
+
+ if (!partitioned_file_read_region(&image_region, image_file,
+ region_name)) {
+ partitioned_file_close(image_file);
+ ERROR("The image will be left unmodified.\n");
+ return 1;
+ }
+
+ struct buffer bootblock;
+ // The bootblock is part of the CBFS on x86
+ buffer_clone(&bootblock, &image_region);
+
+ struct cbfs_image image;
+ if (cbfs_image_from_buffer(&image, &image_region, headeroffset)) {
+ partitioned_file_close(image_file);
+ return 1;
+ }
+
+ struct fit_table *fit = fit_get_table(&bootblock,
+ convert_to_from_top_aligned,
+ topswap_size);
+ if (!fit) {
+ partitioned_file_close(image_file);
+ ERROR("FIT not found.\n");
+ return 1;
+ }
+
+ if (clear_table) {
+ if (fit_clear_table(fit)) {
+ partitioned_file_close(image_file);
+ ERROR("Failed to clear table.\n");
+ return 1;
+ }
+ }
+
+ switch (op) {
+ case ADD_REGI_OP:
+ {
+ struct buffer region;
+ addr = 0;
+
+ if (partitioned_file_read_region(&region, image_file, name)) {
+ addr = -convert_to_from_top_aligned(&region, 0);
+ } else {
+ partitioned_file_close(image_file);
+ return 1;
+ }
+
+ if (fit_add_entry(fit, addr, 0, fit_type, max_table_size)) {
+ partitioned_file_close(image_file);
+ ERROR("Adding type %u FIT entry\n", fit_type);
+ return 1;
+ }
+ break;
+ }
+ case ADD_CBFS_OP:
+ {
+ if (fit_type == FIT_TYPE_MICROCODE) {
+ if (fit_add_microcode_file(fit, &image, name,
+ convert_to_from_top_aligned,
+ max_table_size)) {
+ return 1;
+ }
+ } else {
+ uint32_t offset, len;
+ struct cbfs_file *cbfs_file;
+
+ cbfs_file = cbfs_get_entry(&image, name);
+ if (!cbfs_file) {
+ partitioned_file_close(image_file);
+ ERROR("%s not found in CBFS.\n", name);
+ return 1;
+ }
+
+ len = ntohl(cbfs_file->len);
+ offset = offset_to_ptr(convert_to_from_top_aligned,
+ &image.buffer,
+ cbfs_get_entry_addr(&image, cbfs_file) +
+ ntohl(cbfs_file->offset));
+
+
+ if (fit_add_entry(fit, offset, len, fit_type,
+ max_table_size)) {
+ partitioned_file_close(image_file);
+ ERROR("Adding type %u FIT entry\n", fit_type);
+ return 1;
+ }
+ }
+ break;
+ }
+ case ADD_ADDR_OP:
+ {
+ if (fit_add_entry(fit, addr, 0, fit_type, max_table_size)) {
+ partitioned_file_close(image_file);
+ ERROR("Adding type %u FIT entry\n", fit_type);
+ return 1;
+ }
+ }
+ break;
+ case DEL_OP:
+ {
+ if (fit_delete_entry(fit, table_entry)) {
+ partitioned_file_close(image_file);
+ ERROR("Deleting FIT entry %zu failed\n", table_entry);
+ return 1;
+ }
+ break;
+ }
+ case NO_OP:
+ default:
+ break;
+ }
+
+ if (op != NO_OP || clear_table) {
+ if (!partitioned_file_write_region(image_file, &bootblock)) {
+ ERROR("Failed to write changes to disk.\n");
+ partitioned_file_close(image_file);
+ return 1;
+ }
+ }
+
+ if (dump) {
+ if (fit_dump(fit)) {
+ partitioned_file_close(image_file);
+ return 1;
+ }
+ }
+
+ partitioned_file_close(image_file);
+
+ return 0;
+}