alder_lake_bios/Insyde/InsydeModulePkg/Library/Tpm2PtpCommLib/Tpm2Ptp.c

450 lines
12 KiB
C

/** @file
This library abstract how to access TPM2 hardware device.
;******************************************************************************
;* Copyright (c) 2014 - 2016, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#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/PcdLib.h>
#include <Library/Tpm2PtpCommLib.h>
#ifdef EFI_DEBUG
/**
Prints command or response buffer for debugging purposes.
@param[in] Buffer Buffer to print.
@param[in] BufferSize Buffer data length.
**/
VOID
EFIAPI
PtpPrintBuffer(IN UINT8 *Buffer, IN UINT32 BufferSize)
{
UINT32 Index;
DEBUG ((DEBUG_INFO, "Buffer Address: 0x%08x, Size: 0x%08x, Value:\n", Buffer, BufferSize));
for(Index = 0; Index < BufferSize; Index++){
DEBUG ((DEBUG_INFO, "%02x ", *(Buffer + Index)));
if((Index+1) % 16 == 0) DEBUG ((DEBUG_INFO, "\n"));
}
DEBUG ((DEBUG_INFO, "\n"));
}
#endif // EFI_DEBUG
/**
Copy data from MMIO region to system memory by using 8-bit access.
Copy data from MMIO region specified by starting address StartAddress
to system memory specified by Buffer by using 8-bit access. The total
number of byte to be copied is specified by Length. Buffer is returned.
If Length is greater than (MAX_ADDRESS - StartAddress + 1), then ASSERT().
If Length is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT().
@param StartAddress The starting address for the MMIO region to be copied from.
@param Length The size in bytes of the copy.
@param Buffer The pointer to a system memory buffer receiving the data read.
@return Buffer
**/
UINT8 *
EFIAPI
MmioReadBuffer8 (
IN UINTN StartAddress,
IN UINTN Length,
OUT UINT8 *Buffer
)
{
UINT8 *ReturnBuffer;
ASSERT ((Length - 1) <= (MAX_ADDRESS - StartAddress));
ASSERT ((Length - 1) <= (MAX_ADDRESS - (UINTN) Buffer));
ReturnBuffer = Buffer;
while (Length-- > 0) {
*(Buffer++) = MmioRead8 (StartAddress++);
}
return ReturnBuffer;
}
/**
Copy data from system memory to the MMIO region by using 8-bit access.
Copy data from system memory specified by Buffer to the MMIO region specified
by starting address StartAddress by using 8-bit access. The total number
of byte to be copied is specified by Length. Buffer is returned.
If Length is greater than (MAX_ADDRESS - StartAddress + 1), then ASSERT().
If Length is greater than (MAX_ADDRESS -Buffer + 1), then ASSERT().
@param StartAddress The starting address for the MMIO region to be copied to.
@param Length The size, in bytes, of Buffer.
@param Buffer The pointer to a system memory buffer containing the data to write.
@return Buffer
**/
UINT8 *
EFIAPI
MmioWriteBuffer8 (
IN UINTN StartAddress,
IN UINTN Length,
IN CONST UINT8 *Buffer
)
{
VOID* ReturnBuffer;
ASSERT ((Length - 1) <= (MAX_ADDRESS - StartAddress));
ASSERT ((Length - 1) <= (MAX_ADDRESS - (UINTN) Buffer));
ReturnBuffer = (UINT8 *) Buffer;
while (Length-- != 0) {
MmioWrite8 (StartAddress++, *(Buffer++));
}
return ReturnBuffer;
}
/**
Check whether CRB interface activated.
@retval TRUE CRB is active.
@retval FALSE CRB is not active.
**/
BOOLEAN
EFIAPI
IsCrbInterfaceActive (
VOID
)
{
UINT8 InterfaceType;
InterfaceType = MmioRead8 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_TPM_INTERFACE_ID) & 0xF;
if (InterfaceType == B_TPM_INTERFACE_CRB) {
return TRUE;
}
return FALSE;
}
/**
Sets TPM_LOC_CTRL register to a defined value to indicate that a command is
available for processing.
@retval EFI_SUCCESS Register successfully written.
@retval TBD
**/
EFI_STATUS
EFIAPI
PtpTpm2RequestUseTpm (
VOID
)
{
UINT32 LocalityState;
UINT32 ControlStatus;
UINT32 WaitTime;
//
// Make sure TPM is not in fatal error state
//
ControlStatus = MmioRead32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_STS);
if (( ControlStatus & B_CRB_CONTROL_STS_TPM_STATUS ) != 0 ) {
DEBUG ((DEBUG_INFO, "ControlStatus = 0x%x\n", ControlStatus));
}
LocalityState = MmioRead32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_PTP_LOCALITY_STATE);
if((((LocalityState & V_CRB_LOCALITY_STATE_ACTIVE_LOC_MASK) >> 2) == 0) &&
((LocalityState & B_CRB_LOCALITY_STATE_LOCALITY_ASSIGNED) != 0)) {
DEBUG ((DEBUG_INFO, "Locality 0 already assigned\n"));
return EFI_SUCCESS;
} else {
DEBUG ((DEBUG_INFO, "Requesting Locality\n"));
}
//
// Request access to locality
//
MmioWrite32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_TPM_LOCALITY_CONTROL,
B_CRB_LOCALITY_CTL_REQUEST_ACCESS);
//
// Wait for assignment of locality
//
LocalityState = 0;
WaitTime = 0;
while ((WaitTime < PTP_HCI_TIMEOUT_A) &&
(((LocalityState & B_CRB_LOCALITY_STATE_REGISTER_VALID) != B_CRB_LOCALITY_STATE_REGISTER_VALID) ||
((LocalityState & B_CRB_LOCALITY_STATE_LOCALITY_ASSIGNED) != B_CRB_LOCALITY_STATE_LOCALITY_ASSIGNED) ||
(((LocalityState & V_CRB_LOCALITY_STATE_ACTIVE_LOC_MASK) >> 2) == 0))) {
LocalityState = MmioRead32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_PTP_LOCALITY_STATE);
MicroSecondDelay (PTP_HCI_POLLING_PERIOD);
WaitTime += PTP_HCI_POLLING_PERIOD;
}
if (WaitTime >= PTP_HCI_TIMEOUT_A) {
return EFI_TIMEOUT;
}
return EFI_SUCCESS;
}
/**
Sends command to TPM for execution.
@param[in] Buffer Buffer for TPM command data.
@param[in] DataLength TPM command data length.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_TIMEOUT The register can't run into the expected status in time.
**/
EFI_STATUS
EFIAPI
PtpTpm2Send(
IN UINT8 *Buffer,
IN UINT32 DataLength
)
{
UINT32 ControlStatus;
UINT32 WaitTime;
//
// Make sure TPM is not in fatal error state
//
ControlStatus = MmioRead32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_STS);
if (( ControlStatus & B_CRB_CONTROL_STS_TPM_STATUS ) != 0 ) {
return EFI_DEVICE_ERROR;
}
//
// Wait for tpm to go Idle and finish previous command
//
WaitTime = 0;
while ((WaitTime < PTP_HCI_TIMEOUT_C) &&
((ControlStatus & B_CRB_CONTROL_STS_TPM_IDLE) != B_CRB_CONTROL_STS_TPM_IDLE)) {
ControlStatus = MmioRead32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_STS);
MicroSecondDelay (PTP_HCI_POLLING_PERIOD);
WaitTime += PTP_HCI_POLLING_PERIOD;
}
if (WaitTime >= PTP_HCI_TIMEOUT_C) {
DEBUG ((DEBUG_INFO, "TPM Already in command ready state \n"));
}
//
// Request TPM to come out of idle
//
MmioWrite32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_REQ, B_R_CRB_CONTROL_REQ_COMMAND_READY);
//
// Wait for tpm to clear tpmidle
//
WaitTime = 0;
while ((WaitTime < PTP_HCI_TIMEOUT_C) &&
((ControlStatus & B_CRB_CONTROL_STS_TPM_IDLE) != 0)) {
ControlStatus = MmioRead32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_STS);
MicroSecondDelay (PTP_HCI_POLLING_PERIOD);
WaitTime += PTP_HCI_POLLING_PERIOD;
}
if (WaitTime >= PTP_HCI_TIMEOUT_C) {
return EFI_TIMEOUT;
}
MmioWriteBuffer8 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_DATA_BUFFER, DataLength, (UINT8*)Buffer);
//
// Trigger Command processing by writing to start register
//
MmioWrite32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_START, B_CRB_CONTROL_START);
return EFI_SUCCESS;
}
/**
Receives response data of last command from TPM.
@param[out] Buffer Buffer for response data.
@param[out] RespSize Response data length.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_TIMEOUT The register can't run into the expected status in time.
@retval EFI_DEVICE_ERROR Unexpected device status.
@retval EFI_BUFFER_TOO_SMALL Response data is too long.
**/
EFI_STATUS
EFIAPI
PtpTpm2Receive(
OUT UINT8 *Buffer,
OUT UINT32 *RespSize
)
{
EFI_STATUS Status;
UINT32 ControlStatus;
UINT32 WaitTime;
UINT16 Data16;
UINT32 Data32;
Status = EFI_SUCCESS;
DEBUG ((DEBUG_INFO, "TPM_CRB: Tpm2PtpReceive start\n"));
//
// Wait for command completion
//
WaitTime = 0;
ControlStatus = 0;
ControlStatus = MmioRead32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_START);
while ((WaitTime < PTP_HCI_TIMEOUT_B) &&
((ControlStatus & B_CRB_CONTROL_START) != 0)) {
ControlStatus = MmioRead32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_START);
MicroSecondDelay (PTP_HCI_POLLING_PERIOD);
WaitTime += PTP_HCI_POLLING_PERIOD;
}
if (WaitTime >= PTP_HCI_TIMEOUT_B) {
return EFI_TIMEOUT;
}
//
// Read the response data header
//
MmioReadBuffer8 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_DATA_BUFFER, PTP_HCI_RESPONSE_HEADER_SIZE, (UINT8 *)Buffer);
//
// Check the reponse data header (tag, parasize and returncode)
//
CopyMem (&Data16, Buffer, sizeof (UINT16));
DEBUG ((DEBUG_INFO, "TPM2_RESPONSE_HEADER.tag = 0x%04x\n", SwapBytes16(Data16)));
//
// TPM Rev 2.0 Part 2 - 6.9 TPM_ST (Structure Tags)
// TPM_ST_RSP_COMMAND - Used in a response that has an error in the tag.
//
if (SwapBytes16(Data16) == TPM_ST_RSP_COMMAND) {
DEBUG ((DEBUG_ERROR, "TPM2_RESPONSE_HEADER.tag = TPM_ST_RSP_COMMAND - Error in response!\n"));
Status = EFI_DEVICE_ERROR;
goto Exit;
}
CopyMem(&Data32, (Buffer + 2), sizeof(UINT32));
DEBUG ((DEBUG_INFO, "TPM2_RESPONSE_HEADER.paramSize = 0x%08x\n", SwapBytes32(Data32)));
*RespSize = SwapBytes32(Data32);
if(*RespSize == sizeof(TPM2_RESPONSE_HEADER)) {
Status = EFI_SUCCESS;
goto Exit;
}
if(*RespSize < sizeof(TPM2_RESPONSE_HEADER)) {
Status = EFI_DEVICE_ERROR;
goto Exit;
}
if(*RespSize > S_PTP_HCI_CRB_LENGTH) {
Status = EFI_BUFFER_TOO_SMALL;
goto Exit;
}
//
// Read the entire response data header
//
MmioReadBuffer8 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_DATA_BUFFER, *RespSize, (UINT8 *)Buffer);
Exit:
return Status;
}
/**
Sends formatted command to TPM for execution and returns formatted response data.
@param[in] InputBuffer Buffer for the input data.
@param[in] InputBufferSize Size of the input buffer.
@param[out] ReturnBuffer Buffer for the output data.
@param[out] ReturnBufferSize Size of the output buffer.
@retval EFI_SUCCESS Operation completed successfully.
@retval EFI_TIMEOUT The register can't run into the expected status in time.
**/
EFI_STATUS
EFIAPI
PtpTpm2SubmitCommand (
IN UINT32 InputParameterBlockSize,
IN UINT8 *InputParameterBlock,
IN OUT UINT32 *OutputParameterBlockSize,
IN UINT8 *OutputParameterBlock
)
{
EFI_STATUS Status;
DEBUG ((DEBUG_INFO, "TPM_CRB: SubmitCommand start\n"));
if(InputParameterBlock == NULL || OutputParameterBlock == NULL || InputParameterBlockSize == 0) {
DEBUG ((DEBUG_ERROR, "Buffer == NULL or InputBufferSize == 0\n"));
return EFI_INVALID_PARAMETER;
}
PtpTpm2RequestUseTpm ();
//
// Send the command to TPM
//
#ifdef EFI_DEBUG
DEBUG ((DEBUG_INFO, "Buffer Dump: Command \n"));
PtpPrintBuffer ( InputParameterBlock, InputParameterBlockSize);
#endif // EFI_DEBUG
Status = PtpTpm2Send (InputParameterBlock, InputParameterBlockSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Tpm2PtpSend EFI_ERROR = %r\n", Status));
return Status;
}
//
// Receive the response data from TPM
//
Status = PtpTpm2Receive (OutputParameterBlock, OutputParameterBlockSize);
#ifdef EFI_DEBUG
DEBUG ((DEBUG_INFO, "Buffer Dump: Response \n"));
PtpPrintBuffer ( OutputParameterBlock, *OutputParameterBlockSize);
#endif // EFI_DEBUG
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Tpm2PtpReceive EFI_ERROR = %r\n", Status));
if (Status == EFI_TIMEOUT) {
//
// Cancel the command
//
MmioWrite32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_CANCEL, B_CRB_CONTROL_CANCEL);
MicroSecondDelay (PTP_HCI_TIMEOUT_B);
}
}
MmioWrite32 ((UINTN) PcdGet64 (PcdTpmBaseAddress) + R_CRB_CONTROL_REQ, B_R_CRB_CONTROL_REQ_GO_IDLE);
return Status;
}