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