Re: [PATCH v3 09/28] AmpereAltraPkg: Support UEFI non-volatile variable


Leif Lindholm
 

On Wed, Sep 15, 2021 at 22:55:08 +0700, Nhi Pham wrote:
From: Vu Nguyen <vunguyen@os.amperecomputing.com>

This supports storing the UEFI non-volatile varibles on flash through
the MM communication interface instead of emulating. Below are modules
added for this support:
* FlashLib provides APIs to access flash through MM service.
* FlashPei driver helps to restore the saved variables from
flash on each boot.
* FlashFvbDxe driver provides the implementation for the
gEfiFirmwareVolumeBlock protocol

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

Signed-off-by: Vu Nguyen <vunguyen@os.amperecomputing.com>
---
Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec | 3 +
Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec | 8 +
Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc | 10 +-
Platform/Ampere/JadePkg/Jade.dsc | 5 +
Platform/Ampere/JadePkg/Jade.fdf | 62 ++-
Silicon/Ampere/AmpereAltraPkg/Drivers/FlashFvbDxe/FlashFvbDxe.inf | 54 ++
Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.inf | 52 ++
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 | 273 ++++++++++
Silicon/Ampere/AmpereAltraPkg/Library/FlashLib/FlashLib.c | 358 +++++++++++++
12 files changed, 1424 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..646a53fc031c 100644
--- a/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec
+++ b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec
@@ -31,6 +31,11 @@ [Guids]
[Ppis]

[PcdsFixedAtBuild]
+ #
+ # NVRAM
+ #
+ gAmpereTokenSpaceGuid.PcdNvramUuid|"C416535D-970B-41B9-859A-3CAF0FAF198C"|VOID*|0x00000010
This still makes no semantic sense. The UUID does not identify the
NVRAM, it identifies "something somehow related to the NVRAM".
Please eename the Pcd something that describes *what* it identifies.

+
#
# SMpro PMpro Pcds
#
@@ -44,3 +49,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 bb144183164d..7f84ac2df50a 100644
--- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc
+++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc
@@ -226,6 +226,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
@@ -256,6 +257,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
AmpereCpuLib|Silicon/Ampere/AmpereAltraPkg/Library/AmpereCpuLib/RuntimeAmpereCpuLib.inf

[LibraryClasses.ARM,LibraryClasses.AARCH64]
@@ -508,6 +510,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
@@ -528,8 +532,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>
@@ -583,9 +589,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 332cd8ac0c8e..c670fd0f34b6 100644
--- 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 NVRAM_UUID = 84BC921F-9D4A-4D1D-A1A1-1AE13EDD07E5
And rename this define similarly.


#
# Network definition
@@ -89,6 +90,10 @@ [LibraryClasses]
################################################################################
[PcdsFeatureFlag.common]
[PcdsFixedAtBuild.common]
+ #
+ # NVRAM
+ #
+ gAmpereTokenSpaceGuid.PcdNvramUuid|"$(NVRAM_UUID)"

!if $(SECURE_BOOT_ENABLE) == TRUE
# Override the default values from SecurityPkg to ensure images
diff --git a/Platform/Ampere/JadePkg/Jade.fdf b/Platform/Ampere/JadePkg/Jade.fdf
index 1857296a8ea5..375455086d0b 100644
--- 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
As requested for v2, please reword "T.B.D" as "Placeholder" in the
patch where it is introduced.

+# 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..9b640adb6421
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.inf
@@ -0,0 +1,52 @@
+## @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
+ gAmpereTokenSpaceGuid.PcdNvramUuid
+
+[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..0939f1b70869
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FlashPei/FlashPei.c
@@ -0,0 +1,273 @@
+/** @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>
+
+EFI_MM_COMM_REQUEST mEfiMmSpiNorReq;
+
+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 (
This function is very long, and would really benefit from breaking up
into some smaller helper functions. Those could then be given helpful
names which would help clarify what's going on.

+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ CHAR8 BuildUuid[PcdGetSize (PcdNvramUuid)];
+ CHAR8 StoredUuid[PcdGetSize (PcdNvramUuid)];
+ EFI_MM_COMMUNICATE_SPINOR_NVINFO_RES *MmSpiNorNVInfoRes;
+ EFI_MM_COMMUNICATE_SPINOR_RES *MmSpiNorRes;
+ EFI_STATUS Status;
+ UINT64 FWNvRamStartOffset;
+ UINT64 MmData[5];
+ UINTN NvRamSize;
+ UINTN Size;
+ VOID *NvRamAddress;
+
+#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 *)BuildUuid, PcdGetPtr (PcdNvramUuid), sizeof (BuildUuid));
+ if (MmSpiNorNVInfoRes->NVSize < (NvRamSize * 2 + sizeof (BuildUuid))) {
+ /* 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 (StoredUuid);
+ MmData[3] = (UINT64)StoredUuid;
+ 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 *)StoredUuid, (VOID *)BuildUuid, sizeof (BuildUuid))) {
Definitely helper function for this scope.

+ 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 */
Which UUID? Describe the content, not the storage format.
But this also seems like a clear logic block to move to a helper, at
which point the function name will be as useful as a comment.

+ ZeroMem ((VOID *)MmData, sizeof (MmData));
+ MmData[0] = MM_SPINOR_FUNC_ERASE;
+ MmData[1] = (UINT64)(FWNvRamStartOffset + NvRamSize * 2);
+ MmData[2] = (UINT64)sizeof (BuildUuid);
+ 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 (BuildUuid);
+ MmData[3] = (UINT64)BuildUuid;
+ 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 {
Another helper function for the other path.

+ /* 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)
Can the whole contents of the #ifdef block be moved into a helper
function, only built if these conditions match?

/
Leif

+ /* 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

Join devel@edk2.groups.io to automatically receive all group messages.