# PlatformErrorHandler Module

## Overview

SMM platform error handler for Intel Purley platform. This DXE driver runs in SMM to handle platform RAS (Reliability, Availability, Serviceability) errors. It initializes EMCA (Enhanced Machine Check Architecture) platform hooks, populates RAS topology structures (SMBIOS Type 16/17), configures FPGA error handling policies from UEFI setup variables, registers SMM software dispatch callbacks for error notification, and manages per-socket error tracking via linked callbacks.

Source file: `PurleyPlatPkg/Ras/Smm/ErrHandling/PlatformErrorHandler/PlatformErrorHandler.c`

## Address Range

0x300 - 0x5480 (83 functions, 5180 bytes .text)

## Module State: Global Variables

Note: qword_ prefix variables are in the .data segment (0x6880-0x2AF00).

| Address | Name | Purpose |
|---------|------|---------|
| 0x72D8 | qword_72D8 | Module status/result code (initialized to 0x8000000000000001 = EFI_NOT_FOUND?) |
| 0x72E0 | i | Linked list head for error source registrations (fwd callbacks) |
| 0x72E8 | i_1 | Tail pointer for error source linked list |
| 0x72F0 | i_0 | Linked list head for error notification callbacks |
| 0x72F8 | qword_72F8 | Tail pointer for notification callback list |
| 0x7300 | SystemTable | EFI System Table pointer |
| 0x7308 | BootServices | EFI Boot Services table pointer (gBS) |
| 0x7310 | qword_7310 | ImageHandle |
| 0x7318 | qword_7318 | Runtime Services table pointer (gRT) |
| 0x7320 | qword_7320 | SMM System Table 2 pointer (gSmst) |
| 0x7368 | qword_7368 | EMCA Platform Protocol interface pointer (GUID {F4CCBFB7-F6E0-47FD-9DD4-10A8F150C191}) |
| 0x7370 | qword_7370 | SMM System Table pointer (from EMCA protocol) |
| 0x7378 | qword_7378 | SMM MC Bank Protocol |
| 0x7380 | qword_7380 | Platform Info Policy (from EMCA, offset 1780 has boot mode info) |
| 0x7388 | qword_7388 | MC Bank list table (array of 32-byte entries per bank, indexed by socket) |
| 0x7390 | n6 | Number of MC banks (per sub_1AB0) |
| 0x7398 | qword_7398 | Error policy table |
| 0x73A0 | qword_73A0 | MC Bank enable/control table |
| 0x73A8 | n2 | Number of sockets? |
| 0x73B0 | qword_73B0 | MSR MCG_STATUS (0x179) cached value |
| 0x73B8 | qword_73B8 | gDS (EFI_DRIVER_SERVICES table) |
| 0x73C0 | unk_73C0 | SMM IPMI Transport Protocol interface pointer |
| 0x73C8 | unk_73C8 | SMM Variable Protocol interface pointer |
| 0x73D0 | qword_73D0 | SMM Variable Protocol (setup lib) |
| 0x73D8 | byte_73D8 | Is in SMM context flag (from EMCA probe) |
| 0x73E0 | qword_73E0 | EMCA Platform Protocol (setup lib usage) |
| 0x7400 | qword_7400 | SMM Communication buffer pointer (from SMM Comm protocol) |
| 0x7408 | qword_7408 | Setup variable data pointer (VariableGet) |
| 0x7410 | qword_7410 | Setup variable data pointer (HOB) |
| 0x7418 | qword_7418 | Runtime Memory descriptor? |
| 0x7420 | qword_7420 | Accumulated setup variables data buffer (size 0x2A3B) |

## Key Functions

| Address | Name | Purpose |
|---------|------|---------|
| 0x5CC | _ModuleEntryPoint | DXE driver entry point; calls library constructors and main init |
| 0x594 | sub_594 | Alternate entry point (SMM module entry via SW dispatch?) |
| 0x30D8 | sub_30D8 | AutoGen library constructor dispatcher (calls 12 library constructors) |
| 0x3550 | sub_3550 | Main driver initialization logic |
| 0x34E0 | sub_34E0 | Fallback error init (called if main init fails) |
| 0x3088 | sub_3088 | Status coalescing + set event (restore event to -1) |

## Entry Points (Public API)

