From 4e511554872c318f8bce196f1cddf69432886bfa Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Fri, 18 Dec 2020 19:05:16 +0900 Subject: [PATCH] ArmPlatformPkg/NorFlashDxe: implement standalone MM version Implement a version of the NOR Flash driver that can execute in standalone MM context. This is used to access the secure variable storage, it only supports EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL. Signed-off-by: Masahisa Kojima Reviewed-by: Ard Biesheuvel --- ArmPlatformPkg/ArmPlatformPkg.dsc | 9 + .../NorFlashDxe/NorFlashStandaloneMm.c | 364 ++++++++++++++++++ .../NorFlashDxe/NorFlashStandaloneMm.inf | 63 +++ 3 files changed, 436 insertions(+) create mode 100644 ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c create mode 100644 ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf diff --git a/ArmPlatformPkg/ArmPlatformPkg.dsc b/ArmPlatformPkg/ArmPlatformPkg.dsc index b92ef712be..88fe1247c0 100644 --- a/ArmPlatformPkg/ArmPlatformPkg.dsc +++ b/ArmPlatformPkg/ArmPlatformPkg.dsc @@ -90,6 +90,12 @@ MemoryAllocationLib|EmbeddedPkg/Library/PrePiMemoryAllocationLib/PrePiMemoryAllocationLib.inf PrePiHobListPointerLib|ArmPlatformPkg/Library/PrePiHobListPointerLib/PrePiHobListPointerLib.inf +[LibraryClasses.AARCH64.MM_STANDALONE] + HobLib|StandaloneMmPkg/Library/StandaloneMmHobLib/StandaloneMmHobLib.inf + MemoryAllocationLib|StandaloneMmPkg/Library/StandaloneMmMemoryAllocationLib/StandaloneMmMemoryAllocationLib.inf + MmServicesTableLib|MdePkg/Library/StandaloneMmServicesTableLib/StandaloneMmServicesTableLib.inf + StandaloneMmDriverEntryPoint|MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf + [Components.common] ArmPlatformPkg/Drivers/LcdGraphicsOutputDxe/LcdGraphicsOutputDxe.inf ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf @@ -122,3 +128,6 @@ ArmPlatformPkg/PrePi/PeiUniCore.inf ArmPlatformPkg/Library/ArmMaliDp/ArmMaliDp.inf + +[Components.AARCH64] + ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c new file mode 100644 index 0000000000..1ebf6d6ba7 --- /dev/null +++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c @@ -0,0 +1,364 @@ +/** @file NorFlashStandaloneMm.c + + Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.
+ Copyright (c) 2020, Linaro, Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include + +#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; WordIndexStartLba = (PcdGet32 (PcdFlashNvStorageVariableBase) - 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; +} diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf new file mode 100644 index 0000000000..f788472406 --- /dev/null +++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf @@ -0,0 +1,63 @@ +#/** @file +# +# Component description file for NorFlashStandaloneMm module +# +# Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+# Copyright (c) 2020, Linaro, Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmVeNorFlashStandaloneMm + FILE_GUID = e67d82ad-cd56-4071-9151-95ee44990bb0 + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + ENTRY_POINT = NorFlashInitialise + +[Sources.common] + NorFlash.h + NorFlash.c + NorFlashStandaloneMm.c + NorFlashFvb.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + StandaloneMmPkg/StandaloneMmPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + IoLib + MemoryAllocationLib + MmServicesTableLib + NorFlashPlatformLib + StandaloneMmDriverEntryPoint + +[Guids] + gEfiSystemNvDataFvGuid + gEfiVariableGuid + gEfiAuthenticatedVariableGuid + +[Protocols] + gEfiSmmFirmwareVolumeBlockProtocolGuid + +[Pcd.common] + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize + + gArmPlatformTokenSpaceGuid.PcdNorFlashCheckBlockLocked + +[Depex] + TRUE