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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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.
+ 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.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef FW_BLOCK_SERVICE_H_ +#define FW_BLOCK_SERVICE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#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.
+# +# 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.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef SMM_FVB_COMMON_H_ +#define SMM_FVB_COMMON_H_ + +#include + +#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.
+ 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