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