/** @file
FPGA Initialization DXE Driver -- FpgaDxe
This module implements FPGA initialization for the Purley platform (HR650X).
It performs the following tasks:
1. Reads FPGA configuration from a HOB (Hand-Off Block) or creates one
from UEFI variables ("FpgaSocketConfig").
2. Locates PCI(e) root bridge I/O and USRA (Universal Segment Resource
Access) protocols for MMIO/PCI config space access.
3. Initializes S3 boot script services to save/restore FPGA register
state across S3 resumes.
4. On ReadyToBoot, enumerates FPGA devices per active socket, programs
PCI bus/subordinate numbers, FME BARs, temperature thresholds, HSSI
configuration, and miscellaneous FPGA control registers.
5. Triggers a software SMI to finalize FPGA initialization in SMM.
Source path (original build):
e:\hs\PurleySktPkg\Dxe\FpgaInit\FpgaDxe\FpgaDxe.c
e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleySktPkg\Dxe\FpgaInit\FpgaDxe\FpgaDxe\DEBUG\AutoGen.c
Build config: VS2015, X64, DEBUG
Module size: 0x6D80 bytes (28 KB)
Copyright (C) 2026, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "FpgaDxe.h"
//
// ---------------------------------------------------------------------------
// Global variable storage
// ---------------------------------------------------------------------------
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
VOID *gDS = NULL; // qword_67B0
VOID *mPcd = NULL; // qword_67A0
VOID *mPciUsra = NULL; // qword_67B8
VOID *mUsra = NULL; // qword_6808
VOID *mHobList = NULL; // qword_67C0
VOID *mEventDxeSmmReadyToLock = NULL; // qword_67E0
VOID *gEfiSmmReadyToLockProtocol = NULL; // qword_67E8
UINTN gPciExpressBaseAddress = 0; // qword_67C8
VOID *mSmmCommunication = NULL; // qword_6818
VOID *mSmmCommRegion = NULL; // qword_6810
//
// FPGA HOB configuration data (from unk_6880)
//
UINT8 gFpgaConfigValid; // byte_6880 -- non-zero if HOB is valid
UINT8 gFpgaSktActive; // byte_6883 -- bitmask of active sockets
UINT8 gFpgaSktConfig5; // byte_6885
UINT8 gFpgaSktConfig6; // byte_6886
UINT8 gFpgaSktConfig31; // byte_689F
UINT8 gFpgaSktConfig32; // byte_68A0
UINT8 gFpgaDxeUnlock; // byte_68A5 -- if ==1, skip DXE lock config
UINT8 gFpgaSktConfigBuf[7]; // unk_6887 (per-socket fields)
UINT8 gFpgaSktTempThresh[2]; // byte_689B, byte_689C
UINT64 gFmeBar[FPGA_MAX_SOCKET]; // qword_6840 (4 entries)
UINT64 gGlobalNvsArea; // qword_68A8
//
// S3 boot script context
//
UINT8 *gS3BootScriptBuf = NULL; // qword_6828
UINT8 *gS3BootScriptBackup = NULL; // qword_6830
UINT8 gBootScriptFlag1 = 0; // byte_67D8
UINT8 gBootScriptFlag2 = 0; // byte_6800
VOID *gSmmReadyToLockEvent1= NULL; // qword_67F0
VOID *gSmmReadyToLockEvent2= NULL; // qword_67D0
VOID *gSmmReadyToLockEvent3= NULL; // qword_67F8
//
// LockBox GUID storage (mutable runtime data, unk_6740/60/70/50)
//
UINT8 gLockBoxGuid1[16]; // qword_6740
UINT8 gLockBoxGuid2[16]; // qword_6760
UINT8 gLockBoxGuid3[16]; // qword_6770
// ---------------------------------------------------------------------------
// Internal helper prototypes
// ---------------------------------------------------------------------------
STATIC
VOID
InternalAssert (
CONST CHAR8 *FileName,
UINTN LineNumber,
CONST CHAR8 *Description
);
STATIC
EFI_STATUS
GetDxeServicesTable (
VOID
);
STATIC
EFI_STATUS
GetPcdProtocol (
VOID
);
STATIC
EFI_STATUS
GetMmPciBaseProtocol (
VOID
);
STATIC
EFI_STATUS
GetHobList (
VOID
);
STATIC
EFI_STATUS
GetUsraProtocol (
VOID
);
STATIC
EFI_STATUS
GetSmmCommunication (
VOID
);
STATIC
VOID
S3BootScriptEventNotify (
IN EFI_EVENT Event,
IN VOID *Context
);
STATIC
VOID
S3BootScriptBackupNotify (
IN EFI_EVENT Event,
IN VOID *Context
);
STATIC
BOOLEAN
CompareGuid (
IN CONST EFI_GUID *Guid1,
IN CONST EFI_GUID *Guid2
);
// ---------------------------------------------------------------------------
// Internal: ZeroMem / CopyMem (base library primitives)
// ---------------------------------------------------------------------------
/**
ZeroMem: fills a buffer with zeros.
@param[out] Buffer Pointer to buffer to zero.
@param[in] Length Number of bytes to zero.
**/
STATIC
VOID *
InternalZeroMem (
OUT VOID *Buffer,
IN UINTN Length
)
{
//
// Zero in 8-byte chunks, then remaining bytes.
//
ZeroMem (Buffer, Length);
return Buffer;
}
/**
CopyMem: copies one buffer to another, handling overlap correctly.
@param[out] DestinationBuffer Pointer to the destination buffer.
@param[in] SourceBuffer Pointer to the source buffer.
@param[in] Length Number of bytes to copy.
**/
STATIC
VOID *
InternalCopyMem (
OUT VOID *DestinationBuffer,
IN CONST VOID *SourceBuffer,
IN UINTN Length
)
{
return CopyMem (DestinationBuffer, SourceBuffer, Length);
}
// ---------------------------------------------------------------------------
// 1 -- Library constructor: UefiBootServicesTableLib, UefiRuntimeServicesTableLib
// ---------------------------------------------------------------------------
/**
Initialize global UEFI boot/runtime services table pointers.
Called from the DXE driver entry point before any other initialization.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
**/
STATIC
VOID
InitializeLibServices (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
gImageHandle = ImageHandle;
ASSERT (gImageHandle != NULL);
gSystemTable = SystemTable;
ASSERT (gSystemTable != NULL);
gBootServices = SystemTable->BootServices;
ASSERT (gBootServices != NULL);
gRuntimeServices = SystemTable->RuntimeServices;
ASSERT (gRuntimeServices != NULL);
}
// ---------------------------------------------------------------------------
// 2 -- DxeServicesTableLib constructor
// ---------------------------------------------------------------------------
/**
Locates and caches the DXE Services Table pointer.
@retval EFI_SUCCESS gDS is now valid.
@retval other DXE Services Table not found.
**/
STATIC
EFI_STATUS
GetDxeServicesTable (
VOID
)
{
EFI_STATUS Status;
Status = gBS->LocateProtocol (
&gEfiDxeServicesTableGuid,
NULL,
&gDS
);
ASSERT_EFI_ERROR (Status);
ASSERT (gDS != NULL);
return Status;
}
// ---------------------------------------------------------------------------
// 3 -- PcdLib constructor
// ---------------------------------------------------------------------------
/**
Locates and caches the PCD protocol.
@retval EFI_SUCCESS mPcd is valid.
@retval other PCD protocol not found.
**/
STATIC
EFI_STATUS
GetPcdProtocol (
VOID
)
{
EFI_STATUS Status;
if (mPcd != NULL) {
return EFI_SUCCESS;
}
Status = gBS->LocateProtocol (
&gEfiPcdProtocolGuid,
NULL,
&mPcd
);
ASSERT_EFI_ERROR (Status);
ASSERT (mPcd != NULL);
return Status;
}
// ---------------------------------------------------------------------------
// 4 -- DxeMmPciBaseLib constructor
// ---------------------------------------------------------------------------
/**
Locates and caches the MM PCI USRA protocol.
@retval EFI_SUCCESS mPciUsra is valid.
@retval other Protocol not found.
**/
STATIC
EFI_STATUS
GetMmPciBaseProtocol (
VOID
)
{
EFI_STATUS Status;
if (mPciUsra != NULL) {
return EFI_SUCCESS;
}
Status = gBS->LocateProtocol (
&gEfiMmPciBaseProtocolGuid,
NULL,
&mPciUsra
);
ASSERT_EFI_ERROR (Status);
ASSERT (mPciUsra != NULL);
return Status;
}
// ---------------------------------------------------------------------------
// 5 -- DxeHobLib constructor
// ---------------------------------------------------------------------------
/**
Locates and caches the HOB list pointer from the DXE Services Table.
@retval EFI_SUCCESS mHobList is valid.
@retval other HOB list not found.
**/
STATIC
EFI_STATUS
GetHobList (
VOID
)
{
EFI_STATUS Status;
if (mHobList != NULL) {
return EFI_SUCCESS;
}
Status = EfiGetSystemConfigurationTable (
&gEfiHobListGuid,
&mHobList
);
ASSERT_EFI_ERROR (Status);
ASSERT (mHobList != NULL);
return Status;
}
// ---------------------------------------------------------------------------
// 6 -- Guid comparison helper
// ---------------------------------------------------------------------------
/**
Compares two EFI GUIDs.
@param[in] Guid1 Pointer to first GUID.
@param[in] Guid2 Pointer to second GUID.
@retval TRUE The GUIDs are equal.
@retval FALSE The GUIDs differ.
**/
STATIC
BOOLEAN
CompareGuid (
IN CONST EFI_GUID *Guid1,
IN CONST EFI_GUID *Guid2
)
{
UINT64 *Ptr1;
UINT64 *Ptr2;
Ptr1 = (UINT64 *)Guid1;
Ptr2 = (UINT64 *)Guid2;
return (Ptr1[0] == Ptr2[0] && Ptr1[1] == Ptr2[1]);
}
// ---------------------------------------------------------------------------
// 7 -- HOB traversal helpers
// ---------------------------------------------------------------------------
/**
Returns the first HOB of type EFI_HOB_TYPE_GUID_EXT.
@param[in] Guid Pointer to the GUID to match.
@return Pointer to the HOB if found, NULL otherwise.
**/
STATIC
VOID *
GetFirstGuidHob (
IN EFI_GUID *Guid
)
{
EFI_PEI_HOB_POINTERS Hob;
if (mHobList == NULL) {
return NULL;
}
Hob.Raw = mHobList;
while (Hob.Header->HobType != EFI_HOB_TYPE_END_OF_HOB_LIST) {
if (Hob.Header->HobType == EFI_HOB_TYPE_GUID_EXT) {
if (CompareGuid (Guid, (EFI_GUID *)(Hob.Raw + 8))) {
return Hob.Raw;
}
}
Hob.Raw = (UINT8 *)Hob.Raw + Hob.Header->HobLength;
}
return NULL;
}
// ---------------------------------------------------------------------------
// 8 -- Debug library: DebugPrint / DebugAssert wrappers
// ---------------------------------------------------------------------------
/**
Internal debug print wrapper. Writes to the debug console.
@param[in] ErrorLevel Debug error level.
@param[in] Format Print format string.
@param[in] ... Variable arguments.
@return The status from the underlying debug protocol, or 0 if not available.
**/
UINT8
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Marker;
EFI_DEBUG_PROTOCOL *DebugProtocol;
DebugProtocol = (EFI_DEBUG_PROTOCOL *)gDebugProtocol;
if (DebugProtocol == NULL) {
return 0;
}
//
// Check whether this error level should be displayed and whether
// the debug protocol supports it.
//
if (DebugProtocol->IsValid (ErrorLevel)) {
VA_START (Marker, Format);
DebugProtocol->Write (ErrorLevel, Format, Marker);
VA_END (Marker);
}
return 0;
}
// ---------------------------------------------------------------------------
// 9 -- PCI Express MMIO access
// ---------------------------------------------------------------------------
/**
Translates a PCI Express address (in the ECAM range) to the MMIO base
and reads/writes via the PCI Express memory-mapped configuration space.
Address format: [31:28] unused | [27:20] Bus | [19:15] Device | [14:12] Func | [11:0] Register
@param[in] Address PCI Express address (up to 28 bits valid).
@return The MMIO virtual address of the config register.
**/
STATIC
UINT8 *
PciExpressGetAddr (
IN UINTN Address
)
{
ASSERT ((Address & ~0xFFFFFFF) == 0);
return (UINT8 *)(gPciExpressBaseAddress + Address);
}
/**
Gets PCD value (UINT64).
@param[in] TokenNumber PCD token number.
@return The PCD value.
**/
UINT64
PcdGet64 (
IN UINTN TokenNumber
)
{
UINT64 Value;
if (mPcd == NULL) {
return 0;
}
//
// mPcd->Get64 (TokenNumber)
//
Value = ((PCD_PROTOCOL *)mPcd)->Get64 (TokenNumber);
return Value;
}
// ---------------------------------------------------------------------------
// 10 -- USRA (Universal Segment Resource Access) wrappers
// ---------------------------------------------------------------------------
/**
Reads 32-bit via USRA MMIO operation.
Builds a USRA descriptor with operation type 512 (MMIO read)
and calls mUsra->Read().
@param[in] Socket Socket index.
@param[in] Bus Bus number.
@param[in] DevFunc Device and function (encoded).
@param[in] Offset Register offset.
@return The 32-bit value read.
**/
UINT32
UsraReadMmio (
IN UINT8 Socket,
IN UINT8 Bus,
IN UINT8 DevFunc,
IN UINT16 Offset
)
{
USRA_DESCRIPTOR Descriptor;
UINT32 Value;
Descriptor.OpCode = 0;
Descriptor.Socket = Socket;
Descriptor.OpType = USRA_OP_READ_MMIO; // 512
Descriptor.Address = (Offset & 0xFFF) |
((Socket & 7) |
(8 * (DevFunc & 0x1F | (32 * Bus)))) << 12;
((USRA_PROTOCOL *)mUsra)->Read (&Descriptor, &Value);
return Value;
}
/**
Writes 32-bit via USRA MMIO operation.
Builds a USRA descriptor with operation type 8704 (MMIO write)
and calls mUsra->Write().
@param[in] Socket Socket index.
@param[in] Bus Bus number.
@param[in] DevFunc Device and function (encoded).
@param[in] Offset Register offset.
@param[in] Value Value to write.
**/
VOID
UsraWriteMmio (
IN UINT8 Socket,
IN UINT8 Bus,
IN UINT8 DevFunc,
IN UINT16 Offset,
IN UINT32 Value
)
{
USRA_DESCRIPTOR Descriptor;
Descriptor.OpCode = 0;
Descriptor.Socket = Socket;
Descriptor.OpType = USRA_OP_WRITE_MMIO; // 8704
Descriptor.Address = (Offset & 0xFFF) |
((Socket & 7) |
(8 * (DevFunc & 0x1F | (32 * Bus)))) << 12;
((USRA_PROTOCOL *)mUsra)->Write (&Descriptor, &Value);
}
/**
Reads 32-bit via USRA PCI config operation.
Builds a USRA descriptor with operation type 0x2000 (PCI config read)
and calls mUsra->Read().
@param[in] Socket Socket index.
@param[in] Bus Bus number.
@param[in] DevFunc Device and function (encoded).
@param[in] Offset Register offset.
@return The 32-bit value read.
**/
UINT32
UsraReadPciCfg (
IN UINT8 Socket,
IN UINT8 Bus,
IN UINT8 DevFunc,
IN UINT16 Offset
)
{
USRA_DESCRIPTOR Descriptor;
UINT32 Value;
Descriptor.OpCode = 0;
Descriptor.Socket = Socket;
Descriptor.OpType = USRA_OP_READ_PCI; // 0x2000
Descriptor.Address = (Offset & 0xFFF) |
((DevFunc & 0x1F | (32 * Bus)) << 15);
((USRA_PROTOCOL *)mUsra)->Read (&Descriptor, &Value);
return Value;
}
/**
Reads byte via USRA PCI config operation (non-aligned access).
Builds a USRA descriptor with operation type 0 (PCI config read, no alignment)
and calls mUsra->Read().
@param[in] Socket Socket index.
@param[in] Bus Bus number.
@param[in] RegAddress Full register address (bus, device, function, offset).
@return The 8-bit value read.
**/
UINT8
UsraReadPciCfgByte (
IN UINT8 Socket,
IN UINT8 Bus,
IN UINT32 RegAddress
)
{
USRA_DESCRIPTOR Descriptor;
UINT8 Value;
Descriptor.OpCode = 0;
Descriptor.Socket = Socket;
Descriptor.OpType = 0; // PCI byte read
Descriptor.Address = (RegAddress & 0xFFF) | (Bus << 20);
((USRA_PROTOCOL *)mUsra)->Read (&Descriptor, &Value);
return Value;
}
// ---------------------------------------------------------------------------
// 11 -- SMM LockBox protocol locate
// ---------------------------------------------------------------------------
/**
Locates the SMM Communication protocol for LockBox operations.
@return Pointer to SMM Communication protocol, or NULL if not found.
**/
STATIC
SMM_COMMUNICATION_PROTOCOL *
GetSmmCommunication (
VOID
)
{
EFI_STATUS Status;
if (mSmmCommunication == NULL) {
Status = gBS->LocateProtocol (
&gEfiSmmCommunicationProtocolGuid,
NULL,
&mSmmCommunication
);
if (EFI_ERROR (Status)) {
mSmmCommunication = NULL;
}
}
return (SMM_COMMUNICATION_PROTOCOL *)mSmmCommunication;
}
/**
Locates the SMM Communication Region Table for LockBox buffer sharing.
@return Virtual address of the SMM communication buffer, or NULL.
**/
STATIC
VOID *
GetSmmCommRegion (
VOID
)
{
EFI_STATUS Status;
EFI_SMM_COMMUNICATION_REGION_TABLE *RegionTable;
UINT32 Index;
UINT32 NumberOfEntries;
UINT8 *Entry;
if (mSmmCommRegion != NULL) {
return mSmmCommRegion;
}
Status = EfiGetSystemConfigurationTable (
&gEfiSmmCommunicationRegionTableGuid,
(VOID **)&RegionTable
);
if (EFI_ERROR (Status)) {
return NULL;
}
ASSERT (RegionTable != NULL);
NumberOfEntries = RegionTable->NumberOfEntries;
Entry = (UINT8 *)&RegionTable->Descriptor[0];
for (Index = 0; Index < NumberOfEntries; Index++) {
if (RegionTable->Descriptor[Index].Type == SMM_COMM_REGION_TYPE_STANDARD &&
(RegionTable->Descriptor[Index].PhysicalStart << 12) >= 0x50) {
mSmmCommRegion = (VOID *)(UINTN)RegionTable->Descriptor[Index].CpuStart;
break;
}
Entry += RegionTable->Descriptor[Index].DescriptorSize;
}
return mSmmCommRegion;
}
// ---------------------------------------------------------------------------
// 12 -- SMM LockBox: SaveLockBox
// ---------------------------------------------------------------------------
/**
Saves a buffer to the SMM LockBox.
This function communicates with SMM via the SMM Communication protocol
to store a buffer identified by a GUID for later restoration (e.g., S3 resume).
@param[in] Guid GUID identifying the LockBox entry.
@param[in] Buffer Pointer to the data to save.
@param[in] Length Length of the data in bytes.
@retval EFI_SUCCESS The LockBox save succeeded.
@retval EFI_INVALID_PARAMETER Guid, Buffer, or Length is NULL.
@retval EFI_UNSUPPORTED SMM Communication protocol not available.
@retval other Error from SMM communication.
**/
EFI_STATUS
SaveLockBox (
IN GUID *Guid,
IN VOID *Buffer,
IN UINTN Length
)
{
SMM_COMMUNICATION_PROTOCOL *SmmComm;
VOID *CommBuffer;
LOCK_BOX_COMMAND *Command;
EFI_STATUS Status;
UINTN CommSize;
DEBUG ((DEBUG_INFO, "SmmLockBoxDxeLib SaveLockBox - Enter\n"));
if (Guid == NULL || Buffer == NULL || Length == 0) {
return EFI_INVALID_PARAMETER;
}
SmmComm = GetSmmCommunication ();
if (SmmComm == NULL) {
return EFI_UNSUPPORTED;
}
CommBuffer = GetSmmCommRegion ();
if (CommBuffer == NULL) {
CommBuffer = &mSmmCommStackBuffer; // fallback to stack buffer
}
//
// Initialize the LockBox command structure
//
InternalCopyMem (CommBuffer, &gLockBoxGuid1, 16);
((LOCK_BOX_COMMAND *)CommBuffer)->HeaderSize = 48;
((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus = (UINT64)-1;
((LOCK_BOX_COMMAND *)CommBuffer)->Command = LOCK_BOX_SAVE;
((LOCK_BOX_COMMAND *)CommBuffer)->DataSize = 48;
InternalCopyMem ((UINT8 *)CommBuffer + 40, Guid, 16);
((LOCK_BOX_COMMAND *)CommBuffer)->Buffer = Buffer;
((LOCK_BOX_COMMAND *)CommBuffer)->Length = Length;
CommSize = 72;
Status = SmmComm->Communicate (SmmComm, CommBuffer, &CommSize);
ASSERT_EFI_ERROR (Status);
DEBUG ((DEBUG_INFO, "SmmLockBoxDxeLib SaveLockBox - Exit (%r)\n",
((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus));
return ((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus;
}
// ---------------------------------------------------------------------------
// 13 -- SMM LockBox: SetLockBoxAttributes
// ---------------------------------------------------------------------------
/**
Sets attributes on an existing LockBox entry.
@param[in] Guid GUID of the LockBox entry.
@param[in] Attributes 64-bit attribute flags to set.
@retval EFI_SUCCESS Attributes set successfully.
@retval EFI_INVALID_PARAMETER Guid is NULL.
@retval EFI_UNSUPPORTED SMM Communication protocol not available.
@retval other Error from SMM communication.
**/
EFI_STATUS
SetLockBoxAttributes (
IN GUID *Guid,
IN UINT64 Attributes
)
{
SMM_COMMUNICATION_PROTOCOL *SmmComm;
VOID *CommBuffer;
LOCK_BOX_COMMAND *Command;
EFI_STATUS Status;
UINTN CommSize;
DEBUG ((DEBUG_INFO, "SmmLockBoxDxeLib SetLockBoxAttributes - Enter\n"));
if (Guid == NULL) {
return EFI_INVALID_PARAMETER;
}
SmmComm = GetSmmCommunication ();
if (SmmComm == NULL) {
return EFI_UNSUPPORTED;
}
CommBuffer = GetSmmCommRegion ();
if (CommBuffer == NULL) {
CommBuffer = &mSmmCommStackBuffer;
}
InternalCopyMem (CommBuffer, &gLockBoxGuid1, 16);
((LOCK_BOX_COMMAND *)CommBuffer)->HeaderSize = 40;
((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus = (UINT64)-1;
((LOCK_BOX_COMMAND *)CommBuffer)->Command = LOCK_BOX_SET_ATTR;
((LOCK_BOX_COMMAND *)CommBuffer)->DataSize = 40;
InternalCopyMem ((UINT8 *)CommBuffer + 40, Guid, 16);
((LOCK_BOX_COMMAND *)CommBuffer)->Attributes = 1;
CommSize = 64;
Status = SmmComm->Communicate (SmmComm, CommBuffer, &CommSize);
ASSERT_EFI_ERROR (Status);
DEBUG ((DEBUG_INFO, "SmmLockBoxDxeLib SetLockBoxAttributes - Exit (%r)\n",
((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus));
return ((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus;
}
// ---------------------------------------------------------------------------
// 14 -- SMM LockBox: RestoreLockBox
// ---------------------------------------------------------------------------
/**
Restores a buffer from the SMM LockBox.
@param[in] Guid GUID of the LockBox entry.
@param[out] Buffer Pointer to the buffer receiving the data (optional).
@param[in,out] Length On input, size of Buffer. On output, actual restored size.
@retval EFI_SUCCESS LockBox restored successfully.
@retval EFI_UNSUPPORTED SMM Communication protocol not available.
@retval other Error from SMM communication.
**/
EFI_STATUS
RestoreLockBox (
IN GUID *Guid,
OUT VOID *Buffer OPTIONAL,
IN OUT UINTN *Length OPTIONAL
)
{
SMM_COMMUNICATION_PROTOCOL *SmmComm;
VOID *CommBuffer;
LOCK_BOX_COMMAND *Command;
EFI_STATUS Status;
UINTN CommSize;
DEBUG ((DEBUG_INFO, "SmmLockBoxDxeLib RestoreLockBox - Enter\n"));
SmmComm = GetSmmCommunication ();
if (SmmComm == NULL) {
return EFI_UNSUPPORTED;
}
CommBuffer = GetSmmCommRegion ();
if (CommBuffer == NULL) {
CommBuffer = &mSmmCommStackBuffer;
}
InternalCopyMem (CommBuffer, &gLockBoxGuid1, 16);
((LOCK_BOX_COMMAND *)CommBuffer)->HeaderSize = 48;
((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus = (UINT64)-1;
((LOCK_BOX_COMMAND *)CommBuffer)->Command = LOCK_BOX_RESTORE;
((LOCK_BOX_COMMAND *)CommBuffer)->DataSize = 48;
InternalCopyMem ((UINT8 *)CommBuffer + 40, &gLockBoxGuid3, 16);
((LOCK_BOX_COMMAND *)CommBuffer)->Buffer = NULL;
((LOCK_BOX_COMMAND *)CommBuffer)->Length = 0;
CommSize = 72;
Status = SmmComm->Communicate (SmmComm, CommBuffer, &CommSize);
ASSERT_EFI_ERROR (Status);
DEBUG ((DEBUG_INFO, "SmmLockBoxDxeLib RestoreLockBox - Exit (%r)\n",
((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus));
return ((LOCK_BOX_COMMAND *)CommBuffer)->ReturnStatus;
}
// ---------------------------------------------------------------------------
// 15 -- Boot script save event notification
// ---------------------------------------------------------------------------
/**
Notification callback for the SMM Ready-To-Lock event.
Saves the boot script context to the LockBox.
@param[in] Event Event that triggered this notification.
@param[in] Context Event-specific context (unused).
**/
STATIC
VOID
EFIAPI
S3BootScriptEventNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
Status = SaveLockBox (
(GUID *)&gLockBoxGuid2,
gS3BootScriptBuf,
*(UINT32 *)(gS3BootScriptBuf + 16)
);
ASSERT_EFI_ERROR (Status);
gS3BootScriptBuf[15] = 1; // mark saved
SetLockBoxAttributes ((GUID *)&gLockBoxGuid2, 0);
gS3BootScriptBuf[21] = 0; // clear lock
}
// ---------------------------------------------------------------------------
// 16 -- Boot script backup notification
// ---------------------------------------------------------------------------
/**
Notification callback for boot script backup. Copies primary script
data to a backup buffer and sets the backup flag.
@param[in] Event Event that triggered this notification.
@param[in] Context Event-specific context (unused).
**/
STATIC
VOID
EFIAPI
S3BootScriptBackupNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
if (gS3BootScriptBuf != NULL && gS3BootScriptBuf[15] == 0) {
//
// Calculate new length and save the boot script to LockBox
//
*(UINT32 *)(gS3BootScriptBuf + 16) = *(UINT32 *)(gS3BootScriptBuf + 8) + 3;
SaveLockBox (
(GUID *)&gLockBoxGuid2,
gS3BootScriptBuf,
*(UINT32 *)(gS3BootScriptBuf + 16)
);
ASSERT_EFI_ERROR (Status);
gS3BootScriptBuf[15] = 1;
SetLockBoxAttributes ((GUID *)&gLockBoxGuid2, 0);
gS3BootScriptBuf[21] = 0;
}
//
// Backup: copy the primary script buffer to the backup location
//
if (gS3BootScriptBackup != NULL) {
if (*(UINT64 *)gS3BootScriptBackup == 0) {
InternalCopyMem (
gS3BootScriptBackup,
gS3BootScriptBuf,
32
);
gS3BootScriptBackup[14] = 1;
}
gS3BootScriptBuf = gS3BootScriptBackup;
}
}
// ---------------------------------------------------------------------------
// 17 -- Boot script context sync (S3 Ready-To-Lock notification event)
// ---------------------------------------------------------------------------
/**
Called when the SMM Ready-To-Lock protocol is installed.
Synchronizes the boot script context between primary and backup buffers
and registers save events.
@param[in] Event Event that triggered this notification.
@param[in] Context Event-specific context (unused).
**/
STATIC
VOID
EFIAPI
S3BootScriptSyncNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
//
// If primary and backup are different, copy primary -> backup
//
if (gS3BootScriptBuf != gS3BootScriptBackup) {
S3BootScriptEventNotify (Event, Context);
if (gS3BootScriptBackup != NULL) {
if (*(UINT64 *)gS3BootScriptBackup == 0) {
InternalCopyMem (gS3BootScriptBackup, gS3BootScriptBuf, 32);
gS3BootScriptBackup[14] = 1;
}
gS3BootScriptBuf = gS3BootScriptBackup;
}
}
return 0;
}
// ---------------------------------------------------------------------------
// 18 -- S3 Boot Script Library init
// ---------------------------------------------------------------------------
/**
Initializes the S3 Boot Script library: allocates a boot script buffer,
registers notification events for SMM Ready-To-Lock and related protocols.
@retval EFI_SUCCESS Boot script library initialized.
**/
EFI_STATUS
S3BootScriptLibInit (
VOID
)
{
UINT64 PcdS3BootScriptBufferSize;
EFI_STATUS Status;
UINT64 BufferSize;
EFI_EVENT Event;
PcdS3BootScriptBufferSize = PcdGet64 (PcdS3BootScriptBufferSizeToken); // 137
if (PcdS3BootScriptBufferSize == 0) {
//
// Allocate boot script table from boot services data
//
Status = gBS->AllocatePool (
EfiBootServicesData,
sizeof (UINT64),
(VOID **)&BufferSize
);
ASSERT_EFI_ERROR (Status);
gS3BootScriptBuf = (UINT8 *)(UINTN)BufferSize;
gBootScriptFlag1 = 1;
PcdGet64 (PcdS3BootScriptBufferSize) = BufferSize; // PcdSet64S
//
// Zero-initialize the buffer to size 32
//
InternalZeroMem (gS3BootScriptBuf, 32);
//
// Register event notification for when SMM Ready-To-Lock protocol appears
//
mEventDxeSmmReadyToLock = EfiCreateEventReadyToBoot (
TPL_CALLBACK,
S3BootScriptEventNotify,
NULL
);
ASSERT (mEventDxeSmmReadyToLock != NULL);
}
gS3BootScriptBuf[20] = (UINT8)(UINTN)gS3BootScriptBuf; // store low byte as flag
//
// Locate SMM Ready-To-Lock protocol and register additional notifications
//
if (!EFI_ERROR (gBS->LocateProtocol (
&gEfiSmmReadyToLockProtocolGuid,
NULL,
&gEfiSmmReadyToLockProtocol
))) {
Status = gBS->LocateProtocol (
&gEfiSmmReadyToLock2ProtocolGuid,
NULL,
&gEfiSmmReadyToLockProtocol2
);
ASSERT_EFI_ERROR (Status);
if (Status == EFI_SUCCESS) {
//
// Get S3 boot script backup buffer
//
BufferSize = PcdGet64 (PcdS3BootScriptBufferSizeToken); // 138
if (BufferSize == 0) {
Status = gEfiSmmReadyToLockProtocol2->GetBufferSize (
gEfiSmmReadyToLockProtocol2,
6,
&BufferSize
);
ASSERT_EFI_ERROR (Status);
gBootScriptFlag2 = 1;
PcdGet64 (PcdS3BootScriptBufferSizeToken) = BufferSize; // PcdSet64S
//
// Register event for the backup save
//
InternalZeroMem (gS3BootScriptBackup, 32);
Status = gEfiSmmReadyToLockProtocol2->RegisterNotification (
gEfiSmmReadyToLockProtocol2,
S3BootScriptBackupNotify,
&gSmmReadyToLockEvent1
);
ASSERT_EFI_ERROR (Status);
Status = gEfiSmmReadyToLockProtocol2->RegisterNotification (
gEfiSmmReadyToLockProtocol2,
S3BootScriptBackupNotify,
&gSmmReadyToLockEvent2
);
ASSERT_EFI_ERROR (Status);
}
}
//
// Register sync notification for the final protocol
//
Status = gEfiSmmReadyToLockProtocol2->RegisterNotification (
gEfiSmmReadyToLockProtocol2,
S3BootScriptSyncNotify,
&gSmmReadyToLockEvent3
);
ASSERT_EFI_ERROR (Status);
}
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// 19 -- FPGA Configuration HOB retrieval / creation
// ---------------------------------------------------------------------------
/**
Retrieves the FPGA Configuration HOB. If not found, creates a new HOB
and initializes it from the "FpgaSocketConfig" UEFI variable.
The HOB contains:
- Byte 0: Valid flag
- Bytes 1-5: Socket presence mask and config
- Byte 15: Per-socket configuration block
- Bytes 23-32: Temperature threshold data
- Byte 37: DXE unlock flag
@param[out] FpgaConfigHob Pointer to receive the HOB data pointer.
@retval EFI_SUCCESS HOB found or created successfully.
@retval EFI_NOT_FOUND HOB not found and could not be created.
**/
EFI_STATUS
FpgaConfigurationGetValues (
OUT VOID **FpgaConfigHob
)
{
EFI_STATUS Status;
EFI_PEI_HOB_POINTERS Hob;
FPGA_CONFIG_HOB *FpgaHob;
UINTN VariableSize;
UINT8 *VariableData;
UINT8 *Src;
UINT8 *Dst;
UINTN Index;
//
// Search for the FPGA configuration HOB by its GUID
//
FpgaHob = (FPGA_CONFIG_HOB *)GetFirstGuidHob (&gFpgaConfigHobGuid);
if (FpgaHob != NULL) {
*FpgaConfigHob = (VOID *)((UINT8 *)FpgaHob + 24);
return EFI_SUCCESS;
}
//
// HOB not found -- create it
//
DEBUG ((DEBUG_ERROR, "FPGA Configuration Get HOB-> HOB is not found, create it!\n"));
FpgaHob = (FPGA_CONFIG_HOB *)BuildGuidHob (
&gFpgaConfigHobGuid,
sizeof (FPGA_CONFIG_HOB)
);
if (FpgaHob == NULL) {
goto HOB_FAILED;
}
InternalZeroMem (FpgaHob, sizeof (FPGA_CONFIG_HOB));
FpgaHob->Valid = 1;
FpgaHob->SegGroup = 0; // defaults: bus range unassigned
FpgaHob->BusLimit = 0xFF00; // secondary=0xFF, subordinate=0x00
FpgaHob->SocketMask = 0;
FpgaHob->MaxBus = 0;
FpgaHob->MinBus = 0;
//
// Initialize per-socket entries (at offset 15)
//
Dst = (UINT8 *)&FpgaHob->Socket[0];
for (Index = 0; Index < 4; Index++) {
Dst[0] = 0xFF; // bus start
Dst[1] = 11; // device
Dst[2] = 0xFF; // max bus
Dst[3] = 0xFF; // subordinate
Dst[4] = 0; // reserved
Dst[5] = 0; // reserved
Dst[6] = 0; // temp threshold N0
Dst[7] = 0; // temp threshold N1
Dst += 8;
}
//
// Try to restore from UEFI variable "FpgaSocketConfig"
//
VariableSize = 27;
VariableData = (UINT8 *)AllocatePool (VariableSize);
if (VariableData == NULL) {
goto HOB_FAILED;
}
InternalZeroMem (VariableData, VariableSize);
Status = gRT->GetVariable (
L"FpgaSocketConfig",
&gFpgaVariableGuid,
NULL,
&VariableSize,
VariableData
);
if (!EFI_ERROR (Status) && VariableData != NULL) {
DEBUG ((DEBUG_ERROR, "FPGA Configuration Get HOB-> Vaiable found use it!\n"));
FpgaHob->Socket[0].ConfigReg6 = VariableData[0];
FpgaHob->MaxBus = VariableData[13];
FpgaHob->MinBus = VariableData[14];
FpgaHob->DxeUnlock = VariableData[19];
//
// Copy per-socket data (indices 0..4)
//
Src = VariableData;
Dst = (UINT8 *)&FpgaHob->Socket[0];
for (Index = 0; Index < 4; Index++) {
Dst[0] = Src[7]; // bus
Dst[4] = Src[11]; // gap
Dst[5] = Src[3]; // temp N0
if (Dst[5] != 0) {
if (Dst[5] == 0xFF) {
Dst[5] = 0xFF;
} else {
Dst[5] = Dst[5] - 1;
}
}
Dst[6] = Src[8]; // temp N1
Dst += 7;
Src += 1;
}
FreePool (VariableData);
}
FpgaHob->Valid = 1;
*FpgaConfigHob = (VOID *)((UINT8 *)FpgaHob + 24);
return EFI_SUCCESS;
HOB_FAILED:
DEBUG ((DEBUG_ERROR, "FPGA Configuration Get HOB-> HOB IS NULL, could not create!\n"));
return EFI_NOT_FOUND;
}
// ---------------------------------------------------------------------------
// 20 -- FpgaDxe entry point
// ---------------------------------------------------------------------------
/**
Main FPGA initialization entry point.
Initializes all library services, locates protocols, reads the FPGA
configuration HOB, programs per-socket FPGA active state into ACPI
NVS (GlobalNvsArea), registers a ReadyToBoot notification event for
final FPGA device configuration, and initializes S3 boot script support.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS Initialization completed successfully.
@retval EFI_NOT_FOUND FPGA configuration not available.
@retval EFI_INVALID_PARAMETER Invalid parameters.
**/
EFI_STATUS
EFIAPI
FpgaDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
FPGA_CONFIG_HOB_ENTRY *FpgaHob;
UINTN PcdData;
UINT8 SocketIndex;
UINT8 *GlobalNvsPtr;
UINT8 IoData;
UINTN TimerValue;
UINTN *FpgaProtocol;
BOOLEAN FpgaActive;
//
// Step 1: Initialize library service globals
//
InitializeLibServices (ImageHandle, SystemTable);
//
// Step 2: Get DXE Services Table
//
Status = GetDxeServicesTable ();
ASSERT_EFI_ERROR (Status);
//
// Step 3: Get PCD protocol
//
Status = GetPcdProtocol ();
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "FpgaDxe: PCD protocol error %r\n", Status));
goto EXIT;
}
//
// Step 4: Get MM PCI base protocol (for ECAM access)
//
Status = GetMmPciBaseProtocol ();
ASSERT_EFI_ERROR (Status);
//
// Step 5: Get HOB list
//
Status = GetHobList ();
ASSERT_EFI_ERROR (Status);
//
// Step 6: Get PCI Express base address from PCD
//
gPciExpressBaseAddress = PcdGet64 (PcdPciExpressBaseAddress); // Token 5
//
// Step 7: Enable PCI Express MMIO access via PCD bit
//
PcdData = PcdGet64 (PcdPciExpressEnable); // Token 4
if ((INT8)PcdData >= 0) {
IoData = *(UINT8 *)PciExpressGetAddr (PcdPciExpressEnable); // PcdGet8 wrapper
IoData |= 0x80;
*(UINT8 *)PciExpressGetAddr (PcdPciExpressEnable) = IoData;
}
//
// Step 8: Read CMOS diagnostic status
//
TimerValue = IoRead32 (0x508); // CMOS port 0x508
FpgaActive = (TimerValue & 0x200) != 0;
//
// Step 9: Short delay to stabilize FPGA
//
TimerValue = IoRead32 (0x508) & 0xFFFFFF;
while (((TimerValue + 0x165 - IoRead32 (0x508)) & 0x800000) == 0) {
CpuPause ();
}
//
// Restore CMOS access
//
if (FpgaActive) {
IoWrite8 (0xCF9, 0x08); // reset request
} else {
// No special action needed
}
//
// Step 10: Initialize S3 Boot Script Library
//
Status = S3BootScriptLibInit ();
ASSERT_EFI_ERROR (Status);
//
// Step 11: Locate USRA protocol
//
Status = gBS->LocateProtocol (
&gEfiUsraProtocolGuid,
NULL,
&mUsra
);
ASSERT_EFI_ERROR (Status);
ASSERT (mUsra != NULL);
//
// Step 12: Get FPGA configuration from HOB
//
Status = FpgaConfigurationGetValues (&FpgaHob);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"FpgaConfigurationGetValues-> HOB error, return EFI_NOT_FOUND!\n"
));
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Extract configuration bytes from HOB
//
gFpgaConfigValid = *(UINT8 *)((UINT8 *)FpgaHob + 0);
gFpgaSktActive = *(UINT8 *)((UINT8 *)FpgaHob + 3);
gFpgaSktConfig5 = *(UINT8 *)((UINT8 *)FpgaHob + 5);
gFpgaSktConfig6 = *(UINT8 *)((UINT8 *)FpgaHob + 6);
gFpgaSktConfig31 = *(UINT8 *)((UINT8 *)FpgaHob + 31);
gFpgaSktConfig32 = *(UINT8 *)((UINT8 *)FpgaHob + 32);
gFpgaDxeUnlock = *(UINT8 *)((UINT8 *)FpgaHob + 37);
//
// Copy per-socket data
//
InternalCopyMem (&gFpgaSktConfigBuf, (UINT8 *)FpgaHob + 7, 7);
InternalCopyMem (&gFpgaSktTempThresh, (UINT8 *)FpgaHob + 27, 2);
DEBUG ((DEBUG_INFO, "FpgaSktActive = 0x%X.\n", gFpgaSktActive));
//
// Step 13: If no FPGA is active, return successfully (nothing to do)
//
if (gFpgaSktActive == 0) {
DEBUG ((DEBUG_INFO, "FpgaDxeEntryPoint() no FPGA activated.\n"));
return EFI_SUCCESS;
}
//
// Step 14: Locate GlobalNvsArea ACPI protocol and cache pointer
//
Status = gBS->LocateProtocol (
&gGlobalNvsAreaProtocolGuid,
NULL,
&FpgaProtocol
);
ASSERT_EFI_ERROR (Status);
gGlobalNvsArea = (UINT64)*FpgaProtocol;
ASSERT (gGlobalNvsArea != NULL);
//
// Step 15: Program per-socket FPGA active state into NVS
//
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
FpgaActive = ((1 << SocketIndex) & gFpgaSktActive) != 0;
*(UINT8 *)((UINTN)gGlobalNvsArea + SocketIndex + 1016) = FpgaActive;
DEBUG ((DEBUG_INFO, "Socket[%x] FPGA active state = %x\n", SocketIndex, FpgaActive));
}
//
// Step 16: Locate protocol for OEM FPGA data
//
Status = gBS->LocateProtocol (
&gOemFpgaProtocolGuid,
NULL,
&gOemFpgaProtocol
);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (
&gFpgaSkuProtocolGuid,
NULL,
&gFpgaSkuProtocol
);
ASSERT_EFI_ERROR (Status);
Status = gBS->LocateProtocol (
&gFpgaDeviceProtocolGuid,
NULL,
&gFpgaDeviceProtocol
);
ASSERT_EFI_ERROR (Status);
//
// Step 17: Initialize SMBus for FPGA access
//
SMBUS_PROTOCOL_OP SMBusOp;
SMBusOp.Command = 0x93; // SMBus read byte
SMBusOp.Address = 0;
SMBusOp.Value = 0xFF;
((SMBUS_PROTOCOL *)((UINTN)gGlobalNvsArea + 8588))->Execute (&SMBusOp);
//
// Step 18: Register ReadyToBoot notification callback
//
EfiCreateEventReadyToBoot (
TPL_CALLBACK,
FpgaOnReadyToBoot,
NULL
);
DEBUG ((DEBUG_INFO, "Register Event to Update for FPGA device.\n"));
EXIT:
return Status;
}
// ---------------------------------------------------------------------------
// 21 -- Module entry point (AutoGen)
// ---------------------------------------------------------------------------
/**
Module entry point. Calls FpgaDxeEntryPoint and, if it fails, enters
CpuDeadLoop after calling the debug library cleanup.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@return Status code from FpgaDxeEntryPoint (or the ASSERT handler).
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = FpgaDxeEntryPoint (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
//
// Call the driver unload / cleanup handler
//
ProcessLibraryDestructor (ImageHandle, SystemTable);
}
return Status;
}
// ---------------------------------------------------------------------------
// 22 -- FpgaOnReadyToBoot (ReadyToBoot notification)
// ---------------------------------------------------------------------------
/**
ReadyToBoot notification callback. Enumerates all FPGA-connected sockets,
programs PCI configuration space (bus numbers, FME BARs, temperature
thresholds), configures HSSI and miscellaneous FPGA registers, applies
DXE lock settings, and triggers a software SMI to finalize FPGA init
in SMM.
@param[in] Event EFI event that triggered this callback.
@param[in] Context Event context (unused).
**/
VOID
EFIAPI
FpgaOnReadyToBoot (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINTN SocketIndex;
UINT8 SocketBit;
UINT8 McpLimitBus;
UINTN FpgaGlobalData;
UINT8 BusNum;
UINT16 TempWord;
UINT8 SecondaryBus;
UINT8 SubordinateBus;
UINT8 SegmentNum;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
UINTN Handle;
EFI_PCI_IO_PROTOCOL *PciIo;
UINTN Segment;
UINTN Bus;
UINTN Device;
UINTN Func;
VOID *FpgaBar;
UINT32 TmpThresholdN0;
UINT32 TmpThresholdN1;
UINT32 RegValue;
DEBUG ((DEBUG_ERROR, "FpgaOnReadyToBoot()...\n"));
//
// Signal that we have entered ReadyToBoot
//
gBS->SignalEvent (Event);
DEBUG ((DEBUG_INFO, "FpgaSktActive = 0x%X.\n", gFpgaSktActive));
//
// Initialize SMBus
//
((SMBUS_PROTOCOL *)((UINTN)gGlobalNvsArea + 8588))->Command = 0x94;
SMBusOp.Execute (0, 0xFF);
//
// Zero FME BAR array
//
InternalZeroMem (&gFmeBar, sizeof (gFmeBar));
//
// Process each active socket
//
SocketIndex = 0;
for (SocketBit = 0; SocketBit < 4; SocketBit++) {
SocketIndex = SocketBit;
if ((gFpgaSktActive & (1 << SocketBit)) == 0) {
continue;
}
//
// Get MCP limit bus and socket bus number
//
McpLimitBus = *(UINT8 *)((UINTN)gGlobalNvsArea + SocketBit * 6 + 6904);
BusNum = *(UINT8 *)((UINTN)gGlobalNvsArea + SocketBit * 6 + 6856);
DEBUG ((DEBUG_INFO, "MCP Limit BusNumber = %x\n", McpLimitBus));
TempWord = BusNum;
//
// Read current PCI secondary/subordinate buses
//
SecondaryBus = UsraReadPciCfgByte (SocketBit, BusNum, 25);
SubordinateBus = UsraReadPciCfgByte (SocketBit, BusNum, 26);
//
// Get segment number from protocol
//
SegmentNum = *(UINT8 *)((UINTN)gFpgaSkuProtocol + SocketBit * 43 + 31);
DEBUG ((
DEBUG_INFO,
"FPGA Root Port SegmentNumber = %x, BusNumber = %x, Secondary = 0x%X, Subordinate = 0x%X\n",
SegmentNum,
BusNum,
SecondaryBus,
SubordinateBus
));
//
// Enumerate PCI devices to find the virtual FPGA bus
//
HandleBuffer = NULL;
HandleCount = 0;
if (!EFI_ERROR (gBS->LocateHandleBuffer (
ByProtocol,
&gEfiPciIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
))) {
for (Index = 0; Index < HandleCount; Index++) {
Handle = HandleBuffer[Index];
if (!EFI_ERROR (gBS->OpenProtocol (
Handle,
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo,
NULL,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
))) {
PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Func);
if (Segment == SegmentNum && Bus == BusNum && Device == 0 && Func == 0) {
break;
}
gBS->CloseProtocol (
Handle,
&gEfiPciIoProtocolGuid,
NULL,
NULL
);
}
}
if (Index >= HandleCount) {
DEBUG ((DEBUG_ERROR, "Cannot find virtual FPGA bus.\n"));
} else {
//
// Program subordinate bus limit
//
DEBUG ((DEBUG_INFO, "Update Device on B%x:D%x:F%x\n", Bus, Device, Func));
//
// Program the FPGA device behind this root port
//
FpgaPcieDeviceConfig (PciIo, SocketBit);
//
// Write secondary/subordinate limits via USRA
//
UsraWriteMmio (SocketBit, BusNum, 0, 25, McpLimitBus - 1);
UsraWriteMmio (SocketBit, BusNum, 0, 26, McpLimitBus - 1);
//
// Read FME BAR
//
RegValue = UsraReadMmio (SocketBit, (UINT8)(McpLimitBus - 1), 0, 16);
FpgaBar = (VOID *)(UINTN)(RegValue & 0xFFFFFFF0);
gFmeBar[SocketBit] = (UINT64)(UINTN)FpgaBar;
DEBUG ((DEBUG_INFO, "FmeBar[%X] = %X\n", SocketBit, FpgaBar));
//
// Set primary bus
//
UsraWriteMmio (SocketBit, BusNum, 2, 25, McpLimitBus);
UsraWriteMmio (SocketBit, BusNum, 2, 26, McpLimitBus);
gBS->CloseProtocol (
Handle,
&gEfiPciIoProtocolGuid,
NULL,
NULL
);
}
gBS->FreePool (HandleBuffer);
}
}
//
// Dump Blue BitStream version for active sockets
//
DEBUG ((DEBUG_INFO, "DumpBbsVersion () ...\n"));
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
if (gFpgaSktActive & (1 << SocketIndex)) {
RegValue = *(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 100);
DEBUG ((
DEBUG_INFO,
"Socket[%X] Blue BitStream Version: %X.%X.%X.%X\n",
SocketIndex,
(RegValue >> 24) & 0xF,
(RegValue >> 20) & 0xF,
(RegValue >> 12) & 0xF,
RegValue & 0xF
));
}
}
//
// Program temperature threshold registers
//
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
if (gFpgaSktActive & (1 << SocketIndex)) {
TmpThresholdN0 = *(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 4104) & 0xFFFF0000;
TmpThresholdN1 = *(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 4108) & 0xFFFFEFFF;
if ((UINT8)(gFpgaSktTempThresh[SocketIndex] - 1) <= 0xFD) {
TmpThresholdN0 |= (gFpgaSktTempThresh[SocketIndex] & 0xFF) | 0x80;
TmpThresholdN1 |= 0x1000;
}
if ((UINT8)(gFpgaSktTempThresh[SocketIndex] - 1) <= 0xFD) {
TmpThresholdN0 |= ((gFpgaSktTempThresh[SocketIndex] | 0x80) << 8);
}
DEBUG ((
DEBUG_INFO,
"TmpThreshold_N0.Data = %X, TmpThreshold_N1.Data = %X\n",
TmpThresholdN0,
TmpThresholdN1
));
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 4104) = TmpThresholdN0;
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 4108) = TmpThresholdN1;
}
}
//
// MCP power limit configuration
//
if (FpgaMcpPowerLimitConfig () < 0) {
DEBUG ((DEBUG_ERROR, "FPGA MCP power limit setting failed.\n"));
}
//
// FPGA miscellaneous initialization
//
FpgaPcieEarlyConfig ();
FpgaDxeInit ();
//
// PCIe device config
//
DEBUG ((DEBUG_INFO, "FpgaPcieDeviceConfig() ...\n"));
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
if (gFpgaSktActive & (1 << SocketIndex)) {
RegValue = *(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 156);
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 152) |= 0x11;
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 156) = RegValue;
}
}
FpgaProgramHssiConfig ();
DEBUG ((DEBUG_INFO, "FpgaMiscConfig () ...\n"));
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
if (gFpgaSktActive & (1 << SocketIndex)) {
RegValue = *(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 16396);
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 16392) &= ~0x40;
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 16396) = RegValue;
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 16400) = 64;
}
}
//
// DXE lock configuration (unless unlock flag is set)
//
if (gFpgaDxeUnlock == 1) {
DEBUG ((DEBUG_INFO, "FPGA DXE unlock enabled, skip DXE lock config.\n"));
} else {
DEBUG ((DEBUG_INFO, "FpgaDxeLockConfig() ...\n"));
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
if (gFpgaSktActive & (1 << SocketIndex)) {
RegValue = *(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 132);
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 128) |= 3;
*(UINT32 *)((UINTN)gFmeBar[SocketIndex] + 132) = RegValue;
}
}
}
//
// Finalize: SMBus command, trigger SMI
//
((SMBUS_PROTOCOL *)((UINTN)gGlobalNvsArea + 8588))->Command = 0x95;
SMBusOp.Execute (0, 0xFF);
DEBUG ((DEBUG_INFO, "Trigger software SMI for FPGA.\n"));
IoWrite8 (0xB2, (UINT8)PcdGet64 (PcdSoftwareSmiTrigger)); // Token 182
DEBUG ((DEBUG_INFO, "FpgaOnReadyToBoot() End...\n"));
}
// ---------------------------------------------------------------------------
// 23 -- S3 boot script library destructor / cleanup
// ---------------------------------------------------------------------------
/**
Library destructor called on module unload. Unregisters boot script
events and closes SMM communication.
If gS3BootScriptBuf was allocated, signals events, saves boot script
data to the LockBox, and cleans up SMM protocol registrations.
@retval EFI_SUCCESS Cleanup succeeded.
@retval other Error during cleanup.
**/
EFI_STATUS
EFIAPI
ProcessLibraryDestructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Close SMM Ready-To-Lock event notifications
//
if (gEfiSmmReadyToLockProtocol != NULL) {
if (gSmmReadyToLockEvent1 != NULL) {
Status = gEfiSmmReadyToLockProtocol->RegisterNotification (
gEfiSmmReadyToLockProtocol,
NULL,
&gSmmReadyToLockEvent1
);
}
if (gSmmReadyToLockEvent2 != NULL) {
Status = gEfiSmmReadyToLockProtocol->RegisterNotification (
gEfiSmmReadyToLockProtocol,
NULL,
&gSmmReadyToLockEvent2
);
}
}
if (gEfiSmmReadyToLockProtocol2 != NULL) {
if (gSmmReadyToLockEvent3 != NULL) {
Status = gEfiSmmReadyToLockProtocol2->RegisterNotification (
gEfiSmmReadyToLockProtocol2,
NULL,
&gSmmReadyToLockEvent3
);
}
}
//
// Close SMM communication protocol
//
if (mSmmCommunication != NULL) {
gBS->CloseProtocol (
mSmmCommunication,
&gEfiSmmCommunicationProtocolGuid,
NULL,
NULL
);
mSmmCommunication = NULL;
}
return EFI_SUCCESS;
}