/** @file
TcgDxeplatform - TCG Platform DXE Driver
This DXE driver initializes the TCG platform protocol, retrieves
the TCG platform protocol interface via HOB list and LocateProtocol,
and dispatches TCG callbacks for PPI request processing, TCG setup
configuration, PCI option ROM measurement, and Ready-to-Boot
notification.
The driver performs the following:
1. Stores ImageHandle, SystemTable, BootServices, RuntimeServices
into their standard UEFI global variables (with ASSERTs).
2. Locates the HOB list from the system configuration table using
gEfiDxeServicesTableGuid.
3. Retrieves the PCD protocol and reads the PCI Express memory-mapped
base address (PCD token 5).
4. Optionally sets the PCI Express capability register to 0x500.
5. Performs a short TSC-based spin-wait delay (port 0x508) while
preserving the interrupt flag state.
6. Parses the HOB list (backwards from the configuration table)
for a GUID Extension HOB containing the TCG platform protocol
pointer (GUID: {BFB01142-3061-48A4-922F-9D246E201120}).
7. Locates the TCG platform protocol via gBS->LocateProtocol
(GUID: {8C939604-0700-4415-9D62-1161DB8164A6}).
8. Dispatches four callbacks on the protocol:
- Initialize (notify of first dispatch, if revision == 1)
- ProcessPpiRequest
- ProcessTcgSetup
- MeasurePciOptionRoms
- ReadyToBootCallback
Copyright (C) 2025, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "TcgDxeplatform.h"
//
// Global variables
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gST = NULL;
EFI_BOOT_SERVICES *gBS = NULL;
EFI_RUNTIME_SERVICES *gRT = NULL;
VOID *mHobList = NULL;
UINT64 mPciExpressBaseAddress = 0;
PCD_PROTOCOL *mPcdProtocol = NULL;
REPORT_STATUS_CODE_HANDLER_PROTOCOL *mReportStatusCodeHandler = NULL;
//
// GUID used for HOB search (TCG platform protocol HOB GUID)
//
STATIC CONST GUID mTcgPlatformHobGuid = TCG_PLATFORM_HOB_GUID;
//
// GUID used for LocateProtocol (TCG platform protocol GUID)
//
STATIC CONST GUID mTcgPlatformProtocolGuid = TCG_PLATFORM_PROTOCOL_GUID;
//
// GUID: gEfiDxeServicesTableGuid
// {7739F24C-93D7-11D4-9A3A-0090273FC14D}
//
STATIC CONST GUID mEfiDxeServicesTableGuid = EFI_DXE_SERVICES_TABLE_GUID;
/**
Compares two memory buffers byte-by-byte.
Returns the difference between the first non-matching bytes
(Destination - Source) cast to INTN.
@param[in] DestinationBuffer Pointer to the first buffer.
@param[in] SourceBuffer Pointer to the second buffer.
@param[in] Length Number of bytes to compare.
@return Zero if buffers are equal, non-zero otherwise.
**/
INTN
CompareMem (
IN CONST VOID *DestinationBuffer,
IN CONST VOID *SourceBuffer,
IN UINTN Length
)
{
CONST UINT8 *Dest;
CONST UINT8 *Src;
Dest = (CONST UINT8 *)DestinationBuffer;
Src = (CONST UINT8 *)SourceBuffer;
while (Length > 0 && *Dest == *Src) {
Dest++;
Src++;
Length--;
}
return (Length == 0) ? 0
: ((INTN)(*(Dest - 1) - *(Src - 1)));
}
/**
Reads an unaligned UINT64 from memory.
@param[in] Buffer Pointer to the memory location. Must not be NULL.
@return The UINT64 value read.
**/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(CONST UINT64 *)Buffer;
}
/**
Wrapper for CompareMem with parameter validation in debug builds.
Asserts that both buffers are non-NULL and that the comparison length
does not overflow past the end of either buffer.
@param[in] DestinationBuffer Pointer to the destination buffer.
@param[in] SourceBuffer Pointer to the source buffer.
@param[in] Length Number of bytes to compare.
@return Zero if buffers are identical, non-zero otherwise.
**/
INTN
CompareMemWrapper (
IN CONST VOID *DestinationBuffer,
IN CONST VOID *SourceBuffer,
IN UINTN Length
)
{
if (DestinationBuffer == SourceBuffer) {
return 0;
}
ASSERT (DestinationBuffer != NULL);
ASSERT (SourceBuffer != NULL);
ASSERT ((Length - 1) <= (MAX_UINTN - (UINTN)DestinationBuffer));
ASSERT ((Length - 1) <= (MAX_UINTN - (UINTN)SourceBuffer));
return CompareMem (DestinationBuffer, SourceBuffer, Length);
}
/**
Reads a 32-bit value from the specified I/O port.
Asserts that the port is 4-byte aligned.
@param[in] Port The I/O port number.
@return The 32-bit value read from the I/O port.
**/
UINT32
IoRead32 (
IN UINT16 Port
)
{
ASSERT ((Port & 3) == 0);
return IoRead32 (Port);
}
/**
Retrieves the singleton REPORT_STATUS_CODE_HANDLER_PROTOCOL.
Cached after first successful lookup. Returns NULL if the protocol
cannot be located or the system has more than 16 bytes of low memory
(indicating an environment where the protocol is not available).
@return A pointer to the REPORT_STATUS_CODE_HANDLER_PROTOCOL, or NULL.
**/
REPORT_STATUS_CODE_HANDLER_PROTOCOL *
GetReportStatusCodeHandler (
VOID
)
{
EFI_STATUS Status;
if (mReportStatusCodeHandler == NULL) {
//
// Check if we have more than 16 bytes of memory per index 31;
// if so, this is likely an SMM environment and we should not
// attempt to locate the protocol.
//
if (IoRead32 (31) > 0x10) {
return NULL;
}
Status = gBS->LocateProtocol (
&gEfiStatusCodeRuntimeProtocolGuid,
NULL,
(VOID **)&mReportStatusCodeHandler
);
if (EFI_ERROR (Status)) {
mReportStatusCodeHandler = NULL;
}
}
return mReportStatusCodeHandler;
}
/**
ASSERT helper: reports assertion failure via status code handler.
Called when an ASSERT condition evaluates to FALSE. Uses the status
code handler protocol to report the assertion if available.
@param[in] FileName Source file name string.
@param[in] LineNumber Line number in the source file.
@param[in] AssertText The ASSERT condition text.
**/
VOID
EFIAPI
AssertReport (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *AssertText
)
{
REPORT_STATUS_CODE_HANDLER_PROTOCOL *Handler;
Handler = GetReportStatusCodeHandler ();
if (Handler != NULL) {
Handler->ReportStatusCode (
PLATFORM_ERROR | EFI_ERROR_MAJOR,
FileName,
LineNumber,
AssertText
);
}
}
/**
Reports a status code via the status code handler, with platform-type
validation.
Reads CMOS index 0x4B to determine the platform type. On platforms
where the type value is > 3 or == 0, a hardware register at
0xFEDAF0490 is consulted. The status code is only reported if it
matches the platform type mask:
- Type 1: mask = 0x80000004
- Type 4: mask = 0x80000006
@param[in] Code The status code value.
@param[in] Format Format string for extended data.
@param[in] ... Variable arguments for the format string.
@retval TRUE Status code was reported.
@retval FALSE Status code was suppressed.
**/
BOOLEAN
ReportStatusCode (
IN EFI_STATUS_CODE_VALUE Code,
IN CONST CHAR8 *Format,
...
)
{
REPORT_STATUS_CODE_HANDLER_PROTOCOL *Handler;
UINT8 PlatformType;
VA_LIST Va;
EFI_STATUS_CODE_VALUE PlatformMask;
BOOLEAN Result;
Result = FALSE;
Handler = GetReportStatusCodeHandler ();
if (Handler == NULL) {
return Result;
}
//
// Read CMOS index 0x4B to determine platform type.
// This register provides the platform type identifier.
//
IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
PlatformType = IoRead8 (0x71);
//
// Determine platform type mask
//
PlatformMask = 0;
if (PlatformType > 3) {
//
// Platform type 0 has special handling; check a hardware register
// at 0xFEDAF0490 for the actual type.
//
if (PlatformType == 0) {
PlatformType = (MmioRead8 (0xFEDAF0490) & 2) | 1;
}
}
if ((PlatformType - 1) <= 0xFD) {
//
// Valid platform type; determine the mask.
//
if (PlatformType == 1) {
PlatformMask = 0x80000004;
} else {
PlatformMask = 0x80000006; // Type 4 or other
}
}
if ((PlatformMask & Code) != 0) {
VA_START (Va, Format);
Result = Handler->ReportStatusCode (
Code,
Format,
Va
);
VA_END (Va);
}
return Result;
}
/**
Translates a PCI Express address to a memory-mapped address.
@param[in] Address The PCI Express bus/device/function/offset address.
Must be within the 28-bit MMCFG address space.
@return The MMIO address for the PCI Express register.
**/
UINTN
PciExpressReadAddress (
IN UINTN Address
)
{
ASSERT ((Address & ~0xFFFFFFF) == 0);
return Address + mPciExpressBaseAddress;
}
/**
Writes 0x500 to a UINT16 at the specified address.
Used during initialization to set the PCI Express capability register.
@param[in] Address Pointer to the PCI configuration register to write.
**/
VOID
PciWriteWord (
IN UINT16 *Address
)
{
ASSERT (((UINTN)Address & 1) == 0);
*Address = 0x500;
}
/**
Compares two GUIDs.
@param[in] Guid1 Pointer to the first GUID (16 bytes).
@param[in] Guid2 Pointer to the second GUID (16 bytes).
@retval TRUE The GUIDs are equal.
@retval FALSE The GUIDs are not equal.
**/
BOOLEAN
CompareGuid (
IN CONST GUID *Guid1,
IN CONST GUID *Guid2
)
{
return (CompareMemWrapper (Guid1, Guid2, sizeof (GUID)) == 0);
}
/**
Parses GUID Extension HOBs (type 4) from a HOB list, searching for
one matching the platform-specific TCG HOB GUID.
Walks a linked list of HOB headers starting from the provided pointer.
Each HOB has:
- Header type (UINT16 at offset 0)
- Header length (UINT16 at offset 2)
Type 0x0004 indicates a GUID Extension HOB. If the type is 0xFFFF,
the end of sub-HOBs is reached. Sub-HOBs are nested within each HOB
entry.
When a GUID Extension HOB whose GUID (at offset +4) matches
mTcgPlatformHobGuid is found, it sets *Resource to point to the
resource data (at offset +12) and returns success.
@param[in,out] Hob Pointer to the current HOB pointer. Updated
to advance past the matched HOB entry.
@param[out] Resource On success, receives pointer to the HOB
data (after the 12-byte GUID header).
@retval EFI_SUCCESS A matching GUID HOB was found.
@retval EFI_NOT_FOUND End of HOB list reached with no match.
**/
EFI_STATUS
GetNextGuidHob (
IN OUT VOID **Hob,
OUT VOID **Resource
)
{
UINT16 *HobStart;
UINT16 *CurrentHob;
UINT16 *HobEnd;
CurrentHob = *(UINT16 **)Hob;
while (*CurrentHob != 0xFFFF) {
HobStart = CurrentHob;
HobEnd = CurrentHob;
//
// Walk through sub-HOBs within the current HOB entry, following
// the length field at offset +2 (in UINT16 units).
//
while (*HobEnd != 0xFFFF && *HobEnd != 0x0004) {
HobEnd = (UINT16 *)((UINT8 *)HobEnd + *(UINT16 *)((UINT8 *)HobEnd + 2));
}
//
// If end of sub-HOBs, wrap back to the HOB start.
//
if (*HobEnd == 0xFFFF) {
HobEnd = HobStart;
}
//
// Check if the sub-HOB is a GUID Extension HOB (type 4)
// whose GUID matches our target HOB GUID.
//
if ((*HobEnd == 0x0004) &&
(ReadUnaligned64 ((UINT64 *)(HobEnd + 4)) == ReadUnaligned64 ((UINT64 *)&mTcgPlatformHobGuid)) &&
(ReadUnaligned64 ((UINT64 *)((UINT8 *)(HobEnd + 4) + 8)) == ReadUnaligned64 ((UINT8 *)&mTcgPlatformHobGuid + 8)))
{
//
// Found it; resource data starts at offset 12 (past the 16-byte GUID).
//
*Resource = (UINT8 *)(HobEnd + 6);
*Hob = (UINT8 *)HobEnd + *(UINT16 *)((UINT8 *)HobEnd + 2);
return EFI_SUCCESS;
}
//
// Advance to the next HOB.
//
*Hob = (UINT8 *)HobEnd + *(UINT16 *)((UINT8 *)HobEnd + 2);
CurrentHob = *(UINT16 **)Hob;
}
return EFI_NOT_FOUND;
}
/**
Returns the HOB list pointer by scanning the system configuration table.
Searches the system configuration table for the entry with
gEfiDxeServicesTableGuid and extracts the vendor table pointer,
which is the HOB list in the DXE phase.
@return Pointer to the start of the HOB list, or NULL.
**/
VOID *
GetHobList (
VOID
)
{
UINTN Index;
EFI_CONFIGURATION_TABLE *ConfigTable;
if (mHobList != NULL) {
return mHobList;
}
mHobList = NULL;
ConfigTable = gST->ConfigurationTable;
if (gST->NumberOfTableEntries == 0) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT (FALSE);
return NULL;
}
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
if (CompareGuid (&ConfigTable[Index].VendorGuid, &mEfiDxeServicesTableGuid)) {
mHobList = ConfigTable[Index].VendorTable;
break;
}
}
if (mHobList == NULL) {
//
// HOB list must be present in DXE phase.
//
ASSERT (mHobList != NULL);
}
return mHobList;
}
/**
Retrieves the PCD protocol singleton via gBS->LocateProtocol.
Cached after first successful lookup. If the protocol cannot be
located, an ASSERT is raised and the error is reported.
@return Pointer to PCD_PROTOCOL interface, or NULL.
**/
PCD_PROTOCOL *
GetPcdProtocol (
VOID
)
{
EFI_STATUS Status;
if (mPcdProtocol == NULL) {
Status = gBS->LocateProtocol (
&gEfiPcdProtocolGuid,
NULL,
(VOID **)&mPcdProtocol
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
if (mPcdProtocol == NULL) {
ASSERT (mPcdProtocol != NULL);
}
}
}
return mPcdProtocol;
}
/**
Main initialization routine.
Stores the UEFI service table pointers into their standard globals
(with ASSERT that each is non-NULL), retrieves the HOB list,
gets the PCD protocol, reads the PCI Express base address from
PCD token 5, optionally configures the PCI Express capability
register (setting it to 0x500), and performs a short spin-wait
delay using the TSC and I/O port 0x508.
Interrupts are disabled during the delay and restored afterward.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
**/
VOID
TcgDxePlatformInit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINT32 TscDelayValue;
BOOLEAN InterruptsEnabled;
//
// Store global service table pointers and assert they are valid.
//
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
AssertReport (
__FILE__,
__LINE__,
"gImageHandle != ((void *) 0)"
);
}
gST = SystemTable;
if (SystemTable == NULL) {
AssertReport (
__FILE__,
__LINE__,
"gST != ((void *) 0)"
);
}
gBS = SystemTable->BootServices;
if (gBS == NULL) {
AssertReport (
__FILE__,
__LINE__,
"gBS != ((void *) 0)"
);
}
gRT = SystemTable->RuntimeServices;
if (gRT == NULL) {
AssertReport (
__FILE__,
__LINE__,
"gRT != ((void *) 0)"
);
}
//
// Retrieve the HOB list from the system configuration table.
//
GetHobList ();
//
// Retrieve the PCD protocol and read the PCI Express base address.
//
mPciExpressBaseAddress = GetPcdProtocol ()->GetPcd (5);
//
// Check the PCI Express capability register at offset PCI_EXPRESS_CAP_REG_OFFSET.
// If bit 7 is set (PCI Express capability present), write 0x500 to the register.
//
if ((*(volatile INT8 *)PciExpressReadAddress (PCI_EXPRESS_CAP_REG_OFFSET) & 0x80) != 0) {
//
// Write 0x500 to the capability register at offset PCI_EXPRESS_CAP_REG_OFFSET.
//
PciWriteWord ((UINT16 *)PciExpressReadAddress (PCI_EXPRESS_CAP_REG_OFFSET));
//
// Set the enable bit (bit 7) at the same address.
//
*(volatile UINT8 *)PciExpressReadAddress (PCI_EXPRESS_CAP_REG_OFFSET) |= 0x80;
}
//
// Save and then disable interrupts during the stabilization delay.
//
InterruptsEnabled = (AsmReadEflags () & EFI_FLAGS_IF) != 0;
AsmCli ();
//
// Read the initial TSC value and the delay timer from I/O port 0x508.
//
TscDelayValue = IoRead32 (TSC_DELAY_PORT) & 0xFFFFFF;
AsmReadTsc ();
//
// Spin-wait until the TSC-based timer expires.
// The lower 24 bits of (TscDelayValue + 357 - port 0x508) are checked;
// when bit 23 becomes set, the delay is complete.
//
while (((TscDelayValue + TSC_DELAY_CONSTANT - IoRead32 (TSC_DELAY_PORT)) & TSC_DELAY_MASK) == 0) {
AsmPause ();
}
AsmReadTsc ();
//
// Restore the interrupt flag state.
//
if (InterruptsEnabled) {
AsmSti ();
} else {
AsmCli ();
}
}
/**
Main TCG dispatch routine.
Walks the system configuration table backwards to find the DXE Services
Table entry. From that entry, retrieves the HOB list and parses GUID
Extension HOBs looking for the platform-specific TCG platform protocol
HOB GUID ({BFB01142-3061-48A4-922F-9D246E201120}).
Then installs the TCG platform protocol via gBS->LocateProtocol
(GUID: {8C939604-0700-4415-9D62-1161DB8164A6}) and dispatches four
callbacks in sequence:
1. ProcessPpiRequest (+24 from protocol base)
2. ProcessTcgSetup (+16 from protocol base)
3. MeasurePciOptionRoms (+8 from protocol base)
4. ReadyToBootCallback (+32 from protocol base)
If the HOB data indicates revision 1, the Initialize callback (+48)
is called before dispatching.
Each callback failure is reported but does not abort the remaining
callbacks. The final status from ReadyToBootCallback is returned.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@retval EFI_SUCCESS All TCG callbacks completed successfully.
@retval Others An error occurred during callback dispatch.
**/
EFI_STATUS
TcgDxePlatformDispatch (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
TCG_PLATFORM_PROTOCOL *TcgPlatformProtocol;
UINTN NumberOfTableEntries;
UINTN Index;
EFI_CONFIGURATION_TABLE *ConfigTable;
VOID *Hob;
VOID *HobData;
EFI_CONFIGURATION_TABLE *Entry;
TcgPlatformProtocol = NULL;
Status = EFI_SUCCESS;
HobData = NULL;
//
// Walk the system configuration table backwards, searching for
// the DXE Services Table GUID.
//
NumberOfTableEntries = gST->NumberOfTableEntries;
if (NumberOfTableEntries > 0) {
ConfigTable = gST->ConfigurationTable;
Entry = &ConfigTable[NumberOfTableEntries];
while (NumberOfTableEntries > 0) {
Entry--;
NumberOfTableEntries--;
//
// Compare entry GUID against gEfiDxeServicesTableGuid.
//
if (CompareMemWrapper (&Entry->VendorGuid, &mEfiDxeServicesTableGuid, sizeof (GUID)) == 0) {
//
// Found the DXE Services Table. The vendor table pointer contains
// the HOB list. Extract the TCG platform protocol pointer from it.
//
Hob = Entry->VendorTable;
if (GetNextGuidHob (&Hob, &HobData) >= 0) {
break;
}
}
}
}
//
// Install the TCG platform protocol via LocateProtocol.
//
Status = gBS->LocateProtocol (
&mTcgPlatformProtocolGuid,
NULL,
(VOID **)&TcgPlatformProtocol
);
//
// If we have a HOB-derived protocol interface with revision 1,
// call the Initialize notification first.
//
if ((HobData != NULL) && (*(UINT8 *)HobData == 1)) {
TcgPlatformProtocol->Initialize ();
}
//
// Dispatch the four TCG callbacks through the protocol.
//
if (!EFI_ERROR (Status)) {
//
// 1. Process PPI Request from OS
//
if (TcgPlatformProtocol->ProcessPpiRequest () < 0) {
ReportStatusCode (
EFI_ERROR_CODE | EFI_ERROR_MAJOR,
"\n Possible ERROR Processing Ppi Request from O.S.\n"
);
}
//
// 2. Process TCG Setup
//
if (TcgPlatformProtocol->ProcessTcgSetup () < 0) {
ReportStatusCode (
EFI_ERROR_CODE | EFI_ERROR_MAJOR,
"\n Possible ERROR Processing Tcg Setup\n"
);
}
//
// 3. Measure PCI Option ROMs
//
if (TcgPlatformProtocol->MeasurePciOptionRoms () < 0) {
ReportStatusCode (
EFI_ERROR_CODE | EFI_ERROR_MAJOR,
"\n Possible ERROR Measuring PCI Option Roms\n"
);
}
//
// 4. Process TCG Ready to Boot Callback
//
Status = TcgPlatformProtocol->ReadyToBootCallback ();
if (Status < 0) {
ReportStatusCode (
EFI_ERROR_CODE | EFI_ERROR_MAJOR,
"\n Possible ERROR process Tcg Ready to boot Callback\n"
);
}
}
return Status;
}
/**
Entry point of the TCG platform DXE driver.
Performs platform initialization and then dispatches all TCG callbacks.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS All TCG callbacks completed successfully.
@retval Others An error occurred during callback dispatch.
**/
EFI_STATUS
EFIAPI
TcgDxePlatformEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
TcgDxePlatformInit (ImageHandle, SystemTable);
return TcgDxePlatformDispatch (ImageHandle);
}