]> git.proxmox.com Git - mirror_edk2.git/blame - QuarkPlatformPkg/Library/Tpm12DeviceLibAtmelI2c/TisPc.c
QuarkPlatformPkg/Tpm12DeviceLibAtmelI2c: Fix SubmitCommand() out size
[mirror_edk2.git] / QuarkPlatformPkg / Library / Tpm12DeviceLibAtmelI2c / TisPc.c
CommitLineData
df865245
MK
1/** @file\r
2 Basic TIS (TPM Interface Specification) functions for Atmel I2C TPM.\r
3\r
a0f26e27 4 Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>\r
df865245
MK
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include <PiPei.h>\r
16#include <Library/Tpm12DeviceLib.h>\r
17#include <Library/BaseLib.h>\r
18#include <Library/TimerLib.h>\r
19#include <Library/DebugLib.h>\r
20#include <Library/I2cLib.h>\r
df865245
MK
21\r
22//\r
23// Atmel I2C TPM slave address\r
24//\r
25#define ATMEL_I2C_TPM_SLAVE_ADDRESS 0x29\r
26\r
27//\r
28// Maximum I2C transfer size for Atmel I2C TPM\r
29//\r
30#define ATMEL_I2C_TPM_MAX_TRANSFER_SIZE 0x10\r
31\r
32//\r
33// Default TimeOut values in microseconds\r
34//\r
35#define TIS_TIMEOUT_A ( 750 * 1000) // 750ms\r
36#define TIS_TIMEOUT_B (2000 * 1000) // 2s\r
37#define TIS_TIMEOUT_C ( 750 * 1000) // 750ms\r
38#define TIS_TIMEOUT_D ( 750 * 1000) // 750ms\r
39\r
40/**\r
41 Send command to Atmel I2c TPM breaking request up into multiple I2C transfers\r
42 if required.\r
43\r
44 @param[in] Buffer Pointer to TPM command data.\r
45 @param[in] Length Number of bytes of TPM command data.\r
46\r
47 @retval EFI_SUCCESS TPM command sent.\r
48 @retval EFI_NOT_FOUND TPM chip doesn't exit.\r
49 @retval EFI_TIMEOUT Can't get the TPM control in time.\r
50**/\r
51EFI_STATUS\r
52WriteTpmBufferMultiple (\r
53 IN UINT8 *Buffer,\r
54 IN UINTN Length\r
55 )\r
56{\r
57 EFI_STATUS Status;\r
58 EFI_I2C_DEVICE_ADDRESS I2CDeviceAddr;\r
59 UINTN Index;\r
60 UINTN PartialLength;\r
61\r
62 I2CDeviceAddr.I2CDeviceAddress = ATMEL_I2C_TPM_SLAVE_ADDRESS;\r
63\r
64 DEBUG ((EFI_D_VERBOSE, "WriteTpmBufferMultiple: Addr=%02x Length=%02x\n", I2CDeviceAddr.I2CDeviceAddress, Length));\r
65\r
66 for (PartialLength = 0; Length > 0; Length -= PartialLength, Buffer += PartialLength) {\r
67 //\r
68 // Write data to TPM.\r
69 //\r
70 PartialLength = MIN (Length, ATMEL_I2C_TPM_MAX_TRANSFER_SIZE);\r
71 Status = I2cWriteMultipleByte (\r
72 I2CDeviceAddr,\r
73 EfiI2CSevenBitAddrMode,\r
74 &PartialLength,\r
75 Buffer\r
76 );\r
77 DEBUG ((EFI_D_VERBOSE, " "));\r
78 for (Index = 0; Index < PartialLength; Index++) {\r
79 DEBUG ((EFI_D_VERBOSE, "%02x ", Buffer[Index]));\r
80 }\r
81 DEBUG ((EFI_D_VERBOSE, "\n"));\r
82 if (EFI_ERROR (Status)) {\r
83 DEBUG ((EFI_D_VERBOSE, " Status = %r\n", Status));\r
84 return Status;\r
85 }\r
86 }\r
87\r
88 DEBUG ((EFI_D_VERBOSE, " Status = %r\n", Status));\r
89 return Status;\r
90}\r
91\r
92/**\r
93 Receive a response to a command from Atmel I2c TPM breaking response into\r
94 multiple I2C transfers if required.\r
95\r
96 @param[out] Buffer Pointer to TPM response data.\r
97 @param[in] Length Maximum number of bytes to receive.\r
98\r
99 @retval EFI_SUCCESS TPM response received.\r
100 @retval EFI_NOT_FOUND TPM chip doesn't exit.\r
101 @retval EFI_TIMEOUT Can't get the TPM control in time.\r
102**/\r
103EFI_STATUS\r
104ReadTpmBufferMultiple (\r
105 OUT UINT8 *Buffer,\r
106 IN UINTN Length\r
107 )\r
108{\r
109 EFI_STATUS Status;\r
110 EFI_I2C_DEVICE_ADDRESS I2CDeviceAddr;\r
111 UINTN WriteLength;\r
112 UINTN Index;\r
113 UINTN PartialLength;\r
114\r
115 I2CDeviceAddr.I2CDeviceAddress = ATMEL_I2C_TPM_SLAVE_ADDRESS;\r
116 WriteLength = 0;\r
117\r
118 DEBUG ((EFI_D_VERBOSE, "ReadTpmBufferMultiple: Addr=%02x Length=%02x\n", I2CDeviceAddr.I2CDeviceAddress, Length));\r
119\r
120 for (PartialLength = 0; Length > 0; Length -= PartialLength, Buffer += PartialLength) {\r
121 //\r
122 // Read data from TPM.\r
123 //\r
124 PartialLength = MIN (Length, ATMEL_I2C_TPM_MAX_TRANSFER_SIZE);\r
125 Status = I2cReadMultipleByte (\r
126 I2CDeviceAddr,\r
127 EfiI2CSevenBitAddrMode,\r
128 &WriteLength,\r
129 &PartialLength,\r
130 Buffer\r
131 );\r
132 if (!EFI_ERROR (Status)) {\r
133 DEBUG ((EFI_D_VERBOSE, " "));\r
134 for (Index = 0; Index < PartialLength; Index++) {\r
135 DEBUG ((EFI_D_VERBOSE, "%02x ", Buffer[Index]));\r
136 }\r
137 DEBUG ((EFI_D_VERBOSE, "\n"));\r
138 }\r
139 if (EFI_ERROR (Status)) {\r
140 DEBUG ((EFI_D_VERBOSE, " Status = %r\n", Status));\r
141 return Status;\r
142 }\r
143 }\r
144\r
145 DEBUG ((EFI_D_VERBOSE, " Status = %r\n", Status));\r
146 return Status;\r
147}\r
148\r
149/**\r
150 This service requests use TPM12.\r
151\r
152 @retval EFI_SUCCESS Get the control of TPM12 chip.\r
153 @retval EFI_NOT_FOUND TPM12 not found.\r
154 @retval EFI_DEVICE_ERROR Unexpected device behavior.\r
155**/\r
156EFI_STATUS\r
157EFIAPI\r
158Tpm12RequestUseTpm (\r
159 VOID\r
160 )\r
161{\r
162 EFI_STATUS Status;\r
163 UINT8 Data;\r
164 UINT64 Current;\r
165 UINT64 Previous;\r
166 UINT64 Total;\r
167 UINT64 Start;\r
168 UINT64 End;\r
169 UINT64 Timeout;\r
170 INT64 Cycle;\r
171 INT64 Delta;\r
172\r
173 //\r
174 // Get the current timer value\r
175 //\r
176 Current = GetPerformanceCounter();\r
177\r
178 //\r
179 // Initialize local variables\r
180 //\r
181 Start = 0;\r
182 End = 0;\r
183 Total = 0;\r
184\r
185 //\r
186 // Retrieve the performance counter properties and compute the number of\r
187 // performance counter ticks required to reach the maximum TIS timeout of\r
188 // TIS_TIMEOUT_A. TIS_TIMEOUT_A is in microseconds.\r
189 //\r
190 Timeout = DivU64x32 (\r
191 MultU64x32 (\r
192 GetPerformanceCounterProperties (&Start, &End),\r
193 TIS_TIMEOUT_A\r
194 ),\r
195 1000000\r
196 );\r
197 Cycle = End - Start;\r
198 if (Cycle < 0) {\r
199 Cycle = -Cycle;\r
200 }\r
201 Cycle++;\r
202\r
203 //\r
204 // Attempt to read a byte from the Atmel I2C TPM\r
205 //\r
206 do {\r
207 Status = ReadTpmBufferMultiple (&Data, sizeof(Data));\r
208\r
209 Previous = Current;\r
210 Current = GetPerformanceCounter();\r
211 Delta = (INT64) (Current - Previous);\r
212 if (Start > End) {\r
213 Delta = -Delta;\r
214 }\r
215 if (Delta < 0) {\r
216 Delta += Cycle;\r
217 }\r
218 Total += Delta;\r
219 if (Total >= Timeout) {\r
220 Status = EFI_TIMEOUT;\r
221 DEBUG ((EFI_D_ERROR, "Atmel I2C TPM failed to read: %r\n", Status));\r
222 return Status;\r
223 }\r
224 } while (EFI_ERROR (Status));\r
225\r
df865245
MK
226 return EFI_SUCCESS;\r
227}\r
228\r
229/**\r
230 This service enables the sending of commands to the TPM12.\r
231\r
232 @param[in] InputParameterBlockSize Size of the TPM12 input parameter block.\r
233 @param[in] InputParameterBlock Pointer to the TPM12 input parameter block.\r
234 @param[in,out] OutputParameterBlockSize Size of the TPM12 output parameter block.\r
235 @param[in] OutputParameterBlock Pointer to the TPM12 output parameter block.\r
236\r
237 @retval EFI_SUCCESS The command byte stream was successfully sent to\r
238 the device and a response was successfully received.\r
239 @retval EFI_DEVICE_ERROR The command was not successfully sent to the\r
240 device or a response was not successfully received\r
241 from the device.\r
242 @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small.\r
243**/\r
244EFI_STATUS\r
245EFIAPI\r
246Tpm12SubmitCommand (\r
247 IN UINT32 InputParameterBlockSize,\r
248 IN UINT8 *InputParameterBlock,\r
249 IN OUT UINT32 *OutputParameterBlockSize,\r
250 IN UINT8 *OutputParameterBlock\r
251 )\r
252{\r
253 EFI_STATUS Status;\r
254 UINT32 TpmOutSize;\r
255 TPM_RSP_COMMAND_HDR *ResponseHeader;\r
256 UINT64 Current;\r
257 UINT64 Previous;\r
258 UINT64 Total;\r
259 UINT64 Start;\r
260 UINT64 End;\r
261 UINT64 Timeout;\r
262 INT64 Cycle;\r
263 INT64 Delta;\r
264\r
265 //\r
266 // Make sure response buffer is big enough to hold a response header\r
267 //\r
268 if (*OutputParameterBlockSize < sizeof (TPM_RSP_COMMAND_HDR)) {\r
269 Status = EFI_BUFFER_TOO_SMALL;\r
270 goto Done;\r
271 }\r
272\r
273 //\r
274 // Get the current timer value\r
275 //\r
276 Current = GetPerformanceCounter();\r
277\r
278 //\r
279 // Initialize local variables\r
280 //\r
281 Start = 0;\r
282 End = 0;\r
283 Total = 0;\r
284\r
285 //\r
286 // Retrieve the performance counter properties and compute the number of\r
287 // performance counter ticks required to reach the maximum TIS timeout of\r
288 // TIS_TIMEOUT_A. TIS_TIMEOUT_A is in microseconds.\r
289 //\r
290 Timeout = DivU64x32 (\r
291 MultU64x32 (\r
292 GetPerformanceCounterProperties (&Start, &End),\r
293 TIS_TIMEOUT_A\r
294 ),\r
295 1000000\r
296 );\r
297 Cycle = End - Start;\r
298 if (Cycle < 0) {\r
299 Cycle = -Cycle;\r
300 }\r
301 Cycle++;\r
302\r
303 //\r
304 // Send command\r
305 //\r
306 do {\r
307 Status = WriteTpmBufferMultiple (InputParameterBlock, InputParameterBlockSize);\r
308\r
309 Previous = Current;\r
310 Current = GetPerformanceCounter();\r
311 Delta = (INT64) (Current - Previous);\r
312 if (Start > End) {\r
313 Delta = -Delta;\r
314 }\r
315 if (Delta < 0) {\r
316 Delta += Cycle;\r
317 }\r
318 Total += Delta;\r
319 if (Total >= Timeout) {\r
320 Status = EFI_TIMEOUT;\r
321 goto Done;\r
322 }\r
323 } while (EFI_ERROR (Status));\r
324\r
325 //\r
326 // Receive response header\r
327 //\r
328 do {\r
329 Status = ReadTpmBufferMultiple (OutputParameterBlock, sizeof (TPM_RSP_COMMAND_HDR));\r
330\r
331 Previous = Current;\r
332 Current = GetPerformanceCounter();\r
333 Delta = (INT64) (Current - Previous);\r
334 if (Start > End) {\r
335 Delta = -Delta;\r
336 }\r
337 if (Delta < 0) {\r
338 Delta += Cycle;\r
339 }\r
340 Total += Delta;\r
341 if (Total >= Timeout) {\r
342 Status = EFI_TIMEOUT;\r
343 goto Done;\r
344 }\r
345 } while (EFI_ERROR (Status));\r
346\r
347 //\r
348 // Check the response data header (tag, parasize and returncode)\r
349 //\r
350 ResponseHeader = (TPM_RSP_COMMAND_HDR *)OutputParameterBlock;\r
351 if (SwapBytes16 (ReadUnaligned16 (&ResponseHeader->tag)) != TPM_TAG_RSP_COMMAND) {\r
352 Status = EFI_DEVICE_ERROR;\r
353 goto Done;\r
354 }\r
355\r
356 TpmOutSize = SwapBytes32 (ReadUnaligned32 (&ResponseHeader->paramSize));\r
357 if (TpmOutSize == sizeof (TPM_RSP_COMMAND_HDR)) {\r
39dfd12d 358 *OutputParameterBlockSize = TpmOutSize;\r
df865245
MK
359 Status = EFI_SUCCESS;\r
360 goto Done;\r
361 }\r
362 if (TpmOutSize < sizeof (TPM_RSP_COMMAND_HDR)) {\r
363 Status = EFI_DEVICE_ERROR;\r
364 goto Done;\r
365 }\r
366 if (*OutputParameterBlockSize < TpmOutSize) {\r
367 Status = EFI_BUFFER_TOO_SMALL;\r
368 goto Done;\r
369 }\r
370 *OutputParameterBlockSize = TpmOutSize;\r
371\r
372 //\r
373 // Receive the remaining data in the response header\r
374 //\r
375 do {\r
376 Status = ReadTpmBufferMultiple (\r
377 OutputParameterBlock + sizeof (TPM_RSP_COMMAND_HDR),\r
378 TpmOutSize - sizeof (TPM_RSP_COMMAND_HDR)\r
379 );\r
380\r
381 Previous = Current;\r
382 Current = GetPerformanceCounter();\r
383 Delta = (INT64) (Current - Previous);\r
384 if (Start > End) {\r
385 Delta = -Delta;\r
386 }\r
387 if (Delta < 0) {\r
388 Delta += Cycle;\r
389 }\r
390 Total += Delta;\r
391 if (Total >= Timeout) {\r
392 Status = EFI_TIMEOUT;\r
393 goto Done;\r
394 }\r
395 } while (EFI_ERROR (Status));\r
396\r
397Done:\r
398 DEBUG ((\r
399 EFI_D_VERBOSE,\r
400 "Tpm12SubmitCommand() Status = %r Time = %ld ms\n",\r
401 Status,\r
402 DivU64x64Remainder (\r
403 MultU64x32 (Total, 1000),\r
404 GetPerformanceCounterProperties (NULL, NULL),\r
405 NULL\r
406 )\r
407 ));\r
408\r
409 return Status;\r
410}\r