Newer
Older
AMI-Aptio-BIOS-Reversed / TcgLegacy / TcgLegacy.c
@Ajax Dong Ajax Dong 2 days ago 41 KB Init
/** @file
  TcgLegacy.c -- TCG Legacy DXE Driver

  This DXE driver implements TCG Legacy BIOS support for TPM 1.2 on UEFI
  systems. It bridges the EFI TCG (Trusted Computing Group) protocol with
  legacy BIOS INT hooks, providing TPM functionality to legacy option ROMs
  and non-UEFI boot paths.

  Functional Overview:
  1. The entry point (ModuleEntryPoint) saves required UEFI boot service
     globals and calls InitTCGLegacyInterface().
  2. InitTCGLegacyInterface() locates the Legacy BIOS Protocol, optionally
     the TCG2 Protocol, and loads two firmware volume files:
     - LEGX16: The legacy BIOS 16-bit thunk layer (copied to F000-E000).
     - TPM32:  The TPM 1.2 legacy driver (copied to a page-aligned region).
     - MPTPM:  An optional multi-processor TPM binary.
  3. It then allocates TPM code space, installs callbacks, and patches the
     BFI ($BIN) marker in the shadow area to complete the legacy interface.
  4. A LegacyBoot event is registered via CreateEventEx to unlink the
     TPM driver when the OS boots.

  File: AmiModulePkg/TCG2/Common/TcgLegacy/TcgLegacy.c
  Binary: TcgLegacy.efi (X64)
  Size: ~8 KB (0x1FC0)
  Source: AMI BIOS, HR650X platform

  Copyright (C) AMI Corporation. All rights reserved.
**/

#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Protocol/LegacyBios.h>
#include <Protocol/TcgService.h>
#include "TcgLegacy.h"

//
// ---------------------------------------------------------------------------
// External global data references (populated by ModuleEntryPoint)
// ---------------------------------------------------------------------------
//
// These are written once by the DXE core bootstrap code before the driver's
// entry point is called; the entry point simply caches them in variables.
//
#pragma data_seg(".data")

//
// Legacy BIOS interface function pointer (stored at 0x1DD0)
// When byte_1DE0 == 0: points to the EFI_LEGACY_BIOS_PROTOCOL instance.
// When byte_1DE0 == 1: set to the TCG2 protocol interface.
//
UINT64  qword_1DD0 = 0;

//
// Installed TPM32 header pointer (stored at 0x1DD8)
// Non-NULL once LinkTPM32Driver() succeeds.
//
UINT64  qword_1DD8 = 0;

//
// Protocol selection flag (stored at 0x1DE0 as byte)
// If 0: Legacy BIOS Protocol is used.
// If 1: TCG2 Protocol is used instead.
//
UINT8   byte_1DE0 = 0;

//
// TCG2 Protocol pointer (stored at 0x1DE8)
// Valid only when byte_1DE0 == 1.
//
UINT64  qword_1DE8 = 0;

//
// System Table pointer (stored at 0x1DF0)
//
EFI_SYSTEM_TABLE  *gST = NULL;

//
// Boot Services pointer (stored at 0x1DF8)
//
EFI_BOOT_SERVICES *gBS = NULL;

//
// Image handle (stored at 0x1E00)
//
EFI_HANDLE        gImageHandle = NULL;

//
// Runtime Services pointer (stored at 0x1E08)
//
EFI_RUNTIME_SERVICES *gRT = NULL;

//
// Debug Console Output protocol (stored at 0x1E10, cached from GetTcgProtocol)
//
EFI_TCG_PROTOCOL  *gTcgProtocol = NULL;

//
// HOB List pointer (stored at 0x1E18)
//
VOID              *gHobList = NULL;

//
// CMOS variable byte read from NVRAM index 0x4B (stored at 0x1E20)
// Used by DebugPrint to determine verbosity level.
//
UINT8             gDebugNvramByte = 0;

//
// LEGX16 configuration words (stored at 0x1E28-0x1E30)
// These are saved from the LEGX16 header and written to legacy BIOS
// configuration space during install.
//
UINT16  gLegX16Segment  = 0;   // +0x1E28
UINT16  gLegX16Shadow   = 0;   // +0x1E2A
UINT16  gLegX16Init     = 0;   // +0x1E2C
UINT16  gLegX16Checksum = 0;   // +0x1E2E
UINT16  gLegX16Size     = 0;   // +0x1E30

#pragma data_seg()

//
// TPM Vendor ID Lookup Table (stored at 0x1DC0)
// Each 4-byte entry contains a TPM hardware vendor/device ID.
// The table is compared against MEMORY[0xFED40F00] to detect if
// TPM hardware is already present.
//
// Format: 3 entries, each 4 bytes:
//   Entry 0: 0xF5190100 (vendor:device flags)
//   Entry 1: 0x011B4E01 (vendor: "NS" = National Semiconductor?)
//   Entry 2: 0x00020000 (placeholder/end marker)
//
UINT32  gTpmVendorIdTable[3] = {
  0xF5190100,
  0x011B4E01,
  0x00020000
};

//
// GUID data blocks (must match the GUIDs declared in the header)
//
EFI_GUID  gEfiTcgEventLogFormatGuid     = TCG_EVENT_LOG_FORMAT_GUID;
EFI_GUID  gEfiHobListGuid               = EFI_HOB_LIST_GUID;
EFI_GUID  gEfiEventLegacyBootGuid       = EFI_EVENT_LEGACY_BOOT_GUID;
EFI_GUID  gEfiLegacyBiosProtocolGuid    = EFI_LEGACY_BIOS_PROTOCOL_GUID;
EFI_GUID  gEfiLegacyInterruptProtocolGuid = EFI_LEGACY_INTERRUPT_PROTOCOL_GUID;
EFI_GUID  gEfiLegacyBiosPlatformGuid    = EFI_LEGACY_BIOS_PLATFORM_PROTOCOL_GUID;
EFI_GUID  gAmiTpmPlatformProtocolGuid   = AMI_TPM_PLATFORM_PROTOCOL_GUID;
EFI_GUID  gEfiTcgProtocolGuid           = EFI_TCG_PROTOCOL_GUID;
EFI_GUID  gEfiTcg2ProtocolGuid          = EFI_TCG2_PROTOCOL_GUID;

