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