]> git.proxmox.com Git - mirror_edk2.git/blame - QuarkPlatformPkg/Library/Tpm12DeviceLibInfineonI2c/TisPc.c
ArmPkg/CompilerIntrinsicsLib: Add uread, uwrite GCC assembly sources
[mirror_edk2.git] / QuarkPlatformPkg / Library / Tpm12DeviceLibInfineonI2c / TisPc.c
CommitLineData
957649a7
MK
1/** @file\r
2 Basic TIS (TPM Interface Specification) functions for Infineon I2C TPM.\r
3\r
4 Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>\r
0eb3de2e 5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
957649a7
MK
6\r
7**/\r
8\r
9#include <PiPei.h>\r
10#include <Library/Tpm12DeviceLib.h>\r
11#include <Library/BaseLib.h>\r
12#include <Library/TimerLib.h>\r
13#include <Library/DebugLib.h>\r
14#include <Library/I2cLib.h>\r
15\r
16//\r
17// Default TPM (Infineon SLB9645) I2C Slave Device Address on Crosshill board.\r
18//\r
19#define TPM_I2C_SLAVE_DEVICE_ADDRESS 0x20\r
20\r
21//\r
22// Default Infineon SLB9645 TPM I2C mapped registers (SLB9645 I2C Comm. Protocol Application Note).\r
23//\r
24#define INFINEON_TPM_ACCESS_0_ADDRESS_DEFAULT 0x0\r
25#define INFINEON_TPM_STS_0_ADDRESS_DEFAULT 0x01\r
26#define INFINEON_TPM_BURST0_COUNT_0_DEFAULT 0x02\r
27#define INFINEON_TPM_BURST1_COUNT_0_DEFAULT 0x03\r
28#define INFINEON_TPM_DATA_FIFO_0_ADDRESS_DEFAULT 0x05\r
29#define INFINEON_TPM_DID_VID_0_DEFAULT 0x09\r
30\r
31//\r
32// Max. retry count for read transfers (as recommended by Infineon).\r
33//\r
34#define READ_RETRY 3\r
35\r
36//\r
37// Guard time of 250us between I2C read and next I2C write transfer (as recommended by Infineon).\r
38//\r
39#define GUARD_TIME 250\r
40\r
41//\r
42// Define bits of ACCESS and STATUS registers\r
43//\r
44\r
45///\r
46/// This bit is a 1 to indicate that the other bits in this register are valid.\r
47///\r
48#define TIS_PC_VALID BIT7\r
49///\r
50/// Indicate that this locality is active.\r
51///\r
52#define TIS_PC_ACC_ACTIVE BIT5\r
53///\r
54/// Set to 1 to indicate that this locality had the TPM taken away while\r
55/// this locality had the TIS_PC_ACC_ACTIVE bit set.\r
56///\r
57#define TIS_PC_ACC_SEIZED BIT4\r
58///\r
59/// Set to 1 to indicate that TPM MUST reset the\r
60/// TIS_PC_ACC_ACTIVE bit and remove ownership for localities less than the\r
61/// locality that is writing this bit.\r
62///\r
63#define TIS_PC_ACC_SEIZE BIT3\r
64///\r
65/// When this bit is 1, another locality is requesting usage of the TPM.\r
66///\r
67#define TIS_PC_ACC_PENDIND BIT2\r
68///\r
69/// Set to 1 to indicate that this locality is requesting to use TPM.\r
70///\r
71#define TIS_PC_ACC_RQUUSE BIT1\r
72///\r
73/// A value of 1 indicates that a T/OS has not been established on the platform\r
74///\r
75#define TIS_PC_ACC_ESTABLISH BIT0\r
76\r
77///\r
78/// When this bit is 1, TPM is in the Ready state,\r
79/// indicating it is ready to receive a new command.\r
80///\r
81#define TIS_PC_STS_READY BIT6\r
82///\r
83/// Write a 1 to this bit to cause the TPM to execute that command.\r
84///\r
85#define TIS_PC_STS_GO BIT5\r
86///\r
87/// This bit indicates that the TPM has data available as a response.\r
88///\r
89#define TIS_PC_STS_DATA BIT4\r
90///\r
91/// The TPM sets this bit to a value of 1 when it expects another byte of data for a command.\r
92///\r
93#define TIS_PC_STS_EXPECT BIT3\r
94///\r
95/// Writes a 1 to this bit to force the TPM to re-send the response.\r
96///\r
97#define TIS_PC_STS_RETRY BIT1\r
98\r
99//\r
100// Default TimeOut values in microseconds\r
101//\r
102#define TIS_TIMEOUT_A (750 * 1000) // 750ms\r
103#define TIS_TIMEOUT_B (2000 * 1000) // 2s\r
104#define TIS_TIMEOUT_C (750 * 1000) // 750ms\r
105#define TIS_TIMEOUT_D (750 * 1000) // 750ms\r
106\r
107//\r
108// Global variable to indicate if TPM I2C Read Transfer has previously occurred.\r
109// NOTE: Given the GUARD_TIME requirement (TpmAccess.h), if this library loaded\r
110// by PEI Drivers this global variable required to be resident in R/W memory\r
111//\r
112BOOLEAN mI2CPrevReadTransfer = FALSE;\r
113\r
114/**\r
115 Writes single byte data to TPM specified by I2C register address.\r
116\r
117 @param[in] TpmAddress The register to write.\r
118 @param[in] Data The data to write to the register.\r
119\r
120**/\r
121VOID\r
122TpmWriteByte (\r
123 IN UINTN TpmAddress,\r
124 IN UINT8 Data\r
125 )\r
126{\r
127 EFI_STATUS Status;\r
128 UINTN WriteLength;\r
129 UINT8 WriteData[2];\r
130 EFI_I2C_DEVICE_ADDRESS I2CDeviceAddr;\r
131\r
132 //\r
133 // Setup I2C Slave device address and address mode (7-bit).\r
134 //\r
135 I2CDeviceAddr.I2CDeviceAddress = TPM_I2C_SLAVE_DEVICE_ADDRESS;\r
136\r
137 //\r
138 // As recommended by Infineon (SLB9645 I2C Communication protocol application\r
139 // note revision 1.0) wait 250 microseconds between a read and a write transfer.\r
140 //\r
141 if (mI2CPrevReadTransfer) {\r
142 MicroSecondDelay (GUARD_TIME);\r
143 }\r
144\r
145 //\r
146 // Write to TPM register.\r
147 //\r
148 WriteLength = 2;\r
149 WriteData[0] = (UINT8)TpmAddress;\r
150 WriteData[1] = Data;\r
151\r
152 Status = I2cWriteMultipleByte (\r
153 I2CDeviceAddr,\r
154 EfiI2CSevenBitAddrMode,\r
155 &WriteLength,\r
156 &WriteData\r
157 );\r
158 if (EFI_ERROR(Status)) {\r
159 DEBUG ((EFI_D_ERROR, "TpmWriteByte(): I2C Write to TPM address %0x failed (%r)\n", TpmAddress, Status));\r
160 ASSERT (FALSE); // Writes to TPM should always succeed.\r
161 }\r
162\r
163 mI2CPrevReadTransfer = FALSE;\r
164}\r
165\r
166/**\r
167 Reads single byte data from TPM specified by I2C register address.\r
168\r
169 Due to stability issues when using I2C combined write/read transfers (with\r
170 RESTART) to TPM (specifically read from status register), a single write is\r
171 performed followed by single read (with STOP condition in between).\r
172\r
173 @param[in] TpmAddress Address of register to read.\r
174\r
175 @return The value register read.\r
176\r
177**/\r
178UINT8\r
179TpmReadByte (\r
180 IN UINTN TpmAddress\r
181 )\r
182{\r
183 UINT8 Data[1];\r
184 UINT8 ReadData;\r
185 UINT8 ReadCount;\r
186\r
187 EFI_I2C_DEVICE_ADDRESS I2CDeviceAddr;\r
188 EFI_I2C_ADDR_MODE I2CAddrMode;\r
189\r
190 EFI_STATUS Status;\r
191\r
192 Status = EFI_SUCCESS;\r
193 ReadData = 0xFF;\r
194 ReadCount = 0;\r
195\r
196 //\r
197 // Locate I2C protocol for TPM I2C access.\r
198 //\r
199 I2CDeviceAddr.I2CDeviceAddress = TPM_I2C_SLAVE_DEVICE_ADDRESS;\r
200 I2CAddrMode = EfiI2CSevenBitAddrMode;\r
201\r
202 //\r
203 // As recommended by Infineon (SLB9645 I2C Communication protocol application\r
204 // note revision 1.0) retry up to 3 times if TPM status, access or burst count\r
205 // registers return 0xFF.\r
206 //\r
207 while ((ReadData == 0xFF) && (ReadCount < READ_RETRY)) {\r
208 //\r
209 // As recommended by Infineon (SLB9645 I2C Communication protocol application\r
210 // note revision 1.0) wait 250 microseconds between a read and a write transfer.\r
211 //\r
212 if (mI2CPrevReadTransfer) {\r
213 MicroSecondDelay (GUARD_TIME);\r
214 }\r
215\r
216 //\r
217 // Write address to TPM.\r
218 //\r
219 Data[0] = (UINT8)TpmAddress;\r
220 Status = I2cWriteByte (\r
221 I2CDeviceAddr,\r
222 I2CAddrMode,\r
223 &Data\r
224 );\r
225\r
226 if (EFI_ERROR(Status)) {\r
227 DEBUG ((EFI_D_INFO, "TpmReadByte(): write to TPM address %0x failed (%r)\n", TpmAddress, Status));\r
228 }\r
229\r
230 mI2CPrevReadTransfer = FALSE;\r
231\r
232 //\r
233 // Read data from TPM.\r
234 //\r
235 Data[0] = (UINT8)TpmAddress;\r
236 Status = I2cReadByte (\r
237 I2CDeviceAddr,\r
238 I2CAddrMode,\r
239 &Data\r
240 );\r
241\r
242 if (EFI_ERROR(Status)) {\r
243 DEBUG ((EFI_D_INFO, "TpmReadByte(): read from TPM address %0x failed (%r)\n", TpmAddress, Status));\r
244 ReadData = 0xFF;\r
245 } else {\r
246 ReadData = Data[0];\r
247 }\r
248\r
249 //\r
250 // Only need to retry 3 times for TPM status, access, and burst count registers.\r
251 // If read transfer is to TPM Data FIFO, do not retry, exit loop.\r
252 //\r
253 if (TpmAddress == INFINEON_TPM_DATA_FIFO_0_ADDRESS_DEFAULT) {\r
254 ReadCount = READ_RETRY;\r
255 } else {\r
256 ReadCount++;\r
257 }\r
258\r
259 mI2CPrevReadTransfer = TRUE;\r
260 }\r
261\r
262 if (EFI_ERROR(Status)) {\r
263 //\r
264 // Only reads to access register allowed to fail.\r
265 //\r
266 if (TpmAddress != INFINEON_TPM_ACCESS_0_ADDRESS_DEFAULT) {\r
267 DEBUG ((EFI_D_ERROR, "TpmReadByte(): read from TPM address %0x failed\n", TpmAddress));\r
268 ASSERT_EFI_ERROR (Status);\r
269 }\r
270 }\r
271 return ReadData;\r
272}\r
273\r
274/**\r
275 Check whether the value of a TPM chip register satisfies the input BIT setting.\r
276\r
277 @param[in] Register TPM register to be checked.\r
278 @param[in] BitSet Check these data bits are set.\r
279 @param[in] BitClear Check these data bits are clear.\r
280 @param[in] TimeOut The max wait time (unit MicroSecond) when checking register.\r
281\r
282 @retval EFI_SUCCESS The register satisfies the check bit.\r
283 @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
284**/\r
285EFI_STATUS\r
286TisPcWaitRegisterBits (\r
287 IN UINTN Register,\r
288 IN UINT8 BitSet,\r
289 IN UINT8 BitClear,\r
290 IN UINT32 TimeOut\r
291 )\r
292{\r
293 UINT8 RegRead;\r
294 UINT32 WaitTime;\r
295\r
296 for (WaitTime = 0; WaitTime < TimeOut; WaitTime += 30){\r
297 RegRead = TpmReadByte (Register);\r
298 if ((RegRead & BitSet) == BitSet && (RegRead & BitClear) == 0)\r
299 return EFI_SUCCESS;\r
300 MicroSecondDelay (30);\r
301 }\r
302 return EFI_TIMEOUT;\r
303}\r
304\r
305/**\r
306 Get BurstCount by reading the burstCount field of a TIS register\r
307 in the time of default TIS_TIMEOUT_D.\r
308\r
309 @param[out] BurstCount Pointer to a buffer to store the got BurstConut.\r
310\r
311 @retval EFI_SUCCESS Get BurstCount.\r
312 @retval EFI_INVALID_PARAMETER BurstCount is NULL.\r
313 @retval EFI_TIMEOUT BurstCount can't be got in time.\r
314**/\r
315EFI_STATUS\r
316TisPcReadBurstCount (\r
317 OUT UINT16 *BurstCount\r
318 )\r
319{\r
320 UINT32 WaitTime;\r
321 UINT8 DataByte0;\r
322 UINT8 DataByte1;\r
323\r
324 if (BurstCount == NULL) {\r
325 return EFI_INVALID_PARAMETER;\r
326 }\r
327\r
328 WaitTime = 0;\r
329 do {\r
330 //\r
331 // BurstCount is UINT16, but it is not 2bytes aligned,\r
332 // so it needs to use TpmReadByte to read two times\r
333 //\r
334 DataByte0 = TpmReadByte (INFINEON_TPM_BURST0_COUNT_0_DEFAULT);\r
335 DataByte1 = TpmReadByte (INFINEON_TPM_BURST1_COUNT_0_DEFAULT);\r
336 *BurstCount = (UINT16)((DataByte1 << 8) + DataByte0);\r
337 if (*BurstCount != 0) {\r
338 return EFI_SUCCESS;\r
339 }\r
340 MicroSecondDelay (30);\r
341 WaitTime += 30;\r
342 } while (WaitTime < TIS_TIMEOUT_D);\r
343\r
344 return EFI_TIMEOUT;\r
345}\r
346\r
347/**\r
348 Set TPM chip to ready state by sending ready command TIS_PC_STS_READY\r
349 to Status Register in time.\r
350\r
351 @retval EFI_SUCCESS TPM chip enters into ready state.\r
352 @retval EFI_TIMEOUT TPM chip can't be set to ready state in time.\r
353**/\r
354EFI_STATUS\r
355TisPcPrepareCommand (\r
356 VOID\r
357 )\r
358{\r
359 EFI_STATUS Status;\r
360\r
361 TpmWriteByte (INFINEON_TPM_STS_0_ADDRESS_DEFAULT, TIS_PC_STS_READY);\r
362 Status = TisPcWaitRegisterBits (\r
363 INFINEON_TPM_STS_0_ADDRESS_DEFAULT,\r
364 TIS_PC_STS_READY,\r
365 0,\r
366 TIS_TIMEOUT_B\r
367 );\r
368 return Status;\r
369}\r
370\r
371/**\r
372 This service requests use TPM12.\r
373\r
374 @retval EFI_SUCCESS Get the control of TPM12 chip.\r
375 @retval EFI_NOT_FOUND TPM12 not found.\r
376 @retval EFI_DEVICE_ERROR Unexpected device behavior.\r
377**/\r
378EFI_STATUS\r
379EFIAPI\r
380Tpm12RequestUseTpm (\r
381 VOID\r
382 )\r
383{\r
384 EFI_STATUS Status;\r
385\r
386 //\r
387 // Check to see if TPM exists\r
388 //\r
389 if (TpmReadByte (INFINEON_TPM_ACCESS_0_ADDRESS_DEFAULT) == 0xFF) {\r
390 return EFI_NOT_FOUND;\r
391 }\r
392\r
393 TpmWriteByte (INFINEON_TPM_ACCESS_0_ADDRESS_DEFAULT, TIS_PC_ACC_RQUUSE);\r
394\r
395 //\r
396 // No locality set before, ACCESS_X.activeLocality MUST be valid within TIMEOUT_A\r
397 //\r
398 Status = TisPcWaitRegisterBits (\r
399 INFINEON_TPM_ACCESS_0_ADDRESS_DEFAULT,\r
400 (UINT8)(TIS_PC_ACC_ACTIVE |TIS_PC_VALID),\r
401 0,\r
402 TIS_TIMEOUT_A\r
403 );\r
404 return Status;\r
405}\r
406\r
407/**\r
408 Send command to TPM for execution.\r
409\r
410 @param[in] TpmBuffer Buffer for TPM command data.\r
411 @param[in] DataLength TPM command data length.\r
412\r
413 @retval EFI_SUCCESS Operation completed successfully.\r
414 @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
415\r
416**/\r
417EFI_STATUS\r
418TisPcSend (\r
419 IN UINT8 *TpmBuffer,\r
420 IN UINT32 DataLength\r
421 )\r
422{\r
423 UINT16 BurstCount;\r
424 UINT32 Index;\r
425 EFI_STATUS Status;\r
426\r
427 Status = TisPcPrepareCommand ();\r
428 if (EFI_ERROR (Status)){\r
429 DEBUG ((DEBUG_ERROR, "The TPM is not ready!\n"));\r
430 goto Done;\r
431 }\r
432 Index = 0;\r
433 while (Index < DataLength) {\r
434 Status = TisPcReadBurstCount (&BurstCount);\r
435 if (EFI_ERROR (Status)) {\r
436 Status = EFI_TIMEOUT;\r
437 goto Done;\r
438 }\r
439 for (; BurstCount > 0 && Index < DataLength; BurstCount--) {\r
440 TpmWriteByte (INFINEON_TPM_DATA_FIFO_0_ADDRESS_DEFAULT, *(TpmBuffer + Index));\r
441 Index++;\r
442 }\r
443 }\r
444 //\r
445 // Ensure the TPM status STS_EXPECT change from 1 to 0\r
446 //\r
447 Status = TisPcWaitRegisterBits (\r
448 INFINEON_TPM_STS_0_ADDRESS_DEFAULT,\r
449 (UINT8) TIS_PC_VALID,\r
450 TIS_PC_STS_EXPECT,\r
451 TIS_TIMEOUT_C\r
452 );\r
453 if (EFI_ERROR (Status)) {\r
454 goto Done;\r
455 }\r
456\r
457 //\r
458 // Start the command\r
459 //\r
460 TpmWriteByte (INFINEON_TPM_STS_0_ADDRESS_DEFAULT, TIS_PC_STS_GO);\r
461\r
462Done:\r
463 if (EFI_ERROR (Status)) {\r
464 //\r
465 // Ensure the TPM state change from "Reception" to "Idle/Ready"\r
466 //\r
467 TpmWriteByte (INFINEON_TPM_STS_0_ADDRESS_DEFAULT, TIS_PC_STS_READY);\r
468 }\r
469\r
470 return Status;\r
471}\r
472\r
473/**\r
474 Receive response data of last command from TPM.\r
475\r
476 @param[out] TpmBuffer Buffer for response data.\r
477 @param[out] RespSize Response data length.\r
478\r
479 @retval EFI_SUCCESS Operation completed successfully.\r
480 @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
481 @retval EFI_DEVICE_ERROR Unexpected device status.\r
482 @retval EFI_BUFFER_TOO_SMALL Response data is too long.\r
483\r
484**/\r
485EFI_STATUS\r
486TisPcReceive (\r
487 OUT UINT8 *TpmBuffer,\r
488 OUT UINT32 *RespSize\r
489 )\r
490{\r
491 EFI_STATUS Status;\r
492 UINT16 BurstCount;\r
493 UINT32 Index;\r
494 UINT32 ResponseSize;\r
495 TPM_RSP_COMMAND_HDR *ResponseHeader;\r
496\r
497 //\r
498 // Wait for the command completion\r
499 //\r
500 Status = TisPcWaitRegisterBits (\r
501 INFINEON_TPM_STS_0_ADDRESS_DEFAULT,\r
502 (UINT8) (TIS_PC_VALID | TIS_PC_STS_DATA),\r
503 0,\r
504 TIS_TIMEOUT_B\r
505 );\r
506 if (EFI_ERROR (Status)) {\r
507 Status = EFI_TIMEOUT;\r
508 goto Done;\r
509 }\r
510 //\r
511 // Read the response data header and check it\r
512 //\r
513 Index = 0;\r
514 BurstCount = 0;\r
515 while (Index < sizeof (TPM_RSP_COMMAND_HDR)) {\r
516 Status = TisPcReadBurstCount (&BurstCount);\r
517 if (EFI_ERROR (Status)) {\r
518 Status = EFI_TIMEOUT;\r
519 goto Done;\r
520 }\r
521 for (; BurstCount > 0 ; BurstCount--) {\r
522 *(TpmBuffer + Index) = TpmReadByte (INFINEON_TPM_DATA_FIFO_0_ADDRESS_DEFAULT);\r
523 Index++;\r
524 if (Index == sizeof (TPM_RSP_COMMAND_HDR))\r
525 break;\r
526 }\r
527 }\r
528\r
529 //\r
530 // Check the response data header (tag, parasize and returncode)\r
531 //\r
532 ResponseHeader = (TPM_RSP_COMMAND_HDR *)TpmBuffer;\r
533 if (SwapBytes16 (ReadUnaligned16 (&ResponseHeader->tag)) != TPM_TAG_RSP_COMMAND) {\r
534 Status = EFI_DEVICE_ERROR;\r
535 goto Done;\r
536 }\r
537\r
538 ResponseSize = SwapBytes32 (ReadUnaligned32 (&ResponseHeader->paramSize));\r
539 if (ResponseSize == sizeof (TPM_RSP_COMMAND_HDR)) {\r
540 Status = EFI_SUCCESS;\r
541 goto Done;\r
542 }\r
543 if (ResponseSize < sizeof (TPM_RSP_COMMAND_HDR)) {\r
544 Status = EFI_DEVICE_ERROR;\r
545 goto Done;\r
546 }\r
547 if (*RespSize < ResponseSize) {\r
548 Status = EFI_BUFFER_TOO_SMALL;\r
549 goto Done;\r
550 }\r
551 *RespSize = ResponseSize;\r
552\r
553 //\r
554 // Continue reading the remaining data\r
555 //\r
556 while (Index < ResponseSize) {\r
557 for (; BurstCount > 0 ; BurstCount--) {\r
558 *(TpmBuffer + Index) = TpmReadByte (INFINEON_TPM_DATA_FIFO_0_ADDRESS_DEFAULT);\r
559 Index++;\r
560 if (Index == ResponseSize) {\r
561 Status = EFI_SUCCESS;\r
562 goto Done;\r
563 }\r
564 }\r
565 Status = TisPcReadBurstCount (&BurstCount);\r
566 if (EFI_ERROR (Status) && (Index < ResponseSize)) {\r
567 Status = EFI_DEVICE_ERROR;\r
568 goto Done;\r
569 }\r
570 }\r
571\r
572Done:\r
573 //\r
574 // Ensure the TPM state change from "Execution" or "Completion" to "Idle/Ready"\r
575 //\r
576 TpmWriteByte (INFINEON_TPM_STS_0_ADDRESS_DEFAULT, TIS_PC_STS_READY);\r
577\r
578 return Status;\r
579}\r
580\r
581/**\r
582 This service enables the sending of commands to the TPM12.\r
583\r
584 @param[in] InputParameterBlockSize Size of the TPM12 input parameter block.\r
585 @param[in] InputParameterBlock Pointer to the TPM12 input parameter block.\r
586 @param[in,out] OutputParameterBlockSize Size of the TPM12 output parameter block.\r
587 @param[in] OutputParameterBlock Pointer to the TPM12 output parameter block.\r
588\r
589 @retval EFI_SUCCESS The command byte stream was successfully sent to\r
590 the device and a response was successfully received.\r
591 @retval EFI_DEVICE_ERROR The command was not successfully sent to the\r
592 device or a response was not successfully received\r
593 from the device.\r
594 @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small.\r
595**/\r
596EFI_STATUS\r
597EFIAPI\r
598Tpm12SubmitCommand (\r
599 IN UINT32 InputParameterBlockSize,\r
600 IN UINT8 *InputParameterBlock,\r
601 IN OUT UINT32 *OutputParameterBlockSize,\r
602 IN UINT8 *OutputParameterBlock\r
603 )\r
604{\r
605 EFI_STATUS Status;\r
606\r
607 Status = TisPcSend (InputParameterBlock, InputParameterBlockSize);\r
608 if (!EFI_ERROR (Status)) {\r
609 Status = TisPcReceive (OutputParameterBlock, OutputParameterBlockSize);\r
610 }\r
611 return Status;\r
612}\r