Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / TCG2 / Samples / AmiTcgNvflagSample / AmiTcgNvflagSample.c
@Ajax Dong Ajax Dong 2 days ago 22 KB Full restructure
/** @file
  AmiTcgNvflagSample - TCG NV Flag Management Sample Driver

  This module manages TPM NV flags across BIOS phases using UEFI runtime
  variables. It reads the physical TPM NV registers at 0xFED40F00 to
  detect the TPM manufacturer, then synchronizes NV flag state through
  TcgInterfaceVar, INTERNALPERBIOSFLAGS, and TPMPERBIOSFLAGS variables.

  The driver performs the following:
  1. Initializes UEFI boot/runtime services table pointers.
  2. Locates the TCG Protocol and TCG Configuration Protocol.
  3. Probes TPM NV registers for manufacturer-specific flags.
  4. Reads existing TcgInterfaceVar to determine interface type.
  5. If valid, reads INTERNALPERBIOSFLAGS and propagates to TPMPERBIOSFLAGS.
  6. Registers as a UEFI driver binding to provide flag management services.

  Copyright (c) AMI Corporation. All rights reserved.
  SPDX-License-Identifier: AMI-Module-Patent-License
**/

#include "AmiTcgNvflagSample.h"

//
// Module Global Data
//
EFI_HANDLE               gImageHandle      = NULL;
EFI_SYSTEM_TABLE         *gST              = NULL;
EFI_BOOT_SERVICES        *gBS              = NULL;
EFI_RUNTIME_SERVICES     *gRT              = NULL;

UINT64                   mPciExpressBaseAddress = 0;  // qword_15B0
VOID                     *mHobList         = NULL;     // qword_15A8
VOID                     *mPcd             = NULL;     // qword_15B8
UINT64                   gDebugLevel       = 0;        // qword_15A0

//
// TCG Protocol GUID
// {F67D28B8-1E78-4C19-B87B-1A99C2406A54}
//
EFI_GUID gEfiTcgProtocolGuid           = { 0xF67D28B8, 0x1E78, 0x4C19, { 0xB8, 0x7B, 0x1A, 0x99, 0xC2, 0x40, 0x6A, 0x54 } };

//
// TCG Configuration Protocol GUID
// {D7F12B7E-64B7-4B0B-99BF-CB7BFA8F1D8C}
//
EFI_GUID gEfiTcgConfigProtocolGuid     = { 0xD7F12B7E, 0x64B7, 0x4B0B, { 0x99, 0xBF, 0xCB, 0x7B, 0xFA, 0x8F, 0x1D, 0x8C } };

//
// Variable GUIDs
//
EFI_GUID gTcgInterfaceVarGuid          = { 0x94B0F9D0, 0x8B8A, 0x4F63, { 0x87, 0x0E, 0x35, 0x5D, 0xFE, 0x42, 0x26, 0x55 } };  // unk_1510
EFI_GUID gInternalTcgNvDataGuid        = { 0x8B10C0A0, 0x9C52, 0x4E90, { 0xA8, 0x6A, 0xDE, 0x60, 0x8C, 0x79, 0x5C, 0x41 } };  // unk_14F0
EFI_GUID gTcgNvDataGuid                = { 0xAB954E08, 0x6F10, 0x4C29, { 0xB5, 0xA5, 0xE8, 0x3E, 0x60, 0x5D, 0x71, 0x23 } };  // unk_14E0

//
// PCD Token Space GUID
// {9B3ADA11-5CBC-4C41-A81F-0EAD647BBE6C}
//
EFI_GUID gPcdTokenSpaceGuid            = { 0x9B3ADA11, 0x5CBC, 0x4C41, { 0xA8, 0x1F, 0x0E, 0xAD, 0x64, 0x7B, 0xBE, 0x6C } };

//
// HOB GUID for locating HOB list in configuration table
//
EFI_GUID gHobGuid                      = { 0x7739F24C, 0x93D7, 0x11D4, { 0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } };  // unk_1520/unk_1528

//
// EFI Driver Binding Protocol instance
// unk_1500 - The driver binding protocol instance
//
EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {
  AmiTcgNvflagSampleDriver,
  NULL,  // Supported
  NULL,  // Stop
  0x10,  // Version
  NULL,  // ImageHandle
  NULL   // DriverBindingHandle
};

