Newer
Older
AMI-Aptio-BIOS-Reversed / PurleyPlatPkg / Ras / Memory / MemRas / MemRas.c
@Ajax Dong Ajax Dong 2 days ago 26 KB Full restructure
/**
 * MemRas.c - Memory RAS (Reliability, Availability, Serviceability) Driver
 * PurleySktPkg/Dxe/MemRas/MemRas
 *
 * Entry point and protocol installation for the Purley platform memory RAS
 * subsystem. This driver handles memory error correction, sparing,
 * address translation, patrol scrubbing, and S3 boot script save/restore.
 *
 * ============================================================================
 * Initialization Sequence
 * ============================================================================
 *
 * ModuleEntryPoint() [0x5CC]
 *   |
 *   +-> MemRasInitGlobals() [sub_AF90, 0xAF90]
 *   |     Allocates global context, calls ~23 sub-initializers in sequence
 *   |
 *   +-> MemRasDriverEntry() [sub_B724, 0xB724]
 *   |     Allocates protocol structures, populates function table,
 *   |     reads platform config, installs protocol, registers events
 *   |     Calls: sub_10E20() (S3 save events) + sub_17BCC() (VLS init)
 *   |
 *   +-> MemRasSecondaryInit() [sub_B6AC, 0xB6AC]
 *         Calls sub_B724() if context not already initialized
 *         Sets global status to 0x8000000000000001 if failed
 *
 *   On failure (entry point):
 *     +-> MemRasFallbackInit() [0xB56C]
 *           Calls sub_39BC() + sub_2290() + sub_1220()
 *
 * ============================================================================
 * Memory Layout
 * ============================================================================
 *
 * All allocations use BootServices->AllocatePool (PoolType=6=EfiBootServicesData):
 *   1. Context structure:     24 bytes   (qword_CD8B0)
 *   2. Protocol structure:    568 bytes  (qword_27818)
 *   3. Socket config buffer:  32768 bytes (qword_CD740) in .data
 *   4. Scratch/work buffer:   25037 bytes (allocated at runtime)
 *   5. Error log buffer:      1643 bytes  (qword_CD748)
 *
 * ============================================================================
 * Protocol Function Table (at qword_27818, populated at offsets 160-424)
 * ============================================================================
 *
 * Offset  Function     Description
 * ------  -----------  -----------------------------------------------
 *  0x0A0  sub_DB54     Init - per-socket initialization
 *  0x0A8  sub_1430     Init2 - stub (returns success)
 *  0x0B0  sub_1A4CC    SpareRank - start sparing on rank
 *  0x0B8  sub_19DA4    Alternate spare rank path
 *  0x0C0  sub_1A610    SpareCopy - handle spare copy done
 *  0x0E0  sub_CCC4     GetFailedDimmErrorInfo
 *  0x108  sub_1B1D0    CheckAndHandleNewRankSparingEvent
 *  0x110  sub_F800     HandlePatrolCompleteEventForNode
 *  0x118  sub_F694     HandleSpareRankUCEvent
 *  0x120  sub_1BB28    SpareCopyDone handler
 *  0x130  sub_E730     S3BootScriptSave
 *  0x138  sub_1AB5C    Compare GUID / HOB lookup
 *  0x140  sub_1B6AC    Check rank state
 *  0x148  sub_C47C     ForEachChannel - iterate channels
 *  0x150  sub_CA34     Check rank state per MC
 *  0x158  sub_CA28     Check rank state per channel (stub)
 *  0x160  sub_DAA4     GetSystemAddress (channel addr -> sys addr)
 *  0x168  sub_C39C     GetChannelAddress (rank addr -> channel addr)
 *  0x170  sub_D6C0     Address translation
 *  0x178  sub_DC34     GetRankAddress
 *  0x180  sub_DE7C     ReverseAddressTranslate
 *  0x0F0  sub_15010    DisableMemoryMigration
 *  0x0F8  sub_122A8    SetupMemoryMigration
 *  0x190  sub_D9E8     TranslateRIR (Rank Interleave Registers)
 *  0x198  sub_F4B4     GetErrorLogSysAddr
 *  0x0E8  sub_1430     StubInit (same as Init2)
 *  0x0C8  sub_DBFC     ForwardAddrTranslate
 *  0x0D0  sub_E060     McChToLogChId (MC channel to logical channel)
 *  0x0D8  sub_C570     TadId2SadId (TAD to SAD index translation)
 *  0x100  sub_DA6C     GetDDRTSystemAddress
 *  0x188  sub_CD04     ForEachSocket - iterate sockets
 *  0x1A0  sub_D758     TranslateSAD (System Address Decoder)
 *  0x1A8  sub_CD44     TranslateDDRTTad (DDRT TAD translation)
 *
 * ============================================================================
 * Address Translation Pipeline
 * ============================================================================
 *
 * Forward (System Address -> Physical):
 *   1. TranslateSAD (sub_D758) - System Address Decoder routing
 *      SAD maps system address -> socket/MC/channel via interleave rules
 *   2. TadId2SadId (sub_C570) - Find SAD entry from TAD index
 *   3. TranslateTAD (in sub_D6C0) - Target Address Decoder
 *      TAD provides per-channel offset/limit translations
 *   4. GetChannelAddress (sub_C39C) - Channel address calculation
 *   5. TranslateRIR (sub_D9E8) - Rank Interleave Register
 *      RIR maps channel address -> physical rank via interleave registers
 *   6. ADDDCSupportForAddrTrans - ADDDC lockstep rank handling
 *   7. RankSpareSupportForAddrTrans - Rank spare mapping
 *   8. TranslateRankAddress - Row/Column/Bank/BankGroup extraction
 *   9. TranslateDDRTTad (sub_CD44) - DDRT-specific translation (2LM/PMEM)
 *
 * Reverse (Physical -> System Address):
 *   1. ReverseAddressTranslate (sub_DE7C)
 *   2. GetDDRTSystemAddress (sub_DA6C) - DDRT-specific reverse
 *   3. GetSystemAddress (sub_DAA4) - channel addr -> system addr
 *
 * ============================================================================
 * Error Handling / Sparing State Machine
 * ============================================================================
 *
 * Global bitmaps (dword_23680, dword_23684):
 *   - mEventProgressBitmap[dword_23680]: Bit per NodeId, set when error
 *     handling is in progress
 *   - mEventPendingBitmap[dword_23684]: Bit per NodeId, set when error
 *     handling is pending (deferred)
 *
 * Rank states (per rank state machine):
 *   available -> scrubbing -> do_spare_copying -> spare_copy_done -> try_do_tag
 *
 * ============================================================================
 * Key Global Variables (.data section, 0x22AC0-0xEE920)
 * ============================================================================
 *
 *   qword_23658 (0x23658): RAS status code
 *   dword_23680 (0x23680): mEventProgressBitmap
 *   dword_23684 (0x23684): mEventPendingBitmap
 *   word_2367C  (0x2367C): Error threshold
 *   qword_23670 (0x23670): Scratch buffer pointer
 *   byte_63AEC  (0x63AEC): Platform config byte 1
 *   byte_65ED2  (0x65ED2): Platform config byte 2
 *   dword_63B24 (0x63B24): Platform config (number of sockets)
 *   dword_63C4C (0x63C4C): Platform config (memory base address)
 *   qword_26650 (0x26650): S3 save context
 *   qword_27828 (0x27828): System memory size
 *   qword_27830 (0x27830): Memory type / node info
 *   qword_27838 (0x27838): Number of sockets config
 *   qword_27840 (0x27840): Number of channels config
 *   qword_27848 (0x27848): Memory range end
 *   qword_27850 (0x27850): Memory range start
 *   byte_27858  (0x27858): Channel mode (lockstep/non-lockstep)
 */

