]> git.proxmox.com Git - mirror_edk2.git/blame - SecurityPkg/Library/Tpm2DeviceLibDTpm/Tpm2Tis.c
SecurityPkg: Add TPM PTP support in TPM2 device lib.
[mirror_edk2.git] / SecurityPkg / Library / Tpm2DeviceLibDTpm / Tpm2Tis.c
CommitLineData
c1d93242
JY
1/** @file\r
2 TIS (TPM Interface Specification) functions used by dTPM2.0 library.\r
3 \r
79e748cf 4Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>\r
6aaac383 5(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
c1d93242
JY
6This program and the accompanying materials \r
7are licensed and made available under the terms and conditions of the BSD License \r
8which accompanies this distribution. The full text of the license may be found at \r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include <IndustryStandard/Tpm20.h>\r
17\r
18#include <Library/BaseLib.h>\r
19#include <Library/BaseMemoryLib.h>\r
20#include <Library/IoLib.h>\r
21#include <Library/TimerLib.h>\r
22#include <Library/DebugLib.h>\r
23#include <Library/Tpm2DeviceLib.h>\r
24#include <Library/PcdLib.h>\r
25\r
79e748cf 26#include <IndustryStandard/TpmTis.h>\r
c1d93242
JY
27\r
28#define TIS_TIMEOUT_MAX (90000 * 1000) // 90s\r
29\r
30//\r
31// Max TPM command/reponse length\r
32//\r
33#define TPMCMDBUFLENGTH 0x500\r
34\r
35/**\r
36 Check whether TPM chip exist.\r
37\r
38 @param[in] TisReg Pointer to TIS register.\r
39\r
40 @retval TRUE TPM chip exists.\r
41 @retval FALSE TPM chip is not found.\r
42**/\r
43BOOLEAN\r
44TisPcPresenceCheck (\r
45 IN TIS_PC_REGISTERS_PTR TisReg\r
46 )\r
47{\r
48 UINT8 RegRead;\r
49 \r
50 RegRead = MmioRead8 ((UINTN)&TisReg->Access);\r
51 return (BOOLEAN)(RegRead != (UINT8)-1);\r
52}\r
53\r
54/**\r
55 Check whether the value of a TPM chip register satisfies the input BIT setting.\r
56\r
57 @param[in] Register Address port of register to be checked.\r
58 @param[in] BitSet Check these data bits are set.\r
59 @param[in] BitClear Check these data bits are clear.\r
60 @param[in] TimeOut The max wait time (unit MicroSecond) when checking register.\r
61\r
62 @retval EFI_SUCCESS The register satisfies the check bit.\r
63 @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
64**/\r
65EFI_STATUS\r
c1d93242
JY
66TisPcWaitRegisterBits (\r
67 IN UINT8 *Register,\r
68 IN UINT8 BitSet,\r
69 IN UINT8 BitClear,\r
70 IN UINT32 TimeOut\r
71 )\r
72{\r
73 UINT8 RegRead;\r
74 UINT32 WaitTime;\r
75\r
76 for (WaitTime = 0; WaitTime < TimeOut; WaitTime += 30){\r
77 RegRead = MmioRead8 ((UINTN)Register);\r
78 if ((RegRead & BitSet) == BitSet && (RegRead & BitClear) == 0)\r
79 return EFI_SUCCESS;\r
80 MicroSecondDelay (30);\r
81 }\r
82 return EFI_TIMEOUT;\r
83}\r
84\r
85/**\r
86 Get BurstCount by reading the burstCount field of a TIS regiger \r
87 in the time of default TIS_TIMEOUT_D.\r
88\r
89 @param[in] TisReg Pointer to TIS register.\r
90 @param[out] BurstCount Pointer to a buffer to store the got BurstConut.\r
91\r
92 @retval EFI_SUCCESS Get BurstCount.\r
93 @retval EFI_INVALID_PARAMETER TisReg is NULL or BurstCount is NULL.\r
94 @retval EFI_TIMEOUT BurstCount can't be got in time.\r
95**/\r
96EFI_STATUS\r
c1d93242
JY
97TisPcReadBurstCount (\r
98 IN TIS_PC_REGISTERS_PTR TisReg,\r
99 OUT UINT16 *BurstCount\r
100 )\r
101{\r
102 UINT32 WaitTime;\r
103 UINT8 DataByte0;\r
104 UINT8 DataByte1;\r
105\r
106 if (BurstCount == NULL || TisReg == NULL) {\r
107 return EFI_INVALID_PARAMETER;\r
108 }\r
109\r
110 WaitTime = 0;\r
111 do {\r
112 //\r
113 // TIS_PC_REGISTERS_PTR->burstCount is UINT16, but it is not 2bytes aligned,\r
114 // so it needs to use MmioRead8 to read two times\r
115 //\r
116 DataByte0 = MmioRead8 ((UINTN)&TisReg->BurstCount);\r
117 DataByte1 = MmioRead8 ((UINTN)&TisReg->BurstCount + 1);\r
118 *BurstCount = (UINT16)((DataByte1 << 8) + DataByte0);\r
119 if (*BurstCount != 0) {\r
120 return EFI_SUCCESS;\r
121 }\r
122 MicroSecondDelay (30);\r
123 WaitTime += 30;\r
124 } while (WaitTime < TIS_TIMEOUT_D);\r
125\r
126 return EFI_TIMEOUT;\r
127}\r
128\r
129/**\r
130 Set TPM chip to ready state by sending ready command TIS_PC_STS_READY \r
131 to Status Register in time.\r
132\r
133 @param[in] TisReg Pointer to TIS register.\r
134\r
135 @retval EFI_SUCCESS TPM chip enters into ready state.\r
136 @retval EFI_INVALID_PARAMETER TisReg is NULL.\r
137 @retval EFI_TIMEOUT TPM chip can't be set to ready state in time.\r
138**/\r
139EFI_STATUS\r
c1d93242
JY
140TisPcPrepareCommand (\r
141 IN TIS_PC_REGISTERS_PTR TisReg\r
142 )\r
143{\r
144 EFI_STATUS Status;\r
145\r
146 if (TisReg == NULL) {\r
147 return EFI_INVALID_PARAMETER;\r
148 }\r
149\r
150 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_READY);\r
151 Status = TisPcWaitRegisterBits (\r
152 &TisReg->Status,\r
153 TIS_PC_STS_READY,\r
154 0,\r
155 TIS_TIMEOUT_B\r
156 );\r
157 return Status;\r
158}\r
159\r
160/**\r
161 Get the control of TPM chip by sending requestUse command TIS_PC_ACC_RQUUSE \r
162 to ACCESS Register in the time of default TIS_TIMEOUT_A.\r
163\r
164 @param[in] TisReg Pointer to TIS register.\r
165\r
166 @retval EFI_SUCCESS Get the control of TPM chip.\r
167 @retval EFI_INVALID_PARAMETER TisReg is NULL.\r
168 @retval EFI_NOT_FOUND TPM chip doesn't exit.\r
169 @retval EFI_TIMEOUT Can't get the TPM control in time.\r
170**/\r
171EFI_STATUS\r
c1d93242
JY
172TisPcRequestUseTpm (\r
173 IN TIS_PC_REGISTERS_PTR TisReg\r
174 )\r
175{\r
176 EFI_STATUS Status;\r
177 \r
178 if (TisReg == NULL) {\r
179 return EFI_INVALID_PARAMETER;\r
180 }\r
181 \r
182 if (!TisPcPresenceCheck (TisReg)) {\r
183 return EFI_NOT_FOUND;\r
184 }\r
185\r
186 MmioWrite8((UINTN)&TisReg->Access, TIS_PC_ACC_RQUUSE);\r
187 Status = TisPcWaitRegisterBits (\r
188 &TisReg->Access,\r
189 (UINT8)(TIS_PC_ACC_ACTIVE |TIS_PC_VALID),\r
190 0,\r
191 TIS_TIMEOUT_A\r
192 );\r
193 return Status;\r
194}\r
195\r
196/**\r
197 Send a command to TPM for execution and return response data.\r
198\r
199 @param[in] TisReg TPM register space base address. \r
200 @param[in] BufferIn Buffer for command data. \r
201 @param[in] SizeIn Size of command data. \r
202 @param[in, out] BufferOut Buffer for response data. \r
203 @param[in, out] SizeOut Size of response data. \r
204 \r
205 @retval EFI_SUCCESS Operation completed successfully.\r
c1d93242
JY
206 @retval EFI_BUFFER_TOO_SMALL Response data buffer is too small.\r
207 @retval EFI_DEVICE_ERROR Unexpected device behavior.\r
208 @retval EFI_UNSUPPORTED Unsupported TPM version\r
209\r
210**/\r
211EFI_STATUS\r
c2967d35 212Tpm2TisTpmCommand (\r
c1d93242
JY
213 IN TIS_PC_REGISTERS_PTR TisReg,\r
214 IN UINT8 *BufferIn,\r
215 IN UINT32 SizeIn,\r
216 IN OUT UINT8 *BufferOut,\r
217 IN OUT UINT32 *SizeOut\r
218 )\r
219{\r
220 EFI_STATUS Status;\r
221 UINT16 BurstCount;\r
222 UINT32 Index;\r
223 UINT32 TpmOutSize;\r
224 UINT16 Data16;\r
225 UINT32 Data32;\r
226\r
227 DEBUG_CODE (\r
228 UINTN DebugSize;\r
229\r
6aaac383 230 DEBUG ((EFI_D_VERBOSE, "Tpm2TisTpmCommand Send - "));\r
c1d93242
JY
231 if (SizeIn > 0x100) {\r
232 DebugSize = 0x40;\r
233 } else {\r
234 DebugSize = SizeIn;\r
235 }\r
236 for (Index = 0; Index < DebugSize; Index++) {\r
6aaac383 237 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferIn[Index]));\r
c1d93242
JY
238 }\r
239 if (DebugSize != SizeIn) {\r
6aaac383 240 DEBUG ((EFI_D_VERBOSE, "...... "));\r
c1d93242 241 for (Index = SizeIn - 0x20; Index < SizeIn; Index++) {\r
6aaac383 242 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferIn[Index]));\r
c1d93242
JY
243 }\r
244 }\r
6aaac383 245 DEBUG ((EFI_D_VERBOSE, "\n"));\r
c1d93242
JY
246 );\r
247 TpmOutSize = 0;\r
248\r
249 Status = TisPcPrepareCommand (TisReg);\r
250 if (EFI_ERROR (Status)){\r
6f785cfc
JY
251 DEBUG ((DEBUG_ERROR, "Tpm2 is not ready for command!\n"));\r
252 return EFI_DEVICE_ERROR;\r
c1d93242
JY
253 }\r
254 //\r
255 // Send the command data to Tpm\r
256 //\r
257 Index = 0;\r
258 while (Index < SizeIn) {\r
259 Status = TisPcReadBurstCount (TisReg, &BurstCount);\r
260 if (EFI_ERROR (Status)) {\r
6f785cfc 261 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
262 goto Exit;\r
263 }\r
264 for (; BurstCount > 0 && Index < SizeIn; BurstCount--) {\r
265 MmioWrite8((UINTN)&TisReg->DataFifo, *(BufferIn + Index));\r
266 Index++;\r
267 }\r
268 }\r
269 //\r
270 // Check the Tpm status STS_EXPECT change from 1 to 0\r
271 //\r
272 Status = TisPcWaitRegisterBits (\r
273 &TisReg->Status,\r
274 (UINT8) TIS_PC_VALID,\r
275 TIS_PC_STS_EXPECT,\r
276 TIS_TIMEOUT_C\r
277 );\r
278 if (EFI_ERROR (Status)) {\r
6f785cfc 279 DEBUG ((DEBUG_ERROR, "Tpm2 The send buffer too small!\n"));\r
c1d93242
JY
280 Status = EFI_BUFFER_TOO_SMALL;\r
281 goto Exit;\r
282 }\r
283 //\r
284 // Executed the TPM command and waiting for the response data ready\r
285 //\r
286 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_GO);\r
287\r
288 //\r
289 // NOTE: That may take many seconds to minutes for certain commands, such as key generation.\r
290 //\r
291 Status = TisPcWaitRegisterBits (\r
292 &TisReg->Status,\r
293 (UINT8) (TIS_PC_VALID | TIS_PC_STS_DATA),\r
294 0,\r
295 TIS_TIMEOUT_MAX\r
296 );\r
297 if (EFI_ERROR (Status)) {\r
6f785cfc
JY
298 DEBUG ((DEBUG_ERROR, "Wait for Tpm2 response data time out!!\n"));\r
299 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
300 goto Exit;\r
301 }\r
302 //\r
303 // Get response data header\r
304 //\r
305 Index = 0;\r
306 BurstCount = 0;\r
307 while (Index < sizeof (TPM2_RESPONSE_HEADER)) {\r
308 Status = TisPcReadBurstCount (TisReg, &BurstCount);\r
309 if (EFI_ERROR (Status)) {\r
6f785cfc 310 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
311 goto Exit;\r
312 }\r
313 for (; BurstCount > 0; BurstCount--) {\r
314 *(BufferOut + Index) = MmioRead8 ((UINTN)&TisReg->DataFifo);\r
315 Index++;\r
316 if (Index == sizeof (TPM2_RESPONSE_HEADER)) break;\r
317 }\r
318 }\r
319 DEBUG_CODE (\r
c2967d35 320 DEBUG ((EFI_D_VERBOSE, "Tpm2TisTpmCommand ReceiveHeader - "));\r
c1d93242 321 for (Index = 0; Index < sizeof (TPM2_RESPONSE_HEADER); Index++) {\r
6aaac383 322 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferOut[Index]));\r
c1d93242 323 }\r
6aaac383 324 DEBUG ((EFI_D_VERBOSE, "\n"));\r
c1d93242
JY
325 );\r
326 //\r
327 // Check the reponse data header (tag,parasize and returncode )\r
328 //\r
329 CopyMem (&Data16, BufferOut, sizeof (UINT16));\r
330 // TPM2 should not use this RSP_COMMAND\r
331 if (SwapBytes16 (Data16) == TPM_ST_RSP_COMMAND) {\r
6f785cfc 332 DEBUG ((EFI_D_ERROR, "TPM2: TPM_ST_RSP error - %x\n", TPM_ST_RSP_COMMAND));\r
c1d93242
JY
333 Status = EFI_UNSUPPORTED;\r
334 goto Exit;\r
335 }\r
336\r
337 CopyMem (&Data32, (BufferOut + 2), sizeof (UINT32));\r
338 TpmOutSize = SwapBytes32 (Data32);\r
339 if (*SizeOut < TpmOutSize) {\r
340 Status = EFI_BUFFER_TOO_SMALL;\r
341 goto Exit;\r
342 }\r
343 *SizeOut = TpmOutSize;\r
344 //\r
345 // Continue reading the remaining data\r
346 //\r
347 while ( Index < TpmOutSize ) {\r
348 for (; BurstCount > 0; BurstCount--) {\r
349 *(BufferOut + Index) = MmioRead8 ((UINTN)&TisReg->DataFifo);\r
350 Index++;\r
351 if (Index == TpmOutSize) {\r
352 Status = EFI_SUCCESS;\r
353 goto Exit;\r
354 }\r
355 }\r
356 Status = TisPcReadBurstCount (TisReg, &BurstCount);\r
357 if (EFI_ERROR (Status)) {\r
6f785cfc 358 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
359 goto Exit;\r
360 }\r
361 }\r
362Exit:\r
363 DEBUG_CODE (\r
6aaac383 364 DEBUG ((EFI_D_VERBOSE, "Tpm2TisTpmCommand Receive - "));\r
c1d93242 365 for (Index = 0; Index < TpmOutSize; Index++) {\r
6aaac383 366 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferOut[Index]));\r
c1d93242 367 }\r
6aaac383 368 DEBUG ((EFI_D_VERBOSE, "\n"));\r
c1d93242
JY
369 );\r
370 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_READY);\r
371 return Status;\r
372}\r
373\r
374/**\r
375 This service enables the sending of commands to the TPM2.\r
376\r
377 @param[in] InputParameterBlockSize Size of the TPM2 input parameter block.\r
378 @param[in] InputParameterBlock Pointer to the TPM2 input parameter block.\r
379 @param[in,out] OutputParameterBlockSize Size of the TPM2 output parameter block.\r
380 @param[in] OutputParameterBlock Pointer to the TPM2 output parameter block.\r
381\r
382 @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received.\r
383 @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device.\r
384 @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. \r
385**/\r
386EFI_STATUS\r
387EFIAPI\r
79e748cf 388DTpm2TisSubmitCommand (\r
c1d93242
JY
389 IN UINT32 InputParameterBlockSize,\r
390 IN UINT8 *InputParameterBlock,\r
391 IN OUT UINT32 *OutputParameterBlockSize,\r
392 IN UINT8 *OutputParameterBlock\r
393 )\r
394{\r
c2967d35 395 return Tpm2TisTpmCommand (\r
c1d93242
JY
396 (TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress),\r
397 InputParameterBlock,\r
398 InputParameterBlockSize,\r
399 OutputParameterBlock,\r
400 OutputParameterBlockSize\r
401 );\r
402}\r
403\r
404/**\r
405 This service requests use TPM2.\r
406\r
407 @retval EFI_SUCCESS Get the control of TPM2 chip.\r
408 @retval EFI_NOT_FOUND TPM2 not found.\r
409 @retval EFI_DEVICE_ERROR Unexpected device behavior.\r
410**/\r
411EFI_STATUS\r
412EFIAPI\r
79e748cf 413DTpm2TisRequestUseTpm (\r
c1d93242
JY
414 VOID\r
415 )\r
416{\r
417 return TisPcRequestUseTpm ((TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress));\r
418}\r