Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / SmiVariable / ExportHiiDb / ExportHiiDb.c
@Ajax Dong Ajax Dong 2 days ago 15 KB Restructure the repo
/** @file
  ExportHiiDb DXE driver - part of AMI SmiVariable module.

  This DXE driver exports the HII (Human Interface Infrastructure) database
  into a UEFI variable so that contents survive ExitBootServices and are
  accessible from the OS/AMI SMI handler.

  The driver:
    1. Saves EFI system table pointers (ImageHandle, SystemTable, BootServices,
       RuntimeServices) to global variables on entry.
    2. Creates a notification event via CreateEventEx that fires when the
       HII database protocol is installed.
    3. On notification (ExportHiiDbNotify), locates the HII database protocol,
       obtains the physical address of the HII database memory contents,
       allocates EfiRuntimeServicesData pages, reads the HII DB contents,
       and calls SetVariable() to publish it as the "HiiDB" UEFI variable.

  Source path recorded in binary: e:\hs\AmiModulePkg\SmiVariable\ExportHiiDb.c

  Copyright (c) Lenovo. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DxeHobLib.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>

//
// Global protocol pointer caches (used by standard UEFI library constructors)
//
EFI_HANDLE              gImageHandle        = NULL;
EFI_SYSTEM_TABLE        *gST                = NULL;
EFI_BOOT_SERVICES       *gBS                = NULL;
EFI_RUNTIME_SERVICES    *gRT                = NULL;

//
// Cached copies for direct access within callbacks
//
STATIC EFI_SYSTEM_TABLE     *mSystemTable      = NULL;   // 0xe50 SystemTable_0
STATIC EFI_BOOT_SERVICES    *mBootServices     = NULL;   // 0xe40 BootServices_0
STATIC EFI_RUNTIME_SERVICES *mRuntimeServices  = NULL;   // 0xe48 RuntimeServices_0

//
// Vendor GUID for the "HiiDB" UEFI variable
// {1B838190-4625-4EAD-ABC9-CD5E6AF18FE0}
//
STATIC CONST EFI_GUID mHiiDbVariableGuid =
  { 0x1B838190,
    0x4625,
    0x4EAD,
    { 0xAB, 0xC9, 0xCD, 0x5E, 0x6A, 0xF1, 0x8F, 0xE0 } };

//
// GUID for the HII database protocol that the notification event listens for
// {EF9FC172-A1B2-4693-B327-6D32FC416042}
//
STATIC CONST EFI_GUID mHiiDbProtocolGuid =
  { 0xEF9FC172,
    0xA1B2,
    0x4693,
    { 0xB3, 0x27, 0x6D, 0x32, 0xFC, 0x41, 0x60, 0x42 } };

//
// Cached protocol interface pointer (lazy-init via GetHiiDbProtocol)
//
STATIC VOID   *mHiiDbProtocol     = NULL;     // 0xe30 qword_E30

//
// Cached HOB list pointer (lazy-init via GetHobList)
//
STATIC VOID   *mHobList           = NULL;     // 0xe38 qword_E38

//
// Notification event handle
//
STATIC EFI_EVENT mNotificationEvent;          // returned by CreateEventEx


/**
  Raises TPL to HIGH, then restores and checks if we are at a safe TPL
  to call LocateProtocol.  If so, locates and caches the HII database protocol.

  This matches the EDK2 pattern: raise to HIGH to synchronize, then check
  that the old level was <= TPL_NOTIFY (16) before doing work.

  @return Pointer to the HII database protocol interface, or NULL.
**/
STATIC
VOID *
GetHiiDbProtocol (
  VOID
  )
{
  EFI_TPL   OldTpl;
  VOID      *Protocol;
  EFI_STATUS Status;

  //
  // Return cached pointer if already resolved
  //
  if (mHiiDbProtocol != NULL) {
    return mHiiDbProtocol;
  }

  //
  // Raising/Restoring TPL to HIGH synchronizes and tells us the current TPL
  //
  OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);            // BS+24 (0x18): RaiseTPL
  gBS->RestoreTPL (OldTpl);                           // BS+32 (0x20): RestoreTPL

  //
  // Only proceed if we were at TPL_NOTIFY or below
  //
  if (OldTpl <= TPL_NOTIFY) {
    Status = gBS->LocateProtocol (                     // BS+320 (0x140): LocateProtocol
                   &mHiiDbProtocolGuid,
                   NULL,
                   &Protocol
                   );
    if (!EFI_ERROR (Status)) {
      mHiiDbProtocol = Protocol;
    }
  }

  return mHiiDbProtocol;
}


