Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / TCG / TCM / TCMDXE / TCMDXE.c
@Ajax Dong Ajax Dong 2 days ago 27 KB Full restructure
/** @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;
}