/**
* @file SetupConfigUpdateDxeNeonCityEPRP.c
* @brief UEFI DXE driver - SetupConfigUpdate for NeonCity EPRP platform
*
* This driver is a small UEFI DXE driver responsible for:
* 1. Initializing UEFI boot services and runtime services globals
* 2. Locating the HOB (Hand-off Block) list for firmware configuration
* 3. Locating the SetupConfig protocol and registering a notification callback
* 4. Providing debug output and assertion infrastructure
*
* The module is approximately 3.2 KB and contains 8 functions.
*/
#include "SetupConfigUpdateDxeNeonCityEPRP.h"
//
// ---------------------------------------------------------------------------
// Global data
// ---------------------------------------------------------------------------
EFI_GUID gDebugProtocolGuid = { 0x00000000, 0x0000, 0x0000,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
EFI_GUID gSetupConfigProtocolGuid = { 0x00000000, 0x0000, 0x0000,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
EFI_GUID gTargetHobGuid_lo = { 0x00000000, 0x0000, 0x0000,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
EFI_GUID gTargetHobGuid_hi = { 0x00000000, 0x0000, 0x0000,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
EFI_GUID gSetupConfigNotifyGuid = { 0x00000000, 0x0000, 0x0000,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
VOID *gDebugProtocolInterface = NULL;
VOID *gHobList = NULL;
VOID *gSetupConfigRegistration = NULL;
//
// CMOS debug-level storage (data segment at 0xBC8)
//
UINT8 gCmosDebugLevel;
//
// ---------------------------------------------------------------------------
// EfiGetLastErrorCode
// ---------------------------------------------------------------------------
/**
* Returns the EFI_ALREADY_STARTED status code.
*
* This function is used internally to provide a well-known error status
* value for assertion failure and debug reporting.
*
* @return EFI_ALREADY_STARTED (0x800000000000000E)
*/
EFI_STATUS
EFIAPI
EfiGetLastErrorCode (
VOID
)
{
return EFI_ALREADY_STARTED;
}
//
// ---------------------------------------------------------------------------
// GetDebugProtocol
// ---------------------------------------------------------------------------
/**
* Retrieves the debug protocol interface.
*
* Uses a lazy initialization pattern: checks the cached pointer first;
* if NULL, allocates memory and calls gBS->LocateProtocol to find the
* debug protocol. The result is cached globally.
*
* @return Pointer to the debug protocol interface, or NULL on failure.
*/
VOID *
GetDebugProtocol (
VOID
)
{
VOID *DebugProtocol;
UINT64 PoolSize;
EFI_STATUS Status;
DebugProtocol = gDebugProtocolInterface;
if (DebugProtocol == NULL) {
//
// Allocate pool header check - verify pool allocation size <= 0x10
// This is a standard UEFI memory allocation guard
//
PoolSize = (UINT64)gBS->AllocatePool (EfiBootServicesData, 31);
gBS->FreePool (PoolSize);
if (PoolSize <= 0x10) {
Status = gBS->LocateProtocol (
&gDebugProtocolGuid,
NULL,
&gDebugProtocolInterface
);
DebugProtocol = gDebugProtocolInterface;
if (EFI_ERROR (Status)) {
DebugProtocol = NULL;
gDebugProtocolInterface = NULL;
}
}
}
return DebugProtocol;
}
//
// ---------------------------------------------------------------------------
// DebugPrint
// ---------------------------------------------------------------------------
/**
* Debug print with CMOS-based debug level filtering.
*
* Reads the CMOS diagnostic register (offset 0x4B) to determine the current
* platform debug level. The debug level determines which error level masks
* are enabled. Messages are only printed if the ErrorLevel matches the
* current debug configuration:
*
* Debug Level 1: ErrorLevel 0x80000004 (EFI_D_ERROR | EFI_D_INIT)
* Debug Level 2+: ErrorLevel 0x80000006 (broader)
* Debug Level 0: Falls back to hardware strap (FDAF0490 bit 1 | 1)
*
* @param[in] ErrorLevel Debug error level mask.
* @param[in] Format Format string.
* @param[in] ... Variable arguments.
*/
VOID
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VOID *DebugProtocol;
UINTN EnabledMask;
UINT8 DiagValue;
UINT8 DebugLevel;
VA_LIST VaList;
VA_START (VaList, Format);
DebugProtocol = GetDebugProtocol ();
EnabledMask = 0;
if (DebugProtocol != NULL) {
//
// Read CMOS diagnostic register at index 0x4B via RTC ports 0x70/0x71
//
IoWrite8 (RTC_INDEX_PORT, IoRead8 (RTC_INDEX_PORT) & 0x80 | CMOS_DIAG_ADDR);
DebugLevel = IoRead8 (RTC_DATA_PORT);
//
// Validate debug level; handle special values
//
if (DebugLevel > 3) {
if (DebugLevel == 0) {
//
// Debug level 0: read hardware strap from memory-mapped GPIO
//
DebugLevel = (*(volatile UINT8 *)(UINTN)0xFDAF0490) & 2 | 1;
}
}
//
// Determine enabled error masks based on debug level
//
if ((DebugLevel - 1) <= 0xFD) {
//
// Debug level >= 1: standard masks apply
//
if (DebugLevel == 1) {
EnabledMask = 0x80000004; // EFI_D_ERROR | EFI_D_INIT
} else {
EnabledMask = 0x80000006; // EFI_D_ERROR | EFI_D_INIT | broader
}
}
//
// Call the protocol's DebugPrint function if error level matches
//
if ((EnabledMask & ErrorLevel) != 0) {
((DEBUG_PROTOCOL_PRINT)DebugProtocol) (ErrorLevel, Format, VaList);
}
}
}
//
// ---------------------------------------------------------------------------
// DebugAssert
// ---------------------------------------------------------------------------
/**
* Debug assertion handler.
*
* Retrieves the debug protocol and calls its assertion handler.
* The assertion handler typically halts or reboots the system.
*
* @param[in] FileName Source file name where the assertion occurred.
* @param[in] LineNumber Line number of the assertion.
* @param[in] Description Description of the assertion condition.
*/
VOID
EFIAPI
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
VOID *DebugProtocol;
DebugProtocol = GetDebugProtocol ();
if (DebugProtocol != NULL) {
((DEBUG_PROTOCOL_ASSERT)DebugProtocol) (FileName, LineNumber, Description);
}
}
//
// ---------------------------------------------------------------------------
// GetHobList
// ---------------------------------------------------------------------------
/**
* Retrieves the HOB (Hand-off Block) list pointer by GUID matching.
*
* Walks the system table's HOB list (from SystemTable->HobList at offset
* 0x68 = 104 decimal in x64). Each HOB entry is 24 bytes and contains
* a GUID at offset +0 which is compared against the target GUID.
*
* On failure, issues an ASSERT_EFI_ERROR and falls back to cached value.
*
* @return Pointer to the matching HOB, or NULL if the HOB list was empty
* or no matching HOB was found.
*/
VOID *
EFIAPI
GetHobList (
VOID
)
{
VOID *Hob;
UINT64 HobCount;
UINT64 Index;
UINT64 Offset;
Hob = (VOID *)gHobList;
if (gHobList == NULL) {
gHobList = NULL;
HobCount = *(UINT64 *)(SystemTable + 104);
if (HobCount != 0) {
Offset = 0;
for (Index = 0; Index < HobCount; Index++) {
if (GuidCompare (
(EFI_GUID *)(UINTN)(Offset + *(UINT64 *)(SystemTable + 112)),
(EFI_GUID *)(UINTN)(Offset + *(UINT64 *)(SystemTable + 112))
)) {
//
// Found matching HOB GUID
//
Hob = *(VOID **)(*(UINT64 *)(SystemTable + 112) + 24 * Index + 16);
gHobList = (UINT64)Hob;
return Hob;
}
Offset += 24;
}
//
// No match found: ASSERT
//
DebugPrint (DEBUG_ERROR, L"\nASSERT_EFI_ERROR (Status = %r)\n", EFI_ALREADY_STARTED);
DebugAssert (__FILE__, __LINE__, "!EFI_ERROR (Status)");
Hob = (VOID *)gHobList;
}
if (Hob == NULL) {
DebugAssert (__FILE__, __LINE__, "mHobList != ((void *) 0)");
}
}
return Hob;
}
//
// ---------------------------------------------------------------------------
// GuidCompare
// ---------------------------------------------------------------------------
/**
* Compares two 16-byte GUIDs by splitting into high/low QWORD pairs.
*
* Uses unaligned QWORD reads to compare both halves of the GUIDs.
* The target GUID is stored split across gTargetHobGuid_lo and
* gTargetHobGuid_hi.
*
* @param[in] Guid1 Pointer to first GUID (from HOB list).
* @param[in] Guid2 Pointer to second GUID (target).
*
* @return TRUE if both halves match, FALSE otherwise.
*/
BOOLEAN
EFIAPI
GuidCompare (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
UINT64 Lo1, Lo2;
UINT64 Hi1, Hi2;
Lo1 = ReadUnalignedQword (&gTargetHobGuid_lo);
Lo2 = ReadUnalignedQword (Guid2);
Hi1 = ReadUnalignedQword (&gTargetHobGuid_hi);
Hi2 = ReadUnalignedQword ((UINT8 *)Guid2 + 8);
return (Lo1 == Lo2) && (Hi1 == Hi2);
}
//
// ---------------------------------------------------------------------------
// ReadUnalignedQword
// ---------------------------------------------------------------------------
/**
* Reads an unaligned QWORD from memory.
*
* Performs a direct 8-byte read from the given address. Asserts if the
* buffer pointer is NULL.
*
* @param[in] Buffer Pointer to the data to read (must not be NULL).
*
* @return The QWORD value at Buffer.
*/
UINT64
EFIAPI
ReadUnalignedQword (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(volatile UINT64 *)Buffer;
}
//
// ---------------------------------------------------------------------------
// ModuleEntryPoint
// ---------------------------------------------------------------------------
/**
* DXE module entry point.
*
* Initialization sequence:
* 1. Save the ImageHandle and SystemTable to globals (via standard UEFI
* boot services library initialization with asserts).
* 2. Locate the HOB list via GetHobList().
* 3. Emit a debug message identifying this driver.
* 4. Locate the SetupConfig protocol via gBS->LocateProtocol.
* 5. Register a notification callback on the SetupConfig notify GUID.
*
* @param[in] ImageHandle The firmware allocated handle for the EFI image.
* @param[in] SystemTable A pointer to the EFI System Table.
*
* @return EFI_SUCCESS if all initialization steps completed.
*/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *SetupConfigProtocol;
UINT64 RegistrationHandle;
//
// Standard UEFI driver initialization: save ImageHandle and SystemTable
//
gImageHandle = (UINT64)ImageHandle;
ASSERT (ImageHandle != NULL);
gST = (UINT64)SystemTable;
ASSERT (SystemTable != NULL);
gBS = (UINT64)SystemTable->BootServices;
ASSERT (gBS != NULL);
gRT = (UINT64)SystemTable->RuntimeServices;
ASSERT (gRT != NULL);
//
// Initialize HOB list
//
GetHobList ();
//
// Register debug message for this driver
//
RegistrationHandle = 0;
DebugPrint (DEBUG_ERROR, "UBA:SETUPConfigUpdate-TypeNeonCityEPRP\n");
//
// Locate the SetupConfig protocol
//
Status = gBS->LocateProtocol (
&gSetupConfigProtocolGuid,
NULL,
&SetupConfigProtocol
);
if (!EFI_ERROR (Status)) {
//
// Register notification callback for SetupConfig updates
// The protocol at offset +16 (notify function) is called with:
// - The SetupConfig protocol interface
// - The notification GUID
// - The registration handle
// - A 24-byte context/configuration block
//
return ((EFI_STATUS (EFIAPI *)(VOID *, VOID *, VOID *, UINTN))(
SetupConfigProtocol + 16)) (
SetupConfigProtocol,
&gSetupConfigNotifyGuid,
&gSetupConfigRegistration,
24
);
}
return Status;
}