1 /** @file
2   TIS (TPM Interface Specification) functions used by TPM Dxe driver.
3 
4 Copyright (c) 2005 - 2012, 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/Tpm12.h>
16 #include <Library/TimerLib.h>
17 #include <Library/TpmCommLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/IoLib.h>
20 #include <Library/BaseLib.h>
21 #include <Library/BaseMemoryLib.h>
22 
23 STATIC UINT8                        TpmCommandBuf[TPMCMDBUFLENGTH];
24 
25 /**
26   Send command to TPM for execution.
27 
28   @param[in] TisReg     TPM register space base address.
29   @param[in] TpmBuffer  Buffer for TPM command data.
30   @param[in] DataLength TPM command data length.
31 
32   @retval EFI_SUCCESS   Operation completed successfully.
33   @retval EFI_TIMEOUT   The register can't run into the expected status in time.
34 
35 **/
36 EFI_STATUS
TisPcSend(IN TIS_PC_REGISTERS_PTR TisReg,IN UINT8 * TpmBuffer,IN UINT32 DataLength)37 TisPcSend (
38   IN     TIS_PC_REGISTERS_PTR       TisReg,
39   IN     UINT8                      *TpmBuffer,
40   IN     UINT32                     DataLength
41   )
42 {
43   UINT16                            BurstCount;
44   UINT32                            Index;
45   EFI_STATUS                        Status;
46 
47   Status = TisPcPrepareCommand (TisReg);
48   if (EFI_ERROR (Status)){
49     DEBUG ((DEBUG_ERROR, "The Tpm not ready!\n"));
50     return Status;
51   }
52   Index = 0;
53   while (Index < DataLength) {
54     Status = TisPcReadBurstCount (TisReg, &BurstCount);
55     if (EFI_ERROR (Status)) {
56       return EFI_TIMEOUT;
57     }
58     for (; BurstCount > 0 && Index < DataLength; BurstCount--) {
59       MmioWrite8 ((UINTN) &TisReg->DataFifo, *(TpmBuffer + Index));
60       Index++;
61     }
62   }
63   //
64   // Ensure the Tpm status STS_EXPECT change from 1 to 0
65   //
66   Status = TisPcWaitRegisterBits (
67              &TisReg->Status,
68              (UINT8) TIS_PC_VALID,
69              TIS_PC_STS_EXPECT,
70              TIS_TIMEOUT_C
71              );
72   return Status;
73 }
74 
75 /**
76   Receive response data of last command from TPM.
77 
78   @param[in]  TisReg            TPM register space base address.
79   @param[out] TpmBuffer         Buffer for response data.
80   @param[out] RespSize          Response data length.
81 
82   @retval EFI_SUCCESS           Operation completed successfully.
83   @retval EFI_TIMEOUT           The register can't run into the expected status in time.
84   @retval EFI_DEVICE_ERROR      Unexpected device status.
85   @retval EFI_BUFFER_TOO_SMALL  Response data is too long.
86 
87 **/
88 EFI_STATUS
TisPcReceive(IN TIS_PC_REGISTERS_PTR TisReg,OUT UINT8 * TpmBuffer,OUT UINT32 * RespSize)89 TisPcReceive (
90   IN      TIS_PC_REGISTERS_PTR      TisReg,
91      OUT  UINT8                     *TpmBuffer,
92      OUT  UINT32                    *RespSize
93   )
94 {
95   EFI_STATUS                        Status;
96   UINT16                            BurstCount;
97   UINT32                            Index;
98   UINT32                            ResponseSize;
99   UINT32                            Data32;
100 
101   //
102   // Wait for the command completion
103   //
104   Status = TisPcWaitRegisterBits (
105              &TisReg->Status,
106              (UINT8) (TIS_PC_VALID | TIS_PC_STS_DATA),
107              0,
108              TIS_TIMEOUT_B
109              );
110   if (EFI_ERROR (Status)) {
111     return EFI_TIMEOUT;
112   }
113   //
114   // Read the response data header and check it
115   //
116   Index = 0;
117   BurstCount = 0;
118   while (Index < sizeof (TPM_RSP_COMMAND_HDR)) {
119     Status = TisPcReadBurstCount (TisReg, &BurstCount);
120     if (EFI_ERROR (Status)) {
121       return EFI_TIMEOUT;
122     }
123     for (; BurstCount > 0 ; BurstCount--) {
124       *(TpmBuffer + Index) = MmioRead8 ((UINTN) &TisReg->DataFifo);
125       Index++;
126       if (Index == sizeof (TPM_RSP_COMMAND_HDR))
127         break;
128     }
129   }
130   //
131   // Check the reponse data header (tag,parasize and returncode )
132   //
133   CopyMem (&Data32, (TpmBuffer + 2), sizeof (UINT32));
134   ResponseSize = SwapBytes32 (Data32);
135   *RespSize =  ResponseSize;
136   if (ResponseSize == sizeof (TPM_RSP_COMMAND_HDR)) {
137     return EFI_SUCCESS;
138   }
139   if (ResponseSize < sizeof (TPM_RSP_COMMAND_HDR)) {
140     return EFI_DEVICE_ERROR;
141   }
142   if (ResponseSize > TPMCMDBUFLENGTH) {
143     return EFI_BUFFER_TOO_SMALL;
144   }
145   //
146   // Continue reading the remaining data
147   //
148   while (Index < ResponseSize) {
149     for (; BurstCount > 0 ; BurstCount--) {
150       *(TpmBuffer + Index) = MmioRead8 ((UINTN) &TisReg->DataFifo);
151       Index++;
152       if (Index == ResponseSize) {
153         return EFI_SUCCESS;
154       }
155     }
156     Status = TisPcReadBurstCount (TisReg, &BurstCount);
157     if (EFI_ERROR (Status) && (Index < ResponseSize)) {
158       return EFI_DEVICE_ERROR;
159     }
160   }
161   return EFI_SUCCESS;
162 }
163 
164 /**
165   Format TPM command data according to the format control character.
166 
167   @param[in]      FmtChar       Format control character.
168   @param[in, out] ap            List of arguments.
169   @param[in]      TpmBuffer     Buffer for TPM command data.
170   @param[out]     DataLength    TPM command data length.
171 
172   @retval EFI_SUCCESS           Operation completed successfully.
173   @retval EFI_INVALID_PARAMETER Invalid format control character.
174   @retval EFI_BUFFER_TOO_SMALL  Buffer too small for command data.
175 
176 **/
177 EFI_STATUS
TisPcSendV(IN UINT8 FmtChar,IN OUT VA_LIST * ap,UINT8 * TpmBuffer,UINT32 * DataLength)178 TisPcSendV (
179   IN      UINT8                     FmtChar,
180   IN OUT  VA_LIST                   *ap,
181   UINT8                             *TpmBuffer,
182   UINT32                            *DataLength
183   )
184 {
185   UINT8                             DataByte;
186   UINT16                            DataWord;
187   UINT32                            DataDword;
188   TPM_RQU_COMMAND_HDR               TpmCmdHdr;
189   TPM_RQU_COMMAND_HDR               *TpmCmdPtr;
190   UINTN                             Size;
191   UINT8                             *Raw;
192 
193   switch (FmtChar) {
194 
195     case 'b':
196       DataByte  = VA_ARG (*ap, UINT8);
197       Raw = &DataByte;
198       Size = sizeof (DataByte);
199       break;
200 
201     case 'w':
202       DataWord  = VA_ARG (*ap, UINT16);
203       DataWord  = SwapBytes16 (DataWord);
204       Raw = (UINT8*)&DataWord;
205       Size = sizeof (DataWord);
206       break;
207 
208     case 'd':
209       DataDword  = VA_ARG (*ap, UINT32);
210       DataDword  = SwapBytes32 (DataDword);
211       Raw = (UINT8*)&DataDword;
212       Size = sizeof (DataDword);
213       break;
214 
215     case 'h':
216       TpmCmdPtr           = VA_ARG (*ap, TPM_RQU_COMMAND_HDR*);
217       TpmCmdHdr.tag       = SwapBytes16 (TpmCmdPtr->tag);
218       TpmCmdHdr.paramSize = SwapBytes32 (TpmCmdPtr->paramSize);
219       TpmCmdHdr.ordinal   = SwapBytes32 (TpmCmdPtr->ordinal);
220       Raw                 = (UINT8*) &TpmCmdHdr;
221       Size                = sizeof (TpmCmdHdr);
222       break;
223 
224     case 'r':
225       Raw  = VA_ARG (*ap, UINT8*);
226       Size = VA_ARG (*ap, UINTN);
227       break;
228 
229     case '\0':
230       return EFI_INVALID_PARAMETER;
231 
232     default:
233       return EFI_INVALID_PARAMETER;
234   }
235 
236   //
237   // Check input to avoid overflow.
238   //
239   if ((UINT32) (~0)- *DataLength < (UINT32)Size) {
240     return EFI_INVALID_PARAMETER;
241   }
242 
243   if(*DataLength + (UINT32) Size > TPMCMDBUFLENGTH) {
244     return EFI_BUFFER_TOO_SMALL;
245   }
246   CopyMem (TpmBuffer + *DataLength, Raw, Size);
247   *DataLength += (UINT32) Size;
248   return EFI_SUCCESS;
249 }
250 
251 /**
252   Format reponse data according to the format control character.
253 
254   @param[in]      FmtChar       Format control character.
255   @param[in, out] ap            List of arguments.
256   @param[out]     TpmBuffer     Buffer for reponse data.
257   @param[in, out] DataIndex     Data offset in reponse data buffer.
258   @param[in]      RespSize      Response data length.
259   @param[out]     DataFinished  Reach the end of Response data.
260 
261   @retval EFI_SUCCESS           Operation completed successfully.
262   @retval EFI_INVALID_PARAMETER Invalid format control character.
263   @retval EFI_BUFFER_TOO_SMALL  Buffer too small for command data.
264 
265 **/
266 EFI_STATUS
TisPcReceiveV(IN UINT8 FmtChar,IN OUT VA_LIST * ap,OUT UINT8 * TpmBuffer,IN OUT UINT32 * DataIndex,IN UINT32 RespSize,OUT BOOLEAN * DataFinished)267 TisPcReceiveV (
268   IN      UINT8                     FmtChar,
269   IN OUT  VA_LIST                   *ap,
270      OUT  UINT8                     *TpmBuffer,
271   IN OUT  UINT32                    *DataIndex,
272   IN      UINT32                    RespSize,
273      OUT  BOOLEAN                   *DataFinished
274   )
275 {
276   UINT8                             *Raw;
277   TPM_RSP_COMMAND_HDR               *TpmRspPtr;
278   UINTN                             Size;
279 
280   Raw = VA_ARG (*ap, UINT8*);
281   switch (FmtChar) {
282 
283     case 'b':
284       Size = sizeof (UINT8);
285       break;
286 
287     case 'w':
288       Size = sizeof (UINT16);
289       break;
290 
291     case 'd':
292       Size = sizeof (UINT32);
293       break;
294 
295     case 'h':
296       Size = sizeof (*TpmRspPtr);
297       break;
298 
299     case 'r':
300       Size = VA_ARG (*ap, UINTN);
301       //
302       // If overflowed, which means Size is big enough for Response data.
303       // skip this check. Copy the whole data
304       //
305       if ((UINT32) (~0)- *DataIndex >= (UINT32)Size) {
306         if(*DataIndex + (UINT32) Size <= RespSize) {
307           break;
308         }
309       }
310 
311       *DataFinished = TRUE;
312       if (*DataIndex >= RespSize) {
313         return EFI_SUCCESS;
314       }
315       CopyMem (Raw, TpmBuffer + *DataIndex, RespSize - *DataIndex);
316       *DataIndex += RespSize - *DataIndex;
317       return EFI_SUCCESS;
318 
319     case '\0':
320       return EFI_INVALID_PARAMETER;
321 
322     default:
323       return EFI_WARN_UNKNOWN_GLYPH;
324   }
325 
326   if(*DataIndex + (UINT32) Size > RespSize) {
327     *DataFinished = TRUE;
328     return EFI_SUCCESS;
329   }
330 
331   if( *DataIndex + (UINT32) Size > TPMCMDBUFLENGTH )
332     return EFI_BUFFER_TOO_SMALL;
333 
334   CopyMem (Raw, TpmBuffer + *DataIndex, Size);
335   *DataIndex += (UINT32) Size;
336 
337   switch (FmtChar) {
338 
339     case 'w':
340       *(UINT16*)Raw = SwapBytes16 (*(UINT16*) Raw);
341       break;
342 
343     case 'd':
344       *(UINT32*)Raw = SwapBytes32 (*(UINT32*) Raw);
345       break;
346 
347     case 'h':
348       TpmRspPtr = (TPM_RSP_COMMAND_HDR*) Raw;
349       TpmRspPtr->tag = SwapBytes16 (TpmRspPtr->tag);
350       TpmRspPtr->paramSize = SwapBytes32 (TpmRspPtr->paramSize);
351       TpmRspPtr->returnCode = SwapBytes32 (TpmRspPtr->returnCode);
352       break;
353   }
354   return EFI_SUCCESS;
355 }
356 
357 /**
358   Send formatted command to TPM for execution and return formatted data from response.
359 
360   @param[in] TisReg    TPM Handle.
361   @param[in] Fmt       Format control string.
362   @param[in] ...       The variable argument list.
363 
364   @retval EFI_SUCCESS  Operation completed successfully.
365   @retval EFI_TIMEOUT  The register can't run into the expected status in time.
366 
367 **/
368 EFI_STATUS
369 EFIAPI
TisPcExecute(IN TIS_TPM_HANDLE TisReg,IN CONST CHAR8 * Fmt,...)370 TisPcExecute (
371   IN      TIS_TPM_HANDLE            TisReg,
372   IN      CONST CHAR8               *Fmt,
373   ...
374   )
375 {
376   EFI_STATUS                        Status;
377   VA_LIST                           Ap;
378   UINT32                            BufSize;
379   UINT32                            ResponseSize;
380   BOOLEAN                           DataFinished;
381 
382   VA_START (Ap, Fmt);
383 
384   //
385   // Put the formatted command to the TpmCommandBuf
386   //
387   BufSize = 0;
388   while (*Fmt != '\0') {
389     if (*Fmt == '%') Fmt++;
390     if (*Fmt == '/') break;
391     Status = TisPcSendV (*Fmt, &Ap, TpmCommandBuf, &BufSize);
392     if (EFI_ERROR( Status )) {
393       goto Error;
394     }
395     Fmt++;
396   }
397   //
398   // Send the command to TPM
399   //
400   Status = TisPcSend (TisReg, TpmCommandBuf, BufSize);
401   if (EFI_ERROR (Status))  {
402     //
403     // Ensure the TPM state change from "Reception" to "Idle/Ready"
404     //
405     MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_READY);
406     goto Error;
407   }
408 
409   MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_GO);
410   Fmt++;
411   //
412   // Receive the response data from TPM
413   //
414   ZeroMem (TpmCommandBuf, TPMCMDBUFLENGTH);
415   Status = TisPcReceive (TisReg, TpmCommandBuf, &ResponseSize);
416   //
417   // Ensure the TPM state change from "Execution" or "Completion" to "Idle/Ready"
418   //
419   MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_READY);
420   if (EFI_ERROR (Status)) {
421     goto Error;
422   }
423 
424   //
425   // Get the formatted data from the TpmCommandBuf.
426   //
427   BufSize =0;
428   DataFinished = FALSE;
429   while (*Fmt != '\0') {
430     if (*Fmt == '%') {
431       Fmt++;
432     }
433     Status = TisPcReceiveV (*Fmt, &Ap, TpmCommandBuf, &BufSize, ResponseSize, &DataFinished);
434     if (EFI_ERROR (Status)) {
435       goto Error;
436     }
437     if (DataFinished) {
438       VA_END (Ap);
439       return EFI_SUCCESS;
440     }
441     Fmt++;
442   }
443 
444 Error:
445   VA_END (Ap);
446   return Status;
447 }
448 
449