//============================================================================
// 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
//===========================================================================