//
// Forward declarations for protocol function table
// off_1550 - Function table for protocol interface
// off_1560 - Alternative function table for protocol interface
//
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY mProtocolFuncTable[2];

/**
  Set a buffer to a specified value (memset wrapper).

  @param[in,out] Buffer  Pointer to the buffer to fill.
  @param[in]     Count   Number of bytes to set.
  @param[in]     Value   Value to set each byte to.

  @return Pointer to the buffer.
**/
VOID *
EFIAPI
SetMem (
  OUT VOID       *Buffer,
  IN  UINTN      Count,
  IN  UINT8      Value
  )
{
  return memset (Buffer, Value, Count);
}

/**
  Read a UINT64 from a buffer (unaligned-safe).

  @param[in] Buffer  Pointer to the data (must not be NULL).

  @return The UINT64 value at Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}

/**
  Read a 32-bit I/O port.

  @param[in] Port  I/O port number (must be 4-byte aligned).

  @return Value read from the I/O port.
**/
UINT32
EFIAPI
IoRead32 (
  IN UINTN  Port
  )
{
  ASSERT ((Port & 3) == 0);
  return __indword (Port);
}

/**
  Pause CPU (hint to hypervisor for spin-wait).
**/
VOID
EFIAPI
CpuPause (
  VOID
  )
{
  _mm_pause ();
}

/**
  Read the current RDTSC timestamp counter.

  @return Current TSC value.
**/
UINT64
EFIAPI
ReadTimeStampCounter (
  VOID
  )
{
  return __rdtsc ();
}

/**
  Enable interrupts.
**/
VOID
EFIAPI
EnableInterrupts (
  VOID
  )
{
  _enable ();
}

/**
  Disable interrupts.
**/
VOID
EFIAPI
DisableInterrupts (
  VOID
  )
{
  _disable ();
}

/**
  Read the current EFLAGS register value.

  @return Current EFLAGS.
**/
UINTN
EFIAPI
GetCallerEflags (
  VOID
  )
{
  return __getcallerseflags ();
}

/**
  Write a 16-bit value to a memory-mapped I/O address.

  @param[in] Address  The MMIO address (must be 2-byte aligned).
  @param[in] Value    The value to write.
**/
VOID
EFIAPI
MmioWrite16 (
  IN UINT16  *Address,
  IN UINT16  Value
  )
{
  ASSERT (((UINTN)Address & 1) == 0);
  *Address = Value;
}

/**
  Get the PCD protocol pointer.

  Locates the PCD protocol from the boot services database. On failure,
  asserts and returns NULL.

  @return Pointer to the PCD protocol, or NULL if not found.
**/
VOID *
EFIAPI
GetPcdProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

  if (mPcd == NULL) {
    Status = gBS->LocateProtocol (&gPcdTokenSpaceGuid, NULL, &mPcd);
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
      ASSERT_EFI_ERROR (FALSE);
    }
    if (mPcd == NULL) {
      ASSERT (mPcd != NULL);
    }
  }

  return mPcd;
}

/**
  Display an error message via the debug protocol.

  @param[in]  ErrorLevel  The debug error level.
  @param[in]  Format      Format string.
  @param[in]  ...         Variable arguments.
**/
VOID
EFIAPI
DebugPrintErrorLevel (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST         Args;
  DEBUG_PRINT     *DebugPrint;

  DebugPrint = (DEBUG_PRINT *)GetPcdProtocol ();
  if (DebugPrint == NULL) {
    return;
  }

  //
  // Check error level filtering
  //
  {
    UINT8   DebugRegister;
    UINT8   CmosIndex;
    UINTN   ErrorLevelFilter;

    //
    // Read CMOS index 0x4B to determine debug level
    //
    IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
    DebugRegister = IoRead8 (0x71);

    if (DebugRegister > 3) {
      if (DebugRegister == 0) {
        DebugRegister = (MmioRead8 (0xFDAF0490) & 2) | 1;
      }
    }

    switch (DebugRegister) {
      case 1:
        ErrorLevelFilter = EFI_D_ERROR;
        break;
      default:
        ErrorLevelFilter = EFI_D_ERROR | EFI_D_WARN;
        break;
    }

    if ((ErrorLevel & ErrorLevelFilter) == 0) {
      return;
    }
  }

  VA_START (Args, Format);
  DebugPrint->Print (ErrorLevel, Format, Args);
  VA_END (Args);
}