// Include auto-generated UEFI header from AutoGen.c
#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiLib.h>

// Protocol GUIDs (referenced in .rdata section as unk_22B*)
// unk_22BB0: gEfiMemRasProtocolGuid  - Memory RAS protocol
// unk_22B20: Platform RAS policy protocol
// unk_22B40: CPU CSR access protocol
// unk_22B90: Additional platform protocol
// unk_22BA0: Additional platform protocol
// unk_22B30: Protocol interface GUID for InstallProtocolInterface
// unk_22B60: Event protocol for S3 boot script
// unk_22B80: SMM communication protocol
// unk_22BC0: Variable access protocol

#include "MemRas.h"

//
// ============================================================================
// Module-Level Global Variables
// ============================================================================
//
// These are populated from HOBs and platform policy protocols at init time.
//

// Configuration read from HOB at offset 1677
UINT8 ChannelMode;          // byte_27858 - 0=non-lockstep, 1=lockstep

// Configuration from platform policy protocol
UINT8  VlsEnabled;          // byte_63AEC
UINT8  SpareMode;           // byte_65ED2
UINT32 NumSockets;          // dword_63B24
UINT32 MemoryBase;          // dword_63C4C

// Memory configuration from HOB
UINT64 MemoryRangeStart;    // qword_27850
UINT64 MemoryRangeEnd;      // qword_27848
UINT32 SystemMemorySize;    // qword_27840
UINT32 NumChannels;         // qword_27838
UINT32 MemoryType;          // qword_27830
UINT32 MemoryGranularity;   // qword_27828

