/** @file
TCMDXE - TPM Compatibility Module (TCM) DXE Driver implementation
This module implements a Trusted Computing Module (TCM) driver for UEFI
DXE phase. It communicates with TCM hardware via memory-mapped FIFO
registers at 0xFED40000 (locality 0).
The driver flow:
1. ModuleEntryPoint initializes UEFI library globals and HOB list.
2. Detects TCM hardware presence (vendor ID check).
3. If TCM found and ready (signature 0x1B4D), registers a callback via
the TCG physical presence protocol (gTcpaCallbackGuid).
4. The callback invokes TCM operations: Startup, SelfTest, PhysicalEnable,
PhysicalSetDeactivated, ForceClear, etc.
Copyright (C) 2025, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "TCMDXE.h"
//
// Global data
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
UINT64 gPciExpressBaseAddress = 0;
VOID *mHobList = NULL;
VOID *mPcdDb = NULL;
//
// TCM callback GUID from .rdata section
//
extern EFI_GUID gTcpaCallbackGuid; // {0x6B221186, 0x7E6F, 0x4A71, ...} @ 0x18D0
extern EFI_GUID gEfiHobListGuid; // {0x7739F24C, 0x928D, 0x46A6, ...} @ 0x18E0
extern EFI_GUID gEfiGuid78; // { ... } @ 0x18E8
extern EFI_GUID gEfiDxeServicesGuid; // { ... } @ 0x18C0
extern UINT64 gTcmInterfaceType; // @ 0x18F0
extern VOID *gTcpaCallbackNotifyFn; // @ 0x1900
//
// TCM vendor/device ID signature
//
#define TCM_SIGNATURE_WORD 0x1B4D // "M\x1B" at offset 0xF00
#define TCM_LOCALITY_ACQUIRE_COMPARE 0xFF
#define TCM_LOCALITY_ACQUIRE_VALUE 2
#define TCM_LOCALITY_RELEASE_VALUE 0x20
//
// TCM FIFO interface constants
//
#define TCM_FIFO_REG_ACCESS 0x00
#define TCM_FIFO_REG_STS 0x18
#define TCM_FIFO_REG_BURST_CNT 0x26
#define TCM_FIFO_REG_DATA_FIFO 0x24
#define TCM_FIFO_REG_INTF_ID 0x30
#define TCM_FIFO_REG_DID_VID 0xF00
#define TCM_STS_VALID 0x80
#define TCM_STS_CMD_READY 0x40
#define TCM_STS_GO 0x20
#define TCM_STS_DATA_AVAIL 0x10
#define TCM_STS_EXPECT 0x08
#define TCM_STS_RESPONSE_RETRY 0x02
#define TCM_ACCESS_LOCALITY_CHANGE 0x20
#define TCM_ACCESS_ACTIVE_LOCALITY 0x10
#define TCM_ACCESS_REQUEST_USE 0x02
//
// TPM_STANY response tag
//
#define TPM_TAG_RQU_RESPONSE 0x00C4
// ---------------------------------------------------------------------------
// Debug print wrappers
// ---------------------------------------------------------------------------
/**
Report an error message to the registered debug handler.
The function queries the debug protocol (via gEfiDxeServicesGuid) and,
if available, formats and prints the error message via the protocol's
output function. It also checks the CMOS index 0x4B to determine the
debug level.
@param[in] ErrorCode Error code to filter against (bitmask of debug
levels is evaluated).
@param[in] Format Printf-style format string.
@param[in] ... Variable arguments.
@retval 1 Message was printed (high byte of return may appear bogus).
@retval 0 No debug handler available or error level below threshold.
**/
BOOLEAN
TcmReportError (
IN UINT64 ErrorCode,
IN CONST CHAR8 *Format,
...
)
{
EFI_STATUS Status;
UINT64 DebugLevel;
UINTN Pages;
VOID *Interface;
UINT64 (**PrintFn)(UINT64, CONST CHAR8 *, UINT64 *);
Status = EFI_SUCCESS;
Pages = 0;
Interface = TcmGetHobList ();
if (Interface != NULL) {
//
// Check CMOS register 0x4B for debug level
//
IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
DebugLevel = IoRead8 (0x71);
if (DebugLevel > 3) {
if (DebugLevel == 0) {
DebugLevel = (MmioRead8 (0xFDAF0490) & 2) | 1;
}
} else {
DebugLevel -= 1;
if (DebugLevel <= 0xFD) {
DebugLevel = 4;
if (DebugLevel == 1) {
DebugLevel = 2147483652ULL; // EFI_D_INFO equivalent
} else {
DebugLevel = 2147483718ULL; // EFI_D_ERROR equivalent
}
}
}
if ((DebugLevel & ErrorCode) != 0) {
PrintFn = (UINT64 (**)(UINT64, CONST CHAR8 *, UINT64 *))Interface;
(*PrintFn)(ErrorCode, Format, (UINT64 *)&Format + 1);
return TRUE;
}
}
return FALSE;
}
/**
Assertion failure handler. Invokes the registered debug assertion handler.
@param[in] FileName Pointer to the source file name string.
@param[in] LineNumber Line number in the source file.
@param[in] Description Assertion description string.
@return Status code from the registered debug handler, or 0 if none.
**/
UINT64
TcmAssertFail (
IN UINT64 FileName,
IN UINT64 LineNumber,
IN UINT64 Description
)
{
UINT64 Result;
VOID *Interface;
Interface = TcmGetHobList ();
if (Interface != NULL) {
return ((UINT64 (*)(VOID))(*(UINT64 **)Interface + 8))(
FileName,
LineNumber,
Description
);
}
return 0;
}
// ---------------------------------------------------------------------------
// UEFI Library context init
// ---------------------------------------------------------------------------
/**
Retrieve the HOB list pointer from the System Table's HOB list GUID.
The function scans the configuration table entries in the System Table for
the HOB list GUID ({0x7739F24C, ...}). On first call the result is cached
in mHobList.
@return Pointer to the HOB list, or NULL if not found.
**/
VOID *
TcmGetHobList (
VOID
)
{
UINTN Index;
UINT64 Count;
if (mHobList == NULL) {
Count = gSystemTable->NumberOfTableEntries;
for (Index = 0; Index < Count; Index++) {
if (TcmReadUnaligned64 (&gSystemTable->ConfigurationTable[Index].VendorGuid) ==
TcmReadUnaligned64 (&gEfiHobListGuid))
{
mHobList = gSystemTable->ConfigurationTable[Index].VendorTable;
return mHobList;
}
}
//
// HOB list not found - assert
//
TcmReportError (
0x8000000000000000ULL,
"\nASSERT_EFI_ERROR (Status = %r)\n",
0x800000000000000EULL
);
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
(UINT64)"!EFI_ERROR (Status)"
);
if (mHobList == NULL) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
(UINT64)"mHobList != ((void *) 0)"
);
}
}
return mHobList;
}
/**
Locate the PCD database protocol via UEFI boot services.
On first call the result is cached in mPcdDb.
@return Pointer to the PCD database protocol structure, or NULL on failure.
**/
VOID *
TcmGetPcdDb (
VOID
)
{
EFI_STATUS Status;
if (mPcdDb == NULL) {
Status = gBootServices->LocateProtocol (
&gTcpaCallbackGuid,
NULL,
&mPcdDb
);
if (EFI_ERROR (Status)) {
TcmReportError (
0x8000000000000000ULL,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\DxePcdLib\\DxePcdLib.c",
78,
(UINT64)"!EFI_ERROR (Status)"
);
}
if (mPcdDb == NULL) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\DxePcdLib\\DxePcdLib.c",
79,
(UINT64)"mPcdDb != ((void *) 0)"
);
}
}
return mPcdDb;
}
/**
Read a UINT64 from potentially unaligned memory.
Asserts if Buffer is NULL.
@param[in] Buffer Pointer to read from.
@return The UINT64 value at Buffer.
**/
UINT64
TcmReadUnaligned64 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
(UINT64)"Buffer != ((void *) 0)"
);
}
return *(UINT64 *)Buffer;
}
// ---------------------------------------------------------------------------
// PCI Express / MMIO helpers
// ---------------------------------------------------------------------------
/**
Translate a PCI Express access address to the MMIO base and return the
virtual address.
The address must be in the range 0-0xFFFFF (28-bit PCIe config space).
The function adds the cached gPciExpressBaseAddress.
@param[in] Address PCI Express config address.
@return Virtual address for the MMIO access.
**/
UINT64
TcmPciExpressGetAddr (
IN UINT64 Address
)
{
if ((Address & 0xFFFFFFFFF0000000ULL) != 0) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\SmmPciExpressLib\\PciExpressLib.c",
118,
(UINT64)"((Address) & ~0xfffffff) == 0"
);
}
return Address + gPciExpressBaseAddress;
}
/**
Write 1280 (0x0500) to the specified UINT16-aligned IO port.
Used to enable decoding of specified IO range.
@param[in] Address Target IO port address (must be even).
**/
VOID
TcmIoWriteEnable (
IN UINT16 *Address
)
{
if (((UINTN)Address & 1) != 0) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\BaseIoLibIntrinsic\\IoLib.c",
183,
(UINT64)"(Address & 1) == 0"
);
}
*Address = 1280; // 0x0500 - enable decode
}
// ---------------------------------------------------------------------------
// TCM FIFO I/O primitives
// ---------------------------------------------------------------------------
/**
Write a byte to a TCM register with polling for the expected value.
Retries up to TCM_TIMEOUT_MAX (50000) iterations with PAUSE between polls.
@param[in] Address TCM register MMIO address (relative to base).
@param[in] Data Value to write.
@param[in] Mask Required bitmask: after write, (register & Mask) must
equal (Data & Mask). Extra bits in Mask are checked
as zero (i.e., after read, the byte AND Mask must
equal 0 for extra bits; the Data bits must match).
@retval 0 Success - register accepted the value.
@retval 1 Timeout - value did not stick within the retry limit.
**/
UINT64
TcmRegisterWrite (
IN UINT32 Address,
IN UINT8 Data,
IN UINT8 Mask
)
{
UINTN Retries;
Retries = 0;
while (((IoRead8 (Address) & Mask) != (Data & Mask)) ||
((IoRead8 (Address) & Mask) != 0))
{
TcmMicroDelay (107); // ~107us delay per iteration
if (++Retries >= TCM_TIMEOUT_MAX) {
return 1;
}
}
return 0;
}
/**
Read a status/response word from the TCM data FIFO register.
Retries up to TCM_TIMEOUT_MAX iterations.
@param[in] Locality TCM locality number (shifted to compute register addr).
@param[out] Data Pointer to store the 16-bit value read from
FIFO status bytes at offset +0x18, +0x19.
@retval 0 Success.
@retval 1 Timeout reading status.
**/
UINT64
TcmReadWord (
IN UINT8 Locality,
OUT UINT16 *Data
)
{
UINTN Retries;
UINT64 Addr;
Addr = ((UINT64)Locality << 12) + TCM_BASE_ADDRESS;
Retries = 0;
for (;;) {
*Data = IoRead8 (Addr + TCM_FIFO_REG_STS);
*Data |= (UINT16)IoRead8 (Addr + TCM_FIFO_REG_STS + 1) << 8;
if (*Data != 0) {
return 0;
}
TcmMicroDelay (107);
if (++Retries >= TCM_TIMEOUT_MAX) {
return 1;
}
}
}
/**
Check a TCM command response for success.
@param[in] ResponseHeader Pointer to the response buffer, interpreted
as UINT16 to read the tag field.
@retval 0 Response tag is 0xC400 (TPM_TAG_RQU_RESPONSE).
@retval TCM_DEVICE_ERROR Response tag is not 0xC400.
**/
UINT64
TcmCheckResponse (
IN UINT16 *ResponseHeader
)
{
if (*ResponseHeader != TPM_TAG_RQU_RESPONSE) {
TcmReportError (0x8000000000000000ULL, "Tcm no response.\n");
return TCM_DEVICE_ERROR;
}
return 0;
}
// ---------------------------------------------------------------------------
// Microsecond delay via RDTSC
// ---------------------------------------------------------------------------
/**
Perform a microsecond-range delay by busy-waiting on RDTSC.
The calibration: 357 RDTSC ticks roughly equals 1 microsecond on the
target platform. The function waits until the desired elapsed time has
passed, polling RDTSC in a tight loop.
@param[in] Microseconds Number of microseconds to delay.
**/
VOID
TcmMicroDelay (
IN UINT32 Microseconds
)
{
UINT64 StartTsc;
UINT64 CurrentTsc;
UINT64 ElapsedDelta;
BOOLEAN InterruptsEnabled;
//
// Check EFLAGS.IF before disabling interrupts
//
InterruptsEnabled = (AsmReadEflags () & 0x200) != 0;
//
// Disable interrupts during the calibrated delay loop
//
AsmDisableInterrupts ();
StartTsc = AsmReadTsc ();
do {
AsmPause ();
CurrentTsc = AsmReadTsc ();
ElapsedDelta = CurrentTsc - StartTsc + 357;
} while (((UINT32)ElapsedDelta & 0x800000) == 0);
//
// Re-enable interrupts if they were enabled before
//
AsmReadTsc ();
if (InterruptsEnabled) {
AsmEnableInterrupts ();
} else {
AsmDisableInterrupts ();
}
}
// ---------------------------------------------------------------------------
// TCM FIFO command transmit/receive
// ---------------------------------------------------------------------------
/**
Transmit a TCM command buffer to the FIFO and receive the response.
This function performs the full TCM FIFO transaction:
1. Acquire locality.
2. Check TCM presence (DID_VID != 0xFF).
3. Set locality access register.
4. Write command bytes to data FIFO.
5. Trigger GO.
6. Wait for data available.
7. Read response bytes.
@param[in] Locality TCM locality (0 for default).
@param[in] SendBuffer Pointer to command byte buffer.
@param[in] SendLength Length of command in bytes.
@param[out] RecvBuffer Buffer for response data.
@param[in,out] RecvLength On input: max bytes to read.
On output: actual bytes received.
@retval 0 Transaction completed successfully.
@retval 3 TCM not present (DID_VID == 0xFF).
@retval 6 Locality could not be acquired.
@retval 1 Timeout during command/data handshake.
@retval 5 Response larger than provided buffer.
**/
UINT64
TcmFifoTransmit (
IN UINT8 Locality,
IN UINT8 *SendBuffer,
IN UINT32 SendLength,
OUT UINT8 *RecvBuffer,
IN OUT UINT32 *RecvLength
)
{
UINT64 Status;
UINT64 TcmAddr;
UINT32 LocalityShifted;
UINT32 Index;
UINT32 BurstCount;
UINT32 RecvIdx;
UINT16 FifoWord;
UINT8 ByteVal;
UINT8 ResponseTag;
UINT32 TotalResponseSize;
UINT32 DataSize;
TcmAddr = ((UINT64)Locality << 12) + TCM_BASE_ADDRESS;
LocalityShifted = (UINT32)Locality << 12;
//
// Check TCM presence: DID_VID must not be 0xFF
//
if (MmioRead8 (TcmAddr + TCM_FIFO_REG_DID_VID) == 0xFF) {
return TCM_NOT_PRESENT;
}
//
// Acquire locality: write active locality request
//
if (MmioRead8 (TcmAddr + TCM_FIFO_REG_ACCESS) == 0xFF) {
return TCM_LOCALITY_ERROR;
}
MmioWrite8 (TcmAddr + TCM_FIFO_REG_ACCESS, TCM_LOCALITY_ACQUIRE_VALUE);
//
// Wait for locality to be granted: Access register < 0xFF
//
Status = TcmRegisterWrite (
TcmAddr + TCM_FIFO_REG_ACCESS,
TCM_LOCALITY_ACQUIRE_VALUE,
0x00
);
if (Status != 0) {
return Status;
}
//
// Prepare for command: set STS.commandReady
//
MmioWrite8 (TcmAddr + TCM_FIFO_REG_STS, TCM_STS_CMD_READY);
Status = TcmRegisterWrite (
LocalityShifted - 0x12C0000, // STS reg relative
TCM_STS_CMD_READY,
0
);
if (Status != 0) {
return Status;
}
//
// Write command bytes to FIFO
//
Index = 0;
while (Index < SendLength) {
//
// Wait for STS_EXPECT to be set
//
Status = TcmRegisterWrite (
LocalityShifted - 0x12C0000,
0x40,
0
);
if (Status != 0) {
return Status;
}
//
// Check burst count before writing
//
FifoWord = 0;
TcmReadWord (Locality, &FifoWord);
BurstCount = FifoWord & 0xFFFF;
(VOID)BurstCount; // May be unused depending on TCM model
//
// Write data byte to FIFO
//
ByteVal = SendBuffer[Index];
MmioWrite8 (TcmAddr + TCM_FIFO_REG_DATA_FIFO, ByteVal);
MmioWrite8 (
TcmAddr + TCM_FIFO_REG_STS,
TCM_STS_CMD_READY
);
Index++;
}
//
// Signal end of data
//
MmioWrite8 (TcmAddr + TCM_FIFO_REG_STS, TCM_STS_CMD_READY);
TcmRegisterWrite (
LocalityShifted - 0x12C0000,
0x80,
0
);
//
// Trigger command execution: write GO bit
//
MmioWrite8 (TcmAddr + TCM_FIFO_REG_STS, TCM_STS_GO);
Status = TcmRegisterWrite (
LocalityShifted - 0x12C0000,
0x20,
0
);
if (Status != 0) {
return Status;
}
//
// Wait for data available
//
Status = TcmRegisterWrite (
LocalityShifted - 0x12C0000,
0x10,
0
);
if (Status != 0) {
return Status;
}
//
// Read response data
//
RecvIdx = 0;
for (;;) {
//
// Read status word to get FIFO byte count
//
TcmReadWord (Locality, &FifoWord);
if (FifoWord == 0) {
break;
}
ResponseTag = (UINT8)FifoWord;
if (RecvIdx == 0) {
if (RecvIdx >= *RecvLength) {
return 5; // Buffer too small
}
RecvBuffer[RecvIdx++] = ResponseTag;
--FifoWord;
}
//
// Read remaining bytes
//
while (FifoWord > 0) {
if (RecvIdx >= *RecvLength) {
return 5;
}
RecvBuffer[RecvIdx++] = IoRead8 (TcmAddr + TCM_FIFO_REG_DATA_FIFO);
--FifoWord;
}
//
// Check whether we have the full response header
//
if (RecvIdx >= 6) {
TotalResponseSize = (UINT32)RecvBuffer[2] << 24 |
(UINT32)RecvBuffer[3] << 16 |
(UINT32)RecvBuffer[4] << 8 |
(UINT32)RecvBuffer[5];
if (RecvIdx >= TotalResponseSize) {
break;
}
}
}
//
// Locality release: clear access register
//
MmioWrite8 (TcmAddr + TCM_FIFO_REG_ACCESS, 0x20);
TcmRegisterWrite (
LocalityShifted - 0x12C0000,
0x20,
0
);
MmioWrite8 (TcmAddr + TCM_FIFO_REG_ACCESS, TCM_LOCALITY_RELEASE_VALUE);
//
// Strip header: response is at offset 10 from raw data
//
DataSize = RecvIdx - 10;
if (*RecvLength > DataSize) {
*RecvLength = DataSize;
}
//
// Return success / status
//
return TcmCheckResponse ((UINT16 *)RecvBuffer);
}
// ---------------------------------------------------------------------------
// TCM command wrappers
// ---------------------------------------------------------------------------
/**
TCM Startup command.
Sends TPM_Startup with the given startup type via the FIFO.
@param[in] StartupType TCM_ST_CLEAR (1) or TCM_ST_STATE (2).
@retval 0 Success.
@retval 1 Timeout or device error.
**/
UINT64
TcmStartup (
IN UINT16 StartupType
)
{
return TcmFifoTransmit (
TCM_LOCALITY_0,
(UINT8 *)&StartupType,
sizeof (StartupType),
(UINT8 *)&StartupType, // reuse buffer for response
(UINT32 *)&StartupType
);
}
/**
TPM_ContinueSelfTest command.
Sends the ContinueSelfTest ordinal and waits for completion.
@retval 0 Success.
@retval 1 Timeout or device error.
**/
UINT64
TcmContinueSelfTest (
VOID
)
{
return TcmFifoTransmit (
TCM_LOCALITY_0,
(UINT8 *)&TCM_ORD_ContinueSelfTest,
sizeof (TCM_ORD_ContinueSelfTest),
(UINT8 *)&TCM_ORD_ContinueSelfTest,
(UINT32 *)&TCM_ORD_ContinueSelfTest
);
}
/**
TCM physical enable.
@retval 0 Success.
@retval 1 Timeout or device error.
**/
UINT64
TcmPhysicalEnable (
VOID
)
{
UINT64 Status;
Status = TcmFifoTransmit (
TCM_LOCALITY_0,
(UINT8 *)&TCM_ORD_PhysicalEnable,
sizeof (TCM_ORD_PhysicalEnable),
(UINT8 *)&TCM_ORD_PhysicalEnable,
(UINT32 *)&TCM_ORD_PhysicalEnable
);
if (!Status) {
//
// After physical enable, set deactivated flag to FALSE
//
Status = TcmPhysicalSetDeactivated (FALSE);
}
return Status;
}
/**
TCM physical disable.
@retval 0 Success.
@retval 1 Timeout or device error.
**/
UINT64
TcmPhysicalDisable (
VOID
)
{
UINT64 Status;
//
// First set deactivated flag to TRUE, then physical disable
//
Status = TcmPhysicalSetDeactivated (TRUE);
if (!Status) {
Status = TcmFifoTransmit (
TCM_LOCALITY_0,
(UINT8 *)&TCM_ORD_PhysicalDisable,
sizeof (TCM_ORD_PhysicalDisable),
(UINT8 *)&TCM_ORD_PhysicalDisable,
(UINT32 *)&TCM_ORD_PhysicalDisable
);
}
return Status;
}
/**
TCM physical set deactivated (activate/deactivate).
@param[in] Deactivated TRUE to deactivate, FALSE to activate.
@retval 0 Success.
@retval 1 Timeout or device error.
**/
UINT64
TcmPhysicalSetDeactivated (
IN BOOLEAN Deactivated
)
{
return TcmFifoTransmit (
TCM_LOCALITY_0,
(UINT8 *)&Deactivated,
sizeof (Deactivated),
(UINT8 *)&Deactivated,
(UINT32 *)&Deactivated
);
}
/**
TCM force clear.
@retval 0 Success.
@retval 1 Timeout or device error.
**/
UINT64
TcmForceClear (
VOID
)
{
UINT64 Status;
Status = TcmFifoTransmit (
TCM_LOCALITY_0,
(UINT8 *)&TCM_ORD_ForceClear,
sizeof (TCM_ORD_ForceClear),
(UINT8 *)&TCM_ORD_ForceClear,
(UINT32 *)&TCM_ORD_ForceClear
);
if (Status) {
TcmReportError (
0x8000000000000000ULL,
"Tcm Force clear error, returned %r\n",
Status
);
}
return Status;
}
/**
TCM get permanent flags (Pflag) and volatile flags (Vflag).
@param[out] PhysicalPresence TRUE if physical presence asserted.
@param[out] PhysicalEnable TRUE if TCM physically enabled.
@param[out] PhysicalDeactivated TRUE if TCM physically deactivated.
@retval 0 Success.
@retval 1 Timeout or device error.
**/
UINT64
TcmGetFlags (
OUT BOOLEAN *PhysicalPresence,
OUT BOOLEAN *PhysicalEnable,
OUT BOOLEAN *PhysicalDeactivated
)
{
UINT64 Status;
UINT8 CapBuf[40];
CONST UINT32 CapSize = 264;
CONST UINT32 SubCap = 17;
UINT8 VflagByte;
UINT32 Ordinal;
Ordinal = TCM_ORD_GetCapability;
Status = TcmFifoTransmit (
TCM_LOCALITY_0,
(UINT8 *)&Ordinal,
4,
CapBuf,
(UINT32 *)&CapBuf
);
if (Status) {
TcmReportError (
0x8000000000000000ULL,
"Tcm Get Pflag error, returned %r\n",
Status
);
return Status;
}
*PhysicalPresence = (CapBuf[2] == 0);
*PhysicalEnable = (CapBuf[4] == 0);
//
// Get volatile flags via sub-capability 273
//
Ordinal = 273;
Status = TcmFifoTransmit (
TCM_LOCALITY_0,
(UINT8 *)&Ordinal,
1,
&VflagByte,
(UINT32 *)&VflagByte
);
if (Status) {
TcmReportError (
0x8000000000000000ULL,
"Tcm Get Vflag error, returned %r\n",
Status
);
return Status;
}
*PhysicalDeactivated = (VflagByte != 0);
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// UEFI Driver Entry Point
// ---------------------------------------------------------------------------
/**
TCM DXE driver entry point.
Initializes all UEFI library globals (ImageHandle, SystemTable, BootServices,
RuntimeServices), stores PCI Express MMIO base address, detects TCM hardware,
and if present, registers a callback for TCG physical presence protocol events.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The driver initialized successfully.
@retval EFI_UNSUPPORTED TCM hardware not found or not initialized.
**/
EFI_STATUS
EFIAPI
TcmDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT64 TcmSignature;
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
//
// Must have ImageHandle
//
if (ImageHandle == NULL) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
(UINT64)"gImageHandle != ((void *) 0)"
);
}
//
// Must have SystemTable
//
if (SystemTable == NULL) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
(UINT64)"gST != ((void *) 0)"
);
}
//
// Must have BootServices
//
if (gBootServices == NULL) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
(UINT64)"gBS != ((void *) 0)"
);
}
//
// Must have RuntimeServices
//
if (gRuntimeServices == NULL) {
TcmAssertFail (
(UINT64)"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
(UINT64)"gRT != ((void *) 0)"
);
}
//
// Initialize HOB list
//
TcmGetHobList ();
//
// Initialize PCI Express MMIO base address
//
gPciExpressBaseAddress = ((UINT64 (*)(UINTN))TcmGetPcdDb ()->GetPcd ())(5);
//
// If PCI Express decoding is enabled, write the decode enable register
//
if ((INT8)TcmPciExpressGetAddr (1024068) >= 0) {
TcmIoWriteEnable ((UINT16 *)TcmPciExpressGetAddr (1024064));
*(UINT8 *)TcmPciExpressGetAddr (1024068) |= 0x80;
}
//
// Check for TCM presence via signature word at DID_VID register
//
TcmSignature = AsmReadEflags ();
//
// Disable interrupts, check TCM signature
//
AsmDisableInterrupts ();
TcmSignature = (UINT16)IoRead32 (1288) & 0xFFFFFF;
//
// If TCM signature matches, register callback
//
if (MmioRead16 (TCM_BASE_ADDRESS + TCM_FIFO_REG_DID_VID) == TCM_SIGNATURE_WORD) {
//
// Calibrated delay to ensure TCM is ready
//
TcmMicroDelay (1288);
//
// Wait for TCM ready...
//
while ((((UINT32)TcmSignature + 357 - (UINT32)IoRead32 (1288)) & 0x800000) == 0) {
AsmPause ();
}
IoRead32 (1288); // Consume remaining RDTSC delta
//
// TCM found - register the TCG callback
//
AsmReadTsc (); // Flush
//
// Register callback for TCG physical presence protocol
//
Status = gBootServices->RegisterProtocolNotify (
&gTcpaCallbackGuid,
(VOID *)TcmGetHobList // Callback function
);
if (EFI_ERROR (Status)) {
return Status;
}
}
return EFI_SUCCESS;
}