Newer
Older
AMI-Aptio-BIOS-Reversed / POSTDataTransfer / POSTDataTransfer.c
@Ajax Dong Ajax Dong 2 days ago 25 KB Init
/** @file POSTDataTransfer.efi - HR650X BIOS POST Data Transfer Driver This UEFI driver collects PCI bus configuration data and SAD/AD topology data during POST and transmits it to the BMC via IPMI transport.

 Functions:
 ModuleEntryPoint - UEFI entry point ProcessPci - Enumerate all PCI buses/devices/functions,
 report vendor/device IDs to BMC ProcessSADTAD - Enumerate CPU sockets and collect IIO bus numbers and SAD/AD topology, report to BMC CollectPOSTData - Timer callback that orchestrates PCI and SAD/AD collection DebugPrint - Conditional debug print via IPMI or serial DebugAssert - Assertion handler that prints to debug output HobLocate - Locate HOB list from system table UnalignedRead64 - Read 64-bit value from potentially unaligned pointer GuidCompare - Compare two GUIDs for equality PcdLocate - Locate PCD protocol from system table MmioPciExpressRead32 - Read 32-bit value via PCIe MMIO config space PciExpressBaseAddr - Return base address of PCIe MMIO config space GetDebugInterface - Locate debug port protocol EFI/PI protocols used:
 gEfiPciRootBridgeIoProtocolGuid gEfiIioUdsProtocolGuid gEfiCpuCsrAccessGuid gEfiDebugPortProtocolGuid gIpmiTransportProtocolGuid Copyright (c) 2025, Intel Corporation. All rights reserved.
 SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "POSTDataTransfer.h"

//
// Global variables
//
UINT64 gImageHandle;
UINT64 gSystemTable;
UINT64 gBootServices;
UINT64 gRuntimeServices;
UINT64 gBS;
UINT64 gDebugPort;
UINT64 gIpmiTransport;
UINT64 gPciExpressBaseAddr;
UINT64 gPcdProtocol;
UINT64 mPcd;
UINT8 gSocketBusInfo[4][9]; // byte_26E0: bus info per socket UINT64 gGpioCsrAccessProtocol; // qword_2708 UINT64 gIioUdsProtocol; // qword_2710 UINT64 gHobList; // qword_2740 UINT64 gMtrrSettings; // qword_2748 UINT64 gPcdHandle; // qword_2750 UINT64 gPort80DebugValue; // qword_2758

//
// GUID definitions (from UEFI spec / platform headers)
//
EFI_GUID gEfiPciRootBridgeIoProtocolGuid = { 0x2F707EBB, 0x4A1A, 0x11D4, { 0x9A, 0x38, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } };
EFI_GUID gEfiDebugPortProtocolGuid = { 0xEBA4E8D2, 0x3858, 0x41EC, { 0xA2, 0x81, 0x26, 0x47, 0xBA, 0x96, 0x60, 0xD0 } };
EFI_GUID gEfiIioUdsProtocolGuid = { 0xBD7C1C7E, 0xAA2D, 0x4A8A, { 0x8E, 0x01, 0xE4, 0x2C, 0x4A, 0xD6, 0xCE, 0x1C } };
EFI_GUID gEfiCpuCsrAccessGuid = { 0x4FDB53C0, 0x0188, 0x4254, { 0x81, 0x5B, 0x6D, 0x52, 0xA5, 0x63, 0xFE, 0x6C } };
EFI_GUID gIpmiTransportProtocolGuid = { 0x1D7A83F2, 0xE6C7, 0x4B0E, { 0x88, 0x5C, 0x70, 0xC1, 0x0E, 0x5D, 0x44, 0x89 } };

//
// SAD/AD topology lookup table at unk_1940
// Each entry: { Bus, Device, Function, Register offset, Value mask }
// 696 entries (0x2B8 bytes), 5 bytes each
//
STATIC CONST UINT8 mSadAdTopologyTable[696 *5] = { 0 };

/**Locate the debug port protocol interface.

 @return Pointer to debug port protocol interface, or NULL if not found.
**/
UINT64 GetDebugInterface (
 VOID
 )
{
 UINT64 DebugPort;
 UINT64 Status;

 DebugPort = gDebugPort;
 if (DebugPort != 0) {
 return DebugPort;
 }

 //
 // Check if we have enough buffer space (>= 16 bytes)
 //
 if (gBS >= 0x10) {
 Status = (*(UINT64 ( **)(VOID *, UINT64, UINT64 *))(gBootServices + 320))(
 &gEfiDebugPortProtocolGuid,
 0,
 &DebugPort
 );
 if (Status >= 0) {
 gDebugPort = DebugPort;
 }
 }

 return DebugPort;
}

