Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / TCG2 / Common / Tpm20Acpi / Tpm20Acpi.c
@Ajax Dong Ajax Dong 2 days ago 32 KB Full restructure
/** @file
  Tpm20Acpi - TPM 2.0 ACPI Table Driver for HR650X platform

  This module provides TPM 2.0 ACPI table initialization and management
  functionality. It communicates with the TPM hardware via memory-mapped
  I/O at 0xFED40000 and publishes ACPI tables (TPM2, SSDT) describing
  the TPM device to the OS.

  Key features:
  - Detects TPM 2.0 hardware via memory-mapped registers
  - Locates DSDT table from RSDT/XSDT
  - Programs ACPI SSDT tables with TPM configuration information
  - Supports TPM Physical Presence (TPP) interface
  - Supports TPM command/response buffer interface

  Copyright (c) 2025, Dell Inc. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Tpm20Acpi.h"

//
// Global variables - UEFI boot/runtime services
//
EFI_HANDLE           gImageHandle = NULL;
EFI_SYSTEM_TABLE     *gST         = NULL;
EFI_BOOT_SERVICES    *gBS         = NULL;
EFI_RUNTIME_SERVICES *gRT         = NULL;

//
// Module global variables
//
UINT64  mHobList        = 0;  ///< HOB list pointer (cached)
UINT64  mPcdDb          = 0;  ///< PCD database protocol pointer
UINT64  mPciExpressBase = 0;  ///< PCI Express MMIO base address
UINT64  mTpmStolenAddr  = 0;  ///< TPM stolen memory address (cached)
UINT32  mTpmMode        = 0;  ///< TPM operating mode
UINT64  mDsdtAddr       = 0;  ///< DSDT address from RSDT
UINT64  mXsdtDsdtAddr   = 0;  ///< DSDT address from XSDT
VOID    *mAcpiSupport   = 0;  ///< ACPI Support protocol interface

UINT32  mAcpiGpeSignature = 0x1E941A97; ///< ACPI GPE block GUID-like constant
UINT32  mAcpiGpeVal       = 0;
UINT32  m541674817        = 0;          ///< Cached signature

//
// Static (internal) function prototypes
//

/**
  Copy memory from source to destination.

  @param[out] Destination  Pointer to the destination buffer.
  @param[in]  Source       Pointer to the source buffer.
  @param[in]  Length       Number of bytes to copy.

  @return Destination pointer.

**/
STATIC
VOID *
EFIAPI
InternalCopyMem (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Length
  );

/**
  Compare two memory buffers.

  @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 0 if the buffers are equal; nonzero if they differ.

**/
STATIC
INTN
EFIAPI
InternalCompareMem (
  IN CONST VOID *DestinationBuffer,
  IN CONST VOID *SourceBuffer,
  IN UINTN      Length
  );

/**
  Read a 64-bit value from an unaligned pointer.

  @param[in] Buffer  Pointer to read from.

  @return The 64-bit value at the pointer.

**/
STATIC
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID *Buffer
  );

/**
  Read the GUID portion of a HOB entry for comparison.

  @param[in] HobGuid  Pointer to the GUID in a HOB.
  @param[in] Guid2    Pointer to the GUID to compare against.

  @retval TRUE   The GUIDs match.
  @retval FALSE  The GUIDs do not match.

**/
STATIC
BOOLEAN
EFIAPI
IsHobGuidEqual (
  IN CONST EFI_GUID  *HobGuid,
  IN CONST EFI_GUID  *Guid2
  );

/**
  Write a 16-bit value to an I/O port.

  @param[in] Port  The I/O port address to write to.

**/
STATIC
VOID
EFIAPI
IoWrite16 (
  IN UINT16  Port
  );

/**
  Read a 32-bit value from an I/O port.

  @param[in] Port  The I/O port address to read from.

  @return The 32-bit value read from the port.

**/
STATIC
UINT32
EFIAPI
IoRead32 (
  IN UINT16  Port
  );

/**
  Read the CPU's timestamp counter.

  @return The current TSC value.

**/
STATIC
UINT64
EFIAPI
ReadTimestampCounter (
  VOID
  );

/**
  Read the caller's EFLAGS register.

  @return The EFLAGS value at the call site.

**/
STATIC
UINT64
EFIAPI
ReadCallerEflags (
  VOID
  );

/**
  Halt the CPU (PAUSE instruction).

**/
STATIC
VOID
EFIAPI
CpuPause (
  VOID
  );

/**
  Enable interrupts.

**/
STATIC
VOID
EFIAPI
EnableInterrupts (
  VOID
  );

/**
  Disable interrupts.

**/
STATIC
VOID
EFIAPI
DisableInterrupts (
  VOID
  );

/**
  Locate the ACPI Support protocol.

  @return Pointer to the ACPI Support protocol interface, or NULL if not found.

**/
STATIC
VOID *
EFIAPI
GetAcpiSupportProtocol (
  VOID
  );

