]> git.proxmox.com Git - mirror_edk2.git/blame - SecurityPkg/Library/Tpm2DeviceLibDTpm/Tpm2Tis.c
SecurityPkg:Tpm2DeviceLibDTpm: Support TPM command cancel
[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
11cf02f6 4Copyright (c) 2013 - 2018, 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
11cf02f6
ZC
298 //\r
299 // dataAvail check timeout. Cancel the currently executing command by writing commandCancel,\r
300 // Expect TPM_RC_CANCELLED or successfully completed response.\r
301 //\r
302 DEBUG ((DEBUG_ERROR, "Wait for Tpm2 response data time out. Trying to cancel the command!!\n"));\r
303\r
304 MmioWrite32((UINTN)&TisReg->Status, TIS_PC_STS_CANCEL);\r
305 Status = TisPcWaitRegisterBits (\r
306 &TisReg->Status,\r
307 (UINT8) (TIS_PC_VALID | TIS_PC_STS_DATA),\r
308 0,\r
309 TIS_TIMEOUT_B\r
310 );\r
311 //\r
312 // Do not clear CANCEL bit here bicoz Writes of 0 to this bit are ignored\r
313 //\r
314 if (EFI_ERROR (Status)) {\r
315 //\r
316 // Cancel executing command fail to get any response\r
317 // Try to abort the command with write of a 1 to commandReady in Command Execution state\r
318 //\r
319 Status = EFI_DEVICE_ERROR;\r
320 goto Exit;\r
321 }\r
c1d93242 322 }\r
11cf02f6 323\r
c1d93242
JY
324 //\r
325 // Get response data header\r
326 //\r
327 Index = 0;\r
328 BurstCount = 0;\r
329 while (Index < sizeof (TPM2_RESPONSE_HEADER)) {\r
330 Status = TisPcReadBurstCount (TisReg, &BurstCount);\r
331 if (EFI_ERROR (Status)) {\r
6f785cfc 332 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
333 goto Exit;\r
334 }\r
335 for (; BurstCount > 0; BurstCount--) {\r
336 *(BufferOut + Index) = MmioRead8 ((UINTN)&TisReg->DataFifo);\r
337 Index++;\r
338 if (Index == sizeof (TPM2_RESPONSE_HEADER)) break;\r
339 }\r
340 }\r
341 DEBUG_CODE (\r
c2967d35 342 DEBUG ((EFI_D_VERBOSE, "Tpm2TisTpmCommand ReceiveHeader - "));\r
c1d93242 343 for (Index = 0; Index < sizeof (TPM2_RESPONSE_HEADER); Index++) {\r
6aaac383 344 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferOut[Index]));\r
c1d93242 345 }\r
6aaac383 346 DEBUG ((EFI_D_VERBOSE, "\n"));\r
c1d93242
JY
347 );\r
348 //\r
349 // Check the reponse data header (tag,parasize and returncode )\r
350 //\r
351 CopyMem (&Data16, BufferOut, sizeof (UINT16));\r
352 // TPM2 should not use this RSP_COMMAND\r
353 if (SwapBytes16 (Data16) == TPM_ST_RSP_COMMAND) {\r
6f785cfc 354 DEBUG ((EFI_D_ERROR, "TPM2: TPM_ST_RSP error - %x\n", TPM_ST_RSP_COMMAND));\r
c1d93242
JY
355 Status = EFI_UNSUPPORTED;\r
356 goto Exit;\r
357 }\r
358\r
359 CopyMem (&Data32, (BufferOut + 2), sizeof (UINT32));\r
360 TpmOutSize = SwapBytes32 (Data32);\r
361 if (*SizeOut < TpmOutSize) {\r
362 Status = EFI_BUFFER_TOO_SMALL;\r
363 goto Exit;\r
364 }\r
365 *SizeOut = TpmOutSize;\r
366 //\r
367 // Continue reading the remaining data\r
368 //\r
369 while ( Index < TpmOutSize ) {\r
370 for (; BurstCount > 0; BurstCount--) {\r
371 *(BufferOut + Index) = MmioRead8 ((UINTN)&TisReg->DataFifo);\r
372 Index++;\r
373 if (Index == TpmOutSize) {\r
374 Status = EFI_SUCCESS;\r
375 goto Exit;\r
376 }\r
377 }\r
378 Status = TisPcReadBurstCount (TisReg, &BurstCount);\r
379 if (EFI_ERROR (Status)) {\r
6f785cfc 380 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
381 goto Exit;\r
382 }\r
383 }\r
384Exit:\r
385 DEBUG_CODE (\r
6aaac383 386 DEBUG ((EFI_D_VERBOSE, "Tpm2TisTpmCommand Receive - "));\r
c1d93242 387 for (Index = 0; Index < TpmOutSize; Index++) {\r
6aaac383 388 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferOut[Index]));\r
c1d93242 389 }\r
6aaac383 390 DEBUG ((EFI_D_VERBOSE, "\n"));\r
c1d93242
JY
391 );\r
392 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_READY);\r
393 return Status;\r
394}\r
395\r
396/**\r
397 This service enables the sending of commands to the TPM2.\r
398\r
399 @param[in] InputParameterBlockSize Size of the TPM2 input parameter block.\r
400 @param[in] InputParameterBlock Pointer to the TPM2 input parameter block.\r
401 @param[in,out] OutputParameterBlockSize Size of the TPM2 output parameter block.\r
402 @param[in] OutputParameterBlock Pointer to the TPM2 output parameter block.\r
403\r
404 @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received.\r
405 @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device.\r
406 @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. \r
407**/\r
408EFI_STATUS\r
409EFIAPI\r
79e748cf 410DTpm2TisSubmitCommand (\r
c1d93242
JY
411 IN UINT32 InputParameterBlockSize,\r
412 IN UINT8 *InputParameterBlock,\r
413 IN OUT UINT32 *OutputParameterBlockSize,\r
414 IN UINT8 *OutputParameterBlock\r
415 )\r
416{\r
c2967d35 417 return Tpm2TisTpmCommand (\r
c1d93242
JY
418 (TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress),\r
419 InputParameterBlock,\r
420 InputParameterBlockSize,\r
421 OutputParameterBlock,\r
422 OutputParameterBlockSize\r
423 );\r
424}\r
425\r
426/**\r
427 This service requests use TPM2.\r
428\r
429 @retval EFI_SUCCESS Get the control of TPM2 chip.\r
430 @retval EFI_NOT_FOUND TPM2 not found.\r
431 @retval EFI_DEVICE_ERROR Unexpected device behavior.\r
432**/\r
433EFI_STATUS\r
434EFIAPI\r
79e748cf 435DTpm2TisRequestUseTpm (\r
c1d93242
JY
436 VOID\r
437 )\r
438{\r
439 return TisPcRequestUseTpm ((TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress));\r
440}\r