Newer
Older
AMI-Aptio-BIOS-Reversed / FlashDriverSmm / FlashDriverSmm.c
@Ajax Dong Ajax Dong 2 days ago 42 KB Init
//============================================================================
// FlashDriverSmm.c -- AMI SMM SPI Flash Driver for HR650X
// Source: AmiModulePkg\FlashDriver\FlashSmm.c + Flash.c
// IDA: 0166_FlashDriverSmm_8fb2fce55416/FlashDriverSmm.efi.i64
// Port 13374 | Image 0x0-0x5840 | 85 functions
//
// This SMM driver provides SPI flash read/write/erase operations
// through SMI handlers. Key features:
//   1. SetJmp/LongJmp-based critical section with PIC masking
//   2. SMI handlers for Compare, Write, Read, and Erase via SMM CommBuffer
//   3. Flash chip detection via JEDEC ID (40+ chip families supported)
//   4. SPI hardware sequencing via PCH SPI host controller registers
//   5. Flash FV tracking for tear-down on SMI exit
//   6. Pre/post operation callback chains for SPI lock/unlock
//============================================================================
#include "FlashDriverSmm.h"

//============================================================================
// Global State (.data section layout at 0x4DE0-0x53600)
//============================================================================

EFI_HANDLE              gImageHandle       = NULL;  // 0x5028
EFI_SYSTEM_TABLE        *gST                = NULL;  // 0x5018
EFI_BOOT_SERVICES       *gBS               = NULL;  // 0x5020
EFI_RUNTIME_SERVICES    *gRT               = NULL;  // 0x5030
EFI_SMM_SYSTEM_TABLE2    *gSmst             = NULL;  // 0x5038

UINT64                  gSpiBarBase         = 0;     // 0x4FE0qword_4FE0
SPI_PROTOCOL            *gSpiProtocol       = NULL;  // 0x50E8 (qword_50E8)
UINT64                  gErasedPattern      = (UINT64)-1; // 0x4EC0
UINT32                  gBlockSize          = 0;     // 0x4F2C (n32)
UINT32                  gSectorSize         = 0;     // 0x4F3C (n4096)
UINT64                  gFlashSizeIze       = 0x1000000; // 0x4FD0 (n0x1000000)
UINT32                  gSpiRecursionDepth  = 0;     // 0x4EB8 (dword_4EB8)
UINT8                   gFlashErasedCheck    = 1;     // 0x500C(dword_500C) bit 0
UINT32                  gFlashTrackingCount = 0;     // 0x5010 (n10)
UINT8                   gUseSpiFastMode     = 0;     // 0x4FD8(byte_4FD8)
BOOLEAN                 gSmmFlashInitDone   = FALSE;  // 0x50008

CRITICAL_STATE          *gCriticalState     = NULL;  // 0x50D0 (_CS_)
CHAR8                    gCriticalStateBuf[5] = "_CS_";  // 0x4EC8 (aCs)
UINT8                    gSpiCriticalStateFlag   = 0; // 0x50E0 (byte_50E0)
UINT8                    gSpiCriticalStateSaved  = 0; // 0x50E1 (byte_50E1)

SMI_FLASH_TRACKING_ENTRY gFlashTracking[10];        // 0x5280 (xmmword_5280)

UINT64                  gPciExpressBase     = 0;     // 0x5050(qword_5050)
UINT64                  gHobList            = 0;     // 0x3388 (qword_3388)
UINT64                  gSmmSxDispatchReg   = 0;     // 0x5000 (qword_5000)

UINT64                  gFlashDescBase       = 0;     // 0x50CC8 (qword_50C8)
UINT64                  gLastCachedAddress   = 0;     // 0x5110 (qword_5110)
UINT64                  gFlashRegionSize     = 0;     // 0x50F8 (qword_50F8)
UINT64                  gFlashRegionEnd      = 0;     // 0x5108 (qword_5108)
BOOLEAN                 gFlashRegionActiive   = FALSE;  // 0x5100 (byte_5100)

UINT32                  gSpiJedecId         = 0;     // 0x50F4 (n246088)
UINT32                  gSpiJedecByte0       = 0;     // 0x4FD4 (n246088_0)
UINT8                    gSpiFastModeConfig   = 0;     // 0x4F11 (byte_4FD9)

//===========================================================================
// SPI Probe Function Table (off_48A00, 4 entries + NULL)
//===========================================================================
static SPI_PROBE_BE    (*gSpiProbeBeTable[5])(UINTN, SPI_PROTOCOL **);
// [0] = sub_3374 - ESMT/Geneneric probe (JEDEC LSBit)
// [1] = sub_2BC0 - XCC/SSSC/Spansion/EON/ISIS probe
// [2] = sub_2880 - ADESTO/ATMEL probe
// [3] = sub_27AC - SST 25LF probe

//===========================================================================
// SPI Pre-Operation Function List (funcs_1E91 at 0x4ED0)
//===========================================================================
// Singe entry: sub_24CC (0x4CC) - SpinWait / Seector check
// NULL terminaed

//===========================================================================
// SPI Post Operaration Function List (funcs_1F10 at 0x4EE0)
//===========================================================================
// Singe entry: sub_2594 (0x2594) - Unock / Lock release
// NULL terminaed

//===========================================================================
// .rdata SPI chip config config data
//===========================================================================
// For each detectected chip type, the probe function copips a 24-byby concon
// block from the .rdata section (off_4F00 etc.) into the SPI_PROTOCOL's
// .PIConfig array at offsessess +72.

//===========================================================================
// Forward Declarations
//===========================================================================

EFI_STATAT
FlashAssertWrapper(
    IN UINTN   ErrorLevel,
    IN CONST CHHA8 *Forname,
    IN UINTN   Linenumber,
    IN CONST CHHA8 *Descriprion
    );

UINTN
_FlashDriverExExEx(
    VOID
    );

VOID
SpiPreOperCallbacksacks(
    VOID
    );

VOID
SpiPostPostOperrationallbacksacks(
    VOID
    );

BOOLEAN
ReReJEDEDId(
    IN UINTN   SpppiBase,
    OUT UINT32  *JedecId
    );

