/** @file
Copyright (c) 2013-2021, Arm Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ARM_FVP_BASE_VIRTIO_BLOCK_BASE 0x1c130000
// SMMUv3 Global Bypass Attribute (GBPA) register offset.
#define SMMU_GBPA 0x0044
// SMMU_GBPA register fields.
#define SMMU_GBPA_UPDATE BIT31
#define SMMU_GBPA_ABORT BIT20
#pragma pack(1)
typedef struct {
VENDOR_DEVICE_PATH Vendor;
EFI_DEVICE_PATH_PROTOCOL End;
} VIRTIO_BLK_DEVICE_PATH;
#pragma pack()
VIRTIO_BLK_DEVICE_PATH mVirtioBlockDevicePath =
{
{
{
HARDWARE_DEVICE_PATH,
HW_VENDOR_DP,
{
(UINT8)( sizeof(VENDOR_DEVICE_PATH) ),
(UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8)
}
},
EFI_CALLER_ID_GUID,
},
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{
sizeof (EFI_DEVICE_PATH_PROTOCOL),
0
}
}
};
/**
Poll the SMMU register and test the value based on the mask.
@param [in] SmmuReg Base address of the SMMU register.
@param [in] Mask Mask of register bits to monitor.
@param [in] Value Expected value.
@retval EFI_SUCCESS Success.
@retval EFI_TIMEOUT Timeout.
**/
STATIC
EFI_STATUS
EFIAPI
SmmuV3Poll (
IN UINT64 SmmuReg,
IN UINT32 Mask,
IN UINT32 Value
)
{
UINT32 RegVal;
UINTN Count;
// Set 1ms timeout value.
Count = 10;
do {
RegVal = MmioRead32 (SmmuReg);
if ((RegVal & Mask) == Value) {
return EFI_SUCCESS;
}
MicroSecondDelay (100);
} while ((--Count) > 0);
DEBUG ((DEBUG_ERROR, "Timeout polling SMMUv3 register @%p\n", SmmuReg));
DEBUG ((
DEBUG_ERROR,
"Read value 0x%x, expected 0x%x\n",
RegVal,
((Value == 0) ? (RegVal & ~Mask) : (RegVal | Mask))
));
return EFI_TIMEOUT;
}
/**
Initialise the SMMUv3 to set Non-secure streams to bypass the SMMU.
@param [in] SmmuReg Base address of the SMMUv3.
@retval EFI_SUCCESS Success.
@retval EFI_TIMEOUT Timeout.
**/
STATIC
EFI_STATUS
EFIAPI
SmmuV3Init (
IN UINT64 SmmuBase
)
{
EFI_STATUS Status;
UINT32 RegVal;
// Attribute update has completed when SMMU_(S)_GBPA.Update bit is 0.
Status = SmmuV3Poll (SmmuBase + SMMU_GBPA, SMMU_GBPA_UPDATE, 0);
if (EFI_ERROR (Status)) {
return Status;
}
// SMMU_(S)_CR0 resets to zero with all streams bypassing the SMMU,
// so just abort all incoming transactions.
RegVal = MmioRead32 (SmmuBase + SMMU_GBPA);
// TF-A configures the SMMUv3 to abort all incoming transactions.
// Clear the SMMU_GBPA.ABORT to allow Non-secure streams to bypass
// the SMMU.
RegVal &= ~SMMU_GBPA_ABORT;
RegVal |= SMMU_GBPA_UPDATE;
MmioWrite32 (SmmuBase + SMMU_GBPA, RegVal);
Status = SmmuV3Poll (SmmuBase + SMMU_GBPA, SMMU_GBPA_UPDATE, 0);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
/**
* Generic UEFI Entrypoint for 'ArmFvpDxe' driver
* See UEFI specification for the details of the parameters
*/
EFI_STATUS
EFIAPI
ArmFvpInitialise (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT32 SysId;
Status = gBS->InstallProtocolInterface (&ImageHandle,
&gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,
&mVirtioBlockDevicePath);
if (EFI_ERROR (Status)) {
return Status;
}
// Declare the Virtio BlockIo device
Status = VirtioMmioInstallDevice (ARM_FVP_BASE_VIRTIO_BLOCK_BASE, ImageHandle);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ArmFvpDxe: Failed to install Virtio block device\n"));
}
// Install dynamic Shell command to run baremetal binaries.
Status = ShellDynCmdRunAxfInstall (ImageHandle);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ArmFvpDxe: Failed to install ShellDynCmdRunAxf\n"));
}
// If FVP RevC - Configure SMMUv3 to set NS transactions in bypass mode.
SysId = MmioRead32 (ARM_VE_SYS_ID_REG);
if ((SysId & ARM_FVP_SYS_ID_REV_MASK) == ARM_FVP_BASE_REVC_REV) {
Status = SmmuV3Init (FVP_REVC_SMMUV3_BASE);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"ArmFvpDxe: Failed to initialise SMMUv3 in bypass mode.\n"
));
}
}
return Status;
}