summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c151
-rw-r--r--UefiPayloadPkg/FvbRuntimeDxe/FvbService.c1088
-rw-r--r--UefiPayloadPkg/FvbRuntimeDxe/FvbService.h187
-rw-r--r--UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c139
-rw-r--r--UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf71
-rw-r--r--UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h69
-rw-r--r--UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h24
-rw-r--r--UefiPayloadPkg/UefiPayloadPkg.dec6
8 files changed, 1735 insertions, 0 deletions
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c b/UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c
new file mode 100644
index 0000000000..78b6b81f66
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbInfo.c
@@ -0,0 +1,151 @@
+/** @file
+
+ Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Library/PcdLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Guid/FirmwareFileSystem2.h>
+#include <Guid/SystemNvDataGuid.h>
+#include <Guid/NvVariableInfoGuid.h>
+#include <Library/HobLib.h>
+
+#define FVB_MEDIA_BLOCK_SIZE 0x1000
+
+typedef struct {
+ EFI_FIRMWARE_VOLUME_HEADER FvInfo;
+ EFI_FV_BLOCK_MAP_ENTRY End[1];
+} EFI_FVB2_MEDIA_INFO;
+
+//
+// This data structure contains a template of FV header which is used to restore
+// Fv header if it's corrupted.
+//
+EFI_FVB2_MEDIA_INFO mFvbMediaInfo = {
+ {
+ {0,}, // ZeroVector[16]
+ EFI_SYSTEM_NV_DATA_FV_GUID,
+ 0,
+ EFI_FVH_SIGNATURE,
+ 0x0004feff, // check PiFirmwareVolume.h for details on EFI_FVB_ATTRIBUTES_2
+ sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
+ 0, // CheckSum which will be calucated dynamically.
+ 0, // ExtHeaderOffset
+ {0,},
+ EFI_FVH_REVISION,
+ {
+ {
+ 0,
+ FVB_MEDIA_BLOCK_SIZE,
+ }
+ }
+ },
+ {
+ {
+ 0,
+ 0
+ }
+ }
+};
+
+/**
+ Initialize the variable store
+
+ @retval EFI_SUCCESS if initialize the store success.
+
+**/
+EFI_STATUS
+InitVariableStore (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT32 NvStorageBase;
+ UINT32 NvStorageSize;
+ UINT32 NvVariableSize;
+ UINT32 FtwWorkingSize;
+ UINT32 FtwSpareSize;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ NV_VARIABLE_INFO *NvVariableInfo;
+
+ //
+ // Find SPI flash variable hob
+ //
+ GuidHob = GetFirstGuidHob (&gNvVariableInfoGuid);
+ if (GuidHob == NULL) {
+ ASSERT (FALSE);
+ return EFI_NOT_FOUND;
+ }
+ NvVariableInfo = (NV_VARIABLE_INFO *) GET_GUID_HOB_DATA (GuidHob);
+
+ //
+ // Get variable region base and size.
+ //
+ NvStorageSize = NvVariableInfo->VariableStoreSize;
+ NvStorageBase = NvVariableInfo->VariableStoreBase;
+
+ //
+ // NvStorageBase needs to be 4KB aligned, NvStorageSize needs to be 8KB * n
+ //
+ if (((NvStorageBase & (SIZE_4KB - 1)) != 0) || ((NvStorageSize & (SIZE_8KB - 1)) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FtwSpareSize = NvStorageSize / 2;
+ FtwWorkingSize = 0x2000;
+ NvVariableSize = NvStorageSize / 2 - FtwWorkingSize;
+ DEBUG ((DEBUG_INFO, "NvStorageBase:0x%x, NvStorageSize:0x%x\n", NvStorageBase, NvStorageSize));
+
+ if (NvVariableSize >= 0x80000000) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Status = PcdSet32S(PcdFlashNvStorageVariableSize, NvVariableSize);
+ ASSERT_EFI_ERROR (Status);
+ Status = PcdSet32S(PcdFlashNvStorageVariableBase, NvStorageBase);
+ ASSERT_EFI_ERROR (Status);
+ Status = PcdSet64S(PcdFlashNvStorageVariableBase64, NvStorageBase);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = PcdSet32S(PcdFlashNvStorageFtwWorkingSize, FtwWorkingSize);
+ ASSERT_EFI_ERROR (Status);
+ Status = PcdSet32S(PcdFlashNvStorageFtwWorkingBase, NvStorageBase + NvVariableSize);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = PcdSet32S(PcdFlashNvStorageFtwSpareSize, FtwSpareSize);
+ ASSERT_EFI_ERROR (Status);
+ Status = PcdSet32S(PcdFlashNvStorageFtwSpareBase, NvStorageBase + FtwSpareSize);
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get a heathy FV header used for variable store recovery
+
+ @retval The FV header.
+
+**/
+EFI_FIRMWARE_VOLUME_HEADER *
+GetFvHeaderTemplate (
+ VOID
+ )
+{
+ EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
+ UINTN FvSize;
+
+ FvSize = PcdGet32(PcdFlashNvStorageFtwSpareSize) * 2;
+ FvHeader = &mFvbMediaInfo.FvInfo;
+ FvHeader->FvLength = FvSize;
+ FvHeader->BlockMap[0].NumBlocks = (UINT32) (FvSize / FvHeader->BlockMap[0].Length);
+ FvHeader->Checksum = 0;
+ FvHeader->Checksum = CalculateCheckSum16 ((UINT16 *) FvHeader, FvHeader->HeaderLength);
+
+ return FvHeader;
+}
+
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbService.c b/UefiPayloadPkg/FvbRuntimeDxe/FvbService.c
new file mode 100644
index 0000000000..d90a39b564
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbService.c
@@ -0,0 +1,1088 @@
+/** @file
+Firmware Volume Block Driver to provide FVB service.
+
+ Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "FvbService.h"
+
+//
+// Global variable for this FVB driver which contains
+// the private data of all firmware volume block instances
+//
+FWB_GLOBAL mFvbModuleGlobal;
+
+FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_MEMMAP_DP,
+ {
+ (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
+ (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
+ }
+ },
+ EfiMemoryMappedIO,
+ (EFI_PHYSICAL_ADDRESS) 0,
+ (EFI_PHYSICAL_ADDRESS) 0,
+ },
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ END_DEVICE_PATH_LENGTH,
+ 0
+ }
+ }
+};
+
+FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate = {
+ {
+ {
+ MEDIA_DEVICE_PATH,
+ MEDIA_PIWG_FW_VOL_DP,
+ {
+ (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
+ (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
+ }
+ },
+ { 0 }
+ },
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ END_DEVICE_PATH_LENGTH,
+ 0
+ }
+ }
+};
+
+
+EFI_FW_VOL_BLOCK_DEVICE mFvbDeviceTemplate = {
+ FVB_DEVICE_SIGNATURE,
+ NULL,
+ 0, // Instance
+ {
+ FvbProtocolGetAttributes,
+ FvbProtocolSetAttributes,
+ FvbProtocolGetPhysicalAddress,
+ FvbProtocolGetBlockSize,
+ FvbProtocolRead,
+ FvbProtocolWrite,
+ FvbProtocolEraseBlocks,
+ NULL
+ } // FwVolBlockInstance
+};
+
+
+/**
+ Get the pointer to EFI_FW_VOL_INSTANCE from the buffer pointed
+ by mFvbModuleGlobal.FvInstance based on a index.
+ Each EFI_FW_VOL_INSTANCE is with variable length as
+ we have a block map at the end of the EFI_FIRMWARE_VOLUME_HEADER.
+
+ @param[in] Instance The index of the EFI_FW_VOL_INSTANCE.
+
+ @return A pointer to EFI_FW_VOL_INSTANCE.
+
+**/
+EFI_FW_VOL_INSTANCE *
+GetFvbInstance (
+ IN UINTN Instance
+ )
+{
+ EFI_FW_VOL_INSTANCE *FwhRecord;
+
+ if ( Instance >= mFvbModuleGlobal.NumFv ) {
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ //
+ // Find the right instance of the FVB private data
+ //
+ FwhRecord = mFvbModuleGlobal.FvInstance;
+ while ( Instance > 0 ) {
+ FwhRecord = (EFI_FW_VOL_INSTANCE *) ((UINTN)((UINT8 *)FwhRecord) +
+ FwhRecord->VolumeHeader.HeaderLength +
+ (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER)));
+ Instance--;
+ }
+
+ return FwhRecord;
+
+}
+
+
+/**
+ Get the EFI_FVB_ATTRIBUTES_2 of a FV.
+
+ @param[in] Instance The index of the EFI_FW_VOL_INSTANCE.
+
+ @retval EFI_FVB_ATTRIBUTES_2 of the FV identified by Instance.
+
+**/
+STATIC
+EFI_FVB_ATTRIBUTES_2
+FvbGetVolumeAttributes (
+ IN UINTN Instance
+ )
+{
+ EFI_FW_VOL_INSTANCE * FwInstance;
+ FwInstance = GetFvbInstance(Instance);
+ ASSERT (FwInstance != NULL);
+
+ if (FwInstance == NULL) {
+ return 0;
+ }
+
+ return FwInstance->VolumeHeader.Attributes;
+}
+
+
+
+/**
+ Retrieves the starting address of an LBA in an FV. It also
+ return a few other attribut of the FV.
+
+ @param[in] Instance The index of the EFI_FW_VOL_INSTANCE.
+ @param[in] Lba The logical block address
+ @param[out] LbaAddress On output, contains the physical starting address
+ of the Lba
+ @param[out] LbaLength On output, contains the length of the block
+ @param[out] NumOfBlocks A pointer to a caller allocated UINTN in which the
+ number of consecutive blocks starting with Lba is
+ returned. All blocks in this range have a size of
+ BlockSize
+
+ @retval EFI_SUCCESS Successfully returns
+ @retval EFI_INVALID_PARAMETER Instance not found
+
+**/
+STATIC
+EFI_STATUS
+FvbGetLbaAddress (
+ IN UINTN Instance,
+ IN EFI_LBA Lba,
+ OUT UINTN *LbaAddress,
+ OUT UINTN *LbaLength,
+ OUT UINTN *NumOfBlocks
+ )
+{
+ UINT32 NumBlocks;
+ UINT32 BlockLength;
+ UINTN Offset;
+ EFI_LBA StartLba;
+ EFI_LBA NextLba;
+ EFI_FW_VOL_INSTANCE *FwhInstance;
+ EFI_FV_BLOCK_MAP_ENTRY *BlockMap;
+
+ //
+ // Find the right instance of the FVB private data
+ //
+ FwhInstance = GetFvbInstance (Instance);
+ if (FwhInstance == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ StartLba = 0;
+ Offset = 0;
+ BlockMap = &FwhInstance->VolumeHeader.BlockMap[0];
+ ASSERT (BlockMap != NULL);
+
+ //
+ // Parse the blockmap of the FV to find which map entry the Lba belongs to
+ //
+ while (TRUE) {
+ if ( BlockMap != NULL) {
+ NumBlocks = BlockMap->NumBlocks;
+ BlockLength = BlockMap->Length;
+ }
+
+ if ( NumBlocks == 0 || BlockLength == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NextLba = StartLba + NumBlocks;
+
+ //
+ // The map entry found
+ //
+ if (Lba >= StartLba && Lba < NextLba) {
+ Offset = Offset + (UINTN)MultU64x32((Lba - StartLba), BlockLength);
+ if (LbaAddress != NULL) {
+ *LbaAddress = FwhInstance->FvBase + Offset;
+ }
+
+ if (LbaLength != NULL) {
+ *LbaLength = BlockLength;
+ }
+
+ if (NumOfBlocks != NULL) {
+ *NumOfBlocks = (UINTN)(NextLba - Lba);
+ }
+ return EFI_SUCCESS;
+ }
+
+ StartLba = NextLba;
+ Offset = Offset + NumBlocks * BlockLength;
+ BlockMap++;
+ }
+}
+
+
+/**
+ Reads specified number of bytes into a buffer from the specified block
+
+ @param[in] Instance The FV instance to be read from
+ @param[in] Lba The logical block address to be read from
+ @param[in] BlockOffset Offset into the block at which to begin reading
+ @param[in, out] NumBytes Pointer that on input contains the total size of
+ the buffer. On output, it contains the total number
+ of bytes read
+ @param[in] Buffer Pointer to a caller allocated buffer that will be
+ used to hold the data read
+
+
+ @retval EFI_SUCCESS The firmware volume was read successfully and
+ contents are in Buffer
+ @retval EFI_BAD_BUFFER_SIZE Read attempted across a LBA boundary. On output,
+ NumBytes contains the total number of bytes returned
+ in Buffer
+ @retval EFI_ACCESS_DENIED The firmware volume is in the ReadDisabled state
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and
+ could not be read
+ @retval EFI_INVALID_PARAMETER Instance not found, or NumBytes, Buffer are NULL
+
+**/
+STATIC
+EFI_STATUS
+FvbReadBlock (
+ IN UINTN Instance,
+ IN EFI_LBA Lba,
+ IN UINTN BlockOffset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_FVB_ATTRIBUTES_2 Attributes;
+ UINTN LbaAddress;
+ UINTN LbaLength;
+ EFI_STATUS Status;
+ EFI_STATUS ReadStatus;
+
+ if ( (NumBytes == NULL) || (Buffer == NULL)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ if (*NumBytes == 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ Attributes = FvbGetVolumeAttributes (Instance);
+
+ if ( (Attributes & EFI_FVB2_READ_STATUS) == 0) {
+ return (EFI_ACCESS_DENIED);
+ }
+
+ if (BlockOffset > LbaLength) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if (LbaLength < ( *NumBytes + BlockOffset ) ) {
+ *NumBytes = (UINT32) (LbaLength - BlockOffset);
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ ReadStatus = LibFvbFlashDeviceRead (LbaAddress + BlockOffset, NumBytes, Buffer);
+ if (EFI_ERROR(ReadStatus)) {
+ return ReadStatus;
+ }
+
+ return Status;
+}
+
+
+/**
+ Writes specified number of bytes from the input buffer to the block
+
+ @param[in] Instance The FV instance to be written to
+ @param[in] Lba The starting logical block index to write to
+ @param[in] BlockOffset Offset into the block at which to begin writing
+ @param[in, out] NumBytes Pointer that on input contains the total size of
+ the buffer. On output, it contains the total number
+ of bytes actually written
+ @param[in] Buffer Pointer to a caller allocated buffer that contains
+ the source for the write
+ @retval EFI_SUCCESS The firmware volume was written successfully
+ @retval EFI_BAD_BUFFER_SIZE Write attempted across a LBA boundary. On output,
+ NumBytes contains the total number of bytes
+ actually written
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and
+ could not be written
+ @retval EFI_INVALID_PARAMETER Instance not found, or NumBytes, Buffer are NULL
+
+**/
+EFI_STATUS
+FvbWriteBlock (
+ IN UINTN Instance,
+ IN EFI_LBA Lba,
+ IN UINTN BlockOffset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_FVB_ATTRIBUTES_2 Attributes;
+ UINTN LbaAddress;
+ UINTN LbaLength;
+ EFI_STATUS Status;
+
+ if ( (NumBytes == NULL) || (Buffer == NULL)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ if (*NumBytes == 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Check if the FV is write enabled
+ //
+ Attributes = FvbGetVolumeAttributes (Instance);
+ if ( (Attributes & EFI_FVB2_WRITE_STATUS) == 0) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Perform boundary checks and adjust NumBytes
+ //
+ if (BlockOffset > LbaLength) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ( LbaLength < ( *NumBytes + BlockOffset ) ) {
+ DEBUG ((DEBUG_ERROR,
+ "FvWriteBlock: Reducing Numbytes from 0x%x to 0x%x\n",
+ *NumBytes, (UINT32)(LbaLength - BlockOffset)));
+ *NumBytes = (UINT32) (LbaLength - BlockOffset);
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, FALSE);
+ Status = LibFvbFlashDeviceWrite (LbaAddress + BlockOffset, NumBytes, Buffer);
+
+ LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, TRUE);
+ WriteBackInvalidateDataCacheRange ((VOID *) (LbaAddress + BlockOffset), *NumBytes);
+ return Status;
+}
+
+
+/**
+ Erases and initializes a firmware volume block
+
+ @param[in] Instance The FV instance to be erased
+ @param[in] Lba The logical block index to be erased
+
+ @retval EFI_SUCCESS The erase request was successfully completed
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and
+ could not be written. Firmware device may have been
+ partially erased
+ @retval EFI_INVALID_PARAMETER Instance not found
+
+**/
+EFI_STATUS
+FvbEraseBlock (
+ IN UINTN Instance,
+ IN EFI_LBA Lba
+ )
+{
+
+ EFI_FVB_ATTRIBUTES_2 Attributes;
+ UINTN LbaAddress;
+ UINTN LbaLength;
+ EFI_STATUS Status;
+
+ //
+ // Check if the FV is write enabled
+ //
+ Attributes = FvbGetVolumeAttributes (Instance);
+
+ if( (Attributes & EFI_FVB2_WRITE_STATUS) == 0) {
+ return (EFI_ACCESS_DENIED);
+ }
+
+ //
+ // Get the starting address of the block for erase.
+ //
+ Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, FALSE);
+
+ Status = LibFvbFlashDeviceBlockErase (LbaAddress, LbaLength);
+
+ LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, TRUE);
+
+ WriteBackInvalidateDataCacheRange ((VOID *) LbaAddress, LbaLength);
+
+ return Status;
+}
+
+/**
+ Modifies the current settings of the firmware volume according to the
+ input parameter, and returns the new setting of the volume
+
+ @param[in] Instance The FV instance whose attributes is going to be
+ modified
+ @param[in, out] Attributes On input, it is a pointer to EFI_FVB_ATTRIBUTES_2
+ containing the desired firmware volume settings.
+ On successful return, it contains the new settings
+ of the firmware volume
+
+ @retval EFI_SUCCESS Successfully returns
+ @retval EFI_ACCESS_DENIED The volume setting is locked and cannot be modified
+ @retval EFI_INVALID_PARAMETER Instance not found, or The attributes requested are
+ in conflict with the capabilities as declared in the
+ firmware volume header
+
+**/
+STATIC
+EFI_STATUS
+FvbSetVolumeAttributes (
+ IN UINTN Instance,
+ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ )
+{
+ EFI_FW_VOL_INSTANCE *FwhInstance;
+ EFI_FVB_ATTRIBUTES_2 OldAttributes;
+ EFI_FVB_ATTRIBUTES_2 *AttribPtr;
+ EFI_FVB_ATTRIBUTES_2 UnchangedAttributes;
+ UINT32 Capabilities;
+ UINT32 OldStatus;
+ UINT32 NewStatus;
+
+ //
+ // Find the right instance of the FVB private data
+ //
+ FwhInstance = GetFvbInstance (Instance);
+ if (FwhInstance == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AttribPtr = (EFI_FVB_ATTRIBUTES_2 *) &(FwhInstance->VolumeHeader.Attributes);
+ ASSERT (AttribPtr != NULL);
+ if ( AttribPtr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldAttributes = *AttribPtr;
+ Capabilities = OldAttributes & EFI_FVB2_CAPABILITIES;
+ OldStatus = OldAttributes & EFI_FVB2_STATUS;
+ NewStatus = *Attributes & EFI_FVB2_STATUS;
+
+ UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP | \
+ EFI_FVB2_READ_ENABLED_CAP | \
+ EFI_FVB2_WRITE_DISABLED_CAP | \
+ EFI_FVB2_WRITE_ENABLED_CAP | \
+ EFI_FVB2_LOCK_CAP | \
+ EFI_FVB2_STICKY_WRITE | \
+ EFI_FVB2_MEMORY_MAPPED | \
+ EFI_FVB2_ERASE_POLARITY | \
+ EFI_FVB2_READ_LOCK_CAP | \
+ EFI_FVB2_WRITE_LOCK_CAP | \
+ EFI_FVB2_ALIGNMENT;
+
+ //
+ // Some attributes of FV is read only can *not* be set
+ //
+ if ((OldAttributes & UnchangedAttributes) ^ (*Attributes & UnchangedAttributes)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If firmware volume is locked, no status bit can be updated
+ //
+ if ((OldAttributes & EFI_FVB2_LOCK_STATUS) != 0) {
+ if ((OldStatus ^ NewStatus) != 0) {
+ return EFI_ACCESS_DENIED;
+ }
+ }
+
+ //
+ // Test read disable
+ //
+ if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
+ if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Test read enable
+ //
+ if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
+ if ((NewStatus & EFI_FVB2_READ_STATUS) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Test write disable
+ //
+ if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
+ if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Test write enable
+ //
+ if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
+ if ((NewStatus & EFI_FVB2_WRITE_STATUS) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Test lock
+ //
+ if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
+ if ((NewStatus & EFI_FVB2_LOCK_STATUS) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ *AttribPtr = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
+ *AttribPtr = (*AttribPtr) | NewStatus;
+ *Attributes = *AttribPtr;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retrieves the physical address of the device.
+
+ @param[in] This A pointer to EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL.
+ @param[out] Address Output buffer containing the address.
+
+ @retval EFI_SUCCESS The function always return successfully.
+ @retval EFI_INVALID_PARAMETER Instance not found.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ OUT EFI_PHYSICAL_ADDRESS *Address
+ )
+{
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+ EFI_FW_VOL_INSTANCE *FwhInstance;
+
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);
+ FwhInstance = GetFvbInstance(FvbDevice->Instance);
+ if (FwhInstance == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Address = FwhInstance->FvBase;
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Retrieve the size of a logical block
+
+ @param[in] This Calling context
+ @param[in] Lba Indicates which block to return the size for.
+ @param[out] BlockSize A pointer to a caller allocated UINTN in which
+ the size of the block is returned
+ @param[out] NumOfBlocks A pointer to a caller allocated UINTN in which the
+ number of consecutive blocks starting with Lba is
+ returned. All blocks in this range have a size of
+ BlockSize
+
+ @retval EFI_SUCCESS The function always return successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ OUT UINTN *BlockSize,
+ OUT UINTN *NumOfBlocks
+ )
+{
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);
+ return FvbGetLbaAddress (FvbDevice->Instance, Lba, NULL, BlockSize, NumOfBlocks);
+}
+
+
+/**
+ Retrieves Volume attributes. No polarity translations are done.
+
+ @param[in] This Calling context
+ @param[out] Attributes Output buffer which contains attributes
+
+ @retval EFI_SUCCESS The function always return successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ )
+{
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);
+ *Attributes = FvbGetVolumeAttributes (FvbDevice->Instance);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Sets Volume attributes. No polarity translations are done.
+
+ @param[in] This Calling context
+ @param[in, out] Attributes Output buffer which contains attributes
+
+ @retval EFI_SUCCESS The function always return successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ )
+{
+ EFI_STATUS Status;
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);
+ Status = FvbSetVolumeAttributes (FvbDevice->Instance, Attributes);
+ return Status;
+}
+
+
+
+/**
+ This function erases one or more blocks as denoted by the
+ variable argument list. The entire parameter list of blocks must be verified
+ prior to erasing any blocks. If a block is requested that does not exist
+ within the associated firmware volume (it has a larger index than the last
+ block of the firmware volume), the EraseBlock() function must return
+ EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.
+
+ @param[in] This Calling context
+ @param[in] ... Starting LBA followed by Number of Lba to erase.
+ a -1 to terminate the list.
+
+ @retval EFI_SUCCESS The erase request was successfully completed
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and
+ could not be written. Firmware device may have been
+ partially erased
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ ...
+ )
+{
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+ EFI_FW_VOL_INSTANCE *FwhInstance;
+ UINTN NumOfBlocks;
+ VA_LIST args;
+ EFI_LBA StartingLba;
+ UINTN NumOfLba;
+ EFI_STATUS Status;
+
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);
+ FwhInstance = GetFvbInstance (FvbDevice->Instance);
+ if (FwhInstance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NumOfBlocks = FwhInstance->NumOfBlocks;
+ VA_START (args, This);
+
+ do {
+ StartingLba = VA_ARG (args, EFI_LBA);
+ if ( StartingLba == EFI_LBA_LIST_TERMINATOR ) {
+ break;
+ }
+
+ NumOfLba = VA_ARG (args, UINT32);
+
+ //
+ // Check input parameters
+ //
+ if (NumOfLba == 0) {
+ VA_END (args);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ( ( StartingLba + NumOfLba ) > NumOfBlocks ) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } while ( 1 );
+
+ VA_END (args);
+
+ VA_START (args, This);
+ do {
+ StartingLba = VA_ARG (args, EFI_LBA);
+ if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+ break;
+ }
+
+ NumOfLba = VA_ARG (args, UINT32);
+
+ while ( NumOfLba > 0 ) {
+ Status = FvbEraseBlock (FvbDevice->Instance, StartingLba);
+ if ( EFI_ERROR(Status)) {
+ VA_END (args);
+ return Status;
+ }
+ StartingLba++;
+ NumOfLba--;
+ }
+ } while ( 1 );
+
+ VA_END (args);
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Writes data beginning at Lba:Offset from FV. The write terminates either
+ when *NumBytes of data have been written, or when a block boundary is
+ reached. *NumBytes is updated to reflect the actual number of bytes
+ written. The write opertion does not include erase. This routine will
+ attempt to write only the specified bytes. If the writes do not stick,
+ it will return an error.
+
+ @param[in] This Calling context
+ @param[in] Lba Block in which to begin write
+ @param[in] Offset Offset in the block at which to begin write
+ @param[in,out] NumBytes On input, indicates the requested write size. On
+ output, indicates the actual number of bytes written
+ @param[in] Buffer Buffer containing source data for the write.
+
+ @retval EFI_SUCCESS The firmware volume was written successfully
+ @retval EFI_BAD_BUFFER_SIZE Write attempted across a LBA boundary. On output,
+ NumBytes contains the total number of bytes
+ actually written
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and
+ could not be written
+ @retval EFI_INVALID_PARAMETER NumBytes or Buffer are NULL
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+ EFI_STATUS Status;
+
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);
+ Status = FvbWriteBlock (FvbDevice->Instance, Lba, Offset, NumBytes, Buffer);
+ DEBUG((DEBUG_VERBOSE,
+ "FvbWrite: Lba: 0x%lx Offset: 0x%x NumBytes: 0x%x, Buffer: 0x%x Status:%r\n",
+ Lba, Offset, *NumBytes, Buffer, Status));
+
+ return Status;
+}
+
+
+/**
+ Reads data beginning at Lba:Offset from FV. The Read terminates either
+ when *NumBytes of data have been read, or when a block boundary is
+ reached. *NumBytes is updated to reflect the actual number of bytes
+ written. The write opertion does not include erase. This routine will
+ attempt to write only the specified bytes. If the writes do not stick,
+ it will return an error.
+
+ @param[in] This Calling context
+ @param[in] Lba Block in which to begin write
+ @param[in] Offset Offset in the block at which to begin write
+ @param[in,out] NumBytes On input, indicates the requested write size. On
+ output, indicates the actual number of bytes written
+ @param[out] Buffer Buffer containing source data for the write.
+
+
+Returns:
+ @retval EFI_SUCCESS The firmware volume was read successfully and
+ contents are in Buffer
+ @retval EFI_BAD_BUFFER_SIZE Read attempted across a LBA boundary. On output,
+ NumBytes contains the total number of bytes returned
+ in Buffer
+ @retval EFI_ACCESS_DENIED The firmware volume is in the ReadDisabled state
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and
+ could not be read
+ @retval EFI_INVALID_PARAMETER NumBytes or Buffer are NULL
+
+**/
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ OUT UINT8 *Buffer
+ )
+{
+
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+ EFI_STATUS Status;
+
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);
+ Status = FvbReadBlock (FvbDevice->Instance, Lba, Offset, NumBytes, Buffer);
+ DEBUG((DEBUG_VERBOSE,
+ "FvbRead: Lba: 0x%lx Offset: 0x%x NumBytes: 0x%x, Buffer: 0x%x, Status:%r\n",
+ Lba, Offset, *NumBytes, Buffer, Status));
+
+ return Status;
+}
+
+/**
+ Check the integrity of firmware volume header in FvBase
+
+ @param[in] FvBase A pointer to firmware volume base address.
+
+ @retval TRUE The firmware volume is consistent
+ @retval FALSE The firmware volume has corrupted.
+
+**/
+BOOLEAN
+IsFvHeaderValid (
+ IN EFI_PHYSICAL_ADDRESS FvBase
+ )
+{
+ UINT16 Sum;
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
+
+ FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) FvBase;
+ if (FvBase == PcdGet32(PcdFlashNvStorageVariableBase)) {
+ if (CompareMem (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid, sizeof(EFI_GUID)) != 0 ) {
+ DEBUG((DEBUG_INFO, " --FileSystemGuid not match: %g\n", &FwVolHeader->FileSystemGuid));
+ return FALSE;
+ }
+ } else {
+ if (CompareMem (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem2Guid, sizeof(EFI_GUID)) != 0 ) {
+ DEBUG((DEBUG_INFO, " --not expected guid.\n"));
+ return FALSE;
+ }
+ }
+
+ if ( (FwVolHeader->Revision != EFI_FVH_REVISION) ||
+ (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
+ (FwVolHeader->FvLength == ((UINTN) -1)) ||
+ ((FwVolHeader->HeaderLength & 0x01 ) !=0) ) {
+ DEBUG((DEBUG_INFO, " -- >Revision = 0x%x, Signature = 0x%x\n", FwVolHeader->Revision, FwVolHeader->Signature ));
+ DEBUG((DEBUG_INFO, " -- >FvLength = 0x%lx, HeaderLength = 0x%x\n", FwVolHeader->FvLength, FwVolHeader->HeaderLength ));
+ return FALSE;
+ }
+
+ Sum = CalculateSum16 ((UINT16 *) FwVolHeader, FwVolHeader->HeaderLength);
+ if (Sum != 0) {
+ DEBUG((DEBUG_INFO, "error: checksum: 0x%04X (expect 0x0)\n", Sum));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Get intial variable data.
+
+ @param[out] VarData Valid variable data.
+ @param[out] VarSize Valid variable size.
+
+ @retval RETURN_SUCCESS Successfully found initial variable data.
+ @retval RETURN_NOT_FOUND Failed to find the variable data file from FV.
+ @retval EFI_INVALID_PARAMETER VarData or VarSize is null.
+
+**/
+EFI_STATUS
+GetInitialVariableData (
+ OUT VOID **VarData,
+ OUT UINTN *VarSize
+ )
+{
+ EFI_STATUS Status;
+ VOID *ImageData;
+ UINTN ImageSize;
+ EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
+ VARIABLE_STORE_HEADER *VariableStore;
+ AUTHENTICATED_VARIABLE_HEADER *Variable;
+ UINTN VariableSize;
+ UINTN VarEndAddr;
+
+ if ((VarData == NULL) || (VarSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = GetSectionFromAnyFv (PcdGetPtr(PcdNvsDataFile), EFI_SECTION_RAW, 0, &ImageData, &ImageSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ImageData;
+ VariableStore = (VARIABLE_STORE_HEADER *) ((UINT8 *)ImageData + FvHeader->HeaderLength);
+ VarEndAddr = (UINTN)VariableStore + VariableStore->Size;
+ Variable = (AUTHENTICATED_VARIABLE_HEADER *) HEADER_ALIGN (VariableStore + 1);
+ *VarData = (VOID *)Variable;
+ while (((UINTN)Variable < VarEndAddr)) {
+ if (Variable->StartId != VARIABLE_DATA) {
+ break;
+ }
+ VariableSize = sizeof (AUTHENTICATED_VARIABLE_HEADER) + Variable->DataSize + Variable->NameSize;
+ Variable = (AUTHENTICATED_VARIABLE_HEADER *) HEADER_ALIGN ((UINTN) Variable + VariableSize);
+ }
+
+ *VarSize = (UINTN)Variable - HEADER_ALIGN (VariableStore + 1);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The function does the necessary initialization work for
+ Firmware Volume Block Driver.
+
+ @retval EFI_SUCCESS This funtion always return EFI_SUCCESS.
+ It will ASSERT on errors.
+
+**/
+EFI_STATUS
+FvbInitialize (
+ VOID
+ )
+{
+ EFI_FW_VOL_INSTANCE *FwVolInstance;
+ EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
+ EFI_FV_BLOCK_MAP_ENTRY *BlockMap;
+ EFI_PHYSICAL_ADDRESS BaseAddress;
+ UINTN WriteAddr;
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ UINTN Length;
+ VARIABLE_STORE_HEADER VariableStore;
+ VOID *VarData;
+
+ InitVariableStore ();
+ BaseAddress = PcdGet32(PcdFlashNvStorageVariableBase);
+ FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress;
+
+ //
+ // Check FV header and variable store header
+ //
+ if (!IsFvHeaderValid (BaseAddress)) {
+ //
+ // Write back a healthy FV header
+ //
+ DEBUG ((DEBUG_ERROR, "Fvb: Writing back a healthy FV header: 0x%lx\n", BaseAddress));
+ FvHeader = GetFvHeaderTemplate ();
+ LibFvbFlashDeviceBlockLock ((UINTN)BaseAddress, FvHeader->BlockMap->Length, FALSE);
+
+ Status = LibFvbFlashDeviceBlockErase ((UINTN)BaseAddress, FvHeader->BlockMap->Length);
+ ASSERT_EFI_ERROR(Status);
+
+ Length = FvHeader->HeaderLength;
+ WriteAddr = (UINTN)BaseAddress;
+ Status = LibFvbFlashDeviceWrite (WriteAddr, &Length, (UINT8 *) FvHeader);
+ WriteAddr += Length;
+ ASSERT_EFI_ERROR(Status);
+
+ //
+ // Write back variable store header
+ //
+ VariableStore.Size = PcdGet32(PcdFlashNvStorageVariableSize) - FvHeader->HeaderLength;
+ VariableStore.Format = VARIABLE_STORE_FORMATTED;
+ VariableStore.State = VARIABLE_STORE_HEALTHY;
+ CopyGuid (&VariableStore.Signature, &gEfiAuthenticatedVariableGuid);
+ BufferSize = sizeof (VARIABLE_STORE_HEADER);
+ Status = LibFvbFlashDeviceWrite (WriteAddr, &BufferSize, (UINT8 *) &VariableStore);
+ WriteAddr += BufferSize;
+ ASSERT_EFI_ERROR(Status);
+
+ //
+ // Write initial variable data if found
+ //
+ Status = GetInitialVariableData (&VarData, &Length);
+ if (!EFI_ERROR (Status)) {
+ Status = LibFvbFlashDeviceWrite (WriteAddr, &Length, (UINT8 *) VarData);
+ ASSERT_EFI_ERROR(Status);
+ }
+
+ LibFvbFlashDeviceBlockLock ((UINTN)BaseAddress, FvHeader->BlockMap->Length, TRUE);
+ WriteBackInvalidateDataCacheRange ((VOID *) (UINTN) BaseAddress, FvHeader->BlockMap->Length);
+ }
+
+ //
+ // Create a new FW volume instance for NVS variable
+ //
+ BufferSize = FvHeader->HeaderLength + sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER);
+ FwVolInstance = (EFI_FW_VOL_INSTANCE *) AllocateRuntimeZeroPool (BufferSize);
+ if (FwVolInstance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ FwVolInstance->FvBase = (UINTN)BaseAddress;
+ CopyMem (&FwVolInstance->VolumeHeader, FvHeader, FvHeader->HeaderLength);
+
+ //
+ // Process the block map for each FV. Assume it has same block size.
+ //
+ FwVolInstance->NumOfBlocks = 0;
+ FvHeader = &FwVolInstance->VolumeHeader;
+ for (BlockMap = FvHeader->BlockMap; BlockMap->NumBlocks != 0; BlockMap++) {
+ FwVolInstance->NumOfBlocks += BlockMap->NumBlocks;
+ }
+
+ //
+ // Add a FVB Protocol Instance
+ //
+ Status = InstallFvbProtocol (FwVolInstance, mFvbModuleGlobal.NumFv);
+ mFvbModuleGlobal.NumFv++;
+ mFvbModuleGlobal.FvInstance = FwVolInstance;
+
+ return Status;
+}
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbService.h b/UefiPayloadPkg/FvbRuntimeDxe/FvbService.h
new file mode 100644
index 0000000000..c07562cbfd
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbService.h
@@ -0,0 +1,187 @@
+/** @file
+The header file for Firmware volume block driver.
+
+Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef FW_BLOCK_SERVICE_H_
+#define FW_BLOCK_SERVICE_H_
+
+#include <Guid/EventGroup.h>
+#include <Guid/FirmwareFileSystem2.h>
+#include <Guid/SystemNvDataGuid.h>
+#include <Guid/VariableFormat.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/IoLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/FlashDeviceLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/HobLib.h>
+#include <Library/DxeServicesLib.h>
+#include <Guid/NvVariableInfoGuid.h>
+#include <Register/ArchitecturalMsr.h>
+
+//
+// Define two helper macro to extract the Capability field or Status field in FVB
+// bit fields
+//
+#define EFI_FVB2_CAPABILITIES (EFI_FVB2_READ_DISABLED_CAP | \
+ EFI_FVB2_READ_ENABLED_CAP | \
+ EFI_FVB2_WRITE_DISABLED_CAP | \
+ EFI_FVB2_WRITE_ENABLED_CAP | \
+ EFI_FVB2_LOCK_CAP \
+ )
+
+#define EFI_FVB2_STATUS (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | EFI_FVB2_LOCK_STATUS)
+
+
+typedef struct {
+ UINTN FvBase;
+ UINTN NumOfBlocks;
+ //
+ // Note!!!: VolumeHeader must be the last element
+ // of the structure.
+ //
+ EFI_FIRMWARE_VOLUME_HEADER VolumeHeader;
+} EFI_FW_VOL_INSTANCE;
+
+
+typedef struct {
+ EFI_FW_VOL_INSTANCE *FvInstance;
+ UINT32 NumFv;
+ UINT32 Flags;
+} FWB_GLOBAL;
+
+//
+// Fvb Protocol instance data
+//
+#define FVB_DEVICE_FROM_THIS(a) CR(a, EFI_FW_VOL_BLOCK_DEVICE, FwVolBlockInstance, FVB_DEVICE_SIGNATURE)
+#define FVB_EXTEND_DEVICE_FROM_THIS(a) CR(a, EFI_FW_VOL_BLOCK_DEVICE, FvbExtension, FVB_DEVICE_SIGNATURE)
+#define FVB_DEVICE_SIGNATURE SIGNATURE_32('F','V','B','C')
+
+typedef struct {
+ MEDIA_FW_VOL_DEVICE_PATH FvDevPath;
+ EFI_DEVICE_PATH_PROTOCOL EndDevPath;
+} FV_PIWG_DEVICE_PATH;
+
+typedef struct {
+ MEMMAP_DEVICE_PATH MemMapDevPath;
+ EFI_DEVICE_PATH_PROTOCOL EndDevPath;
+} FV_MEMMAP_DEVICE_PATH;
+
+typedef struct {
+ UINT32 Signature;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ UINTN Instance;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL FwVolBlockInstance;
+} EFI_FW_VOL_BLOCK_DEVICE;
+
+/**
+ Get a heathy FV header used for variable store recovery
+
+ @retval The FV header.
+
+**/
+EFI_FIRMWARE_VOLUME_HEADER *
+GetFvHeaderTemplate (
+ VOID
+ );
+
+EFI_STATUS
+InitVariableStore (
+ VOID
+ );
+
+//
+// Protocol APIs
+//
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ OUT EFI_PHYSICAL_ADDRESS *Address
+ );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ OUT UINTN *BlockSize,
+ OUT UINTN *NumOfBlocks
+ );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ OUT UINT8 *Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ ...
+ );
+
+EFI_FW_VOL_INSTANCE *
+GetFvbInstance (
+ IN UINTN Instance
+ );
+
+EFI_STATUS
+InstallFvbProtocol (
+ IN EFI_FW_VOL_INSTANCE *FwhInstance,
+ IN UINTN InstanceNum
+ );
+
+EFI_STATUS
+FvbInitialize (
+ VOID
+ );
+
+extern FWB_GLOBAL mFvbModuleGlobal;
+extern EFI_FW_VOL_BLOCK_DEVICE mFvbDeviceTemplate;
+extern FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate;
+extern FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate;
+
+#endif
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c b/UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c
new file mode 100644
index 0000000000..0f1f4b369c
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbServiceSmm.c
@@ -0,0 +1,139 @@
+/** @file
+ SMM Firmware Volume Block Driver.
+
+ Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiSmm.h>
+#include <Library/SmmServicesTableLib.h>
+#include "FvbSmmCommon.h"
+#include "FvbService.h"
+
+/**
+ The function installs EFI_SMM_FIRMWARE_VOLUME_BLOCK protocol
+ for each FV in the system.
+
+ @param[in] FwhInstance The pointer to a FW volume instance structure,
+ which contains the information about one FV.
+ @param[in] InstanceNum The instance number which can be used as a ID
+ to locate this FwhInstance in other functions.
+
+ @retval EFI_SUCESS Installed successfully.
+ @retval Else Did not install successfully.
+
+**/
+EFI_STATUS
+InstallFvbProtocol (
+ IN EFI_FW_VOL_INSTANCE *FwhInstance,
+ IN UINTN InstanceNum
+ )
+{
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
+ EFI_STATUS Status;
+ EFI_HANDLE FvbHandle;
+ FV_MEMMAP_DEVICE_PATH *FvDevicePath;
+ VOID *TempPtr;
+
+ FvbDevice = (EFI_FW_VOL_BLOCK_DEVICE *) AllocateRuntimeCopyPool (
+ sizeof (EFI_FW_VOL_BLOCK_DEVICE),
+ &mFvbDeviceTemplate
+ );
+ if (FvbDevice == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ FvbDevice->Instance = InstanceNum;
+ FwVolHeader = &FwhInstance->VolumeHeader;
+
+ //
+ // Set up the devicepath
+ //
+ if (FwVolHeader->ExtHeaderOffset == 0) {
+ //
+ // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
+ //
+ TempPtr = AllocateRuntimeCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH), &mFvMemmapDevicePathTemplate);
+ FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
+ if (FvbDevice->DevicePath == NULL) {
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ FvDevicePath = (FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath;
+ FvDevicePath->MemMapDevPath.StartingAddress = FwhInstance->FvBase;
+ FvDevicePath->MemMapDevPath.EndingAddress = FwhInstance->FvBase + FwVolHeader->FvLength - 1;
+ } else {
+ TempPtr = AllocateRuntimeCopyPool (sizeof (FV_PIWG_DEVICE_PATH), &mFvPIWGDevicePathTemplate);
+ FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
+ if (FvbDevice->DevicePath == NULL) {
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyGuid (
+ &((FV_PIWG_DEVICE_PATH *)FvbDevice->DevicePath)->FvDevPath.FvName,
+ (GUID *)(UINTN)(FwhInstance->FvBase + FwVolHeader->ExtHeaderOffset)
+ );
+ }
+
+ //
+ // Install the SMM Firmware Volume Block Protocol and Device Path Protocol
+ //
+ FvbHandle = NULL;
+ Status = gSmst->SmmInstallProtocolInterface (
+ &FvbHandle,
+ &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &FvbDevice->FwVolBlockInstance
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gSmst->SmmInstallProtocolInterface (
+ &FvbHandle,
+ &gEfiDevicePathProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ FvbDevice->DevicePath
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Notify the Fvb wrapper driver SMM fvb is ready
+ //
+ FvbHandle = NULL;
+ Status = gBS->InstallProtocolInterface (
+ &FvbHandle,
+ &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &FvbDevice->FwVolBlockInstance
+ );
+
+ return Status;
+}
+
+
+/**
+ The driver entry point for SMM Firmware Volume Block Driver.
+
+ The function does the necessary initialization work
+ Firmware Volume Block Driver.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI system table.
+
+ @retval EFI_SUCCESS This funtion always return EFI_SUCCESS.
+ It will ASSERT on errors.
+
+**/
+EFI_STATUS
+EFIAPI
+FvbSmmInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ FvbInitialize ();
+
+ return EFI_SUCCESS;
+}
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf b/UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
new file mode 100644
index 0000000000..2a262076d6
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbSmm.inf
@@ -0,0 +1,71 @@
+## @file
+# This driver installs the EFI_SMM_FIRMWARE_VOLUMEN_PROTOCOL.
+#
+#
+# Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = FvbSmm
+ FILE_GUID = A4EC8ADB-B7A8-47d1-8E52-EC820D0ACF6F
+ MODULE_TYPE = DXE_SMM_DRIVER
+ VERSION_STRING = 1.0
+ PI_SPECIFICATION_VERSION = 0x0001000A
+ ENTRY_POINT = FvbSmmInitialize
+
+[Sources]
+ FvbInfo.c
+ FvbService.h
+ FvbService.c
+ FvbServiceSmm.c
+ FvbSmmCommon.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+ UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+ FlashDeviceLib
+ PcdLib
+ MemoryAllocationLib
+ CacheMaintenanceLib
+ IoLib
+ BaseMemoryLib
+ DebugLib
+ BaseLib
+ UefiLib
+ SmmServicesTableLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ HobLib
+ DxeServicesLib
+
+[Guids]
+ gEfiFirmwareFileSystem2Guid # ALWAYS_CONSUMED
+ gEfiSystemNvDataFvGuid # ALWAYS_CONSUMED
+ gEfiAuthenticatedVariableGuid
+ gNvVariableInfoGuid
+
+ [Protocols]
+ gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+ gEfiSmmFirmwareVolumeBlockProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
+ gUefiPayloadPkgTokenSpaceGuid.PcdNvsDataFile
+
+[Depex]
+ TRUE
diff --git a/UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h b/UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h
new file mode 100644
index 0000000000..5886996cd8
--- /dev/null
+++ b/UefiPayloadPkg/FvbRuntimeDxe/FvbSmmCommon.h
@@ -0,0 +1,69 @@
+/** @file
+ The common header file for SMM FVB module.
+
+Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SMM_FVB_COMMON_H_
+#define SMM_FVB_COMMON_H_
+
+#include <Protocol/SmmFirmwareVolumeBlock.h>
+
+#define EFI_FUNCTION_GET_ATTRIBUTES 1
+#define EFI_FUNCTION_SET_ATTRIBUTES 2
+#define EFI_FUNCTION_GET_PHYSICAL_ADDRESS 3
+#define EFI_FUNCTION_GET_BLOCK_SIZE 4
+#define EFI_FUNCTION_READ 5
+#define EFI_FUNCTION_WRITE 6
+#define EFI_FUNCTION_ERASE_BLOCKS 7
+
+typedef struct {
+ UINTN Function;
+ EFI_STATUS ReturnStatus;
+ UINT8 Data[1];
+} SMM_FVB_COMMUNICATE_FUNCTION_HEADER;
+
+
+///
+/// Size of SMM communicate header, without including the payload.
+///
+#define SMM_COMMUNICATE_HEADER_SIZE (OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data))
+
+///
+/// Size of SMM FVB communicate function header, without including the payload.
+///
+#define SMM_FVB_COMMUNICATE_HEADER_SIZE (OFFSET_OF (SMM_FVB_COMMUNICATE_FUNCTION_HEADER, Data))
+
+typedef struct {
+ EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
+ EFI_FVB_ATTRIBUTES_2 Attributes;
+} SMM_FVB_ATTRIBUTES_HEADER;
+
+typedef struct {
+ EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
+ EFI_PHYSICAL_ADDRESS Address;
+} SMM_FVB_PHYSICAL_ADDRESS_HEADER;
+
+typedef struct {
+ EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
+ EFI_LBA Lba;
+ UINTN BlockSize;
+ UINTN NumOfBlocks;
+} SMM_FVB_BLOCK_SIZE_HEADER;
+
+typedef struct {
+ EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
+ EFI_LBA Lba;
+ UINTN Offset;
+ UINTN NumBytes;
+} SMM_FVB_READ_WRITE_HEADER;
+
+typedef struct {
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *SmmFvb;
+ EFI_LBA StartLba;
+ UINTN NumOfLba;
+} SMM_FVB_BLOCKS_HEADER;
+
+#endif
diff --git a/UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h b/UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h
new file mode 100644
index 0000000000..f22e4e6122
--- /dev/null
+++ b/UefiPayloadPkg/Include/Guid/NvVariableInfoGuid.h
@@ -0,0 +1,24 @@
+/** @file
+ This file defines the hob structure for the SPI flash variable info.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef NV_VARIABLE_INFO_GUID_H_
+#define NV_VARIABLE_INFO_GUID_H_
+
+//
+// NV variable hob info GUID
+//
+extern EFI_GUID gNvVariableInfoGuid;
+
+typedef struct {
+ UINT8 Revision;
+ UINT8 Reserved[3];
+ UINT32 VariableStoreBase;
+ UINT32 VariableStoreSize;
+} NV_VARIABLE_INFO;
+
+#endif
diff --git a/UefiPayloadPkg/UefiPayloadPkg.dec b/UefiPayloadPkg/UefiPayloadPkg.dec
index 3ffdce550d..e385dc7219 100644
--- a/UefiPayloadPkg/UefiPayloadPkg.dec
+++ b/UefiPayloadPkg/UefiPayloadPkg.dec
@@ -37,6 +37,8 @@
gUefiSerialPortInfoGuid = { 0x6c6872fe, 0x56a9, 0x4403, { 0xbb, 0x98, 0x95, 0x8d, 0x62, 0xde, 0x87, 0xf1 } }
gLoaderMemoryMapInfoGuid = { 0xa1ff7424, 0x7a1a, 0x478e, { 0xa9, 0xe4, 0x92, 0xf3, 0x57, 0xd1, 0x28, 0x32 } }
+ # SMM variable support
+ gNvVariableInfoGuid = { 0x7a345dca, 0xc26, 0x4f2a, { 0xa8, 0x9a, 0x57, 0xc0, 0x8d, 0xdd, 0x22, 0xee } }
gSpiFlashInfoGuid = { 0x2d4aac1b, 0x91a5, 0x4cd5, { 0x9b, 0x5c, 0xb4, 0x0f, 0x5d, 0x28, 0x51, 0xa1 } }
gSmmRegisterInfoGuid = { 0xaa9bd7a7, 0xcafb, 0x4499, { 0xa4, 0xa9, 0xb, 0x34, 0x6b, 0x40, 0xa6, 0x22 } }
gS3CommunicationGuid = { 0x88e31ba1, 0x1856, 0x4b8b, { 0xbb, 0xdf, 0xf8, 0x16, 0xdd, 0x94, 0xa, 0xef } }
@@ -81,3 +83,7 @@ gUefiPayloadPkgTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode|0x80|UINT32|0x
gUefiPayloadPkgTokenSpaceGuid.PcdSystemMemoryUefiRegionSize|0x02000000|UINT32|0x00000017
gUefiPayloadPkgTokenSpaceGuid.PcdPcdDriverFile|{ 0x57, 0x72, 0xcf, 0x80, 0xab, 0x87, 0xf9, 0x47, 0xa3, 0xfe, 0xD5, 0x0B, 0x76, 0xd8, 0x95, 0x41 }|VOID*|0x00000018
+
+## FFS filename to find the default variable initial data file.
+# @Prompt FFS Name of variable initial data file
+ gUefiPayloadPkgTokenSpaceGuid.PcdNvsDataFile |{ 0x1a, 0xf1, 0xb1, 0xae, 0x42, 0xcc, 0xcf, 0x4e, 0xac, 0x60, 0xdb, 0xab, 0xf6, 0xca, 0x69, 0xe6 }|VOID*|0x00000025