SMM driver that handles FPGA (Field Programmable Gate Array) error status monitoring and correction for the Intel Purley platform. This module runs in System Management Mode (SMM), monitors FPGA error registers via MMIO, and performs error acknowledgment and system reset when critical FPGA errors are detected. Also integrates with the MpSyncData library for multi-processor synchronization.
0x280 - 0x1E80 (0x1C00 bytes .text, 44 functions)
| Address | Name | Purpose |
|---|---|---|
| 0x514 | _ModuleEntryPoint | SMM module entry point, initializes error handler |
| 0x5C0 | sub_5C0 | Auto-generated UEFI init: saves ImageHandle, SystemTable, BootServices, RuntimeServices, gSmst |
| 0xEAC | sub_EAC | Main FPGA error handler registration -- locates protocols, registers callbacks |
| 0xDFC | sub_DFC | Error status collection callback -- reads FPGA error status registers per socket |
| 0xD48 | sub_D48 | Error polling function -- checks FPGA error bits and triggers correction |
| 0xCB4 | sub_CB4 | Fatal error handler -- logs error, writes GPIO, triggers warm reset via 0xCF9 |
| 0xC90 | sub_C90 | Error status query -- checks if a specific error bit is set |
| 0xB48 | sub_B48 | Error clear function -- clears FPGA error status registers |
| 0xBF0 | sub_BF0 | Error buffer clear function -- resets FPGA error buffer to zero |
| 0xB38 | sub_B38 | FPGA error presence check -- returns whether any FPGA error is active |
| 0xA30 | sub_A30 | Error logging function -- writes error info via MpSyncData protocol |
| 0x1580 | sub_1580 | MpSyncData library initialization -- sets up CPU topology tracking |
_ModuleEntryPoint: Standard UEFI SMM driver entry point. Saves boot services, locates protocols, registers FPGA error callbacks, and initializes error monitoring.sub_5C0: UEFI boot services initialization. Saves ImageHandle, SystemTable, gBS, gRT, gSmst pointers from the UEFI system table. Generated by AutoGen.csub_280: SetJump implementation -- saves CPU register context (GP registers, XMM, MXCSR) into a jump buffer structure for non-local goto. Used to protect the entry point during error handling.sub_320: LongJump implementation -- restores CPU context from a jump buffer and longjmps back. Used to recover from errors during initialization.sub_11E0: SetJump validation wrapper -- validates jump buffer alignment (8-byte aligned) and non-null.sub_3A0: ZeroMem -- zero-fills memory buffers (aligned + tail).sub_12FC: ZeroMem wrapper with validation -- validates buffer non-null and length bounds.sub_430: RDTSC wrapper -- reads timestamp counter.sub_420: _mm_pause wrapper -- CPU spin-loop hint.sub_4A0: _enable() -- enable interrupts.sub_4B0: _disable() -- disable interrupts.sub_4C0: __getcallerseflags() -- read EFLAGS.sub_440: __cpuid wrapper (leaf-based) -- CPUID with EAX input, returns EAX/EBX/ECX/EDX.sub_470: __cpuid wrapper (function-based) -- CPUID with query type input.sub_128C: 64-bit MMIO read -- reads a QWORD from an MMIO address with alignment check.sub_12BC: 64-bit MMIO write -- writes a QWORD to an MMIO address with alignment check.sub_1228: 16-bit MMIO read -- reads a WORD from IO address with alignment check.sub_1258: 16-bit MMIO write (constant 0x500) -- writes 0x500 to a WORD MMIO address.sub_1D54: __indword -- reads a DWORD from an I/O port.sub_1D24: Unaligned read -- reads a QWORD from potentially unaligned address.sub_1544: PCI Express MMIO address translation -- validates PCIe address (upper bits must be zero) and adds the MMIO base from qword_2E68.sub_143C: MMIO read via PciRootBridge protocol -- reads a DWORD from PCI config space (Bus 0, Dev 0, Func 0, Reg 0xF8000 = 1015808) using protocol at qword_2E58.sub_1C00: PCH info query -- reads LPC device ID to determine PCH SKU, validates it (checks for supported PCH), returns PCH-specific data from unk_2B00.sub_1BA0: GPIO value read -- reads a GPIO value via MMIO (address 0xFD000148 + shifted offset) using PCH info.sub_13D4: Free pool -- frees the allocated buffer at qword_23FF0. Uses either InternalSmmFreePool or gBS->FreePool depending on context.sub_1360: MM RAM range check -- checks if an address is within any registered SMM memory region.sub_13A4: SMM allocate pool -- allocates memory via the SMM memory allocator.sub_10C8: Lazy-loads debug logging protocol at GUID unk_2A60 into qword_2E50.sub_1C98: Lazy-loads PCD protocol at GUID unk_2A50 into qword_2EA0.sub_146C: Lazy-loads HOB list from SystemTable's HOB list pointer.sub_1D84: GUID comparison -- compares two GUID values (16 bytes each, split as two QWORDs). Used to find matching HOB entries.sub_1990: CPU topology discovery -- uses CPUID to determine thread bits and core bits for the CPU topology.sub_1DF4: Spinlock initialization -- sets a spinlock QWORD to 1 (unlocked).sub_11A0: Debug logging -- calls debug print protocol if available (lazy-loaded).sub_1118: Conditional debug print with platform check -- checks CMOS (I/O 0x70/0x71) for platform type and error severity level before printing.| Address | Size | Name | Purpose |
|---|---|---|---|
| 0x2E28 | 8 | SystemTable | Saved UEFI SystemTable pointer |
| 0x2E30 | 8 | BootServices | Saved gBS pointer |
| 0x2E38 | 8 | ImageHandle | Saved driver image handle |
| 0x2E40 | 8 | RuntimeServices | Saved gRT pointer |
| 0x2E48 | 8 | qword_2E48 | SMM System Table pointer (gSmst) |
| 0x2E50 | 8 | qword_2E50 | Debug print protocol (lazy-loaded) |
| 0x2E58 | 8 | qword_2E58 | PciRootBridge protocol |
| 0x2E60 | 8 | qword_2E60 | HOB list pointer (lazy-loaded) |
| 0x2E68 | 8 | qword_2E68 | PCI Express MMIO base address |
| 0x2E78 | 8 | qword_2E78 | MpSyncData protocol pointer |
| 0x2E80 | 8 | qword_2E80 | MpSyncData service protocol |
| 0x2E88 | 8 | qword_2E88 | MpSyncData CPU info protocol |
| 0x2E90 | 8 | unk_2E90 | MpSyncData second protocol |
| 0x2E98 | 8 | qword_2E98 | FPGA MMIO protocol (PciRootBridge IO access) |
| 0x2EA0 | 8 | qword_2EA0 | PCD protocol pointer |
| 0x2FA8 | 8 | qword_2FA8 | Module return status (initialized to 0x8000000000000001) |
| 0x24050 | 8 | qword_24050 | FPGA state structure pointer (from MmPciBase protocol) |
| 0x24058 | 8 | qword_24058 | FPGA protocol interface for callback registration |
| 0x24000 | 8 | qword_24000 | MmPciBase protocol instance |
| 0x23FF0 | 8 | qword_23FF0 | SMM memory allocation buffer |
| 0x23FE8 | 8 | qword_23FE8 | SMM descriptor count |
| 0x23FE0 | 4 | dword_23FE0 | Topology: socket mask (1 << socket) |
| 0x7FD8 | 4 | dword_7FD8 | Topology: thread mask (1 << (core + socket)) |
| 0x24020 | 8 | psub_B38 | Function pointer: FPGA error presence check |
| 0x24028 | 8 | psub_B48 | Function pointer: FPGA error clear |
| 0x24030 | 8 | psub_BF0 | Function pointer: FPGA error buffer clear |
| 0x24038 | 8 | psub_C90 | Function pointer: FPGA error status query |
| 0x24040 | 8 | psub_CB4 | Function pointer: FPGA fatal error handler |
| 0x24048 | 8 | psub_D48 | Function pointer: FPGA error poll handler |
| 0x24060 | 16 | buf_ | FPGA error status buffer (4 x DWORD, one per socket) |
| 0x2EB0 | 248 | unk_2EB0 | SetJump buffer (248 bytes for CPU context save) |
| Address | Purpose |
|---|---|
| 0x7FC8 | unk_7FC8 - Spinlock |
| 0x7FD0 | unk_7FD0 - Spinlock |
| 0x7FE0 | byte_7FE0 - Per-CPU active state flags |
| 0x87E0 | qword_87E0 - Per-CPU APIC ID mapping table |
| 0xC7E0 | dword_C7E0 - Per-CPU initial APIC ID table |
| 0xE7E0 | byte_E7E0 - Per-CPU state byte table |
| 0x2FC0 | unk_2FC0 - Per-CPU spinlock array (0x5000 bytes, 40 bytes per entry) |
| 0x2FC8 | byte_2FC8 - Per-CPU active flag array (0x5000 bytes, 40 bytes stride) |
| Address | Data | Purpose |
|---|---|---|
| 0x1FD0 | 6 x WORD offsets | FPGA register offset table (0x394, 0x39C, 0x3A4, 0x3AC, 0x3B4, 0x3BC) |
| 0x1FE0 | 6 x WORD offsets | FPGA register offset table group 2 (0x390, 0x398, 0x3A0, 0x3A8, 0x3B0, 0x3B8) |
| Address | GUID Binary | Likely Protocol |
|---|---|---|
| 0x2A00 | 3BA7E14B-176D-4B2A-948A-C86FB001943C | MmPciBase protocol (get FPGA base address) |
| 0x2A10 | 86B091ED-1463-43B5-82A1-2C8B83CB8917 | MmPciBase FPGA cfg protocol |
| 0x2A20 | 0067835F-9A50-433A-8CBB-852078197814 | MpSyncData protocol |
| 0x2A70 | ED32D533-99E6-4209-9CC0-2D72CDD998A7 | FPGA MMIO access protocol |
| 0x2A80 | 1D202CAB-C8AB-4D5C-94F7-3CFCC0D3D335 | MpSyncData CPU info protocol |
| 0x2A90 | 6820ABD4-A292-4817-9147-D91DC83542 | PCI config protocol |
| 0x2AB0 | 47B7FA8C-F4BD-4AF6-8200-333086F0D2C8 | FPGA callback registration protocol |
| 0x2AC0 | 7739F24C-93D7-11D4-9A3A-0090273FC14D | HOB GUID (gEfiHobMemoryAllocModuleGuid) |
| 0x2AC8 | 0090273FC14D... | Part of HOB entry GUID |
| Address | GUID Binary | Likely Protocol |
|---|---|---|
| 0x2AD0 | F4CCBFB7-F6E0-47FD-9DD4-10A8F150C191 | MpSyncData protocol |
| 0x2A40 | A7CED760-C71C-4E1A-ACB1-89604D5216CB | MpSyncData protocol |
| 0x2A50 | 11B34006-D85B-4D0A-A290-D5A571310EF7 | PCD protocol |
| Address | GUID Binary | Purpose |
|---|---|---|
| 0x2A20 | 0067835F-9A50-433A-8CBB-852078197814 | MpSyncData protocol |
| 0x2A80 | 1D202CAB-C8AB-4D5C-94F7-3CFCC0D3D335 | MpSyncData CPU info |
The FPGA state object is obtained from MmPciBase protocol. Key offsets:
Protocol with a function at offset 0 that takes an array of 6 function pointers and a parameter:
typedef struct {
void (*Register)(FPGA_CALLBACK_ARRAY *Callbacks, UINT8 Param);
} FPGA_CALLBACK_PROTOCOL;
The callback array has 6 entries:
The module allocates large tables indexed by (socket, core, thread):
Offset calculation: idx = thread + (core + 448 * socket) * 64
_ModuleEntryPoint (0x514)
-> sub_5C0 (0x5C0): Save boot services, gBS, gRT, gSmst
-> sub_280 (0x280): SetJump to protect against errors
-> sub_EAC (0xEAC): Main FPGA error handler setup
-> LocateProtocol (MmPciBase) -> gets FPGA base -> qword_24050
-> LocateProtocol (PCI config) -> qword_24058
-> LocateProtocol (MpSyncData) -> qword_2E20
-> Register protocol callbacks:
RegisterCallback( {sub_B38, sub_B48, sub_BF0, sub_C90, sub_CB4, sub_D48}, 3)
-> sub_11E0: Validate SetJump buffer
-> sub_320: LongJump back if error occurred
sub_DFC (0xDFC) - called from FPCA callback framework
-> For each of 4 sockets:
-> Check if bit N is set in FPGA state byte (+22)
-> Read FPGA error register via MMIO protocol at qword_2E98
-> Store status in buf_ array
sub_D48 (0xD48) - poll handler
-> For each active socket:
-> Check error register at offset +16400 (error pending)
-> If pending: call sub_A30 to log, set output flag
-> Check register at offset +968 (secondary error)
-> If pending: call sub_A30 to log, set output flag
sub_CB4 (0xCB4) - fatal FPGA error handler -> Read GPIO value via sub_1BA0 (MMIO 0xFD000148 + shift) -> Write GPIO output bit -> __outbyte(0xCF9, 2): Reset CPU -> __outbyte(0xCF9, 6): Full system reset -> Infinite loop
sub_1580 (0x1580) - initialize CPU sync data -> LocateProtocol (MpSyncData) -> LocateProtocol (MpSyncData second) -> LocateProtocol (MpSyncData CPU info) -> sub_1990: Get CPU topology (thread_bits, core_bits) -> Initialize per-CPU data tables (spinlocks, flags, APIC IDs) -> Enumerate all CPUs via MpSyncData CPU info protocol
Source file: PurleyPlatPkg/Ras/Smm/ErrHandling/FpgaErrorHandler/FpgaErrorHandler.c -- SMM driver for FPGA error handling on Intel Purley platform.
Multi-socket support: All error handling iterates over sockets 0-3, using a bitmask at FPGA state byte +22 to determine which sockets are populated.
FPGA Register Layout: The FPGA error status registers are at offsets 0x390-0x3BC in the FPGA PCI config space. Two sets of 6 WORD-sized registers (set1: 0x394/0x39C/0x3A4/0x3AC/0x3B4/0x3BC, set2: 0x390/0x398/0x3A0/0x3A8/0x3B0/0x3B8).
Error severity levels: The callbacks are registered with parameter "3", and the sub_1118 debug print function checks the platform type via CMOS to control which error levels get logged.
CMOS-based debug control: sub_1118 reads CMOS offset 0x4C (via I/O ports 0x70/0x71) to determine platform type and control error message output.
Warm reset signaling: Fatal errors trigger a warm reset via the standard 0xCF9 reset port (write 2 then 6). The GPIO register at 0xFD000148 is used to signal the error cause to the hardware.
CPU topology: sub_1990 uses CPUID leaf 0xB (Extended Topology) to discover thread bits per core and core bits per package, supporting up to 4 sockets, ~448 cores per socket, and 64 threads per core.
SetJump protection: The entry point uses SetJump/LongJump (sub_280/sub_320) to protect against crashes during initialization -- if sub_EAC fails (longjmp called), the module continues gracefully.