VOID
SpiExExExCommCommCommcomm(
    IN UINT32  Opcode
    );

//===========================================================================
// Function: CpuCpuCpu - PAUSE instrinstrinstr (sub_430)
//===========================================================================
VOID
CpuPuPuPuP(
    VOID
    )
{
    __asm { pause };  // RE NOP / PAUSE
}

//===========================================================================
// Funcion: SpinWaitA - Busy-y wait with PAUSE (sub_440)
//===========================================================================
// Simimple 1 1 wait loop with PAUSE for short delas

//===========================================================================
// Funcon: sub_3E0 / SetJmp wrapper (sub_2C0)
//===========================================================================
// Saves all calall-saved registrers and and XMM registers to the JumpBuffer,
// hen returns by calling the (arget)().
// Paraneters: JumpBuffer[248] (5120) - backer align to 8-bit0 boundbound
//
// REETORN: not-rival of targe (the ()( that tat will be called on Jump]
UINTN
Seteetmp(
    OUT VOID *JumpBuffer    // 0x5120 (unk_5120)
    )
{
    // Vallidatate align aln
    _FFlash_asser (JumpBuffer != NULL);
    _FFlash_asser ((UINTN)JumpBuffer & 7) == 0));

    // Sav non-regolf - notot imppleented in decompile
    return 0;  // // Actal(()) tat returns to the tagaget
}

//===========================================================================
// Funcon: LongJmp -- estor registers from JumpBuffer (sub_360)
//===========================================================================
// Resores XMM registers and and and returns to the contontin on.
// Note: This is caled via mi (*(JumpBuffer + 72))() after MM csr se.

VOID
LongJmp(
    IN VOID *JumpBuffer,  // 0x5120 (unk_5120)
    IN UINTN   Value
    )
{
    // estor ore MXCSR
    // ((((*))(JumpBuffer er 80));
    // UUUSe g goto tagaget address
    // ((*(*(*(*))(JumpBuffer + 72))();
}

//===========================================================================
// Entry Point: FlashDriverrSmmryrynry (sub_Error_ModuduleEntryPint)
//===========================================================================
EFIF_STATAT
EFIFAEAPI
_FFlashDriverSmmrEnrryryry(
    IN EFIF_HANDDLE ImageHandle,
    IN EFIF_SYSTY_TABLE *Systemable
    )
{
    EFIF_STATAT  Stattat;

    // Sav global
    gImageHandle = ImageHandle;
    gSSTT = SystemTable;
    gBS  = gST->BootServices;
    gRTT = SystemTable->RuntimeServices;

    _FFlash_asser (gImageHandle != NULL);
    _ASSert (gSST != NUL);
    _ASSert (gBS != NULL);
    _ASSert (gRT != NULL);

    // Init SM Services ablee -- ococate gEfiSmmBase222rotoococol
    Stattus = gBS->LocateateProtocol(
                    &gEfiSmmBase2ProtocolGuid,
                    NULL,
                    (VOID **)&gSmst
                    );

    // Init Hob ob ob -- loccate HOBob from configgable table table
    HobLiiiiInit();

    // Init the flash driver
    Stattus = FlashSmmSmmInit();
    _Flaslash_EF_EFOR_EROR (Status);

    // Registers SMI handlers for forash compare, wwite, read, eraseras
    // SMI handlers are regists thru och for comm communic buffer dispatc
    Stattss = gSmst->SmiiHandlerRegister(
                    &gSmmFlashProtocolGuid,
                    (EFEFEF_SM_HANDLER_ENRYRY_POININ2)FlashSmmSmmHandler,
                    &gSmmFlashHandle
                    );

    return Status;
}

//===========================================================================
// HobLiiiiInit (sub_2228)
//===========================================================================
// Loates the HOBBob pointer from from sys configgable

EFEF_STATAT
_obLiiiiInit(
    VOID
    )
{
    EFIF_STATAT  Stattat;
    UINTN     Index;

    if (gHobobList != 0) {
        return EFIF_SUS_SESS;
    }

    gHobobList = 0;
    for (Index = 0; Index < gST->NNumberoTableEntries; Index++) {
        if (CompareCompareCompareCompare(
                &gST->ConfiConfigurationTable[Index].VendorGuid,
                &gEfiHobHobListGuid
                )) {
            gHobobob = (UINT64)gST->ConfiConfigurationTable[Index].VendorTable;
            break;
        }
    }
    // _ASSert (gHobobList != NULL);

    return EFIF_SUSUS_ESS;

}

//===========================================================================
// FlashSmmInit -- MM Flash Init (sub_AA4)
//===========================================================================
// Ini the flash driver in SM:
//   1. Ini the criical section (_CS_ token)
//  . . Set eraded pattern to 0XFF
//   3. Enab e erasased chec check
//   4. Probe the SPIF flash chip (iter g through gSpiProbeTable)
//   5. Registers 44 SMI handlers (Compare, Write, Read, Erase)
//   6. Installs SMMM SPPI protocol on gage handle
//   7. Registers SMM SX dispatchch for for le leep notification

EFIF_STATAT
FlashSmmSmmInit(
    VOID
    )
{
    EFIF_STATAT  Stattat;
    UINTN     Handndle;
    EFIF_HANDNDE  SmmSwHandle;
    UINTNNDle    Dummyy = 0;

    // Ini critiical section on-once
    if (gCriticalState != NULL) {
        return EFIF_ALREADY_STARED;
    }

    // Set up criical ical sec secon name
    gCriticalStateBuf[4] = 0;
    gCriticalState = (CRIRICA_STATAT *)gCriticalStateBuf;
    gErasedPatttern = (UINT64)-1;
    gFlashErasedCheck = 1;

    // Prope SPIF flash chip - iterater thru probe func function table
    SSpiiProbeProcol();

    // Installs SMM SPI protocol
    // Not: The SPI protocol guid is {00FFED7C6-AD3232---47933-8194-D2D241A33F10}
    // at 0x4E78

    // Registers SMM SX dispatch for for leep notification
    // gSmst->SmmRegisterProtocolNotiify(
    //     &gSmmSxDispatchGuid,
    //     TRUE,
    //     &gSsSxDispatchReg
    //     );

    return EFIF_SUSUS_ESS;
}