//
// ---------------------------------------------------------------------------
// Internal / Library function implementations
// ---------------------------------------------------------------------------

/**
  ZeroMemory -- fill a buffer with zeros.

  This is called by ZeroMem() and from the internal AllocateAndZeroPages()
  function.  It uses memset() internally.

  @param[in,out] Buffer  Pointer to the buffer to fill with zeros.
  @param[in]     Length  Number of bytes to fill.

  @return Buffer as passed.
**/
VOID *
EFIAPI
InternalZeroMem (
  VOID   *Buffer,
  UINTN  Length
  )
{
  //
  // Zero the buffer using UINT64-aligned writes for the bulk,
  // and byte writes for the remainder.
  //
  ZeroMem (Buffer, Length);
  return Buffer;
}

/**
  CompareMem -- compare two memory buffers.

  @param[in] DestinationBuffer  Pointer to the first memory buffer.
  @param[in] SourceBuffer       Pointer to the second memory buffer.
  @param[in] Length             Number of bytes to compare.

  @return 0 if the buffers are equal; non-zero otherwise.
**/
INTN
EFIAPI
InternalCompareMem (
  CONST VOID  *DestinationBuffer,
  CONST VOID  *SourceBuffer,
  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);
}

/**
  ReadUnaligned64 -- read a 64-bit value from an unaligned address.

  @param[in] Buffer  Pointer to the potentially unaligned buffer.

  @return The 64-bit value read from Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  CONST UINT64  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *Buffer;
}

/**
  DebugPrint -- formatted debug output via EFI debug console protocol.

  Checks CMOS NVRAM byte at index 0x4B to determine debug verbosity.
  If the current error level matches, the message is printed through
  the EFI_TCG_PROTOCOL's debug output method (or a console protocol).

  @param[in] ErrorLevel  Debug error level (bitmask).
  @param[in] Format      PRINT-style format string.
  @param[in] ...         Variable arguments.

  @return TRUE if the message was output; FALSE otherwise.
**/
BOOLEAN
EFIAPI
DebugPrint (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  EFI_TCG_PROTOCOL  *TcgProtocol;
  UINTN             PrintLevel;
  UINT8             NvramByte;
  UINT8             NvramStatus;
  BOOLEAN           Result;

  PrintLevel   = 0;
  Result       = FALSE;
  TcgProtocol  = GetTcgProtocol ();

  if (TcgProtocol != NULL) {
    //
    // Read CMOS NVRAM index 0x4B to determine debug verbosity.
    //
    NvramByte    = IoRead8 (0x70);
    IoWrite8 (0x70, NvramByte & 0x80 | 0x4B);
    NvramStatus  = IoRead8 (0x71);

    if (NvramStatus > 3) {
      if (NvramStatus == 0) {
        //
        // Read platform-specific debug flag from 0xFDAF0490.
        //
        NvramStatus = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
      }
    }

    gDebugNvramByte = NvramStatus;

    //
    // Determine print level: EFI_D_INFO (0x80000004) if verbosity == 1,
    // EFI_D_WARN (0x80000006) otherwise.
    //
    if ((NvramStatus - 1) <= 0xFD) {
      PrintLevel = EFI_D_WARN;
      if (NvramStatus == 1) {
        PrintLevel = EFI_D_INFO;
      }
    }

    //
    // If the message's error level matches the current verbosity, print it.
    //
    if ((PrintLevel & ErrorLevel) != 0) {
      VA_LIST  Args;
      VA_START (Args, Format);
      Result = (BOOLEAN)TcgProtocol->DebugPrint (ErrorLevel, Format, Args);
      VA_END (Args);
    }
  }

  return Result;
}

/**
  DebugAssert -- break on assertion failure.

  Invokes the TCG protocol assertion handler if available.

  @param[in] FileName    Source file name.
  @param[in] LineNumber  Line number of the assertion.
  @param[in] Description Assertion description string.
**/
VOID
EFIAPI
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  EFI_TCG_PROTOCOL  *TcgProtocol;

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

/**
  ZeroMem wrapper with bounds checking.

  Checks that the buffer is valid and that the length does not overflow,
  then calls InternalZeroMem.

  @param[in,out] Buffer  Pointer to the buffer to zero.
  @param[in]     Length  Number of bytes to zero.
**/
VOID
EFIAPI
ZeroMem (
  IN VOID   *Buffer,
  IN UINTN  Length
  )
{
  if (Length == 0) {
    return;
  }

  ASSERT (Buffer != NULL);
  ASSERT (Length <= (MAX_UINTN - (UINTN)Buffer + 1));

  InternalZeroMem (Buffer, Length);
}

/**
  Get the HOB (Hand-Off Block) list pointer.

  Scans the System Table's configuration table for the HOB List GUID
  ({36232936-0E76-31C8-A13A-3AF2FC1C3932}) and caches the pointer.
  This is the DXE equivalent of GetHobList() from the HOB Library.

  @return Pointer to the HOB list, or NULL if not found.
**/
VOID *
EFIAPI
GetHobList (
  VOID
  )
{
  UINTN     Index;
  UINT64    SystemTable;
  UINT64    ConfigTable;
  EFI_GUID  *Guid;

  if (gHobList == NULL) {
    SystemTable = (UINT64)gST;
    gHobList    = NULL;

    if (gST->NumberOfTableEntries > 0) {
      for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
        ConfigTable = (UINT64)gST->ConfigurationTable;
        Guid        = &gST->ConfigurationTable[Index].VendorGuid;

        if (IsHobGuidMatch ((EFI_GUID *)EFI_HOB_LIST_GUID_PTR, Guid)) {
          //
          // Found the HOB List entry; cache the pointer.
          //
          gHobList = gST->ConfigurationTable[Index].VendorTable;
          break;
        }
      }
    }

    if (gHobList == NULL) {
      DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = Not Found)\n"));
      ASSERT (!EFI_ERROR (EFI_NOT_FOUND));
    }

    ASSERT (gHobList != NULL);
  }

  return gHobList;
}

