]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - SecurityPkg/Library/Tpm2DeviceLibDTpm/Tpm2Tis.c
SecurityPkg: Add TPM PTP support in TPM2 device lib.
[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 - 2016, Intel Corporation. All rights reserved.<BR>\r
5(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
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
26#include <IndustryStandard/TpmTis.h>\r
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
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
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
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
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
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
212Tpm2TisTpmCommand (\r
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
230 DEBUG ((EFI_D_VERBOSE, "Tpm2TisTpmCommand Send - "));\r
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
237 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferIn[Index]));\r
238 }\r
239 if (DebugSize != SizeIn) {\r
240 DEBUG ((EFI_D_VERBOSE, "...... "));\r
241 for (Index = SizeIn - 0x20; Index < SizeIn; Index++) {\r
242 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferIn[Index]));\r
243 }\r
244 }\r
245 DEBUG ((EFI_D_VERBOSE, "\n"));\r
246 );\r
247 TpmOutSize = 0;\r
248\r
249 Status = TisPcPrepareCommand (TisReg);\r
250 if (EFI_ERROR (Status)){\r
251 DEBUG ((DEBUG_ERROR, "Tpm2 is not ready for command!\n"));\r
252 return EFI_DEVICE_ERROR;\r
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
261 Status = EFI_DEVICE_ERROR;\r
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
279 DEBUG ((DEBUG_ERROR, "Tpm2 The send buffer too small!\n"));\r
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
298 DEBUG ((DEBUG_ERROR, "Wait for Tpm2 response data time out!!\n"));\r
299 Status = EFI_DEVICE_ERROR;\r
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
310 Status = EFI_DEVICE_ERROR;\r
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
320 DEBUG ((EFI_D_VERBOSE, "Tpm2TisTpmCommand ReceiveHeader - "));\r
321 for (Index = 0; Index < sizeof (TPM2_RESPONSE_HEADER); Index++) {\r
322 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferOut[Index]));\r
323 }\r
324 DEBUG ((EFI_D_VERBOSE, "\n"));\r
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
332 DEBUG ((EFI_D_ERROR, "TPM2: TPM_ST_RSP error - %x\n", TPM_ST_RSP_COMMAND));\r
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
358 Status = EFI_DEVICE_ERROR;\r
359 goto Exit;\r
360 }\r
361 }\r
362Exit:\r
363 DEBUG_CODE (\r
364 DEBUG ((EFI_D_VERBOSE, "Tpm2TisTpmCommand Receive - "));\r
365 for (Index = 0; Index < TpmOutSize; Index++) {\r
366 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferOut[Index]));\r
367 }\r
368 DEBUG ((EFI_D_VERBOSE, "\n"));\r
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
388DTpm2TisSubmitCommand (\r
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
395 return Tpm2TisTpmCommand (\r
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
413DTpm2TisRequestUseTpm (\r
414 VOID\r
415 )\r
416{\r
417 return TisPcRequestUseTpm ((TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress));\r
418}\r