/**
* @file OpromUpdateDxeLightningRidgeEXECB2.c
* @brief UBA OpromUpdate DXE driver for the LightningRidge (C621/C622) EXEC B2 board.
*
* This driver is part of Intel's UBA (Unified BIOS Architecture) OpromUpdate subsystem.
* It provides board-specific PCIe slot configuration data and population detection
* for the LightningRidge PCH (Lewisburg/C620 series) EXEC B2 server board variant.
*
* The driver is tiny (< 4KB) and stateless. All its data is baked into the .rdata/.data
* sections. It does not install any protocols -- instead, it registers callbacks with
* the UBA Config Protocol so that the OpromUpdate DXE driver can call back into this
* board-specific implementation when it needs slot/port information.
*/
#include "OpromUpdateDxeLightningRidgeEXECB2.h"
//=============================================================================
// GUID Instances
//=============================================================================
///
/// EFI_PCI_IO_PROTOCOL GUID - used to read PCI config space via LocateProtocol
/// {2f707ebb-4a1a-11d4-9a38-0090273fc14d}
/// Reference at .rdata:0xCE0
///
STATIC CONST EFI_GUID mEfiPciIoProtocolGuid = EFI_PCI_IO_PROTOCOL_GUID;
///
/// UBA Debug Protocol GUID - used to locate the debug output protocol
/// {36232936-0e76-31c8-a13a-3af2fc1c3932}
/// Reference at .rdata:0xCF0
///
STATIC CONST EFI_GUID mUbaDebugProtocolGuid = UBA_DEBUG_PROTOCOL_GUID;
///
/// UBA Config Protocol GUID - main protocol for registering board config
/// {e03e0d46-5263-4845-b0a4-58d57b3177e2}
/// Reference at .rdata:0xD00
///
STATIC CONST EFI_GUID mUbaConfigProtocolGuid = UBA_CONFIG_PROTOCOL_GUID;
///
/// EFI Guided Section Extraction Protocol GUID (unused by this driver)
/// {7739f24c-93d7-11d4-9a3a-0090273fc14d}
/// Reference at .rdata:0xD10
///
STATIC CONST EFI_GUID mEfiGuidedSectionExtractionProtocolGuid =
EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID;
//
// NOTE: mEfiGuidedSectionExtractionProtocolGuid is stored at 0xD10 in the
// .rdata section but is NOT referenced by any code in this driver. It may
// be used by the UBA framework or by external consumers of the data section.
//
///
/// OpromUpdate LightningRidge EXEC B2 Board Registration GUID
/// {371bd79c-de79-4c5f-aa2b-bc9ebefa988f}
/// Reference at .rdata:0xEF0
///
STATIC CONST EFI_GUID mOpromUpdateBoardGuid = OPROM_UPDATE_BOARD_GUID;
//=============================================================================
// PCIe Root Port BDF Lookup Table (.data@0xED1)
//=============================================================================
///
/// PCIe Root Port BDF lookup table.
/// Maps 8 PCIe root port indices to Bus:Device:Function addresses.
/// Used by IsFunctionInPopulatedSlot() to locate root port config space.
///
/// Format: each entry is a 3-byte descriptor (at stride 4):
/// byte 0 = Register offset (in the encoding: shifted to bits [15:8])
/// byte 1 = [2:0]=Function, [7:3]=Device (in the encoding: shifted to bits [23:16])
/// byte 2 = Bus number (in the encoding: shifted to bits [31:24])
///
/// The "in the encoding" means combined as:
/// EFI_PCI_IO_PROTOCOL_PCI_ADDRESS(Bus, Dev, Func, Reg) =
/// Reg | (Func << 8) | (Dev << 11) | (Bus << 16) | BIT28 (extended cfg)
///
/// Decoded entries (from 0xED1 data):
/// Entry 0: { 0x00, 0x03, 0x00 } -> B=0 D=0 F=3 Reg=0x00 (PCH internal)
/// Entry 1: { 0x00, 0x01, 0x80 } -> B=128 D=0 F=1 Reg=0x00 (PCIe domain)
/// Entry 2: { 0x00, 0x02, 0x80 } -> B=128 D=0 F=2 Reg=0x00 (PCIe domain)
/// Entry 3: { 0x02, 0x03, 0x80 } -> B=128 D=0 F=3 Reg=0x02 (PCIe domain)
/// Entry 4: { 0x00, 0x02, 0x00 } -> B=0 D=0 F=2 Reg=0x00 (PCH internal)
/// Entry 5: { 0x00, 0x01, 0x00 } -> B=0 D=0 F=1 Reg=0x00 (PCH internal)
/// Entry 6: { 0x05, 0x1c, 0x00 } -> B=0 D=3 F=4 Reg=0x05 (PCH internal)
/// Entry 7: { 0x00, 0x03, 0x80 } -> B=128 D=0 F=3 Reg=0x00 (PCIe domain, dup)
///
/// Bus 0x80 (128) entries are in the PCIe domain range (typically bus 128+).
/// Bus 0 entries are PCH-internal PCIe root ports in the legacy bus 0 range.
///
STATIC CONST UINT8 mPcieRootPortBdfTable[MAX_PCIE_ROOT_PORTS * 4] __attribute__((aligned(4))) = {
// Each entry: { RegOffset, DevFunc, Bus, Pad } (4 bytes)
0x00, 0x03, 0x00, 0x00, // Entry 0
0x00, 0x01, 0x80, 0x00, // Entry 1
0x00, 0x02, 0x80, 0x00, // Entry 2
0x02, 0x03, 0x80, 0x00, // Entry 3
0x00, 0x02, 0x00, 0x00, // Entry 4
0x00, 0x01, 0x00, 0x00, // Entry 5
0x05, 0x1C, 0x00, 0x00, // Entry 6
0x00, 0x03, 0x80, 0x00 // Entry 7
};
//=============================================================================
// PCIe Slot Configuration Tables (.data section)
//=============================================================================
///
/// PCIe Slot Configuration Table 1 (at .data@0xD60).
/// Contains 6 entries describing PCIe slot-to-BDF mappings.
///
/// Each entry encodes slot port info, capability flags, and PCI address.
/// Used by GetSlotConfig1() callback.
///
STATIC CONST PCIE_SLOT_CONFIG_ENTRY mSlotConfigTable1[MAX_SLOT_CONFIG1_ENTRIES] = {
{ 0x00000001, 0x00020001, 0x15288086 },
{ 0x48010802, 0x01000000, 0x0000002D },
{ 0x00000001, 0x0000002C, 0x00000001 },
{ 0xFF2FFF2E, 0x00000100, 0x02000100 },
{ 0x21808600, 0x01010215, 0x00000048 },
{ 0x00002D01, 0x00000100, 0x00002C00 }
};
///
/// PCIe Slot Configuration Table 2 (at .data@0xE60).
/// Contains 10 entries describing additional PCIe slot-to-BDF mappings.
///
/// Used by GetSlotConfig2() callback.
///
STATIC CONST PCIE_SLOT_CONFIG_ENTRY mSlotConfigTable2[MAX_SLOT_CONFIG2_ENTRIES] = {
{ 0x358010D3, 0x00010100, 0x15210000 },
{ 0x01003580, 0x00000001, 0x35B01521 },
{ 0x00010100, 0x15220000, 0x01003580 },
{ 0x00000001, 0x35561521, 0x00010100 },
{ 0x10FB0000, 0x01013557, 0x00000101 },
{ 0x35581528, 0x01010101, 0x15280000 },
{ 0x010135C5, 0x00000101, 0x10031003 },
{ 0x00000000, 0x15280001, 0x010135A0 },
{ 0x00000101, 0x00000000, 0x00000000 },
{ 0x03000000, 0x01000000, 0x02000080 }
};
//=============================================================================
// Empty Board Info Structure (.data@0xD20)
//=============================================================================
///
/// Empty board info structure. LightningRidge EXEC B2 does not provide
/// any additional board-specific data beyond the PCIe slot config tables.
///
STATIC CONST UINT64 mEmptyBoardInfo[8] = { 0 };
//=============================================================================
// PBDS Board Config Registration Structure (.data@0xEF0)
//=============================================================================
///
/// PBDS board config registration structure.
/// Contains the board GUID and 4 callback function RVAs.
/// Registered with UBA Config Protocol at driver entry.
///
/// Layout (48 bytes):
/// offset 0: Board GUID (OPROM_UPDATE_BOARD_GUID, 16 bytes)
/// offset 16: PBDS_CONFIG_SIGNATURE ("PBDS", 4 bytes)
/// offset 20: Version (1, 4 bytes)
/// offset 24: Callback 0 - IsFunctionInPopulatedSlot (8 bytes, relocatable RVA)
/// offset 32: Callback 1 - GetBoardInfo (8 bytes, relocatable RVA)
/// offset 40: Callback 2 - GetSlotConfig1 (8 bytes, relocatable RVA)
/// offset 48 (= 0x30): Callback 3 - GetSlotConfig2 (8 bytes, relocatable RVA)
///
/// Total from start of GUID: 16 + 32 = 48 bytes
///
typedef struct {
EFI_GUID BoardGuid;
UINT32 Signature; ///< "PBDS"
UINT32 Version; ///< 1
UINT64 Callbacks[4];///< Relocatable callback pointers
} PBDS_REGISTRATION;
STATIC CONST PBDS_REGISTRATION mBoardRegistration = {
OPROM_UPDATE_BOARD_GUID,
SIGNATURE_32('P', 'B', 'D', 'S'),
1,
{ (UINT64)(UINTN)IsFunctionInPopulatedSlot,
(UINT64)(UINTN)GetBoardInfo,
(UINT64)(UINTN)GetSlotConfig1,
(UINT64)(UINTN)GetSlotConfig2 }
};
//=============================================================================
// Global Variables (.data section)
//=============================================================================
///
/// Cached pointer to the UEFI System Table.
/// Initialized in _ModuleEntryPoint().
///
EFI_SYSTEM_TABLE *gSystemTable = NULL;
///
/// Cached pointer to the UEFI Boot Services Table.
/// Initialized in _ModuleEntryPoint().
///
EFI_BOOT_SERVICES *gBootServices = NULL;
///
/// Cached pointer to the UEFI Runtime Services Table.
/// Initialized in _ModuleEntryPoint().
///
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
///
/// Cached pointer to the UBA Debug Protocol interface.
/// Lazily initialized by LocateDebugProtocol().
///
VOID *gDebugProtocol = NULL;
///
/// Cached pointer to the HOB (Hand-Off Block) list.
/// Populated by GetHobList().
///
VOID *gHobList = NULL;
///
/// Debug output control flags.
/// Determines whether debug output is suppressed or displayed.
///
UINTN gDebugFlags = 0;
//=============================================================================
// Helper: ReadUnaligned64
//=============================================================================
/**
* Reads a 64-bit value from an unaligned memory address.
*
* @param[in] Buffer Memory address (possibly unaligned)
* @return UINT64 The 64-bit value at the address
*/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
//
// Trigger assertion using UBA Debug Protocol assert handler.
//
ReportAssert (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
return *(CONST UINT64 *)Buffer;
}
//=============================================================================
// Helper: ReportAssert
//=============================================================================
/**
* Reports an assertion failure to the UBA Debug Protocol.
*
* If the debug protocol is available (cached in gDebugProtocol),
* invokes its ReportAssert function at interface+0.
*
* @param[in] FileName Source file where assertion failed
* @param[in] LineNumber Line number of failure
* @param[in] Description Assert condition string
*
* @return EFI_STATUS from debug protocol assert handler, or EFI_NOT_FOUND
*/
EFI_STATUS
EFIAPI
ReportAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
EFI_STATUS Status;
VOID *DebugProtocol;
//
// Lazily locate the UBA Debug Protocol if not yet cached.
//
DebugProtocol = LocateDebugProtocol ();
if (DebugProtocol != NULL) {
//
// Call the ReportAssert function at interface offset +0.
// The debug protocol interface has:
// +0: ReportAssert(FileName, LineNumber, Description)
// +8: Report(ErrorLevel, Format, ...)
//
Status = ((EFI_STATUS (EFIAPI *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))DebugProtocol)(
FileName,
LineNumber,
Description
);
return Status;
}
return EFI_NOT_FOUND;
}
//=============================================================================
// Helper: DebugPrint
//=============================================================================
/**
* Outputs a debug message via the UBA Debug Protocol.
*
* Performs a CMOS status check before printing:
* - Reads CMOS index 0x4B (CMOS status register)
* - Checks if the CMOS byte indicates debug verbosity level
* - Only prints if the requested ErrorLevel matches configured level
*
* @param[in] ErrorLevel Debug severity bitmask
* @param[in] Format Format string
* @param[in] ... Variable arguments
*
* @retval 0 Message was suppressed (protocol unavailable or level mismatch)
* @retval 1 Message was printed successfully
*/
UINT8
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST VaList;
VOID *DebugProtocol;
UINT8 DebugLevel;
UINTN EffectiveErrorLevel;
DebugProtocol = LocateDebugProtocol ();
if (DebugProtocol == NULL) {
return 0;
}
//
// Check CMOS debug verbosity configuration.
// CMOS index 0x4B (at I/O ports 0x70/0x71) stores board-specific
// debug output control bits.
//
// CMOS register 0x4B layout:
// bit 7 = valid flag (if set, use configured level)
// bits [6:0] = debug verbosity level
//
// Read CMOS by writing index to 0x70, then reading data from 0x71.
// The index byte preserves bit 7 of the CMOS index register
// (NMI enable/disable).
//
{
UINT8 IndexReg;
UINT8 DataByte;
IndexReg = __inbyte (0x70);
__outbyte (0x70, (UINT8)(IndexReg & 0x80) | 0x4B);
DataByte = __inbyte (0x71);
if (DataByte > 3 && DataByte == 0) {
//
// If the debug level is > 3 AND also 0... this is a hardware quirk
// for this platform. Check an additional hardware register for
// the override flag.
//
// NOTE: MEMORY[0xFDAF0490] is a platform-specific MMIO register
// that provides debug override. This is specific to the C620
// chipset/LightningRidge PCH memory-mapped configuration space.
// bit 1 = debug enable
//
// This MMIO address is a virtual/physical address in the
// chipset's MMIO configuration space (typically mapped by
// the PCH root complex).
//
DataByte = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
}
DebugLevel = DataByte;
}
//
// Determine if our error level matches the CMOS-configured level.
// The CMOS level is 0-based (0=disable, 1=error, 2=warn, 3=info, 4+=verbose)
// while ErrorLevel is a UEFI-style bitmask.
//
EffectiveErrorLevel = 0x80000000; // DEBUG_INFO = bit 31
if (DebugLevel == 1) {
EffectiveErrorLevel = 0x80000004; // DEBUG_ERROR = bit 2
}
if ((EffectiveErrorLevel & ErrorLevel) == 0) {
//
// The requested error level is not enabled in the current debug config.
//
return 0;
}
//
// Call the UBA Debug Protocol's Report function at interface offset +8.
// Takes (ErrorLevel, FormatString, VariableArgs) as parameters.
//
va_start (VaList, Format);
((VOID (EFIAPI *)(UINTN, CONST CHAR8 *, VA_LIST))((UINT8 *)DebugProtocol + 8))(
ErrorLevel,
Format,
VaList
);
va_end (VaList);
return 1;
}
//=============================================================================
// Helper: LocateDebugProtocol
//=============================================================================
/**
* Lazily locates and caches the UBA Debug Protocol.
*
* This function uses a trick to detect if it has been called before
* in a high-TPL context (where protocol lookup would be dangerous):
* it raises TPL to TPL_NOTIFY (31), restores, and checks if old TPL was
* <= 0x10 (TPL_APPLICATION level). If so, it is safe to locate the protocol.
*
* The protocol interface is located via gBS->LocateProtocol() and cached
* in gDebugProtocol for subsequent calls.
*
* @return Pointer to the UBA Debug Protocol interface, or NULL on failure
*/
VOID *
LocateDebugProtocol (
VOID
)
{
EFI_STATUS Status;
UINT64 PagesAndMemory;
if (gDebugProtocol != NULL) {
//
// Already cached.
//
return gDebugProtocol;
}
//
// Raise TPL to TPL_NOTIFY (31) to prevent any callbacks during
// our critical section (protocol location can re-enter).
//
// gBS->RaiseTPL (TPL_NOTIFY) returns the old TPL level.
// We save the old TPL and immediately restore it.
//
PagesAndMemory = gBS->RaiseTPL (TPL_NOTIFY);
gBS->RestoreTPL ((EFI_TPL)PagesAndMemory);
//
// If the old TPL was <= TPL_APPLICATION (16 = 0x10),
// we are in a safe context to locate the protocol.
//
if (PagesAndMemory <= 0x10) {
//
// Locate the UBA Debug Protocol.
//
Status = gBS->LocateProtocol (
&mUbaDebugProtocolGuid,
NULL, // Registration (not used)
&gDebugProtocol
);
if (EFI_ERROR (Status)) {
//
// Protocol not found; clear the cached pointer.
//
gDebugProtocol = NULL;
}
}
return gDebugProtocol;
}
//=============================================================================
// Helper: CompareGuid
//=============================================================================
/**
* Compares two GUIDs for byte-level equality.
*
* @param[in] Guid1 Pointer to first GUID
* @param[in] Guid2 Pointer to second GUID
* @retval TRUE GUIDs are equal
* @retval FALSE GUIDs differ or either pointer is NULL
*/
BOOLEAN
EFIAPI
CompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
CONST UINT64 *Guid1Data;
CONST UINT64 *Guid2Data;
if (Guid1 == NULL || Guid2 == NULL) {
//
// Trigger assertion via UBA Debug Protocol.
//
ReportAssert (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
return FALSE;
}
//
// Compare as two 64-bit values. The GUID structure is:
// Data1: UINT32 at offset 0
// Data2: UINT16 at offset 4
// Data3: UINT16 at offset 6
// Data4: UINT8[8] at offset 8
//
// For 64-bit comparison, first 8 bytes and last 8 bytes.
// Use ReadUnaligned64 for safe access (GUID may not be aligned).
//
Guid1Data = (CONST UINT64 *)Guid1;
Guid2Data = (CONST UINT64 *)Guid2;
//
// WARNING: The original binary uses ReadUnaligned64 which reads
// a full UINT64 from the given address. GUID comparison in the
// original code is: ((UINT64 *)Guid1)[0] == ((UINT64 *)Guid2)[0] &&
// ((UINT64 *)Guid1)[1] == ((UINT64 *)Guid2)[1]
//
if (ReadUnaligned64 (Guid1) == ReadUnaligned64 (Guid2) &&
ReadUnaligned64 ((UINT8 *)Guid1 + 8) == ReadUnaligned64 ((UINT8 *)Guid2 + 8)) {
return TRUE;
}
return FALSE;
}
//=============================================================================
// Helper: GetHobList
//=============================================================================
/**
* Locates and caches the HOB (Hand-Off Block) list pointer.
*
* The HOB list pointer is stored in the UEFI SystemTable's
* configuration table. The SystemTable->ConfigurationTable is an array
* of EFI_CONFIGURATION_TABLE entries. This function scans the table
* for an entry whose VendorGuid matches gHobListGuid
* ({ 0x7739f24c, 0x93d7, 0xd4, ... } = gEfiHobListGuid),
* then caches the HOB list pointer.
*
* @return Pointer to the HOB list, or NULL if not found
*/
VOID *
GetHobList (
VOID
)
{
UINTN Index;
EFI_CONFIGURATION_TABLE *ConfigTable;
UINTN NumberOfTableEntries;
if (gHobList != NULL) {
//
// Already cached.
//
return gHobList;
}
gHobList = NULL;
//
// Get the configuration table pointer.
// SystemTable->ConfigurationTable is at offset 0x70.
// SystemTable->NumberOfTableEntries is at offset 0x68.
//
// In the original binary's compiled code:
// NumberOfTableEntries = *(UINTN *)(SystemTable + 0x68)
// ConfigTable = *(VOID **)(SystemTable + 0x70)
//
NumberOfTableEntries = gSystemTable->NumberOfTableEntries;
ConfigTable = gSystemTable->ConfigurationTable;
if (NumberOfTableEntries > 0) {
for (Index = 0; Index < NumberOfTableEntries; Index++) {
//
// Compare the GUID of each configuration table entry
// against the HOB list GUID.
//
if (CompareGuid (&(gHobListGuid), &(ConfigTable[Index].VendorGuid))) {
//
// Found the HOB list entry.
// ConfigTable[Index].VendorTable is the pointer to the HOB list.
//
gHobList = ConfigTable[Index].VendorTable;
break;
}
}
if (gHobList == NULL) {
//
// Failed to find the HOB list in the configuration table.
// Report assert.
//
DebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000EULL);
ReportAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
}
}
if (gHobList == NULL) {
ReportAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return gHobList;
}
//=============================================================================
// Board-Specific Callbacks
//=============================================================================
/**
* Checks if a PCI function belongs to a populated PCIe slot.
*
* Iterates the 8-entry PCIe root port BDF table. For each root port
* i where (SlotBitmask >> i) & 1 == 0 (slot is populated), reads
* the root port's PCI Express Device Capabilities register to
* determine the supported function number range.
*
* The root port config space is read at offsets:
* BDF_Base | 0x19 = PCI Express Device Capabilities (lower byte = First Function Number)
* BDF_Base | 0x1A = PCI Express Device Capabilities 2 (lower byte = Last Function?)
*
* Where BDF_Base is the EFI_PCI_IO_PROTOCOL_PCI_ADDRESS encoded address
* from the root port BDF entry.
*
* Uses EFI_PCI_IO_PROTOCOL->Pci.Read() via a per-call LocateProtocol.
*
* @param[in] FuncNum The PCI function number to test
* @param[in] SlotBitmask 8-bit mask: bit=0 means populated slot to check
*
* @retval TRUE The function number is within range of a populated slot
* @retval FALSE Not behind any populated slot
*/
BOOLEAN
EFIAPI
IsFunctionInPopulatedSlot (
IN UINT64 FuncNum,
IN UINT32 SlotBitmask
)
{
UINTN Index;
BOOLEAN IsInRange;
UINT32 RegBase;
UINT32 PciAddr;
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_STATUS Status;
UINT8 CapFuncMin;
UINT8 CapFuncMax;
CONST PCIE_ROOT_PORT_BDF_ENTRY *RootPortEntry;
RootPortEntry = (CONST PCIE_ROOT_PORT_BDF_ENTRY *)mPcieRootPortBdfTable;
IsInRange = FALSE;
PciIo = NULL;
Status = gBS->LocateProtocol (
&mEfiPciIoProtocolGuid,
NULL,
(VOID **)&PciIo
);
if (EFI_ERROR (Status) || PciIo == NULL) {
//
// Cannot access PCI config space; assume function is not in a populated slot.
//
return FALSE;
}
for (Index = 0; Index < MAX_PCIE_ROOT_PORTS; Index++) {
if (((SlotBitmask >> Index) & 1) != 0) {
//
// Bit is set: this slot is NOT populated (or is excluded from check).
//
continue;
}
//
// Build EFI_PCI_IO_PROTOCOL_PCI_ADDRESS from the root port entry.
// Encoding:
// Bits [7:0] = Register base (RootPortEntry->RegisterOffset)
// Bits [10:8] = Function = RootPortEntry->DevFunc & 0x07
// Bits [15:11] = Device = (RootPortEntry->DevFunc >> 3) & 0x1F
// Bits [23:16] = Bus = RootPortEntry->Bus
// Bit 28 = 1 (extended config space)
//
PciAddr = (RootPortEntry[Index].RegisterOffset & 0xFF) |
((RootPortEntry[Index].DevFunc & 0x07) << 8) |
(((RootPortEntry[Index].DevFunc >> 3) & 0x1F) << 11) |
((RootPortEntry[Index].Bus) << 16) |
BIT28;
//
// Read PCI Express Device Capabilities function range.
// Offset 0x19 relative to the capability base:
// - Lower byte = First Function Number of the device
// Offset 0x1A relative to the capability base:
// - Lower byte = Function range / capability indicator
//
// These reads identify which function numbers the downstream
// device (behind this root port) occupies.
//
PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint8,
PciAddr | 0x19,
1,
&CapFuncMin
);
PciIo->Pci.Read (
PciIo,
EfiPciIoWidthUint8,
PciAddr | 0x1A,
1,
&CapFuncMax
);
//
// Check if the given function number falls within the range.
//
if (FuncNum >= CapFuncMin && FuncNum <= CapFuncMax) {
IsInRange = TRUE;
}
}
return IsInRange;
}
/**
* Returns the board info structure pointer.
*
* LightningRidge EXEC B2 has no additional board info beyond what's
* contained in the PCIe slot config tables. Returns a pointer to
* an empty zeroed structure.
*
* @param[out] BoardInfo Set to point to the (empty) board info structure
* @return EFI_SUCCESS
*/
EFI_STATUS
EFIAPI
GetBoardInfo (
OUT VOID **BoardInfo
)
{
*BoardInfo = (VOID *)&mEmptyBoardInfo;
return EFI_SUCCESS;
}
/**
* Returns the first PCIe slot configuration table and its count.
*
* The mSlotConfigTable1 table contains 6 entries describing
* PCIe slot-to-BDF mappings for the LightningRidge EXEC B2 board.
*
* @param[out] SlotConfig Set to point to the first slot config table
* @param[out] EntryCount Set to 6
* @return EFI_SUCCESS
*/
EFI_STATUS
EFIAPI
GetSlotConfig1 (
OUT VOID **SlotConfig,
OUT UINTN *EntryCount
)
{
*SlotConfig = (VOID *)&mSlotConfigTable1;
*EntryCount = MAX_SLOT_CONFIG1_ENTRIES; // 6
return EFI_SUCCESS;
}
/**
* Returns the second PCIe slot configuration table and its count.
*
* The mSlotConfigTable2 table contains 10 entries describing
* additional PCIe slot-to-BDF mappings for the LightningRidge
* EXEC B2 board.
*
* @param[out] SlotConfig Set to point to the second slot config table
* @param[out] EntryCount Set to 10
* @return EFI_SUCCESS
*/
EFI_STATUS
EFIAPI
GetSlotConfig2 (
OUT VOID **SlotConfig,
OUT UINTN *EntryCount
)
{
*SlotConfig = (VOID *)&mSlotConfigTable2;
*EntryCount = MAX_SLOT_CONFIG2_ENTRIES; // 10
return EFI_SUCCESS;
}
/**
* Debug callback invoked when UBA sets/overrides a PCIe slot number.
*
* LightningRidge EXEC B2 does not override PCIe slot numbers.
* The slot number output is written as 0 (use hardware default).
* Prints a debug message indicating this callback was invoked.
*
* @param[out] SlotNumber Set to 0 (no override)
* @return EFI_SUCCESS
*/
EFI_STATUS
EFIAPI
SetPcieSlotNumber (
OUT UINT8 *SlotNumber
)
{
*SlotNumber = 0;
DebugPrint (0x80000000, "[UBA]:SetPcieSlotNumber callback - %d\n", *SlotNumber);
return EFI_SUCCESS;
}
//=============================================================================
// Driver Entry Point
//=============================================================================
/**
* UEFI Driver Entry Point.
*
* Initializes UEFI globals (ImageHandle, SystemTable, BootServices,
* RuntimeServices), locates the HOB list, then registers the
* LightningRidge EXEC B2 board configuration with the UBA Config Protocol.
*
* The registration is done via:
* 1. LocateProtocol(gUbaConfigProtocolGuid, NULL, &UbaConfig)
* 2. UbaConfig->RegisterBoardConfig(This, gOpromUpdateBoardGuid,
* &BoardConfigData, sizeof(PBDS_REGISTRATION))
*
* where BoardConfigData is the PBDS structure with:
* - Board GUID: OPROM_UPDATE_BOARD_GUID (371bd79c-...)
* - Signature: "PBDS"
* - Version: 1
* - Callbacks: IsFunctionInPopulatedSlot,
* GetBoardInfo,
* GetSlotConfig1,
* GetSlotConfig2
*
* @param[in] ImageHandle UEFI image handle
* @param[in] SystemTable UEFI system table
*
* @return EFI_SUCCESS Board config registered
* @return EFI_UNSUPPORTED UBA Config Protocol not found
* @return EFI_NOT_FOUND UBA Config Protocol function not available
*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_IMAGE_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *UbaConfigProtocol;
PBDS_REGISTRATION *Registration;
//
// --- BEGIN: UefiBootServicesTableLib initialization ---
//
// Cache ImageHandle: This is required for driver unload and protocol operations.
//
gImageHandle = ImageHandle;
if (gImageHandle == NULL) {
ReportAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)"
);
}
//
// Cache SystemTable.
//
gSystemTable = SystemTable;
if (gSystemTable == NULL) {
ReportAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)"
);
}
//
// Cache BootServices from SystemTable.
//
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
ReportAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)"
);
}
//
// --- BEGIN: UefiRuntimeServicesTableLib initialization ---
//
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
ReportAssert (
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)"
);
}
//
// Locate and cache the HOB list from SystemTable configuration table.
//
GetHobList ();
//
// Register board configuration with UBA Config Protocol.
// First, log registration attempt.
//
DebugPrint (
0x80000000,
"UBA:OpromUpdate-TypeLightningRidgeEXECB2\n"
);
//
// Locate the UBA Config Protocol.
// The protocol GUID is e03e0d46-5263-4845-b0a4-58d57b3177e2.
//
Registration = (PBDS_REGISTRATION *)&mBoardRegistration;
UbaConfigProtocol = NULL;
Status = gBS->LocateProtocol (
&mUbaConfigProtocolGuid,
NULL,
&UbaConfigProtocol
);
if (!EFI_ERROR (Status) && UbaConfigProtocol != NULL) {
//
// The UBA Config Protocol interface provides:
// +0: Unknown (version/query?)
// +8: Unknown
// +16: RegisterBoardConfig(This, BoardGuid, ConfigData, ConfigSize)
// ... potentially more functions
//
// Register the board:
// Guid = OPROM_UPDATE_BOARD_GUID (371bd79c-...)
// Data = PBDS_BOARD_CONFIG structure with callbacks
// Size = sizeof(PBDS_REGISTRATION) (48 bytes)
//
// Function at protocol+16 (index 2) takes:
// (ProtocolInterface, BoardGuid, ConfigData, ConfigSize)
//
Status = ((EFI_STATUS (EFIAPI *)(VOID *, EFI_GUID *, VOID *, UINTN))(
((UINT8 *)UbaConfigProtocol + 16)))(
UbaConfigProtocol,
&Registration->BoardGuid,
(UINT8 *)Registration + sizeof (EFI_GUID), // Skip GUID, point to PBDS data
sizeof (PBDS_BOARD_CONFIG) // 32 bytes: sig + ver + 4 funcs
);
}
return Status;
}