--- /dev/null
+/** @file\r
+Firmware Volume Block Driver to provide FVB service.\r
+\r
+ Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "FvbService.h"\r
+\r
+//\r
+// Global variable for this FVB driver which contains\r
+// the private data of all firmware volume block instances\r
+//\r
+FWB_GLOBAL mFvbModuleGlobal;\r
+\r
+FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate = {\r
+ {\r
+ {\r
+ HARDWARE_DEVICE_PATH,\r
+ HW_MEMMAP_DP,\r
+ {\r
+ (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),\r
+ (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)\r
+ }\r
+ },\r
+ EfiMemoryMappedIO,\r
+ (EFI_PHYSICAL_ADDRESS) 0,\r
+ (EFI_PHYSICAL_ADDRESS) 0,\r
+ },\r
+ {\r
+ END_DEVICE_PATH_TYPE,\r
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+ {\r
+ END_DEVICE_PATH_LENGTH,\r
+ 0\r
+ }\r
+ }\r
+};\r
+\r
+FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate = {\r
+ {\r
+ {\r
+ MEDIA_DEVICE_PATH,\r
+ MEDIA_PIWG_FW_VOL_DP,\r
+ {\r
+ (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),\r
+ (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)\r
+ }\r
+ },\r
+ { 0 }\r
+ },\r
+ {\r
+ END_DEVICE_PATH_TYPE,\r
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+ {\r
+ END_DEVICE_PATH_LENGTH,\r
+ 0\r
+ }\r
+ }\r
+};\r
+\r
+\r
+EFI_FW_VOL_BLOCK_DEVICE mFvbDeviceTemplate = {\r
+ FVB_DEVICE_SIGNATURE,\r
+ NULL,\r
+ 0, // Instance\r
+ {\r
+ FvbProtocolGetAttributes,\r
+ FvbProtocolSetAttributes,\r
+ FvbProtocolGetPhysicalAddress,\r
+ FvbProtocolGetBlockSize,\r
+ FvbProtocolRead,\r
+ FvbProtocolWrite,\r
+ FvbProtocolEraseBlocks,\r
+ NULL\r
+ } // FwVolBlockInstance\r
+};\r
+\r
+\r
+/**\r
+ Get the pointer to EFI_FW_VOL_INSTANCE from the buffer pointed\r
+ by mFvbModuleGlobal.FvInstance based on a index.\r
+ Each EFI_FW_VOL_INSTANCE is with variable length as\r
+ we have a block map at the end of the EFI_FIRMWARE_VOLUME_HEADER.\r
+\r
+ @param[in] Instance The index of the EFI_FW_VOL_INSTANCE.\r
+\r
+ @return A pointer to EFI_FW_VOL_INSTANCE.\r
+\r
+**/\r
+EFI_FW_VOL_INSTANCE *\r
+GetFvbInstance (\r
+ IN UINTN Instance\r
+ )\r
+{\r
+ EFI_FW_VOL_INSTANCE *FwhRecord;\r
+\r
+ if ( Instance >= mFvbModuleGlobal.NumFv ) {\r
+ ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Find the right instance of the FVB private data\r
+ //\r
+ FwhRecord = mFvbModuleGlobal.FvInstance;\r
+ while ( Instance > 0 ) {\r
+ FwhRecord = (EFI_FW_VOL_INSTANCE *) ((UINTN)((UINT8 *)FwhRecord) +\r
+ FwhRecord->VolumeHeader.HeaderLength +\r
+ (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER)));\r
+ Instance--;\r
+ }\r
+\r
+ return FwhRecord;\r
+\r
+}\r
+\r
+\r
+/**\r
+ Get the EFI_FVB_ATTRIBUTES_2 of a FV.\r
+\r
+ @param[in] Instance The index of the EFI_FW_VOL_INSTANCE.\r
+\r
+ @retval EFI_FVB_ATTRIBUTES_2 of the FV identified by Instance.\r
+\r
+**/\r
+STATIC\r
+EFI_FVB_ATTRIBUTES_2\r
+FvbGetVolumeAttributes (\r
+ IN UINTN Instance\r
+ )\r
+{\r
+ EFI_FW_VOL_INSTANCE * FwInstance;\r
+ FwInstance = GetFvbInstance(Instance);\r
+ ASSERT (FwInstance != NULL);\r
+\r
+ if (FwInstance == NULL) {\r
+ return 0;\r
+ }\r
+\r
+ return FwInstance->VolumeHeader.Attributes;\r
+}\r
+\r
+\r
+\r
+/**\r
+ Retrieves the starting address of an LBA in an FV. It also\r
+ return a few other attribut of the FV.\r
+\r
+ @param[in] Instance The index of the EFI_FW_VOL_INSTANCE.\r
+ @param[in] Lba The logical block address\r
+ @param[out] LbaAddress On output, contains the physical starting address\r
+ of the Lba\r
+ @param[out] LbaLength On output, contains the length of the block\r
+ @param[out] NumOfBlocks A pointer to a caller allocated UINTN in which the\r
+ number of consecutive blocks starting with Lba is\r
+ returned. All blocks in this range have a size of\r
+ BlockSize\r
+\r
+ @retval EFI_SUCCESS Successfully returns\r
+ @retval EFI_INVALID_PARAMETER Instance not found\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+FvbGetLbaAddress (\r
+ IN UINTN Instance,\r
+ IN EFI_LBA Lba,\r
+ OUT UINTN *LbaAddress,\r
+ OUT UINTN *LbaLength,\r
+ OUT UINTN *NumOfBlocks\r
+ )\r
+{\r
+ UINT32 NumBlocks;\r
+ UINT32 BlockLength;\r
+ UINTN Offset;\r
+ EFI_LBA StartLba;\r
+ EFI_LBA NextLba;\r
+ EFI_FW_VOL_INSTANCE *FwhInstance;\r
+ EFI_FV_BLOCK_MAP_ENTRY *BlockMap;\r
+\r
+ //\r
+ // Find the right instance of the FVB private data\r
+ //\r
+ FwhInstance = GetFvbInstance (Instance);\r
+ if (FwhInstance == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ StartLba = 0;\r
+ Offset = 0;\r
+ BlockMap = &FwhInstance->VolumeHeader.BlockMap[0];\r
+ ASSERT (BlockMap != NULL);\r
+\r
+ //\r
+ // Parse the blockmap of the FV to find which map entry the Lba belongs to\r
+ //\r
+ while (TRUE) {\r
+ if ( BlockMap != NULL) {\r
+ NumBlocks = BlockMap->NumBlocks;\r
+ BlockLength = BlockMap->Length;\r
+ }\r
+\r
+ if ( NumBlocks == 0 || BlockLength == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ NextLba = StartLba + NumBlocks;\r
+\r
+ //\r
+ // The map entry found\r
+ //\r
+ if (Lba >= StartLba && Lba < NextLba) {\r
+ Offset = Offset + (UINTN)MultU64x32((Lba - StartLba), BlockLength);\r
+ if (LbaAddress != NULL) {\r
+ *LbaAddress = FwhInstance->FvBase + Offset;\r
+ }\r
+\r
+ if (LbaLength != NULL) {\r
+ *LbaLength = BlockLength;\r
+ }\r
+\r
+ if (NumOfBlocks != NULL) {\r
+ *NumOfBlocks = (UINTN)(NextLba - Lba);\r
+ }\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ StartLba = NextLba;\r
+ Offset = Offset + NumBlocks * BlockLength;\r
+ BlockMap++;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Reads specified number of bytes into a buffer from the specified block\r
+\r
+ @param[in] Instance The FV instance to be read from\r
+ @param[in] Lba The logical block address to be read from\r
+ @param[in] BlockOffset Offset into the block at which to begin reading\r
+ @param[in, out] NumBytes Pointer that on input contains the total size of\r
+ the buffer. On output, it contains the total number\r
+ of bytes read\r
+ @param[in] Buffer Pointer to a caller allocated buffer that will be\r
+ used to hold the data read\r
+\r
+\r
+ @retval EFI_SUCCESS The firmware volume was read successfully and\r
+ contents are in Buffer\r
+ @retval EFI_BAD_BUFFER_SIZE Read attempted across a LBA boundary. On output,\r
+ NumBytes contains the total number of bytes returned\r
+ in Buffer\r
+ @retval EFI_ACCESS_DENIED The firmware volume is in the ReadDisabled state\r
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and\r
+ could not be read\r
+ @retval EFI_INVALID_PARAMETER Instance not found, or NumBytes, Buffer are NULL\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+FvbReadBlock (\r
+ IN UINTN Instance,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BlockOffset,\r
+ IN OUT UINTN *NumBytes,\r
+ IN UINT8 *Buffer\r
+ )\r
+{\r
+ EFI_FVB_ATTRIBUTES_2 Attributes;\r
+ UINTN LbaAddress;\r
+ UINTN LbaLength;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReadStatus;\r
+\r
+ if ( (NumBytes == NULL) || (Buffer == NULL)) {\r
+ return (EFI_INVALID_PARAMETER);\r
+ }\r
+ if (*NumBytes == 0) {\r
+ return (EFI_INVALID_PARAMETER);\r
+ }\r
+\r
+ Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Attributes = FvbGetVolumeAttributes (Instance);\r
+\r
+ if ( (Attributes & EFI_FVB2_READ_STATUS) == 0) {\r
+ return (EFI_ACCESS_DENIED);\r
+ }\r
+\r
+ if (BlockOffset > LbaLength) {\r
+ return (EFI_INVALID_PARAMETER);\r
+ }\r
+\r
+ if (LbaLength < ( *NumBytes + BlockOffset ) ) {\r
+ *NumBytes = (UINT32) (LbaLength - BlockOffset);\r
+ Status = EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ ReadStatus = LibFvbFlashDeviceRead (LbaAddress + BlockOffset, NumBytes, Buffer);\r
+ if (EFI_ERROR(ReadStatus)) {\r
+ return ReadStatus;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Writes specified number of bytes from the input buffer to the block\r
+\r
+ @param[in] Instance The FV instance to be written to\r
+ @param[in] Lba The starting logical block index to write to\r
+ @param[in] BlockOffset Offset into the block at which to begin writing\r
+ @param[in, out] NumBytes Pointer that on input contains the total size of\r
+ the buffer. On output, it contains the total number\r
+ of bytes actually written\r
+ @param[in] Buffer Pointer to a caller allocated buffer that contains\r
+ the source for the write\r
+ @retval EFI_SUCCESS The firmware volume was written successfully\r
+ @retval EFI_BAD_BUFFER_SIZE Write attempted across a LBA boundary. On output,\r
+ NumBytes contains the total number of bytes\r
+ actually written\r
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state\r
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and\r
+ could not be written\r
+ @retval EFI_INVALID_PARAMETER Instance not found, or NumBytes, Buffer are NULL\r
+\r
+**/\r
+EFI_STATUS\r
+FvbWriteBlock (\r
+ IN UINTN Instance,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BlockOffset,\r
+ IN OUT UINTN *NumBytes,\r
+ IN UINT8 *Buffer\r
+ )\r
+{\r
+ EFI_FVB_ATTRIBUTES_2 Attributes;\r
+ UINTN LbaAddress;\r
+ UINTN LbaLength;\r
+ EFI_STATUS Status;\r
+\r
+ if ( (NumBytes == NULL) || (Buffer == NULL)) {\r
+ return (EFI_INVALID_PARAMETER);\r
+ }\r
+ if (*NumBytes == 0) {\r
+ return (EFI_INVALID_PARAMETER);\r
+ }\r
+\r
+ Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check if the FV is write enabled\r
+ //\r
+ Attributes = FvbGetVolumeAttributes (Instance);\r
+ if ( (Attributes & EFI_FVB2_WRITE_STATUS) == 0) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Perform boundary checks and adjust NumBytes\r
+ //\r
+ if (BlockOffset > LbaLength) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ( LbaLength < ( *NumBytes + BlockOffset ) ) {\r
+ DEBUG ((DEBUG_ERROR,\r
+ "FvWriteBlock: Reducing Numbytes from 0x%x to 0x%x\n",\r
+ *NumBytes, (UINT32)(LbaLength - BlockOffset)));\r
+ *NumBytes = (UINT32) (LbaLength - BlockOffset);\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, FALSE);\r
+ Status = LibFvbFlashDeviceWrite (LbaAddress + BlockOffset, NumBytes, Buffer);\r
+\r
+ LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, TRUE);\r
+ WriteBackInvalidateDataCacheRange ((VOID *) (LbaAddress + BlockOffset), *NumBytes);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Erases and initializes a firmware volume block\r
+\r
+ @param[in] Instance The FV instance to be erased\r
+ @param[in] Lba The logical block index to be erased\r
+\r
+ @retval EFI_SUCCESS The erase request was successfully completed\r
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state\r
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and\r
+ could not be written. Firmware device may have been\r
+ partially erased\r
+ @retval EFI_INVALID_PARAMETER Instance not found\r
+\r
+**/\r
+EFI_STATUS\r
+FvbEraseBlock (\r
+ IN UINTN Instance,\r
+ IN EFI_LBA Lba\r
+ )\r
+{\r
+\r
+ EFI_FVB_ATTRIBUTES_2 Attributes;\r
+ UINTN LbaAddress;\r
+ UINTN LbaLength;\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Check if the FV is write enabled\r
+ //\r
+ Attributes = FvbGetVolumeAttributes (Instance);\r
+\r
+ if( (Attributes & EFI_FVB2_WRITE_STATUS) == 0) {\r
+ return (EFI_ACCESS_DENIED);\r
+ }\r
+\r
+ //\r
+ // Get the starting address of the block for erase.\r
+ //\r
+ Status = FvbGetLbaAddress (Instance, Lba, &LbaAddress, &LbaLength, NULL);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, FALSE);\r
+\r
+ Status = LibFvbFlashDeviceBlockErase (LbaAddress, LbaLength);\r
+\r
+ LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, TRUE);\r
+\r
+ WriteBackInvalidateDataCacheRange ((VOID *) LbaAddress, LbaLength);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Modifies the current settings of the firmware volume according to the\r
+ input parameter, and returns the new setting of the volume\r
+\r
+ @param[in] Instance The FV instance whose attributes is going to be\r
+ modified\r
+ @param[in, out] Attributes On input, it is a pointer to EFI_FVB_ATTRIBUTES_2\r
+ containing the desired firmware volume settings.\r
+ On successful return, it contains the new settings\r
+ of the firmware volume\r
+\r
+ @retval EFI_SUCCESS Successfully returns\r
+ @retval EFI_ACCESS_DENIED The volume setting is locked and cannot be modified\r
+ @retval EFI_INVALID_PARAMETER Instance not found, or The attributes requested are\r
+ in conflict with the capabilities as declared in the\r
+ firmware volume header\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+FvbSetVolumeAttributes (\r
+ IN UINTN Instance,\r
+ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes\r
+ )\r
+{\r
+ EFI_FW_VOL_INSTANCE *FwhInstance;\r
+ EFI_FVB_ATTRIBUTES_2 OldAttributes;\r
+ EFI_FVB_ATTRIBUTES_2 *AttribPtr;\r
+ EFI_FVB_ATTRIBUTES_2 UnchangedAttributes;\r
+ UINT32 Capabilities;\r
+ UINT32 OldStatus;\r
+ UINT32 NewStatus;\r
+\r
+ //\r
+ // Find the right instance of the FVB private data\r
+ //\r
+ FwhInstance = GetFvbInstance (Instance);\r
+ if (FwhInstance == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ AttribPtr = (EFI_FVB_ATTRIBUTES_2 *) &(FwhInstance->VolumeHeader.Attributes);\r
+ ASSERT (AttribPtr != NULL);\r
+ if ( AttribPtr == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ OldAttributes = *AttribPtr;\r
+ Capabilities = OldAttributes & EFI_FVB2_CAPABILITIES;\r
+ OldStatus = OldAttributes & EFI_FVB2_STATUS;\r
+ NewStatus = *Attributes & EFI_FVB2_STATUS;\r
+\r
+ UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP | \\r
+ EFI_FVB2_READ_ENABLED_CAP | \\r
+ EFI_FVB2_WRITE_DISABLED_CAP | \\r
+ EFI_FVB2_WRITE_ENABLED_CAP | \\r
+ EFI_FVB2_LOCK_CAP | \\r
+ EFI_FVB2_STICKY_WRITE | \\r
+ EFI_FVB2_MEMORY_MAPPED | \\r
+ EFI_FVB2_ERASE_POLARITY | \\r
+ EFI_FVB2_READ_LOCK_CAP | \\r
+ EFI_FVB2_WRITE_LOCK_CAP | \\r
+ EFI_FVB2_ALIGNMENT;\r
+\r
+ //\r
+ // Some attributes of FV is read only can *not* be set\r
+ //\r
+ if ((OldAttributes & UnchangedAttributes) ^ (*Attributes & UnchangedAttributes)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // If firmware volume is locked, no status bit can be updated\r
+ //\r
+ if ((OldAttributes & EFI_FVB2_LOCK_STATUS) != 0) {\r
+ if ((OldStatus ^ NewStatus) != 0) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Test read disable\r
+ //\r
+ if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {\r
+ if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Test read enable\r
+ //\r
+ if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {\r
+ if ((NewStatus & EFI_FVB2_READ_STATUS) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Test write disable\r
+ //\r
+ if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {\r
+ if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Test write enable\r
+ //\r
+ if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {\r
+ if ((NewStatus & EFI_FVB2_WRITE_STATUS) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Test lock\r
+ //\r
+ if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {\r
+ if ((NewStatus & EFI_FVB2_LOCK_STATUS) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ *AttribPtr = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));\r
+ *AttribPtr = (*AttribPtr) | NewStatus;\r
+ *Attributes = *AttribPtr;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Retrieves the physical address of the device.\r
+\r
+ @param[in] This A pointer to EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL.\r
+ @param[out] Address Output buffer containing the address.\r
+\r
+ @retval EFI_SUCCESS The function always return successfully.\r
+ @retval EFI_INVALID_PARAMETER Instance not found.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvbProtocolGetPhysicalAddress (\r
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
+ OUT EFI_PHYSICAL_ADDRESS *Address\r
+ )\r
+{\r
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
+ EFI_FW_VOL_INSTANCE *FwhInstance;\r
+\r
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+ FwhInstance = GetFvbInstance(FvbDevice->Instance);\r
+ if (FwhInstance == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *Address = FwhInstance->FvBase;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+/**\r
+ Retrieve the size of a logical block\r
+\r
+ @param[in] This Calling context\r
+ @param[in] Lba Indicates which block to return the size for.\r
+ @param[out] BlockSize A pointer to a caller allocated UINTN in which\r
+ the size of the block is returned\r
+ @param[out] NumOfBlocks A pointer to a caller allocated UINTN in which the\r
+ number of consecutive blocks starting with Lba is\r
+ returned. All blocks in this range have a size of\r
+ BlockSize\r
+\r
+ @retval EFI_SUCCESS The function always return successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvbProtocolGetBlockSize (\r
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
+ IN EFI_LBA Lba,\r
+ OUT UINTN *BlockSize,\r
+ OUT UINTN *NumOfBlocks\r
+ )\r
+{\r
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
+\r
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+ return FvbGetLbaAddress (FvbDevice->Instance, Lba, NULL, BlockSize, NumOfBlocks);\r
+}\r
+\r
+\r
+/**\r
+ Retrieves Volume attributes. No polarity translations are done.\r
+\r
+ @param[in] This Calling context\r
+ @param[out] Attributes Output buffer which contains attributes\r
+\r
+ @retval EFI_SUCCESS The function always return successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvbProtocolGetAttributes (\r
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
+ OUT EFI_FVB_ATTRIBUTES_2 *Attributes\r
+ )\r
+{\r
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
+\r
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+ *Attributes = FvbGetVolumeAttributes (FvbDevice->Instance);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Sets Volume attributes. No polarity translations are done.\r
+\r
+ @param[in] This Calling context\r
+ @param[in, out] Attributes Output buffer which contains attributes\r
+\r
+ @retval EFI_SUCCESS The function always return successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvbProtocolSetAttributes (\r
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
+ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
+\r
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+ Status = FvbSetVolumeAttributes (FvbDevice->Instance, Attributes);\r
+ return Status;\r
+}\r
+\r
+\r
+\r
+/**\r
+ This function erases one or more blocks as denoted by the\r
+ variable argument list. The entire parameter list of blocks must be verified\r
+ prior to erasing any blocks. If a block is requested that does not exist\r
+ within the associated firmware volume (it has a larger index than the last\r
+ block of the firmware volume), the EraseBlock() function must return\r
+ EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.\r
+\r
+ @param[in] This Calling context\r
+ @param[in] ... Starting LBA followed by Number of Lba to erase.\r
+ a -1 to terminate the list.\r
+\r
+ @retval EFI_SUCCESS The erase request was successfully completed\r
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state\r
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and\r
+ could not be written. Firmware device may have been\r
+ partially erased\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvbProtocolEraseBlocks (\r
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
+ ...\r
+ )\r
+{\r
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
+ EFI_FW_VOL_INSTANCE *FwhInstance;\r
+ UINTN NumOfBlocks;\r
+ VA_LIST args;\r
+ EFI_LBA StartingLba;\r
+ UINTN NumOfLba;\r
+ EFI_STATUS Status;\r
+\r
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+ FwhInstance = GetFvbInstance (FvbDevice->Instance);\r
+ if (FwhInstance == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ NumOfBlocks = FwhInstance->NumOfBlocks;\r
+ VA_START (args, This);\r
+\r
+ do {\r
+ StartingLba = VA_ARG (args, EFI_LBA);\r
+ if ( StartingLba == EFI_LBA_LIST_TERMINATOR ) {\r
+ break;\r
+ }\r
+\r
+ NumOfLba = VA_ARG (args, UINT32);\r
+\r
+ //\r
+ // Check input parameters\r
+ //\r
+ if (NumOfLba == 0) {\r
+ VA_END (args);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ( ( StartingLba + NumOfLba ) > NumOfBlocks ) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ } while ( 1 );\r
+\r
+ VA_END (args);\r
+\r
+ VA_START (args, This);\r
+ do {\r
+ StartingLba = VA_ARG (args, EFI_LBA);\r
+ if (StartingLba == EFI_LBA_LIST_TERMINATOR) {\r
+ break;\r
+ }\r
+\r
+ NumOfLba = VA_ARG (args, UINT32);\r
+\r
+ while ( NumOfLba > 0 ) {\r
+ Status = FvbEraseBlock (FvbDevice->Instance, StartingLba);\r
+ if ( EFI_ERROR(Status)) {\r
+ VA_END (args);\r
+ return Status;\r
+ }\r
+ StartingLba++;\r
+ NumOfLba--;\r
+ }\r
+ } while ( 1 );\r
+\r
+ VA_END (args);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+/**\r
+ Writes data beginning at Lba:Offset from FV. The write terminates either\r
+ when *NumBytes of data have been written, or when a block boundary is\r
+ reached. *NumBytes is updated to reflect the actual number of bytes\r
+ written. The write opertion does not include erase. This routine will\r
+ attempt to write only the specified bytes. If the writes do not stick,\r
+ it will return an error.\r
+\r
+ @param[in] This Calling context\r
+ @param[in] Lba Block in which to begin write\r
+ @param[in] Offset Offset in the block at which to begin write\r
+ @param[in,out] NumBytes On input, indicates the requested write size. On\r
+ output, indicates the actual number of bytes written\r
+ @param[in] Buffer Buffer containing source data for the write.\r
+\r
+ @retval EFI_SUCCESS The firmware volume was written successfully\r
+ @retval EFI_BAD_BUFFER_SIZE Write attempted across a LBA boundary. On output,\r
+ NumBytes contains the total number of bytes\r
+ actually written\r
+ @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state\r
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and\r
+ could not be written\r
+ @retval EFI_INVALID_PARAMETER NumBytes or Buffer are NULL\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvbProtocolWrite (\r
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN Offset,\r
+ IN OUT UINTN *NumBytes,\r
+ IN UINT8 *Buffer\r
+ )\r
+{\r
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
+ EFI_STATUS Status;\r
+\r
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+ Status = FvbWriteBlock (FvbDevice->Instance, Lba, Offset, NumBytes, Buffer);\r
+ DEBUG((DEBUG_VERBOSE,\r
+ "FvbWrite: Lba: 0x%lx Offset: 0x%x NumBytes: 0x%x, Buffer: 0x%x Status:%r\n",\r
+ Lba, Offset, *NumBytes, Buffer, Status));\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Reads data beginning at Lba:Offset from FV. The Read terminates either\r
+ when *NumBytes of data have been read, or when a block boundary is\r
+ reached. *NumBytes is updated to reflect the actual number of bytes\r
+ written. The write opertion does not include erase. This routine will\r
+ attempt to write only the specified bytes. If the writes do not stick,\r
+ it will return an error.\r
+\r
+ @param[in] This Calling context\r
+ @param[in] Lba Block in which to begin write\r
+ @param[in] Offset Offset in the block at which to begin write\r
+ @param[in,out] NumBytes On input, indicates the requested write size. On\r
+ output, indicates the actual number of bytes written\r
+ @param[out] Buffer Buffer containing source data for the write.\r
+\r
+\r
+Returns:\r
+ @retval EFI_SUCCESS The firmware volume was read successfully and\r
+ contents are in Buffer\r
+ @retval EFI_BAD_BUFFER_SIZE Read attempted across a LBA boundary. On output,\r
+ NumBytes contains the total number of bytes returned\r
+ in Buffer\r
+ @retval EFI_ACCESS_DENIED The firmware volume is in the ReadDisabled state\r
+ @retval EFI_DEVICE_ERROR The block device is not functioning correctly and\r
+ could not be read\r
+ @retval EFI_INVALID_PARAMETER NumBytes or Buffer are NULL\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FvbProtocolRead (\r
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN Offset,\r
+ IN OUT UINTN *NumBytes,\r
+ OUT UINT8 *Buffer\r
+ )\r
+{\r
+\r
+ EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;\r
+ EFI_STATUS Status;\r
+\r
+ FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+ Status = FvbReadBlock (FvbDevice->Instance, Lba, Offset, NumBytes, Buffer);\r
+ DEBUG((DEBUG_VERBOSE,\r
+ "FvbRead: Lba: 0x%lx Offset: 0x%x NumBytes: 0x%x, Buffer: 0x%x, Status:%r\n",\r
+ Lba, Offset, *NumBytes, Buffer, Status));\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check the integrity of firmware volume header in FvBase\r
+\r
+ @param[in] FvBase A pointer to firmware volume base address.\r
+\r
+ @retval TRUE The firmware volume is consistent\r
+ @retval FALSE The firmware volume has corrupted.\r
+\r
+**/\r
+BOOLEAN\r
+IsFvHeaderValid (\r
+ IN EFI_PHYSICAL_ADDRESS FvBase\r
+ )\r
+{\r
+ UINT16 Sum;\r
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
+\r
+ FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) FvBase;\r
+ if (FvBase == PcdGet32(PcdFlashNvStorageVariableBase)) {\r
+ if (CompareMem (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid, sizeof(EFI_GUID)) != 0 ) {\r
+ DEBUG((DEBUG_INFO, " --FileSystemGuid not match: %g\n", &FwVolHeader->FileSystemGuid));\r
+ return FALSE;\r
+ }\r
+ } else {\r
+ if (CompareMem (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem2Guid, sizeof(EFI_GUID)) != 0 ) {\r
+ DEBUG((DEBUG_INFO, " --not expected guid.\n"));\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ if ( (FwVolHeader->Revision != EFI_FVH_REVISION) ||\r
+ (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||\r
+ (FwVolHeader->FvLength == ((UINTN) -1)) ||\r
+ ((FwVolHeader->HeaderLength & 0x01 ) !=0) ) {\r
+ DEBUG((DEBUG_INFO, " -- >Revision = 0x%x, Signature = 0x%x\n", FwVolHeader->Revision, FwVolHeader->Signature ));\r
+ DEBUG((DEBUG_INFO, " -- >FvLength = 0x%lx, HeaderLength = 0x%x\n", FwVolHeader->FvLength, FwVolHeader->HeaderLength ));\r
+ return FALSE;\r
+ }\r
+\r
+ Sum = CalculateSum16 ((UINT16 *) FwVolHeader, FwVolHeader->HeaderLength);\r
+ if (Sum != 0) {\r
+ DEBUG((DEBUG_INFO, "error: checksum: 0x%04X (expect 0x0)\n", Sum));\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ Get intial variable data.\r
+\r
+ @param[out] VarData Valid variable data.\r
+ @param[out] VarSize Valid variable size.\r
+\r
+ @retval RETURN_SUCCESS Successfully found initial variable data.\r
+ @retval RETURN_NOT_FOUND Failed to find the variable data file from FV.\r
+ @retval EFI_INVALID_PARAMETER VarData or VarSize is null.\r
+\r
+**/\r
+EFI_STATUS\r
+GetInitialVariableData (\r
+ OUT VOID **VarData,\r
+ OUT UINTN *VarSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *ImageData;\r
+ UINTN ImageSize;\r
+ EFI_FIRMWARE_VOLUME_HEADER *FvHeader;\r
+ VARIABLE_STORE_HEADER *VariableStore;\r
+ AUTHENTICATED_VARIABLE_HEADER *Variable;\r
+ UINTN VariableSize;\r
+ UINTN VarEndAddr;\r
+\r
+ if ((VarData == NULL) || (VarSize == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = GetSectionFromAnyFv (PcdGetPtr(PcdNvsDataFile), EFI_SECTION_RAW, 0, &ImageData, &ImageSize);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ImageData;\r
+ VariableStore = (VARIABLE_STORE_HEADER *) ((UINT8 *)ImageData + FvHeader->HeaderLength);\r
+ VarEndAddr = (UINTN)VariableStore + VariableStore->Size;\r
+ Variable = (AUTHENTICATED_VARIABLE_HEADER *) HEADER_ALIGN (VariableStore + 1);\r
+ *VarData = (VOID *)Variable;\r
+ while (((UINTN)Variable < VarEndAddr)) {\r
+ if (Variable->StartId != VARIABLE_DATA) {\r
+ break;\r
+ }\r
+ VariableSize = sizeof (AUTHENTICATED_VARIABLE_HEADER) + Variable->DataSize + Variable->NameSize;\r
+ Variable = (AUTHENTICATED_VARIABLE_HEADER *) HEADER_ALIGN ((UINTN) Variable + VariableSize);\r
+ }\r
+\r
+ *VarSize = (UINTN)Variable - HEADER_ALIGN (VariableStore + 1);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The function does the necessary initialization work for\r
+ Firmware Volume Block Driver.\r
+\r
+ @retval EFI_SUCCESS This funtion always return EFI_SUCCESS.\r
+ It will ASSERT on errors.\r
+\r
+**/\r
+EFI_STATUS\r
+FvbInitialize (\r
+ VOID\r
+ )\r
+{\r
+ EFI_FW_VOL_INSTANCE *FwVolInstance;\r
+ EFI_FIRMWARE_VOLUME_HEADER *FvHeader;\r
+ EFI_FV_BLOCK_MAP_ENTRY *BlockMap;\r
+ EFI_PHYSICAL_ADDRESS BaseAddress;\r
+ UINTN WriteAddr;\r
+ EFI_STATUS Status;\r
+ UINTN BufferSize;\r
+ UINTN Length;\r
+ VARIABLE_STORE_HEADER VariableStore;\r
+ VOID *VarData;\r
+\r
+ InitVariableStore ();\r
+ BaseAddress = PcdGet32(PcdFlashNvStorageVariableBase);\r
+ FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress;\r
+\r
+ //\r
+ // Check FV header and variable store header\r
+ //\r
+ if (!IsFvHeaderValid (BaseAddress)) {\r
+ //\r
+ // Write back a healthy FV header\r
+ //\r
+ DEBUG ((DEBUG_ERROR, "Fvb: Writing back a healthy FV header: 0x%lx\n", BaseAddress));\r
+ FvHeader = GetFvHeaderTemplate ();\r
+ LibFvbFlashDeviceBlockLock ((UINTN)BaseAddress, FvHeader->BlockMap->Length, FALSE);\r
+\r
+ Status = LibFvbFlashDeviceBlockErase ((UINTN)BaseAddress, FvHeader->BlockMap->Length);\r
+ ASSERT_EFI_ERROR(Status);\r
+\r
+ Length = FvHeader->HeaderLength;\r
+ WriteAddr = (UINTN)BaseAddress;\r
+ Status = LibFvbFlashDeviceWrite (WriteAddr, &Length, (UINT8 *) FvHeader);\r
+ WriteAddr += Length;\r
+ ASSERT_EFI_ERROR(Status);\r
+\r
+ //\r
+ // Write back variable store header\r
+ //\r
+ VariableStore.Size = PcdGet32(PcdFlashNvStorageVariableSize) - FvHeader->HeaderLength;\r
+ VariableStore.Format = VARIABLE_STORE_FORMATTED;\r
+ VariableStore.State = VARIABLE_STORE_HEALTHY;\r
+ CopyGuid (&VariableStore.Signature, &gEfiAuthenticatedVariableGuid);\r
+ BufferSize = sizeof (VARIABLE_STORE_HEADER);\r
+ Status = LibFvbFlashDeviceWrite (WriteAddr, &BufferSize, (UINT8 *) &VariableStore);\r
+ WriteAddr += BufferSize;\r
+ ASSERT_EFI_ERROR(Status);\r
+\r
+ //\r
+ // Write initial variable data if found\r
+ //\r
+ Status = GetInitialVariableData (&VarData, &Length);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = LibFvbFlashDeviceWrite (WriteAddr, &Length, (UINT8 *) VarData);\r
+ ASSERT_EFI_ERROR(Status);\r
+ }\r
+\r
+ LibFvbFlashDeviceBlockLock ((UINTN)BaseAddress, FvHeader->BlockMap->Length, TRUE);\r
+ WriteBackInvalidateDataCacheRange ((VOID *) (UINTN) BaseAddress, FvHeader->BlockMap->Length);\r
+ }\r
+\r
+ //\r
+ // Create a new FW volume instance for NVS variable\r
+ //\r
+ BufferSize = FvHeader->HeaderLength + sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER);\r
+ FwVolInstance = (EFI_FW_VOL_INSTANCE *) AllocateRuntimeZeroPool (BufferSize);\r
+ if (FwVolInstance == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ FwVolInstance->FvBase = (UINTN)BaseAddress;\r
+ CopyMem (&FwVolInstance->VolumeHeader, FvHeader, FvHeader->HeaderLength);\r
+\r
+ //\r
+ // Process the block map for each FV. Assume it has same block size.\r
+ //\r
+ FwVolInstance->NumOfBlocks = 0;\r
+ FvHeader = &FwVolInstance->VolumeHeader;\r
+ for (BlockMap = FvHeader->BlockMap; BlockMap->NumBlocks != 0; BlockMap++) {\r
+ FwVolInstance->NumOfBlocks += BlockMap->NumBlocks;\r
+ }\r
+\r
+ //\r
+ // Add a FVB Protocol Instance\r
+ //\r
+ Status = InstallFvbProtocol (FwVolInstance, mFvbModuleGlobal.NumFv);\r
+ mFvbModuleGlobal.NumFv++;\r
+ mFvbModuleGlobal.FvInstance = FwVolInstance;\r
+\r
+ return Status;\r
+}\r