SecurityPkg: Add TPM PTP support in TCG2 SMM.
[mirror_edk2.git] / SecurityPkg / Tcg / Tcg2Smm / Tcg2Smm.c
CommitLineData
1abfa4ce
JY
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
d967d6d9 12Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
1abfa4ce
JY
13This program and the accompanying materials \r
14are licensed and made available under the terms and conditions of the BSD License \r
15which accompanies this distribution. The full text of the license may be found at \r
16http://opensource.org/licenses/bsd-license.php\r
17\r
18THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
19WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
20\r
21**/\r
22\r
23#include "Tcg2Smm.h"\r
24\r
d967d6d9
JY
25typedef enum {\r
26 PtpInterfaceTis,\r
27 PtpInterfaceFifo,\r
28 PtpInterfaceCrb,\r
29 PtpInterfaceMax,\r
30} PTP_INTERFACE_TYPE;\r
31\r
32/**\r
33 Return PTP interface type.\r
34\r
35 @param[in] Register Pointer to PTP register.\r
36\r
37 @return PTP interface type.\r
38**/\r
39PTP_INTERFACE_TYPE\r
40GetPtpInterface (\r
41 IN VOID *Register\r
42 )\r
43{\r
44 PTP_CRB_INTERFACE_IDENTIFIER InterfaceId;\r
45 PTP_FIFO_INTERFACE_CAPABILITY InterfaceCapability;\r
46\r
47 //\r
48 // Check interface id\r
49 //\r
50 InterfaceId.Uint32 = MmioRead32 ((UINTN)&((PTP_CRB_REGISTERS *)Register)->InterfaceId);\r
51 InterfaceCapability.Uint32 = MmioRead32 ((UINTN)&((PTP_FIFO_REGISTERS *)Register)->InterfaceCapability);\r
52\r
53 if ((InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_CRB) &&\r
54 (InterfaceId.Bits.InterfaceVersion == PTP_INTERFACE_IDENTIFIER_INTERFACE_VERSION_CRB) &&\r
55 (InterfaceId.Bits.CapCRB != 0)) {\r
56 return PtpInterfaceCrb;\r
57 }\r
58 if ((InterfaceId.Bits.InterfaceType == PTP_INTERFACE_IDENTIFIER_INTERFACE_TYPE_FIFO) &&\r
59 (InterfaceId.Bits.InterfaceVersion == PTP_INTERFACE_IDENTIFIER_INTERFACE_VERSION_FIFO) &&\r
60 (InterfaceId.Bits.CapFIFO != 0) &&\r
61 (InterfaceCapability.Bits.InterfaceVersion == INTERFACE_CAPABILITY_INTERFACE_VERSION_PTP)) {\r
62 return PtpInterfaceFifo;\r
63 }\r
64 return PtpInterfaceTis;\r
65}\r
66\r
1abfa4ce
JY
67EFI_TPM2_ACPI_TABLE mTpm2AcpiTemplate = {\r
68 {\r
69 EFI_ACPI_5_0_TRUSTED_COMPUTING_PLATFORM_2_TABLE_SIGNATURE,\r
70 sizeof (mTpm2AcpiTemplate),\r
71 EFI_TPM2_ACPI_TABLE_REVISION,\r
72 //\r
73 // Compiler initializes the remaining bytes to 0\r
74 // These fields should be filled in in production\r
75 //\r
76 },\r
77 0, // Flags\r
78 0, // Control Area\r
79 EFI_TPM2_ACPI_TABLE_START_METHOD_TIS, // StartMethod\r
80};\r
81\r
82EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable;\r
83TCG_NVS *mTcgNvs;\r
84\r
85/**\r
86 Software SMI callback for TPM physical presence which is called from ACPI method.\r
87\r
88 Caution: This function may receive untrusted input.\r
89 Variable and ACPINvs are external input, so this function will validate\r
90 its data structure to be valid value.\r
91\r
92 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().\r
93 @param[in] Context Points to an optional handler context which was specified when the\r
94 handler was registered.\r
95 @param[in, out] CommBuffer A pointer to a collection of data in memory that will\r
96 be conveyed from a non-SMM environment into an SMM environment.\r
97 @param[in, out] CommBufferSize The size of the CommBuffer.\r
98\r
99 @retval EFI_SUCCESS The interrupt was handled successfully.\r
100\r
101**/\r
102EFI_STATUS\r
103EFIAPI\r
104PhysicalPresenceCallback (\r
105 IN EFI_HANDLE DispatchHandle,\r
106 IN CONST VOID *Context,\r
107 IN OUT VOID *CommBuffer,\r
108 IN OUT UINTN *CommBufferSize\r
109 )\r
110{\r
111 UINT32 MostRecentRequest;\r
112 UINT32 Response;\r
113\r
114 if (mTcgNvs->PhysicalPresence.Parameter == TCG_ACPI_FUNCTION_RETURN_REQUEST_RESPONSE_TO_OS) {\r
115 mTcgNvs->PhysicalPresence.ReturnCode = Tcg2PhysicalPresenceLibReturnOperationResponseToOsFunction (\r
116 &MostRecentRequest,\r
117 &Response\r
118 );\r
119 mTcgNvs->PhysicalPresence.LastRequest = MostRecentRequest;\r
120 mTcgNvs->PhysicalPresence.Response = Response;\r
121 return EFI_SUCCESS;\r
122 } else if ((mTcgNvs->PhysicalPresence.Parameter == TCG_ACPI_FUNCTION_SUBMIT_REQUEST_TO_BIOS) \r
123 || (mTcgNvs->PhysicalPresence.Parameter == TCG_ACPI_FUNCTION_SUBMIT_REQUEST_TO_BIOS_2)) {\r
124 mTcgNvs->PhysicalPresence.ReturnCode = Tcg2PhysicalPresenceLibSubmitRequestToPreOSFunction (\r
125 mTcgNvs->PhysicalPresence.Request,\r
126 mTcgNvs->PhysicalPresence.RequestParameter\r
127 );\r
128 } else if (mTcgNvs->PhysicalPresence.Parameter == TCG_ACPI_FUNCTION_GET_USER_CONFIRMATION_STATUS_FOR_REQUEST) {\r
129 mTcgNvs->PhysicalPresence.ReturnCode = Tcg2PhysicalPresenceLibGetUserConfirmationStatusFunction (mTcgNvs->PhysicalPresence.Request);\r
130 } \r
131\r
132 return EFI_SUCCESS;\r
133}\r
134\r
135\r
136/**\r
137 Software SMI callback for MemoryClear which is called from ACPI method.\r
138\r
139 Caution: This function may receive untrusted input.\r
140 Variable and ACPINvs are external input, so this function will validate\r
141 its data structure to be valid value.\r
142\r
143 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().\r
144 @param[in] Context Points to an optional handler context which was specified when the\r
145 handler was registered.\r
146 @param[in, out] CommBuffer A pointer to a collection of data in memory that will\r
147 be conveyed from a non-SMM environment into an SMM environment.\r
148 @param[in, out] CommBufferSize The size of the CommBuffer.\r
149\r
150 @retval EFI_SUCCESS The interrupt was handled successfully.\r
151\r
152**/\r
153EFI_STATUS\r
154EFIAPI\r
155MemoryClearCallback (\r
156 IN EFI_HANDLE DispatchHandle,\r
157 IN CONST VOID *Context,\r
158 IN OUT VOID *CommBuffer,\r
159 IN OUT UINTN *CommBufferSize\r
160 )\r
161{\r
162 EFI_STATUS Status;\r
163 UINTN DataSize;\r
164 UINT8 MorControl;\r
165\r
166 mTcgNvs->MemoryClear.ReturnCode = MOR_REQUEST_SUCCESS;\r
167 if (mTcgNvs->MemoryClear.Parameter == ACPI_FUNCTION_DSM_MEMORY_CLEAR_INTERFACE) {\r
168 MorControl = (UINT8) mTcgNvs->MemoryClear.Request;\r
169 } else if (mTcgNvs->MemoryClear.Parameter == ACPI_FUNCTION_PTS_CLEAR_MOR_BIT) {\r
170 DataSize = sizeof (UINT8);\r
171 Status = mSmmVariable->SmmGetVariable (\r
172 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,\r
173 &gEfiMemoryOverwriteControlDataGuid,\r
174 NULL,\r
175 &DataSize,\r
176 &MorControl\r
177 );\r
178 if (EFI_ERROR (Status)) {\r
179 mTcgNvs->MemoryClear.ReturnCode = MOR_REQUEST_GENERAL_FAILURE;\r
180 DEBUG ((EFI_D_ERROR, "[TPM] Get MOR variable failure! Status = %r\n", Status));\r
181 return EFI_SUCCESS;\r
182 }\r
183\r
184 if (MOR_CLEAR_MEMORY_VALUE (MorControl) == 0x0) {\r
185 return EFI_SUCCESS;\r
186 }\r
187 MorControl &= ~MOR_CLEAR_MEMORY_BIT_MASK;\r
188 }\r
189\r
190 DataSize = sizeof (UINT8);\r
191 Status = mSmmVariable->SmmSetVariable (\r
192 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,\r
193 &gEfiMemoryOverwriteControlDataGuid,\r
194 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
195 DataSize,\r
196 &MorControl\r
197 );\r
198 if (EFI_ERROR (Status)) { \r
199 mTcgNvs->MemoryClear.ReturnCode = MOR_REQUEST_GENERAL_FAILURE;\r
200 DEBUG ((EFI_D_ERROR, "[TPM] Set MOR variable failure! Status = %r\n", Status));\r
201 }\r
202\r
203 return EFI_SUCCESS;\r
204}\r
205\r
206/**\r
207 Find the operation region in TCG ACPI table by given Name and Size,\r
208 and initialize it if the region is found.\r
209\r
210 @param[in, out] Table The TPM item in ACPI table.\r
211 @param[in] Name The name string to find in TPM table.\r
212 @param[in] Size The size of the region to find.\r
213\r
214 @return The allocated address for the found region.\r
215\r
216**/\r
217VOID *\r
218AssignOpRegion (\r
219 EFI_ACPI_DESCRIPTION_HEADER *Table,\r
220 UINT32 Name,\r
221 UINT16 Size\r
222 )\r
223{\r
224 EFI_STATUS Status;\r
225 AML_OP_REGION_32_8 *OpRegion;\r
226 EFI_PHYSICAL_ADDRESS MemoryAddress;\r
227\r
228 MemoryAddress = SIZE_4GB - 1;\r
229\r
230 //\r
231 // Patch some pointers for the ASL code before loading the SSDT.\r
232 //\r
233 for (OpRegion = (AML_OP_REGION_32_8 *) (Table + 1);\r
234 OpRegion <= (AML_OP_REGION_32_8 *) ((UINT8 *) Table + Table->Length);\r
235 OpRegion = (AML_OP_REGION_32_8 *) ((UINT8 *) OpRegion + 1)) {\r
236 if ((OpRegion->OpRegionOp == AML_EXT_REGION_OP) && \r
237 (OpRegion->NameString == Name) &&\r
238 (OpRegion->DWordPrefix == AML_DWORD_PREFIX) &&\r
239 (OpRegion->BytePrefix == AML_BYTE_PREFIX)) {\r
240\r
241 Status = gBS->AllocatePages(AllocateMaxAddress, EfiACPIMemoryNVS, EFI_SIZE_TO_PAGES (Size), &MemoryAddress);\r
242 ASSERT_EFI_ERROR (Status);\r
243 ZeroMem ((VOID *)(UINTN)MemoryAddress, Size);\r
244 OpRegion->RegionOffset = (UINT32) (UINTN) MemoryAddress;\r
245 OpRegion->RegionLen = (UINT8) Size;\r
246 break;\r
247 }\r
248 }\r
249\r
250 return (VOID *) (UINTN) MemoryAddress;\r
251}\r
252\r
253/**\r
254 Initialize and publish TPM items in ACPI table.\r
255\r
256 @retval EFI_SUCCESS The TCG ACPI table is published successfully.\r
257 @retval Others The TCG ACPI table is not published.\r
258\r
259**/\r
260EFI_STATUS\r
261PublishAcpiTable (\r
262 VOID\r
263 )\r
264{\r
265 EFI_STATUS Status;\r
266 EFI_ACPI_TABLE_PROTOCOL *AcpiTable;\r
267 UINTN TableKey;\r
268 EFI_ACPI_DESCRIPTION_HEADER *Table;\r
269 UINTN TableSize;\r
270\r
271 Status = GetSectionFromFv (\r
272 &gEfiCallerIdGuid,\r
273 EFI_SECTION_RAW,\r
274 0,\r
275 (VOID **) &Table,\r
276 &TableSize\r
277 );\r
278 ASSERT_EFI_ERROR (Status);\r
279\r
280\r
281 //\r
282 // Measure to PCR[0] with event EV_POST_CODE ACPI DATA\r
283 //\r
284 TpmMeasureAndLogData(\r
285 0,\r
286 EV_POST_CODE,\r
287 EV_POSTCODE_INFO_ACPI_DATA,\r
288 ACPI_DATA_LEN,\r
289 Table,\r
290 TableSize\r
291 );\r
292\r
293\r
294 ASSERT (Table->OemTableId == SIGNATURE_64 ('T', 'p', 'm', '2', 'T', 'a', 'b', 'l'));\r
295 CopyMem (Table->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (Table->OemId) );\r
296 mTcgNvs = AssignOpRegion (Table, SIGNATURE_32 ('T', 'N', 'V', 'S'), (UINT16) sizeof (TCG_NVS));\r
297 ASSERT (mTcgNvs != NULL);\r
298\r
299 //\r
300 // Publish the TPM ACPI table\r
301 //\r
302 Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTable);\r
303 ASSERT_EFI_ERROR (Status);\r
304\r
305 TableKey = 0;\r
306 Status = AcpiTable->InstallAcpiTable (\r
307 AcpiTable,\r
308 Table,\r
309 TableSize,\r
310 &TableKey\r
311 );\r
312 ASSERT_EFI_ERROR (Status);\r
313\r
314 return Status;\r
315}\r
316\r
317/**\r
318 Publish TPM2 ACPI table\r
319\r
320 @retval EFI_SUCCESS The TPM2 ACPI table is published successfully.\r
321 @retval Others The TPM2 ACPI table is not published.\r
322\r
323**/\r
324EFI_STATUS\r
325PublishTpm2 (\r
326 VOID\r
327 )\r
328{\r
329 EFI_STATUS Status;\r
330 EFI_ACPI_TABLE_PROTOCOL *AcpiTable;\r
331 UINTN TableKey;\r
332 UINT64 OemTableId;\r
d967d6d9
JY
333 EFI_TPM2_ACPI_CONTROL_AREA *ControlArea;\r
334 PTP_INTERFACE_TYPE InterfaceType;\r
1abfa4ce
JY
335\r
336 //\r
337 // Measure to PCR[0] with event EV_POST_CODE ACPI DATA\r
338 //\r
339 TpmMeasureAndLogData(\r
340 0,\r
341 EV_POST_CODE,\r
342 EV_POSTCODE_INFO_ACPI_DATA,\r
343 ACPI_DATA_LEN,\r
344 &mTpm2AcpiTemplate,\r
345 sizeof(mTpm2AcpiTemplate)\r
346 );\r
347\r
d967d6d9
JY
348 InterfaceType = GetPtpInterface ((VOID *) (UINTN) PcdGet64 (PcdTpmBaseAddress));\r
349 switch (InterfaceType) {\r
350 case PtpInterfaceCrb:\r
351 mTpm2AcpiTemplate.StartMethod = EFI_TPM2_ACPI_TABLE_START_METHOD_COMMAND_RESPONSE_BUFFER_INTERFACE;\r
352 mTpm2AcpiTemplate.AddressOfControlArea = PcdGet64 (PcdTpmBaseAddress) + 0x40;\r
353 ControlArea = (EFI_TPM2_ACPI_CONTROL_AREA *)(UINTN)mTpm2AcpiTemplate.AddressOfControlArea;\r
354 ControlArea->CommandSize = 0xF80;\r
355 ControlArea->ResponseSize = 0xF80;\r
356 ControlArea->Command = PcdGet64 (PcdTpmBaseAddress) + 0x80;\r
357 ControlArea->Response = PcdGet64 (PcdTpmBaseAddress) + 0x80;\r
358 break;\r
359 case PtpInterfaceFifo:\r
360 case PtpInterfaceTis:\r
361 break;\r
362 default:\r
363 break;\r
364 }\r
365\r
1abfa4ce
JY
366 CopyMem (mTpm2AcpiTemplate.Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (mTpm2AcpiTemplate.Header.OemId));\r
367 OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);\r
368 CopyMem (&mTpm2AcpiTemplate.Header.OemTableId, &OemTableId, sizeof (UINT64));\r
369 mTpm2AcpiTemplate.Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision);\r
370 mTpm2AcpiTemplate.Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId);\r
371 mTpm2AcpiTemplate.Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);\r
372\r
373 //\r
374 // Construct ACPI table\r
375 //\r
376 Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTable);\r
377 ASSERT_EFI_ERROR (Status);\r
378\r
379 Status = AcpiTable->InstallAcpiTable (\r
380 AcpiTable,\r
381 &mTpm2AcpiTemplate,\r
382 sizeof(mTpm2AcpiTemplate),\r
383 &TableKey\r
384 );\r
385 ASSERT_EFI_ERROR (Status);\r
386\r
387 return Status;\r
388}\r
389\r
390/**\r
391 The driver's entry point.\r
392\r
393 It install callbacks for TPM physical presence and MemoryClear, and locate \r
394 SMM variable to be used in the callback function.\r
395\r
396 @param[in] ImageHandle The firmware allocated handle for the EFI image. \r
397 @param[in] SystemTable A pointer to the EFI System Table.\r
398 \r
399 @retval EFI_SUCCESS The entry point is executed successfully.\r
400 @retval Others Some error occurs when executing this entry point.\r
401\r
402**/\r
403EFI_STATUS\r
404EFIAPI\r
405InitializeTcgSmm (\r
406 IN EFI_HANDLE ImageHandle,\r
407 IN EFI_SYSTEM_TABLE *SystemTable\r
408 )\r
409{\r
410 EFI_STATUS Status;\r
411 EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;\r
412 EFI_SMM_SW_REGISTER_CONTEXT SwContext;\r
413 EFI_HANDLE SwHandle;\r
414\r
415 if (!CompareGuid (PcdGetPtr(PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceTpm20DtpmGuid)){\r
416 DEBUG ((EFI_D_ERROR, "No TPM2 DTPM instance required!\n"));\r
417 return EFI_UNSUPPORTED;\r
418 }\r
419\r
420 Status = PublishAcpiTable ();\r
421 ASSERT_EFI_ERROR (Status);\r
422\r
423 //\r
424 // Get the Sw dispatch protocol and register SMI callback functions.\r
425 //\r
426 Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID**)&SwDispatch);\r
427 ASSERT_EFI_ERROR (Status);\r
428 SwContext.SwSmiInputValue = (UINTN) -1;\r
429 Status = SwDispatch->Register (SwDispatch, PhysicalPresenceCallback, &SwContext, &SwHandle);\r
430 ASSERT_EFI_ERROR (Status);\r
431 if (EFI_ERROR (Status)) {\r
432 return Status;\r
433 }\r
434 mTcgNvs->PhysicalPresence.SoftwareSmi = (UINT8) SwContext.SwSmiInputValue;\r
435\r
436 SwContext.SwSmiInputValue = (UINTN) -1;\r
437 Status = SwDispatch->Register (SwDispatch, MemoryClearCallback, &SwContext, &SwHandle);\r
438 ASSERT_EFI_ERROR (Status);\r
439 if (EFI_ERROR (Status)) {\r
440 return Status;\r
441 }\r
442 mTcgNvs->MemoryClear.SoftwareSmi = (UINT8) SwContext.SwSmiInputValue;\r
443 \r
444 //\r
445 // Locate SmmVariableProtocol.\r
446 //\r
447 Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID**)&mSmmVariable);\r
448 ASSERT_EFI_ERROR (Status);\r
449\r
450 //\r
451 // Set TPM2 ACPI table\r
452 //\r
453 Status = PublishTpm2 ();\r
454 ASSERT_EFI_ERROR (Status);\r
455\r
456\r
457 return EFI_SUCCESS;\r
458}\r
459\r