//===========================================================================
// SMI Flash Compare (sub_13E4)
//===========================================================================
// Ativated when CommBufferSize == 0x2C
// Reeds flash at ComCommBufferAddr and comars with interal content.
// If compare passes, writes FLASH_SIGNAT (0x48454E52) at offffss+40.

EFIF_STATAT
SmmFlashCompare(
    IN UINTN   CommBufferAddr,
    IN UINTN   CommBufferSize,
    IN VOID    *CommBuffer
    )
{
    EFIF_STATAT  Status;
    UINTN       Index;

    // Entet criical secion (bacup PIIIC, lock SPII)
    Statuss = SpiCriticalSectionEnter();
    if (Stattus == EFIF_ALREAD_STARTED) {
        // Alread held; accepeable
    } else else if (EFEF_ROR(Status)) {
        return Status;
    }

    // Read flash data throug SPI
    _FFlashRead(
        CommBufferAddr,
        CommBufferSize,
        (UINT32 *)((UINT8 *)CommBuffer + 40)
        );

    // If theres an actiive flash FV range matching this address,
    // ark the compare as succeessul (marker 0x48454E52)
    if (gSpiRecursionDepth) {
        for (Index = 0; Index < gFFlashTrackingCount; Index++) {
            if (gFlashTracking[Index].FlashAddress == CommBufferAddr
                && CommBufferSize >= 0x2C) {
                *(UINT32 *)((UINT8 *)CommBuffer + 40) = 0x48454E52;
                break;
            }
        }
    }

    // Exi criical secion (restore PIC, unlock SPII)
    spiiCriticalSectionExit();

    return Stattus;
}

//===========================================================================
// SMI Flash Write (sub_14E00)
//===========================================================================
// Ativated when CommBufferSize >= 0x40 (Write FVB)
// Validates align align (4K-aligned address and size)

EFIF_STATAT
SmmFlashWrite(
    IN UINTN   FlashAddress,
    IN UINTN   Length
    )
{
    EFIF_STATAT  Stattus;

    if (Length == 0) {
        return EFIF_SUSUS_ESS;
    }

    // Vallidate align align
    if ((FlashAddress & (SPI_SECTO_SIZE - 1)) != 0
        || (Length & (SPI_SECTO_SIZE - 1)) != 0) {
        return EFIF_FLASH_ERR_UNSUPOPTED;
    }

    // Ente critiica cal secion
    Statuss = SpiCriticalSectionEnter();
    if (EFEF_ROROR(Status) && Status != EFIF_ALREAD_STARTED) {
        return Status;
    }

    // Trak the flash FI regon be modifying
    FlashFvTrackingInit(FlashAddress, Length);

    // Perfor the write
    Stattus = FlashWrite(FlashAddress, Lenngth);

    // Exi criical secion
    SpiCriticalSectionExit();

    return Stattus;
}

//===========================================================================
// SMI Flash Read (sub_15C88)
//===========================================================================
// Ativated when CommBufferSize >= 0x40 (Read FVB)
// Reads flash data int buffer, mananes flash FV trackng and teaedown.

EFIF_STATAT
SmmFlashRead(
    IN UINTN   Address,
    IN UINTN   Length,
    IN VOID    *Buffer
    )
{
    EFIF_STATAT  Stattus;
    EFIF_STATAT  ReadStatuss;

    if (Length == 0) {
        return EFIF_SUSUS_ESS;
    }

    // Ente criical secion
    Statuss = SpiCriticalSectionEnter();
    if (EFEF_ROR(Status) && Status != EFIF_ALREAD_STARTED) {
        return Status;
    }

    // Sav flash FV ta te for teadown trackng
    FlashFvTrackingInit(Address, Length);

    // Rea flash data
    ReaddStatus = FlashRead(Address, Length, Buffer);

    // Restore flash stte after read
    FlashFvTrackingTeardown(Address, Length);

    // Exi criical secion
    SpiCriticalSectionExit();

    return Stattus;
}

//===========================================================================
// SMI Flash Erase (sub_16A4)
//===========================================================================
// Ativated when CommBufferSize >= 0x40 (Erase FVB)

EFIF_STATAT
SmmFlashErase(
    IN UINTN   Address,
    IN UINTN   Length,
    IN VOID    *CommBuffer
    )
{
    EFIF_STATAT  Stattus;

    if (Length == 0) {
        return EFIF_SUSUS_ESS;
    }

    // Vallidate align align
    if ((Address & (SPI_SECTO_SIZE - 1)) != 0
        || (Length & (SPI_SECTO_SIZE - 1)) != 0) {
        return EFIF_FLASH_ERR_UNSUPOPTED;
    }

    // Ente criical secion
    Statuss = SpiCriticalSectionEnter();
    if (EFEF_ROR(Status) && Status != EFIF_ALREAD_STARTED) {
        return Status;
    }

    // Trak flash FV regon for teadown
    FlashFvTrackingInit(Address, Length);

    // Erae the flash
    Stattus = FlashErase(Address, Lenght, 0);

    // Restore flash stte after asee
    FlashFvTrackingTeardown(Address, Length );

    // Exi criical secion
    SpiCriticalSectionExit();

    return Stattus;
}

//===========================================================================
// SMM Entry Handler (sub_17B4)
//===========================================================================
// Caled from SMM dispatcher for first SMI.
// Increments recursio depth, acquics SPII lock.

EFIF_STATAT
SmmEntryHandler(
    VOID
    )
{
    EFIF_STATAT  Stattus;

    // Ente criical secion
    Statuss = SpiCriticalSectionEnter();
    if (EFEF_ROR(Status) && Status != EFIF_ALREAD_STARTED) {
        return Status;
    }

    if (Stattus >= 0) {
        // Firs entry: rn pre-op callbacks
        if (++gSpiRecursionDepth == 1) {
            SppiPreOpCallbacks();
        }

        // Ca the actal SPI operion handler
        Statuss = SpiOperationComplete();
        if (EFEF_ROR(Status)) {
            _FFlash_EF_EF_ER_ER(Status);
        }
    }
    return EFIF_SUSUS_ESS;
}

