[edk2-devel] [edk2-platforms][PATCH v2 09/32] AmpereAltraPkg: Support non-volatile variables

Nhi Pham via groups.io nhi=os.amperecomputing.com at groups.io
Wed May 26 10:07:01 UTC 2021


From: Vu Nguyen <vunguyen at os.amperecomputing.com>

Non-volatile variables now can be stored on flash. MM communication
protocol is used to access storage on flash.

Included in this change are:
* FlashPei module is used to compare saved UUID with firmware's
  UEFI_UUID on each boot. If UUIDs match, data is stored to RAM.
  Otherwise fill the storage with default value from NVRAM FV.
* FlashLib provide APIs to access flash through MM Communication
  protocol
* FlashFvbDxe installs gEfiFirmwareVolumeBlock protocol which will be
  used by the variable services to get/set variables.

Cc: Thang Nguyen <thang at os.amperecomputing.com>
Cc: Chuong Tran <chuong at os.amperecomputing.com>
Cc: Phong Vo <phong at os.amperecomputing.com>
Cc: Leif Lindholm <leif at nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney at intel.com>
Cc: Ard Biesheuvel <ardb+tianocore at kernel.org>
Cc: Nate DeSimone <nathaniel.l.desimone at intel.com>

Signed-off-by: Vu Nguyen <vunguyen at os.amperecomputing.com>
---
 Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec                  |   3 +
 Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec              |   3 +
 Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc              |  11 +-
 Platform/Ampere/JadePkg/Jade.dsc                                  |   1 +
 Platform/Ampere/JadePkg/Jade.fdf                                  |  62 ++-
 Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.inf |  54 ++
 Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.inf       |  51 ++
 Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.inf       |  36 ++
 Silicon/Ampere/AmpereAltraPkg/Include/Library/FlashLib.h          |  42 ++
 Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.c   | 525 ++++++++++++++++++++
 Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.c         | 283 +++++++++++
 Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.c         | 358 +++++++++++++
 12 files changed, 1425 insertions(+), 4 deletions(-)

diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
index be827dd19a96..d5b12a81e9bf 100644
--- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
+++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
@@ -37,6 +37,9 @@ [LibraryClasses]
   ##  @libraryclass  Defines a set of methods to communicate with secure parition over MM interface.
   MmCommunicationLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/MmCommunicationLib.h
 
+  ##  @libraryclass  Defines a set of methods to access flash memory.
+  FlashLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/FlashLib.h
+
   ##  @libraryclass  Defines a set of methods to generate random numbers by using Hardware RNG.
   TrngLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/TrngLib.h
 
diff --git a/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec
index 6ebdf7db0a57..26e020715290 100755
--- a/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec
+++ b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec
@@ -44,3 +44,6 @@ [PcdsFixedAtBuild, PcdsDynamic, PcdsDynamicEx]
   # Firmware Volume Pcds
   #
   gAmpereTokenSpaceGuid.PcdFvBlockSize|0|UINT32|0xB0000001
+
+  # NVRam Pcds
+  gAmpereTokenSpaceGuid.PcdNvramErased|FALSE|BOOLEAN|0xB0000009
diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc
index 9f75da6f05ad..65973569a41d 100755
--- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc
+++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc
@@ -14,6 +14,7 @@ [BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER]
 
 [BuildOptions]
   GCC:RELEASE_*_*_CC_FLAGS  = -DMDEPKG_NDEBUG
+  GCC:*_*_*_CC_FLAGS = -DUEFI_UUID=$(UEFI_UUID)
 
 [LibraryClasses.common]
 !if $(TARGET) == RELEASE
@@ -224,6 +225,7 @@ [LibraryClasses.common.DXE_DRIVER]
   SecurityManagementLib|MdeModulePkg/Library/DxeSecurityManagementLib/DxeSecurityManagementLib.inf
   PerformanceLib|MdeModulePkg/Library/DxePerformanceLib/DxePerformanceLib.inf
   MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  FlashLib|Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.inf
 
 [LibraryClasses.common.UEFI_APPLICATION]
   UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiTianoCustomDecompressLib.inf
@@ -254,6 +256,7 @@ [LibraryClasses.common.DXE_RUNTIME_DRIVER]
 
   EfiResetSystemLib|ArmPkg/Library/ArmPsciResetSystemLib/ArmPsciResetSystemLib.inf
   ArmSmcLib|ArmPkg/Library/ArmSmcLib/ArmSmcLib.inf
+  FlashLib|Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.inf
 
 [LibraryClasses.ARM,LibraryClasses.AARCH64]
   #
@@ -500,6 +503,8 @@ [PcdsDynamicDefault.common]
   gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64|0x0
   gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64|0x0
 
+  gAmpereTokenSpaceGuid.PcdNvramErased|FALSE
+
 ################################################################################
 #
 # Component Section - list of all EDK II Component Entries defined by this Platform
@@ -520,8 +525,10 @@ [Components.common]
   Silicon/Ampere/AmpereAltraPkg/Drivers/ATFHobPei/ATFHobPeim.inf
   Silicon/Ampere/AmpereAltraPkg/Drivers/MemoryInitPeim/MemoryInitPeim.inf
   Silicon/Ampere/AmpereAltraPkg/Drivers/MmCommunicationPei/MmCommunicationPei.inf
+  Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.inf
   ArmPkg/Drivers/CpuPei/CpuPei.inf
   UefiCpuPkg/CpuIoPei/CpuIoPei.inf