/**
  Assertion handling.

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

  DebugPrint = (DEBUG_PRINT *)GetPcdProtocol ();
  if (DebugPrint != NULL) {
    DebugPrint->Assert (FileName, LineNumber, Description);
  }
}

/**
  Get the HOB list from the system table.

  Iterates through the configuration table to find the HOB list GUID.
  On failure, asserts and returns NULL.

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

  if (mHobList == NULL) {
    mHobList = NULL;

    if (gST->NumberOfTableEntries > 0) {
      for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
        if (MatchHobGuid (&gHobGuid, &gST->ConfigurationTable[Index])) {
          mHobList = gST->ConfigurationTable[Index].VendorTable;
          break;
        }
      }
    }

    if (mHobList == NULL) {
      DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
      ASSERT_EFI_ERROR (FALSE);
    }

    if (mHobList == NULL) {
      ASSERT (mHobList != NULL);
    }
  }

  return mHobList;
}

/**
  Check if a HOB entry matches the expected GUID.

  @param[in]  HobGuid   Expected GUID of the HOB.
  @param[in]  HobEntry  Pointer to the HOB entry header.

  @retval TRUE   The HOB entry GUID matches.
  @retval FALSE  The HOB entry GUID does not match.
**/
BOOLEAN
EFIAPI
MatchHobGuid (
  IN EFI_GUID  *HobGuid,
  IN VOID      *HobEntry
  )
{
  EFI_GUID  *EntryGuid1;
  EFI_GUID  *EntryGuid2;

  EntryGuid1 = (EFI_GUID *)ReadUnaligned64 ((UINT64 *)&gHobGuid);
  EntryGuid2 = (EFI_GUID *)ReadUnaligned64 (HobEntry);

  return CompareGuid (EntryGuid1, &gHobGuid) && CompareGuid (EntryGuid2, HobGuid);
}

/**
  Translate a PCI Express address to the MMIO base.

  @param[in] Address  PCI CF8 address.

  @return Pointer to the PCI Express MMIO location.
**/
VOID *
EFIAPI
PciExpressLibGetPciExpressAddress (
  IN UINTN  Address
  )
{
  ASSERT ((Address & ~0xFFFFFFF) == 0);
  return (VOID *)(Address + mPciExpressBaseAddress);
}

/**
  Initialize UEFI boot services and runtime services table pointers.

  Stores ImageHandle, SystemTable, BootServices, and RuntimeServices
  into module globals. Initializes the HOB list, PCI Express base address,
  and performs delay calibration via RDTSC.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.
**/
VOID
EFIAPI
UefiBootServicesTableLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  UINTN  Eflags;
  BOOLEAN InterruptsEnabled;
  UINTN  StartTsc;
  UINTN  CurrentTsc;
  UINTN  DelayCount;

  //
  // Save ImageHandle and SystemTable
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

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

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

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

  //
  // Initialize HOB list
  //
  GetHobList ();

  //
  // Initialize PCI Express base address from PCD
  //
  mPciExpressBaseAddress = ((UINT64 (*)(UINTN))GetPcdProtocol ()->GetPcdValue (PcdPciExpressBaseAddress))(5);

  //
  // Enable PCI Express MMIO access
  //
  if ((INT8)*((UINT8 *)PciExpressLibGetPciExpressAddress (PCI_EXPRESS_LIB_ADDRESS (0, 0, 0, 0))) >= 0) {
    MmioWrite16 (
      (UINT16 *)PciExpressLibGetPciExpressAddress (PCI_EXPRESS_LIB_ADDRESS (0, 0, 0, 0)),
      0x500
      );
    *((UINT8 *)PciExpressLibGetPciExpressAddress (PCI_EXPRESS_LIB_ADDRESS (0, 0, 0, 0))) |= 0x80;
  }

  //
  // Delay calibration: use RDTSC to calibrate a ~1ms delay
  //
  Eflags = GetCallerEflags ();
  DisableInterrupts ();
  InterruptsEnabled = (Eflags & 0x200) != 0;
  StartTsc = IoRead32 (TIMER_PORT) & 0xFFFFFF;
  ReadTimeStampCounter ();

  //
  // Busy-wait for timer tick (delay calibration)
  //
  while ((((CurrentTsc + 357 - (UINTN)IoRead32 (TIMER_PORT)) & 0x800000) == 0)) {
    CpuPause ();
  }

  ReadTimeStampCounter ();

  if (InterruptsEnabled) {
    EnableInterrupts ();
  } else {
    DisableInterrupts ();
  }
}