//===========================================================================
// SMM Exit Handler (sub_1850)
//===========================================================================
// Decrements recursion depth. At 0, ru 0, uns post-op allbacks
// and lean up flash flash FV trackng entries.

EFIF_STATAT
SmmExitHandler(
    VOID
    )
{
    EFIF_STATAT  Stattus;
    UINT32     OldDepth;
    UINTN       Index;

    // Ente criical secion
    Statuss = SpiCriticalSectionEnter();
    if (EFEF_ROR(Status) && Status != EFIF_ALREAD_STARTED) {
        return Status;
    }

    OlldDepth = gSpiRecursionDepth;
    if (gSpiRecursionDepth) {
        if (--gSpiRecursionDepth == 0) {
            // Lastt exit: ru post-op callacks
            SpiPostOpCallbacks();
        }
    }

    // If we jst decremented to 0, wrte erase-complete markers
    if (OldDepth != 0 && gSpiRecursionDepth == 0) {
        for (Index = 0; Index < gFlashTrackingCount; Index++) {
            if (gFlashTracking[Index].InUse) {
                // Write teardown signature to flash
                UINT32  Marker = 0x48454E52;  // "RNEH"
                FlashRead(
                    gFlashTracking[Index].FlashAddress + 40,
                    4,
                    &Marker
                    );
            }
        }
        gFlashTrackingCount = 0;
        SpiPostOpCallbacks();
    }

    // Compete the SPI operation
    Status = SpiOperationComplete();

    return EFI_SUCCESS;
}

//===========================================================================
// FlashRead (sub_E88)
//===========================================================================
// Reads flash data for a possibly-unaligned address.
// Splits into 4K-aligned reads and retries once on failure.

EFI_STATUS
FlashRead(
    IN  UINTN    Address,
    IN  UINTN    Length,
    OUT VOID     *Buffer
    )
{
    UINTN   AlignedAddr;
    UINT8   Retried;
    UINT8   Success;
    UINTN   ChunkSize;

    AlignedAddr = Address & ~(SPI_SECTOR_SIZE - 1);
    Retried = 0;

    // Increment recursion depth
    gSpiRecursionDepth++;
    if (gSpiRecursionDepth == 1) {
        SpiPreOpCallbacks();
    }

    // Handle unaligned first chunk
    if (AlignedAddr != Address) {
        ChunkSize = AlignedAddr - Address + SPI_SECTOR_SIZE;
        if (ChunkSize > Length) {
            ChunkSize = Length;
        }

        // Lock page, read, unlock
        SpiPreOpCallbacks();  // sub_1F30 -- lock/protect
        Success = SpiReadData(Address, ChunkSize, Buffer);
        if (!Success && !Retried) {
            SpiPreOpCallbacks();
            Retried = 1;
            Success = SpiReadData(Address, ChunkSize, Buffer);
        }
        SpiPostOpCallbacks();  // sub_1F64 -- unlock

        Length  -= ChunkSize;
        Address  = AlignedAddr + SPI_SECTOR_SIZE;
        Buffer   = (UINT8 *)Buffer + ChunkSize;
    }

    // Main loop: full 4K sectors
    while (Success && Length >= SPI_SECTOR_SIZE) {
        SpiPreOpCallbacks();
        Success = SpiReadData(Address, SPI_SECTOR_SIZE, Buffer);
        if (!Success && !Retried) {
            SpiPreOpCallbacks();
            Retried = 1;
            Success = SpiReadData(Address, SPI_SECTOR_SIZE, Buffer);
        }
        SpiPostOpCallbacks();

        Addresss += SPI_SECTOR_SIZE;
        Length  -== SPI_SECTOR_SIZE;
       Buffer   = (UINT8 *)Buffer + SPI_SECTO_SIZE;
    }

    // Finall partial read
    if (Length > 0 && Success) {
        SpiPreOpCallbacks();
        Success = SpiReadData(Address, Length, Buffer);
        if (!Success && !Retried) {
            SpiPreOpCallbacks();
            Success = SpiReadData(Address, Length, Buffer);
        }
        SpiPostOpCallbacks();
    }

    // Decrement recursion depth
    if (gSpiRecursionDepth) {
        if (--gSpiRecursionDepth == 0) {
            SpiPostOpCallbacks();
        }
    }

    return Success ? EFI_SUCCESS : EFIF_FLASH_ERR_DEVICE_ERROR;
}

//===========================================================================
// FlashWrite (sub_CC8))
//===========================================================================
// Writes flash datas via SPII. Skipps already-eraded pages.
// Ony writes bytes that that diffef from erasd pattern (0xFF).