/**
  Initialize the ACPI Support protocol and cache the interface pointer.

**/
STATIC
VOID
EFIAPI
InitializeAcpiSupport (
  VOID
  );

/**
  Retrieve the HOB list pointer.

  @return Pointer to the HOB list, or 0 if not found.

**/
STATIC
UINT64
EFIAPI
GetHobList (
  VOID
  );

/**
  Find the next HOB of type EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
  in the HOB list.

  @param[in] HobStart  Pointer to the current HOB.

  @return Pointer to the resource descriptor HOB, or 0 if not found.

**/
STATIC
VOID *
EFIAPI
GetNextResourceHob (
  IN VOID  *HobStart
  );

/**
  Assertion handler used by UEFI library code. Prints the file/line/expression
  and enters deadloop.

  @param[in] FileName     File name where the assertion occurred.
  @param[in] LineNumber   Line number of the assertion.
  @param[in] Description  The assertion expression string.

**/
STATIC
VOID
EFIAPI
AssertHandler (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  );

/**
  Debug print implementation using CMOS port 0x70/0x71 serial redirection.

  @param[in] ErrorLevel  Debug error level mask.
  @param[in] Format      Format string.
  @param[in] ...         Variable arguments.

  @return The character printed (or a status indicator).

**/
STATIC
CHAR8
EFIAPI
DebugPrint (
  IN UINT64       ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  );

//
// UEFI Boot Services Table Library initialization
//

/**
  Constructor for UefiBootServicesTableLib.

  Saves ImageHandle, SystemTable, BootServices, and RuntimeServices
  into global variables.

  @param[in] ImageHandle  The firmware allocated handle for the UEFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

**/
STATIC
VOID
EFIAPI
InitializeUefiBootServicesTable (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  gST = SystemTable;
  ASSERT (gST != NULL);

  gBS = SystemTable->BootServices;
  ASSERT (gBS != NULL);

  gRT = SystemTable->RuntimeServices;
  ASSERT (gRT != NULL);
}

/**
  PCD Get interface wrapper.
  Retrieves a token from the PCD database.

  @param[in] TokenNumber  The PCD token number to retrieve.

  @return The value of the PCD token.

**/
STATIC
UINT64
EFIAPI
PcdGet32 (
  IN UINTN  TokenNumber
  )
{
  ASSERT (mPcdDb != NULL);
  return ((UINT64 (*)(UINTN))(*((UINT64 *)mPcdDb + 4)))(TokenNumber);
}

/**
  PCD Set interface wrapper.
  Sets a token in the PCD database.

  @param[in] TokenNumber  The PCD token number to set.
  @param[in] Value        The value to write.

**/
STATIC
VOID
EFIAPI
PcdSet32 (
  IN UINTN   TokenNumber,
  IN UINT64  Value
  )
{
  ASSERT (mPcdDb != NULL);
  ((VOID (*)(UINTN, UINT64))(*((UINT64 *)mPcdDb + 5)))(TokenNumber, Value);
}

//
// Memory Operations
//

/**
  Copy memory from source to destination.

  Implements a memcpy with overlapping support for forward/backward copies.

  @param[out] Destination  Pointer to the destination buffer.
  @param[in]  Source       Pointer to the source buffer.
  @param[in]  Length       Number of bytes to copy.

  @return Destination pointer.

**/
STATIC
VOID *
EFIAPI
InternalCopyMem (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Length
  )
{
  VOID   *Dst;
  UINTN  LengthRemaining;

  Dst = Destination;

  if (Source < Destination && (UINT8 *)Source + Length - 1 >= (UINT8 *)Destination) {
    //
    // Overlapping with Source before Destination: copy backwards
    //
    CopyMemBackwards (Dst, Source, Length);
  } else {
    //
    // Non-overlapping or Destination before Source: copy forwards
    //
    LengthRemaining = Length;
    Length &= 7;
    LengthRemaining >>= 3;
    if (LengthRemaining > 0) {
      CopyMem (Dst, Source, LengthRemaining * 8);
    }
    CopyMem ((UINT8 *)Dst + (LengthRemaining * 8),
             (UINT8 *)Source + (LengthRemaining * 8),
             Length);
  }

  return Dst;
}

/**
  Compare two memory buffers.

  @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 0 if the buffers are equal; nonzero if they differ.

**/
STATIC
INTN
EFIAPI
InternalCompareMem (
  IN CONST VOID *DestinationBuffer,
  IN CONST VOID *SourceBuffer,
  IN UINTN      Length
  )
{
  CONST UINT8  *Dst;
  CONST UINT8  *Src;
  BOOLEAN      IsEqual;

  Dst = (CONST UINT8 *)DestinationBuffer;
  Src = (CONST UINT8 *)SourceBuffer;

  do {
    if (Length == 0) {
      break;
    }
    IsEqual = (*Dst++ == *Src++);
    Length--;
  } while (IsEqual);

  return (INTN)(*(Dst - 1) - *(Src - 1));
}

//
// Unaligned and string operations
//

