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