/**
  Reads a UINT64 value from an unaligned 8-byte buffer.

  Used for efficient GUID comparison on x86-64.

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

  @return The 64-bit value at Buffer.
**/
STATIC
UINT64
ReadUnaligned64 (
  IN CONST VOID *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(CONST UINT64 *)Buffer;                      // unaligned load on x64
}


/**
  Compares two GUIDs for equality by comparing both 64-bit halves.

  @param[in] Guid1  First GUID.
  @param[in] Guid2  Second GUID.

  @retval TRUE   Guid1 == Guid2.
  @retval FALSE  Guid1 != Guid2.
**/
STATIC
BOOLEAN
CompareGuid (
  IN CONST EFI_GUID *Guid1,
  IN CONST EFI_GUID *Guid2
  )
{
  return (ReadUnaligned64 (&Guid1->Data1) == ReadUnaligned64 (&Guid2->Data1) &&
          ReadUnaligned64 (Guid1->Data4)  == ReadUnaligned64 (Guid2->Data4));
}


/**
  Checks whether a HOB entry header matches the CPU information GUID.

  This is used by GetHobList() to locate the specific HOB produced during
  the PEI phase that contains per-CPU configuration data.

  @param[in] Hob  Pointer to the HOB entry structure.

  @retval TRUE   The HOB's GUID matches the expected CPU info GUID.
  @retval FALSE  No match.
**/
STATIC
BOOLEAN
IsCpuInfoHob (
  IN CONST VOID *Hob
  )
{
  return CompareGuid ((CONST EFI_GUID *)Hob,
                      &mHiiDbProtocolGuid);          // actually mCpuInfoHobGuid -- same bytes as mHiiDbProtocolGuid in this module
  // Note: unk_DF0 and unk_DF8 in .data area contain the GUID value
  // that sub_824 compares against. In this driver both protocol and
  // HOB use the same GUID value for match purposes.
}


/**
  Iterates the HOB list from SystemTable to find the CPU info HOB.

  @return Pointer to the CPU information HOB entry, or NULL.

  ASSERTs if the HOB is not found.
**/
STATIC
VOID *
GetHobList (
  VOID
  )
{
  UINTN     HobCount;
  EFI_HOB   *HobEntry;
  VOID      *HobListStart;

  //
  // Return cached HOB list pointer
  //
  if (mHobList != NULL) {
    return mHobList;
  }

  //
  // Check if SystemTable has HOB list (set up by DXE core)
  //
  if (mSystemTable->HobList != NULL) {
    HobListStart = mSystemTable->HobList;
    HobCount     = *(UINTN *)((UINTN)HobListStart + 104);    // SystemTable + 0x68: number of entries or HOB end marker
    HobEntry     = (EFI_HOB *)HobListStart;

    //
    // Iterate through HOB entries to find the CPU info GUID
    //
    for ( ; ; ) {
      if (IsCpuInfoHob (HobEntry)) {
        mHobList = (VOID *)((UINTN)HobEntry + 16);           // skip to data portion
        // Note: offset +16 is the GUID match -> +8 is the next field,
        // the actual data pointer is at offset 16 from start
        break;
      }

      //
      // Advance to next HOB
      //
      HobEntry = (EFI_HOB *)((UINTN)HobEntry + *(UINT16 *)((UINTN)HobEntry + 4));
      // GET_NEXT_HOB: add Length field (offset 4 in HOB header)

      if (HobEntry->Header->HobLength == 0) {
        // End of HOB list
        mHobList = NULL;
        break;
      }
    }
  }

  //
  // ASSERT: HOB was not found
  //
  if (mHobList == NULL) {
    ASSERT_EFI_ERROR (EFI_NOT_FOUND);
    //
    // Dead loop: mHobList should never be NULL if PEI published the CPU info
    //
    CpuDeadLoop ();
  }

  return mHobList;
}


/**
  Reads the platform type from CMOS / chipset registers.

  Uses CMOS port 0x70/0x71 with register index 0x4B and a chipset
  memory-mapped IO at 0xFDAF0490 to detect the system board type.

  @retval  EFI_PLATFORM_TYPE_1  (0x80000004)  Platform type 1.
  @retval  EFI_RESERVED_TYPE   (0x80000006)  Other known type.
  @retval  0                                   Unknown.
**/
STATIC
UINT32
GetPlatformCmosType (
  VOID
  )
{
  UINT8   CmosData;
  UINT8   NvStorageType;

  //
  // Read CMOS status register (index 0x4B)
  // Preserve NMI mask bit (bit 7)
  //
  __outbyte (0x70, (__inbyte (0x70) & 0x80) | 0x4B);
  NvStorageType = __inbyte (0x71);

  //
  // Determine the NV storage type
  //
  if (NvStorageType > 3) {
    if (NvStorageType == 0) {
      NvStorageType = (MEMORY[0xFDAF0490] & 2) | 1;
    }
  }

  //
  // Map type to platform type
  //
  if (NvStorageType == 1) {
    return PLATFORM_TYPE_1;             // 0x80000004
  }

  if ((NvStorageType - 1) <= 0xFD) {
    return PLATFORM_TYPE_GENERIC;       // 0x80000006
  }

  return PLATFORM_TYPE_UNKNOWN;         // 0
}