- 0x5CC `_ModuleEntryPoint`: Standard UEFI DXE driver entry. Calls sub_30D8 for library constructor chain, then sub_3550 for main initialization. If main init returns error (<0), calls sub_34E0 fallback.

- 0x594 `sub_594`: SMM module entry path (registered via SMM SW dispatch or standalone entry). Calls sub_F0C (gSmst init) then sub_34E0.

## Internal Helpers

### Library Constructors (called from sub_30D8 chain):

| Address | Name | Source |
|---------|------|--------|
| 0x6BC | sub_6BC | UefiBootServicesTableLib constructor - sets gImageHandle, gST, gBS |
| 0x758 | sub_758 | SmmServicesTableLib constructor - sets gSmst |
| 0x794 | sub_794 | DxeHobLib constructor |
| 0xC28 | sub_C28 | BaseLib constructor |
| 0xEA8 | sub_EA8 | SmmMemoryAllocationLib constructor |
| 0xF0C | sub_F0C | Thunk to gSmst init |
| 0x1094 | sub_1094 | BaseSynchronizationLib constructor |
| 0x10FC | sub_10FC | IoLib constructor |
| 0x13CC | sub_13CC | EmcaPlatformHooksLib constructor |
| 0x1B6C | sub_1B6C | EmcaPlatformHooksLib main constructor - locates EMCA protocol, MC banks, SMM MC Bank protocol |
| 0x248C | sub_248C | DxeServicesTableLib constructor - locates gDS |
| 0x25A8 | sub_25A8 | SmmLnvSendIpmiCmdLib + SmmVariableLib constructors - locates IPMI transport + SMM variable protocols |

### RAS Topology & Setup Variable Handlers:

| Address | Name | Purpose |
|---------|------|---------|
| 0x3970 | sub_3970 | Main initialization body: reads RAS topology from HOB, reads Setup variables, registers SMM SW dispatch handler, calls EMCA platform hooks init, sends IPMI commands |
| 0x4700 | sub_4700 | PopulateRasTopologyStructure - Reads HOB to build RAS topology (socket/channel/dimm counts, SMBIOS Type16/17 data) |
| 0x3E0C | sub_3E0C | Read setup variables from HOB - queries FPGA and platform config |
| 0x3D4C | sub_3D4C | Read single setup variable (size 30) and initialize error policy defaults |
| 0x5354 | sub_5354 | Apply error policy based on setup variable values (handles error modes 3/4/5) |
| 0x5390 | sub_5390 | Iterate SMM variable protocol to read setup data into per-socket tables |
| 0x2690 | sub_2690 | DxeSetupLib implementation - reads all platform setup variables into accumulated buffer |

### Error Policy Initialization:

| Address | Name | Purpose |
|---------|------|---------|
| 0x3890 | sub_3890 | EmcaPlatformHooks init - allocates per-socket MC bank structures (512 banks x 216 bytes each) |
| 0x1AB0 | sub_1AB0 | Initialize MC bank list for detected socket configuration (6 banks x 2 sockets or 4-6 banks) |
| 0x29A4 | sub_29A4 | Read OemRasLib setup variable (offset 145 = bit9?), configure error enable mask (clear bit 4) |
| 0x23EC | sub_23EC | Read MSR 0x179 (MCG_STATUS) to detect error state; check for MC status bits from EMCA platform protocol callback |
| 0x2AE0 | sub_2AE0 | FPGA Configuration HOB management - reads from HOB or creates a new FPGA config structure; merges setup variable data into HOB |

### Callback Registration (Public API for notification consumers):

| Address | Name | Purpose |
|---------|------|---------|
| 0x36B8 | sub_36B8 | RegisterErrorNotificationCallback - Allocates a 24-byte callback node, inserts into priority-sorted linked list (head at qword_72F0, tail at qword_72F8). Sorted by priority byte at offset +16. Returns EFI_INVALID_PARAMETER if a1 is NULL. |
| 0x35C8 | sub_35C8 | RegisterErrorSource - Allocates a 16-byte node, inserts into source linked list (head at qword_72E0). Returns EFI_INVALID_PARAMETER if a1 is NULL. |

### Callback Dispatch:

| Address | Name | Purpose |
|---------|------|---------|
| 0x3810 | sub_3810 | DispatchNotification - Iterates all registered notification callbacks (head at qword_72F0), calls each with the context pointer |
| 0x3844 | sub_3844 | DispatchWithEarlyOut - Same as above but with a "stop" flag (char at [rbp+0x10]), stops when flag set |