/**
  HOB GUID comparison helper.

  Compares two GUIDs by reading their QWORD values.

  @param[in] Guid1  Pointer to first GUID.
  @param[in] Guid2  Pointer to second GUID.

  @return TRUE if both GUIDs match.
**/
BOOLEAN
IsHobGuidMatch (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  UINT64  Val1a, Val1b;
  UINT64  Val2a, Val2b;

  Val1a = ReadUnaligned64 ((UINT64 *)&gEfiHobListGuid);
  Val2a = ReadUnaligned64 ((UINT64 *)Guid2);
  Val1b = ReadUnaligned64 (((UINT64 *)&gEfiHobListGuid) + 1);
  Val2b = ReadUnaligned64 (((UINT64 *)Guid2) + 1);

  return (Val1a == Val2a) && (Val1b == Val2b);
}

/**
  Locate the EFI TCG Protocol.

  Looks up the TCG Protocol by GUID from the handle database.  If the
  system has >= 17 bytes of CMOS bank 0x4B (an unusual configuration),
  the protocol is not returned.  The result is cached in gTcgProtocol.

  @return Pointer to the EFI_TCG_PROTOCOL instance, or NULL if not found.
**/
EFI_TCG_PROTOCOL *
GetTcgProtocol (
  VOID
  )
{
  UINT64  NvramSize;

  if (gTcgProtocol == NULL) {
    //
    // Read CMOS index 0x4B to determine NVRAM bank size.
    //
    IoWrite8 (0x70, 0x4B);
    NvramSize = IoRead8 (0x71);
    IoWrite8 (0x70, 0x4B);
    IoRead8 (0x71);

    if (NvramSize <= 0x10) {
      //
      // NVRAM bank is 16 bytes or fewer; try to locate the TCG protocol.
      //
      if (gBS->LocateProtocol (
                 &gEfiTcgProtocolGuid,
                 NULL,
                 (VOID **)&gTcgProtocol
                 ) < 0) {
        gTcgProtocol = NULL;
      }
    } else {
      gTcgProtocol = NULL;
    }
  }

  return gTcgProtocol;
}

/**
  Locate a firmware file by GUID from the firmware volume protocol.

  Scans all FV instances and searches for a file matching the given GUID.
  Returns the file's buffer and size.

  @param[in]  FileGuid    GUID of the file to locate.
  @param[out] FileBuffer  Receives pointer to the file buffer.
  @param[out] FileSize    Receives the file size.

  @return EFI_SUCCESS if found; EFI_NOT_FOUND if not found.
  @return Error codes from LocateHandleBuffer or ReadSection.
**/
EFI_STATUS
LocateFirmwareVolumeFile (
  IN  EFI_GUID  *FileGuid,
  OUT VOID      **FileBuffer,
  OUT UINTN     *FileSize
  )
{
  EFI_STATUS            Status;
  UINTN                 HandleCount;
  EFI_HANDLE            *HandleBuffer;
  UINTN                 Index;
  EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
  UINTN                 Key;
  UINT32                AuthenticationStatus;

  *FileBuffer = NULL;
  *FileSize   = 0;
  HandleCount = 0;
  HandleBuffer = NULL;

  //
  // Locate all FV protocol handles.
  //
  Status = gBS->LocateHandleBuffer (
                  ByProtocol,
                  &gEfiFirmwareVolume2ProtocolGuid,
                  NULL,
                  &HandleCount,
                  &HandleBuffer
                  );
  if (EFI_ERROR (Status) || HandleCount == 0) {
    return EFI_NOT_FOUND;
  }

  //
  // Scan each FV for the file by GUID.
  //
  for (Index = 0; Index < HandleCount; Index++) {
    Status = gBS->HandleProtocol (
                    HandleBuffer[Index],
                    &gEfiFirmwareVolume2ProtocolGuid,
                    (VOID **)&FvProtocol
                    );
    if (EFI_ERROR (Status)) {
      continue;
    }

    Status = FvProtocol->ReadSection (
                           FvProtocol,
                           FileGuid,
                           EFI_SECTION_RAW,
                           0,
                           FileBuffer,
                           FileSize,
                           &AuthenticationStatus
                           );
    if (!EFI_ERROR (Status)) {
      //
      // Found the file.
      //
      gBS->FreePool (HandleBuffer);
      return EFI_SUCCESS;
    }
  }

  gBS->FreePool (HandleBuffer);
  return EFI_NOT_FOUND;
}

/**
  Allocate one or more pages and zero them.

  Wrapper around gBS->AllocatePages() with AllocateAnyPages type and
  EFI_MEMORY_TYPE 5 (EfiReservedMemoryType).  Returns the physical
  address of the allocated pages.

  @param[in] Pages  Number of 4 KB pages.

  @return Physical address (as UINT64), or 0xFFFFFFFF on failure.
**/
UINT64
AllocateAndZeroPages (
  IN UINTN  Pages
  )
{
  EFI_STATUS            Status;
  EFI_PHYSICAL_ADDRESS  Address;
  UINT64                Result;

  Address = 0xFFFFFFFFFFFFFFFFULL;
  Status  = gBS->AllocatePages (
                   AllocateAnyPages,
                   EfiReservedMemoryType,
                   Pages,
                   &Address
                   );

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT (!EFI_ERROR (Status));
  }

  if (!EFI_ERROR (Status)) {
    ZeroMem ((VOID *)(UINTN)Address, EFI_PAGES_TO_SIZE (Pages));
  }

  return (UINT64)Address;
}

///
/// Forward declaration of internal helper.
///
UINT16 *
FindTpmVendorEntry (
  IN  UINTN   NumberOfTableEntries,
  IN  UINT64  DevicePathTable,
  OUT UINT16  *OutMatchingEntry
  );