/**Debug print function. Sends formatted output to the debug port if the debug level matches the current platform debug settings.

 @param[in] DebugLevel Debug message level (bitmask)
 @param[in] Format Format string
 @param[in] ... Variable arguments for format string
**/
VOID DebugPrint (
 IN UINT64 DebugLevel,
 IN CONST CHAR8 *Format,
 ...
 )
{
 UINT64 DebugPort;
 UINT64 Port80Val;
 UINT8 CmosIndex;
 UINT8 DebugLevelSelect;
 VA_LIST Va;

 VA_START (Va, Format);

 DebugPort = GetDebugInterface ();
 Port80Val = 0;

 if (DebugPort != 0) {
 CmosIndex = __inbyte (0x70);
 __outbyte (0x70, CmosIndex & 0x80 | 0x4B);

 DebugLevelSelect = __inbyte (0x71);

 if (DebugLevelSelect > 3) {
 if (DebugLevelSelect == 0) {
 DebugLevelSelect = (UINT8)((*(volatile UINT8 *)0xFDAF0490 & 2) | 1);
 }
 }

 if (DebugLevelSelect > 0) {
 Port80Val = 0x7FFFFFC6;

 if (DebugLevelSelect == 1) {
 Port80Val = 0x7FFFFFC4;
 }

 if ((Port80Val & DebugLevel) != 0) {
 (*(VOID ( **)(UINT64, CONST CHAR8 *, VA_LIST))(DebugPort))(DebugLevel, Format, Va);
 }
 }
 }
}

/**Assertion failure handler. Prints the file, line number, and assertion expression via the debug interface.

 @param[in] FileName Source file name where assertion occurred
 @param[in] LineNumber Line number of the assertion
 @param[in] Description Assertion expression text
**/
VOID DebugAssert (
 IN CONST CHAR8 *FileName,
 IN UINTN LineNumber,
 IN CONST CHAR8 *Description
 )
{
 UINT64 DebugPort;

 DebugPort = GetDebugInterface ();
 if (DebugPort != 0) {
 (*(VOID ( **)(CONST CHAR8 *, UINTN, CONST CHAR8 *))(DebugPort + 8))(
 FileName,
 LineNumber,
 Description
 );
 }
}

/**Read a 64-bit value from a potentially unaligned pointer.
 Wrapper around BaseLib aligned read.

 @param[in] Buffer Pointer to the 64-bit value (may be unaligned)

 @return The 64-bit value read.
**/
UINT64 UnalignedRead64 (
 IN CONST VOID *Buffer
 )
{
 if (Buffer == NULL) {
 DebugAssert ("e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c", 192, "Buffer != ((void *) 0)");
 }

 return *(UINT64 *)Buffer;
}

/**Compare two GUIDs for equality by reading their 64-bit components.

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

 @return TRUE if the GUIDs are equal, FALSE otherwise.
**/
BOOLEAN GuidCompare (
 IN CONST EFI_GUID *Guid1,
 IN CONST EFI_GUID *Guid2
 )
{
 UINT64 Guid1Low;
 UINT64 Guid1High;
 UINT64 Guid2Low;
 UINT64 Guid2High;

 Guid1Low = UnalignedRead64 ((CONST VOID *)Guid1);
 Guid2Low = UnalignedRead64 ((CONST VOID *)Guid2);
 Guid1High = UnalignedRead64 ((CONST VOID *)((UINT8 *)Guid1 + 8));
 Guid2High = UnalignedRead64 ((CONST VOID *)((UINT8 *)Guid2 + 8));

 return (Guid1Low == Guid2Low) && (Guid1High == Guid2High);
}

/**Locate the HOB (Hand-Off Block) list pointer from the system table.

 @param[in] SystemTable Pointer to the EFI system table.

 @return Pointer to the HOB list (gHobList).
**/
UINT64 HobLocate (
 IN UINT64 SystemTable
 )
{
 UINT64 Result;
 UINT64 Index;
 UINT64 ConfigTable;
 UINT64 Entry;

 Result = gHobList;
 if (gHobList == 0) {
 gHobList = 0;
 Index = 0;

 if (*(UINT64 *)(SystemTable + 104) != 0) {
 ConfigTable = *(UINT64 *)(SystemTable + 112);

 while (Index < *(UINT64 *)(SystemTable + 104)) {
 if (GuidCompare (SystemTable, ConfigTable)) {
 gHobList = *(UINT64 *)(ConfigTable + 16);
 return gHobList;
 }

 Index++;
 ConfigTable += 24;
 }

 //
 // GUID not found: ASSERT and fall through
 //
 DebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000E);
 DebugAssert ("e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c", 54, "!EFI_ERROR (Status)");
 Result = gHobList;
 } else {
 DebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000E);
 DebugAssert ("e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c", 54, "!EFI_ERROR (Status)");
 Result = gHobList;
 }

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

 return Result;
}

