aboutsummaryrefslogtreecommitdiff
path: root/util/cbfstool/fit.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/fit.c
parent42c44c2f8391a3b56acf9215044d2b2787061738 (diff)
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/fit.c')
-rw-r--r--util/cbfstool/fit.c707
1 files changed, 590 insertions, 117 deletions
diff --git a/util/cbfstool/fit.c b/util/cbfstool/fit.c
index aeb1755032..86dde4d23d 100644
--- a/util/cbfstool/fit.c
+++ b/util/cbfstool/fit.c
@@ -2,6 +2,8 @@
* Firmware Interface Table support.
*
* Copyright (C) 2012 Google Inc.
+ * 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
@@ -25,17 +27,42 @@
#define FIT_POINTER_LOCATION 0xffffffc0
#define FIT_TABLE_LOWEST_ADDRESS ((uint32_t)(-(16 << 20)))
#define FIT_ENTRY_CHECKSUM_VALID 0x80
-#define FIT_TYPE_HEADER 0x0
-#define FIT_HEADER_VERSION 0x0100
-#define FIT_HEADER_ADDRESS "_FIT_ "
-#define FIT_TYPE_MICROCODE 0x1
-#define FIT_MICROCODE_VERSION 0x0100
+#define FIT_HEADER_VERSION 0x0100
+#define FIT_HEADER_ADDRESS "_FIT_ "
+#define FIT_MICROCODE_VERSION 0x0100
+#define FIT_TXT_VERSION 0x0100
+
+#define FIT_SIZE_ALIGNMENT 16
struct fit_entry {
+ /**
+ * Address is the base address of the firmware component
+ * must be aligned on 16 byte boundary
+ */
uint64_t address;
+ /**
+ * Size is the span of the component in multiple of 16 bytes
+ * Bits [24:31] are reserved and must be set to 0
+ */
uint32_t size_reserved;
+ /**
+ * Component's version number in binary coded decimal (BCD) format.
+ * For the FIT header entry, the value in this field will indicate the
+ * revision number of the FIT data structure. The upper byte of the
+ * revision field indicates the major revision and the lower byte
+ * indicates the minor revision.
+ */
uint16_t version;
+ /**
+ * FIT types 0x00 to 0x7F
+ * Bit 7 (C_V) indicates whether component has valid checksum.
+ */
uint8_t type_checksum_valid;
+ /**
+ * Component's checksum. The modulo sum of all the bytes in the
+ * component and the value in this field (Chksum) must add up to zero.
+ * This field is only valid if the C_V flag is non-zero.
+ */
uint8_t checksum;
} __packed;
@@ -67,20 +94,20 @@ static inline void *rom_buffer_pointer(struct buffer *buffer, int offset)
return &buffer->data[offset];
}
-static inline int fit_entry_size_bytes(struct fit_entry *entry)
+static inline size_t fit_entry_size_bytes(const struct fit_entry *entry)
{
return (entry->size_reserved & 0xffffff) << 4;
}
static inline void fit_entry_update_size(struct fit_entry *entry,
- int size_bytes)
+ const int size_bytes)
{
/* Size is multiples of 16 bytes. */
entry->size_reserved = (size_bytes >> 4) & 0xffffff;
}
static inline void fit_entry_add_size(struct fit_entry *entry,
- int size_bytes)
+ const int size_bytes)
{
int size = fit_entry_size_bytes(entry);
size += size_bytes;
@@ -114,8 +141,70 @@ static inline uint32_t offset_to_ptr(fit_offset_converter_t helper,
return -helper(region, offset);
}
+/*
+ * Return the number of FIT entries.
+ */
+static inline size_t fit_table_entries(const struct fit_table *fit)
+{
+ if (!fit)
+ return 0;
+
+ return (fit_entry_size_bytes(&fit->header) / FIT_SIZE_ALIGNMENT) - 1;
+}
+
+/*
+ * Return the number of unused entries.
+ */
+static inline size_t fit_free_space(struct fit_table *fit,
+ const size_t max_entries)
+{
+ if (!fit)
+ return 0;
+
+ return max_entries - fit_table_entries(fit);
+}
+
+/*
+ * Sort entries by type and fill gaps (entries with type unused).
+ * To be called after adding or deleting entries.
+ *
+ * This one is critical, as mentioned in Chapter 1.2.1 "FIT Ordering Rules"
+ * "Firmware Interface Table BIOS Specification".
+ *
+ * We need to use a stable sorting algortihm, as the order of
+ * FIT_TYPE_BIOS_STARTUP matter for measurements.
+ */
+static void sort_fit_table(struct fit_table *fit)
+{
+ struct fit_entry tmp;
+ size_t i, j;
+ int swapped;
+
+ /* Bubble sort entries */
+ for (j = 0; j < fit_table_entries(fit) - 1; j++) {
+ swapped = 0;
+ for (i = 0; i < fit_table_entries(fit) - j - 1; i++) {
+ if (fit->entries[i].type_checksum_valid <=
+ fit->entries[i + 1].type_checksum_valid)
+ continue;
+ /* SWAP entries */
+ memcpy(&tmp, &fit->entries[i], sizeof(tmp));
+ memcpy(&fit->entries[i], &fit->entries[i + 1],
+ sizeof(fit->entries[i]));
+ memcpy(&fit->entries[i + 1], &tmp,
+ sizeof(fit->entries[i + 1]));
+ swapped = 1;
+ }
+ if (!swapped)
+ break;
+ }
+}
+
static int fit_table_verified(struct fit_table *table)
{
+ if (!table)
+ return 0;
+
/* Check that the address field has the proper signature. */
if (strncmp((const char *)&table->header.address, FIT_HEADER_ADDRESS,
sizeof(table->header.address)))
@@ -127,35 +216,17 @@ static int fit_table_verified(struct fit_table *table)
if (fit_entry_type(&table->header) != FIT_TYPE_HEADER)
return 0;
- /* Assume that the FIT table only contains the header */
- if (fit_entry_size_bytes(&table->header) != sizeof(struct fit_entry))
+ /* Assume that the FIT table contains at least the header */
+ if (fit_entry_size_bytes(&table->header) < sizeof(struct fit_entry))
return 0;
return 1;
}
-static struct fit_table *locate_fit_table(fit_offset_converter_t offset_helper,
- struct buffer *buffer)
-{
- struct fit_table *table;
- uint32_t *fit_pointer;
-
- fit_pointer = rom_buffer_pointer(buffer,
- ptr_to_offset(offset_helper, buffer,
- FIT_POINTER_LOCATION));
-
- /* Ensure pointer is below 4GiB and within 16MiB of 4GiB */
- if (fit_pointer[1] != 0 || fit_pointer[0] < FIT_TABLE_LOWEST_ADDRESS)
- return NULL;
-
- table = rom_buffer_pointer(buffer,
- ptr_to_offset(offset_helper, buffer, *fit_pointer));
- if (!fit_table_verified(table))
- return NULL;
- else
- return table;
-}
-
+/*
+ * Update the FIT checksum.
+ * To be called after modifiying the table.
+ */
static void update_fit_checksum(struct fit_table *fit)
{
int size_bytes;
@@ -163,6 +234,9 @@ static void update_fit_checksum(struct fit_table *fit)
uint8_t result;
int i;
+ if (!fit)
+ return;
+
fit->header.checksum = 0;
size_bytes = fit_entry_size_bytes(&fit->header);
result = 0;
@@ -172,92 +246,68 @@ static void update_fit_checksum(struct fit_table *fit)
fit->header.checksum = -result;
}
-static void update_fit_ucode_entry(struct fit_table *fit,
- struct fit_entry *entry, uint64_t mcu_addr)
-{
- entry->address = mcu_addr;
- /*
- * While loading MCU, its size is not referred from FIT and
- * rather from the MCU header, hence we can assign zero here
- */
- entry->size_reserved = 0x0000;
- entry->type_checksum_valid = FIT_TYPE_MICROCODE;
- entry->version = FIT_MICROCODE_VERSION;
- entry->checksum = 0;
- fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
-}
-
-static void add_microcodde_entries(struct fit_table *fit,
- const struct cbfs_image *image,
- int num_mcus, struct microcode_entry *mcus,
- fit_offset_converter_t offset_helper,
- uint32_t first_mcu_addr)
-{
- int i = 0;
- /*
- * Check if an entry has to be forced into the FIT at index 0.
- * first_mcu_addr is an address (in ROM) that will point to a
- * microcode patch.
- */
- if (first_mcu_addr) {
- struct fit_entry *entry = &fit->entries[0];
- update_fit_ucode_entry(fit, entry, first_mcu_addr);
- i = 1;
- }
-
- struct microcode_entry *mcu = &mcus[0];
- for (; i < num_mcus; i++) {
- struct fit_entry *entry = &fit->entries[i];
- update_fit_ucode_entry(fit, entry, offset_to_ptr(offset_helper,
- &image->buffer, mcu->offset));
- mcu++;
- }
-}
-
-static void cbfs_file_get_header(struct buffer *buf, struct cbfs_file *file)
+/*
+ * Return a pointer to the next free entry.
+ * Caller must take care if enough space is available.
+ */
+static struct fit_entry *get_next_free_entry(struct fit_table *fit)
{
- bgets(buf, &file->magic, sizeof(file->magic));
- file->len = xdr_be.get32(buf);
- file->type = xdr_be.get32(buf);
- file->attributes_offset = xdr_be.get32(buf);
- file->offset = xdr_be.get32(buf);
+ return &fit->entries[fit_table_entries(fit)];
}
-static int fit_header(void *ptr, uint32_t *current_offset, uint32_t *file_length)
+static void fit_location_from_cbfs_header(uint32_t *current_offset,
+ uint32_t *file_length, void *ptr)
{
struct buffer buf;
struct cbfs_file header;
+ memset(&buf, 0, sizeof(buf));
+
buf.data = ptr;
buf.size = sizeof(header);
- cbfs_file_get_header(&buf, &header);
+
+ bgets(&buf, header.magic, sizeof(header.magic));
+ header.len = xdr_be.get32(&buf);
+ header.type = xdr_be.get32(&buf);
+ header.attributes_offset = xdr_be.get32(&buf);
+ header.offset = xdr_be.get32(&buf);
+
*current_offset = header.offset;
*file_length = header.len;
- return 0;
}
-static int parse_microcode_blob(struct cbfs_image *image,
- struct cbfs_file *mcode_file,
- struct microcode_entry *mcus,
- int total_entries, int *mcus_found)
+static int
+parse_microcode_blob(struct cbfs_image *image,
+ const char *blob_name,
+ size_t *mcus_found,
+ struct microcode_entry *mcus,
+ const size_t max_fit_entries)
{
- int num_mcus;
+ size_t num_mcus;
uint32_t current_offset;
uint32_t file_length;
+ struct cbfs_file *mcode_file;
+
+ mcode_file = cbfs_get_entry(image, blob_name);
+ if (!mcode_file)
+ return 1;
- fit_header(mcode_file, &current_offset, &file_length);
- current_offset += (int)((char *)mcode_file - image->buffer.data);
+ fit_location_from_cbfs_header(&current_offset, &file_length,
+ mcode_file);
+ current_offset += cbfs_get_entry_addr(image, mcode_file);
num_mcus = 0;
- while (file_length > sizeof(struct microcode_header))
- {
+ while (file_length > sizeof(struct microcode_header)) {
const struct microcode_header *mcu_header;
mcu_header = rom_buffer_pointer(&image->buffer, current_offset);
+ if (!mcu_header) {
+ ERROR("Couldn't parse microcode header.\n");
+ return 1;
+ }
/* Newer microcode updates include a size field, whereas older
* containers set it at 0 and are exactly 2048 bytes long */
- uint32_t total_size = mcu_header->total_size
- ? mcu_header->total_size : 2048;
+ uint32_t total_size = mcu_header->total_size ?: 2048;
/* Quickly sanity check a prospective microcode update. */
if (total_size < sizeof(*mcu_header))
@@ -271,9 +321,8 @@ static int parse_microcode_blob(struct cbfs_image *image,
current_offset += mcus[num_mcus].size;
file_length -= mcus[num_mcus].size;
num_mcus++;
-
/* Reached limit of FIT entries. */
- if (num_mcus == total_entries)
+ if (num_mcus == max_fit_entries)
break;
if (file_length < sizeof(struct microcode_header))
break;
@@ -285,42 +334,466 @@ static int parse_microcode_blob(struct cbfs_image *image,
return 0;
}
+/* There can be zero or more FIT_TYPE_MICROCODE entries */
+static void update_fit_ucode_entry(struct fit_table *fit,
+ struct fit_entry *entry,
+ const uint64_t mcu_addr)
+{
+ entry->address = mcu_addr;
+ /*
+ * While loading MCU, its size is not referred from FIT and
+ * rather from the MCU header, hence we can assign zero here.
+ */
+ entry->size_reserved = 0;
+ entry->type_checksum_valid = FIT_TYPE_MICROCODE;
+ entry->version = FIT_MICROCODE_VERSION;
+ entry->checksum = 0;
+ fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
+}
+
+/*
+ * There can be zero or one FIT_TYPE_BIOS_ACM entry per table.
+ * In case there's a FIT_TYPE_BIOS_ACM entry, at least one
+ * FIT_TYPE_BIOS_STARTUP entry must exist.
+ *
+ * The caller has to provide valid arguments as those aren't verfied.
+ */
+static void update_fit_bios_acm_entry(struct fit_table *fit,
+ struct fit_entry *entry,
+ const uint64_t acm_addr)
+{
+ entry->address = acm_addr;
+ /*
+ * The Address field points to a BIOS ACM. The Address field points to
+ * the first byte of the AC module header. When BIOS ACM is loaded in
+ * Authenticated Code RAM, one MTRR base/limit pair is used to map it.
+ */
+ entry->size_reserved = 0;
+ entry->type_checksum_valid = FIT_TYPE_BIOS_ACM;
+ entry->version = FIT_TXT_VERSION;
+ entry->checksum = 0;
+ fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
+}
+
+/*
+ * In case there's a FIT_TYPE_BIOS_ACM entry, at least one
+ * FIT_TYPE_BIOS_STARTUP entry must exist.
+ *
+ * The caller has to provide valid arguments as those aren't verfied.
+ */
+static void update_fit_bios_startup_entry(struct fit_table *fit,
+ struct fit_entry *entry,
+ const uint64_t sm_addr,
+ const uint32_t sm_size)
+{
+ entry->address = sm_addr;
+ assert(sm_size % 16 == 0);
+ /*
+ * BIOS Startup code is defined as the code that gets control at the
+ * reset vector and continues the chain of trust in TCG-compliant
+ * fashion. In addition, this code may also configure memory and SMRAM.
+ */
+ fit_entry_update_size(entry, sm_size);
+ entry->type_checksum_valid = FIT_TYPE_BIOS_STARTUP;
+ entry->version = FIT_TXT_VERSION;
+ entry->checksum = 0;
+ fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
+}
+
+/*
+ * There can be zero or one FIT_TYPE_BIOS_POLICY Record in the FIT.
+ * If the platform uses the hash comparison method and employs a
+ * failsafe bootblock, one FIT_TYPE_BIOS_POLICY entry is needed to
+ * contain the failsafe hash.
+ * If the platform uses the Signature verification method, one
+ * FIT_TYPE_BIOS_POLICY entry is needed. In this case, the entry
+ * contains the OEM key, hash of the BIOS and signature over the hash
+ * using the OEM key.
+ * In all other cases, the FIT_TYPE_BIOS_POLICY record is not required.
+ *
+ * The caller has to provide valid arguments as those aren't verfied.
+ */
+static void update_fit_bios_policy_entry(struct fit_table *fit,
+ struct fit_entry *entry,
+ const uint64_t lcp_policy_addr,
+ const uint32_t lcp_policy_size)
+{
+ entry->address = lcp_policy_addr;
+ fit_entry_update_size(entry, lcp_policy_size);
+ entry->type_checksum_valid = FIT_TYPE_BIOS_POLICY;
+ entry->version = FIT_TXT_VERSION;
+ entry->checksum = 0;
+ fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
+}
+
+/*
+ * There can be zero or one FIT_TYPE_TXT_POLICY entries
+ *
+ * The caller has to provide valid arguments as those aren't verfied.
+ */
+static void update_fit_txt_policy_entry(struct fit_table *fit,
+ struct fit_entry *entry,
+ uint64_t txt_policy_addr)
+{
+ entry->address = txt_policy_addr;
+ /*
+ * Points to the flag indicating if TXT is enabled on this platform.
+ * If not present, TXT is not disabled by FIT.
+ */
+ entry->size_reserved = 0;
+ entry->type_checksum_valid = FIT_TYPE_TXT_POLICY;
+ entry->version = 0x1;
+ entry->checksum = 0;
+ fit_entry_add_size(&fit->header, sizeof(struct fit_entry));
+}
+
+/* Special case for ucode CBFS file, as it might contain more than one ucode */
+int fit_add_microcode_file(struct fit_table *fit,
+ struct cbfs_image *image,
+ const char *blob_name,
+ fit_offset_converter_t offset_helper,
+ const size_t max_fit_entries)
+{
+ struct microcode_entry *mcus;
+
+ size_t i;
+ size_t mcus_found;
+
+ mcus = malloc(sizeof(*mcus) * max_fit_entries);
+ if (!mcus) {
+ ERROR("Couldn't allocate memory for microcode entries.\n");
+ return 1;
+ }
+
+ if (parse_microcode_blob(image, blob_name, &mcus_found, mcus,
+ max_fit_entries)) {
+ ERROR("Couldn't parse microcode blob.\n");
+ free(mcus);
+ return 1;
+ }
+
+ if (mcus_found > fit_free_space(fit, max_fit_entries)) {
+ ERROR("Maximum of FIT entries reached.\n");
+ free(mcus);
+ return 1;
+ }
+
+ for (i = 0; i < mcus_found; i++) {
+ if (fit_add_entry(fit,
+ offset_to_ptr(offset_helper, &image->buffer,
+ mcus[i].offset),
+ 0,
+ FIT_TYPE_MICROCODE,
+ max_fit_entries)) {
+
+ free(mcus);
+ return 1;
+ }
+ }
+
+ free(mcus);
+ return 0;
+}
+
+/*
+ * Return a pointer to the active FIT.
+ */
+struct fit_table *fit_get_table(struct buffer *bootblock,
+ fit_offset_converter_t offset_fn,
+ uint32_t topswap_size)
+{
+ struct fit_table *fit;
+ uint32_t *fit_pointer;
+
+ fit_pointer = rom_buffer_pointer(bootblock,
+ ptr_to_offset(offset_fn, bootblock,
+ FIT_POINTER_LOCATION));
+
+ /* Ensure pointer is below 4GiB and within 16MiB of 4GiB */
+ if (fit_pointer[1] != 0 || fit_pointer[0] < FIT_TABLE_LOWEST_ADDRESS) {
+ ERROR("FIT not found.\n");
+ return NULL;
+ }
+
+ fit = rom_buffer_pointer(bootblock,
+ ptr_to_offset(offset_fn, bootblock, *fit_pointer));
+ if (!fit_table_verified(fit)) {
+ ERROR("FIT not found.\n");
+ return NULL;
+ }
+
+ if (topswap_size) {
+ struct fit_table *fit2 = (struct fit_table *)((uintptr_t)fit -
+ topswap_size);
+ if (!fit_table_verified(fit2)) {
+ ERROR("second FIT is invalid\n");
+ return NULL;
+ }
+ fit = fit2;
+ }
+
+ DEBUG("Operating on table (0x%x)\n", *fit_pointer - topswap_size);
+
+ return fit;
+}
+
+/*
+ * Dump the current FIT in human readable format to stdout.
+ */
+int fit_dump(struct fit_table *fit)
+{
+ size_t i;
+
+ if (!fit)
+ return 1;
+
+ printf("\n");
+ printf(" FIT table:\n");
+
+ if (fit_table_entries(fit) < 1) {
+ printf(" empty\n\n");
+ return 0;
+ }
+
+ printf(" %-6s %-20s %-16s %-8s\n", "Index", "Type", "Addr", "Size");
+
+ for (i = 0; i < fit_table_entries(fit); i++) {
+ const char *name;
+
+ switch (fit->entries[i].type_checksum_valid) {
+ case FIT_TYPE_MICROCODE:
+ name = "Microcode";
+ break;
+ case FIT_TYPE_BIOS_ACM:
+ name = "BIOS ACM";
+ break;
+ case FIT_TYPE_BIOS_STARTUP:
+ name = "BIOS Startup Module";
+ break;
+ case FIT_TYPE_TPM_POLICY:
+ name = "TPM Policy";
+ break;
+ case FIT_TYPE_BIOS_POLICY:
+ name = "BIOS Policy";
+ break;
+ case FIT_TYPE_TXT_POLICY:
+ name = "TXT Policy";
+ break;
+ case FIT_TYPE_KEY_MANIFEST:
+ name = "Key Manifest";
+ break;
+ case FIT_TYPE_BOOT_POLICY:
+ name = "Boot Policy";
+ break;
+ case FIT_TYPE_CSE_SECURE_BOOT:
+ name = "CSE SecureBoot";
+ break;
+ case FIT_TYPE_TXTSX_POLICY:
+ name = "TXTSX policy";
+ break;
+ case FIT_TYPE_JMP_DEBUG_POLICY:
+ name = "JMP debug policy";
+ break;
+ case FIT_TYPE_UNUSED:
+ name = "unused";
+ break;
+ default:
+ name = "unknown";
+ }
+
+ printf(" %6zd %-20s 0x%08"PRIx64" 0x%08zx\n", i, name,
+ fit->entries[i].address,
+ fit_entry_size_bytes(&fit->entries[i]));
+ }
+ printf("\n");
+ return 0;
+}
+
+/*
+ * Remove all entries from table.
+ */
+int fit_clear_table(struct fit_table *fit)
+{
+ if (!fit)
+ return 1;
+
+ memset(fit->entries, 0,
+ sizeof(struct fit_entry) * fit_table_entries(fit));
+
+ /* Reset entry counter in header */
+ fit_entry_update_size(&fit->header, sizeof(fit->header));
+
+ update_fit_checksum(fit);
+
+ return 0;
+}
+
+/*
+ * Returns true if the FIT type is know and can be added to the table.
+ */
+int fit_is_supported_type(const enum fit_type type)
+{
+ switch (type) {
+ case FIT_TYPE_MICROCODE:
+ case FIT_TYPE_BIOS_ACM:
+ case FIT_TYPE_BIOS_STARTUP:
+ case FIT_TYPE_BIOS_POLICY:
+ case FIT_TYPE_TXT_POLICY:
+ return 1;
+ case FIT_TYPE_TPM_POLICY:
+ case FIT_TYPE_KEY_MANIFEST:
+ case FIT_TYPE_BOOT_POLICY:
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Adds an known entry to the FIT.
+ * len is optional for same types and might be zero.
+ * offset is an absolute address in 32-bit protected mode address space.
+ */
+int fit_add_entry(struct fit_table *fit,
+ const uint32_t offset,
+ const uint32_t len,
+ const enum fit_type type,
+ const size_t max_fit_entries)
+{
+ struct fit_entry *entry;
+
+ if (!fit) {
+ ERROR("Internal error.");
+ return 1;
+ }
+
+ if (fit_free_space(fit, max_fit_entries) < 1) {
+ ERROR("No space left in FIT.");
+ return 1;
+ }
+
+ if (!fit_is_supported_type(type)) {
+ ERROR("Unsupported FIT type %u\n", type);
+ return 1;
+ }
+
+ DEBUG("Adding new entry type %u at offset %zd\n", type,
+ fit_table_entries(fit));
+
+ entry = get_next_free_entry(fit);
+
+ switch (type) {
+ case FIT_TYPE_MICROCODE:
+ update_fit_ucode_entry(fit, entry, offset);
+ break;
+ case FIT_TYPE_BIOS_ACM:
+ update_fit_bios_acm_entry(fit, entry, offset);
+ break;
+ case FIT_TYPE_BIOS_STARTUP:
+ update_fit_bios_startup_entry(fit, entry, offset, len);
+ break;
+ case FIT_TYPE_BIOS_POLICY:
+ update_fit_bios_policy_entry(fit, entry, offset, len);
+ break;
+ case FIT_TYPE_TXT_POLICY:
+ update_fit_txt_policy_entry(fit, entry, offset);
+ break;
+ default:
+ return 1;
+ }
+
+ sort_fit_table(fit);
+
+ update_fit_checksum(fit);
+
+ return 0;
+}
+
+/*
+ * Delete one entry from table.
+ */
+int fit_delete_entry(struct fit_table *fit,
+ const size_t idx)
+{
+ if (!fit) {
+ ERROR("Internal error.");
+ return 1;
+ }
+
+ if (idx >= fit_table_entries(fit)) {
+ ERROR("Index out of range.");
+ return 1;
+ }
+
+ memset(&fit->entries[idx], 0, sizeof(struct fit_entry));
+
+ fit->entries[idx].type_checksum_valid = FIT_TYPE_UNUSED;
+
+ sort_fit_table(fit);
+
+ /* The unused entry is now the last one */
+ fit_entry_add_size(&fit->header, -(int)sizeof(struct fit_entry));
+
+ update_fit_checksum(fit);
+
+ return 0;
+}
+
+/* Legacy code. TODO: Remove once ifittool is merged. */
+
+static void add_microcodde_entries(struct fit_table *fit,
+ const struct cbfs_image *image,
+ ssize_t num_mcus,
+ struct microcode_entry *mcus,
+ fit_offset_converter_t offset_helper,
+ uint32_t first_mcu_addr)
+{
+ int i = 0;
+ /*
+ * Check if an entry has to be forced into the FIT at index 0.
+ * first_mcu_addr is an address (in ROM) that will point to a
+ * microcode patch.
+ */
+ if (first_mcu_addr) {
+ struct fit_entry *entry = &fit->entries[0];
+ update_fit_ucode_entry(fit, entry, first_mcu_addr);
+ i = 1;
+ }
+
+ struct microcode_entry *mcu = &mcus[0];
+ for (; i < num_mcus; i++) {
+ struct fit_entry *entry = &fit->entries[i];
+ update_fit_ucode_entry(fit, entry, offset_to_ptr(offset_helper,
+ &image->buffer, mcu->offset));
+ mcu++;
+ }
+}
+
int fit_update_table(struct buffer *bootblock, struct cbfs_image *image,
- const char *microcode_blob_name, int empty_entries,
+ const char *microcode_blob_name,
+ unsigned int empty_entries,
fit_offset_converter_t offset_fn, uint32_t topswap_size,
- uint32_t first_mcu_addr)
+ uint32_t first_mcu_addr)
{
struct fit_table *fit, *fit2;
- struct cbfs_file *mcode_file;
struct microcode_entry *mcus;
- int mcus_found;
+ size_t mcus_found;
int ret = 0;
- // struct rom_image image = { .rom = rom, .size = romsize, };
-
- fit = locate_fit_table(offset_fn, bootblock);
+ fit = fit_get_table(bootblock, offset_fn, 0);
if (!fit) {
ERROR("FIT not found.\n");
return 1;
}
- mcode_file = cbfs_get_entry(image, microcode_blob_name);
- if (!mcode_file) {
- ERROR("File '%s' not found in CBFS.\n",
- microcode_blob_name);
- return 1;
- }
-
mcus = malloc(sizeof(*mcus) * empty_entries);
-
if (!mcus) {
- ERROR("Couldn't allocate memory for microcode update entries.\n");
+ ERROR("Couldn't allocate memory for microcode entries.\n");
return 1;
}
- if (parse_microcode_blob(image, mcode_file, mcus, empty_entries,
- &mcus_found)) {
+ if (parse_microcode_blob(image, microcode_blob_name, &mcus_found,
+ mcus, empty_entries)) {
ERROR("Couldn't parse microcode blob.\n");
ret = 1;
goto out;
@@ -333,7 +806,7 @@ int fit_update_table(struct buffer *bootblock, struct cbfs_image *image,
/* A second fit is exactly topswap size away from the bottom one */
if (topswap_size) {
- fit2 = (struct fit_table *)((uintptr_t)fit - topswap_size);
+ fit2 = fit_get_table(bootblock, offset_fn, topswap_size);
if (!fit_table_verified(fit2)) {
ERROR("second FIT is invalid\n");
@@ -343,8 +816,8 @@ int fit_update_table(struct buffer *bootblock, struct cbfs_image *image,
/* Check if we have room for first entry */
if (first_mcu_addr) {
if (mcus_found >= empty_entries) {
- ERROR("No room, blob mcus = %d, total entries = %d\n",
- mcus_found, empty_entries);
+ ERROR("No room, blob mcus = %zd, total entries"
+ " = %d\n", mcus_found, empty_entries);
ret = 1;
goto out;
}