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