/**Translate a PCIe configuration space address to an MMIO address.

 @param[in] Address PCIe configuration space address (format:
 bus<<20 | dev<<15 | func<<12 | reg)

 @return The MMIO address for the PCIe configuration register.
**/
UINTN MmioPciExpressRead32 (
 IN UINTN Address
 )
{
 //
 // Validate that the address fits within PCIe config space range
 //
 if ((Address & ~0xFFFFFFF) != 0) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\SmmPciExpressLib\\PciExpressLib.c",
 118,
 "((Address) & ~0xfffffff) == 0"
 );
 }

 return Address + gPciExpressBaseAddr;
}

/**Return the base address of the PCIe MMIO configuration space.

 @return The base address (gPciExpressBaseAddr / qword_2748).
**/
UINT64 PciExpressBaseAddr (
 VOID
 )
{
 return gPciExpressBaseAddr;
}

/**Locate the PCD protocol from the boot services.

 @return Pointer to the PCD protocol interface.
**/
UINT64 PcdLocate (
 VOID
 )
{
 UINT64 Status;

 if (gPcdHandle != 0) {
 return gPcdHandle;
 }

 Status = (*(UINT64 ( **)(VOID *, UINT64, UINT64 *))(gBootServices + 320))(
 &gEfiDebugPortProtocolGuid,
 0,
 &gPcdHandle
 );

 if (Status < 0) {
 DebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
 DebugAssert ("e:\\hs\\MdePkg\\Library\\DxePcdLib\\DxePcdLib.c", 78, "!EFI_ERROR (Status)");
 }

 if (gPcdHandle == 0) {
 DebugAssert ("e:\\hs\\MdePkg\\Library\\DxePcdLib\\DxePcdLib.c", 79, "mPcd != ((void *) 0)");
 }

 return gPcdHandle;
}

/**Initialize UEFI boot services, runtime services, and PCD protocol.

 @param[in] ImageHandle Handle of the loaded driver image
 @param[in] SystemTable Pointer to the UEFI system table
**/
VOID EFIAPI sub_39C (
 IN EFI_HANDLE ImageHandle,
 IN EFI_SYSTEM_TABLE *SystemTable
 )
{
 UINT64 PcdProtocol;
 UINT64 Status;

 gImageHandle = (UINT64)ImageHandle;

 if (ImageHandle == NULL) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
 51,
 "gImageHandle != ((void *) 0)"
 );
 }

 gSystemTable = (UINT64)SystemTable;

 if (SystemTable == NULL) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
 57,
 "gST != ((void *) 0)"
 );
 }

 gBootServices = (UINT64)SystemTable->BootServices;

 if (gBootServices == 0) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
 63,
 "gBS != ((void *) 0)"
 );
 }

 gRuntimeServices = (UINT64)SystemTable->RuntimeServices;

 if (gRuntimeServices == 0) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
 47,
 "gRT != ((void *) 0)"
 );
 }

 HobLocate ((UINT64)SystemTable);

 PcdProtocol = PcdLocate ();
 Status = (*(UINT64 ( **)(UINT64))(PcdProtocol + 32))(5);
 gPciExpressBaseAddr = Status;
}