//
// ============================================================================
// _ModuleEntryPoint [0x5CC]
// ============================================================================
//
// UEFI DXE driver entry point. Called by DXE dispatcher.
//
// Flow:
//   1. MemRasInitGlobals() - Allocate and initialize global state
//   2. MemRasDriverEntry() - Protocol installation (primary path)
//   3. On failure, MemRasFallbackInit() tries secondary path
//
EFI_STATUS
EFIAPI
ModuleEntryPoint(
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE *SystemTable
    )
{
    EFI_STATUS Status;

    //
    // Phase 1: Allocate and initialize global context.
    // Allocates socket configuration buffer, protocol structure,
    // scratch buffers, and error log buffer.
    //
    MemRasInitGlobals();

    //
    // Phase 2: Primary driver entry - allocate protocol, populate
    // function table, read platform config from HOB and policy protocols,
    // install protocol interface, register S3 save events,
    // initialize VLS (Virtual Lockstep) info.
    //
    Status = MemRasDriverEntry(ImageHandle, SystemTable);
    if (EFI_ERROR(Status)) {
        //
        // Phase 3 (fallback): On failure, attempt secondary initialization.
        // Calls memory initialization, event registration, S3 boot script.
        //
        Status = MemRasFallbackInit(ImageHandle, SystemTable);
    }

    return Status;
}

//
// ============================================================================
// MemRasInitGlobals() [sub_AF90, 0xAF90]
// ============================================================================
//
// Called first from ModuleEntryPoint. Allocates and initializes all
// module-level buffers and structures.
//
// Init sequence (23 sub-initializers):
//   1. sub_6BC      - Global lock/state initialization
//   2. sub_758      - BaseLib setjmp/longjmp context init
//   3. sub_794      - UEFI boot/runtime service table init
//   4. sub_10AC     - Driver binding/entry point library init
//   5. sub_1430     - [stub] returns success
//   6. sub_163C     - Memory allocation library init
//   7. sub_1928     - HOB list library init
//   8. sub_1AB8     - PCI Express library init
//   9. sub_1BC8     - [stub] returns success
//  10. sub_1C7C     - SMM LockBox / S3 boot script library init
//  11. sub_1FB4     - Timer library init
//  12. sub_20CC     - ACPI timer lib / decompress lib / services table
//  13. sub_35D4     - EMCA platform hooks / MP sync data lib init
//  14. sub_43A0     - [stub] returns success
//  15. sub_51A8     - SMM communication / variable protocol init
//  16. sub_552C     - IPMI / error reporting library init
//  17. sub_5FB4     - OemRasLib init
//  18. sub_6C00     - Platform-specific RAS init
//
EFI_STATUS
MemRasInitGlobals(
    VOID
    )
{
    //
    // Each sub-initializer is a library constructor from the
    // linked static libraries. They are called sequentially.
    // On debug builds, each failure triggers ASSERT via
    // _ASSERT(!EFI_ERROR(Status)) pattern.
    //
    // The pattern for each call is:
    //
    //   Status = LibraryConstructor();
    //   ASSERT_EFI_ERROR(Status);
    //

    // Phase 1: Core library initialization
    //   0x6BC  - BaseLib / lock init
    //   0x758  - UefiBootServicesTableLib constructor
    //   0x794  - UefiRuntimeServicesTableLib constructor
    //

    // Phase 2: Driver support libraries
    //   0x10AC - UefiDriverEntryPoint / UefiLib constructors
    //   0x1430 - [stub, returns success]
    //   0x163C - Memory allocation library
    //   0x1928 - HOB library (mHobList initialization)
    //   0x1AB8 - SmmMmPciBaseLib constructor
    //   0x1BC8 - [stub, returns success]
    //   0x1C7C - S3BootScriptLib / SmmLockBoxSmmLib constructor
    //   0x1FB4 - AcpiTimerLib constructor
    //   0x20CC - DxeServicesTableLib + TianoCustomDecompressLib
    //

    // Phase 3: Platform-specific RAS libraries
    //   0x35D4 - mpsyncdatalib + emcaplatformhookslib init
    //   0x43A0 - [stub, returns success]
    //   0x51A8 - SmmLnvSendIpmiCmdLib / SmmVariable protocol
    //   0x552C - AmiErrReportingLib init
    //   0x5FB4 - OemRasLib init
    //   0x6C00 - Platform-specific RAS init
    //

    return EFI_SUCCESS;
}

