]> git.proxmox.com Git - mirror_edk2.git/blob - SecurityPkg/Tcg/TcgDxe/TisDxe.c
Add security package to repository.
[mirror_edk2.git] / SecurityPkg / Tcg / TcgDxe / TisDxe.c
1 /** @file
2 TIS (TPM Interface Specification) functions used by TPM Dxe driver.
3
4 Copyright (c) 2005 - 2010, 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 if(*DataLength + (UINT32) Size > TPMCMDBUFLENGTH) {
237 return EFI_BUFFER_TOO_SMALL;
238 }
239 CopyMem (TpmBuffer + *DataLength, Raw, Size);
240 *DataLength += (UINT32) Size;
241 return EFI_SUCCESS;
242 }
243
244 /**
245 Format reponse data according to the format control character.
246
247 @param[in] FmtChar Format control character.
248 @param[in, out] ap List of arguments.
249 @param[out] TpmBuffer Buffer for reponse data.
250 @param[in, out] DataIndex Data offset in reponse data buffer.
251 @param[in] RespSize Response data length.
252 @param[out] DataFinished Reach the end of Response data.
253
254 @retval EFI_SUCCESS Operation completed successfully.
255 @retval EFI_INVALID_PARAMETER Invalid format control character.
256 @retval EFI_BUFFER_TOO_SMALL Buffer too small for command data.
257
258 **/
259 EFI_STATUS
260 TisPcReceiveV (
261 IN UINT8 FmtChar,
262 IN OUT VA_LIST *ap,
263 OUT UINT8 *TpmBuffer,
264 IN OUT UINT32 *DataIndex,
265 IN UINT32 RespSize,
266 OUT BOOLEAN *DataFinished
267 )
268 {
269 UINT8 *Raw;
270 TPM_RSP_COMMAND_HDR *TpmRspPtr;
271 UINTN Size;
272
273 Raw = VA_ARG (*ap, UINT8*);
274 switch (FmtChar) {
275
276 case 'b':
277 Size = sizeof (UINT8);
278 break;
279
280 case 'w':
281 Size = sizeof (UINT16);
282 break;
283
284 case 'd':
285 Size = sizeof (UINT32);
286 break;
287
288 case 'h':
289 Size = sizeof (*TpmRspPtr);
290 break;
291
292 case 'r':
293 Size = VA_ARG (*ap, UINTN);
294 if(*DataIndex + (UINT32) Size <= RespSize) {
295 break;
296 }
297 *DataFinished = TRUE;
298 if (*DataIndex >= RespSize) {
299 return EFI_SUCCESS;
300 }
301 CopyMem (Raw, TpmBuffer + *DataIndex, RespSize - *DataIndex);
302 *DataIndex += RespSize - *DataIndex;
303 return EFI_SUCCESS;
304
305 case '\0':
306 return EFI_INVALID_PARAMETER;
307
308 default:
309 return EFI_WARN_UNKNOWN_GLYPH;
310 }
311
312 if(*DataIndex + (UINT32) Size > RespSize) {
313 *DataFinished = TRUE;
314 return EFI_SUCCESS;
315 }
316
317 if( *DataIndex + (UINT32) Size > TPMCMDBUFLENGTH )
318 return EFI_BUFFER_TOO_SMALL;
319
320 CopyMem (Raw, TpmBuffer + *DataIndex, Size);
321 *DataIndex += (UINT32) Size;
322
323 switch (FmtChar) {
324
325 case 'w':
326 *(UINT16*)Raw = SwapBytes16 (*(UINT16*) Raw);
327 break;
328
329 case 'd':
330 *(UINT32*)Raw = SwapBytes32 (*(UINT32*) Raw);
331 break;
332
333 case 'h':
334 TpmRspPtr = (TPM_RSP_COMMAND_HDR*) Raw;
335 TpmRspPtr->tag = SwapBytes16 (TpmRspPtr->tag);
336 TpmRspPtr->paramSize = SwapBytes32 (TpmRspPtr->paramSize);
337 TpmRspPtr->returnCode = SwapBytes32 (TpmRspPtr->returnCode);
338 break;
339 }
340 return EFI_SUCCESS;
341 }
342
343 /**
344 Send formatted command to TPM for execution and return formatted data from response.
345
346 @param[in] TisReg TPM Handle.
347 @param[in] Fmt Format control string.
348 @param[in] ... The variable argument list.
349
350 @retval EFI_SUCCESS Operation completed successfully.
351 @retval EFI_TIMEOUT The register can't run into the expected status in time.
352
353 **/
354 EFI_STATUS
355 EFIAPI
356 TisPcExecute (
357 IN TIS_TPM_HANDLE TisReg,
358 IN CONST CHAR8 *Fmt,
359 ...
360 )
361 {
362 EFI_STATUS Status;
363 VA_LIST Ap;
364 UINT32 BufSize;
365 UINT32 ResponseSize;
366 BOOLEAN DataFinished;
367
368 VA_START (Ap, Fmt);
369
370 //
371 // Put the formatted command to the TpmCommandBuf
372 //
373 BufSize = 0;
374 while (*Fmt != '\0') {
375 if (*Fmt == '%') Fmt++;
376 if (*Fmt == '/') break;
377 Status = TisPcSendV (*Fmt, &Ap, TpmCommandBuf, &BufSize);
378 if (EFI_ERROR( Status )) {
379 return Status;
380 }
381 Fmt++;
382 }
383 //
384 // Send the command to TPM
385 //
386 Status = TisPcSend (TisReg, TpmCommandBuf, BufSize);
387 if (EFI_ERROR (Status)) {
388 //
389 // Ensure the TPM state change from "Reception" to "Idle/Ready"
390 //
391 MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_READY);
392 return Status;
393 }
394
395 MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_GO);
396 Fmt++;
397 //
398 // Receive the response data from TPM
399 //
400 ZeroMem (TpmCommandBuf, TPMCMDBUFLENGTH);
401 Status = TisPcReceive (TisReg, TpmCommandBuf, &ResponseSize);
402 //
403 // Ensure the TPM state change from "Execution" or "Completion" to "Idle/Ready"
404 //
405 MmioWrite8 ((UINTN) &(((TIS_PC_REGISTERS_PTR) TisReg)->Status), TIS_PC_STS_READY);
406 if (EFI_ERROR (Status)) {
407 return Status;
408 }
409
410 //
411 // Get the formatted data from the TpmCommandBuf.
412 //
413 BufSize =0;
414 DataFinished = FALSE;
415 while (*Fmt != '\0') {
416 if (*Fmt == '%') {
417 Fmt++;
418 }
419 Status = TisPcReceiveV (*Fmt, &Ap, TpmCommandBuf, &BufSize, ResponseSize, &DataFinished);
420 if (EFI_ERROR (Status)) {
421 return Status;
422 }
423 if (DataFinished) {
424 return EFI_SUCCESS;
425 }
426 Fmt++;
427 }
428
429 VA_END (Ap);
430 return Status;
431 }
432