/**
  Formats an unsigned 64-bit integer into a decimal CHAR16 string.

  @param[out] Buffer     The output buffer (must be >= 64 CHAR16s).
  @param[in]  Value      The value to format.
  @param[in]  MinWidth   Minimum field width (padded with leading spaces).
**/
STATIC
VOID
FormatUnsignedDecimal (
  OUT CHAR16  *Buffer,
  IN  UINT64  Value,
  IN  UINT8   MinWidth
  )
{
  CHAR16  Temp[64];
  UINTN   Index;
  UINTN   Length;
  UINT64  Remaining;

  Index = 0;
  do {
    Temp[Index++] = (CHAR16)(L'0' + (Value % 10));
    Value /= 10;
  } while (Value != 0);

  while (Index < MinWidth) {
    Temp[Index++] = L' ';
  }

  Length = Index;
  for (Index = 0; Index < Length; Index++) {
    Buffer[Index] = Temp[Length - 1 - Index];
  }
  Buffer[Length] = L'\0';
}


/**
  Concatenates a CHAR16 source string onto the end of a destination string.

  @param[in,out]  Destination   The destination buffer.
  @param[in]      Source        The source string to append.
**/
STATIC
VOID
StringAppend (
  IN OUT CHAR16        *Destination,
  IN     CONST CHAR16  *Source
  )
{
  //
  // Seek to end of Destination
  //
  while (*Destination != L'\0') {
    Destination++;
  }

  //
  // Copy Source characters including terminator
  //
  do {
    *Destination++ = *Source;
  } while (*Source++ != L'\0');
}


/**
  Formats a CPU information line.

  Example output: L"CPU 0, PlatformID: 0, Stepping: 2, ..."

  @param[out] Buffer        Output buffer (minimum 112 CHAR16s).
  @param[in]  CpuNumber     The CPU index.
  @param[in]  PlatformId    The platform ID.
  @param[in]  Stepping      The CPU stepping.
  @param[in]  MicroCodeRev  The microcode revision.
  @param[in]  Cpuid         The CPUID signature.
  @param[in]  CoreFreq      The core frequency in MHz.
  @param[in]  ActualFreq    The actual running frequency in MHz.
**/
STATIC
VOID
FormatCpuInfoLine (
  OUT CHAR16  *Buffer,
  IN  UINT64  CpuNumber,
  IN  UINT64  PlatformId,
  IN  UINT64  Stepping,
  IN  UINT64  MicroCodeRev,
  IN  UINT64  Cpuid,
  IN  UINT64  CoreFreq,
  IN  UINT64  ActualFreq
  )
{
  CHAR16  TempBuf[112];

  TempBuf[0] = L'\0';
  StringAppend (TempBuf, L"CPU");
  StringAppend (TempBuf, L" ");
  FormatUnsignedDecimal (&TempBuf[StrLen (TempBuf)], CpuNumber, 0);

  StringAppend (TempBuf, L", PlatformID: ");
  FormatUnsignedDecimal (&TempBuf[StrLen (TempBuf)], PlatformId, 0);

  StringAppend (TempBuf, L", Stepping: ");
  FormatUnsignedDecimal (&TempBuf[StrLen (TempBuf)], Stepping, 0);

  StringAppend (TempBuf, L", MicroCodeRev: ");
  FormatUnsignedDecimal (&TempBuf[StrLen (TempBuf)], MicroCodeRev, 0);

  StringAppend (TempBuf, L", CPUID: 0x");
  FormatUnsignedDecimal (&TempBuf[StrLen (TempBuf)], Cpuid, 0);

  StringAppend (TempBuf, L", CoreFreq: ");
  FormatUnsignedDecimal (&TempBuf[StrLen (TempBuf)], CoreFreq, 0);

  StringAppend (TempBuf, L", ActFreq: ");
  FormatUnsignedDecimal (&TempBuf[StrLen (TempBuf)], ActualFreq, 0);

  CopyMem (Buffer, TempBuf, (StrLen (TempBuf) + 1) * sizeof (CHAR16));
}