### EMCA MC Bank Access (from emcaplatformhookslib.c):

| Address | Name | Purpose |
|---------|------|---------|
| 0x1D8C | sub_1D8C | Get McBank status + 1 (offset 0 from 32-byte entry) |
| 0x1DE4 | sub_1DE4 | Get McBank status + 2 (offset 4 from 32-byte entry) |
| 0x1E3C | sub_1E3C | Get McBank status + 3 (offset 8 from 32-byte entry) |

### IO/Port Access:

| Address | Name | Purpose |
|---------|------|---------|
| 0x1360 | sub_1360 | Read CPU package ID via IO ports (uses 0x0CF8/0x0CFC PCI config mechanism) |
| 0x510 | sub_510 | IoWrite32 (port IO write) |
| 0x540 | sub_540 | IoRead32 (port IO read) |

### Memory Helpers:

| Address | Name | Source |
|---------|------|---------|
| 0x9CC | sub_9CC | SetMem (memset) |
| 0xA3C | sub_A3C | ZeroMem |
| 0x920 | sub_920 | CopyMem |
| 0xDF8 | sub_DF8 | AllocatePool (via Smst->AllocatePool) |
| 0x130C | sub_130C | InitializeSpinLock |
| 0x3020 | sub_3020 | CompareGuid |

### ASSERT/Debug Helpers:

| Address | Name | Purpose |
|---------|------|---------|
| 0xB10 | sub_B10 | DebugPrint - prints formatted debug message |
| 0xB9C | sub_B9C | DebugAssert - ASSERT failure handler |
| 0xC00 | sub_C00 | Get debug error level flag |
| 0xC0C | sub_C0C | Check if debug is enabled |
| 0xC18 | sub_C18 | Check debug level |

## Data Structures

### MC Bank Entry (32 bytes per bank, indexed by socket number):
```
+0x00: uint32_t bank_status_1
+0x04: uint32_t bank_status_2  
+0x08: uint32_t bank_status_3
+...  : (rest 20 bytes)
```
Referenced via qword_7388, accessed by sub_1D8C, sub_1DE4, sub_1E3C with 32 * socket_index offset.

### Notification Callback Node (24 bytes):
```
+0x00: uint64_t callback_function_pointer
+0x08: uint64_t next_node_ptr (linked list)
+0x10: uint8_t  priority (lower = higher priority, inserted sorted)
```
Allocated by sub_36B8, stored in priority-sorted linked list. Head: qword_72F0, Tail: qword_72F8.

### Error Source Node (16 bytes):
```
+0x00: uint64_t source_data
+0x08: uint64_t next_node_ptr
```
Allocated by sub_35C8, stored in linked list. Head: qword_72E0.

### FPGA Configuration HOB Structure (38 bytes):
Used by sub_2AE0 for FPGA config. Various bitfields for error enable/disable.

### Setup Variable Table (at off_70D0, 3-word entries):
Triplets of (GUID_ptr, Name_ptr, Size) for each platform setup variable read by sub_2690:
- SocketIioConfig (0x1A0C bytes from 0x68B0/GUID)
- SocketCommonRcConfig (0xE0 bytes)
- SocketMpLinkConfig (0x155 bytes)
- SocketMemoryConfig (0x202 bytes)
- SocketPowerManagementConfig (0x1CE bytes)
- SocketProcessorCoreConfig (0x12D bytes)
- IntelSetup (0x2A4 bytes)
- PchRcConfiguration (0x5D7 bytes)
- MeRcConfiguration
- FpgaSocketConfig

## GUIDs Used