/**
  Write to a TPM NV flag variable via the TCG runtime services.

  If the variable does not exist (EFI_NOT_FOUND), it is created first
  and then written. If the runtime service is unavailable, the write is
  still attempted.

  @param[in] VariableName  The UEFI variable name (wide string).
  @param[in] VendorGuid    The vendor GUID.
  @param[in] Attributes    Variable attributes.
  @param[in] DataSize      Size of the data buffer.
  @param[in] Data          Pointer to the data.

  @retval EFI_SUCCESS           The variable was written successfully.
  @retval Others                Error from UEFI variable services.
**/
EFI_STATUS
EFIAPI
TpmNvFlagWriteVariable (
  IN CHAR16    *VariableName,
  IN EFI_GUID  *VendorGuid,
  IN UINT32    Attributes,
  IN UINTN     DataSize,
  IN VOID      *Data
  )
{
  EFI_STATUS  Status;

  //
  // Query the variable; create it if it doesn't exist
  //
  Status = gRT->QueryVariableInfo (VariableName, VendorGuid, 0, 0, 0);
  if (Status == EFI_UNSUPPORTED) {
    //
    // Variable doesn't exist; attempt to create it
    //
    Status = gRT->SetVariable (VariableName, VendorGuid, Attributes, DataSize, Data);
    if (!EFI_ERROR (Status)) {
      //
      // Now write to the variable with the same data
      //
      Status = gRT->SetVariable (VariableName, VendorGuid, Attributes, DataSize, Data);
    }
  } else if (Status == EFI_NOT_FOUND) {
    //
    // Variable doesn't exist; create and write
    //
    gRT->SetVariable (VariableName, VendorGuid, Attributes, DataSize, Data);
  }

  return Status;
}

/**
  Read flags from TPM NV storage into caller's buffer.

  Attempts to read TcgInterfaceVar and INTERNALPERBIOSFLAGS. If the
  interface variable indicates a valid TCG interface, the internal
  per-BIOS flags are copied to the output buffer. If the internal
  flags variable is not found, default values are set (flags[4] |= 0x10,
  flags[0:2] = {1, 0}).

  @param[out] Flags     Pointer to receive the 6-byte flags.
  @param[in]  Index     NV flag index offset.
  @param[in]  DefaultFlags  Pointer to default flag values.
  @param[in]  DataSize  Size of the default flags data.

  @retval EFI_SUCCESS           Flags read successfully.
  @retval EFI_INVALID_PARAMETER Flags is NULL.
  @retval EFI_NOT_FOUND         TCG protocol not available.
  @retval Others                Error from UEFI variable services.
**/
EFI_STATUS
EFIAPI
GetPersistentTpmFlags (
  OUT VOID   *Flags,
  IN  UINTN  Index,
  IN  VOID   *DefaultFlags,
  IN  UINT64 DataSize
  )
{
  EFI_STATUS                    Status;
  UINTN                         BufferSize;
  UINT8                         TcgInterface;
  UINT8                         InternalFlags[6];
  UINTN                         InternalFlagsSize;
  VOID                          *TcgProtocol;
  VOID                          *TcgConfigProtocol;

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

  //
  // Locate TCG protocol
  //
  Status = gBS->LocateProtocol (&gEfiTcgProtocolGuid, NULL, &TcgProtocol);
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  Status = gBS->LocateProtocol (&gEfiTcgConfigProtocolGuid, NULL, &TcgConfigProtocol);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Error: failed to locate TCG protocol: %r\n", Status));
    return EFI_NOT_FOUND;
  }

  //
  // Read TcgInterfaceVar to check interface state
  //
  BufferSize = sizeof (TcgInterface);
  Status = gRT->GetVariable (
                  TCG_INTERFACE_VARIABLE_NAME,
                  &gTcgInterfaceVarGuid,
                  NULL,
                  &BufferSize,
                  &TcgInterface
                  );
  if (EFI_ERROR (Status) || (TcgInterface & 1) == 0) {
    return Status;
  }

  //
  // Read INTERNALPERBIOSFLAGS
  //
  InternalFlagsSize = sizeof (InternalFlags);
  Status = gRT->GetVariable (
                  INTERNAL_PERBIOS_FLAGS_NAME,
                  &gInternalTcgNvDataGuid,
                  NULL,
                  &InternalFlagsSize,
                  InternalFlags
                  );
  if (EFI_ERROR (Status)) {
    //
    // Internal flags not found; set defaults
    //
    *((UINT16 *)Flags + 2) |= 0x10;  // Set bit 4 in flags
    *(UINT16 *)Flags = 1;
    *((UINT8 *)Flags + 2) = 0;
  } else {
    //
    // Copy internal flags to output buffer
    //
    CopyMem (Flags, InternalFlags, 6);
  }

  return EFI_SUCCESS;
}

