From f9e01d3ffc4af53b0403c7e93d731280d9bdde53 Mon Sep 17 00:00:00 2001 From: rdiaz Date: Thu, 11 Jun 2026 21:49:55 +0000 Subject: [PATCH 1/2] Revert "SecurityPkg: Add an assert to TCG log function if log is full (#257)" This reverts commit 348b4bd65e20263f3655ad9437550ffc5dd9951a. --- SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c | 1 - 1 file changed, 1 deletion(-) diff --git a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c index c9e292159e..d4b8941d92 100644 --- a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c +++ b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c @@ -894,7 +894,6 @@ TcgCommLogEvent ( DEBUG ((DEBUG_INFO, " NewLogSize - 0x%x\n", NewLogSize)); DEBUG ((DEBUG_INFO, " LogSize - 0x%x\n", EventLogAreaStruct->EventLogSize)); DEBUG ((DEBUG_INFO, "TcgCommLogEvent - %r\n", EFI_OUT_OF_RESOURCES)); - ASSERT (FALSE); // MU_CHANGE: Assert to catch systematic TCG log truncation during DEBUG testing. return EFI_OUT_OF_RESOURCES; } From ffa28218f6fe6423ddc9223f0a23791a2d21f270 Mon Sep 17 00:00:00 2001 From: rdiaz Date: Fri, 8 May 2026 18:49:25 +0000 Subject: [PATCH 2/2] SecurityPkg: Introduce Dynamic TCG Log Scaling Implemented dynamic TCG log scaling in Tcg2Dxe. When the log would become truncated it instead now dynamically scales doubling the size each time. An ERROR log is reported that an increase to your base log size should occur such that scaling is not necessary. This is a precaution against platforms that log a lot and the addition of new hashing algorithms for PQC. The log is allocated in BootServices memory. Tests were added via TcgLogTest which includes a DXE driver and a UEFI shell UnitTest app. The DXE driver handles pre-ReadyToBoot tests while the TestApp handles post-ReadyToBoot tests as well as gathering the test results from the DXE driver. Markdown documents were created to detail the changes. The dynamic scaling functionality removes setting the LAML/LASA in the ACPI table. Updated the ACPI code to fix an issue where the template was outdated and the revision was reporting V5 but the template was still using the V4 version of the Start Method specific parameters. Added the Truncation event marker to the end of the FinalEventLog when it becomes truncated. Added a event signal for when scaling occurs on the normal event log. Consumers can trigger callbacks on this event; the test app uses this to know when scaling occurs. Signed-off-by: Raymond Diaz --- MdePkg/Include/IndustryStandard/Tpm2Acpi.h | 71 ++- SecurityPkg/Include/Guid/Tcg2EventLogScaled.h | 20 + SecurityPkg/SecurityPkg.dec | 13 + SecurityPkg/SecurityPkg.dsc | 10 + SecurityPkg/Tcg/Tcg2Acpi/Tcg2Acpi.c | 88 ++- SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.c | 118 ++-- SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.inf | 4 + SecurityPkg/Tcg/Tcg2Dxe/README.md | 120 ++++ SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c | 392 ++++++++++++- SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf | 5 +- SecurityPkg/Tcg/TcgLogTest/README.md | 279 +++++++++ SecurityPkg/Tcg/TcgLogTest/TcgLogTest.h | 67 +++ SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.c | 544 ++++++++++++++++++ SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf | 46 ++ SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.c | 372 ++++++++++++ SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.h | 70 +++ SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.c | 261 +++++++++ SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf | 50 ++ 18 files changed, 2414 insertions(+), 116 deletions(-) create mode 100644 SecurityPkg/Include/Guid/Tcg2EventLogScaled.h create mode 100644 SecurityPkg/Tcg/Tcg2Dxe/README.md create mode 100644 SecurityPkg/Tcg/TcgLogTest/README.md create mode 100644 SecurityPkg/Tcg/TcgLogTest/TcgLogTest.h create mode 100644 SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.c create mode 100644 SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf create mode 100644 SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.c create mode 100644 SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.h create mode 100644 SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.c create mode 100644 SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf diff --git a/MdePkg/Include/IndustryStandard/Tpm2Acpi.h b/MdePkg/Include/IndustryStandard/Tpm2Acpi.h index 879a6058d7..4288dba2d9 100644 --- a/MdePkg/Include/IndustryStandard/Tpm2Acpi.h +++ b/MdePkg/Include/IndustryStandard/Tpm2Acpi.h @@ -24,19 +24,25 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #define EFI_TPM2_ACPI_TABLE_START_METHOD_SPECIFIC_PARAMETERS_MAX_SIZE_REVISION_5 16 #define EFI_TPM2_ACPI_TABLE_START_METHOD_SPECIFIC_PARAMETERS_MAX_SIZE EFI_TPM2_ACPI_TABLE_START_METHOD_SPECIFIC_PARAMETERS_MAX_SIZE_REVISION_5 -typedef struct { - EFI_ACPI_DESCRIPTION_HEADER Header; - // Flags field is replaced in version 4 and above - // BIT0~15: PlatformClass This field is only valid for version 4 and above - // BIT16~31: Reserved - UINT32 Flags; - UINT64 AddressOfControlArea; +// MU_CHANGE - [BEGIN] + +// Common fields shared across all TPM2 ACPI table revisions. +// Flags field is replaced in version 4 and above: +// BIT0~15: PlatformClass This field is only valid for version 4 and above +// BIT16~31: Reserved +// +#define EFI_TPM2_ACPI_TABLE_COMMON_FIELDS \ + EFI_ACPI_DESCRIPTION_HEADER Header; \ + UINT32 Flags; \ + UINT64 AddressOfControlArea; \ UINT32 StartMethod; - // UINT8 PlatformSpecificParameters[]; // size up to 16 - // UINT32 Laml; // Optional - // UINT64 Lasa; // Optional + +typedef struct { + EFI_TPM2_ACPI_TABLE_COMMON_FIELDS } EFI_TPM2_ACPI_TABLE; +// MU_CHANGE - [END] + #define EFI_TPM2_ACPI_TABLE_START_METHOD_ACPI 2 #define EFI_TPM2_ACPI_TABLE_START_METHOD_TIS 6 #define EFI_TPM2_ACPI_TABLE_START_METHOD_COMMAND_RESPONSE_BUFFER_INTERFACE 7 @@ -64,7 +70,8 @@ typedef struct { UINT32 Interrupt; UINT8 Flags; UINT8 OperationFlags; - UINT8 Reserved[2]; + UINT8 Attributes; // MU_CHANGE + UINT8 Reserved; // MU_CHANGE UINT32 SmcFunctionId; } EFI_TPM2_ACPI_START_METHOD_SPECIFIC_PARAMETERS_ARM_SMC; @@ -80,6 +87,48 @@ typedef struct { UINT8 Reserved[8]; } EFI_TPM2_ACPI_START_METHOD_SPECIFIC_PARAMETERS_ARM_FFA; +// MU_CHANGE - [BEGIN] + +typedef struct { + EFI_TPM2_ACPI_TABLE_COMMON_FIELDS + + // StartMethodSpecificParameters is variable in size and LAML/LASA are + // optional fields. It is the user's responsibility to access the + // Header.Length field to determine what is accessible in the table. + union { + UINT8 PlatformSpecificParameters[EFI_TPM2_ACPI_TABLE_START_METHOD_SPECIFIC_PARAMETERS_MAX_SIZE_REVISION_4]; + EFI_TPM2_ACPI_START_METHOD_SPECIFIC_PARAMETERS_ARM_SMC SmcParameters; + } StartMethodSpecificParameters; + + UINT32 Laml; // Optional + UINT64 Lasa; // Optional +} EFI_TPM2_ACPI_TABLE_V4; + +typedef struct { + EFI_TPM2_ACPI_TABLE_COMMON_FIELDS + + // StartMethodSpecificParameters is variable in size and LAML/LASA are + // optional fields. It is the user's responsibility to access the + // Header.Length field to determine what is accessible in the table. + union { + UINT8 PlatformSpecificParameters[EFI_TPM2_ACPI_TABLE_START_METHOD_SPECIFIC_PARAMETERS_MAX_SIZE_REVISION_5]; + EFI_TPM2_ACPI_START_METHOD_SPECIFIC_PARAMETERS_ARM_SMC SmcParameters; + EFI_TPM2_ACPI_START_METHOD_SPECIFIC_PARAMETERS_ARM_FFA FfaParameters; + } StartMethodSpecificParameters; + + UINT32 Laml; // Optional + UINT64 Lasa; // Optional +} EFI_TPM2_ACPI_TABLE_V5; + +typedef struct { + EFI_TPM2_ACPI_TABLE_COMMON_FIELDS + UINT8 PlatformSpecificParameters[EFI_TPM2_ACPI_TABLE_START_METHOD_SPECIFIC_PARAMETERS_MAX_SIZE]; + UINT32 Laml; // Optional + UINT64 Lasa; // Optional +} EFI_TPM2_ACPI_TABLE_TEMPLATE; + +// MU_CHANGE - [END] + #define EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_FLAG_NOTIFICATION_SUPPORT BIT0 #define EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_MEM_TYPE_MASK 0x3 diff --git a/SecurityPkg/Include/Guid/Tcg2EventLogScaled.h b/SecurityPkg/Include/Guid/Tcg2EventLogScaled.h new file mode 100644 index 0000000000..1b3fb81cbf --- /dev/null +++ b/SecurityPkg/Include/Guid/Tcg2EventLogScaled.h @@ -0,0 +1,20 @@ +/** @file + Defines the GUID used to signal that the TCG event log has been dynamically + scaled. Consumers may register a notification callback on this event group + to react to the scaling event. + + Copyright (c), Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef TCG2_EVENT_LOG_SCALED_H_ +#define TCG2_EVENT_LOG_SCALED_H_ + +#define TCG2_EVENT_LOG_SCALED_GUID \ + { \ + 0x9b4f7c2a, 0x1d3e, 0x4a8b, { 0x9c, 0x6f, 0x5e, 0x8d, 0x2a, 0x1b, 0x4c, 0x7f } \ + } + +extern EFI_GUID gTcg2EventLogScaledGuid; + +#endif // TCG2_EVENT_LOG_SCALED_H_ diff --git a/SecurityPkg/SecurityPkg.dec b/SecurityPkg/SecurityPkg.dec index 70d70da0e7..249187a5c0 100644 --- a/SecurityPkg/SecurityPkg.dec +++ b/SecurityPkg/SecurityPkg.dec @@ -202,6 +202,11 @@ ## Include/Guid/TcgEventHob.h gTcg800155PlatformIdEventHobGuid = { 0xe2c3bc69, 0x615c, 0x4b5b, { 0x8e, 0x5c, 0xa0, 0x33, 0xa9, 0xc2, 0x5e, 0xd6 }} + ## MU_CHANGE + ## GUID used to signal dynamic scaling of the TCG event log. + ## Include/Guid/Tcg2EventLogScaled.h + gTcg2EventLogScaledGuid = { 0x9b4f7c2a, 0x1d3e, 0x4a8b, { 0x9c, 0x6f, 0x5e, 0x8d, 0x2a, 0x1b, 0x4c, 0x7f }} + ## HOB GUID used to pass all PEI measured FV info to DXE Driver. # Include/Guid/MeasuredFvHob.h gMeasuredFvHobGuid = { 0xb2360b42, 0x7173, 0x420a, { 0x86, 0x96, 0x46, 0xca, 0x6b, 0xab, 0x10, 0x60 }} @@ -301,6 +306,14 @@ # Include/Protocol/MuTcg2Protocol.h gMuTcg2ProtocolExGuid = {0x227e7984, 0x1a77, 0x4762, { 0x96, 0x69, 0x57, 0x4c, 0xda, 0xd1, 0xa0, 0x1e }} ## MU_CHANGE - END - Add a new protocol to support Log-only events. + + ## MU_CHANGE - [BEGIN] + ## Protocol used to test dynamic TCG log scaling functionality. This is a private protocol with visibility to + ## only the TestApp and DXE driver. + # Tcg/TcgLogTest/TcgLogTest.h + gTcgLogTestProtocolGuid = {0xa3c12f80, 0x7d9e, 0x4b5a, { 0x91, 0xe4, 0x6c, 0xf8, 0x2d, 0xa1, 0xb7, 0x03 }} + ## MU_CHANGE - [END] + [Ppis] ## The PPI GUID for that TPM physical presence should be locked. # Include/Ppi/LockPhysicalPresence.h diff --git a/SecurityPkg/SecurityPkg.dsc b/SecurityPkg/SecurityPkg.dsc index 82dd0be714..9029fa7a82 100644 --- a/SecurityPkg/SecurityPkg.dsc +++ b/SecurityPkg/SecurityPkg.dsc @@ -249,6 +249,16 @@ SecurityPkg/Applications/TpmShellApp/TpmShellApp.inf ## MU_CHANGE + ## MU_CHANGE - [BEGIN] + SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf + SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf { + + UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf + UnitTestPersistenceLib|UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf + UnitTestResultReportLib|UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf + } + ## MU_CHANGE - [END] + # # TCG Storage. # diff --git a/SecurityPkg/Tcg/Tcg2Acpi/Tcg2Acpi.c b/SecurityPkg/Tcg/Tcg2Acpi/Tcg2Acpi.c index 87bd22cc93..87e9c0d202 100644 --- a/SecurityPkg/Tcg/Tcg2Acpi/Tcg2Acpi.c +++ b/SecurityPkg/Tcg/Tcg2Acpi/Tcg2Acpi.c @@ -74,7 +74,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent // #define MAX_PRS_INT_BUF_SIZE (15*4) -#pragma pack(1) +// MU_CHANGE - [BEGIN] + +#if 0 + + #pragma pack(1) typedef struct { EFI_ACPI_DESCRIPTION_HEADER Header; @@ -89,23 +93,13 @@ typedef struct { UINT64 Lasa; // Optional } EFI_TPM2_ACPI_TABLE_V4; -#pragma pack() + #pragma pack() -EFI_TPM2_ACPI_TABLE_V4 mTpm2AcpiTemplate = { - { - EFI_ACPI_5_0_TRUSTED_COMPUTING_PLATFORM_2_TABLE_SIGNATURE, - sizeof (mTpm2AcpiTemplate), - EFI_TPM2_ACPI_TABLE_REVISION, - // - // Compiler initializes the remaining bytes to 0 - // These fields should be filled in in production - // - }, - 0, // BIT0~15: PlatformClass - // BIT16~31: Reserved - 0, // Control Area - EFI_TPM2_ACPI_TABLE_START_METHOD_TIS, // StartMethod -}; +#endif + +EFI_TPM2_ACPI_TABLE_TEMPLATE mTpm2AcpiTemplate; + +// MU_CHANGE - [END] TCG_NVS *mTcgNvs; @@ -793,6 +787,8 @@ PublishTpm2 ( UINT64 OemTableId; EFI_TPM2_ACPI_CONTROL_AREA *ControlArea; TPM2_PTP_INTERFACE_TYPE InterfaceType; + EFI_TPM2_ACPI_TABLE_V4 *Tpm2AcpiTableV4; // MU_CHANGE + EFI_TPM2_ACPI_TABLE_V5 *Tpm2AcpiTableV5; // MU_CHANGE // // Measure to PCR[0] with event EV_POST_CODE ACPI DATA. @@ -810,13 +806,14 @@ PublishTpm2 ( EV_POSTCODE_INFO_ACPI_DATA, ACPI_DATA_LEN, &mTpm2AcpiTemplate, - mTpm2AcpiTemplate.Header.Length + sizeof (EFI_TPM2_ACPI_TABLE_TEMPLATE) ); } // MU_CHANGE [END] - mTpm2AcpiTemplate.Header.Revision = PcdGet8 (PcdTpm2AcpiTableRev); + mTpm2AcpiTemplate.Header.Signature = EFI_ACPI_5_0_TRUSTED_COMPUTING_PLATFORM_2_TABLE_SIGNATURE; // MU_CHANGE + mTpm2AcpiTemplate.Header.Revision = PcdGet8 (PcdTpm2AcpiTableRev); // MU_CHANGE DEBUG ((DEBUG_INFO, "Tpm2 ACPI table revision is %d\n", mTpm2AcpiTemplate.Header.Revision)); // @@ -829,17 +826,50 @@ PublishTpm2 ( DEBUG ((DEBUG_INFO, "Tpm2 ACPI table PlatformClass is %d\n", (mTpm2AcpiTemplate.Flags & 0x0000FFFF))); } - mTpm2AcpiTemplate.Laml = PcdGet32 (PcdTpm2AcpiTableLaml); - mTpm2AcpiTemplate.Lasa = PcdGet64 (PcdTpm2AcpiTableLasa); - if ((mTpm2AcpiTemplate.Header.Revision < EFI_TPM2_ACPI_TABLE_REVISION_4) || - (mTpm2AcpiTemplate.Laml == 0) || (mTpm2AcpiTemplate.Lasa == 0)) - { - // - // If version is smaller than 4 or Laml/Lasa is not valid, rollback to original Length. - // - mTpm2AcpiTemplate.Header.Length = sizeof (EFI_TPM2_ACPI_TABLE); + // MU_CHANGE - [BEGIN] + + switch (mTpm2AcpiTemplate.Header.Revision) { + case EFI_TPM2_ACPI_TABLE_REVISION_3: + mTpm2AcpiTemplate.Header.Length = sizeof (EFI_TPM2_ACPI_TABLE); + break; + + case EFI_TPM2_ACPI_TABLE_REVISION_4: + mTpm2AcpiTemplate.Header.Length = sizeof (EFI_TPM2_ACPI_TABLE_V4); + Tpm2AcpiTableV4 = (EFI_TPM2_ACPI_TABLE_V4 *)&mTpm2AcpiTemplate; + Tpm2AcpiTableV4->Laml = PcdGet32 (PcdTpm2AcpiTableLaml); + Tpm2AcpiTableV4->Lasa = PcdGet64 (PcdTpm2AcpiTableLasa); + + if ((Tpm2AcpiTableV4->Laml == 0) || (Tpm2AcpiTableV4->Lasa == 0)) { + // Remove LAML/LASA from the length if either is 0. + mTpm2AcpiTemplate.Header.Length -= (sizeof (UINT32) + sizeof (UINT64)); + } + + break; + + case EFI_TPM2_ACPI_TABLE_REVISION_5: + mTpm2AcpiTemplate.Header.Length = sizeof (EFI_TPM2_ACPI_TABLE_V5); + Tpm2AcpiTableV5 = (EFI_TPM2_ACPI_TABLE_V5 *)&mTpm2AcpiTemplate; + Tpm2AcpiTableV5->Laml = PcdGet32 (PcdTpm2AcpiTableLaml); + Tpm2AcpiTableV5->Lasa = PcdGet64 (PcdTpm2AcpiTableLasa); + + if ((Tpm2AcpiTableV5->Laml == 0) || (Tpm2AcpiTableV5->Lasa == 0)) { + // Remove LAML/LASA from the length if either is 0. + mTpm2AcpiTemplate.Header.Length -= (sizeof (UINT32) + sizeof (UINT64)); + } + + break; + + default: + mTpm2AcpiTemplate.Header.Length = sizeof (EFI_TPM2_ACPI_TABLE_TEMPLATE); + DEBUG ((DEBUG_ERROR, "TPM2 revision get error! %d\n", mTpm2AcpiTemplate.Header.Revision)); + ASSERT (FALSE); + break; } + DEBUG ((DEBUG_INFO, "Tpm2 ACPI table size %d\n", mTpm2AcpiTemplate.Header.Length)); + + // MU_CHANGE - [END] + InterfaceType = PcdGet8 (PcdActiveTpmInterfaceType); switch (InterfaceType) { case Tpm2PtpInterfaceCrb: @@ -853,9 +883,11 @@ PublishTpm2 ( break; case Tpm2PtpInterfaceFifo: case Tpm2PtpInterfaceTis: + mTpm2AcpiTemplate.StartMethod = EFI_TPM2_ACPI_TABLE_START_METHOD_TIS; // MU_CHANGE break; default: DEBUG ((DEBUG_ERROR, "TPM2 InterfaceType get error! %d\n", InterfaceType)); + ASSERT (FALSE); // MU_CHANGE break; } diff --git a/SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.c b/SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.c index c3ae72e622..4eebc73e9f 100644 --- a/SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.c +++ b/SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.c @@ -66,7 +66,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent // #define MAX_PRS_INT_BUF_SIZE (15*4) -#pragma pack(1) +// MU_CHANGE - [BEGIN] + +#if 0 + + #pragma pack(1) typedef struct { EFI_ACPI_DESCRIPTION_HEADER Header; @@ -81,23 +85,13 @@ typedef struct { UINT64 Lasa; // Optional } EFI_TPM2_ACPI_TABLE_V5; -#pragma pack() + #pragma pack() -EFI_TPM2_ACPI_TABLE_V5 mTpm2AcpiTemplate = { - { - EFI_ACPI_5_0_TRUSTED_COMPUTING_PLATFORM_2_TABLE_SIGNATURE, - sizeof (mTpm2AcpiTemplate), - EFI_TPM2_ACPI_TABLE_REVISION, - // - // Compiler initializes the remaining bytes to 0 - // These fields should be filled in in production - // - }, - 0, // BIT0~15: PlatformClass - // BIT16~31: Reserved - 0, // Control Area - EFI_TPM2_ACPI_TABLE_START_METHOD_TIS, // StartMethod -}; +#endif + +EFI_TPM2_ACPI_TABLE_TEMPLATE mTpm2AcpiTemplate; + +// MU_CHANGE - [END] /** Patch version string of Physical Presence interface supported by platform. The initial string tag in TPM @@ -340,7 +334,7 @@ PublishTpm2 ( UINT64 OemTableId; EFI_TPM2_ACPI_CONTROL_AREA *ControlArea; TPM2_PTP_INTERFACE_TYPE InterfaceType; - UINT64 PartitionId; + EFI_TPM2_ACPI_TABLE_V5 *Tpm2AcpiTableV5; // MU_CHANGE STATIC_ASSERT ((FixedPcdGet64 (PcdTpmMaxAddress) - FixedPcdGet64 (PcdTpmBaseAddress)) == (FixedPcdGet32 (PcdTpmCrbRegionSize) - 1), "TPM CRB region size mismatch"); @@ -350,60 +344,70 @@ PublishTpm2 ( // Otherwise, the PCR record would be different after event log update // or the PCD configuration change. // - TpmMeasureAndLogData ( - 0, - EV_POST_CODE, - EV_POSTCODE_INFO_ACPI_DATA, - ACPI_DATA_LEN, - &mTpm2AcpiTemplate, - mTpm2AcpiTemplate.Header.Length - ); + // MU_CHANGE [BEGIN] + // Allow a platform to drop TCG ACPI measurements until we have a chance to make them more + // consistent and functional. + if (!FixedPcdGetBool (PcdSkipTcgSmmAcpiMeasurements)) { + TpmMeasureAndLogData ( + 0, + EV_POST_CODE, + EV_POSTCODE_INFO_ACPI_DATA, + ACPI_DATA_LEN, + &mTpm2AcpiTemplate, + sizeof (EFI_TPM2_ACPI_TABLE_TEMPLATE) + ); + } - mTpm2AcpiTemplate.Header.Revision = PcdGet8 (PcdTpm2AcpiTableRev); + mTpm2AcpiTemplate.Header.Signature = EFI_ACPI_5_0_TRUSTED_COMPUTING_PLATFORM_2_TABLE_SIGNATURE; + mTpm2AcpiTemplate.Header.Length = sizeof (EFI_TPM2_ACPI_TABLE_V5); + mTpm2AcpiTemplate.Header.Revision = PcdGet8 (PcdTpm2AcpiTableRev); DEBUG ((DEBUG_INFO, "Tpm2 ACPI table revision is %d\n", mTpm2AcpiTemplate.Header.Revision)); + // FF-A is only supported in revisions 5 and up. if (mTpm2AcpiTemplate.Header.Revision < EFI_TPM2_ACPI_TABLE_REVISION_5) { - DEBUG ((DEBUG_ERROR, "%a The minimum revision supported for TPM over FFA table is 5, not %d.\n", __func__, mTpm2AcpiTemplate.Header.Revision)); + DEBUG ((DEBUG_ERROR, "The minimum revision supported for TPM over FF-A table is 5\n")); ASSERT (FALSE); return EFI_UNSUPPORTED; } - mTpm2AcpiTemplate.Flags = (mTpm2AcpiTemplate.Flags & 0xFFFF0000) | PcdGet8 (PcdTpmPlatformClass); - DEBUG ((DEBUG_INFO, "Tpm2 ACPI table PlatformClass is %d\n", (mTpm2AcpiTemplate.Flags & 0x0000FFFF))); - - mTpm2AcpiTemplate.Laml = PcdGet32 (PcdTpm2AcpiTableLaml); - mTpm2AcpiTemplate.Lasa = PcdGet64 (PcdTpm2AcpiTableLasa); - if ((mTpm2AcpiTemplate.Laml == 0) || (mTpm2AcpiTemplate.Lasa == 0)) { - // - // If version is smaller than 4 or Laml/Lasa is not valid, rollback to original Length. - // - mTpm2AcpiTemplate.Header.Length = sizeof (EFI_TPM2_ACPI_TABLE); - } - + // CRB over FF-A only supports the CRB interface type. InterfaceType = PcdGet8 (PcdActiveTpmInterfaceType); DEBUG ((DEBUG_INFO, "Tpm Active Interface Type %d\n", InterfaceType)); - - PartitionId = PcdGet16 (PcdTpmServiceFfaPartitionId); - ASSERT (PartitionId != 0); - if (InterfaceType == Tpm2PtpInterfaceCrb) { - mTpm2AcpiTemplate.StartMethod = EFI_TPM2_ACPI_TABLE_START_METHOD_COMMAND_RESPONSE_BUFFER_INTERFACE_WITH_FFA; - mTpm2AcpiTemplate.AddressOfControlArea = PcdGet64 (PcdTpmBaseAddress) + 0x40; - mTpm2AcpiTemplate.FfaParameters.Flags = 0x00; // Notifications Not Supported - mTpm2AcpiTemplate.FfaParameters.Attributes = (EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_CRB_REGION_SIZE_4KB << EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_CRB_REGION_SIZE_SHIFT) | - (EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_MEM_TYPE_NOT_CACHABLE << EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_MEM_TYPE_SHIFT); - mTpm2AcpiTemplate.FfaParameters.PartitionId = PartitionId; // Partition ID - ControlArea = (EFI_TPM2_ACPI_CONTROL_AREA *)(UINTN)mTpm2AcpiTemplate.AddressOfControlArea; - ControlArea->CommandSize = 0xF80; - ControlArea->ResponseSize = 0xF80; - ControlArea->Command = PcdGet64 (PcdTpmBaseAddress) + 0x80; - ControlArea->Response = PcdGet64 (PcdTpmBaseAddress) + 0x80; - } else { - DEBUG ((DEBUG_ERROR, "TPM2 InterfaceType get error! %d\n", InterfaceType)); + if (InterfaceType != Tpm2PtpInterfaceCrb) { + DEBUG ((DEBUG_ERROR, "TPM over FF-A only supports CRB interface\n")); return EFI_UNSUPPORTED; } + mTpm2AcpiTemplate.Flags = (mTpm2AcpiTemplate.Flags & 0xFFFF0000) | PcdGet8 (PcdTpmPlatformClass); + DEBUG ((DEBUG_INFO, "Tpm2 ACPI table PlatformClass is %d\n", (mTpm2AcpiTemplate.Flags & 0x0000FFFF))); + + Tpm2AcpiTableV5 = (EFI_TPM2_ACPI_TABLE_V5 *)&mTpm2AcpiTemplate; + Tpm2AcpiTableV5->Laml = PcdGet32 (PcdTpm2AcpiTableLaml); + Tpm2AcpiTableV5->Lasa = PcdGet64 (PcdTpm2AcpiTableLasa); + if ((Tpm2AcpiTableV5->Laml == 0) || (Tpm2AcpiTableV5->Lasa == 0)) { + // Remove LAML/LASA from the length if either is 0. + mTpm2AcpiTemplate.Header.Length -= (sizeof (UINT32) + sizeof (UINT64)); + } + DEBUG ((DEBUG_INFO, "Tpm2 ACPI table size %d\n", mTpm2AcpiTemplate.Header.Length)); + mTpm2AcpiTemplate.StartMethod = EFI_TPM2_ACPI_TABLE_START_METHOD_COMMAND_RESPONSE_BUFFER_INTERFACE_WITH_FFA; + mTpm2AcpiTemplate.AddressOfControlArea = PcdGet64 (PcdTpmBaseAddress) + 0x40; + ControlArea = (EFI_TPM2_ACPI_CONTROL_AREA *)(UINTN)mTpm2AcpiTemplate.AddressOfControlArea; + ControlArea->CommandSize = 0xF80; + ControlArea->ResponseSize = 0xF80; + ControlArea->Command = PcdGet64 (PcdTpmBaseAddress) + 0x80; + ControlArea->Response = PcdGet64 (PcdTpmBaseAddress) + 0x80; + + // Set the FF-A specific parameters. + Tpm2AcpiTableV5->StartMethodSpecificParameters.FfaParameters.Flags = 0x00; // Notifications Not Supported + Tpm2AcpiTableV5->StartMethodSpecificParameters.FfaParameters.Attributes = (EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_CRB_REGION_SIZE_4KB << EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_CRB_REGION_SIZE_SHIFT) | + (EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_MEM_TYPE_NOT_CACHABLE << EFI_TPM2_ACPI_TABLE_ARM_FFA_PARAMETER_ATTR_MEM_TYPE_SHIFT); + Tpm2AcpiTableV5->StartMethodSpecificParameters.FfaParameters.PartitionId = PcdGet16 (PcdTpmServiceFfaPartitionId); + ASSERT (Tpm2AcpiTableV5->StartMethodSpecificParameters.FfaParameters.PartitionId != 0); + + // MU_CHANGE [END] + CopyMem (mTpm2AcpiTemplate.Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (mTpm2AcpiTemplate.Header.OemId)); OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId); CopyMem (&mTpm2AcpiTemplate.Header.OemTableId, &OemTableId, sizeof (UINT64)); diff --git a/SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.inf b/SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.inf index 5d2e80af90..75309c94f2 100644 --- a/SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.inf +++ b/SecurityPkg/Tcg/Tcg2AcpiFfa/Tcg2AcpiFfa.inf @@ -66,6 +66,10 @@ gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableLaml ## CONSUMES gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableLasa ## CONSUMES gEfiSecurityPkgTokenSpaceGuid.PcdTpmServiceFfaPartitionId ## CONSUMES + ## MU_CHANGE + # Allow a platform to drop TCG ACPI measurements until we have a chance to make them more + # consistent and functional. + gEfiSecurityPkgTokenSpaceGuid.PcdSkipTcgSmmAcpiMeasurements ## CONSUMES [FixedPcd] gEfiSecurityPkgTokenSpaceGuid.PcdTpmBaseAddress ## CONSUMES diff --git a/SecurityPkg/Tcg/Tcg2Dxe/README.md b/SecurityPkg/Tcg/Tcg2Dxe/README.md new file mode 100644 index 0000000000..581c695697 --- /dev/null +++ b/SecurityPkg/Tcg/Tcg2Dxe/README.md @@ -0,0 +1,120 @@ +# Tcg2Dxe + +Tcg2Dxe is a DXE-phase UEFI driver that publishes the TCG2 protocol defined +by the [TCG EFI Protocol Specification](https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/). +Its main responsibilites are to expose a standard interface to a TPM device, +measure components and events into PCRs, support measured boot, and enable +secure boot attestation. + +## Dynamic Event Log Scaling + +The TCG event log is initially allocated with a fixed size defined by a +PCD: PcdTcgLogAreaMinLen. As firmware components log measured boot +events the log fills up. Traditionally, when the log is full, subsequent events +are dropped and the log is marked as truncated. + +Tcg2Dxe extends this behavior with **dynamic scaling**: when the log is about +to overflow, the driver doubles its allocation, copies the existing log into +the new buffer, and frees the old one. This allows the log to grow as needed +and avoids losing events. + +### How It Works + +1. **Scaling check** — Before logging a TCG 2.0 event, + `TcgLogDynamicScalingNeeded` calculates whether the new event would exceed + the current allocation (`EventLogAreaStruct->Laml`). + +2. **Reallocation** — When scaling is needed, `TcgScaleEventLog` allocates a + new `EfiBootServicesData` region at twice the current size, copies the + existing log, updates the `Lasa`/`Laml` fields in the event log area + struct, and frees the old region. + +3. **Logging** — After scaling, the new event is logged into the resized buffer + via `TcgDxeLogEvent` inside a TPL-raised critical section. + +### NormalEventLog vs. FinalEventLog + +Tcg2Dxe maintains two distinct event log regions: + +| Log | Memory Type | Lifetime | Can Scale | +| --- | ----------- | -------- | --------- | +| **Normal log** | `EfiBootServicesData` | Available until `ExitBootServices` | Yes | +| **Final Events log** | `EfiACPIMemoryNVS` | Persistent | No | + +- The **Normal log** is the main log copy which is returned via `GetEventLog`. + It can grow dynamically via scaling. Note that previous calls to `GetEventLog` + could contain stale data if the log was scaled after. It is recommended to + call `GetEventLog` each time access is required. +- The **Final Events log** (`EFI_TCG2_FINAL_EVENTS_TABLE`) records events + logged after `GetEventLog` has been called. It is installed as a UEFI + configuration table so the OS can discover events that occurred between its + call to `GetEventLog` and `ExitBootServices`. Because the **Final Events log** + does not scale, it can become truncated. + +### Scale Limit + +The number of times the normal event log region may be dynamically scaled is +capped by `TCG_EVENT_LOG_MAX_SCALE_COUNT`. Each successful scale doubles the +allocation, so this caps total growth at `PcdTcgLogAreaMinLen << TCG_EVENT_LOG_MAX_SCALE_COUNT`. +Once the limit is reached: + +1. `TcgScaleEventLog` refuses to scale further and returns + `EFI_OUT_OF_RESOURCES`. +2. `EventLogAreaStruct->EventLogTruncated` is set to `TRUE`, so subsequent + `GetEventLog` callers see `EventLogTruncated == TRUE`. +3. `HashLogExtendEvent` returns `EFI_VOLUME_FULL` for events that would have + triggered the refused scale. + +### Scaling Notification (`gTcg2EventLogScaledGuid`) + +Each time the normal log is successfully resized, `TcgScaleEventLog` calls +`EfiEventGroupSignal (&gTcg2EventLogScaledGuid)` to notify interested parties +that the log moved in memory. + +Consumers that cache the log base address returned by `GetEventLog` (for +example, parsers walking the log incrementally) must invalidate their cache +on this signal and call `GetEventLog` again to get the current `Lasa`/last +entry. A typical consumer: + +```c +gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + OnTcgEventLogScaled, + Context, + &gTcg2EventLogScaledGuid, + &Event + ); +``` + +The event is declared in `gTcg2EventLogScaledGuid` (see +`SecurityPkg/Include/Guid/Tcg2EventLogScaled.h`) and listed in `Tcg2Dxe.inf`. + +## FinalEventLog Truncation Marker + +Because the **FinalEventLog** is fixed-size and cannot scale, it can fill +up before `ExitBootServices`. When the next event would overflow the log, +`TcgDxeLogEvent` calls `AppendTruncationMarker` which writes a final +`EV_NO_ACTION` event whose payload is the ASCII string +`TCG_LOG_TRUNCATION_EVENT_STRING`. The `NumberOfEvents` counter in +`EFI_TCG2_FINAL_EVENTS_TABLE` is incremented to include the marker, and +`EventLogTruncated` is set so subsequent attempts return `EFI_VOLUME_FULL` +without re-appending the marker. + +To guarantee the marker always fits, FinalEventLog initialization in +`SetupEventLog` subtracts `GetTruncationEventSize()` from the usable `Laml`: + +```c +mTcgDxeData.FinalEventLogAreaStruct[Index].Laml = + PcdGet32 (PcdTcg2FinalLogAreaLen) + - sizeof (EFI_TCG2_FINAL_EVENTS_TABLE) + - GetTruncationEventSize (); +``` + +`AppendTruncationMarker` temporarily restores this reserved space so +`TcgCommLogEvent` will accept the marker write. + +OS-side and pre-OS consumers can detect FinalEventLog truncation by: + +- walking `EFI_TCG2_FINAL_EVENTS_TABLE` and inspecting the last entry for an + `EV_NO_ACTION` event whose payload begins with `"TCG Event Log Truncated"`. diff --git a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c index d4b8941d92..600411cbb0 100644 --- a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c +++ b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c @@ -11,10 +11,12 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include // MU_CHANGE #include #include #include +#include // MU_CHANGE #include #include #include @@ -76,6 +78,17 @@ TCG2_EVENT_INFO_STRUCT mTcg2EventInfo[] = { #define TCG_EVENT_LOG_AREA_COUNT_MAX 2 +// MU_CHANGE - [BEGIN] + +// Maximum number of times the TCG event log can be dynamically scaled +// before the log is considered truncated. +#define TCG_EVENT_LOG_MAX_SCALE_COUNT 4 + +// Payload written as the last event of the FinalEventLog when it becomes truncated. +#define TCG_LOG_TRUNCATION_EVENT_STRING "TCG Event Log Truncated" + +// MU_CHANGE - [END] + typedef struct { EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat; EFI_PHYSICAL_ADDRESS Lasa; @@ -85,6 +98,7 @@ typedef struct { BOOLEAN EventLogStarted; BOOLEAN EventLogTruncated; UINTN Next800155EventOffset; + UINTN ScaleCount; // MU_CHANGE } TCG_EVENT_LOG_AREA_STRUCT; // Mapping of TPM return status to BIOS/OS TPM support and related flags (TPMPresentFlag, TpmUpdateFlag) @@ -132,7 +146,12 @@ VARIABLE_TYPE mVariableType[] = { { EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid }, }; +// MU_CHANGE - [BEGIN] + EFI_HANDLE mImageHandle; +BOOLEAN mReadyToBoot = FALSE; + +// MU_CHANGE - [END] /** Measure PE image into TPM log based on the authenticode image hashing in @@ -941,6 +960,130 @@ TcgCommLogEvent ( return EFI_SUCCESS; } +// MU_CHANGE - [BEGIN] + +/** + Get TPML_DIGEST_VALUES compact binary buffer size. + + @param[in] DigestListBin TPML_DIGEST_VALUES compact binary buffer. + + @return TPML_DIGEST_VALUES compact binary buffer size. +**/ +UINT32 +GetDigestListBinSize ( + IN VOID *DigestListBin + ) +{ + UINTN Index; + UINT16 DigestSize; + UINT32 TotalSize; + UINT32 Count; + TPMI_ALG_HASH HashAlg; + + Count = ReadUnaligned32 (DigestListBin); + TotalSize = sizeof (Count); + DigestListBin = (UINT8 *)DigestListBin + sizeof (Count); + for (Index = 0; Index < Count; Index++) { + HashAlg = ReadUnaligned16 (DigestListBin); + TotalSize += sizeof (HashAlg); + DigestListBin = (UINT8 *)DigestListBin + sizeof (HashAlg); + + DigestSize = GetHashSizeFromAlgo (HashAlg); + TotalSize += DigestSize; + DigestListBin = (UINT8 *)DigestListBin + DigestSize; + } + + return TotalSize; +} + +/** + Build the EV_NO_ACTION truncation marker event. + + @param[out] EventHdr TCG_PCR_EVENT2 header initialized for the + truncation marker. + @param[out] EventHdrSize Size in bytes of the serialized header + (excluding the payload string). +**/ +STATIC +VOID +BuildTruncationEvent ( + OUT TCG_PCR_EVENT2_HDR *EventHdr, + OUT UINT32 *EventHdrSize + ) +{ + InitNoActionEvent (EventHdr, sizeof (TCG_LOG_TRUNCATION_EVENT_STRING)); + *EventHdrSize = (UINT32)(sizeof (EventHdr->PCRIndex) + + sizeof (EventHdr->EventType) + + GetDigestListBinSize (&EventHdr->Digests) + + sizeof (EventHdr->EventSize)); +} + +/** + Compute the total size in bytes of the EV_NO_ACTION truncation marker event + (header for the active PCR banks + truncation payload). + + Used to reserve headroom in the Final Events log so the marker is always + guaranteed to fit when truncation occurs. + + @return Size in bytes of the truncation marker event. +**/ +STATIC +UINTN +GetTruncationEventSize ( + VOID + ) +{ + TCG_PCR_EVENT2_HDR NoActionEvent; + UINT32 EventHdrSize; + + BuildTruncationEvent (&NoActionEvent, &EventHdrSize); + + return EventHdrSize + sizeof (TCG_LOG_TRUNCATION_EVENT_STRING); +} + +/** + Append an EV_NO_ACTION truncation marker as the final entry of the Final + Events log. The marker is written into the headroom reserved at log + initialization (see Final log Laml setup in SetupEventLog) so it is always + guaranteed to fit even when the log is otherwise full. + + @param[in,out] EventLogAreaStruct Final Events log area. + + @retval EFI_SUCCESS The truncation marker was logged. + @retval Other TcgCommLogEvent failed; nothing was logged. +**/ +STATIC +EFI_STATUS +AppendTruncationMarker ( + IN OUT TCG_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct + ) +{ + EFI_STATUS Status; + TCG_PCR_EVENT2_HDR TruncationHdr; + UINT32 TruncationHdrSize; + + BuildTruncationEvent (&TruncationHdr, &TruncationHdrSize); + + // Restore the reserved space so TcgCommLogEvent accepts the marker. + EventLogAreaStruct->Laml += TruncationHdrSize + sizeof (TCG_LOG_TRUNCATION_EVENT_STRING); + + Status = TcgCommLogEvent ( + EventLogAreaStruct, + &TruncationHdr, + TruncationHdrSize, + (UINT8 *)TCG_LOG_TRUNCATION_EVENT_STRING, + sizeof (TCG_LOG_TRUNCATION_EVENT_STRING) + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to log truncation marker - %r\n", __func__, Status)); + } + + return Status; +} + +// MU_CHANGE - [END] + /** Add a new entry to the Event Log. @@ -1026,6 +1169,15 @@ TcgDxeLogEvent ( NewEventSize ); if (Status == EFI_OUT_OF_RESOURCES) { + // MU_CHANGE - [BEGIN] + + Status = AppendTruncationMarker (EventLogAreaStruct); + if (!EFI_ERROR (Status)) { + (mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents++; + } + + // MU_CHANGE - [END] + EventLogAreaStruct->EventLogTruncated = TRUE; return EFI_VOLUME_FULL; } else if (Status == EFI_SUCCESS) { @@ -1042,6 +1194,10 @@ TcgDxeLogEvent ( return Status; } +// MU_CHANGE - [BEGIN] + +#if 0 + /** Get TPML_DIGEST_VALUES compact binary buffer size. @@ -1076,6 +1232,10 @@ GetDigestListBinSize ( return TotalSize; } +#endif + +// MU_CHANGE - [END] + /** Copy TPML_DIGEST_VALUES compact binary into a buffer @@ -1132,6 +1292,159 @@ CopyDigestListBinToBuffer ( return Buffer; } +// MU_CHANGE - [BEGIN] + +/** + Dynamically scale the TCG event log, this should only occur when the + log is filled/truncated. + + @param[in, out] EventLogAreaStruct The event log area data structure. + + @retval EFI_SUCCESS Log was successfully scaled. + @retval EFI_OUT_OF_RESOURCES Allocation failed. + @retval EFI_VOLUME_FULL EventLog truncated. + +**/ +STATIC +EFI_STATUS +TcgScaleEventLog ( + IN OUT TCG_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS NewLasa; + UINT64 NewLaml; + EFI_PHYSICAL_ADDRESS OldLasa; + UINT64 OldLaml; + EFI_TPL OldTpl; + + // Make sure EventLogAreaStruct is valid. + if (EventLogAreaStruct == NULL) { + return EFI_INVALID_PARAMETER; + } + + // If the log was scaled the maximum number of times, mark it as truncated. + if (EventLogAreaStruct->ScaleCount >= TCG_EVENT_LOG_MAX_SCALE_COUNT) { + DEBUG ((DEBUG_ERROR, "%a: Scale limit reached (%u), truncating log\n", __func__, TCG_EVENT_LOG_MAX_SCALE_COUNT)); + EventLogAreaStruct->EventLogTruncated = TRUE; + return EFI_VOLUME_FULL; + } + + NewLaml = EventLogAreaStruct->Laml * 2; + if (NewLaml <= EventLogAreaStruct->Laml) { + DEBUG ((DEBUG_ERROR, "%a: Laml overflow (0x%lx * 2)\n", __func__, EventLogAreaStruct->Laml)); + EventLogAreaStruct->EventLogTruncated = TRUE; + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES ((UINTN)NewLaml), + &NewLasa + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to allocate new TCG event log\n")); + return EFI_OUT_OF_RESOURCES; + } + + // Enter a critical section, we do not want to be interrupted while copying. + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + // Copy the data from the old event log to the new event log. + CopyMem ((VOID *)(UINTN)NewLasa, (VOID *)(UINTN)EventLogAreaStruct->Lasa, EventLogAreaStruct->EventLogSize); + + // Store the old Lasa and Laml before updating. + OldLasa = EventLogAreaStruct->Lasa; + OldLaml = EventLogAreaStruct->Laml; + + DEBUG ((DEBUG_INFO, "OldLasa: 0x%lx, OldLaml: 0x%lx\n", OldLasa, OldLaml)); + DEBUG ((DEBUG_INFO, "NewLasa: 0x%lx, NewLaml: 0x%lx\n", NewLasa, NewLaml)); + + // Update the EventLogAreaStruct. + EventLogAreaStruct->Lasa = NewLasa; + EventLogAreaStruct->Laml = NewLaml; + + // Update the LastEvent pointer. LastEvent = Lasa + Offset. To calculate + // the offset we can do: Offset = LastEvent - Lasa. + EventLogAreaStruct->LastEvent = (UINT8 *)(UINTN)NewLasa + ((UINTN)EventLogAreaStruct->LastEvent - (UINTN)OldLasa); + + // Track the number of times we've scaled. + EventLogAreaStruct->ScaleCount++; + + // Exit the critical section once we finish copying/updated the struct. + gBS->RestoreTPL (OldTpl); + + // Free the old log region. + gBS->FreePages (OldLasa, EFI_SIZE_TO_PAGES ((UINTN)OldLaml)); + + // Notify that the event log was scaled. + EfiEventGroupSignal (&gTcg2EventLogScaledGuid); + + return Status; +} + +/** + Check if the TCG log needs to be dynamically scaled. + + @param[in] EventLogAreaStruct Pointer to the event log area structure. + @param[in] NewEventHdrSize New event header size. + @param[in] NewEventSize New event data size. + + @retval TRUE Dynamic scaling needed. + @retval FALSE Dynamic scaling not needed. + +**/ +STATIC +BOOLEAN +TcgLogDynamicScalingNeeded ( + IN TCG_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct, + IN UINT32 NewEventHdrSize, + IN UINT32 NewEventSize + ) +{ + UINTN NewLogSize; + + // Make sure EventLogAreaStruct is valid. + if (EventLogAreaStruct == NULL) { + return FALSE; + } + + // Validate NewEventSize + NewEventHdrSize doesn't cause an overflow. + if (NewEventSize > MAX_ADDRESS - NewEventHdrSize) { + ASSERT (FALSE); + return FALSE; + } + + NewLogSize = NewEventHdrSize + NewEventSize; + + // Validate EventLogSize + NewLogSize doesn't cause an overflow. + if (NewLogSize > MAX_ADDRESS - EventLogAreaStruct->EventLogSize) { + ASSERT (FALSE); + return FALSE; + } + + // Determine if dynamic scaling is needed. + if (NewLogSize + EventLogAreaStruct->EventLogSize > EventLogAreaStruct->Laml) { + DEBUG ((DEBUG_INFO, " Laml - 0x%lx\n", EventLogAreaStruct->Laml)); + DEBUG ((DEBUG_INFO, " NewLogSize - 0x%lx\n", NewLogSize)); + DEBUG ((DEBUG_INFO, " LogSize - 0x%lx\n", EventLogAreaStruct->EventLogSize)); + DEBUG ((DEBUG_ERROR, "Dynamic scaling required! Recommended to update your TCG log size!\n")); + + // Log an error if we attempt to scale post ReadyToBoot. + if (mReadyToBoot) { + DEBUG ((DEBUG_ERROR, "Unexpected dynamic scaling occurring post ReadyToBoot!\n")); + } + + return TRUE; + } + + return FALSE; +} + +// MU_CHANGE - [END] + /** Add a new entry to the Event Log. @@ -1149,6 +1462,8 @@ TcgDxeLogHashEvent ( IN UINT8 *NewEventData ) { + // MU_CHANGE - [BEGIN] + EFI_STATUS Status; EFI_TPL OldTpl; UINTN Index; @@ -1156,6 +1471,9 @@ TcgDxeLogHashEvent ( TCG_PCR_EVENT2 TcgPcrEvent2; UINT8 *DigestBuffer; UINT32 *EventSizePtr; + BOOLEAN DynamicScalingNeeded; + + // MU_CHANGE - [END] RetStatus = EFI_SUCCESS; for (Index = 0; Index < sizeof (mTcg2EventInfo)/sizeof (mTcg2EventInfo[0]); Index++) { @@ -1194,6 +1512,30 @@ TcgDxeLogHashEvent ( EventSizePtr = CopyDigestListToBuffer (DigestBuffer, DigestList, mTcgDxeData.BsCap.ActivePcrBanks); CopyMem (EventSizePtr, &NewEventHdr->EventSize, sizeof (NewEventHdr->EventSize)); + // MU_CHANGE - [BEGIN] + + // Continually scale until we have enough space to log. We need to dynamically + // scale the TCG log before we enter a critical region. + while (TRUE) { + DynamicScalingNeeded = TcgLogDynamicScalingNeeded ( + &mTcgDxeData.EventLogAreaStruct[Index], + sizeof (TcgPcrEvent2.PCRIndex) + sizeof (TcgPcrEvent2.EventType) + GetDigestListBinSize (DigestBuffer) + sizeof (TcgPcrEvent2.EventSize), + NewEventHdr->EventSize + ); + + if (!DynamicScalingNeeded) { + break; + } + + Status = TcgScaleEventLog (&mTcgDxeData.EventLogAreaStruct[Index]); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Unable to scale the TCG event log!\n")); + return Status; + } + } + + // MU_CHANGE - [END] + // // Enter critical region // @@ -1701,21 +2043,18 @@ SetupEventLog ( for (Index = 0; Index < sizeof (mTcg2EventInfo)/sizeof (mTcg2EventInfo[0]); Index++) { if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { mTcgDxeData.EventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat; - if (PcdGet8 (PcdTpm2AcpiTableRev) >= 4) { - Status = gBS->AllocatePages ( - AllocateAnyPages, - EfiACPIMemoryNVS, - EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)), - &Lasa - ); - } else { - Status = gBS->AllocatePages ( - AllocateAnyPages, - EfiBootServicesData, - EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)), - &Lasa - ); - } + + // MU_CHANGE - [BEGIN] + + // Always allocate BootServicesData + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)), + &Lasa + ); + + // MU_CHANGE - [END] if (EFI_ERROR (Status)) { return Status; @@ -1724,7 +2063,9 @@ SetupEventLog ( mTcgDxeData.EventLogAreaStruct[Index].Lasa = Lasa; mTcgDxeData.EventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcgLogAreaMinLen); mTcgDxeData.EventLogAreaStruct[Index].Next800155EventOffset = 0; + mTcgDxeData.EventLogAreaStruct[Index].ScaleCount = 0; // MU_CHANGE + /* MU_CHANGE if ((PcdGet8 (PcdTpm2AcpiTableRev) >= 4) || (mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2)) { @@ -1735,6 +2076,7 @@ SetupEventLog ( PcdSet32S (PcdTpm2AcpiTableLaml, (UINT32)mTcgDxeData.EventLogAreaStruct[Index].Laml); PcdSet64S (PcdTpm2AcpiTableLasa, mTcgDxeData.EventLogAreaStruct[Index].Lasa); } + */ // // To initialize them as 0xFF is recommended @@ -1885,6 +2227,16 @@ SetupEventLog ( for (Index = 0; Index < sizeof (mTcg2EventInfo)/sizeof (mTcg2EventInfo[0]); Index++) { if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { if (mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) { + // MU_CHANGE - [BEGIN] + // PcdTcg2FinalLogAreaLen is platform-configurable; ensure it has room for both the + // EFI_TCG2_FINAL_EVENTS_TABLE header and the EV_NO_ACTION truncation marker event. + if (PcdGet32 (PcdTcg2FinalLogAreaLen) < sizeof (EFI_TCG2_FINAL_EVENTS_TABLE) + GetTruncationEventSize ()) { + DEBUG ((DEBUG_ERROR, "Insufficient FinalEventLogArea length\n")); + return EFI_INVALID_PARAMETER; + } + + // MU_CHANGE - [END] + Status = gBS->AllocatePages ( AllocateAnyPages, EfiACPIMemoryNVS, @@ -1904,9 +2256,11 @@ SetupEventLog ( (mTcgDxeData.FinalEventsTable[Index])->Version = EFI_TCG2_FINAL_EVENTS_TABLE_VERSION; (mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents = 0; - mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat; - mTcgDxeData.FinalEventLogAreaStruct[Index].Lasa = Lasa + sizeof (EFI_TCG2_FINAL_EVENTS_TABLE); - mTcgDxeData.FinalEventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcg2FinalLogAreaLen) - sizeof (EFI_TCG2_FINAL_EVENTS_TABLE); + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat; + mTcgDxeData.FinalEventLogAreaStruct[Index].Lasa = Lasa + sizeof (EFI_TCG2_FINAL_EVENTS_TABLE); + // MU_CHANGE + // Reserve space in the FinalEventLog for the EV_NO_ACTION truncation marker event. + mTcgDxeData.FinalEventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcg2FinalLogAreaLen) - sizeof (EFI_TCG2_FINAL_EVENTS_TABLE) - GetTruncationEventSize (); mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogSize = 0; mTcgDxeData.FinalEventLogAreaStruct[Index].LastEvent = (VOID *)(UINTN)mTcgDxeData.FinalEventLogAreaStruct[Index].Lasa; mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogStarted = FALSE; @@ -2595,6 +2949,8 @@ OnReadyToBoot ( PERF_FUNCTION_BEGIN (); + mReadyToBoot = TRUE; // MU_CHANGE + // MU_CHANGE_23086 // MU_CHANGE [BEGIN] - Call OEM init hook. Status = OemTpm2InitDxeReadyToBootEvent (mBootAttempts); diff --git a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf index 74877e13db..98b4921a44 100644 --- a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf +++ b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf @@ -99,6 +99,7 @@ gTpm2StartupLocalityHobGuid ## SOMETIMES_CONSUMES ## HOB gTcg800155PlatformIdEventHobGuid ## SOMETIMES_CONSUMES ## HOB gEfiDeviceSignatureDatabaseGuid + gTcg2EventLogScaledGuid ## SOMETIMES_PRODUCES ## Event # MU_CHANGE [Protocols] gEfiTcg2ProtocolGuid ## PRODUCES @@ -118,8 +119,8 @@ gEfiSecurityPkgTokenSpaceGuid.PcdTcgLogAreaMinLen ## CONSUMES gEfiSecurityPkgTokenSpaceGuid.PcdTcg2FinalLogAreaLen ## CONSUMES gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableRev ## CONSUMES - gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableLaml ## PRODUCES - gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableLasa ## PRODUCES + # gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableLaml ## PRODUCES # MU_CHANGE + # gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableLasa ## PRODUCES # MU_CHANGE gEfiMdeModulePkgTokenSpaceGuid.PcdTcgPfpMeasurementRevision ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdEnableSpdmDeviceAuthentication ## CONSUMES gEfiSecurityPkgTokenSpaceGuid.TcgMeasureBootStringsInPcr4 ## CONSUMES # MU_CHANGE diff --git a/SecurityPkg/Tcg/TcgLogTest/README.md b/SecurityPkg/Tcg/TcgLogTest/README.md new file mode 100644 index 0000000000..c5387aa6a5 --- /dev/null +++ b/SecurityPkg/Tcg/TcgLogTest/README.md @@ -0,0 +1,279 @@ +# TcgLogTest + +TcgLogTest validates the dynamic event log scaling functionality implemented +by `Tcg2Dxe`. It consists of a DXE driver (`TcgLogTestDxe`) and a UEFI shell +unit test application (`TcgLogTestApp`) that coordinate across multiple boots +to exercise scaling both before and after `ReadyToBoot`. + +## Components + +### TcgLogTestDxe (DXE_DRIVER) + +A DXE driver that runs pre-ReadyToBoot scaling tests on demand. It installs +the `TCG_LOG_TEST_PROTOCOL` which allows the test application to enable/disable +the tests and retrieve results. + +**Entry flow:** + +1. Installs the `TCG_LOG_TEST_PROTOCOL` on a new handle. +2. Checks the NV variable `TcgLogTestEnable` (existence-based: present = + enabled, absent = disabled). +3. If disabled: + - Returns immediately. The protocol is still available for the test app + to call `Enable` on. +4. If enabled: + - Deletes the enable variable. This makes it so the test only runs once. + - Locates `EFI_TCG2_PROTOCOL`. + - Runs `TestPreReadyToBootScaling`. + - Records results in an internal log buffer which can be acquired via + `GetLog`. + +#### Protocol + +The `TCG_LOG_TEST_PROTOCOL` provides the following function(s): + +| Function | Description | +| -------- | ----------- | +| `GetLog` | Returns a pointer to the DXE driver's internal ASCII log buffer and its size. Returns `EFI_NOT_STARTED` if the test did not run this boot. | +| `Enable` | Creates or deletes the `TcgLogTestEnable` NV variable to enable or disable the DXE test for the next boot. | + +The `TCG_LOG_TEST_PROTOCOL` GUID is defined in `TcgLogTest.h` and declared +in `SecurityPkg.dec`. + +```code +#define TCG_LOG_TEST_PROTOCOL_GUID \ + { 0xA3C12F80, 0x7D9E, 0x4B5A, { 0x91, 0xE4, 0x6C, 0xF8, 0x2D, 0xA1, 0xB7, 0x03 } } +``` + +#### NV Variable + +The enable/disable mechanism uses an NV variable rather than UnitTest saved +context because the DXE driver and the test application are separate binaries. +The DXE driver does not use `UnitTestLib` and cannot access the framework's +persisted state. An NV variable is the standard cross-module communication +channel in UEFI. + +| Attribute | Value | +| --------- | ----- | +| Name | `TcgLogTestEnable` | +| Vendor GUID | `gTcgLogTestProtocolGuid` | +| Attributes | `NV + BS` | +| Semantics | Existence-based: variable present = enabled, variable absent = disabled | + +#### Test: TestPreReadyToBootScaling + +Executed before `ReadyToBoot` when the NV variable is present indicating the +test was enabled. Exercises dynamic scaling before `ReadyToBoot` has fired. + +1. Calls `TcgLogTestLogEventsUntilScaled` to repeatedly log `EV_NO_ACTION` + events to PCR 8. Internally, the helper registers a notification callback + on `gTcg2EventLogScaledGuid` and stops as soon as `Tcg2Dxe` signals that + GUID, indicating the event log was dynamically scaled. +2. Writes `PASS` or `FAIL` (with details) to the internal log buffer. + +### TcgLogTestApp (UEFI_APPLICATION) + +A UnitTest framework shell application that runs post-ReadyToBoot scaling tests. + +The suite registers five test cases. Each case shares a `LocateProtocols` +prerequisite that resolves `EFI_TCG2_PROTOCOL` and `TCG_LOG_TEST_PROTOCOL`. + +| # | Test Case (Class Name) | Function | Cleanup | +| - | ---------------------- | -------- | ------- | +| 1 | `PostRtbScaling` | `TestPostReadyToBootScaling` | none | +| 2 | `ScaleLimitTruncates` | `TestScaleLimitTruncatesLog` | `EnableDxeTestAndReboot` | +| 3 | `SnapshotPlusFinalMatches` | `TestSnapshotPlusFinalMatchesEventLog` | none | +| 4 | `FinalEventLogTruncates` | `TestFinalEventLogTruncationMarker` | none | +| 5 | `PreRtbResults` | `TestPreReadyToBootScaling` | `SaveAndReboot` | + +#### Test: TestPostReadyToBootScaling + +Executed after `ReadyToBoot` in the UEFI shell. Exercises dynamic scaling +after `ReadyToBoot` has fired. + +1. Calls `TcgLogTestLogEventsUntilScaled` to repeatedly log `EV_NO_ACTION` + events to PCR 8. Internally, the helper registers a notification callback + on `gTcg2EventLogScaledGuid` and stops as soon as `Tcg2Dxe` signals that + GUID, indicating the event log was dynamically scaled. + +#### Test: TestScaleLimitTruncatesLog + +Verifies that the normal event log can only be dynamically scaled up to +`TCG_EVENT_LOG_MAX_SCALE_COUNT` times, after which scaling is refused and +`GetEventLog` reports the log as truncated. + +1. `TestPostReadyToBootScaling` already consumed one scale, so this test + calls `TcgLogTestLogEventsUntilScaled` repeatedly to exhaust the remaining + `TCG_EVENT_LOG_MAX_SCALE_COUNT - 1` scales. +2. Calls `TcgLogTestLogEventsUntilScaled` once more and asserts it returns + an error and reports `Scaled = FALSE`. +3. Calls `GetEventLog` and asserts the `Truncated` flag is `TRUE`. + +#### Test: TestSnapshotPlusFinalMatchesEventLog + +Validates the relationship between the normal event log and the +`FinalEventLog`: after the first `GetEventLog` call activates FinalEventLog +logging, every newly logged event must be appended to **both** the normal +log and the FinalEventLog. Therefore: + +```text +Snapshot1 + FinalEventLog_entries == Snapshot2 +``` + +1. Calls `GetEventLog` to capture `Snapshot1` (and activate FinalEventLog + logging). +2. Calls `TcgLogTestLogSingleEvent` `MAX_NUM_EXTRA_EVENTS` times so the new + entries are small enough to avoid triggering scaling. +3. Locates `EFI_TCG2_FINAL_EVENTS_TABLE` from the system configuration table + and asserts `NumberOfEvents == MAX_NUM_EXTRA_EVENTS`. +4. Walks the FinalEventLog entries to compute their combined byte size. +5. Calls `GetEventLog` again to capture `Snapshot2`. +6. Asserts `Snapshot2 == Snapshot1 || FinalEventLog_entries` byte-for-byte. + +#### Test: TestFinalEventLogTruncationMarker + +Verifies that when the `FinalEventLog` fills up, `Tcg2Dxe` appends a final +`EV_NO_ACTION` event carrying the `TCG_LOG_TRUNCATION_EVENT_STRING` marker +(`"TCG Event Log Truncated"`) so consumers can recognise the truncated state. + +1. Calls `GetEventLog` to activate FinalEventLog logging and assert it is + not yet truncated. +2. Calls `TcgLogTestLogEventsUntilScaled` in a loop until `HashLogExtendEvent` + returns `EFI_VOLUME_FULL`, signalling FinalEventLog truncation. (The normal + log can still scale; the FinalEventLog cannot.) +3. Locates `EFI_TCG2_FINAL_EVENTS_TABLE` and walks all `NumberOfEvents` entries + to land on the last entry. +4. Asserts the last entry is `EV_NO_ACTION` and its payload begins with the + ASCII bytes of `TCG_LOG_TRUNCATION_EVENT_STRING`. + +#### Test: TestPreReadyToBootScaling Results + +Verifies the DXE driver's pre-ReadyToBoot results. + +1. Locates `TCG_LOG_TEST_PROTOCOL` and calls `GetLog`. +2. Dumps the DXE driver's log for visibility. +3. Asserts the log contains `"PASS"` and does not contain `"FAIL"`. + +## Three-Boot Reboot Flow + +The tests require three boots to complete because scaling must be tested in +two different phases of the boot process, and each phase requires a separate +boot. The final boot should guarantee that the TCG event log is not polluted +with the test `NO_ACTION_EVENT` events used to scale the log. + +```text +Boot 1 (Post-ReadyToBoot scaling + truncation) +├── TcgLogTestDxe: +│ ├── Installs TCG_LOG_TEST_PROTOCOL. +│ ├── NV variable absent → Test not enabled → SKIPPED. +├── TcgLogTestApp: +│ ├── Launched from UEFI shell. (UnitTest Framework) +│ ├── Test Prerequisites: +│ │ └── Calls LocateProtocols() to locate the TCG2 and TcgLogTest protocols. +│ ├── TestPostReadyToBootScaling(): +│ │ ├── Calls TcgLogTestLogEventsUntilScaled() to scale the event log once. +│ │ └── PASS. +│ ├── TestScaleLimitTruncatesLog(): +│ │ ├── Scales the remaining (TCG_EVENT_LOG_MAX_SCALE_COUNT - 1) times. +│ │ ├── Attempts one more scale and asserts it fails. +│ │ ├── Calls GetEventLog() and asserts Truncated == TRUE. +│ │ └── PASS. +│ └── Test Cleanup (for TestScaleLimitTruncatesLog): +│ └── Calls EnableDxeTestAndReboot(). +│ ├── Calls Enable (TRUE) to create the NV variable. +│ └── Calls SaveAndReboot() to SaveFrameworkState + EfiResetCold. +│ +Boot 2 (DXE pre-ReadyToBoot test + FinalEventLog tests + DXE results) +├── TcgLogTestDxe: +│ ├── Installs TCG_LOG_TEST_PROTOCOL. +│ ├── NV variable present → Test enabled → Deletes the NV variable → Runs. +│ ├── Calls TestPreReadyToBootScaling(): +│ │ ├── Calls TcgLogTestLogEventsUntilScaled() to scale the event log. +│ │ ├── PASS. +│ │ └── Logs results into internal buffer for later access via GetLog(). +├── TcgLogTestApp: +│ ├── Resumes execution from UEFI shell. (UnitTest Framework) +│ ├── TestPostReadyToBootScaling() → already PASSED → SKIPPED. +│ ├── TestScaleLimitTruncatesLog() → already PASSED → SKIPPED. +│ ├── TestSnapshotPlusFinalMatchesEventLog(): +│ │ ├── Captures Snapshot1 of the normal log and activates FinalEventLog. +│ │ ├── Logs MAX_NUM_EXTRA_EVENTS individual events. +│ │ ├── Reads FinalEventsTable and asserts NumberOfEvents matches. +│ │ ├── Captures Snapshot2 and asserts Snapshot1 || FinalEntries == Snapshot2. +│ │ └── PASS. +│ ├── TestFinalEventLogTruncationMarker(): +│ │ ├── Logs events until HashLogExtendEvent returns EFI_VOLUME_FULL. +│ │ ├── Walks FinalEventsTable to the last entry. +│ │ ├── Asserts the entry is EV_NO_ACTION carrying TCG_LOG_TRUNCATION_EVENT_STRING. +│ │ └── PASS. +│ ├── TestPreReadyToBootScaling() (DXE results): +│ │ ├── Calls GetLog() to acquire the TcgLogTestDxe log. +│ │ ├── Verifies PASS in TcgLogTestDxe log. +│ │ └── PASS. +│ └── Test Cleanup (for TestPreReadyToBootScaling): +│ └── Calls SaveAndReboot() to SaveFrameworkState + EfiResetCold. +│ +Boot 3 (Final Report/Results) +├── TcgLogTestDxe: +│ ├── Installs TCG_LOG_TEST_PROTOCOL. +│ ├── NV variable absent → Test not enabled → Exit. +├── TcgLogTestApp: +│ ├── Resumes execution from UEFI shell. (UnitTest Framework) +│ ├── All tests already PASSED → SKIPPED. +│ └── Reports final results, cleans up framework state. +``` + +## Shared Code (TcgLogTestCommon) + +Common functions compiled into both binaries: + +| Function | Description | +| -------- | ----------- | +| `TcgLogTestAdvanceEvent` | Parses one TCG 2.0 event entry, advancing the pointer to the next event. Handles SHA-1/256/384/512/SM3 digest algorithms. | +| `TcgLogTestLogEventsUntilScaled` | Builds a test event and logs it repeatedly via `HashLogExtendEvent` until `Tcg2Dxe` signals `gTcg2EventLogScaledGuid` (indicating the event log was dynamically scaled) or `HashLogExtendEvent` returns an error. | +| `TcgLogTestLogSingleEvent` | Builds and logs exactly one fixed test event via `HashLogExtendEvent`. Used by tests that need to add a small, deterministic number of events without triggering scaling. | + +## Truncation Marker Event + +When the `FinalEventLog` fills up and `Tcg2Dxe` can no longer append new +entries, it writes a final `EV_NO_ACTION` event whose data payload is the +ASCII string `"TCG Event Log Truncated"` (`TCG_LOG_TRUNCATION_EVENT_STRING`). +`TestFinalEventLogTruncationMarker` exercises this code path and verifies the +marker is present as the last entry of the table. The string constant in the +test sources must stay in sync with the definition in `Tcg2Dxe.c`. + +## Platform Integration + +### DSC + +Add both modules to the platform DSC under the `[Components]` section, +typically gated behind a TPM enable flag: + +```ini +!if $(TPM2_ENABLE) == TRUE + SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf + SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf +!endif +``` + +### FDF + +Add both modules to the platform FDF so they are included in the firmware +volume, typically gated behind a TPM enable flag. The DXE driver must be in +the DXE FV so it loads during DXE dispatch. The test application can be in +the same FV or a separate one accessible from the UEFI shell: + +```ini +!if $(TPM2_ENABLE) == TRUE + INF SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf + INF SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf +!endif +``` + +### Running the Test + +1. Boot to the UEFI shell. +2. Run the test application: `TcgLogTestApp.efi` +3. The system will automatically reboot twice more to complete the three-boot + flow. +4. On the third boot, the framework reports final results to the shell. diff --git a/SecurityPkg/Tcg/TcgLogTest/TcgLogTest.h b/SecurityPkg/Tcg/TcgLogTest/TcgLogTest.h new file mode 100644 index 0000000000..ee5795cc41 --- /dev/null +++ b/SecurityPkg/Tcg/TcgLogTest/TcgLogTest.h @@ -0,0 +1,67 @@ +/** @file + TCG Log Test protocol definition. + + Defines the protocol produced by TcgLogTestDxe that allows the TcgLogTestApp + to retrieve pre-ReadyToBoot test results and to enable/disable the DXE test + via an NV variable. + + Copyright (c), Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef TCG_LOG_TEST_H_ +#define TCG_LOG_TEST_H_ + +#include + +#define TCG_LOG_TEST_PROTOCOL_GUID \ + { 0xA3C12F80, 0x7D9E, 0x4B5A, { 0x91, 0xE4, 0x6C, 0xF8, 0x2D, 0xA1, 0xB7, 0x03 } } + +#define TCG_LOG_TEST_ENABLE_VARIABLE_NAME L"TcgLogTestEnable" + +typedef struct _TCG_LOG_TEST_PROTOCOL TCG_LOG_TEST_PROTOCOL; + +/** + Retrieve the pre-ReadyToBoot test log produced by TcgLogTestDxe. + + @param[in] This Protocol instance. + @param[out] LogBuffer Pointer to the internal log buffer (NULL-terminated). + @param[out] LogSize Number of valid bytes in LogBuffer (including NULL). + + @retval EFI_SUCCESS Log data returned. + @retval EFI_NOT_STARTED The DXE test did not run this boot. + @retval EFI_INVALID_PARAMETER NULL pointer supplied. +**/ +typedef +EFI_STATUS +(EFIAPI *TCG_LOG_TEST_GET_LOG)( + IN TCG_LOG_TEST_PROTOCOL *This, + OUT CHAR8 **LogBuffer, + OUT UINTN *LogSize + ); + +/** + Enable or disable the DXE pre-ReadyToBoot test for the next boot by + writing an NV variable. + + @param[in] This Protocol instance. + @param[in] Enable TRUE to enable the test on next boot, FALSE to disable. + + @retval EFI_SUCCESS Variable written successfully. + @retval Other SetVariable failure. +**/ +typedef +EFI_STATUS +(EFIAPI *TCG_LOG_TEST_ENABLE)( + IN TCG_LOG_TEST_PROTOCOL *This, + IN BOOLEAN Enable + ); + +struct _TCG_LOG_TEST_PROTOCOL { + TCG_LOG_TEST_GET_LOG GetLog; + TCG_LOG_TEST_ENABLE Enable; +}; + +extern EFI_GUID gTcgLogTestProtocolGuid; + +#endif // TCG_LOG_TEST_H_ diff --git a/SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.c b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.c new file mode 100644 index 0000000000..aacc6f0764 --- /dev/null +++ b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.c @@ -0,0 +1,544 @@ +/** @file + UEFI Shell UnitTest application that validates TCG2 event log dynamic + scaling after ReadyToBoot. + + This application locates the TcgLogTestProtocol produced by TcgLogTestDxe to + retrieve pre-ReadyToBoot test logs, then exercises post-ReadyToBoot scaling. + + Copyright (c), Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TcgLogTest.h" +#include "TcgLogTestCommon.h" + +#define UNIT_TEST_NAME "TCG Log Scaling Test" +#define UNIT_TEST_VERSION "1.0" + +// Must match TCG_EVENT_LOG_MAX_SCALE_COUNT in Tcg2Dxe.c. +#define TCG_EVENT_LOG_MAX_SCALE_COUNT 4 + +// Must match TCG_LOG_TRUNCATION_EVENT_STRING in Tcg2Dxe.c. +#define TCG_LOG_TRUNCATION_EVENT_STRING "TCG Event Log Truncated" + +// Number of extra events to log +#define MAX_NUM_EXTRA_EVENTS 8 + +STATIC EFI_TCG2_PROTOCOL *mTcg2Protocol = NULL; +STATIC TCG_LOG_TEST_PROTOCOL *mTcgLogTestProtocol = NULL; + +/** + Test that the DXE driver ran and its pre-ReadyToBoot log contains PASS. + + This runs on the second boot after TestPostReadyToBootScaling enabled the + DXE driver and rebooted. The DXE driver ran before ReadyToBoot on this + boot, so results are available via the protocol. + + @param[in] Context Unit test context (unused). + + @retval UNIT_TEST_PASSED Log contains PASS and no FAIL. + @retval UNIT_TEST_ERROR_TEST_FAILED Assertion failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestPreReadyToBootScaling ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + CHAR8 *LogBuffer; + UINTN LogSize; + + // The prerequisite is skipped on resume from a reboot, so locate the + // protocol here if it was not already set. + if (mTcgLogTestProtocol == NULL) { + Status = gBS->LocateProtocol (&gTcgLogTestProtocolGuid, NULL, (VOID **)&mTcgLogTestProtocol); + UT_ASSERT_NOT_EFI_ERROR (Status); + } + + Status = mTcgLogTestProtocol->GetLog (mTcgLogTestProtocol, &LogBuffer, &LogSize); + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ("GetLog failed: %r\n", Status); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + // Dump the DXE driver's log for visibility. + UT_LOG_INFO ("TcgLogTestDxe Log (%u bytes):\n%a\n", LogSize, LogBuffer); + + // Verify the log contains "PASS". + UT_ASSERT_NOT_NULL (AsciiStrStr (LogBuffer, "PASS")); + + // Verify the log does not contain "FAIL". + UT_ASSERT_TRUE (AsciiStrStr (LogBuffer, "FAIL") == NULL); + + return UNIT_TEST_PASSED; +} + +/** + Test post-ReadyToBoot scaling: log events until the log scales. + + @param[in] Context Unit test context (unused). + + @retval UNIT_TEST_PASSED Scaling verified. + @retval UNIT_TEST_ERROR_TEST_FAILED Assertion failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestPostReadyToBootScaling ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + BOOLEAN Scaled; + + Status = TcgLogTestLogEventsUntilScaled (mTcg2Protocol, &Scaled); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_TRUE (Scaled); + + UT_LOG_INFO ("Post-ReadyToBoot scaling succeeded\n"); + + return UNIT_TEST_PASSED; +} + +/** + Test that the TCG event log can only be dynamically scaled up to + TCG_EVENT_LOG_MAX_SCALE_COUNT times, after which the log is marked as + truncated. + + TestPostReadyToBootScaling already consumed one scale in this boot, so this + test exhausts the remaining (TCG_EVENT_LOG_MAX_SCALE_COUNT - 1) scales and + then verifies the next attempt fails and the log is marked truncated. + + @param[in] Context Unit test context (unused). + + @retval UNIT_TEST_PASSED Scaling limit and truncation verified. + @retval UNIT_TEST_ERROR_TEST_FAILED Assertion failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestScaleLimitTruncatesLog ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + BOOLEAN Scaled; + BOOLEAN Truncated; + UINTN ScaleCount; + EFI_PHYSICAL_ADDRESS LogBase; + EFI_PHYSICAL_ADDRESS LastEntry; + + // TestPostReadyToBootScaling scaled once, scale the remaining amount. + for (ScaleCount = 1; ScaleCount < TCG_EVENT_LOG_MAX_SCALE_COUNT; ScaleCount++) { + Scaled = FALSE; + Status = TcgLogTestLogEventsUntilScaled (mTcg2Protocol, &Scaled); + UT_LOG_INFO ("Scale %u/%u: status=%r scaled=%d\n", ScaleCount + 1, TCG_EVENT_LOG_MAX_SCALE_COUNT, Status, Scaled); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_TRUE (Scaled); + } + + // Attempt to scale once more which should fail. + Scaled = FALSE; + Status = TcgLogTestLogEventsUntilScaled (mTcg2Protocol, &Scaled); + UT_LOG_INFO ("Scale beyond limit: status=%r scaled=%d\n", Status, Scaled); + UT_ASSERT_TRUE (EFI_ERROR (Status)); + UT_ASSERT_FALSE (Scaled); + + // Verify GetEventLog reports the log as truncated. + Status = mTcg2Protocol->GetEventLog ( + mTcg2Protocol, + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, + &LogBase, + &LastEntry, + &Truncated + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_TRUE (Truncated); + + UT_LOG_INFO ("Test scaling limit succeeded\n"); + + return UNIT_TEST_PASSED; +} + +/** + Test that an EV_NO_ACTION truncation marker is appended to the + FinalEventLog when it becomes truncated. + + Activates FinalEventLog via GetEventLog, then logs events until + HashLogExtendEvent reports an error. Because the FinalEventLog does not scale + it is possible to truncate at which point the truncation marker is appended + as the final entry. The test then walks the table and verifies the last entry + is an EV_NO_ACTION carrying the truncation marker string. + + @param[in] Context Unit test context (unused). + + @retval UNIT_TEST_PASSED Truncation marker located and verified. + @retval UNIT_TEST_ERROR_TEST_FAILED Assertion failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestFinalEventLogTruncationMarker ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS LogBase; + EFI_PHYSICAL_ADDRESS LastEntry; + BOOLEAN Truncated; + BOOLEAN Scaled; + EFI_TCG2_FINAL_EVENTS_TABLE *FinalTable; + UINT8 *CurrentEvent; + UINT32 EventType; + UINT32 EventSize; + UINT8 *EventData; + UINT64 Index; + + // Activate FinalEventLog logging. + Status = mTcg2Protocol->GetEventLog ( + mTcg2Protocol, + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, + &LogBase, + &LastEntry, + &Truncated + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_FALSE (Truncated); + + // Log events until HashLogExtendEvent reports an error. When the + // FinalEventLog becomes truncated, HashLogExtendEvent will return + // EFI_VOLUME_FULL. + Scaled = FALSE; + do { + Status = TcgLogTestLogEventsUntilScaled (mTcg2Protocol, &Scaled); + } while (!EFI_ERROR (Status)); + + UT_LOG_INFO ("Log events stopped: status=%r", Status); + UT_ASSERT_TRUE (Status == EFI_VOLUME_FULL); + + // Walk the FinalEventsTable and verify the last entry is the truncation marker. + FinalTable = NULL; + Status = EfiGetSystemConfigurationTable (&gEfiTcg2FinalEventsTableGuid, (VOID **)&FinalTable); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_NOT_NULL (FinalTable); + + UT_LOG_INFO ("FinalEventsTable: Version=%lu NumberOfEvents=%lu\n", FinalTable->Version, FinalTable->NumberOfEvents); + UT_ASSERT_TRUE (FinalTable->NumberOfEvents > 0); + + CurrentEvent = (UINT8 *)(FinalTable + 1); + EventType = 0; + EventSize = 0; + EventData = NULL; + + for (Index = 0; Index < FinalTable->NumberOfEvents; Index++) { + if (!TcgLogTestAdvanceEvent ( + &CurrentEvent, + (UINT8 *)(UINTN)MAX_ADDRESS, + NULL, + &EventType, + &EventSize, + &EventData + )) + { + UT_LOG_ERROR ("AdvanceEvent failed at index %lu\n", Index); + return UNIT_TEST_ERROR_TEST_FAILED; + } + } + + UT_LOG_INFO ("Last FinalEventLog entry: type=0x%x size=%u\n", EventType, EventSize); + UT_ASSERT_EQUAL (EventType, EV_NO_ACTION); + UT_ASSERT_NOT_NULL (EventData); + UT_ASSERT_TRUE (EventSize >= sizeof (TCG_LOG_TRUNCATION_EVENT_STRING)); + UT_ASSERT_MEM_EQUAL (EventData, TCG_LOG_TRUNCATION_EVENT_STRING, sizeof (TCG_LOG_TRUNCATION_EVENT_STRING)); + + return UNIT_TEST_PASSED; +} + +/** + Test that a snapshot of the event log plus the FinalEventLog entries + exactly reconstructs a later snapshot of the event log. + + After Tcg2Dxe begins maintaining the FinalEventLog (triggered by the first + call to GetEventLog), every subsequent event is appended to both the normal + event log and the FinalEventLog. Therefore: + + Snapshot1 + FinalEventLog_entries == Snapshot2 + + where Snapshot1 is the normal log captured immediately after the first + GetEventLog call and Snapshot2 is the normal log captured after additional + events have been logged. + + @param[in] Context Unit test context (unused). + + @retval UNIT_TEST_PASSED Reconstruction matches. + @retval UNIT_TEST_ERROR_TEST_FAILED Assertion failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestSnapshotPlusFinalMatchesEventLog ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS LogLocation1; + EFI_PHYSICAL_ADDRESS LastEntry1; + EFI_PHYSICAL_ADDRESS LogLocation2; + EFI_PHYSICAL_ADDRESS LastEntry2; + BOOLEAN Truncated; + EFI_TCG2_FINAL_EVENTS_TABLE *FinalTable; + UINT8 *Snapshot; + UINTN SnapshotSize1; + UINTN SnapshotSize2; + UINTN FinalEntriesSize; + UINT8 *FinalEntriesStart; + UINT8 *LogPtr; + UINT64 Index; + + // First snapshot. Also activates FinalEventLog logging. + Status = mTcg2Protocol->GetEventLog ( + mTcg2Protocol, + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, + &LogLocation1, + &LastEntry1, + &Truncated + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_FALSE (Truncated); + + // Snapshot length runs from LogLocation1 to one byte past LastEntry1. + LogPtr = (UINT8 *)(UINTN)LastEntry1; + if (!TcgLogTestAdvanceEvent ( + &LogPtr, + (UINT8 *)(UINTN)MAX_ADDRESS, + NULL, + NULL, + NULL, + NULL + )) + { + UT_LOG_ERROR ("AdvanceEvent failed past LastEntry1\n"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + SnapshotSize1 = (UINTN)LogPtr - (UINTN)LogLocation1; + UT_LOG_INFO ("Snapshot1: base=0x%lx size=%u\n", LogLocation1, SnapshotSize1); + + Snapshot = AllocateCopyPool (SnapshotSize1, (VOID *)(UINTN)LogLocation1); + UT_ASSERT_NOT_NULL (Snapshot); + + // Log a small number of additional events. Few enough not to trigger + // scaling, but enough to make the test meaningful. + for (Index = 0; Index < MAX_NUM_EXTRA_EVENTS; Index++) { + Status = TcgLogTestLogSingleEvent (mTcg2Protocol); + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ("LogSingleEvent %u failed: %r\n", Index, Status); + FreePool (Snapshot); + return UNIT_TEST_ERROR_TEST_FAILED; + } + } + + // Retrieve the FinalEventLog from the system configuration table. + FinalTable = NULL; + Status = EfiGetSystemConfigurationTable (&gEfiTcg2FinalEventsTableGuid, (VOID **)&FinalTable); + if (EFI_ERROR (Status) || (FinalTable == NULL)) { + UT_LOG_ERROR ("FinalEventsTable lookup failed: %r\n", Status); + FreePool (Snapshot); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + UT_LOG_INFO ("FinalEventsTable: NumberOfEvents=%lu\n", FinalTable->NumberOfEvents); + UT_ASSERT_EQUAL (FinalTable->NumberOfEvents, MAX_NUM_EXTRA_EVENTS); + + // Walk all FinalEventLog entries to compute their combined byte size. + FinalEntriesStart = (UINT8 *)(FinalTable + 1); + LogPtr = FinalEntriesStart; + for (Index = 0; Index < FinalTable->NumberOfEvents; Index++) { + if (!TcgLogTestAdvanceEvent ( + &LogPtr, + (UINT8 *)(UINTN)MAX_ADDRESS, + NULL, + NULL, + NULL, + NULL + )) + { + UT_LOG_ERROR ("AdvanceEvent failed at FinalEventLog index %lu\n", Index); + FreePool (Snapshot); + return UNIT_TEST_ERROR_TEST_FAILED; + } + } + + FinalEntriesSize = (UINTN)LogPtr - (UINTN)FinalEntriesStart; + UT_LOG_INFO ("FinalEventLog entries total bytes: %u\n", FinalEntriesSize); + + // Second snapshot. + Status = mTcg2Protocol->GetEventLog ( + mTcg2Protocol, + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, + &LogLocation2, + &LastEntry2, + &Truncated + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_FALSE (Truncated); + + LogPtr = (UINT8 *)(UINTN)LastEntry2; + if (!TcgLogTestAdvanceEvent ( + &LogPtr, + (UINT8 *)(UINTN)MAX_ADDRESS, + NULL, + NULL, + NULL, + NULL + )) + { + UT_LOG_ERROR ("AdvanceEvent failed past LastEntry2\n"); + FreePool (Snapshot); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + SnapshotSize2 = (UINTN)LogPtr - (UINTN)LogLocation2; + UT_LOG_INFO ("Snapshot2: base=0x%lx size=%u\n", LogLocation2, SnapshotSize2); + + // Snapshot2 must be exactly Snapshot1 followed by the FinalEventLog entries. + UT_ASSERT_EQUAL (SnapshotSize2, SnapshotSize1 + FinalEntriesSize); + UT_ASSERT_MEM_EQUAL ((VOID *)(UINTN)LogLocation2, Snapshot, SnapshotSize1); + UT_ASSERT_MEM_EQUAL ((VOID *)((UINTN)LogLocation2 + SnapshotSize1), FinalEntriesStart, FinalEntriesSize); + + FreePool (Snapshot); + return UNIT_TEST_PASSED; +} + +/** + Save the unit test framework state and perform a cold reboot. + + @param[in] Context Unit test context (unused). +**/ +STATIC +VOID +EFIAPI +SaveAndReboot ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SaveFrameworkState (NULL, 0); + gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); +} + +/** + Cleanup for TestPostReadyToBootScaling: enable the DXE pre-ReadyToBoot test + for the next boot, then save and reboot so the DXE driver runs before + ReadyToBoot on the second boot. + + @param[in] Context Unit test context (unused). +**/ +STATIC +VOID +EFIAPI +EnableDxeTestAndReboot ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + + if (mTcgLogTestProtocol != NULL) { + Status = mTcgLogTestProtocol->Enable (mTcgLogTestProtocol, TRUE); + DEBUG ((DEBUG_INFO, "%a: Enable (TRUE) - %r\n", __func__, Status)); + } else { + DEBUG ((DEBUG_ERROR, "%a: mTcgLogTestProtocol is NULL, cannot enable\n", __func__)); + } + + SaveAndReboot (Context); +} + +/** + Prerequisite: locate the TCG2 and TcgLogTest protocols. + + @param[in] Context Unit test context (unused). + + @retval UNIT_TEST_PASSED Protocols located. + @retval UNIT_TEST_ERROR_PREREQUISITE_NOT_MET Protocol not found. +**/ +UNIT_TEST_STATUS +EFIAPI +LocateProtocols ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol (&gEfiTcg2ProtocolGuid, NULL, (VOID **)&mTcg2Protocol); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_PREREQUISITE_NOT_MET; + } + + Status = gBS->LocateProtocol (&gTcgLogTestProtocolGuid, NULL, (VOID **)&mTcgLogTestProtocol); + if (EFI_ERROR (Status)) { + return UNIT_TEST_ERROR_PREREQUISITE_NOT_MET; + } + + return UNIT_TEST_PASSED; +} + +/** + Entry point for TcgLogTestApp. + + @param[in] ImageHandle Image handle. + @param[in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS Tests dispatched and framework freed. + @retval Other Framework initialization failed. +**/ +EFI_STATUS +EFIAPI +TcgLogTestAppEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE Suite; + + Framework = NULL; + + Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: InitUnitTestFramework failed: %r\n", __func__, Status)); + return Status; + } + + Status = CreateUnitTestSuite (&Suite, Framework, "TCG Log Scaling Tests", "TcgLogTest", NULL, NULL); + if (EFI_ERROR (Status)) { + goto Done; + } + + AddTestCase (Suite, "Post-ReadyToBoot scaling succeeds", "PostRtbScaling", TestPostReadyToBootScaling, LocateProtocols, NULL, NULL); + AddTestCase (Suite, "Scaling occurs X times before truncating", "ScaleLimitTruncates", TestScaleLimitTruncatesLog, LocateProtocols, EnableDxeTestAndReboot, NULL); + AddTestCase (Suite, "Snapshot plus FinalEventLog matches full log", "SnapshotPlusFinalMatches", TestSnapshotPlusFinalMatchesEventLog, LocateProtocols, NULL, NULL); + AddTestCase (Suite, "FinalEventLog gets truncation marker", "FinalEventLogTruncates", TestFinalEventLogTruncationMarker, LocateProtocols, NULL, NULL); + AddTestCase (Suite, "Pre-ReadyToBoot DXE results contain PASS", "PreRtbResults", TestPreReadyToBootScaling, LocateProtocols, SaveAndReboot, NULL); + + Status = RunAllTestSuites (Framework); + +Done: + if (Framework != NULL) { + FreeUnitTestFramework (Framework); + } + + return Status; +} diff --git a/SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf new file mode 100644 index 0000000000..96bcd9c38e --- /dev/null +++ b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf @@ -0,0 +1,46 @@ +## @file +# UEFI Shell application that validates TCG2 event log dynamic scaling +# after ReadyToBoot using the UnitTest framework. +# +# Copyright (c), Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = TcgLogTestApp + FILE_GUID = 1F3A9C52-6E8B-4D07-B2A3-8C4E7F1D5E90 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = TcgLogTestAppEntry + +[Sources] + TcgLogTestApp.c + TcgLogTestCommon.c + TcgLogTestCommon.h + TcgLogTest.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + UefiApplicationEntryPoint + UefiBootServicesTableLib + UefiLib + UefiRuntimeServicesTableLib + UnitTestLib + +[Protocols] + gEfiTcg2ProtocolGuid ## CONSUMES + gTcgLogTestProtocolGuid ## CONSUMES + gEfiTcg2FinalEventsTableGuid ## CONSUMES ## SystemTable + +[Guids] + gTcg2EventLogScaledGuid ## CONSUMES ## Event diff --git a/SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.c b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.c new file mode 100644 index 0000000000..60f44bec6e --- /dev/null +++ b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.c @@ -0,0 +1,372 @@ +/** @file + TCG Log Test common implementation shared by TcgLogTestDxe and TcgLogTestApp. + + Copyright (c), Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TcgLogTestCommon.h" + +#define TCG_LOG_TEST_PCR_INDEX 8 +#define TCG_LOG_TEST_EVENT_TYPE EV_NO_ACTION +#define TCG_LOG_TEST_EVENT_PAYLOAD "TcgLogTestDxeEvent" + +/** + Allocate and initialize an EFI_TCG2_EVENT structure with a fixed payload. + + @param[out] Event On success, pointer to the allocated event. Caller must + free with FreePool(). + + @retval EFI_SUCCESS Event allocated and initialized. + @retval EFI_OUT_OF_RESOURCES Allocation failed. +**/ +STATIC +EFI_STATUS +TcgLogTestBuildEvent ( + OUT EFI_TCG2_EVENT **Event + ) +{ + UINT32 PayloadSize; + UINT32 TotalSize; + EFI_TCG2_EVENT *TestEvent; + + PayloadSize = (UINT32)sizeof (TCG_LOG_TEST_EVENT_PAYLOAD); + TotalSize = (UINT32)(sizeof (EFI_TCG2_EVENT) - sizeof (TestEvent->Event) + PayloadSize); + + TestEvent = AllocateZeroPool (TotalSize); + if (TestEvent == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TestEvent->Size = TotalSize; + TestEvent->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); + TestEvent->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; + TestEvent->Header.PCRIndex = TCG_LOG_TEST_PCR_INDEX; + TestEvent->Header.EventType = TCG_LOG_TEST_EVENT_TYPE; + + CopyMem (TestEvent->Event, TCG_LOG_TEST_EVENT_PAYLOAD, PayloadSize); + + *Event = TestEvent; + return EFI_SUCCESS; +} + +/** + Log a single event via the TCG2 protocol. + + @param[in] Tcg2Protocol TCG2 protocol instance. + @param[in] Event Pre-built TCG2 event structure. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + @retval EFI_DEVICE_ERROR The command was unsuccessful. +**/ +STATIC +EFI_STATUS +TcgLogTestLogEvent ( + IN EFI_TCG2_PROTOCOL *Tcg2Protocol, + IN EFI_TCG2_EVENT *Event + ) +{ + return Tcg2Protocol->HashLogExtendEvent ( + Tcg2Protocol, + 0, + (EFI_PHYSICAL_ADDRESS)(UINTN)(CONST CHAR8 *)TCG_LOG_TEST_EVENT_PAYLOAD, + sizeof (TCG_LOG_TEST_EVENT_PAYLOAD), + Event + ); +} + +/** + Advance one entry in the event log. + + @param[in,out] CurrentEvent On entry, points to the start of the event (PCRIndex). + On success, updated to point to the next event. + @param[in] LogEnd One byte past the end of the log buffer. + @param[out] PcrIndex PCRIndex of the parsed event. + @param[out] EventType EventType of the parsed event. + @param[out] EventSize Size of the event data payload. + @param[out] EventData Pointer to the event data payload. + + @retval TRUE Event parsed successfully and CurrentEvent updated. + @retval FALSE Invalid pointers, buffer, or digest algorithm. +**/ +BOOLEAN +TcgLogTestAdvanceEvent ( + IN OUT UINT8 **CurrentEvent, + IN UINT8 *LogEnd, + OUT UINT32 *PcrIndex OPTIONAL, + OUT UINT32 *EventType OPTIONAL, + OUT UINT32 *EventSize OPTIONAL, + OUT UINT8 **EventData OPTIONAL + ) +{ + UINT32 DigestCount; + UINT32 DigestIndex; + UINT16 AlgId; + UINT32 DigestLen; + UINT32 Size; + UINT8 *EventPtr; + + // Verify the required pointers are valid + if ((CurrentEvent == NULL) || (LogEnd == NULL)) { + DEBUG ((DEBUG_ERROR, "%a: Invalid input parameters\n", __func__)); + return FALSE; + } + + // Start on the current event. + EventPtr = *CurrentEvent; + + // Verify there are 8 bytes (PCRIndex (4 bytes) + EventType (4 bytes)) + // in the log before attempting to read. + if ((UINTN)(LogEnd - EventPtr) < sizeof (UINT32) + sizeof (UINT32)) { + DEBUG ((DEBUG_ERROR, "%a: PCRIndex & EventType invalid\n", __func__)); + return FALSE; + } + + // Store the PCRIndex, if provided. + if (PcrIndex != NULL) { + *PcrIndex = ReadUnaligned32 ((UINT32 *)EventPtr); + } + + // Store the EventType, if provided. + if (EventType != NULL) { + *EventType = ReadUnaligned32 ((UINT32 *)(EventPtr + sizeof (UINT32))); + } + + // Move the pointer past the PcrIndex and EventType. + EventPtr += sizeof (UINT32) + sizeof (UINT32); + + // Verify there are 4 bytes (DigestCount (4 bytes)) in the log before + // attempting to read. + // TPML_DIGEST_VALUES = DigestCount followed by (AlgId + Digest) pairs. + if ((UINTN)(LogEnd - EventPtr) < sizeof (UINT32)) { + DEBUG ((DEBUG_ERROR, "%a: DigestCount invalid\n", __func__)); + return FALSE; + } + + // Acquire the DigestCount. + DigestCount = ReadUnaligned32 ((UINT32 *)EventPtr); + + // Move the pointer past the DigestCount. + EventPtr += sizeof (UINT32); + + // Loop through the number of digests. + for (DigestIndex = 0; DigestIndex < DigestCount; DigestIndex++) { + // Verify there are 2 bytes (AlgId (2 bytes)) in the log + // before attempting to read. + if ((UINTN)(LogEnd - EventPtr) < sizeof (UINT16)) { + DEBUG ((DEBUG_ERROR, "%a: AlgId invalid\n", __func__)); + return FALSE; + } + + // Acquire the AlgId. + AlgId = ReadUnaligned16 ((UINT16 *)EventPtr); + + // Move the pointer past the AlgId. + EventPtr += sizeof (UINT16); + + // DigestLen depends on the AlgId. + switch (AlgId) { + case TPM_ALG_SHA1: + DigestLen = SHA1_DIGEST_SIZE; + break; + case TPM_ALG_SHA256: + DigestLen = SHA256_DIGEST_SIZE; + break; + case TPM_ALG_SHA384: + DigestLen = SHA384_DIGEST_SIZE; + break; + case TPM_ALG_SHA512: + DigestLen = SHA512_DIGEST_SIZE; + break; + case TPM_ALG_SM3_256: + DigestLen = SM3_256_DIGEST_SIZE; + break; + default: + DEBUG ((DEBUG_ERROR, "%a: Unknown AlgId 0x%x\n", __func__, AlgId)); + return FALSE; + } + + // Verify there are DigestLen bytes in the log. + if ((UINTN)(LogEnd - EventPtr) < DigestLen) { + DEBUG ((DEBUG_ERROR, "%a: DigestLen invalid\n", __func__)); + return FALSE; + } + + // Move the pointer past the Digest based on the DigestLen. + EventPtr += DigestLen; + } + + // Verify there are 4 bytes (EventSize (4 bytes)) in the log + // before attempting to read. + if ((UINTN)(LogEnd - EventPtr) < sizeof (UINT32)) { + DEBUG ((DEBUG_ERROR, "%a: EventSize invalid\n", __func__)); + return FALSE; + } + + // Acquire the size of the event. + Size = ReadUnaligned32 ((UINT32 *)EventPtr); + + // Move the pointer past the event size + EventPtr += sizeof (UINT32); + + // Verify there are EventSize bytes in the log. + if ((UINTN)(LogEnd - EventPtr) < Size) { + DEBUG ((DEBUG_ERROR, "%a: Size of event invalid\n", __func__)); + return FALSE; + } + + // Store the EventSize, if provided. + if (EventSize != NULL) { + *EventSize = Size; + } + + // Store the EventData, if provided. + if (EventData != NULL) { + *EventData = EventPtr; + } + + // Move the pointer to next Event. + EventPtr += Size; + + // Update the current event pointer. + *CurrentEvent = EventPtr; + + return TRUE; +} + +/** + Notification callback registered on gTcg2EventLogScaledGuid. Sets the BOOLEAN + pointed to by Context to TRUE. + + @param[in] Event Event handle (unused). + @param[in] Context Pointer to a BOOLEAN to flip when the event fires. +**/ +STATIC +VOID +EFIAPI +TcgLogTestScaleNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if (Context != NULL) { + *(BOOLEAN *)Context = TRUE; + } +} + +/** + Log events until Tcg2Dxe signals that the event log was dynamically scaled + or until an error is returned. + + @param[in] Tcg2Protocol TCG2 protocol instance. + @param[out] Scaled TRUE if scaling was detected. + + @retval EFI_SUCCESS Scaling detected and events logged successfully. + @retval EFI_INVALID_PARAMETER One or more invalid parameters. +**/ +EFI_STATUS +TcgLogTestLogEventsUntilScaled ( + IN EFI_TCG2_PROTOCOL *Tcg2Protocol, + OUT BOOLEAN *Scaled + ) +{ + EFI_STATUS Status; + EFI_EVENT ScaleEvent; + EFI_TCG2_EVENT *Event; + BOOLEAN LogScaled; + + // Validate the input parameters. + if ((Tcg2Protocol == NULL) || (Scaled == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *Scaled = FALSE; + LogScaled = FALSE; + Event = NULL; + + // Build a test event. + Status = TcgLogTestBuildEvent (&Event); + if (EFI_ERROR (Status)) { + return Status; + } + + // Register a notification callback for when the TCG log gets scaled. + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + TcgLogTestScaleNotify, + &LogScaled, + &gTcg2EventLogScaledGuid, + &ScaleEvent + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: CreateEventEx failed - %r\n", __func__, Status)); + goto Exit; + } + + // Log events until the scale callback fires or LogEvent returns an error. + while (!LogScaled) { + Status = TcgLogTestLogEvent (Tcg2Protocol, Event); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: LogEvent failed - %r\n", __func__, Status)); + break; + } + } + + gBS->CloseEvent (ScaleEvent); + + if (LogScaled) { + *Scaled = TRUE; + DEBUG ((DEBUG_INFO, "%a: Log scaled\n", __func__)); + Status = EFI_SUCCESS; + } + +Exit: + FreePool (Event); + return Status; +} + +/** + Log a single fixed test event via the TCG2 protocol. + + @param[in] Tcg2Protocol TCG2 protocol instance. + + @retval EFI_SUCCESS Event logged. + @retval EFI_INVALID_PARAMETER NULL argument. + @retval EFI_OUT_OF_RESOURCES Allocation failed. + @retval Other HashLogExtendEvent failure. +**/ +EFI_STATUS +TcgLogTestLogSingleEvent ( + IN EFI_TCG2_PROTOCOL *Tcg2Protocol + ) +{ + EFI_STATUS Status; + EFI_TCG2_EVENT *Event; + + if (Tcg2Protocol == NULL) { + return EFI_INVALID_PARAMETER; + } + + Event = NULL; + Status = TcgLogTestBuildEvent (&Event); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = TcgLogTestLogEvent (Tcg2Protocol, Event); + FreePool (Event); + return Status; +} diff --git a/SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.h b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.h new file mode 100644 index 0000000000..93b1370fcb --- /dev/null +++ b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.h @@ -0,0 +1,70 @@ +/** @file + TCG Log Test common function declarations shared by TcgLogTestDxe and + TcgLogTestApp. + + Copyright (c), Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef TCG_LOG_TEST_COMMON_H_ +#define TCG_LOG_TEST_COMMON_H_ + +#include +#include +#include + +/** + Advance one entry in the event log. + + @param[in,out] CurrentEvent On entry, points to the start of the event (PCRIndex). + On success, updated to point to the next event. + @param[in] LogEnd One byte past the end of the log buffer. + @param[out] PcrIndex PCRIndex of the parsed event. + @param[out] EventType EventType of the parsed event. + @param[out] EventSize Size of the event data payload. + @param[out] EventData Pointer to the event data payload. + + @retval TRUE Event parsed successfully and CurrentEvent updated. + @retval FALSE Invalid pointers, buffer, or digest algorithm. +**/ +BOOLEAN +TcgLogTestAdvanceEvent ( + IN OUT UINT8 **CurrentEvent, + IN UINT8 *LogEnd, + OUT UINT32 *PcrIndex OPTIONAL, + OUT UINT32 *EventType OPTIONAL, + OUT UINT32 *EventSize OPTIONAL, + OUT UINT8 **EventData OPTIONAL + ); + +/** + Log events via TCG2 until the event log base address changes (dynamic + scaling) or an error is returned. + + @param[in] Tcg2 TCG2 protocol instance. + @param[out] Scaled TRUE if scaling was detected. + + @retval EFI_SUCCESS Scaling detected. + @retval EFI_INVALID_PARAMETER NULL argument. +**/ +EFI_STATUS +TcgLogTestLogEventsUntilScaled ( + IN EFI_TCG2_PROTOCOL *Tcg2, + OUT BOOLEAN *Scaled + ); + +/** + Log a single fixed test event via the TCG2 protocol. + + @param[in] Tcg2 TCG2 protocol instance. + + @retval EFI_SUCCESS Event logged. + @retval EFI_INVALID_PARAMETER NULL argument. + @retval Other HashLogExtendEvent failure. +**/ +EFI_STATUS +TcgLogTestLogSingleEvent ( + IN EFI_TCG2_PROTOCOL *Tcg2 + ); + +#endif // TCG_LOG_TEST_COMMON_H_ diff --git a/SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.c b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.c new file mode 100644 index 0000000000..f27be3672c --- /dev/null +++ b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.c @@ -0,0 +1,261 @@ +/** @file + DXE driver that validates TCG2 event log dynamic scaling before ReadyToBoot. + + On entry the driver checks the NV variable TcgLogTestEnable. If not set, + the driver installs the protocol (with SetEnabled only) and returns without + running any tests. When the variable is set, the driver runs the + pre-ReadyToBoot scaling test, logs results into an internal buffer, clears + the variable, and installs the protocol so TcgLogTestApp can retrieve logs. + + Copyright (c), Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TcgLogTest.h" +#include "TcgLogTestCommon.h" + +#define TCG_LOG_TEST_MAX_LOG_SIZE 4096 + +STATIC CHAR8 mLogBuffer[TCG_LOG_TEST_MAX_LOG_SIZE]; +STATIC UINTN mLogOffset = 0; +STATIC EFI_TCG2_PROTOCOL *mTcg2Protocol = NULL; +STATIC EFI_HANDLE mTcgLogTestHandle = NULL; + +/** + Append a formatted message to the internal log buffer. + + @param[in] Format Printf-style format string. + @param[in] ... Variable arguments for the format string. +**/ +STATIC +VOID +EFIAPI +LogAppend ( + IN CONST CHAR8 *Format, + ... + ) +{ + VA_LIST Args; + UINTN Remaining; + UINTN Written; + + if (mLogOffset >= TCG_LOG_TEST_MAX_LOG_SIZE - 1) { + return; + } + + Remaining = TCG_LOG_TEST_MAX_LOG_SIZE - mLogOffset - 1; + + VA_START (Args, Format); + Written = AsciiVSPrint (mLogBuffer + mLogOffset, Remaining, Format, Args); + VA_END (Args); + + mLogOffset += Written; +} + +/** + Protocol function: retrieve the pre-ReadyToBoot test log. + + @param[in] This Protocol instance. + @param[out] LogBuffer On success, pointer to the internal log buffer. + @param[out] LogSize On success, size of the log data in bytes. + + @retval EFI_SUCCESS Log retrieved. + @retval EFI_INVALID_PARAMETER LogBuffer or LogSize is NULL. + @retval EFI_NOT_STARTED The test has not run this boot. +**/ +STATIC +EFI_STATUS +EFIAPI +TcgLogTestGetLog ( + IN TCG_LOG_TEST_PROTOCOL *This, + OUT CHAR8 **LogBuffer, + OUT UINTN *LogSize + ) +{ + if ((LogBuffer == NULL) || (LogSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (mLogOffset == 0) { + DEBUG ((DEBUG_ERROR, "%a: Log is empty.\n", __func__)); + return EFI_NOT_STARTED; + } + + *LogBuffer = mLogBuffer; + *LogSize = mLogOffset + 1; + return EFI_SUCCESS; +} + +/** + Protocol function: enable or disable the DXE test for the next boot. + + @param[in] This Protocol instance. + @param[in] Enable TRUE to enable, FALSE to disable. + + @retval EFI_SUCCESS Variable updated successfully. + @retval Other SetVariable failure. +**/ +STATIC +EFI_STATUS +EFIAPI +TcgLogTestEnable ( + IN TCG_LOG_TEST_PROTOCOL *This, + IN BOOLEAN Enable + ) +{ + BOOLEAN Dummy; + + if (Enable) { + Dummy = TRUE; + // Create the variable to signal the test should run. + return gRT->SetVariable ( + TCG_LOG_TEST_ENABLE_VARIABLE_NAME, + &gTcgLogTestProtocolGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (Dummy), + &Dummy + ); + } else { + // Delete the variable entirely. + return gRT->SetVariable ( + TCG_LOG_TEST_ENABLE_VARIABLE_NAME, + &gTcgLogTestProtocolGuid, + 0, + 0, + NULL + ); + } +} + +STATIC TCG_LOG_TEST_PROTOCOL mTcgLogTestProtocol = { + TcgLogTestGetLog, + TcgLogTestEnable +}; + +/** + Check whether the NV enable variable exists. + + @retval TRUE Variable exists (test is enabled). + @retval FALSE Variable absent. +**/ +STATIC +BOOLEAN +IsTestEnabled ( + VOID + ) +{ + EFI_STATUS Status; + BOOLEAN Value; + UINTN Size; + + Size = sizeof (Value); + Status = gRT->GetVariable ( + TCG_LOG_TEST_ENABLE_VARIABLE_NAME, + &gTcgLogTestProtocolGuid, + NULL, + &Size, + &Value + ); + + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Exercise dynamic scaling before ReadyToBoot. Verifies that scaling occurs + (Tcg2Dxe signals gTcg2EventLogScaledGuid). +**/ +STATIC +VOID +TestPreReadyToBootScaling ( + VOID + ) +{ + EFI_STATUS Status; + BOOLEAN Scaled; + + Status = TcgLogTestLogEventsUntilScaled (mTcg2Protocol, &Scaled); + if (EFI_ERROR (Status) || !Scaled) { + DEBUG ((DEBUG_ERROR, "%a: LogEventsUntilScaled failed - %r, Scaled=%d\n", __func__, Status, Scaled)); + LogAppend ("FAIL: Pre-ReadyToBoot: LogEventsUntilScaled - %r\n", Status); + return; + } + + DEBUG ((DEBUG_INFO, "%a: Pre-ReadyToBoot scaling succeeded\n", __func__)); + LogAppend ("PASS: Pre-ReadyToBoot scaling succeeded\n"); +} + +/** + Entry point for TcgLogTestDxe. + + @param[in] ImageHandle Image handle. + @param[in] SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS Driver initialized and protocol installed. +**/ +EFI_STATUS +EFIAPI +TcgLogTestDxeEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // Install the protocol so the TcgLogTestApp can enable the test and/or acquire the logs. + Status = gBS->InstallProtocolInterface ( + &mTcgLogTestHandle, + &gTcgLogTestProtocolGuid, + EFI_NATIVE_INTERFACE, + &mTcgLogTestProtocol + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: InstallProtocolInterface failed: %r\n", __func__, Status)); + return Status; + } + + DEBUG ((DEBUG_INFO, "%a: Protocol installed, checking enable state\n", __func__)); + + // Only run the test if it has been enabled. + if (!IsTestEnabled ()) { + DEBUG ((DEBUG_INFO, "%a: Test not enabled, skipping\n", __func__)); + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_INFO, "%a: Test enabled, running pre-ReadyToBoot test\n", __func__)); + + // Clear the enable variable so we don't re-run on the next boot. + Status = TcgLogTestEnable (&mTcgLogTestProtocol, FALSE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to clear enable variable: %r\n", __func__, Status)); + } + + Status = gBS->LocateProtocol (&gEfiTcg2ProtocolGuid, NULL, (VOID **)&mTcg2Protocol); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to locate TCG2 protocol: %r\n", __func__, Status)); + LogAppend ("FAIL: TCG2 protocol not found - %r\n", Status); + return EFI_SUCCESS; + } + + // Run the pre-ReadyToBoot scaling test. + TestPreReadyToBootScaling (); + + return EFI_SUCCESS; +} diff --git a/SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf new file mode 100644 index 0000000000..ead24bcd12 --- /dev/null +++ b/SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf @@ -0,0 +1,50 @@ +## @file +# DXE driver that validates TCG2 event log dynamic scaling before ReadyToBoot. +# +# Checks an NV variable to determine whether to run. If enabled, exercises +# pre-ReadyToBoot log scaling and records results via a local protocol that +# TcgLogTestApp retrieves post-boot. +# +# Copyright (c), Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = TcgLogTestDxe + FILE_GUID = B4D2F8A7-3E16-4C9B-A1F0-7D5E9B2C4A81 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = TcgLogTestDxeEntry + +[Sources] + TcgLogTestDxe.c + TcgLogTestCommon.c + TcgLogTestCommon.h + TcgLogTest.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + PrintLib + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiLib + UefiRuntimeServicesTableLib + +[Protocols] + gEfiTcg2ProtocolGuid ## CONSUMES + gTcgLogTestProtocolGuid ## PRODUCES + +[Guids] + gTcg2EventLogScaledGuid ## CONSUMES ## Event + +[Depex] + gEfiVariableArchProtocolGuid AND gEfiTcg2ProtocolGuid