/**
  Search a table of ACPI device path entries for a TPM vendor match.

  The table is an array of fixed-size entries (24 bytes each), where
  each entry has a 16-byte GUID followed by vendor-specific fields.
  If the first 16 bytes of an entry match gEfiTcgEventLogFormatGuid and
  the entry's type field is 4 (vendor-specific ACPI), it returns the
  matching entry pointer + 12.

  @param[in]  NumberOfTableEntries  Number of entries in the table.
  @param[in]  DevicePathTable       Base address of the table.
  @param[out] OutMatchingEntry      Unused (may be NULL).

  @return Pointer to the matching entry + 12 (the vendor data), or NULL.
**/
UINT16 *
FindTpmVendorEntry (
  IN  UINTN   NumberOfTableEntries,
  IN  UINT64  DevicePathTable,
  OUT UINT16  *OutMatchingEntry
  )
{
  UINTN    Index;
  UINT64   EntryBase;
  UINT16   *Entry;
  UINT16   *Current;
  INTN     Type;
  UINT16   *Result;

  if (NumberOfTableEntries == 0) {
    return NULL;
  }

  for (Index = 0; Index < NumberOfTableEntries; Index++) {
    //
    // Each entry is 24 bytes; navigate backwards from the end.
    //
    EntryBase = DevicePathTable + (NumberOfTableEntries - 1 - Index) * 24;

    //
    // Compare first 16 bytes against the TCG event log GUID.
    //
    if (InternalCompareMem (
          (VOID *)EntryBase,
          &gEfiTcgEventLogFormatGuid,
          sizeof (EFI_GUID)
          ) != 0) {
      continue;
    }

    Entry = (UINT16 *)(EntryBase + 16);

    if (Entry != NULL) {
      Result = NULL;

      //
      // Walk the vendor data entries following the GUID.
      //
      Current = Entry;
      while (*Current != 0xFFFF) {
        Type = *Current;
        if (Type == 4) {
          //
          // Vendor-specific entry found.
          //
          break;
        }
        //
        // Skip to next entry (advance by the entry's total length).
        //
        Current = (UINT16 *)((UINT8 *)Current + Current[1]);
        Type   = *Current;
      }

      if (*Current == 0xFFFF) {
        Current = Entry;
      }

      if (*Current == 4) {
        //
        // Type 4 entry: check if the next 16 bytes match the expected data.
        //
        if (InternalCompareMem (
              (VOID *)(Current + 4),
              (VOID *)0x1D20,   // Alternative GUID for comparison
              sizeof (EFI_GUID)
              ) == 0) {
          Result = Current + 12;
        }
      }

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

  return NULL;
}

/**
  Allocate pages, zero them, and return the physical address.

  Wrapper used by the TCG Legacy init to allocate TPM32 code pages
  and MPTPM pages.

  @param[in] Pages  Number of 4 KB pages to allocate.

  @return Physical address on success; 0xFFFFFFFF on failure.
**/
UINT64
AllocateAndZeroPagesWrapper (
  IN UINTN  Pages
  )
{
  return AllocateAndZeroPages (Pages);
}

/**
  Scan the legacy ROM area at physical address 0xE0000 (0x8000 dwords)
  for a BFI ('$BIN') signature marker.

  When found, patch the LEGX16 segment number into the BFI structure
  and recalculate the checksum.

  @param[in] LegBiosPlatform   Legacy BIOS Platform Protocol pointer.
  @param[in] LegBios           Legacy BIOS Protocol pointer.
  @param[in] LegX16Segment     LEGX16 segment value to patch into BFI.
**/
VOID
ScanAndPatchBfi (
  IN UINT64  LegBiosPlatform,
  IN UINT64  LegBios,
  IN UINT16  LegX16Segment
  )
{
  UINT32   *RomScan;
  UINT32   Index;
  UINT8    *BfiEntry;
  UINT8    Checksum;
  UINT8    *BfiData;
  UINTN    Count;

  BfiEntry    = NULL;
  RomScan     = (UINT32 *)0xE0000;
  Count       = 0x8000;  // 128 KB / 4

  for (Index = 0; Index < Count; Index++) {
    if (RomScan[Index] == 0x244E4942) {     // '$BIN' signature
      BfiEntry  = (UINT8 *)&RomScan[Index];
      Checksum  = 0;
      BfiData   = (UINT8 *)&RomScan[Index];
      Count     = RomScan[Index + 1];       // Size field in next dword

      //
      // Calculate checksum over the BFI structure bytes.
      //
      while (Count > 0) {
        Checksum += *BfiData++;
        Count--;
      }

      if (Checksum == 0) {
        break;  // Valid BFI with matching checksum
      }
    }
  }

  if (Index >= 0x8000 || BfiEntry == NULL) {
    DEBUG ((EFI_D_ERROR, "\t!!!Not Found BFI\n"));
    return;
  }

  DEBUG ((EFI_D_INFO, "\tFound BFI at 0x%x\n", (UINT32)(UINTN)BfiEntry));
  //
  // Patch the LEGX16 segment into the BFI structure (offset 35 words = byte 70).
  // Recalculate the checksum after patching.
  //
  *((UINT16 *)BfiEntry + 35) = (UINT16)(LegX16Segment >> 4);
  *((UINT16 *)BfiEntry + 36) = 0;

  //
  // Recalculate checksum.
  //
  BfiData = BfiEntry + 4;   // Skip the signature dword itself
  *BfiData = 0;             // Clear first byte of remaining structure

  if (BfiEntry[5] != 0) {
    Checksum = 0;
    Count    = BfiEntry[5];  // Length of the checksummed region
    while (Count > 0) {
      Checksum += *BfiEntry++;
      Count--;
    }
    //
    // Store the two's complement checksum.
    //
    *BfiData = ~(Checksum - 1);
  }
}

/**
  Link the TPM32 driver into the legacy BIOS interface.

  Sets the TPM32 header's PModeEntry callback and initializes fields:
  - byte_1DE0 == 0: Uses Legacy BIOS Protocol callback.
  - byte_1DE0 == 1: Uses TCG2 Protocol callback.

  @param[in] Tpm32Header  Pointer to the TPM32 header structure.
  @param[in] LegBios      Legacy BIOS Protocol interface.
**/
VOID
LinkTPM32Driver (
  IN TPM32_HEADER                  *Tpm32Header,
  IN EFI_LEGACY_BIOS_PROTOCOL      *LegBios
  )
{
  EFI_STATUS  Status;
  UINT64      StatusCode;
  UINT64      TpmAddress;

  Tpm32Header->PmodeEntry  = 0;   // Offset +0x42 (2 bytes), cleared initially
  Tpm32Header->EntryPoint  = 0;   // Offset +0x04

  //
  // Cache the Legacy BIOS interface pointer globally.
  //
  qword_1DD0 = (UINT64)LegBios;

  if (byte_1DE0) {
    //
    // TCG2 path: register a null callback and use TCG2 protocol for init.
    //
    if (qword_1DE8 == 0) {
      return;
    }

    Tpm32Header->PmodeEntry = (UINT32)(UINTN)NullCallback;  // nullsub_1
    Status = ((EFI_TCG2_PROTOCOL *)qword_1DE8)->RegisterCallback (
                                                  (EFI_TCG2_PROTOCOL *)qword_1DE8,
                                                  1,
                                                  &TpmAddress,
                                                  &StatusCode
                                                  );

    DEBUG ((EFI_D_INFO, "\n\n TcgLegacy.c Status = %r \n", Status));
    Tpm32Header->EntryPoint      = 0;
    Tpm32Header->TpmSegment      = (UINT32)Status;
    Tpm32Header->TpmLogSize      = 0x8000;                     // 32 KB log buffer
  } else {
    //
    // Legacy BIOS Protocol path: call the interface's callback registration.
    //
    Tpm32Header->PmodeEntry = (UINT32)(UINTN)GenericCallback;  // sub_410
    LegBios->Callback (
              LegBios,
              (VOID *)&TpmAddress,
              0,
              &StatusCode,
              &TpmAddress
              );

    DEBUG ((EFI_D_INFO, "\n\n linkTPMDriver: TCGLOG( %x )\n", TpmAddress - 40));
    Tpm32Header->TpmSegment = (UINT32)TpmAddress;
    Tpm32Header->TpmLogSize = *(UINT32 *)(TpmAddress - 40);
  }

  Tpm32Header->PmodeEntry = 0;   // Ensure PM entry is cleared after init
}

/**
  Unlink the TPM32 driver from the legacy BIOS region.

  Called from the LegacyBoot event notification.  Restores the TPM32 header
  fields to their default states and optionally tears down the TCG2 binding.

  @param[in] Event  Legacy Boot event (unused).
  @param[in] Tpm32  Pointer to the TPM32 header to unlink.
**/
VOID
EFIAPI
UnlinkTPM32FromEFI (
  IN EFI_EVENT  Event,
  IN VOID       *Tpm32
  )
{
  EFI_STATUS          Status;
  UINT64              StatusCode;
  UINT64              Buffer;
  EFI_LEGACY_BIOS_PROTOCOL  *LegBios;

  StatusCode = 0;
  Buffer     = 0;

  if (qword_1DD8 == 0) {
    return;
  }

  ASSERT (qword_1DD8 == (UINT64)Tpm32);

  DEBUG ((EFI_D_INFO, "UnlinkTPM32fromEFI: TPM32( %x )\n", Tpm32));

  qword_1DD8 = 0;

  if (byte_1DE0) {
    //
    // TCG2 path: unregister via TCG2 protocol.
    //
    if (qword_1DE8 != 0) {
      ((EFI_TCG2_PROTOCOL *)qword_1DE8)->RegisterCallback (
                                            (EFI_TCG2_PROTOCOL *)qword_1DE8,
                                            1,
                                            &Buffer,
                                            &StatusCode
                                            );
      ((TPM32_HEADER *)Tpm32)->EntryPoint    = 0;
      ((TPM32_HEADER *)Tpm32)->PmodeEntry    = 0;
      ((TPM32_HEADER *)Tpm32)->TpmLogAddress = (UINT32)StatusCode;
      ((TPM32_HEADER *)Tpm32)->Reserved1     = 0;
      ((TPM32_HEADER *)Tpm32)->TpmSegment    = 0;       // +0x20
      ((TPM32_HEADER *)Tpm32)->TpmLogSize    = 11;      // +0x39
      ((UINT8 *)Tpm32)[0x41]                 = 1;       // +0x41 flag
    }
  } else {
    //
    // Legacy BIOS path: unregister via Legacy BIOS Protocol callback.
    //
    ((TPM32_HEADER *)Tpm32)->PmodeEntry = 0;
    LegBios = (EFI_LEGACY_BIOS_PROTOCOL *)qword_1DD0;
    Status  = LegBios->Callback (
                        LegBios,
                        (VOID *)&Buffer,
                        0,
                        &StatusCode,
                        &Buffer
                        );
    if (!EFI_ERROR (Status)) {
      ((TPM32_HEADER *)Tpm32)->TpmLogAddress = (UINT32)(StatusCode - Buffer);
      ((UINT8 *)Tpm32)[0x1C]                 = ((UINT8 *)&Buffer)[11];
      ((TPM32_HEADER *)Tpm32)->TpmSegment    = *(UINT32 *)(Buffer - 36);
      ((TPM32_HEADER *)Tpm32)->TpmLogSize    = *(UINT32 *)(Buffer - 32) + 1;
      ((UINT8 *)Tpm32)[0x41]                 = 0;
    } else {
      ((UINT32 *)Tpm32)[12] = 0;  // Zero TpmLogAddress and TpmLogSize
    }
  }
}

/**
  Dummy/placeholder callback for the TCG2 protocol path.

  Does nothing -- used only when byte_1DE0 == 1 to satisfy the interface.

  @param[in] a1  Unused.
**/
VOID
NullCallback (
  VOID  *a1
  )
{
  //
  // Intentionally empty.
  //
}

/**
  Generic callback for the Legacy BIOS Protocol path.

  Invokes the Legacy BIOS interface's callback function +16 entry point
  with the given parameters.

  @param[in] a1  Parameter forwarded to the callback.
**/
VOID
GenericCallback (
  VOID  *a1
  )
{
  UINT8   Buffer;
  ((EFI_LEGACY_BIOS_PROTOCOL *)qword_1DD0)->Callback (
    (EFI_LEGACY_BIOS_PROTOCOL *)qword_1DD0,
    a1,
    &Buffer,
    1
    );
}

//
// ---------------------------------------------------------------------------
// Main initialization and entry point
// ---------------------------------------------------------------------------

/**
  TCG Legacy Driver entry point.

  Caches the EFI system table pointers, gets the HOB list, and calls
  InitTCGLegacyInterface().

  @param[in] ImageHandle  The loaded image's handle.
  @param[in] SystemTable  The UEFI system table.

  @return Status from InitTCGLegacyInterface().
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Save global handles required by the boot and runtime services libraries.
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

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

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

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

  //
  // Initialize the HOB list for platform configuration.
  //
  GetHobList ();

  //
  // Perform the TCG Legacy interface initialization.
  //
  return InitTCGLegacyInterface ();
}

/**
  Main TCG Legacy interface initialization routine.

  This is the core function that:
  1. Checks for existing TPM hardware at memory-mapped I/O 0xFED40F00.
  2. Ensures no TPM32 is already installed.
  3. Locates the Legacy BIOS protocols (or falls back to TCG2 Protocol).
  4. Loads the LEGX16 and TPM32 firmware volumes from flash.
  5. Allocates and populates the TPM shadow region.
  6. Installs the TPM32 driver in the legacy BIOS space.
  7. Copies LEGX16 to the F000-E000 legacy region.
  8. Scans and patches the BFI marker.
  9. Registers a LegacyBoot notification event.

  @return EFI_SUCCESS on success; error codes otherwise.
**/
EFI_STATUS
EFIAPI
InitTCGLegacyInterface (
  VOID
  )
{
  UINT64                            Index;
  BOOLEAN                           MptpmLoaded;
  UINT64                            LegX16Addr;
  EFI_LEGACY_BIOS_PROTOCOL          *LegBios;
  EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegBiosPlatform;
  UINT64                            TpmCodePageSize;
  UINT64                            LegX16FileSize;
  UINT64                            Tpm32FileSize;
  UINT64                            MptpmFileSize;
  UINT64                            TpmCodePhysAddr;
  UINT64                            LegX16Buffer;
  UINT64                            Tpm32Buffer;
  UINT64                            MptpmBuffer;
  UINT64                            LegX16FinalAddr;
  UINT64                            LegacyRegionBase;
  UINT64                            LegacyRegionLen;
  TPM32_HEADER                      *Tpm32Header;
  UINT64                            TpmEntry;
  UINT64                            TpmSegEntry;
  UINTN                             BfiScanCount;
  UINT32                            *BfiScan;
  UINT8                             *BfiEntry;
  UINT8                             BfiChecksum;

  LegX16FinalAddr   = 0;
  LegX16FileSize    = 0;
  Tpm32FileSize     = 0;
  MptpmFileSize     = 0;
  MptpmLoaded       = FALSE;
  LegBios           = NULL;
  LegBiosPlatform   = NULL;
  Tpm32Header       = NULL;

  //
  // Step 1: Check if there is already TPM hardware at the legacy TPM
  // memory-mapped location (0xFED40F00 = TPM_ACCESS register).
  //
  for (Index = 0; Index < TPM_VENDOR_ID_COUNT; Index++) {
    if (*(UINT16 *)((UINT8 *)gTpmVendorIdTable + Index * 4) == *(volatile UINT16 *)0xFED40F00 &&
        *(UINT16 *)((UINT8 *)gTpmVendorIdTable + Index * 4 + 2) == *(volatile UINT16 *)0xFED40F02) {
      //
      // TPM hardware is already present; nothing for us to do.
      //
      return EFI_SUCCESS;
    }
  }

  //
  // Step 2: Ensure TPM32 is not already installed.
  //
  if (qword_1DD8 != 0) {
    DEBUG ((EFI_D_ERROR, "installedTpm32 == 0\n"));
    ASSERT (qword_1DD8 == 0);
    return EFI_UNSUPPORTED;
  }

  //
  // Step 3: Locate the Legacy BIOS Protocol (0x1DB0) or TCG2 protocol.
  //
  if (gBS->LocateProtocol (
             (EFI_GUID *)EFI_TCG2_PROTOCOL_GUID_PTR,
             NULL,
             (VOID **)&LegBios
             ) < 0) {
    //
    // TCG2 not found; try the AMI TCG protocol.
    //
    LegBios = NULL;
    if (gBS->LocateProtocol (
               (EFI_GUID *)AMI_TPM_PLATFORM_GUID_PTR,
               NULL,
               (VOID **)&qword_1DE8
               ) < 0) {
      return EFI_UNSUPPORTED;
    }
    byte_1DE0 = 1;   // Use TCG2 / AMI TCG protocol path
  }

  //
  // Step 4: Locate the Legacy BIOS Platform Protocol (0x1D80).
  //
  if (gBS->LocateProtocol (
             (EFI_GUID *)EFI_LEGACY_BIOS_PLATFORM_GUID_PTR,
             NULL,
             (VOID **)&LegBiosPlatform
             ) < 0) {
    DEBUG ((EFI_D_ERROR, "Failed Locate Legacybiosprotocol %r\n", Status));
    return EFI_UNSUPPORTED;
  }

  //
  // Step 5: Locate the Legacy BIOS Protocol (0x1D60).
  //
  if (gBS->LocateProtocol (
             (EFI_GUID *)EFI_LEGACY_BIOS_PROTOCOL_GUID_PTR,
             NULL,
             (VOID **)&LegBios
             ) < 0) {
    DEBUG ((EFI_D_ERROR, "Failed Locate Legacybiosprotocol2 %r\n", Status));
    return EFI_UNSUPPORTED;
  }

  //
  // Step 6: Load the LEGX16 binary from a firmware volume file GUID.
  //
  LegX16Addr = 0;
  Status     = LocateFirmwareVolumeFile (
                 (EFI_GUID *)TCG_EVENT_LOG_FORMAT_GUID_PTR,  // Placeholder GUID; actual file GUID
                 (VOID **)&LegX16Addr,
                 (UINTN *)&LegX16FileSize
                 );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to load LEGX16: error=%r\n", Status));
    return Status;
  }

  //
  // Step 7: Allocate legacy region via Legacy BIOS Platform Protocol (+72).
  // This allocates a region in the F000-E000 area for LEGX16.
  //
  Status = LegBiosPlatform->AllocateLegacyRegion (
                              LegBiosPlatform,
                              LegX16FileSize + 16,
                              0,
                              16,
                              &LegacyRegionBase
                              );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to allocate legacy region LEGX16 file error=%r\n", Status));
    return Status;
  }

  //
  // Step 8: Load the TPM32 binary from firmware volume.
  //
  Status = LocateFirmwareVolumeFile (
             (EFI_GUID *)EFI_LEGACY_BIOS_PROTOCOL_GUID_PTR,  // Placeholder
             (VOID **)&Tpm32Buffer,
             (UINTN *)&Tpm32FileSize
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to load TPM32: error=%r\n", Status));
    return Status;
  }

  //
  // Step 9: Allocate page-aligned memory for the TPM32 code.
  //
  TpmCodePhysAddr = AllocateAndZeroPagesWrapper (
                      (Tpm32FileSize >> 12) + ((Tpm32FileSize & 0xFFF) != 0)
                      );
  if (TpmCodePhysAddr == 0xFFFFFFFFFFFFFFFFULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Step 9b: Copy the TPM32 binary to the allocated page.
  //
  gBS->CopyMem (
         (VOID *)(UINTN)TpmCodePhysAddr,
         (VOID *)Tpm32Buffer,
         Tpm32FileSize
         );

  //
  // Step 10: Allocate a TPM log/data page for the TPM32 driver's use.
  //
  TpmEntry = AllocateAndZeroPagesWrapper (1);   // 1 page = 4 KB
  if (TpmEntry == 0xFFFFFFFFFFFFFFFFULL) {
    //
    // Free the previously allocated TPM code pages.
    //
    gBS->FreePages (TpmCodePhysAddr, (Tpm32FileSize >> 12) + ((Tpm32FileSize & 0xFFF) != 0));
    return EFI_OUT_OF_RESOURCES;
  }

  Tpm32Header = (TPM32_HEADER *)TpmCodePhysAddr;
  Tpm32Header->CodeSegmentBase = (UINT32)TpmEntry;
  Tpm32Header->CodeSegmentSize = 1024;

  //
  // Step 11: Check for vendor ACPI table match (MPTPM or ACPI entry).
  //
  for (Index = 0; Index < TPM_VENDOR_ID_COUNT; Index++) {
    if (*(UINT16 *)((UINT8 *)gTpmVendorIdTable + Index * 4) == *(volatile UINT16 *)0xFED40F00 &&
        *(UINT16 *)((UINT8 *)gTpmVendorIdTable + Index * 4 + 2) == *(volatile UINT16 *)0xFED40F02) {
      //
      // TPM hardware already present; find matching ACPI device path entry.
      //
      UINT16  *MatchingEntry;
      MatchingEntry = FindTpmVendorEntry (
                        gST->NumberOfTableEntries,
                        (UINT64)gST->ConfigurationTable,
                        NULL
                        );
      Tpm32FileSize = (UINT64)(*MatchingEntry - MatchingEntry[2]);   // Calculate size
      LegX16Addr    = Tpm32FileSize;
      goto InstallDriver;  // Skip MPTPM loading
    }
  }

  //
  // Step 12: Load MPTPM (multi-processor TPM) if needed.
  //
  Status = LocateFirmwareVolumeFile (
             (EFI_GUID *)EFI_HOB_LIST_GUID_PTR,  // Placeholder
             (VOID **)&MptpmBuffer,
             (UINTN *)&MptpmFileSize
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to load MPTPM: error=%r\n", Status));
    goto ErrorCleanup;
  }

  LegX16Addr = AllocateAndZeroPagesWrapper (
                 (MptpmFileSize >> 12) + ((MptpmFileSize & 0xFFF) != 0)
                 );
  if (LegX16Addr == 0xFFFFFFFFFFFFFFFFULL) {
    DEBUG ((EFI_D_ERROR, "Failed to allocate MPTPM space =%r\n", Status));
    goto ErrorCleanup;
  }
  MptpmLoaded = TRUE;

  gBS->CopyMem (
         (VOID *)(UINTN)LegX16Addr,
         (VOID *)MptpmBuffer,
         MptpmFileSize
         );

InstallDriver:
  //
  // Step 13: Set the TPM32 header fields.
  //
  TpmSegEntry            = LegX16Addr;
  Tpm32Header->TpmLogAddress = (UINT32)TpmSegEntry;
  //
  // Write the TPM32 entry point into the LEGX16 header at the correct offset.
  //
  *(UINT32 *)(*(UINT16 *)(LegX16FileSize + 8) + LegX16Buffer) = (UINT32)TpmCodePhysAddr + Tpm32Header->EntryPoint;
  *(UINT32 *)(LegX16FileSize + 4)                              = (UINT32)TpmCodePhysAddr;

  //
  // Step 14: Link the TPM32 driver into the legacy BIOS interface.
  //
  LinkTPM32Driver (Tpm32Header, LegBios);

  //
  // Step 15: Set the LEGX16 control flag (-7424 = 0xE300 shadow enable).
  //
  *(UINT16 *)(LegX16FileSize + 22) = 0xE300;

  //
  // Step 16: Copy the LEGX16 image to the final F000-E000 legacy region.
  //
  Status = LegBiosPlatform->CopyLegacyRegion (
                              LegBiosPlatform,
                              LegX16FileSize,
                              LegacyRegionBase,
                              LegX16Buffer
                              );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to Copy final LegX16 to dest (F000-E000 area) error=%r\n", Status));
    goto ErrorCleanup;
  }

  //
  // Step 17: Install the legacy interrupt handler.
  //
  {
    EFI_LEGACY_INTERRUPT_PROTOCOL *LegInt;
    UINT64                        ShadowBase;

    //
    // The second legacy BIOS protocol instance (0x1D80) provides interrupt
    // registration.
    //
    LegBiosPlatform = (EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *)LegBios; // Reuse for platform operations

    Status = LegBiosPlatform->AllocateLegacyRegion (
                                LegBiosPlatform,
                                917504,
                                0x20000,
                                &ShadowBase
                                );
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
      ASSERT (!EFI_ERROR (Status));
    }

    //
    // Initialize the shadow region data structure (48 bytes).
    //
    ZeroMem (v46, 48);

    LegInt = (EFI_LEGACY_INTERRUPT_PROTOCOL *)ShadowBase;
    // Use Legacy BIOS Platform Protocol function +8 to install the interrupt.
    LegBiosPlatform->ShadowAndInstallInterruptHandler (
                       LegBiosPlatform,
                       (UINT16)(ShadowBase >> 4),
                       (UINT16)LegInt[9],
                       v46,
                       0,
                       0
                       );

    //
    // Save LEGX16 configuration words for later use.
    //
    gLegX16Segment  = (UINT16)(ShadowBase >> 4);
    gLegX16Shadow   = LegInt[5];
    gLegX16Init     = LegInt[6];
    gLegX16Checksum = LegInt[7];
    gLegX16Size     = LegInt[8];

    DEBUG ((EFI_D_INFO, "\tLEGX16: %x:%x, %x, %x\n",
            gLegX16Segment, gLegX16Shadow, gLegX16Init, gLegX16Checksum));
    DEBUG ((EFI_D_INFO, "\tTPM32: header:%x entry:%x log:%x logsize:%x\n",
            (UINT32)(UINTN)Tpm32Header,
            (UINT32)(UINTN)Tpm32Header + Tpm32Header->EntryPoint,
            Tpm32Header->TpmLogAddress,
            Tpm32Header->TpmLogSize));
    DEBUG ((EFI_D_INFO, "\tMPTPM: %x\n", Tpm32Header->TpmSegment));
  }

  //
  // Step 18: Allocate legacy region for the configuration data.
  //
  {
    UINT64  ConfigBase;

    Status = LegBiosPlatform->AllocateLegacyRegion (
                                LegBiosPlatform,
                                26,
                                0,
                                16,
                                &ConfigBase
                                );
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
      ASSERT (!EFI_ERROR (Status));
      goto RegionCleanup;
    }

    //
    // Write the configuration words to the legacy region.
    //
    Status = LegBiosPlatform->CopyLegacyRegion (
                                LegBiosPlatform,
                                10,
                                ConfigBase,
                                &gLegX16Segment
                                );
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
      ASSERT (!EFI_ERROR (Status));
      goto RegionCleanup;
    }
  }

  //
  // Step 19: Release and re-acquire the legacy region for shadow operations.
  //
  Status = LegBiosPlatform->AllocateLegacyRegion (
                              LegBiosPlatform,
                              917504,
                              0x20000,
                              &ShadowBase
                              );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT (!EFI_ERROR (Status));
    goto RegionCleanup;
  }

  //
  // Step 20: Scan and patch the BFI marker in the legacy ROM region.
  //
  ScanAndPatchBfi ((UINT64)LegBiosPlatform, (UINT64)LegBios, gLegX16Segment);

  //
  // Step 21: Release the shadow region.
  //
  LegBiosPlatform->AllocateLegacyRegion (
                     LegBiosPlatform,
                     917504,
                     0x20000,
                     &ShadowBase
                     );

  //
  // Step 22: Save the installed TPM32 header pointer.
  //
  qword_1DD8 = (UINT64)Tpm32Header;

  //
  // Step 23: Register a Legacy Boot notification event.
  //
  {
    EFI_EVENT  LegacyBootEvent;

    Status = gBS->CreateEventEx (
                    EVT_NOTIFY_SIGNAL,
                    TPL_NOTIFY,
                    UnlinkTPM32FromEFI,
                    Tpm32Header,
                    (EFI_GUID *)EFI_EVENT_LEGACY_BOOT_GUID_PTR,
                    &LegacyBootEvent
                    );
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR,
              "!(((INTN)(RETURN_STATUS)(Status = CreateLegacyBootEvent("
              " 8, UnlinkTPM32fromEFI, tpm32hdr, &event ))) < 0)\n"));
      ASSERT (!EFI_ERROR (Status));
    }
  }

  return EFI_SUCCESS;

RegionCleanup:
  //
  // Release the shadow region on error.
  //
  LegBiosPlatform->AllocateLegacyRegion (
                     LegBiosPlatform,
                     917504,
                     0x20000,
                     &ShadowBase
                     );

ErrorCleanup:
  //
  // Free allocated pages on error.
  //
  if (TpmCodePhysAddr != 0xFFFFFFFFFFFFFFFFULL) {
    gBS->FreePages (
           TpmCodePhysAddr,
           (Tpm32FileSize >> 12) + ((Tpm32FileSize & 0xFFF) != 0)
           );
  }
  if (TpmEntry != 0xFFFFFFFFFFFFFFFFULL) {
    gBS->FreePages (TpmEntry, 1);
  }
  if (MptpmLoaded) {
    gBS->FreePages (
           LegX16Addr,
           (MptpmFileSize >> 12) + ((MptpmFileSize & 0xFFF) != 0)
           );
  }

  DEBUG ((EFI_D_ERROR, "InitTCGLegacyInterface exit fail Status = %r\n", Status));
  return Status;
}