mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
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. 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. Added an ACPI log region for the ACPI table LAML/LASA. This region does not scale and can become truncated indicated by the Truncation event marker. Signed-off-by: Raymond Diaz <raymonddiaz@microsoft.com>
This commit is contained in:
parent
ef437b30ad
commit
ffaaf0f14c
14 changed files with 2482 additions and 53 deletions
17
SecurityPkg/Include/Guid/Tcg2EventLogScaled.h
Normal file
17
SecurityPkg/Include/Guid/Tcg2EventLogScaled.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/** @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
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define TCG2_EVENT_LOG_SCALED_GUID \
|
||||
{ \
|
||||
0x9b4f7c2a, 0x1d3e, 0x4a8b, { 0x9c, 0x6f, 0x5e, 0x8d, 0x2a, 0x1b, 0x4c, 0x7f } \
|
||||
}
|
||||
|
||||
extern EFI_GUID gTcg2EventLogScaledGuid;
|
||||
|
|
@ -164,6 +164,10 @@
|
|||
## Include/Guid/TcgEventHob.h
|
||||
gTcg800155PlatformIdEventHobGuid = { 0xe2c3bc69, 0x615c, 0x4b5b, { 0x8e, 0x5c, 0xa0, 0x33, 0xa9, 0xc2, 0x5e, 0xd6 }}
|
||||
|
||||
## 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 }}
|
||||
|
|
@ -255,6 +259,12 @@
|
|||
# Include/Guid/Tpm2ServiceFfa.h
|
||||
gTpm2ServiceFfaGuid = { 0x17b862a4, 0x1806, 0x4faf, { 0x86, 0xb3, 0x08, 0x9a, 0x58, 0x35, 0x38, 0x61 } }
|
||||
|
||||
[Protocols]
|
||||
## 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 }}
|
||||
|
||||
[Ppis]
|
||||
## The PPI GUID for that TPM physical presence should be locked.
|
||||
# Include/Ppi/LockPhysicalPresence.h
|
||||
|
|
|
|||
|
|
@ -254,6 +254,14 @@
|
|||
SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLib/PeiDxeTpmPlatformHierarchyLib.inf
|
||||
SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLibNull/PeiDxeTpmPlatformHierarchyLib.inf
|
||||
|
||||
SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf
|
||||
SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf {
|
||||
<LibraryClasses>
|
||||
UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
|
||||
UnitTestPersistenceLib|UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
|
||||
UnitTestResultReportLib|UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
|
||||
}
|
||||
|
||||
#
|
||||
# TCG Storage.
|
||||
#
|
||||
|
|
|
|||
148
SecurityPkg/Tcg/Tcg2Dxe/README.md
Normal file
148
SecurityPkg/Tcg/Tcg2Dxe/README.md
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# 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 |
|
||||
| **ACPI event 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.
|
||||
- The **ACPI event log** is a fixed-size mirror of the normal log, allocated
|
||||
in `EfiACPIMemoryNVS` and sized from `PcdTcgLogAreaMinLen`. Because this region
|
||||
does not scale, it can become truncated. Its address and length are published
|
||||
to `PcdTpm2AcpiTableLasa` and `PcdTpm2AcpiTableLaml` so `Tcg2Acpi` picks them
|
||||
up when populating the TPM2 ACPI table.
|
||||
|
||||
### 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"`.
|
||||
|
||||
## ACPI Event Log Truncation Marker
|
||||
|
||||
The **ACPI event log** uses the same truncation-marker mechanism as
|
||||
FinalEventLog. Because it is fixed-size and does not scale, `TcgDxeLogEvent`
|
||||
appends an `EV_NO_ACTION` truncation event whose payload is
|
||||
`TCG_LOG_TRUNCATION_EVENT_STRING` when the next event would overflow the
|
||||
region. `EventLogTruncated` is then set so subsequent writes silently skip the
|
||||
ACPI region without re-appending the marker. Unlike FinalEventLog, the
|
||||
truncated ACPI region does **not** cause `HashLogExtendEvent` to return
|
||||
`EFI_VOLUME_FULL`.
|
||||
|
||||
To guarantee the marker always fits, the ACPI event log initialization in
|
||||
`SetupEventLog` subtracts `GetTruncationEventSize()` from the usable `Laml`:
|
||||
|
||||
```c
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].Laml =
|
||||
PcdGet32 (PcdTcgLogAreaMinLen) - GetTruncationEventSize ();
|
||||
```
|
||||
|
||||
Truncation can be detected by walking the region and inspecting the last
|
||||
entry for the `"TCG Event Log Truncated"` payload.
|
||||
|
|
@ -15,6 +15,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
|
|||
#include <Guid/GlobalVariable.h>
|
||||
#include <Guid/HobList.h>
|
||||
#include <Guid/TcgEventHob.h>
|
||||
#include <Guid/Tcg2EventLogScaled.h>
|
||||
#include <Guid/EventGroup.h>
|
||||
#include <Guid/EventExitBootServiceFailed.h>
|
||||
#include <Guid/ImageAuthentication.h>
|
||||
|
|
@ -67,6 +68,13 @@ TCG2_EVENT_INFO_STRUCT mTcg2EventInfo[] = {
|
|||
|
||||
#define TCG_EVENT_LOG_AREA_COUNT_MAX 2
|
||||
|
||||
// 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"
|
||||
|
||||
typedef struct {
|
||||
EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat;
|
||||
EFI_PHYSICAL_ADDRESS Lasa;
|
||||
|
|
@ -76,6 +84,7 @@ typedef struct {
|
|||
BOOLEAN EventLogStarted;
|
||||
BOOLEAN EventLogTruncated;
|
||||
UINTN Next800155EventOffset;
|
||||
UINTN ScaleCount;
|
||||
} TCG_EVENT_LOG_AREA_STRUCT;
|
||||
|
||||
// Mapping of TPM return status to BIOS/OS TPM support and related flags (TPMPresentFlag, TpmUpdateFlag)
|
||||
|
|
@ -93,6 +102,7 @@ typedef struct _TCG_DXE_DATA {
|
|||
BOOLEAN GetEventLogCalled[TCG_EVENT_LOG_AREA_COUNT_MAX];
|
||||
TCG_EVENT_LOG_AREA_STRUCT FinalEventLogAreaStruct[TCG_EVENT_LOG_AREA_COUNT_MAX];
|
||||
EFI_TCG2_FINAL_EVENTS_TABLE *FinalEventsTable[TCG_EVENT_LOG_AREA_COUNT_MAX];
|
||||
TCG_EVENT_LOG_AREA_STRUCT AcpiEventLogAreaStruct[TCG_EVENT_LOG_AREA_COUNT_MAX];
|
||||
} TCG_DXE_DATA;
|
||||
|
||||
TCG_DXE_DATA mTcgDxeData = {
|
||||
|
|
@ -124,6 +134,7 @@ VARIABLE_TYPE mVariableType[] = {
|
|||
};
|
||||
|
||||
EFI_HANDLE mImageHandle;
|
||||
BOOLEAN mReadyToBoot = FALSE;
|
||||
|
||||
/**
|
||||
Measure PE image into TPM log based on the authenticode image hashing in
|
||||
|
|
@ -930,6 +941,128 @@ TcgCommLogEvent (
|
|||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
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;
|
||||
UINT8 *Current;
|
||||
|
||||
Current = (UINT8 *)DigestListBin;
|
||||
Count = ReadUnaligned32 ((UINT32 *)Current);
|
||||
TotalSize = sizeof (Count);
|
||||
Current += sizeof (Count);
|
||||
for (Index = 0; Index < Count; Index++) {
|
||||
HashAlg = ReadUnaligned16 ((UINT16 *)Current);
|
||||
TotalSize += sizeof (HashAlg);
|
||||
Current += sizeof (HashAlg);
|
||||
|
||||
DigestSize = GetHashSizeFromAlgo (HashAlg);
|
||||
TotalSize += DigestSize;
|
||||
Current += 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;
|
||||
}
|
||||
|
||||
/**
|
||||
Add a new entry to the Event Log.
|
||||
|
||||
|
|
@ -1015,6 +1148,11 @@ TcgDxeLogEvent (
|
|||
NewEventSize
|
||||
);
|
||||
if (Status == EFI_OUT_OF_RESOURCES) {
|
||||
Status = AppendTruncationMarker (EventLogAreaStruct);
|
||||
if (!EFI_ERROR (Status)) {
|
||||
(mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents++;
|
||||
}
|
||||
|
||||
EventLogAreaStruct->EventLogTruncated = TRUE;
|
||||
return EFI_VOLUME_FULL;
|
||||
} else if (Status == EFI_SUCCESS) {
|
||||
|
|
@ -1028,41 +1166,34 @@ TcgDxeLogEvent (
|
|||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
//
|
||||
// Also record to the fixed-size ACPI event log region.
|
||||
//
|
||||
EventLogAreaStruct = &mTcgDxeData.AcpiEventLogAreaStruct[Index];
|
||||
|
||||
/**
|
||||
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 = Tpm2GetHashSizeFromAlgo (HashAlg);
|
||||
TotalSize += DigestSize;
|
||||
DigestListBin = (UINT8 *)DigestListBin + DigestSize;
|
||||
if (EventLogAreaStruct->Lasa == 0) {
|
||||
// No need to handle ACPI event log region.
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return TotalSize;
|
||||
if (!EventLogAreaStruct->EventLogTruncated) {
|
||||
Status = TcgCommLogEvent (
|
||||
EventLogAreaStruct,
|
||||
NewEventHdr,
|
||||
NewEventHdrSize,
|
||||
NewEventData,
|
||||
NewEventSize
|
||||
);
|
||||
if (Status == EFI_OUT_OF_RESOURCES) {
|
||||
AppendTruncationMarker (EventLogAreaStruct);
|
||||
EventLogAreaStruct->EventLogTruncated = TRUE;
|
||||
Status = EFI_SUCCESS;
|
||||
} else if (Status == EFI_SUCCESS) {
|
||||
EventLogAreaStruct->EventLogStarted = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1121,6 +1252,153 @@ CopyDigestListBinToBuffer (
|
|||
return Buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
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)\n", __func__, TCG_EVENT_LOG_MAX_SCALE_COUNT));
|
||||
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));
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
Add a new entry to the Event Log.
|
||||
|
||||
|
|
@ -1145,6 +1423,7 @@ TcgDxeLogHashEvent (
|
|||
TCG_PCR_EVENT2 TcgPcrEvent2;
|
||||
UINT8 *DigestBuffer;
|
||||
UINT32 *EventSizePtr;
|
||||
BOOLEAN DynamicScalingNeeded;
|
||||
|
||||
RetStatus = EFI_SUCCESS;
|
||||
for (Index = 0; Index < sizeof (mTcg2EventInfo)/sizeof (mTcg2EventInfo[0]); Index++) {
|
||||
|
|
@ -1183,6 +1462,27 @@ TcgDxeLogHashEvent (
|
|||
EventSizePtr = Tpm2CopyDigestListToBuffer (DigestBuffer, DigestList, mTcgDxeData.BsCap.ActivePcrBanks);
|
||||
CopyMem (EventSizePtr, &NewEventHdr->EventSize, sizeof (NewEventHdr->EventSize));
|
||||
|
||||
// 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)) {
|
||||
mTcgDxeData.EventLogAreaStruct[Index].EventLogTruncated = TRUE;
|
||||
DEBUG ((DEBUG_ERROR, "Unable to scale the TCG event log!\n"));
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Enter critical region
|
||||
//
|
||||
|
|
@ -1622,26 +1922,71 @@ SetupEventLog (
|
|||
DEBUG ((DEBUG_INFO, "SetupEventLog\n"));
|
||||
|
||||
//
|
||||
// 1. Create Log Area
|
||||
// 0. Create ACPI Log Area.
|
||||
//
|
||||
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) {
|
||||
if ((PcdGet8 (PcdTpm2AcpiTableRev) >= 4) &&
|
||||
(mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2))
|
||||
{
|
||||
// PcdTcgLogAreaMinLen is platform-configurable; ensure it has room for the
|
||||
// EV_NO_ACTION truncation marker event.
|
||||
if (PcdGet32 (PcdTcgLogAreaMinLen) < GetTruncationEventSize ()) {
|
||||
DEBUG ((DEBUG_ERROR, "Insufficient AcpiEventLogArea length\n"));
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Status = gBS->AllocatePages (
|
||||
AllocateAnyPages,
|
||||
EfiACPIMemoryNVS,
|
||||
EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)),
|
||||
&Lasa
|
||||
);
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
SetMem ((VOID *)(UINTN)Lasa, PcdGet32 (PcdTcgLogAreaMinLen), 0xFF);
|
||||
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].Lasa = Lasa;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcgLogAreaMinLen) - GetTruncationEventSize ();
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogSize = 0;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].LastEvent = (VOID *)(UINTN)Lasa;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogStarted = FALSE;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogTruncated = FALSE;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].Next800155EventOffset = 0;
|
||||
|
||||
PcdSet32S (PcdTpm2AcpiTableLaml, PcdGet32 (PcdTcgLogAreaMinLen));
|
||||
PcdSet64S (PcdTpm2AcpiTableLasa, mTcgDxeData.AcpiEventLogAreaStruct[Index].Lasa);
|
||||
} else {
|
||||
Status = gBS->AllocatePages (
|
||||
AllocateAnyPages,
|
||||
EfiBootServicesData,
|
||||
EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)),
|
||||
&Lasa
|
||||
);
|
||||
// No need to handle EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 or if PcdTpm2AcpiTableRev < 4
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].Lasa = 0;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].Laml = 0;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogSize = 0;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].LastEvent = 0;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogStarted = FALSE;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogTruncated = FALSE;
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].Next800155EventOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 1. Create Log Area
|
||||
//
|
||||
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;
|
||||
|
||||
Status = gBS->AllocatePages (
|
||||
AllocateAnyPages,
|
||||
EfiBootServicesData,
|
||||
EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)),
|
||||
&Lasa
|
||||
);
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
|
|
@ -1650,17 +1995,7 @@ SetupEventLog (
|
|||
mTcgDxeData.EventLogAreaStruct[Index].Lasa = Lasa;
|
||||
mTcgDxeData.EventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcgLogAreaMinLen);
|
||||
mTcgDxeData.EventLogAreaStruct[Index].Next800155EventOffset = 0;
|
||||
|
||||
if ((PcdGet8 (PcdTpm2AcpiTableRev) >= 4) ||
|
||||
(mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2))
|
||||
{
|
||||
//
|
||||
// Report TCG2 event log address and length, so that they can be reported in TPM2 ACPI table.
|
||||
// Ignore the return status, because those fields are optional.
|
||||
//
|
||||
PcdSet32S (PcdTpm2AcpiTableLaml, (UINT32)mTcgDxeData.EventLogAreaStruct[Index].Laml);
|
||||
PcdSet64S (PcdTpm2AcpiTableLasa, mTcgDxeData.EventLogAreaStruct[Index].Lasa);
|
||||
}
|
||||
mTcgDxeData.EventLogAreaStruct[Index].ScaleCount = 0;
|
||||
|
||||
//
|
||||
// To initialize them as 0xFF is recommended
|
||||
|
|
@ -1753,6 +2088,9 @@ SetupEventLog (
|
|||
mTcgDxeData.EventLogAreaStruct[Index].Next800155EventOffset = \
|
||||
mTcgDxeData.EventLogAreaStruct[Index].EventLogSize;
|
||||
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].Next800155EventOffset = \
|
||||
mTcgDxeData.AcpiEventLogAreaStruct[Index].EventLogSize;
|
||||
|
||||
//
|
||||
// Tcg800155PlatformIdEvent. Event format is TCG_PCR_EVENT2
|
||||
//
|
||||
|
|
@ -1811,6 +2149,13 @@ 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) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
Status = gBS->AllocatePages (
|
||||
AllocateAnyPages,
|
||||
EfiACPIMemoryNVS,
|
||||
|
|
@ -1832,7 +2177,7 @@ SetupEventLog (
|
|||
|
||||
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].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;
|
||||
|
|
@ -2502,6 +2847,9 @@ OnReadyToBoot (
|
|||
TPM_PCRINDEX PcrIndex;
|
||||
|
||||
PERF_FUNCTION_BEGIN ();
|
||||
|
||||
mReadyToBoot = TRUE;
|
||||
|
||||
if (mBootAttempts == 0) {
|
||||
//
|
||||
// Measure handoff tables.
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
gTpm2StartupLocalityHobGuid ## SOMETIMES_CONSUMES ## HOB
|
||||
gTcg800155PlatformIdEventHobGuid ## SOMETIMES_CONSUMES ## HOB
|
||||
gEfiDeviceSignatureDatabaseGuid
|
||||
gTcg2EventLogScaledGuid ## SOMETIMES_PRODUCES ## Event
|
||||
|
||||
[Protocols]
|
||||
gEfiTcg2ProtocolGuid ## PRODUCES
|
||||
|
|
|
|||
279
SecurityPkg/Tcg/TcgLogTest/README.md
Normal file
279
SecurityPkg/Tcg/TcgLogTest/README.md
Normal file
|
|
@ -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.
|
||||
64
SecurityPkg/Tcg/TcgLogTest/TcgLogTest.h
Normal file
64
SecurityPkg/Tcg/TcgLogTest/TcgLogTest.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/** @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
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Uefi.h>
|
||||
|
||||
#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;
|
||||
758
SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.c
Normal file
758
SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.c
Normal file
|
|
@ -0,0 +1,758 @@
|
|||
/** @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 <Uefi.h>
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
#include <Library/UefiApplicationEntryPoint.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Library/UefiLib.h>
|
||||
#include <Library/UefiRuntimeServicesTableLib.h>
|
||||
#include <Library/UnitTestLib.h>
|
||||
#include <Protocol/Tcg2Protocol.h>
|
||||
#include <IndustryStandard/Acpi.h>
|
||||
#include <IndustryStandard/Tpm2Acpi.h>
|
||||
|
||||
#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;
|
||||
|
||||
/**
|
||||
Locate the fixed-size ACPI TCG event log region published by Tcg2Dxe via
|
||||
the TPM2 ACPI table LAML/LASA fields.
|
||||
|
||||
The fields only exist for revision >= 4; the Header.Length must include
|
||||
them or they are considered absent.
|
||||
|
||||
@param[out] Lasa On success, physical address of the ACPI event log region.
|
||||
@param[out] Laml On success, length in bytes of the ACPI event log region.
|
||||
|
||||
@retval EFI_SUCCESS ACPI event log located.
|
||||
@retval EFI_INVALID_PARAMETER NULL argument.
|
||||
@retval EFI_NOT_FOUND TPM2 ACPI table not present, revision < 4,
|
||||
header length excludes LAML/LASA, or the
|
||||
fields are zero.
|
||||
**/
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
GetAcpiEventLog (
|
||||
OUT EFI_PHYSICAL_ADDRESS *Lasa,
|
||||
OUT UINT32 *Laml
|
||||
)
|
||||
{
|
||||
EFI_ACPI_DESCRIPTION_HEADER *Header;
|
||||
EFI_TPM2_ACPI_TABLE_V4 *TableV4;
|
||||
EFI_TPM2_ACPI_TABLE_V5 *TableV5;
|
||||
UINT32 FoundLaml;
|
||||
UINT64 FoundLasa;
|
||||
|
||||
if ((Lasa == NULL) || (Laml == NULL)) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Header = (EFI_ACPI_DESCRIPTION_HEADER *)EfiLocateFirstAcpiTable (
|
||||
EFI_ACPI_5_0_TRUSTED_COMPUTING_PLATFORM_2_TABLE_SIGNATURE
|
||||
);
|
||||
if (Header == NULL) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: TPM2 ACPI table not found\n", __func__));
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
// LAML/LASA are optional and only exist for revision 4 and above. Confirm
|
||||
// the table's Header.Length is large enough to include those trailing fields
|
||||
// before dereferencing them.
|
||||
switch (Header->Revision) {
|
||||
case EFI_TPM2_ACPI_TABLE_REVISION_4:
|
||||
if (Header->Length < sizeof (EFI_TPM2_ACPI_TABLE_V4)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: TPM2 ACPI rev4 length %u < %u\n", __func__, Header->Length, (UINT32)sizeof (EFI_TPM2_ACPI_TABLE_V4)));
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
TableV4 = (EFI_TPM2_ACPI_TABLE_V4 *)Header;
|
||||
FoundLaml = TableV4->Laml;
|
||||
FoundLasa = TableV4->Lasa;
|
||||
break;
|
||||
|
||||
case EFI_TPM2_ACPI_TABLE_REVISION_5:
|
||||
if (Header->Length < sizeof (EFI_TPM2_ACPI_TABLE_V5)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: TPM2 ACPI rev5 length %u < %u\n", __func__, Header->Length, (UINT32)sizeof (EFI_TPM2_ACPI_TABLE_V5)));
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
TableV5 = (EFI_TPM2_ACPI_TABLE_V5 *)Header;
|
||||
FoundLaml = TableV5->Laml;
|
||||
FoundLasa = TableV5->Lasa;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG ((DEBUG_ERROR, "%a: TPM2 ACPI revision %u does not carry LAML/LASA\n", __func__, Header->Revision));
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
if ((FoundLaml == 0) || (FoundLasa == 0)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: TPM2 ACPI LAML/LASA are zero\n", __func__));
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
*Lasa = (EFI_PHYSICAL_ADDRESS)FoundLasa;
|
||||
*Laml = FoundLaml;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify the ACPI event log region contains same events as the normal event log.
|
||||
|
||||
@param[in] Context Unit test context (unused).
|
||||
|
||||
@retval UNIT_TEST_PASSED Contents match.
|
||||
@retval UNIT_TEST_ERROR_TEST_FAILED Assertion failed.
|
||||
**/
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestAcpiEventLogMirrorsNormalLog (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_PHYSICAL_ADDRESS AcpiLasa;
|
||||
UINT32 AcpiLaml;
|
||||
EFI_PHYSICAL_ADDRESS LogLocation;
|
||||
EFI_PHYSICAL_ADDRESS LastEntry;
|
||||
BOOLEAN Truncated;
|
||||
UINT8 *LogPtr;
|
||||
UINTN NormalSize;
|
||||
|
||||
Status = GetAcpiEventLog (&AcpiLasa, &AcpiLaml);
|
||||
UT_ASSERT_NOT_EFI_ERROR (Status);
|
||||
|
||||
Status = mTcg2Protocol->GetEventLog (
|
||||
mTcg2Protocol,
|
||||
EFI_TCG2_EVENT_LOG_FORMAT_TCG_2,
|
||||
&LogLocation,
|
||||
&LastEntry,
|
||||
&Truncated
|
||||
);
|
||||
|
||||
UT_ASSERT_NOT_EFI_ERROR (Status);
|
||||
UT_ASSERT_FALSE (Truncated);
|
||||
|
||||
LogPtr = (UINT8 *)(UINTN)LastEntry;
|
||||
if (!TcgLogTestAdvanceEvent (
|
||||
&LogPtr,
|
||||
(UINT8 *)(UINTN)MAX_ADDRESS,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
))
|
||||
{
|
||||
UT_LOG_ERROR ("AdvanceEvent past LastEntry failed\n");
|
||||
return UNIT_TEST_ERROR_TEST_FAILED;
|
||||
}
|
||||
|
||||
NormalSize = (UINTN)LogPtr - (UINTN)LogLocation;
|
||||
UT_LOG_INFO ("Normal log: base=0x%lx size=%u; ACPI region: base=0x%lx size=%u\n", LogLocation, NormalSize, AcpiLasa, AcpiLaml);
|
||||
|
||||
// The normal log must fit within the ACPI region for the mirror test to be
|
||||
// meaningful; otherwise the ACPI region has already truncated and this test
|
||||
// cannot make a byte-for-byte comparison.
|
||||
UT_ASSERT_TRUE (NormalSize <= AcpiLaml);
|
||||
UT_ASSERT_MEM_EQUAL ((VOID *)(UINTN)AcpiLasa, (VOID *)(UINTN)LogLocation, NormalSize);
|
||||
|
||||
return UNIT_TEST_PASSED;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify that when the normal event log scales the ACPI region receives
|
||||
an EV_NO_ACTION truncation marker as its final event. Walks the ACPI
|
||||
region event-by-event, tracks the last valid event, and asserts that
|
||||
it is EV_NO_ACTION carrying truncation event marker.
|
||||
|
||||
@param[in] Context Unit test context (unused).
|
||||
|
||||
@retval UNIT_TEST_PASSED Truncation marker located.
|
||||
@retval UNIT_TEST_ERROR_TEST_FAILED Assertion failed.
|
||||
**/
|
||||
UNIT_TEST_STATUS
|
||||
EFIAPI
|
||||
TestAcpiEventLogTruncationMarker (
|
||||
IN UNIT_TEST_CONTEXT Context
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_PHYSICAL_ADDRESS AcpiLasa;
|
||||
UINT32 AcpiLaml;
|
||||
UINT8 *EventPtr;
|
||||
UINT8 *RegionEnd;
|
||||
UINT32 PcrIndex;
|
||||
UINT32 EventType;
|
||||
UINT32 EventSize;
|
||||
UINT8 *EventData;
|
||||
UINT32 LastType;
|
||||
UINT32 LastSize;
|
||||
UINT8 *LastData;
|
||||
UINTN EventCount;
|
||||
|
||||
Status = GetAcpiEventLog (&AcpiLasa, &AcpiLaml);
|
||||
UT_ASSERT_NOT_EFI_ERROR (Status);
|
||||
|
||||
EventPtr = (UINT8 *)(UINTN)AcpiLasa;
|
||||
RegionEnd = EventPtr + AcpiLaml;
|
||||
LastType = 0;
|
||||
LastSize = 0;
|
||||
LastData = NULL;
|
||||
EventCount = 0;
|
||||
|
||||
while (TcgLogTestAdvanceEvent (&EventPtr, RegionEnd, &PcrIndex, &EventType, &EventSize, &EventData)) {
|
||||
LastType = EventType;
|
||||
LastSize = EventSize;
|
||||
LastData = EventData;
|
||||
EventCount++;
|
||||
|
||||
if (EventPtr >= RegionEnd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UT_LOG_INFO ("ACPI region events walked: %u, last type=0x%x size=%u\n", EventCount, LastType, LastSize);
|
||||
UT_ASSERT_TRUE (EventCount > 0);
|
||||
UT_ASSERT_NOT_NULL (LastData);
|
||||
|
||||
// The last valid event in the ACPI region must be the truncation marker.
|
||||
UT_ASSERT_EQUAL (LastType, EV_NO_ACTION);
|
||||
UT_ASSERT_TRUE (LastSize >= sizeof (TCG_LOG_TRUNCATION_EVENT_STRING));
|
||||
UT_ASSERT_MEM_EQUAL (LastData, TCG_LOG_TRUNCATION_EVENT_STRING, sizeof (TCG_LOG_TRUNCATION_EVENT_STRING));
|
||||
|
||||
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, "ACPI event log mirrors normal log", "AcpiLogMirrors", TestAcpiEventLogMirrorsNormalLog, LocateProtocols, SaveAndReboot, NULL);
|
||||
AddTestCase (Suite, "Post-ReadyToBoot scaling succeeds", "PostRtbScaling", TestPostReadyToBootScaling, LocateProtocols, NULL, NULL);
|
||||
AddTestCase (Suite, "ACPI event log gets truncation marker", "AcpiLogTruncates", TestAcpiEventLogTruncationMarker, 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;
|
||||
}
|
||||
46
SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf
Normal file
46
SecurityPkg/Tcg/TcgLogTest/TcgLogTestApp.inf
Normal file
|
|
@ -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
|
||||
372
SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.c
Normal file
372
SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.c
Normal file
|
|
@ -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 <Uefi.h>
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Protocol/Tcg2Protocol.h>
|
||||
#include <Guid/Tcg2EventLogScaled.h>
|
||||
#include <IndustryStandard/UefiTcgPlatform.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
67
SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.h
Normal file
67
SecurityPkg/Tcg/TcgLogTest/TcgLogTestCommon.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/** @file
|
||||
TCG Log Test common function declarations shared by TcgLogTestDxe and
|
||||
TcgLogTestApp.
|
||||
|
||||
Copyright (c), Microsoft Corporation.
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Uefi.h>
|
||||
#include <Protocol/Tcg2Protocol.h>
|
||||
#include <IndustryStandard/UefiTcgPlatform.h>
|
||||
|
||||
/**
|
||||
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
|
||||
);
|
||||
261
SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.c
Normal file
261
SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.c
Normal file
|
|
@ -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 <Uefi.h>
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
#include <Library/PrintLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Library/UefiDriverEntryPoint.h>
|
||||
#include <Library/UefiLib.h>
|
||||
#include <Library/UefiRuntimeServicesTableLib.h>
|
||||
#include <Protocol/Tcg2Protocol.h>
|
||||
#include <IndustryStandard/UefiTcgPlatform.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
50
SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf
Normal file
50
SecurityPkg/Tcg/TcgLogTest/TcgLogTestDxe.inf
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue