/** @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;
}