/**
* @file SetupConfigUpdateDxeNeonCityFPGA.c
*
* @brief SetupConfigUpdateDxeNeonCityFPGA - UEFI DXE driver for ROM layout
* identification and UBA setup configuration registration for the
* NeonCityFPGA platform.
*
* MODULE TYPE: DXE Driver (Index 0007 in BIOS FFS)
* UEFI PHASE: DXE
*
* FLOW SUMMARY:
* 1. _ModuleEntryPoint() (0x390) initializes UEFI globals, locates the HOB
* list via GetHobList(), prints a debug banner, locates the UBA
* NeonCityFPGA board-type protocol, and registers the setup configuration.
* 2. GetHobList() (0x5E0) scans SystemTable->ConfigurationTable[] for
* EFI_HOB_LIST_GUID using IsHobListGuid().
* 3. DebugPrint() (0x518) checks CMOS debug level and calls the DebugLib
* protocol output function.
* 4. DebugAssert() (0x5A0) calls the DebugLib protocol's assertion handler.
* 5. ReadUnaligned64() (0x728) reads a 64-bit value from potentially
* unaligned memory with a NULL check.
*
* GUIDs:
* - EFI_HOB_LIST_GUID: {7739F24C-93D7-11D4-9A3A-0090273FC14D}
* - UBA NeonCityFPGA Board-Type Protocol: {E03E0D46-5263-4845-B0A4-58D57B3177E2}
* - UBA NeonCityFPGA Setup Config Protocol: {CD1F9574-DD03-4196-96AD-4965146F9665}
*
* HARDWARE:
* - CMOS RTC ports 0x70/0x71: Debug level register at index 0x4B
* - MMIO 0xFDAF0490: Board configuration register (fallback for debug level)
*
* @note This module is binary-identical to RomLayoutDxe (index 0000).
* Both drivers have the exact same code, GUIDs, and function layout.
*/
#include "SetupConfigUpdateDxeNeonCityFPGA.h"
// ============================================================================
// Static (Module-Level) Global Variables
// ===========================================================================/
///
/// Cached pointer to the DebugLib protocol interface.
/// Initialized lazily by GetDebugProtocol(). Located via gBS->LocateProtocol()
/// against the DebugLib protocol GUID stored in the data section.
///
STATIC VOID *mDebugProtocol; // qword_BB8 at 0xBB8
///
/// Cached pointer to the HOB list.
/// Initialized lazily by GetHobList() by searching the system configuration
/// table for the EFI_HOB_LIST_GUID entry.
///
STATIC VOID *mHobList; // qword_BC0 at 0xBC0
///
/// Cached CMOS debug level byte (n3 at 0xBC8).
/// Read from CMOS register 0x4B during debug output filtering.
///
STATIC UINT8 mCmosDebugLevel; // n3 at 0xBC8
// ============================================================================
// Constant Data (in .data section)
// ===========================================================================/
//
// These constants are embedded in the .data section of the binary. They are
// referenced by absolute address in the compiled code and are provided here
// for reference documentation. The actual values must match what the compiled
// code expects through the fixed .data layout.
//
// EFI_GUID mDebugProtocolGuid @ 0xB40 = 36232936-0E76-31C8-A13A-3AF2FC1C3932
// EFI_GUID mUbaBoardTypeProtocolGuid @ 0xB50 = E03E0D46-5263-4845-B0A4-58D57B3177E2
// EFI_GUID mEfiHobListGuid @ 0xB60 = 7739F24C-93D7-11D4-9A3A-0090273FC14D
// EFI_GUID mUbaSetupConfigGuid @ 0xB70 = CD1F9574-DD03-4196-96AD-4965146F9665
//
// The GUID halves used for the optimized comparison in IsHobListGuid() reside
// at the same GUID buffer:
// UINT64 mEfiHobListGuidFirstHalf (first 8 bytes of GUID at 0xB60)
// UINT64 mEfiHobListGuidSecondHalf (second 8 bytes of GUID at 0xB68)
//
// UBA_SETUP_CONFIG_DATA mSetupConfigData @ 0xB80
// Signature: "PSET" (0x54455350)
// Version: 1
// Size: 0x48c
// SizeDuplicate: 0x48c
// ============================================================================
// Local (Forward) Function Declarations
// ===========================================================================/
/**
* Retrieves the DebugLib protocol interface from gBS, caching the result.
*
* Allocates a pool buffer (type EfiBootServicesData), then calls
* gBS->LocateProtocol() with the DebugLib protocol GUID. If the buffer size
* exceeds 16 bytes, the protocol is not obtained (optimization/filter for
* minimal UEFI implementations). The result is cached in mDebugProtocol.
*
* @return Pointer to the DebugLib protocol interface, or NULL if unavailable.
*/
STATIC
VOID *
GetDebugProtocol (
VOID
);
// ============================================================================
// Function Implementations
// ===========================================================================/
/**
* Module entry point for SetupConfigUpdateDxeNeonCityFPGA.
*
* Initializes UEFI global variables (gImageHandle, gST, gBS, gRT), locates the
* HOB list via GetHobList(), prints a debug banner via DebugPrint(), locates
* the UBA NeonCityFPGA board-type protocol, and registers the setup
* configuration data by calling the protocol's RegisterSetupConfig function.
*
* Calling sequence:
* 1. gImageHandle = ImageHandle (asserts if NULL)
* 2. gST = SystemTable (asserts if NULL)
* 3. gBS = SystemTable->BootServices (asserts if NULL)
* 4. gRT = SystemTable->RuntimeServices (asserts if NULL)
* 5. GetHobList() (caches HobList pointer)
* 6. DebugPrint(DEBUG_INFO, "UBA:SETUPConfigUpdate-TypeNeonCityFPGA\n")
* 7. gBS->LocateProtocol(&mUbaBoardTypeProtocolGuid, NULL, &Interface)
* 8. Interface->RegisterSetupConfig(Interface, &mUbaSetupConfigGuid,
* &mSetupConfigData, sizeof(UBA_SETUP_CONFIG_DATA))
*
* @param[in] ImageHandle The firmware-allocated handle for this driver image.
* @param[in] SystemTable A pointer to the EFI System Table.
*
* @return EFI_SUCCESS The setup config protocol was registered.
* @return Other Returned directly from LocateProtocol if the UBA
* board-type protocol is not available.
*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Interface;
//
// Cache ImageHandle with assertion check.
//
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
DebugAssert (
__FILE__,
__LINE__,
"gImageHandle != ((void *) 0)"
);
}
//
// Cache SystemTable with assertion check.
//
gST = SystemTable;
if (SystemTable == NULL) {
DebugAssert (
__FILE__,
__LINE__,
"gST != ((void *) 0)"
);
}
//
// Cache BootServices from SystemTable with assertion check.
//
gBS = SystemTable->BootServices;
if (gBS == NULL) {
DebugAssert (
__FILE__,
__LINE__,
"gBS != ((void *) 0)"
);
}
//
// Cache RuntimeServices from SystemTable with assertion check.
//
gRT = SystemTable->RuntimeServices;
if (gRT == NULL) {
DebugAssert (
__FILE__,
__LINE__,
"gRT != ((void *) 0)"
);
}
//
// Locate the HOB list from the system configuration table.
// This is required for HOB-based drivers that follow.
//
GetHobList (ImageHandle);
//
// Print debug banner indicating this driver is executing.
//
Interface = NULL;
DebugPrint (DEBUG_INFO, "UBA:SETUPConfigUpdate-TypeNeonCityFPGA\n");
//
// Locate the UBA NeonCityFPGA board-type protocol.
// @note 0x45a: call gBS->LocateProtocol (gBS + 0x140)
//
Status = gBS->LocateProtocol (
&mUbaBoardTypeProtocolGuid,
NULL,
&Interface
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Call the board-type protocol's RegisterSetupConfig function at offset 0x10.
// This registers the setup configuration protocol for NeonCityFPGA.
//
// @note 0x481: call [Interface + 0x10]
// Args: rcx = Interface (This)
// rdx = &mUbaSetupConfigGuid (protocol GUID to register)
// r8 = &mSetupConfigData ("PSET" structure)
// r9 = sizeof(UBA_SETUP_CONFIG_DATA) = 0x18 (24 bytes)
//
return ((UBA_NEONCITYFPGA_BOARD_TYPE_PROTOCOL *)Interface)->RegisterSetupConfig (
Interface,
&mUbaSetupConfigGuid,
&mSetupConfigData,
sizeof (UBA_SETUP_CONFIG_DATA)
);
}
/**
* Locates the HOB (Hand-Off Block) list from the UEFI System Table's
* configuration table array.
*
* Iterates through SystemTable->ConfigurationTable[] looking for an entry
* whose VendorGuid matches EFI_HOB_LIST_GUID. The comparison is done by
* comparing the first 8 bytes and second 8 bytes of the GUID as 64-bit
* integers via ReadUnaligned64().
*
* Results are cached in mHobList. If the HOB list GUID is not found,
* an ASSERT_EFI_ERROR is raised via DebugPrint and DebugAssert.
*
* @param[in] ImageHandle Passed through from entry but unused in the loop.
*
* @return Pointer to the HOB list, or 0 if not found.
*
* @note 0x60f: SystemTable->NumberOfTableEntries is at gST + 0x68
* @note 0x617: SystemTable->ConfigurationTable is at gST + 0x70
* Each entry is 0x18 (24) bytes:
* +0x00: EFI_GUID VendorGuid (16 bytes)
* +0x10: VOID *VendorTable (8 bytes)
*/
VOID *
GetHobList (
IN EFI_HANDLE ImageHandle
)
{
UINTN Index;
UINTN TableCount;
EFI_CONFIGURATION_TABLE *ConfigTable;
//
// Return cached value if already resolved.
//
if (mHobList != NULL) {
return mHobList;
}
//
// Initialize HOB list pointer to NULL.
//
mHobList = NULL;
//
// Get the number of configuration table entries.
//
TableCount = gST->NumberOfTableEntries;
//
// If there are entries, scan them for EFI_HOB_LIST_GUID.
//
if (TableCount > 0) {
//
// Get pointer to the configuration table array.
//
ConfigTable = gST->ConfigurationTable;
for (Index = 0; Index < TableCount; Index++) {
//
// Compare current entry's VendorGuid against EFI_HOB_LIST_GUID.
// The comparison splits the 16-byte GUID into two 8-byte halves:
// - First 8 bytes (GUID.Data1 + GUID.Data2 + GUID.Data3 high)
// - Second 8 bytes (GUID.Data3 low + GUID.Data4)
// This matches the EFI_HOB_LIST_GUID: 7739F24C-93D7-11D4-9A3A-0090273FC14D
// - First 8 bytes: 0x11D493D77739F24C
// - Second 8 bytes: 0x4DC13F2700903A9A
//
if (IsHobListGuid (ImageHandle, &ConfigTable[Index].VendorGuid)) {
//
// Found the HOB list entry. Extract the VendorTable pointer.
//
mHobList = ConfigTable[Index].VendorTable;
return mHobList;
}
}
}
//
// HOB list GUID not found in configuration table.
// Raise ASSERT_EFI_ERROR with EFI_NOT_FOUND (0x800000000000000E).
//
DebugPrint (EFI_NOT_FOUND, "\nASSERT_EFI_ERROR (Status = %r)\n");
DebugAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
//
// If mHobList is still NULL after the search, raise another assertion.
//
if (mHobList == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return mHobList;
}
/**
* Compares a GUID against the EFI_HOB_LIST_GUID by comparing its first 8 bytes
* and second 8 bytes independently.
*
* Instead of a full 16-byte EFI_GUID comparison, this function uses two
* 8-byte unaligned reads (ReadUnaligned64) to compare the GUID halves against
* the pre-cached values.
*
* @param[in] ImageHandle Unused parameter passed through from GetHobList().
* @param[in] GuidPtr Pointer to the EFI_GUID to compare.
*
* @retval TRUE The GUID at GuidPtr matches EFI_HOB_LIST_GUID.
* @retval FALSE The GUID does not match.
*/
BOOLEAN
IsHobListGuid (
IN EFI_HANDLE ImageHandle,
IN EFI_GUID *GuidPtr
)
{
//
// Compare first 8 bytes of the GUID.
//
if (ReadUnaligned64 (&mEfiHobListGuidFirstHalf) != ReadUnaligned64 (GuidPtr)) {
return FALSE;
}
//
// Compare second 8 bytes of the GUID.
// GuidPtr + 8 points to the second half of the 16-byte GUID structure.
//
return ReadUnaligned64 (&mEfiHobListGuidSecondHalf) == ReadUnaligned64 ((UINT8 *)GuidPtr + 8);
}
/**
* Debug print function.
*
* Resolves the DebugLib protocol interface via GetDebugProtocol(), checks the
* CMOS debug level to determine if the requested error level is enabled, and
* if so, calls the DebugLib protocol's output function (first function pointer,
* at offset 0x00 of the protocol interface).
*
* The CMOS debug level is read from RTC CMOS register 0x4B:
* - Register index is set to 0x4B (preserving bit 7 for NMI enable)
* - Values 0-3 mean level 4 (DEBUG_INFO) for the mask
* - Value 0 with a special board config at MMIO 0xFDAF0490 uses
* (register & 2) | 1 instead.
* - Otherwise the raw value - 1 determines the level:
* level 1 -> mask 0x80000004 (DEBUG_INIT | DEBUG_INFO)
* level >1 -> mask 0x80000046 (various debug bits)
*
* @param[in] ErrorLevel The debug error level mask to check.
* @param[in] Format A format string for the debug message.
* @param[in] ... Variable arguments for the format string.
*
* @return The return value from the DebugLib protocol's output function,
* or 0 if the protocol is not available or the error level is
* not enabled.
*/
UINTN
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
UINTN ReturnValue;
UINT64 DebugLevel;
UINT8 CmosValue;
UINT32 DebugMask;
VOID *DebugProtocol;
VA_LIST VaList;
VA_START (VaList, Format);
//
// Get the DebugLib protocol interface (cached).
//
DebugProtocol = GetDebugProtocol ();
ReturnValue = 0;
if (DebugProtocol != NULL) {
//
// Read debug level from CMOS register 0x4B.
// Access RTC CMOS ports 0x70/0x71:
// Port 0x70 = CMOS index/address register
// Port 0x71 = CMOS data register
//
// Read current CMOS index register value, mask off bits to preserve
// NMI enable (bit 7 = 0x80), and set the register address to 0x4B.
//
// @note 0x543: in al, 0x70 -- read current CMOS index
// @note 0x544: and al, 0xCB -- preserve NMI bit, clear bits 2,4,5
// @note 0x546: or al, 0x4B -- select CMOS register 0x4B
// @note 0x548: out 0x70, al -- write CMOS index
// @note 0x54d: in al, 0x71 -- read value from CMOS data port
//
CmosValue = IoRead8 (RTC_INDEX_PORT);
CmosValue = (CmosValue & 0xCB) | CMOS_DEBUG_LEVEL_REGISTER;
IoWrite8 (RTC_INDEX_PORT, CmosValue);
//
// Read the debug level value from CMOS data port.
//
DebugLevel = IoRead8 (RTC_DATA_PORT);
//
// Determine the debug mask based on the CMOS value.
//
if (DebugLevel > 3) {
//
// For values > 3, check the cached CMOS debug level.
// If the cached level is 0, fall through to the board config check.
//
DebugLevel = mCmosDebugLevel;
if (DebugLevel == 0) {
//
// Read board configuration from MMIO register 0xFDAF0490.
// This is a platform-specific register that indicates the board type
// or configuration variant.
//
// @note 0x560: mov eax, 0xFDAF0490
// @note 0x565: mov ecx, [rax] -- read 32-bit board config
// @note 0x567: and cl, 2 -- isolate bit 1
// @note 0x56a: or cl, 1 -- set bit 0 (always 1)
// Result: (board_config & 2) | 1
//
DebugLevel = (*(volatile UINT32 *)BOARD_CONFIG_MMIO_ADDR & 2) | 1;
}
}
//
// Calculate the debug mask from the debug level.
// level - 1 must be <= 0xFD (i.e., level >= 1 and level < 0xFF)
//
if ((DebugLevel > 0) && ((DebugLevel - 1) <= 0xFD)) {
//
// Level 1 -> mask = 0x80000004 (DEBUG_INIT | DEBUG_INFO)
// Level >1 -> mask = 0x80000046 (multiple debug flags)
//
// @note 0x57b: mov r8d, 0x80000046 (level > 1 mask)
// @note 0x581: cmovz r8d, 0x80000004 (level == 1 mask)
//
if (DebugLevel == 1) {
DebugMask = 0x80000004;
} else {
DebugMask = 0x80000046;
}
//
// Check if the requested ErrorLevel is enabled by the mask.
//
if ((DebugMask & ErrorLevel) != 0) {
//
// Call the DebugLib protocol's output function.
// @note 0x597: call [r9] -- call DebugLib protocol interface + 0x00
//
ReturnValue = ((DEBUGLIB_PROTOCOL *)DebugProtocol)->DebugPrint (
ErrorLevel,
Format,
VaList
);
}
}
}
return ReturnValue;
}
/**
* ASSERT assertion failure handler.
*
* Resolves the DebugLib protocol via GetDebugProtocol() and calls its
* assertion failure handler function at offset 0x08 in the protocol interface.
*
* The assertion handler takes:
* rcx = FileName (source file name)
* rdx = LineNumber (line in source file)
* r8 = Description (assertion description string)
*
* @param[in] FileName Source file name where the assertion occurred.
* @param[in] LineNumber Line number of the assertion.
* @param[in] Description Description of the failed assertion.
*
* @return 0 if the DebugLib protocol is not available.
*
* @note 0x5cb: call [result + 8] -- call DebugLib protocol's Assert function
*/
UINTN
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
VOID *DebugProtocol;
UINTN Result;
DebugProtocol = GetDebugProtocol ();
Result = 0;
if (DebugProtocol != NULL) {
Result = ((DEBUGLIB_PROTOCOL *)DebugProtocol)->DebugAssert (
FileName,
LineNumber,
Description
);
}
return Result;
}
/**
* Retrieves the DebugLib protocol interface from gBS, caching the result.
*
* Allocates a boot services data buffer (pool type = EfiBootServicesData)
* using gBS->AllocatePool (BootServices + 0x18 = 24) and immediately frees it
* with gBS->FreePool (BootServices + 0x20 = 32).
*
* This pool allocation/free cycle serves as a size check: if the allocation
* succeeds and the returned buffer is <= 16 bytes, it indicates a valid UEFI
* environment. Otherwise (size > 16), NULL is returned as a guard.
*
* Then calls gBS->LocateProtocol() (BootServices + 0x140 = 320) to obtain the
* DebugLib protocol interface. The result is cached in mDebugProtocol.
*
* @return Pointer to the DebugLib protocol interface, or NULL if unavailable
* or if the pool check indicates an invalid UEFI environment.
*
* @note 0x4c7: call [BootServices + 0x18] = gBS->AllocatePool
* @note 0x4ca: call [BootServices + 0x20] = gBS->FreePool
* @note 0x4ee: call [BootServices + 0x140] = gBS->LocateProtocol
*/
STATIC
VOID *
GetDebugProtocol (
VOID
)
{
VOID *Buffer;
EFI_STATUS Status;
//
// Return cached value if already resolved.
//
if (mDebugProtocol != NULL) {
return mDebugProtocol;
}
//
// Allocate a small pool buffer (EfiBootServicesData = 31) and free it.
// This is a UEFI environment validation check: if the allocation succeeds
// and the buffer address is within a reasonable range (<= 0x10), proceed.
// On minimal or non-UEFI environments, the allocation may behave differently.
//
Buffer = NULL;
gBS->AllocatePool (EfiBootServicesData, 0, &Buffer);
gBS->FreePool (Buffer);
if ((UINTN)Buffer <= 0x10) {
//
// The buffer size check suggests we are in a valid UEFI environment with
// properly functioning boot services.
// Locate the DebugLib protocol.
//
Status = gBS->LocateProtocol (
&mDebugProtocolGuid,
NULL,
&mDebugProtocol
);
if (EFI_ERROR (Status)) {
mDebugProtocol = NULL;
}
} else {
mDebugProtocol = NULL;
}
return mDebugProtocol;
}
/**
* Returns the UEFI status code for "Not Found".
*
* @return EFI_NOT_FOUND (0x800000000000000E).
*/
EFI_STATUS
ReturnNotFound (
VOID
)
{
return EFI_NOT_FOUND;
}
/**
* Reads an unaligned 64-bit value from memory.
*
* Wraps the BaseLib ReadUnaligned64() function with a NULL pointer check.
* If the Buffer pointer is NULL, raises an assertion.
*
* @param[in] Buffer Pointer to the memory to read. Must not be NULL.
*
* @return The 64-bit value read from the given address.
*
* @note 0x734: if Buffer is NULL, calls DebugAssert at BaseLib\Unaligned.c:192
* @note 0x751: return *(_QWORD *)Buffer (simple unaligned dereference)
*/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
return *(volatile UINT64 *)Buffer;
}