Browse Source

fvb: add firmware volume block layer

Signed-off-by: Yuin Yee Chew <yuinyee.chew@starfivetech.com>
Yuin Yee Chew 10 months ago
parent
commit
4fc6f73143

+ 1230 - 0
Silicon/StarFive/JH7110/Driver/Spi/FvbDxe/FvbDxe.c

@@ -0,0 +1,1230 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include <PiDxe.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <Guid/NvVarStoreFormatted.h>
+#include <Guid/SystemNvDataGuid.h>
+#include <Guid/VariableFormat.h>
+
+#include "FvbDxe.h"
+
+STATIC EFI_EVENT     mFvbVirtualAddrChangeEvent;
+STATIC FVB_DEVICE    *mFvbDevice;
+
+STATIC CONST FVB_DEVICE mFvbFlashInstanceTemplate = {
+  {
+    0,    // SpiFlash Transfer Mode
+    0,    // SpiFlash Address Size
+    NULL, // SpiFlash detailed information
+    0,    // SpiFlash RegBase
+    0,    // SpiFlash MaxHz
+    0,    // SpiFlash PrevHz
+    0,    // SpiFlash CaliHz
+    0,    // SpiFlash ReadDelay
+    0,    // SpiFlash RefClkHz
+    0,    // SpiFlash CaliCs
+    0,    // SpiFlash IsDecodedCs
+    0,    // SpiFlash AbhBase
+    0,    // SpiFlash AhbSize
+    0,    // SpiFlash FifoDepth
+    0,    // SpiFlash FifoWidth
+    0,    // SpiFlash TrigAdd
+    0,    // SpiFlash DacMode
+    0,    // SpiFlash WRDelay
+    0,    // SpiFlash Dtr
+  }, // SpiDevice
+
+  NULL, // SpiFlashProtocol 
+  NULL, // SpiMasterProtocol 
+  NULL, // Handle 
+
+  FVB_FLASH_SIGNATURE, // Signature
+
+  FALSE, // IsMemoryMapped 
+  0, // RegionBaseAddress 
+  SIZE_256KB, // Size
+  0, // FvbOffset 
+  0, // FvbSize 
+  0, // StartLba
+
+  {
+    0,     // MediaId 
+    FALSE, // RemovableMedia
+    TRUE,  // MediaPresent
+    FALSE, // LogicalPartition
+    FALSE, // ReadOnly
+    FALSE, // WriteCaching;
+    0,     // BlockSize 
+    4,     // IoAlign
+    0,     // LastBlock 
+    0,     // LowestAlignedLba
+    1,     // LogicalBlocksPerPhysicalBlock
+  }, //Media;
+
+  {
+    FvbGetAttributes,       // GetAttributes
+    FvbSetAttributes,       // SetAttributes
+    FvbGetPhysicalAddress,  // GetPhysicalAddress
+    FvbGetBlockSize,        // GetBlockSize
+    FvbRead,                // Read
+    FvbWrite,               // Write
+    FvbEraseBlocks,         // EraseBlocks
+    NULL,                   // ParentHandle
+  }, //  FvbProtocol;
+
+  {
+    {
+      {
+        HARDWARE_DEVICE_PATH,
+        HW_VENDOR_DP,
+        {
+          (UINT8)sizeof (VENDOR_DEVICE_PATH),
+          (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+        }
+      },
+      { 0xfc0cb972, 0x21df, 0x44d2, { 0x92, 0xa5, 0x78, 0x98, 0x99, 0xcb, 0xf6, 0x61 } }
+    },
+    {
+      END_DEVICE_PATH_TYPE,
+      END_ENTIRE_DEVICE_PATH_SUBTYPE,
+      { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
+    }
+  } // DevicePath
+};
+
+//
+// The Firmware Volume Block Protocol is the low-level interface
+// to a firmware volume. File-level access to a firmware volume
+// should not be done using the Firmware Volume Block Protocol.
+// Normal access to a firmware volume must use the Firmware
+// Volume Protocol. Typically, only the file system driver that
+// produces the Firmware Volume Protocol will bind to the
+// Firmware Volume Block Protocol.
+//
+
+/**
+  Initialises the FV Header and Variable Store Header
+  to support variable operations.
+
+  @param[in]  Ptr - Location to initialise the headers
+
+**/
+STATIC
+EFI_STATUS
+FvbInitFvAndVariableStoreHeaders (
+  IN FVB_DEVICE *FlashInstance
+  )
+{
+  EFI_FIRMWARE_VOLUME_HEADER  *FirmwareVolumeHeader;
+  VARIABLE_STORE_HEADER       *VariableStoreHeader;
+  EFI_STATUS                  Status;
+  VOID*                       Headers;
+  UINTN                       HeadersLength;
+  UINTN                       BlockSize;
+
+  HeadersLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) +
+                  sizeof (EFI_FV_BLOCK_MAP_ENTRY) +
+                  sizeof (VARIABLE_STORE_HEADER);
+  Headers = AllocateZeroPool (HeadersLength);
+
+  BlockSize = FlashInstance->Media.BlockSize;
+
+  //
+  // FirmwareVolumeHeader->FvLength is declared to have the Variable area
+  // AND the FTW working area AND the FTW Spare contiguous.
+  //
+  ASSERT (PcdGet64 (PcdFlashNvStorageVariableBase64) +
+    PcdGet32 (PcdFlashNvStorageVariableSize) ==
+    PcdGet64 (PcdFlashNvStorageFtwWorkingBase64));
+  ASSERT (PcdGet64 (PcdFlashNvStorageFtwWorkingBase64) +
+    PcdGet32 (PcdFlashNvStorageFtwWorkingSize) ==
+    PcdGet64 (PcdFlashNvStorageFtwSpareBase64));
+
+  // Check if the size of the area is at least one block size
+  ASSERT ((PcdGet32 (PcdFlashNvStorageVariableSize) > 0) &&
+    (PcdGet32 (PcdFlashNvStorageVariableSize) / BlockSize > 0));
+  ASSERT ((PcdGet32 (PcdFlashNvStorageFtwWorkingSize) > 0) &&
+    (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) / BlockSize > 0));
+  ASSERT ((PcdGet32 (PcdFlashNvStorageFtwSpareSize) > 0) &&
+    (PcdGet32 (PcdFlashNvStorageFtwSpareSize) / BlockSize > 0));
+
+  // Ensure the Variable areas are aligned on block size boundaries
+  ASSERT ((PcdGet64 (PcdFlashNvStorageVariableBase64) % BlockSize) == 0);
+  ASSERT ((PcdGet64 (PcdFlashNvStorageFtwWorkingBase64) % BlockSize) == 0);
+  ASSERT ((PcdGet64 (PcdFlashNvStorageFtwSpareBase64) % BlockSize) == 0);
+
+  //
+  // EFI_FIRMWARE_VOLUME_HEADER
+  //
+  FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Headers;
+  CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid);
+  FirmwareVolumeHeader->FvLength = FlashInstance->FvbSize;
+  FirmwareVolumeHeader->Signature = EFI_FVH_SIGNATURE;
+  FirmwareVolumeHeader->Attributes = EFI_FVB2_READ_ENABLED_CAP |
+                                     EFI_FVB2_READ_STATUS |
+                                     EFI_FVB2_STICKY_WRITE |
+                                     EFI_FVB2_ERASE_POLARITY |
+                                     EFI_FVB2_WRITE_STATUS |
+                                     EFI_FVB2_WRITE_ENABLED_CAP;
+
+  if (FlashInstance->IsMemoryMapped) {
+    FirmwareVolumeHeader->Attributes |= EFI_FVB2_MEMORY_MAPPED;
+  }
+
+  FirmwareVolumeHeader->HeaderLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) +
+                                       sizeof (EFI_FV_BLOCK_MAP_ENTRY);
+  FirmwareVolumeHeader->Revision = EFI_FVH_REVISION;
+  FirmwareVolumeHeader->BlockMap[0].NumBlocks = FlashInstance->Media.LastBlock + 1;
+  FirmwareVolumeHeader->BlockMap[0].Length = FlashInstance->Media.BlockSize;
+  FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0;
+  FirmwareVolumeHeader->BlockMap[1].Length = 0;
+  FirmwareVolumeHeader->Checksum = CalculateCheckSum16 (
+                                     (UINT16 *)FirmwareVolumeHeader,
+                                     FirmwareVolumeHeader->HeaderLength);
+
+  //
+  // VARIABLE_STORE_HEADER
+  //
+  VariableStoreHeader = (VOID *)((UINTN)Headers +
+                                 FirmwareVolumeHeader->HeaderLength);
+  CopyGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid);
+  VariableStoreHeader->Size = PcdGet32(PcdFlashNvStorageVariableSize) -
+                              FirmwareVolumeHeader->HeaderLength;
+  VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED;
+  VariableStoreHeader->State = VARIABLE_STORE_HEALTHY;
+
+  // Install the combined super-header in the flash device
+  Status = FvbWrite (&FlashInstance->FvbProtocol, 0, 0, &HeadersLength, Headers);
+
+  FreePool (Headers);
+
+  return Status;
+}
+
+/**
+  Check the integrity of firmware volume header.
+
+  @param[in] FwVolHeader - A pointer to a firmware volume header
+
+  @retval  EFI_SUCCESS   - The firmware volume is consistent
+  @retval  EFI_NOT_FOUND - The firmware volume has been corrupted.
+
+**/
+STATIC
+EFI_STATUS
+FvbValidateFvHeader (
+  IN  FVB_DEVICE *FlashInstance
+  )
+{
+  UINT16                      Checksum;
+  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
+  VARIABLE_STORE_HEADER       *VariableStoreHeader;
+  UINTN                       VariableStoreLength;
+
+  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FlashInstance->RegionBaseAddress;
+
+  // Verify the header revision, header signature, length
+  if ((FwVolHeader->Revision  != EFI_FVH_REVISION) ||
+      (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
+      (FwVolHeader->FvLength  != FlashInstance->FvbSize)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: No Firmware Volume header present\n",
+      __FUNCTION__));
+    return EFI_NOT_FOUND;
+  }
+
+  // Check the Firmware Volume Guid
+  if (!CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Firmware Volume Guid non-compatible\n",
+      __FUNCTION__));
+    return EFI_NOT_FOUND;
+  }
+
+  // Verify the header checksum
+  Checksum = CalculateSum16 ((UINT16 *)FwVolHeader, FwVolHeader->HeaderLength);
+  if (Checksum != 0) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: FV checksum is invalid (Checksum:0x%x)\n",
+      __FUNCTION__,
+      Checksum));
+    return EFI_NOT_FOUND;
+  }
+
+  VariableStoreHeader = (VOID *)((UINTN)FwVolHeader + FwVolHeader->HeaderLength);
+
+  // Check the Variable Store Guid
+  if (!CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) &&
+      !CompareGuid (&VariableStoreHeader->Signature,
+                    &gEfiAuthenticatedVariableGuid)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Variable Store Guid non-compatible\n",
+      __FUNCTION__));
+    return EFI_NOT_FOUND;
+  }
+
+  VariableStoreLength = PcdGet32 (PcdFlashNvStorageVariableSize) -
+                        FwVolHeader->HeaderLength;
+  if (VariableStoreHeader->Size != VariableStoreLength) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Variable Store Length does not match\n",
+      __FUNCTION__));
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+ The GetAttributes() function retrieves the attributes and
+ current settings of the block.
+
+ @param This         Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Attributes   Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes and
+                     current settings are returned.
+                     Type EFI_FVB_ATTRIBUTES_2 is defined in
+                     EFI_FIRMWARE_VOLUME_HEADER.
+
+ @retval EFI_SUCCESS The firmware volume attributes were returned.
+
+ **/
+EFI_STATUS
+EFIAPI
+FvbGetAttributes (
+  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL    *This,
+  OUT       EFI_FVB_ATTRIBUTES_2                   *Attributes
+  )
+{
+  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
+  EFI_FVB_ATTRIBUTES_2        *FlashFvbAttributes;
+  FVB_DEVICE                  *FlashInstance;
+
+  FlashInstance = INSTANCE_FROM_FVB_THIS (This);
+
+  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FlashInstance->RegionBaseAddress;
+  FlashFvbAttributes = (EFI_FVB_ATTRIBUTES_2 *)&(FwVolHeader->Attributes);
+
+  *Attributes = *FlashFvbAttributes;
+
+  return EFI_SUCCESS;
+}
+
+/**
+ The SetAttributes() function sets configurable firmware volume attributes
+ and returns the new settings of the firmware volume.
+
+
+ @param This                     EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Attributes               On input, Attributes is a pointer to
+                                 EFI_FVB_ATTRIBUTES_2 that contains the desired
+                                 firmware volume settings.
+                                 On successful return, it contains the new
+                                 settings of the firmware volume.
+
+ @retval EFI_SUCCESS             The firmware volume attributes were returned.
+
+ @retval EFI_INVALID_PARAMETER   The attributes requested are in conflict with
+                                 the capabilities as declared in the firmware
+                                 volume header.
+
+ **/
+EFI_STATUS
+EFIAPI
+FvbSetAttributes (
+  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL  *This,
+  IN OUT    EFI_FVB_ATTRIBUTES_2                 *Attributes
+  )
+{
+  EFI_FVB_ATTRIBUTES_2  OldAttributes;
+  EFI_FVB_ATTRIBUTES_2  FlashFvbAttributes;
+  EFI_FVB_ATTRIBUTES_2  UnchangedAttributes;
+  FVB_DEVICE           *FlashInstance;
+  UINT32                Capabilities;
+  UINT32                OldStatus;
+  UINT32                NewStatus;
+
+  FlashInstance = INSTANCE_FROM_FVB_THIS (This);
+
+  //
+  // Obtain attributes from FVB header
+  //
+  FvbGetAttributes (This, &FlashFvbAttributes);
+
+  OldAttributes = FlashFvbAttributes;
+  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_ERASE_POLARITY     | \
+                        EFI_FVB2_READ_LOCK_CAP      | \
+                        EFI_FVB2_WRITE_LOCK_CAP     | \
+                        EFI_FVB2_ALIGNMENT;
+
+  if (FlashInstance->IsMemoryMapped) {
+    UnchangedAttributes |= EFI_FVB2_MEMORY_MAPPED;
+  }
+
+  //
+  // 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) {
+    if (OldStatus ^ NewStatus) {
+      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) {
+      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) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // Test lock
+  //
+  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_LOCK_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  FlashFvbAttributes = FlashFvbAttributes & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
+  FlashFvbAttributes = FlashFvbAttributes | NewStatus;
+  *Attributes = FlashFvbAttributes;
+
+  return EFI_SUCCESS;
+}
+
+/**
+ The GetPhysicalAddress() function retrieves the base address of
+ a memory-mapped firmware volume. This function should be called
+ only for memory-mapped firmware volumes.
+
+ @param This               EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Address            Pointer to a caller-allocated
+                           EFI_PHYSICAL_ADDRESS that, on successful
+                           return from GetPhysicalAddress(), contains the
+                           base address of the firmware volume.
+
+ @retval EFI_SUCCESS       The firmware volume base address was returned.
+
+ @retval EFI_NOT_SUPPORTED The firmware volume is not memory mapped.
+
+ **/
+EFI_STATUS
+EFIAPI
+FvbGetPhysicalAddress (
+  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL  *This,
+  OUT       EFI_PHYSICAL_ADDRESS                 *Address
+  )
+{
+  FVB_DEVICE *FlashInstance;
+
+  ASSERT (Address != NULL);
+
+  FlashInstance = INSTANCE_FROM_FVB_THIS (This);
+
+  *Address = FlashInstance->RegionBaseAddress;
+
+  return EFI_SUCCESS;
+}
+
+/**
+ The GetBlockSize() function retrieves the size of the requested
+ block. It also returns the number of additional blocks with
+ the identical size. The GetBlockSize() function is used to
+ retrieve the block map (see EFI_FIRMWARE_VOLUME_HEADER).
+
+
+ @param This                     EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Lba                      Indicates the block whose size to return
+
+ @param BlockSize                Pointer to a caller-allocated UINTN in which
+                                 the size of the block is returned.
+
+ @param NumberOfBlocks           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 firmware volume base address was returned.
+
+ @retval EFI_INVALID_PARAMETER   The requested LBA is out of range.
+
+ **/
+EFI_STATUS
+EFIAPI
+FvbGetBlockSize (
+  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL  *This,
+  IN        EFI_LBA                              Lba,
+  OUT       UINTN                                *BlockSize,
+  OUT       UINTN                                *NumberOfBlocks
+  )
+{
+  FVB_DEVICE *FlashInstance;
+
+  FlashInstance = INSTANCE_FROM_FVB_THIS (This);
+
+  if (Lba > FlashInstance->Media.LastBlock) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Error: Requested LBA %ld is beyond the last available LBA (%ld).\n",
+      __FUNCTION__,
+      Lba,
+      FlashInstance->Media.LastBlock));
+    return EFI_INVALID_PARAMETER;
+  } else {
+    // Assume equal sized blocks in all flash devices
+    *BlockSize = (UINTN)FlashInstance->Media.BlockSize;
+    *NumberOfBlocks = (UINTN)(FlashInstance->Media.LastBlock - Lba + 1);
+
+    return EFI_SUCCESS;
+  }
+}
+
+/**
+ Reads the specified number of bytes into a buffer from the specified block.
+
+ The Read() function reads the requested number of bytes from the
+ requested block and stores them in the provided buffer.
+ Implementations should be mindful that the firmware volume
+ might be in the ReadDisabled state. If it is in this state,
+ the Read() function must return the status code
+ EFI_ACCESS_DENIED without modifying the contents of the
+ buffer. The Read() function must also prevent spanning block
+ boundaries. If a read is requested that would span a block
+ boundary, the read must read up to the boundary but not
+ beyond. The output parameter NumBytes must be set to correctly
+ indicate the number of bytes actually read. The caller must be
+ aware that a read may be partially completed.
+
+ @param This                 EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Lba                  The starting logical block index from which to read
+
+ @param Offset               Offset into the block at which to begin reading.
+
+ @param NumBytes             Pointer to a UINTN.
+                             At entry, *NumBytes contains the total size of the
+                             buffer.
+                             At exit, *NumBytes contains the total number of
+                             bytes read.
+
+ @param Buffer               Pointer to a caller-allocated buffer that will be
+                             used to hold the data that is read.
+
+ @retval EFI_SUCCESS         The firmware volume was read successfully, and
+                             contents are in Buffer.
+
+ @retval EFI_BAD_BUFFER_SIZE Read attempted across an 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.
+
+ **/
+EFI_STATUS
+EFIAPI
+FvbRead (
+  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL   *This,
+  IN        EFI_LBA                               Lba,
+  IN        UINTN                                 Offset,
+  IN OUT    UINTN                                 *NumBytes,
+  IN OUT    UINT8                                 *Buffer
+  )
+{
+  FVB_DEVICE   *FlashInstance;
+  UINTN         BlockSize;
+  UINTN         DataOffset;
+
+  FlashInstance = INSTANCE_FROM_FVB_THIS (This);
+
+
+  // Cache the block size to avoid de-referencing pointers all the time
+  BlockSize = FlashInstance->Media.BlockSize;
+
+  //
+  // The read must not span block boundaries.
+  // We need to check each variable individually because adding two large
+  // values together overflows.
+  //
+  if (Offset               >= BlockSize ||
+      *NumBytes            >  BlockSize ||
+      (Offset + *NumBytes) >  BlockSize) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Wrong buffer size: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n",
+      __FUNCTION__,
+      Offset,
+      *NumBytes,
+      BlockSize));
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // No bytes to read
+  if (*NumBytes == 0) {
+    return EFI_SUCCESS;
+  }
+
+  DataOffset = GET_DATA_OFFSET (FlashInstance->RegionBaseAddress + Offset,
+                 FlashInstance->StartLba + Lba,
+                 FlashInstance->Media.BlockSize);
+
+  // Read the memory-mapped data
+  CopyMem (Buffer, (UINTN *)DataOffset, *NumBytes);
+
+  return EFI_SUCCESS;
+}
+
+/**
+ Writes the specified number of bytes from the input buffer to the block.
+
+ The Write() function writes the specified number of bytes from
+ the provided buffer to the specified block and offset. If the
+ firmware volume is sticky write, the caller must ensure that
+ all the bits of the specified range to write are in the
+ EFI_FVB_ERASE_POLARITY state before calling the Write()
+ function, or else the result will be unpredictable. This
+ unpredictability arises because, for a sticky-write firmware
+ volume, a write may negate a bit in the EFI_FVB_ERASE_POLARITY
+ state but cannot flip it back again.  Before calling the
+ Write() function,  it is recommended for the caller to first call
+ the EraseBlocks() function to erase the specified block to
+ write. A block erase cycle will transition bits from the
+ (NOT)EFI_FVB_ERASE_POLARITY state back to the
+ EFI_FVB_ERASE_POLARITY state. Implementations should be
+ mindful that the firmware volume might be in the WriteDisabled
+ state. If it is in this state, the Write() function must
+ return the status code EFI_ACCESS_DENIED without modifying the
+ contents of the firmware volume. The Write() function must
+ also prevent spanning block boundaries. If a write is
+ requested that spans a block boundary, the write must store up
+ to the boundary but not beyond. The output parameter NumBytes
+ must be set to correctly indicate the number of bytes actually
+ written. The caller must be aware that a write may be
+ partially completed. All writes, partial or otherwise, must be
+ fully flushed to the hardware before the Write() service
+ returns.
+
+ @param This                 EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Lba                  The starting logical block index to write to.
+
+ @param Offset               Offset into the block at which to begin writing.
+
+ @param NumBytes             The pointer to a UINTN.
+                             At entry, *NumBytes contains the total size of the
+                             buffer.
+                             At exit, *NumBytes contains the total number of
+                             bytes actually written.
+
+ @param Buffer               The 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 The write was attempted across an 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 malfunctioning and could not be
+                             written.
+
+
+ **/
+EFI_STATUS
+EFIAPI
+FvbWrite (
+  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL   *This,
+  IN        EFI_LBA                               Lba,
+  IN        UINTN                                 Offset,
+  IN OUT    UINTN                                 *NumBytes,
+  IN        UINT8                                 *Buffer
+  )
+{
+  EFI_STATUS    Status;
+  FVB_DEVICE   *FlashInstance;
+  UINTN         DataOffset;
+
+  FlashInstance = INSTANCE_FROM_FVB_THIS (This);
+
+  DataOffset = GET_DATA_OFFSET (FlashInstance->FvbOffset + Offset,
+                 FlashInstance->StartLba + Lba,
+                 FlashInstance->Media.BlockSize);
+
+  Status = FlashInstance->SpiFlashProtocol->Write (&FlashInstance->SpiDevice,
+                                              DataOffset,
+                                              *NumBytes,
+                                              Buffer);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Failed to write to Spi device\n",
+      __FUNCTION__));
+    return Status;
+  }
+
+  // Update shadow buffer
+  if (!FlashInstance->IsMemoryMapped) {
+    DataOffset = GET_DATA_OFFSET (FlashInstance->RegionBaseAddress + Offset,
+                   FlashInstance->StartLba + Lba,
+                   FlashInstance->Media.BlockSize);
+
+    CopyMem ((UINTN *)DataOffset, Buffer, *NumBytes);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+ Erases and initialises a firmware volume block.
+
+ The EraseBlocks() function erases one or more blocks as denoted
+ by the variable argument list. The entire parameter list of
+ blocks must be verified before 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 EraseBlocks() function must return the
+ status code EFI_INVALID_PARAMETER without modifying the contents
+ of the firmware volume. Implementations should be mindful that
+ the firmware volume might be in the WriteDisabled state. If it
+ is in this state, the EraseBlocks() function must return the
+ status code EFI_ACCESS_DENIED without modifying the contents of
+ the firmware volume. All calls to EraseBlocks() must be fully
+ flushed to the hardware before the EraseBlocks() service
+ returns.
+
+ @param This                     EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL
+ instance.
+
+ @param ...                      The variable argument list is a list of tuples.
+                                 Each tuple describes a range of LBAs to erase
+                                 and consists of the following:
+                                 - An EFI_LBA that indicates the starting LBA
+                                 - A UINTN that indicates the number of blocks
+                                   to erase.
+
+                                 The list is terminated with an
+                                 EFI_LBA_LIST_TERMINATOR.
+
+ @retval EFI_SUCCESS             The erase request 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.
+                                 The firmware device may have been partially
+                                 erased.
+
+ @retval EFI_INVALID_PARAMETER   One or more of the LBAs listed in the variable
+                                 argument list do not exist in the firmware
+                                 volume.
+
+ **/
+EFI_STATUS
+EFIAPI
+FvbEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+  ...
+  )
+{
+  EFI_FVB_ATTRIBUTES_2   FlashFvbAttributes;
+  FVB_DEVICE            *FlashInstance;
+  EFI_STATUS             Status;
+  VA_LIST                Args;
+  UINTN                  BlockAddress; // Physical address of Lba to erase
+  EFI_LBA                StartingLba;  // Lba from which we start erasing
+  UINTN                  NumOfLba;     // Number of Lba blocks to erase
+
+  FlashInstance = INSTANCE_FROM_FVB_THIS (This);
+
+  Status = EFI_SUCCESS;
+
+  // Detect WriteDisabled state
+  FvbGetAttributes (This, &FlashFvbAttributes);
+  if ((FlashFvbAttributes & EFI_FVB2_WRITE_STATUS) == 0) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Device is in WriteDisabled state.\n",
+      __FUNCTION__));
+    return EFI_ACCESS_DENIED;
+  }
+
+  //
+  // Before erasing, check the entire list of parameters to ensure
+  // all specified blocks are valid.
+  //
+  VA_START (Args, This);
+  do {
+    // Get the Lba from which we start erasing
+    StartingLba = VA_ARG (Args, EFI_LBA);
+
+    // Have we reached the end of the list?
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      //Exit the while loop
+      break;
+    }
+
+    // How many Lba blocks are we requested to erase?
+    NumOfLba = VA_ARG (Args, UINT32);
+
+    // All blocks must be within range
+    if (NumOfLba == 0 ||
+        (FlashInstance->StartLba + StartingLba + NumOfLba - 1) >
+        FlashInstance->Media.LastBlock) {
+
+      DEBUG ((DEBUG_ERROR,
+        "%a: Error: Requested LBA are beyond the last available LBA (%ld).\n",
+        __FUNCTION__,
+        FlashInstance->Media.LastBlock));
+
+      VA_END (Args);
+
+      return EFI_INVALID_PARAMETER;
+    }
+  } while (TRUE);
+  VA_END (Args);
+
+  //
+  // Start erasing
+  //
+  VA_START (Args, This);
+  do {
+    // Get the Lba from which we start erasing
+    StartingLba = VA_ARG (Args, EFI_LBA);
+
+    // Have we reached the end of the list?
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      // Exit the while loop
+      break;
+    }
+
+    // How many Lba blocks are we requested to erase?
+    NumOfLba = VA_ARG (Args, UINT32);
+
+    // Go through each one and erase it
+    while (NumOfLba > 0) {
+
+      // Get the physical address of Lba to erase
+      BlockAddress = GET_DATA_OFFSET (FlashInstance->FvbOffset,
+                       FlashInstance->StartLba + StartingLba,
+                       FlashInstance->Media.BlockSize);
+
+      // Erase single block
+      Status = FlashInstance->SpiFlashProtocol->Erase (&FlashInstance->SpiDevice,
+                                                  BlockAddress,
+                                                  FlashInstance->Media.BlockSize);
+      if (EFI_ERROR (Status)) {
+        VA_END (Args);
+        return EFI_DEVICE_ERROR;
+      }
+
+      // Move to the next Lba
+      StartingLba++;
+      NumOfLba--;
+    }
+  } while (TRUE);
+  VA_END (Args);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Fixup internal data so that EFI can be call in virtual mode.
+  Call the passed in Child Notify event and convert any pointers in
+  lib to virtual mode.
+
+  @param[in]    Event   The Event that is being processed
+  @param[in]    Context Event Context
+**/
+STATIC
+VOID
+EFIAPI
+FvbVirtualNotifyEvent (
+  IN EFI_EVENT        Event,
+  IN VOID             *Context
+  )
+{
+  // Convert SPI memory mapped region
+  EfiConvertPointer (0x0, (VOID**)&mFvbDevice->RegionBaseAddress);
+
+  // Convert SPI device description
+  EfiConvertPointer (0x0, (VOID**)&mFvbDevice->SpiDevice.Info);
+  EfiConvertPointer (0x0, (VOID**)&mFvbDevice->SpiDevice.RegBase);
+  EfiConvertPointer (0x0, (VOID**)&mFvbDevice->SpiDevice);
+
+  // Convert SpiFlashProtocol
+  EfiConvertPointer (0x0, (VOID**)&mFvbDevice->SpiFlashProtocol->Erase);
+  EfiConvertPointer (0x0, (VOID**)&mFvbDevice->SpiFlashProtocol->Write);
+  EfiConvertPointer (0x0, (VOID**)&mFvbDevice->SpiFlashProtocol);
+
+  return;
+}
+
+STATIC
+EFI_STATUS
+FvbFlashProbe (
+  IN FVB_DEVICE *FlashInstance
+  )
+{
+  SPI_FLASH_PROTOCOL *SpiFlashProtocol;
+  EFI_STATUS Status;
+
+  SpiFlashProtocol = FlashInstance->SpiFlashProtocol;
+
+  // Read SPI flash ID
+  Status = SpiFlashProtocol->ReadId (&FlashInstance->SpiDevice, TRUE);
+  if (EFI_ERROR (Status)) {
+    return EFI_NOT_FOUND;
+  }
+
+  Status = SpiFlashProtocol->Init (SpiFlashProtocol, &FlashInstance->SpiDevice);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Cannot initialize flash device\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // SPI flash may require 20ms interval between enabling it and
+  // accessing in Direct Mode to its memory mapped content.
+  //
+  gBS->Stall (20000);
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+FvbPrepareFvHeader (
+  IN FVB_DEVICE *FlashInstance
+  )
+{
+  EFI_BOOT_MODE BootMode;
+  EFI_STATUS Status;
+
+  // Check if it is required to use default environment
+  BootMode = GetBootModeHob ();
+  if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {
+    Status = EFI_INVALID_PARAMETER;
+  } else {
+    // Validate header at the beginning of FV region
+    Status = FvbValidateFvHeader (FlashInstance);
+  }
+
+  // Install the default FVB header if required
+  if (EFI_ERROR (Status)) {
+  //if (1) {
+    // There is no valid header, so time to install one.
+    DEBUG ((DEBUG_ERROR, "%a: The FVB Header is not valid.\n", __FUNCTION__));
+    DEBUG ((DEBUG_ERROR,
+      "%a: Installing a correct one for this volume.\n",
+      __FUNCTION__));
+
+    // Erase entire region that is reserved for variable storage
+    Status = FlashInstance->SpiFlashProtocol->Erase (&FlashInstance->SpiDevice,
+                                                FlashInstance->FvbOffset,
+                                                FlashInstance->FvbSize);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    // Install all appropriate headers
+    Status = FvbInitFvAndVariableStoreHeaders (FlashInstance);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+FvbConfigureFlashInstance (
+  IN OUT FVB_DEVICE *FlashInstance
+  )
+{
+  EFI_STATUS Status;
+  UINTN     DataOffset;
+  UINTN     VariableSize, FtwWorkingSize, FtwSpareSize, MemorySize;
+
+  // Locate SPI protocols
+  Status = gBS->LocateProtocol (&gJH7110SpiFlashProtocolGuid,
+                  NULL,
+                  (VOID **)&FlashInstance->SpiFlashProtocol);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Cannot locate SpiFlash protocol\n", __FUNCTION__));
+    return Status;
+  }
+
+  Status = gBS->LocateProtocol (&gJH7110SpiMasterProtocolGuid,
+                  NULL,
+                  (VOID **)&FlashInstance->SpiMasterProtocol);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Cannot locate SpiMaster protocol\n", __FUNCTION__));
+    return Status;
+  }
+
+  // Setup and probe SPI flash
+  FlashInstance->SpiMasterProtocol->SetupDevice (FlashInstance->SpiMasterProtocol,
+                                      &FlashInstance->SpiDevice,
+                                      0);
+
+  Status = FvbFlashProbe (FlashInstance);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Error while performing SPI flash probe\n",
+      __FUNCTION__));
+    return Status;
+  }
+
+  // Fill remaining flash description
+  VariableSize = PcdGet32 (PcdFlashNvStorageVariableSize);
+  FtwWorkingSize = PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
+  FtwSpareSize = PcdGet32 (PcdFlashNvStorageFtwSpareSize);
+
+  FlashInstance->IsMemoryMapped = PcdGetBool (PcdSpiMemoryMapped);
+  FlashInstance->FvbSize = VariableSize + FtwWorkingSize + FtwSpareSize;
+  FlashInstance->FvbOffset = PcdGet32 (PcdSpiVariableOffset);
+
+  FlashInstance->Media.MediaId = 0;
+  FlashInstance->Media.BlockSize = FlashInstance->SpiDevice.Info->SectorSize;
+  FlashInstance->Media.LastBlock = FlashInstance->Size /
+                                   FlashInstance->Media.BlockSize - 1;
+
+  if (FlashInstance->IsMemoryMapped) {
+    FlashInstance->RegionBaseAddress = PcdGet64 (PcdFlashNvStorageVariableBase64);
+  } else {
+    MemorySize = EFI_SIZE_TO_PAGES (FlashInstance->FvbSize);
+
+    // FaultTolerantWriteDxe requires memory to be aligned to FtwWorkingSize
+    FlashInstance->RegionBaseAddress = (UINTN) AllocateAlignedRuntimePages (MemorySize,
+                                                 SIZE_64KB);
+    if (FlashInstance->RegionBaseAddress == (UINTN) NULL) {
+        return EFI_OUT_OF_RESOURCES;
+    }
+
+    Status = PcdSet64S (PcdFlashNvStorageVariableBase64,
+               (UINT64) FlashInstance->RegionBaseAddress);
+    ASSERT_EFI_ERROR (Status);
+    Status = PcdSet64S (PcdFlashNvStorageFtwWorkingBase64,
+               (UINT64) FlashInstance->RegionBaseAddress
+               + VariableSize);
+    ASSERT_EFI_ERROR (Status);
+    Status = PcdSet64S (PcdFlashNvStorageFtwSpareBase64,
+               (UINT64) FlashInstance->RegionBaseAddress
+               + VariableSize
+               + FtwWorkingSize);
+    ASSERT_EFI_ERROR (Status);
+
+    // Fill the buffer with data from flash
+    DataOffset = GET_DATA_OFFSET (FlashInstance->FvbOffset,
+                   FlashInstance->StartLba,
+                   FlashInstance->Media.BlockSize);
+    Status = FlashInstance->SpiFlashProtocol->Read (&FlashInstance->SpiDevice,
+                                                DataOffset,
+                                                FlashInstance->FvbSize,
+                                                (VOID *)FlashInstance->RegionBaseAddress);
+    if (EFI_ERROR (Status)) {
+      goto ErrorFreeAllocatedPages;
+    }
+  }
+
+  Status = gBS->InstallMultipleProtocolInterfaces (&FlashInstance->Handle,
+                  &gEfiDevicePathProtocolGuid, &FlashInstance->DevicePath,
+                  &gEfiFirmwareVolumeBlockProtocolGuid, &FlashInstance->FvbProtocol,
+                  NULL);
+  if (EFI_ERROR (Status)) {
+    goto ErrorFreeAllocatedPages;
+  }
+
+  Status = FvbPrepareFvHeader (FlashInstance);
+  if (EFI_ERROR (Status)) {
+    goto ErrorPrepareFvbHeader;
+  }
+
+  return EFI_SUCCESS;
+
+ErrorPrepareFvbHeader:
+  gBS->UninstallMultipleProtocolInterfaces (&FlashInstance->Handle,
+         &gEfiDevicePathProtocolGuid,
+         &gEfiFirmwareVolumeBlockProtocolGuid,
+         NULL);
+
+ErrorFreeAllocatedPages:
+  if (!FlashInstance->IsMemoryMapped) {
+    FreeAlignedPages ((VOID *)FlashInstance->RegionBaseAddress,
+      MemorySize);
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+FvbEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       RuntimeMmioRegionSize;
+  UINTN       RegionBaseAddress;
+
+  //
+  // Create FVB flash device
+  //
+  mFvbDevice = AllocateRuntimeCopyPool (sizeof (mFvbFlashInstanceTemplate),
+                 &mFvbFlashInstanceTemplate);
+  if (mFvbDevice == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: Cannot allocate memory\n", __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Detect and configure flash device
+  //
+  Status = FvbConfigureFlashInstance (mFvbDevice);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Fail to configure Fvb SPI device\n", __FUNCTION__));
+    goto ErrorConfigureFlash;
+  }
+
+  //
+  // The driver implementing the variable read service can now be dispatched;
+  // the varstore headers are in place.
+  //
+  Status = gBS->InstallProtocolInterface (&gImageHandle,
+                  &gEdkiiNvVarStoreFormattedGuid,
+                  EFI_NATIVE_INTERFACE,
+                  NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Failed to install gEdkiiNvVarStoreFormattedGuid\n",
+      __FUNCTION__));
+    goto ErrorInstallNvVarStoreFormatted;
+  }
+
+  //
+  // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME
+  //
+  RegionBaseAddress = mFvbDevice->RegionBaseAddress;
+
+  if (mFvbDevice->IsMemoryMapped) {
+    RuntimeMmioRegionSize = mFvbDevice->FvbSize;
+    Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo,
+                    RegionBaseAddress,
+                    RuntimeMmioRegionSize,
+                    EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: Failed to add memory space\n", __FUNCTION__));
+      goto ErrorAddSpace;
+    }
+
+    Status = gDS->SetMemorySpaceAttributes (RegionBaseAddress,
+                    RuntimeMmioRegionSize,
+                    EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
+    if (EFI_ERROR (Status)) {
+     DEBUG ((DEBUG_ERROR, "%a: Failed to set memory attributes\n", __FUNCTION__));
+      goto ErrorSetMemAttr;
+    }
+  }
+
+  //
+  // Register for the virtual address change event
+  //
+  Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  FvbVirtualNotifyEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &mFvbVirtualAddrChangeEvent);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Failed to register VA change event\n", __FUNCTION__));
+    goto ErrorSetMemAttr;
+  }
+
+  return Status;
+
+ErrorSetMemAttr:
+  gDS->RemoveMemorySpace (RegionBaseAddress, RuntimeMmioRegionSize);
+
+ErrorAddSpace:
+  gBS->UninstallProtocolInterface (gImageHandle,
+         &gEdkiiNvVarStoreFormattedGuid,
+         NULL);
+
+ErrorInstallNvVarStoreFormatted:
+  gBS->UninstallMultipleProtocolInterfaces (&mFvbDevice->Handle,
+         &gEfiDevicePathProtocolGuid,
+         &gEfiFirmwareVolumeBlockProtocolGuid,
+         NULL);
+
+ErrorConfigureFlash:
+  FreePool (mFvbDevice);
+
+  return Status;
+}

