--- /dev/null
+/** @file \r
+ TIS (TPM Interface Specification) functions used by TPM Dxe driver.\r
+ \r
+Copyright (c) 2005 - 2010, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials \r
+are licensed and made available under the terms and conditions of the BSD License \r
+which accompanies this distribution. The full text of the license may be found at \r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <IndustryStandard/Tpm12.h>\r
+#include <Library/TimerLib.h>\r
+#include <Library/TpmCommLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+\r
+STATIC UINT8 TpmCommandBuf[TPMCMDBUFLENGTH];\r
+\r
+/**\r
+ Send command to TPM for execution.\r
+\r
+ @param[in] TisReg TPM register space base address. \r
+ @param[in] TpmBuffer Buffer for TPM command data. \r
+ @param[in] DataLength TPM command data length. \r
+ \r
+ @retval EFI_SUCCESS Operation completed successfully.\r
+ @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
+\r
+**/\r
+EFI_STATUS\r
+TisPcSend (\r
+ IN TIS_PC_REGISTERS_PTR TisReg,\r
+ IN UINT8 *TpmBuffer,\r
+ IN UINT32 DataLength\r
+ )\r
+{\r
+ UINT16 BurstCount;\r
+ UINT32 Index;\r
+ EFI_STATUS Status;\r
+\r
+ Status = TisPcPrepareCommand (TisReg);\r
+ if (EFI_ERROR (Status)){\r
+ DEBUG ((DEBUG_ERROR, "The Tpm not ready!\n"));\r
+ return Status;\r
+ }\r
+ Index = 0;\r
+ while (Index < DataLength) {\r
+ Status = TisPcReadBurstCount (TisReg, &BurstCount);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_TIMEOUT;\r
+ }\r
+ for (; BurstCount > 0 && Index < DataLength; BurstCount--) {\r
+ MmioWrite8 ((UINTN) &TisReg->DataFifo, *(TpmBuffer + Index));\r
+ Index++;\r
+ }\r
+ }\r
+ //\r
+ // Ensure the Tpm status STS_EXPECT change from 1 to 0\r
+ //\r
+ Status = TisPcWaitRegisterBits (\r
+ &TisReg->Status,\r
+ (UINT8) TIS_PC_VALID,\r
+ TIS_PC_STS_EXPECT,\r
+ TIS_TIMEOUT_C\r
+ );\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Receive response data of last command from TPM.\r
+\r
+ @param[in] TisReg TPM register space base address. \r
+ @param[out] TpmBuffer Buffer for response data. \r
+ @param[out] RespSize Response data length. \r
+ \r
+ @retval EFI_SUCCESS Operation completed successfully.\r
+ @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
+ @retval EFI_DEVICE_ERROR Unexpected device status.\r
+ @retval EFI_BUFFER_TOO_SMALL Response data is too long.\r
+\r
+**/\r
+EFI_STATUS\r
+TisPcReceive (\r
+ IN TIS_PC_REGISTERS_PTR TisReg,\r
+ OUT UINT8 *TpmBuffer,\r
+ OUT UINT32 *RespSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT16 BurstCount;\r
+ UINT32 Index;\r
+ UINT32 ResponseSize;\r
+ UINT32 Data32;\r
+\r
+ //\r
+ // Wait for the command completion\r
+ //\r
+ Status = TisPcWaitRegisterBits (\r
+ &TisReg->Status,\r
+ (UINT8) (TIS_PC_VALID | TIS_PC_STS_DATA),\r
+ 0,\r
+ TIS_TIMEOUT_B\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_TIMEOUT;\r
+ }\r
+ //\r
+ // Read the response data header and check it\r
+ //\r
+ Index = 0;\r
+ BurstCount = 0;\r
+ while (Index < sizeof (TPM_RSP_COMMAND_HDR)) {\r
+ Status = TisPcReadBurstCount (TisReg, &BurstCount);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_TIMEOUT;\r
+ }\r
+ for (; BurstCount > 0 ; BurstCount--) {\r
+ *(TpmBuffer + Index) = MmioRead8 ((UINTN) &TisReg->DataFifo);\r
+ Index++;\r
+ if (Index == sizeof (TPM_RSP_COMMAND_HDR))\r
+ break;\r
+ }\r
+ }\r
+ //\r
+ // Check the reponse data header (tag,parasize and returncode )\r
+ //\r
+ CopyMem (&Data32, (TpmBuffer + 2), sizeof (UINT32));\r
+ ResponseSize = SwapBytes32 (Data32);\r
+ *RespSize = ResponseSize;\r
+ if (ResponseSize == sizeof (TPM_RSP_COMMAND_HDR)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ if (ResponseSize < sizeof (TPM_RSP_COMMAND_HDR)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ if (ResponseSize > TPMCMDBUFLENGTH) {\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ //\r
+ // Continue reading the remaining data\r
+ //\r
+ while (Index < ResponseSize) {\r
+ for (; BurstCount > 0 ; BurstCount--) {\r
+ *(TpmBuffer + Index) = MmioRead8 ((UINTN) &TisReg->DataFifo);\r
+ Index++;\r
+ if (Index == ResponseSize) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ Status = TisPcReadBurstCount (TisReg, &BurstCount);\r
+ if (EFI_ERROR (Status) && (Index < ResponseSize)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Format TPM command data according to the format control character.\r
+\r
+ @param[in] FmtChar Format control character. \r
+ @param[in, out] ap List of arguments. \r
+ @param[in] TpmBuffer Buffer for TPM command data. \r
+ @param[out] DataLength TPM command data length. \r
+ \r
+ @retval EFI_SUCCESS Operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid format control character.\r
+ @retval EFI_BUFFER_TOO_SMALL Buffer too small for command data.\r
+\r
+**/\r
+EFI_STATUS\r
+TisPcSendV (\r
+ IN UINT8 FmtChar,\r
+ IN OUT VA_LIST *ap,\r
+ UINT8 *TpmBuffer,\r
+ UINT32 *DataLength\r
+ )\r
+{\r
+ UINT8 DataByte;\r
+ UINT16 DataWord;\r
+ UINT32 DataDword;\r
+ TPM_RQU_COMMAND_HDR TpmCmdHdr;\r
+ TPM_RQU_COMMAND_HDR *TpmCmdPtr;\r
+ UINTN Size;\r
+ UINT8 *Raw;\r
+\r
+ switch (FmtChar) {\r
+\r
+ case 'b':\r
+ DataByte = VA_ARG (*ap, UINT8);\r
+ Raw = &DataByte;\r
+ Size = sizeof (DataByte);\r
+ break;\r
+\r
+ case 'w':\r
+ DataWord = VA_ARG (*ap, UINT16);\r
+ DataWord = SwapBytes16 (DataWord);\r
+ Raw = (UINT8*)&DataWord;\r
+ Size = sizeof (DataWord);\r
+ break;\r
+\r
+ case 'd':\r
+ DataDword = VA_ARG (*ap, UINT32);\r
+ DataDword = SwapBytes32 (DataDword);\r
+ Raw = (UINT8*)&DataDword;\r
+ Size = sizeof (DataDword);\r
+ break;\r
+\r
+ case 'h':\r
+ TpmCmdPtr = VA_ARG (*ap, TPM_RQU_COMMAND_HDR*);\r
+ TpmCmdHdr.tag = SwapBytes16 (TpmCmdPtr->tag);\r
+ TpmCmdHdr.paramSize = SwapBytes32 (TpmCmdPtr->paramSize);\r
+ TpmCmdHdr.ordinal = SwapBytes32 (TpmCmdPtr->ordinal);\r
+ Raw = (UINT8*) &TpmCmdHdr;\r
+ Size = sizeof (TpmCmdHdr);\r
+ break;\r
+\r
+ case 'r':\r
+ Raw = VA_ARG (*ap, UINT8*);\r
+ Size = VA_ARG (*ap, UINTN);\r
+ break;\r
+\r
+ case '\0':\r
+ return EFI_INVALID_PARAMETER;\r
+\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if(*DataLength + (UINT32) Size > TPMCMDBUFLENGTH) {\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+ CopyMem (TpmBuffer + *DataLength, Raw, Size);\r
+ *DataLength += (UINT32) Size;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Format reponse data according to the format control character.\r
+\r
+ @param[in] FmtChar Format control character. \r
+ @param[in, out] ap List of arguments. \r
+ @param[out] TpmBuffer Buffer for reponse data. \r
+ @param[in, out] DataIndex Data offset in reponse data buffer. \r
+ @param[in] RespSize Response data length. \r
+ @param[out] DataFinished Reach the end of Response data. \r
+ \r
+ @retval EFI_SUCCESS Operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid format control character.\r
+ @retval EFI_BUFFER_TOO_SMALL Buffer too small for command data.\r
+\r
+**/\r
+EFI_STATUS\r
+TisPcReceiveV (\r
+ IN UINT8 FmtChar,\r
+ IN OUT VA_LIST *ap,\r
+ OUT UINT8 *TpmBuffer,\r
+ IN OUT UINT32 *DataIndex,\r
+ IN UINT32 RespSize,\r
+ OUT BOOLEAN *DataFinished\r
+ )\r
+{\r
+ UINT8 *Raw;\r
+ TPM_RSP_COMMAND_HDR *TpmRspPtr;\r
+ UINTN Size;\r
+\r
+ Raw = VA_ARG (*ap, UINT8*);\r
+ switch (FmtChar) {\r
+\r
+ case 'b':\r
+ Size = sizeof (UINT8);\r
+ break;\r
+\r
+ case 'w':\r
+ Size = sizeof (UINT16);\r
+ break;\r
+\r
+ case 'd':\r
+ Size = sizeof (UINT32);\r
+ break;\r
+\r
+ case 'h':\r
+ Size = sizeof (*TpmRspPtr);\r
+ break;\r
+\r
+ case 'r':\r
+ Size = VA_ARG (*ap, UINTN);\r
+ if(*DataIndex + (UINT32) Size <= RespSize) {\r
+ break;\r
+ }\r
+ *DataFinished = TRUE;\r
+ if (*DataIndex >= RespSize) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ CopyMem (Raw, TpmBuffer + *DataIndex, RespSize - *DataIndex);\r
+ *DataIndex += RespSize - *DataIndex;\r
+ return EFI_SUCCESS;\r
+\r
+ case '\0':\r
+ return EFI_INVALID_PARAMETER;\r
+\r
+ default:\r
+ return EFI_WARN_UNKNOWN_GLYPH;\r
+ }\r
+\r
+ if(*DataIndex + (UINT32) Size > RespSize) {\r
+ *DataFinished = TRUE;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if( *DataIndex + (UINT32) Size > TPMCMDBUFLENGTH )\r
+ return EFI_BUFFER_TOO_SMALL;\r
+\r
+ CopyMem (Raw, TpmBuffer + *DataIndex, Size);\r
+ *DataIndex += (UINT32) Size;\r
+\r
+ switch (FmtChar) {\r
+\r
+ case 'w':\r
+ *(UINT16*)Raw = SwapBytes16 (*(UINT16*) Raw);\r
+ break;\r
+\r
+ case 'd':\r
+ *(UINT32*)Raw = SwapBytes32 (*(UINT32*) Raw);\r
+ break;\r
+\r
+ case 'h':\r
+ TpmRspPtr = (TPM_RSP_COMMAND_HDR*) Raw;\r
+ TpmRspPtr->tag = SwapBytes16 (TpmRspPtr->tag);\r
+ TpmRspPtr->paramSize = SwapBytes32 (TpmRspPtr->paramSize);\r
+ TpmRspPtr->returnCode = SwapBytes32 (TpmRspPtr->returnCode);\r
+ break;\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Send formatted command to TPM for execution and return formatted data from response.\r
+\r
+ @param[in] TisReg TPM Handle. \r
+ @param[in] Fmt Format control string. \r
+ @param[in] ... The variable argument list.\r
+ \r
+ @retval EFI_SUCCESS Operation completed successfully.\r
+ @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TisPcExecute (\r
+ IN TIS_TPM_HANDLE TisReg,\r
+ IN CONST CHAR8 *Fmt,\r
+ ...\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VA_LIST Ap;\r
+ UINT32 BufSize;\r
+ UINT32 ResponseSize;\r
+ BOOLEAN DataFinished;\r
+\r
+ VA_START (Ap, Fmt);\r
+\r
+ //\r
+ // Put the formatted command to the TpmCommandBuf\r
+ //\r
+ BufSize = 0;\r
+ while (*Fmt != '\0') {\r
+ if (*Fmt == '%') Fmt++;\r
+ if (*Fmt == '/') break;\r
+ Status = TisPcSendV (*Fmt, &Ap, TpmCommandBuf, &BufSize);\r
+ if (EFI_ERROR( Status )) {\r
+ return Status;\r
+ }\r
+ Fmt++;\r
+ }\r
+ //\r
+ // Send the command to TPM\r
+ //\r
+ Status = TisPcSend (TisReg, TpmCommandBuf, BufSize);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Ensure the TPM state change from "Reception" to "Idle/Ready"\r
+ //\r
+ MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_READY);\r
+ return Status; \r
+ }\r
+\r
+ MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_GO);\r
+ Fmt++;\r
+ //\r
+ // Receive the response data from TPM\r
+ //\r
+ ZeroMem (TpmCommandBuf, TPMCMDBUFLENGTH);\r
+ Status = TisPcReceive (TisReg, TpmCommandBuf, &ResponseSize);\r
+ //\r
+ // Ensure the TPM state change from "Execution" or "Completion" to "Idle/Ready"\r
+ //\r
+ MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_READY);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ \r
+ //\r
+ // Get the formatted data from the TpmCommandBuf.\r
+ //\r
+ BufSize =0;\r
+ DataFinished = FALSE;\r
+ while (*Fmt != '\0') {\r
+ if (*Fmt == '%') {\r
+ Fmt++;\r
+ }\r
+ Status = TisPcReceiveV (*Fmt, &Ap, TpmCommandBuf, &BufSize, ResponseSize, &DataFinished);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ if (DataFinished) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ Fmt++;\r
+ }\r
+\r
+ VA_END (Ap);\r
+ return Status;\r
+}\r
+\r