/** @file This library provides an implementation of Tpm2DeviceLib using ARM64 SMC calls to request TPM service. The implementation is only supporting the Command Response Buffer (CRB) for sharing data with the TPM. Copyright (c), Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include "Tpm2DeviceLibFfa.h" UINT16 mFfaTpm2PartitionId = TPM2_FFA_PARTITION_ID_INVALID; /** Check the return status from the FF-A call and returns EFI_STATUS @param EFI_LOAD_ERROR FF-A status code returned in x0 @retval EFI_SUCCESS The entry point is executed successfully. **/ EFI_STATUS TranslateTpmReturnStatus ( UINTN TpmReturnStatus ) { EFI_STATUS Status; switch (TpmReturnStatus) { case TPM2_FFA_SUCCESS_OK: case TPM2_FFA_SUCCESS_OK_RESULTS_RETURNED: Status = EFI_SUCCESS; break; case TPM2_FFA_ERROR_NOFUNC: Status = EFI_NOT_FOUND; break; case TPM2_FFA_ERROR_NOTSUP: Status = EFI_UNSUPPORTED; break; case TPM2_FFA_ERROR_INVARG: Status = EFI_INVALID_PARAMETER; break; case TPM2_FFA_ERROR_INV_CRB_CTRL_DATA: Status = EFI_COMPROMISED_DATA; break; case TPM2_FFA_ERROR_ALREADY: Status = EFI_ALREADY_STARTED; break; case TPM2_FFA_ERROR_DENIED: Status = EFI_ACCESS_DENIED; break; case TPM2_FFA_ERROR_NOMEM: Status = EFI_OUT_OF_RESOURCES; break; default: Status = EFI_DEVICE_ERROR; } return Status; } /** This function is used to get the TPM service partition id via FF-A. @param[out] PartitionId - Supplies the pointer to the TPM service partition id. @retval EFI_SUCCESS The TPM command was successfully sent to the TPM and the response was copied to the Output buffer. @retval EFI_INVALID_PARAMETER The TPM command buffer is NULL or the TPM command buffer size is 0. @retval EFI_DEVICE_ERROR The TPM partition information is wrong. @retval EFI_DEVICE_ERROR An error occurred in communication with the TPM. **/ EFI_STATUS FfaTpm2GetServicePartitionId ( OUT UINT16 *PartitionId ) { EFI_STATUS Status; UINT32 Count; UINT32 Size; EFI_FFA_PART_INFO_DESC *TpmPartInfo; VOID *TxBuffer; UINT64 TxBufferSize; VOID *RxBuffer; UINT64 RxBufferSize; UINT16 PartId; if (PartitionId == NULL) { Status = EFI_INVALID_PARAMETER; goto Exit; } Status = ArmFfaLibPartitionIdGet (&PartId); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "Failed to get partition id. Status: %r\n", Status )); goto Exit; } Status = ArmFfaLibGetRxTxBuffers ( &TxBuffer, &TxBufferSize, &RxBuffer, &RxBufferSize ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to get Rx/Tx Buffer. Status: %r\n", Status)); goto Exit; } Status = ArmFfaLibPartitionInfoGet ( &gTpm2ServiceFfaGuid, FFA_PART_INFO_FLAG_TYPE_DESC, &Count, &Size ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to get Tpm2 partition info. Status: %r\n", Status)); goto RxRelease; } 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", &gTpm2ServiceFfaGuid, Count, Size)); } else { TpmPartInfo = (EFI_FFA_PART_INFO_DESC *)RxBuffer; *PartitionId = TpmPartInfo->PartitionId; if (TpmPartInfo->PartitionId == TPM2_FFA_PARTITION_ID_INVALID) { /* * Tpm partition id never be TPM2_FFA_PARTITION_ID_INVALID. */ Status = EFI_DEVICE_ERROR; } } RxRelease: ArmFfaLibRxRelease (PartId); Exit: return Status; } /** This function is used to get the TPM interface version. @param[out] Version - Supplies the pointer to the TPM interface version. @retval EFI_SUCCESS The TPM command was successfully sent to the TPM and the response was copied to the Output buffer. @retval EFI_INVALID_PARAMETER The TPM command buffer is NULL or the TPM command buffer size is 0. @retval EFI_DEVICE_ERROR An error occurred in communication with the TPM. **/ EFI_STATUS Tpm2GetInterfaceVersion ( OUT UINT32 *Version ) { EFI_STATUS Status; DIRECT_MSG_ARGS FfaDirectReq2Args; if (Version == NULL) { Status = EFI_INVALID_PARAMETER; goto Exit; } if (mFfaTpm2PartitionId == TPM2_FFA_PARTITION_ID_INVALID) { GetTpmServicePartitionId (&mFfaTpm2PartitionId); } ZeroMem (&FfaDirectReq2Args, sizeof (DIRECT_MSG_ARGS)); FfaDirectReq2Args.Arg0 = TPM2_FFA_GET_INTERFACE_VERSION; Status = ArmFfaLibMsgSendDirectReq2 (mFfaTpm2PartitionId, &gTpm2ServiceFfaGuid, &FfaDirectReq2Args); while (Status == EFI_INTERRUPT_PENDING) { // We are assuming vCPU0 of the TPM SP since it is UP. Status = ArmFfaLibRun (mFfaTpm2PartitionId, 0x00); } if (EFI_ERROR (Status)) { goto Exit; } Status = TranslateTpmReturnStatus (FfaDirectReq2Args.Arg0); if (!EFI_ERROR (Status)) { *Version = FfaDirectReq2Args.Arg1; } Exit: return Status; } /** This function is used to get the TPM feature information. @param[out] FeatureInfo - Supplies the pointer to the feature information. @retval EFI_SUCCESS The TPM command was successfully sent to the TPM and the response was copied to the Output buffer. @retval EFI_INVALID_PARAMETER The TPM command buffer is NULL or the TPM command buffer size is 0. @retval EFI_DEVICE_ERROR An error occurred in communication with the TPM. **/ EFI_STATUS Tpm2GetFeatureInfo ( OUT UINT32 *FeatureInfo ) { EFI_STATUS Status; DIRECT_MSG_ARGS FfaDirectReq2Args; if (FeatureInfo == NULL) { Status = EFI_INVALID_PARAMETER; goto Exit; } if (mFfaTpm2PartitionId == TPM2_FFA_PARTITION_ID_INVALID) { GetTpmServicePartitionId (&mFfaTpm2PartitionId); } ZeroMem (&FfaDirectReq2Args, sizeof (DIRECT_MSG_ARGS)); FfaDirectReq2Args.Arg0 = TPM2_FFA_GET_FEATURE_INFO; FfaDirectReq2Args.Arg1 = TPM_SERVICE_FEATURE_SUPPORT_NOTIFICATION; Status = ArmFfaLibMsgSendDirectReq2 (mFfaTpm2PartitionId, &gTpm2ServiceFfaGuid, &FfaDirectReq2Args); while (Status == EFI_INTERRUPT_PENDING) { // We are assuming vCPU0 of the TPM SP since it is UP. Status = ArmFfaLibRun (mFfaTpm2PartitionId, 0x00); } if (EFI_ERROR (Status)) { goto Exit; } Status = TranslateTpmReturnStatus (FfaDirectReq2Args.Arg0); Exit: return Status; } /** This service enables the sending of commands to the TPM2. @param[in] FuncQualifier Function qualifier. @param[in] LocalityQualifier Locality qualifier. @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received. @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device. @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. **/ EFI_STATUS Tpm2ServiceStart ( IN UINT64 FuncQualifier, IN UINT64 LocalityQualifier ) { EFI_STATUS Status; DIRECT_MSG_ARGS FfaDirectReq2Args; if (mFfaTpm2PartitionId == TPM2_FFA_PARTITION_ID_INVALID) { GetTpmServicePartitionId (&mFfaTpm2PartitionId); } ZeroMem (&FfaDirectReq2Args, sizeof (DIRECT_MSG_ARGS)); FfaDirectReq2Args.Arg0 = TPM2_FFA_START; FfaDirectReq2Args.Arg1 = (FuncQualifier & 0xFF); FfaDirectReq2Args.Arg2 = (LocalityQualifier & 0xFF); Status = ArmFfaLibMsgSendDirectReq2 (mFfaTpm2PartitionId, &gTpm2ServiceFfaGuid, &FfaDirectReq2Args); while (Status == EFI_INTERRUPT_PENDING) { // We are assuming vCPU0 of the TPM SP since it is UP. Status = ArmFfaLibRun (mFfaTpm2PartitionId, 0x00); } if (EFI_ERROR (Status)) { goto Exit; } Status = TranslateTpmReturnStatus (FfaDirectReq2Args.Arg0); Exit: return Status; } /** Register TPM2 device notification. @param[in] NotificationTypeQualifier Notification type qualifier. @param[in] vCpuId vCPU ID. @param[in] NotificationId Bitmap ID for the notification. @retval EFI_SUCCESS The command was successfully sent to the device and a response was successfully received. @retval Others Some error occurred in communication with the device. **/ EFI_STATUS Tpm2RegisterNotification ( IN BOOLEAN NotificationTypeQualifier, IN UINT16 vCpuId, IN UINT64 NotificationId ) { EFI_STATUS Status; DIRECT_MSG_ARGS FfaDirectReq2Args; if (mFfaTpm2PartitionId == TPM2_FFA_PARTITION_ID_INVALID) { GetTpmServicePartitionId (&mFfaTpm2PartitionId); } ZeroMem (&FfaDirectReq2Args, sizeof (DIRECT_MSG_ARGS)); FfaDirectReq2Args.Arg0 = TPM2_FFA_REGISTER_FOR_NOTIFICATION; FfaDirectReq2Args.Arg1 = (NotificationTypeQualifier << 16 | vCpuId); FfaDirectReq2Args.Arg2 = (NotificationId & 0xFF); Status = ArmFfaLibMsgSendDirectReq2 (mFfaTpm2PartitionId, &gTpm2ServiceFfaGuid, &FfaDirectReq2Args); while (Status == EFI_INTERRUPT_PENDING) { // We are assuming vCPU0 of the TPM SP since it is UP. Status = ArmFfaLibRun (mFfaTpm2PartitionId, 0x00); } if (EFI_ERROR (Status)) { goto Exit; } Status = TranslateTpmReturnStatus (FfaDirectReq2Args.Arg0); Exit: return Status; } /** Unregister TPM2 device notification. @retval EFI_SUCCESS The command was successfully sent to the device and a response was successfully received. @retval Others Some error occurred in communication with the device. **/ EFI_STATUS Tpm2UnregisterNotification ( VOID ) { EFI_STATUS Status; DIRECT_MSG_ARGS FfaDirectReq2Args; if (mFfaTpm2PartitionId == TPM2_FFA_PARTITION_ID_INVALID) { GetTpmServicePartitionId (&mFfaTpm2PartitionId); } ZeroMem (&FfaDirectReq2Args, sizeof (DIRECT_MSG_ARGS)); FfaDirectReq2Args.Arg0 = TPM2_FFA_UNREGISTER_FROM_NOTIFICATION; Status = ArmFfaLibMsgSendDirectReq2 (mFfaTpm2PartitionId, &gTpm2ServiceFfaGuid, &FfaDirectReq2Args); while (Status == EFI_INTERRUPT_PENDING) { // We are assuming vCPU0 of the TPM SP since it is UP. Status = ArmFfaLibRun (mFfaTpm2PartitionId, 0x00); } if (EFI_ERROR (Status)) { goto Exit; } Status = TranslateTpmReturnStatus (FfaDirectReq2Args.Arg0); Exit: return Status; } /** Issue a finished notification command to the TPM service over FF-A. @retval EFI_SUCCESS The command was successfully sent to the device and a response was successfully received. @retval Others Some error occurred in communication with the device. **/ EFI_STATUS Tpm2FinishNotified ( VOID ) { EFI_STATUS Status; DIRECT_MSG_ARGS FfaDirectReq2Args; if (mFfaTpm2PartitionId == TPM2_FFA_PARTITION_ID_INVALID) { GetTpmServicePartitionId (&mFfaTpm2PartitionId); } ZeroMem (&FfaDirectReq2Args, sizeof (DIRECT_MSG_ARGS)); FfaDirectReq2Args.Arg0 = TPM2_FFA_FINISH_NOTIFIED; Status = ArmFfaLibMsgSendDirectReq2 (mFfaTpm2PartitionId, &gTpm2ServiceFfaGuid, &FfaDirectReq2Args); while (Status == EFI_INTERRUPT_PENDING) { // We are assuming vCPU0 of the TPM SP since it is UP. Status = ArmFfaLibRun (mFfaTpm2PartitionId, 0x00); } if (EFI_ERROR (Status)) { goto Exit; } Status = TranslateTpmReturnStatus (FfaDirectReq2Args.Arg0); Exit: return Status; }