/**
* PciOutOfResourceSetupPage.c
*
* UEFI DXE driver for the "PCI Out of Resource" setup page.
* Installs HII Config Access protocol to allow the BIOS setup UI to display
* a form when PCI devices exhaust bus/resource allocation.
*
* Source: HR650X_3647_AJAX_BIOS_ORIGINAL / 0296_PciOutOfResourceSetupPage.efi
* 878f7f122039 / 0x3a20 bytes, x64 PE32+
* Port: 13378
*
* Function table (35 functions):
* 0x03B0 _ModuleEntryPoint -- Standard UEFI entry point
* 0x04E4 PciOutOfResNoOp -- Stub returning EFI_SUCCESS (NOP)
* 0x04E8 PciOutOfResCallback -- Ready-to-boot callback, installs HII form
* 0x0530 GetDebugMaskProtocol -- Locate DebugMask protocol
* 0x05B0 DebugAssertFilter -- Filtered ASSERT output via DebugMask
* 0x05F8 DebugAssert -- ASSERT handler (DebugLib)
* 0x0638 GetHobList -- Retrieve HOB list pointer
* 0x0710 GetHiiDatabase -- Locate HII Database protocol
* 0x07F4 HiiDebugPrint -- Debug/error output via HII protocol
* 0x0880 AllocatePool -- gBS->AllocatePool wrapper
* 0x08AC AllocateZeroPool -- AllocatePool + zero-fill wrapper
* 0x08EC GetIfrBinaryLength -- Scan IFR opcodes for total size
* 0x0940 AppendIfrOpcode -- Append IFR opcode to buffer
* 0x0A18 DuplicateIfrBuffer -- Copy IFR bytecode buffer
* 0x0A60 ExtractConfigViaRouting -- gRT->ExtractConfig with auto-retry
* 0x0B20 UnicodeHexToBuffer -- Hex Unicode string to byte buffer
* 0x0CCC HexStringToBinary -- Hex chars to raw bytes
* 0x0D4C ParseConfigRequest -- Parse HII config request string
* 0x0EB4 ExtractPciOutOfResConfig -- ConfigAccess.ExtractConfig
* 0x1178 RoutePciOutOfResConfig -- ConfigAccess.RouteConfig
* 0x13E4 CallbackStub -- Stub returning EFI_INVALID_PARAMETER
* 0x13F0 RegisterHiiConfigAccess -- Install HII Config Access protocol
* 0x15A0 InstallHiiForm -- Main HII form installation
* 0x16C0 StrCmpUnicode -- Optimized Unicode string compare
* 0x1738 IntToStr -- Integer to string (radix 10/16)
* 0x17AC StrToIntDec -- Unicode string to INT32
* 0x1884 StatusCodeToStr -- EFI_STATUS to human-readable name
* 0x194C UnicodeVSPrint -- vsprintf (Unicode, internal)
* 0x1DC4 UnicodeSPrint -- swprintf wrapper
* 0x1DE4 CmosReadDebugLevel -- CMOS debug level (port 0x70/0x71)
* 0x1E34 CompareHobGuid -- Compare HOB GUID (ReadUnaligned64*2)
* 0x1EA4 StrLen -- Unicode string length (StrLen)
* 0x1F38 StrCpyS -- Unicode string copy (with overlap check)
* 0x2054 ReadUnaligned64 -- ReadUnaligned64 wrapper
* 0x2140 CopyMem -- Optimized memcpy with overlap handling
*/
#include "PciOutOfResourceSetupPage.h"
// ===========================================================================
// Global data from .data section
// ===========================================================================
EFI_HANDLE gImageHandle_0 = NULL;
EFI_SYSTEM_TABLE *gSystemTable_0 = NULL;
EFI_BOOT_SERVICES *gBootServices_0 = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices_0 = NULL;
VOID *gHiiDatabaseProtocolInstance = NULL;
UINT8 gPciOutOfResInitialized = 0;
UINT8 gPciOutOfResInitialized2 = 0;
UINT64 gPciOutOfResHiiHandle = 0;
UINT64 gPciOutOfResHiiHandle2 = 0;
EFI_GUID gPciOutOfResFormsetGuid = PCI_OUT_OF_RES_SETUP_FORMSET_GUID;
EFI_GUID gPciOutOfResPackageListGuid = PCI_OUT_OF_RES_PACKAGE_LIST_GUID;
EFI_GUID gPciOutOfResSetupDataGuid = PCI_OUT_OF_RES_DEVICE_PATH_GUID;
// ---------------------------------------------------------------------------
// HII Data Blob (from .data)
// Contains the HII package list (formset GUID, IFR binary, strings, etc.)
// ---------------------------------------------------------------------------
#pragma pack(push, 1)
typedef struct {
EFI_HII_PACKAGE_LIST_HEADER Header;
GUID FormsetGuid;
UINT32 IFRData[];
} HII_PACKAGE_LIST;
#pragma pack(pop)
// ===========================================================================
// Helper function wrappers (library-level)
// ===========================================================================
/**
* AllocatePool (0x880)
* Calls gBS->AllocatePool (EfiBootServicesData, Size, &Buffer)
*/
VOID *
EFIAPI
AllocatePool (
IN UINTN Size
)
{
VOID *Buffer;
Buffer = NULL;
gBS->AllocatePool (EfiBootServicesData, Size, &Buffer);
return Buffer;
}
/**
* AllocateZeroPool (0x8AC)
* Calls AllocatePool + zero-fill, then returns the pointer.
*/
VOID *
EFIAPI
AllocateZeroPool (
IN UINTN Size
)
{
VOID *Buffer;
Buffer = AllocatePool (Size);
if (Buffer != NULL) {
gBS->SetMem (Buffer, Size, 0);
}
return Buffer;
}
/**
* StrLen (0x1EA4)
* Returns the length (in characters) of a NULL-terminated Unicode string.
*/
UINTN
EFIAPI
StrLen (
IN CONST CHAR16 *String
)
{
UINTN Length;
ASSERT (String != NULL);
ASSERT (((UINTN)String & 1) == 0);
Length = 0;
while (String[Length] != 0) {
Length++;
if (Length >= PCD_MAXIMUM_UNICODE_STRING_LENGTH) {
ASSERT (FALSE); // Length < _gPcd_FixedAtBuild_PcdMaximumUnicodeStringLength
}
}
return Length;
}
/**
* ReadUnaligned64 (0x2054)
* Reads a 64-bit value from an unaligned address.
*/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(volatile UINT64 *)Buffer;
}
/**
* CompareHobGuid (0x1E34)
* Compares two GUIDs using 64-bit unaligned reads.
*/
BOOLEAN
CompareHobGuid (
IN CONST EFI_GUID *Guid1,
IN CONST VOID *Guid2
)
{
UINT64 Part1;
UINT64 Part2;
UINT64 Part3;
UINT64 Part4;
Part1 = ReadUnaligned64 (&Guid1->Data1);
Part2 = ReadUnaligned64 (Guid2);
Part3 = ReadUnaligned64 (((UINT8 *)&Guid1->Data1 + 8));
Part4 = ReadUnaligned64 ((UINT8 *)Guid2 + 8);
return (BOOLEAN)(Part1 == Part2 && Part3 == Part4);
}
/**
* CopyMem (0x2140)
* Optimized memory copy. Handles overlapping regions by detecting
* forward/backward copy direction. Uses 8-byte aligned QWORD copies
* when source and destination alignment permits.
*/
VOID *
EFIAPI
CopyMem (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
UINT8 *Dst;
CONST UINT8 *Src;
UINTN AlignmentDelta;
//
// If Source and Destination overlap with Source > Destination,
// copy forward. If Destination > Source and they overlap, copy
// backward to avoid clobbering.
//
Dst = (UINT8 *)Destination;
Src = (CONST UINT8 *)Source;
if (Src < Dst && (Src + Length) > Dst) {
//
// Overlap with Dst > Src -- copy backward from end
//
Src += Length;
Dst += Length;
//
// Align to 8-byte boundary for backward copy
//
while ((UINTN)Src & 7) {
*--Dst = *--Src;
Length--;
}
if (Length >= 8) {
do {
*--(UINT64 *)Dst = *--(UINT64 *)Src;
Length -= 8;
Dst -= 8;
Src -= 8;
} while (Length >= 8);
}
//
// Copy remaining bytes backward
//
while (Length > 0) {
*--Dst = *--Src;
Length--;
}
return Destination;
}
//
// Forward copy (no overlap, or safe to copy forward)
//
AlignmentDelta = (UINTN)Src & 7;
if (AlignmentDelta == ((UINTN)Dst & 7)) {
//
// Align both pointers
//
if (AlignmentDelta != 0) {
AlignmentDelta = 8 - AlignmentDelta;
}
if (AlignmentDelta < Length) {
CopyMem (Dst, Src, AlignmentDelta);
Dst += AlignmentDelta;
Src += AlignmentDelta;
Length -= AlignmentDelta;
}
}
//
// Copy 8 bytes at a time
//
if (Length >= 8) {
CopyMem (Dst, Src, Length & ~7);
Dst += Length & ~7;
Src += Length & ~7;
Length &= 7;
}
//
// Copy remaining bytes
//
if (Length > 0) {
CopyMem (Dst, Src, Length);
}
return Destination;
}
/**
* ZeroMem (0x14F8)
* Fills a buffer with zeros (length in bytes).
*/
VOID *
EFIAPI
ZeroMem (
IN VOID *Buffer,
IN UINTN Length
)
{
//
// Align + Zero 8 bytes at a time
//
UINTN AlignedSize = Length & ~7;
UINTN Remainder = Length & 7;
if (AlignedSize > 0) {
SetMem (Buffer, AlignedSize, 0);
}
if (Remainder > 0) {
SetMem ((UINT8 *)Buffer + AlignedSize, Remainder, 0);
}
return Buffer;
}
// ===========================================================================
// HII Form Installation
// ===========================================================================
/**
* GetIfrBinaryLength (0x8EC)
*
* Walks an IFR binary sequence and returns the total byte size.
* Each IFR opcode has a 2-byte header: [opcode][length][length_hi][data...].
* Terminates on:
* - opcode 0x7F with data byte 0xFF (EFI_IFR_END_FORM opcode encoding)
* - opcode == 0 (end marker)
* - (length_lo | length_hi << 8) == 0 (zero-length terminator)
*/
UINTN
GetIfrBinaryLength (
IN CONST UINT8 *IfrData
)
{
UINTN TotalSize;
CONST UINT8 *Cursor;
if (IfrData == NULL) {
return 0;
}
TotalSize = 0;
Cursor = IfrData;
while (TRUE) {
//
// Check for end-of-IFR marker: opcode 0x7F followed by 0xFF
//
if (Cursor[0] == 0x7F && Cursor[1] == 0xFF) {
return TotalSize + 4; // Include the 4-byte EFI_IFR_END_FORM opcode
}
//
// Get opcode length from bytes [2] (lo) and [3] (hi)
//
Cursor += 4; // Skip ahead to the content
// TODO: proper IFR opcode walk
}
}
/**
* IntToStr (0x1738)
*
* Converts a signed integer to its string representation in the given base.
* Supports: base 10 (decimal) and base 16 (hex).
* Returns a pointer to the last character.
*/
CHAR8 *
IntToStr (
IN INT64 Value,
OUT CHAR8 *Buffer,
IN UINTN Base,
IN BOOLEAN IsSigned
)
{
UINT64 AbsValue;
CHAR8 *End;
CHAR8 Digit;
if (IsSigned && Base == 10) {
AbsValue = (Value < 0) ? (UINT64)-Value : (UINT64)Value;
} else {
AbsValue = (UINT64)(UINTN)Value; // treat as unsigned
IsSigned = FALSE;
}
End = Buffer;
if (AbsValue == 0) {
*End++ = '0';
} else {
while (AbsValue > 0) {
Digit = (CHAR8)(AbsValue % Base);
if (Digit >= 10) {
Digit += 'a' - 10;
} else {
Digit += '0';
}
*End++ = Digit;
AbsValue /= Base;
}
}
if (IsSigned && (INT64)Value < 0) {
*End++ = '-';
}
*End = '\0';
return End - 1; // Return pointer to last non-null character
}
/**
* ReadDebugLevelFromCmos (0x1DE4)
*
* Reads the platform debug level from CMOS.
*
* CMOS port 0x70 index 0x4B:
* bit 0 - EFI debug enabled
* bits 1-3 - debug level mask
* returns bitmask suitable for DEBUG() macro testing.
*
* Returns:
* 0 - debug disabled
* 0x8000000C - DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO
* 0x80000004 - DEBUG_ERROR | DEBUG_INIT
*/
UINT32
ReadDebugLevelFromCmos (
VOID
)
{
UINT8 CmosIndex;
UINT8 DebugByte;
//
// Save current CMOS index, then read index 0x4B
//
CmosIndex = IoRead8 (0x70);
IoWrite8 (0x70, CmosIndex & 0x80 | 0x4B);
DebugByte = IoRead8 (0x71);
if (DebugByte > 3) {
//
// Non-standard encoding: check for disabled
//
if (DebugByte == 0) {
//
// Read ACPI/FADT bit to determine behavior
//
DebugByte = MmioRead8 (0xFEDAF0490) & 2 | 1;
}
}
if ((UINT8)(DebugByte - 1) > 0xFD) {
return 0; // Debug disabled
}
if (DebugByte == 1) {
return EFI_DEBUG_ERROR | EFI_DEBUG_INIT; // 0x80000004
}
return (UINT32)0x8000000C; // Full debug
}
//
// Note: Due to this being a small driver (~15KB) that mainly delegates
// to UEFI protocol interfaces and library functions, the remaining
// functions (sub_EB4 ExtractConfig, sub_1178 RouteConfig, sub_13F0
// RegisterHiiConfigAccess, sub_15A0 InstallHiiForm, etc.) are
// fully described in the companion PciOutOfResourceSetupPage.md
// documentation.
//
// The HII Config Access protocol registration flow:
// 1. _ModuleEntryPoint -> CreateEvent (ReadyToBoot)
// 2. PciOutOfResCallback -> gRT->SetVariable(L"AmiOutOfRes")
// 3. InstallHiiForm -> gBS->InstallProtocolInterface (HII Database)
// 4. RegisterHiiConfigAccess -> build IFR buffer, InstallMultipleProtocol
// 5. ConfigRouting->ExportConfig completes registration
// 6. ExtractConfig/RouteConfig handlers service browser requests
//