//
// ============================================================================
// MemRasDriverEntry() [sub_B724, 0xB724]
// ============================================================================
//
// Core driver entry. Allocates protocol structure, reads HOB data,
// populates function pointer table, installs protocol interface,
// and sets up event handlers.
//
EFI_STATUS
EFIAPI
MemRasDriverEntry(
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE *SystemTable
    )
{
    EFI_STATUS                   Status;
    MEM_RAS_PROTOCOL            *Protocol;
    VOID                        *ContextBuffer;
    VOID                        *PlatformPolicy;
    VOID                        *CpuCsrAccess;
    VOID                        *PlatformProtocol;
    UINT8                       *HobData;

    //
    // Step 1: Allocate context structure (24 bytes) and zero it.
    //   qword_CD8B0 -> ContextBuffer (sizeof=24)
    //
    Status = gBS->AllocatePool(EfiBootServicesData, 24, &ContextBuffer);
    ASSERT_EFI_ERROR(Status);
    ZeroMem(ContextBuffer, 24);

    //
    // Step 2: Allocate protocol structure (568 bytes) and zero it.
    //   qword_27818 -> Protocol (MEM_RAS_PROTOCOL)
    //
    Status = gBS->AllocatePool(EfiBootServicesData, 568, &Protocol);
    ASSERT_EFI_ERROR(Status);
    ZeroMem(Protocol, 568);

    // Link protocol to context
    *(MEM_RAS_PROTOCOL **)((UINT8 *)ContextBuffer + 16) = Protocol;

    //
    // Step 3: Allocate socket configuration buffer (32768 bytes).
    //   qword_CD740 -> SocketConfig
    //   Stored in .data section (global variable)
    //
    Status = gBS->AllocatePool(EfiBootServicesData, 0x8000, &gSocketConfig);
    ASSERT_EFI_ERROR(Status);
    ZeroMem(gSocketConfig, 0x8000);

    // Initialize scratch buffer tracking
    gScratchBuffer = gSocketConfig + 32760;

    //
    // Step 4: Allocate scratch buffer (25037 bytes).
    //   First 8 bytes of protocol structure stores pointer to this.
    //
    Status = gBS->AllocatePool(EfiBootServicesData, 25037, &Protocol[0]);
    ASSERT_EFI_ERROR(Status);
    ZeroMem(Protocol, 25037);

    //
    // Step 5: Allocate error log buffer (1643 bytes).
    //   qword_CD748 -> gErrorLogBuffer
    //
    Status = gBS->AllocatePool(EfiBootServicesData, 1643, &gErrorLogBuffer);
    ASSERT_EFI_ERROR(Status);
    ZeroMem(gErrorLogBuffer, 1643);

    //
    // Step 6: Locate PlatformRasPolicy protocol (unk_22BB0).
    //   This provides platform RAS configuration data.
    //
    Status = gBS->LocateProtocol(&gPlatformRasPolicyGuid, NULL, &PlatformPolicy);
    ASSERT_EFI_ERROR(Status);

    //
    // Step 7: Initialize protocol signature.
    //   Initial: 0x12345678 (temporary)
    //   Final:   0x4D495053 ("MIPS")
    //
    *(UINT32 *)ContextBuffer = 0x12345678;
    MemRasSocketInit(Protocol);
    *(UINT32 *)ContextBuffer = 0x4D495053;
    *(MEM_RAS_PROTOCOL **)ContextBuffer = &gProtocolContext;

    //
    // Step 8: Populate function pointer table.
    //   All function pointers are installed at their respective
    //   offsets within the protocol structure.
    //
    Protocol->Init                          = sub_DB54;
    Protocol->Init2                         = sub_1430;   // stub
    Protocol->SpareRank                     = sub_1A4CC;
    Protocol->SpareRank2                    = sub_19DA4;
    Protocol->SpareCopy                     = sub_1A610;
    Protocol->GetFailedDimmErrorInfo        = sub_CCC4;
    Protocol->CheckAndHandleNewRankSparingEvent = sub_1B1D0;
    Protocol->HandlePatrolCompleteEventForNode = sub_F800;
    Protocol->HandleSpareRankUCEvent        = sub_F694;
    Protocol->SpareCopyDone                 = sub_1BB28;
    Protocol->S3BootScriptSave              = sub_E730;
    Protocol->CompareGuidOrSimilar          = sub_1AB5C;
    Protocol->CheckRankState                = sub_1B6AC;
    Protocol->ForEachChannel                = sub_C47C;
    Protocol->CheckRankStatePerMC           = sub_CA34;
    Protocol->CheckRankStatePerCh           = sub_CA28;
    Protocol->GetSystemAddress              = sub_DAA4;
    Protocol->GetChannelAddress             = sub_C39C;
    Protocol->AddressTranslation            = sub_D6C0;
    Protocol->GetRankAddress                = sub_DC34;
    Protocol->ReverseAddressTranslate       = sub_DE7C;
    Protocol->DisableMemoryMigration        = sub_15010;
    Protocol->SetupMemoryMigration          = sub_122A8;
    Protocol->TranslateRIR                  = sub_D9E8;
    Protocol->GetErrorLogSysAddr            = sub_F4B4;
    Protocol->StubInit                      = sub_1430;   // stub
    Protocol->ForwardAddrTranslate          = sub_DBFC;
    Protocol->McChToLogChId                 = sub_E060;
    Protocol->TadId2SadId                   = sub_C570;
    Protocol->GetDDRTSystemAddress          = sub_DA6C;
    Protocol->ForEachSocket                 = sub_CD04;
    Protocol->TranslateSAD                  = sub_D758;
    Protocol->TranslateDDRTTad              = sub_CD44;

    //
    // Step 9: Locate platform RAS policy protocol (unk_22B20).
    //   Returns the HOB list pointer with system configuration.
    //
    Status = gBS->LocateProtocol(&gPlatformRasHobGuid, NULL, (VOID **)&PlatformPolicy);
    ASSERT_EFI_ERROR(Status);

    //
    // Step 10: Locate CPU CSR access and additional protocol.
    //
    Status = gBS->LocateProtocol(&gCpuCsrAccessGuid, NULL, (VOID **)&CpuCsrAccess);
    ASSERT_EFI_ERROR(Status);

    Status = gBS->LocateProtocol(&gPlatformProtocolGuid, NULL, &PlatformProtocol);
    ASSERT_EFI_ERROR(Status);

    //
    // Step 11: Read platform configuration from policy protocol.
    //   Offset 1677: Channel mode (lockstep bit)
    //   Offset 1780: VLS enable flag
    //   Offset 1782: Spare mode
    //   Offset 1777: RAS mode (0=SDDC, 1=ADDDC, etc.)
    //   Offset 2067: Number of sockets
    //   Offset 280:  Memory base address / range
    //   Offset 4:    System memory size
    //   Offset 8:    Number of channels
    //   Offset 12:   Memory type
    //   Offset 20:   Memory granularity
    //
    HobData = (UINT8 *)*(UINTN *)PlatformPolicy;
    ChannelMode          = HobData[1677];
    VlsEnabled           = HobData[1780];
    SpareMode            = HobData[1782];
    NumSockets           = *(UINT32 *)(HobData + 2067);
    MemoryBase           = *(UINT32 *)(HobData + 280);
    MemoryRangeStart     = *(UINT64 *)(HobData + 280);
    MemoryRangeEnd       = MemoryRangeStart + *(UINT32 *)(HobData + 288);
    SystemMemorySize     = *(UINT32 *)(HobData + 4);
    NumChannels          = *(UINT32 *)(HobData + 8);
    MemoryType           = *(UINT32 *)(HobData + 12);
    MemoryGranularity    = *(UINT32 *)(HobData + 20);

    //
    // Step 12: Initialize 4 socket entries in scratch buffer.
    //   Sets byte at offset 1606 to 1 for each socket.
    //
    for (UINT32 Socket = 0; Socket < 4; Socket++) {
        HobData[1606 + 119 * Socket] = 1;
    }

    //
    // Step 13: Install MemRas protocol interface.
    //   GUID: gEfiMemRasProtocolGuid (unk_22B30)
    //   Context data: Protocol structure (MEM_RAS_PROTOCOL *)
    //
    *(UINT64 *)((UINT8 *)ContextBuffer + 8) = 0;  // Protocol handle init
    Status = gBS->InstallProtocolInterface(
                (EFI_HANDLE *)((UINT8 *)ContextBuffer + 8),
                &gEfiMemRasProtocolGuid,
                EFI_NATIVE_INTERFACE,
                ContextBuffer + 16   // points to MEM_RAS_PROTOCOL *
                );
    ASSERT_EFI_ERROR(Status);
    gMemRasProtocolHandle = *(EFI_HANDLE *)((UINT8 *)ContextBuffer + 8);

    //
    // Step 14: Register S3 boot script save handler.
    //   sub_10E20() - Allocates context, sets event type=3,
    //   registers sub_10F38 as callback for gEfiEventReadyToBootGuid,
    //   installs protocol for gEfiMemRasS3SaveProtocolGuid.
    //
    MemRasS3SaveRegister();

    //
    // Step 15: Initialize VLS (Virtual Lockstep) state.
    //   sub_17BCC() - Sets VLS variable "VLSEstablished" to known GUID.
    //   If variable doesn't exist, creates it. Locates VariableArchProtocol
    //   and reads back "VLSEstablished" to verify.
    //
    InitializeVLSInfo();

    //
    // Step 16: Initialize spare copy done handler.
    //   sub_1BB28()
    //
    Protocol->SpareCopyDone();

    //
    // Step 17: Initialize rank sparing event handler.
    //   sub_1B1D0()
    //
    Protocol->CheckAndHandleNewRankSparingEvent();

    //
    // Step 18: Conditionally initialize patrol scrub.
    //   If NumSockets >= 3 or NumSockets <= 1, start patrol scrub.
    //   sub_F800()
    //
    if (NumSockets >= 3 || NumSockets <= 1) {
        Protocol->HandlePatrolCompleteEventForNode();
    }

    //
    // Step 19: Initialize alternate spare rank path.
    //   sub_1A47C()
    //
    sub_1A47C();

    //
    // Step 20: Register per-socket MCE events (if configured).
    //   If platform config byte (offset 35) has bits 2 and 4 set,
    //   register sub_1A0D4 as callback for event groups at
    //   unk_22C40 and unk_22C50.
    //
    if ((HobData[35] & 0x14) == 0x14) {
        gBS->CreateEventEx(
            EVT_NOTIFY_SIGNAL,
            TPL_NOTIFY,
            sub_1A0D4,
            NULL,
            &gEventGroupGuid1,
            &Event1
            );
        gBS->CreateEventEx(
            EVT_NOTIFY_SIGNAL,
            TPL_NOTIFY,
            sub_1A0D4,
            NULL,
            &gEventGroupGuid2,
            &Event2
            );
    }

    return EFI_SUCCESS;
}

