mirror of
https://github.com/acidanthera/audk.git
synced 2025-08-17 23:58:12 +02:00
Commit 8015f3f6d4005d83 ("ArmPlatformPkg: Enable support for flash in 64-bit address space") updated the NorFlash DXE and StMM drivers to take alternate PCDs into account when discovering the base of the NOR flash regions. This introduced a disparity between the declarations of the PCD references in the .INF files, which permits the use of dynamic PCDs, and the code itself, which now uses FixedPcdGet() accessors. On platforms that actually use dynamic PCDs, this results in a build error. So let's clean this up: - for the DXE version, use the generic PcdGet() accessors, so dynamic PCDs are permitted - for the standalone MM version, redeclare the PCDs as [FixedPcd] in the .INF description, and switch to the FixedPcdGet() accessors. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@arm.com> Acked-by: Laszlo Ersek <lersek@redhat.com> Tested-by: Vijayenthiran Subramaniam <vijayenthiran.subramaniam@arm.com>
375 lines
12 KiB
C
375 lines
12 KiB
C
/** @file NorFlashStandaloneMm.c
|
|
|
|
Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
|
|
Copyright (c) 2020, Linaro, Ltd. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/MmServicesTableLib.h>
|
|
|
|
#include "NorFlash.h"
|
|
|
|
//
|
|
// Global variable declarations
|
|
//
|
|
NOR_FLASH_INSTANCE **mNorFlashInstances;
|
|
UINT32 mNorFlashDeviceCount;
|
|
UINTN mFlashNvStorageVariableBase;
|
|
|
|
NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = {
|
|
NOR_FLASH_SIGNATURE, // Signature
|
|
NULL, // Handle ... NEED TO BE FILLED
|
|
|
|
0, // DeviceBaseAddress ... NEED TO BE FILLED
|
|
0, // RegionBaseAddress ... NEED TO BE FILLED
|
|
0, // Size ... NEED TO BE FILLED
|
|
0, // StartLba
|
|
|
|
{
|
|
EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision
|
|
NULL, // Media ... NEED TO BE FILLED
|
|
NULL, // Reset;
|
|
NULL, // ReadBlocks
|
|
NULL, // WriteBlocks
|
|
NULL // FlushBlocks
|
|
}, // BlockIoProtocol
|
|
|
|
{
|
|
0, // MediaId ... NEED TO BE FILLED
|
|
FALSE, // RemovableMedia
|
|
TRUE, // MediaPresent
|
|
FALSE, // LogicalPartition
|
|
FALSE, // ReadOnly
|
|
FALSE, // WriteCaching;
|
|
0, // BlockSize ... NEED TO BE FILLED
|
|
4, // IoAlign
|
|
0, // LastBlock ... NEED TO BE FILLED
|
|
0, // LowestAlignedLba
|
|
1, // LogicalBlocksPerPhysicalBlock
|
|
}, //Media;
|
|
|
|
{
|
|
EFI_DISK_IO_PROTOCOL_REVISION, // Revision
|
|
NULL, // ReadDisk
|
|
NULL // WriteDisk
|
|
},
|
|
|
|
{
|
|
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 } }, // GUID ... NEED TO BE FILLED
|
|
},
|
|
0, // Index
|
|
{
|
|
END_DEVICE_PATH_TYPE,
|
|
END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
{ sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
|
|
}
|
|
} // DevicePath
|
|
};
|
|
|
|
EFI_STATUS
|
|
NorFlashCreateInstance (
|
|
IN UINTN NorFlashDeviceBase,
|
|
IN UINTN NorFlashRegionBase,
|
|
IN UINTN NorFlashSize,
|
|
IN UINT32 Index,
|
|
IN UINT32 BlockSize,
|
|
IN BOOLEAN SupportFvb,
|
|
OUT NOR_FLASH_INSTANCE** NorFlashInstance
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
NOR_FLASH_INSTANCE* Instance;
|
|
|
|
ASSERT(NorFlashInstance != NULL);
|
|
|
|
Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate);
|
|
if (Instance == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Instance->DeviceBaseAddress = NorFlashDeviceBase;
|
|
Instance->RegionBaseAddress = NorFlashRegionBase;
|
|
Instance->Size = NorFlashSize;
|
|
|
|
Instance->BlockIoProtocol.Media = &Instance->Media;
|
|
Instance->Media.MediaId = Index;
|
|
Instance->Media.BlockSize = BlockSize;
|
|
Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;
|
|
|
|
CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);
|
|
Instance->DevicePath.Index = (UINT8)Index;
|
|
|
|
Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);;
|
|
if (Instance->ShadowBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
if (SupportFvb) {
|
|
NorFlashFvbInitialize (Instance);
|
|
|
|
Status = gMmst->MmInstallProtocolInterface (
|
|
&Instance->Handle,
|
|
&gEfiSmmFirmwareVolumeBlockProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&Instance->FvbProtocol
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
FreePool (Instance);
|
|
return Status;
|
|
}
|
|
} else {
|
|
DEBUG((DEBUG_ERROR,"standalone MM NOR Flash driver only support FVB.\n"));
|
|
FreePool (Instance);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*NorFlashInstance = Instance;
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
* This function unlock and erase an entire NOR Flash block.
|
|
**/
|
|
EFI_STATUS
|
|
NorFlashUnlockAndEraseSingleBlock (
|
|
IN NOR_FLASH_INSTANCE *Instance,
|
|
IN UINTN BlockAddress
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
|
|
Index = 0;
|
|
// The block erase might fail a first time (SW bug ?). Retry it ...
|
|
do {
|
|
// Unlock the block if we have to
|
|
Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
Status = NorFlashEraseSingleBlock (Instance, BlockAddress);
|
|
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));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
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 BufferIndex;
|
|
UINTN BlockAddress;
|
|
UINTN BuffersInBlock;
|
|
UINTN RemainingWords;
|
|
UINTN Cnt;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
// To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
|
|
|
|
// Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
|
|
if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {
|
|
|
|
// First, break the entire block into buffer-sized chunks.
|
|
BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;
|
|
|
|
// Then feed each buffer chunk to the NOR Flash
|
|
// If a buffer does not contain any data, don't write it.
|
|
for(BufferIndex=0;
|
|
BufferIndex < BuffersInBlock;
|
|
BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS
|
|
) {
|
|
// Check the buffer to see if it contains any data (not set all 1s).
|
|
for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {
|
|
if (~DataBuffer[Cnt] != 0 ) {
|
|
// Some data found, write the buffer.
|
|
Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES,
|
|
DataBuffer);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Finally, finish off any remaining words that are less than the maximum size of the buffer
|
|
RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;
|
|
|
|
if(RemainingWords != 0) {
|
|
Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// For now, use the single word programming algorithm
|
|
// It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
|
|
// i.e. which ends in the range 0x......01 - 0x......7F.
|
|
for(WordIndex=0; WordIndex<BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {
|
|
Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);
|
|
if (EFI_ERROR(Status)) {
|
|
goto EXIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXIT:
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((DEBUG_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
NorFlashInitialise (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_MM_SYSTEM_TABLE *MmSystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Index;
|
|
NOR_FLASH_DESCRIPTION* NorFlashDevices;
|
|
BOOLEAN ContainVariableStorage;
|
|
|
|
Status = NorFlashPlatformInitialization ();
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((DEBUG_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
|
|
return Status;
|
|
}
|
|
|
|
Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG((DEBUG_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
|
|
return Status;
|
|
}
|
|
|
|
mNorFlashInstances = AllocatePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);
|
|
|
|
for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
|
|
// Check if this NOR Flash device contain the variable storage region
|
|
|
|
if (FixedPcdGet64 (PcdFlashNvStorageVariableBase64) != 0) {
|
|
ContainVariableStorage =
|
|
(NorFlashDevices[Index].RegionBaseAddress <= FixedPcdGet64 (PcdFlashNvStorageVariableBase64)) &&
|
|
(FixedPcdGet64 (PcdFlashNvStorageVariableBase64) + FixedPcdGet32 (PcdFlashNvStorageVariableSize) <=
|
|
NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
|
|
} else {
|
|
ContainVariableStorage =
|
|
(NorFlashDevices[Index].RegionBaseAddress <= FixedPcdGet32 (PcdFlashNvStorageVariableBase)) &&
|
|
(FixedPcdGet32 (PcdFlashNvStorageVariableBase) + FixedPcdGet32 (PcdFlashNvStorageVariableSize) <=
|
|
NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
|
|
}
|
|
|
|
Status = NorFlashCreateInstance (
|
|
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));
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
NorFlashFvbInitialize (
|
|
IN NOR_FLASH_INSTANCE* Instance
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 FvbNumLba;
|
|
|
|
ASSERT((Instance != NULL));
|
|
|
|
|
|
mFlashNvStorageVariableBase = (FixedPcdGet64 (PcdFlashNvStorageVariableBase64) != 0) ?
|
|
FixedPcdGet64 (PcdFlashNvStorageVariableBase64) : FixedPcdGet32 (PcdFlashNvStorageVariableBase);
|
|
// Set the index of the first LBA for the FVB
|
|
Instance->StartLba = (mFlashNvStorageVariableBase - Instance->RegionBaseAddress) / Instance->Media.BlockSize;
|
|
|
|
// 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", __FUNCTION__));
|
|
DEBUG ((DEBUG_INFO, "%a: Installing a correct one for this volume.\n",
|
|
__FUNCTION__));
|
|
|
|
// Erase all the NorFlash that is reserved for variable storage
|
|
FvbNumLba = (PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) + PcdGet32(PcdFlashNvStorageFtwSpareSize)) / Instance->Media.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;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|