diff --git a/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.c b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.c new file mode 100644 index 0000000000..88b9165bf4 --- /dev/null +++ b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.c @@ -0,0 +1,90 @@ +/** @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 "Tpm2DeviceLibFfa.h" + +/** + Send a command to TPM for execution and return response data. + Used during boot only. + + @retval EFI_SUCCESS Command was successfully sent to the TPM + and the response was copied to the Output buffer. + @retval Other Some error occurred in communication with the TPM. +**/ +EFI_STATUS +EFIAPI +Tpm2SubmitCommand ( + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN OUT UINT32 *OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ) +{ + return FfaTpm2SubmitCommand ( + InputParameterBlockSize, + InputParameterBlock, + OutputParameterBlockSize, + OutputParameterBlock + ); +} + +/** + This service requests use TPM2. + Since every communication with the TPM is blocking + you are always good to start communicating with the TPM. + + @retval EFI_SUCCESS Get the control of TPM2 chip. +**/ +EFI_STATUS +EFIAPI +Tpm2RequestUseTpm ( + VOID + ) +{ + return FfaTpm2RequestUseTpm (); +} + +/** + This service register TPM2 device. + + @param Tpm2Device TPM2 device + + @retval EFI_UNSUPPORTED System does not support register this TPM2 device. +**/ +EFI_STATUS +EFIAPI +Tpm2RegisterTpm2DeviceLib ( + IN TPM2_DEVICE_INTERFACE *Tpm2Device + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Check that we have an address for the CRB + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval EFI_NO_MAPPING The TPM base address is not set up. + @retval EFI_UNSUPPORTED The TPM interface type is not supported. +**/ +EFI_STATUS +EFIAPI +Tpm2DeviceLibFfaConstructor ( + VOID + ) +{ + return InternalTpm2DeviceLibFfaConstructor (); +} diff --git a/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.h b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.h new file mode 100644 index 0000000000..665b594dc1 --- /dev/null +++ b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.h @@ -0,0 +1,213 @@ +/** @file + Provides function interfaces to communicate with TPM 2.0 device + + This library helps to use TPM 2.0 device in library function API + based on SMC using Command Response Buffer (CRB). + + The TPM2DeviceLib library is sitting at the bottom of the TPM stack in UEFI. + It is responsible for sending and receiving commands to and from the TPM. + + This TPM library performs the following actions: + + 1) Receives a TPM command from the upper TPM layers. + 2) Moves the TPM command into the Command/Response Buffer (CRB). + a) The address of the CRB is received through: + gEfiSecurityPkgTokenSpaceGuid.PcdTpmBaseAddress + b) The interface to the CRB is described in: + https://trustedcomputinggroup.org/wp-content/uploads/Mobile-Command-Response-Buffer-Interface-v2-r12-Specification_FINAL2.pdf + 3) Set the CRB start bit to indicate that a TPM command is sitting in the CRB. + 4) Execute an SMC instruction to transfer execution to the Secure Monitor in EL3. + 4) The CRB/TPM command moves through the following components: + NS -> SP -> TPM + 5) After returning from the SMC instruction the TPM command has been processed. + 6) Check status codes etc. + 7) Read the response length from the CRB and copy the response into the output buffer delivered with the TPM command. + 8) Return back to the upper layers of the TPM stack. + + This module is only to be used during boot. This will not persist after exit boot services is called. + + Copyright (c), Microsoft Corporation. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef TPM2_DEVICE_LIB_FFA_H_ +#define TPM2_DEVICE_LIB_FFA_H_ + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Return cached PTP CRB interface IdleByPass state. + + @return Cached PTP CRB interface IdleByPass state. +**/ +UINT8 +GetCachedIdleByPass ( + VOID + ); + +/** + Return PTP interface type. + + @param[in] Register Pointer to PTP register. + + @return PTP interface type. +**/ +TPM2_PTP_INTERFACE_TYPE +Tpm2GetPtpInterface ( + IN VOID *Register + ); + +/** + Return PTP CRB interface IdleByPass state. + + @param[in] Register Pointer to PTP register. + + @return PTP CRB interface IdleByPass state. +**/ +UINT8 +Tpm2GetIdleByPass ( + IN VOID *Register + ); + +/** + This service enables the sending of commands to the TPM2. + + @param[in] InputParameterBlockSize Size of the TPM2 input parameter block. + @param[in] InputParameterBlock Pointer to the TPM2 input parameter block. + @param[in,out] OutputParameterBlockSize Size of the TPM2 output parameter block. + @param[in] OutputParameterBlock Pointer to the TPM2 output parameter block. + + @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 +FfaTpm2SubmitCommand ( + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN OUT UINT32 *OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ); + +/** + This service requests use TPM2 over FF-A. + + @retval EFI_SUCCESS Get the control of TPM2 chip. + @retval EFI_NOT_FOUND TPM2 not found. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +FfaTpm2RequestUseTpm ( + VOID + ); + +/** + Dump PTP register information. + + @param[in] Register Pointer to PTP register. +**/ +VOID +DumpPtpInfo ( + IN VOID *Register + ); + +/** + Check that we have an address for the CRB + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval EFI_NO_MAPPING The TPM base address is not set up. + @retval EFI_UNSUPPORTED The TPM interface type is not supported. +**/ +EFI_STATUS +EFIAPI +InternalTpm2DeviceLibFfaConstructor ( + VOID + ); + +#endif /* _TPM2_DEVICE_LIB_SMC_H_ */ diff --git a/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.inf b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.inf new file mode 100644 index 0000000000..68a0094a06 --- /dev/null +++ b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.inf @@ -0,0 +1,52 @@ +## @file +# Provides function interfaces to communicate with TPM 2.0 device +# +# This library helps to use TPM 2.0 device in library function API +# based on FF-A using Command Response Buffer (CRB). +# +# Copyright (c), Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Tpm2DeviceLibFfa + FILE_GUID = 30AB5914-CB27-4022-89A9-9942FBDB14E7 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = Tpm2DeviceLib + CONSTRUCTOR = Tpm2DeviceLibFfaConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = AARCH64 +# + +[Sources.common] + Tpm2DeviceLibFfa.c + Tpm2ServiceFfaRaw.c + Tpm2DeviceLibFfaBase.c + Tpm2Ptp.c + Tpm2DeviceLibFfa.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + BaseMemoryLib + IoLib + ArmFfaLib + +[Guids] + gTpm2ServiceFfaGuid + +[Pcd.common] + gEfiSecurityPkgTokenSpaceGuid.PcdTpmBaseAddress ## CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdActiveTpmInterfaceType ## PRODUCES + gEfiSecurityPkgTokenSpaceGuid.PcdTpmServiceFfaPartitionId ## SOMETIMES_PRODUCES diff --git a/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfaBase.c b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfaBase.c new file mode 100644 index 0000000000..85815a002f --- /dev/null +++ b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfaBase.c @@ -0,0 +1,83 @@ +/** @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 "Tpm2DeviceLibFfa.h" + +TPM2_PTP_INTERFACE_TYPE mActiveTpmInterfaceType; +UINT8 mCRBIdleByPass; + +/** + Return cached PTP CRB interface IdleByPass state. + + @return Cached PTP CRB interface IdleByPass state. +**/ +UINT8 +GetCachedIdleByPass ( + VOID + ) +{ + return mCRBIdleByPass; +} + +/** + Check that we have an address for the CRB + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval EFI_NO_MAPPING The TPM base address is not set up. + @retval EFI_UNSUPPORTED The TPM interface type is not supported. +**/ +EFI_STATUS +EFIAPI +InternalTpm2DeviceLibFfaConstructor ( + VOID + ) +{ + EFI_STATUS Status; + + mActiveTpmInterfaceType = PcdGet8 (PcdActiveTpmInterfaceType); + mCRBIdleByPass = 0xFF; + + if (PcdGet64 (PcdTpmBaseAddress) == 0) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + // + // Start by checking the PCD out of the gate and read from the CRB if it is invalid + // + if (mActiveTpmInterfaceType == 0xFF) { + mActiveTpmInterfaceType = Tpm2GetPtpInterface ((VOID *)(UINTN)PcdGet64 (PcdTpmBaseAddress)); + PcdSet8S (PcdActiveTpmInterfaceType, mActiveTpmInterfaceType); + } + + if (mActiveTpmInterfaceType != Tpm2PtpInterfaceCrb) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + DEBUG ((DEBUG_INFO, "Setting Tpm Active Interface Type %d\n", mActiveTpmInterfaceType)); + mCRBIdleByPass = Tpm2GetIdleByPass ((VOID *)(UINTN)PcdGet64 (PcdTpmBaseAddress)); + + Status = EFI_SUCCESS; + +Exit: + return Status; +} diff --git a/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2Ptp.c b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2Ptp.c new file mode 100644 index 0000000000..f4574bc3ef --- /dev/null +++ b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2Ptp.c @@ -0,0 +1,433 @@ +/** @file + PTP (Platform TPM Profile) CRB (Command Response Buffer) interface used by dTPM2.0 library. + + Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.
+ 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" + +/** + Check whether TPM PTP register exist. + + @param[in] Reg Pointer to PTP register. + + @retval TRUE TPM PTP exists. + @retval FALSE TPM PTP is not found. +**/ +BOOLEAN +Tpm2IsPtpPresence ( + IN VOID *Reg + ) +{ + UINT8 RegRead; + + RegRead = MmioRead8 ((UINTN)Reg); + if (RegRead == 0xFF) { + // + // No TPM chip + // + return FALSE; + } + + return TRUE; +} + +/** + Return PTP interface type. + + @param[in] Register Pointer to PTP register. + + @return PTP interface type. +**/ +TPM2_PTP_INTERFACE_TYPE +Tpm2GetPtpInterface ( + IN VOID *Register + ) +{ + PTP_CRB_INTERFACE_IDENTIFIER InterfaceId; + PTP_FIFO_INTERFACE_CAPABILITY InterfaceCapability; + + if (!Tpm2IsPtpPresence (Register)) { + return Tpm2PtpInterfaceMax; + } + + // + // Check interface id + // + InterfaceId.Uint32 = MmioRead32 ((UINTN)&((PTP_CRB_REGISTERS *)Register)->InterfaceId); + InterfaceCapability.Uint32 = MmioRead32 ((UINTN)&((PTP_FIFO_REGISTERS *)Register)->InterfaceCapability); + + if ((InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_CRB) && + (InterfaceId.Bits.InterfaceVersion == PTP_INTERFACE_IDENTIFIER_INTERFACE_VERSION_CRB) && + (InterfaceId.Bits.CapCRB != 0)) + { + return Tpm2PtpInterfaceCrb; + } + + if ((InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_FIFO) && + (InterfaceId.Bits.InterfaceVersion == PTP_INTERFACE_IDENTIFIER_INTERFACE_VERSION_FIFO) && + (InterfaceId.Bits.CapFIFO != 0) && + (InterfaceCapability.Bits.InterfaceVersion == INTERFACE_CAPABILITY_INTERFACE_VERSION_PTP)) + { + return Tpm2PtpInterfaceFifo; + } + + if (InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_TIS) { + return Tpm2PtpInterfaceTis; + } + + return Tpm2PtpInterfaceMax; +} + +/** + Return PTP CRB interface IdleByPass state. + + @param[in] Register Pointer to PTP register. + + @return PTP CRB interface IdleByPass state. +**/ +UINT8 +Tpm2GetIdleByPass ( + IN VOID *Register + ) +{ + PTP_CRB_INTERFACE_IDENTIFIER InterfaceId; + + // + // Check interface id + // + InterfaceId.Uint32 = MmioRead32 ((UINTN)&((PTP_CRB_REGISTERS *)Register)->InterfaceId); + + return (UINT8)(InterfaceId.Bits.CapCRBIdleBypass); +} + +/** + Get the control of TPM chip. + + @param[in] CrbReg Pointer to CRB register. + + @retval EFI_SUCCESS Get the control of TPM chip. + @retval EFI_INVALID_PARAMETER CrbReg is NULL. + @retval EFI_NOT_FOUND TPM chip doesn't exit. + @retval EFI_TIMEOUT Can't get the TPM control in time. +**/ +EFI_STATUS +PtpCrbRequestUseTpm ( + IN PTP_CRB_REGISTERS_PTR CrbReg + ) +{ + EFI_STATUS Status; + + if (!Tpm2IsPtpPresence (CrbReg)) { + return EFI_NOT_FOUND; + } + + MmioWrite32 ((UINTN)&CrbReg->LocalityControl, PTP_CRB_LOCALITY_CONTROL_REQUEST_ACCESS); + Status = Tpm2ServiceStart ( + TPM2_FFA_START_FUNC_QUALIFIER_LOCALITY, + 0 + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "PtpCrbRequestUseTpm: Request access failed - %r\n", Status)); + goto Exit; + } + + // Double check to see if the locality we requested is granted or not. + if ((MmioRead32 ((UINTN)&CrbReg->LocalityStatus) & PTP_CRB_LOCALITY_STATUS_GRANTED) == 0) { + DEBUG ((DEBUG_ERROR, "PtpCrbRequestUseTpm: Locality not granted - %r\n", Status)); + Status = EFI_TIMEOUT; + goto Exit; + } + +Exit: + return Status; +} + +/** + Send a command to TPM for execution and return response data. + + @param[in] CrbReg TPM register space base address. + @param[in] BufferIn Buffer for command data. + @param[in] SizeIn Size of command data. + @param[in, out] BufferOut Buffer for response data. + @param[in, out] SizeOut Size of response data. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_BUFFER_TOO_SMALL Response data buffer is too small. + @retval EFI_DEVICE_ERROR Unexpected device behavior. + @retval EFI_UNSUPPORTED Unsupported TPM version + +**/ +EFI_STATUS +PtpCrbTpmCommand ( + IN PTP_CRB_REGISTERS_PTR CrbReg, + IN UINT8 *BufferIn, + IN UINT32 SizeIn, + IN OUT UINT8 *BufferOut, + IN OUT UINT32 *SizeOut + ) +{ + EFI_STATUS Status; + UINT32 Index; + UINT32 TpmOutSize; + UINT16 Data16; + UINT32 Data32; + + DEBUG_CODE_BEGIN (); + UINTN DebugSize; + + DEBUG ((DEBUG_VERBOSE, "PtpCrbTpmCommand Send - ")); + if (SizeIn > 0x100) { + DebugSize = 0x40; + } else { + DebugSize = SizeIn; + } + + for (Index = 0; Index < DebugSize; Index++) { + DEBUG ((DEBUG_VERBOSE, "%02x ", BufferIn[Index])); + } + + if (DebugSize != SizeIn) { + DEBUG ((DEBUG_VERBOSE, "...... ")); + for (Index = SizeIn - 0x20; Index < SizeIn; Index++) { + DEBUG ((DEBUG_VERBOSE, "%02x ", BufferIn[Index])); + } + } + + DEBUG ((DEBUG_VERBOSE, "\n")); + DEBUG_CODE_END (); + + TpmOutSize = 0; + + // + // STEP 1: + // Ready is any time the TPM is ready to receive a command, following a write + // of 1 by software to Request.cmdReady, as indicated by the Status field + // being cleared to 0. + // + MmioWrite32 ((UINTN)&CrbReg->CrbControlRequest, PTP_CRB_CONTROL_AREA_REQUEST_COMMAND_READY); + Status = Tpm2ServiceStart ( + TPM2_FFA_START_FUNC_QUALIFIER_COMMAND, + 0 + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "PtpCrbTpmCommand: Request command ready failed - %r\n", Status)); + goto Exit; + } + + for (Index = 0; Index < SizeIn; Index++) { + MmioWrite8 ((UINTN)&CrbReg->CrbDataBuffer[Index], BufferIn[Index]); + } + + // + // STEP 2: + // Command Execution occurs after receipt of a 1 to Start and the TPM + // clearing Start to 0. + // + MmioWrite32 ((UINTN)&CrbReg->CrbControlStart, PTP_CRB_CONTROL_START); + Status = Tpm2ServiceStart ( + TPM2_FFA_START_FUNC_QUALIFIER_COMMAND, + 0 + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "PtpCrbTpmCommand: Control start failed - %r\n", Status)); + goto Exit; + } + + // + // STEP 3: + // Command Completion occurs after completion of a command (indicated by the + // TPM clearing TPM_CRB_CTRL_Start_x to 0) and before a write of a 1 by the + // software to Request.goIdle. + // + + // + // Get response data header + // + for (Index = 0; Index < sizeof (TPM2_RESPONSE_HEADER); Index++) { + BufferOut[Index] = MmioRead8 ((UINTN)&CrbReg->CrbDataBuffer[Index]); + } + + DEBUG_CODE_BEGIN (); + DEBUG ((DEBUG_VERBOSE, "PtpCrbTpmCommand ReceiveHeader - ")); + for (Index = 0; Index < sizeof (TPM2_RESPONSE_HEADER); Index++) { + DEBUG ((DEBUG_VERBOSE, "%02x ", BufferOut[Index])); + } + + DEBUG ((DEBUG_VERBOSE, "\n")); + DEBUG_CODE_END (); + // + // Check the response data header (tag, parasize and returncode) + // + CopyMem (&Data16, BufferOut, sizeof (UINT16)); + // TPM2 should not use this RSP_COMMAND + if (SwapBytes16 (Data16) == TPM_ST_RSP_COMMAND) { + DEBUG ((DEBUG_ERROR, "TPM2: TPM_ST_RSP error - %x\n", TPM_ST_RSP_COMMAND)); + Status = EFI_UNSUPPORTED; + goto Exit; + } + + CopyMem (&Data32, (BufferOut + 2), sizeof (UINT32)); + TpmOutSize = SwapBytes32 (Data32); + if (*SizeOut < TpmOutSize) { + // + // Command completed, but buffer is not enough + // + Status = EFI_BUFFER_TOO_SMALL; + goto Exit; + } + + *SizeOut = TpmOutSize; + // + // Continue reading the remaining data + // + for (Index = sizeof (TPM2_RESPONSE_HEADER); Index < TpmOutSize; Index++) { + BufferOut[Index] = MmioRead8 ((UINTN)&CrbReg->CrbDataBuffer[Index]); + } + + DEBUG_CODE_BEGIN (); + DEBUG ((DEBUG_VERBOSE, "PtpCrbTpmCommand Receive - ")); + for (Index = 0; Index < TpmOutSize; Index++) { + DEBUG ((DEBUG_VERBOSE, "%02x ", BufferOut[Index])); + } + + DEBUG ((DEBUG_VERBOSE, "\n")); + DEBUG_CODE_END (); + +Exit: + + // + // Return to Idle state by setting TPM_CRB_CTRL_STS_x.Status.goIdle to 1. + // + MmioWrite32 ((UINTN)&CrbReg->CrbControlRequest, PTP_CRB_CONTROL_AREA_REQUEST_GO_IDLE); + Status = Tpm2ServiceStart ( + TPM2_FFA_START_FUNC_QUALIFIER_COMMAND, + 0 + ); + + return Status; +} + +/** + Dump PTP register information. + + @param[in] Register Pointer to PTP register. +**/ +VOID +DumpPtpInfo ( + IN VOID *Register + ) +{ + PTP_CRB_INTERFACE_IDENTIFIER InterfaceId; + PTP_FIFO_INTERFACE_CAPABILITY InterfaceCapability; + UINT8 StatusEx; + UINT16 Vid; + UINT16 Did; + UINT8 Rid; + + if (!Tpm2IsPtpPresence (Register)) { + return; + } + + InterfaceId.Uint32 = MmioRead32 ((UINTN)&((PTP_CRB_REGISTERS *)Register)->InterfaceId); + InterfaceCapability.Uint32 = MmioRead32 ((UINTN)&((PTP_FIFO_REGISTERS *)Register)->InterfaceCapability); + StatusEx = MmioRead8 ((UINTN)&((PTP_FIFO_REGISTERS *)Register)->StatusEx); + + // + // Dump InterfaceId Register for PTP + // + DEBUG ((DEBUG_INFO, "InterfaceId - 0x%08x\n", InterfaceId.Uint32)); + DEBUG ((DEBUG_INFO, " InterfaceType - 0x%02x\n", InterfaceId.Bits.InterfaceType)); + if (InterfaceId.Bits.InterfaceType != PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_TIS) { + DEBUG ((DEBUG_INFO, " InterfaceVersion - 0x%02x\n", InterfaceId.Bits.InterfaceVersion)); + DEBUG ((DEBUG_INFO, " CapFIFO - 0x%x\n", InterfaceId.Bits.CapFIFO)); + DEBUG ((DEBUG_INFO, " CapCRB - 0x%x\n", InterfaceId.Bits.CapCRB)); + } + + // + // Dump Capability Register for TIS and FIFO + // + DEBUG ((DEBUG_INFO, "InterfaceCapability - 0x%08x\n", InterfaceCapability.Uint32)); + if ((InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_TIS) || + (InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_FIFO)) + { + DEBUG ((DEBUG_INFO, " InterfaceVersion - 0x%x\n", InterfaceCapability.Bits.InterfaceVersion)); + } + + // + // Dump StatusEx Register for PTP FIFO + // + DEBUG ((DEBUG_INFO, "StatusEx - 0x%02x\n", StatusEx)); + if (InterfaceCapability.Bits.InterfaceVersion == INTERFACE_CAPABILITY_INTERFACE_VERSION_PTP) { + DEBUG ((DEBUG_INFO, " TpmFamily - 0x%x\n", (StatusEx & PTP_FIFO_STS_EX_TPM_FAMILY) >> PTP_FIFO_STS_EX_TPM_FAMILY_OFFSET)); + } + + Vid = MmioRead16 ((UINTN)&((PTP_CRB_REGISTERS *)Register)->Vid); + Did = MmioRead16 ((UINTN)&((PTP_CRB_REGISTERS *)Register)->Did); + Rid = (UINT8)InterfaceId.Bits.Rid; + + DEBUG ((DEBUG_INFO, "VID - 0x%04x\n", Vid)); + DEBUG ((DEBUG_INFO, "DID - 0x%04x\n", Did)); + DEBUG ((DEBUG_INFO, "RID - 0x%02x\n", Rid)); +} + +/** + This service enables the sending of commands to the TPM2. + + @param[in] InputParameterBlockSize Size of the TPM2 input parameter block. + @param[in] InputParameterBlock Pointer to the TPM2 input parameter block. + @param[in,out] OutputParameterBlockSize Size of the TPM2 output parameter block. + @param[in] OutputParameterBlock Pointer to the TPM2 output parameter block. + + @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 +FfaTpm2SubmitCommand ( + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN OUT UINT32 *OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ) +{ + return PtpCrbTpmCommand ( + (PTP_CRB_REGISTERS_PTR)(UINTN)PcdGet64 (PcdTpmBaseAddress), + InputParameterBlock, + InputParameterBlockSize, + OutputParameterBlock, + OutputParameterBlockSize + ); +} + +/** + This service requests use TPM2 over FF-A. + + @retval EFI_SUCCESS Get the control of TPM2 chip. + @retval EFI_NOT_FOUND TPM2 not found. + @retval EFI_DEVICE_ERROR Unexpected device behavior. +**/ +EFI_STATUS +FfaTpm2RequestUseTpm ( + VOID + ) +{ + return PtpCrbRequestUseTpm ((PTP_CRB_REGISTERS_PTR)(UINTN)PcdGet64 (PcdTpmBaseAddress)); +} diff --git a/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2ServiceFfaRaw.c b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2ServiceFfaRaw.c new file mode 100644 index 0000000000..3e36bcfaea --- /dev/null +++ b/SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2ServiceFfaRaw.c @@ -0,0 +1,441 @@ +/** @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" + +UINT32 mFfaTpm2PartitionId = MAX_UINT32; + +/** + 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. + + @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 An error occurred in communication with the TPM. +**/ +EFI_STATUS +GetTpmServicePartitionId ( + OUT UINT32 *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; + } + + if (mFfaTpm2PartitionId != MAX_UINT32) { + *PartitionId = mFfaTpm2PartitionId; + Status = EFI_SUCCESS; + goto Exit; + } + + if (PcdGet16 (PcdTpmServiceFfaPartitionId) != 0) { + mFfaTpm2PartitionId = PcdGet16 (PcdTpmServiceFfaPartitionId); + *PartitionId = mFfaTpm2PartitionId; + Status = EFI_SUCCESS; + + 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; + mFfaTpm2PartitionId = TpmPartInfo->PartitionId; + *PartitionId = mFfaTpm2PartitionId; + + Status = PcdSet16S (PcdTpmServiceFfaPartitionId, mFfaTpm2PartitionId); + } + +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 == MAX_UINT32) { + 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 == MAX_UINT32) { + 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 == MAX_UINT32) { + 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 == MAX_UINT32) { + 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 == MAX_UINT32) { + 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 == MAX_UINT32) { + 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; +} diff --git a/SecurityPkg/SecurityPkg.dsc b/SecurityPkg/SecurityPkg.dsc index 7bf5df1969..83c80004a2 100644 --- a/SecurityPkg/SecurityPkg.dsc +++ b/SecurityPkg/SecurityPkg.dsc @@ -425,6 +425,7 @@ [Components.AARCH64] SecurityPkg/Tcg/Tcg2StandaloneMmArm/Tcg2StandaloneMmArm.inf SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.inf + SecurityPkg/Library/Tpm2DeviceLibFfa/Tpm2DeviceLibFfa.inf [BuildOptions] MSFT:*_*_IA32_DLINK_FLAGS = /ALIGN:256