f2b7104308
There is a function to fetch the fit table at both the regular address and the TS address. So reuse that function instead of attempting to find the TS fit using some pointer aritmetics that is incorrect. Change-Id: I9114f5439202ede7e01cd0fcbb1e3c4cdb8698b0 Signed-off-by: Arthur Heymans <arthur@aheymans.xyz> Reviewed-on: https://review.coreboot.org/c/coreboot/+/54680 Reviewed-by: Rizwan Qureshi <rizwan.qureshi@intel.com> Reviewed-by: Meera Ravindranath <meera.ravindranath@intel.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
787 lines
20 KiB
C
787 lines
20 KiB
C
/* Firmware Interface Table support */
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "fit.h"
|
|
|
|
/* FIXME: This code assumes it is being executed on a little endian machine. */
|
|
|
|
#define FIT_POINTER_LOCATION 0xffffffc0
|
|
#define FIT_TABLE_LOWEST_ADDRESS ((uint32_t)(-(16 << 20)))
|
|
#define FIT_ENTRY_CHECKSUM_VALID 0x80
|
|
#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;
|
|
|
|
struct fit_table {
|
|
struct fit_entry header;
|
|
struct fit_entry entries[];
|
|
} __packed;
|
|
|
|
struct microcode_header {
|
|
uint32_t version;
|
|
uint32_t revision;
|
|
uint32_t date;
|
|
uint32_t processor_signature;
|
|
uint32_t checksum;
|
|
uint32_t loader_revision;
|
|
uint32_t processor_flags;
|
|
uint32_t data_size;
|
|
uint32_t total_size;
|
|
uint8_t reserved[12];
|
|
} __packed;
|
|
|
|
struct microcode_entry {
|
|
int offset;
|
|
int size;
|
|
};
|
|
|
|
static inline void *rom_buffer_pointer(struct buffer *buffer, int offset)
|
|
{
|
|
return &buffer->data[offset];
|
|
}
|
|
|
|
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,
|
|
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,
|
|
const int size_bytes)
|
|
{
|
|
int size = fit_entry_size_bytes(entry);
|
|
size += size_bytes;
|
|
fit_entry_update_size(entry, size);
|
|
}
|
|
|
|
static inline int fit_entry_type(struct fit_entry *entry)
|
|
{
|
|
return entry->type_checksum_valid & ~FIT_ENTRY_CHECKSUM_VALID;
|
|
}
|
|
|
|
/*
|
|
* Get an offset from a host pointer. 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 int ptr_to_offset(fit_offset_converter_t helper,
|
|
const struct buffer *region, uint32_t host_ptr)
|
|
{
|
|
return helper(region, -host_ptr);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* 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 algorithm, 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)))
|
|
return 0;
|
|
|
|
if (table->header.version != FIT_HEADER_VERSION)
|
|
return 0;
|
|
|
|
if (fit_entry_type(&table->header) != FIT_TYPE_HEADER)
|
|
return 0;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/*
|
|
* Update the FIT checksum.
|
|
* To be called after modifiying the table.
|
|
*/
|
|
static void update_fit_checksum(struct fit_table *fit)
|
|
{
|
|
int size_bytes;
|
|
uint8_t *buffer;
|
|
uint8_t result;
|
|
int i;
|
|
|
|
if (!fit)
|
|
return;
|
|
|
|
fit->header.checksum = 0;
|
|
size_bytes = fit_entry_size_bytes(&fit->header);
|
|
result = 0;
|
|
buffer = (void *)fit;
|
|
for (i = 0; i < size_bytes; i++)
|
|
result += buffer[i];
|
|
fit->header.checksum = -result;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
return &fit->entries[fit_table_entries(fit)];
|
|
}
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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_location_from_cbfs_header(¤t_offset, &file_length,
|
|
mcode_file);
|
|
current_offset += cbfs_get_entry_addr(image, mcode_file);
|
|
|
|
num_mcus = 0;
|
|
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 ?: 2048;
|
|
|
|
/* Quickly sanity check a prospective microcode update. */
|
|
if (total_size < sizeof(*mcu_header))
|
|
break;
|
|
|
|
/* FIXME: Should the checksum be validated? */
|
|
mcus[num_mcus].offset = current_offset;
|
|
mcus[num_mcus].size = total_size;
|
|
|
|
/* Proceed to next payload. */
|
|
current_offset += mcus[num_mcus].size;
|
|
file_length -= mcus[num_mcus].size;
|
|
num_mcus++;
|
|
/* Reached limit of FIT entries. */
|
|
if (num_mcus == max_fit_entries)
|
|
break;
|
|
if (file_length < sizeof(struct microcode_header))
|
|
break;
|
|
}
|
|
|
|
/* Update how many microcode updates we found. */
|
|
*mcus_found = num_mcus;
|
|
|
|
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));
|
|
}
|
|
|
|
/*
|
|
* There can be zero or one FIT_TYPE_BOOT_POLICY entries
|
|
*
|
|
* The caller has to provide valid arguments as those aren't verified.
|
|
*/
|
|
static void update_fit_boot_policy_entry(struct fit_table *fit,
|
|
struct fit_entry *entry,
|
|
uint64_t boot_policy_addr,
|
|
uint32_t boot_policy_size)
|
|
{
|
|
entry->address = boot_policy_addr;
|
|
entry->type_checksum_valid = FIT_TYPE_BOOT_POLICY;
|
|
entry->size_reserved = boot_policy_size;
|
|
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_KEY_MANIFEST entries
|
|
*
|
|
* The caller has to provide valid arguments as those aren't verified.
|
|
*/
|
|
static void update_fit_key_manifest_entry(struct fit_table *fit,
|
|
struct fit_entry *entry,
|
|
uint64_t key_manifest_addr,
|
|
uint32_t key_manifest_size)
|
|
{
|
|
entry->address = key_manifest_addr;
|
|
|
|
entry->type_checksum_valid = FIT_TYPE_KEY_MANIFEST;
|
|
entry->size_reserved = key_manifest_size;
|
|
entry->version = FIT_TXT_VERSION;
|
|
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;
|
|
}
|
|
|
|
static uint32_t *get_fit_ptr(struct buffer *bootblock, fit_offset_converter_t offset_fn,
|
|
uint32_t topswap_size)
|
|
{
|
|
return rom_buffer_pointer(bootblock,
|
|
ptr_to_offset(offset_fn, bootblock,
|
|
FIT_POINTER_LOCATION - topswap_size));
|
|
}
|
|
|
|
/* Set the FIT pointer to a FIT table. */
|
|
int set_fit_pointer(struct buffer *bootblock,
|
|
const uint32_t fit_address,
|
|
fit_offset_converter_t offset_fn,
|
|
uint32_t topswap_size)
|
|
{
|
|
struct fit_table *fit;
|
|
uint32_t *fit_pointer = get_fit_ptr(bootblock, offset_fn, topswap_size);
|
|
|
|
fit = rom_buffer_pointer(bootblock, ptr_to_offset(offset_fn, bootblock, fit_address));
|
|
|
|
if (fit_address < FIT_TABLE_LOWEST_ADDRESS) {
|
|
ERROR("FIT must be reside in the top 16MiB.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!fit_table_verified(fit)) {
|
|
ERROR("FIT not found at address.\n");
|
|
return 1;
|
|
}
|
|
|
|
fit_pointer[0] = fit_address;
|
|
fit_pointer[1] = 0;
|
|
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 = get_fit_ptr(bootblock, offset_fn, topswap_size);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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:
|
|
case FIT_TYPE_KEY_MANIFEST:
|
|
case FIT_TYPE_BOOT_POLICY:
|
|
return 1;
|
|
case FIT_TYPE_TPM_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;
|
|
case FIT_TYPE_KEY_MANIFEST:
|
|
update_fit_key_manifest_entry(fit, entry, offset, len);
|
|
break;
|
|
case FIT_TYPE_BOOT_POLICY:
|
|
update_fit_boot_policy_entry(fit, entry, offset, len);
|
|
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;
|
|
}
|