Newer
Older
AMI-Aptio-BIOS-Reversed / AmiBoardInfo2 / AmiBoardInfo2.c
@Ajax Dong Ajax Dong 2 days ago 23 KB Init
/** @file
 *AmiBoardInfo2.c - AMI Board Info DXE Driver
 *
 *UEFI DXE driver that collects platform board configuration data
 * (DSDT ACPI tables, SIO data, IOAPIC data) from HOBs created during
 *PEI phase and installs them via a UEFI protocol for consumption
 *by other DXE drivers.
 *
 *Source: e:\hs\AmiModulePkg\BoardInfo\AmiBoardInfo2.c
 *Module: AmiBoardInfo2.efi
 *Arch: x86_64
 *Size: 0x18a0 (6304 bytes)
 *MD5: bc8960b98848df805337c991a63a7ac3
 *SHA256: 2e0d2b3091d5abc6bd7a02b495380cf534aec2e56326ad9e267dbfef864375f5
 */

/*=============================================================================
 *HEADERS
 *============================================================================*/
#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/HobLib.h>
#include <Protocol/BoardInfo.h> /*AMI-specific board info protocol */

#include "AmiBoardInfo2.h"

/*=============================================================================
 *GLOBAL VARIABLES
 *============================================================================*/

//
// Standard UEFI globals (set by _ModuleEntryPoint)
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;

//
// Persistent copies for AmiBoardInfo2Entry
//
EFI_SYSTEM_TABLE *gSavedSystemTable = NULL; /*0x1760 */
EFI_BOOT_SERVICES *gSavedBootServices = NULL; /*0x1740 */
EFI_RUNTIME_SERVICES *gSavedRuntimeServices = NULL; /*0x1748 */

//
// Debug library globals
//
VOID *gDebugOutputProtocol = NULL; /*0x1720 (serial debug) */
VOID *gDebugOutputProtocol2 = NULL; /*0x1758 (console debug) */
UINT8 gDebugInitialized = 0; /*0x1730 - 0=serial, 1=console */
UINT8 gDebugMode = 0; /*0x1731 */

//
// HOB list pointer (lazily initialized by GetHobList)
//
VOID *gHobList = NULL; /*0x1728 */

/*=============================================================================
 *DEBUG OUTPUT FUNCTIONS (linked from DebugLib)
 *============================================================================*/

/**
 *Get the debug output protocol pointer.
 *
 *Lazily locates the debug output protocol (serial I/O protocol)
 *via gBS->LocateProtocol(&gEfiSerialIoProtocolGuid, ...).
 *
 * @return Pointer to the debug output protocol, or NULL if not found.
 */
VOID *DebugLibGetDebugOutput (
 VOID
 )
{
 UINTN Pages;
 VOID *Protocol;

 Protocol = gDebugOutputProtocol;
 if (Protocol != NULL) {
 return Protocol;
 }

 //
 // Allocate one page for the serial I/O interface
 //
 Pages = gBootServices->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1);
 gBootServices->FreePages (Pages, 1);

 if (Pages > 0x10) {
 return NULL;
 }

 //
 // Locate the Serial I/O protocol
 //
 gBootServices->LocateProtocol (&gEfiSerialIoProtocolGuid, NULL, &gDebugOutputProtocol);
 Protocol = gDebugOutputProtocol;
 if (Protocol == NULL) {
 Protocol = NULL;
 }
 gDebugOutputProtocol = Protocol;
 return Protocol;
}

/**
 *Get the current platform debug print level.
 *
 *Reads CMOS register 0x4B (diagnostic status register DSR) to
 *determine the enabled debug mask. Also checks a known memory-mapped
 *register at 0xFDAF0490 for additional platform-specific debug flags.
 *
 * @return Debug level bitmask:
 *0x80000004 (EFI_D_INFO | EFI_D_ERROR) if debug enabled at level 3+
 *0x80000006 (EFI_D_WARN | EFI_D_INFO | EFI_D_ERROR) if debug level 1
 *0 if debug level 0 (none)
 */
