/** @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; | |
} |