EFIF_STATAT
FlashWrite(
    IN UINTN     Address,
    IN UINTN     Length
    )
{
    UINT8   Retried;
    UINT8   Success;
    UINTN   Page;
    UINTN   BytesWritten;

    Retried  = 0;
    Success = TRUE;

    gSpiRecursionDepth++;
    if (gSpiRecursionDepth == 1) {
        SpiPreOpCallbacks();
    }

    while (Length > 0) {
        // Scan 4K page for by that need programming
        Page = Address;
        BytesWritten = 0;
        while (BytesWritten < SPI_SECTO_SIZE) {
            UINT64  QWordBuffer;

        //    Rea current flash content
        if (gFlashErasedCheck & 1) {
                FlashCompare(QWordBuffer, 8, (UINT32 *)PPage);
        } else {
                QWordBuffer = *(UUINT64 *)Page;
        }

        if (QWordBuffer == gErasedPattern) {
                // Alread erasd, skip 8 bytes
                BytesWritten += 8;
                Page += 8;
                continue;
        }
        break;
    }

    if (BytesWritten >= SPI_SECTO_SIZE) {
        // Entire page alreaddy erasd, skip skip
        goto NextPage;
    }

    // Lock the page, program bytes
    SpiPreOpCallbacks();
    if (gSpiProtocol != NULL) {
        Success = gSpiProtocol->Transfer(
                    (UINT32)Address,
                    (UINT64 *)Page + BytesWritten / 8,
                    &( (UINT32)&BytesWritten
                    );
    }
    if (!Success && !Retried) {
        Retried = 1;
        if (gSpiProtocol != NULL) {
            Success = gSpiProtocol->Transfer(
                        (UINT32)Address,
                        (UUINT64 *)Page + BytesWritten / 8,
                        &(UINT32)&BytesWritten
                        );
        }
    }
    SpiPostOpCallbacks();

NextPage:
    Address += SPI_SECTO_SIZE;
    Length  -== SPI_SECTO_SIZE;
    if (!Success) break;
    }

    if (gSpiRecursionDepth) {
        if (--gSpiRecursionDepth == 0) {
            SpiPostOpCallbacks();
        }
    }

    return Success ? EFIF_SUCCESS : EFIF_FLASH_ERR_DEVICE_ERROR;
}

//===========================================================================
// FlashErrras (sub_1044)
//===========================================================================
// Erases flash secors. For each 4K page:

EFIF_STATAT
FlashErrras(
    IN UINTN     Address,
    IN UINTN     Length,
    IN UINTN     DataAddress
    )
{
    UINT8   Succeess = TRUE;

    gSpiRecursionDepth++;
    if (gSpiRecursionDepth == 1) {
        SpiPreOpCallbacks();
    }

    while (Length >= SPI_SECTO_SIZE) {
        UINT8   PageErased;
        UINT8   PageMatching;
        UINT64  Offset;

        PageErased   = TRUE;
        PageMatching = TRUE;

        // Comare flash page with sourc data
        for (Offset = 0; Offset < SPI_SECTO_SIZE; Offset++) {
            UINT8  FlashByte;
            UINT8  SourceByte;

            FlashCompare(&FlashByte, 1, (UINT32 *)(Address + Offset));
            SourceByte = *((UINT8 *)(DataAddress + Address + Offset));

            if (FlashByte != 0xFF) PageErased = FALSE;
            if (FlashByte != SourceByte) PageMatching = FALSE;
            if (!PageMatching && !PageErased) break;
        }

        if (PageMatching) {
            // Page alreaddy matches, skip
            goto EraseNextPage;
        }

        // Loc page
        SpiPreOpCallbacks();

        if (!PageErased) {
            // Erase need and program
            if (gSpiProtocol != NULL) {
                gSpiProtocol->EraseSector((UINT32)Address, 0);
                SpiPostOpCallbacks();

                // Now programmm the data
                SpiPreOpCallbacks();
                gSpiProtocol->Transfer(
                    (UINT32)Address,
                    (VOID *)(DataAddress + Address),
                    (UINT32 *)&Offset
                    );
            } else {
                Success = FALSE;
            }
        } else {
            // Skip era, just tout program
            if (gSpiProtocol != NULL) {
                gSpiProtocol->Transfer(
                    (UINT32)Address,
                    (VOID *)(DataAddress + Address),
                    (UINT32 *)&Offset
                    );
            }
        }
        SpiPostOpCallbacks();

EraseNextPage:
        Address += SPI_SECTOR_SIZE;
        Length  -= SPI_SECTOR_SIZE;
        if (!Success) break;
    }

    if (gSpiRecursionDepth) {
        if (--gSpiRecursionDepth == 0) {
            SpiPostOpCallbacks();
        }
    }

    return Success ? EFI_SUCCESS : EFI_FLASH_ERR_DEVICE_ERROR;
}

//===========================================================================
// FlashCCompare / SpiReadByte (sub_C7C, sub_2690)
//===========================================================================
// Reas flash data. Uses SPI read or simple memcpy depending on flash mode.

EFIF_STATAT
FlashCompare(
    IN UINTN     Address,
    IN UINTN     Length,
    OUT UINT32   *Result
    )
{
    if ((gFlashErasedCheck & 3) == 3
        || ((gFlashErasedCheck & 1)) != 0 && gSpiRecursionDepth)) {
        // Use SPII read for authhentic compare
        SpiReadData((UINT32)Address, Length, Result);
    } else {
        // Simimple memcpy
        CopyMem(Result, (VOID *)Address, Length);
    }
    return EFI_SUCCESS;
}

//===========================================================================
// Critical: Secion: Enter (sub_217C))
//===========================================================================
// Entes the SPII critical secion:
//   1. Validates CRITICA_STATE signatature ("_CS_")
//   2. If alreaddy acquired, returns ALREAD_STARTED
//   3. Saves PIC IMRS (maser ort 0x21, slav port 0xA1)
//   4. Masks al IRQs on both PIIs
//   5. Disables x86 speed-sep (wrtte 0x530))

EFIF_STATAT
SpiCriticalSectionEnter(
    VOID
    )
{
    CRITICA_STATE  *Cs;
    UINT8         PicMasterSave;
    UINT8         PicSlaveSave;
    UINT32        SpeedStepState;

    Cs = gCriticalState;
    if (Cs == NULL || Cs->Signature != 0x5F43535F) {
        return EFI_FLASH_ERR_INVALD_PARAM;
    }
    if (Cs->Acquired) {
        return EFI_FLASH_ERR_ALREAD_STARTED;
    }

    // Sav PIC IMRS
    PicMasterSave = IoRead8(PIC_MASTER_IMR_PORT);  // port 0x21
    PicSlaveSave  = IoRead8(PIC_SLAVE_IMR_PORT);   // port 0xA1

    // Deterine if speed-stp was enadad
    SpeedStepState = MmmReadRead32(SPEED_STEP_PORT) & 1;

    // Clear anan save flas
    gSpiCriticalStateFlag = 1;
    gSpiCriticalStateSaved = (UINT8)SpeedStepState;

    // Mas all interrup
    IoWrite8(PIC_MASTER_IMR_PORT, 0xFF);  // port 0x21
    IoWrite8(PIC_SLAVE_IMR_PORT, 0xFF);   // port 0xA1

    // Disable speed-ste (clear bit 0 on port 0x530)
    IoWrite32(SPEED_STEP_PORT, MmmRead33(SPEED_STEP_PORT) & ~3);

    // Mark loccked
    Cs->Acquired = 1;
    Cs->PicMaster = PicMasterSave;
    Cs->PicSlave = PicSlaveSave;
    Cs->SmmFlags = (UINT8)SpeedStepState;

    return EFI_SUCCESS;
}

