/** @file Arm Ffa library common code. Copyright (c) 2024, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Glossary: - FF-A - Firmware Framework for Arm A-profile @par Reference(s): - Arm Firmware Framework for Arm A-Profile v1.3 ALP1: [https://developer.arm.com/documentation/den0077/l] **/ #include #include #include #include #include #include #include #include #include #include #include #include #include "ArmFfaCommon.h" #include "ArmFfaRxTxMap.h" EFI_HANDLE mArmFfaRxTxBufferStmmInfoHandle = NULL; ARM_FFA_RX_TX_BUFFER_INFO *mArmFfaRxTxBufferStmmInfo = NULL; /** Get mapped Rx/Tx buffers. @param [out] TxBuffer Address of TxBuffer @param [out] TxBufferSize Size of TxBuffer @param [out] RxBuffer Address of RxBuffer @param [out] RxBufferSize Size of RxBuffer @retval EFI_SUCCESS @retval Others Error. **/ EFI_STATUS EFIAPI ArmFfaLibGetRxTxBuffers ( OUT VOID **TxBuffer OPTIONAL, OUT UINT64 *TxBufferSize OPTIONAL, OUT VOID **RxBuffer OPTIONAL, OUT UINT64 *RxBufferSize OPTIONAL ) { UINTN TxBufferAddr; UINTN RxBufferAddr; EFI_STATUS Status = gMmst->MmLocateProtocol ( &gArmFfaRxTxBufferInfoGuid, NULL, (VOID **)&mArmFfaRxTxBufferStmmInfo ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Failed to locate Rx/Tx buffer protocol... Status: %r\n", __func__, Status)); return Status; } TxBufferAddr = (UINTN)mArmFfaRxTxBufferStmmInfo->TxBufferAddr; RxBufferAddr = (UINTN)mArmFfaRxTxBufferStmmInfo->RxBufferAddr; if ((TxBufferAddr == 0x00) || (RxBufferAddr == 0x00)) { return EFI_NOT_READY; } if (TxBuffer != NULL) { *TxBuffer = (VOID *)TxBufferAddr; } if (TxBufferSize != NULL) { *TxBufferSize = mArmFfaRxTxBufferStmmInfo->TxBufferSize; } if (RxBuffer != NULL) { *RxBuffer = (VOID *)RxBufferAddr; } if (RxBufferSize != NULL) { *RxBufferSize = mArmFfaRxTxBufferStmmInfo->RxBufferSize; } return EFI_SUCCESS; } /** Mapping Rx/Tx buffers. This function is only called in ArmFfaLibConstructor because Rx/Tx buffer is registered only once per partition. @retval EFI_SUCCESS @retval EFI_ALREADY_STARTED Rx/Tx buffer already mapped. @retval EFI_OUT_OF_RESOURCE Out of memory @retval EFI_INVALID_PARAMETER Invalid alignment of Rx/Tx buffer @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibRxTxMap ( IN VOID ) { EFI_STATUS Status; ARM_FFA_ARGS FfaArgs; UINTN Property1; UINTN Property2; UINTN MinSizeAndAlign; UINTN MaxSize; VOID *Buffers; VOID *TxBuffer; VOID *RxBuffer; UINT64 BufferSize; Status = gMmst->MmLocateProtocol ( &gArmFfaRxTxBufferInfoGuid, NULL, (VOID **)&mArmFfaRxTxBufferStmmInfo ); if (!EFI_ERROR (Status)) { // Great, we got what we need. return EFI_ALREADY_STARTED; } Status = ArmFfaLibGetFeatures ( ARM_FID_FFA_RXTX_MAP, FFA_RXTX_MAP_INPUT_PROPERTY_DEFAULT, &Property1, &Property2 ); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "%a: Failed to get RX/TX buffer property... Status: %r\n", __func__, Status )); return Status; } ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); MinSizeAndAlign = ((Property1 >> ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_SHIFT) & ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_MASK); switch (MinSizeAndAlign) { case ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_4K: MinSizeAndAlign = SIZE_4KB; break; case ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_16K: MinSizeAndAlign = SIZE_16KB; break; case ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_64K: MinSizeAndAlign = SIZE_64KB; break; default: DEBUG ((DEBUG_ERROR, "%a: Invalid MinSizeAndAlign: 0x%x\n", __func__, MinSizeAndAlign)); return EFI_UNSUPPORTED; } MaxSize = (Property1 >> ARM_FFA_BUFFER_MAXSIZE_PAGE_COUNT_SHIFT) & ARM_FFA_BUFFER_MAXSIZE_PAGE_COUNT_MASK; MaxSize = ((MaxSize == 0) ? MAX_UINTN : (MaxSize * MinSizeAndAlign)); if ((MinSizeAndAlign > (PcdGet64 (PcdFfaTxRxPageCount) * EFI_PAGE_SIZE)) || (MaxSize < (PcdGet64 (PcdFfaTxRxPageCount) * EFI_PAGE_SIZE))) { DEBUG (( DEBUG_ERROR, "%a: Buffer is too small! MinSize: 0x%x, MaxSize: 0x%x, PageCount: %d\n", __func__, MinSizeAndAlign, MaxSize, PcdGet64 (PcdFfaTxRxPageCount) )); return EFI_INVALID_PARAMETER; } Buffers = AllocateAlignedPages ((PcdGet64 (PcdFfaTxRxPageCount) * 2), MinSizeAndAlign); if (Buffers == NULL) { return EFI_OUT_OF_RESOURCES; } BufferSize = PcdGet64 (PcdFfaTxRxPageCount) * EFI_PAGE_SIZE; TxBuffer = Buffers; RxBuffer = Buffers + BufferSize; FfaArgs.Arg0 = ARM_FID_FFA_RXTX_MAP; FfaArgs.Arg1 = (UINTN)TxBuffer; FfaArgs.Arg2 = (UINTN)RxBuffer; /* * PcdFfaTxRxPageCount sets with count of EFI_PAGE_SIZE granularity * But, PageCounts for Tx/Rx buffer should set with * count of Tx/Rx Buffer's MinSizeAndAlign. granularity. */ FfaArgs.Arg3 = PcdGet64 (PcdFfaTxRxPageCount) / EFI_SIZE_TO_PAGES (MinSizeAndAlign); ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "%a: Failed to map Rx/Tx buffer. Status: %r\n", __func__, Status )); goto ErrorHandler; } mArmFfaRxTxBufferStmmInfo = AllocateZeroPool (sizeof (ARM_FFA_RX_TX_BUFFER_INFO)); if (mArmFfaRxTxBufferStmmInfo == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ErrorHandler; } mArmFfaRxTxBufferStmmInfo->TxBufferAddr = TxBuffer; mArmFfaRxTxBufferStmmInfo->RxBufferAddr = RxBuffer; mArmFfaRxTxBufferStmmInfo->TxBufferSize = BufferSize; mArmFfaRxTxBufferStmmInfo->RxBufferSize = BufferSize; Status = gMmst->MmInstallProtocolInterface ( &mArmFfaRxTxBufferStmmInfoHandle, &gArmFfaRxTxBufferInfoGuid, EFI_NATIVE_INTERFACE, mArmFfaRxTxBufferStmmInfo ); return Status; ErrorHandler: FreeAlignedPages (Buffers, (PcdGet64 (PcdFfaTxRxPageCount) * 2)); TxBuffer = NULL; RxBuffer = NULL; return Status; } /** Unmap Rx/Tx buffer. This function is only called in Exit boot service because Rx/Tx buffer is registered only once per partition. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETERS Already unregistered @retval EFI_UNSUPPORTED Not supported **/ EFI_STATUS EFIAPI ArmFfaLibRxTxUnmap ( IN VOID ) { EFI_STATUS Status; ARM_FFA_ARGS FfaArgs; VOID *Buffers; if (mArmFfaRxTxBufferStmmInfoHandle == NULL) { // This means that the agent tried to unmap the buffers before even know them. // Let's be a nice player... return EFI_UNSUPPORTED; } ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_RXTX_UNMAP; FfaArgs.Arg1 = (gPartId << ARM_FFA_SOURCE_EP_SHIFT); ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { return Status; } /* * Rx/Tx Buffer are allocated with continuous pages. * and start address of these pages is set on PcdFfaTxBuffer. * See ArmFfaLibRxTxMap(). */ Buffers = (VOID *)(UINTN)mArmFfaRxTxBufferStmmInfo->TxBufferAddr; if (Buffers != NULL) { FreeAlignedPages (Buffers, (EFI_SIZE_TO_PAGES (mArmFfaRxTxBufferStmmInfo->TxBufferSize) * 2)); } Status = gMmst->MmUninstallProtocolInterface ( mArmFfaRxTxBufferStmmInfoHandle, &gArmFfaRxTxBufferInfoGuid, mArmFfaRxTxBufferStmmInfo ); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "%a: Failed to uninstall Rx/Tx buffer protocol... Status: %r\n", __func__, Status )); return Status; } FreePool (mArmFfaRxTxBufferStmmInfo); mArmFfaRxTxBufferStmmInfo = NULL; return EFI_SUCCESS; }