/**
* @file UsbOcUpdateDxeNeonCityFPGA.c
*
* @brief UsbOcUpdateDxeNeonCityFPGA - UEFI DXE driver for USB over-current
* (OC) protection configuration on the NeonCityFPGA platform.
*
* MODULE TYPE: DXE Driver (Index 0010 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 USB OC configuration.
* 2. GetHobList() (0x5F8) scans SystemTable->ConfigurationTable[] for
* EFI_HOB_LIST_GUID using IsHobListGuid().
* 3. DebugPrint() (0x530) resolves the DebugLib protocol, checks the CMOS
* debug level, and calls the DebugLib protocol's output function.
* 4. DebugAssert() (0x5B8) calls the DebugLib protocol's assertion handler.
* 5. ReadUnaligned64() (0x740) reads a 64-bit value from potentially
* unaligned memory with a NULL check.
*
* GUIDs (in .data segment):
* 0xB40 - DebugLib protocol GUID: 36232936-0E76-31C8-A13A-3AF2FC1C3932
* 0xB50 - UBA Board-Type Protocol: E03E0D46-5263-4845-B0A4-58D57B3177E2
* 0xB60 - USB OC Config Protocol: 2638009E-3850-4E4B-B05D-042A32DBB9D1
* 0xB70 - EFI HOB List GUID: 7739F24C-93D7-11D4-9A3A-0090273FC14D
*
* USB OC Configuration Data (in .data segment):
* 0xB80 - UBA_USBOC_CONFIG_DATA header ("PUSB", version 2, size 0x48c)
* 0xB90 - Table 1: Front panel port-to-OC-pin mapping (16 UINT32 entries)
* 0xBD0 - Table 2: Rear panel/backplane port-to-OC-pin mapping (20 UINT32)
*
* HARDWARE:
* - CMOS RTC ports 0x70/0x71: Debug level register at index 0x4B
* - MMIO 0xFDAF0490: Board configuration register (fallback for debug level)
*/
#include "UsbOcUpdateDxeNeonCityFPGA.h"
// ============================================================================
// Static (Module-Level) Global Variables
// ===========================================================================/
//
// NOTE: These globals are placed at specific absolute addresses in the binary's
// .data segment. Their names and types match the decompiled references.
//
// Address Name Type
// -------- ---- ----
// 0xC30 gST EFI_SYSTEM_TABLE* (SystemTable)
// 0xC38 BootServices EFI_BOOT_SERVICES* (gBS)
// 0xC40 gImageHandle EFI_HANDLE (ImageHandle)
// 0xC48 RuntimeServices EFI_RUNTIME_SERVICES* (gRT)
// 0xC50 qword_C50 VOID* (mDebugProtocol)
// 0xC58 qword_C58 VOID* (mHobList)
// 0xC60 byte_C60 UINT8 (mCmosDebugLevel)
//
static VOID *mDebugProtocol; /* qword_C50 at 0xC50 */
static VOID *mHobList; /* qword_C58 at 0xC58 */
static UINT8 mCmosDebugLevel; /* byte_C60 at 0xC60 */
// ============================================================================
// Constant Data (in .rdata and .data sections)
// ===========================================================================/
//
// These constants are embedded in the .data section of the binary. They are
// referenced by absolute address in the compiled code.
//
// EFI_GUID mDebugProtocolGuid @ 0xB40 = DEBUGLIB_PROTOCOL_GUID
// EFI_GUID mUbaBoardTypeProtocolGuid @ 0xB50 = UBA_BOARD_TYPE_PROTOCOL_GUID
// EFI_GUID mUsbOcConfigProtocolGuid @ 0xB60 = USBOC_CONFIG_PROTOCOL_GUID
// EFI_GUID mEfiHobListGuid @ 0xB70 = HOB_LIST_GUID
//
// The GUID halves used for the optimized comparison in IsHobListGuid() reside
// in the same GUID buffer:
// UINT64 mEfiHobListGuidFirstHalf (first 8 bytes of GUID at 0xB70)
// UINT64 mEfiHobListGuidSecondHalf (second 8 bytes of GUID at 0xB78)
//
// UBA_USBOC_CONFIG_DATA mUsbOcConfigData @ 0xB80
// Signature: "PUSB" (0x42535550)
// Version: 2
// Size: 0x48c
// PortToOcPinUsbPort1[16]: @ 0xB90 Front panel port-OC mapping
// PortToOcPinUsbPort2[20]: @ 0xBD0 Rear panel port-OC mapping
//
// ============================================================================
// Local (Forward) Function Declarations
// ===========================================================================/
/**
* Retrieves the DebugLib protocol interface from gBS, caching the result.
*
* Performs a UEFI environment validation check via gBS->AllocatePool and
* gBS->FreePool, then calls gBS->LocateProtocol() with the DebugLib protocol
* GUID. 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 UsbOcUpdateDxeNeonCityFPGA.
*
* 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 USB OC
* configuration data by calling the protocol's RegisterUsbOcConfig 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(ImageHandle) (caches HobList pointer)
* 6. DebugPrint(DEBUG_INFO, "UBA:UsbOcUpdate-TypeNeonCityFPGA\n")
* 7. gBS->LocateProtocol(&mUbaBoardTypeProtocolGuid, NULL, &Interface)
* 8. Interface->RegisterUsbOcConfig(Interface,
* &mUsbOcConfigProtocolGuid, &mUsbOcConfigData, sizeof(mUsbOcConfigData))
*
* @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 USB OC config protocol was registered.
* @return Other Returned directly from LocateProtocol if the UBA
* board-type protocol is not available.
*
* @note 0x45a: call [gBS + 0x140] = gBS->LocateProtocol
* @note 0x481: call [Interface + 0x10] = Interface->RegisterUsbOcConfig
*/
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.
// Required for HOB-based drivers that follow.
//
GetHobList (ImageHandle);
//
// Print debug banner identifying this driver.
//
Interface = NULL;
DebugPrint (DEBUG_INFO, "UBA:UsbOcUpdate-TypeNeonCityFPGA\n");
//
// Locate the UBA NeonCityFPGA board-type protocol.
// @note 0x45a: call [gBS + 0x140] (gBS->LocateProtocol)
// rcx = &mUbaBoardTypeProtocolGuid (UBA protocol GUID)
// rdx = NULL (no registration handle)
// r8 = &Interface (output pointer)
//
Status = gBS->LocateProtocol (
&mUbaBoardTypeProtocolGuid,
NULL,
&Interface
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Call the board-type protocol's RegisterUsbOcConfig function at offset 0x10.
// This registers the USB OC configuration protocol for NeonCityFPGA.
//
// @note 0x481: call [Interface + 0x10]
// rcx = Interface (This pointer)
// rdx = &mUsbOcConfigProtocolGuid (USB OC config protocol GUID)
// r8 = &mUsbOcConfigData ("PUSB" config data structure)
// r9 = sizeof(UBA_USBOC_CONFIG_DATA) = 0x10 (16 bytes for header only)
//
// NOTE: The fourth parameter (ConfigDataSize) is 0x10 (16 bytes) on this
// platform, which covers only the UBA_USBOC_CONFIG_DATA header (Signature +
// Version + Size). The port mapping tables follow the header in the same
// structure but the registration function knows the total size from the
// Size field within the structure.
//
return ((UBA_NEONCITYFPGA_BOARD_TYPE_PROTOCOL *)Interface)->RegisterUsbOcConfig (
Interface,
&mUsbOcConfigProtocolGuid,
&mUsbOcConfigData,
sizeof (UBA_USBOC_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, followed
* by a second assertion if mHobList is still NULL.
*
* @param[in] ImageHandle Passed through from entry but unused in the loop.
*
* @return Pointer to the HOB list, or 0 if not found.
*
* @note 0x627: SystemTable->NumberOfTableEntries is at gST + 0x68
* @note 0x635: 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 from gST.
//
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 the 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.
* The reference values are the pre-cached first and second 8-byte halves
* of the EFI_HOB_LIST_GUID in the .data section.
*
* @param[in] ImageHandle Unused parameter (passed for calling convention
* compatibility with the UBA protocol interface).
* @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.
// The reference value is the first 8 bytes of the EFI_HOB_LIST_GUID
// stored in the .data section at offset 0xB70 (mEfiHobListGuidFirstHalf).
//
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 (board configuration check)
* - Otherwise the raw value - 1 determines the level:
* level 1 -> mask 0x80000004 (DEBUG_INIT | DEBUG_INFO)
* level >1 -> mask 0x80000046 (extended debug flags)
*
* @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 masked.
*
* @note 0x55b: in al, 0x70 -- read current CMOS index
* @note 0x560: out 0x70, al -- write CMOS index (set reg 0x4B)
* @note 0x565: in al, 0x71 -- read value from CMOS data port
* @note 0x582: MMIO read at 0xFDAF0490 (board config fallback)
*/
UINTN
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
UINTN ReturnValue;
UINT8 CmosValue;
UINT8 DebugLevel;
UINT32 DebugMask;
VOID *DebugProtocol;
VA_LIST VaList;
VA_START (VaList, Format);
//
// Get the DebugLib protocol interface (cached, or resolved lazily).
//
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 NMI enable bit (bit 7)
// and certain control bits (mask 0xCB), set the register address to 0x4B.
//
// @note 0x55b: in al, 0x70 -- io_read8(RTC_INDEX_PORT)
// @note 0x55d: and al, 0xCB -- preserve NMI bit, clear bits 2,4,5
// @note 0x55f: or al, 0x4B -- select CMOS register 0x4B
// @note 0x561: out 0x70, al -- io_write8(RTC_INDEX_PORT, value)
// @note 0x566: in al, 0x71 -- io_read8(RTC_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 (byte_C60 at 0xC60).
// 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 0x582: mov ecx, [0xFDAF0490] -- read board config register
// @note 0x588: and cl, 2 -- isolate bit 1
// @note 0x58b: 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 0x593: mov r8d, 0x80000046 (level > 1 mask)
// @note 0x599: cmovz r8d, 0x80000004 (level == 1 mask, zero from sub)
//
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 DebugPrint function.
// @note 0x5af: call [r9] -- call 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 Result from the DebugLib protocol's DebugAssert function,
* or 0 if the protocol is not available.
*
* @note 0x5e3: 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 = 0x1F)
* using gBS->AllocatePool (BootServices + 0x18) and immediately frees it with
* gBS->FreePool (BootServices + 0x20).
*
* This allocation/free cycle serves as a UEFI environment validation check:
* if the allocation succeeds and the returned buffer address is <= 0x10,
* it indicates a valid UEFI environment. Otherwise NULL is returned.
*
* Then calls gBS->LocateProtocol() (BootServices + 0x140) 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 0x4df: call [BootServices + 0x18] = gBS->AllocatePool
* @note 0x4e2: call [BootServices + 0x20] = gBS->FreePool
* @note 0x506: 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 = 0x1F = 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 validates we are in a functioning UEFI environment.
// Locate the DebugLib protocol using its GUID in the .data section.
//
Status = gBS->LocateProtocol (
&mDebugProtocolGuid,
NULL,
&mDebugProtocol
);
if (EFI_ERROR (Status)) {
mDebugProtocol = NULL;
}
} else {
mDebugProtocol = NULL;
}
return mDebugProtocol;
}
/**
* 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 via DebugAssert.
*
* @param[in] Buffer Pointer to the memory to read. Must not be NULL.
*
* @return The 64-bit value read from the given address.
*
* @note 0x752: if Buffer is NULL, calls DebugAssert at BaseLib\Unaligned.c:192
* @note 0x769: 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;
}