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