/**Enumerate all PCI buses and devices, reading configuration space and reporting vendor/device IDs to the BMC via IPMI.

 For each PCI function found, this function:
 1. Reads Vendor/Device ID 2. Reads PCI header type and config space 3. For each valid BAR, reads the original and modified values 4. Sends the data as IPMI messages via gIpmiTransport
**/
EFI_STATUS ProcessPci (
 VOID
 )
{
 UINT64 Status;
 UINT64 HandleIndex;
 UINT64 NumHandles;
 UINT64 *HandleBuffer;
 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
 UINT64 ResourceList;
 INT64 Segment;
 UINT16 StartBus;
 UINT16 EndBus;
 UINT16 Bus;
 UINT8 Device;
 UINT8 Function;
 UINT64 NumReported;
 UINT64 ConfigData;
 UINT64 ConfigDataOrig;
 UINT16 VendorId;
 UINT8 HeaderType;
 UINT8 ConfigSpace[64];
 UINT16 SavedCommand;
 UINT16 SavedCacheLineSize;
 INT64 BarOffset;
 UINT64 BarValue;
 UINT64 BarValueOrig;
 UINT64 BarValueMask;
 UINT64 BarDelta;
 UINT8 BarIndex;
 UINT8 BarCount;
 UINTN PciAddress;
 UINT64 IpmiTransport;
 UINT16 IpmiMsgType;
 UINT8 IpmiDataReady;
 UINT8 IpmiFunction;

 DebugPrint (0x80000000, "ProcessPci\n");

 //
 // Locate all handles that support gEfiPciRootBridgeIoProtocolGuid
 //
 NumHandles = 0;
 HandleBuffer = NULL;
 Status = (*(UINT64 ( **)(UINT64, VOID *, UINT64, UINT64 *, UINT64 **))(
 gBootServices + 312))(
 2,
 &gEfiPciRootBridgeIoProtocolGuid,
 0,
 &NumHandles,
 &HandleBuffer
 );

 NumReported = 0;

 if (Status < 0) {
 DebugPrint (0x80000000, "LocateHandle gEfiPciRootBridgeIoProtocolGuid Failed\n");
 return Status;
 }

 //
 // Iterate over all PCI root bridge handles
 //
 for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) {
 //
 // Get the PCI Root Bridge IO protocol for this handle
 //
 PciRootBridgeIo = NULL;
 Status = (*(UINT64 ( **)(UINT64, VOID *, UINT64 *))(gBootServices + 152))(
 HandleBuffer[HandleIndex],
 &gEfiPciRootBridgeIoProtocolGuid,
 (UINT64 *)&PciRootBridgeIo
 );

 if (Status < 0) {
 DebugPrint (0x80000000, "PciGetProtocolAndResource Failed\n");
 ResourceList = 0;
 goto NextHandleOrScan;
 }

 //
 // Get resource list to determine bus range
 //
 Status = (*(UINT64 ( **)(EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *, UINT64 *))(
 PciRootBridgeIo + 136))(
 PciRootBridgeIo,
 &ResourceList
 );

 if (Status == EFI_UNSUPPORTED) {
 ResourceList = 0;
 } else if (Status < 0) {
 DebugPrint (0x80000000, "PciGetProtocolAndResource Failed\n");
 ResourceList = 0;
 goto NextHandleOrScan;
 }

 //
 // If no resource list, do a full bus scan (0-255)
 //
 if (ResourceList == 0) {
 EndBus = 0;
 StartBus = 255;
 } else {
 StartBus = 0;
 EndBus = 0;
 goto ScanSegments;
 }

 //
 // Set initial bus range from resource list
 //
 Bus = StartBus;
 Device = 0;

ScanSegments:
 Bus = (UINT16)(StartBus & 0xFF);
 Device = 0;

 //
 // Scan all buses, devices, functions
 //
 while (Bus <= EndBus) {
 Function = 0;

 while (Function <= 0xFF) {
 Device = 0;

 while (Device <= 0x1F) {
 PciAddress = PCI_EXPRESS_ADDRESS (Bus, 0, Function, 0);

 //
 // Retry up to 8 times on failure
 //
 Status = PciRootBridgeIo->Pci.Read (
 PciRootBridgeIo,
 EfiPciWidthUint16,
 PciAddress,
 1,
 &VendorId
 );
 if (Status >= 0) {
 break;
 }

 DebugPrint (0x80000000, "Read VendorId Failed\n");
 Function++;
 if (Function > 7) {
 goto NextDevice;
 }
 }

 //
 // Check if device is present (VendorId != 0xFFFF)
 //
 if (VendorId == 0xFFFF) {
 if (Device == 0) {
 goto NextDevice;
 }
 Function++;
 if (Function > 7) {
 goto NextDevice;
 }
 continue;
 }

 PciAddress = PCI_EXPRESS_ADDRESS (Bus, Function, Device, 0);

 //
 // Read PCI header type
 //
 Status = PciRootBridgeIo->Pci.Read (
 PciRootBridgeIo,
 EfiPciWidthUint8,
 PciAddress + PCI_OFFSET (PCI_HEADER_TYPE),
 1,
 &HeaderType
 );
 if (Status < 0) {
 DebugPrint (0x80000000, "Read PciHeader Failed\n");
 Function++;
 if (Function > 7) {
 goto NextDevice;
 }
 continue;
 }

 //
 // Read full config space (64 bytes)
 //
 PciAddress = PCI_EXPRESS_ADDRESS (Bus, Function, Device, 0);
 Status = PciRootBridgeIo->Pci.Read (
 PciRootBridgeIo,
 EfiPciWidthUint8,
 PciAddress,
 64,
 ConfigSpace
 );
 if (Status < 0) {
 DebugPrint (0x80000000, "Read ConfigSpace Failed\n");
 Function++;
 if (Function > 7) {
 goto NextDevice;
 }
 continue;
 }

 HeaderType = ConfigSpace[0x0E];
 if ((HeaderType & 0x7F) == 0) {
 //
 // Valid PCI device (non-multifunction or function 0)
 //
 IpmiMsgType = ConfigSpace[0x11]; // Subsystem Vendor ID (or similar)
 IpmiDataReady = 1;
 BarIndex = 0;

 while (BarIndex < 6) {
 BarOffset = (INT64)(&ConfigSpace[BarIndex *4] - ConfigSpace);

 if (*(UINT32 *)&ConfigSpace[BarIndex *4] != 0 &&
 (*(UINT32 *)&ConfigSpace[BarIndex *4] & 1) == 0) {
 //
 // Non-zero, non-IO BAR - process it
 //
 DebugPrint (0x80000000, "[%X/%X/%X] ", Bus, Device, Function);

 BarValue = 0;

 //
 // 64-bit BAR if bit 2 is 0 and bit 1 is 1), else 32-bit
 //
 if ((ConfigSpace[BarIndex *4] & 2) != 0 ||
 (ConfigSpace[BarIndex *4] & 4) == 0) {
 //
 // 32-bit BAR
 //
 BarValue = *(UINT32 *)&ConfigSpace[BarIndex *4];
 IpmiDataReady = 1;
 } else {
 //
 // 64-bit BAR (read both dwords)
 //
 (*(VOID ( **)(UINT64 *, UINT8 *, UINT64))(gBootServices + 352))(
 &BarValue,
 &ConfigSpace[BarIndex *4],
 8
 );
 BarIndex++;
 IpmiDataReady = 0;
 }

 NumReported++;
 BarValueOrig = BarValue & ~0xF;
 PciAddress = PCI_EXPRESS_ADDRESS (Bus, Function, Device, 4);
 SavedCommand = ConfigSpace[0x11]; // Command register backup SavedCacheLineSize = ConfigSpace[0x11] & 0xFFFC;

 //
 // Disable decoding by clearing command register bits
 //
 PciRootBridgeIo->Pci.Write (
 PciRootBridgeIo,
 EfiPciWidthUint16,
 PciAddress + 4,
 1,
 &SavedCacheLineSize
 );

 //
 // Calculate PCI config space offset for the BAR
 //
 if (IpmiDataReady) {
 //
 // 32-bit BAR: write all 1s to measure size, restore
 //
 BarValueMask = *(UINT32 *)&ConfigSpace[BarIndex *4];
 PciRootBridgeIo->Pci.Write (
 PciRootBridgeIo,
 EfiPciWidthUint32,
 PciAddress + BarOffset,
 &BarValueMask
 );
 PciRootBridgeIo->Pci.Read (
 PciRootBridgeIo,
 EfiPciWidthUint32,
 PciAddress + BarOffset,
 &BarValueMask
 );
 PciRootBridgeIo->Pci.Write (
 PciRootBridgeIo,
 EfiPciWidthUint32,
 PciAddress + BarOffset,
 &BarValueOrig
 );
 BarValueMask = (*(UINT32 *)&ConfigSpace[BarIndex *4] & 0xFFFFFFF0) - ~(BarValueMask & 0xFFFFFFF0);
 BarDelta = BarValueMask;
 } else {
 //
 // 64-bit BAR: write all 1s to both dwords
 //
 BarValueMask = ~0ULL;
 PciRootBridgeIo->Pci.Write (
 PciRootBridgeIo,
 EfiPciWidthUint64,
 PciAddress + BarOffset,
 2,
 &BarValueMask
 );
 PciRootBridgeIo->Pci.Read (
 PciRootBridgeIo,
 EfiPciWidthUint64,
 PciAddress + BarOffset,
 2,
 &BarValueMask
 );
 PciRootBridgeIo->Pci.Write (
 PciRootBridgeIo,
 EfiPciWidthUint64,
 PciAddress + BarOffset,
 2,
 &BarValueOrig
 );
 BarDelta = 0;
 BarValueMask = (BarValueOrig & 0xFFFFFFFFFFFFFFF0) - ~(BarValueMask & 0xFFFFFFFFFFFFFFF0);
 (*(VOID ( **)(UINT64 *, UINT64 *, UINT64))(gBootServices + 352))(
 &BarDelta,
 &BarValueMask,
 8
 );
 }

 //
 // Restore command register
 //
 PciRootBridgeIo->Pci.Write (
 PciRootBridgeIo,
 EfiPciWidthUint16,
 PciAddress,
 1,
 &SavedCommand
 );

 DebugPrint (0x80000000, "%08x%08x ", BarDelta & 0xFFFFFFF0);

 //
 // Send via IPMI
 //
 IpmiMsgType = 4865;
 IpmiDataReady = 1;
 IpmiTransport = gIpmiTransport;
 IpmiFunction = 45;

 Status = (*(UINT64 ( **)(UINT64, UINT64, UINT64, UINT64, UINT16 *, INT32, CHAR8 *, CHAR8 *))(
 IpmiTransport + 16))(
 IpmiTransport,
 46,
 0,
 45,
 &IpmiMsgType,
 21,
 &ConfigSpace[0],
 &IpmiDataReady
 );

 DebugPrint (0x80000000, "%r\n", Status);
 }

 if (Function != 0) {
 IpmiFunction = 1;
 break;
 }

 NumReported = 1;
 BarIndex++;
 if (BarIndex >= 6) {
 break;
 }
 }
 }

 //
 // Handle multi-function devices
 //
 Function++;
 NumReported = 0;
 if (Function > 7) {
 goto NextDevice;
 }
 }

NextDevice:
 Device++;
 }

 if (ResourceList == 0) {
 goto NextHandleOrScan;
 }

 //
 // Process resource list to find acpi (0x79) and pnp (0x02) descriptors
 //
 while (ResourceList != 0) {
 if (*(UINT8 *)ResourceList == 121) {
 //
 // End tag
 //
 Status = 1;
 goto DoneHandle;
 }

 if (*(UINT8 *)(ResourceList + 3) == 2) {
 //
 // Type 2 descriptor (DWORD address space): extract bus range
 //
 StartBus = *(UINT16 *)(ResourceList + 14);
 EndBus = *(UINT16 *)(ResourceList + 22);
 ResourceList += 46;

 if (StartBus <= EndBus) {
 goto ScanSegments;
 }
 }

 ResourceList += 46;
 }

DoneHandle:
 HandleBuffer = NULL;
 }

 //
 // Free the handle buffer
 //
 if (HandleBuffer != NULL) {
 (*(VOID ( **)(UINT64 *))(gBootServices + 72))(HandleBuffer);
 }

 return Status;
}

