Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/52564 )
Change subject: [WIP]drivers/efi: Add UEFI variable store option support ......................................................................
[WIP]drivers/efi: Add UEFI variable store option support
Add a driver to read UEFI variables stored in the SMMSTORE region. This is particullary useful for EDK2 as payload and allows to reuse existing tools to set/get options used by the firmware.
No write support has been implemented yet.
Change-Id: I8079f71d29da5dc2db956fc68bef1486fe3906bb Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- A src/drivers/efi/Kconfig A src/drivers/efi/Makefile.inc A src/drivers/efi/option.c M src/drivers/smmstore/Makefile.inc M src/drivers/smmstore/store.c M src/include/option.h M src/include/smmstore.h M src/vendorcode/intel/Makefile.inc A src/vendorcode/intel/edk2/UDK2017/MdeModulePkg/Include/Guid/VariableFormat.h A src/vendorcode/intel/edk2/option/option.c A src/vendorcode/intel/edk2/option/option.h 11 files changed, 710 insertions(+), 7 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/64/52564/1
diff --git a/src/drivers/efi/Kconfig b/src/drivers/efi/Kconfig new file mode 100644 index 0000000..8fbd426 --- /dev/null +++ b/src/drivers/efi/Kconfig @@ -0,0 +1,4 @@ +config DRIVERS_EFI_SMMSTORE_OPTIONS + bool "Use UEFI variable store in SMMSTORE" + default y if SMMSTORE_V2 && PAYLOAD_TIANOCORE + depends on SMMSTORE_V2 diff --git a/src/drivers/efi/Makefile.inc b/src/drivers/efi/Makefile.inc new file mode 100644 index 0000000..a047711 --- /dev/null +++ b/src/drivers/efi/Makefile.inc @@ -0,0 +1,3 @@ + +all-$(CONFIG_DRIVERS_EFI_SMMSTORE_OPTIONS) += option.c +smm-$(CONFIG_DRIVERS_EFI_SMMSTORE_OPTIONS) += option.c diff --git a/src/drivers/efi/option.c b/src/drivers/efi/option.c new file mode 100644 index 0000000..9237ba0 --- /dev/null +++ b/src/drivers/efi/option.c @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <option.h> +#include <fmap.h> +#include <boot_device.h> +#include <vendorcode/intel/edk2/option/option.h> +#include <smmstore.h> + +enum cb_err efi_smmstore_get_option(const char *name, void **dest, uint32_t *size) +{ + struct region_device rdev; + if (!smmstore_lookup_region(&rdev)) { + return CB_CMOS_OTABLE_DISABLED; + } + return efi_fv_get_option(&rdev, name, dest, size); +} + +enum cb_err efi_smmstore_set_option(const char *name, void *data, size_t size) +{ + struct region_device rdev; + if (!smmstore_lookup_region(&rdev)) { + return CB_CMOS_OTABLE_DISABLED; + } + return efi_fv_set_option(&rdev, name, data, size); +} diff --git a/src/drivers/smmstore/Makefile.inc b/src/drivers/smmstore/Makefile.inc index 90bcdec..8e17cf4 100644 --- a/src/drivers/smmstore/Makefile.inc +++ b/src/drivers/smmstore/Makefile.inc @@ -1,4 +1,4 @@ -ramstage-$(CONFIG_SMMSTORE) += store.c +all-$(CONFIG_SMMSTORE) += store.c ramstage-$(CONFIG_SMMSTORE_V2) += ramstage.c
smm-$(CONFIG_SMMSTORE) += store.c smi.c diff --git a/src/drivers/smmstore/store.c b/src/drivers/smmstore/store.c index 9f9ab01..fd30367 100644 --- a/src/drivers/smmstore/store.c +++ b/src/drivers/smmstore/store.c @@ -94,6 +94,20 @@ return rdev_chain(rstore, rdev, 0, region_device_sz(rdev)); }
+int smmstore_lookup_region(struct region_device *rstore) +{ + static int done; + static int ret; + static struct region_device rdev; + + if (!done) { + done = 1; + ret = lookup_store(&rdev); + } + *rstore = rdev; + + return ret; +} /* * Read entire store into user provided buffer * @@ -296,7 +310,6 @@ return 0; }
-#if ENV_RAMSTAGE /** * Provide metadata for the coreboot tables. * Must only be called in ramstage, but not in SMM. @@ -326,7 +339,6 @@
return 0; } -#endif
/* Returns -1 on error, 0 on success */ static int lookup_block_in_store(struct region_device *store, uint32_t block_id) diff --git a/src/include/option.h b/src/include/option.h index 4de032b..1e20932 100644 --- a/src/include/option.h +++ b/src/include/option.h @@ -4,27 +4,47 @@ #define _OPTION_H_
#include <types.h> +#include <string.h> +#include <commonlib/region.h>
void sanitize_cmos(void);
enum cb_err cmos_set_option(const char *name, void *val); enum cb_err cmos_get_option(void *dest, const char *name);
+enum cb_err efi_smmstore_get_option(const char *name, void **dest, uint32_t *size); +enum cb_err efi_smmstore_set_option(const char *name, void *data, size_t size); + static inline enum cb_err set_int_option(const char *name, int value) { if (CONFIG(USE_OPTION_TABLE)) return cmos_set_option(name, &value); + else if (CONFIG(DRIVERS_EFI_SMMSTORE_OPTIONS)) { + uint32_t size = sizeof(value); + return efi_smmstore_set_option(name, (void **)&value, size); + }
return CB_CMOS_OTABLE_DISABLED; }
static inline int get_int_option(const char *name, const int fallback) { + int value = 0; + if (CONFIG(USE_OPTION_TABLE)) { - int value = 0; - if (cmos_get_option(&value, name) == CB_SUCCESS) + if (cmos_get_option(&value, name) == CB_SUCCESS) + return value; + } else if (CONFIG(DRIVERS_EFI_SMMSTORE_OPTIONS)) { + void *data = NULL; + uint32_t size = 0; + if(efi_smmstore_get_option(name, (void **)&value, &size) == CB_SUCCESS) { + if (size > sizeof(value)) + size = sizeof(value); + memcpy(&value, data, size); return value; + } } + return fallback; }
diff --git a/src/include/smmstore.h b/src/include/smmstore.h index 2c37ca3..9dd1a0c 100644 --- a/src/include/smmstore.h +++ b/src/include/smmstore.h @@ -112,9 +112,8 @@ int smmstore_rawread_region(uint32_t block_id, uint32_t offset, uint32_t bufsize); int smmstore_rawwrite_region(uint32_t block_id, uint32_t offset, uint32_t bufsize); int smmstore_rawclear_region(uint32_t block_id); -#if ENV_RAMSTAGE int smmstore_get_info(struct smmstore_params_info *info); -#endif +int smmstore_lookup_region(struct region_device *rstore);
/* Advertise SMMSTORE v2 support */ struct lb_header; diff --git a/src/vendorcode/intel/Makefile.inc b/src/vendorcode/intel/Makefile.inc index b49dc64..ff2974b 100644 --- a/src/vendorcode/intel/Makefile.inc +++ b/src/vendorcode/intel/Makefile.inc @@ -19,6 +19,9 @@ CPPFLAGS_x86_64 += -I$(src)/vendorcode/intel/edk2/UDK2017/MdePkg/Include/X64 CPPFLAGS_common += -I$(src)/vendorcode/intel/edk2/UDK2017/MdePkg/Include CPPFLAGS_common += -I$(src)/vendorcode/intel/edk2/UDK2017/IntelFsp2Pkg/Include +CPPFLAGS_common += -I$(src)/vendorcode/intel/edk2/UDK2017/MdeModulePkg/Include +all-y += edk2/option/option.c +smm-y += edk2/option/option.c else ifeq ($(CONFIG_UDK_202005_BINDING),y) CPPFLAGS_x86_32 += -I$(src)/vendorcode/intel/edk2/edk2-stable202005/MdePkg/Include/Ia32 CPPFLAGS_x86_64 += -I$(src)/vendorcode/intel/edk2/edk2-stable202005/MdePkg/Include/X64 diff --git a/src/vendorcode/intel/edk2/UDK2017/MdeModulePkg/Include/Guid/VariableFormat.h b/src/vendorcode/intel/edk2/UDK2017/MdeModulePkg/Include/Guid/VariableFormat.h new file mode 100644 index 0000000..a5574fe --- /dev/null +++ b/src/vendorcode/intel/edk2/UDK2017/MdeModulePkg/Include/Guid/VariableFormat.h @@ -0,0 +1,221 @@ +/** @file + The variable data structures are related to EDK II-specific implementation of UEFI variables. + VariableFormat.h defines variable data headers and variable storage region headers. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __VARIABLE_FORMAT_H__ +#define __VARIABLE_FORMAT_H__ + +#define EFI_VARIABLE_GUID \ + { 0xddcf3616, 0x3275, 0x4164, { 0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d } } + +#define EFI_AUTHENTICATED_VARIABLE_GUID \ + { 0xaaf32c78, 0x947b, 0x439a, { 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92 } } + +extern EFI_GUID gEfiVariableGuid; +extern EFI_GUID gEfiAuthenticatedVariableGuid; + +/// +/// Alignment of variable name and data, according to the architecture: +/// * For IA-32 and Intel(R) 64 architectures: 1. +/// +#define ALIGNMENT 1 + +// +// GET_PAD_SIZE calculates the miminal pad bytes needed to make the current pad size satisfy the alignment requirement. +// +#if (ALIGNMENT == 1) +#define GET_PAD_SIZE(a) (0) +#else +#define GET_PAD_SIZE(a) (((~a) + 1) & (ALIGNMENT - 1)) +#endif + +/// +/// Alignment of Variable Data Header in Variable Store region. +/// +#define HEADER_ALIGNMENT 4 +#define HEADER_ALIGN(Header) (((UINTN) (Header) + HEADER_ALIGNMENT - 1) & (~(HEADER_ALIGNMENT - 1))) + +/// +/// Status of Variable Store Region. +/// +typedef enum { + EfiRaw, + EfiValid, + EfiInvalid, + EfiUnknown +} VARIABLE_STORE_STATUS; + +#pragma pack(1) + +#define VARIABLE_STORE_SIGNATURE EFI_VARIABLE_GUID +#define AUTHENTICATED_VARIABLE_STORE_SIGNATURE EFI_AUTHENTICATED_VARIABLE_GUID + +/// +/// Variable Store Header Format and State. +/// +#define VARIABLE_STORE_FORMATTED 0x5a +#define VARIABLE_STORE_HEALTHY 0xfe + +/// +/// Variable Store region header. +/// +typedef struct { + /// + /// Variable store region signature. + /// + EFI_GUID Signature; + /// + /// Size of entire variable store, + /// including size of variable store header but not including the size of FvHeader. + /// + UINT32 Size; + /// + /// Variable region format state. + /// + UINT8 Format; + /// + /// Variable region healthy state. + /// + UINT8 State; + UINT16 Reserved; + UINT32 Reserved1; +} VARIABLE_STORE_HEADER; + +/// +/// Variable data start flag. +/// +#define VARIABLE_DATA 0x55AA + +/// +/// Variable State flags. +/// +#define VAR_IN_DELETED_TRANSITION 0xfe ///< Variable is in obsolete transition. +#define VAR_DELETED 0xfd ///< Variable is obsolete. +#define VAR_HEADER_VALID_ONLY 0x7f ///< Variable header has been valid. +#define VAR_ADDED 0x3f ///< Variable has been completely added. + +/// +/// Variable Attribute combinations. +/// +#define VARIABLE_ATTRIBUTE_NV_BS (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) +#define VARIABLE_ATTRIBUTE_BS_RT (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS) +#define VARIABLE_ATTRIBUTE_BS_RT_AT (VARIABLE_ATTRIBUTE_BS_RT | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) +#define VARIABLE_ATTRIBUTE_NV_BS_RT (VARIABLE_ATTRIBUTE_BS_RT | EFI_VARIABLE_NON_VOLATILE) +#define VARIABLE_ATTRIBUTE_NV_BS_RT_HR (VARIABLE_ATTRIBUTE_NV_BS_RT | EFI_VARIABLE_HARDWARE_ERROR_RECORD) +#define VARIABLE_ATTRIBUTE_NV_BS_RT_AT (VARIABLE_ATTRIBUTE_NV_BS_RT | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) +#define VARIABLE_ATTRIBUTE_AT EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS +#define VARIABLE_ATTRIBUTE_NV_BS_RT_HR_AT (VARIABLE_ATTRIBUTE_NV_BS_RT_HR | VARIABLE_ATTRIBUTE_AT) +/// +/// EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated and should be considered as reserved +/// +#define VARIABLE_ATTRIBUTE_AT_AW (EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) +#define VARIABLE_ATTRIBUTE_NV_BS_RT_AW (VARIABLE_ATTRIBUTE_NV_BS_RT | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) +#define VARIABLE_ATTRIBUTE_NV_BS_RT_HR_AT_AW (VARIABLE_ATTRIBUTE_NV_BS_RT_HR | VARIABLE_ATTRIBUTE_AT_AW) + +/// +/// Single Variable Data Header Structure. +/// +typedef struct { + /// + /// Variable Data Start Flag. + /// + UINT16 StartId; + /// + /// Variable State defined above. + /// + UINT8 State; + UINT8 Reserved; + /// + /// Attributes of variable defined in UEFI specification. + /// + UINT32 Attributes; + /// + /// Size of variable null-terminated Unicode string name. + /// + UINT32 NameSize; + /// + /// Size of the variable data without this header. + /// + UINT32 DataSize; + /// + /// A unique identifier for the vendor that produces and consumes this varaible. + /// + EFI_GUID VendorGuid; +} VARIABLE_HEADER; + +/// +/// Single Authenticated Variable Data Header Structure. +/// +typedef struct { + /// + /// Variable Data Start Flag. + /// + UINT16 StartId; + /// + /// Variable State defined above. + /// + UINT8 State; + UINT8 Reserved; + /// + /// Attributes of variable defined in UEFI specification. + /// + UINT32 Attributes; + /// + /// Associated monotonic count value against replay attack. + /// + UINT64 MonotonicCount; + /// + /// Associated TimeStamp value against replay attack. + /// + EFI_TIME TimeStamp; + /// + /// Index of associated public key in database. + /// + UINT32 PubKeyIndex; + /// + /// Size of variable null-terminated Unicode string name. + /// + UINT32 NameSize; + /// + /// Size of the variable data without this header. + /// + UINT32 DataSize; + /// + /// A unique identifier for the vendor that produces and consumes this varaible. + /// + EFI_GUID VendorGuid; +} AUTHENTICATED_VARIABLE_HEADER; + +typedef struct { + EFI_GUID *Guid; + CHAR16 *Name; + UINTN VariableSize; +} VARIABLE_ENTRY_CONSISTENCY; + +#pragma pack() + +typedef struct _VARIABLE_INFO_ENTRY VARIABLE_INFO_ENTRY; + +/// +/// This structure contains the variable list that is put in EFI system table. +/// The variable driver collects all variables that were used at boot service time and produces this list. +/// This is an optional feature to dump all used variables in shell environment. +/// +struct _VARIABLE_INFO_ENTRY { + VARIABLE_INFO_ENTRY *Next; ///< Pointer to next entry. + EFI_GUID VendorGuid; ///< Guid of Variable. + CHAR16 *Name; ///< Name of Variable. + UINT32 Attributes; ///< Attributes of variable defined in UEFI specification. + UINT32 ReadCount; ///< Number of times to read this variable. + UINT32 WriteCount; ///< Number of times to write this variable. + UINT32 DeleteCount; ///< Number of times to delete this variable. + UINT32 CacheCount; ///< Number of times that cache hits this variable. + BOOLEAN Volatile; ///< TRUE if volatile, FALSE if non-volatile. +}; + +#endif // _EFI_VARIABLE_H_ diff --git a/src/vendorcode/intel/edk2/option/option.c b/src/vendorcode/intel/edk2/option/option.c new file mode 100644 index 0000000..9cfe728 --- /dev/null +++ b/src/vendorcode/intel/edk2/option/option.c @@ -0,0 +1,403 @@ +#include <stdlib.h> +#include <string.h> +#include <console/console.h> +#include <option.h> +#include <smmstore.h> + +#include <Uefi/UefiBaseType.h> +#include <Pi/PiFirmwareVolume.h> +#include <Guid/VariableFormat.h> +#include <vendorcode/intel/edk2/option/option.h> + +static bool CompareGuid(const EFI_GUID *a, const EFI_GUID *b) { + return memcmp(a, b, 16) == 0; +} + +static +UINT16 +CalculateSum16 ( + IN UINT16 *Buffer, + IN UINTN Size + ) +{ + UINTN Index; + UINT16 Sum; + + Sum = 0; + Size /= sizeof (*Buffer); + + // + // Perform the word sum for buffer + // + for (Index = 0; Index < Size; Index++) { + Sum = (UINT16) (Sum + Buffer[Index]); + } + + return (UINT16) Sum; +} + + +/** + + Gets the pointer to the first variable header in given variable store area. + + @param[in] VarStoreHeader Pointer to the Variable Store Header. + + @return Pointer to the first variable header. + +**/ +STATIC +VARIABLE_HEADER * +GetStartPointer ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ) +{ + // + // The start of variable store. + // + return (VARIABLE_HEADER *) ALIGN_UP ((uintptr_t)(VarStoreHeader + 1), 4); +} + +/** + + Gets the pointer to the end of the variable storage area. + + This function gets pointer to the end of the variable storage + area, according to the input variable store header. + + @param[in] VarStoreHeader Pointer to the Variable Store Header. + + @return Pointer to the end of the variable storage area. + +**/ +STATIC +VARIABLE_HEADER * +GetEndPointer ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ) +{ + // + // The end of variable store + // + return (VARIABLE_HEADER *) ALIGN_UP ((UINTN) VarStoreHeader + VarStoreHeader->Size, 4); +} + +STATIC +CHAR16 * +CharToWChar ( + IN CONST CHAR8 *msg + ) +{ + static CHAR16 buf[32]; + + if (strlen(msg) >= sizeof(buf)) + return NULL; + + // Simple ASCII to UTF-16 conversion + for (int i = 0; i < strlen(msg) + 1; i++) { + buf[i] = msg[i]; + } + return buf; +} + +/** + Check the integrity of firmware volume header. + + @param[in] FwVolHeader - A pointer to a firmware volume header + + @retval EFI_SUCCESS - The firmware volume is consistent + @retval EFI_NOT_FOUND - The firmware volume has been corrupted. + +**/ +STATIC +EFI_STATUS +ValidateFvHeader ( + IN CONST VOID *FvBaseAddress, + IN CONST UINTN FvSize, + OUT EFI_FIRMWARE_VOLUME_HEADER **FwVolHeader + ) +{ + UINT16 Checksum; + + STATIC CONST EFI_GUID EfiSystemNvDataFvGuid = { 0xFFF12B8D, 0x7696, 0x4C8B, { 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50 }}; + printk (BIOS_INFO, "%s: Entering %p\n", __func__, FvBaseAddress); + + *FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER*)FvBaseAddress; + + // + // Verify the header revision, header signature, length + // Length of FvBlock cannot be 2**64-1 + // HeaderLength cannot be an odd number + // + if ( ((*FwVolHeader)->Revision != EFI_FVH_REVISION) + || ((*FwVolHeader)->Signature != EFI_FVH_SIGNATURE) + || ((*FwVolHeader)->FvLength > FvSize) + || ((*FwVolHeader)->HeaderLength > FvSize) + || ((*FwVolHeader)->HeaderLength & 1) + ) + { + printk (BIOS_INFO, "%s: No Firmware Volume header present\n", + __FUNCTION__); + return EFI_NOT_FOUND; + } + + // Check the Firmware Volume Guid + if( CompareGuid (&(*FwVolHeader)->FileSystemGuid, &EfiSystemNvDataFvGuid) == FALSE ) { + printk (BIOS_INFO, "%s: Firmware Volume Guid non-compatible\n", + __FUNCTION__); + return EFI_NOT_FOUND; + } + + // Verify the header checksum + Checksum = CalculateSum16((UINT16*)*FwVolHeader, (*FwVolHeader)->HeaderLength); + if (Checksum != 0) { + printk (BIOS_INFO, "%s: FV checksum is invalid (Checksum:0x%X)\n", + __FUNCTION__, Checksum); + return EFI_NOT_FOUND; + } + + printk(BIOS_INFO, "%s: UEFI FV with size %lld found at %p\n", + __FUNCTION__, (*FwVolHeader)->FvLength, *FwVolHeader); + + return EFI_SUCCESS; +} + + +/** + Check the integrity of firmware volume header. + + @param[in] FwVolHeader - A pointer to a firmware volume header + + @retval EFI_SUCCESS - The firmware volume is consistent + @retval EFI_NOT_FOUND - The firmware volume has been corrupted. + +**/ +STATIC +EFI_STATUS +ValidateVariableStoreHeader ( + IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader, + IN CONST UINTN FvSize, + OUT VARIABLE_STORE_HEADER **VariableStoreHeader, + OUT BOOLEAN *AuthFormat + ) +{ + UINTN VariableStoreLength; + STATIC CONST EFI_GUID EfiVariableGuid = { 0xddcf3616, 0x3275, 0x4164, { 0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d } }; + STATIC CONST EFI_GUID EfiAuthenticatedVariableGuid = { 0xaaf32c78, 0x947b, 0x439a, { 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92 } }; + + *VariableStoreHeader = (VARIABLE_STORE_HEADER*)((UINTN)FwVolHeader + FwVolHeader->HeaderLength); + + // Check the Variable Store Guid + if (!CompareGuid (&(*VariableStoreHeader)->Signature, &EfiVariableGuid) && + !CompareGuid (&(*VariableStoreHeader)->Signature, &EfiAuthenticatedVariableGuid)) { + printk (BIOS_INFO, "%s: Variable Store Guid non-compatible\n", + __FUNCTION__); + return EFI_NOT_FOUND; + } + + *AuthFormat = CompareGuid (&(*VariableStoreHeader)->Signature, &EfiAuthenticatedVariableGuid); + + VariableStoreLength = FvSize - FwVolHeader->HeaderLength; + if ((*VariableStoreHeader)->Size > VariableStoreLength) { + printk (BIOS_INFO, "%s: Variable Store Length does not match\n", + __FUNCTION__); + return EFI_NOT_FOUND; + } + + if ((*VariableStoreHeader)->Format != VARIABLE_STORE_FORMATTED) { + return EFI_NOT_FOUND; + } + + if ((*VariableStoreHeader)->State != VARIABLE_STORE_HEALTHY) { + return EFI_NOT_FOUND; + } + + printk(BIOS_INFO, "%s: UEFI variable store with size %d at %p\n", + __FUNCTION__, (*VariableStoreHeader)->Size, *VariableStoreHeader); + + + return EFI_SUCCESS; +} + +static int find_auth_varible(VARIABLE_STORE_HEADER *VariableStoreHeader, CONST CHAR8 *Name, VOID **Data, UINT32 *Size) { + AUTHENTICATED_VARIABLE_HEADER *ptr, *next; + CHAR16 *VarName; + VOID *VarData; + UINTN NameSize; + UINTN DataSize; + CHAR16 *WName; + + *Data = NULL; + *Size = 0; + + WName = CharToWChar(Name); + if (!WName) + return CB_ERR; + + for (ptr = (AUTHENTICATED_VARIABLE_HEADER *)GetStartPointer(VariableStoreHeader); + ptr && + ptr >= (AUTHENTICATED_VARIABLE_HEADER *)GetStartPointer(VariableStoreHeader) && + ptr < (AUTHENTICATED_VARIABLE_HEADER *)GetEndPointer(VariableStoreHeader) && + ptr->StartId == VARIABLE_DATA; + ptr = next) { + + VarName = (CHAR16 *)(ptr + 1); + NameSize = ptr->NameSize; + DataSize = ptr->DataSize; + + if (ptr->State == (UINT8) (-1) || + ptr->DataSize == (UINT32) (-1) || + ptr->NameSize == (UINT32) (-1) || + ptr->Attributes == (UINT32) (-1)) { + NameSize = 0; + DataSize = 0; + } + + VarData = ((uint8_t *)VarName) + NameSize; + next = (AUTHENTICATED_VARIABLE_HEADER*)(((uint8_t *)VarData) + DataSize); + next = (AUTHENTICATED_VARIABLE_HEADER*)ALIGN_UP((uintptr_t)next, 4); + if (NameSize == 0 || DataSize == 0) + continue; + if (memcmp(VarName, WName, strlen(Name) * 2) != 0) + continue; + + *Data = VarData; + *Size = DataSize; + } + + if (!*Size) + return CB_CMOS_OPTION_NOT_FOUND; + + return CB_SUCCESS; +} + +static int find_varible(VARIABLE_STORE_HEADER *VariableStoreHeader, CONST CHAR8 *Name, VOID **Data, UINT32 *Size) { + VARIABLE_HEADER *ptr, *next; + CHAR16 *VarName; + VOID *VarData; + UINTN NameSize; + UINTN DataSize; + CHAR16 *WName; + + *Data = NULL; + *Size = 0; + + WName = CharToWChar(Name); + if (!WName) + return CB_ERR; + + printk(BIOS_ERR, "%s:%p\n", __func__, VariableStoreHeader); + + for (ptr = (VARIABLE_HEADER *)GetStartPointer(VariableStoreHeader); + ptr && + ptr >= (VARIABLE_HEADER *)GetStartPointer(VariableStoreHeader) && + ptr < (VARIABLE_HEADER *)GetEndPointer(VariableStoreHeader) && + ptr->StartId == VARIABLE_DATA; + ptr = next) { + VarName = (CHAR16 *)(ptr + 1); + NameSize = ptr->NameSize; + DataSize = ptr->DataSize; + + if (ptr->State == (UINT8) (-1) || + ptr->DataSize == (UINT32) (-1) || + ptr->NameSize == (UINT32) (-1) || + ptr->Attributes == (UINT32) (-1)) { + NameSize = 0; + DataSize = 0; + } + + VarData = ((uint8_t *)VarName) + NameSize; + next = (VARIABLE_HEADER*)(((uint8_t *)VarData) + DataSize); + next = (VARIABLE_HEADER*)ALIGN_UP((uintptr_t)next, 4); + + if (NameSize == 0 || DataSize == 0) + continue; + if (memcmp(VarName, WName, strlen(Name) * 2) != 0) + continue; + *Data = VarData; + *Size = DataSize; + } + + if (!*Size) + return CB_CMOS_OPTION_NOT_FOUND; + + return CB_SUCCESS; +} + + +/* + * efi_fv_get_option returns a pointer to readonly MMAP SPI flash variable storage. + * The following assumptions are made: + * - SPI flash is memory mapped + * - Running on x86_32/x86_64 + * - A UEFI firmware volume containing a VariableStorage is placed at the beginning + * of the SMMSTORE + * - The SMMSTORE has been initialized + * - The last variable found with matching name contains the data to return + */ +enum cb_err efi_fv_get_option(struct region_device *fv, const char *name, void **dest, uint32_t *size) +{ + VARIABLE_STORE_HEADER *VariableStoreHeader = NULL; + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader = NULL; + VOID *RDevMMAP; + UINTN FvSize; + BOOLEAN AuthFormat; + enum cb_err err; + + RDevMMAP = rdev_mmap_full(fv); + FvSize = region_device_sz(fv); + if (!RDevMMAP || !FvSize) + return CB_CMOS_LAYOUT_NOT_FOUND; + + if (ValidateFvHeader(RDevMMAP, FvSize, &FwVolHeader) != EFI_SUCCESS) { + return CB_CMOS_CHECKSUM_INVALID; + } + + if (ValidateVariableStoreHeader(FwVolHeader, FvSize, &VariableStoreHeader, &AuthFormat) != EFI_SUCCESS) { + return CB_CMOS_CHECKSUM_INVALID; + } + + if (AuthFormat) { + err = find_auth_varible(VariableStoreHeader, name, dest, size); + } else { + err = find_varible(VariableStoreHeader, name, dest, size); + } + + return err; +} + +/* + * efi_fv_set_option writes a new variable to the VariableStore. + * The following assumptions are made: + * - SPI flash is memory mapped + * - Running on x86_32/x86_64 + * - A UEFI firmware volume containing a VariableStorage is placed at the beginning + * of the SMMSTORE + * - The SMMSTORE has been initialized + * - There is enough free space in the VariableStorage + * - The data is backed by a NOR flash, that means settings bits is not possible + */ +enum cb_err efi_fv_set_option(struct region_device *fv, const char *name, void *data, size_t size) +{ + VARIABLE_STORE_HEADER *VariableStoreHeader; + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + VOID *RDevMMAP; + UINTN FvSize; + BOOLEAN AuthFormat; + + RDevMMAP = rdev_mmap_full(fv); + FvSize = region_device_sz(fv); + if (!RDevMMAP || !FvSize) + return CB_CMOS_LAYOUT_NOT_FOUND; + + if (ValidateFvHeader(RDevMMAP, FvSize, &FwVolHeader) != EFI_SUCCESS) { + return CB_CMOS_CHECKSUM_INVALID; + } + + if (ValidateVariableStoreHeader(FwVolHeader, FvSize, &VariableStoreHeader, &AuthFormat) != EFI_SUCCESS) { + return CB_CMOS_CHECKSUM_INVALID; + } + + //FIXME: Implement this + return CB_ERR; +} \ No newline at end of file diff --git a/src/vendorcode/intel/edk2/option/option.h b/src/vendorcode/intel/edk2/option/option.h new file mode 100644 index 0000000..e764d21 --- /dev/null +++ b/src/vendorcode/intel/edk2/option/option.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _EDK2_OPTION_H_ +#define _EDK2_OPTION_H_ + +#include <types.h> +#include <commonlib/region.h> + +enum cb_err efi_fv_get_option(struct region_device *fv, const char *name, void **dest, uint32_t *size); +enum cb_err efi_fv_set_option(struct region_device *fv, const char *name, void *data, size_t size); + +#endif /* _EDK2_OPTION_H_ */