UINTN DebugGetPlatformDebugLevel (
 VOID
 )
{
 UINT8 CmosDebugLevel;
 UINT8 DebugLevel;
 UINT8 PlatformFlags;

 //
 // Read CMOS diagnostic status register (0x70/0x71, index 0x4B)
 //
 CmosDebugLevel = IoRead8 (0x70);
 IoWrite8 (0x70, CmosDebugLevel & 0x80 | 0x4B);
 DebugLevel = IoRead8 (0x71);

 if (DebugLevel > 3) {
 //
 // Check platform-specific memory-mapped register
 //
 PlatformFlags = *(volatile UINT8 *)(UINTN)0xFDAF0490;
 if (DebugLevel == 0) {
 DebugLevel = (PlatformFlags & 2) | 1;
 }
 }

 if (DebugLevel - 1 > 0xFD) {
 //
 // DebugLevel is 0: debug disabled
 //
 return 0;
 }

 if (DebugLevel == 1) {
 return EFI_D_WARN | EFI_D_INFO | EFI_D_ERROR; /*0x80000006 */
 }

 return EFI_D_INFO | EFI_D_ERROR; /*0x80000004 */
}

/**
 *Initialize the debug library.
 *
 *Sets up the debug output protocol based on the configuration.
 *If console debug is requested (byte_1730 != 0), attempts to
 *locate the StdErr protocol; otherwise, uses the serial I/O
 *protocol obtained via AllocatePages + LocateProtocol.
 *
 * @return EFI_SUCCESS Debug output initialized.
 *EFI_NOT_READY Could not allocate or locate protocol.
 */
EFI_STATUS DebugLibInitialize (
 VOID
 )
{
 EFI_STATUS Status;
 UINTN Pages;
 VOID *Protocol;

 if (gDebugInitialized) {
 //
 // Console debug mode: locate StdErr protocol
 //
 if (gDebugOutputProtocol2 != NULL) {
 return EFI_SUCCESS;
 }
 if (gDebugMode == 1) {
 return EFI_NOT_READY;
 }
 //
 // This path not taken in console mode since gDebugInitialized=0
 //
 return EFI_NOT_READY;
 } else {
 //
 // Serial debug mode (default)
 //
 if (gDebugOutputProtocol2 != NULL) {
 return EFI_SUCCESS;
 }
 if (gDebugMode == 1) {
 return EFI_NOT_READY;
 }

 //
 // Allocate/free memory to probe available pages
 //
 Pages = gSavedBootServices->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1);
 gSavedBootServices->FreePages (Pages, 1);

 if (Pages > 0x10) {
 return EFI_NOT_READY;
 }

 Status = gSavedBootServices->LocateProtocol (
 &gEfiSerialIoProtocolGuid,
 NULL,
 &gDebugOutputProtocol2
 );
 if (EFI_ERROR (Status)) {
 gDebugOutputProtocol2 = NULL;
 }
 }

 return Status;
}

/**
 *Debug print with level check and format conversion.
 *
 *Converts %r (EFI_STATUS format) to the standard %r and
 * %s (wide char) to %s / %g (GUID) to %g before forwarding
 *to the debug output protocol.
 *
 * @param ErrorLevel Debug error level mask.
 * @param Format Format string (modified in-place for %r->%s conversion).
 * @param ... Variable arguments.
 *
 * @return Result from the debug output protocol, or error status.
 */
