/** @file
OpromUpdateDxeNeonCityFPGA - UEFI DXE driver implementation.
This DXE driver provides Option ROM (OpROM) update configuration for the
NeonCityFPGA platform via the UBA (Unified Board Architecture) protocol.
It registers callback functions that define PCIe slot-to-adapter mappings,
enabling the platform to correctly configure option ROMs for installed
PCIe devices.
The driver follows the standard DXE initialization pattern:
1. Cache system table pointers (gImageHandle, gST, gBS, gRT)
2. Locate HOB list from configuration table
3. Locate UBA NeonCityFPGA board-type protocol
4. Register OpROM update configuration callbacks
Build info:
Source: e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\TypeNeonCityFPGA\OpromUpdateDxe\OpromUpdateDxe\DEBUG\
Toolchain: VS2015, X64 DEBUG
Copyright (c) Lenovo. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
DECOMPILATION NOTE: This file is a reconstructed representation based on
static analysis of the compiled binary. Some function signatures and types
are inferred and may not match the original source exactly. The code uses
MSVC extensions (__inbyte/__outbyte, VA_START/VA_END macros) and assumes
a UEFI environment with DXE drivers.
**/
#include "OpromUpdateDxeNeonCityFPGA.h"
///
/// Global variables - cached UEFI system table pointers and protocol interfaces.
/// Initialized by _ModuleEntryPoint.
///
EFI_HANDLE gImageHandle = NULL; ///< @ 0xF20: Image handle from DXE dispatcher
EFI_SYSTEM_TABLE *gST = NULL; ///< @ 0xF10: System table pointer
EFI_BOOT_SERVICES *gBS = NULL; ///< @ 0xF18: Boot services pointer
EFI_RUNTIME_SERVICES *gRT = NULL; ///< @ 0xF28: Runtime services pointer
VOID *gDebugProtocol = NULL; ///< @ 0xF30: Cached DebugLib protocol interface
VOID *gHobList = NULL; ///< @ 0xF38: Cached HOB list pointer
///
/// Protocol GUIDs and configuration data (defined in .rdata section).
///
/// @ 0xCC0: EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID
/// {BB7E702F-1A4A-11D4-9A38-0090273FC14D}
extern EFI_GUID gPciRootBridgeIoProtocolGuid;
/// @ 0xCD0: DEBUG_LIB_PROTOCOL_GUID
/// {36232936-0E76-31C8-A13A-3AF2FC1C3932}
extern EFI_GUID gDebugLibProtocolGuid;
/// @ 0xCE0: UBA_NEONCITY_FPGA_BOARD_TYPE_PROTOCOL_GUID
/// {E03E0D46-5263-4845-B0A4-58D57B3177E2}
extern EFI_GUID gUbaBoardTypeProtocolGuid;
/// @ 0xCF0: EFI_HOB_LIST_GUID first half (8 bytes)
extern UINT64 gEfiHobListGuidFirstHalf;
/// @ 0xCF8: EFI_HOB_LIST_GUID second half (8 bytes)
extern UINT64 gEfiHobListGuidSecondHalf;
/// @ 0xD00: PCIe slot configuration protocol table 0 (8 QWORDs)
extern UINT64 gPcieSlotConfigTable0[8];
/// @ 0xD40: PCIe slot table 1 (6 entries, 40 bytes each)
extern UINT8 gPcieSlotTable1[240];
/// @ 0xE40: PCIe slot table 2 (10 entries, 40 bytes each)
extern UINT8 gPcieSlotTable2[400];
/// @ 0xEA8: OPCROM_UPDATE_CONFIG_GUID
/// {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F}
extern EFI_GUID gOpromUpdateConfigGuid;
/// @ 0xEB8: UBA_OPROM_UPDATE_CONFIG
extern UBA_OPROM_UPDATE_CONFIG gUbaOpromUpdateConfig;
/// @ 0xEF1: PCIE_SLOT_BIT_CONFIG[8]
extern PCIE_SLOT_BIT_CONFIG gPcieSlotBitConfig[8];
///
/// Forward declarations of internal functions
///
VOID *EFIAPI GetHobList (VOID);
VOID *EFIAPI GetDebugProtocol (VOID);
UINTN EFIAPI DebugPrint (UINTN ErrorLevel, CONST CHAR8 *Format, ...);
VOID EFIAPI DebugAssert (CONST CHAR8 *FileName, UINTN LineNumber, CONST CHAR8 *Description);
// ---------------------------------------------------------------------------
// Helper: ReadUnaligned64
// @address 0x8A8
// ---------------------------------------------------------------------------
/**
Reads an unaligned 64-bit value from memory with NULL pointer assertion.
@param[in] Buffer Pointer to the 64-bit value to read (must not be NULL).
@return The 64-bit value at the given address.
**/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST UINT64 *Buffer
)
{
if (Buffer == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
return *Buffer;
}
// ---------------------------------------------------------------------------
// Helper: IsGuidEqual
// @address 0x838
// ---------------------------------------------------------------------------
/**
Compares two GUIDs by reading each as two 8-byte unaligned halves.
@param[in] Guid1 Pointer to the first GUID.
@param[in] Guid2 Pointer to the second GUID.
@retval TRUE GUIDs match.
@retval FALSE GUIDs differ.
**/
BOOLEAN
EFIAPI
IsGuidEqual (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
return (BOOLEAN)(
ReadUnaligned64 ((UINT64 *)Guid1) == ReadUnaligned64 ((UINT64 *)Guid2) &&
ReadUnaligned64 ((UINT64 *)Guid1 + 1) == ReadUnaligned64 ((UINT64 *)Guid2 + 1)
);
}
// ---------------------------------------------------------------------------
// Internal: GetHobList
// @address 0x760
// ---------------------------------------------------------------------------
/**
Scans SystemTable->ConfigurationTable[] for EFI_HOB_LIST_GUID and caches
the result in gHobList.
@return Pointer to the HOB list, or NULL if not found.
**/
VOID *
EFIAPI
GetHobList (
VOID
)
{
UINTN Index;
UINTN EntryCount;
UINT64 EntryBase;
if (gHobList != NULL) {
return gHobList;
}
gHobList = NULL;
//
// SystemTable is at a fixed offset from gST.
// gST + 0x68 (104) = NumberOfTableEntries
// gST + 0x70 (112) = ConfigurationTable pointer
//
EntryCount = *(UINTN *)((UINT8 *)gST + 104);
if (EntryCount != 0) {
EntryBase = *(UINT64 *)((UINT8 *)gST + 112);
for (Index = 0; Index < EntryCount; Index++) {
//
// Each config table entry: 16-byte GUID + 8-byte pointer (24 bytes total)
//
if (IsGuidEqual (
(EFI_GUID *)(EntryBase + Index * 24),
(EFI_GUID *)&gEfiHobListGuidFirstHalf
))
{
gHobList = *(VOID **)(EntryBase + Index * 24 + 16);
break;
}
}
}
if (gHobList == NULL) {
DebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000EULL);
DebugAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
}
if (gHobList == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return gHobList;
}
// ---------------------------------------------------------------------------
// Internal: GetDebugProtocol
// @address 0x618
// ---------------------------------------------------------------------------
/**
Resolves and caches the DebugLib protocol interface via gBS->LocateProtocol.
Uses a pool alloc/free as a UEFI environment validity check. If the
allocation returns a pointer <= 0x10, boot services are considered broken.
@return Pointer to the DebugLib protocol interface, or NULL on failure.
**/
VOID *
EFIAPI
GetDebugProtocol (
VOID
)
{
UINTN PoolSize;
if (gDebugProtocol != NULL) {
return gDebugProtocol;
}
//
// gBS->AllocatePool = BootServices + 24
// gBS->FreePool = BootServices + 32
//
PoolSize = (UINTN)gBS->AllocatePool (31);
gBS->FreePool ((VOID *)PoolSize);
if (PoolSize <= 0x10) {
return NULL;
}
//
// gBS->LocateProtocol = BootServices + 320 (0x140)
//
if (gBS->LocateProtocol (&gDebugLibProtocolGuid, NULL, &gDebugProtocol) < 0) {
gDebugProtocol = NULL;
}
return gDebugProtocol;
}
// ---------------------------------------------------------------------------
// Internal: DebugPrint
// @address 0x698
// ---------------------------------------------------------------------------
/**
Debug output with CMOS-based level filtering.
Reads CMOS RTC register 0x4B via I/O ports 0x70/0x71 to determine the
debug verbosity level. Falls back to MMIO 0xFDAF0490 if the CMOS level
is 0. Calls the DebugLib protocol's DebugPrint if the ErrorLevel matches.
@param[in] ErrorLevel Debug mask (typically 0x80000000 for errors).
@param[in] Format Format string.
@param[in] ... Variable arguments.
**/
UINTN
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Va;
UINTN Result;
DEBUG_LIB_PROTOCOL *Protocol;
UINT8 CmosNmiSave;
UINT8 DebugLevel;
Protocol = (DEBUG_LIB_PROTOCOL *)GetDebugProtocol ();
Result = 0;
if (Protocol != NULL) {
//
// Read current CMOS index, preserving NMI enable bit
//
CmosNmiSave = __inbyte (0x70);
__outbyte (0x70, CmosNmiSave & 0x80 | 0x4B);
//
// Read debug level from CMOS register 0x4B
//
DebugLevel = __inbyte (0x71);
if (DebugLevel > 3) {
if (DebugLevel == 0) {
//
// Fallback: read board config register at MMIO 0xFDAF0490
// Bit 1 indicates debug capability
//
DebugLevel = (*(volatile UINT8 *)0xFDAF0490) & 2 | 1;
}
}
//
// Convert level 1..4 into ErrorLevel mask
//
if (DebugLevel - 1 <= 0xFD) {
Result = 4;
if (DebugLevel == 1) {
ErrorLevel = 0x80000004; // EFI_D_ERROR
} else {
ErrorLevel = 0x80000006;
}
}
//
// Check mask match and call DebugPrint if it matches
//
if ((ErrorLevel & 0x80000000) != 0) {
VA_START (Va, Format);
Result = Protocol->DebugPrint (ErrorLevel, Format, Va);
VA_END (Va);
}
}
return Result;
}
// ---------------------------------------------------------------------------
// Internal: DebugAssert
// @address 0x720
// ---------------------------------------------------------------------------
/**
Debug assertion handler.
@param[in] FileName Source file name.
@param[in] LineNumber Line number.
@param[in] Description Assertion description.
**/
VOID
EFIAPI
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
DEBUG_LIB_PROTOCOL *Protocol;
Protocol = (DEBUG_LIB_PROTOCOL *)GetDebugProtocol ();
if (Protocol != NULL) {
Protocol->DebugAssert (FileName, LineNumber, Description);
}
}
// ---------------------------------------------------------------------------
// Callback 0: PcieSlotChecker
// @address 0x48C
// ---------------------------------------------------------------------------
/**
Checks whether a PCIe address falls within valid range for any slot whose
bit is set in the bitmap.
For each enabled slot bit, opens the PCI Root Bridge I/O Protocol to read
configuration space registers at offsets 0x19 (sub-class) and 0x1A (base class),
then checks if PcieAddress is within the [ConfigByte1, ConfigByte2] range.
@param[in] PcieAddress PCIe address (segment:bus:device:function) to test.
@param[in] SlotBitmap Bitmap of slots to test (bits 0..7 correspond to 8 slots).
@retval TRUE The address falls within a valid slot range.
@retval FALSE The address does not match any enabled slot.
**/
BOOLEAN
EFIAPI
PcieSlotChecker (
IN UINT64 PcieAddress,
IN UINT32 SlotBitmap
)
{
UINT32 Bitmap;
PCIE_SLOT_BIT_CONFIG *Entry;
BOOLEAN InRange;
UINTN Index;
EFI_STATUS Status;
VOID *RootBridgeInterface;
UINT8 ConfigByte1;
UINT8 ConfigByte2;
UINT32 ConfigBase;
Bitmap = SlotBitmap;
Entry = &gPcieSlotBitConfig[0];
InRange = FALSE;
for (Index = 0; Index < 8; Index++) {
if (((Bitmap >> Index) & 1) == 0) {
//
// This slot bit is not set - skip
//
Bitmap = SlotBitmap;
Entry++;
continue;
}
//
// Open PCI Root Bridge I/O Protocol for PCI config access
//
gBS->LocateProtocol (&gPciRootBridgeIoProtocolGuid, NULL, &RootBridgeInterface);
//
// Build config address: ((Bus | ((Device | (Function << 8)) << 8)) << 8)
//
ConfigBase = (Entry->Bus | ((Entry->Device | (Entry->Function << 8)) << 8)) << 8;
//
// Read PCI config register at offset 0x19 (sub-class code)
//
((EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *)RootBridgeInterface)->Pci.Read (
RootBridgeInterface,
EfiPciWidthUint8,
ConfigBase | 0x19,
1,
&ConfigByte1
);
//
// Read PCI config register at offset 0x1A (base class code)
//
((EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *)RootBridgeInterface)->Pci.Read (
RootBridgeInterface,
EfiPciWidthUint8,
ConfigBase | 0x1A,
1,
&ConfigByte2
);
//
// Check if PcieAddress is in range [ConfigByte1, ConfigByte2]
//
InRange = FALSE;
if (PcieAddress >= ConfigByte1) {
InRange = (BOOLEAN)(PcieAddress <= ConfigByte2);
}
Bitmap = SlotBitmap;
Entry++;
}
return InRange;
}
// ---------------------------------------------------------------------------
// Callback 1: GetPcieSlotConfigProtocol0
// @address 0x5B8
// ---------------------------------------------------------------------------
/**
Returns the base configuration protocol pointer (table 0 at 0xD00).
@param[out] Protocol Receives the protocol/structure pointer.
@return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
GetPcieSlotConfigProtocol0 (
OUT VOID **Protocol
)
{
*Protocol = (VOID *)&gPcieSlotConfigTable0;
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// Callback 2: GetPcieSlotTable1
// @address 0x5C8
// ---------------------------------------------------------------------------
/**
Returns the first PCIe slot configuration table (6 entries at 0xD40).
@param[out] Table Receives the table base pointer.
@param[out] Count Receives the entry count (6).
@return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
GetPcieSlotTable1 (
OUT VOID **Table,
OUT UINTN *Count
)
{
*Table = (VOID *)&gPcieSlotTable1;
*Count = 6;
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// Callback 3: GetPcieSlotTable2
// @address 0x5DC
// ---------------------------------------------------------------------------
/**
Returns the second PCIe slot configuration table (10 entries at 0xE40).
@param[out] Table Receives the table base pointer.
@param[out] Count Receives the entry count (10).
@return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
GetPcieSlotTable2 (
OUT VOID **Table,
OUT UINTN *Count
)
{
*Table = (VOID *)&gPcieSlotTable2;
*Count = 10;
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// Callback 4: SetPcieSlotNumberCallback
// @address 0x5F0
// ---------------------------------------------------------------------------
/**
Sets the PCIe slot number to 2 and logs the callback invocation.
@param[out] SlotNumber Receives the slot number (2).
@return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
SetPcieSlotNumberCallback (
OUT UINT8 *SlotNumber
)
{
*SlotNumber = 2;
DebugPrint (0x80000000, "[UBA]:SetPcieSlotNumber callback - %d\n", *SlotNumber);
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// Entry Point: _ModuleEntryPoint
// @address 0x390
// ---------------------------------------------------------------------------
/**
DXE driver entry point.
Initializes global pointers, locates the HOB list, and registers the
OpROM update configuration with the UBA board-type protocol.
@param[in] ImageHandle The firmware-allocated handle for this EFI image.
@param[in] SystemTable Pointer to the EFI System Table.
@retval EFI_SUCCESS Registration succeeded.
@retval EFI_NOT_FOUND UBA board-type protocol not found.
@retval other Error from gBS->LocateProtocol or registration.
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UBA_NEONCITY_BOARD_PROTOCOL *BoardProtocol;
//
// Save image handle (assert if NULL)
//
gImageHandle = (UINT64)ImageHandle;
if (ImageHandle == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)"
);
}
//
// Save system table (assert if NULL)
//
gST = (UINT64)SystemTable;
if (SystemTable == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)"
);
}
//
// Save boot services (assert if NULL)
//
gBS = (UINT64)SystemTable->BootServices;
if (gBS == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)"
);
}
//
// Save runtime services (assert if NULL)
//
gRT = (UINT64)SystemTable->RuntimeServices;
if (gRT == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)"
);
}
//
// Locate HOB list
//
GetHobList ();
//
// Log driver type
//
DebugPrint (0x80000000, "UBA:OpromUpdate-TypeNeonCityFPGA\n");
//
// Locate UBA board-type protocol
// gBS->LocateProtocol(...) = BootServices + 0x140 (320)
//
BoardProtocol = NULL;
Status = gBS->LocateProtocol (
&gUbaBoardTypeProtocolGuid,
NULL,
(VOID **)&BoardProtocol
);
//
// Register OpROM update config: 5 callback functions + GUID + 48 bytes config
//
if (!EFI_ERROR (Status)) {
return BoardProtocol->RegisterOpromUpdate (
BoardProtocol,
&gOpromUpdateConfigGuid,
&gUbaOpromUpdateConfig,
sizeof (UBA_OPROM_UPDATE_CONFIG)
);
}
return Status;
}