### Protocol GUIDs:
| Address | GUID | Protocol |
|---------|------|----------|
| 0x6940 | {6820ABD4-A292-4817-9147-D91DC8C53542} | gEfiSmmSwDispatch2ProtocolGuid |
| 0x6950 | {86B091ED-1463-43B5-82A1-2C8B83CB8917} | gEfiSmmCommunicationProtocolGuid? |
| 0x6970 | {3FDDA605-A76E-4F46-AD29-12F4531B3D08} | gEfiSmmBase2ProtocolGuid |
| 0x69C0 | {F4CCBFB7-F6E0-47FD-9DD4-10A8F150C191} | mLnvPurleySmmProtocolGuid (EMCA Platform Protocol) |
| 0x69D0 | {ED32D533-99E6-4209-9CC0-2D72CDD998A7} | gEfiSmmVariableProtocolGuid |
| 0x6A10 | {A7CED760-C71C-4E1A-ACB1-89604D5216CB} | gEfiSmmMcBankProtocolGuid? |
| 0x6990 | {0067835F-9A50-433A-8CBB-852078197814} | EmcaSmmVariableProtocolGuid? |
| 0x68A0 | {05AD34BA-6F02-4214-952E-4DA0398E2BB9} | gEfiDxeServicesTableGuid |
| 0x6A40 | {1DBD1503-0A60-4230-AAA3-8016D8C3DE2F} | gEfiSmmIpmiTransportProtocolGuid |
| 0x6930 | {D759C710-49EA-4D26-9F7C-DE1064876E2F} | FPGA Configuration HOB GUID |

## Calling Patterns

### Driver Initialization Flow:
1. `_ModuleEntryPoint` (0x5CC)
2. `sub_30D8` (0x30D8) - library constructor chain (12 constructors)
3. `sub_3550` (0x3550) - main init
4. `sub_3970` (0x3970) - core logic:
   a. Read RAS topology from HOB via `sub_4700`
   b. Read setup variables via `sub_3E0C` / `sub_3D4C`
   c. Apply error policy from setup via `sub_5354`
   d. Initialize OEM RAS via `sub_29A4`
   e. Save back setup via `sub_F0C` / `sub_2998`
   f. Init EMCA platform hooks via `sub_3890`
   g. Register SMM SW dispatch handlers (sub_252C + sub_25A8)
   h. Register SMM communication protocol

### Error Notification Flow:
1. SMI received -> SMM SW Dispatch handler fires
2. Dispatches to `sub_3810` / `sub_3844` which iterate registered callbacks
3. Callbacks registered via `sub_36B8` (notification) or `sub_35C8` (source)

### Setup Variable Read Flow:
1. `sub_2690` reads all platform variables
2. Reads variables described by table at `off_70D0`
3. Either via gRT->GetVariable (DXE) or SmmVariableProtocol (SMM)
4. Accumulates into `qword_7420` buffer (size 0x2A3B)

## Dependencies

### Consumed (this module calls):
- **gBS (Boot Services)**: LocateProtocol, FreePool, etc.
- **gSmst (SMM System Table 2)**: AllocatePool, LocateProtocol, SMM SW Dispatch2 register
- **gRT (Runtime Services)**: GetVariable (for setup variables)
- **EMCA Platform Protocol** ({F4CCBFB7-F6E0-47FD-9DD4-10A8F150C191}): GetSmmSystemTable, IsInSmm, ReadMca
- **SMM MC Bank Protocol**: MC bank access
- **SMM IPMI Transport Protocol** ({1DBD1503-0A60-4230-AAA3-8016D8C3DE2F}): Send IPMI commands
- **SMM Variable Protocol** ({ED32D533-99E6-4209-9CC0-2D72CDD998A7}): Setup variable access in SMM
- **SMM SW Dispatch2 Protocol** ({6820ABD4-A292-4817-9147-D91DC8C53542}): Register SMI handlers

### Consumed By (other modules call this):
- **UEFI DXE drivers**: Call `_ModuleEntryPoint` as standard DXE driver
- **SMM modules**: Call via SMM SW Dispatch (SMI triggers)
- **RAS error handlers**: Register callbacks via `sub_36B8` (register notification callback) for error event delivery
- **Platform OEM libraries**: Via EMCA platform hooks protocol (OemRasLib.c sources)

## Notes

1. This module has dual-mode operation: DXE (standard entry at startup) and SMM (interrupt-driven entry via SMI).
2. The `off_6B60` callback table at 0x6B60 likely contains function pointers for SMM SW Dispatch handlers registered in `sub_3970`.
3. The module communicates with the FPGA to configure error handling at runtime via the FPGA Configuration HOB.
4. Debug strings reference "LenovoServerPkg/Library/LnvPurleyLib/OemRasLib/OemRasLib.c" for OEM RAS policy reading.
5. The EMCA platform hooks library at `PurleySktPkg/Library/emcaplatformhookslib/emcaplatformhookslib.c` provides the platform-specific MC bank initialization.
6. sub_3B94 at 0x3B94 initializes a large ~400+ byte structure with error policy configuration (per-socket/per-bank error enable/disable settings, thresholds).