UINTN BoardInfoDebugPrint (
 IN UINTN ErrorLevel,
 IN CONST CHAR8 *Format,
 ...
 )
{
 UINTN Result;
 UINTN Ret;
 UINTN DebugLevel;
 CHAR8 *Fmt;
 VA_LIST Args;
 VA_LIST ArgsCopy;

 VA_START (ArgsCopy, Format);
 VA_START (Args, Format);

 Result = DebugLibInitialize ();
 if (!EFI_ERROR (Result)) {
 DebugLevel = gDebugOutputProtocol2;
 if (gDebugInitialized) {
 DebugLevel = (UINTN)gDebugOutputProtocol2;
 }
 Ret = DebugGetPlatformDebugLevel ();
 if ((UINTN)DebugLevel & (UINTN)Ret) {
 //
 // Convert %r (EFI_STATUS) to %r and %s/%g to ASCII-compatible format
 //
 Fmt = (CHAR8 *)Format;
 if (*Format != 0) {
 do {
 if (*Fmt == '%') {
 if (*(Fmt + 1) == 's') {
 *(Fmt + 1) = 'a'; /* %s -> %a for CHAR8 strings */
 } else if (*(Fmt + 1) == 'G') {
 *(Fmt + 1) = 'g'; /* %G -> %g for GUIDs */
 }
 }
 Fmt++;
 } while (*Fmt != 0);
 Fmt = (CHAR8 *)Format;
 }
 return ((UINTN ( *)(UINTN, CHAR8 *, VA_LIST))DebugLevel)(
 ErrorLevel,
 Fmt,
 VA_ARGS (Args)
 );
 }
 }
 return Result;
}

/**
 *Debug print with ASSERT-level error level.
 *
 * @param ErrorLevel Error level (always 0x80000000 = EFI_D_ERROR).
 * @param Format Format string.
 * @param ... Variable arguments.
 *
 * @return Result from the debug output protocol.
 */
UINTN DebugVPrint (
 IN UINTN ErrorLevel,
 IN CONST CHAR8 *Format,
 ...
 )
{
 VA_LIST Args;
 VA_LIST ArgsCopy;
 UINTN Result;
 UINTN Ret;
 VOID *DebugOutput;

 VA_START (ArgsCopy, Format);
 VA_START (Args, Format);

 Result = (UINTN)DebugLibGetDebugOutput ();
 DebugOutput = (VOID *)Result;

 if (DebugOutput != NULL) {
 Ret = DebugGetPlatformDebugLevel ();
 if (((UINTN)Result & ErrorLevel) != 0) {
 return ((UINTN ( *)(UINTN, CONST CHAR8 *, VA_LIST))DebugOutput)(
 ErrorLevel,
 Format,
 VA_ARGS (Args)
 );
 }
 }
 return Result;

 VA_END (Args);
 VA_END (ArgsCopy);
}

/**
 *Debug assertion handler.
 *
 *Prints the failed assertion via the debug output protocol.
 *
 * @param FileName Source file name.
 * @param LineNumber Line number of the assertion.
 * @param FailedCondition The failed assertion expression.
 */
VOID DebugPrintAssert (
 IN CONST CHAR8 *FileName,
 IN UINTN LineNumber,
 IN CONST CHAR8 *FailedCondition
 )
{
 VOID *DebugOutput;

 DebugOutput = DebugLibGetDebugOutput ();
 if (DebugOutput != NULL) {
 ((VOID ( *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))DebugOutput)(
 FileName,
 LineNumber,
 FailedCondition
 );
 }
}

/*=============================================================================
 *HOB (HAND-OFF BLOCK) FUNCTIONS
 *============================================================================*/

/**
 *Get the HOB list pointer.
 *
 *Traverses the SystemTable's HOB list entries looking for the
 *PHIT (Phase Hand-off Information Table) HOB which contains
 *the pointer to the start of the HOB list. The HOB list is
 *identified by comparing HOB GUIDs against known GUIDs
 * (gEfiHobListGuid or similar).
 *
 * @return Pointer to the HOB list, or NULL if not found.
 */