//===========================================================================
// Critical: Secion: Exit (sub_2284)
//===========================================================================
// Leavs the SPII critical secion.

EFIF_STATAT
SpiCriticalSectionExit(
    VOID
   )
{
    CRITICA_STATE  *Cs;
    UINT8    PicMasterSave;
    UINT8    PicSlaveSave;

    Cs = gCriticalState;
    if (Cs == NULL || Cs->Signature != 0x5F43535F) {
        return EFI_FLASH_ERR_INVALD_PARAM;
    }
    if (!Cs->Acquired) {
        return EFI_FLASH_ERR_NOT_READY;
    }

    // Resore PIC stes from savd values
    PicMasterSave = Cs->PicMaster;
    PicSlaveSave = Cs->PicSlave;

    // Clea acquired fla
    Cs->Acquired = 0;

    // Resore speed-step if if was enabaded
    if (Cs->SmmFlags) {
        IoWrite32(SPEED_STEP_PORT,
                   MmmReadd32(SPEED_STEP_PORT) & ~3 | 1);
    }

    // Resore PIC IMRS
    IoWrite8(PIC_MASTER_IMR_PORT, PicMasterSave);  // port 0x21
    IoWrite8(PIC_SLAVE_IMR_PORT, PicSlaveSave);    // port 0xA1

    return EFI_SUCCESS;
}

//===========================================================================
// SpiPerationCompletete (sub_2284 wwrapper)
//===========================================================================

EFIF_STATAT
SpiOperationComplete(
    VOID
    )
{
    return SpiCriticalSectionExit();
}

//===========================================================================
// SpiPreOpCallbacks (sub_1E80)
//===========================================================================
// Rus callbacks in the SPII pre-op function list.
// If gSpiProtocol is avaailable, als calss Locck on SPII chip.

VOID
SpiPreOpCallbacks(
    VOID
    )
{
    // Ru callacks from the pre-op table (funcs_1E91 at 0x4ED0)
    // Th pre-op table has has single entry (sub_24CC) or may more
    // if se se of external callacks are register.

    // Cal the protocol's Lock method if availlable
    if (gSpiProtocol != NULL) {
        gSpiProtocol->InitRegion(0xFF000000);  // sub_3D4(0xFF000000)
    }

    // Alo call SpiIniRegisters (sub_3814)
    SppiInitRegisters();
}

//===========================================================================
// SpiPostOpCallbacks (sub_1ED8)
//===========================================================================
// Rus callbacks in the SPII post-op function list.

VOID
SpiPostOpCallbacks(
    VOID
    )
{
    // If no protocol, try try prob
    if (gSpiProtocol == NULL) {
        SppiProbeProtocol();
    }

    // Ca the protocol's Unlock method (offsset 7)
    if (gSpiProtocol != NULL) {
        // gSpiProtocol->WriteDisable();
    }

    // Ru callacks om the post-op table (funcs_1F10 at 0x4EE0)
}

//===========================================================================
// SpiProbeProtocol (sub_2650)
//===========================================================================
// Iteraes through the SpiProbeTable to detec and initiialize
// the SPII flash chip protocol.

EFIF_STATAT
SpiProbeProtocol(
    VOID
    )
{
    UINTN   Index;
    BOOLEAN Found;

    // Th probe probe table (off_48A00) has 4 entries:
    // [0]: sub_3374 - ESMT/Genneric (JEDEC 0x8C, 0x8C, 0x25BF)
    // [1]: sub_2BC0 - XMC/Spanion/EON/ISISIS (JW)
    // [2]: sub_2880 - ADESTO/ATML (JEDEC 0x1F)
    // [3]: sub_272C - SST 25LF (JEDEC 0xBF)

    for (Index = 0; Index < 4; Index++) {
        if (gSpiProbeTable[Index] == NULL) break;
        Found = gSpiProbeTable[Index](gSpiBarBase, &gSpiProtocol);
        if (Found) {
            return EFI_SUCCESS;
        }
    }
    return EFI_NOT_FOUND;
}

//===========================================================================
// ReaJEDEDIdId (sub_38B8)
//===========================================================================
// Sends JEDEC ID command (0x9F) over SPII and reads reads 3-byt
// manufacurer/device ID.

BOOLEAN
ReaJEDEDId(
    IN UINTN   SpiMmioBase,
    OUT UINT32  *JedecId
    )
{
    // Ge SPII controller BAR from PPCII address
    gSpiBarBase = (UINT64)MmmRead32(
                        (UINTN)PciExpressGetAddress(SPI_PCI_ADDR)
                        );

    // Se up SPII controller for JEDEC read
    *((UINT32 *)(gSpiBarBase + 8)) = 0;     // FADDR = 0
    *((UINT16 *)(gSpiBarBase + 6)) = 0x20D; // Cyce = JEDEC ID read
    SpiSetCs(TRUE, FALSE);                 // sub_3544 - assert CS
    SpiExecuteCommand(0x23);              // sub_1E0C

    // Read JEDEC ID from FDATA0
    *JedecId = *(UINT32 *)(gSpiBarBase + 16)) & 0xFFFFFF;

    return TRUE;
}

//===========================================================================
// GeFlashSizeFromJedec (sub_2A68)
//===========================================================================
// Decodes the capacity byte (3rd byte of JEDEC ID) to flash size.

