| /** @file | |
| An instance of the NorFlashPlatformLib for Kvmtool platform. | |
| Copyright (c) 2020 - 2023, Arm Ltd. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Library/BaseLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/VirtNorFlashPlatformLib.h> | |
| #include <Protocol/FdtClient.h> | |
| /** 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; | |
| } |