/**
* @file SetupConfigUpdateDxeLightningRidgeEXECB3.c
* @brief UEFI DXE driver for UBA setup configuration update on
* LightningRidgeEXECB3 platform.
*
* This module is structurally identical to the other SetupConfigUpdateDxe
* variants (NeonCityFPGA, LightningRidgeEXECB1/2/4) with platform-specific
* GUIDs substituted. The LightningRidgeEXECB3 variant uses:
* - Debug string: "UBA:SETUPConfigUpdate-TypeLightningRidgeEXECB3"
* - Board-type GUID: {E03E0D46-5263-4845-B0A4-58D57B3177E2}
* - Setup config protocol GUID: {CD1F9574-DD03-4196-96AD-4965146F9665}
*
* Build path:
* e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\
* TypeLightningRidgeEXECB3\SetupCfgUpdateDxe\SetupCfgUpdateDxe\DEBUG\
* SetupConfigUpdateDxeLightningRidgeEXECB3.pdb
*/
#include "SetupConfigUpdateDxeLightningRidgeEXECB3.h"
//=============================================================================
// Module Global Variables
//=============================================================================
/// Image handle for this driver.
/// Set by ModuleEntryPoint from the ImageHandle parameter.
/// Asserted non-NULL before use.
EFI_HANDLE gImageHandle = NULL; // Address: 0xBA8
/// System table pointer.
/// Set by ModuleEntryPoint from the SystemTable parameter.
/// Asserted non-NULL before use.
EFI_SYSTEM_TABLE *gSystemTable = NULL; // Address: 0xB98
/// Boot services pointer, extracted from gSystemTable->BootServices.
/// Asserted non-NULL before use.
EFI_BOOT_SERVICES *gBootServices = NULL; // Address: 0xBA0
/// Runtime services pointer, extracted from gSystemTable->RuntimeServices.
/// Asserted non-NULL before use (cached but not called directly by this module).
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL; // Address: 0xBB0
/// Cached DebugLib protocol interface pointer.
/// Initialized on first call to GetDebugProtocol().
/// Address: 0xBB8
UBA_DEBUG_PROTOCOL *gDebugProtocol = NULL;
/// Cached HOB list pointer.
/// Initialized on first call to GetHobList().
/// Address: 0xBC0
VOID *gHobList = NULL;
/// Cached CMOS debug level byte from register 0x4B.
/// Address: 0xBC8
UINT8 gCmosDebugLevel = 0;
//=============================================================================
// Data Section Contents
//=============================================================================
/**
* DebugLib protocol GUID at 0xB40.
* Used with gBS->LocateProtocol() to resolve the UEFI debug library.
* GUID: {36232936-0E76-31C8-A13A-3AF2FC1C3932}
*/
EFI_GUID gDebugProtocolGuid = DEBUG_LIB_PROTOCOL_GUID;
/**
* UBA board-type protocol GUID at 0xB50.
* Identifies the LightningRidgeEXECB3 platform-specific UBA protocol.
* GUID: {E03E0D46-5263-4845-B0A4-58D57B3177E2}
*/
EFI_GUID gUbaBoardTypeProtocolGuid = UBA_BOARD_TYPE_PROTOCOL_GUID;
/**
* Standard EFI HOB list GUID at 0xB60.
* Used to scan the configuration table for the HOB list pointer.
* Split into two 8-byte halves for fast comparison in IsGuidMatch().
* GUID: {7739F24C-93D7-11D4-9A3A-0090273FC14D}
*/
EFI_GUID gEfiHobListGuid = EFI_HOB_LIST_GUID;
/**
* UBA Setup Config protocol GUID at 0xB70.
* The protocol located via RegisterProtocolNotify has its
* RegisterSetupConfig callback at offset +0x10.
* GUID: {CD1F9574-DD03-4196-96AD-4965146F9665}
*/
EFI_GUID gUbaSetupConfigProtocolGuid = UBA_SETUP_CONFIG_PROTOCOL_GUID;
/**
* Setup Configuration Data descriptor at 0xB80.
* 24-byte structure registered with the UBA board-type protocol.
*/
UBA_SETUP_CONFIG_DATA gSetupConfigData = {
.Signature = SETUP_CONFIG_SIGNATURE, // "PSET"
.Version = SETUP_CONFIG_VERSION, // 1
.DataSize = SETUP_CONFIG_DATA_SIZE, // 0x48C
.DataSizeDuplicate = SETUP_CONFIG_DATA_SIZE // 0x48C
};
//=============================================================================
// Function Implementations
//=============================================================================
/**
* @brief Returns EFI_NOT_FOUND constant.
*
* Trivial leaf function that returns the EFI_NOT_FOUND status code.
* This function has no callers within this module and appears to be
* a dead code stub from library linkage.
*
* @return 0x800000000000000E (EFI_NOT_FOUND as unsigned 64-bit).
*/
UINT64
ReturnNotFound(
VOID
)
{
return EFI_NOT_FOUND;
}
/**
* @brief Reads a UINT64 from a pointer with NULL assertion.
*
* Simple aligned dereference of the input pointer as UINT64*.
* Asserts (calls DebugAssert) if the input pointer is NULL.
* Used by IsGuidMatch() to compare GUID halves.
*
* This function corresponds to a UEFI BaseLib ReadUnaligned64() inline
* with an added NULL check assertion from the debug build.
*
* @param[in] Buffer Pointer to read from. Must not be NULL.
*
* @return The UINT64 value at the pointer.
*/
UINT64
ReadUnaligned64(
IN VOID *Buffer
)
{
if (Buffer == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
return *(UINT64 *)Buffer;
}
/**
* @brief Compares two GUIDs using 64-bit unaligned reads.
*
* Splits each GUID into two 64-bit halves:
* - First half: Data1 (4 bytes) + Data2 (2 bytes) + Data3_High (2 bytes)
* - Second half: Data3_Low (2 bytes) + Data4 (6 bytes)
*
* Compares each half independently. This avoids a full CompareGuid()
* function call and is an optimization for inlined code size.
*
* @param[in] Guid1 Pointer to first EFI_GUID (from global data).
* @param[in] Guid2 Pointer to second EFI_GUID (from ConfigTable entry).
*
* @return TRUE if both halves match, FALSE otherwise.
*/
BOOLEAN
IsGuidMatch(
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
UINT64 FirstHalf1;
UINT64 FirstHalf2;
UINT64 SecondHalf1;
UINT64 SecondHalf2;
FirstHalf1 = ReadUnaligned64(Guid1);
FirstHalf2 = ReadUnaligned64(Guid2);
SecondHalf1 = ReadUnaligned64((UINT8 *)Guid1 + 8);
SecondHalf2 = ReadUnaligned64((UINT8 *)Guid2 + 8);
return (FirstHalf1 == FirstHalf2) && (SecondHalf1 == SecondHalf2);
}
/**
* @brief Resolves and caches the DebugLib protocol interface.
*
* This function lazily initializes the gDebugProtocol global pointer.
* It performs a UEFI environment validation check first:
* 1. Allocates POOL_ALLOC_SIZE_CHECK (31) bytes of EfiBootServicesData
* 2. Frees the allocation immediately
* 3. Checks if the returned pointer is <= MAX_VALID_ALLOC_PTR (0x10)
*
* If the allocation pointer is unexpectedly small or NULL, this indicates
* a broken or missing UEFI boot services environment, and the function
* returns NULL (no debug protocol).
*
* On passing the check, calls gBS->LocateProtocol() with the
* DEBUG_LIB_PROTOCOL_GUID and caches the result in gDebugProtocol.
* If LocateProtocol fails, gDebugProtocol is set to NULL.
*
* @return Pointer to the UBA_DEBUG_PROTOCOL interface, or NULL if not found.
*/
UBA_DEBUG_PROTOCOL *
GetDebugProtocol(
VOID
)
{
if (gDebugProtocol == NULL) {
VOID *PoolBuffer;
EFI_STATUS Status;
// Environment validation: allocate and free a small pool buffer
// to verify boot services are responding.
PoolBuffer = gBootServices->AllocatePool(EfiBootServicesData, POOL_ALLOC_SIZE_CHECK);
gBootServices->FreePool(PoolBuffer);
if ((UINTN)PoolBuffer <= MAX_VALID_ALLOC_PTR) {
// Boot services appear broken -- return NULL
return NULL;
}
// Locate the DebugLib protocol
Status = gBootServices->LocateProtocol(
&gDebugProtocolGuid,
NULL,
(VOID **)&gDebugProtocol
);
if (EFI_ERROR(Status)) {
gDebugProtocol = NULL;
}
}
return gDebugProtocol;
}
/**
* @brief Handles assertion failures via the DebugLib protocol.
*
* Calls the DebugAssert function at offset +0x08 of the UBA_DEBUG_PROTOCOL
* interface. If the debug protocol has not been resolved yet, GetDebugProtocol()
* is called to lazily initialize it.
*
* @param[in] FileName Source file name of the ASSERT.
* @param[in] LineNumber Source line number of the ASSERT.
* @param[in] Description ASSERT description string.
*/
VOID
DebugAssert(
IN CHAR8 *FileName,
IN UINTN LineNumber,
IN CHAR8 *Description
)
{
UBA_DEBUG_PROTOCOL *Protocol;
Protocol = GetDebugProtocol();
if (Protocol != NULL) {
Protocol->DebugAssert(Protocol, FileName, LineNumber, Description);
}
}
/**
* @brief Debug print with platform-aware error level filtering.
*
* Before printing, the function reads the platform's debug verbosity level
* from CMOS RTC register 0x4B:
* 1. Save current NMI state (bit 7 of CMOS address port)
* 2. Select CMOS register 0x4B
* 3. Read the debug level value
* 4. Restore NMI state
*
* The debug level value is interpreted as follows:
* - 0: Board is not in debug mode; fall back to the BOARD_CONFIG_REGISTER
* (MMIO 0xFDAF0490, bit 1). If bit 1 is set, use level 3; else level 1.
* - 1-3: Use the value as-is (1 = board type 1, 2 = board type 2, etc.)
* - >3: Use the value as-is
*
* The ErrorLevel is then filtered against a mask:
* - If board type is 1: ErrorLevel filter = 0x80000004 (error-only)
* - Otherwise: ErrorLevel filter = 0x80000006 (all debug)
*
* If the ErrorLevel matches the filter mask, the DebugPrint function is
* called. Otherwise, the call is silently dropped.
*
* @param[in] ErrorLevel Debug error level mask.
* @param[in] Format Format string.
* @param[in] ... Variable arguments.
*
* @return Non-zero if the message was printed, 0 if filtered or no protocol.
*/
UINT8
DebugPrint(
IN UINTN ErrorLevel,
IN CHAR8 *Format,
...
)
{
UBA_DEBUG_PROTOCOL *Protocol;
UINT64 LevelFilter;
UINT8 NmiSave;
UINT8 BoardType;
VA_LIST Args;
Protocol = GetDebugProtocol();
if (Protocol == NULL) {
return 0;
}
// Read CMOS register 0x4B for board type / debug level
NmiSave = __inbyte(CMOS_ADDRESS_PORT);
__outbyte(CMOS_ADDRESS_PORT, NmiSave & 0x80 | CMOS_REGISTER_DEBUG);
BoardType = __inbyte(CMOS_DATA_PORT);
// Interpret the board type value
if (BoardType > 3) {
// Value > 3: if zero, look up fallback
if (BoardType == 0) {
// Use MMIO board configuration register as fallback
BoardType = (*(volatile UINT32 *)BOARD_CONFIG_REGISTER & 2) | 1;
}
}
// Set the error level filter based on board type
LevelFilter = 0;
if ((BoardType - 1) <= 0xFD) {
LevelFilter = 0x80000006; // Error + Warning + Info
if (BoardType == 1) {
LevelFilter = 0x80000004; // Error only
}
}
// If the ErrorLevel matches the filter, call the protocol
if ((LevelFilter & ErrorLevel) != 0) {
VA_START(Args, Format);
Protocol->DebugPrint(Protocol, Format, Args);
VA_END(Args);
}
return (LevelFilter & ErrorLevel) != 0 ? 1 : 0;
}
/**
* @brief Locates and caches the HOB list pointer.
*
* Scans SystemTable->ConfigurationTable[] for the EFI_HOB_LIST_GUID entry.
* The configuration table is an array of CONFIG_TABLE_ENTRY structures
* (24 bytes each: 16-byte GUID + 8-byte pointer).
*
* Entry in the ConfigurationTable is found by comparing the GUID using
* the fast-path IsGuidMatch() function. If found, the pointer at offset
* +0x10 within the matching entry is cached in gHobList.
*
* If the HOB list is not found (empty configuration table or matching
* entry not present), an ASSERT is raised and gHobList remains NULL.
*
* @return Pointer to the HOB list, or NULL if not found.
*/
VOID *
GetHobList(
VOID
)
{
if (gHobList == NULL) {
UINTN Index;
UINTN TableCount;
CONFIG_TABLE_ENTRY *Table;
// Initialize
gHobList = NULL;
TableCount = gSystemTable->NumberOfTableEntries;
Table = gSystemTable->ConfigurationTable;
if (TableCount > 0) {
// Scan the configuration table for the HOB list GUID
for (Index = 0; Index < TableCount; Index++) {
if (IsGuidMatch(&gEfiHobListGuid, &Table[Index].VendorGuid)) {
gHobList = Table[Index].VendorTable;
break;
}
}
}
// Assert if HOB list was not found
if (gHobList == NULL) {
DebugPrint(DEBUG_ERROR_LEVEL_NOT_FOUND, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND);
DebugAssert(
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
}
// Assert if HOB list pointer is NULL after attempted resolution
if (gHobList == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
}
return gHobList;
}
/**
* @brief Module entry point. Called by the DXE dispatcher.
*
* The entry point performs all module work inline -- no registration of
* timer callbacks or protocol notifications. The flow is:
*
* 1. Initialize global UEFI pointers (with assertions):
* - gImageHandle = ImageHandle
* - gSystemTable = SystemTable
* - gBootServices = SystemTable->BootServices
* - gRuntimeServices = SystemTable->RuntimeServices
*
* 2. Call GetHobList() to locate the HOB list from the configuration table
* (asserts if not found)
*
* 3. Log platform identification via DebugPrint():
* "UBA:SETUPConfigUpdate-TypeLightningRidgeEXECB3"
*
* 4. Call gBS->LocateProtocol() with the UBA board-type GUID to resolve
* the UBA_BOARD_TYPE_PROTOCOL interface
*
* 5. Call RegisterSetupConfig() on the protocol interface at offset +0x10,
* passing the UBA Setup Config protocol GUID, the UBA_SETUP_CONFIG_DATA
* descriptor, and the data size (24 bytes)
*
* @param[in] ImageHandle Handle for this driver image.
* @param[in] SystemTable Pointer to the UEFI system table.
*
* @return EFI_STATUS from LocateProtocol if it fails, or from
* RegisterSetupConfig on success.
*/
EFI_STATUS
EFIAPI
ModuleEntryPoint(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UBA_BOARD_TYPE_PROTOCOL *BoardProtocol;
// Step 1: Initialize global pointers
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)"
);
}
gSystemTable = SystemTable;
if (SystemTable == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)"
);
}
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)"
);
}
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)"
);
}
// Step 2: Locate the HOB list
GetHobList();
// Step 3: Log platform identification string
DebugPrint(
DEBUG_ERROR_LEVEL_NOT_FOUND,
"UBA:SETUPConfigUpdate-TypeLightningRidgeEXECB3\n"
);
// Step 4: Locate the UBA board-type protocol
Status = gBootServices->LocateProtocol(
&gUbaBoardTypeProtocolGuid,
NULL, // No registration -- just locate existing protocol
(VOID **)&BoardProtocol
);
if (!EFI_ERROR(Status)) {
// Step 5: Register the setup configuration data
// The RegisterSetupConfig callback is at offset +0x10 in the protocol
Status = BoardProtocol->RegisterSetupConfig(
BoardProtocol,
&gUbaSetupConfigProtocolGuid,
&gSetupConfigData,
sizeof(gSetupConfigData) // 24 bytes
);
}
return Status;
}