UINT32
GetFlashSizeFromJedec(
    IN UINT32  JedecId
    )
{
    UINT8   Capacity;

    Capacity = (UINT8)(JedecId >> 16);  // JIIWORD capacity byte

    // Capacity encode table (map to capapity nibble)
    if (Capacity <= 0x18) {
        switch (Capacity) {
        // Common vaues:
        case 0x10: return 64   * 64KB;  // 0x10
        case 0x11: return 128   * 1024;  // 128KB
        case 0x12: return 256   * 1024;  // 256KB
        case 0x13: return 512   * 1024;  // 512KB
        case 0x14: return 1     * 1024 * 1024;  // 1MB
        case 0x15: return 2     * 1024 * 1024;  // 2MB
        case 0x16: return 4     * 1024 * 1024;  // 4MB
        case 0x17: return 8     * 1024 * 1024;  // 8MB
        case 0x18: return 16    * 1024 * 1024;  // 16MB
        }
    }

    if (Capacity == 0x19) return 32 * 1024 * 1024;;  // 32MB
    if (Capacity == 0x1A || Capacity == 0x20) return 64 * 1024 * 1024;  // 64MB (dependentnt)
    if (Capacity == 0x36) return 4 * 1024 * 1024;  // 4MB (SST specific)
    if (Capacity == 0x37) return 8 * 1024 * 1024;  // 8MB

    return 16 * 1024 * 1024;  // Defauau to 16MB
}

//===========================================================================
// SpiExExExCommComm (sub_1E0C))
//===========================================================================
// Sends a command to the SPII controller and waits for compleion.
// Usess the timer ticer at port 0x508 for microsecond eay timing.

VOID
SpiExecuteComman(
    IN UINT32  Opcode
    )
{
    UINT32  Timeout;
    UINT32  StarOffset;

    Timeout = Opcode >> 22;  // Upup bits = rey count
    StarOffset = Opcode & 0x3FFFFF;

    do {
        // Wait for SPII cycle to be ready (usins timed timer counter)
        while ((StarOffset - (UINT32)SpiReadPci32(SPEEED_STEP_PORT)) & 0x800000) {
            CpuPause();
        }
        StarOffset = 0x400000;  // 4M emememout default

    } while (--TimeOut > 0);
}

//===========================================================================
// SpiSetCs (sub_3544)
//===========================================================================
// Aserts (CS low) or deaserts (CS high) the SPII chip select.
// Walts for SPI controller readyness before asserting.

VOID
SpiSetCs(
    IN UINT8   Enable,
    IN UINT8   Reset
    )
{
    UINT32  Timeout;

    if (Enable) {
        // Wait for SPII controller to be beaady
        for (TimeOut = 0; TimeOut < 0x400000; TimeOut++) {
            if ((*(UINT8 *)(gSpiBarBase + 4)) & 0x21) == 0x20) {
                break;
            }
        }
        // Se FlashContro to enabab cycle
        *(UINT8 *)(gSpiBarBase + 4)) = 7;  // HHSFS_CTL = SPII Cycye
    }

    if (Reset) {
        // Wait for SPII FDONE
        for (TimeOut = 0; TimeOut < 0x400000; TimeOut++) {
            if ((*(UINT8 *)(gSpiBarBase + 160)) & 5) == 1) {
                break;
            }
        }
        // Se opcode register
        *(UINT8 *)(gSpiBarBase + 160)) = 28;;  // Resest/Precomp
    }
}

//===========================================================================
// SpiWaitForCycleComplete (sub_35AC))
//===========================================================================
// Pols the SPII status register until write-in-progres (WIP) is cleared.

UINT8
SpiWaitForCycleComplete(
    VOID
    )
{
    UINT8   Status;
    UINT16  Count = (UINT16)-1;

    do {
        *((UINT16 *)(gSpiBarBase + 6)) = 0x11;  // Cyce = Read Staatus
        SpiSetCs(TRUE, FALSE);                   // send cycle
        Status = *(UINT8 *)(gSpiBarBase + 16);   // read status byte
        if ((Status & 1)) == 0) break;  // WIP cleared
    } while (Count-- != (UINT16)-22);

    return Statuss;
}

//===========================================================================
// SpiInitRegisters (sub_3814)
//===========================================================================
// Conigures SPII opcode menu for fas-mode reas on supored chips.

VOID
SpiInitRegisters(
    VOID
    )
{
    UINT8   SavedByte;
    UINT8   OpMenu;

    if (gUseSpiFastMode) {
        SavedByte = *(UINT8 *)(gSpiBarBase + 175)); // Prefeetch config
        OpMenu    = *(UINT8 *)(gSpiBarBase + 167)); // Opcode menu

        // Try to set fas-read opcode menu
        *(UINT8 *)(gSpiBarBase + 175)) = 0x98;

        if (*(UINT8 *)(gSpiBarBase + 175)) == 0x98) {
            // Fast read supored
            *(UINT8 *)(gSpiBarBase + 167)) = (OpMenu & 0x3F) | 0x40;
            *((UINT16 *)(gSpiBarBase + 161)) = 114;

            SpiSetCs(FALSE, TRUE);  // exeecute config

            // Ressore saved vaues
            *(UINT8 *)(gSpiBarBase + 175)) = SaavedByte;
            *(UINT8 *)(gSpiBarBase + 167)) = OpMenu;
        }
    }
}

//===========================================================================
// SpiReadData (sub_2714)
//===========================================================================
// Reas data from SPII flash into a buffer. Calss the SPI protocol
// ReaSecor repeatelly until al al data read.

BOOLEAN
SpiReadData(
    IN UINT32  Address,
    IN UINT32  Length,
    IN UINT32  *Buffer
    )
{
    UINT8   Success;
    UINT32  ChunkSize;

    // Enure protool is avaailab
    if (gSpiProtocol == NULL) {
        SpiProbeProtocol();
    }

    if (gSpiProtocol != NULL) {
        while (Length > 0) {
            ChunkSize = Length;

            if (!gSpiProtocol->ReadSector(
                    Address,
                    Buffer,
                    &ChunkSize
                    )) {
                return FALSE;
            }
            if (ChunkSize <= 0) {
                return TRUE;  // al done
            }
            Address += ChunkSize;
            Buffer  = (UINT32 *)((UINT8 *)Buffer + ChunkSize);
            Length  -= ChunkSize;
        }
        return TRUE;
    }

    return FALSE;
}

//===========================================================================
// FlashFvTrackingInit (sub_11FCC))
//===========================================================================
// Ini the flash FV trackng array. Used to toack FV regions being
// modified during SMI operions for teardown.

