]> git.proxmox.com Git - mirror_edk2.git/blame - SecurityPkg/Tcg/Tcg2Acpi/Tcg2Acpi.c
SecurityPkg: Tcg2Acpi: Added unblock memory interface for NVS region
[mirror_edk2.git] / SecurityPkg / Tcg / Tcg2Acpi / Tcg2Acpi.c
CommitLineData
3c2dc30d
KQ
1/** @file\r
2 This driver implements TPM 2.0 definition block in ACPI table and\r
3 populates registered SMI callback functions for Tcg2 physical presence\r
4 and MemoryClear to handle the requests for ACPI method. It needs to be\r
5 used together with Tcg2 MM drivers to exchange information on registered\r
6 SwSmiValue and allocated NVS region address.\r
7\r
8 Caution: This module requires additional review when modified.\r
9 This driver will have external input - variable and ACPINvs data in SMM mode.\r
10 This external input must be validated carefully to avoid security issue.\r
11\r
12Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
13Copyright (c) Microsoft Corporation.\r
14SPDX-License-Identifier: BSD-2-Clause-Patent\r
15\r
16**/\r
17\r
18#include <PiDxe.h>\r
19\r
20#include <IndustryStandard/Tpm2Acpi.h>\r
21\r
22#include <Guid/TpmInstance.h>\r
23#include <Guid/TpmNvsMm.h>\r
24#include <Guid/PiSmmCommunicationRegionTable.h>\r
25\r
26#include <Protocol/AcpiTable.h>\r
27#include <Protocol/Tcg2Protocol.h>\r
28#include <Protocol/MmCommunication.h>\r
29\r
30#include <Library/BaseLib.h>\r
31#include <Library/BaseMemoryLib.h>\r
32#include <Library/DxeServicesLib.h>\r
33#include <Library/UefiBootServicesTableLib.h>\r
34#include <Library/DebugLib.h>\r
35#include <Library/PcdLib.h>\r
36#include <Library/PrintLib.h>\r
37#include <Library/TpmMeasurementLib.h>\r
38#include <Library/Tpm2DeviceLib.h>\r
39#include <Library/Tpm2CommandLib.h>\r
40#include <Library/UefiLib.h>\r
59a3ccb0 41#include <Library/MmUnblockMemoryLib.h>\r
3c2dc30d
KQ
42\r
43//\r
44// Physical Presence Interface Version supported by Platform\r
45//\r
46#define PHYSICAL_PRESENCE_VERSION_TAG "$PV"\r
47#define PHYSICAL_PRESENCE_VERSION_SIZE 4\r
48\r
49//\r
50// PNP _HID for TPM2 device\r
51//\r
52#define TPM_HID_TAG "NNNN0000"\r
53#define TPM_HID_PNP_SIZE 8\r
54#define TPM_HID_ACPI_SIZE 9\r
55\r
56#define TPM_PRS_RESL "RESL"\r
57#define TPM_PRS_RESS "RESS"\r
58#define TPM_PRS_RES_NAME_SIZE 4\r
59//\r
60// Minimum PRS resource template size\r
61// 1 byte for BufferOp\r
62// 1 byte for PkgLength\r
63// 2 bytes for BufferSize\r
64// 12 bytes for Memory32Fixed descriptor\r
65// 5 bytes for Interrupt descriptor\r
66// 2 bytes for END Tag\r
67//\r
68#define TPM_POS_RES_TEMPLATE_MIN_SIZE (1 + 1 + 2 + 12 + 5 + 2)\r
69\r
70//\r
71// Max Interrupt buffer size for PRS interrupt resource\r
72// Now support 15 interrupts in maxmum\r
73//\r
74#define MAX_PRS_INT_BUF_SIZE (15*4)\r
75\r
76#pragma pack(1)\r
77\r
78typedef struct {\r
79 EFI_ACPI_DESCRIPTION_HEADER Header;\r
80 // Flags field is replaced in version 4 and above\r
81 // BIT0~15: PlatformClass This field is only valid for version 4 and above\r
82 // BIT16~31: Reserved\r
83 UINT32 Flags;\r
84 UINT64 AddressOfControlArea;\r
85 UINT32 StartMethod;\r
86 UINT8 PlatformSpecificParameters[12]; // size up to 12\r
87 UINT32 Laml; // Optional\r
88 UINT64 Lasa; // Optional\r
89} EFI_TPM2_ACPI_TABLE_V4;\r
90\r
91#pragma pack()\r
92\r
93EFI_TPM2_ACPI_TABLE_V4 mTpm2AcpiTemplate = {\r
94 {\r
95 EFI_ACPI_5_0_TRUSTED_COMPUTING_PLATFORM_2_TABLE_SIGNATURE,\r
96 sizeof (mTpm2AcpiTemplate),\r
97 EFI_TPM2_ACPI_TABLE_REVISION,\r
98 //\r
99 // Compiler initializes the remaining bytes to 0\r
100 // These fields should be filled in in production\r
101 //\r
102 },\r
103 0, // BIT0~15: PlatformClass\r
104 // BIT16~31: Reserved\r
105 0, // Control Area\r
106 EFI_TPM2_ACPI_TABLE_START_METHOD_TIS, // StartMethod\r
107};\r
108\r
109TCG_NVS *mTcgNvs;\r
110\r
111/**\r
112 Find the operation region in TCG ACPI table by given Name and Size,\r
113 and initialize it if the region is found.\r
114\r
115 @param[in, out] Table The TPM item in ACPI table.\r
116 @param[in] Name The name string to find in TPM table.\r
117 @param[in] Size The size of the region to find.\r
118\r
119 @return The allocated address for the found region.\r
120\r
121**/\r
122VOID *\r
123AssignOpRegion (\r
124 EFI_ACPI_DESCRIPTION_HEADER *Table,\r
125 UINT32 Name,\r
126 UINT16 Size\r
127 )\r
128{\r
129 EFI_STATUS Status;\r
130 AML_OP_REGION_32_8 *OpRegion;\r
131 EFI_PHYSICAL_ADDRESS MemoryAddress;\r
132\r
133 MemoryAddress = SIZE_4GB - 1;\r
134\r
135 //\r
136 // Patch some pointers for the ASL code before loading the SSDT.\r
137 //\r
138 for (OpRegion = (AML_OP_REGION_32_8 *) (Table + 1);\r
139 OpRegion <= (AML_OP_REGION_32_8 *) ((UINT8 *) Table + Table->Length);\r
140 OpRegion = (AML_OP_REGION_32_8 *) ((UINT8 *) OpRegion + 1)) {\r
141 if ((OpRegion->OpRegionOp == AML_EXT_REGION_OP) &&\r
142 (OpRegion->NameString == Name) &&\r
143 (OpRegion->DWordPrefix == AML_DWORD_PREFIX) &&\r
144 (OpRegion->BytePrefix == AML_BYTE_PREFIX)) {\r
145\r
146 Status = gBS->AllocatePages(AllocateMaxAddress, EfiACPIMemoryNVS, EFI_SIZE_TO_PAGES (Size), &MemoryAddress);\r
147 ASSERT_EFI_ERROR (Status);\r
148 ZeroMem ((VOID *)(UINTN)MemoryAddress, Size);\r
149 OpRegion->RegionOffset = (UINT32) (UINTN) MemoryAddress;\r
150 OpRegion->RegionLen = (UINT8) Size;\r
59a3ccb0
KQ
151 // Request to unblock this region from MM core\r
152 Status = MmUnblockMemoryRequest (MemoryAddress, EFI_SIZE_TO_PAGES (Size));\r
153 if (Status != EFI_UNSUPPORTED && EFI_ERROR (Status)) {\r
154 ASSERT_EFI_ERROR (Status);\r
155 }\r
3c2dc30d
KQ
156 break;\r
157 }\r
158 }\r
159\r
160 return (VOID *) (UINTN) MemoryAddress;\r
161}\r
162\r
163/**\r
164 Locate the MM communication buffer and protocol, then use it to exchange information with\r
165 Tcg2StandaloneMmm on NVS address and SMI value.\r
166\r
167 @param[in, out] TcgNvs The NVS subject to send to MM environment.\r
168\r
169 @return The status for locating MM common buffer, communicate to MM, etc.\r
170\r
171**/\r
172EFI_STATUS\r
173EFIAPI\r
174ExchangeCommonBuffer (\r
175 IN OUT TCG_NVS *TcgNvs\r
176)\r
177{\r
178 EFI_STATUS Status;\r
179 EFI_MM_COMMUNICATION_PROTOCOL *MmCommunication;\r
180 EDKII_PI_SMM_COMMUNICATION_REGION_TABLE *PiSmmCommunicationRegionTable;\r
181 EFI_MEMORY_DESCRIPTOR *MmCommMemRegion;\r
182 EFI_MM_COMMUNICATE_HEADER *CommHeader;\r
183 TPM_NVS_MM_COMM_BUFFER *CommBuffer;\r
184 UINTN CommBufferSize;\r
185 UINTN Index;\r
186\r
187 // Step 0: Sanity check for input argument\r
188 if (TcgNvs == NULL) {\r
189 DEBUG ((DEBUG_ERROR, "%a - Input argument is NULL!\n", __FUNCTION__));\r
190 return EFI_INVALID_PARAMETER;\r
191 }\r
192\r
193 // Step 1: Grab the common buffer header\r
194 Status = EfiGetSystemConfigurationTable (&gEdkiiPiSmmCommunicationRegionTableGuid, (VOID**) &PiSmmCommunicationRegionTable);\r
195 if (EFI_ERROR (Status)) {\r
196 DEBUG ((DEBUG_ERROR, "%a - Failed to locate SMM communciation common buffer - %r!\n", __FUNCTION__, Status));\r
197 return Status;\r
198 }\r
199\r
200 // Step 2: Grab one that is large enough to hold TPM_NVS_MM_COMM_BUFFER, the IPL one should be sufficient\r
201 CommBufferSize = 0;\r
202 MmCommMemRegion = (EFI_MEMORY_DESCRIPTOR*) (PiSmmCommunicationRegionTable + 1);\r
203 for (Index = 0; Index < PiSmmCommunicationRegionTable->NumberOfEntries; Index++) {\r
204 if (MmCommMemRegion->Type == EfiConventionalMemory) {\r
205 CommBufferSize = EFI_PAGES_TO_SIZE ((UINTN)MmCommMemRegion->NumberOfPages);\r
206 if (CommBufferSize >= (sizeof (TPM_NVS_MM_COMM_BUFFER) + OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data))) {\r
207 break;\r
208 }\r
209 }\r
210 MmCommMemRegion = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)MmCommMemRegion + PiSmmCommunicationRegionTable->DescriptorSize);\r
211 }\r
212\r
213 if (Index >= PiSmmCommunicationRegionTable->NumberOfEntries) {\r
214 // Could not find one that meets our goal...\r
215 DEBUG ((DEBUG_ERROR, "%a - Could not find a common buffer that is big enough for NVS!\n", __FUNCTION__));\r
216 return EFI_OUT_OF_RESOURCES;\r
217 }\r
218\r
219 // Step 3: Start to populate contents\r
220 // Step 3.1: MM Communication common header\r
221 CommHeader = (EFI_MM_COMMUNICATE_HEADER *) (UINTN) MmCommMemRegion->PhysicalStart;\r
222 CommBufferSize = sizeof (TPM_NVS_MM_COMM_BUFFER) + OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data);\r
223 ZeroMem (CommHeader, CommBufferSize);\r
224 CopyGuid (&CommHeader->HeaderGuid, &gTpmNvsMmGuid);\r
225 CommHeader->MessageLength = sizeof (TPM_NVS_MM_COMM_BUFFER);\r
226\r
227 // Step 3.2: TPM_NVS_MM_COMM_BUFFER content per our needs\r
228 CommBuffer = (TPM_NVS_MM_COMM_BUFFER *) (CommHeader->Data);\r
229 CommBuffer->Function = TpmNvsMmExchangeInfo;\r
230 CommBuffer->TargetAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) TcgNvs;\r
231\r
232 // Step 4: Locate the protocol and signal Mmi.\r
233 Status = gBS->LocateProtocol (&gEfiMmCommunicationProtocolGuid, NULL, (VOID**) &MmCommunication);\r
234 if (!EFI_ERROR (Status)) {\r
235 Status = MmCommunication->Communicate (MmCommunication, CommHeader, &CommBufferSize);\r
236 DEBUG ((DEBUG_INFO, "%a - Communicate() = %r\n", __FUNCTION__, Status));\r
237 }\r
238 else {\r
239 DEBUG ((DEBUG_ERROR, "%a - Failed to locate MmCommunication protocol - %r\n", __FUNCTION__, Status));\r
240 return Status;\r
241 }\r
242\r
243 // Step 5: If everything goes well, populate the channel number\r
244 if (!EFI_ERROR (CommBuffer->ReturnStatus)) {\r
245 // Need to demote to UINT8 according to SMI value definition\r
246 TcgNvs->PhysicalPresence.SoftwareSmi = (UINT8) CommBuffer->RegisteredPpSwiValue;\r
247 TcgNvs->MemoryClear.SoftwareSmi = (UINT8) CommBuffer->RegisteredMcSwiValue;\r
248 DEBUG ((\r
249 DEBUG_INFO,\r
250 "%a Communication returned software SMI value. PP: 0x%x; MC: 0x%x.\n",\r
251 __FUNCTION__,\r
252 TcgNvs->PhysicalPresence.SoftwareSmi,\r
253 TcgNvs->MemoryClear.SoftwareSmi\r
254 ));\r
255 }\r
256\r
257 return (EFI_STATUS) CommBuffer->ReturnStatus;\r
258}\r
259\r
260/**\r
261 Patch version string of Physical Presence interface supported by platform. The initial string tag in TPM\r
262ACPI table is "$PV".\r
263\r
264 @param[in, out] Table The TPM item in ACPI table.\r
265 @param[in] PPVer Version string of Physical Presence interface supported by platform.\r
266\r
267 @return The allocated address for the found region.\r
268\r
269**/\r
270EFI_STATUS\r
271UpdatePPVersion (\r
272 EFI_ACPI_DESCRIPTION_HEADER *Table,\r
273 CHAR8 *PPVer\r
274 )\r
275{\r
276 EFI_STATUS Status;\r
277 UINT8 *DataPtr;\r
278\r
279 //\r
280 // Patch some pointers for the ASL code before loading the SSDT.\r
281 //\r
282 for (DataPtr = (UINT8 *)(Table + 1);\r
283 DataPtr <= (UINT8 *) ((UINT8 *) Table + Table->Length - PHYSICAL_PRESENCE_VERSION_SIZE);\r
284 DataPtr += 1) {\r
285 if (AsciiStrCmp((CHAR8 *)DataPtr, PHYSICAL_PRESENCE_VERSION_TAG) == 0) {\r
286 Status = AsciiStrCpyS((CHAR8 *)DataPtr, PHYSICAL_PRESENCE_VERSION_SIZE, PPVer);\r
287 DEBUG((DEBUG_INFO, "TPM2 Physical Presence Interface Version update status 0x%x\n", Status));\r
288 return Status;\r
289 }\r
290 }\r
291\r
292 return EFI_NOT_FOUND;\r
293}\r
294\r
295/**\r
296 Patch interrupt resources returned by TPM _PRS. ResourceTemplate to patch is determined by input\r
297 interrupt buffer size. BufferSize, PkgLength and interrupt descriptor in ByteList need to be patched\r
298\r
299 @param[in, out] Table The TPM item in ACPI table.\r
300 @param[in] IrqBuffer Input new IRQ buffer.\r
301 @param[in] IrqBuffserSize Input new IRQ buffer size.\r
302 @param[out] IsShortFormPkgLength If _PRS returns Short length Package(ACPI spec 20.2.4).\r
303\r
304 @return patch status.\r
305\r
306**/\r
307EFI_STATUS\r
308UpdatePossibleResource (\r
309 IN OUT EFI_ACPI_DESCRIPTION_HEADER *Table,\r
310 IN UINT32 *IrqBuffer,\r
311 IN UINT32 IrqBuffserSize,\r
312 OUT BOOLEAN *IsShortFormPkgLength\r
313 )\r
314{\r
315 UINT8 *DataPtr;\r
316 UINT8 *DataEndPtr;\r
317 UINT32 NewPkgLength;\r
318 UINT32 OrignalPkgLength;\r
319\r
320 NewPkgLength = 0;\r
321 OrignalPkgLength = 0;\r
322 DataEndPtr = NULL;\r
323\r
324 //\r
325 // Follow ACPI spec\r
326 // 6.4.3 Extend Interrupt Descriptor.\r
327 // 19.3.3 ASL Resource Template\r
328 // 20 AML specification\r
329 // to patch TPM ACPI object _PRS returned ResourceTemplate() containing 2 resource descriptors and an auto appended End Tag\r
330 //\r
331 // AML data is organized by following rule.\r
332 // Code need to patch BufferSize and PkgLength and interrupt descriptor in ByteList\r
333 //\r
334 // ============= Buffer ====================\r
335 // DefBuffer := BufferOp PkgLength BufferSize ByteList\r
336 // BufferOp := 0x11\r
337 //\r
338 // ==============PkgLength==================\r
339 // PkgLength := PkgLeadByte |\r
340 // <PkgLeadByte ByteData> |\r
341 // <PkgLeadByte ByteData ByteData> |\r
342 // <PkgLeadByte ByteData ByteData ByteData>\r
343 //\r
344 // PkgLeadByte := <bit 7-6: ByteData count that follows (0-3)>\r
345 // <bit 5-4: Only used if PkgLength <= 63 >\r
346 // <bit 3-0: Least significant package length nybble>\r
347 //\r
348 //==============BufferSize==================\r
349 // BufferSize := Integer\r
350 // Integer := ByteConst|WordConst|DwordConst....\r
351 //\r
352 // ByteConst := BytePrefix ByteData\r
353 //\r
354 //==============ByteList===================\r
355 // ByteList := ByteData ByteList\r
356 //\r
357 //=========================================\r
358\r
359 //\r
360 // 1. Check TPM_PRS_RESS with PkgLength <=63 can hold the input interrupt number buffer for patching\r
361 //\r
362 for (DataPtr = (UINT8 *)(Table + 1);\r
363 DataPtr < (UINT8 *) ((UINT8 *) Table + Table->Length - (TPM_PRS_RES_NAME_SIZE + TPM_POS_RES_TEMPLATE_MIN_SIZE));\r
364 DataPtr += 1) {\r
365 if (CompareMem(DataPtr, TPM_PRS_RESS, TPM_PRS_RES_NAME_SIZE) == 0) {\r
366 //\r
367 // Jump over object name & BufferOp\r
368 //\r
369 DataPtr += TPM_PRS_RES_NAME_SIZE + 1;\r
370\r
371 if ((*DataPtr & (BIT7|BIT6)) == 0) {\r
372 OrignalPkgLength = (UINT32)*DataPtr;\r
373 DataEndPtr = DataPtr + OrignalPkgLength;\r
374\r
375 //\r
376 // Jump over PkgLength = PkgLeadByte only\r
377 //\r
378 NewPkgLength++;\r
379\r
380 //\r
381 // Jump over BufferSize\r
382 //\r
383 if (*(DataPtr + 1) == AML_BYTE_PREFIX) {\r
384 NewPkgLength += 2;\r
385 } else if (*(DataPtr + 1) == AML_WORD_PREFIX) {\r
386 NewPkgLength += 3;\r
387 } else if (*(DataPtr + 1) == AML_DWORD_PREFIX) {\r
388 NewPkgLength += 5;\r
389 } else {\r
390 ASSERT(FALSE);\r
391 return EFI_UNSUPPORTED;\r
392 }\r
393 } else {\r
394 ASSERT(FALSE);\r
395 return EFI_UNSUPPORTED;\r
396 }\r
397\r
398 //\r
399 // Include Memory32Fixed Descriptor (12 Bytes) + Interrupt Descriptor header(5 Bytes) + End Tag(2 Bytes)\r
400 //\r
401 NewPkgLength += 19 + IrqBuffserSize;\r
402 if (NewPkgLength > 63) {\r
403 break;\r
404 }\r
405\r
406 if (NewPkgLength > OrignalPkgLength) {\r
407 ASSERT(FALSE);\r
408 return EFI_INVALID_PARAMETER;\r
409 }\r
410\r
411 //\r
412 // 1.1 Patch PkgLength\r
413 //\r
414 *DataPtr = (UINT8)NewPkgLength;\r
415\r
416 //\r
417 // 1.2 Patch BufferSize = sizeof(Memory32Fixed Descriptor + Interrupt Descriptor + End Tag).\r
418 // It is Little endian. So only patch lowest byte of BufferSize due to current interrupt number limit.\r
419 //\r
420 *(DataPtr + 2) = (UINT8)(IrqBuffserSize + 19);\r
421\r
422 //\r
423 // Notify _PRS to report short formed ResourceTemplate\r
424 //\r
425 *IsShortFormPkgLength = TRUE;\r
426\r
427 break;\r
428 }\r
429 }\r
430\r
431 //\r
432 // 2. Use TPM_PRS_RESL with PkgLength > 63 to hold longer input interrupt number buffer for patching\r
433 //\r
434 if (NewPkgLength > 63) {\r
435 NewPkgLength = 0;\r
436 OrignalPkgLength = 0;\r
437 for (DataPtr = (UINT8 *)(Table + 1);\r
438 DataPtr < (UINT8 *) ((UINT8 *) Table + Table->Length - (TPM_PRS_RES_NAME_SIZE + TPM_POS_RES_TEMPLATE_MIN_SIZE));\r
439 DataPtr += 1) {\r
440 if (CompareMem(DataPtr, TPM_PRS_RESL, TPM_PRS_RES_NAME_SIZE) == 0) {\r
441 //\r
442 // Jump over object name & BufferOp\r
443 //\r
444 DataPtr += TPM_PRS_RES_NAME_SIZE + 1;\r
445\r
446 if ((*DataPtr & (BIT7|BIT6)) != 0) {\r
447 OrignalPkgLength = (UINT32)(*(DataPtr + 1) << 4) + (*DataPtr & 0x0F);\r
448 DataEndPtr = DataPtr + OrignalPkgLength;\r
449 //\r
450 // Jump over PkgLength = PkgLeadByte + ByteData length\r
451 //\r
452 NewPkgLength += 1 + ((*DataPtr & (BIT7|BIT6)) >> 6);\r
453\r
454 //\r
455 // Jump over BufferSize\r
456 //\r
457 if (*(DataPtr + NewPkgLength) == AML_BYTE_PREFIX) {\r
458 NewPkgLength += 2;\r
459 } else if (*(DataPtr + NewPkgLength) == AML_WORD_PREFIX) {\r
460 NewPkgLength += 3;\r
461 } else if (*(DataPtr + NewPkgLength) == AML_DWORD_PREFIX) {\r
462 NewPkgLength += 5;\r
463 } else {\r
464 ASSERT(FALSE);\r
465 return EFI_UNSUPPORTED;\r
466 }\r
467 } else {\r
468 ASSERT(FALSE);\r
469 return EFI_UNSUPPORTED;\r
470 }\r
471\r
472 //\r
473 // Include Memory32Fixed Descriptor (12 Bytes) + Interrupt Descriptor header(5 Bytes) + End Tag(2 Bytes)\r
474 //\r
475 NewPkgLength += 19 + IrqBuffserSize;\r
476\r
477 if (NewPkgLength > OrignalPkgLength) {\r
478 ASSERT(FALSE);\r
479 return EFI_INVALID_PARAMETER;\r
480 }\r
481\r
482 //\r
483 // 2.1 Patch PkgLength. Only patch PkgLeadByte and first ByteData\r
484 //\r
485 *DataPtr = (UINT8)((*DataPtr) & 0xF0) | (NewPkgLength & 0x0F);\r
486 *(DataPtr + 1) = (UINT8)((NewPkgLength & 0xFF0) >> 4);\r
487\r
488 //\r
489 // 2.2 Patch BufferSize = sizeof(Memory32Fixed Descriptor + Interrupt Descriptor + End Tag).\r
490 // It is Little endian. Only patch lowest byte of BufferSize due to current interrupt number limit.\r
491 //\r
492 *(DataPtr + 2 + ((*DataPtr & (BIT7|BIT6)) >> 6)) = (UINT8)(IrqBuffserSize + 19);\r
493\r
494 //\r
495 // Notify _PRS to report long formed ResourceTemplate\r
496 //\r
497 *IsShortFormPkgLength = FALSE;\r
498 break;\r
499 }\r
500 }\r
501 }\r
502\r
503 if (DataPtr >= (UINT8 *) ((UINT8 *) Table + Table->Length - (TPM_PRS_RES_NAME_SIZE + TPM_POS_RES_TEMPLATE_MIN_SIZE))) {\r
504 return EFI_NOT_FOUND;\r
505 }\r
506\r
507 //\r
508 // 3. Move DataPtr to Interrupt descriptor header and patch interrupt descriptor.\r
509 // 5 bytes for interrupt descriptor header, 2 bytes for End Tag\r
510 //\r
511 DataPtr += NewPkgLength - (5 + IrqBuffserSize + 2);\r
512 //\r
513 // 3.1 Patch Length bit[7:0] of Interrupt descriptor patch interrupt descriptor\r
514 //\r
515 *(DataPtr + 1) = (UINT8)(2 + IrqBuffserSize);\r
516 //\r
517 // 3.2 Patch Interrupt Table Length\r
518 //\r
519 *(DataPtr + 4) = (UINT8)(IrqBuffserSize / sizeof(UINT32));\r
520 //\r
521 // 3.3 Copy patched InterruptNumBuffer\r
522 //\r
523 CopyMem(DataPtr + 5, IrqBuffer, IrqBuffserSize);\r
524\r
525 //\r
526 // 4. Jump over Interrupt descriptor and Patch END Tag, set Checksum field to 0\r
527 //\r
528 DataPtr += 5 + IrqBuffserSize;\r
529 *DataPtr = ACPI_END_TAG_DESCRIPTOR;\r
530 *(DataPtr + 1) = 0;\r
531\r
532 //\r
533 // 5. Jump over new ResourceTemplate. Stuff rest bytes to NOOP\r
534 //\r
535 DataPtr += 2;\r
536 if (DataPtr < DataEndPtr) {\r
537 SetMem(DataPtr, (UINTN)DataEndPtr - (UINTN)DataPtr, AML_NOOP_OP);\r
538 }\r
539\r
540 return EFI_SUCCESS;\r
541}\r
542\r
543/**\r
544 Patch TPM2 device HID string. The initial string tag in TPM2 ACPI table is "NNN0000".\r
545\r
546 @param[in, out] Table The TPM2 SSDT ACPI table.\r
547\r
548 @return HID Update status.\r
549\r
550**/\r
551EFI_STATUS\r
552UpdateHID (\r
553 EFI_ACPI_DESCRIPTION_HEADER *Table\r
554 )\r
555{\r
556 EFI_STATUS Status;\r
557 UINT8 *DataPtr;\r
558 CHAR8 Hid[TPM_HID_ACPI_SIZE];\r
559 UINT32 ManufacturerID;\r
560 UINT32 FirmwareVersion1;\r
561 UINT32 FirmwareVersion2;\r
562 BOOLEAN PnpHID;\r
563\r
564 PnpHID = TRUE;\r
565\r
566 //\r
567 // Initialize HID with Default PNP string\r
568 //\r
569 ZeroMem(Hid, TPM_HID_ACPI_SIZE);\r
570\r
571 //\r
572 // Get Manufacturer ID\r
573 //\r
574 Status = Tpm2GetCapabilityManufactureID(&ManufacturerID);\r
575 if (!EFI_ERROR(Status)) {\r
576 DEBUG((DEBUG_INFO, "TPM_PT_MANUFACTURER 0x%08x\n", ManufacturerID));\r
577 //\r
578 // ManufacturerID defined in TCG Vendor ID Registry\r
579 // may tailed with 0x00 or 0x20\r
580 //\r
581 if ((ManufacturerID >> 24) == 0x00 || ((ManufacturerID >> 24) == 0x20)) {\r
582 //\r
583 // HID containing PNP ID "NNN####"\r
584 // NNN is uppercase letter for Vendor ID specified by manufacturer\r
585 //\r
586 CopyMem(Hid, &ManufacturerID, 3);\r
587 } else {\r
588 //\r
589 // HID containing ACP ID "NNNN####"\r
590 // NNNN is uppercase letter for Vendor ID specified by manufacturer\r
591 //\r
592 CopyMem(Hid, &ManufacturerID, 4);\r
593 PnpHID = FALSE;\r
594 }\r
595 } else {\r
596 DEBUG ((DEBUG_ERROR, "Get TPM_PT_MANUFACTURER failed %x!\n", Status));\r
597 ASSERT(FALSE);\r
598 return Status;\r
599 }\r
600\r
601 Status = Tpm2GetCapabilityFirmwareVersion(&FirmwareVersion1, &FirmwareVersion2);\r
602 if (!EFI_ERROR(Status)) {\r
603 DEBUG((DEBUG_INFO, "TPM_PT_FIRMWARE_VERSION_1 0x%x\n", FirmwareVersion1));\r
604 DEBUG((DEBUG_INFO, "TPM_PT_FIRMWARE_VERSION_2 0x%x\n", FirmwareVersion2));\r
605 //\r
606 // #### is Firmware Version 1\r
607 //\r
608 if (PnpHID) {\r
609 AsciiSPrint(Hid + 3, TPM_HID_PNP_SIZE - 3, "%02d%02d", ((FirmwareVersion1 & 0xFFFF0000) >> 16), (FirmwareVersion1 & 0x0000FFFF));\r
610 } else {\r
611 AsciiSPrint(Hid + 4, TPM_HID_ACPI_SIZE - 4, "%02d%02d", ((FirmwareVersion1 & 0xFFFF0000) >> 16), (FirmwareVersion1 & 0x0000FFFF));\r
612 }\r
613\r
614 } else {\r
615 DEBUG ((DEBUG_ERROR, "Get TPM_PT_FIRMWARE_VERSION_X failed %x!\n", Status));\r
616 ASSERT(FALSE);\r
617 return Status;\r
618 }\r
619\r
620 //\r
621 // Patch HID in ASL code before loading the SSDT.\r
622 //\r
623 for (DataPtr = (UINT8 *)(Table + 1);\r
624 DataPtr <= (UINT8 *) ((UINT8 *) Table + Table->Length - TPM_HID_PNP_SIZE);\r
625 DataPtr += 1) {\r
626 if (AsciiStrCmp((CHAR8 *)DataPtr, TPM_HID_TAG) == 0) {\r
627 if (PnpHID) {\r
628 CopyMem(DataPtr, Hid, TPM_HID_PNP_SIZE);\r
629 //\r
630 // if HID is PNP ID, patch the last byte in HID TAG to Noop\r
631 //\r
632 *(DataPtr + TPM_HID_PNP_SIZE) = AML_NOOP_OP;\r
633 } else {\r
634\r
635 CopyMem(DataPtr, Hid, TPM_HID_ACPI_SIZE);\r
636 }\r
637 DEBUG((DEBUG_INFO, "TPM2 ACPI _HID is patched to %a\n", DataPtr));\r
638\r
639 return Status;\r
640 }\r
641 }\r
642\r
643 DEBUG((DEBUG_ERROR, "TPM2 ACPI HID TAG for patch not found!\n"));\r
644 return EFI_NOT_FOUND;\r
645}\r
646\r
647/**\r
648 Initialize and publish TPM items in ACPI table.\r
649\r
650 @retval EFI_SUCCESS The TCG ACPI table is published successfully.\r
651 @retval Others The TCG ACPI table is not published.\r
652\r
653**/\r
654EFI_STATUS\r
655PublishAcpiTable (\r
656 VOID\r
657 )\r
658{\r
659 EFI_STATUS Status;\r
660 EFI_ACPI_TABLE_PROTOCOL *AcpiTable;\r
661 UINTN TableKey;\r
662 EFI_ACPI_DESCRIPTION_HEADER *Table;\r
663 UINTN TableSize;\r
664 UINT32 *PossibleIrqNumBuf;\r
665 UINT32 PossibleIrqNumBufSize;\r
666 BOOLEAN IsShortFormPkgLength;\r
667\r
668 IsShortFormPkgLength = FALSE;\r
669\r
670 Status = GetSectionFromFv (\r
671 &gEfiCallerIdGuid,\r
672 EFI_SECTION_RAW,\r
673 0,\r
674 (VOID **) &Table,\r
675 &TableSize\r
676 );\r
677 ASSERT_EFI_ERROR (Status);\r
678\r
679 //\r
680 // Measure to PCR[0] with event EV_POST_CODE ACPI DATA.\r
681 // The measurement has to be done before any update.\r
682 // Otherwise, the PCR record would be different after TPM FW update\r
683 // or the PCD configuration change.\r
684 //\r
685 TpmMeasureAndLogData(\r
686 0,\r
687 EV_POST_CODE,\r
688 EV_POSTCODE_INFO_ACPI_DATA,\r
689 ACPI_DATA_LEN,\r
690 Table,\r
691 TableSize\r
692 );\r
693\r
694 //\r
695 // Update Table version before measuring it to PCR\r
696 //\r
697 Status = UpdatePPVersion(Table, (CHAR8 *)PcdGetPtr(PcdTcgPhysicalPresenceInterfaceVer));\r
698 ASSERT_EFI_ERROR (Status);\r
699\r
700 DEBUG ((\r
701 DEBUG_INFO,\r
702 "Current physical presence interface version - %a\n",\r
703 (CHAR8 *) PcdGetPtr(PcdTcgPhysicalPresenceInterfaceVer)\r
704 ));\r
705\r
706 //\r
707 // Update TPM2 HID after measuring it to PCR\r
708 //\r
709 Status = UpdateHID(Table);\r
710 if (EFI_ERROR(Status)) {\r
711 return Status;\r
712 }\r
713\r
714 if (PcdGet32(PcdTpm2CurrentIrqNum) != 0) {\r
715 //\r
716 // Patch _PRS interrupt resource only when TPM interrupt is supported\r
717 //\r
718 PossibleIrqNumBuf = (UINT32 *)PcdGetPtr(PcdTpm2PossibleIrqNumBuf);\r
719 PossibleIrqNumBufSize = (UINT32)PcdGetSize(PcdTpm2PossibleIrqNumBuf);\r
720\r
721 if (PossibleIrqNumBufSize <= MAX_PRS_INT_BUF_SIZE && (PossibleIrqNumBufSize % sizeof(UINT32)) == 0) {\r
722 Status = UpdatePossibleResource(Table, PossibleIrqNumBuf, PossibleIrqNumBufSize, &IsShortFormPkgLength);\r
723 DEBUG ((\r
724 DEBUG_INFO,\r
725 "UpdatePossibleResource status - %x. TPM2 service may not ready in OS.\n",\r
726 Status\r
727 ));\r
728 } else {\r
729 DEBUG ((\r
730 DEBUG_INFO,\r
731 "PcdTpm2PossibleIrqNumBuf size %x is not correct. TPM2 service may not ready in OS.\n",\r
732 PossibleIrqNumBufSize\r
733 ));\r
734 }\r
735 }\r
736\r
737 ASSERT (Table->OemTableId == SIGNATURE_64 ('T', 'p', 'm', '2', 'T', 'a', 'b', 'l'));\r
738 CopyMem (Table->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (Table->OemId) );\r
739 mTcgNvs = AssignOpRegion (Table, SIGNATURE_32 ('T', 'N', 'V', 'S'), (UINT16) sizeof (TCG_NVS));\r
740 ASSERT (mTcgNvs != NULL);\r
741 mTcgNvs->TpmIrqNum = PcdGet32(PcdTpm2CurrentIrqNum);\r
742 mTcgNvs->IsShortFormPkgLength = IsShortFormPkgLength;\r
743\r
744 Status = ExchangeCommonBuffer (mTcgNvs);\r
745\r
746 //\r
747 // Publish the TPM ACPI table. Table is re-checksummed.\r
748 //\r
749 Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTable);\r
750 ASSERT_EFI_ERROR (Status);\r
751\r
752 TableKey = 0;\r
753 Status = AcpiTable->InstallAcpiTable (\r
754 AcpiTable,\r
755 Table,\r
756 TableSize,\r
757 &TableKey\r
758 );\r
759 ASSERT_EFI_ERROR (Status);\r
760\r
761 return Status;\r
762}\r
763\r
764/**\r
765 Publish TPM2 ACPI table\r
766\r
767 @retval EFI_SUCCESS The TPM2 ACPI table is published successfully.\r
768 @retval Others The TPM2 ACPI table is not published.\r
769\r
770**/\r
771EFI_STATUS\r
772PublishTpm2 (\r
773 VOID\r
774 )\r
775{\r
776 EFI_STATUS Status;\r
777 EFI_ACPI_TABLE_PROTOCOL *AcpiTable;\r
778 UINTN TableKey;\r
779 UINT64 OemTableId;\r
780 EFI_TPM2_ACPI_CONTROL_AREA *ControlArea;\r
781 TPM2_PTP_INTERFACE_TYPE InterfaceType;\r
782\r
783 //\r
784 // Measure to PCR[0] with event EV_POST_CODE ACPI DATA.\r
785 // The measurement has to be done before any update.\r
786 // Otherwise, the PCR record would be different after event log update\r
787 // or the PCD configuration change.\r
788 //\r
789 TpmMeasureAndLogData(\r
790 0,\r
791 EV_POST_CODE,\r
792 EV_POSTCODE_INFO_ACPI_DATA,\r
793 ACPI_DATA_LEN,\r
794 &mTpm2AcpiTemplate,\r
795 mTpm2AcpiTemplate.Header.Length\r
796 );\r
797\r
798 mTpm2AcpiTemplate.Header.Revision = PcdGet8(PcdTpm2AcpiTableRev);\r
799 DEBUG((DEBUG_INFO, "Tpm2 ACPI table revision is %d\n", mTpm2AcpiTemplate.Header.Revision));\r
800\r
801 //\r
802 // PlatformClass is only valid for version 4 and above\r
803 // BIT0~15: PlatformClass\r
804 // BIT16~31: Reserved\r
805 //\r
806 if (mTpm2AcpiTemplate.Header.Revision >= EFI_TPM2_ACPI_TABLE_REVISION_4) {\r
807 mTpm2AcpiTemplate.Flags = (mTpm2AcpiTemplate.Flags & 0xFFFF0000) | PcdGet8(PcdTpmPlatformClass);\r
808 DEBUG((DEBUG_INFO, "Tpm2 ACPI table PlatformClass is %d\n", (mTpm2AcpiTemplate.Flags & 0x0000FFFF)));\r
809 }\r
810\r
811 mTpm2AcpiTemplate.Laml = PcdGet32(PcdTpm2AcpiTableLaml);\r
812 mTpm2AcpiTemplate.Lasa = PcdGet64(PcdTpm2AcpiTableLasa);\r
813 if ((mTpm2AcpiTemplate.Header.Revision < EFI_TPM2_ACPI_TABLE_REVISION_4) ||\r
814 (mTpm2AcpiTemplate.Laml == 0) || (mTpm2AcpiTemplate.Lasa == 0)) {\r
815 //\r
816 // If version is smaller than 4 or Laml/Lasa is not valid, rollback to original Length.\r
817 //\r
818 mTpm2AcpiTemplate.Header.Length = sizeof(EFI_TPM2_ACPI_TABLE);\r
819 }\r
820\r
821 InterfaceType = PcdGet8(PcdActiveTpmInterfaceType);\r
822 switch (InterfaceType) {\r
823 case Tpm2PtpInterfaceCrb:\r
824 mTpm2AcpiTemplate.StartMethod = EFI_TPM2_ACPI_TABLE_START_METHOD_COMMAND_RESPONSE_BUFFER_INTERFACE;\r
825 mTpm2AcpiTemplate.AddressOfControlArea = PcdGet64 (PcdTpmBaseAddress) + 0x40;\r
826 ControlArea = (EFI_TPM2_ACPI_CONTROL_AREA *)(UINTN)mTpm2AcpiTemplate.AddressOfControlArea;\r
827 ControlArea->CommandSize = 0xF80;\r
828 ControlArea->ResponseSize = 0xF80;\r
829 ControlArea->Command = PcdGet64 (PcdTpmBaseAddress) + 0x80;\r
830 ControlArea->Response = PcdGet64 (PcdTpmBaseAddress) + 0x80;\r
831 break;\r
832 case Tpm2PtpInterfaceFifo:\r
833 case Tpm2PtpInterfaceTis:\r
834 break;\r
835 default:\r
836 DEBUG((DEBUG_ERROR, "TPM2 InterfaceType get error! %d\n", InterfaceType));\r
837 break;\r
838 }\r
839\r
840 CopyMem (mTpm2AcpiTemplate.Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (mTpm2AcpiTemplate.Header.OemId));\r
841 OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);\r
842 CopyMem (&mTpm2AcpiTemplate.Header.OemTableId, &OemTableId, sizeof (UINT64));\r
843 mTpm2AcpiTemplate.Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision);\r
844 mTpm2AcpiTemplate.Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId);\r
845 mTpm2AcpiTemplate.Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);\r
846\r
847 //\r
848 // Construct ACPI table\r
849 //\r
850 Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTable);\r
851 ASSERT_EFI_ERROR (Status);\r
852\r
853 Status = AcpiTable->InstallAcpiTable (\r
854 AcpiTable,\r
855 &mTpm2AcpiTemplate,\r
856 mTpm2AcpiTemplate.Header.Length,\r
857 &TableKey\r
858 );\r
859 ASSERT_EFI_ERROR (Status);\r
860\r
861 return Status;\r
862}\r
863\r
864/**\r
865 The driver's entry point.\r
866\r
867 It patches and installs ACPI tables used for handling TPM physical presence\r
868 and Memory Clear requests through ACPI method.\r
869\r
870 @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
871 @param[in] SystemTable A pointer to the EFI System Table.\r
872\r
873 @retval EFI_SUCCESS The entry point is executed successfully.\r
874 @retval Others Some error occurs when executing this entry point.\r
875\r
876**/\r
877EFI_STATUS\r
878EFIAPI\r
879InitializeTcgAcpi (\r
880 IN EFI_HANDLE ImageHandle,\r
881 IN EFI_SYSTEM_TABLE *SystemTable\r
882 )\r
883{\r
884 EFI_STATUS Status;\r
885\r
886 if (!CompareGuid (PcdGetPtr(PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceTpm20DtpmGuid)){\r
887 DEBUG ((DEBUG_ERROR, "No TPM2 DTPM instance required!\n"));\r
888 return EFI_UNSUPPORTED;\r
889 }\r
890\r
891 Status = PublishAcpiTable ();\r
892 ASSERT_EFI_ERROR (Status);\r
893\r
894 //\r
895 // Set TPM2 ACPI table\r
896 //\r
897 Status = PublishTpm2 ();\r
898 ASSERT_EFI_ERROR (Status);\r
899\r
900 return EFI_SUCCESS;\r
901}\r
902\r