VOID *GetHobList (
 VOID
 )
{
 UINTN Count;
 EFI_GUID *HobGuid;
 VOID *HobList;

 HobList = gHobList;
 if (HobList != NULL) {
 return HobList;
 }

 gHobList = NULL;
 Count = 0;
 HobList = NULL;

 if (gSystemTable->NumberOfTableEntries != 0) {
 //
 // Walk the configuration table looking for HOB list GUID
 //
 while (Count < gSystemTable->NumberOfTableEntries) {
 if (HobIsMatchingGuid (
 (EFI_GUID *)(UINTN)Count,
 *(EFI_GUID **)(gSystemTable->ConfigurationTable + HobList + 24 *Count)
 )) {
 gHobList = *(VOID **)((UINTN)gSystemTable->ConfigurationTable + 24 *Count + 16);
 break;
 }
 Count++;
 HobList = (CHAR8 *)HobList + 24;
 }

 if (gHobList == NULL) {
 DebugVPrint (
 EFI_D_ERROR,
 "\nASSERT_EFI_ERROR (Status = %r)\n",
 EFI_NOT_FOUND
 );
 DebugPrintAssert (
 "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
 54,
 "!EFI_ERROR (Status)"
 );
 }
 }

 if (gHobList == NULL) {
 DebugPrintAssert (
 "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
 55,
 "mHobList != ((void *) 0)"
 );
 }

 return gHobList;
}

/**
 *Read a 64-bit value from a potentially unaligned buffer.
 *
 * @param Buffer Pointer to the 64-bit value (may be unaligned).
 *
 * @return The 64-bit value read from the buffer.
 */
UINT64 ReadUnaligned64 (
 IN CONST UINT64 *Buffer
 )
{
 ASSERT (Buffer != NULL);
 return *Buffer;
}

/**
 *Compare two HOB GUIDs for equality.
 *
 * @param Guid1 First GUID.
 * @param Guid2 Second GUID.
 *
 * @return TRUE if the GUIDs are equal, FALSE otherwise.
 */
BOOLEAN HobIsMatchingGuid (
 IN CONST EFI_GUID *Guid1,
 IN CONST EFI_GUID *Guid2
 )
{
 return ReadUnaligned64 ((UINT64 *)Guid1) == ReadUnaligned64 ((UINT64 *)Guid2)
 && ReadUnaligned64 ((UINT64 *)Guid1 + 1) == ReadUnaligned64 ((UINT64 *)Guid2 + 1);
}

/**
 *Compare two memory buffers.
 *
 * @param Buffer1 First buffer.
 * @param Buffer2 Second buffer.
 * @param Length Number of bytes to compare.
 *
 * @return 0 if the buffers are equal; negative if Buffer1 < Buffer2;
 *positive if Buffer1 > Buffer2.
 */
INTN CompareMem (
 IN CONST VOID *Buffer1,
 IN CONST VOID *Buffer2,
 IN UINTN Length
 )
{
 //
 // Optimized comparison handling misaligned start, aligned 8-byte blocks,
 // and trailing bytes.
 //
 CONST UINT8 *B1;
 CONST UINT8 *B2;
 UINTN AlignDelta;

 B1 = (CONST UINT8 *)Buffer1;
 B2 = (CONST UINT8 *)Buffer2;
 AlignDelta = (UINTN)Buffer1 & 7;

 //
 // Handle head misalignment if both pointers have same alignment
 //
 if (AlignDelta != 0 && AlignDelta == ((UINTN)Buffer2 & 7)) {
 UINTN HeadBytes = 8 - AlignDelta;
 while (HeadBytes != 0 && Length > 0) {
 if (*B1 != *B2) {
 return *B1 - *B2;
 }
 B1++;
 B2++;
 HeadBytes--;
 Length--;
 }
 }

 //
 // Compare aligned 8-byte chunks
 //
 while (Length >= 8 && *(UINT64 *)B1 == *(UINT64 *)B2) {
 B1 += 8;
 B2 += 8;
 Length -= 8;
 }

 //
 // Compare remaining bytes
 //
 while (Length > 0) {
 if (*B1 != *B2) {
 return *B1 - *B2;
 }
 B1++;
 B2++;
 Length--;
 }

 return 0;
}