/**
  Write flags to TPM permanent NV storage.

  Reads existing TcgInterfaceVar to check interface type. If the interface
  variable indicates valid TCG interface, writes the provided flags to
  INTERNALPERBIOSFLAGS and TPMPERBIOSFLAGS variables via the TCG protocol.

  @param[in] Flags  Pointer to 6-byte NV flags structure.

  @retval EFI_SUCCESS           Flags were written successfully.
  @retval EFI_INVALID_PARAMETER Flags is NULL.
  @retval EFI_NOT_FOUND         TCG protocol not available.
  @retval Others                Error from UEFI variable services.
**/
EFI_STATUS
EFIAPI
SetPersistentTpmFlags (
  IN VOID   *Flags
  )
{
  EFI_STATUS                    Status;
  UINTN                         BufferSize;
  UINT8                         TcgInterface;
  UINT8                         InternalFlags[6];
  EFI_TCG_PROTOCOL              *TcgProtocol;
  EFI_TCG_CONFIG_PROTOCOL       *TcgConfigProtocol;

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

  //
  // Locate TCG protocols
  //
  Status = gBS->LocateProtocol (&gEfiTcgProtocolGuid, NULL, (VOID **)&TcgProtocol);
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  Status = gBS->LocateProtocol (&gEfiTcgConfigProtocolGuid, NULL, (VOID **)&TcgConfigProtocol);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Error: failed to locate TCG protocol: %r\n", Status));
    return EFI_NOT_FOUND;
  }

  //
  // Read TcgInterfaceVar to check if TCG interface is active
  //
  BufferSize = sizeof (TcgInterface);
  Status = gRT->GetVariable (
                  TCG_INTERFACE_VARIABLE_NAME,
                  &gTcgInterfaceVarGuid,
                  NULL,
                  &BufferSize,
                  &TcgInterface
                  );

  if (!EFI_ERROR (Status) && (TcgInterface & 1) != 0) {
    //
    // TCG interface is active; write flags to INTERNALPERBIOSFLAGS
    //
    CopyMem (InternalFlags, Flags, sizeof (InternalFlags));

    Status = TpmNvFlagWriteVariable (
               INTERNAL_PERBIOS_FLAGS_NAME,
               &gInternalTcgNvDataGuid,
               NV_FLAGS_ATTRIBUTES,
               sizeof (InternalFlags),
               InternalFlags
               );
    if (EFI_ERROR (Status)) {
      DEBUG ((
        EFI_D_ERROR,
        "Error: Failure %d %a Status = %r\n",
        163,
        "Set_Persistent_Bios_TPM_Flags",
        Status
        ));
    }
  }

  //
  // Set/clear TCG interface variable and write all three variables
  //
  TcgInterface |= 1;
  SetMem (&TcgInterface, sizeof (TcgInterface), 1);

  Status = TpmNvFlagWriteVariable (
             TCG_INTERFACE_VARIABLE_NAME,
             &gTcgInterfaceVarGuid,
             NV_FLAGS_ATTRIBUTES,
             sizeof (TcgInterface),
             &TcgInterface
             );
  if (!EFI_ERROR (Status)) {
    //
    // Write INTERNALPERBIOSFLAGS again with actual flags
    //
    CopyMem (InternalFlags, Flags, sizeof (InternalFlags));
    Status = TpmNvFlagWriteVariable (
               INTERNAL_PERBIOS_FLAGS_NAME,
               &gInternalTcgNvDataGuid,
               NV_FLAGS_ATTRIBUTES,
               sizeof (InternalFlags),
               InternalFlags
               );

    if (!EFI_ERROR (Status)) {
      //
      // Finally, write TPMPERBIOSFLAGS
      //
      Status = TpmNvFlagWriteVariable (
                 TPM_PERBIOS_FLAGS_NAME,
                 &gTcgNvDataGuid,
                 TPM_PERBIOS_FLAGS_ATTRIBUTES,
                 sizeof (InternalFlags),
                 InternalFlags
                 );
    }
  }

  return Status;
}

