SecurityPkg: Tpm2DeviceLibFfa: Introduce TPM device library over FF-A

This change introduces a `Tpm2DeviceLibFfa` library to support TPM over
FF-A.

The implementation follows the TPM over FF-A spec v1.0 BET:
https://developer.arm.com/documentation/den0138/latest/

The change is tested on QEMU SBSA virtual platform and proprietary
hardware platforms.

Co-authored-by: Raymond Diaz <raymonddiaz@microsoft.com>
Signed-off-by: Kun Qin <kun.qin@microsoft.com>
This commit is contained in:
kuqin12
2025-02-25 16:05:42 -08:00
committed by mergify[bot]
parent df77417d39
commit 86d5680817
7 changed files with 1313 additions and 0 deletions

View File

@@ -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 <Library/BaseLib.h>
#include <Library/Tpm2DeviceLib.h>
#include <IndustryStandard/Tpm20.h>
#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 ();
}

View File

@@ -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_ */

View File

@@ -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

View File

@@ -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 <IndustryStandard/ArmFfaSvc.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/Tpm2DeviceLib.h>
#include <IndustryStandard/ArmStdSmc.h>
#include <IndustryStandard/Tpm20.h>
#include <Library/TimerLib.h>
#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;
}

View File

@@ -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.<BR>
Copyright (c), Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <IndustryStandard/Tpm20.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/IoLib.h>
#include <Library/TimerLib.h>
#include <Library/DebugLib.h>
#include <Library/Tpm2DeviceLib.h>
#include <Library/PcdLib.h>
#include <Library/IoLib.h>
#include <IndustryStandard/TpmPtp.h>
#include <Guid/Tpm2ServiceFfa.h>
#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));
}

View File

@@ -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 <Uefi.h>
#include <Pi/PiMultiPhase.h>
#include <IndustryStandard/ArmFfaSvc.h>
#include <IndustryStandard/ArmFfaBootInfo.h>
#include <IndustryStandard/ArmFfaPartInfo.h>
#include <Guid/Tpm2ServiceFfa.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/Tpm2DeviceLib.h>
#include <Library/ArmFfaLib.h>
#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;
}

View File

@@ -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