/**
 *Find HOB data matching a specific signature and optional name filter.
 *
 *Traverses the HOB entries looking for a protocol-data HOB with
 *a matching signature (32-bit tag). If a name filter is provided,
 *additional comparison is performed against the name.
 *
 *The HOB data format expected:
 * - DataSize = HOB data length
 * - DataAddr = Physical address of the HOB data
 *
 * @param DataAddr [out] Address of the matching HOB data.
 * @param DataSize [out] Size of the matching HOB data.
 * @param Signature 32-bit signature to match (e.g. 'DATA').
 * @param NameFilter Optional name string to match against HOB name.
 *
 * @return EFI_SUCCESS Data found.
 *EFI_NOT_FOUND No matching HOB data found.
 */
EFI_STATUS HobFindProtocolData (
 OUT UINT64 *DataAddr,
 OUT UINT64 *DataSize,
 IN UINT32 Signature,
 IN CHAR8 *NameFilter OPTIONAL
 )
{
 UINT64 HobEntryCount;
 UINT64 Index;
 UINT64 *HobHandles;
 UINT64 Size;
 UINT64 DataAddress;
 UINT64 HobListHandle;
 UINT64 *HobData;
 EFI_STATUS Status;
 UINT64 HobHandle;
 UINT8 SearchType;
 UINT64 EntryDataAddr, EntryDataSize;
 UINT64 EntryPtr;
 UINT64 DataPhysicalAddr;
 UINT64 HobBuffer;

 *DataAddr = 0;
 *DataSize = 0;
 HobHandles = NULL;

 //
 // Get the HOB protocol handle list via gBS->LocateHandle
 //
 Status = gSavedBootServices->LocateHandle (
 AllHandles,
 &gEfiHobProtocolGuid,
 NULL,
 &HobEntryCount,
 &HobHandles
 );
 if (EFI_ERROR (Status)) {
 return Status;
 }

 for (Index = 0; Index < HobEntryCount; Index++) {
 //
 // Open the HOB protocol for this handle
 //
 Status = gSavedBootServices->OpenProtocol (
 HobHandles[Index],
 &gEfiHobProtocolGuid,
 &HobData,
 ...
 );
 if (EFI_ERROR (Status)) {
 continue;
 }

 for (;;) {
 //
 // Get the next HOB entry
 //
 SearchType = (NameFilter != NULL) ? HobTypeCodeWithName : HobTypeCode;
 Status = HobData->GetNextHob (HobData, SearchType, Index, &HobBuffer);

 if (Status == EFI_NOT_FOUND) {
 break;
 }

 EntryDataAddr = HobBuffer;
 EntryDataSize = *(UINT64 *)(EntryDataAddr + 8);
 //
 // If name filter provided, copy and compare the name
 //
 if (NameFilter != NULL) {
 if (CompareMem (
 (VOID *)(UINTN)HobBuffer,
 NameFilter,
 *(UINT8 *)(HobBuffer + 16)
 ) != 0) {
 //
 // Name doesn't match; adjust data pointer and free
 //
 EntryDataSize -= 16;
 DataPhysicalAddr = HobGetDataPtr ((VOID *)(UINTN)EntryDataSize);
 HobBuffer = DataPhysicalAddr;
 if (DataPhysicalAddr != 0) {
 InternalCopyMem (
 (VOID *)(UINTN)DataPhysicalAddr,
 (VOID *)(UINTN)(EntryDataAddr + 16),
 EntryDataSize
 );
 }
 gSavedBootServices->FreePages (HobBuffer, 1);
 }
 //
 // Name matches; fall through to return
 //
 }

 //
 // Success: return the data address and size
 //
 gSavedBootServices->FreePages (HobHandles, 1);
 *DataAddr = HobBuffer;
 *DataSize = EntryDataSize;
 return EFI_SUCCESS;
 }

 if (HobBuffer != 0) {
 gSavedBootServices->FreePages (HobBuffer, 1);
 }
 }

 gSavedBootServices->FreePages (HobHandles, 1);
 return EFI_NOT_FOUND;
}

/**
 *Get the pointer to the data section of a HOB.
 *
 *Uses gBS->GetHobData to retrieve a pointer to the HOB's data payload.
 *
 * @param Hob Pointer to the HOB entry.
 *
 * @return Pointer to the data section of the HOB, or 0 if error.
 */
