| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| |
| #include "fv.h" |
| |
| #include <assert.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #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; |
| } |