/**
* @file OpromUpdateDxeLightningRidgeEXECB4.c
*
* @brief OpromUpdateDxeLightningRidgeEXECB4 - UEFI DXE driver implementation.
* UBA board-type driver for Lightning Ridge EXEC B4 PCIe OpROM update
* stub configuration (no OpROM updates supported on this platform).
*
* This driver registers a minimal stub OpROM update configuration with the
* UBA (Unified Board Architecture) framework. Unlike the EXEC B1/B2/B3 variants
* which provide full callback-based PCIe OpROM configuration tables, the EXEC B4
* platform registers a single callback that returns EFI_UNSUPPORTED for all
* operations, indicating that this platform does not support PCIe OpROM updates.
*
* ADDRESS RANGE: 0x2C0 - 0x760 (code segment, 0x4A0 bytes)
* ENTRY POINT: _ModuleEntryPoint at 0x390
* TOTAL FUNCTIONS: 8
*
* FUNCTION INDEX
* _ModuleEntryPoint (0x390) - Driver entry point
* OpromOperationUnsupported (0x48C) - Stub callback returning EFI_UNSUPPORTED
* GetDebugLibProtocol (0x498) - Resolve DebugLib/OpROM protocol interface
* DebugPrint (0x518) - Debug output via DebugLib protocol
* DebugAssert (0x5A0) - Assertion failure handler
* GetHobList (0x5E0) - Locate HOB list from system table
* IsHobListGuid (0x6B8) - Compare GUID against HOB list GUID
* ReadUnaligned64 (0x728) - Unaligned 64-bit memory read
*/
#include "OpromUpdateDxeLightningRidgeEXECB4.h"
#include <stdarg.h> // va_list, va_start, va_end
#include <stddef.h> // NULL
// ============================================================================
// Platform I/O Intrinsics (MSVC __inbyte / __outbyte replacements)
// ============================================================================
#if defined(__GNUC__) || defined(__clang__)
static __inline__
UINT8
__inbyte (
UINT16 Port
)
{
UINT8 Value;
__asm__ __volatile__ ("inb %1, %0" : "=a" (Value) : "d" (Port));
return Value;
}
static __inline__
VOID
__outbyte (
UINT16 Port,
UINT8 Value
)
{
__asm__ __volatile__ ("outb %0, %1" : : "a" (Value), "d" (Port));
}
#endif
// ============================================================================
// Embedded Binary Data (GUIDs and Protocol Interface)
// ============================================================================
///
/// GUID storage block at 0xB40 in .data.
/// Contains GUIDs used by the driver.
///
static CONST UINT8 GuidBlock[0x40] = {
//
// [0x00] DEBUG_LIB_PROTOCOL_GUID
// {36232936-0E76-31C8-A13A-3AF2FC1C3932}
//
0x36, 0x29, 0x23, 0x36, 0x76, 0x0E, 0xC8, 0x31,
0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32,
//
// [0x10] UBA_LIGHTNING_RIDGE_EXEC_B4_BOARD_TYPE_PROTOCOL_GUID
// {E03E0D46-5263-4845-B0A4-58D57B3177E2}
//
0x46, 0x0D, 0x3E, 0xE0, 0x63, 0x52, 0x45, 0x48,
0xB0, 0xA4, 0x58, 0xD5, 0x7B, 0x31, 0x77, 0xE2,
//
// [0x20] EFI_HOB_LIST_GUID
// {7739F24C-93D7-11D4-9A3A-0090273FC14D}
//
0x4C, 0xF2, 0x39, 0x77, 0xD7, 0x93, 0xD4, 0x11,
0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D,
//
// [0x30] OPROM_UPDATE_PROTOCOL_REGISTRATION_GUID
// {CD1F9574-DD03-4196-96AD-4965146F9665}
//
0x74, 0x95, 0x1F, 0xCD, 0x03, 0xDD, 0x96, 0x41,
0x96, 0xAD, 0x49, 0x65, 0x14, 0x6F, 0x96, 0x65,
};
///
/// OpROM update stub protocol interface data block (at 0xB80 in .data).
/// This structure serves as the registered protocol interface.
/// It starts with a signature/header ("PSET") followed by a callback pointer.
///
/// Layout (24 bytes):
/// +0x00: 0x0000000154455350 = "PSET" signature + version 1
/// +0x08: Callback pointer to OpromOperationUnsupported (0x48C)
/// +0x10: Reserved / second copy of callback pointer (0x48C)
///
static CONST UINT8 OpromUpdateProtocolData[OPROM_UPDATE_PROTOCOL_DATA_SIZE] = {
0x50, 0x53, 0x45, 0x54, // "PSET" signature
0x01, 0x00, 0x00, 0x00, // Version = 1
0x8C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Callback -> sub_48C (0x48C)
0x8C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved / second copy -> sub_48C
};
// ============================================================================
// Global Variables (.data segment)
// ============================================================================
///
/// Cached pointer to the OpROM update protocol interface.
/// Resolved once by GetDebugLibProtocol() and reused thereafter.
///
static VOID *gOpromUpdateProtocol = NULL;
///
/// Cached pointer to the HOB (Hand-Off Block) list.
/// Resolved once by GetHobList() and reused thereafter.
///
VOID *gHobList = NULL;
// ============================================================================
// Stub Callback Implementation
// ============================================================================
/**
* Stub OpROM operation callback.
*
* This is the only callback registered by the EXEC B4 platform.
* It returns EFI_UNSUPPORTED (0x800000000000000E), indicating that
* OpROM updates are not supported on this platform variant.
*
* Unlike the EXEC B1/B2/B3 variants which provide full configuration
* tables with 4 callbacks (GetConfigA/B/C, SetPcieSlotNumber), the
* EXEC B4 platform simply reports "unsupported" for any OpROM update
* request.
*
* @return EFI_UNSUPPORTED Always returns 0x800000000000000E.
*/
EFI_STATUS
OpromOperationUnsupported (
VOID
)
{
return OPROM_UNSUPPORTED;
}
// ============================================================================
// Protocol Resolution
// ============================================================================
/**
* Locates the DebugLib protocol interface and initializes the OpROM update
* stub interface.
*
* This function performs the following:
* 1. Allocates pool memory for the OpROM update protocol interface.
* 2. Initializes the interface with the stub callback pointer.
* 3. Locates the DebugLib protocol via gBS->LocateProtocol() using the
* DebugLib GUID (36232936-0E76-31C8-A13A-3AF2FC1C3932).
* 4. Caches the result in gOpromUpdateProtocol.
*
* The DebugLib protocol interface is a structure with functions at offsets
* 0x00 and 0x08 (relative to the interface pointer).
* - Offset 0x00: Debug output function
* - Offset 0x08: Debug assertion handler
*
* If gBS->LocateProtocol() fails, gOpromUpdateProtocol is set to NULL.
*
* @return Pointer to the OpROM update protocol interface, or NULL on failure.
*/
VOID *
GetDebugLibProtocol (
VOID
)
{
//
// Check if protocol was already resolved.
//
if (gOpromUpdateProtocol != NULL) {
return gOpromUpdateProtocol;
}
//
// Allocate pool for the protocol interface.
// The pool type used corresponds to memory type 31 (implementation-specific
// allocation type for UBA protocol interfaces).
// AllocatePool signature: (PoolType, Size, Buffer).
//
(*gBS->AllocatePool)(31, sizeof(OPROM_UPDATE_PROTOCOL_STUB_INTERFACE), &gOpromUpdateProtocol);
//
// Initialize the protocol interface with the single callback pointer.
//
if (gOpromUpdateProtocol != NULL) {
((OPROM_UPDATE_PROTOCOL_STUB_INTERFACE *)gOpromUpdateProtocol)->OpromOperation = OpromOperationUnsupported;
}
//
// Locate the DebugLib protocol using gBS->LocateProtocol().
// If the protocol cannot be located (e.g., not yet installed), set the
// cached pointer to NULL so the caller can handle gracefully.
//
if (EFI_ERROR((*gBS->LocateProtocol)((EFI_GUID *)&GuidBlock[0x00], NULL, &gOpromUpdateProtocol))) {
gOpromUpdateProtocol = NULL;
}
return gOpromUpdateProtocol;
}
// ============================================================================
// Debug Output
// ============================================================================
/**
* Debug print function.
*
* Resolves the DebugLib protocol via GetDebugLibProtocol() and calls its
* debug output function if the current debug level matches the requested
* severity mask.
*
* Debug level determination:
* The platform type is determined by reading CMOS register 0x4B:
* 1. Read CMOS index 0x4B (preserving bit 7 - NMI disable bit).
* 2. If the value is > 3, use the value directly.
* 3. If the value is 0, fall back to MMIO register 0xFDAF0490
* (read bit 1, OR with 1).
* 4. Platform type = (value - 1).
* - Type 1: Lightning Ridge EXEC B4 (enables DEBUG_INFO output)
* - Other types: Different platform variant
*
* Debug output is only emitted when:
* - A valid DebugLib protocol interface is available, AND
* - The platform type matches, AND
* - The ErrorLevel mask matches (DEBUG_INFO = 0x80000000)
*
* @param[in] ErrorLevel Debug severity mask (e.g., DEBUG_INFO = 0x80000000).
* @param[in] Format Printf-style format string.
* @param[in] ... Variable arguments for the format string.
*
* @return Result from the DebugLib output function, or 0 on failure.
*/
UINTN
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VOID *Protocol;
UINTN Result;
UINT8 CmosValue;
UINT8 PlatformType;
UINTN ErrorMask;
Result = 0;
//
// Resolve the DebugLib/OpROM protocol interface.
//
Protocol = GetDebugLibProtocol ();
if (Protocol == NULL) {
return Result;
}
//
// Determine platform type from CMOS register 0x4B.
// Read the CMOS register using port I/O instructions.
//
CmosValue = __inbyte (RTC_INDEX_PORT);
__outbyte (RTC_INDEX_PORT, (CmosValue & ~DEBUG_CMOS_MASK) | CMOS_DEBUG_LEVEL_REGISTER);
PlatformType = __inbyte (RTC_DATA_PORT);
//
// Validate platform type. If > 3, use as-is.
// If == 0, fall back to MMIO register at 0xFDAF0490.
//
if (PlatformType > 3) {
// PlatformType is valid as-is
} else if (PlatformType == 0) {
PlatformType = (*(volatile UINT8 *)BOARD_CONFIG_MMIO_ADDR & 2) | 1;
} else {
// Platform type 1..3 = valid
}
//
// Check if the platform type matches the Lightning Ridge EXEC B4 type.
// (PlatformType - 1) is computed; if <= 0xFD, the type is valid.
//
if ((PlatformType - 1) <= 0xFD) {
//
// Build the error mask for this platform:
// - Type 1 (Lightning Ridge): ErrorMask = 0x80000004
// - Type 2 (other): ErrorMask = 0x80000006
//
if (PlatformType == OPROM_PLATFORM_TYPE_LIGHTNING_RIDGE) {
ErrorMask = 0x80000004;
} else {
ErrorMask = 0x80000006;
}
//
// If the requested ErrorLevel matches the platform's error mask,
// call the DebugLib protocol's output function (at offset 0x00).
//
if ((ErrorMask & ErrorLevel) != 0) {
//
// Invoke the debug output function.
// Parameters: Protocol, ErrorLevel, Format.
// Note: The original binary passes a va_list as the 4th argument by
// directly pointing to the stack location after Format. In the actual
// UEFI DebugLib implementation, this is the DebugVPrint function.
//
((UINTN (*)(VOID *, UINTN, CONST CHAR8 *))Protocol)(Protocol, ErrorLevel, Format);
}
}
return Result;
}
// ============================================================================
// Assertion Handler
// ============================================================================
/**
* ASSERT assertion failure handler.
*
* Called when a runtime assertion fails. This function is invoked by the
* ASSERT() macro when its condition evaluates to FALSE.
*
* The handler resolves the DebugLib protocol and calls its assertion
* handler function at offset 0x08 in the protocol interface.
*
* @param[in] FileName Source file name where the assertion failed.
* @param[in] LineNumber Line number of the assertion.
* @param[in] Description Description of the assertion that failed.
*
* @return 0 if the DebugLib protocol is not available, otherwise the
* return value from the protocol's assertion handler.
*/
UINTN
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
VOID *Protocol;
UINTN Result;
Result = 0;
//
// Resolve the DebugLib protocol interface.
//
Protocol = GetDebugLibProtocol ();
if (Protocol != NULL) {
//
// Call the assertion handler function at offset 0x08 in the
// protocol interface. The assertion handler is accessed via pointer
// arithmetic on the raw protocol interface.
//
{ UINTN (*AssertFn)(VOID *, CONST CHAR8 *, UINTN, CONST CHAR8 *);
AssertFn = *(UINTN (**)(VOID *, CONST CHAR8 *, UINTN, CONST CHAR8 *))((UINT8 *)Protocol + 8);
Result = AssertFn (Protocol, FileName, LineNumber, Description);
}
}
return Result;
}
// ============================================================================
// HOB List Management
// ============================================================================
/**
* Locates the HOB (Hand-Off Block) list from the UEFI System Table's
* configuration table array.
*
* Iterates through the configuration table array looking for a table whose
* VendorGuid matches EFI_HOB_LIST_GUID. The HOB list contains system
* initialization information passed from PEI to DXE, including PCI resource
* allocation data.
*
* The function performs the following:
* 1. Checks the cached gHobList first; if already resolved, returns it.
* 2. Iterates through SystemTable->ConfigurationTable[] entries.
* 3. For each entry, calls IsHobListGuid() to check if the VendorGuid matches.
* 4. When found, caches the table pointer in gHobList.
* 5. If not found and no configuration tables exist, triggers an assertion
* via DebugAssert() with the message "!EFI_ERROR (Status)".
* 6. If gHobList remains NULL after the search, triggers an assertion
* with the message "mHobList != ((void *) 0)".
*
* Results are cached globally in gHobList so subsequent calls are fast.
*
* @param[in] ImageHandle The driver image handle (not directly used).
*
* @return Pointer to the HOB list, or NULL if the HOB list GUID was not
* found in the system configuration table.
*/
VOID *
GetHobList (
IN EFI_HANDLE ImageHandle
)
{
UINTN Index;
UINTN TableCount;
EFI_CONFIGURATION_TABLE *ConfigTable;
//
// Return cached value if already resolved.
//
if (gHobList != NULL) {
return gHobList;
}
//
// Initialize and iterate through configuration tables.
//
gHobList = NULL;
TableCount = gST->NumberOfTableEntries;
ConfigTable = gST->ConfigurationTable;
if (TableCount > 0) {
//
// Walk the configuration table array.
// Each entry is 24 bytes (sizeof(EFI_CONFIGURATION_TABLE)).
//
for (Index = 0; Index < TableCount; Index++) {
//
// Check if this table's VendorGuid matches EFI_HOB_LIST_GUID.
//
if (IsHobListGuid (NULL, &ConfigTable[Index].VendorGuid)) {
gHobList = ConfigTable[Index].Table;
break;
}
}
}
//
// If no HOB list was found, trigger assertion failure.
//
if (gHobList == NULL) {
DebugPrint (DEBUG_INFO, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000EULL);
DebugAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
DebugAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return gHobList;
}
/**
* Compares a GUID against the EFI_HOB_LIST_GUID.
*
* Performs the comparison as two 64-bit unaligned reads:
* Compare first 8 bytes of the GUID against the expected first half
* (stored at GuidBlock + 0x20, i.e., the EFI_HOB_LIST_GUID in the block).
* Compare second 8 bytes of the GUID against the expected second half
* (stored at GuidBlock + 0x28).
*
* Both halves must match for the GUID to be considered equal.
*
* @param[in] ImageHandle Unused parameter (for compatibility with entry point
* signature; value is ignored).
* @param[in] GuidPtr Pointer to the 16-byte EFI_GUID to compare.
*
* @retval TRUE The GUID matches EFI_HOB_LIST_GUID.
* @retval FALSE The GUID does not match.
*/
BOOLEAN
IsHobListGuid (
IN EFI_HANDLE ImageHandle,
IN EFI_GUID *GuidPtr
)
{
UINT64 GuidFirstHalf;
UINT64 GuidSecondHalf;
UINT64 ExpectedFirstHalf;
UINT64 ExpectedSecondHalf;
//
// Read the expected GUID halves from the GUID storage block.
// The EFI_HOB_LIST_GUID is stored at GuidBlock + 0x20.
//
ExpectedFirstHalf = ReadUnaligned64 (&GuidBlock[0x20]);
ExpectedSecondHalf = ReadUnaligned64 (&GuidBlock[0x28]);
//
// Read the target GUID as two 64-bit halves.
//
GuidFirstHalf = ReadUnaligned64 (GuidPtr);
GuidSecondHalf = ReadUnaligned64 ((UINT8 *)GuidPtr + 8);
//
// Both halves must match.
//
return (GuidFirstHalf == ExpectedFirstHalf) && (GuidSecondHalf == ExpectedSecondHalf);
}
// ============================================================================
// Utility Functions
// ============================================================================
/**
* Reads an unaligned 64-bit value from memory.
*
* Performs a NULL pointer check. If Buffer is NULL, an assertion is
* triggered via DebugAssert() with the message "Buffer != ((void *) 0)".
*
* On valid input, reads 8 bytes from the given address and returns them
* as a 64-bit integer. The read is performed as a direct memory access
* (the platform supports unaligned access).
*
* @param[in] Buffer Pointer to the 8-byte value to read. Must not be NULL.
*
* @return The 64-bit value at the given memory address.
*/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
//
// Validate Buffer is not NULL.
//
if (Buffer == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
//
// Read 8 bytes from the buffer as a 64-bit value.
// Cast to volatile to prevent compiler optimizations from re-ordering
// or combining the access.
//
return *(CONST UINT64 *)Buffer;
}
// ============================================================================
// Module Entry Point
// ============================================================================
/**
* Module entry point for OpromUpdateDxeLightningRidgeEXECB4.
*
* DXE driver entry point called by the UEFI Boot Manager when this driver
* image is loaded and dispatched.
*
* Entry point flow:
* 1. Cache global variables: ImageHandle, SystemTable, BootServices,
* RuntimeServices (standard UefiBootServicesTableLib initialization).
* 2. Locate HOB list via GetHobList() to ensure PCI enumeration data
* is available.
* 3. Log debug banner: "UBA:SETUPConfigUpdate-TypeLightningRidgeEXECB4\n"
* via DebugPrint().
* 4. Locate the UBA Lightning Ridge EXEC B4 board-type protocol via
* gBS->LocateProtocol() using GUID E03E0D46-5263-4845-B0A4-58D57B3177E2.
* 5. Call the protocol's registration function (at offset 0x10 within
* the protocol interface) to register the OpROM update stub config.
* 6. The registration function receives:
* - The protocol interface
* - The registration GUID (CD1F9574-DD03-4196-96AD-4965146F9665)
* - The OpROM update stub protocol data block
* - Size: 24 bytes (0x18)
*
* @param[in] ImageHandle The firmware-allocated handle for this driver image.
* @param[in] SystemTable A pointer to the EFI System Table.
*
* @return EFI_SUCCESS Registration succeeded.
* @return EFI_INVALID_PARAMETER ImageHandle or SystemTable is NULL.
* @return EFI_NOT_FOUND The UBA board-type protocol could not be located.
* @return Other Registration via the board-type protocol failed.
*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *UbaProtocol;
UINT64 RegistrationResult;
//
// Cache global pointers.
//
gImageHandle = ImageHandle;
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
//
// Validate input parameters.
//
if (ImageHandle == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)"
);
}
if (SystemTable == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)"
);
}
if (SystemTable->BootServices == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)"
);
}
if (SystemTable->RuntimeServices == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)"
);
}
//
// Locate HOB list (ensures PCI enumeration data is available).
// Initializes gHobList for use by the UBA framework.
//
GetHobList (ImageHandle);
//
// Log debug banner indicating this is the OpROM Update driver for
// the Lightning Ridge EXEC B4 platform.
//
DebugPrint (DEBUG_INFO, "UBA:SETUPConfigUpdate-TypeLightningRidgeEXECB4\n");
//
// Locate the UBA Lightning Ridge EXEC B4 board-type protocol.
// The protocol GUID is stored at GuidBlock + 0x10.
//
UbaProtocol = NULL;
Status = (*gBS->LocateProtocol) ((EFI_GUID *)&GuidBlock[0x10], NULL, &UbaProtocol);
//
// If the UBA protocol was found, register the OpROM update configuration.
// The registration function is at offset 0x10 in the protocol interface.
//
if (!EFI_ERROR (Status)) {
Status = ((EFI_STATUS (*)(VOID *, EFI_GUID *, VOID *, UINT32))
((UBA_OPROM_UPDATE_BOARD_TYPE_PROTOCOL *)UbaProtocol)->RegisterOpromUpdateConfig) (
UbaProtocol,
(EFI_GUID *)&GuidBlock[0x30], // OPROM_UPDATE_PROTOCOL_REGISTRATION_GUID
(VOID *)OpromUpdateProtocolData,
OPROM_UPDATE_PROTOCOL_DATA_SIZE // 24 bytes
);
return Status;
}
return Status;
}