]> git.proxmox.com Git - mirror_edk2.git/blame - SecurityPkg/Library/Tpm12DeviceLibDTpm/Tpm12Tis.c
SecurityPkg: Add TPM PTP detection in TPM12 device lib.
[mirror_edk2.git] / SecurityPkg / Library / Tpm12DeviceLibDTpm / Tpm12Tis.c
CommitLineData
c1d93242
JY
1/** @file\r
2 TIS (TPM Interface Specification) functions used by TPM1.2.\r
3 \r
8e997ab8 4Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>\r
6aaac383 5(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
c1d93242
JY
6This program and the accompanying materials \r
7are licensed and made available under the terms and conditions of the BSD License \r
8which accompanies this distribution. The full text of the license may be found at \r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include <Uefi.h>\r
17#include <IndustryStandard/Tpm12.h>\r
18#include <Library/BaseLib.h>\r
19#include <Library/BaseMemoryLib.h>\r
20#include <Library/IoLib.h>\r
21#include <Library/TimerLib.h>\r
22#include <Library/DebugLib.h>\r
23#include <Library/Tpm12CommandLib.h>\r
24#include <Library/PcdLib.h>\r
25\r
8e997ab8
JY
26#include <IndustryStandard/TpmPtp.h>\r
27#include <IndustryStandard/TpmTis.h>\r
c1d93242 28\r
8e997ab8
JY
29typedef enum {\r
30 PtpInterfaceTis,\r
31 PtpInterfaceFifo,\r
32 PtpInterfaceCrb,\r
33 PtpInterfaceMax,\r
34} PTP_INTERFACE_TYPE;\r
c1d93242
JY
35\r
36//\r
37// Max TPM command/reponse length\r
38//\r
39#define TPMCMDBUFLENGTH 1024\r
40\r
41/**\r
42 Check whether TPM chip exist.\r
43\r
44 @param[in] TisReg Pointer to TIS register.\r
45\r
46 @retval TRUE TPM chip exists.\r
47 @retval FALSE TPM chip is not found.\r
48**/\r
49BOOLEAN\r
50Tpm12TisPcPresenceCheck (\r
51 IN TIS_PC_REGISTERS_PTR TisReg\r
52 )\r
53{\r
54 UINT8 RegRead;\r
55 \r
56 RegRead = MmioRead8 ((UINTN)&TisReg->Access);\r
57 return (BOOLEAN)(RegRead != (UINT8)-1);\r
58}\r
59\r
60/**\r
61 Check whether the value of a TPM chip register satisfies the input BIT setting.\r
62\r
63 @param[in] Register Address port of register to be checked.\r
64 @param[in] BitSet Check these data bits are set.\r
65 @param[in] BitClear Check these data bits are clear.\r
66 @param[in] TimeOut The max wait time (unit MicroSecond) when checking register.\r
67\r
68 @retval EFI_SUCCESS The register satisfies the check bit.\r
69 @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
70**/\r
71EFI_STATUS\r
c1d93242
JY
72Tpm12TisPcWaitRegisterBits (\r
73 IN UINT8 *Register,\r
74 IN UINT8 BitSet,\r
75 IN UINT8 BitClear,\r
76 IN UINT32 TimeOut\r
77 )\r
78{\r
79 UINT8 RegRead;\r
80 UINT32 WaitTime;\r
81\r
82 for (WaitTime = 0; WaitTime < TimeOut; WaitTime += 30){\r
83 RegRead = MmioRead8 ((UINTN)Register);\r
84 if ((RegRead & BitSet) == BitSet && (RegRead & BitClear) == 0)\r
85 return EFI_SUCCESS;\r
86 MicroSecondDelay (30);\r
87 }\r
88 return EFI_TIMEOUT;\r
89}\r
90\r
91/**\r
92 Get BurstCount by reading the burstCount field of a TIS regiger \r
93 in the time of default TIS_TIMEOUT_D.\r
94\r
95 @param[in] TisReg Pointer to TIS register.\r
96 @param[out] BurstCount Pointer to a buffer to store the got BurstConut.\r
97\r
98 @retval EFI_SUCCESS Get BurstCount.\r
99 @retval EFI_INVALID_PARAMETER TisReg is NULL or BurstCount is NULL.\r
100 @retval EFI_TIMEOUT BurstCount can't be got in time.\r
101**/\r
102EFI_STATUS\r
c1d93242
JY
103Tpm12TisPcReadBurstCount (\r
104 IN TIS_PC_REGISTERS_PTR TisReg,\r
105 OUT UINT16 *BurstCount\r
106 )\r
107{\r
108 UINT32 WaitTime;\r
109 UINT8 DataByte0;\r
110 UINT8 DataByte1;\r
111\r
112 if (BurstCount == NULL || TisReg == NULL) {\r
113 return EFI_INVALID_PARAMETER;\r
114 }\r
115\r
116 WaitTime = 0;\r
117 do {\r
118 //\r
119 // TIS_PC_REGISTERS_PTR->burstCount is UINT16, but it is not 2bytes aligned,\r
120 // so it needs to use MmioRead8 to read two times\r
121 //\r
122 DataByte0 = MmioRead8 ((UINTN)&TisReg->BurstCount);\r
123 DataByte1 = MmioRead8 ((UINTN)&TisReg->BurstCount + 1);\r
124 *BurstCount = (UINT16)((DataByte1 << 8) + DataByte0);\r
125 if (*BurstCount != 0) {\r
126 return EFI_SUCCESS;\r
127 }\r
128 MicroSecondDelay (30);\r
129 WaitTime += 30;\r
130 } while (WaitTime < TIS_TIMEOUT_D);\r
131\r
132 return EFI_TIMEOUT;\r
133}\r
134\r
135/**\r
136 Set TPM chip to ready state by sending ready command TIS_PC_STS_READY \r
137 to Status Register in time.\r
138\r
139 @param[in] TisReg Pointer to TIS register.\r
140\r
141 @retval EFI_SUCCESS TPM chip enters into ready state.\r
142 @retval EFI_INVALID_PARAMETER TisReg is NULL.\r
143 @retval EFI_TIMEOUT TPM chip can't be set to ready state in time.\r
144**/\r
145EFI_STATUS\r
c1d93242
JY
146Tpm12TisPcPrepareCommand (\r
147 IN TIS_PC_REGISTERS_PTR TisReg\r
148 )\r
149{\r
150 EFI_STATUS Status;\r
151\r
152 if (TisReg == NULL) {\r
153 return EFI_INVALID_PARAMETER;\r
154 }\r
155\r
156 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_READY);\r
157 Status = Tpm12TisPcWaitRegisterBits (\r
158 &TisReg->Status,\r
159 TIS_PC_STS_READY,\r
160 0,\r
161 TIS_TIMEOUT_B\r
162 );\r
163 return Status;\r
164}\r
165\r
166/**\r
167 Get the control of TPM chip by sending requestUse command TIS_PC_ACC_RQUUSE \r
168 to ACCESS Register in the time of default TIS_TIMEOUT_A.\r
169\r
170 @param[in] TisReg Pointer to TIS register.\r
171\r
172 @retval EFI_SUCCESS Get the control of TPM chip.\r
173 @retval EFI_INVALID_PARAMETER TisReg is NULL.\r
174 @retval EFI_NOT_FOUND TPM chip doesn't exit.\r
175 @retval EFI_TIMEOUT Can't get the TPM control in time.\r
176**/\r
177EFI_STATUS\r
c1d93242
JY
178Tpm12TisPcRequestUseTpm (\r
179 IN TIS_PC_REGISTERS_PTR TisReg\r
180 )\r
181{\r
182 EFI_STATUS Status;\r
183 \r
184 if (TisReg == NULL) {\r
185 return EFI_INVALID_PARAMETER;\r
186 }\r
187 \r
188 if (!Tpm12TisPcPresenceCheck (TisReg)) {\r
189 return EFI_NOT_FOUND;\r
190 }\r
191\r
192 MmioWrite8((UINTN)&TisReg->Access, TIS_PC_ACC_RQUUSE);\r
193 Status = Tpm12TisPcWaitRegisterBits (\r
194 &TisReg->Access,\r
195 (UINT8)(TIS_PC_ACC_ACTIVE |TIS_PC_VALID),\r
196 0,\r
197 TIS_TIMEOUT_A\r
198 );\r
199 return Status;\r
200}\r
201\r
202/**\r
203 Send a command to TPM for execution and return response data.\r
204\r
205 @param[in] TisReg TPM register space base address. \r
206 @param[in] BufferIn Buffer for command data. \r
207 @param[in] SizeIn Size of command data. \r
208 @param[in, out] BufferOut Buffer for response data. \r
209 @param[in, out] SizeOut Size of response data. \r
210 \r
211 @retval EFI_SUCCESS Operation completed successfully.\r
c1d93242
JY
212 @retval EFI_BUFFER_TOO_SMALL Response data buffer is too small.\r
213 @retval EFI_DEVICE_ERROR Unexpected device behavior.\r
214 @retval EFI_UNSUPPORTED Unsupported TPM version\r
215\r
216**/\r
217EFI_STATUS\r
218Tpm12TisTpmCommand (\r
219 IN TIS_PC_REGISTERS_PTR TisReg,\r
220 IN UINT8 *BufferIn,\r
221 IN UINT32 SizeIn,\r
222 IN OUT UINT8 *BufferOut,\r
223 IN OUT UINT32 *SizeOut\r
224 )\r
225{\r
226 EFI_STATUS Status;\r
227 UINT16 BurstCount;\r
228 UINT32 Index;\r
229 UINT32 TpmOutSize;\r
230 UINT16 Data16;\r
231 UINT32 Data32;\r
232\r
233 DEBUG_CODE (\r
234 UINTN DebugSize;\r
235\r
6aaac383 236 DEBUG ((EFI_D_VERBOSE, "Tpm12TisTpmCommand Send - "));\r
c1d93242
JY
237 if (SizeIn > 0x100) {\r
238 DebugSize = 0x40;\r
239 } else {\r
240 DebugSize = SizeIn;\r
241 }\r
242 for (Index = 0; Index < DebugSize; Index++) {\r
6aaac383 243 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferIn[Index]));\r
c1d93242
JY
244 }\r
245 if (DebugSize != SizeIn) {\r
6aaac383 246 DEBUG ((EFI_D_VERBOSE, "...... "));\r
c1d93242 247 for (Index = SizeIn - 0x20; Index < SizeIn; Index++) {\r
6aaac383 248 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferIn[Index]));\r
c1d93242
JY
249 }\r
250 }\r
6aaac383 251 DEBUG ((EFI_D_VERBOSE, "\n"));\r
c1d93242
JY
252 );\r
253 TpmOutSize = 0;\r
254\r
255 Status = Tpm12TisPcPrepareCommand (TisReg);\r
256 if (EFI_ERROR (Status)){\r
257 DEBUG ((DEBUG_ERROR, "Tpm12 is not ready for command!\n"));\r
6f785cfc 258 return EFI_DEVICE_ERROR;\r
c1d93242
JY
259 }\r
260 //\r
261 // Send the command data to Tpm\r
262 //\r
263 Index = 0;\r
264 while (Index < SizeIn) {\r
265 Status = Tpm12TisPcReadBurstCount (TisReg, &BurstCount);\r
266 if (EFI_ERROR (Status)) {\r
6f785cfc 267 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
268 goto Exit;\r
269 }\r
270 for (; BurstCount > 0 && Index < SizeIn; BurstCount--) {\r
271 MmioWrite8((UINTN)&TisReg->DataFifo, *(BufferIn + Index));\r
272 Index++;\r
273 }\r
274 }\r
275 //\r
276 // Check the Tpm status STS_EXPECT change from 1 to 0\r
277 //\r
278 Status = Tpm12TisPcWaitRegisterBits (\r
279 &TisReg->Status,\r
280 (UINT8) TIS_PC_VALID,\r
281 TIS_PC_STS_EXPECT,\r
282 TIS_TIMEOUT_C\r
283 );\r
284 if (EFI_ERROR (Status)) {\r
285 DEBUG ((DEBUG_ERROR, "Tpm12 The send buffer too small!\n"));\r
286 Status = EFI_BUFFER_TOO_SMALL;\r
287 goto Exit;\r
288 }\r
289 //\r
290 // Executed the TPM command and waiting for the response data ready\r
291 //\r
292 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_GO);\r
293 Status = Tpm12TisPcWaitRegisterBits (\r
294 &TisReg->Status,\r
295 (UINT8) (TIS_PC_VALID | TIS_PC_STS_DATA),\r
296 0,\r
297 TIS_TIMEOUT_B\r
298 );\r
299 if (EFI_ERROR (Status)) {\r
300 DEBUG ((DEBUG_ERROR, "Wait for Tpm12 response data time out!!\n"));\r
6f785cfc 301 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
302 goto Exit;\r
303 }\r
304 //\r
305 // Get response data header\r
306 //\r
307 Index = 0;\r
308 BurstCount = 0;\r
309 while (Index < sizeof (TPM_RSP_COMMAND_HDR)) {\r
310 Status = Tpm12TisPcReadBurstCount (TisReg, &BurstCount);\r
311 if (EFI_ERROR (Status)) {\r
6f785cfc 312 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
313 goto Exit;\r
314 }\r
315 for (; BurstCount > 0; BurstCount--) {\r
316 *(BufferOut + Index) = MmioRead8 ((UINTN)&TisReg->DataFifo);\r
317 Index++;\r
318 if (Index == sizeof (TPM_RSP_COMMAND_HDR)) break;\r
319 }\r
320 }\r
321 DEBUG_CODE (\r
6aaac383 322 DEBUG ((EFI_D_VERBOSE, "Tpm12TisTpmCommand ReceiveHeader - "));\r
c1d93242 323 for (Index = 0; Index < sizeof (TPM_RSP_COMMAND_HDR); Index++) {\r
6aaac383 324 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferOut[Index]));\r
c1d93242 325 }\r
6aaac383 326 DEBUG ((EFI_D_VERBOSE, "\n"));\r
c1d93242
JY
327 );\r
328 //\r
329 // Check the reponse data header (tag,parasize and returncode )\r
330 //\r
331 CopyMem (&Data16, BufferOut, sizeof (UINT16));\r
332 if (SwapBytes16 (Data16) != TPM_TAG_RSP_COMMAND) {\r
333 DEBUG ((EFI_D_ERROR, "TPM12: TPM_ST_RSP error - %x\n", TPM_TAG_RSP_COMMAND));\r
334 Status = EFI_UNSUPPORTED;\r
335 goto Exit;\r
336 }\r
337\r
338 CopyMem (&Data32, (BufferOut + 2), sizeof (UINT32));\r
339 TpmOutSize = SwapBytes32 (Data32);\r
340 if (*SizeOut < TpmOutSize) {\r
341 Status = EFI_BUFFER_TOO_SMALL;\r
342 goto Exit;\r
343 }\r
344 *SizeOut = TpmOutSize;\r
345 //\r
346 // Continue reading the remaining data\r
347 //\r
348 while ( Index < TpmOutSize ) {\r
349 for (; BurstCount > 0; BurstCount--) {\r
350 *(BufferOut + Index) = MmioRead8 ((UINTN)&TisReg->DataFifo);\r
351 Index++;\r
352 if (Index == TpmOutSize) {\r
353 Status = EFI_SUCCESS;\r
354 goto Exit;\r
355 }\r
356 }\r
357 Status = Tpm12TisPcReadBurstCount (TisReg, &BurstCount);\r
358 if (EFI_ERROR (Status)) {\r
6f785cfc 359 Status = EFI_DEVICE_ERROR;\r
c1d93242
JY
360 goto Exit;\r
361 }\r
362 }\r
363Exit:\r
364 DEBUG_CODE (\r
6aaac383 365 DEBUG ((EFI_D_VERBOSE, "Tpm12TisTpmCommand Receive - "));\r
c1d93242 366 for (Index = 0; Index < TpmOutSize; Index++) {\r
6aaac383 367 DEBUG ((EFI_D_VERBOSE, "%02x ", BufferOut[Index]));\r
c1d93242 368 }\r
6aaac383 369 DEBUG ((EFI_D_VERBOSE, "\n"));\r
c1d93242
JY
370 );\r
371 MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_READY);\r
372 return Status;\r
373}\r
374\r
375/**\r
376 This service enables the sending of commands to the TPM12.\r
377\r
378 @param[in] InputParameterBlockSize Size of the TPM12 input parameter block.\r
379 @param[in] InputParameterBlock Pointer to the TPM12 input parameter block.\r
380 @param[in,out] OutputParameterBlockSize Size of the TPM12 output parameter block.\r
381 @param[in] OutputParameterBlock Pointer to the TPM12 output parameter block.\r
382\r
383 @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received.\r
384 @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device.\r
385 @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. \r
386**/\r
387EFI_STATUS\r
388EFIAPI\r
389Tpm12SubmitCommand (\r
390 IN UINT32 InputParameterBlockSize,\r
391 IN UINT8 *InputParameterBlock,\r
392 IN OUT UINT32 *OutputParameterBlockSize,\r
393 IN UINT8 *OutputParameterBlock\r
394 )\r
395{\r
396 return Tpm12TisTpmCommand (\r
397 (TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress),\r
398 InputParameterBlock,\r
399 InputParameterBlockSize,\r
400 OutputParameterBlock,\r
401 OutputParameterBlockSize\r
402 );\r
403}\r
404\r
8e997ab8
JY
405/**\r
406 Return PTP interface type.\r
407\r
408 @param[in] Register Pointer to PTP register.\r
409\r
410 @return PTP interface type.\r
411**/\r
412PTP_INTERFACE_TYPE\r
413Tpm12GetPtpInterface (\r
414 IN VOID *Register\r
415 )\r
416{\r
417 PTP_CRB_INTERFACE_IDENTIFIER InterfaceId;\r
418 PTP_FIFO_INTERFACE_CAPABILITY InterfaceCapability;\r
419\r
420 if (!Tpm12TisPcPresenceCheck (Register)) {\r
421 return PtpInterfaceMax;\r
422 }\r
423 //\r
424 // Check interface id\r
425 //\r
426 InterfaceId.Uint32 = MmioRead32 ((UINTN)&((PTP_CRB_REGISTERS *)Register)->InterfaceId);\r
427 InterfaceCapability.Uint32 = MmioRead32 ((UINTN)&((PTP_FIFO_REGISTERS *)Register)->InterfaceCapability);\r
428\r
429 if ((InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_CRB) &&\r
430 (InterfaceId.Bits.InterfaceVersion == PTP_INTERFACE_IDENTIFIER_INTERFACE_VERSION_CRB) &&\r
431 (InterfaceId.Bits.CapCRB != 0)) {\r
432 return PtpInterfaceCrb;\r
433 }\r
434 if ((InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_FIFO) &&\r
435 (InterfaceId.Bits.InterfaceVersion == PTP_INTERFACE_IDENTIFIER_INTERFACE_VERSION_FIFO) &&\r
436 (InterfaceId.Bits.CapFIFO != 0) &&\r
437 (InterfaceCapability.Bits.InterfaceVersion == INTERFACE_CAPABILITY_INTERFACE_VERSION_PTP)) {\r
438 return PtpInterfaceFifo;\r
439 }\r
440 return PtpInterfaceTis;\r
441}\r
442\r
443/**\r
444 Check whether the value of a TPM chip register satisfies the input BIT setting.\r
445\r
446 @param[in] Register Address port of register to be checked.\r
447 @param[in] BitSet Check these data bits are set.\r
448 @param[in] BitClear Check these data bits are clear.\r
449 @param[in] TimeOut The max wait time (unit MicroSecond) when checking register.\r
450\r
451 @retval EFI_SUCCESS The register satisfies the check bit.\r
452 @retval EFI_TIMEOUT The register can't run into the expected status in time.\r
453**/\r
454EFI_STATUS\r
455Tpm12PtpCrbWaitRegisterBits (\r
456 IN UINT32 *Register,\r
457 IN UINT32 BitSet,\r
458 IN UINT32 BitClear,\r
459 IN UINT32 TimeOut\r
460 )\r
461{\r
462 UINT32 RegRead;\r
463 UINT32 WaitTime;\r
464\r
465 for (WaitTime = 0; WaitTime < TimeOut; WaitTime += 30){\r
466 RegRead = MmioRead32 ((UINTN)Register);\r
467 if ((RegRead & BitSet) == BitSet && (RegRead & BitClear) == 0) {\r
468 return EFI_SUCCESS;\r
469 }\r
470 MicroSecondDelay (30);\r
471 }\r
472 return EFI_TIMEOUT;\r
473}\r
474\r
475/**\r
476 Get the control of TPM chip.\r
477\r
478 @param[in] CrbReg Pointer to CRB register.\r
479\r
480 @retval EFI_SUCCESS Get the control of TPM chip.\r
481 @retval EFI_INVALID_PARAMETER CrbReg is NULL.\r
482 @retval EFI_NOT_FOUND TPM chip doesn't exit.\r
483 @retval EFI_TIMEOUT Can't get the TPM control in time.\r
484**/\r
485EFI_STATUS\r
486Tpm12PtpCrbRequestUseTpm (\r
487 IN PTP_CRB_REGISTERS_PTR CrbReg\r
488 )\r
489{\r
490 EFI_STATUS Status;\r
491\r
492 MmioWrite32((UINTN)&CrbReg->LocalityControl, PTP_CRB_LOCALITY_CONTROL_REQUEST_ACCESS);\r
493 Status = Tpm12PtpCrbWaitRegisterBits (\r
494 &CrbReg->LocalityStatus,\r
495 PTP_CRB_LOCALITY_STATUS_GRANTED,\r
496 0,\r
497 PTP_TIMEOUT_A\r
498 );\r
499 return Status;\r
500}\r
501\r
c1d93242
JY
502/**\r
503 This service requests use TPM12.\r
504\r
505 @retval EFI_SUCCESS Get the control of TPM12 chip.\r
506 @retval EFI_NOT_FOUND TPM12 not found.\r
507 @retval EFI_DEVICE_ERROR Unexpected device behavior.\r
508**/\r
509EFI_STATUS\r
510EFIAPI\r
511Tpm12RequestUseTpm (\r
512 VOID\r
513 )\r
514{\r
8e997ab8
JY
515 PTP_INTERFACE_TYPE PtpInterface;\r
516\r
517 //\r
518 // Special handle for TPM1.2 to check PTP too, because PTP/TIS share same register address.\r
519 // Some other program might leverage this function to check the existence of TPM chip.\r
520 //\r
521 PtpInterface = Tpm12GetPtpInterface ((VOID *) (UINTN) PcdGet64 (PcdTpmBaseAddress));\r
522 switch (PtpInterface) {\r
523 case PtpInterfaceCrb:\r
524 return Tpm12PtpCrbRequestUseTpm ((PTP_CRB_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress));\r
525 case PtpInterfaceFifo:\r
526 case PtpInterfaceTis:\r
527 return Tpm12TisPcRequestUseTpm ((TIS_PC_REGISTERS_PTR) (UINTN) PcdGet64 (PcdTpmBaseAddress));\r
528 default:\r
529 return EFI_NOT_FOUND;\r
530 }\r
c1d93242 531}\r