VOID *HobGetDataPtr (
 IN CONST VOID *Hob
 )
{
 UINT64 DataPtr;

 DataPtr = 0;
 gSavedBootServices->GetHobData (4, (UINT64)Hob, &DataPtr);
 return (VOID *)(UINTN)DataPtr;
}

/**
 *Search the HOB list for a specific GUID.
 *
 *Walks the HOB list looking for a HOB entry whose GUID matches
 *the given GUID (passed as a UINT64 signature).
 *
 * @param Signature 64-bit signature to match (little-endian ASCII GUID).
 * @param Hob [out] Pointer to the matching HOB entry.
 * @param HobList Pointer to the HOB list (third arg via ).
 *
 * @return EFI_SUCCESS Matching HOB found.
 *EFI_NOT_FOUND No matching HOB found.
 *EFI_INVALID_PARAMETER Invalid input.
 */
EFI_STATUS HobFindGuid (
 IN EFI_GUID *Guid,
 OUT VOID **Hob,
 ...
 )
{
 UINT64 *HobListStart;

 HobListStart = (UINT64 *)Hob;
 if (Hob == NULL || HobListStart == NULL) {
 return EFI_INVALID_PARAMETER;
 }

 if (*HobListStart == 8) {
 return EFI_NOT_FOUND;
 }

 while (*(UINT64 *)(UINTN)HobListStart != (UINT64)Guid) {
 HobListStart++;
 if ((UINTN)HobListStart >= (UINTN)(*HobListStart - 8)) {
 return EFI_NOT_FOUND;
 }
 }

 *Hob = (VOID *)HobListStart;
 return EFI_SUCCESS;
}

/**
 *Internal memory copy with overlap handling.
 *
 *Copies Length bytes from Source to Destination, correctly
 *handling overlapping regions by detecting direction and
 *selecting forward/backward copy as appropriate.
 *
 * @param Destination Pointer to the destination buffer.
 * @param Source Pointer to the source buffer.
 * @param Length Number of bytes to copy.
 *
 * @return Destination pointer.
 */
VOID *InternalCopyMem (
 OUT VOID *Destination,
 IN CONST VOID *Source,
 IN UINTN Length
 )
{
 UINT8 *Dst;
 CONST UINT8 *Src;
 UINT8 Direction;
 UINTN AlignDelta;
 UINT8 Count;

 Dst = (UINT8 *)Destination;
 Src = (CONST UINT8 *)Source;
 Direction = 0;

 //
 // Detect overlap: if Source < Destination and overlap exists,
 // copy backwards from the end.
 //
 if (Src < Dst && Src + Length > Dst) {
 Src += Length;
 Dst += Length;
 Direction = 1;
 }

 //
 // If aligned and length >= 8, copy aligned head first
 //
 if (Length >= 8 && (UINTN)Src - (UINTN)Dst < 8) {
 if (Direction) {
 Src -= 7;
 Dst -= 7;
 }
 //
 // Copy misaligned head
 //
 AlignDelta = (UINTN)Src & 7;
 if ((AlignDelta & 7) == ((UINTN)Dst & 7) && AlignDelta != 0) {
 if (!Direction) {
 AlignDelta = 8 - AlignDelta;
 }
 CopyMem (Dst, Src, AlignDelta);
 Src += AlignDelta;
 Dst += AlignDelta;
 Length -= AlignDelta;
 }

 if (Direction) {
 Src -= 7;
 Dst -= 7;
 }
 }

 //
 // Copy aligned 8-byte chunks
 //
 if (Length >= 8) {
 CopyMem (Dst, Src, Length & ~7);
 Src += Length & ~7;
 Dst += Length & ~7;
 Length &= 7;
 }

 //
 // Copy trailing bytes
 //
 if (Length != 0) {
 if (Direction) {
 Src += 8;
 Dst += 8;
 }
 CopyMem (Dst, Src, Length);
 }

 return Destination;
}