/**
  Calculate the length of an ASCII string.

  @param[in] String  Pointer to the null-terminated ASCII string.

  @return The number of characters in the string, excluding the terminator.

**/
STATIC
UINTN
EFIAPI
AsciiStrLen (
  IN CONST CHAR8  *String
  )
{
  UINTN  Length;

  ASSERT (String != NULL);

  Length = 0;
  while (*String != '\0') {
    if (Length >= MAX_UINT32) {
      ASSERT (FALSE);
    }
    String++;
    Length++;
  }

  return Length;
}

/**
  Read a 64-bit value from an unaligned pointer.

  @param[in] Buffer  Pointer to read from.

  @return The 64-bit value at the pointer.

**/
STATIC
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}

/**
  Read the GUID portion of a HOB entry for comparison.

  @param[in] HobGuid  Pointer to the GUID in a HOB.
  @param[in] Guid2    Pointer to the GUID to compare against.

  @retval TRUE   The GUIDs match.
  @retval FALSE  The GUIDs do not match.

**/
STATIC
BOOLEAN
EFIAPI
IsHobGuidEqual (
  IN CONST EFI_GUID  *HobGuid,
  IN CONST EFI_GUID  *Guid2
  )
{
  return ReadUnaligned64 (HobGuid) == ReadUnaligned64 (Guid2) &&
         ReadUnaligned64 ((UINT8 *)HobGuid + 8) == ReadUnaligned64 ((UINT8 *)Guid2 + 8);
}

//
// I/O and CPU helpers
//

/**
  Write a 16-bit value to an I/O port.

  @param[in] Port  The I/O port address to write to.

**/
STATIC
VOID
EFIAPI
IoWrite16 (
  IN UINT16  Port
  )
{
  ASSERT ((Port & 1) == 0);
  *(volatile UINT16 *)(UINTN)Port = 0x500;  // ACPI PM1a_CNT.SLP_TYPx + SLP_EN
}

/**
  Read a 32-bit value from an I/O port.

  @param[in] Port  The I/O port address to read from.

  @return The 32-bit value read from the port.

**/
STATIC
UINT32
EFIAPI
IoRead32 (
  IN UINT16  Port
  )
{
  ASSERT ((Port & 3) == 0);
  return __indword (Port);
}

/**
  Read the CPU's timestamp counter.

  @return The current TSC value.

**/
STATIC
UINT64
EFIAPI
ReadTimestampCounter (
  VOID
  )
{
  return __rdtsc ();
}

/**
  Read the caller's EFLAGS register via __getcallerseflags().

  @return The EFLAGS value at the call site.

**/
STATIC
UINT64
EFIAPI
ReadCallerEflags (
  VOID
  )
{
  return __getcallerseflags ();
}

/**
  Halt the CPU (PAUSE instruction).

**/
STATIC
VOID
EFIAPI
CpuPause (
  VOID
  )
{
  _mm_pause ();
}

/**
  Enable interrupts.

**/
STATIC
VOID
EFIAPI
EnableInterrupts (
  VOID
  )
{
  _enable ();
}

/**
  Disable interrupts.

**/
STATIC
VOID
EFIAPI
DisableInterrupts (
  VOID
  )
{
  _disable ();
}

//
// Protocol locator helpers
//

/**
  Locate the ACPI Support protocol.

  @return Pointer to the ACPI Support protocol interface, or NULL if not found.

**/
STATIC
VOID *
EFIAPI
GetAcpiSupportProtocol (
  VOID
  )
{
  UINT64  Status;
  VOID    *Interface;

  if (mAcpiSupport != NULL) {
    return mAcpiSupport;
  }

  Status = gBS->LocateProtocol (&gEfiAcpiSupportProtocolGuid, NULL, &Interface);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "AcpiResLib: LibGetDsdt(): LocateProtocol(ACPISupport) returned %r \n", Status));
    return NULL;
  }

  mAcpiSupport = Interface;
  return Interface;
}

/**
  Initialize ACPI Support protocol.
  Allocates and enables the ACPI table storage.

**/
STATIC
VOID
EFIAPI
InitializeAcpiSupport (
  VOID
  )
{
  VOID    *AcpiSupport;
  UINT64  Status;

  AcpiSupport = GetAcpiSupportProtocol ();

  Status = AcpiSupport->OpenTable (5, &mPciExpressBase);  // Allocate 5 pages for ACPI tables
  ASSERT_EFI_ERROR (Status);

  AcpiSupport->SetAcpiTable (&mPciExpressBase, 0, 0);
}

//
// HOB list management
//