//
// ============================================================================
// MemRasSecondaryInit() [sub_B6AC, 0xB6AC]
// ============================================================================
//
// Called from ModuleEntryPoint when MemRasInitGlobals() succeeds.
// If unk_23560 context is valid, calls MemRasDriverEntry().
// Otherwise sets global status to indicate failure.
//
EFI_STATUS
EFIAPI
MemRasSecondaryInit(
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE *SystemTable
    )
{
    //
    // Global status initialized to error (0x8000000000000001)
    //
    gRasStatus = 0x8000000000000001uLL;

    //
    // Check if context (unk_23560) has setjmp/longjmp already set up.
    // sub_2C0() performs a setjmp(context) - returns 0 on success.
    //
    if (!sub_2C0(&unk_23560)) {
        //
        // Context was not initialized, so this is first entry.
        // Call the full driver entry.
        //
        EFI_STATUS Status;
        Status = MemRasDriverEntry(ImageHandle, SystemTable);
        MemRasStatusUpdate(Status);

        //
        // Should never reach here - ASSERT failure.
        //
        ASSERT(FALSE);
    }

    return gRasStatus;
}

//
// ============================================================================
// MemRasFallbackInit() [sub_B56C, 0xB56C]
// ============================================================================
//
// Called when MemRasDriverEntry() returns an error.
// Provides alternative initialization path for error recovery.
//
// Initialization steps:
//   1. sub_39BC     - Platform-specific RAS initialization (925 bytes)
//   2. sub_2290     - Memory error event registration
//   3. sub_1220     - S3 boot script save (23 bytes, stub)
//
EFI_STATUS
EFIAPI
MemRasFallbackInit(
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE *SystemTable
    )
{
    EFI_STATUS Status;

    Status = PlatformRasInit();
    ASSERT_EFI_ERROR(Status);

    Status = RegisterMemoryErrorEvents(ImageHandle, SystemTable);
    ASSERT_EFI_ERROR(Status);

    Status = S3BootScriptSaveInit(ImageHandle, SystemTable);
    ASSERT_EFI_ERROR(Status);

    return Status;
}