/**Process SAD (System Address Decoder) and AD (Address Decoder) topology information for each CPU socket.

 For each socket:
 1. Read CPUBUSNO and CPUBUSNO1 CSR registers via gGpioCsrAccessProtocol 2. Derive combined CPUBUSNUM 3. Check CPUBUSNO_VALID 4. Walk the SAD/AD topology table and send each entry via IPMI

 @return EFI_STATUS.
**/
UINT64 ProcessSADTAD (
 VOID
 )
{
 UINT64 Status;
 UINT64 IioUds;
 UINT64 CpuCsrAccess;
 UINT8 Socket;
 UINT8 TableIndex;
 UINT8 EntryCount;
 UINT8 SadAdEntry[5];
 UINT8 BusNum;
 UINT32 CpuBusNo;
 UINT32 CpuBusNo1;
 UINT64 CpuBusNum;
 UINT32 CpuBusNoValid;
 UINT8 SavedSocket;
 UINT32 Temp;
 UINT8 Index;
 UINT8 SubIndex;

 DebugPrint (0x80000000, "ProcessSADTAD\n");

 //
 // Locate gEfiIioUdsProtocolGuid
 //
 Status = (*(UINT64 ( **)(VOID *, UINT64, UINT64 *))(gBootServices + 320))(
 &gEfiIioUdsProtocolGuid,
 0,
 &gIioUdsProtocol
 );

 if (Status < 0) {
 return DebugPrint (0x80000000, "Locate gEfiIioUdsProtocolGuid FAILED\n");
 }

 //
 // Locate gEfiCpuCsrAccessGuid
 //
 Status = (*(UINT64 ( **)(VOID *, UINT64, UINT64 *))(gBootServices + 320))(
 &gEfiCpuCsrAccessGuid,
 0,
 &gGpioCsrAccessProtocol
 );

 if (Status < 0) {
 return DebugPrint (0x80000000, "Locate gEfiCpuCsrAccessGuid FAILED\n");
 }

 //
 // Iterate over 4 possible sockets
 //
 for (Socket = 0; Socket < 4; Socket++) {
 if ((*(UINT32 *)(*(UINT64 *)gIioUdsProtocol + 2067) >> Socket) & 1) {
 //
 // Socket is present
 //

 //
 // Process SAD/AD topology table (696 entries)
 //
 TableIndex = 9 *Socket;
 for (EntryCount = 0; EntryCount < 696; EntryCount++) {
 SadAdEntry[0] = mSadAdTopologyTable[TableIndex];
 SadAdEntry[1] = mSadAdTopologyTable[TableIndex + 1];
 SadAdEntry[2] = mSadAdTopologyTable[TableIndex + 2];
 SadAdEntry[3] = mSadAdTopologyTable[TableIndex + 3];
 SadAdEntry[4] = mSadAdTopologyTable[TableIndex + 4];

 if (gSocketBusInfo[Socket][0] == 0) {
 //
 // First pass: read CPUBUSNO and CPUBUSNO1 for this socket
 //
 CpuBusNo = (*(UINT32 ( **)(UINT8, UINT32, UINT32))(gGpioCsrAccessProtocol + 8))(
 Socket,
 0,
 0x1302880C // CPUBUSNO CSR
 );
 DebugPrint (64, "Socket: %x, CPUBUSNO: %08x\n", Socket, CpuBusNo);

 CpuBusNo1 = (*(UINT32 ( **)(UINT8, UINT32, UINT32))(gGpioCsrAccessProtocol + 8))(
 Socket,
 0,
 0x13028810 // CPUBUSNO1 CSR
 );
 DebugPrint (64, "Socket: %x, CPUBUSNO1: %08x\n", Socket, CpuBusNo1);

 CpuBusNum = CpuBusNo | ((UINT64)CpuBusNo1 << 32);
 DebugPrint (64, "Socket: %x, CPUBUSNUM: %016lx\n", Socket, CpuBusNum);

 //
 // Check CPUBUSNO_VALID
 //
 CpuBusNoValid = (*(UINT32 ( **)(UINT8, UINT32, UINT32))(gGpioCsrAccessProtocol + 8))(
 Socket,
 0,
 0x13028814 // CPUBUSNO_VALID CSR
 );
 DebugPrint (64, "Socket: %x, CPUBUSNO_VALID: %08x\n", Socket, CpuBusNoValid);

 if (CpuBusNoValid >= 0x80000000) {
 //
 // Bus information valid: store it
 //
 *(UINT64 *)&gSocketBusInfo[Socket][1] = CpuBusNum;
 } else {
 //
 // No bus information for this socket; use default
 //
 DebugPrint (0x80000000, "No Bus Information in Socket %x.", Socket);
 *(UINT64 *)&gSocketBusInfo[Socket][1] = 0x06050403020100ULL;
 }

 gSocketBusInfo[Socket][0] = 1;
 }

 DebugPrint (0x80000000, "%x %x %x ", Socket, SadAdEntry, Temp);

 //
 // Calculate the full PCI address using the socket's bus number
 //
 BusNum = gSocketBusInfo[Socket][1 + SadAdEntry[4]];
 Temp = (*(UINT32 (*)(UINTN))MmioPciExpressRead32)(
 SadAdEntry[3] & 0xFFF
 | ((SadAdEntry[2] & 7
 | (8 * (SadAdEntry[1] & 0x1F | (32 *SadAdEntry[0])))
 ) << 12)
 | ((UINTN)BusNum << 20)
 );

 DebugPrint (0x80000000, "%x %x %x ", Socket, SadAdEntry, Temp);

 //
 // Send via IPMI
 //
 {
 UINT16 IpmiMsgType = 2304;
 UINT8 IpmiDataReady = 1;
 INT32 IpmiFunction = 11;
 UINT8 IpmiSubFunction = 45;

 Status = (*(UINT64 ( **)(UINT64, UINT64, UINT64, UINT64, UINT16 *, INT32, CHAR8 *, CHAR8 *))(
 gIpmiTransport + 16))(
 gIpmiTransport,
 46,
 0,
 45,
 &IpmiMsgType,
 IpmiFunction,
 &SavedSocket,
 &IpmiDataReady
 );
 }

 DebugPrint (0x80000000, "%r\n", Status);
 TableIndex += 5;
 }

 //
 // Now process CSR-based DIMM/DDRIO data for the 24 VCU/MC channels
 //
 for (Index = 0; Index < 24; Index++) {
 for (SubIndex = 0; SubIndex < 2; SubIndex++) {
 //
 // Write selector register
 //
 (*(VOID ( **)(UINT8, UINT8, UINT32, UINT32))(gGpioCsrAccessProtocol + 48))(
 Socket,
 SubIndex,
 0x6003028, // Some CSR selector
 (4 * (Index & 0x1F)) | 2
 );

 //
 // Read the data register
 //
 Temp = (*(UINT32 ( **)(UINT8, UINT8, UINT32))(gGpioCsrAccessProtocol + 40))(
 Socket,
 SubIndex,
 0x6003028
 );

 DebugPrint (0x80000000, "%x %x %x ", Socket, SubIndex ? 2392232 : 2359464, Temp);

 //
 // Send via IPMI
 //
 {
 UINT16 IpmiMsgType = 2304;
 UINT8 IpmiDataReady = 1;
 INT32 IpmiFunction = 11;
 UINT8 IpmiSubFunction = 45;

 Status = (*(UINT64 ( **)(UINT64, UINT64, UINT64, UINT64, UINT16 *, INT32, CHAR8 *, CHAR8 *))(
 gIpmiTransport + 16))(
 gIpmiTransport,
 46,
 0,
 45,
 &IpmiMsgType,
 IpmiFunction,
 &SavedSocket,
 &IpmiDataReady
 );
 }
 }
 }
 }
 }

 return Status;
}

