From 04bd9651435843ce4b03c9717f2965fe344fe5cc Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Fri, 17 Nov 2023 19:31:20 +0200 Subject: util: add smmstoretool for editing SMMSTORE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Offline SMMSTORE variable modification tool. Can be used to pre-configure ROM image or debug EFI state stored in a dump. Change-Id: I6c1c06f1d0c39c13b5be76a3070f09b715aca6e0 Signed-off-by: Sergii Dmytruk Reviewed-on: https://review.coreboot.org/c/coreboot/+/79080 Tested-by: build bot (Jenkins) Reviewed-by: Michał Żygowski Reviewed-by: Arthur Heymans --- util/smmstoretool/fv.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 util/smmstoretool/fv.c (limited to 'util/smmstoretool/fv.c') diff --git a/util/smmstoretool/fv.c b/util/smmstoretool/fv.c new file mode 100644 index 0000000000..c9d342e162 --- /dev/null +++ b/util/smmstoretool/fv.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "fv.h" + +#include +#include +#include +#include +#include + +#include "udk2017.h" + +// The same as in `smmstore.h` header, which isn't in `commonlib`. +#define SMM_BLOCK_SIZE (64*1024) + +static const EFI_GUID EfiVariableGuid = EFI_VARIABLE_GUID; + +static const EFI_GUID EfiAuthenticatedVariableGuid = + EFI_AUTHENTICATED_VARIABLE_GUID; + +static const EFI_GUID EfiSystemNvDataFvGuid = { + 0xfff12b8d, 0x7696, 0x4c8b, + { 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50 } +}; + +static uint16_t calc_checksum(const uint16_t *hdr, size_t size) +{ + assert(size % 2 == 0 && "Header can't have odd length."); + + uint16_t checksum = 0; + for (size_t i = 0; i < size / 2; ++i) + checksum += hdr[i]; + return checksum; +} + +bool +fv_init(struct mem_range_t fv) +{ + if (fv.length % SMM_BLOCK_SIZE != 0) { + fprintf(stderr, + "Firmware Volume size is not a multiple of 64KiB\n"); + return false; + } + + memset(fv.start, 0xff, fv.length); + + const EFI_FIRMWARE_VOLUME_HEADER vol_hdr = { + .FileSystemGuid = EfiSystemNvDataFvGuid, + .FvLength = fv.length, + .Signature = EFI_FVH_SIGNATURE, + .Attributes = EFI_FVB2_READ_ENABLED_CAP + | EFI_FVB2_READ_STATUS + | EFI_FVB2_WRITE_ENABLED_CAP + | EFI_FVB2_WRITE_STATUS + | EFI_FVB2_STICKY_WRITE + | EFI_FVB2_MEMORY_MAPPED + | EFI_FVB2_ERASE_POLARITY, + .HeaderLength = sizeof(vol_hdr) + + sizeof(EFI_FV_BLOCK_MAP_ENTRY), + .Revision = EFI_FVH_REVISION, + .BlockMap[0] = { + .NumBlocks = fv.length / SMM_BLOCK_SIZE, + .Length = SMM_BLOCK_SIZE, + }, + }; + + EFI_FIRMWARE_VOLUME_HEADER *vol_hdr_dst = (void *)fv.start; + *vol_hdr_dst = vol_hdr; + vol_hdr_dst->BlockMap[1].NumBlocks = 0; + vol_hdr_dst->BlockMap[1].Length = 0; + + vol_hdr_dst->Checksum = + ~calc_checksum((const void *)vol_hdr_dst, vol_hdr.HeaderLength); + ++vol_hdr_dst->Checksum; + + const VARIABLE_STORE_HEADER var_store_hdr = { + // Authentication-related fields will be filled with 0xff. + .Signature = EfiAuthenticatedVariableGuid, + // Actual size of the storage is block size, the rest is + // Fault Tolerant Write (FTW) space and the FTW spare space. + .Size = SMM_BLOCK_SIZE - vol_hdr.HeaderLength, + .Format = VARIABLE_STORE_FORMATTED, + .State = VARIABLE_STORE_HEALTHY, + }; + + VARIABLE_STORE_HEADER *var_store_hdr_dst = + (void *)(fv.start + vol_hdr.HeaderLength); + *var_store_hdr_dst = var_store_hdr; + + return true; +} + +static bool guid_eq(const EFI_GUID *lhs, const EFI_GUID *rhs) +{ + return memcmp(lhs, rhs, sizeof(*lhs)) == 0; +} + +static bool check_fw_vol_hdr(const EFI_FIRMWARE_VOLUME_HEADER *hdr, + size_t max_size) +{ + if (hdr->Revision != EFI_FVH_REVISION || + hdr->Signature != EFI_FVH_SIGNATURE || + hdr->FvLength > max_size || + hdr->HeaderLength > max_size || + hdr->HeaderLength % 2 != 0) { + fprintf(stderr, "No firmware volume header present\n"); + return false; + } + + if (!guid_eq(&hdr->FileSystemGuid, &EfiSystemNvDataFvGuid)) { + fprintf(stderr, "Firmware volume GUID non-compatible\n"); + return false; + } + + uint16_t checksum = calc_checksum((const void *)hdr, hdr->HeaderLength); + if (checksum != 0) { + fprintf(stderr, + "Firmware Volume checksum is non-zero: 0x%04X\n", + checksum); + return false; + } + + return true; +} + +static bool check_var_store_hdr(const VARIABLE_STORE_HEADER *hdr, + size_t max_size, + bool *auth_vars) +{ + *auth_vars = guid_eq(&hdr->Signature, &EfiAuthenticatedVariableGuid); + if (!*auth_vars && !guid_eq(&hdr->Signature, &EfiVariableGuid)) { + fprintf(stderr, "Variable store has unexpected GUID\n"); + return false; + } + + if (hdr->Size > max_size) { + fprintf(stderr, "Variable store size is too large: %zu > %zu\n", + (size_t)hdr->Size, max_size); + return false; + } + + if (hdr->Format != VARIABLE_STORE_FORMATTED) { + fprintf(stderr, "Variable store is not formatted\n"); + return false; + } + + if (hdr->State != VARIABLE_STORE_HEALTHY) { + fprintf(stderr, "Variable store is not in a healthy state\n"); + return false; + } + + return true; +} + +bool +fv_parse(struct mem_range_t fv, struct mem_range_t *var_store, bool *auth_vars) +{ + const EFI_FIRMWARE_VOLUME_HEADER *vol_hdr = (void *)fv.start; + if (!check_fw_vol_hdr(vol_hdr, fv.length)) { + fprintf(stderr, "No valid firmware volume was found\n"); + return false; + } + + uint8_t *fw_vol_data = fv.start + vol_hdr->HeaderLength; + size_t volume_size = fv.length - vol_hdr->HeaderLength; + const VARIABLE_STORE_HEADER *var_store_hdr = (void *)fw_vol_data; + if (!check_var_store_hdr(var_store_hdr, volume_size, auth_vars)) { + fprintf(stderr, "No valid variable store was found"); + return false; + } + + var_store->start = fw_vol_data + sizeof(*var_store_hdr); + var_store->length = volume_size - sizeof(*var_store_hdr); + return true; +} -- cgit v1.2.3