+ 121 - 0
Silicon/StarFive/JH7110/Driver/Spi/FvbDxe/FvbDxe.h

@@ -0,0 +1,121 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#ifndef __FVB_DXE_H__
+#define __FVB_DXE_H__
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Protocol/Spi.h>
+#include <Protocol/SpiFlash.h>
+
+#define GET_DATA_OFFSET(BaseAddr, Lba, LbaSize) ((BaseAddr) + (UINTN)((Lba) * (LbaSize)))
+
+#define FVB_FLASH_SIGNATURE                       SIGNATURE_32('S', 'n', 'o', 'r')
+#define INSTANCE_FROM_FVB_THIS(a)                 CR(a, FVB_DEVICE, FvbProtocol, FVB_FLASH_SIGNATURE)
+
+//
+// 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 {
+  VENDOR_DEVICE_PATH                  Vendor;
+  EFI_DEVICE_PATH_PROTOCOL            End;
+} FVB_DEVICE_PATH;
+
+typedef struct {
+  SPI_DEVICE                          SpiDevice;
+
+  SPI_FLASH_PROTOCOL                  *SpiFlashProtocol;
+  SPI_MASTER_PROTOCOL                 *SpiMasterProtocol;
+
+  EFI_HANDLE                          Handle;
+
+  UINT32                              Signature;
+
+  BOOLEAN                             IsMemoryMapped;
+  UINTN                               RegionBaseAddress;
+  UINTN                               Size;
+  UINTN                               FvbOffset;
+  UINTN                               FvbSize;
+  EFI_LBA                             StartLba;
+
+  EFI_BLOCK_IO_MEDIA                  Media;
+  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol;
+
+  FVB_DEVICE_PATH                     DevicePath;
+} FVB_DEVICE;
+
+EFI_STATUS
+EFIAPI
+FvbGetAttributes(
+    IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL*     This,
+    OUT       EFI_FVB_ATTRIBUTES_2*                    Attributes
+);
+
+EFI_STATUS
+EFIAPI
+FvbSetAttributes(
+    IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL*     This,
+    IN OUT    EFI_FVB_ATTRIBUTES_2*                    Attributes
+);
+
+EFI_STATUS
+EFIAPI
+FvbGetPhysicalAddress(
+    IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL*     This,
+    OUT       EFI_PHYSICAL_ADDRESS*                    Address
+);
+
+EFI_STATUS
+EFIAPI
+FvbGetBlockSize(
+    IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL*     This,
+    IN        EFI_LBA                                  Lba,
+    OUT       UINTN*                                   BlockSize,
+    OUT       UINTN*                                   NumberOfBlocks
+);
+
+EFI_STATUS
+EFIAPI
+FvbRead(
+    IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL*     This,
+    IN        EFI_LBA                                  Lba,
+    IN        UINTN                                    Offset,
+    IN OUT    UINTN*                                   NumBytes,
+    IN OUT    UINT8*                                   Buffer
+);
+
+EFI_STATUS
+EFIAPI
+FvbWrite(
+    IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL*     This,
+    IN        EFI_LBA                                  Lba,
+    IN        UINTN                                    Offset,
+    IN OUT    UINTN*                                   NumBytes,
+    IN        UINT8*                                   Buffer
+);
+
+EFI_STATUS
+EFIAPI
+FvbEraseBlocks(
+    IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL*     This,
+    ...
+);
+
+#endif //__FVB_DXE_H__