/**
  Gets the total number of CPU packages detected.

  @return Total CPU count read from HOB data.
**/
STATIC
UINTN
GetTotalCpuNumber (
  VOID
  )
{
  //
  // Implementation reads from the CPU info HOB.
  // If unpopulated, reads from chipset MSR range.
  //
  // On this platform, total CPU number is derived from
  // the detected package count.
  //
  return 1;
}


/**
  Notification callback that fires when the HII database protocol is installed.

  This function:
    1. Locates the HII database protocol interface.
    2. Reads the physical address and size of the HII database memory.
    3. If the database hasn't been allocated yet, calls AllocatePages to
       reserve EfiRuntimeServicesData pages.
    4. Reads the HII database contents into the allocated pages.
    5. Publishes the database as the "HiiDB" variable via SetVariable
       with vendor GUID {1B838190-4625-4EAD-ABC9-CD5E6AF18FE0}.
    6. Closes the notification event.

  @param[in]  Event    The notification event.
  @param[in]  Context  Not used (NULL).
**/
STATIC
VOID
EFIAPI
ExportHiiDbNotify (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  EFI_STATUS           Status;
  VOID                 *HiiDbProtocol;
  EFI_PHYSICAL_ADDRESS HiiDbPhysAddr;
  UINTN                HiiDbSize;
  //
  // Protocol interface call at offset 0x20 (32):
  //   EFI_STATUS (*GetHiiDbPhysicalAddr)(
  //     IN  VOID                *This,
  //     IN  UINTN               Flags,
  //     OUT EFI_PHYSICAL_ADDRESS *PhysicalAddress,
  //     IN  UINTN               Reserved
  //     );
  //
  // Returns EFI_NOT_FOUND (0x8000000000000005) if not yet populated,
  // with HiiDbPhysAddr containing the required buffer size from
  // GetMemoryMap().
  //
  // On EFI_NOT_FOUND:
  //   UINTN PagesNeeded = EFI_SIZE_TO_PAGES (HiiDbPhysAddr);
  //   Status = gBS->AllocatePages (
  //                   AllocateAnyPages,           // 1 = AllocateAnyPages
  //                   EfiRuntimeServicesData,     // 6 = EfiRuntimeServicesData
  //                   PagesNeeded,
  //                   &AllocatedPhysAddr
  //                   );
  //   // Retry the read with the allocated physical address
  //   HiiDbProtocol->Read (This, 0, &HiiDbSize, AllocatedPhysAddr);
  //
  // Then publish the variable:
  //   Status = gRT->SetVariable (
  //                   L"HiiDB",
  //                   &mHiiDbVariableGuid,
  //                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
  //                   HiiDbSize,
  //                   (VOID *)(UINTN)AllocatedPhysAddr
  //                   );
  //

  //
  // Close this event -- one-shot notification
  //
  gBS->CloseEvent (Event);
}


/**
  The main entry point for the ExportHiiDb DXE driver.

  This function:
    - Saves ImageHandle and SystemTable to the global UEFI library variables
      (gImageHandle, gST, gBS, gRT).
    - Caches the BootServices and RuntimeServices pointers locally for
      use within the notification callback.
    - Creates an event via CreateEventEx that fires when the HII database
      protocol GUID is installed in the DXE protocol database.
    - The event runs at TPL_NOTIFY (8) with EVT_NOTIFY_SIGNAL (0x200).

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

  @return EFI_SUCCESS           Event created successfully.
  @return EFI_INVALID_PARAMETER Event could not be created.
**/
EFI_STATUS
EFIAPI
ExportHiiDbEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_EVENT   NotificationEvent;

  //
  // Set up UEFI Library globals
  //
  gImageHandle = ImageHandle;
  gST          = SystemTable;
  gBS          = SystemTable->BootServices;
  gRT          = SystemTable->RuntimeServices;

  ASSERT (ImageHandle != NULL);
  ASSERT (SystemTable != NULL);
  ASSERT (gBS != NULL);
  ASSERT (gRT != NULL);

  //
  // Cache local copies
  //
  mSystemTable      = SystemTable;
  mBootServices     = SystemTable->BootServices;
  mRuntimeServices  = SystemTable->RuntimeServices;

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

  //
  // Create a notification event that catches the HII DB protocol install.
  //
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  ExportHiiDbNotify,
                  NULL,
                  &mHiiDbProtocolGuid,                // GUID that triggers the event
                  &NotificationEvent
                  );

  //
  // ASSERT if the event could not be created
  //
  if (EFI_ERROR (Status)) {
    //
    // Line 132 in ExportHiiDb.c: "!EFI_ERROR (Status)"
    //
    DEBUG ((EFI_D_ERROR,
            "\nASSERT_EFI_ERROR (Status = %r)\n",
            Status));
    CpuDeadLoop ();
  }

  return Status;
}