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