+ 67 - 0
Silicon/StarFive/JH7110/Driver/Spi/FvbDxe/FvbDxe.inf

@@ -0,0 +1,67 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+
+[Defines]
+  INF_VERSION                    = 0x0001001A
+  BASE_NAME                      = FvbDxe
+  FILE_GUID                      = DB8BFC83-6DEA-4AD1-AD3D-FDC579430233
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 0.1
+  ENTRY_POINT                    = FvbEntryPoint
+
+[Sources]
+  FvbDxe.c
+
+[Packages]
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/StarFive/JH7110/JH7110.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  DevicePathLib
+  DxeServicesTableLib
+  HobLib
+  IoLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+  UefiRuntimeLib
+  UefiRuntimeServicesTableLib
+
+[Guids]
+  gEdkiiNvVarStoreFormattedGuid
+  gEfiAuthenticatedVariableGuid
+  gEfiEventVirtualAddressChangeGuid
+  gEfiSystemNvDataFvGuid
+  gEfiVariableGuid
+
+[Protocols]
+  gEfiDevicePathProtocolGuid
+  gEfiFirmwareVolumeBlockProtocolGuid
+  gJH7110SpiFlashProtocolGuid
+  gJH7110SpiMasterProtocolGuid
+
+[FixedPcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVariableStoreSize
+  gJH7110TokenSpaceGuid.PcdSpiMemoryMapped
+  gJH7110TokenSpaceGuid.PcdSpiVariableOffset
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
+
+[Depex]
+  gEfiCpuArchProtocolGuid AND
+  gJH7110SpiMasterProtocolGuid AND
+  gJH7110SpiFlashProtocolGuid