1 /** @file
2   It updates TPM items in ACPI table and registers SMI callback
3   functions for physical presence and ClearMemory.
4 
5   Caution: This module requires additional review when modified.
6   This driver will have external input - variable and ACPINvs data in SMM mode.
7   This external input must be validated carefully to avoid security issue.
8 
9   PhysicalPresenceCallback() and MemoryClearCallback() will receive untrusted input and do some check.
10 
11 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
12 SPDX-License-Identifier: BSD-2-Clause-Patent
13 
14 **/
15 
16 #include "TcgSmm.h"
17 
18 EFI_SMM_VARIABLE_PROTOCOL  *mSmmVariable;
19 TCG_NVS                    *mTcgNvs;
20 
21 /**
22   Software SMI callback for TPM physical presence which is called from ACPI method.
23 
24   Caution: This function may receive untrusted input.
25   Variable and ACPINvs are external input, so this function will validate
26   its data structure to be valid value.
27 
28   @param[in]      DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
29   @param[in]      Context         Points to an optional handler context which was specified when the
30                                   handler was registered.
31   @param[in, out] CommBuffer      A pointer to a collection of data in memory that will
32                                   be conveyed from a non-SMM environment into an SMM environment.
33   @param[in, out] CommBufferSize  The size of the CommBuffer.
34 
35   @retval EFI_SUCCESS             The interrupt was handled successfully.
36 
37 **/
38 EFI_STATUS
39 EFIAPI
PhysicalPresenceCallback(IN EFI_HANDLE DispatchHandle,IN CONST VOID * Context,IN OUT VOID * CommBuffer,IN OUT UINTN * CommBufferSize)40 PhysicalPresenceCallback (
41   IN EFI_HANDLE                  DispatchHandle,
42   IN CONST VOID                  *Context,
43   IN OUT VOID                    *CommBuffer,
44   IN OUT UINTN                   *CommBufferSize
45   )
46 {
47   EFI_STATUS                     Status;
48   UINTN                          DataSize;
49   EFI_PHYSICAL_PRESENCE          PpData;
50   EFI_PHYSICAL_PRESENCE_FLAGS    Flags;
51   BOOLEAN                        RequestConfirmed;
52 
53   //
54   // Get the Physical Presence variable
55   //
56   DataSize = sizeof (EFI_PHYSICAL_PRESENCE);
57   Status = mSmmVariable->SmmGetVariable (
58                            PHYSICAL_PRESENCE_VARIABLE,
59                            &gEfiPhysicalPresenceGuid,
60                            NULL,
61                            &DataSize,
62                            &PpData
63                            );
64 
65   DEBUG ((EFI_D_INFO, "[TPM] PP callback, Parameter = %x\n", mTcgNvs->PhysicalPresence.Parameter));
66   if (mTcgNvs->PhysicalPresence.Parameter == ACPI_FUNCTION_RETURN_REQUEST_RESPONSE_TO_OS) {
67     if (EFI_ERROR (Status)) {
68       mTcgNvs->PhysicalPresence.ReturnCode  = PP_RETURN_TPM_OPERATION_RESPONSE_FAILURE;
69       mTcgNvs->PhysicalPresence.LastRequest = 0;
70       mTcgNvs->PhysicalPresence.Response    = 0;
71       DEBUG ((EFI_D_ERROR, "[TPM] Get PP variable failure! Status = %r\n", Status));
72       return EFI_SUCCESS;
73     }
74     mTcgNvs->PhysicalPresence.ReturnCode  = PP_RETURN_TPM_OPERATION_RESPONSE_SUCCESS;
75     mTcgNvs->PhysicalPresence.LastRequest = PpData.LastPPRequest;
76     mTcgNvs->PhysicalPresence.Response    = PpData.PPResponse;
77   } else if ((mTcgNvs->PhysicalPresence.Parameter == ACPI_FUNCTION_SUBMIT_REQUEST_TO_BIOS)
78           || (mTcgNvs->PhysicalPresence.Parameter == ACPI_FUNCTION_SUBMIT_REQUEST_TO_BIOS_2)) {
79     if (EFI_ERROR (Status)) {
80       mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_SUBMIT_REQUEST_TO_PREOS_GENERAL_FAILURE;
81       DEBUG ((EFI_D_ERROR, "[TPM] Get PP variable failure! Status = %r\n", Status));
82       return EFI_SUCCESS;
83     }
84     if (mTcgNvs->PhysicalPresence.Request == PHYSICAL_PRESENCE_SET_OPERATOR_AUTH) {
85       //
86       // This command requires UI to prompt user for Auth data.
87       //
88       mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_SUBMIT_REQUEST_TO_PREOS_NOT_IMPLEMENTED;
89       return EFI_SUCCESS;
90     }
91 
92     if (PpData.PPRequest != mTcgNvs->PhysicalPresence.Request) {
93       PpData.PPRequest = (UINT8) mTcgNvs->PhysicalPresence.Request;
94       DataSize = sizeof (EFI_PHYSICAL_PRESENCE);
95       Status = mSmmVariable->SmmSetVariable (
96                                PHYSICAL_PRESENCE_VARIABLE,
97                                &gEfiPhysicalPresenceGuid,
98                                EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
99                                DataSize,
100                                &PpData
101                                );
102     }
103 
104     if (EFI_ERROR (Status)) {
105       mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_SUBMIT_REQUEST_TO_PREOS_GENERAL_FAILURE;
106       return EFI_SUCCESS;
107     }
108     mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_SUBMIT_REQUEST_TO_PREOS_SUCCESS;
109 
110     if (mTcgNvs->PhysicalPresence.Request >= TCG_PHYSICAL_PRESENCE_VENDOR_SPECIFIC_OPERATION) {
111       DataSize = sizeof (EFI_PHYSICAL_PRESENCE_FLAGS);
112       Status = mSmmVariable->SmmGetVariable (
113                                PHYSICAL_PRESENCE_FLAGS_VARIABLE,
114                                &gEfiPhysicalPresenceGuid,
115                                NULL,
116                                &DataSize,
117                                &Flags
118                                );
119       if (EFI_ERROR (Status)) {
120         Flags.PPFlags = TCG_BIOS_TPM_MANAGEMENT_FLAG_NO_PPI_PROVISION;
121       }
122       mTcgNvs->PhysicalPresence.ReturnCode = TcgPpVendorLibSubmitRequestToPreOSFunction (mTcgNvs->PhysicalPresence.Request, Flags.PPFlags);
123     }
124   } else if (mTcgNvs->PhysicalPresence.Parameter == ACPI_FUNCTION_GET_USER_CONFIRMATION_STATUS_FOR_REQUEST) {
125     if (EFI_ERROR (Status)) {
126       mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_GET_USER_CONFIRMATION_BLOCKED_BY_BIOS_CONFIGURATION;
127       DEBUG ((EFI_D_ERROR, "[TPM] Get PP variable failure! Status = %r\n", Status));
128       return EFI_SUCCESS;
129     }
130     //
131     // Get the Physical Presence flags
132     //
133     DataSize = sizeof (EFI_PHYSICAL_PRESENCE_FLAGS);
134     Status = mSmmVariable->SmmGetVariable (
135                              PHYSICAL_PRESENCE_FLAGS_VARIABLE,
136                              &gEfiPhysicalPresenceGuid,
137                              NULL,
138                              &DataSize,
139                              &Flags
140                              );
141     if (EFI_ERROR (Status)) {
142       mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_GET_USER_CONFIRMATION_BLOCKED_BY_BIOS_CONFIGURATION;
143       DEBUG ((EFI_D_ERROR, "[TPM] Get PP flags failure! Status = %r\n", Status));
144       return EFI_SUCCESS;
145     }
146 
147     RequestConfirmed = FALSE;
148 
149     switch (mTcgNvs->PPRequestUserConfirm) {
150       case PHYSICAL_PRESENCE_ENABLE:
151       case PHYSICAL_PRESENCE_DISABLE:
152       case PHYSICAL_PRESENCE_ACTIVATE:
153       case PHYSICAL_PRESENCE_DEACTIVATE:
154       case PHYSICAL_PRESENCE_ENABLE_ACTIVATE:
155       case PHYSICAL_PRESENCE_DEACTIVATE_DISABLE:
156       case PHYSICAL_PRESENCE_SET_OWNER_INSTALL_TRUE:
157       case PHYSICAL_PRESENCE_SET_OWNER_INSTALL_FALSE:
158       case PHYSICAL_PRESENCE_ENABLE_ACTIVATE_OWNER_TRUE:
159       case PHYSICAL_PRESENCE_DEACTIVATE_DISABLE_OWNER_FALSE:
160         if ((Flags.PPFlags & TCG_BIOS_TPM_MANAGEMENT_FLAG_NO_PPI_PROVISION) != 0) {
161           RequestConfirmed = TRUE;
162         }
163         break;
164 
165       case PHYSICAL_PRESENCE_CLEAR:
166       case PHYSICAL_PRESENCE_ENABLE_ACTIVATE_CLEAR:
167         if ((Flags.PPFlags & TCG_BIOS_TPM_MANAGEMENT_FLAG_NO_PPI_CLEAR) != 0) {
168           RequestConfirmed = TRUE;
169         }
170         break;
171 
172       case PHYSICAL_PRESENCE_DEFERRED_PP_UNOWNERED_FIELD_UPGRADE:
173         if ((Flags.PPFlags & TCG_BIOS_TPM_MANAGEMENT_FLAG_NO_PPI_MAINTENANCE) != 0) {
174           RequestConfirmed = TRUE;
175         }
176         break;
177 
178       case PHYSICAL_PRESENCE_ENABLE_ACTIVATE_CLEAR_ENABLE_ACTIVATE:
179       case PHYSICAL_PRESENCE_CLEAR_ENABLE_ACTIVATE:
180         if ((Flags.PPFlags & TCG_BIOS_TPM_MANAGEMENT_FLAG_NO_PPI_CLEAR) != 0 && (Flags.PPFlags & TCG_BIOS_TPM_MANAGEMENT_FLAG_NO_PPI_PROVISION) != 0) {
181           RequestConfirmed = TRUE;
182         }
183         break;
184 
185       case PHYSICAL_PRESENCE_SET_NO_PPI_PROVISION_FALSE:
186       case PHYSICAL_PRESENCE_SET_NO_PPI_CLEAR_FALSE:
187       case PHYSICAL_PRESENCE_SET_NO_PPI_MAINTENANCE_FALSE:
188       case PHYSICAL_PRESENCE_NO_ACTION:
189         RequestConfirmed = TRUE;
190         break;
191 
192       case PHYSICAL_PRESENCE_SET_OPERATOR_AUTH:
193         //
194         // This command requires UI to prompt user for Auth data
195         //
196         mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_GET_USER_CONFIRMATION_NOT_IMPLEMENTED;
197         return EFI_SUCCESS;
198       default:
199         break;
200     }
201 
202     if (RequestConfirmed) {
203       mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_GET_USER_CONFIRMATION_ALLOWED_AND_PPUSER_NOT_REQUIRED;
204     } else {
205       mTcgNvs->PhysicalPresence.ReturnCode = TCG_PP_GET_USER_CONFIRMATION_ALLOWED_AND_PPUSER_REQUIRED;
206     }
207     if (mTcgNvs->PhysicalPresence.Request >= TCG_PHYSICAL_PRESENCE_VENDOR_SPECIFIC_OPERATION) {
208       mTcgNvs->PhysicalPresence.ReturnCode = TcgPpVendorLibGetUserConfirmationStatusFunction (mTcgNvs->PhysicalPresence.Request, Flags.PPFlags);
209     }
210   }
211 
212   return EFI_SUCCESS;
213 }
214 
215 
216 /**
217   Software SMI callback for MemoryClear which is called from ACPI method.
218 
219   Caution: This function may receive untrusted input.
220   Variable and ACPINvs are external input, so this function will validate
221   its data structure to be valid value.
222 
223   @param[in]      DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
224   @param[in]      Context         Points to an optional handler context which was specified when the
225                                   handler was registered.
226   @param[in, out] CommBuffer      A pointer to a collection of data in memory that will
227                                   be conveyed from a non-SMM environment into an SMM environment.
228   @param[in, out] CommBufferSize  The size of the CommBuffer.
229 
230   @retval EFI_SUCCESS             The interrupt was handled successfully.
231 
232 **/
233 EFI_STATUS
234 EFIAPI
MemoryClearCallback(IN EFI_HANDLE DispatchHandle,IN CONST VOID * Context,IN OUT VOID * CommBuffer,IN OUT UINTN * CommBufferSize)235 MemoryClearCallback (
236   IN EFI_HANDLE                  DispatchHandle,
237   IN CONST VOID                  *Context,
238   IN OUT VOID                    *CommBuffer,
239   IN OUT UINTN                   *CommBufferSize
240   )
241 {
242   EFI_STATUS                     Status;
243   UINTN                          DataSize;
244   UINT8                          MorControl;
245 
246   mTcgNvs->MemoryClear.ReturnCode = MOR_REQUEST_SUCCESS;
247   if (mTcgNvs->MemoryClear.Parameter == ACPI_FUNCTION_DSM_MEMORY_CLEAR_INTERFACE) {
248     MorControl = (UINT8) mTcgNvs->MemoryClear.Request;
249   } else if (mTcgNvs->MemoryClear.Parameter == ACPI_FUNCTION_PTS_CLEAR_MOR_BIT) {
250     DataSize = sizeof (UINT8);
251     Status = mSmmVariable->SmmGetVariable (
252                              MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
253                              &gEfiMemoryOverwriteControlDataGuid,
254                              NULL,
255                              &DataSize,
256                              &MorControl
257                              );
258     if (EFI_ERROR (Status)) {
259       mTcgNvs->MemoryClear.ReturnCode = MOR_REQUEST_GENERAL_FAILURE;
260       DEBUG ((EFI_D_ERROR, "[TPM] Get MOR variable failure! Status = %r\n", Status));
261       return EFI_SUCCESS;
262     }
263 
264     if (MOR_CLEAR_MEMORY_VALUE (MorControl) == 0x0) {
265       return EFI_SUCCESS;
266     }
267     MorControl &= ~MOR_CLEAR_MEMORY_BIT_MASK;
268   } else {
269     mTcgNvs->MemoryClear.ReturnCode = MOR_REQUEST_GENERAL_FAILURE;
270     DEBUG ((EFI_D_ERROR, "[TPM] MOR Parameter error! Parameter = %x\n", mTcgNvs->MemoryClear.Parameter));
271     return EFI_SUCCESS;
272   }
273 
274   DataSize = sizeof (UINT8);
275   Status = mSmmVariable->SmmSetVariable (
276                            MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
277                            &gEfiMemoryOverwriteControlDataGuid,
278                            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
279                            DataSize,
280                            &MorControl
281                            );
282   if (EFI_ERROR (Status)) {
283     mTcgNvs->MemoryClear.ReturnCode = MOR_REQUEST_GENERAL_FAILURE;
284     DEBUG ((EFI_D_ERROR, "[TPM] Set MOR variable failure! Status = %r\n", Status));
285   }
286 
287   return EFI_SUCCESS;
288 }
289 
290 /**
291   Find the operation region in TCG ACPI table by given Name and Size,
292   and initialize it if the region is found.
293 
294   @param[in, out] Table          The TPM item in ACPI table.
295   @param[in]      Name           The name string to find in TPM table.
296   @param[in]      Size           The size of the region to find.
297 
298   @return                        The allocated address for the found region.
299 
300 **/
301 VOID *
AssignOpRegion(EFI_ACPI_DESCRIPTION_HEADER * Table,UINT32 Name,UINT16 Size)302 AssignOpRegion (
303   EFI_ACPI_DESCRIPTION_HEADER    *Table,
304   UINT32                         Name,
305   UINT16                         Size
306   )
307 {
308   EFI_STATUS                     Status;
309   AML_OP_REGION_32_8             *OpRegion;
310   EFI_PHYSICAL_ADDRESS           MemoryAddress;
311 
312   MemoryAddress = SIZE_4GB - 1;
313 
314   //
315   // Patch some pointers for the ASL code before loading the SSDT.
316   //
317   for (OpRegion  = (AML_OP_REGION_32_8 *) (Table + 1);
318        OpRegion <= (AML_OP_REGION_32_8 *) ((UINT8 *) Table + Table->Length);
319        OpRegion  = (AML_OP_REGION_32_8 *) ((UINT8 *) OpRegion + 1)) {
320     if ((OpRegion->OpRegionOp  == AML_EXT_REGION_OP) &&
321         (OpRegion->NameString  == Name) &&
322         (OpRegion->DWordPrefix == AML_DWORD_PREFIX) &&
323         (OpRegion->BytePrefix  == AML_BYTE_PREFIX)) {
324 
325       Status = gBS->AllocatePages(AllocateMaxAddress, EfiACPIMemoryNVS, EFI_SIZE_TO_PAGES (Size), &MemoryAddress);
326       ASSERT_EFI_ERROR (Status);
327       ZeroMem ((VOID *)(UINTN)MemoryAddress, Size);
328       OpRegion->RegionOffset = (UINT32) (UINTN) MemoryAddress;
329       OpRegion->RegionLen    = (UINT8) Size;
330       break;
331     }
332   }
333 
334   return (VOID *) (UINTN) MemoryAddress;
335 }
336 
337 /**
338   Initialize and publish TPM items in ACPI table.
339 
340   @retval   EFI_SUCCESS     The TCG ACPI table is published successfully.
341   @retval   Others          The TCG ACPI table is not published.
342 
343 **/
344 EFI_STATUS
PublishAcpiTable(VOID)345 PublishAcpiTable (
346   VOID
347   )
348 {
349   EFI_STATUS                     Status;
350   EFI_ACPI_TABLE_PROTOCOL        *AcpiTable;
351   UINTN                          TableKey;
352   EFI_ACPI_DESCRIPTION_HEADER    *Table;
353   UINTN                          TableSize;
354 
355   Status = GetSectionFromFv (
356              &gEfiCallerIdGuid,
357              EFI_SECTION_RAW,
358              0,
359              (VOID **) &Table,
360              &TableSize
361              );
362   ASSERT_EFI_ERROR (Status);
363 
364 
365   //
366   // Measure to PCR[0] with event EV_POST_CODE ACPI DATA
367   //
368   TpmMeasureAndLogData(
369     0,
370     EV_POST_CODE,
371     EV_POSTCODE_INFO_ACPI_DATA,
372     ACPI_DATA_LEN,
373     Table,
374     TableSize
375     );
376 
377 
378   ASSERT (Table->OemTableId == SIGNATURE_64 ('T', 'c', 'g', 'T', 'a', 'b', 'l', 'e'));
379   CopyMem (Table->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (Table->OemId) );
380   mTcgNvs = AssignOpRegion (Table, SIGNATURE_32 ('T', 'N', 'V', 'S'), (UINT16) sizeof (TCG_NVS));
381   ASSERT (mTcgNvs != NULL);
382 
383   //
384   // Publish the TPM ACPI table
385   //
386   Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTable);
387   ASSERT_EFI_ERROR (Status);
388 
389   TableKey = 0;
390   Status = AcpiTable->InstallAcpiTable (
391                         AcpiTable,
392                         Table,
393                         TableSize,
394                         &TableKey
395                         );
396   ASSERT_EFI_ERROR (Status);
397 
398   return Status;
399 }
400 
401 /**
402   The driver's entry point.
403 
404   It install callbacks for TPM physical presence and MemoryClear, and locate
405   SMM variable to be used in the callback function.
406 
407   @param[in] ImageHandle  The firmware allocated handle for the EFI image.
408   @param[in] SystemTable  A pointer to the EFI System Table.
409 
410   @retval EFI_SUCCESS     The entry point is executed successfully.
411   @retval Others          Some error occurs when executing this entry point.
412 
413 **/
414 EFI_STATUS
415 EFIAPI
InitializeTcgSmm(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)416 InitializeTcgSmm (
417   IN EFI_HANDLE                  ImageHandle,
418   IN EFI_SYSTEM_TABLE            *SystemTable
419   )
420 {
421   EFI_STATUS                     Status;
422   EFI_SMM_SW_DISPATCH2_PROTOCOL  *SwDispatch;
423   EFI_SMM_SW_REGISTER_CONTEXT    SwContext;
424   EFI_HANDLE                     SwHandle;
425 
426   if (!CompareGuid (PcdGetPtr(PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceTpm12Guid)){
427     DEBUG ((EFI_D_ERROR, "No TPM12 instance required!\n"));
428     return EFI_UNSUPPORTED;
429   }
430 
431   Status = PublishAcpiTable ();
432   ASSERT_EFI_ERROR (Status);
433 
434   //
435   // Get the Sw dispatch protocol and register SMI callback functions.
436   //
437   Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID**)&SwDispatch);
438   ASSERT_EFI_ERROR (Status);
439   SwContext.SwSmiInputValue = (UINTN) -1;
440   Status = SwDispatch->Register (SwDispatch, PhysicalPresenceCallback, &SwContext, &SwHandle);
441   ASSERT_EFI_ERROR (Status);
442   if (EFI_ERROR (Status)) {
443     return Status;
444   }
445   mTcgNvs->PhysicalPresence.SoftwareSmi = (UINT8) SwContext.SwSmiInputValue;
446 
447   SwContext.SwSmiInputValue = (UINTN) -1;
448   Status = SwDispatch->Register (SwDispatch, MemoryClearCallback, &SwContext, &SwHandle);
449   ASSERT_EFI_ERROR (Status);
450   if (EFI_ERROR (Status)) {
451     return Status;
452   }
453   mTcgNvs->MemoryClear.SoftwareSmi = (UINT8) SwContext.SwSmiInputValue;
454 
455   //
456   // Locate SmmVariableProtocol.
457   //
458   Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID**)&mSmmVariable);
459   ASSERT_EFI_ERROR (Status);
460 
461   return EFI_SUCCESS;
462 }
463 
464