--- /dev/null
+/** @file\r
+ An instance of the NorFlashPlatformLib for Kvmtool platform.\r
+\r
+ Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+ **/\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/NorFlashPlatformLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Protocol/FdtClient.h>\r
+\r
+/** Macro defining the NOR block size configured in Kvmtool.\r
+*/\r
+#define KVMTOOL_NOR_BLOCK_SIZE SIZE_64KB\r
+\r
+/** Macro defining the maximum number of Flash devices.\r
+*/\r
+#define MAX_FLASH_DEVICES 4\r
+\r
+/** Macro defining the cfi-flash label describing the UEFI variable store.\r
+*/\r
+#define LABEL_UEFI_VAR_STORE "System-firmware"\r
+\r
+STATIC NOR_FLASH_DESCRIPTION mNorFlashDevices[MAX_FLASH_DEVICES];\r
+STATIC UINTN mNorFlashDeviceCount = 0;\r
+STATIC INT32 mUefiVarStoreNode = MAX_INT32;\r
+STATIC FDT_CLIENT_PROTOCOL *mFdtClient;\r
+\r
+/** This function performs platform specific actions to initialise\r
+ the NOR flash, if required.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+**/\r
+EFI_STATUS\r
+NorFlashPlatformInitialization (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ DEBUG ((DEBUG_INFO, "NorFlashPlatformInitialization\n"));\r
+\r
+ if ((mNorFlashDeviceCount > 0) && (mUefiVarStoreNode != MAX_INT32)) {\r
+ //\r
+ // UEFI takes ownership of the cfi-flash hardware, and exposes its\r
+ // functionality through the UEFI Runtime Variable Service. This means we\r
+ // need to disable it in the device tree to prevent the OS from attaching\r
+ // its device driver as well.\r
+ // Note: This library is loaded twice. First by FaultTolerantWriteDxe to\r
+ // setup the PcdFlashNvStorageFtw* and later by NorFlashDxe to provide the\r
+ // NorFlashPlatformLib interfaces. If the node is disabled when the library\r
+ // is first loaded, then during the subsequent loading of the library the\r
+ // call to FindNextCompatibleNode() from the library constructor skips the\r
+ // FDT node used for UEFI storage variable. Due to this we cannot setup the\r
+ // NOR flash device description i.e. mNorFlashDevices[].\r
+ // Since NorFlashPlatformInitialization() is called only by NorFlashDxe,\r
+ // we know it is safe to disable the node here.\r
+ //\r
+ Status = mFdtClient->SetNodeProperty (\r
+ mFdtClient,\r
+ mUefiVarStoreNode,\r
+ "status",\r
+ "disabled",\r
+ sizeof ("disabled")\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_WARN, "Failed to set cfi-flash status to 'disabled'\n"));\r
+ }\r
+ } else {\r
+ Status = EFI_NOT_FOUND;\r
+ DEBUG ((DEBUG_ERROR, "Flash device for UEFI variable storage not found\n"));\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/** Initialise Non volatile Flash storage variables.\r
+\r
+ @param [in] FlashDevice Pointer to the NOR Flash device.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient flash storage space.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SetupVariableStore (\r
+ IN NOR_FLASH_DESCRIPTION * FlashDevice\r
+ )\r
+{\r
+ UINTN FlashRegion;\r
+ UINTN FlashNvStorageVariableBase;\r
+ UINTN FlashNvStorageFtwWorkingBase;\r
+ UINTN FlashNvStorageFtwSpareBase;\r
+ UINTN FlashNvStorageVariableSize;\r
+ UINTN FlashNvStorageFtwWorkingSize;\r
+ UINTN FlashNvStorageFtwSpareSize;\r
+\r
+ FlashNvStorageVariableSize = PcdGet32 (PcdFlashNvStorageVariableSize);\r
+ FlashNvStorageFtwWorkingSize = PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
+ FlashNvStorageFtwSpareSize = PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
+\r
+ if ((FlashNvStorageVariableSize == 0) ||\r
+ (FlashNvStorageFtwWorkingSize == 0) ||\r
+ (FlashNvStorageFtwSpareSize == 0)) {\r
+ DEBUG ((DEBUG_ERROR, "FlashNvStorage size not defined\n"));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Setup the variable store\r
+ FlashRegion = FlashDevice->DeviceBaseAddress;\r
+\r
+ FlashNvStorageVariableBase = FlashRegion;\r
+ FlashRegion += PcdGet32 (PcdFlashNvStorageVariableSize);\r
+\r
+ FlashNvStorageFtwWorkingBase = FlashRegion;\r
+ FlashRegion += PcdGet32 (PcdFlashNvStorageFtwWorkingSize);\r
+\r
+ FlashNvStorageFtwSpareBase = FlashRegion;\r
+ FlashRegion += PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
+\r
+ if (FlashRegion > (FlashDevice->DeviceBaseAddress + FlashDevice->Size)) {\r
+ DEBUG ((DEBUG_ERROR, "Insufficient flash storage size\n"));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ PcdSet32S (\r
+ PcdFlashNvStorageVariableBase,\r
+ FlashNvStorageVariableBase\r
+ );\r
+\r
+ PcdSet32S (\r
+ PcdFlashNvStorageFtwWorkingBase,\r
+ FlashNvStorageFtwWorkingBase\r
+ );\r
+\r
+ PcdSet32S (\r
+ PcdFlashNvStorageFtwSpareBase,\r
+ FlashNvStorageFtwSpareBase\r
+ );\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "PcdFlashNvStorageVariableBase = 0x%x\n",\r
+ FlashNvStorageVariableBase\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "PcdFlashNvStorageVariableSize = 0x%x\n",\r
+ FlashNvStorageVariableSize\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "PcdFlashNvStorageFtwWorkingBase = 0x%x\n",\r
+ FlashNvStorageFtwWorkingBase\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "PcdFlashNvStorageFtwWorkingSize = 0x%x\n",\r
+ FlashNvStorageFtwWorkingSize\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "PcdFlashNvStorageFtwSpareBase = 0x%x\n",\r
+ FlashNvStorageFtwSpareBase\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "PcdFlashNvStorageFtwSpareSize = 0x%x\n",\r
+ FlashNvStorageFtwSpareSize\r
+ ));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Return the Flash devices on the platform.\r
+\r
+ @param [out] NorFlashDescriptions Pointer to the Flash device description.\r
+ @param [out] Count Number of Flash devices.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_NOT_FOUND Flash device not found.\r
+**/\r
+EFI_STATUS\r
+NorFlashPlatformGetDevices (\r
+ OUT NOR_FLASH_DESCRIPTION **NorFlashDescriptions,\r
+ OUT UINT32 *Count\r
+ )\r
+{\r
+ if (mNorFlashDeviceCount > 0) {\r
+ *NorFlashDescriptions = mNorFlashDevices;\r
+ *Count = mNorFlashDeviceCount;\r
+ return EFI_SUCCESS;\r
+ }\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/** Entrypoint for NorFlashPlatformLib.\r
+\r
+ @param [in] ImageHandle The handle to the image.\r
+ @param [in] SystemTable Pointer to the System Table.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.\r
+ @retval EFI_NOT_FOUND Flash device not found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NorFlashPlatformLibConstructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE * SystemTable\r
+ )\r
+{\r
+ INT32 Node;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS FindNodeStatus;\r
+ CONST UINT32 *Reg;\r
+ UINT32 PropSize;\r
+ UINT64 Base;\r
+ UINT64 Size;\r
+ UINTN UefiVarStoreIndex;\r
+ CONST CHAR8 *Label;\r
+ UINT32 LabelLen;\r
+\r
+ if (mNorFlashDeviceCount != 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Status = gBS->LocateProtocol (\r
+ &gFdtClientProtocolGuid,\r
+ NULL,\r
+ (VOID **)&mFdtClient\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ UefiVarStoreIndex = MAX_UINTN;\r
+ for (FindNodeStatus = mFdtClient->FindCompatibleNode (\r
+ mFdtClient,\r
+ "cfi-flash",\r
+ &Node\r
+ );\r
+ !EFI_ERROR (FindNodeStatus) &&\r
+ (mNorFlashDeviceCount < MAX_FLASH_DEVICES);\r
+ FindNodeStatus = mFdtClient->FindNextCompatibleNode (\r
+ mFdtClient,\r
+ "cfi-flash",\r
+ Node,\r
+ &Node\r
+ )) {\r
+ Status = mFdtClient->GetNodeProperty (\r
+ mFdtClient,\r
+ Node,\r
+ "label",\r
+ (CONST VOID **)&Label,\r
+ &LabelLen\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a: GetNodeProperty ('label') failed (Status == %r)\n",\r
+ __FUNCTION__,\r
+ Status\r
+ ));\r
+ } else if (AsciiStrCmp (Label, LABEL_UEFI_VAR_STORE) == 0) {\r
+ UefiVarStoreIndex = mNorFlashDeviceCount;\r
+ mUefiVarStoreNode = Node;\r
+ }\r
+\r
+ Status = mFdtClient->GetNodeProperty (\r
+ mFdtClient,\r
+ Node,\r
+ "reg",\r
+ (CONST VOID **)&Reg,\r
+ &PropSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: GetNodeProperty () failed (Status == %r)\n",\r
+ __FUNCTION__, Status));\r
+ continue;\r
+ }\r
+\r
+ ASSERT ((PropSize % (4 * sizeof (UINT32))) == 0);\r
+\r
+ while ((PropSize >= (4 * sizeof (UINT32))) &&\r
+ (mNorFlashDeviceCount < MAX_FLASH_DEVICES)) {\r
+ Base = SwapBytes64 (ReadUnaligned64 ((VOID *)&Reg[0]));\r
+ Size = SwapBytes64 (ReadUnaligned64 ((VOID *)&Reg[2]));\r
+ Reg += 4;\r
+\r
+ PropSize -= 4 * sizeof (UINT32);\r
+\r
+ //\r
+ // Disregard any flash devices that overlap with the primary FV.\r
+ // The firmware is not updatable from inside the guest anyway.\r
+ //\r
+ if ((PcdGet64 (PcdFvBaseAddress) + PcdGet32 (PcdFvSize) > Base) &&\r
+ (Base + Size) > PcdGet64 (PcdFvBaseAddress)) {\r
+ continue;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "NOR%d : Base = 0x%lx, Size = 0x%lx\n",\r
+ mNorFlashDeviceCount,\r
+ Base,\r
+ Size\r
+ ));\r
+\r
+ mNorFlashDevices[mNorFlashDeviceCount].DeviceBaseAddress = (UINTN)Base;\r
+ mNorFlashDevices[mNorFlashDeviceCount].RegionBaseAddress = (UINTN)Base;\r
+ mNorFlashDevices[mNorFlashDeviceCount].Size = (UINTN)Size;\r
+ mNorFlashDevices[mNorFlashDeviceCount].BlockSize = KVMTOOL_NOR_BLOCK_SIZE;\r
+ mNorFlashDeviceCount++;\r
+ }\r
+ } // for\r
+\r
+ // Setup the variable store in the last device\r
+ if (mNorFlashDeviceCount > 0) {\r
+ if (UefiVarStoreIndex == MAX_UINTN) {\r
+ // We did not find a label matching the UEFI Variable store. Default to\r
+ // using the last cfi-flash device as the variable store.\r
+ UefiVarStoreIndex = mNorFlashDeviceCount - 1;\r
+ mUefiVarStoreNode = Node;\r
+ }\r
+ if (mNorFlashDevices[UefiVarStoreIndex].DeviceBaseAddress != 0) {\r
+ return SetupVariableStore (&mNorFlashDevices[UefiVarStoreIndex]);\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r