/**
  Stub function that returns EFI_UNSUPPORTED.

  @retval EFI_UNSUPPORTED  Always returned.
**/
EFI_STATUS
EFIAPI
TpmNvFlagStub (
  VOID
  )
{
  return EFI_UNSUPPORTED;
}

/**
  Main driver entry logic after protocol/BS/RT init.

  Locates TCG Protocol and TCG Config Protocol. Checks TPM NV flag
  registers at physical address 0xFED40F00 for matching manufacturer
  flags. If TcgInterfaceVar exists and has valid flags, reads them;
  otherwise initializes default NV flags and writes them to UEFI
  variables.

  @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 completed successfully.
  @retval EFI_NOT_FOUND         TCG protocol not found.
  @retval EFI_INVALID_PARAMETER Invalid argument.
  @retval Others                Error from UEFI variable services.
**/
EFI_STATUS
EFIAPI
AmiTcgNvflagSampleDriver (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                    Status;
  UINTN                         Index;
  UINT16                        TpmManufacturerId;
  UINT16                        TpmManufacturerId2;
  UINT8                         DefaultFlags[6];
  UINT8                         CurrentFlags[6];
  EFI_TCG_PROTOCOL              *TcgProtocol;
  EFI_TCG_CONFIG_PROTOCOL       *TcgConfigProtocol;
  UINTN                         BufferSize;
  UINT8                         TcgInterface;
  UINT64                        ManufacturerCheckValue;

  //
  // Locate TCG protocol
  //
  Status = gBS->LocateProtocol (&gEfiTcgProtocolGuid, NULL, (VOID **)&TcgProtocol);
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  //
  // Locate TCG Config protocol
  //
  Status = gBS->LocateProtocol (&gEfiTcgConfigProtocolGuid, NULL, (VOID **)&TcgConfigProtocol);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Error: failed to locate TCG protocol: %r\n", Status));
    return EFI_NOT_FOUND;
  }

  //
  // Check TPM NV registers at physical address 0xFED40F00 for
  // manufacturer-specific flags. The known manufacturer flag table
  // contains 3 entries (12 bytes total, 4 bytes per entry).
  //
  for (Index = 0; Index < 0xC; Index += 4) {
    TpmManufacturerId  = MmioRead16 (0xFED40F00);
    TpmManufacturerId2 = MmioRead16 (0xFED40F02);
    //
    // Compare against known manufacturer flag table
    //
    if (*(UINT16 *)((UINT8 *)&ManufacturerCheckValue + Index)     == TpmManufacturerId &&
        *(UINT16 *)((UINT8 *)&ManufacturerCheckValue + Index + 2) == TpmManufacturerId2) {
      //
      // Manufacturer match found; register driver binding
      //
      return gBS->InstallMultipleProtocolInterfaces (
                    &ImageHandle,
                    &gEfiTcgProtocolGuid2,
                    mProtocolFuncTable[0],
                    NULL
                    );
    }
  }

  //
  // No manufacturer match; try reading existing flags
  //
  ZeroMem (DefaultFlags, sizeof (DefaultFlags));
  Status = GetPersistentTpmFlags (
             CurrentFlags,
             Index,
             DefaultFlags,
             sizeof (DefaultFlags)
             );
  if (EFI_ERROR (Status)) {
    //
    // Failed to read flags; set defaults
    //
    *(UINT16 *)CurrentFlags       = 0x101;  // Version 1, flags byte 0 = 1
    *((UINT8 *)CurrentFlags + 2)  = 0;
    SetPersistentTpmFlags (CurrentFlags);
  }

  //
  // Register the driver binding protocol
  //
  return gBS->InstallMultipleProtocolInterfaces (
                &ImageHandle,
                &gEfiTcgProtocolGuid2,
                mProtocolFuncTable[1],
                NULL
                );
}

/**
  UEFI Driver Entry Point.

  @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 was initialized.
  @retval EFI_INVALID_PARAMETER ImageHandle or SystemTable was NULL.
  @retval Others                Error status from child operations.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Initialize UEFI boot/runtime services and hardware abstraction
  //
  UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);

  //
  // Execute the main TCG NV flag sample logic
  //
  return AmiTcgNvflagSampleDriver (ImageHandle, SystemTable);
}