]> git.proxmox.com Git - mirror_edk2.git/blobdiff - Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbService.c
Upload BSD-licensed Vlv2TbltDevicePkg and Vlv2DeviceRefCodePkg to
[mirror_edk2.git] / Vlv2TbltDevicePkg / FvbRuntimeDxe / FvbService.c
diff --git a/Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbService.c b/Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbService.c
new file mode 100644 (file)
index 0000000..da7dce6
--- /dev/null
@@ -0,0 +1,1114 @@
+/** @file\r
+  Firmware Volume Block Driver for Lakeport Platform.\r
+\r
+  Firmware volume block driver for FWH or SPI device.\r
+  It depends on which Flash Device Library to be linked with this driver.\r
+\r
+Copyright (c) 2006  - 2014, Intel Corporation. All rights reserved.<BR>\r
+                                                                                   \r\r
+  This program and the accompanying materials are licensed and made available under\r\r
+  the terms and conditions of the BSD License that accompanies this distribution.  \r\r
+  The full text of the license may be found at                                     \r\r
+  http://opensource.org/licenses/bsd-license.php.                                  \r\r
+                                                                                   \r\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,            \r\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.    \r\r
+                                                                                   \r\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
+//\r
+// This platform driver knows there are 3 FVs on\r
+// FD, which are FvRecovery, FvMain and FvNvStorage.\r
+//\r
+UINT32 mPlatformFvBaseAddress[] = {\r
+  FixedPcdGet32(PcdFlashNvStorageVariableBase),\r
+};\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
+// Template structure used when installing FVB protocol.\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]  The index of the EFI_FW_VOL_INSTANCE.\r
+\r
+  @return     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 = NULL;\r
+  FwInstance = GetFvbInstance(Instance);\r
+  ASSERT_EFI_ERROR (FwInstance != NULL);\r
+\r
+  if ( FwInstance != NULL ) {\r
+    return FwInstance->VolumeHeader.Attributes;\r
+  } else {\r
+    return 0;\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 = 0;\r
+  UINT32                                  BlockLength = 0;\r
+  UINTN                                   Offset;\r
+  EFI_LBA                                 StartLba;\r
+  EFI_LBA                                 NextLba;\r
+  EFI_FW_VOL_INSTANCE                     *FwhInstance;\r
+  EFI_FV_BLOCK_MAP_ENTRY                  *BlockMap = NULL;\r
+\r
+  //\r
+  // Find the right instance of the FVB private data.\r
+  //\r
+  FwhInstance = GetFvbInstance (Instance);\r
+\r
+  StartLba  = 0;\r
+  Offset    = 0;\r
+  BlockMap  = &(FwhInstance->VolumeHeader.BlockMap[0]);\r
+  ASSERT_EFI_ERROR (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 && FwhInstance ) {\r
+        *LbaAddress = FwhInstance->FvBase + Offset;\r
+      }\r
+\r
+      if (LbaLength ) {\r
+        *LbaLength = BlockLength;\r
+      }\r
+\r
+      if (NumOfBlocks ) {\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]      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
+\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
+  LibFvbFlashDeviceRead (LbaAddress + BlockOffset, NumBytes, Buffer);\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]  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 writte.\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_FW_VOL_INSTANCE                     *FwhInstance;\r
+  EFI_STATUS                              Status;\r
+  EFI_STATUS                              Status1;\r
+\r
+  FwhInstance = GetFvbInstance (Instance);\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 ((EFI_D_ERROR,\r
+      "FvWriteBlock: Reducing Numbytes from 0x%x to 0x%x\n",\r
+      *NumBytes,\r
+      (UINT32)(LbaLength-BlockOffset))\r
+      );\r
+    *NumBytes = (UINT32) (LbaLength - BlockOffset);\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, FALSE);\r
+\r
+  Status1 = LibFvbFlashDeviceWrite (LbaAddress + BlockOffset, NumBytes, Buffer);\r
+\r
+  LibFvbFlashDeviceBlockLock (LbaAddress, LbaLength, TRUE);\r
+  WriteBackInvalidateDataCacheRange ((VOID *) (LbaAddress + BlockOffset), *NumBytes);\r
+\r
+  if ( EFI_ERROR (Status1) ) {\r
+    return Status1;\r
+  }\r
+\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
+  EFI_FVB_ATTRIBUTES_2                    Attributes;\r
+  UINTN                                   LbaAddress;\r
+  EFI_FW_VOL_INSTANCE                     *FwhInstance;\r
+  UINTN                                   LbaLength;\r
+  EFI_STATUS                              Status;\r
+\r
+  //\r
+  // Find the right instance of the FVB private data.\r
+  //\r
+  FwhInstance = GetFvbInstance (Instance);\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
+/**\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]  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 = NULL;\r
+  EFI_FVB_ATTRIBUTES_2                      OldAttributes = 0;\r
+  EFI_FVB_ATTRIBUTES_2                      *AttribPtr = NULL;\r
+  EFI_FVB_ATTRIBUTES_2                      UnchangedAttributes;\r
+  UINT32                                    Capabilities;\r
+  UINT32                                    OldStatus, NewStatus;\r
+\r
+  //\r
+  // Find the right instance of the FVB private data.\r
+  //\r
+  FwhInstance = GetFvbInstance (Instance);\r
+\r
+  AttribPtr     = (EFI_FVB_ATTRIBUTES_2 *) & (FwhInstance->VolumeHeader.Attributes);\r
+  ASSERT_EFI_ERROR (AttribPtr != NULL);\r
+\r
+  if ( AttribPtr != NULL) {\r
+    OldAttributes = *AttribPtr;\r
+  }\r
+\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 ) {\r
+    if ( OldStatus ^ NewStatus ) {\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) {\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) {\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) {\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
+// FVB protocol APIs.\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
+\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                   *FvInstance;\r
+\r
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+  FvInstance = GetFvbInstance(FvbDevice->Instance);\r
+\r
+  if (FvInstance != NULL) {\r
+    *Address = FvInstance->FvBase;\r
+  }\r
+\r
+  return EFI_SUCCESS;\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
+  DEBUG((EFI_D_INFO,\r
+    "FvbProtocolGetBlockSize: Lba: 0x%lx BlockSize: 0x%x NumOfBlocks: 0x%x\n",\r
+    Lba,\r
+    BlockSize,\r
+    NumOfBlocks)\r
+    );\r
+\r
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+\r
+  return FvbGetLbaAddress (\r
+           FvbDevice->Instance,\r
+           Lba,\r
+           NULL,\r
+           BlockSize,\r
+           NumOfBlocks\r
+           );\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
+\r
+  *Attributes = FvbGetVolumeAttributes (FvbDevice->Instance);\r
+\r
+  DEBUG ((EFI_D_INFO,\r
+    "FvbProtocolGetAttributes: This: 0x%x Attributes: 0x%x\n",\r
+    This,\r
+    *Attributes)\r
+    );\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[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
+  DEBUG((EFI_D_INFO,\r
+    "FvbProtocolSetAttributes: Before SET -  This: 0x%x Attributes: 0x%x\n",\r
+    This,\r
+    *Attributes)\r
+    );\r
+\r
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+\r
+  Status = FvbSetVolumeAttributes (FvbDevice->Instance, Attributes);\r
+\r
+  DEBUG((EFI_D_INFO,\r
+    "FvbProtocolSetAttributes: After SET -  This: 0x%x Attributes: 0x%x\n",\r
+    This,\r
+    *Attributes)\r
+    );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The EraseBlock() 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 = 0;\r
+  VA_LIST                               args;\r
+  EFI_LBA                               StartingLba;\r
+  UINTN                                 NumOfLba;\r
+  EFI_STATUS                            Status;\r
+\r
+  DEBUG((EFI_D_INFO, "FvbProtocolEraseBlocks: \n"));\r
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+\r
+  FwhInstance  = GetFvbInstance (FvbDevice->Instance);\r
+\r
+  if (FwhInstance != NULL) {\r
+    NumOfBlocks = FwhInstance->NumOfBlocks;\r
+  }\r
+\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
+\r
+  } while ( 1 );\r
+\r
+  VA_END (args);\r
+\r
+  return EFI_SUCCESS;\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
+\r
+  EFI_FW_VOL_BLOCK_DEVICE               *FvbDevice;\r
+\r
+  FvbDevice = FVB_DEVICE_FROM_THIS (This);\r
+\r
+  DEBUG((EFI_D_INFO,\r
+    "FvbProtocolWrite: Lba: 0x%lx Offset: 0x%x NumBytes: 0x%x, Buffer: 0x%x\n",\r
+    Lba,\r
+    Offset,\r
+    *NumBytes,\r
+    Buffer)\r
+    );\r
+\r
+  return FvbWriteBlock (FvbDevice->Instance, Lba, Offset, NumBytes, Buffer);\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[in]      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((EFI_D_INFO,\r
+    "FvbProtocolRead: Lba: 0x%lx Offset: 0x%x NumBytes: 0x%x, Buffer: 0x%x\n",\r
+    Lba,\r
+    Offset,\r
+    *NumBytes,\r
+    Buffer)\r
+    );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Check the integrity of firmware volume header.\r
+\r
+  @param[in]  FwVolHeader   A pointer to a firmware volume header.\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
+  IN CONST EFI_FIRMWARE_VOLUME_HEADER    *FwVolHeader\r
+  )\r
+{\r
+  if (FvBase == PcdGet32(PcdFlashNvStorageVariableBase)) {\r
+    if (CompareMem (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid, sizeof(EFI_GUID)) != 0 ) {\r
+      return FALSE;\r
+    }\r
+  } else {\r
+    if (CompareMem (&FwVolHeader->FileSystemGuid, &gEfiFirmwareFileSystem2Guid, sizeof(EFI_GUID)) != 0 ) {\r
+      return FALSE;\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
+    return FALSE;\r
+  }\r
+\r
+  if (CalculateCheckSum16 ((UINT16 *) FwVolHeader, FwVolHeader->HeaderLength) != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\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                   *FwhInstance;\r
+  EFI_FIRMWARE_VOLUME_HEADER            *FwVolHeader;\r
+  EFI_FIRMWARE_VOLUME_HEADER            *FvHeader;\r
+  EFI_FV_BLOCK_MAP_ENTRY                *PtrBlockMapEntry;\r
+  EFI_PHYSICAL_ADDRESS                  BaseAddress;\r
+  EFI_STATUS                            Status;\r
+  UINTN                                 BufferSize;\r
+  UINTN                                 TmpHeaderLength;\r
+  UINTN                                 Idx;\r
+  UINT32                                MaxLbaSize;\r
+  BOOLEAN                               FvHeaderValid;\r
+\r
+  //\r
+  // Calculate the total size for all firmware volume block instances.\r
+  //\r
+  BufferSize = 0;\r
+  for (Idx = 0; Idx < 1; Idx++) {\r
+    FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) mPlatformFvBaseAddress[Idx];\r
+    BufferSize +=  (FvHeader->HeaderLength +\r
+                    sizeof (EFI_FW_VOL_INSTANCE) -\r
+                    sizeof (EFI_FIRMWARE_VOLUME_HEADER)\r
+                   );\r
+  }\r
+\r
+  mFvbModuleGlobal.FvInstance =  (EFI_FW_VOL_INSTANCE *) AllocateRuntimeZeroPool (BufferSize);\r
+  ASSERT (NULL != mFvbModuleGlobal.FvInstance);\r
+\r
+\r
+  MaxLbaSize      = 0;\r
+  FwhInstance     = mFvbModuleGlobal.FvInstance;\r
+  mFvbModuleGlobal.NumFv   = 0;\r
+\r
+  for (Idx = 0; Idx < 1; Idx++) {\r
+    BaseAddress = mPlatformFvBaseAddress[Idx];\r
+    FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress;\r
+\r
+    if (!IsFvHeaderValid (BaseAddress, FwVolHeader)) {\r
+      FvHeaderValid = FALSE;\r
+      //\r
+      // If not valid, get FvbInfo from the information carried in\r
+      // FVB driver.\r
+      //\r
+      DEBUG ((EFI_D_ERROR, "Fvb: FV header @ 0x%lx invalid\n", BaseAddress));\r
+      Status          = GetFvbInfo (BaseAddress, &FwVolHeader);\r
+      ASSERT_EFI_ERROR(Status);\r
+      //\r
+      //  Write back a healthy FV header.\r
+      //\r
+      DEBUG ((EFI_D_ERROR, "FwBlockService.c: Writing back healthy FV header\n"));\r
+      LibFvbFlashDeviceBlockLock ((UINTN)BaseAddress, FwVolHeader->BlockMap->Length, FALSE);\r
+\r
+      Status = LibFvbFlashDeviceBlockErase ((UINTN)BaseAddress, FwVolHeader->BlockMap->Length);\r
+\r
+      TmpHeaderLength = (UINTN) FwVolHeader->HeaderLength;\r
+      Status = LibFvbFlashDeviceWrite (\r
+                 (UINTN)BaseAddress,\r
+                 &TmpHeaderLength,\r
+                 (UINT8 *) FwVolHeader\r
+                 );\r
+\r
+      LibFvbFlashDeviceBlockLock ((UINTN)BaseAddress, FwVolHeader->BlockMap->Length, TRUE);\r
+\r
+      WriteBackInvalidateDataCacheRange (\r
+        (VOID *) (UINTN) BaseAddress,\r
+        FwVolHeader->BlockMap->Length\r
+        );\r
+\r
+    }\r
+\r
+    CopyMem (&(FwhInstance->VolumeHeader), FwVolHeader, FwVolHeader->HeaderLength);\r
+\r
+    FwVolHeader = &(FwhInstance->VolumeHeader);\r
+    FwhInstance->FvBase = (UINTN)BaseAddress;\r
+\r
+    //\r
+    // Process the block map for each FV.\r
+    //\r
+    FwhInstance->NumOfBlocks = 0;\r
+    for (PtrBlockMapEntry = FwVolHeader->BlockMap; PtrBlockMapEntry->NumBlocks != 0; PtrBlockMapEntry++) {\r
+      //\r
+      // Get the maximum size of a block.\r
+      //\r
+      if (MaxLbaSize < PtrBlockMapEntry->Length) {\r
+        MaxLbaSize  = PtrBlockMapEntry->Length;\r
+      }\r
+      FwhInstance->NumOfBlocks += PtrBlockMapEntry->NumBlocks;\r
+    }\r
+\r
+    //\r
+    // Add a FVB Protocol Instance.\r
+    //\r
+    mFvbModuleGlobal.NumFv++;\r
+    InstallFvbProtocol (FwhInstance, mFvbModuleGlobal.NumFv - 1);\r
+\r
+    //\r
+    // Move on to the next FwhInstance.\r
+    //\r
+    FwhInstance = (EFI_FW_VOL_INSTANCE *) ((UINTN)((UINT8 *)FwhInstance) +\r
+                                          FwVolHeader->HeaderLength +\r
+                                          (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER)));\r
+\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r