/**Timer notification function called when the collect-post-data timer fires.
 Gathers PCI bus data and SAD/AD topology, then transmits via IPMI.

 @param[in] Event The event being signaled
 @param[in] Context Event context (not used)
**/
VOID EFIAPI CollectPOSTData (
 IN EFI_EVENT Event,
 IN VOID *Context
 )
{
 //
 // Register the boot services timer event
 //
 (*(VOID ( **)(UINT64))(gBootServices + 112))(Event);

 DebugPrint (0x80000000, "CollectPOSTData Processing\n");

 //
 // Ensure IPMI transport protocol is available
 //
 if (gIpmiTransport == 0) {
 if ((*(UINT64 ( **)(VOID *, UINT64, UINT64 *))(gBootServices + 320))(
 &gIpmiTransportProtocolGuid,
 0,
 &gIpmiTransport
 ) < 0) {
 DebugPrint (0x80000000, "Locate gIpmiTransport Failed\n");
 return;
 }
 }

 //
 // Collect PCI data and SAD/AD data
 //
 ProcessPci ();
 ProcessSADTAD ();
}

/**Process a UEFI configuration table entry for HOB list lookup.
 This is a simplified wrapper used by HobLocate.

 @param[in] SystemTable Pointer to EFI system table

 @return TRUE if GUID matches the HOB GUID, FALSE otherwise.
**/
BOOLEAN EFIAPI sub_1148 (
 IN UINT64 SystemTable,
 IN UINT64 ConfigEntry
 )
{
 return GuidCompare (
 (EFI_GUID *)&mHobGuid,
 (EFI_GUID *)ConfigEntry
 );
}

