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