/*=============================================================================
 *MAIN DRIVER ENTRY POINTS
 *============================================================================*/

/**
 *Standard UEFI DXE driver entry point.
 *
 *Initializes global variables, locates the HOB list, then
 *calls AmiBoardInfo2Entry to perform board info collection.
 *
 * @param ImageHandle Handle for this image.
 * @param SystemTable Pointer to the UEFI system table.
 *
 * @return EFI_STATUS from AmiBoardInfo2Entry.
 */
EFI_STATUS EFIAPI _ModuleEntryPoint (
 IN EFI_HANDLE ImageHandle,
 IN EFI_SYSTEM_TABLE *SystemTable
 )
{
 //
 // Save global pointers with ASSERT checks (standard UEFI boilerplate)
 //
 gImageHandle = ImageHandle;
 ASSERT (gImageHandle != NULL);

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

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

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

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

 //
 // Dispatch to the main board info entry function
 //
 return AmiBoardInfo2Entry (ImageHandle, SystemTable);
}

/**
 *Main board information driver entry.
 *
 *Collects platform board configuration by:
 *1. Checking if Multi-Platform Board Info protocol already exists
 * (if so, prints message and exits)
 *2. Finding DSDT ACPI table data in HOBs
 *3. Finding SIO (Super I/O) configuration data in HOBs
 *4. Finding IOAPIC configuration data in HOBs
 *5. Installing the AMI Board Info protocol with collected data
 *
 *The HOBs are identified by their signature strings:
 * "$PCIDATA" (DSDT),
 * "$SIODATA" (SIO),
 * "$APIDATA" (IOAPIC)
 *
 * @param ImageHandle Handle for this image.
 * @param SystemTable Pointer to the UEFI system table.
 *
 * @return EFI_SUCCESS Protocol installed.
 *EFI_ALREADY_STARTED Multi-Platform Board Info already present.
 *EFI_NOT_FOUND Required HOB data missing.
 */
