--- /dev/null
+/** @file\r
+ Basic TIS (TPM Interface Specification) functions for Atmel I2C TPM.\r
+\r
+ Copyright (c) 2016, 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 <PiPei.h>\r
+#include <Library/Tpm12DeviceLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/TimerLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/I2cLib.h>\r
+#include <Library/Tpm12CommandLib.h>\r
+\r
+//\r
+// Atmel I2C TPM slave address\r
+//\r
+#define ATMEL_I2C_TPM_SLAVE_ADDRESS 0x29\r
+\r
+//\r
+// Maximum I2C transfer size for Atmel I2C TPM\r
+//\r
+#define ATMEL_I2C_TPM_MAX_TRANSFER_SIZE 0x10\r
+\r
+//\r
+// Default TimeOut values in microseconds\r
+//\r
+#define TIS_TIMEOUT_A ( 750 * 1000) // 750ms\r
+#define TIS_TIMEOUT_B (2000 * 1000) // 2s\r
+#define TIS_TIMEOUT_C ( 750 * 1000) // 750ms\r
+#define TIS_TIMEOUT_D ( 750 * 1000) // 750ms\r
+\r
+/**\r
+ Send command to Atmel I2c TPM breaking request up into multiple I2C transfers\r
+ if required.\r
+\r
+ @param[in] Buffer Pointer to TPM command data.\r
+ @param[in] Length Number of bytes of TPM command data.\r
+\r
+ @retval EFI_SUCCESS TPM command sent.\r
+ @retval EFI_NOT_FOUND TPM chip doesn't exit.\r
+ @retval EFI_TIMEOUT Can't get the TPM control in time.\r
+**/\r
+EFI_STATUS\r
+WriteTpmBufferMultiple (\r
+ IN UINT8 *Buffer,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_I2C_DEVICE_ADDRESS I2CDeviceAddr;\r
+ UINTN Index;\r
+ UINTN PartialLength;\r
+\r
+ I2CDeviceAddr.I2CDeviceAddress = ATMEL_I2C_TPM_SLAVE_ADDRESS;\r
+\r
+ DEBUG ((EFI_D_VERBOSE, "WriteTpmBufferMultiple: Addr=%02x Length=%02x\n", I2CDeviceAddr.I2CDeviceAddress, Length));\r
+\r
+ for (PartialLength = 0; Length > 0; Length -= PartialLength, Buffer += PartialLength) {\r
+ //\r
+ // Write data to TPM.\r
+ //\r
+ PartialLength = MIN (Length, ATMEL_I2C_TPM_MAX_TRANSFER_SIZE);\r
+ Status = I2cWriteMultipleByte (\r
+ I2CDeviceAddr,\r
+ EfiI2CSevenBitAddrMode,\r
+ &PartialLength,\r
+ Buffer\r
+ );\r
+ DEBUG ((EFI_D_VERBOSE, " "));\r
+ for (Index = 0; Index < PartialLength; Index++) {\r
+ DEBUG ((EFI_D_VERBOSE, "%02x ", Buffer[Index]));\r
+ }\r
+ DEBUG ((EFI_D_VERBOSE, "\n"));\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_VERBOSE, " Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ DEBUG ((EFI_D_VERBOSE, " Status = %r\n", Status));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Receive a response to a command from Atmel I2c TPM breaking response into\r
+ multiple I2C transfers if required.\r
+\r
+ @param[out] Buffer Pointer to TPM response data.\r
+ @param[in] Length Maximum number of bytes to receive.\r
+\r
+ @retval EFI_SUCCESS TPM response received.\r
+ @retval EFI_NOT_FOUND TPM chip doesn't exit.\r
+ @retval EFI_TIMEOUT Can't get the TPM control in time.\r
+**/\r
+EFI_STATUS\r
+ReadTpmBufferMultiple (\r
+ OUT UINT8 *Buffer,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_I2C_DEVICE_ADDRESS I2CDeviceAddr;\r
+ UINTN WriteLength;\r
+ UINTN Index;\r
+ UINTN PartialLength;\r
+\r
+ I2CDeviceAddr.I2CDeviceAddress = ATMEL_I2C_TPM_SLAVE_ADDRESS;\r
+ WriteLength = 0;\r
+\r
+ DEBUG ((EFI_D_VERBOSE, "ReadTpmBufferMultiple: Addr=%02x Length=%02x\n", I2CDeviceAddr.I2CDeviceAddress, Length));\r
+\r
+ for (PartialLength = 0; Length > 0; Length -= PartialLength, Buffer += PartialLength) {\r
+ //\r
+ // Read data from TPM.\r
+ //\r
+ PartialLength = MIN (Length, ATMEL_I2C_TPM_MAX_TRANSFER_SIZE);\r
+ Status = I2cReadMultipleByte (\r
+ I2CDeviceAddr,\r
+ EfiI2CSevenBitAddrMode,\r
+ &WriteLength,\r
+ &PartialLength,\r
+ Buffer\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_VERBOSE, " "));\r
+ for (Index = 0; Index < PartialLength; Index++) {\r
+ DEBUG ((EFI_D_VERBOSE, "%02x ", Buffer[Index]));\r
+ }\r
+ DEBUG ((EFI_D_VERBOSE, "\n"));\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_VERBOSE, " Status = %r\n", Status));\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ DEBUG ((EFI_D_VERBOSE, " Status = %r\n", Status));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This service requests use TPM12.\r
+\r
+ @retval EFI_SUCCESS Get the control of TPM12 chip.\r
+ @retval EFI_NOT_FOUND TPM12 not found.\r
+ @retval EFI_DEVICE_ERROR Unexpected device behavior.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tpm12RequestUseTpm (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 Data;\r
+ UINT64 Current;\r
+ UINT64 Previous;\r
+ UINT64 Total;\r
+ UINT64 Start;\r
+ UINT64 End;\r
+ UINT64 Timeout;\r
+ INT64 Cycle;\r
+ INT64 Delta;\r
+\r
+ //\r
+ // Get the current timer value\r
+ //\r
+ Current = GetPerformanceCounter();\r
+\r
+ //\r
+ // Initialize local variables\r
+ //\r
+ Start = 0;\r
+ End = 0;\r
+ Total = 0;\r
+\r
+ //\r
+ // Retrieve the performance counter properties and compute the number of\r
+ // performance counter ticks required to reach the maximum TIS timeout of\r
+ // TIS_TIMEOUT_A. TIS_TIMEOUT_A is in microseconds.\r
+ //\r
+ Timeout = DivU64x32 (\r
+ MultU64x32 (\r
+ GetPerformanceCounterProperties (&Start, &End),\r
+ TIS_TIMEOUT_A\r
+ ),\r
+ 1000000\r
+ );\r
+ Cycle = End - Start;\r
+ if (Cycle < 0) {\r
+ Cycle = -Cycle;\r
+ }\r
+ Cycle++;\r
+\r
+ //\r
+ // Attempt to read a byte from the Atmel I2C TPM\r
+ //\r
+ do {\r
+ Status = ReadTpmBufferMultiple (&Data, sizeof(Data));\r
+\r
+ Previous = Current;\r
+ Current = GetPerformanceCounter();\r
+ Delta = (INT64) (Current - Previous);\r
+ if (Start > End) {\r
+ Delta = -Delta;\r
+ }\r
+ if (Delta < 0) {\r
+ Delta += Cycle;\r
+ }\r
+ Total += Delta;\r
+ if (Total >= Timeout) {\r
+ Status = EFI_TIMEOUT;\r
+ DEBUG ((EFI_D_ERROR, "Atmel I2C TPM failed to read: %r\n", Status));\r
+ return Status;\r
+ }\r
+ } while (EFI_ERROR (Status));\r
+\r
+ //\r
+ // Send Physical Presence Command to Atmel I2C TPM\r
+ //\r
+ Status = Tpm12PhysicalPresence (TPM_PHYSICAL_PRESENCE_PRESENT);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Atmel I2C TPM failed to submit physical presence command: %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This service enables the sending of commands to the TPM12.\r
+\r
+ @param[in] InputParameterBlockSize Size of the TPM12 input parameter block.\r
+ @param[in] InputParameterBlock Pointer to the TPM12 input parameter block.\r
+ @param[in,out] OutputParameterBlockSize Size of the TPM12 output parameter block.\r
+ @param[in] OutputParameterBlock Pointer to the TPM12 output parameter block.\r
+\r
+ @retval EFI_SUCCESS The command byte stream was successfully sent to\r
+ the device and a response was successfully received.\r
+ @retval EFI_DEVICE_ERROR The command was not successfully sent to the\r
+ device or a response was not successfully received\r
+ from the device.\r
+ @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tpm12SubmitCommand (\r
+ IN UINT32 InputParameterBlockSize,\r
+ IN UINT8 *InputParameterBlock,\r
+ IN OUT UINT32 *OutputParameterBlockSize,\r
+ IN UINT8 *OutputParameterBlock\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 TpmOutSize;\r
+ TPM_RSP_COMMAND_HDR *ResponseHeader;\r
+ UINT64 Current;\r
+ UINT64 Previous;\r
+ UINT64 Total;\r
+ UINT64 Start;\r
+ UINT64 End;\r
+ UINT64 Timeout;\r
+ INT64 Cycle;\r
+ INT64 Delta;\r
+\r
+ //\r
+ // Make sure response buffer is big enough to hold a response header\r
+ //\r
+ if (*OutputParameterBlockSize < sizeof (TPM_RSP_COMMAND_HDR)) {\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Get the current timer value\r
+ //\r
+ Current = GetPerformanceCounter();\r
+\r
+ //\r
+ // Initialize local variables\r
+ //\r
+ Start = 0;\r
+ End = 0;\r
+ Total = 0;\r
+\r
+ //\r
+ // Retrieve the performance counter properties and compute the number of\r
+ // performance counter ticks required to reach the maximum TIS timeout of\r
+ // TIS_TIMEOUT_A. TIS_TIMEOUT_A is in microseconds.\r
+ //\r
+ Timeout = DivU64x32 (\r
+ MultU64x32 (\r
+ GetPerformanceCounterProperties (&Start, &End),\r
+ TIS_TIMEOUT_A\r
+ ),\r
+ 1000000\r
+ );\r
+ Cycle = End - Start;\r
+ if (Cycle < 0) {\r
+ Cycle = -Cycle;\r
+ }\r
+ Cycle++;\r
+\r
+ //\r
+ // Send command\r
+ //\r
+ do {\r
+ Status = WriteTpmBufferMultiple (InputParameterBlock, InputParameterBlockSize);\r
+\r
+ Previous = Current;\r
+ Current = GetPerformanceCounter();\r
+ Delta = (INT64) (Current - Previous);\r
+ if (Start > End) {\r
+ Delta = -Delta;\r
+ }\r
+ if (Delta < 0) {\r
+ Delta += Cycle;\r
+ }\r
+ Total += Delta;\r
+ if (Total >= Timeout) {\r
+ Status = EFI_TIMEOUT;\r
+ goto Done;\r
+ }\r
+ } while (EFI_ERROR (Status));\r
+\r
+ //\r
+ // Receive response header\r
+ //\r
+ do {\r
+ Status = ReadTpmBufferMultiple (OutputParameterBlock, sizeof (TPM_RSP_COMMAND_HDR));\r
+\r
+ Previous = Current;\r
+ Current = GetPerformanceCounter();\r
+ Delta = (INT64) (Current - Previous);\r
+ if (Start > End) {\r
+ Delta = -Delta;\r
+ }\r
+ if (Delta < 0) {\r
+ Delta += Cycle;\r
+ }\r
+ Total += Delta;\r
+ if (Total >= Timeout) {\r
+ Status = EFI_TIMEOUT;\r
+ goto Done;\r
+ }\r
+ } while (EFI_ERROR (Status));\r
+\r
+ //\r
+ // Check the response data header (tag, parasize and returncode)\r
+ //\r
+ ResponseHeader = (TPM_RSP_COMMAND_HDR *)OutputParameterBlock;\r
+ if (SwapBytes16 (ReadUnaligned16 (&ResponseHeader->tag)) != TPM_TAG_RSP_COMMAND) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ TpmOutSize = SwapBytes32 (ReadUnaligned32 (&ResponseHeader->paramSize));\r
+ if (TpmOutSize == sizeof (TPM_RSP_COMMAND_HDR)) {\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+ if (TpmOutSize < sizeof (TPM_RSP_COMMAND_HDR)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+ if (*OutputParameterBlockSize < TpmOutSize) {\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ goto Done;\r
+ }\r
+ *OutputParameterBlockSize = TpmOutSize;\r
+\r
+ //\r
+ // Receive the remaining data in the response header\r
+ //\r
+ do {\r
+ Status = ReadTpmBufferMultiple (\r
+ OutputParameterBlock + sizeof (TPM_RSP_COMMAND_HDR),\r
+ TpmOutSize - sizeof (TPM_RSP_COMMAND_HDR)\r
+ );\r
+\r
+ Previous = Current;\r
+ Current = GetPerformanceCounter();\r
+ Delta = (INT64) (Current - Previous);\r
+ if (Start > End) {\r
+ Delta = -Delta;\r
+ }\r
+ if (Delta < 0) {\r
+ Delta += Cycle;\r
+ }\r
+ Total += Delta;\r
+ if (Total >= Timeout) {\r
+ Status = EFI_TIMEOUT;\r
+ goto Done;\r
+ }\r
+ } while (EFI_ERROR (Status));\r
+\r
+Done:\r
+ DEBUG ((\r
+ EFI_D_VERBOSE,\r
+ "Tpm12SubmitCommand() Status = %r Time = %ld ms\n",\r
+ Status,\r
+ DivU64x64Remainder (\r
+ MultU64x32 (Total, 1000),\r
+ GetPerformanceCounterProperties (NULL, NULL),\r
+ NULL\r
+ )\r
+ ));\r
+\r
+ return Status;\r
+}\r