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