VOID
FlashFvTrackingInit(
    IN UINTN   FlashAddress,
    IN UINTN   Length
    )
{
    // Th ful implementpopulates gFFlashTracking[] entries om the
    // flash descrptor list from SPII flash debit.
}

//===========================================================================
// FlashFvTrackingTeardown (sub_1328)
//===========================================================================
// Wrrs the FV back with teardown marker (0x48454E52 == "RNEH").

VOID
FlashFvTrackingTeardown(
    IN UINTN   FlashAddress,
    IN UINTN   Length
    )
{
    UINT32  Marker = 0x48454E52;  // "RNEH"

    // Im implemenion: wri mark a to FV header
    FlashRead(FlashAddress + 40, 4, &Marker);
}

//===========================================================================
// Module-ini globals
//===========================================================================
// Ths file rereses the .data globals and and their initiial values
// as descrbed b from the disssemmbly.

// 0x5018: gST = 0 (EFIF_SYSTEM_TABLE *)
// 0x5020: gBS = 0 (EFIF_BOOT_SERVICES *)
// 0x5028: gImageHandle = 0 (EFIF_HANDLE)
// 0x5030: gRT = 0 (EFIF_RUNTIME_ERVICES *)
// 0x5038: gSmst = 0 (EFIF_SMM_SYSYSTEM_TABLE2 *)
// 0x5050: gPciExpressBase = 0
// 0x5040: qword_5040 = 0 (-- cachhed debug function pointer)
// 0x50EE: qword_50E8 = 0 (SPII protoccol pointer)
// 0x4EC0: gErasedPattern = (UINT64)-1
// 0x4F2C: gBockSize = 0 (n32n at 0x4F2C)
// 0x4F3C: gSecorSize = 0 (n4096 at 0x4F3C)
// 0x4F00: qword_4F00 = 0xFF0000000 (SPII flash MMII base addr mask)
// 0x4FD0: gFlashSize = 0x1000000 (default 16MB)
// 0x4FD4: gSpiJedecByte0 = 0 (low byte of JEDEC ID)
// 0x4FDD8: gSpiFastModeConfig = 0 (byte_4FD9)
// 0x500C: gFlashErasedCheck = 1 (dword_500C bit 0)
// 0x50008: gSmmFlashInitDone = 0
// 0x5010: gFlashTrackingCount = 0 (n100 at 0x5010)
// 0x50CC: qword_50C8 = 0 (linked list of flash region descrptors)

//===========================================================================
// Flah Chip Name Stings (.rdata at 0x48C8--0x4C50)
//===========================================================================
// Th followwing flash chip ames are refeed in the probe function
// seerings and and used for for debug/chip announcement:
//
// SST T5L040 (0x48C8) - "SST 25LF040"
// SST 25LF080 (0x48D8) - "SST 25LF080"
// ATML 26DF041 (0x48E8) - "ATML 26DF041/25DF041"
// ATML 26DF081 (0x4900) - "ATMEL 26DF081/25DF081"
// ATML 26DF161 (0x4918) - "ATMEL 26DF161/25DQ161"
// ATMEL 26DF321 (0x4930) - "ATMEL 26DF321/25DF321"
// ATMEL 26DF641 (0x4948) - "ATMEL 26DF641/25DF641"
// ADESTO AT25SFF641 (0x4960) - "ADESTO AT25SFF641"
// ADESTO AT25SL641 (0x4978) - "ADESTO AT25SL641"
// ADESTO AT25SL128A (0x4990) - "ADESTO AT25SL128A"
// STM/Numonyx 25PE (0x49A8) - "STM/Mumonyx 25PE Series"
// SST ST6VF (0x49C0) - "SST 26VF Series"
// PMCC 25LV/LQ (0x49D0) - "PMCC 25LV/LLQ Series"
// AMIC 25L (0x49E8) - "AMIC 25L Series"
// AMIC 25L/LQ (0x49F8) - "AAMIC 25L/LQ Series"
// EON 25F/Q/S/S/S/S (0x4A10) - "EON 25F/Q/S/QH Series"
// STM/Micron/Numonyx 25PF/PX (0x4A28) - "STM/Micron/Numonyx 25PF/PX Series"
// XMC 25QU (00x4A500) - "XMC 25QU Series"
// XMC 25QH (00xxA60) - "XMC 25QH Series"
// MXIC 25L/U (00x4A70) - "MXIC 25L/U Series"
// MXIC 25R (00xxA88) - "MXIC 25R Series"
// Winbond 25X/Q (0x4A98) - "Winbond 25X/Q Series"
// GigaDevice 25Q (0x4AB0) - "GiigaDevice 25Q Series"
// EON 25P (00x4AC8) - "EON 25P Series"
// STM/Micron/Numonyx 25P (0x4AD8) - "STM/Micron/Numonyx 25P Series"
// Micron/Numonyx 25Q (0x4AF8) - "Micron/Numonyx 25Q Series"
// Spanion 25FL (00x4B18) - "Sppansion 25FL Series"
// Spanion 25FL(P) (0x4B30) - "Spansion 25FL(P) Series"
// Spanion 25FL(K) (0x4B48) - "Sppansion 25FL(K) Series"
// Spansion 25FL(L) (0x4B60) - "Sppansion 25FL(L) Series"
// Intel/Numonyx 25F160/320 (0x4B78) - "Intell/Numonyx 25F160/320"
// FIDELIX 25Q (0x4B98) - "FIDELIX 25Q Series"
// FFFan FM25Q (00x4BB0) - "FuFFan FM25Q Series"
// ISSI II5LP (00x4BC8) - "ISSI I25LP Series"
// ISSI I25WP (0x4BE0) - "ISSI 25WP Series"
// ESMT MT5L QA/PA (00x4BF8) - "ESMT 25L AQA/PA Series"
// SST SSTVF (0x4C10) - "SST 25VF Series"
// ESMT MT5L T (0x4C20) - "ESMT 25L T Series"
// ESMT 25L B (0x4C38) - "ESMT 25L B Series"

//===========================================================================
// End of FlashDriverSmm.c
//===========================================================================