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