diff --git a/ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.c b/ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.c index ccb182668d..864781ee32 100644 --- a/ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.c +++ b/ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.c @@ -14,12 +14,339 @@ #include #include +#include #include #include #include #include #include +#define MM_MAJOR_VER_MASK 0xEFFF0000 +#define MM_MINOR_VER_MASK 0x0000FFFF +#define MM_MAJOR_VER_SHIFT 16 + +#define MM_MAJOR_VER(x) (((x) & MM_MAJOR_VER_MASK) >> MM_MAJOR_VER_SHIFT) +#define MM_MINOR_VER(x) ((x) & MM_MINOR_VER_MASK) + +#define MM_CALLER_MAJOR_VER 0x1UL +#define MM_CALLER_MINOR_VER 0x0 + +// +// Partition ID if FF-A support is enabled +// +STATIC UINT16 mPartId; +STATIC UINT16 mStMmPartId; + +/** + Check mm communication compatibility when use SPM_MM. + +**/ +STATIC +EFI_STATUS +EFIAPI +GetMmCompatibility ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 MmVersion; + ARM_SMC_ARGS MmVersionArgs; + + // MM_VERSION uses SMC32 calling conventions + MmVersionArgs.Arg0 = ARM_SMC_ID_MM_VERSION_AARCH32; + + ArmCallSmc (&MmVersionArgs); + if (MmVersionArgs.Arg0 == ARM_SMC_MM_RET_NOT_SUPPORTED) { + return EFI_UNSUPPORTED; + } + + MmVersion = MmVersionArgs.Arg0; + + if ((MM_MAJOR_VER (MmVersion) == MM_CALLER_MAJOR_VER) && + (MM_MINOR_VER (MmVersion) >= MM_CALLER_MINOR_VER)) + { + DEBUG (( + DEBUG_INFO, + "MM Version: Major=0x%x, Minor=0x%x\n", + MM_MAJOR_VER (MmVersion), + MM_MINOR_VER (MmVersion) + )); + Status = EFI_SUCCESS; + } else { + DEBUG (( + DEBUG_ERROR, + "Incompatible MM Versions.\n Current Version: Major=0x%x, Minor=0x%x.\n Expected: Major=0x%x, Minor>=0x%x.\n", + MM_MAJOR_VER (MmVersion), + MM_MINOR_VER (MmVersion), + MM_CALLER_MAJOR_VER, + MM_CALLER_MINOR_VER + )); + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +/** + Check mm communication compatibility when use FF-A. + +**/ +STATIC +EFI_STATUS +EFIAPI +GetFfaCompatibility ( + VOID + ) +{ + EFI_STATUS Status; + UINT16 CurrentMajorVersion; + UINT16 CurrentMinorVersion; + + Status = ArmFfaLibGetVersion ( + ARM_FFA_MAJOR_VERSION, + ARM_FFA_MINOR_VERSION, + &CurrentMajorVersion, + &CurrentMinorVersion + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + if ((ARM_FFA_MAJOR_VERSION != CurrentMajorVersion) || + (ARM_FFA_MINOR_VERSION > CurrentMinorVersion)) + { + DEBUG (( + DEBUG_INFO, + "Incompatible FF-A Versions for MM_COMM.\n" \ + "Request Version: Major=0x%x, Minor=0x%x.\n" \ + "Current Version: Major=0x%x, Minor>=0x%x.\n", + ARM_FFA_MAJOR_VERSION, + ARM_FFA_MINOR_VERSION, + CurrentMajorVersion, + CurrentMinorVersion + )); + return EFI_UNSUPPORTED; + } + + DEBUG (( + DEBUG_INFO, + "FF-A Version for MM_COMM: Major=0x%x, Minor=0x%x\n", + CurrentMajorVersion, + CurrentMinorVersion + )); + + return EFI_SUCCESS; +} + +/** + Initialize communication via FF-A. + +**/ +STATIC +EFI_STATUS +EFIAPI +InitializeFfaCommunication ( + VOID + ) +{ + EFI_STATUS Status; + VOID *TxBuffer; + UINT64 TxBufferSize; + VOID *RxBuffer; + UINT64 RxBufferSize; + EFI_FFA_PART_INFO_DESC *StmmPartInfo; + UINT32 Count; + UINT32 Size; + + Status = ArmFfaLibPartitionIdGet (&mPartId); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "Failed to get partition id. Status: %r\n", + Status + )); + return Status; + } + + Status = ArmFfaLibGetRxTxBuffers ( + &TxBuffer, + &TxBufferSize, + &RxBuffer, + &RxBufferSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "Failed to get Rx/Tx Buffer. Status: %r\n", + Status + )); + return Status; + } + + Status = ArmFfaLibPartitionInfoGet ( + &gEfiMmCommunication2ProtocolGuid, + FFA_PART_INFO_FLAG_TYPE_DESC, + &Count, + &Size + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "Failed to get Stmm(%g) partition Info. Status: %r\n", + &gEfiMmCommunication2ProtocolGuid, + Status + )); + return Status; + } + + if ((Count != 1) || (Size < sizeof (EFI_FFA_PART_INFO_DESC))) { + Status = EFI_INVALID_PARAMETER; + DEBUG (( + DEBUG_ERROR, + "Invalid partition Info(%g). Count: %d, Size: %d\n", + &gEfiMmCommunication2ProtocolGuid, + Count, + Size + )); + goto ErrorHandler; + } + + StmmPartInfo = (EFI_FFA_PART_INFO_DESC *)RxBuffer; + + if ((StmmPartInfo->PartitionProps & FFA_PART_PROP_RECV_DIRECT_REQ) == 0x00) { + Status = EFI_UNSUPPORTED; + DEBUG ((DEBUG_ERROR, "StandaloneMm doesn't receive DIRECT_MSG_REQ...\n")); + goto ErrorHandler; + } + + mStMmPartId = StmmPartInfo->PartitionId; + +ErrorHandler: + ArmFfaLibRxRelease (mPartId); + return Status; +} + +/** + Initialize mm communication. + +**/ +STATIC +EFI_STATUS +EFIAPI +InitializeCommunication ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EFI_UNSUPPORTED; + + if (IsFfaSupported ()) { + Status = GetFfaCompatibility (); + if (!EFI_ERROR (Status)) { + Status = InitializeFfaCommunication (); + } + } else { + Status = GetMmCompatibility (); + // No further initialisation required for SpmMM + } + + return Status; +} + +/** + Send mm communicate request via FF-A. + + @retval EFI_SUCCESS + @retval Others Error. + +**/ +STATIC +EFI_STATUS +EFIAPI +SendFfaMmCommunicate ( + VOID + ) +{ + EFI_STATUS Status; + DIRECT_MSG_ARGS CommunicateArgs; + + ZeroMem (&CommunicateArgs, sizeof (DIRECT_MSG_ARGS)); + + CommunicateArgs.Arg0 = (UINTN)PcdGet64 (PcdMmBufferBase); + + Status = ArmFfaLibMsgSendDirectReq ( + mStMmPartId, + 0, + &CommunicateArgs + ); + + while (Status == EFI_INTERRUPT_PENDING) { + // We are assuming vCPU0 of the StMM SP since it is UP. + Status = ArmFfaLibRun (mStMmPartId, 0x00); + } + + return Status; +} + +/** + Send mm communicate request via SPM_MM. + + @retval EFI_SUCCESS + @retval Others Error. + +**/ +STATIC +EFI_STATUS +EFIAPI +SendSpmMmCommunicate ( + VOID + ) +{ + EFI_STATUS Status; + ARM_SMC_ARGS CommunicateSmcArgs; + + ZeroMem (&CommunicateSmcArgs, sizeof (ARM_SMC_ARGS)); + + // SMC Function ID + CommunicateSmcArgs.Arg0 = ARM_SMC_ID_MM_COMMUNICATE_AARCH64; + + // Cookie + CommunicateSmcArgs.Arg1 = 0; + + // comm_buffer_address (64-bit physical address) + CommunicateSmcArgs.Arg2 = (UINTN)PcdGet64 (PcdMmBufferBase); + + // comm_size_address (not used, indicated by setting to zero) + CommunicateSmcArgs.Arg3 = 0; + + // Call the Standalone MM environment. + ArmCallSmc (&CommunicateSmcArgs); + + switch (CommunicateSmcArgs.Arg0) { + case ARM_SMC_MM_RET_SUCCESS: + Status = EFI_SUCCESS; + break; + case ARM_SMC_MM_RET_INVALID_PARAMS: + Status = EFI_INVALID_PARAMETER; + break; + case ARM_SMC_MM_RET_DENIED: + Status = EFI_ACCESS_DENIED; + break; + case ARM_SMC_MM_RET_NO_MEMORY: + // Unexpected error since the CommSize was checked for zero length + // prior to issuing the SMC + Status = EFI_OUT_OF_RESOURCES; + ASSERT (0); + break; + default: + Status = EFI_ACCESS_DENIED; + ASSERT (0); + } + + return Status; +} + /** MmCommunicationPeim Communicates with a registered handler. @@ -54,22 +381,9 @@ MmCommunicationPeim ( { EFI_MM_COMMUNICATE_HEADER *CommunicateHeader; EFI_MM_COMMUNICATE_HEADER *TempCommHeader; - ARM_SMC_ARGS CommunicateSmcArgs; EFI_STATUS Status; UINTN BufferSize; - ZeroMem (&CommunicateSmcArgs, sizeof (ARM_SMC_ARGS)); - - // Check that our static buffer is looking good. - // We are using PcdMmBufferBase to transfer variable data. - // We are not using the full size of the buffer since there is a cost - // of copying data between Normal and Secure World. - if ((PcdGet64 (PcdMmBufferBase) == 0) || (PcdGet64 (PcdMmBufferSize) == 0)) { - ASSERT (PcdGet64 (PcdMmBufferSize) > 0); - ASSERT (PcdGet64 (PcdMmBufferBase) != 0); - return EFI_UNSUPPORTED; - } - // // Check parameters // @@ -123,65 +437,32 @@ MmCommunicationPeim ( CopyMem (CommunicateHeader, CommBuffer, *CommSize); - // SMC Function ID - CommunicateSmcArgs.Arg0 = ARM_SMC_ID_MM_COMMUNICATE_AARCH64; - - // Cookie - CommunicateSmcArgs.Arg1 = 0; - - // comm_buffer_address (64-bit physical address) - CommunicateSmcArgs.Arg2 = (UINTN)CommunicateHeader; - - // comm_size_address (not used, indicated by setting to zero) - CommunicateSmcArgs.Arg3 = 0; - - // Call the Standalone MM environment. - ArmCallSmc (&CommunicateSmcArgs); - - switch (CommunicateSmcArgs.Arg0) { - case ARM_SMC_MM_RET_SUCCESS: - // On successful return, the size of data being returned is inferred from - // MessageLength + Header. - BufferSize = CommunicateHeader->MessageLength + - sizeof (CommunicateHeader->HeaderGuid) + - sizeof (CommunicateHeader->MessageLength); - if (BufferSize > (UINTN)PcdGet64 (PcdMmBufferSize)) { - // Something bad has happened, we should have landed in ARM_SMC_MM_RET_NO_MEMORY - DEBUG (( - DEBUG_ERROR, - "%a Returned buffer exceeds communication buffer limit. Has: 0x%llx vs. max: 0x%llx!\n", - __func__, - BufferSize, - (UINTN)PcdGet64 (PcdMmBufferSize) - )); - Status = EFI_BAD_BUFFER_SIZE; - break; - } + if (IsFfaSupported ()) { + Status = SendFfaMmCommunicate (); + } else { + Status = SendSpmMmCommunicate (); + } + if (!EFI_ERROR (Status)) { + // On successful return, the size of data being returned is inferred from + // MessageLength + Header. + BufferSize = CommunicateHeader->MessageLength + + sizeof (CommunicateHeader->HeaderGuid) + + sizeof (CommunicateHeader->MessageLength); + if (BufferSize > (UINTN)PcdGet64 (PcdMmBufferSize)) { + // Something bad has happened, we should have landed in ARM_SMC_MM_RET_NO_MEMORY + Status = EFI_BAD_BUFFER_SIZE; + DEBUG (( + DEBUG_ERROR, + "%a Returned buffer exceeds communication buffer limit. Has: 0x%llx vs. max: 0x%llx!\n", + __func__, + BufferSize, + (UINTN)PcdGet64 (PcdMmBufferSize) + )); + } else { CopyMem (CommBuffer, CommunicateHeader, BufferSize); *CommSize = BufferSize; - Status = EFI_SUCCESS; - break; - - case ARM_SMC_MM_RET_INVALID_PARAMS: - Status = EFI_INVALID_PARAMETER; - break; - - case ARM_SMC_MM_RET_DENIED: - Status = EFI_ACCESS_DENIED; - break; - - case ARM_SMC_MM_RET_NO_MEMORY: - // Unexpected error since the CommSize was checked for zero length - // prior to issuing the SMC - Status = EFI_OUT_OF_RESOURCES; - ASSERT (0); - break; - - default: - Status = EFI_ACCESS_DENIED; - ASSERT (0); - break; + } } return Status; @@ -217,5 +498,23 @@ MmCommunicationPeiInitialize ( IN CONST EFI_PEI_SERVICES **PeiServices ) { + EFI_STATUS Status; + + // Check that our static buffer is looking good. + // We are using PcdMmBufferBase to transfer variable data. + // We are not using the full size of the buffer since there is a cost + // of copying data between Normal and Secure World. + if ((PcdGet64 (PcdMmBufferBase) == 0) || (PcdGet64 (PcdMmBufferSize) == 0)) { + ASSERT (PcdGet64 (PcdMmBufferSize) > 0); + ASSERT (PcdGet64 (PcdMmBufferBase) != 0); + return EFI_UNSUPPORTED; + } + + // Check if we can make the MM call + Status = InitializeCommunication (); + if (EFI_ERROR (Status)) { + return Status; + } + return PeiServicesInstallPpi (&mPeiMmCommunicationPpi); } diff --git a/ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.inf b/ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.inf index c74c539539..2bd4d71826 100644 --- a/ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.inf +++ b/ArmPkg/Drivers/MmCommunicationPei/MmCommunicationPei.inf @@ -25,6 +25,7 @@ [LibraryClasses] DebugLib ArmSmcLib + ArmFfaLib PeimEntryPoint PeiServicesLib HobLib @@ -33,6 +34,9 @@ gArmTokenSpaceGuid.PcdMmBufferBase gArmTokenSpaceGuid.PcdMmBufferSize +[Protocols] + gEfiMmCommunication2ProtocolGuid + [Ppis] gEfiPeiMmCommunicationPpiGuid ## PRODUCES