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