/**
  Retrieve the HOB list pointer from the HOB list GUID in the System Table
  ConfigurationTable array.

  @return Pointer to the HOB list, or 0 if not found.

**/
STATIC
UINT64
EFIAPI
GetHobList (
  VOID
  )
{
  UINTN   Index;
  UINT64  HobListAddr;
  UINTN   NumberOfTableEntries;

  if (mHobList != 0) {
    return mHobList;
  }

  mHobList = 0;
  NumberOfTableEntries = gST->NumberOfTableEntries;

  for (Index = 0; Index < NumberOfTableEntries; Index++) {
    if (IsHobGuidEqual (&gEfiHobListGuid,
                        (EFI_GUID *)(gST->ConfigurationTable + (Index * sizeof (EFI_CONFIGURATION_TABLE))))) {
      HobListAddr = *(UINT64 *)(gST->ConfigurationTable + (Index * sizeof (EFI_CONFIGURATION_TABLE) + sizeof (EFI_GUID)));
      mHobList = HobListAddr;
      return HobListAddr;
    }
  }

  DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
  ASSERT (!EFI_ERROR (EFI_NOT_FOUND));
  ASSERT (mHobList != NULL);

  return mHobList;
}

/**
  Find the next HOB of type EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
  in the HOB list.

  @param[in] HobStart  Pointer to the current HOB.

  @return Pointer to the resource descriptor HOB, or 0 if not found.

**/
STATIC
VOID *
EFIAPI
GetNextResourceHob (
  IN VOID  *HobStart
  )
{
  UINT16  *Hob;

  Hob = (UINT16 *)HobStart;

  if (Hob == NULL) {
    ASSERT (HobStart != NULL);
  }

  while (TRUE) {
    if (*Hob == 0xFFFF) {
      //
      // End of HOB list
      //
      return NULL;
    }

    if (*Hob == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
      break;
    }

    Hob = (UINT16 *)((UINT8 *)Hob + Hob[1]);
  }

  return Hob;
}

//
// Assert and Debug
//

/**
  Assertion handler used by UEFI library code. Prints the file/line/expression
  and enters deadloop if debug is enabled.

  @param[in] FileName     File name where the assertion occurred.
  @param[in] LineNumber   Line number of the assertion.
  @param[in] Description  The assertion expression string.

**/
STATIC
VOID
EFIAPI
AssertHandler (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  VOID  *DebugProtocol;

  DebugProtocol = GetAcpiSupportProtocol ();
  if (DebugProtocol != NULL) {
    DebugProtocol->DebugAssert (FileName, LineNumber, Description);
  }
}

/**
  Debug print implementation using internal debug protocol.

  @param[in] ErrorLevel  Debug error level mask.
  @param[in] Format      Format string.
  @param[in] ...         Variable arguments.

  @return The character printed (or a status indicator).

**/
STATIC
CHAR8
EFIAPI
DebugPrint (
  IN UINT64       ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  VOID   *DebugProtocol;
  UINT8  CmosIndex;
  UINT8  CmosData;
  UINT8  DebugLevel;
  UINT8  DebugLevel2;
  UINT64  OutputType;

  VA_LIST  Args;
  VA_START (Args, Format);

  DebugProtocol = GetAcpiSupportProtocol ();
  if (DebugProtocol == NULL) {
    VA_END (Args);
    return 0;
  }

  //
  // Read CMOS index 0x4B to determine debug routing
  //
  CmosIndex = __inbyte (0x70);
  __outbyte (0x70, CmosIndex & 0x80 | 0x4B);
  CmosData = __inbyte (0x71);

  DebugLevel = CmosData;
  if (DebugLevel > 3) {
    DebugLevel2 = CmosData;
    if (DebugLevel2 == 0) {
      DebugLevel2 = (MEMORY[0xFDAF0490] & 2) | 1;
    }
  }

  DebugLevel = DebugLevel - 1;
  if (DebugLevel <= 0xFD) {
    DebugLevel = 4;
    OutputType = DEBUG_INFO;
    if (CmosData == 1) {
      OutputType = DEBUG_WARN;
    }
  }

  if ((OutputType & ErrorLevel) != 0) {
    return DebugProtocol->DebugPrint (ErrorLevel, Format, Args);
  }

  VA_END (Args);
  return 0;
}

// ---------------------------------------------------------------------------
// DSDT Locator
// ---------------------------------------------------------------------------