/**UEFI Driver Entry Point.

 Registers a timer event that collects PCI configuration and SAD/AD topology data and sends it to the BMC via IPMI.

 @param[in] ImageHandle The firmware allocated handle for the EFI image
 @param[in] SystemTable A pointer to the EFI system table

 @return EFI_SUCCESS The entry point executed successfully.
 @return EFI_ABORTED The entry point could not register the timer event.
**/
EFI_STATUS EFIAPI ModuleEntryPoint (
 IN EFI_HANDLE ImageHandle,
 IN EFI_SYSTEM_TABLE *SystemTable
 )
{
 EFI_STATUS Status;
 VOID *TimerEvent;
 UINT64 HobGuidEntry;

 //
 // Initialize global variables (BootServices, SystemTable, etc.)
 //
 sub_39C (ImageHandle, SystemTable);

 //
 // Create a timer event to collect POST data
 //
 Status = (*(EFI_STATUS ( **)(UINT64, UINT64, EFI_EVENT *))(gBootServices + 80))(
 512, // TimerPeriodic 16, // NotifyTpl CollectPOSTData
 );

 if ((Status & 0x8000000000000000) == 0) {
 return (*(EFI_STATUS ( **)(VOID *, UINT64, VOID *))(gBootServices + 168))(
 &mHobGuid,
 HobGuidEntry,
 &TimerEvent
 );
 }

 return Status;
}