Add NOR flash DXE driver, this brings up NV storage on QSPI's flash device using FVB protocol.
Signed-off-by: sahil <sa...@arm.com> --- Platform/ARM/N1Sdp/N1SdpPlatform.dec | 5 +- Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf | 72 ++ Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h | 33 + Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h | 491 +++++++++ Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.c | 409 ++++++++ Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c | 1100 ++++++++++++++++++++ Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlashFvb.c | 647 ++++++++++++ 7 files changed, 2756 insertions(+), 1 deletion(-) diff --git a/Platform/ARM/N1Sdp/N1SdpPlatform.dec b/Platform/ARM/N1Sdp/N1SdpPlatform.dec index 16937197b8e8..67b5f4c871b6 100644 --- a/Platform/ARM/N1Sdp/N1SdpPlatform.dec +++ b/Platform/ARM/N1Sdp/N1SdpPlatform.dec @@ -1,7 +1,7 @@ ## @file # Describes the N1Sdp configuration. # -# Copyright (c) 2021, ARM Limited. All rights reserved.<BR> +# Copyright (c) 2021-2023, ARM Limited. All rights reserved.<BR> # # SPDX-License-Identifier: BSD-2-Clause-Patent ## @@ -89,3 +89,6 @@ # unmapped reserved region results in a DECERR response. # gArmN1SdpTokenSpaceGuid.PcdCsComponentSize|0x1000|UINT32|0x00000049 + + # Base address of Cadence QSPI controller configuration registers + gArmN1SdpTokenSpaceGuid.PcdCadenceQspiDxeRegBaseAddress|0x1C0C0000|UINT32|0x0000004A diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf new file mode 100644 index 000000000000..62a4944c95db --- /dev/null +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf @@ -0,0 +1,72 @@ +## @file +# NOR flash DXE +# +# Copyright (c) 2023, ARM Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = CadenceQspiDxe + FILE_GUID = CC8A9713-4442-4A6C-B389-8B46490A0641 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 0.1 + ENTRY_POINT = NorFlashInitialise + +[Sources] + CadenceQspiDxe.c + CadenceQspiReg.h + NorFlash.c + NorFlash.h + NorFlashFvb.c + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + Platform/ARM/ARM.dec + Platform/ARM/N1Sdp/N1SdpPlatform.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DevicePathLib + DxeServicesTableLib + HobLib + IoLib + MemoryAllocationLib + NorFlashInfoLib + NorFlashPlatformLib + TimerLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + UefiRuntimeLib + UefiRuntimeServicesTableLib + +[Guids] + gEdkiiNvVarStoreFormattedGuid + gEfiAuthenticatedVariableGuid + gEfiEventVirtualAddressChangeGuid + gEfiSystemNvDataFvGuid + gEfiVariableGuid + gEfiGlobalVariableGuid + +[Protocols] + gEfiDevicePathProtocolGuid + gEfiFirmwareVolumeBlockProtocolGuid + +[FixedPcd] + gArmN1SdpTokenSpaceGuid.PcdCadenceQspiDxeRegBaseAddress + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize + +[Depex] + gEfiCpuArchProtocolGuid diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h new file mode 100644 index 000000000000..535e6d738d31 --- /dev/null +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h @@ -0,0 +1,33 @@ +/** @file + + Copyright (c) 2023, ARM Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef CADENCE_QSPI_REG_H_ +#define CADENCE_QSPI_REG_H_ + +// QSPI Controller defines +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_OFFSET 0x90 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_EXECUTE 0x01 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_ENABLE 0x01 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BIT_POS 19 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_BIT_POS 16 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_STATUS_BIT 0x02 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS 24 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_ENABLE 0x01 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_BYTE_3B 0x02 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_READEN_BIT_POS 23 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_READBYTE_BIT_POS 20 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_DUMMY_8C 0x8 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_DUMMY_BIT_POS 7 +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_DATA_BYTES(x) ((x - 1) << CDNS_QSPI_FLASH_CMD_CTRL_REG_READBYTE_BIT_POS) +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_ADDR_BYTES(x) ((x - 1) << CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_BIT_POS) + +#define CDNS_QSPI_FLASH_CMD_READ_DATA_REG_OFFSET 0xA0 + +#define CDNS_QSPI_FLASH_CMD_ADDR_REG_OFFSET 0x94 + +#endif /* CADENCE_QSPI_REG_H_ */ diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h new file mode 100644 index 000000000000..38ae1c2fae89 --- /dev/null +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h @@ -0,0 +1,491 @@ +/** @file + + Copyright (c) 2023, ARM Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef NOR_FLASH_DXE_H_ +#define NOR_FLASH_DXE_H_ + +#include <Guid/EventGroup.h> +#include <Library/DebugLib.h> +#include <Library/IoLib.h> +#include <Library/NorFlashPlatformLib.h> +#include <PiDxe.h> +#include <Protocol/BlockIo.h> +#include <Protocol/DiskIo.h> +#include <Protocol/FirmwareVolumeBlock.h> + +#include "CadenceQspiReg.h" + +#define NOR_FLASH_ERASE_RETRY 10 + +#define GET_NOR_BLOCK_ADDRESS(BaseAddr, Lba, LbaSize) \ + ((BaseAddr) + (UINTN)((Lba) * (LbaSize))) + +#define NOR_FLASH_SIGNATURE SIGNATURE_32('S', 'n', 'o', 'r') +#define INSTANCE_FROM_FVB_THIS(a) CR(a, NOR_FLASH_INSTANCE, FvbProtocol, \ + NOR_FLASH_SIGNATURE) + +#define NOR_FLASH_POLL_FSR BIT0 + +typedef struct _NOR_FLASH_INSTANCE NOR_FLASH_INSTANCE; + +typedef EFI_STATUS (*NOR_FLASH_INITIALIZE) ( + NOR_FLASH_INSTANCE *Instance + ); + +#pragma pack(1) +typedef struct { + VENDOR_DEVICE_PATH Vendor; + UINT8 Index; + EFI_DEVICE_PATH_PROTOCOL End; +} NOR_FLASH_DEVICE_PATH; +#pragma pack() + +struct _NOR_FLASH_INSTANCE { + UINT32 Signature; + EFI_HANDLE Handle; + + BOOLEAN Initialized; + NOR_FLASH_INITIALIZE Initialize; + + UINTN HostRegisterBaseAddress; + UINTN DeviceBaseAddress; + UINTN RegionBaseAddress; + UINTN Size; + UINTN BlockSize; + UINTN LastBlock; + EFI_LBA StartLba; + EFI_LBA OffsetLba; + + EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol; + VOID *ShadowBuffer; + + NOR_FLASH_DEVICE_PATH DevicePath; + + UINT32 Flags; +}; + +typedef struct { + EFI_TPL OriginalTPL; + BOOLEAN InterruptsEnabled; +} NOR_FLASH_LOCK_CONTEXT; + +/** + Lock all pending read/write to Nor flash device + + @param[in] Context Nor flash device context structure. +**/ +VOID +EFIAPI +NorFlashLock ( + IN NOR_FLASH_LOCK_CONTEXT *Context + ); + +/** + Unlock all pending read/write to Nor flash device + + @param[in] Context Nor flash device context structure. +**/ +VOID +EFIAPI +NorFlashUnlock ( + IN NOR_FLASH_LOCK_CONTEXT *Context + ); + +extern UINTN mFlashNvStorageVariableBase; + +/** + Create Nor flash Instance for given region. + + @param[in] HostRegisterBase Base address of Nor flash controller. + @param[in] NorFlashDeviceBase Base address of flash device. + @param[in] NorFlashRegionBase Base address of flash region on device. + @param[in] NorFlashSize Size of flash region. + @param[in] Index Index of given flash region. + @param[in] BlockSize Block size of NOR flash device. + @param[in] HasVarStore Boolean set for VarStore on given region. + @param[out] NorFlashInstance Instance of given flash region. + + @retval EFI_SUCCESS On successful creation of NOR flash instance. +**/ +EFI_STATUS +NorFlashCreateInstance ( + IN UINTN HostRegisterBase, + IN UINTN NorFlashDeviceBase, + IN UINTN NorFlashRegionBase, + IN UINTN NorFlashSize, + IN UINT32 Index, + IN UINT32 BlockSize, + IN BOOLEAN HasVarStore, + OUT NOR_FLASH_INSTANCE **NorFlashInstance + ); + +/** + Install Fv block on to variable store region + + @param[in] Instance Instance of Nor flash variable region. + + @retval EFI_SUCCESS The entry point is executed successfully. +**/ +EFI_STATUS +EFIAPI +NorFlashFvbInitialize ( + IN NOR_FLASH_INSTANCE *Instance + ); + +/** + Check the integrity of firmware volume header. + + @param[in] Instance Instance of Nor flash variable region. + + @retval EFI_SUCCESS The firmware volume is consistent. + @retval EFI_NOT_FOUND The firmware volume has been corrupted. + +**/ +EFI_STATUS +ValidateFvHeader ( + IN NOR_FLASH_INSTANCE *Instance + ); + +/** + Initialize the FV Header and Variable Store Header + to support variable operations. + + @param[in] Instance Location to Initialize the headers + + @retval EFI_SUCCESS Fv init is done + +**/ +EFI_STATUS +InitializeFvAndVariableStoreHeaders ( + IN NOR_FLASH_INSTANCE *Instance + ); + +/** + Retrieves the attributes and current settings of the block. + + @param[in] This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[out] Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes and + current settings are returned. + Type EFI_FVB_ATTRIBUTES_2 is defined in + EFI_FIRMWARE_VOLUME_HEADER. + + @retval EFI_SUCCESS The firmware volume attributes were returned. + +**/ +EFI_STATUS +EFIAPI +FvbGetAttributes ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + OUT EFI_FVB_ATTRIBUTES_2 *Attributes + ); + +/** + Sets configurable firmware volume attributes and returns the + new settings of the firmware volume. + + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[in, out] Attributes On input, Attributes is a pointer to + EFI_FVB_ATTRIBUTES_2 that contains the desired + firmware volume settings. + On successful return, it contains the new + settings of the firmware volume. + + @retval EFI_UNSUPPORTED The firmware volume attributes are not supported. + +**/ +EFI_STATUS +EFIAPI +FvbSetAttributes ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes + ); + +/** + Retrieves the base address of a memory-mapped firmware volume. + This function should be called only for memory-mapped firmware volumes. + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[out] 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. + +**/ +EFI_STATUS +EFIAPI +FvbGetPhysicalAddress ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + OUT EFI_PHYSICAL_ADDRESS *Address + ); + +/** + 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[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[in] Lba Indicates the block whose size to return + + @param[out] BlockSize Pointer to a caller-allocated UINTN in which + the size of the block is returned. + + @param[out] NumberOfBlocks Pointer to a caller-allocated UINTN in + which the number of consecutive blocks, + starting with Lba, is returned. All + blocks in this range have a size of + BlockSize. + + @retval EFI_SUCCESS The firmware volume base address was returned. + + @retval EFI_INVALID_PARAMETER The requested LBA is out of range. + +**/ +EFI_STATUS +EFIAPI +FvbGetBlockSize ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + IN EFI_LBA Lba, + OUT UINTN *BlockSize, + OUT UINTN *NumberOfBlocks + ); + +/** + 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. + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[in] Lba The starting logical block index from which to read + + @param[in] Offset Offset into the block at which to begin reading. + + @param[in, out] NumBytes Pointer to a UINTN. + At entry, *NumBytes contains the total size of the + buffer. *NumBytes should have a non zero value. + At exit, *NumBytes contains the total number of + bytes read. + + @param[in out] 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. + + @retval EFI_DEVICE_ERROR The block device is not functioning correctly and + could not be read. + +**/ +EFI_STATUS +EFIAPI +FvbRead ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN OUT UINTN *NumBytes, + IN OUT UINT8 *Buffer + ); + +/** + Writes the specified number of bytes from the input buffer to the block. + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[in] Lba The starting logical block index to write to. + + @param[in] Offset Offset into the block at which to begin writing. + + @param[in, out] 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[in] Buffer The pointer to a caller-allocated buffer that + contains the source for the write. + + @retval EFI_SUCCESS The firmware volume was written successfully. + +**/ +EFI_STATUS +EFIAPI +FvbWrite ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN OUT UINTN *NumBytes, + IN UINT8 *Buffer + ); + +/** + Erases and initialises a firmware volume block. + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL + + @param[in] ... The variable argument list is a list of tuples. + Each tuple describes a range of LBAs to erase + and consists of the following: + - An EFI_LBA that indicates the starting LBA + - A UINTN that indicates the number of blocks + to erase. + + The list is terminated with an + EFI_LBA_LIST_TERMINATOR. + + @retval EFI_SUCCESS The erase request successfully completed. + + @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled + state. + + @retval EFI_DEVICE_ERROR The block device is not functioning correctly + and could not be written. + The firmware device may have been partially + erased. + + @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable + argument list do not exist in the firmware + volume. + +**/ +EFI_STATUS +EFIAPI +FvbEraseBlocks ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + ... + ); + +/** + This function unlock and erase an entire NOR Flash block. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] BlockAddress Block address within the variable store region. + + @retval EFI_SUCCESS The erase and unlock successfully completed. +**/ +EFI_STATUS +NorFlashUnlockAndEraseSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ); + +/** + Write a full or portion of a block. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The starting logical block index to write to. + @param[in] Offset Offset into the block at which to begin writing. + @param[in,out] NumBytes The total size of the buffer. + @param[in] Buffer The pointer to a caller-allocated buffer that + contains the source for the write. + + @retval EFI_SUCCESS The write is completed. +**/ +EFI_STATUS +NorFlashWriteSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN Offset, + IN OUT UINTN *NumBytes, + IN UINT8 *Buffer + ); + +/** + Write a full block. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The starting logical block index to write to. + @param[in] BufferSizeInBytes The number of bytes to write. + @param[in] Buffer The pointer to a caller-allocated buffer that + contains the source for the write. + + @retval EFI_SUCCESS The write is completed. +**/ +EFI_STATUS +NorFlashWriteBlocks ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN BufferSizeInBytes, + IN VOID *Buffer + ); + +/** + Read a full block. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The starting logical block index to read from. + @param[in] BufferSizeInBytes The number of bytes to read. + @param[out] Buffer The pointer to a caller-allocated buffer that + should be copied with read data. + + @retval EFI_SUCCESS The read is completed. +**/ +EFI_STATUS +NorFlashReadBlocks ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN BufferSizeInBytes, + OUT VOID *Buffer + ); + +/** + Read from nor flash. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The starting logical block index to read from. + @param[in] Offset Offset into the block at which to begin reading. + @param[in] BufferSizeInBytes The number of bytes to read. + @param[out] Buffer The pointer to a caller-allocated buffer that + should copied with read data. + + @retval EFI_SUCCESS The read is completed. +**/ +EFI_STATUS +NorFlashRead ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN BufferSizeInBytes, + OUT VOID *Buffer + ); + +/** + Read JEDEC ID of NOR flash device. + + @param[in] Instance NOR flash Instance of variable store region. + @param[out] JedecId JEDEC ID of NOR flash device. + + @retval EFI_SUCCESS The write is completed. +**/ +EFI_STATUS +NorFlashReadID ( + IN NOR_FLASH_INSTANCE *Instance, + OUT UINT8 JedecId[3] + ); + +#define SPINOR_SR_WIP BIT0 // Write in progress + +#define SPINOR_OP_WREN 0x06 // Write enable +#define SPINOR_OP_BE_4K 0x20 // Erase 4KiB block +#define SPINOR_OP_RDID 0x9f // Read JEDEC ID +#define SPINOR_OP_RDSR 0x05 // Read status register + +#define SPINOR_SR_WIP_POLL_TIMEOUT_MS 1000u // Status Register read timeout + +#endif /* NOR_FLASH_DXE_H_ */ diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.c b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.c new file mode 100644 index 000000000000..fffe689161a6 --- /dev/null +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.c @@ -0,0 +1,409 @@ +/** @file + NOR flash DXE + + Copyright (c) 2023, ARM Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseMemoryLib.h> +#include <Library/DxeServicesTableLib.h> +#include <Library/HobLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/NorFlashInfoLib.h> +#include <Library/PcdLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> +#include <Library/UefiRuntimeLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> + +#include "NorFlash.h" + +STATIC NOR_FLASH_INSTANCE **mNorFlashInstances; +STATIC UINT32 mNorFlashDeviceCount; + +STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent; + +/** + Install Fv block onto variable store region + + @param[in] Instance Instance of Nor flash variable region. + + @retval EFI_SUCCESS The entry point is executed successfully. +**/ +EFI_STATUS +EFIAPI +NorFlashFvbInitialize ( + IN NOR_FLASH_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + UINT32 FvbNumLba; + EFI_BOOT_MODE BootMode; + UINTN RuntimeMmioRegionSize; + UINTN RuntimeMmioDeviceSize; + UINTN BlockSize; + + DEBUG ((DEBUG_INFO, "NorFlashFvbInitialize\n")); + + BlockSize = Instance->BlockSize; + + // FirmwareVolumeHeader->FvLength is declared to have the Variable area + // AND the FTW working area AND the FTW Spare contiguous. + ASSERT ( + PcdGet32 (PcdFlashNvStorageVariableBase) + + PcdGet32 (PcdFlashNvStorageVariableSize) == + PcdGet32 (PcdFlashNvStorageFtwWorkingBase) + ); + ASSERT ( + PcdGet32 (PcdFlashNvStorageFtwWorkingBase) + + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) == + PcdGet32 (PcdFlashNvStorageFtwSpareBase) + ); + + // Check if the size of the area is at least one block size. + ASSERT ( + (PcdGet32 (PcdFlashNvStorageVariableSize) > 0) && + (PcdGet32 (PcdFlashNvStorageVariableSize) / BlockSize > 0) + ); + ASSERT ( + (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) > 0) && + (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) / BlockSize > 0) + ); + ASSERT ( + (PcdGet32 (PcdFlashNvStorageFtwSpareSize) > 0) && + (PcdGet32 (PcdFlashNvStorageFtwSpareSize) / BlockSize > 0) + ); + + // Ensure the Variable areas are aligned on block size boundaries. + ASSERT ((PcdGet32 (PcdFlashNvStorageVariableBase) % BlockSize) == 0); + ASSERT ((PcdGet32 (PcdFlashNvStorageFtwWorkingBase) % BlockSize) == 0); + ASSERT ((PcdGet32 (PcdFlashNvStorageFtwSpareBase) % BlockSize) == 0); + + Instance->Initialized = TRUE; + mFlashNvStorageVariableBase = FixedPcdGet32 (PcdFlashNvStorageVariableBase); + + // Set the index of the first LBA for the FVB. + Instance->StartLba = (PcdGet32 (PcdFlashNvStorageVariableBase) - + Instance->RegionBaseAddress) / BlockSize; + + BootMode = GetBootModeHob (); + if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) { + Status = EFI_INVALID_PARAMETER; + } else { + // Determine if there is a valid header at the beginning of the NorFlash. + Status = ValidateFvHeader (Instance); + } + + // Install the Default FVB header if required. + if (EFI_ERROR (Status)) { + // There is no valid header, so time to install one. + DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __func__)); + DEBUG (( + DEBUG_INFO, + "%a: Installing a correct one for this volume.\n", + __func__ + )); + + // Erase all the NorFlash that is reserved for variable storage. + FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / + Instance->BlockSize; + + Status = FvbEraseBlocks ( + &Instance->FvbProtocol, + (EFI_LBA)0, + FvbNumLba, + EFI_LBA_LIST_TERMINATOR + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // Install all appropriate headers. + Status = InitializeFvAndVariableStoreHeaders (Instance); + if (EFI_ERROR (Status)) { + return Status; + } + + // validate FV header again if FV was created successfully. + Status = ValidateFvHeader (Instance); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ValidateFvHeader is failed \n")); + return Status; + } + } + + // The driver implementing the variable read service can now be dispatched; + // the varstore headers are in place. + Status = gBS->InstallProtocolInterface ( + &gImageHandle, + &gEdkiiNvVarStoreFormattedGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: Failed to install gEdkiiNvVarStoreFormattedGuid\n", + __func__ + )); + return Status; + } + + // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME. + RuntimeMmioRegionSize = Instance->Size; + RuntimeMmioDeviceSize = Instance->RegionBaseAddress - Instance->DeviceBaseAddress; + + Status = gDS->AddMemorySpace ( + EfiGcdMemoryTypeMemoryMappedIo, + Instance->RegionBaseAddress, + RuntimeMmioRegionSize, + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + + Status = gDS->AddMemorySpace ( + EfiGcdMemoryTypeMemoryMappedIo, + Instance->DeviceBaseAddress, + RuntimeMmioDeviceSize, + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + + Status = gDS->SetMemorySpaceAttributes ( + Instance->RegionBaseAddress, + RuntimeMmioRegionSize, + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + + Status = gDS->SetMemorySpaceAttributes ( + Instance->DeviceBaseAddress, + RuntimeMmioDeviceSize, + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Fixup internal data so that EFI can be called in virtual mode. + convert any pointers in lib to virtual mode. + + @param[in] Event The Event that is being processed + @param[in] Context Event Context +**/ +STATIC +VOID +EFIAPI +NorFlashVirtualNotifyEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Index; + + EfiConvertPointer (0x0, (VOID **)&mFlashNvStorageVariableBase); + + for (Index = 0; Index < mNorFlashDeviceCount; Index++) { + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->HostRegisterBaseAddress + ); + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->DeviceBaseAddress + ); + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->RegionBaseAddress + ); + + // Convert Fvb. + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks + ); + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes + ); + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize + ); + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress + ); + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Read + ); + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes + ); + EfiConvertPointer ( + 0x0, + (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Write + ); + + if (mNorFlashInstances[Index]->ShadowBuffer != NULL) { + EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->ShadowBuffer); + } + } +} + +/** + Entrypoint of Platform Nor flash DXE driver + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. +**/ +EFI_STATUS +EFIAPI +NorFlashInitialise ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS HostRegisterBaseAddress; + UINT32 Index; + NOR_FLASH_DESCRIPTION *NorFlashDevices; + BOOLEAN ContainVariableStorage; + + HostRegisterBaseAddress = PcdGet32 (PcdCadenceQspiDxeRegBaseAddress); + + Status = gDS->AddMemorySpace ( + EfiGcdMemoryTypeMemoryMappedIo, + HostRegisterBaseAddress, + SIZE_64KB, + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + + Status = gDS->SetMemorySpaceAttributes ( + HostRegisterBaseAddress, + SIZE_64KB, + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME + ); + ASSERT_EFI_ERROR (Status); + + // Initialize NOR flash instances. + Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to get Nor Flash devices\n")); + return Status; + } + + mNorFlashInstances = AllocateRuntimePool ( + sizeof (NOR_FLASH_INSTANCE *) * + mNorFlashDeviceCount + ); + + if (mNorFlashInstances == NULL) { + DEBUG (( + DEBUG_ERROR, + "NorFlashInitialise: Failed to allocate mem for NorFlashInstance\n" + )); + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < mNorFlashDeviceCount; Index++) { + // Check if this NOR Flash device contain the variable storage region. + ContainVariableStorage = + (NorFlashDevices[Index].RegionBaseAddress <= + PcdGet32 (PcdFlashNvStorageVariableBase)) && + (PcdGet32 (PcdFlashNvStorageVariableBase) + + PcdGet32 (PcdFlashNvStorageVariableSize) <= + NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size); + + Status = NorFlashCreateInstance ( + HostRegisterBaseAddress, + NorFlashDevices[Index].DeviceBaseAddress, + NorFlashDevices[Index].RegionBaseAddress, + NorFlashDevices[Index].Size, + Index, + NorFlashDevices[Index].BlockSize, + ContainVariableStorage, + &mNorFlashInstances[Index] + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "NorFlashInitialise: Fail to create instance for NorFlash[%d]\n", + Index + )); + continue; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mNorFlashInstances[Index]->Handle, + &gEfiDevicePathProtocolGuid, + &mNorFlashInstances[Index]->DevicePath, + &gEfiFirmwareVolumeBlockProtocolGuid, + &mNorFlashInstances[Index]->FvbProtocol, + NULL + ); + ASSERT_EFI_ERROR (Status); + } + + // Register for the virtual address change event. + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + NorFlashVirtualNotifyEvent, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &mNorFlashVirtualAddrChangeEvent + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Lock all pending read/write to Nor flash device + + @param[in] Context Nor flash device context structure. +**/ +VOID +EFIAPI +NorFlashLock ( + IN NOR_FLASH_LOCK_CONTEXT *Context + ) +{ + if (!EfiAtRuntime ()) { + // Raise TPL to TPL_HIGH to stop anyone from interrupting us. + Context->OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + } else { + Context->InterruptsEnabled = SaveAndDisableInterrupts (); + } +} + +/** + Unlock all pending read/write to Nor flash device + + @param[in] Context Nor flash device context structure. +**/ +VOID +EFIAPI +NorFlashUnlock ( + IN NOR_FLASH_LOCK_CONTEXT *Context + ) +{ + if (!EfiAtRuntime ()) { + // Interruptions can resume. + gBS->RestoreTPL (Context->OriginalTPL); + } else if (Context->InterruptsEnabled) { + SetInterruptState (TRUE); + } +} diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c new file mode 100644 index 000000000000..be7b626c5697 --- /dev/null +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c @@ -0,0 +1,1100 @@ +/** @file + + Copyright (c) 2023 ARM Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/NorFlashInfoLib.h> +#include <Library/PcdLib.h> +#include <Library/TimerLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> + +#include "NorFlash.h" + +STATIC CONST NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { + NOR_FLASH_SIGNATURE, // Signature + NULL, // Handle + + FALSE, // Initialized + NULL, // Initialize + + 0, // HostRegisterBaseAddress + 0, // DeviceBaseAddress + 0, // RegionBaseAddress + 0, // Size + 0, // BlockSize + 0, // LastBlock + 0, // StartLba + 0, // OffsetLba + + { + FvbGetAttributes, // GetAttributes + FvbSetAttributes, // SetAttributes + FvbGetPhysicalAddress, // GetPhysicalAddress + FvbGetBlockSize, // GetBlockSize + FvbRead, // Read + FvbWrite, // Write + FvbEraseBlocks, // EraseBlocks + NULL, // ParentHandle + }, // FvbProtoccol; + NULL, // ShadowBuffer + + { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)), + (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8) + } + }, + { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } + }, + }, + 0, // Index + + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } + } + }, // DevicePath + 0 // Flags +}; + +/** + Execute Flash cmd ctrl and Read Status. + + @param[in] Instance NOR flash Instance. + @param[in] Val Value to be written to Flash cmd ctrl Register. + + @retval EFI_SUCCESS Request is executed successfully. + +**/ +STATIC +EFI_STATUS +CdnsQspiExecuteCommand ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINT32 Val + ) +{ + // Set the command + MmioWrite32 ( + Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_CTRL_REG_OFFSET, + Val + ); + // Execute the command + MmioWrite32 ( + Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_CTRL_REG_OFFSET, + Val | CDNS_QSPI_FLASH_CMD_CTRL_REG_EXECUTE + ); + + // Wait until command has been executed + while ((MmioRead32 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_CTRL_REG_OFFSET) + & CDNS_QSPI_FLASH_CMD_CTRL_REG_STATUS_BIT) == CDNS_QSPI_FLASH_CMD_CTRL_REG_STATUS_BIT) + { + continue; + } + + return EFI_SUCCESS; +} + +/** + Create Nor flash Instance for given region. + + @param[in] HostRegisterBase Base address of Nor flash controller. + @param[in] NorFlashDeviceBase Base address of flash device. + @param[in] NorFlashRegionBase Base address of flash region on device. + @param[in] NorFlashSize Size of flash region. + @param[in] Index Index of given flash region. + @param[in] BlockSize Block size of NOR flash device. + @param[in] HasVarStore Boolean set for VarStore on given region. + @param[out] NorFlashInstance Instance of given flash region. + + @retval EFI_SUCCESS On successful creation of NOR flash instance. +**/ +EFI_STATUS +NorFlashCreateInstance ( + IN UINTN HostRegisterBase, + IN UINTN NorFlashDeviceBase, + IN UINTN NorFlashRegionBase, + IN UINTN NorFlashSize, + IN UINT32 Index, + IN UINT32 BlockSize, + IN BOOLEAN HasVarStore, + OUT NOR_FLASH_INSTANCE **NorFlashInstance + ) +{ + EFI_STATUS Status; + NOR_FLASH_INSTANCE *Instance; + NOR_FLASH_INFO *FlashInfo; + UINT8 JedecId[3]; + + ASSERT (NorFlashInstance != NULL); + Instance = AllocateRuntimeCopyPool ( + sizeof (mNorFlashInstanceTemplate), + &mNorFlashInstanceTemplate + ); + if (Instance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Instance->HostRegisterBaseAddress = HostRegisterBase; + Instance->DeviceBaseAddress = NorFlashDeviceBase; + Instance->RegionBaseAddress = NorFlashRegionBase; + Instance->Size = NorFlashSize; + Instance->BlockSize = BlockSize; + Instance->LastBlock = (NorFlashSize / BlockSize) - 1; + + Instance->OffsetLba = (NorFlashRegionBase - NorFlashDeviceBase) / BlockSize; + + CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid); + Instance->DevicePath.Index = (UINT8)Index; + + Status = NorFlashReadID (Instance, JedecId); + if (EFI_ERROR (Status)) { + goto FreeInstance; + } + + Status = NorFlashGetInfo (JedecId, &FlashInfo, TRUE); + if (EFI_ERROR (Status)) { + goto FreeInstance; + } + + NorFlashPrintInfo (FlashInfo); + + Instance->Flags = 0; + if (FlashInfo->Flags & NOR_FLASH_WRITE_FSR) { + Instance->Flags = NOR_FLASH_POLL_FSR; + } + + Instance->ShadowBuffer = AllocateRuntimePool (BlockSize); + if (Instance->ShadowBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreeInstance; + } + + if (HasVarStore) { + Instance->Initialize = NorFlashFvbInitialize; + } + + *NorFlashInstance = Instance; + FreePool (FlashInfo); + return EFI_SUCCESS; + +FreeInstance: + FreePool (Instance); + return Status; +} + +/** + Converts milliseconds into number of ticks of the performance counter. + + @param[in] Milliseconds Milliseconds to convert into ticks. + + @retval Milliseconds expressed as number of ticks. + +**/ +STATIC +UINT64 +MilliSecondsToTicks ( + IN UINTN Milliseconds + ) +{ + CONST UINT64 NanoSecondsPerTick = GetTimeInNanoSecond (1); + + return (Milliseconds * 1000000) / NanoSecondsPerTick; +} + +/** + Poll Status register for NOR flash erase/write completion. + + @param[in] Instance NOR flash Instance. + + @retval EFI_SUCCESS Request is executed successfully. + @retval EFI_TIMEOUT Operation timed out. + @retval EFI_DEVICE_ERROR Controller operartion failed. + +**/ +STATIC +EFI_STATUS +NorFlashPollStatusRegister ( + IN NOR_FLASH_INSTANCE *Instance + ) +{ + BOOLEAN SRegDone; + UINT32 val; + + val = SPINOR_OP_RDSR << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS | + CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_ENABLE << CDNS_QSPI_FLASH_CMD_CTRL_REG_READEN_BIT_POS | + CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_DATA_BYTES (1) | + CDNS_QSPI_FLASH_CMD_CTRL_REG_DUMMY_8C << CDNS_QSPI_FLASH_CMD_CTRL_REG_DUMMY_BIT_POS; + + CONST UINT64 TickOut = + GetPerformanceCounter () + MilliSecondsToTicks (SPINOR_SR_WIP_POLL_TIMEOUT_MS); + + do { + if (GetPerformanceCounter () > TickOut) { + DEBUG (( + DEBUG_ERROR, + "NorFlashPollStatusRegister: Timeout waiting for erase/write.\n" + )); + return EFI_TIMEOUT; + } + + if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, val))) { + return EFI_DEVICE_ERROR; + } + + SRegDone = + (MmioRead8 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_READ_DATA_REG_OFFSET) + & SPINOR_SR_WIP) == 0; + } while (!SRegDone); + + return EFI_SUCCESS; +} + +/** + Check whether NOR flash opertions are Locked. + + @param[in] Instance NOR flash Instance. + @param[in] BlockAddress BlockAddress in NOR flash device. + + @retval FALSE If NOR flash is not locked. +**/ +STATIC +BOOLEAN +NorFlashBlockIsLocked ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + return FALSE; +} + +/** + Unlock NOR flash operations on given block. + + @param[in] Instance NOR flash instance. + @param[in] BlockAddress BlockAddress in NOR flash device. + + @retval EFI_SUCCESS NOR flash operations is unlocked. +**/ +STATIC +EFI_STATUS +NorFlashUnlockSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + return EFI_SUCCESS; +} + +/** + Unlock NOR flash operations if it is necessary. + + @param[in] Instance NOR flash instance. + @param[in] BlockAddress BlockAddress in NOR flash device. + + @retval EFI_SUCCESS Request is executed successfully. +**/ +STATIC +EFI_STATUS +NorFlashUnlockSingleBlockIfNecessary ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (!NorFlashBlockIsLocked (Instance, BlockAddress)) { + Status = NorFlashUnlockSingleBlock (Instance, BlockAddress); + } + + return Status; +} + +/** + Enable write to NOR flash device. + + @param[in] Instance NOR flash instance. + + @retval EFI_SUCCESS Request is executed successfully. +**/ +STATIC +EFI_STATUS +NorFlashEnableWrite ( + IN NOR_FLASH_INSTANCE *Instance + ) +{ + UINT32 val; + + DEBUG ((DEBUG_INFO, "NorFlashEnableWrite()\n")); + val = (SPINOR_OP_WREN << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS); + if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, val))) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + The following function presumes that the block has already been unlocked. + + @param[in] Instance NOR flash instance. + @param[in] BlockAddress Block address within the variable region. + + @retval EFI_SUCCESS Request is executed successfully. + **/ +EFI_STATUS +NorFlashEraseSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + UINT32 DevConfigVal; + UINT32 EraseOffset; + + EraseOffset = 0x0; + + DEBUG (( + DEBUG_INFO, + "NorFlashEraseSingleBlock(BlockAddress=0x%08x)\n", + BlockAddress + )); + + if (EFI_ERROR (NorFlashEnableWrite (Instance))) { + return EFI_DEVICE_ERROR; + } + + EraseOffset = BlockAddress - Instance->DeviceBaseAddress; + + MmioWrite32 ( + Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_ADDR_REG_OFFSET, + EraseOffset + ); + + DevConfigVal = SPINOR_OP_BE_4K << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS | + CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_ENABLE << CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BIT_POS | + CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_ADDR_BYTES (3); + + if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, DevConfigVal))) { + return EFI_DEVICE_ERROR; + } + + if (EFI_ERROR (NorFlashPollStatusRegister (Instance))) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + This function unlock and erase an entire NOR Flash block. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] BlockAddress Block address within the variable store region. + + @retval EFI_SUCCESS The erase and unlock successfully completed. +**/ +EFI_STATUS +NorFlashUnlockAndEraseSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + EFI_STATUS Status; + UINTN Index; + NOR_FLASH_LOCK_CONTEXT Lock; + + NorFlashLock (&Lock); + + Index = 0; + do { + // Unlock the block if we have to + Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); + if (EFI_ERROR (Status)) { + break; + } + + Status = NorFlashEraseSingleBlock (Instance, BlockAddress); + if (EFI_ERROR (Status)) { + break; + } + + Index++; + } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED)); + + if (Index == NOR_FLASH_ERASE_RETRY) { + DEBUG (( + DEBUG_ERROR, + "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", + BlockAddress, + Index + )); + } + + NorFlashUnlock (&Lock); + + return Status; +} + +/** + Write a single word to given location. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] WordAddress The address in NOR flash to write given word. + @param[in] WriteData The data to write into NOR flash location. + + @retval EFI_SUCCESS The write is completed. +**/ +STATIC +EFI_STATUS +NorFlashWriteSingleWord ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN WordAddress, + IN UINT32 WriteData + ) +{ + DEBUG (( + DEBUG_INFO, + "NorFlashWriteSingleWord(WordAddress=0x%08x, WriteData=0x%08x)\n", + WordAddress, + WriteData + )); + + if (EFI_ERROR (NorFlashEnableWrite (Instance))) { + return EFI_DEVICE_ERROR; + } + + MmioWrite32 (WordAddress, WriteData); + if (EFI_ERROR (NorFlashPollStatusRegister (Instance))) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Write a full block to given location. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The logical block address in NOR flash. + @param[in] DataBuffer The data to write into NOR flash location. + @param[in] BlockSizeInWords The number of bytes to write. + + @retval EFI_SUCCESS The write is completed. +**/ +STATIC +EFI_STATUS +NorFlashWriteFullBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINT32 *DataBuffer, + IN UINT32 BlockSizeInWords + ) +{ + EFI_STATUS Status; + UINTN WordAddress; + UINT32 WordIndex; + UINTN BlockAddress; + NOR_FLASH_LOCK_CONTEXT Lock; + + Status = EFI_SUCCESS; + + // Get the physical address of the block + BlockAddress = GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Lba, + BlockSizeInWords * 4 + ); + + // Start writing from the first address at the start of the block + WordAddress = BlockAddress; + + NorFlashLock (&Lock); + + Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", + BlockAddress + )); + goto EXIT; + } + + for (WordIndex = 0; + WordIndex < BlockSizeInWords; + WordIndex++, DataBuffer++, WordAddress += 4) + { + Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer); + if (EFI_ERROR (Status)) { + goto EXIT; + } + } + +EXIT: + NorFlashUnlock (&Lock); + + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = %r.\n", + WordAddress, + Status + )); + } + + return Status; +} + +/** + Write a full block. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The starting logical block index. + @param[in] BufferSizeInBytes The number of bytes to read. + @param[in] Buffer The pointer to a caller-allocated buffer that + contains the source for the write. + + @retval EFI_SUCCESS The write is completed. +**/ +EFI_STATUS +NorFlashWriteBlocks ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN BufferSizeInBytes, + IN VOID *Buffer + ) +{ + UINT32 *pWriteBuffer; + EFI_STATUS Status; + EFI_LBA CurrentBlock; + UINT32 BlockSizeInWords; + UINT32 NumBlocks; + UINT32 BlockCount; + + Status = EFI_SUCCESS; + // The buffer must be valid + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // We must have some bytes to read + DEBUG (( + DEBUG_INFO, + "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", + BufferSizeInBytes + )); + if (BufferSizeInBytes == 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // The size of the buffer must be a multiple of the block size + DEBUG (( + DEBUG_INFO, + "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", + Instance->BlockSize + )); + if ((BufferSizeInBytes % Instance->BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // All blocks must be within the device + NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize; + + DEBUG (( + DEBUG_INFO, + "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", + NumBlocks, + Instance->LastBlock, + Lba + )); + + if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) { + DEBUG (( + DEBUG_ERROR, + "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n" + )); + return EFI_INVALID_PARAMETER; + } + + ASSERT (((UINTN)Buffer % sizeof (UINT32)) == 0); + + BlockSizeInWords = Instance->BlockSize / 4; + + // Because the target *Buffer is a pointer to VOID, we must put + // all the data into a pointer to a proper data type, so use *ReadBuffer + pWriteBuffer = (UINT32 *)Buffer; + + CurrentBlock = Lba; + for (BlockCount = 0; + BlockCount < NumBlocks; + BlockCount++, CurrentBlock++, pWriteBuffer += BlockSizeInWords) + { + DEBUG (( + DEBUG_INFO, + "NorFlashWriteBlocks: Writing block #%d\n", + (UINTN)CurrentBlock + )); + + Status = NorFlashWriteFullBlock ( + Instance, + CurrentBlock, + pWriteBuffer, + BlockSizeInWords + ); + + if (EFI_ERROR (Status)) { + break; + } + } + + DEBUG ((DEBUG_INFO, "NorFlashWriteBlocks: Exit Status = %r.\n", Status)); + return Status; +} + +/** + Read a full block. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The starting logical block index to read from. + @param[in] BufferSizeInBytes The number of bytes to read. + @param[out] Buffer The pointer to a caller-allocated buffer that + should be copied with read data. + + @retval EFI_SUCCESS The read is completed. +**/ +EFI_STATUS +NorFlashReadBlocks ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN BufferSizeInBytes, + OUT VOID *Buffer + ) +{ + UINT32 NumBlocks; + UINTN StartAddress; + + DEBUG (( + DEBUG_INFO, + "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n", + BufferSizeInBytes, + Instance->BlockSize, + Instance->LastBlock, + Lba + )); + + // The buffer must be valid + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Return if we do not have any byte to read + if (BufferSizeInBytes == 0) { + return EFI_SUCCESS; + } + + // The size of the buffer must be a multiple of the block size + if ((BufferSizeInBytes % Instance->BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize; + + if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) { + DEBUG (( + DEBUG_ERROR, + "NorFlashReadBlocks: ERROR - Read will exceed last block\n" + )); + return EFI_INVALID_PARAMETER; + } + + // Get the address to start reading from + StartAddress = GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Lba, + Instance->BlockSize + ); + + // Readout the data + CopyMem (Buffer, (UINTN *)StartAddress, BufferSizeInBytes); + + return EFI_SUCCESS; +} + +/** + Read from nor flash. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The starting logical block index to read from. + @param[in] Offset Offset into the block at which to begin reading. + @param[in] BufferSizeInBytes The number of bytes to read. + @param[out] Buffer The pointer to a caller-allocated buffer that + should copied with read data. + + @retval EFI_SUCCESS The read is completed. +**/ +EFI_STATUS +NorFlashRead ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN BufferSizeInBytes, + OUT VOID *Buffer + ) +{ + UINTN StartAddress; + + // The buffer must be valid + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Return if we do not have any byte to read + if (BufferSizeInBytes == 0) { + return EFI_SUCCESS; + } + + if (((Lba * Instance->BlockSize) + Offset + BufferSizeInBytes) > + Instance->Size) + { + DEBUG (( + DEBUG_ERROR, + "NorFlashRead: ERROR - Read will exceed device size.\n" + )); + return EFI_INVALID_PARAMETER; + } + + // Get the address to start reading from + StartAddress = GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Lba, + Instance->BlockSize + ); + + // Readout the data + CopyMem (Buffer, (UINTN *)(StartAddress + Offset), BufferSizeInBytes); + + return EFI_SUCCESS; +} + +/** + Write a full or portion of a block. + + @param[in] Instance NOR flash Instance of variable store region. + @param[in] Lba The starting logical block index to write to. + @param[in] Offset Offset into the block at which to begin writing. + @param[in, out] NumBytes The total size of the buffer. + @param[in] Buffer The pointer to a caller-allocated buffer that + contains the source for the write. + + @retval EFI_SUCCESS The write is completed. +**/ +EFI_STATUS +NorFlashWriteSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN Offset, + IN OUT UINTN *NumBytes, + IN UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINT32 Tmp; + UINT32 TmpBuf; + UINT32 WordToWrite; + UINT32 Mask; + BOOLEAN DoErase; + UINTN BytesToWrite; + UINTN CurOffset; + UINTN WordAddr; + UINTN BlockSize; + UINTN BlockAddress; + UINTN PrevBlockAddress; + + if (Buffer == NULL) { + DEBUG (( + DEBUG_ERROR, + "NorFlashWriteSingleBlock: ERROR - Buffer is invalid\n" + )); + return EFI_OUT_OF_RESOURCES; + } + + PrevBlockAddress = 0; + if (!Instance->Initialized && Instance->Initialize) { + Instance->Initialize (Instance); + } + + DEBUG (( + DEBUG_INFO, + "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", + Lba, + Offset, + *NumBytes, + Buffer + )); + + // Localise the block size to avoid de-referencing pointers all the time + BlockSize = Instance->BlockSize; + + // The write must not span block boundaries. + // We need to check each variable individually because adding two large + // values together overflows. + if ((Offset >= BlockSize) || + (*NumBytes > BlockSize) || + ((Offset + *NumBytes) > BlockSize)) + { + DEBUG (( + DEBUG_ERROR, + "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", + Offset, + *NumBytes, + BlockSize + )); + return EFI_BAD_BUFFER_SIZE; + } + + // We must have some bytes to write + if (*NumBytes == 0) { + DEBUG (( + DEBUG_ERROR, + "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", + Offset, + *NumBytes, + BlockSize + )); + return EFI_BAD_BUFFER_SIZE; + } + + // Pick 128bytes as a good start for word operations as opposed to erasing the + // block and writing the data regardless if an erase is really needed. + // It looks like most individual NV variable writes are smaller than 128bytes. + if (*NumBytes <= 128) { + // Check to see if we need to erase before programming the data into NOR. + // If the destination bits are only changing from 1s to 0s we can just write. + // After a block is erased all bits in the block is set to 1. + // If any byte requires us to erase we just give up and rewrite all of it. + DoErase = FALSE; + BytesToWrite = *NumBytes; + CurOffset = Offset; + + while (BytesToWrite > 0) { + // Read full word from NOR, splice as required. A word is the smallest + // unit we can write. + Status = NorFlashRead ( + Instance, + Lba, + CurOffset & ~(0x3), + sizeof (Tmp), + &Tmp + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // Physical address of word in NOR to write. + WordAddr = (CurOffset & ~(0x3)) + + GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Lba, + BlockSize + ); + + // The word of data that is to be written. + TmpBuf = ReadUnaligned32 ((UINT32 *)(Buffer + (*NumBytes - BytesToWrite))); + + // First do word aligned chunks. + if ((CurOffset & 0x3) == 0) { + if (BytesToWrite >= 4) { + // Is the destination still in 'erased' state? + if (~Tmp != 0) { + // Check to see if we are only changing bits to zero. + if ((Tmp ^ TmpBuf) & TmpBuf) { + DoErase = TRUE; + break; + } + } + + // Write this word to NOR + WordToWrite = TmpBuf; + CurOffset += sizeof (TmpBuf); + BytesToWrite -= sizeof (TmpBuf); + } else { + // BytesToWrite < 4. Do small writes and left-overs + Mask = ~((~0) << (BytesToWrite * 8)); + // Mask out the bytes we want. + TmpBuf &= Mask; + // Is the destination still in 'erased' state? + if ((Tmp & Mask) != Mask) { + // Check to see if we are only changing bits to zero. + if ((Tmp ^ TmpBuf) & TmpBuf) { + DoErase = TRUE; + break; + } + } + + // Merge old and new data. Write merged word to NOR + WordToWrite = (Tmp & ~Mask) | TmpBuf; + CurOffset += BytesToWrite; + BytesToWrite = 0; + } + } else { + // Do multiple words, but starting unaligned. + if (BytesToWrite > (4 - (CurOffset & 0x3))) { + Mask = ((~0) << ((CurOffset & 0x3) * 8)); + // Mask out the bytes we want. + TmpBuf &= Mask; + // Is the destination still in 'erased' state? + if ((Tmp & Mask) != Mask) { + // Check to see if we are only changing bits to zero. + if ((Tmp ^ TmpBuf) & TmpBuf) { + DoErase = TRUE; + break; + } + } + + // Merge old and new data. Write merged word to NOR + WordToWrite = (Tmp & ~Mask) | TmpBuf; + BytesToWrite -= (4 - (CurOffset & 0x3)); + CurOffset += (4 - (CurOffset & 0x3)); + } else { + // Unaligned and fits in one word. + Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8); + // Mask out the bytes we want. + TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask; + // Is the destination still in 'erased' state? + if ((Tmp & Mask) != Mask) { + // Check to see if we are only changing bits to zero. + if ((Tmp ^ TmpBuf) & TmpBuf) { + DoErase = TRUE; + break; + } + } + + // Merge old and new data. Write merged word to NOR + WordToWrite = (Tmp & ~Mask) | TmpBuf; + CurOffset += BytesToWrite; + BytesToWrite = 0; + } + } + + BlockAddress = GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Lba, + BlockSize + ); + if (BlockAddress != PrevBlockAddress) { + Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + PrevBlockAddress = BlockAddress; + } + + Status = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + } + + // Exit if we got here and could write all the data. Otherwise do the + // Erase-Write cycle. + if (!DoErase) { + return EFI_SUCCESS; + } + } + + // Check we did get some memory. Buffer is BlockSize. + if (Instance->ShadowBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n")); + return EFI_DEVICE_ERROR; + } + + // Read NOR Flash data into shadow buffer + Status = NorFlashReadBlocks ( + Instance, + Lba, + BlockSize, + Instance->ShadowBuffer + ); + if (EFI_ERROR (Status)) { + // Return one of the pre-approved error statuses + return EFI_DEVICE_ERROR; + } + + // Put the data at the appropriate location inside the buffer area + CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes); + + // Write the modified buffer back to the NorFlash + Status = NorFlashWriteBlocks ( + Instance, + Lba, + BlockSize, + Instance->ShadowBuffer + ); + if (EFI_ERROR (Status)) { + // Return one of the pre-approved error statuses + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Read JEDEC ID of NOR flash device. + + @param[in] Instance NOR flash Instance of variable store region. + @param[out] JedecId JEDEC ID of NOR flash device. + + @retval EFI_SUCCESS The write is completed. +**/ +EFI_STATUS +NorFlashReadID ( + IN NOR_FLASH_INSTANCE *Instance, + OUT UINT8 JedecId[3] + ) +{ + UINT32 val; + + if ((Instance == NULL) || (JedecId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + val = SPINOR_OP_RDID << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS | + CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_ENABLE << CDNS_QSPI_FLASH_CMD_CTRL_REG_READEN_BIT_POS | + CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_DATA_BYTES (3); + + if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, val))) { + return EFI_DEVICE_ERROR; + } + + val = MmioRead32 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_READ_DATA_REG_OFFSET); + + // Manu.ID field + JedecId[0] = (UINT8)val; + // Type field + JedecId[1] = (UINT8)(val >> 8); + // Capacity field + JedecId[2] = (UINT8)(val >> 16); + + DEBUG (( + DEBUG_INFO, + "Nor flash detected, Jedec ID, Manu.Id=%x Type=%x Capacity=%x \n", + JedecId[0], + JedecId[1], + JedecId[2] + )); + + return EFI_SUCCESS; +} diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlashFvb.c b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlashFvb.c new file mode 100644 index 000000000000..8281d4825dc9 --- /dev/null +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlashFvb.c @@ -0,0 +1,647 @@ +/** @file + + Copyright (c) 2023, ARM Limited. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Guid/VariableFormat.h> +#include <Guid/SystemNvDataGuid.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/PcdLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> + +#include <PiDxe.h> + +#include "NorFlash.h" + +UINTN mFlashNvStorageVariableBase; + +/** + Initialize the FV Header and Variable Store Header + to support variable operations. + + @param[in] Instance Location to initialise the headers. + + @retval EFI_SUCCESS Fv init is done. + +**/ +EFI_STATUS +InitializeFvAndVariableStoreHeaders ( + IN NOR_FLASH_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + VOID *Headers; + UINTN HeadersLength; + EFI_FIRMWARE_VOLUME_HEADER *FirmwareVolumeHeader; + VARIABLE_STORE_HEADER *VariableStoreHeader; + + if (!Instance->Initialized && Instance->Initialize) { + Instance->Initialize (Instance); + } + + HeadersLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + + sizeof (EFI_FV_BLOCK_MAP_ENTRY) + + sizeof (VARIABLE_STORE_HEADER); + Headers = AllocateZeroPool (HeadersLength); + + FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Headers; + CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid); + FirmwareVolumeHeader->FvLength = + PcdGet32 (PcdFlashNvStorageVariableSize) + + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + + PcdGet32 (PcdFlashNvStorageFtwSpareSize); + FirmwareVolumeHeader->Signature = EFI_FVH_SIGNATURE; + FirmwareVolumeHeader->Attributes = EFI_FVB2_READ_ENABLED_CAP | + EFI_FVB2_READ_STATUS | + EFI_FVB2_STICKY_WRITE | + EFI_FVB2_MEMORY_MAPPED | + EFI_FVB2_ERASE_POLARITY | + EFI_FVB2_WRITE_STATUS | + EFI_FVB2_WRITE_ENABLED_CAP; + + FirmwareVolumeHeader->HeaderLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + + sizeof (EFI_FV_BLOCK_MAP_ENTRY); + FirmwareVolumeHeader->Revision = EFI_FVH_REVISION; + FirmwareVolumeHeader->BlockMap[0].NumBlocks = Instance->LastBlock + 1; + FirmwareVolumeHeader->BlockMap[0].Length = Instance->BlockSize; + FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0; + FirmwareVolumeHeader->BlockMap[1].Length = 0; + FirmwareVolumeHeader->Checksum = CalculateCheckSum16 ( + (UINT16 *)FirmwareVolumeHeader, + FirmwareVolumeHeader->HeaderLength + ); + + VariableStoreHeader = (VOID *)((UINTN)Headers + + FirmwareVolumeHeader->HeaderLength); + CopyGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid); + VariableStoreHeader->Size = PcdGet32 (PcdFlashNvStorageVariableSize) - + FirmwareVolumeHeader->HeaderLength; + VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED; + VariableStoreHeader->State = VARIABLE_STORE_HEALTHY; + + // Install the combined super-header in the NorFlash + Status = FvbWrite (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers); + + FreePool (Headers); + return Status; +} + +/** + Check the integrity of firmware volume header. + + @param[in] Instance Instance of Nor flash variable region. + + @retval EFI_SUCCESS The firmware volume is consistent. + @retval EFI_NOT_FOUND The firmware volume has been corrupted. + +**/ +EFI_STATUS +ValidateFvHeader ( + IN NOR_FLASH_INSTANCE *Instance + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + VARIABLE_STORE_HEADER *VariableStoreHeader; + UINTN VariableStoreLength; + UINTN FvLength; + + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Instance->RegionBaseAddress; + + FvLength = PcdGet32 (PcdFlashNvStorageVariableSize) + + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + + PcdGet32 (PcdFlashNvStorageFtwSpareSize); + + if ( (FwVolHeader->Revision != EFI_FVH_REVISION) + || (FwVolHeader->Signature != EFI_FVH_SIGNATURE) + || (FwVolHeader->FvLength != FvLength) + ) + { + DEBUG (( + DEBUG_ERROR, + "%a: No Firmware Volume header present\n", + __func__ + )); + return EFI_NOT_FOUND; + } + + // Check the Firmware Volume Guid + if (!CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid)) { + DEBUG (( + DEBUG_ERROR, + "%a: Firmware Volume Guid non-compatible\n", + __func__ + )); + return EFI_NOT_FOUND; + } + + VariableStoreHeader = (VOID *)((UINTN)FwVolHeader + + FwVolHeader->HeaderLength); + + // Check the Variable Store Guid + if (!CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) && + !CompareGuid ( + &VariableStoreHeader->Signature, + &gEfiAuthenticatedVariableGuid + )) + { + DEBUG (( + DEBUG_ERROR, + "%a: Variable Store Guid non-compatible\n", + __func__ + )); + return EFI_NOT_FOUND; + } + + VariableStoreLength = PcdGet32 (PcdFlashNvStorageVariableSize) - + FwVolHeader->HeaderLength; + if (VariableStoreHeader->Size != VariableStoreLength) { + DEBUG (( + DEBUG_ERROR, + "%a: Variable Store Length does not match\n", + __func__ + )); + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + Retrieves the attributes and current settings of the block. + + @param[in] This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[out] Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes and + current settings are returned. + Type EFI_FVB_ATTRIBUTES_2 is defined in + EFI_FIRMWARE_VOLUME_HEADER. + + @retval EFI_SUCCESS The firmware volume attributes were returned. + +**/ +EFI_STATUS +EFIAPI +FvbGetAttributes ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + OUT EFI_FVB_ATTRIBUTES_2 *Attributes + ) +{ + EFI_FVB_ATTRIBUTES_2 FlashFvbAttributes; + + FlashFvbAttributes = EFI_FVB2_READ_ENABLED_CAP | EFI_FVB2_READ_STATUS | + EFI_FVB2_WRITE_ENABLED_CAP | EFI_FVB2_WRITE_STATUS | + EFI_FVB2_STICKY_WRITE | EFI_FVB2_MEMORY_MAPPED | + EFI_FVB2_ERASE_POLARITY; + + *Attributes = FlashFvbAttributes; + + DEBUG ((DEBUG_INFO, "FvbGetAttributes(0x%X)\n", *Attributes)); + + return EFI_SUCCESS; +} + +/** + Sets configurable firmware volume attributes and returns the + new settings of the firmware volume. + + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[in, out] Attributes On input, Attributes is a pointer to + EFI_FVB_ATTRIBUTES_2 that contains the desired + firmware volume settings. + On successful return, it contains the new + settings of the firmware volume. + + @retval EFI_UNSUPPORTED The firmware volume attributes are not supported. + +**/ +EFI_STATUS +EFIAPI +FvbSetAttributes ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes + ) +{ + DEBUG (( + DEBUG_INFO, + "FvbSetAttributes(0x%X) is not supported\n", + *Attributes + )); + return EFI_UNSUPPORTED; +} + +/** + Retrieves the base address of a memory-mapped firmware volume. + This function should be called only for memory-mapped firmware volumes. + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[out] 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. + +**/ +EFI_STATUS +EFIAPI +FvbGetPhysicalAddress ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + OUT EFI_PHYSICAL_ADDRESS *Address + ) +{ + NOR_FLASH_INSTANCE *Instance; + + Instance = INSTANCE_FROM_FVB_THIS (This); + + DEBUG (( + DEBUG_INFO, + "FvbGetPhysicalAddress(BaseAddress=0x%08x)\n", + Instance->RegionBaseAddress + )); + + ASSERT (Address != NULL); + + *Address = Instance->RegionBaseAddress; + return EFI_SUCCESS; +} + +/** + 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[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[in] Lba Indicates the block whose size to return + + @param[out] BlockSize Pointer to a caller-allocated UINTN in which + the size of the block is returned. + + @param[out] NumberOfBlocks Pointer to a caller-allocated UINTN in + which the number of consecutive blocks, + starting with Lba, is returned. All + blocks in this range have a size of + BlockSize. + + @retval EFI_SUCCESS The firmware volume base address was returned. + + @retval EFI_INVALID_PARAMETER The requested LBA is out of range. + +**/ +EFI_STATUS +EFIAPI +FvbGetBlockSize ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + IN EFI_LBA Lba, + OUT UINTN *BlockSize, + OUT UINTN *NumberOfBlocks + ) +{ + EFI_STATUS Status; + NOR_FLASH_INSTANCE *Instance; + + Instance = INSTANCE_FROM_FVB_THIS (This); + + DEBUG (( + DEBUG_INFO, + "FvbGetBlockSize(Lba=%ld, BlockSize=0x%x, LastBlock=%ld)\n", + Lba, + Instance->BlockSize, + Instance->LastBlock + )); + + if (Lba > Instance->LastBlock) { + DEBUG (( + DEBUG_ERROR, + "FvbGetBlockSize: ERROR - Parameter LBA %ld is beyond the last Lba (%ld).\n", + Lba, + Instance->LastBlock + )); + Status = EFI_INVALID_PARAMETER; + } else { + // This is easy because in this platform each NorFlash device has equal sized blocks. + *BlockSize = (UINTN)Instance->BlockSize; + *NumberOfBlocks = (UINTN)(Instance->LastBlock - Lba + 1); + + DEBUG (( + DEBUG_INFO, + "FvbGetBlockSize: *BlockSize=0x%x, *NumberOfBlocks=0x%x.\n", + *BlockSize, + *NumberOfBlocks + )); + + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + 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. + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[in] Lba The starting logical block index from which to read + + @param[in] Offset Offset into the block at which to begin reading. + + @param[in, out] NumBytes Pointer to a UINTN. + At entry, *NumBytes contains the total size of the + buffer. *NumBytes should have a non zero value. + At exit, *NumBytes contains the total number of + bytes read. + + @param[in, out] 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. + + @retval EFI_DEVICE_ERROR The block device is not functioning correctly and + could not be read. + +**/ +EFI_STATUS +EFIAPI +FvbRead ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN OUT UINTN *NumBytes, + IN OUT UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINTN BlockSize; + NOR_FLASH_INSTANCE *Instance; + + Instance = INSTANCE_FROM_FVB_THIS (This); + + DEBUG (( + DEBUG_INFO, + "FvbRead(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", + Instance->StartLba + Lba, + Offset, + *NumBytes, + Buffer + )); + + if (!Instance->Initialized && Instance->Initialize) { + Instance->Initialize (Instance); + } + + BlockSize = Instance->BlockSize; + + DEBUG (( + DEBUG_INFO, + "FvbRead: Check if (Offset=0x%x + NumBytes=0x%x) <= BlockSize=0x%x\n", + Offset, + *NumBytes, + BlockSize + )); + + // The read must not span block boundaries. + // We need to check each variable individually because adding two large + // values together overflows. + if ((Offset >= BlockSize) || + (*NumBytes > BlockSize) || + ((Offset + *NumBytes) > BlockSize)) + { + DEBUG (( + DEBUG_ERROR, + "FvbRead: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", + Offset, + *NumBytes, + BlockSize + )); + return EFI_BAD_BUFFER_SIZE; + } + + // We must have some bytes to read + if (*NumBytes == 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // Decide if we are doing full block reads or not. + if (*NumBytes % BlockSize != 0) { + Status = NorFlashRead ( + Instance, + Instance->StartLba + Lba, + Offset, + *NumBytes, + Buffer + ); + } else { + // Read NOR Flash data into shadow buffer + Status = NorFlashReadBlocks ( + Instance, + Instance->StartLba + Lba, + BlockSize, + Buffer + ); + } + + if (EFI_ERROR (Status)) { + // Return one of the pre-approved error statuses + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Writes the specified number of bytes from the input buffer to the block. + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. + + @param[in] Lba The starting logical block index to write to. + + @param[in] Offset Offset into the block at which to begin writing. + + @param[in, out] 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[in] Buffer The pointer to a caller-allocated buffer that + contains the source for the write. + + @retval EFI_SUCCESS The firmware volume was written successfully. + +**/ +EFI_STATUS +EFIAPI +FvbWrite ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Offset, + IN OUT UINTN *NumBytes, + IN UINT8 *Buffer + ) +{ + NOR_FLASH_INSTANCE *Instance; + + Instance = INSTANCE_FROM_FVB_THIS (This); + + return NorFlashWriteSingleBlock ( + Instance, + Instance->StartLba + Lba, + Offset, + NumBytes, + Buffer + ); +} + +/** + Erases and initialises a firmware volume block. + + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL + + @param[in] ... The variable argument list is a list of tuples. + Each tuple describes a range of LBAs to erase + and consists of the following: + - An EFI_LBA that indicates the starting LBA + - A UINTN that indicates the number of blocks + to erase. + + The list is terminated with an + EFI_LBA_LIST_TERMINATOR. + + @retval EFI_SUCCESS The erase request successfully completed. + + @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled + state. + + @retval EFI_DEVICE_ERROR The block device is not functioning correctly + and could not be written. + The firmware device may have been partially + erased. + + @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable + argument list do not exist in the firmware + volume. + +**/ +EFI_STATUS +EFIAPI +FvbEraseBlocks ( + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, + ... + ) +{ + EFI_STATUS Status; + VA_LIST Args; + UINTN BlockAddress; // Physical address of Lba to erase + EFI_LBA StartingLba; // Lba from which we start erasing + UINTN NumOfLba; // Number of Lba blocks to erase + NOR_FLASH_INSTANCE *Instance; + + Instance = INSTANCE_FROM_FVB_THIS (This); + + DEBUG ((DEBUG_INFO, "FvbEraseBlocks()\n")); + + Status = EFI_SUCCESS; + + // Before erasing, check the entire list of parameters to ensure + // all specified blocks are valid + + VA_START (Args, This); + do { + // Get the Lba from which we start erasing + StartingLba = VA_ARG (Args, EFI_LBA); + + // Have we reached the end of the list? + if (StartingLba == EFI_LBA_LIST_TERMINATOR) { + break; + } + + // How many Lba blocks are we requested to erase? + NumOfLba = VA_ARG (Args, UINT32); + + // All blocks must be within range + DEBUG (( + DEBUG_INFO, + "FvbEraseBlocks: Check if: ( StartingLba=%ld + NumOfLba=%d - 1 ) > LastBlock=%ld.\n", + Instance->StartLba + StartingLba, + NumOfLba, + Instance->LastBlock + )); + if ((NumOfLba == 0) || + ((Instance->StartLba + StartingLba + NumOfLba - 1) > + Instance->LastBlock)) + { + VA_END (Args); + DEBUG (( + DEBUG_ERROR, + "FvbEraseBlocks: ERROR - Lba range goes past the last Lba.\n" + )); + return EFI_INVALID_PARAMETER; + } + } while (TRUE); + + VA_END (Args); + + VA_START (Args, This); + do { + // Get the Lba from which we start erasing + StartingLba = VA_ARG (Args, EFI_LBA); + + // Have we reached the end of the list? + if (StartingLba == EFI_LBA_LIST_TERMINATOR) { + // Exit the while loop + break; + } + + // How many Lba blocks are we requested to erase? + NumOfLba = VA_ARG (Args, UINT32); + + // Go through each one and erase it + while (NumOfLba > 0) { + // Get the physical address of Lba to erase + BlockAddress = GET_NOR_BLOCK_ADDRESS ( + Instance->RegionBaseAddress, + Instance->StartLba + StartingLba, + Instance->BlockSize + ); + + // Erase it + DEBUG (( + DEBUG_INFO, + "FvbEraseBlocks: Erasing Lba=%ld @ 0x%08x.\n", + Instance->StartLba + StartingLba, + BlockAddress + )); + Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress); + if (EFI_ERROR (Status)) { + VA_END (Args); + return EFI_DEVICE_ERROR; + } + + // Move to the next Lba + StartingLba++; + NumOfLba--; + } + } while (TRUE); + + VA_END (Args); + + return Status; +} -- 2.25.1 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#111311): https://edk2.groups.io/g/devel/message/111311 Mute This Topic: https://groups.io/mt/102625036/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-