+  MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf
   MdeModulePkg/Universal/Variable/Pei/VariablePei.inf
   MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf {
     <LibraryClasses>
@@ -575,9 +582,9 @@ [Components.common]
   #
   # Environment Variables Protocol
   #
+  Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.inf
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
   MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf {
-    <PcdsFixedAtBuild>
-      gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable|TRUE
     <LibraryClasses>
       BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
       TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
diff --git a/Platform/Ampere/JadePkg/Jade.dsc b/Platform/Ampere/JadePkg/Jade.dsc
index f37ab1a92e44..9b9a5d0bad0f 100755
--- a/Platform/Ampere/JadePkg/Jade.dsc
+++ b/Platform/Ampere/JadePkg/Jade.dsc
@@ -52,6 +52,7 @@ [Defines]
   DEFINE EDK2_SKIP_PEICORE       = TRUE
   DEFINE SECURE_BOOT_ENABLE      = FALSE
   DEFINE INCLUDE_TFTP_COMMAND    = TRUE
+  DEFINE UEFI_UUID               = 84BC921F-9D4A-4D1D-A1A1-1AE13EDD07E5
 
   #
   # Network definition
diff --git a/Platform/Ampere/JadePkg/Jade.fdf b/Platform/Ampere/JadePkg/Jade.fdf
index 1857296a8ea5..375455086d0b 100755
--- a/Platform/Ampere/JadePkg/Jade.fdf
+++ b/Platform/Ampere/JadePkg/Jade.fdf
@@ -26,7 +26,7 @@ [FD.BL33_JADE_UEFI]
 ErasePolarity = 1
 
 # This one is tricky, it must be: BlockSize * NumBlocks = Size
-BlockSize     = 0x10000
+BlockSize     = 0x10000|gAmpereTokenSpaceGuid.PcdFvBlockSize
 NumBlocks     = 0x7C
 
 ################################################################################
@@ -56,8 +56,61 @@ [FD.BL33_JADE_UEFI]
 
 #
 # NV Variables
-# T.B.D
+# Offset: 0x00740000
+# Size:   0x00080000
 #
+0x00740000|0x00030000
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+DATA = {
+  ## This is the EFI_FIRMWARE_VOLUME_HEADER
+  # ZeroVector []
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  # FileSystemGuid: gEfiSystemNvDataFvGuid         =
+  #   { 0xFFF12B8D, 0x7696, 0x4C8B,
+  #     { 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50 }}
+  0x8D, 0x2B, 0xF1, 0xFF, 0x96, 0x76, 0x8B, 0x4C,
+  0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50,
+  # FvLength: 0x80000
+  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+  # Signature "_FVH"       # Attributes
+  0x5f, 0x46, 0x56, 0x48, 0xff, 0xfe, 0x04, 0x00,
+  # HeaderLength # CheckSum # ExtHeaderOffset #Reserved #Revision
+  0x48, 0x00, 0x2D, 0x09, 0x00, 0x00, 0x00, 0x02,
+  # Blockmap[0]: 0x2 Blocks * 0x40000 Bytes / Block
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+  # Blockmap[1]: End
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  ## This is the VARIABLE_STORE_HEADER
+  # It is compatible with SECURE_BOOT_ENABLE == FALSE as well.
+  # Signature: gEfiAuthenticatedVariableGuid =
+  #   { 0xaaf32c78, 0x947b, 0x439a,
+  #     { 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92 }}
+  0x78, 0x2c, 0xf3, 0xaa, 0x7b, 0x94, 0x9a, 0x43,
+  0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92,
+  # Size: 0x30000 (gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize) -
+  #         0x48 (size of EFI_FIRMWARE_VOLUME_HEADER) = 0x2FFB8
+  # This can speed up the Variable Dispatch a bit.
+  0xB8, 0xFF, 0x02, 0x00,
+  # FORMATTED: 0x5A #HEALTHY: 0xFE #Reserved: UINT16 #Reserved1: UINT32
+  0x5A, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+
+0x00770000|0x00010000
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+DATA = {
+  # EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER->Signature = gEdkiiWorkingBlockSignatureGuid =
+  #  { 0x9e58292b, 0x7c68, 0x497d, { 0xa0, 0xce, 0x65,  0x0, 0xfd, 0x9f, 0x1b, 0x95 }}
+  0x2b, 0x29, 0x58, 0x9e, 0x68, 0x7c, 0x7d, 0x49,
+  0xa0, 0xce, 0x65,  0x0, 0xfd, 0x9f, 0x1b, 0x95,
+  # Crc:UINT32            #WorkingBlockValid:1, WorkingBlockInvalid:1, Reserved
+  0x2c, 0xaf, 0x2c, 0x64, 0xFE, 0xFF, 0xFF, 0xFF,
+  # WriteQueueSize: UINT64 Size: 0x10000 - 0x20 (FTW_WORKING_HEADER) = 0xFFE0
+  0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+
+0x00780000|0x00040000
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
 
 ################################################################################
 #
@@ -102,9 +155,11 @@ [FV.FVMAIN_COMPACT]
   INF Silicon/Ampere/AmpereAltraPkg/Drivers/ATFHobPei/ATFHobPeim.inf
   INF Silicon/Ampere/AmpereAltraPkg/Drivers/MemoryInitPeim/MemoryInitPeim.inf
   INF Silicon/Ampere/AmpereAltraPkg/Drivers/MmCommunicationPei/MmCommunicationPei.inf
+  INF Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.inf
   INF Silicon/Ampere/AmpereAltraPkg/Drivers/BootProgress/BootProgressPeim/BootProgressPeim.inf
   INF ArmPkg/Drivers/CpuPei/CpuPei.inf
   INF MdeModulePkg/Universal/PCD/Pei/Pcd.inf
+  INF MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf
   INF MdeModulePkg/Universal/Variable/Pei/VariablePei.inf
   INF MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.inf
   INF MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPei.inf
@@ -144,6 +199,7 @@ [FV.FvMain]
   INF MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.inf
   INF MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.inf
   INF Silicon/Ampere/AmpereAltraPkg/Drivers/MmCommunicationDxe/MmCommunication.inf
+  INF Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.inf
 }
 
   INF MdeModulePkg/Core/Dxe/DxeMain.inf
@@ -173,6 +229,8 @@ [FV.FvMain]
   # Environment Variables Protocol
   #
   INF MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
+  INF MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
+  INF Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.inf
 
   #
   # Multiple Console IO support
diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.inf b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.inf
new file mode 100644
index 000000000000..782278615094
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.inf
@@ -0,0 +1,54 @@
+## @file
+#
+# Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001001B
+  BASE_NAME                      = FlashFvbDxe
+  FILE_GUID                      = 9E6EA240-DF80-11EA-8B6E-0800200C9A66
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 0.1
+  ENTRY_POINT                    = FlashFvbDxeInitialize
+
+[Sources]
+  FlashFvbDxe.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
+  Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  FlashLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiRuntimeLib
+
+[FixedPcd]
+  gAmpereTokenSpaceGuid.PcdFvBlockSize
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
+  gSpiNorMmGuid
+
+[Protocols]
+  gEfiFirmwareVolumeBlockProtocolGuid             ## PRODUCES
+  gEfiMmCommunicationProtocolGuid                 ## CONSUMES
+
+[Depex]
+  gEfiMmCommunicationProtocolGuid
diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.inf b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.inf
new file mode 100644
index 000000000000..a4eaf5039bb0
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.inf
@@ -0,0 +1,51 @@
+## @file
+#
+# Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001001B
+  BASE_NAME                      = FlashPei
+  FILE_GUID                      = 967CFBD0-DF81-11EA-8B6E-0800200C9A66
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = FlashPeiEntryPoint
+
+[Sources]
+  FlashPei.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
+  Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec
+
+[LibraryClasses]
+  ArmSmcLib
+  BaseMemoryLib
+  DebugLib
+  MmCommunicationLib
+  PcdLib
+  PeimEntryPoint
+
+[Guids]
+  gSpiNorMmGuid
+
+[FixedPcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
+
+  gAmpereTokenSpaceGuid.PcdNvramErased
+
+[Depex]
+  TRUE
diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.inf b/Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.inf
new file mode 100644
index 000000000000..2d5003d1af17
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.inf
@@ -0,0 +1,36 @@
+## @file
+#
+# Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                   = 0x0001001B
+  BASE_NAME                     = FlashLib
+  FILE_GUID                     = 9E9D093D-6484-45AE-BA49-0745AA0BB481
+  MODULE_TYPE                   = DXE_DRIVER
+  VERSION_STRING                = 0.1
+  LIBRARY_CLASS                 = FlashLib
+  CONSTRUCTOR                   = FlashLibConstructor
+
+[Sources.common]
+  FlashLib.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  MemoryAllocationLib
+
+[Guids]
+  gSpiNorMmGuid
+
+[Protocols]
+  gEfiMmCommunicationProtocolGuid
diff --git a/Silicon/Ampere/AmpereAltraPkg/Include/Library/FlashLib.h b/Silicon/Ampere/AmpereAltraPkg/Include/Library/FlashLib.h
new file mode 100644
index 000000000000..9207dee643a5
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Include/Library/FlashLib.h
@@ -0,0 +1,42 @@
+/** @file
+
+  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef FLASH_LIB_H_
+#define FLASH_LIB_H_
+
+EFI_STATUS
+EFIAPI
+FlashGetNvRamInfo (
+  OUT UINT64 *NvRamBase,
+  OUT UINT32 *NvRamSize
+  );
+
+EFI_STATUS
+EFIAPI
+FlashEraseCommand (
+  IN UINT8  *pBlockAddress,
+  IN UINT32 Length
+  );
+
+EFI_STATUS
+EFIAPI
+FlashProgramCommand (
+  IN     UINT8 *pByteAddress,
+  IN     UINT8 *Byte,
+  IN OUT UINTN *Length
+  );
+
+EFI_STATUS
+EFIAPI
+FlashReadCommand (
+  IN     UINT8 *pByteAddress,
+  OUT    UINT8 *Byte,
+  IN OUT UINTN *Length
+  );
+
+#endif /* FLASH_LIB_H_ */
diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.c b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.c
new file mode 100644
index 000000000000..dcd92151b7d2
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.c
@@ -0,0 +1,525 @@
+/** @file
+
+  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/FlashLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+//
+// These temporary buffers are used to calculate and convert linear virtual
+// to physical address
+//
+STATIC UINT64 mNvFlashBase;
+STATIC UINT32 mNvFlashSize;
+STATIC UINT32 mFlashBlockSize;
+STATIC UINT64 mNvStorageBase;
+STATIC UINT64 mNvStorageSize;
+
+/**
+  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
+**/
+VOID
+EFIAPI
+FlashFvbAddressChangeEvent (
+  IN EFI_EVENT Event,
+  IN VOID      *Context
+  )
+{
+  EfiConvertPointer (0x0, (VOID **)&mNvStorageBase);
+}
+
+/**
+  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
+FlashFvbDxeGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+  OUT      EFI_FVB_ATTRIBUTES_2                *Attributes
+  )
+{
+  ASSERT (Attributes != NULL);
+
+  *Attributes = EFI_FVB2_READ_ENABLED_CAP   | // Reads may be enabled
+                EFI_FVB2_READ_STATUS        | // Reads are currently enabled
+                EFI_FVB2_WRITE_STATUS       | // Writes are currently enabled
+                EFI_FVB2_WRITE_ENABLED_CAP  | // Writes may be enabled
+                EFI_FVB2_STICKY_WRITE       | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY
+                EFI_FVB2_MEMORY_MAPPED      | // It is memory mapped
+                EFI_FVB2_ALIGNMENT          |
+                EFI_FVB2_ERASE_POLARITY;      // After erasure all bits take this value (i.e. '1')
+
+  return EFI_SUCCESS;
+}
+
+/**
+  The SetAttributes() function sets configurable firmware volume
+  attributes and returns the new settings of the firmware volume.
+
+  @param This         Indicates the 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. Type
+                      EFI_FVB_ATTRIBUTES_2 is defined in
+                      EFI_FIRMWARE_VOLUME_HEADER.
+
+  @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
+FlashFvbDxeSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+  IN OUT   EFI_FVB_ATTRIBUTES_2                *Attributes
+  )
+{
+  return EFI_SUCCESS;  // ignore for now
+}
+
+/**
+  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     Indicates the 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_UNSUPPORTED   The firmware volume is not memory mapped.
+
+**/
+EFI_STATUS
+EFIAPI
+FlashFvbDxeGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+  OUT      EFI_PHYSICAL_ADDRESS                *Address
+  )
+{
+  ASSERT (Address != NULL);
+
+  *Address = (EFI_PHYSICAL_ADDRESS)mNvStorageBase;
+
+  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           Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+  @param Lba            Indicates the block for which to return the size.
+
+  @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
+FlashFvbDxeGetBlockSize (
+  IN  CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+  IN        EFI_LBA                             Lba,
+  OUT       UINTN                               *BlockSize,
+  OUT       UINTN                               *NumberOfBlocks
+  )
+{
+  UINTN TotalNvStorageBlocks;
+
+  ASSERT (BlockSize != NULL);
+  ASSERT (NumberOfBlocks != NULL);
+
+  TotalNvStorageBlocks = mNvStorageSize / mFlashBlockSize;
+
+  if (TotalNvStorageBlocks <= (UINTN)Lba) {
+    DEBUG ((DEBUG_ERROR, "The requested LBA is out of range\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *NumberOfBlocks = TotalNvStorageBlocks - (UINTN)Lba;
+  *BlockSize = mFlashBlockSize;
+
+  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     Indicates the 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
+FlashFvbDxeRead (
+  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 Status;
+
+  ASSERT (NumBytes != NULL);
+  ASSERT (Buffer != NULL);
+
+  if (Offset + *NumBytes > mFlashBlockSize) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  Status = FlashReadCommand (
+             (UINT8 *)(mNvFlashBase + Lba * mFlashBlockSize + Offset),
+             Buffer,
+             NumBytes
+             );
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to do flash read\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  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     Indicates the 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
+FlashFvbDxeWrite (
+  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;
+
+  ASSERT (NumBytes != NULL);
+  ASSERT (Buffer != NULL);
+
+  if (Offset + *NumBytes > mFlashBlockSize) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  Status = FlashProgramCommand (
+             (UINT8 *)(mNvFlashBase + Lba * mFlashBlockSize + Offset),
+             Buffer,
+             NumBytes
+             );
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to do flash program\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  return Status;
+}
+
+/**
+  Erases and initializes 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   Indicates the 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. For example, the
+                following indicates that two ranges of blocks
+                (5-7 and 10-11) are to be erased: EraseBlocks
+                (This, 5, 3, 10, 2, 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
+FlashFvbDxeErase (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+  ...
+  )
+{
+  VA_LIST    Args;
+  EFI_LBA    Start;
+  UINTN      Length;
+  EFI_STATUS Status;
+
+  Status = EFI_SUCCESS;
+
+  VA_START (Args, This);
+
+  for (Start = VA_ARG (Args, EFI_LBA);
+       Start != EFI_LBA_LIST_TERMINATOR;
+       Start = VA_ARG (Args, EFI_LBA))
+  {
+    Length = VA_ARG (Args, UINTN);
+    Status = FlashEraseCommand (
+               (UINT8 *)(mNvFlashBase + Start * mFlashBlockSize),
+               Length * mFlashBlockSize
+               );
+  }
+
+  VA_END (Args);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to do flash erase\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL mFlashFvbProtocol = {
+  FlashFvbDxeGetAttributes,
+  FlashFvbDxeSetAttributes,
+  FlashFvbDxeGetPhysicalAddress,
+  FlashFvbDxeGetBlockSize,
+  FlashFvbDxeRead,
+  FlashFvbDxeWrite,
+  FlashFvbDxeErase
+};
+
+EFI_STATUS
+EFIAPI
+FlashFvbDxeInitialize (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS Status;
+  EFI_HANDLE FvbHandle = NULL;
+  EFI_EVENT  VirtualAddressChangeEvent;
+
+  // Get NV store FV info
+  mFlashBlockSize = FixedPcdGet32 (PcdFvBlockSize);
+  mNvStorageBase = PcdGet64 (PcdFlashNvStorageVariableBase64);
+  mNvStorageSize = FixedPcdGet32 (PcdFlashNvStorageVariableSize) +
+                   FixedPcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
+                   FixedPcdGet32 (PcdFlashNvStorageFtwSpareSize);
+
+  DEBUG ((
+    DEBUG_INFO,
+    "%a: Using NV store FV in-memory copy at 0x%lx with size 0x%x\n",
+    __FUNCTION__,
+    mNvStorageBase,
+    mNvStorageSize
+    ));
+
+  // Get NV Flash information
+  Status = FlashGetNvRamInfo ((UINT64 *)&mNvFlashBase, (UINT32 *)&mNvFlashSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Failed to get Flash info\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (mNvFlashSize >= (mNvStorageSize * 2)) {
+    DEBUG ((DEBUG_INFO, "%a: NV store on Flash is valid\n", __FUNCTION__));
+  } else {
+    DEBUG ((DEBUG_ERROR, "%a: NV store on Flash is invalid\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  FlashFvbAddressChangeEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &VirtualAddressChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &FvbHandle,
+                  &gEfiFirmwareVolumeBlockProtocolGuid,
+                  &mFlashFvbProtocol,
+                  NULL
+                  );
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to install Firmware Volume Block protocol\n"));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.c b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.c
new file mode 100644
index 000000000000..6bfbbbebaa85
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.c
@@ -0,0 +1,283 @@
+/** @file
+
+  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <Uefi.h>
+
+#include <Library/ArmSmcLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MmCommunicationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <MmLib.h>
+
+// Convert to string
+#define _STR(x)          #x
+
+// Make sure the argument is expanded before converting to string
+#define STR(x)          _STR(x)
+
+// Use dynamic UEFI_UUID of each build time
+#define UEFI_UUID_BUILD      STR(UEFI_UUID)
+
+EFI_MM_COMM_REQUEST mEfiMmSpiNorReq;
+
+STATIC CHAR8 mBuildUuid[sizeof (UEFI_UUID_BUILD)];
+STATIC CHAR8 mStoredUuid[sizeof (UEFI_UUID_BUILD)];
+
+STATIC
+EFI_STATUS
+UefiMmCreateSpiNorReq (
+  VOID   *Data,
+  UINT64 Size
+  )
+{
+  CopyGuid (&mEfiMmSpiNorReq.EfiMmHdr.HeaderGuid, &gSpiNorMmGuid);
+  mEfiMmSpiNorReq.EfiMmHdr.MsgLength = Size;
+
+  if (Size != 0) {
+    ASSERT (Data);
+    ASSERT (Size <= EFI_MM_MAX_PAYLOAD_SIZE);
+
+    CopyMem (mEfiMmSpiNorReq.PayLoad.Data, Data, Size);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Entry point function for the PEIM
+
+  @param FileHandle      Handle of the file being invoked.
+  @param PeiServices     Describes the list of possible PEI Services.
+
+  @return EFI_SUCCESS    If we installed our PPI
+
+**/
+EFI_STATUS
+EFIAPI
+FlashPeiEntryPoint (
+  IN       EFI_PEI_FILE_HANDLE FileHandle,
+  IN CONST EFI_PEI_SERVICES    **PeiServices
+  )
+{
+  UINT64                               FWNvRamStartOffset;
+  EFI_STATUS                           Status;
+  EFI_MM_COMMUNICATE_SPINOR_RES        *MmSpiNorRes;
+  EFI_MM_COMMUNICATE_SPINOR_NVINFO_RES *MmSpiNorNVInfoRes;
+  UINT64                               MmData[5];
+  UINTN                                Size;
+  VOID                                 *NvRamAddress;
+  UINTN                                NvRamSize;
+
+#if defined(RAM_BLOCKIO_START_ADDRESS) && defined(RAM_BLOCKIO_SIZE)
+  EFI_MM_COMMUNICATE_SPINOR_NVINFO_RES *MmSpiNorNV2InfoRes;
+  UINT64                               NV2Base, NV2Size;
+#endif
+
+  NvRamAddress = (VOID *)PcdGet64 (PcdFlashNvStorageVariableBase64);
+  NvRamSize = FixedPcdGet32 (PcdFlashNvStorageVariableSize) +
+              FixedPcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
+              FixedPcdGet32 (PcdFlashNvStorageFtwSpareSize);
+
+  /* Find out about the start offset of NVRAM to be passed to SMC */
+  ZeroMem ((VOID *)MmData, sizeof (MmData));
+  MmData[0] = MM_SPINOR_FUNC_GET_NVRAM_INFO;
+  UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+  Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+  Status = MmCommunicationCommunicate (
+             (VOID *)&mEfiMmSpiNorReq,
+             &Size
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  MmSpiNorNVInfoRes = (EFI_MM_COMMUNICATE_SPINOR_NVINFO_RES *)&mEfiMmSpiNorReq.PayLoad;
+  if (MmSpiNorNVInfoRes->Status != MM_SPINOR_RES_SUCCESS) {
+    /* Old FW so just exit */
+    return EFI_SUCCESS;
+  }
+  FWNvRamStartOffset = MmSpiNorNVInfoRes->NVBase;
+
+  CopyMem ((VOID *)mBuildUuid, (VOID *)UEFI_UUID_BUILD, sizeof (UEFI_UUID_BUILD));
+  if (MmSpiNorNVInfoRes->NVSize < (NvRamSize * 2 + sizeof (mBuildUuid))) {
+    /* NVRAM size provided by FW is not enough */
+    return EFI_INVALID_PARAMETER;
+  }
+
+  /* We stored BIOS UUID build at the offset NVRAM_SIZE * 2 */
+  ZeroMem ((VOID *)MmData, sizeof (MmData));
+  MmData[0] = MM_SPINOR_FUNC_READ;
+  MmData[1] = (UINT64)(FWNvRamStartOffset + NvRamSize * 2);
+  MmData[2] = (UINT64)sizeof (mStoredUuid);
+  MmData[3] = (UINT64)mStoredUuid;
+  UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+  Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+  Status = MmCommunicationCommunicate (
+             (VOID *)&mEfiMmSpiNorReq,
+             &Size
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mEfiMmSpiNorReq.PayLoad;
+  if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+    return Status;
+  }
+
+  if (CompareMem ((VOID *)mStoredUuid, (VOID *)mBuildUuid, sizeof (mBuildUuid))) {
+    ZeroMem ((VOID *)MmData, sizeof (MmData));
+    MmData[0] = MM_SPINOR_FUNC_ERASE;
+    MmData[1] = (UINT64)FWNvRamStartOffset;
+    MmData[2] = (UINT64)(NvRamSize * 2);
+    UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+    Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+    Status = MmCommunicationCommunicate (
+               (VOID *)&mEfiMmSpiNorReq,
+               &Size
+               );
+    ASSERT_EFI_ERROR (Status);
+
+    MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mEfiMmSpiNorReq.PayLoad;
+    if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+      return Status;
+    }
+
+    ZeroMem ((VOID *)MmData, sizeof (MmData));
+    MmData[0] = MM_SPINOR_FUNC_WRITE;
+    MmData[1] = (UINT64)FWNvRamStartOffset;
+    MmData[2] = (UINT64)(NvRamSize * 2);
+    MmData[3] = (UINT64)NvRamAddress;
+    UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+    Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+    Status = MmCommunicationCommunicate (
+               (VOID *)&mEfiMmSpiNorReq,
+               &Size
+               );
+    ASSERT_EFI_ERROR (Status);
+
+    MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mEfiMmSpiNorReq.PayLoad;
+    if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+      return Status;
+    }
+
+    /* Update UUID */
+    ZeroMem ((VOID *)MmData, sizeof (MmData));
+    MmData[0] = MM_SPINOR_FUNC_ERASE;
+    MmData[1] = (UINT64)(FWNvRamStartOffset + NvRamSize * 2);
+    MmData[2] = (UINT64)sizeof (mBuildUuid);
+    UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+    Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+    Status = MmCommunicationCommunicate (
+               (VOID *)&mEfiMmSpiNorReq,
+               &Size
+               );
+    ASSERT_EFI_ERROR (Status);
+
+    MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mEfiMmSpiNorReq.PayLoad;
+    if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+      return Status;
+    }
+
+    ZeroMem ((VOID *)MmData, sizeof (MmData));
+    MmData[0] = MM_SPINOR_FUNC_WRITE;
+    MmData[1] = (UINT64)(FWNvRamStartOffset + NvRamSize * 2);
+    MmData[2] = (UINT64)sizeof (mBuildUuid);
+    MmData[3] = (UINT64)mBuildUuid;
+    UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+    Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+    Status = MmCommunicationCommunicate (
+               (VOID *)&mEfiMmSpiNorReq,
+               &Size
+               );
+    ASSERT_EFI_ERROR (Status);
+
+    MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mEfiMmSpiNorReq.PayLoad;
+    if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+      return Status;
+    }
+    DEBUG ((DEBUG_INFO, "UUID Changed, Update Storage with FV NVRAM\n"));
+
+    /* Indicate that NVRAM was cleared */
+    PcdSetBoolS (PcdNvramErased, TRUE);
+  } else {
+    /* Copy the stored NVRAM to RAM */
+    ZeroMem ((VOID *)MmData, sizeof (MmData));
+    MmData[0] = MM_SPINOR_FUNC_READ;
+    MmData[1] = (UINT64)FWNvRamStartOffset;
+    MmData[2] = (UINT64)(NvRamSize * 2);
+    MmData[3] = (UINT64)NvRamAddress;
+    UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+    Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+    Status = MmCommunicationCommunicate (
+               (VOID *)&mEfiMmSpiNorReq,
+               &Size
+               );
+    ASSERT_EFI_ERROR (Status);
+
+    MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mEfiMmSpiNorReq.PayLoad;
+    if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+      return Status;
+    }
+    DEBUG ((DEBUG_INFO, "Identical UUID, copy stored NVRAM to RAM\n"));
+  }
+
+#if defined(RAM_BLOCKIO_START_ADDRESS) && defined(RAM_BLOCKIO_SIZE)
+  /* Find out about the start offset of NVRAM2 to be passed to SMC */
+  ZeroMem ((VOID *)MmData, sizeof (MmData));
+  MmData[0] = MM_SPINOR_FUNC_GET_NVRAM2_INFO;
+  UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+  Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+  Status = MmCommunicationCommunicate (
+             (VOID *)&mEfiMmSpiNorReq,
+             &Size
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  MmSpiNorNV2InfoRes = (EFI_MM_COMMUNICATE_SPINOR_NVINFO_RES *)&mEfiMmSpiNorReq.PayLoad;
+  if (MmSpiNorNV2InfoRes->Status == MM_SPINOR_RES_SUCCESS) {
+    NV2Base = MmSpiNorNV2InfoRes->NVBase;
+    NV2Size = MmSpiNorNV2InfoRes->NVSize;
+    /* Make sure the requested size is smaller than allocated */
+    if (RAM_BLOCKIO_SIZE <= NV2Size) {
+      /* Copy the ramdisk image to RAM */
+      ZeroMem ((VOID *)MmData, sizeof (MmData));
+      MmData[0] = MM_SPINOR_FUNC_READ;
+      MmData[1] = (UINT64)NV2Base; /* Start virtual address */
+      MmData[2] = (UINT64)RAM_BLOCKIO_SIZE;
+      MmData[3] = (UINT64)RAM_BLOCKIO_START_ADDRESS;
+      UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+
+      Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+      Status = MmCommunicationCommunicate (
+                 (VOID *)&mEfiMmSpiNorReq,
+                 &Size
+                 );
+      ASSERT_EFI_ERROR (Status);
+
+      MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mEfiMmSpiNorReq.PayLoad;
+      ASSERT (MmSpiNorRes->Status == MM_SPINOR_RES_SUCCESS);
+    }
+
+    BuildMemoryAllocationHob (
+      (EFI_PHYSICAL_ADDRESS)RAM_BLOCKIO_START_ADDRESS,
+      EFI_SIZE_TO_PAGES (RAM_BLOCKIO_SIZE) * EFI_PAGE_SIZE,
+      EfiLoaderData
+      );
+  }
+#endif
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.c b/Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.c
new file mode 100644
index 000000000000..cd77aed3cfe1
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.c
@@ -0,0 +1,358 @@
+/** @file
+
+  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/FlashLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <MmLib.h>
+#include <Protocol/MmCommunication.h>
+
+STATIC EFI_MM_COMMUNICATION_PROTOCOL *mMmCommunicationProtocol = NULL;
+STATIC EFI_MM_COMM_REQUEST           *mCommBuffer              = NULL;
+
+BOOLEAN mIsEfiRuntime;
+UINT8   *mTmpBufVirt;
+UINT8   *mTmpBufPhy;
+
+/**
+  This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
+  event. It converts a pointer to a new virtual address.
+
+  @param  Event        Event whose notification function is being invoked.
+  @param  Context      Pointer to the notification function's context
+
+**/
+VOID
+EFIAPI
+FlashLibAddressChangeEvent (
+  IN EFI_EVENT Event,
+  IN VOID      *Context
+  )
+{
+  gRT->ConvertPointer (0x0, (VOID **)&mTmpBufVirt);
+  gRT->ConvertPointer (0x0, (VOID **)&mCommBuffer);
+  gRT->ConvertPointer (0x0, (VOID **)&mMmCommunicationProtocol);
+
+  mIsEfiRuntime = TRUE;
+}
+
+EFI_STATUS
+EFIAPI
+FlashLibConstructor (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_EVENT  VirtualAddressChangeEvent = NULL;
+  EFI_STATUS Status = EFI_SUCCESS;
+
+  mCommBuffer = AllocateRuntimeZeroPool (sizeof (EFI_MM_COMM_REQUEST));
+  ASSERT (mCommBuffer != NULL);
+
+  mTmpBufPhy = AllocateRuntimeZeroPool (EFI_MM_MAX_TMP_BUF_SIZE);
+  mTmpBufVirt = mTmpBufPhy;
+  ASSERT (mTmpBufPhy != NULL);
+
+  Status = gBS->LocateProtocol (
+                  &gEfiMmCommunicationProtocolGuid,
+                  NULL,
+                  (VOID **)&mMmCommunicationProtocol
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
+                  TPL_CALLBACK,
+                  FlashLibAddressChangeEvent,
+                  NULL,
+                  &VirtualAddressChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+FlashMmCommunicate (
+  IN OUT VOID  *CommBuffer,
+  IN OUT UINTN *CommSize
+  )
+{
+  if (mMmCommunicationProtocol == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return mMmCommunicationProtocol->Communicate (
+                                     mMmCommunicationProtocol,
+                                     CommBuffer,
+                                     CommSize
+                                     );
+}
+
+STATIC
+EFI_STATUS
+UefiMmCreateSpiNorReq (
+  IN VOID   *Data,
+  IN UINT64 Size
+  )
+{
+  if (mCommBuffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CopyGuid (&mCommBuffer->EfiMmHdr.HeaderGuid, &gSpiNorMmGuid);
+  mCommBuffer->EfiMmHdr.MsgLength = Size;
+
+  if (Size != 0) {
+    ASSERT (Data);
+    ASSERT (Size <= EFI_MM_MAX_PAYLOAD_SIZE);
+
+    CopyMem (mCommBuffer->PayLoad.Data, Data, Size);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Convert Virtual Address to Physical Address at Runtime Services
+
+  @param  VirtualPtr          Virtual Address Pointer
+  @param  Size                Total bytes of the buffer
+
+  @retval Ptr                 Return the pointer of the converted address
+
+**/
+STATIC
+UINT8 *
+ConvertVirtualToPhysical (
+  IN UINT8 *VirtualPtr,
+  IN UINTN Size
+  )
+{
+  if (mIsEfiRuntime) {
+    ASSERT (VirtualPtr != NULL);
+    CopyMem ((VOID *)mTmpBufVirt, (VOID *)VirtualPtr, Size);
+    return (UINT8 *)mTmpBufPhy;
+  }
+
+  return (UINT8 *)VirtualPtr;
+}
+
+/**
+  Convert Physical Address to Virtual Address at Runtime Services
+
+  @param  VirtualPtr          Physical Address Pointer
+  @param  Size                Total bytes of the buffer
+
+**/
+STATIC
+VOID
+ConvertPhysicaltoVirtual (
+  IN UINT8 *PhysicalPtr,
+  IN UINTN Size
+  )
+{
+  if (mIsEfiRuntime) {
+    ASSERT (PhysicalPtr != NULL);
+    CopyMem ((VOID *)PhysicalPtr, (VOID *)mTmpBufVirt, Size);
+  }
+}
+
+EFI_STATUS
+EFIAPI
+FlashGetNvRamInfo (
+  OUT UINT64 *NvRamBase,
+  OUT UINT32 *NvRamSize
+  )
+{
+  EFI_MM_COMMUNICATE_SPINOR_NVINFO_RES *MmSpiNorNVInfoRes;
+  EFI_STATUS                           Status;
+  UINT64                               MmData[5];
+  UINTN                                Size;
+
+  MmData[0] = MM_SPINOR_FUNC_GET_NVRAM_INFO;
+
+  Status = UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+  Status = FlashMmCommunicate (
+             mCommBuffer,
+             &Size
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  MmSpiNorNVInfoRes = (EFI_MM_COMMUNICATE_SPINOR_NVINFO_RES *)&mCommBuffer->PayLoad;
+  if (MmSpiNorNVInfoRes->Status == MM_SPINOR_RES_SUCCESS) {
+    *NvRamBase = MmSpiNorNVInfoRes->NVBase;
+    *NvRamSize = MmSpiNorNVInfoRes->NVSize;
+    DEBUG ((DEBUG_INFO, "NVInfo Base 0x%llx, Size 0x%lx\n", *NvRamBase, *NvRamSize));
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+FlashEraseCommand (
+  IN UINT8  *pBlockAddress,
+  IN UINT32 Length
+  )
+{
+  EFI_MM_COMMUNICATE_SPINOR_RES *MmSpiNorRes;
+  EFI_STATUS                    Status;
+  UINT64                        MmData[5];
+  UINTN                         Size;
+
+  ASSERT (pBlockAddress != NULL);
+
+  MmData[0] = MM_SPINOR_FUNC_ERASE;
+  MmData[1] = (UINT64)pBlockAddress;
+  MmData[2] = Length;
+
+  Status = UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+  Status = FlashMmCommunicate (
+             mCommBuffer,
+             &Size
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mCommBuffer->PayLoad;
+  if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "Flash Erase: Device error %llx\n", MmSpiNorRes->Status));
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+FlashProgramCommand (
+  IN     UINT8 *pByteAddress,
+  IN     UINT8 *Byte,
+  IN OUT UINTN *Length
+  )
+{
+  EFI_MM_COMMUNICATE_SPINOR_RES *MmSpiNorRes;
+  EFI_STATUS                    Status;
+  UINT64                        MmData[5];
+  UINTN                         Remain, Size, NumWrite;
+  UINTN                         Count = 0;
+
+  ASSERT (pByteAddress != NULL);
+  ASSERT (Byte != NULL);
+  ASSERT (Length != NULL);
+
+  Remain = *Length;
+  while (Remain > 0) {
+    NumWrite = (Remain > EFI_MM_MAX_TMP_BUF_SIZE) ? EFI_MM_MAX_TMP_BUF_SIZE : Remain;
+
+    MmData[0] = MM_SPINOR_FUNC_WRITE;
+    MmData[1] = (UINT64)pByteAddress;
+    MmData[2] = NumWrite;
+    MmData[3] = (UINT64)ConvertVirtualToPhysical (Byte + Count, NumWrite);
+
+    Status = UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+    Status = FlashMmCommunicate (
+               mCommBuffer,
+               &Size
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mCommBuffer->PayLoad;
+    if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+      DEBUG ((DEBUG_ERROR, "Flash program: Device error 0x%llx\n", MmSpiNorRes->Status));
+      return EFI_DEVICE_ERROR;
+    }
+
+    Remain -= NumWrite;
+    Count += NumWrite;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+FlashReadCommand (
+  IN     UINT8 *pByteAddress,
+  OUT    UINT8 *Byte,
+  IN OUT UINTN *Length
+  )
+{
+  EFI_MM_COMMUNICATE_SPINOR_RES *MmSpiNorRes;
+  EFI_STATUS                    Status;
+  UINT64                        MmData[5];
+  UINTN                         Remain, Size, NumRead;
+  UINTN                         Count = 0;
+
+  ASSERT (pByteAddress != NULL);
+  ASSERT (Byte != NULL);
+  ASSERT (Length != NULL);
+
+  Remain = *Length;
+  while (Remain > 0) {
+    NumRead = (Remain > EFI_MM_MAX_TMP_BUF_SIZE) ? EFI_MM_MAX_TMP_BUF_SIZE : Remain;
+
+    MmData[0] = MM_SPINOR_FUNC_READ;
+    MmData[1] = (UINT64)pByteAddress;
+    MmData[2] = NumRead;
+    MmData[3] = (UINT64)ConvertVirtualToPhysical (Byte + Count, NumRead);
+
+    Status = UefiMmCreateSpiNorReq ((VOID *)&MmData, sizeof (MmData));
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Size = sizeof (EFI_MM_COMM_HEADER_NOPAYLOAD) + sizeof (MmData);
+    Status = FlashMmCommunicate (
+               mCommBuffer,
+               &Size
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    MmSpiNorRes = (EFI_MM_COMMUNICATE_SPINOR_RES *)&mCommBuffer->PayLoad;
+    if (MmSpiNorRes->Status != MM_SPINOR_RES_SUCCESS) {
+      DEBUG ((DEBUG_ERROR, "Flash Read: Device error %llx\n", MmSpiNorRes->Status));
+      return EFI_DEVICE_ERROR;
+    }
+
+    ConvertPhysicaltoVirtual (Byte + Count, NumRead);
+    Remain -= NumRead;
+    Count += NumRead;
+  }
+
+  return EFI_SUCCESS;
+}
-- 
2.17.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#75617): https://edk2.groups.io/g/devel/message/75617
Mute This Topic: https://groups.io/mt/83097104/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-






More information about the edk2-devel-archive mailing list