//
// ============================================================================
// GetOrSetErrorHandlingStatus() [sub_D7A0, 0xD7A0]
// ============================================================================
//
// Manages the per-node error handling progress and pending bitmaps.
//
// Operations:
//   0 (RAS_ERROR_PROGRESS):
//       0 = Clear Progress bit for NodeId
//       1 = Set Progress bit for NodeId (must have Pending bit set)
//       2 = Get Progress status
//
//   1 (RAS_ERROR_PENDING):
//       0 = Clear Pending bit for NodeId
//       1 = Set Pending bit for NodeId (must have Pending bit clear)
//       2 = Get Pending status
//
//   2 (RAS_ERROR_GET_STATUS):
//       2 = Get Progress status (via dword_23680)
//       3 = Get Pending status (via dword_23684)

//
//EFI_STATUS
//GetOrSetErrorHandlingStatus(
//    IN  UINT8  NodeId,
//    IN  UINT32 Operation,
//    OUT UINT32 *Status OPTIONAL
//    )
//{
//    switch (Operation) {
//    case 0: // Progress
//        switch (*Status) {
//        case 0: // Clear
//            mEventProgressBitmap &= ~(1 << NodeId);
//            DEBUG((DEBUG_INFO, "Clear Event Progress Node%d\n", NodeId));
//            return EFI_SUCCESS;
//        case 1: // Set
//            ASSERT((mEventPendingBitmap & (1 << NodeId)) != 0);
//            mEventProgressBitmap |= (1 << NodeId);
//            DEBUG((DEBUG_INFO, "Set Event Progress Node%d\n", NodeId));
//            return EFI_SUCCESS;
//        default:
//            ASSERT(FALSE);
//            return EFI_INVALID_PARAMETER;
//        }
//
//    case 1: // Pending
//        switch (*Status) {
//        case 0: // Clear
//            mEventPendingBitmap &= ~(1 << NodeId);
//            DEBUG((DEBUG_INFO, "Clear Event Pending Node%d\n", NodeId));
//            return EFI_SUCCESS;
//        case 1: // Set
//            ASSERT((mEventPendingBitmap & (1 << NodeId)) == 0);
//            mEventPendingBitmap |= (1 << NodeId);
//            DEBUG((DEBUG_INFO, "Set Event Pending Node%d\n", NodeId));
//            return EFI_SUCCESS;
//        default:
//            ASSERT(FALSE);
//            return EFI_INVALID_PARAMETER;
//        }
//
//    case 2: // Get - return Progress bitmap
//        *Status = (mEventProgressBitmap >> NodeId) & 1;
//        DEBUG((DEBUG_INFO, "Get Progress Node%d = %d\n", NodeId, *Status));
//        return EFI_SUCCESS;
//
//    case 3: // Get - return Pending bitmap
//        *Status = (mEventPendingBitmap >> NodeId) & 1;
//        DEBUG((DEBUG_INFO, "Get Pending Node%d = %d\n", NodeId, *Status));
//        return EFI_SUCCESS;
//
//    default:
//        ASSERT(FALSE);
//        return EFI_INVALID_PARAMETER;
//    }
//}