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