EFI_STATUS AmiBoardInfo2Entry (
 IN EFI_HANDLE ImageHandle,
 IN EFI_SYSTEM_TABLE *SystemTable
 )
{
 UINT64 DsdtHobAddr;
 UINT64 DsdtHobDataAddr;
 UINT64 SioHobAddr;
 UINT64 IoApicHobAddr;
 EFI_STATUS Status;
 BOOLEAN HasProtocolData;
 AMI_BOARD_INFO2_PROTOCOL *BoardInfo;

 DsdtHobAddr = 0;
 DsdtHobDataAddr = 0;

 //
 // Save SystemTable, BootServices, RuntimeServices on first call
 //
 if (gSavedSystemTable == NULL) {
 gSavedSystemTable = SystemTable;
 gSavedBootServices = SystemTable->BootServices;
 gSavedRuntimeServices = SystemTable->RuntimeServices;
 }

 //
 // Check if the Multi-Platform Board Info protocol already exists
 // (gBS->LocateProtocol). If so, this is a second call -- exit.
 //
 BoardInfo = NULL;
 Status = gSavedBootServices->LocateProtocol (
 &gAmiBoardInfoProtocolGuid,
 NULL,
 (VOID **)&BoardInfo
 );
 if (!EFI_ERROR (Status)) {
 BoardInfoDebugPrint (
 EFI_D_ERROR,
 "AmiBrdInfo: Multi-Platform BrdInfo present Status=%r Exiting",
 Status
 );
 return EFI_ALREADY_STARTED;
 }

 //
 // Find the protocol-data HOBs (they contain DSDT, SIO, IOAPIC data)
 //
 HasProtocolData = FALSE;
 Status = HobFindProtocolData (
 &gProtocolDataBase,
 &gProtocolDataSize,
 HOB_SIGNATURE_0,
 (CHAR8 *)&gNameFilter
 );
 if (EFI_ERROR (Status)) {
 DebugVPrint (EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
 DebugPrintAssert (
 "e:\\hs\\AmiModulePkg\\BoardInfo\\AmiBoardInfo2.c",
 158,
 "!EFI_ERROR (Status)"
 );
 return Status;
 }

 //
 // Find DSDT HOB data
 //
 if (gProtocolDataBase != 0) {
 Status = HobFindGuid (
 (EFI_GUID *)HOB_SIGNATURE_DSDT,
 (VOID **)&DsdtHobAddr,
 (VOID *)(UINTN)gProtocolDataBase
 );
 DsdtHobDataAddr = DsdtHobAddr;
 }

 if (EFI_ERROR (Status)) {
 DebugVPrint (EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
 DebugPrintAssert (
 "e:\\hs\\AmiModulePkg\\BoardInfo\\AmiBoardInfo2.c",
 167,
 "!EFI_ERROR (Status)"
 );
 return Status;
 }

 //
 // Store DSDT data pointer in the protocol structure (slot 2)
 //
 BoardInfo = (AMI_BOARD_INFO2_PROTOCOL *)&gBoardInfoProtocolStructure;
 BoardInfo->DsdtHobDataAddr = DsdtHobDataAddr;

 //
 // Find DSDT ACPI table via HOB protocol (optional)
 //
 Status = HobFindProtocolData (
 &BoardInfo->DsdtDataPtr,
 &BoardInfo->DsdtDataSize,
 HOB_SIGNATURE_0 & 0xFFFFFFFF,
 NULL
 );
 if (!EFI_ERROR (Status)) {
 //
 // Store DSDT data pointer and size (slots 5, 6)
 //
 } else {
 BoardInfoDebugPrint (-1, "\n======================================================================================\n");
 BoardInfoDebugPrint (
 -1,
 "AmiBrdInfo: !!!WARNING!!! Can't find DSDT Table in BIOS FV - %r.\n"
 " !!!WARNING!!! Check your project ACPI settings...\n",
 Status
 );
 BoardInfoDebugPrint (-1, "======================================================================================\n\n");
 }

 //
 // Find SIO (Super I/O) data HOB
 //
 SioHobAddr = 0;
 if (gProtocolDataBase != 0) {
 Status = HobFindGuid (
 (EFI_GUID *)HOB_SIGNATURE_SIO,
 (VOID **)&SioHobAddr,
 (VOID *)(UINTN)gProtocolDataBase
 );
 }

 if (!EFI_ERROR (Status)) {
 BoardInfo->SioHobDataAddr = SioHobAddr;
 } else {
 BoardInfoDebugPrint (-1, "\n======================================================================================\n");
 BoardInfoDebugPrint (
 -1,
 "AmiBrdInfo: !!!WARNING!!! Can't find SIO Data Table in BIOS FV - %r.\n"
 " !!!WARNING!!! Check your project SIO settings...\n",
 Status
 );
 BoardInfoDebugPrint (-1, "======================================================================================\n\n");
 }

 //
 // Find IOAPIC data HOB
 //
 IoApicHobAddr = 0;
 if (gProtocolDataBase != 0) {
 Status = HobFindGuid (
 (EFI_GUID *)HOB_SIGNATURE_IOAPIC,
 (VOID **)&IoApicHobAddr,
 (VOID *)(UINTN)gProtocolDataBase
 );
 }

 if (!EFI_ERROR (Status)) {
 BoardInfo->IoApicHobDataAddr = IoApicHobAddr;
 } else {
 BoardInfoDebugPrint (-1, "\n======================================================================================\n");
 BoardInfoDebugPrint (
 -1,
 "AmiBrdInfo: !!!WARNING!!! Can't find IOAPIC Data Table in BIOS FV - %r.\n"
 " !!!WARNING!!! Check your project APIC and ROUTER settings...\n",
 Status
 );
 BoardInfoDebugPrint (-1, "======================================================================================\n\n");
 }

 //
 // Install the AMI Board Info protocol
 //
 return gSavedBootServices->InstallMultipleProtocolInterfaces (
 &gBoardInfoProtocolHandle,
 &gAmiBoardInfoProtocolGuid,
 BoardInfo,
 NULL
 );
}