/**
  Locate the DSDT address by searching RSDT/XSDT tables from the
  ACPI Support protocol.

  Walks the RSDT (v1.0b) and XSDT (v2.0+) entries to find the
  DSDT signature and caches the address in mDsdtAddr/mXsdtDsdtAddr.

  @param[out] DsdtAddr  Pointer to receive the DSDT address.

  @retval EFI_SUCCESS           DSDT was found and cached.
  @retval EFI_NOT_FOUND         DSDT could not be located.
  @retval EFI_INVALID_PARAMETER DsdtAddr is NULL.
  @retval EFI_UNSUPPORTED       ACPI Support protocol not available.

**/
EFI_STATUS
EFIAPI
LibGetDsdt (
  OUT UINT64  *DsdtAddr
  )
{
  EFI_STATUS            Status;
  VOID                  *AcpiSupport;
  UINTN                 TableIndex;
  EFI_ACPI_TABLE_VERSION  TableVersion;
  VOID                  *Table;
  UINTN                 TableKey;
  UINT64                DsdtAddress;
  UINT32                *RsdtEntry;
  UINT64                *XsdtEntry;
  UINTN                 NumberOfTableEntries;
  UINTN                 EntryIndex;

  if (DsdtAddr == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check cache first
  //
  if (mDsdtAddr != 0 && mXsdtDsdtAddr != 0) {
    *DsdtAddr = (mXsdtDsdtAddr != 0) ? mXsdtDsdtAddr : mDsdtAddr;
    return EFI_SUCCESS;
  }

  //
  // Locate ACPI Support protocol
  //
  AcpiSupport = GetAcpiSupportProtocol ();
  if (AcpiSupport == NULL) {
    return EFI_UNSUPPORTED;
  }

  //
  // Get the RSDT/XSDT table
  //
  TableIndex = 0;
  while (TRUE) {
    Status = AcpiSupport->GetAcpiTable (TableIndex, &Table, &TableVersion, &TableKey);
    if (EFI_ERROR (Status)) {
      break;
    }

    //
    // Check for RSDT (1.0b) or XSDT (2.0+)
    //
    if (*(UINT32 *)Table == EFI_ACPI_RSDT_SIGNATURE) {
      //
      // RSDT found - search for DSDT pointer
      //
      NumberOfTableEntries = (*(UINT32 *)((UINT8 *)Table + 4) - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof (UINT32);
      RsdtEntry = (UINT32 *)((UINT8 *)Table + sizeof (EFI_ACPI_DESCRIPTION_HEADER));

      for (EntryIndex = 0; EntryIndex < NumberOfTableEntries; EntryIndex++) {
        if (*(UINT32 *)(UINTN)RsdtEntry[EntryIndex] == EFI_ACPI_DSDT_SIGNATURE) {
          mDsdtAddr = RsdtEntry[EntryIndex];
          DEBUG ((DEBUG_INFO, "AcpiResLib: LibGetDsdt(): Found v1.0b   RSDT->DSDT @ 0x%lX\n", mDsdtAddr));
          break;
        }
      }
    }

    if (*(UINT32 *)Table == EFI_ACPI_XSDT_SIGNATURE) {
      //
      // XSDT found - search for DSDT pointer
      //
      NumberOfTableEntries = (*(UINT32 *)((UINT8 *)Table + 4) - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof (UINT64);
      XsdtEntry = (UINT64 *)((UINT8 *)Table + sizeof (EFI_ACPI_DESCRIPTION_HEADER));

      for (EntryIndex = 0; EntryIndex < NumberOfTableEntries; EntryIndex++) {
        if (*(UINT32 *)(UINTN)XsdtEntry[EntryIndex] == EFI_ACPI_DSDT_SIGNATURE) {
          mXsdtDsdtAddr = XsdtEntry[EntryIndex];
          DEBUG ((DEBUG_INFO, "AcpiResLib: LibGetDsdt(): Found v2.0&UP XSDT->DSDT @ 0x%lX\n", mXsdtDsdtAddr));
          break;
        }
      }
    }

    TableIndex++;
  }

  //
  // Report if we couldn't find DSDT
  //
  if (mDsdtAddr == 0 && mXsdtDsdtAddr == 0) {
    DEBUG ((DEBUG_ERROR, "Can't find Dsdt table -> %r search %d Tables\n", EFI_NOT_FOUND, TableIndex));
    return EFI_NOT_FOUND;
  }

  *DsdtAddr = (mXsdtDsdtAddr != 0) ? mXsdtDsdtAddr : mDsdtAddr;
  return EFI_SUCCESS;
}

// ---------------------------------------------------------------------------
// ACPI AML Write Helper
// ---------------------------------------------------------------------------

/**
  Write an integer value into an ACPI AML buffer at a location
  identified by a signature search.

  Searches the DSDT for the specified signature bytes, then writes
  the given value into the AML encoding at that location based on the
  opcode (BYTE, WORD, DWORD, QWORD).

  @param[in] DsdtBase  Base address of the DSDT.
  @param[in] DsdtSize  Size of the DSDT in bytes.
  @param[in] Signature 4-byte signature to search for in the DSDT.
  @param[in] Value     The value to write.

  @retval EFI_SUCCESS             The value was written successfully.
  @retval EFI_NOT_FOUND           The signature was not found in the DSDT.
  @retval EFI_INVALID_PARAMETER   The AML opcode at the location is unrecognized.
  @retval EFI_BUFFER_TOO_SMALL    The DSDT region is too small for the value.

**/
EFI_STATUS
EFIAPI
AcpiAmlWriteInteger (
  IN UINT64  DsdtBase,
  IN UINT64  DsdtSize,
  IN UINT32  Signature,
  IN UINT64  Value
  )
{
  UINT8   *BytePtr;
  UINTN   Offset;
  UINTN   FoundOffset;
  BOOLEAN Found;

  Found     = FALSE;
  FoundOffset = 0;

  while (TRUE) {
    //
    // Search for the signature in the DSDT
    //
    for (Offset = FoundOffset; Offset < DsdtSize; Offset += 4) {
      if (*(UINT32 *)(DsdtBase + Offset) == Signature) {
        FoundOffset = Offset;
        Found       = TRUE;
        break;
      }
    }

    if (!Found) {
      return EFI_NOT_FOUND;
    }

    //
    // Check the AML NameString prefix before the signature
    //
    BytePtr = (UINT8 *)(DsdtBase + FoundOffset - 1);
    if (((*BytePtr - 0x5C) & 0xFD) == 0) {
      BytePtr--;
    }

    if (*(BytePtr - 1) == AML_DUAL_NAME_PREFIX) {
      break;
    }

    //
    // Skip past this match and search again
    //
    DsdtSize -= FoundOffset + 4;
    DsdtBase += FoundOffset + 4;
    FoundOffset = 0;
  }

  //
  // Write the value based on the AML opcode after the signature
  //
  switch (*(BytePtr + 4)) {
  case AML_ZERO_OP:
  case AML_ONE_OP:
    //
    // Replace ZeroOp/OneOp with the value
    //
    if (Value > 1) {
      return EFI_INVALID_PARAMETER;
    }
    *(BytePtr + 4) = (UINT8)Value;
    return EFI_SUCCESS;

  case AML_BYTE_PREFIX:
    *(BytePtr + 5) = (UINT8)Value;
    return EFI_SUCCESS;

  case AML_WORD_PREFIX:
    *(UINT16 *)(BytePtr + 5) = (UINT16)Value;
    return EFI_SUCCESS;

  case AML_DWORD_PREFIX:
    *(UINT32 *)(BytePtr + 5) = (UINT32)Value;
    return EFI_SUCCESS;

  case AML_QWORD_PREFIX:
    *(UINT64 *)(BytePtr + 5) = Value;
    return EFI_SUCCESS;

  default:
    return EFI_INVALID_PARAMETER;
  }
}

// ---------------------------------------------------------------------------
// TPM Hardware Detection
// ---------------------------------------------------------------------------

/**
  Check if TPM 2.0 hardware is present and active.

  Reads the TPM interface ID register at 0xFED40030.
  If the value is not 0xFFFFFFFF (no device) and is nonzero,
  TPM hardware is considered present.

  @retval TRUE   TPM hardware is present.
  @retval FALSE  TPM hardware is not present.

**/
STATIC
BOOLEAN
EFIAPI
IsTpmHwPresent (
  VOID
  )
{
  return (MEMORY[TPM_REG_BASE + 0x30] != 0xFFFFFFFF) &&
         (MEMORY[TPM_REG_BASE + 0x30] != 0);
}

/**
  Get the TPM interface type.

  Reads the lower nibble of the TPM interface ID register.

  @return The TPM interface type (0-15), or the result of sub_1534()
          if hardware is not present.

**/
STATIC
UINT8
EFIAPI
GetTpmInterfaceType (
  VOID
  )
{
  UINT8  InterfaceType;

  InterfaceType = MEMORY[TPM_REG_BASE + 0x30] & 0x0F;

  if (IsTpmHwPresent ()) {
    return InterfaceType;
  } else {
    return (UINT8)sub_1534 ();
  }
}

/**
  Determine if TPM is available.

  Checks via the firmware TPM detection flag, and falls back
  to hardware detection (FIFO/TIS interface type == 1).

  @retval TRUE   TPM is available.
  @retval FALSE  TPM is not available.

**/
STATIC
BOOLEAN
EFIAPI
IsTpmAvailable (
  VOID
  )
{
  if (sub_1534 () != 0) {
    return TRUE;
  }

  return IsTpmHwPresent () && (GetTpmInterfaceType () == 1);
}

//
// PCI Express MMIO access
//

/**
  Read from PCI Express configuration space via MMIO.

  @param[in] Address  The PCI Express extended configuration space address.

  @return The value at the address plus the PCI Express base.

**/
STATIC
UINT64
EFIAPI
PciExpressRead (
  IN UINT64  Address
  )
{
  ASSERT ((Address & ~0xFFFFFFF) == 0);
  return Address + mPciExpressBase;
}

// ---------------------------------------------------------------------------
// Main TPM ACPI Initialization
// ---------------------------------------------------------------------------

/**
  TPM 2.0 ACPI initialization entry function.

  This is the main initialization routine called as a callback. It:
  1. Initializes the ACPI support protocol and PCD database
  2. Locates the DSDT table
  3. Detects TPM hardware (FIFO/TIS vs TPP vs no TPM)
  4. Determines the TPM stolen memory address
  5. Programs the ACPI SSDT tables with TPM configuration
  6. Installs the TPM2 ACPI table

  @param[in] a1  The event context (ImageHandle-like).

  @retval EFI_SUCCESS  The TPM ACPI tables were published successfully.
  @retval Others       An error occurred.

**/
EFI_STATUS
EFIAPI
Tpm20AcpiInitEntry (
  IN UINT64  a1
  )
{
  EFI_STATUS  Status;
  UINT64      DsdtAddr;
  UINT64      StolenAddr;
  UINT64      HobPtr;
  VOID        *ResourceHob;
  UINT64      StolenAddressLoc;
  UINT64      *CtrlAreaMap;
  UINT64      TpmModeN2;
  UINT64      PcdPtr;
  UINT64      Tpm24Data;
  UINT64      Status2;
  VOID        *DsdtInterface;
  UINT64      Result;
  UINT64      Buffer;
  UINT64      DsdtSignature;
  VOID        *BufferPtr;

  //
  // Locate DSDT
  //
  Status = LibGetDsdt (&DsdtAddr);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Initialize ACPI tables
  //
  InitializeAcpiSupport ();

  //
  // Detect TPM hardware availability
  //
  if (!IsTpmAvailable ()) {
    //
    // No TPM: set defaults
    //
    mTpmStolenAddr = 0;
    mTpmMode = 6;  // No TPM

    //
    // Write no-TPM configuration to DSDT
    //
    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_AMDT, 0);
    if (EFI_ERROR (Status)) return Status;

    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_DTPT, 0);
    if (EFI_ERROR (Status)) return Status;

    //
    // Write "TPMM" with default stolen address (no TPM)
    //
    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_TPMM, 0xFED00000);
    if (EFI_ERROR (Status)) return Status;

    return EFI_SUCCESS;
  }

  //
  // TPM is available - write TPMF=1
  //
  Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_TPMF, 1);
  if (EFI_ERROR (Status)) return Status;

  //
  // Check if TPM has active interface
  //
  if (MEMORY[TPM_REG_BASE + TPM_CTRL_AREA_OFFSET] == 0xFFFFFFFF) {
    //
    // TPM with no active interface (TPP mode)
    //
    mTpmMode = 7;
    mTpmStolenAddr = 0xFED00080;

    //
    // Dump CtrlAreaMap registers
    //
    DEBUG ((DEBUG_INFO, "CtrlAreaMap->Error = %x \n", MEMORY[TPM_REG_BASE + TPM_CTRL_ERROR]));
    DEBUG ((DEBUG_INFO, "CtrlAreaMap->Cancel = %x \n", MEMORY[TPM_REG_BASE + TPM_CTRL_CANCEL]));
    DEBUG ((DEBUG_INFO, "CtrlAreaMap->Start = %x \n", MEMORY[TPM_REG_BASE + TPM_CTRL_START]));
    DEBUG ((DEBUG_INFO, "CtrlAreaMap->Reserved = %x \n", MEMORY[TPM_REG_BASE + TPM_CTRL_RESERVED]));
    DEBUG ((DEBUG_INFO, "CtrlAreaMap->CommandSize = %x \n", MEMORY[TPM_REG_BASE + TPM_CTRL_COMMAND_SIZE]));
    DEBUG ((DEBUG_INFO, "CtrlAreaMap->Command = %x \n", MEMORY[TPM_REG_BASE + TPM_CTRL_COMMAND]));
    DEBUG ((DEBUG_INFO, "CtrlAreaMap->ResponseSize = %x \n", MEMORY[TPM_REG_BASE + TPM_CTRL_RESPONSE_SIZE]));
    DEBUG ((DEBUG_INFO, "CtrlAreaMap->Response = %x \n", MEMORY[TPM_REG_BASE + TPM_CTRL_RESPONSE]));

    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_TPMM, (UINT32)(mTpmStolenAddr - 64));
    if (EFI_ERROR (Status)) return Status;

    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_FTPM, (UINT32)mTpmStolenAddr);
    if (EFI_ERROR (Status)) return Status;

    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_AMDT, 0);
    if (EFI_ERROR (Status)) return Status;

    //
    // Program "DTPT" with 1
    //
    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_DTPT, 1);
    if (EFI_ERROR (Status)) return Status;

  } else {
    //
    // TPM with active interface - find stolen address from HOB
    //
    HobPtr = GetHobList ();

    StolenAddressLoc = 0;
    for (HobPtr = GetHobList (); ; HobPtr = (UINT64)((UINT8 *)HobPtr + *(UINT16 *)(HobPtr + 2))) {
      ResourceHob = GetNextResourceHob ((VOID *)HobPtr);
      if (ResourceHob == NULL) {
        break;
      }

      if (IsHobGuidEqual ((EFI_GUID *)((UINT8 *)ResourceHob + 8), (EFI_GUID *)mGuid)) {
        break;
      }
    }

    DEBUG ((DEBUG_INFO, "Tpm20Acpi StolenAddress = %x \n", *(UINT64 *)((UINT8 *)ResourceHob + 24)));
    DEBUG ((DEBUG_INFO, "Tpm20Acpi StolenAddress Loc = %x \n", ResourceHob));

    //
    // Determine stolen address
    //
    StolenAddr = STOLEN_ADDR_DEFAULT;
    if (*(UINT64 *)((UINT8 *)ResourceHob + 24) != 0) {
      StolenAddr = *(UINT64 *)((UINT8 *)ResourceHob + 24);
    }
    mTpmStolenAddr = StolenAddr;
    mTpmMode = 2;  // TPM2 with DMA

    //
    // Zero the stolen memory region if it's not the default location
    //
    if (StolenAddr != STOLEN_ADDR_DEFAULT) {
      gBS->SetMem ((VOID *)StolenAddr, 48, 0);
    }

    //
    // Set up TPM control area in stolen memory
    //
    *(UINT32 *)(StolenAddr + 24) = 0;   // Response
    *(UINT32 *)(StolenAddr + 36) = 0;   // ResponseSize
    *(UINT64 *)(StolenAddr + 28) = (UINT64)(StolenAddr + 128);   // Command buffer
    *(UINT64 *)(StolenAddr + 40) = (UINT64)(StolenAddr + 128);   // Response buffer

    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_TPMM, STOLEN_ADDR_DEFAULT);
    if (EFI_ERROR (Status)) return Status;

    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_FTPM, (UINT32)mTpmStolenAddr);
    if (EFI_ERROR (Status)) return Status;

    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_AMDT, 0);
    if (EFI_ERROR (Status)) return Status;

    //
    // Program "DTPT" with 0
    //
    Status = AcpiAmlWriteInteger (DsdtAddr, MAX_UINT32, SIGNATURE_DTPT, 0);
    if (EFI_ERROR (Status)) return Status;
  }

  //
  // Install TPM2 ACPI table
  //
  if (mTpmMode == 2) {
    //
    // Allocate and populate TPM2 table
    //
    Status = gBS->AllocatePool (EfiBootServicesData, 56, &BufferPtr);
    if (EFI_ERROR (Status)) return Status;

    gBS->SetMem (BufferPtr, 56, 0);

    //
    // Copy "TPM24" signature header (52 bytes)
    //
    CopyMem (BufferPtr, "TPM24", 52);
    *(UINT32 *)((UINT8 *)BufferPtr + 4) = *(UINT32 *)&"TPM24"[4] + 4;

    //
    // Install via ACPI table protocol
    //
    Status = ((EFI_ACPI_TABLE_PROTOCOL *)DsdtInterface)->InstallAcpiTable (
               (EFI_ACPI_TABLE_PROTOCOL *)DsdtInterface,
               BufferPtr,
               56,
               (UINTN *)&Buffer
               );

    gBS->FreePool (BufferPtr);
  } else {
    //
    // Direct install of TPM24 signature
    //
    Status = ((EFI_ACPI_TABLE_PROTOCOL *)DsdtInterface)->InstallAcpiTable (
               (EFI_ACPI_TABLE_PROTOCOL *)DsdtInterface,
               "TPM24",
               52,
               (UINTN *)&Buffer
               );
  }

  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Signal that initialization is complete
  //
  Status = gBS->SignalEvent ((EFI_EVENT)a1);

  return Status;
}

