+++ /dev/null
-/** @file NorFlashDxe.c\r
-\r
- Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.<BR>\r
-\r
- SPDX-License-Identifier: BSD-2-Clause-Patent\r
-\r
-**/\r
-\r
-#include <Library/UefiLib.h>\r
-#include <Library/BaseMemoryLib.h>\r
-#include <Library/MemoryAllocationLib.h>\r
-#include <Library/UefiBootServicesTableLib.h>\r
-#include <Library/PcdLib.h>\r
-#include <Library/HobLib.h>\r
-#include <Library/DxeServicesTableLib.h>\r
-\r
-#include "NorFlash.h"\r
-\r
-STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent;\r
-\r
-//\r
-// Global variable declarations\r
-//\r
-NOR_FLASH_INSTANCE **mNorFlashInstances;\r
-UINT32 mNorFlashDeviceCount;\r
-UINTN mFlashNvStorageVariableBase;\r
-EFI_EVENT mFvbVirtualAddrChangeEvent;\r
-\r
-NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = {\r
- NOR_FLASH_SIGNATURE, // Signature\r
- NULL, // Handle ... NEED TO BE FILLED\r
-\r
- 0, // DeviceBaseAddress ... NEED TO BE FILLED\r
- 0, // RegionBaseAddress ... NEED TO BE FILLED\r
- 0, // Size ... NEED TO BE FILLED\r
- 0, // StartLba\r
-\r
- {\r
- EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision\r
- NULL, // Media ... NEED TO BE FILLED\r
- NorFlashBlockIoReset, // Reset;\r
- NorFlashBlockIoReadBlocks, // ReadBlocks\r
- NorFlashBlockIoWriteBlocks, // WriteBlocks\r
- NorFlashBlockIoFlushBlocks // FlushBlocks\r
- }, // BlockIoProtocol\r
-\r
- {\r
- 0, // MediaId ... NEED TO BE FILLED\r
- FALSE, // RemovableMedia\r
- TRUE, // MediaPresent\r
- FALSE, // LogicalPartition\r
- FALSE, // ReadOnly\r
- FALSE, // WriteCaching;\r
- 0, // BlockSize ... NEED TO BE FILLED\r
- 4, // IoAlign\r
- 0, // LastBlock ... NEED TO BE FILLED\r
- 0, // LowestAlignedLba\r
- 1, // LogicalBlocksPerPhysicalBlock\r
- }, //Media;\r
-\r
- {\r
- EFI_DISK_IO_PROTOCOL_REVISION, // Revision\r
- NorFlashDiskIoReadDisk, // ReadDisk\r
- NorFlashDiskIoWriteDisk // WriteDisk\r
- },\r
-\r
- {\r
- FvbGetAttributes, // GetAttributes\r
- FvbSetAttributes, // SetAttributes\r
- FvbGetPhysicalAddress, // GetPhysicalAddress\r
- FvbGetBlockSize, // GetBlockSize\r
- FvbRead, // Read\r
- FvbWrite, // Write\r
- FvbEraseBlocks, // EraseBlocks\r
- NULL, //ParentHandle\r
- }, // FvbProtoccol;\r
- NULL, // ShadowBuffer\r
- {\r
- {\r
- {\r
- HARDWARE_DEVICE_PATH,\r
- HW_VENDOR_DP,\r
- {\r
- (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)),\r
- (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8)\r
- }\r
- },\r
- { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, // GUID ... NEED TO BE FILLED\r
- },\r
- 0, // Index\r
- {\r
- END_DEVICE_PATH_TYPE,\r
- END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
- { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }\r
- }\r
- } // DevicePath\r
-};\r
-\r
-EFI_STATUS\r
-NorFlashCreateInstance (\r
- IN UINTN NorFlashDeviceBase,\r
- IN UINTN NorFlashRegionBase,\r
- IN UINTN NorFlashSize,\r
- IN UINT32 Index,\r
- IN UINT32 BlockSize,\r
- IN BOOLEAN SupportFvb,\r
- OUT NOR_FLASH_INSTANCE** NorFlashInstance\r
- )\r
-{\r
- EFI_STATUS Status;\r
- NOR_FLASH_INSTANCE* Instance;\r
-\r
- ASSERT(NorFlashInstance != NULL);\r
-\r
- Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate);\r
- if (Instance == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- Instance->DeviceBaseAddress = NorFlashDeviceBase;\r
- Instance->RegionBaseAddress = NorFlashRegionBase;\r
- Instance->Size = NorFlashSize;\r
-\r
- Instance->BlockIoProtocol.Media = &Instance->Media;\r
- Instance->Media.MediaId = Index;\r
- Instance->Media.BlockSize = BlockSize;\r
- Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;\r
-\r
- CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);\r
- Instance->DevicePath.Index = (UINT8)Index;\r
-\r
- Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);;\r
- if (Instance->ShadowBuffer == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- if (SupportFvb) {\r
- NorFlashFvbInitialize (Instance);\r
-\r
- Status = gBS->InstallMultipleProtocolInterfaces (\r
- &Instance->Handle,\r
- &gEfiDevicePathProtocolGuid, &Instance->DevicePath,\r
- &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol,\r
- &gEfiFirmwareVolumeBlockProtocolGuid, &Instance->FvbProtocol,\r
- NULL\r
- );\r
- if (EFI_ERROR(Status)) {\r
- FreePool (Instance);\r
- return Status;\r
- }\r
- } else {\r
- Status = gBS->InstallMultipleProtocolInterfaces (\r
- &Instance->Handle,\r
- &gEfiDevicePathProtocolGuid, &Instance->DevicePath,\r
- &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol,\r
- &gEfiDiskIoProtocolGuid, &Instance->DiskIoProtocol,\r
- NULL\r
- );\r
- if (EFI_ERROR(Status)) {\r
- FreePool (Instance);\r
- return Status;\r
- }\r
- }\r
-\r
- *NorFlashInstance = Instance;\r
- return Status;\r
-}\r
-\r
-/**\r
- * This function unlock and erase an entire NOR Flash block.\r
- **/\r
-EFI_STATUS\r
-NorFlashUnlockAndEraseSingleBlock (\r
- IN NOR_FLASH_INSTANCE *Instance,\r
- IN UINTN BlockAddress\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINTN Index;\r
- EFI_TPL OriginalTPL;\r
-\r
- if (!EfiAtRuntime ()) {\r
- // Raise TPL to TPL_HIGH to stop anyone from interrupting us.\r
- OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
- } else {\r
- // This initialization is only to prevent the compiler to complain about the\r
- // use of uninitialized variables\r
- OriginalTPL = TPL_HIGH_LEVEL;\r
- }\r
-\r
- Index = 0;\r
- // The block erase might fail a first time (SW bug ?). Retry it ...\r
- do {\r
- // Unlock the block if we have to\r
- Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);\r
- if (EFI_ERROR (Status)) {\r
- break;\r
- }\r
- Status = NorFlashEraseSingleBlock (Instance, BlockAddress);\r
- Index++;\r
- } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));\r
-\r
- if (Index == NOR_FLASH_ERASE_RETRY) {\r
- DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress,Index));\r
- }\r
-\r
- if (!EfiAtRuntime ()) {\r
- // Interruptions can resume.\r
- gBS->RestoreTPL (OriginalTPL);\r
- }\r
-\r
- return Status;\r
-}\r
-\r
-EFI_STATUS\r
-NorFlashWriteFullBlock (\r
- IN NOR_FLASH_INSTANCE *Instance,\r
- IN EFI_LBA Lba,\r
- IN UINT32 *DataBuffer,\r
- IN UINT32 BlockSizeInWords\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINTN WordAddress;\r
- UINT32 WordIndex;\r
- UINTN BufferIndex;\r
- UINTN BlockAddress;\r
- UINTN BuffersInBlock;\r
- UINTN RemainingWords;\r
- EFI_TPL OriginalTPL;\r
- UINTN Cnt;\r
-\r
- Status = EFI_SUCCESS;\r
-\r
- // Get the physical address of the block\r
- BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);\r
-\r
- // Start writing from the first address at the start of the block\r
- WordAddress = BlockAddress;\r
-\r
- if (!EfiAtRuntime ()) {\r
- // Raise TPL to TPL_HIGH to stop anyone from interrupting us.\r
- OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
- } else {\r
- // This initialization is only to prevent the compiler to complain about the\r
- // use of uninitialized variables\r
- OriginalTPL = TPL_HIGH_LEVEL;\r
- }\r
-\r
- Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);\r
- if (EFI_ERROR(Status)) {\r
- DEBUG((EFI_D_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));\r
- goto EXIT;\r
- }\r
-\r
- // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.\r
-\r
- // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero\r
- if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {\r
-\r
- // First, break the entire block into buffer-sized chunks.\r
- BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;\r
-\r
- // Then feed each buffer chunk to the NOR Flash\r
- // If a buffer does not contain any data, don't write it.\r
- for(BufferIndex=0;\r
- BufferIndex < BuffersInBlock;\r
- BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS\r
- ) {\r
- // Check the buffer to see if it contains any data (not set all 1s).\r
- for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {\r
- if (~DataBuffer[Cnt] != 0 ) {\r
- // Some data found, write the buffer.\r
- Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES,\r
- DataBuffer);\r
- if (EFI_ERROR(Status)) {\r
- goto EXIT;\r
- }\r
- break;\r
- }\r
- }\r
- }\r
-\r
- // Finally, finish off any remaining words that are less than the maximum size of the buffer\r
- RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;\r
-\r
- if(RemainingWords != 0) {\r
- Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);\r
- if (EFI_ERROR(Status)) {\r
- goto EXIT;\r
- }\r
- }\r
-\r
- } else {\r
- // For now, use the single word programming algorithm\r
- // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,\r
- // i.e. which ends in the range 0x......01 - 0x......7F.\r
- for(WordIndex=0; WordIndex<BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {\r
- Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);\r
- if (EFI_ERROR(Status)) {\r
- goto EXIT;\r
- }\r
- }\r
- }\r
-\r
-EXIT:\r
- if (!EfiAtRuntime ()) {\r
- // Interruptions can resume.\r
- gBS->RestoreTPL (OriginalTPL);\r
- }\r
-\r
- if (EFI_ERROR(Status)) {\r
- DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));\r
- }\r
- return Status;\r
-}\r
-\r
-EFI_STATUS\r
-EFIAPI\r
-NorFlashInitialise (\r
- IN EFI_HANDLE ImageHandle,\r
- IN EFI_SYSTEM_TABLE *SystemTable\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT32 Index;\r
- NOR_FLASH_DESCRIPTION* NorFlashDevices;\r
- BOOLEAN ContainVariableStorage;\r
-\r
- Status = NorFlashPlatformInitialization ();\r
- if (EFI_ERROR(Status)) {\r
- DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));\r
- return Status;\r
- }\r
-\r
- Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);\r
- if (EFI_ERROR(Status)) {\r
- DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));\r
- return Status;\r
- }\r
-\r
- mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);\r
-\r
- for (Index = 0; Index < mNorFlashDeviceCount; Index++) {\r
- // Check if this NOR Flash device contain the variable storage region\r
- ContainVariableStorage =\r
- (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&\r
- (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);\r
-\r
- Status = NorFlashCreateInstance (\r
- NorFlashDevices[Index].DeviceBaseAddress,\r
- NorFlashDevices[Index].RegionBaseAddress,\r
- NorFlashDevices[Index].Size,\r
- Index,\r
- NorFlashDevices[Index].BlockSize,\r
- ContainVariableStorage,\r
- &mNorFlashInstances[Index]\r
- );\r
- if (EFI_ERROR(Status)) {\r
- DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));\r
- }\r
- }\r
-\r
- //\r
- // Register for the virtual address change event\r
- //\r
- Status = gBS->CreateEventEx (\r
- EVT_NOTIFY_SIGNAL,\r
- TPL_NOTIFY,\r
- NorFlashVirtualNotifyEvent,\r
- NULL,\r
- &gEfiEventVirtualAddressChangeGuid,\r
- &mNorFlashVirtualAddrChangeEvent\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- return Status;\r
-}\r
-\r
-EFI_STATUS\r
-EFIAPI\r
-NorFlashFvbInitialize (\r
- IN NOR_FLASH_INSTANCE* Instance\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT32 FvbNumLba;\r
- EFI_BOOT_MODE BootMode;\r
- UINTN RuntimeMmioRegionSize;\r
-\r
- DEBUG((DEBUG_BLKIO,"NorFlashFvbInitialize\n"));\r
- ASSERT((Instance != NULL));\r
-\r
- //\r
- // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME\r
- //\r
-\r
- // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;\r
- // even if we only use the small block region at the top of the NOR Flash.\r
- // The reason is when the NOR Flash memory is set into program mode, the command\r
- // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)\r
- RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;\r
-\r
- Status = gDS->AddMemorySpace (\r
- EfiGcdMemoryTypeMemoryMappedIo,\r
- Instance->DeviceBaseAddress, RuntimeMmioRegionSize,\r
- EFI_MEMORY_UC | EFI_MEMORY_RUNTIME\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- Status = gDS->SetMemorySpaceAttributes (\r
- Instance->DeviceBaseAddress, RuntimeMmioRegionSize,\r
- EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- mFlashNvStorageVariableBase = PcdGet32 (PcdFlashNvStorageVariableBase);\r
-\r
- // Set the index of the first LBA for the FVB\r
- Instance->StartLba = (PcdGet32 (PcdFlashNvStorageVariableBase) - Instance->RegionBaseAddress) / Instance->Media.BlockSize;\r
-\r
- BootMode = GetBootModeHob ();\r
- if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {\r
- Status = EFI_INVALID_PARAMETER;\r
- } else {\r
- // Determine if there is a valid header at the beginning of the NorFlash\r
- Status = ValidateFvHeader (Instance);\r
- }\r
-\r
- // Install the Default FVB header if required\r
- if (EFI_ERROR(Status)) {\r
- // There is no valid header, so time to install one.\r
- DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__));\r
- DEBUG ((DEBUG_INFO, "%a: Installing a correct one for this volume.\n",\r
- __FUNCTION__));\r
-\r
- // Erase all the NorFlash that is reserved for variable storage\r
- FvbNumLba = (PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) + PcdGet32(PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;\r
-\r
- Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);\r
- if (EFI_ERROR(Status)) {\r
- return Status;\r
- }\r
-\r
- // Install all appropriate headers\r
- Status = InitializeFvAndVariableStoreHeaders (Instance);\r
- if (EFI_ERROR(Status)) {\r
- return Status;\r
- }\r
- }\r
-\r
- //\r
- // The driver implementing the variable read service can now be dispatched;\r
- // the varstore headers are in place.\r
- //\r
- Status = gBS->InstallProtocolInterface (\r
- &gImageHandle,\r
- &gEdkiiNvVarStoreFormattedGuid,\r
- EFI_NATIVE_INTERFACE,\r
- NULL\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- //\r
- // Register for the virtual address change event\r
- //\r
- Status = gBS->CreateEventEx (\r
- EVT_NOTIFY_SIGNAL,\r
- TPL_NOTIFY,\r
- FvbVirtualNotifyEvent,\r
- NULL,\r
- &gEfiEventVirtualAddressChangeGuid,\r
- &mFvbVirtualAddrChangeEvent\r
- );\r
- ASSERT_EFI_ERROR (Status);\r
-\r
- return Status;\r
-}\r