Home | History | Annotate | Download | only in TcgDxe
      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
     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
     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
    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
    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
    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