Handle TPM device error and avoid deadloop in BDS.
[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 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include <IndustryStandard/Tpm20.h>
16
17 #include <Library/BaseLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/IoLib.h>
20 #include <Library/TimerLib.h>
21 #include <Library/DebugLib.h>
22 #include <Library/Tpm2DeviceLib.h>
23 #include <Library/PcdLib.h>
24
25 //
26 // Set structure alignment to 1-byte
27 //
28 #pragma pack (1)
29
30 //
31 // Register set map as specified in TIS specification Chapter 10
32 //
33 typedef struct {
34 ///
35 /// Used to gain ownership for this particular port.
36 ///
37 UINT8 Access; // 0
38 UINT8 Reserved1[7]; // 1
39 ///
40 /// Controls interrupts.
41 ///
42 UINT32 IntEnable; // 8
43 ///
44 /// SIRQ vector to be used by the TPM.
45 ///
46 UINT8 IntVector; // 0ch
47 UINT8 Reserved2[3]; // 0dh
48 ///
49 /// What caused interrupt.
50 ///
51 UINT32 IntSts; // 10h
52 ///
53 /// Shows which interrupts are supported by that particular TPM.
54 ///
55 UINT32 IntfCapability; // 14h
56 ///
57 /// Status Register. Provides status of the TPM.
58 ///
59 UINT8 Status; // 18h
60 ///
61 /// Number of consecutive writes that can be done to the TPM.
62 ///
63 UINT16 BurstCount; // 19h
64 ///
65 /// TPM2 support CANCEL at BIT[24] of STATUS register (WO)
66 ///
67 UINT8 StatusEx; // 1Bh
68 UINT8 Reserved3[8];
69 ///
70 /// Read or write FIFO, depending on transaction.
71 ///
72 UINT32 DataFifo; // 24h
73 UINT8 Reserved4[0xed8]; // 28h
74 ///
75 /// Vendor ID
76 ///
77 UINT16 Vid; // 0f00h
78 ///
79 /// Device ID
80 ///
81 UINT16 Did; // 0f02h
82 ///
83 /// Revision ID
84 ///
85 UINT8 Rid; // 0f04h
86 ///
87 /// TCG defined configuration registers.
88 ///
89 UINT8 TcgDefined[0x7b]; // 0f05h
90 ///
91 /// Alias to I/O legacy space.
92 ///
93 UINT32 LegacyAddress1; // 0f80h
94 ///
95 /// Additional 8 bits for I/O legacy space extension.
96 ///
97 UINT32 LegacyAddress1Ex; // 0f84h
98 ///
99 /// Alias to second I/O legacy space.
100 ///
101 UINT32 LegacyAddress2; // 0f88h
102 ///
103 /// Additional 8 bits for second I/O legacy space extension.
104 ///
105 UINT32 LegacyAddress2Ex; // 0f8ch
106 ///
107 /// Vendor-defined configuration registers.
108 ///
109 UINT8 VendorDefined[0x70];// 0f90h
110 } TIS_PC_REGISTERS;
111
112 //
113 // Restore original structure alignment
114 //
115 #pragma pack ()
116
117 //
118 // Define pointer types used to access TIS registers on PC
119 //
120 typedef TIS_PC_REGISTERS *TIS_PC_REGISTERS_PTR;
121
122 //
123 // Define bits of ACCESS and STATUS registers
124 //
125
126 ///
127 /// This bit is a 1 to indicate that the other bits in this register are valid.
128 ///
129 #define TIS_PC_VALID BIT7
130 ///
131 /// Indicate that this locality is active.
132 ///
133 #define TIS_PC_ACC_ACTIVE BIT5
134 ///
135 /// Set to 1 to indicate that this locality had the TPM taken away while
136 /// this locality had the TIS_PC_ACC_ACTIVE bit set.
137 ///
138 #define TIS_PC_ACC_SEIZED BIT4
139 ///
140 /// Set to 1 to indicate that TPM MUST reset the
141 /// TIS_PC_ACC_ACTIVE bit and remove ownership for localities less than the
142 /// locality that is writing this bit.
143 ///
144 #define TIS_PC_ACC_SEIZE BIT3
145 ///
146 /// When this bit is 1, another locality is requesting usage of the TPM.
147 ///
148 #define TIS_PC_ACC_PENDIND BIT2
149 ///
150 /// Set to 1 to indicate that this locality is requesting to use TPM.
151 ///
152 #define TIS_PC_ACC_RQUUSE BIT1
153 ///
154 /// A value of 1 indicates that a T/OS has not been established on the platform
155 ///
156 #define TIS_PC_ACC_ESTABLISH BIT0
157
158 ///
159 /// When this bit is 1, TPM is in the Ready state,
160 /// indicating it is ready to receive a new command.
161 ///
162 #define TIS_PC_STS_READY BIT6
163 ///
164 /// Write a 1 to this bit to cause the TPM to execute that command.
165 ///
166 #define TIS_PC_STS_GO BIT5
167 ///
168 /// This bit indicates that the TPM has data available as a response.
169 ///
170 #define TIS_PC_STS_DATA BIT4
171 ///
172 /// The TPM sets this bit to a value of 1 when it expects another byte of data for a command.
173 ///
174 #define TIS_PC_STS_EXPECT BIT3
175 ///
176 /// Writes a 1 to this bit to force the TPM to re-send the response.
177 ///
178 #define TIS_PC_STS_RETRY BIT1
179
180 //
181 // Default TimeOut value
182 //
183 #define TIS_TIMEOUT_A (1000 * 1000) // 1s
184 #define TIS_TIMEOUT_B (2000 * 1000) // 2s
185 #define TIS_TIMEOUT_C (1000 * 1000) // 1s
186 #define TIS_TIMEOUT_D (1000 * 1000) // 1s
187
188 #define TIS_TIMEOUT_MAX (90000 * 1000) // 90s
189
190 //
191 // Max TPM command/reponse length
192 //
193 #define TPMCMDBUFLENGTH 0x500
194
195 /**
196 Check whether TPM chip exist.
197
198 @param[in] TisReg Pointer to TIS register.
199
200 @retval TRUE TPM chip exists.
201 @retval FALSE TPM chip is not found.
202 **/
203 BOOLEAN
204 TisPcPresenceCheck (
205 IN TIS_PC_REGISTERS_PTR TisReg
206 )
207 {
208 UINT8 RegRead;
209
210 RegRead = MmioRead8 ((UINTN)&TisReg->Access);
211 return (BOOLEAN)(RegRead != (UINT8)-1);
212 }
213
214 /**
215 Check whether the value of a TPM chip register satisfies the input BIT setting.
216
217 @param[in] Register Address port of register to be checked.
218 @param[in] BitSet Check these data bits are set.
219 @param[in] BitClear Check these data bits are clear.
220 @param[in] TimeOut The max wait time (unit MicroSecond) when checking register.
221
222 @retval EFI_SUCCESS The register satisfies the check bit.
223 @retval EFI_TIMEOUT The register can't run into the expected status in time.
224 **/
225 EFI_STATUS
226 TisPcWaitRegisterBits (
227 IN UINT8 *Register,
228 IN UINT8 BitSet,
229 IN UINT8 BitClear,
230 IN UINT32 TimeOut
231 )
232 {
233 UINT8 RegRead;
234 UINT32 WaitTime;
235
236 for (WaitTime = 0; WaitTime < TimeOut; WaitTime += 30){
237 RegRead = MmioRead8 ((UINTN)Register);
238 if ((RegRead & BitSet) == BitSet && (RegRead & BitClear) == 0)
239 return EFI_SUCCESS;
240 MicroSecondDelay (30);
241 }
242 return EFI_TIMEOUT;
243 }
244
245 /**
246 Get BurstCount by reading the burstCount field of a TIS regiger
247 in the time of default TIS_TIMEOUT_D.
248
249 @param[in] TisReg Pointer to TIS register.
250 @param[out] BurstCount Pointer to a buffer to store the got BurstConut.
251
252 @retval EFI_SUCCESS Get BurstCount.
253 @retval EFI_INVALID_PARAMETER TisReg is NULL or BurstCount is NULL.
254 @retval EFI_TIMEOUT BurstCount can't be got in time.
255 **/
256 EFI_STATUS
257 TisPcReadBurstCount (
258 IN TIS_PC_REGISTERS_PTR TisReg,
259 OUT UINT16 *BurstCount
260 )
261 {
262 UINT32 WaitTime;
263 UINT8 DataByte0;
264 UINT8 DataByte1;
265
266 if (BurstCount == NULL || TisReg == NULL) {
267 return EFI_INVALID_PARAMETER;
268 }
269
270 WaitTime = 0;
271 do {
272 //
273 // TIS_PC_REGISTERS_PTR->burstCount is UINT16, but it is not 2bytes aligned,
274 // so it needs to use MmioRead8 to read two times
275 //
276 DataByte0 = MmioRead8 ((UINTN)&TisReg->BurstCount);
277 DataByte1 = MmioRead8 ((UINTN)&TisReg->BurstCount + 1);
278 *BurstCount = (UINT16)((DataByte1 << 8) + DataByte0);
279 if (*BurstCount != 0) {
280 return EFI_SUCCESS;
281 }
282 MicroSecondDelay (30);
283 WaitTime += 30;
284 } while (WaitTime < TIS_TIMEOUT_D);
285
286 return EFI_TIMEOUT;
287 }
288
289 /**
290 Set TPM chip to ready state by sending ready command TIS_PC_STS_READY
291 to Status Register in time.
292
293 @param[in] TisReg Pointer to TIS register.
294
295 @retval EFI_SUCCESS TPM chip enters into ready state.
296 @retval EFI_INVALID_PARAMETER TisReg is NULL.
297 @retval EFI_TIMEOUT TPM chip can't be set to ready state in time.
298 **/
299 EFI_STATUS
300 TisPcPrepareCommand (
301 IN TIS_PC_REGISTERS_PTR TisReg
302 )
303 {
304 EFI_STATUS Status;
305
306 if (TisReg == NULL) {
307 return EFI_INVALID_PARAMETER;
308 }
309
310 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_READY);
311 Status = TisPcWaitRegisterBits (
312 &TisReg->Status,
313 TIS_PC_STS_READY,
314 0,
315 TIS_TIMEOUT_B
316 );
317 return Status;
318 }
319
320 /**
321 Get the control of TPM chip by sending requestUse command TIS_PC_ACC_RQUUSE
322 to ACCESS Register in the time of default TIS_TIMEOUT_A.
323
324 @param[in] TisReg Pointer to TIS register.
325
326 @retval EFI_SUCCESS Get the control of TPM chip.
327 @retval EFI_INVALID_PARAMETER TisReg is NULL.
328 @retval EFI_NOT_FOUND TPM chip doesn't exit.
329 @retval EFI_TIMEOUT Can't get the TPM control in time.
330 **/
331 EFI_STATUS
332 TisPcRequestUseTpm (
333 IN TIS_PC_REGISTERS_PTR TisReg
334 )
335 {
336 EFI_STATUS Status;
337
338 if (TisReg == NULL) {
339 return EFI_INVALID_PARAMETER;
340 }
341
342 if (!TisPcPresenceCheck (TisReg)) {
343 return EFI_NOT_FOUND;
344 }
345
346 MmioWrite8((UINTN)&TisReg->Access, TIS_PC_ACC_RQUUSE);
347 Status = TisPcWaitRegisterBits (
348 &TisReg->Access,
349 (UINT8)(TIS_PC_ACC_ACTIVE |TIS_PC_VALID),
350 0,
351 TIS_TIMEOUT_A
352 );
353 return Status;
354 }
355
356 /**
357 Send a command to TPM for execution and return response data.
358
359 @param[in] TisReg TPM register space base address.
360 @param[in] BufferIn Buffer for command data.
361 @param[in] SizeIn Size of command data.
362 @param[in, out] BufferOut Buffer for response data.
363 @param[in, out] SizeOut Size of response data.
364
365 @retval EFI_SUCCESS Operation completed successfully.
366 @retval EFI_BUFFER_TOO_SMALL Response data buffer is too small.
367 @retval EFI_DEVICE_ERROR Unexpected device behavior.
368 @retval EFI_UNSUPPORTED Unsupported TPM version
369
370 **/
371 EFI_STATUS
372 TisTpmCommand (
373 IN TIS_PC_REGISTERS_PTR TisReg,
374 IN UINT8 *BufferIn,
375 IN UINT32 SizeIn,
376 IN OUT UINT8 *BufferOut,
377 IN OUT UINT32 *SizeOut
378 )
379 {
380 EFI_STATUS Status;
381 UINT16 BurstCount;
382 UINT32 Index;
383 UINT32 TpmOutSize;
384 UINT16 Data16;
385 UINT32 Data32;
386
387 DEBUG_CODE (
388 UINTN DebugSize;
389
390 DEBUG ((EFI_D_INFO, "Tpm2TisTpmCommand Send - "));
391 if (SizeIn > 0x100) {
392 DebugSize = 0x40;
393 } else {
394 DebugSize = SizeIn;
395 }
396 for (Index = 0; Index < DebugSize; Index++) {
397 DEBUG ((EFI_D_INFO, "%02x ", BufferIn[Index]));
398 }
399 if (DebugSize != SizeIn) {
400 DEBUG ((EFI_D_INFO, "...... "));
401 for (Index = SizeIn - 0x20; Index < SizeIn; Index++) {
402 DEBUG ((EFI_D_INFO, "%02x ", BufferIn[Index]));
403 }
404 }
405 DEBUG ((EFI_D_INFO, "\n"));
406 );
407 TpmOutSize = 0;
408
409 Status = TisPcPrepareCommand (TisReg);
410 if (EFI_ERROR (Status)){
411 DEBUG ((DEBUG_ERROR, "Tpm2 is not ready for command!\n"));
412 return EFI_DEVICE_ERROR;
413 }
414 //
415 // Send the command data to Tpm
416 //
417 Index = 0;
418 while (Index < SizeIn) {
419 Status = TisPcReadBurstCount (TisReg, &BurstCount);
420 if (EFI_ERROR (Status)) {
421 Status = EFI_DEVICE_ERROR;
422 goto Exit;
423 }
424 for (; BurstCount > 0 && Index < SizeIn; BurstCount--) {
425 MmioWrite8((UINTN)&TisReg->DataFifo, *(BufferIn + Index));
426 Index++;
427 }
428 }
429 //
430 // Check the Tpm status STS_EXPECT change from 1 to 0
431 //
432 Status = TisPcWaitRegisterBits (
433 &TisReg->Status,
434 (UINT8) TIS_PC_VALID,
435 TIS_PC_STS_EXPECT,
436 TIS_TIMEOUT_C
437 );
438 if (EFI_ERROR (Status)) {
439 DEBUG ((DEBUG_ERROR, "Tpm2 The send buffer too small!\n"));
440 Status = EFI_BUFFER_TOO_SMALL;
441 goto Exit;
442 }
443 //
444 // Executed the TPM command and waiting for the response data ready
445 //
446 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_GO);
447
448 //
449 // NOTE: That may take many seconds to minutes for certain commands, such as key generation.
450 //
451 Status = TisPcWaitRegisterBits (
452 &TisReg->Status,
453 (UINT8) (TIS_PC_VALID | TIS_PC_STS_DATA),
454 0,
455 TIS_TIMEOUT_MAX
456 );
457 if (EFI_ERROR (Status)) {
458 DEBUG ((DEBUG_ERROR, "Wait for Tpm2 response data time out!!\n"));
459 Status = EFI_DEVICE_ERROR;
460 goto Exit;
461 }
462 //
463 // Get response data header
464 //
465 Index = 0;
466 BurstCount = 0;
467 while (Index < sizeof (TPM2_RESPONSE_HEADER)) {
468 Status = TisPcReadBurstCount (TisReg, &BurstCount);
469 if (EFI_ERROR (Status)) {
470 Status = EFI_DEVICE_ERROR;
471 goto Exit;
472 }
473 for (; BurstCount > 0; BurstCount--) {
474 *(BufferOut + Index) = MmioRead8 ((UINTN)&TisReg->DataFifo);
475 Index++;
476 if (Index == sizeof (TPM2_RESPONSE_HEADER)) break;
477 }
478 }
479 DEBUG_CODE (
480 DEBUG ((EFI_D_INFO, "TisTpmCommand ReceiveHeader - "));
481 for (Index = 0; Index < sizeof (TPM2_RESPONSE_HEADER); Index++) {
482 DEBUG ((EFI_D_INFO, "%02x ", BufferOut[Index]));
483 }
484 DEBUG ((EFI_D_INFO, "\n"));
485 );
486 //
487 // Check the reponse data header (tag,parasize and returncode )
488 //
489 CopyMem (&Data16, BufferOut, sizeof (UINT16));
490 // TPM2 should not use this RSP_COMMAND
491 if (SwapBytes16 (Data16) == TPM_ST_RSP_COMMAND) {
492 DEBUG ((EFI_D_ERROR, "TPM2: TPM_ST_RSP error - %x\n", TPM_ST_RSP_COMMAND));
493 Status = EFI_UNSUPPORTED;
494 goto Exit;
495 }
496
497 CopyMem (&Data32, (BufferOut + 2), sizeof (UINT32));
498 TpmOutSize = SwapBytes32 (Data32);
499 if (*SizeOut < TpmOutSize) {
500 Status = EFI_BUFFER_TOO_SMALL;
501 goto Exit;
502 }
503 *SizeOut = TpmOutSize;
504 //
505 // Continue reading the remaining data
506 //
507 while ( Index < TpmOutSize ) {
508 for (; BurstCount > 0; BurstCount--) {
509 *(BufferOut + Index) = MmioRead8 ((UINTN)&TisReg->DataFifo);
510 Index++;
511 if (Index == TpmOutSize) {
512 Status = EFI_SUCCESS;
513 goto Exit;
514 }
515 }
516 Status = TisPcReadBurstCount (TisReg, &BurstCount);
517 if (EFI_ERROR (Status)) {
518 Status = EFI_DEVICE_ERROR;
519 goto Exit;
520 }
521 }
522 Exit:
523 DEBUG_CODE (
524 DEBUG ((EFI_D_INFO, "Tpm2TisTpmCommand Receive - "));
525 for (Index = 0; Index < TpmOutSize; Index++) {
526 DEBUG ((EFI_D_INFO, "%02x ", BufferOut[Index]));
527 }
528 DEBUG ((EFI_D_INFO, "\n"));
529 );
530 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_READY);
531 return Status;
532 }
533
534 /**
535 This service enables the sending of commands to the TPM2.
536
537 @param[in] InputParameterBlockSize Size of the TPM2 input parameter block.
538 @param[in] InputParameterBlock Pointer to the TPM2 input parameter block.
539 @param[in,out] OutputParameterBlockSize Size of the TPM2 output parameter block.
540 @param[in] OutputParameterBlock Pointer to the TPM2 output parameter block.
541
542 @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received.
543 @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device.
544 @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small.
545 **/
546 EFI_STATUS
547 EFIAPI
548 DTpm2SubmitCommand (
549 IN UINT32 InputParameterBlockSize,
550 IN UINT8 *InputParameterBlock,
551 IN OUT UINT32 *OutputParameterBlockSize,
552 IN UINT8 *OutputParameterBlock
553 )
554 {
555 return TisTpmCommand (
556 (TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress),
557 InputParameterBlock,
558 InputParameterBlockSize,
559 OutputParameterBlock,
560 OutputParameterBlockSize
561 );
562 }
563
564 /**
565 This service requests use TPM2.
566
567 @retval EFI_SUCCESS Get the control of TPM2 chip.
568 @retval EFI_NOT_FOUND TPM2 not found.
569 @retval EFI_DEVICE_ERROR Unexpected device behavior.
570 **/
571 EFI_STATUS
572 EFIAPI
573 DTpm2RequestUseTpm (
574 VOID
575 )
576 {
577 return TisPcRequestUseTpm ((TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress));
578 }