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