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