// ---------------------------------------------------------------------------
// Module Entry Point
// ---------------------------------------------------------------------------

/**
  Entry point to the Tpm20Acpi UEFI driver.

  Saves the UEFI boot/runtime service pointers, initializes the
  PCD database, sets up the HOB list, registers the initialization
  event, and returns.

  @param[in] ImageHandle  The firmware allocated handle for the UEFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS  The driver entry point executed successfully.

**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Initialize UEFI Boot/Runtime Services Table
  //
  InitializeUefiBootServicesTable (ImageHandle, SystemTable);

  //
  // Initialize PCD database via LocateProtocol
  //
  mPcdDb = (UINT64)PcdGet32 (5);
  if (*(CHAR8 *)PciExpressRead (PCD_TOKEN_SOMETHING) >= 0) {
    PcdSet32 (PCD_TOKEN_SOMETHING, PcdGet32 (PCD_TOKEN_SOMETHING_ELSE));
    *(UINT8 *)PciExpressRead (PCD_TOKEN_FLAGS) |= 0x80;
  }

  //
  // Detect TPM hardware via EFLAGS.IF check
  //
  if (ReadCallerEflags () & 0x200) {
    EnableInterrupts ();
    DisableInterrupts ();
  }

  //
  // Initialize ACPI tables
  //
  InitializeAcpiSupport ();

  //
  // Timeout loop using TSC
  //
  while (((IoRead32 (1288) + 357 - IoRead32 (1288)) & 0x800000) == 0) {
    CpuPause ();
  }

  //
  // Enable/disable interrupts based on original state
  //
  EnableInterrupts ();
  if (ReadCallerEflags () & 0x200) {
    EnableInterrupts ();
  } else {
    DisableInterrupts ();
  }

  //
  // Register the initialization event via CreateEvent
  //
  return gBS->CreateEvent (
                EVT_NOTIFY_SIGNAL,
                TPL_CALLBACK,
                (EFI_EVENT_NOTIFY)Tpm20AcpiInitEntry,
                NULL,
                &ImageHandle
                );
}