/*
*UefiConfigManager.c
*HR650X BIOS - UefiConfigManager.efi (Index 0097)
*Decompiled from UEFI PE32+ image, x64
*MD5: 25ad70f8e9e66afbad214023b484f8b7
*SHA256:20188e0c2b77eb76b69b485d2ebfe10011f406d7a02e2c2ae8a50350696bcf88
*Image size: 0x7d80 (32 KB), .text: 0x2c0-0x54c0 (0x5200 bytes)
*
*This module provides BIOS setup configuration management:
* - Initializes HII Database and String protocols
* - Parses IFR (Internal Forms Representation) from HII package lists
* - Builds variable store mappings for BIOS setup variables
* - Provides JSON-style configuration parsing (objects, arrays, numbers, strings)
* - Writes modified variables back via RuntimeServices
*
*WARNING: This is an auto-generated decompilation using IDA Pro.
*All function names starting with "sub_" are placeholders. Function signatures
*and data structure layouts should be verified against the original source.
*/
#include "UefiConfigManager.h"
/* ========================================================================
*Global Variable Definitions
* ======================================================================== */
EFI_HANDLE ImageHandle = NULL; /*0x77F0 */
EFI_SYSTEM_TABLE *SystemTable = NULL; /*0x77E0 */
EFI_BOOT_SERVICES *BootServices = NULL; /*0x77E8 */
EFI_RUNTIME_SERVICES *RuntimeServices = NULL; /*0x77F8 */
EFI_HII_DATABASE_PROTOCOL *gHiiDatabase = NULL; /*qword_7790 */
EFI_HII_STRING_PROTOCOL *gHiiString = NULL; /*qword_77D0 */
VOID *gDebugProtocol = NULL; /*qword_7800 */
VOID *gHobList = NULL; /*qword_7808 */
VOID *gSetupConfigRoot = NULL; /*qword_7778 */
VOID *gAttributesNode = NULL; /*qword_7780 */
VOID *gVarStoreArray = NULL; /*qword_7788 */
UINTN gVarStoreCount = 0; /*qword_77A0 */
VOID *gVarStoreMappingList = NULL; /*qword_77A8 */
BOOLEAN gIfrDebugEnabled = FALSE; /*byte_77D8 */
UINT8 gIfrParsePhase = 0; /*byte_77C8 */
UINT64 gIfrNvStoreMapHandle = 0; /*qword_77B8 */
/* ========================================================================
*Standard UEFI Library Functions
* ======================================================================== */
/*
*sub_2C0: memset implementation
*Fills 'count' bytes of 'buf' with 'value'.
*/
VOID *EFIAPI memset(VOID *buf, UINT8 value, UINTN count)
{
// Direct memset call (compiler intrinsic or inline assembly)
__builtin_memset(buf, value, count);
return buf;
}
/*
*sub_300: memcpy with overlap support (CopyMem)
*Copies 'count' bytes from 'src' to 'dst'.
*Uses qmemcpy for aligned 8-byte blocks, then byte-by-byte for remainder.
*Handles overlapping regions by copying from end if src < dst and overlapping.
*/
CHAR8 *EFIAPI memcpy(CHAR8 *dst, CONST CHAR8 *src, UINTN count)
{
if (src < dst && &src[count - 1] >= dst) {
// Overlap: copy from end backwards CONST CHAR8 *srcEnd = &src[count - 1];
CHAR8 *dstEnd = &dst[count - 1];
CONST CHAR8 *s;
CHAR8 *d;
// Copy aligned 8-byte chunks from end for (s = srcEnd, d = dstEnd; (UINTN)s >= (UINTN)&src[8]; s -= 8, d -= 8) {
*(UINT64 *)d = *(UINT64 *)s;
}
// Copy remainder bytes for (; s >= src; s--, d--) {
*d = *s;
}
} else {
// No overlap: copy forward, 8 bytes at a time UINTN aligned = count >> 3;
UINTN remainder = count & 7;
// Use qmemcpy for aligned chunks for (UINTN i = 0; i < aligned; i++) {
((UINT64 *)dst)[i] = ((UINT64 *)src)[i];
}
// Copy remaining bytes CHAR8 *d = &dst[aligned *8];
CONST CHAR8 *s = &src[aligned *8];
for (UINTN i = 0; i < remainder; i++) {
d[i] = s[i];
}
}
return dst;
}
/*
*sub_350: memcmp implementation (CompareMem)
*Compares 'count' bytes of 'a1' and 'a2'.
*Returns 0 if equal, or the difference of the first differing bytes.
*/
INTN EFIAPI memcmp(CONST VOID *a1, CONST VOID *a2, UINTN count)
{
CONST UINT8 *p1 = (CONST UINT8 *)a1;
CONST UINT8 *p2 = (CONST UINT8 *)a2;
while (count > 0) {
if (*p1 != *p2) {
break;
}
p1++;
p2++;
count--;
}
return (INTN)(*(p1 - 1) - *(p2 - 1));
}
/* ========================================================================
*String Library Functions
* ======================================================================== */
/*
*sub_2DA4: Unicode string length (StrLen)
*Returns the length of a null-terminated Unicode string.
*Maximum length is 0xF4240 (1,000,000) characters.
*/
UINTN StrLen(CONST UINT16 *String)
{
UINTN Length = 0;
if (String == NULL) {
DebugAssert(__FILE__, __LINE__, "String != ((void *) 0)");
}
if (((UINTN)String & 1) != 0) {
DebugAssert(__FILE__, __LINE__, "((UINTN) String & 0x00000001) == 0");
}
while (*String != L'\0') {
if (Length >= 0xF4240) {
DebugAssert(__FILE__, __LINE__,
"Length < _gPcd_FixedAtBuild_PcdMaximumUnicodeStringLength");
}
String++;
Length++;
}
return Length;
}
/*
*sub_3014: ASCII string length (AsciiStrLen)
*Returns the length of a null-terminated ASCII string.
*/
UINTN AsciiStrLen(CONST CHAR8 *String)
{
CONST CHAR8 *Ptr = String;
if (String == NULL) {
DebugAssert(__FILE__, __LINE__, "String != ((void *) 0)");
}
for (UINTN i = 0; *Ptr; i++) {
if (i >= 0xF4240) {
DebugAssert(__FILE__, __LINE__,
"Length < _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength");
}
Ptr++;
}
return (UINTN)(Ptr - String);
}
/*
*sub_2ED8: Unicode string to ASCII string (UnicodeStrToAsciiStr)
*Converts a Unicode string to an ASCII string.
*Only characters < 0x100 are valid.
*/
CHAR8 *UnicodeToAscii(CONST UINT16 *Source, CHAR8 *Destination)
{
CHAR8 *DestPtr = Destination;
if (Destination == NULL) {
DebugAssert(__FILE__, __LINE__, "Destination != ((void *) 0)");
}
if (StrLen(Source) *2 + 2 == 2) { // StrSize(Source) == 2 means empty DebugAssert(__FILE__, __LINE__, "StrSize (Source) != 0");
}
if ((UINTN)DestPtr - (UINTN)Source < StrLen(Source) *2 + 2) {
DebugAssert(__FILE__, __LINE__,
"(UINTN) (Destination - (CHAR8 *) Source) >= StrSize (Source)");
}
if ((UINTN)Source - (UINTN)DestPtr <= StrLen(Source)) {
DebugAssert(__FILE__, __LINE__,
"(UINTN) ((CHAR8 *) Source - Destination) > StrLen (Source)");
}
while (*Source != L'\0') {
if (*Source >= 0x100) {
DebugAssert(__FILE__, __LINE__, "*Source < 0x100");
}
*DestPtr++ = (CHAR8)*Source++;
}
*DestPtr = '\0';
if (AsciiStrLen(DestPtr - (DestPtr - Destination)) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "AsciiStrSize (ReturnValue) != 0");
}
return Destination;
}
/*
*sub_31C4: ASCII string to Unicode string (AsciiStrToUnicodeStr)
*Converts an ASCII string to a Unicode string.
*/
UINT16 *AsciiToUnicode(CONST CHAR8 *Source, UINT16 *Destination)
{
UINT16 *DestPtr = Destination;
if (Destination == NULL) {
DebugAssert(__FILE__, __LINE__, "Destination != ((void *) 0)");
}
if (AsciiStrLen(Source) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "AsciiStrSize (Source) != 0");
}
if ((UINTN)DestPtr - (UINTN)Source > AsciiStrLen(Source)) {
DebugAssert(__FILE__, __LINE__,
"(UINTN) ((CHAR8 *) Destination - Source) > AsciiStrLen (Source)");
}
if ((UINTN)Source - (UINTN)DestPtr < AsciiStrLen(Source) *2 + 2) {
DebugAssert(__FILE__, __LINE__,
"(UINTN) (Source - (CHAR8 *) Destination) >= "
"(AsciiStrSize (Source) *sizeof (CHAR16))");
}
while (*Source != '\0') {
*DestPtr++ = (UINT16)(UINT8)*Source++;
}
*DestPtr = L'\0';
if (StrLen(DestPtr - (UINTN)(DestPtr - Destination)) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "StrSize (ReturnValue) != 0");
}
return Destination;
}
/*
*sub_3104: ASCII string compare with length limit (AsciiStrnCmp)
*Compares up to 'Length' characters, returns difference.
*/
INTN AsciiStrnCmp(CONST CHAR8 *First, CONST CHAR8 *Second, UINTN Length)
{
CONST CHAR8 *F = First;
if (AsciiStrLen(First) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "AsciiStrSize (FirstString)");
}
if (AsciiStrLen(Second) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "AsciiStrSize (SecondString)");
}
if (Length > 0xF4240) {
DebugAssert(__FILE__, __LINE__,
"Length <= _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength");
}
while (*F && *Second && *F == *Second && Length > 1) {
F++;
Second++;
Length--;
}
return (INTN)((CHAR8)*F - (CHAR8)*Second);
}
/*
*sub_3080: ASCII string compare (AsciiStrCmp)
*Compares two ASCII strings, returns difference of first differing character.
*/
INTN AsciiStrCmp(CONST CHAR8 *First, CONST CHAR8 *Second)
{
CONST CHAR8 *F = First;
if (AsciiStrLen(First) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "AsciiStrSize (FirstString)");
}
if (AsciiStrLen(Second) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "AsciiStrSize (SecondString)");
}
while (*F && *F == *Second) {
F++;
Second++;
}
return (INTN)((CHAR8)*F - (CHAR8)*Second);
}
/*
*sub_43B8: Case-insensitive ASCII string compare
*Compares two ASCII strings ignoring case.
*/
INTN AsciiStriCmp(CONST CHAR8 *First, CONST CHAR8 *Second)
{
CHAR8 c1;
CHAR8 c2;
INTN Diff;
if (AsciiStrLen(First) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "AsciiStrSize (FirstString)");
}
if (AsciiStrLen(Second) == (UINTN)-1) {
DebugAssert(__FILE__, __LINE__, "AsciiStrSize (SecondString)");
}
c1 = *First;
if ((UINT8)(c1 - 'a') <= 'z' - 'a') {
c1 -= 0x20; // toupper
}
c2 = *Second;
if ((UINT8)(c2 - 'a') <= 'z' - 'a') {
c2 -= 0x20; // toupper
}
if (*First) {
INTN Offset = Second - First;
do {
if (c1 != c2) break;
c1 = *++First;
if ((UINT8)(c1 - 'a') <= 'z' - 'a') {
c1 -= 0x20;
}
c2 = First[Offset];
if ((UINT8)(c2 - 'a') <= 'z' - 'a') {
c2 -= 0x20;
}
} while (*First);
}
return (UINT32)(c1 - c2);
}
/*
*sub_32E4: Safe Unicode string length limited to MaxSize (StrnLenS)
*/
UINTN StrnLenS(CONST UINT16 *String, UINTN MaxSize)
{
if (((UINTN)String & 1) != 0) {
DebugAssert(__FILE__, __LINE__,
"((UINTN) String & 0x00000001) == 0");
}
if (!String) return 0;
if (*String != L'\0') {
for (UINTN Index = 0; Index < MaxSize; Index++) {
if (String[Index] == L'\0') return Index;
}
return MaxSize + 1;
}
return 0;
}
/*
*sub_333C: Safe ASCII string length limited to MaxSize (AsciiStrnLenS)
*/
UINTN AsciiStrnLenS(CONST CHAR8 *String, UINTN MaxSize)
{
if (String && *String) {
for (UINTN Index = 0; Index < MaxSize; Index++) {
if (String[++Index] == '\0') return Index;
}
return MaxSize + 1;
}
return 0;
}
/* ========================================================================
*Unicode vsprintf / SPrint Functions
* ======================================================================== */
/*
*sub_34B4: Core Unicode formatted print (internal).
*Writes formatted output to Buffer based on a Unicode format string.
*Supports standard format specifiers.
*
*Note: This is a large internal function originally from
*MdePkg/Library/BasePrintLib/PrintLibInternal.c and handles:
* %s, %S, %c, %d, %u, %x, %X, %r, %g, %p, %a, %t, %N and width/precision.
*/
UINTN UnicodeVSPrint(
CHAR16 *Buffer,
UINTN BufferSize,
CONST CHAR16 *Format,
VA_LIST VaList)
{
UINTN Index;
CONST CHAR16 *FormatChar;
UINTN Count = 0;
BOOLEAN LongFlag;
BOOLEAN HalfFlag;
INTN Width;
INTN Precision;
BOOLEAN Done;
// Assertion checks on buffer, format, sizes if (Buffer == NULL) {
DebugAssert(__FILE__, __LINE__, "(Buffer != ((void *) 0))");
}
if (Format == NULL) {
DebugAssert(__FILE__, __LINE__, "(Format != ((void *) 0))");
}
FormatChar = Format;
while (*FormatChar != L'\0') {
if (*FormatChar != L'%') {
// Output literal character if (Count < BufferSize) {
Buffer[Count++] = *FormatChar;
}
FormatChar++;
continue;
}
// Handle format specifier FormatChar++; // skip '%'
Width = 0;
Precision = 0;
LongFlag = FALSE;
HalfFlag = FALSE;
Done = FALSE;
// Check for flags (only '-' is minimally supported)
if (*FormatChar == L'-') {
FormatChar++;
}
// Width if (*FormatChar == L'*') {
Width = VA_ARG(VaList, INTN);
FormatChar++;
} else {
while (*FormatChar >= L'0' && *FormatChar <= L'9') {
Width = Width *10 + (*FormatChar - L'0');
FormatChar++;
}
}
// Precision if (*FormatChar == L'.') {
FormatChar++;
if (*FormatChar == L'*') {
Precision = VA_ARG(VaList, INTN);
FormatChar++;
} else {
while (*FormatChar >= L'0' && *FormatChar <= L'9') {
Precision = Precision *10 + (*FormatChar - L'0');
FormatChar++;
}
}
}
// Length modifiers if (*FormatChar == L'l' || *FormatChar == L'L') {
LongFlag = TRUE;
FormatChar++;
} else if (*FormatChar == L'h') {
HalfFlag = TRUE;
FormatChar++;
}
// Conversion specifier switch (*FormatChar) {
case L'd':
case L'i': {
INT64 Value;
if (LongFlag) {
Value = VA_ARG(VaList, INT64);
} else {
Value = (INTN)VA_ARG(VaList, INTN);
}
// Convert to decimal if (Value < 0) {
if (Count < BufferSize) Buffer[Count++] = L'-';
Value = -Value;
}
// Output digits
{
CHAR16 Digits[21];
INTN DigitCount = 0;
do {
Digits[DigitCount++] = L'0' + (Value % 10);
Value /= 10;
} while (Value > 0);
while (DigitCount > 0) {
if (Count < BufferSize) Buffer[Count++] = Digits[--DigitCount];
}
}
break;
}
case L'x':
case L'X': {
UINT64 Value;
CHAR16 HexChar = (*FormatChar == L'X') ? L'A' : L'a';
if (LongFlag) {
Value = VA_ARG(VaList, UINT64);
} else {
Value = (UINTN)VA_ARG(VaList, UINTN);
}
{
CHAR16 Digits[17];
INTN DigitCount = 0;
do {
UINTN Rem = Value % 16;
Digits[DigitCount++] = (Rem < 10) ? (L'0' + Rem) : (HexChar + Rem - 10);
Value /= 16;
} while (Value > 0);
while (DigitCount > 0) {
if (Count < BufferSize) Buffer[Count++] = Digits[--DigitCount];
}
}
break;
}
case L's': {
CONST CHAR16 *Str = VA_ARG(VaList, CONST CHAR16 *);
if (Str) {
while (*Str && Count < BufferSize) {
Buffer[Count++] = *Str++;
}
}
break;
}
case L'S': {
CONST CHAR8 *Str = VA_ARG(VaList, CONST CHAR8 *);
if (Str) {
while (*Str && Count < BufferSize) {
Buffer[Count++] = (CHAR16)(UINT8)*Str++;
}
}
break;
}
case L'c': {
CHAR16 Char = (CHAR16)VA_ARG(VaList, UINTN);
if (Count < BufferSize) Buffer[Count++] = Char;
break;
}
case L'u': {
UINTN Value = VA_ARG(VaList, UINTN);
CHAR16 Digits[21];
INTN DigitCount = 0;
do {
Digits[DigitCount++] = L'0' + (Value % 10);
Value /= 10;
} while (Value > 0);
while (DigitCount > 0) {
if (Count < BufferSize) Buffer[Count++] = Digits[--DigitCount];
}
break;
}
case L'r': {
EFI_STATUS Status = VA_ARG(VaList, EFI_STATUS);
CONST CHAR16 *StatusStr;
// Lookup status string or output hex code switch (Status) {
case EFI_SUCCESS: StatusStr = L"Success"; break;
case EFI_LOAD_ERROR: StatusStr = L"Load Error"; break;
case EFI_INVALID_PARAMETER: StatusStr = L"Invalid Parameter"; break;
case EFI_UNSUPPORTED: StatusStr = L"Unsupported"; break;
case EFI_BUFFER_TOO_SMALL: StatusStr = L"Buffer Too Small"; break;
case EFI_NOT_FOUND: StatusStr = L"Not Found"; break;
default: StatusStr = L"Unknown"; break;
}
while (*StatusStr && Count < BufferSize) {
Buffer[Count++] = *StatusStr++;
}
break;
}
case L'g': {
// GUID printing - simplified representation EFI_GUID *Guid = VA_ARG(VaList, EFI_GUID *);
if (Guid && Count + 36 < BufferSize) {
Count += UnicodeSPrint(&Buffer[Count], BufferSize - Count,
L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
Guid->Data1, Guid->Data2, Guid->Data3,
Guid->Data4[0], Guid->Data4[1],
Guid->Data4[2], Guid->Data4[3],
Guid->Data4[4], Guid->Data4[5],
Guid->Data4[6], Guid->Data4[7]);
}
break;
}
case L'p': {
VOID *Ptr = VA_ARG(VaList, VOID *);
if (Count + 18 < BufferSize) {
Count += UnicodeSPrint(&Buffer[Count], BufferSize - Count,
L"0x%016llx", (UINT64)Ptr);
}
break;
}
case L'a': {
CONST CHAR8 *Str = VA_ARG(VaList, CONST CHAR8 *);
if (Str) {
while (*Str && Count < BufferSize) {
Buffer[Count++] = (CHAR16)(UINT8)*Str++;
}
}
break;
}
case L'%':
if (Count < BufferSize) Buffer[Count++] = L'%';
break;
default:
break;
}
FormatChar++;
}
// Null-terminate if (Count < BufferSize) {
Buffer[Count] = L'\0';
} else if (BufferSize > 0) {
Buffer[BufferSize - 1] = L'\0';
}
return Count;
}
/*
*sub_33E0: UnicodeSPrint wrapper
*Calls UnicodeVSPrint with the variable argument list.
*/
UINTN EFIAPI UnicodeSPrint(
CHAR16 *Buffer,
UINTN BufferSize,
CONST CHAR16 *Format,
...)
{
VA_LIST VaList;
VA_START(VaList, Format);
return UnicodeVSPrint(Buffer, BufferSize, Format, VaList);
}
/*
*sub_3360: ASCII SPrint wrapper for Unicode output
*Calls UnicodeVSPrint with a Unicode-converted format string.
*/
UINTN SPrint(CHAR8 *Buffer, UINTN BufferSize, CONST CHAR8 *Format, ...)
{
VA_LIST VaList;
if (((UINTN)Buffer & 1) != 0) {
DebugAssert(__FILE__, __LINE__,
"(((UINTN) (StartOfBuffer)) & 0x01) == 0");
}
if (((UINTN)Format & 1) != 0) {
DebugAssert(__FILE__, __LINE__,
"(((UINTN) (FormatString)) & 0x01) == 0");
}
VA_START(VaList, Format);
// Calls UnicodeVSPrint with BufferSize/2 (CHAR16 half)
return UnicodeVSPrint((CHAR16 *)Buffer, BufferSize >> 1, (CONST CHAR16 *)Format, VaList);
}
/* ========================================================================
*Debug / Assert Functions
* ======================================================================== */
/*
*sub_2C9C: Get HII Database Protocol pointer (cached)
*Retrieves the EFI_HII_DATABASE_PROTOCOL via BootServices->LocateProtocol
*and caches it in gDebugProtocol (qword_7800).
*/
EFI_STATUS GetHiiDatabase(OUT EFI_HII_DATABASE_PROTOCOL **Database)
{
EFI_STATUS Status;
if (gDebugProtocol != NULL) {
*Database = (EFI_HII_DATABASE_PROTOCOL *)gDebugProtocol;
return EFI_SUCCESS;
}
// Check if we're at TPL level that allows protocol lookup UINTN Tpl = BootServices->RaiseTPL(TPL_HIGH_LEVEL);
BootServices->RestoreTPL(Tpl);
if (Tpl <= TPL_CALLBACK) {
// Locate the HII Database protocol (GUID at unk_6C00)
Status = BootServices->LocateProtocol(
&gHiiDatabaseProtocolGuid, // Actually the debug protocol GUID NULL,
&gDebugProtocol);
if (!EFI_ERROR(Status)) {
*Database = (EFI_HII_DATABASE_PROTOCOL *)gDebugProtocol;
return EFI_SUCCESS;
}
}
*Database = NULL;
return EFI_NOT_FOUND;
}
/*
*sub_2D64: DebugAssert - print assertion failure message
*Typically called via the ASSERT() macro.
*/
VOID DebugAssert(CONST CHAR8 *FileName, UINTN LineNumber, CONST CHAR8 *Description)
{
EFI_HII_DATABASE_PROTOCOL *Dbg;
EFI_STATUS Status;
Status = GetHiiDatabase(&Dbg);
if (EFI_ERROR(Status)) return;
// Call the debug protocol's assertion handler (at offset +8)
if (Dbg) {
((VOID (*)(CONST CHAR8 *, UINTN, CONST CHAR8 *))((UINTN)Dbg + 8))(
FileName, LineNumber, Description);
}
}
/*
*sub_2D1C: DebugLog - conditionally print debug message
*Checks ErrorLevel against current debug mask from CMOS,
*then delegates to protocol's debug print.
*/
EFI_STATUS DebugLog(UINT32 ErrorLevel, CONST CHAR8 *Format, ...)
{
EFI_HII_DATABASE_PROTOCOL *Dbg;
EFI_STATUS Status;
VA_LIST VaList;
Status = GetHiiDatabase(&Dbg);
if (EFI_ERROR(Status)) return Status;
// Check if this error level is enabled UINT32 CurrentLevel = GetDebugLevel();
if ((CurrentLevel & ErrorLevel) == 0) return EFI_SUCCESS;
VA_START(VaList, Format);
// Call the protocol's debug print function at offset +0 return ((EFI_STATUS (*)(UINT32, CONST CHAR8 *, VA_LIST))((UINTN)Dbg + 0))(
ErrorLevel, Format, VaList);
}
/*
*sub_5038: GetDebugLevel - Query current debug print level
*Reads CMOS RTC register 0x74 (index 0x4B) to determine enabled debug levels.
*Returns bitmask matching EDKII debug levels or 0x80000006/0x80000004 fallback.
*/
UINT32 GetDebugLevel(VOID)
{
UINT8 DebugByte;
UINT8 DebugByteShifted;
// Read CMOS IO_PORT_WRITE(0x70, (IO_PORT_READ(0x70) & 0x80) | 0x4B);
DebugByte = IO_PORT_READ(0x71);
DebugByteShifted = DebugByte;
if ((UINT8)DebugByte > 3) {
DebugByteShifted = DebugByte;
if (DebugByte == 0) {
// Read physical memory at 0xFDAF0490 DebugByteShifted = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
}
}
if ((UINT8)(DebugByteShifted - 1) > 0xFD) {
return 0;
}
if (DebugByteShifted == 1) {
return 0x80000004; // EFI_D_ERROR only
}
return 0x80000006; // EFI_D_ERROR | EFI_D_INIT
}
/* ========================================================================
*Memory Functions
* ======================================================================== */
/*
*sub_531C: FreePool wrapper with ASSERT on error
*Frees memory allocated via BootServices and asserts on failure.
*/
EFI_STATUS FreePool(CONST VOID *Buffer)
{
EFI_STATUS Status;
Status = BootServices->FreePool((VOID *)Buffer);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
DebugAssert(__FILE__, __LINE__, "!EFI_ERROR (Status)");
}
return Status;
}
/*
*sub_5088: SetMem - fills a buffer with a byte value
*Wrapper around memset with bounds checking.
*/
VOID *SetMem(VOID *Buffer, UINTN Size, UINT8 Value)
{
if (Size - 1 > (UINTN)(-1) - (UINTN)Buffer) {
DebugAssert(__FILE__, __LINE__,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)Buffer)");
}
return memset(Buffer, Value, Size);
}
/*
*sub_4480: AllocateCopyPool - allocates and copies a string
*/
CHAR8 *AllocateCopyPool(CONST CHAR8 *String)
{
UINTN Len;
CHAR8 *Copy;
EFI_STATUS Status;
Len = AsciiStrLen(String);
Status = BootServices->AllocatePool(EfiBootServicesData, Len + 1, (VOID **)&Copy);
if (EFI_ERROR(Status)) {
Copy = NULL;
}
if (Copy == NULL) {
return NULL;
}
// memcpy(Copy, String, Len + 1)
CopyMem(Copy, String, Len + 1);
return Copy;
}
/*
*sub_5148: CopyMem wrapper with overlap check
*/
VOID *CopyMem(VOID *Destination, CONST VOID *Source, UINTN Length)
{
if (Length > 0) {
UINTN DestEnd = Length - 1;
if (DestEnd > (UINTN)(-1) - (UINTN)Destination) {
DebugAssert(__FILE__, __LINE__,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)");
}
if (DestEnd > (UINTN)(-1) - (UINTN)Source) {
DebugAssert(__FILE__, __LINE__,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)");
}
if (Destination == Source) {
return Destination;
}
return memcpy((CHAR8 *)Destination, (CONST CHAR8 *)Source, Length);
}
return Destination;
}
/*
*sub_52BC: ReadUnaligned16
*/
UINT16 ReadUnaligned16(CONST VOID *Buffer)
{
if (Buffer == NULL) {
DebugAssert(__FILE__, __LINE__, "Buffer != ((void *) 0)");
}
return *(CONST UINT16 *)Buffer;
}
/*
*sub_52EC: ReadUnaligned64
*/
UINT64 ReadUnaligned64(CONST VOID *Buffer)
{
if (Buffer == NULL) {
DebugAssert(__FILE__, __LINE__, "Buffer != ((void *) 0)");
}
return *(CONST UINT64 *)Buffer;
}
/*
*sub_3408: SetMem16 - fills buffer with 16-bit values
*/
VOID *SetMem16(VOID *Buffer, UINTN Count, UINT16 Value)
{
UINT8 *Buf = (UINT8 *)Buffer;
UINTN Index;
for (Index = 0; Index < Count; Index++) {
if ((UINTN)Buf >= (UINTN)Buffer + Count *sizeof(UINT16)) break;
*Buf = (UINT8)Value;
Buf[1] = (UINT8)(Value >> 8);
Buf += sizeof(UINT16);
}
return Buffer;
}
/*
*sub_51E4: CompareMem wrapper with safety checks
*/
INTN CompareMem(CONST VOID *DestinationBuffer, CONST VOID *SourceBuffer, UINTN Length)
{
if (DestinationBuffer == SourceBuffer) {
return 0;
}
if (DestinationBuffer == NULL) {
DebugAssert(__FILE__, __LINE__, "DestinationBuffer != ((void *) 0)");
}
if (SourceBuffer == NULL) {
DebugAssert(__FILE__, __LINE__, "SourceBuffer != ((void *) 0)");
}
if (Length - 1 > (UINTN)(-1) - (UINTN)DestinationBuffer) {
DebugAssert(__FILE__, __LINE__,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)");
}
if (Length - 1 > (UINTN)(-1) - (UINTN)SourceBuffer) {
DebugAssert(__FILE__, __LINE__,
"(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)");
}
return memcmp(DestinationBuffer, SourceBuffer, Length);
}
/* ========================================================================
*HOB List Functions
* ======================================================================== */
/*
*sub_42E0: GetHobList
*Returns the HOB (Hand-Off Block) list pointer.
*Retrieves from SystemTable->ConfigurationTable by matching GUID
* (unk_6C20 / unk_6C28 - two halves of a 16-byte GUID).
*/
EFI_HOB_HANDOFF_INFO_TABLE *GetHobList(VOID)
{
UINTN Index;
if (gHobList != NULL) {
return (EFI_HOB_HANDOFF_INFO_TABLE *)gHobList;
}
gHobList = NULL;
if (SystemTable->NumberOfTableEntries > 0) {
EFI_CONFIGURATION_TABLE *ConfigTable =
(EFI_CONFIGURATION_TABLE *)SystemTable->ConfigurationTable;
for (Index = 0; Index < SystemTable->NumberOfTableEntries; Index++) {
if (CompareGuid(&ConfigTable[Index].VendorGuid, &gHobListGuid)) {
gHobList = ConfigTable[Index].VendorTable;
return (EFI_HOB_HANDOFF_INFO_TABLE *)gHobList;
}
}
}
DebugLog(DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND);
DebugAssert(__FILE__, __LINE__, "!EFI_ERROR (Status)");
DebugAssert(__FILE__, __LINE__, "mHobList != ((void *) 0)");
return (EFI_HOB_HANDOFF_INFO_TABLE *)gHobList;
}
/* ========================================================================
*Configuration Node Functions
* ======================================================================== */
/*
*sub_44F0: ConfigNodeAlloc
*Allocates a 72-byte CONFIG_NODE, zeroes it out.
*/
CONFIG_NODE *ConfigNodeAlloc(VOID)
{
CONFIG_NODE *Node;
EFI_STATUS Status;
Status = BootServices->AllocatePool(EfiBootServicesData, sizeof(CONFIG_NODE), (VOID **)&Node);
if (EFI_ERROR(Status)) {
Node = NULL;
}
// The caller expects the variable to be set even if NULL
// Call SetMem to zero it if (Node != NULL) {
SetMem(Node, sizeof(CONFIG_NODE), 0);
}
return Node;
}
/*
*sub_453C: ConfigNodeFree - recursively free a config node tree
*Walks linked list of siblings, recursively frees each node's children,
*frees the Name string, and frees the node itself.
*/
VOID ConfigNodeFree(CONFIG_NODE *Node)
{
CONFIG_NODE *Next;
if (Node == NULL) return;
do {
Next = (CONFIG_NODE *)Node->Next;
// If this is not a reference (flag 0x100 not set), free children if ((Node->Type & 0x100) == 0) {
if (Node->Name != NULL) {
// Node->Name points to a sub-object, free it recursively ConfigNodeFree((CONFIG_NODE *)Node->Name);
}
if ((Node->Type & 0x100) == 0 && Node->Child.Raw != NULL) {
FreePool(Node->Child.Raw);
}
}
// If flag 0x200 not set, free StringValue if ((Node->Type & 0x200) == 0) {
if (Node->StringValue != NULL) {
FreePool(Node->StringValue);
}
}
FreePool(Node);
Node = Next;
} while (Node != NULL);
}
/* ========================================================================
*Hex Parsing
* ======================================================================== */
/*
*sub_46CC: HexParse4 - parses 4 hex characters to value
*Converts a 4-character hex string to a numeric value.
*/
UINTN HexParse4(CONST CHAR8 *HexStr)
{
UINTN Val = 0;
INTN Digit;
for (INTN i = 0; i < 4; i++) {
CHAR8 c = HexStr[i];
if ((UINT8)(c - '0') <= 9) {
Digit = c - '0';
} else if ((UINT8)(c - 'A') <= 5) {
Digit = c - 'A' + 10;
} else if ((UINT8)(c - 'a') <= 5) {
Digit = c - 'a' + 10;
} else {
return Val;
}
Val = (Val << 4) + Digit;
}
return Val;
}
/* ========================================================================
*JSON Parser Functions
* ======================================================================== */
/*
*sub_4A98: JsonParseValue - parse a JSON value
*Dispatches based on first character of Input.
*Returns pointer after the parsed value, or NULL on failure.
*
*Type dispatch:
* "null" -> Type=Null (0)
* "false" -> Type=Boolean (0)
* "true" -> Type=True (1)
* '"' -> Type=String (4), calls JsonParseString
* '-' or digit -> Type=Number (3), calls JsonParseNumber
* '[' -> Type=Array (5), calls JsonParseArray
* '{' -> Type=Object (6), calls JsonParseObject
*/
CHAR8 *JsonParseValue(CONFIG_NODE *Node, CHAR8 *Input)
{
if (Input == NULL) return NULL;
// Check for "null"
if (AsciiStrnCmp(Input, "null", 4) == 0) {
Node->Type = ConfigTypeNull;
return Input + 4;
}
// Check for "false"
if (AsciiStrnCmp(Input, "false", 5) == 0) {
Node->Type = ConfigTypeBoolean;
return Input + 5;
}
// Check for "true"
if (AsciiStrnCmp(Input, "true", 4) == 0) {
Node->Type = ConfigTypeTrue;
Node->IntValue = 1;
return Input + 4;
}
// Quoted string if (*Input == '\"') {
return JsonParseString(Node, Input);
}
// Number (negative sign or digit)
if (*Input == '-' || (*Input >= '0' && *Input <= '9')) {
return JsonParseNumber(Node, Input);
}
// Array if (*Input == '[') {
return JsonParseArray(Node, Input);
}
// Object if (*Input == '{') {
return JsonParseObject(Node, Input);
}
return NULL;
}
/*
*sub_45B4: JsonParseNumber - parse a JSON number
*Handles integers, floats (with decimal point), and exponents (e/E).
*Returns pointer after the parsed number.
*/
CHAR8 *JsonParseNumber(CONFIG_NODE *Node, CHAR8 *Input)
{
INT64 IntPart = 0;
INT32 FracPart = 0;
INT32 Exponent = 0;
INT32 Sign = 1;
INT32 TypeFlags = 1; // indicates integer initially INT32 ExponentSign = 1;
// Handle negative sign if (*Input == '-') {
Sign = -1;
Input++;
}
// Check for leading zero (octal/hex prefix ignored per JSON spec, but parsed anyway)
CHAR8 *Start = Input;
// Parse integer part if (*Start >= '1' && *Start <= '9') {
do {
IntPart = IntPart *10 + (*Start - '0');
Start++;
} while (*Start >= '0' && *Start <= '9');
} else if (*Start == '0') {
Start++;
}
CHAR8 *EndPtr = Start;
// Parse fractional part if (*EndPtr == '.' && EndPtr[1] >= '0' && EndPtr[1] <= '9') {
TypeFlags = 2; // float EndPtr++;
do {
FracPart = FracPart *10 + (*EndPtr - '0');
EndPtr++;
} while (*EndPtr >= '0' && *EndPtr <= '9');
}
// Parse exponent if ((*EndPtr | 0x20) == 'e') { // toupper check 'e' or 'E'
TypeFlags = 2;
EndPtr++;
if (*EndPtr != '+') {
if (*EndPtr == '-') {
ExponentSign = -1;
}
}
while (*EndPtr >= '0' && *EndPtr <= '9') {
Exponent = Exponent *10 + (*EndPtr - '0');
EndPtr++;
}
}
// Store parsed values in the node Node->IntValue = IntPart *Sign;
Node->FracValue = FracPart;
Node->Type = ConfigTypeNumber;
Node->Exponent = Exponent *ExponentSign;
// Store the type flags (int vs float) in the pad field
// (Offset +28 stores TypeFlags, which indicates numeric sub-type)
return EndPtr;
}
/*
*sub_47DC: JsonParseString - parse a JSON quoted string
*Handles escape sequences: \", \\, \/, \b, \f, \n, \r, \t, \uXXXX
*UTF-8 encodes Unicode escape sequences (including surrogate pairs).
*Returns pointer after the closing quote.
*/
CHAR8 *JsonParseString(CONFIG_NODE *Node, CHAR8 *Input)
{
CHAR8 *Ptr;
CHAR8 *Result;
CHAR8 *Dest;
EFI_STATUS Status;
UINTN Length = 0;
if (*Input != '\"') return NULL;
Ptr = Input + 1;
// Calculate the length of the string (after escape processing)
if (*Ptr != '\"') {
// Count actual characters (escape sequences count as 1)
CHAR8 *Scan = Ptr;
while (*Scan && Scan) {
if (*Scan == '\"') break;
Length++;
if (*Scan == '\\') {
Scan++;
if (*Scan == 'u') Scan += 4; // \uXXXX
}
Scan++;
}
}
// Allocate buffer for the decoded string Status = BootServices->AllocatePool(EfiBootServicesData, Length + 1, (VOID **)&Result);
if (EFI_ERROR(Status)) {
Result = NULL;
}
if (Result == NULL) return NULL;
Dest = Result;
while (*Ptr != '\"') {
CHAR8 c = *Ptr;
if (c == '\0') break;
if (c != '\\') {
*Dest = c;
Dest++;
Ptr++;
continue;
}
// Handle escape sequences Ptr++;
switch (*Ptr) {
case 'b': *Dest++ = '\b'; break;
case 'f': *Dest++ = '\f'; break;
case 'n': *Dest++ = '\n'; break;
case 'r': *Dest++ = '\r'; break;
case 't': *Dest++ = '\t'; break;
case 'u': {
// Unicode escape \uXXXX UINTN Codepoint = HexParse4(Ptr + 1);
Ptr += 4;
// Handle surrogate pairs if (Codepoint >= 0xD800 && Codepoint <= 0xDBFF) {
// High surrogate, expect \uXXXX low surrogate if (Ptr[1] == '\\' && Ptr[2] == 'u') {
UINTN Low = HexParse4(Ptr + 3);
if (Low >= 0xDC00 && Low <= 0xDFFF) {
Codepoint = ((Codepoint & 0x3FF) << 10) | (Low & 0x3FF) | 0x10000;
Ptr += 6;
}
}
}
// UTF-8 encode if (Codepoint < 0x80) {
*Dest++ = (CHAR8)Codepoint;
} else if (Codepoint < 0x800) {
*Dest++ = 0xC0 | (Codepoint >> 6);
*Dest++ = 0x80 | (Codepoint & 0x3F);
} else if (Codepoint < 0x10000) {
*Dest++ = 0xE0 | (Codepoint >> 12);
*Dest++ = 0x80 | ((Codepoint >> 6) & 0x3F);
*Dest++ = 0x80 | (Codepoint & 0x3F);
} else {
*Dest++ = 0xF0 | (Codepoint >> 18);
*Dest++ = 0x80 | ((Codepoint >> 12) & 0x3F);
*Dest++ = 0x80 | ((Codepoint >> 6) & 0x3F);
*Dest++ = 0x80 | (Codepoint & 0x3F);
}
break;
}
default:
*Dest++ = *Ptr;
break;
}
Ptr++;
}
*Dest = '\0';
// Store the decoded string in the node Node->StringValue = Result;
Node->Type = ConfigTypeString;
// Return pointer after closing quote if (*Ptr == '\"') {
return Ptr + 1;
}
return Ptr;
}
/*
*sub_4B88: JsonParseArray - parse a JSON array [...]
*/
CHAR8 *JsonParseArray(CONFIG_NODE *Node, CHAR8 *Input)
{
CONFIG_NODE *Element;
CONFIG_NODE *PrevElement;
CHAR8 *Ptr;
CONFIG_NODE *NewElement;
if (*Input != '[') return NULL;
Ptr = Input + 1;
Node->Type = ConfigTypeArray;
// Skip whitespace while (*Ptr && *Ptr <= 0x20) Ptr++;
// Empty array if (*Ptr == ']') return Ptr + 1;
// Parse first element Element = ConfigNodeAlloc();
Node->Child.Children = Element;
PrevElement = Element;
if (Element == NULL) return NULL;
Ptr = JsonParseValue(Element, Ptr);
if (Ptr == NULL) return NULL;
// Skip whitespace while (*Ptr && *Ptr <= 0x20) Ptr++;
// Parse remaining elements while (Ptr && *Ptr != '\0') {
if (*Ptr == ']') return Ptr + 1;
if (*Ptr != ',') return NULL;
// Parse next element NewElement = ConfigNodeAlloc();
if (NewElement == NULL) return NULL;
PrevElement->Next = (struct _CONFIG_NODE *)NewElement;
NewElement->Prev = (struct _CONFIG_NODE *)PrevElement;
PrevElement = NewElement;
Ptr++;
while (*Ptr && *Ptr <= 0x20) Ptr++;
Ptr = JsonParseValue(NewElement, Ptr);
if (Ptr == NULL) return NULL;
while (*Ptr && *Ptr <= 0x20) Ptr++;
}
return NULL;
}
/*
*sub_4C90: JsonParseObject - parse a JSON object {...}
*Parses key:value pairs separated by commas.
*/
CHAR8 *JsonParseObject(CONFIG_NODE *Node, CHAR8 *Input)
{
CONFIG_NODE *Member;
CONFIG_NODE *PrevMember;
CHAR8 *Ptr;
CHAR8 *KeyPtr;
CONFIG_NODE *NewMember;
CONFIG_NODE *ValueNode;
if (*Input != '{') return NULL;
Ptr = Input + 1;
Node->Type = ConfigTypeObject;
// Skip whitespace while (*Ptr && *Ptr <= 0x20) Ptr++;
// Empty object if (*Ptr == '}') return Ptr + 1;
// Parse first member Member = ConfigNodeAlloc();
Node->Child.Children = Member;
PrevMember = Member;
if (Member == NULL) return NULL;
// Parse key string KeyPtr = JsonParseString(Member, Ptr);
if (KeyPtr == NULL) return NULL;
// Move string value to name, mark as key
// The original decompiled code swaps Name and StringValue pointers Member->Name = Member->StringValue;
Member->StringValue = NULL;
// Skip whitespace while (*KeyPtr && *KeyPtr <= 0x20) KeyPtr++;
if (*KeyPtr != ':') return NULL;
Ptr = KeyPtr + 1;
while (*Ptr && *Ptr <= 0x20) Ptr++;
// Parse value Ptr = JsonParseValue(Member, Ptr);
if (Ptr == NULL) return NULL;
while (*Ptr && *Ptr <= 0x20) Ptr++;
// Parse remaining members while (Ptr && *Ptr != '\0') {
if (*Ptr == '}') return Ptr + 1;
if (*Ptr != ',') return NULL;
NewMember = ConfigNodeAlloc();
if (NewMember == NULL) return NULL;
PrevMember->Next = (struct _CONFIG_NODE *)NewMember;
NewMember->Prev = (struct _CONFIG_NODE *)PrevMember;
PrevMember = NewMember;
Ptr++;
while (*Ptr && *Ptr <= 0x20) Ptr++;
// Parse key KeyPtr = JsonParseString(NewMember, Ptr);
if (KeyPtr == NULL) return NULL;
NewMember->Name = NewMember->StringValue;
NewMember->StringValue = NULL;
while (*KeyPtr && *KeyPtr <= 0x20) KeyPtr++;
if (*KeyPtr != ':') return NULL;
Ptr = KeyPtr + 1;
while (*Ptr && *Ptr <= 0x20) Ptr++;
// Parse value Ptr = JsonParseValue(NewMember, Ptr);
if (Ptr == NULL) return NULL;
while (*Ptr && *Ptr <= 0x20) Ptr++;
}
return NULL;
}
/*
*sub_4A30: JsonParse - parse JSON starting from Input
*Skips leading whitespace, then calls JsonParseValue.
*/
CONFIG_NODE *JsonParse(CONFIG_NODE *Container, CHAR8 *Input)
{
CONFIG_NODE *Node;
Node = (CONFIG_NODE *)ConfigNodeAlloc();
if (Node == NULL) return NULL;
// Skip whitespace while (*Input && *Input <= 0x20) Input++;
if (JsonParseValue(Node, Input)) {
return Node;
}
ConfigNodeFree(Node);
return NULL;
}
/*
*sub_4E5C: JsonFindKey - linear search for key in object
*Searches the linked list children of Parent for a child with matching name.
*/
CONFIG_NODE *JsonFindKey(CONFIG_NODE *Parent, CONST CHAR8 *Key)
{
CONFIG_NODE *Child;
for (Child = (CONFIG_NODE *)Parent->Child.Children;
Child != NULL && AsciiStriCmp(Child->Name, (CHAR8 *)Key) != 0;
Child = (CONFIG_NODE *)Child->Next)
{
// Continue searching
}
return Child;
}
/*
*sub_4E98: JsonObjectAdd - add key-value pair to an object
*Appends the Value node to the end of Parent's child list,
*with Key as the Name.
*/
VOID JsonObjectAdd(CONFIG_NODE *Parent, CONST CHAR8 *Key, CONFIG_NODE *Value)
{
if (Value == NULL) return;
// Free any existing key name on the value if (Value->StringValue != NULL) {
FreePool(Value->StringValue);
}
// Set the key name Value->StringValue = AllocateCopyPool((CHAR8 *)Key);
// Link into parent's child list CONFIG_NODE *FirstChild = (CONFIG_NODE *)Parent->Child.Children;
if (FirstChild != NULL) {
// Find the last sibling CONFIG_NODE *Last = FirstChild;
while (Last->Next != NULL) {
Last = (CONFIG_NODE *)Last->Next;
}
Last->Next = (struct _CONFIG_NODE *)Value;
Value->Prev = (struct _CONFIG_NODE *)Last;
} else {
Parent->Child.Children = Value;
}
}
/*
*sub_4F38: JsonNewString - create a string config node
*/
CONFIG_NODE *JsonNewString(CONST CHAR8 *Value)
{
CONFIG_NODE *Node;
Node = ConfigNodeAlloc();
if (Node != NULL) {
Node->Type = ConfigTypeString;
Node->StringValue = AllocateCopyPool((CHAR8 *)Value);
}
return Node;
}
/*
*sub_4F04: JsonNewInteger - create a number config node
*/
CONFIG_NODE *JsonNewInteger(UINT64 Value)
{
CONFIG_NODE *Node;
Node = ConfigNodeAlloc();
if (Node != NULL) {
Node->Type = ConfigTypeNumber;
Node->IntValue = Value;
}
return Node;
}
/* ========================================================================
*HII String Helper
* ======================================================================== */
/*
*sub_B88: HiiGetString - retrieve a string from HII database
*Gets the string identified by StringId from the HII handle a1
*via the HII String Protocol.
*/
UINT16 *HiiGetString(EFI_HII_HANDLE Handle, UINT16 StringId)
{
UINT16 *String;
UINTN StringSize = 512;
EFI_STATUS Status;
String = NULL;
if (StringId == 0) return NULL;
// First attempt with 512-byte buffer Status = BootServices->AllocatePool(EfiBootServicesData, 512, (VOID **)&String);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not allocate enough memory. Status = %r\n", Status);
return String;
}
*String = L'\0';
Status = gHiiString->GetString(
gHiiString,
"en-US",
Handle,
StringId,
String,
&StringSize,
NULL);
if (Status == EFI_BUFFER_TOO_SMALL) {
// Free and retry with correct size BootServices->FreePool(String);
String = NULL;
Status = BootServices->AllocatePool(EfiBootServicesData, StringSize, (VOID **)&String);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not allocate enough memory. Status = %r\n", Status);
return NULL;
}
*String = L'\0';
Status = gHiiString->GetString(
gHiiString,
"en-US",
Handle,
StringId,
String,
&StringSize,
NULL);
}
if (EFI_ERROR(Status)) {
if (String != NULL) {
DebugLog(DEBUG_ERROR, "**GetString Error: %r**", Status);
BootServices->FreePool(String);
return NULL;
}
}
return String;
}
/* ========================================================================
*HII Handle Enumeration / Buffer Size
* ======================================================================== */
/*
*sub_59C: GetHiiPackageListSize
*Queries HII Database to get the size needed for a package list.
*First calls with 0 buffer to get size, allocates, then calls again.
*Returns the buffer and optionally sets *OutSize to the size in UINT16 units.
*/
EFI_STATUS GetHiiPackageListSize(UINT16 *OutSize)
{
VOID *Buffer = NULL;
UINTN BufferSize = 0;
EFI_STATUS Status;
// First call: get required buffer size Status = gHiiDatabase->ExportPackageLists(gHiiDatabase, 0, &BufferSize, NULL);
if (EFI_ERROR(Status)) {
if (Status != EFI_BUFFER_TOO_SMALL) goto done;
}
// Allocate and re-export Status = BootServices->AllocatePool(EfiBootServicesData, BufferSize, &Buffer);
if (EFI_ERROR(Status)) goto done;
// Export with full buffer Status = gHiiDatabase->ExportPackageLists(gHiiDatabase, 0, &BufferSize, Buffer);
if (EFI_ERROR(Status)) {
BootServices->FreePool(Buffer);
Buffer = NULL;
return 0;
}
done:
if (OutSize != NULL && Buffer != NULL) {
*OutSize = (UINT16)(BufferSize >> 3); // Convert bytes to UINT16 units
}
return (EFI_STATUS)Buffer; // Returns buffer pointer as status
}
/* ========================================================================
*Variable Store Mapping
* ======================================================================== */
/*
*sub_680: RegisterVarStoreNodeMapping
*Registers a varstore mapping: VarId, DataBuffer, and SetupIndex.
*Allocates a linked-list node and appends to gVarStoreMappingList.
*/
EFI_STATUS RegisterVarStoreNodeMapping(UINT16 VarId, VOID *DataBuffer, UINT8 SetupIndex)
{
typedef struct {
UINT16 VarId;
UINT8 SetupIndex;
UINT64 DataBuffer;
UINT64 Next;
} VAR_MAP_NODE;
VAR_MAP_NODE *Node;
EFI_STATUS Status;
Status = BootServices->AllocatePool(EfiBootServicesData, sizeof(VAR_MAP_NODE), (VOID **)&Node);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not allocate enough memory. Status = %r\n", Status);
return Status;
}
Node->VarId = VarId;
Node->SetupIndex = SetupIndex;
Node->DataBuffer = (UINT64)DataBuffer;
Node->Next = 0;
// Append to linked list if (gVarStoreMappingList != NULL) {
VAR_MAP_NODE *Last = (VAR_MAP_NODE *)gVarStoreMappingList;
while (Last->Next != 0) {
Last = (VAR_MAP_NODE *)Last->Next;
}
Last->Next = (UINT64)Node;
} else {
gVarStoreMappingList = Node;
}
return EFI_SUCCESS;
}
/*
*sub_7F0: TrimAsciiSpaces - strip leading/trailing ASCII spaces
*/
CHAR8 *TrimAsciiSpaces(CHAR8 *String)
{
CHAR8 *Start;
CHAR8 c;
UINTN TrimCount;
CHAR8 *Dst;
CHAR8 *End;
c = *String;
TrimCount = 0;
// Skip leading spaces while (c == ' ') {
c = String[++TrimCount];
}
Start = &String[TrimCount];
c = String[TrimCount];
if (c == '\0') return NULL;
if (TrimCount > 0) {
// Shift the string left Dst = String;
UINTN Offset = 0;
c = String[TrimCount];
do {
Start = &String[TrimCount + ++Offset];
Dst[Offset - 1] = c;
c = *Start;
} while (c != '\0');
Dst[Offset] = '\0';
// Trim trailing spaces End = &Dst[Offset - 1];
if (*End == ' ') {
while (1) {
*End-- = '\0';
if ((UINTN)(End + 1) - (UINTN)String <= 1) break;
if (*End != ' ') break;
}
return NULL; // Return NULL if all spaces were stripped
}
}
return String;
}
/* ========================================================================
*IFR Parsing Support
* ======================================================================== */
/*
*sub_744: GetAsciiStringFromUnicode - helper that returns ASCII string length then converts
*Calls AsciiStrLen on the result of UnicodeToAscii.
*/
CHAR8 *GetAsciiFromUnicode(CONST UINT16 *Source, CHAR8 *DestBuf)
{
UINTN Len = AsciiStrLen(UnicodeToAscii(Source, NULL)); // Gets count EFI_STATUS Status;
CHAR8 *Buf;
Status = BootServices->AllocatePool(EfiBootServicesData, Len *2 + 2, (VOID **)&Buf);
if (EFI_ERROR(Status)) {
return Buf;
}
return UnicodeToAscii(Source, Buf);
}
/*
*sub_79c: GetUnicodeFromAscii - helper to allocate Unicode buffer for ASCII string
*/
UINT16 *GetUnicodeFromAscii(CONST CHAR8 *Source)
{
UINTN Len = StrLen((UINT16 *)Source);
EFI_STATUS Status;
UINT16 *Buf;
Status = BootServices->AllocatePool(EfiBootServicesData, (Len + 1) *sizeof(UINT16), (VOID **)&Buf);
if (EFI_ERROR(Status)) {
return Buf;
}
return AsciiToUnicode(Source, Buf);
}
/*
*sub_D18: RegisterVarStoreNode - creates and links a varstore tracking node
*Allocates a 24-byte node and appends to the config at offset +26 of n0x80.
*/
EFI_STATUS RegisterVarStoreNode(UINT64 ConfigAddr, UINT64 NodeData, UINT64 End)
{
typedef struct {
UINT64 ConfigAddr;
UINT64 NodeData;
UINT64 Next;
} VAR_STORE_TRACKER;
VAR_STORE_TRACKER *Node;
EFI_STATUS Status;
if (gIfrNvStoreMapHandle == 0) {
return EFI_INVALID_PARAMETER;
}
Status = BootServices->AllocatePool(EfiBootServicesData, 24, (VOID **)&Node);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not allocate enough memory. Status = %r\n", Status);
return Status;
}
Node->ConfigAddr = ConfigAddr;
Node->NodeData = NodeData;
Node->Next = 0;
// Append at end of linked list at gIfrNvStoreMapHandle + 26 UINT64 *ListHead = (UINT64 *)(gIfrNvStoreMapHandle + 26);
if (*ListHead != 0) {
VAR_STORE_TRACKER *Last = (VAR_STORE_TRACKER *)*ListHead;
while (Last->Next != 0) {
Last = (VAR_STORE_TRACKER *)Last->Next;
}
Last->Next = (UINT64)Node;
} else {
*ListHead = (UINT64)Node;
}
return EFI_SUCCESS;
}
/*
*sub_2E38: StrCmp - Unicode string compare
*/
INTN StrCmp(CONST UINT16 *First, CONST UINT16 *Second)
{
if (StrLen(First) *2 + 2 == 2) {
DebugAssert(__FILE__, __LINE__, "StrSize (FirstString) != 0");
}
if (StrLen(Second) *2 + 2 == 2) {
DebugAssert(__FILE__, __LINE__, "StrSize (SecondString) != 0");
}
while (*First && *First == *Second) {
First++;
Second++;
}
return (INTN)(*First - *Second);
}
/*
*sub_50D8: CompareVarStoreGuid - compare two GUIDs (16 bytes each)
*Uses ReadUnaligned64 to compare two 64-bit halves.
*/
BOOLEAN CompareVarStoreGuid(VOID *Guid1, VOID *Guid2)
{
UINT64 Low1 = ReadUnaligned64(Guid1);
UINT64 Low2 = ReadUnaligned64(Guid2);
UINT64 High1 = ReadUnaligned64((UINT8 *)Guid1 + 8);
UINT64 High2 = ReadUnaligned64((UINT8 *)Guid2 + 8);
return (Low1 == Low2 && High1 == High2);
}
/* ========================================================================
*Variable Store Write-Back
* ======================================================================== */
/*
*sub_29F8: CleanupVarStore - free all variable store entries
*/
VOID CleanupVarStore(VOID)
{
if (gIfrNvStoreMapHandle != 0) {
ConfigNodeFree((CONFIG_NODE *)gIfrNvStoreMapHandle);
gIfrNvStoreMapHandle = 0;
}
if (gVarStoreArray != NULL) {
UINTN Index;
if (gVarStoreCount > 0) {
for (Index = 0; Index < gVarStoreCount; Index++) {
UINT8 *Entry = (UINT8 *)gVarStoreArray + Index *53;
if (*(UINT64 *)(Entry + 1) != 0) {
BootServices->FreePool(*(VOID **)(Entry + 1));
*(UINT64 *)(Entry + 1) = 0;
}
if (*(UINT64 *)(Entry + 9) != 0) {
BootServices->FreePool(*(VOID **)(Entry + 9));
*(UINT64 *)(Entry + 9) = 0;
}
}
}
BootServices->FreePool(gVarStoreArray);
gVarStoreArray = NULL;
}
}
/* ========================================================================
*IFR Opcode Processing
* ======================================================================== */
/*
*sub_864: ProcessIfrOpcode - process a single IFR opcode
*Handles varstore-related opcodes:
*0x24 -> varstore (standard)
*0x25 -> varstore (name-based)
*0x26 -> varstore (EFI variable)
*
*For each, parses the opcode and registers variable mappings.
*/
EFI_STATUS ProcessIfrOpcode(
UINT64 ConfigAddr,
UINT16 *OpcodeData,
UINT64 IfrEnd,
DOUBLE Param)
{
UINT8 Opcode;
UINT16 VarId;
CHAR8 *VarStoreName;
VOID *VarGuid;
UINT32 VarAttribs;
UINT16 VarSize;
UINTN Index;
EFI_STATUS Status;
VOID *Buffer = NULL;
CHAR8 *VarNameStr = NULL;
Opcode = *(UINT8 *)OpcodeData;
switch (Opcode) {
case 0x24: { // EFI_IFR_VARSTORE VarId = OpcodeData[9]; // VarStoreId VarStoreName = (CHAR8 *)(OpcodeData + 1); // Actual variable name follows VarSize = OpcodeData[10]; // Size VarGuid = OpcodeData + 11; // GUID goto process_var;
}
case 0x25: { // EFI_IFR_VARSTORE_NAME_VALUE VarId = OpcodeData[1]; // VarStoreId VarStoreName = (CHAR8 *)(OpcodeData + 2);
VarSize = 0;
VarGuid = NULL;
goto process_var;
}
case 0x26: { // EFI_IFR_VARSTORE_EFI VarStoreName = (CHAR8 *)(OpcodeData + 2);
VarId = OpcodeData[1];
VarSize = OpcodeData[12];
VarGuid = OpcodeData + 13;
VarAttribs = *(UINT32 *)&OpcodeData[5];
goto process_var;
}
default:
return EFI_UNSUPPORTED;
}
process_var:
// Find or create the varstore mapping in the linked list if (ConfigAddr == 0 || gIfrNvStoreMapHandle == 0) {
return EFI_NOT_FOUND;
}
Index = 0;
if (gVarStoreCount > 0) {
// Check for matching config section name for (Index = 0; Index < gVarStoreCount; Index++) {
UINT8 *Entry = (UINT8 *)gVarStoreArray + Index *53;
if (Entry[0] != 0) break;
}
}
if (VarSize > 0 && Buffer == NULL) {
Status = BootServices->AllocatePool(EfiBootServicesData, VarSize, &Buffer);
if (EFI_ERROR(Status)) goto cleanup;
}
if (Buffer != NULL) {
CHAR8 *ConfigName = (CHAR8 *)GetAsciiFromUnicode((UINT16 *)VarStoreName, NULL);
// Read variable from RuntimeServices UINT32 ActualAttribs;
UINTN ActualSize = VarSize;
// Simplified: try to locate the setup variable
// In the original: RuntimeServices->GetVariable(varName, &guid, &attribs, &size, buffer)
// Then check for section name matching "Setup" etc.
if (gVarStoreArray != NULL) {
UINT64 EntryOffset = (UINT64)(Index *53);
UINT8 *Entry = (UINT8 *)gVarStoreArray + EntryOffset;
if (*(UINT64 *)(Entry + 9) == 0) {
// Allocate and store the variable data Status = BootServices->AllocatePool(EfiBootServicesData, VarSize, (VOID **)(Entry + 9));
if (!EFI_ERROR(Status)) {
// Copy data into entry CopyMem(*(VOID **)(Entry + 9), Buffer, VarSize);
// Store the config name reference
// Entry + 17 stores the config name pointer for the section
}
}
}
// Log if debug enabled if (gIfrDebugEnabled && Buffer != NULL) {
CHAR8 *NameStr = GetAsciiFromUnicode((UINT16 *)VarStoreName, NULL);
DebugLog(DEBUG_INFO, " (VarStore) VarName: %s GUID: %g\n", NameStr, Param);
DebugLog(DEBUG_INFO, "Got VarID = %x\n", VarId);
BootServices->FreePool(NameStr);
}
}
cleanup:
// Register the mapping RegisterVarStoreNodeMapping(VarId, Buffer, (UINT8)Index);
return EFI_SUCCESS;
}
/*
*sub_105c: ParseIfrGuidOp - handle GUIDed IFR opcodes
*Processes IFR GUIDed operations (sub-ops within GUID opcodes).
*/
EFI_STATUS ParseIfrGuidOp(
UINT64 ConfigAddr,
UINT16 *OpcodeData,
DOUBLE Param)
{
// GUIDed opcode processing
// Original: walks IFR data looking for varstore-related GUID ops,
// processes the variable ID and data, registers mappings return EFI_SUCCESS;
}
/*
*sub_15E0: ParseIfrOpcodes - main IFR opcode walker
*Walks the IFR bytecode stream, dispatching each opcode.
*/
EFI_STATUS ParseIfrOpcodes(
UINT64 ConfigAddr,
UINT64 *IfrData,
UINT64 IfrEnd,
INT32 Phase)
{
UINT64 Current;
UINT8 Opcode;
UINT8 Scope;
UINT16 OpcodeLen;
UINT16 VarId;
UINTN Index;
CHAR8 *VarName;
DOUBLE DummyParam = 0.0;
Current = (UINT64)IfrData;
while (Current < IfrEnd) {
Opcode = *(UINT8 *)Current;
OpcodeLen = *(UINT16 *)(Current + 2); // IFR opcode length at offset +2 if (OpcodeLen == 0) break;
// Process based on opcode type switch (Opcode) {
case 0x01: // EFI_IFR_FORM case 0x02: // EFI_IFR_SUBTITLE case 0x03: // EFI_IFR_TEXT case 0x05: // EFI_IFR_ONE_OF case 0x06: // EFI_IFR_CHECKBOX case 0x07: // EFI_IFR_NUMERIC case 0x08: // EFI_IFR_STRING case 0x09: // EFI_IFR_PASSWORD case 0x0A: // EFI_IFR_ORDERED_LIST
// Questions with varstore - parse variable info if (gVarStoreMappingList != NULL) {
// Walk the linked list tracking varstore IDs
// ...
}
break;
case 0x24: // EFI_IFR_VARSTORE case 0x25: // EFI_IFR_VARSTORE_NAME_VALUE case 0x26: // EFI_IFR_VARSTORE_EFI ProcessIfrOpcode(ConfigAddr, (UINT16 *)Current, IfrEnd, DummyParam);
break;
case 0x2B: // EFI_IFR_GUID ParseIfrGuidOp(ConfigAddr, (UINT16 *)Current, DummyParam);
break;
case 0x2C: // EFI_IFR_DUP case 0x2D: // EFI_IFR_DEFAULTSTORE default:
break;
}
Current += OpcodeLen;
}
return EFI_SUCCESS;
}
/* ========================================================================
*Setup Store Initialization
* ======================================================================== */
/*
*sub_1B80: ProcessVarStore - process a single varstore from HII database
*Reads the package list for the given HII handle, walks IFR opcodes.
*/
EFI_STATUS ProcessVarStore(EFI_HII_HANDLE HiiHandle, VAR_STORE_ENTRY *Entry)
{
VOID *PackageList;
UINTN PackageListSize;
EFI_STATUS Status;
*(UINT64 *)Entry = 0;
// Query package list size Status = gHiiDatabase->ExportPackageLists(gHiiDatabase, HiiHandle, &PackageListSize, NULL);
if (Status != EFI_BUFFER_TOO_SMALL) {
DebugLog(DEBUG_ERROR, "ExportPackageLists FAILED\n");
return Status;
}
// Allocate buffer Status = BootServices->AllocatePool(EfiBootServicesData, PackageListSize, &PackageList);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not allocate enough memory. Status = %r\n", Status);
goto fail;
}
// Export the package list Status = gHiiDatabase->ExportPackageLists(gHiiDatabase, HiiHandle, &PackageListSize, PackageList);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "ExportPackageLists FAILED\n");
goto fail_alloc;
}
// Parse IFR opcodes from the package list
{
UINT64 PkgListBase = (UINT64)PackageList;
UINT64 Current = PkgListBase + 20; // Skip package list header UINT32 ListLen = *(UINT32 *)(PkgListBase + 16); // Total package list length while (Current <= PkgListBase + ListLen - 4) {
UINT8 PkgType = *(UINT8 *)(Current + 3);
if (PkgType == (UINT8)-33) { // 0xDF = end marker break;
}
if (PkgType == 2) {
// IFR package type 2
UINT32 PkgLen = *(UINT32 *)Current & 0xFFFFFF;
UINT64 IfrData = Current + 4;
ParseIfrOpcodes((UINT64)Entry, (UINT64 *)IfrData, Current + PkgLen, 0);
// Process any pending varstore mapping items from the queue while (gVarStoreMappingList != NULL) {
UINT64 DataBuffer;
// Dequeue and process
// ...
DataBuffer = 0;
BootServices->FreePool((VOID *)DataBuffer);
gVarStoreMappingList = 0;
}
}
Current += *(UINT32 *)Current & 0xFFFFFF;
}
}
BootServices->FreePool(PackageList);
return EFI_SUCCESS;
fail_alloc:
BootServices->FreePool(PackageList);
fail:
return Status;
}
/*
*sub_1FEC: InitSetupStoreList - enumerate HII package lists and build varstore array
*Main initialization routine: enumerates all HII handles, processes each.
*/
EFI_STATUS InitSetupStoreList(VOID)
{
UINTN HandleCount;
EFI_HII_HANDLE *HandleBuffer;
EFI_STATUS Status;
UINTN Index;
UINT16 NumHandles;
UINT64 *VarStoreArrayTmp;
UINTN ArraySize;
// Get list of HII handles (RegisteredHandles)
Status = gHiiDatabase->RegisteredHandles(
gHiiDatabase,
&HandleCount,
&HandleBuffer);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "InitSetupStoreList FAILED\n");
CleanupVarStore();
return Status;
}
// Allocate temporary array for varstore entries (each 53 bytes)
ArraySize = sizeof(VAR_STORE_ENTRY) *HandleCount;
// Using raw 8-byte pointer arithmetic as in original Status = BootServices->AllocatePool(EfiBootServicesData, 8 *HandleCount, (VOID **)&VarStoreArrayTmp);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not allocate enough memory. Status = %r\n", Status);
CleanupVarStore();
return Status;
}
// Initialize varstore array Status = EFI_SUCCESS;
gVarStoreArray = VarStoreArrayTmp;
gVarStoreCount = HandleCount;
// Process each handle for (Index = 0; Index < HandleCount; Index++) {
Status = ProcessVarStore(HandleBuffer[Index], (VAR_STORE_ENTRY *)&VarStoreArrayTmp[Index]);
// Continue on error
}
// Write back phase WriteBackSetupStore();
CleanupVarStore();
BootServices->FreePool(VarStoreArrayTmp);
return Status;
}
/* ========================================================================
*Attributes / Protocol Initialization
* ======================================================================== */
/*
*sub_4F74: GetVariableByName - find a protocol by name in configuration table
*/
VOID *GetVariableByName(CONST CHAR8 *Name)
{
UINTN Count = SystemTable->NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigEntry = SystemTable->ConfigurationTable;
if (Count == 0) return NULL;
while (ConfigEntry != NULL) {
if (CompareVarStoreGuid(ConfigEntry, (VOID *)Name) == 0) {
return ConfigEntry->VendorTable;
}
ConfigEntry++;
if (--Count == 0) break;
}
return NULL;
}
/*
*sub_4FC4: Internal GUID compare helper
*Compares GUID data starting at two addresses using optimized aligned compares.
*/
INTN CompareGuidBytes(CONST UINT8 *a1, CONST UINT8 *a2, UINTN Size)
{
// Optimized byte comparison that tries 8-byte aligned reads UINTN Offset = 0;
// Handle misalignment UINTN Align1 = (UINTN)a1 & 7;
UINTN Align2 = (UINTN)a2 & 7;
if (Align1 != 0 && Align1 == Align2) {
UINTN Remain = 8 - Align1;
while (Remain > 0) {
if (a1[Offset] != a2[Offset]) break;
Offset++;
Remain--;
}
}
// Compare aligned 8-byte chunks while (Offset <= Size - 8) {
if (*(UINT64 *)&a1[Offset] == *(UINT64 *)&a2[Offset]) {
Offset += 8;
} else {
break;
}
}
// Compare remaining bytes while (Offset < Size) {
if (a1[Offset] != a2[Offset]) break;
Offset++;
}
if (Offset >= Size) return 0;
return (INTN)a1[Offset] - (INTN)a2[Offset];
}
/*
*sub_1D20: FindVarStoreProtocol
*Walk through variable store protocols to find the setup store.
*/
EFI_STATUS FindVarStoreProtocol(
VOID *Handle,
UINT64 **ConfigData,
UINT64 *ConfigSize)
{
EFI_STATUS Status;
UINTN NoHandles;
EFI_HANDLE *HandleBuffer;
UINTN Index;
UINT64 Result;
UINTN OriginalTpl;
EFI_GUID gSetupFormMapGuid = {
0x5AE, 0x41AE, 0x4125, {0x4B, 0xAD, 0x4D, 0x1F, 0x22, 0x59, 0x6B, 0x74}
};
// Get all handles supporting the form map protocol Status = BootServices->LocateHandleBuffer(
ByProtocol,
&gSetupFormMapGuid,
NULL,
&NoHandles,
&HandleBuffer);
if (EFI_ERROR(Status)) {
return EFI_NOT_FOUND;
}
// Try each handle for (Index = 0; Index < NoHandles; Index++) {
VOID *FormMap;
Status = BootServices->HandleProtocol(
HandleBuffer[Index],
&gSetupFormMapGuid,
&FormMap);
if (EFI_ERROR(Status)) continue;
// Call GetData(Handle, 0x19, 0, ConfigData, ConfigSize, ...)
Result = ((EFI_STATUS (*)(VOID *, VOID *, UINT8, UINTN, UINT64 **, UINT64 *, UINT8 *))(
(UINTN)FormMap + 24))(
FormMap, Handle, 0x19, 0, ConfigData, ConfigSize, NULL);
if (!EFI_ERROR((EFI_STATUS)Result) ||
(EFI_STATUS)Result == EFI_BUFFER_TOO_SMALL) {
goto done;
}
}
// Retry phase: try ResetData
{
// Walk handles again, call ResetData for (Index = 0; Index < NoHandles; Index++) {
VOID *FormMap;
Status = BootServices->HandleProtocol(
HandleBuffer[Index],
&gSetupFormMapGuid,
&FormMap);
if (EFI_ERROR(Status)) continue;
Status = ((EFI_STATUS (*)(VOID *, EFI_GUID *, UINT64 **, UINT64 *, CHAR8 *, INT32 *, UINT8 *))(
(UINTN)FormMap + 16))(
FormMap, &gSetupFormMapGuid, ConfigData, ConfigSize, NULL, NULL, NULL);
if (!EFI_ERROR(Status)) {
if (*ConfigSize != 0) *ConfigSize = 0;
goto done_free;
}
}
}
done_free:
BootServices->FreePool(HandleBuffer);
if (EFI_ERROR(Status)) {
return EFI_NOT_FOUND;
}
// Re-raise TPL and get the actual protocol OriginalTpl = BootServices->RaiseTPL(TPL_HIGH_LEVEL);
BootServices->RestoreTPL(4); // Restore to fixed TPL
// Find the actual variable store protocol VOID *VarStoreProtocol = GetVariableByName((CONST CHAR8 *)&gSetupFormMapGuid);
if (VarStoreProtocol == NULL) {
return EFI_NOT_FOUND;
}
// Read variable data VOID *ReadData;
UINTN ReadSize;
EFI_GET_NEXT_VARIABLE_NAME GetNext;
BootServices->RaiseTPL(TPL_HIGH_LEVEL);
BootServices->RestoreTPL(OriginalTpl);
// Call ReadFunction Status = ((EFI_STATUS (*)(VOID *, UINT16, UINT64 *))(
(UINTN)VarStoreProtocol + 152))(
*ConfigData,
*(UINT16 *)(*ConfigData + 48),
&ReadData);
// Cleanup BootServices->FreePool(*ConfigData);
*ConfigData = NULL;
if (EFI_ERROR(Status)) return Status;
// Restore TPL again BootServices->RaiseTPL(TPL_HIGH_LEVEL);
BootServices->RestoreTPL(OriginalTpl);
// Re-query Status = BootServices->LocateHandleBuffer(
ByProtocol,
&gSetupFormMapGuid,
NULL,
&NoHandles,
&HandleBuffer);
return EFI_SUCCESS;
done:
BootServices->FreePool(HandleBuffer);
return EFI_SUCCESS;
}
/* ========================================================================
*Setup Store Write Back
* ======================================================================== */
/*
*sub_27DC: WriteBackSetupStore
*Iterates all varstore entries, writing modified variables back,
*then triggers a system reset.
*/
VOID WriteBackSetupStore(VOID)
{
UINTN Index;
BOOLEAN NeedsReset = FALSE;
CHAR16 VarNameBuf[200];
if (gVarStoreArray == NULL) return;
for (Index = 0; Index < gVarStoreCount; Index++) {
UINT8 *Entry = (UINT8 *)gVarStoreArray + Index *53;
if (Entry[0] != 0) { // Entry is valid if (!NeedsReset) {
// Enable system access variable RuntimeServices->SetVariable(
L"SystemAccess",
&gSetupVarStoreGuid,
2,
0,
NULL);
DebugLog(DEBUG_INFO, "Enable SysAccessValue %r\n", EFI_SUCCESS);
}
// Format variable name UnicodeSPrint(VarNameBuf, 200, L"%04d%02d%02d%02d%02d%02d");
// Write the variable EFI_STATUS WriteStatus = RuntimeServices->SetVariable(
VarNameBuf,
(EFI_GUID *)(Entry + 25), // GUID at offset +25
*(UINT32 *)(Entry + 49), // Attributes at offset +49
*(UINT64 *)(Entry + 41), // DataSize at offset +41
*(VOID **)(Entry + 9)); // Data at offset +9 DebugLog(DEBUG_INFO,
"[%r] Update %S, Size = 0x%x, Guid= %g, Attribute = %x, Buf = %p\n",
WriteStatus, VarNameBuf, *(UINT32 *)(Entry + 49),
*(UINT64 *)(Entry + 41), Entry + 25,
*(UINT32 *)(Entry + 49), *(VOID **)(Entry + 9));
NeedsReset = TRUE;
Entry[0] = 0; // Mark as written
}
// Delete the backup variable UnicodeSPrint(VarNameBuf, 200, L"%04d%02d%02d%02d%02d%02d");
RuntimeServices->SetVariable(
VarNameBuf,
&gSetupVarStoreGuid,
0,
0,
NULL);
DebugLog(DEBUG_INFO, "[%r] Delete %S\n", EFI_SUCCESS, VarNameBuf);
}
if (NeedsReset) {
// Trigger system reset via I/O port 0xCF9 IO_PORT_WRITE(0xCF9, 0x0E); // System reset while (1); // Halt
}
}
/* ========================================================================
*Protocol Initialization
* ======================================================================== */
/*
*sub_2ABC: InitAttributesNode - main initialization
*Initializes HII protocols, enumerates handles, sets up varstore.
*
*GUIDs:
*v7[4] = {0xEEBBDC6E, 0x4682, 0xBF47, {0xB3, 0xFE, 0xE6, 0xD4, 0x27, 0x42, 0x6C, 0x04}}
*v8[4] = {0x0FD796F4, 0x4CED, 0xCD83, {0x28, 0xBD, 0x3E, 0x7F, 0x57, 0x0E, 0xF, 0x2A}}
*/
EFI_STATUS InitAttributesNode(VOID)
{
EFI_STATUS Status;
UINT16 NumHandles;
VOID *HandleList;
UINTN HiiHandleCount;
EFI_HII_HANDLE *HiiHandleBuffer;
UINTN ArraySize;
UINTN Index;
EFI_GUID HiiDatabaseGuid = {
0xEEBBDC6E, 0x4682, 0xBF47, {0xB3, 0xFE, 0xE6, 0xD4, 0x27, 0x42, 0x6C, 0x04}
};
EFI_GUID HiiStringGuid = {
0x0FD796F4, 0x4CED, 0xCD83, {0x28, 0xBD, 0x3E, 0x7F, 0x57, 0x0E, 0xF, 0x2A}
};
// Locate HII Database Protocol Status = BootServices->LocateProtocol(&HiiDatabaseGuid, NULL, (VOID **)&gHiiDatabase);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not locate HiiDatabase Protocol\n");
goto fail;
}
// Locate HII String Protocol Status = BootServices->LocateProtocol(&HiiStringGuid, NULL, (VOID **)&gHiiString);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not locate HiiString Protocol\n");
goto fail;
}
// Get number of HII handles HandleList = (VOID *)(UINTN)GetHiiPackageListSize(&NumHandles);
if (HandleList != NULL && NumHandles > 0) {
// Allocate handle buffer ArraySize = sizeof(EFI_HII_HANDLE) *NumHandles;
Status = BootServices->AllocatePool(
EfiBootServicesData, ArraySize, (VOID **)&HiiHandleBuffer);
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "Can not allocate enough memory. Status = %r\n", Status);
goto fail;
}
// Initialize setup store list Status = InitSetupStoreList();
if (EFI_ERROR(Status)) {
DebugLog(DEBUG_ERROR, "InitSetupStoreList FAILED\n");
goto fail_free;
}
// Process each handle for (Index = 0; Index < NumHandles; Index++) {
Status = ProcessVarStore(HiiHandleBuffer[Index],
(VAR_STORE_ENTRY *)((UINTN)HiiHandleBuffer + Index *sizeof(VOID *)));
}
// Write back and cleanup WriteBackSetupStore();
CleanupVarStore();
BootServices->FreePool(HiiHandleBuffer);
} else {
DebugLog(DEBUG_ERROR, "Can not get any HiiPkgHandle\n");
CleanupVarStore();
return 0x8000000000000009; // EFI_OUT_OF_RESOURCES
}
return EFI_SUCCESS;
fail_free:
BootServices->FreePool(HiiHandleBuffer);
fail:
CleanupVarStore();
return Status;
}
/* ========================================================================
*Module Entry
* ======================================================================== */
/*
*sub_45C: UefiConfigManagerEntry - BIOS setup config manager entry point
*
*Creates the "BIOS Setup Information Store" root node with timestamp,
*the "Attributes" child node, then initializes all HII protocols
*and varstore mappings.
*
*The timestamp is formatted as "%04d%02d%02d%02d%02d%02d" and stored
*under the "Modified" key. The "Attributes" node is initialized with
*an attribute type of 6.
*/
EFI_STATUS EFIAPI UefiConfigManagerEntry(
EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable)
{
UINT16 DateBuf;
UINT8 Year, Month, Day, Hour, Minute, Second;
CONFIG_NODE *RootNode;
CONFIG_NODE *ModifiedNode;
CHAR8 TimeStr[200];
CONFIG_NODE *AttrNode;
// Create root "BIOS Setup Information Store" node RootNode = ConfigNodeAlloc();
if (RootNode != NULL) {
RootNode->Type = 6; // Object type
}
gSetupConfigRoot = RootNode;
if (RootNode != NULL) {
// Create "Modified" timestamp string ModifiedNode = JsonNewString("BIOS Setup Information Store");
JsonObjectAdd(RootNode, "Name", ModifiedNode);
// Get current date/time from RuntimeServices RuntimeServices->GetTime(&DateBuf, NULL);
// Format timestamp UnicodeSPrint((CHAR16 *)TimeStr, 200,
L"%04d%02d%02d%02d%02d%02d",
DateBuf, Year, Month, Day, Hour, Minute, Second);
// Store as ASCII via AsciiToUnicode/UnicodeToAscii
// (original stores "Modified" = timestamp string)
CONFIG_NODE *TimeNode = JsonNewString(TimeStr);
JsonObjectAdd(RootNode, "Modified", TimeNode);
// Create Attributes node AttrNode = ConfigNodeAlloc();
if (AttrNode != NULL) {
AttrNode->Type = 6; // Object type
}
gAttributesNode = AttrNode;
if (AttrNode != NULL) {
// Initialize protocols and varstore InitAttributesNode();
JsonObjectAdd(RootNode, "Attributes", gAttributesNode);
}
}
if (gSetupConfigRoot != NULL) {
// Configuration is built; clean up ConfigNodeFree((CONFIG_NODE *)gSetupConfigRoot);
gSetupConfigRoot = NULL;
}
return EFI_SUCCESS;
}
/* ========================================================================
*Module Entry Point
* ======================================================================== */
/*
*_ModuleEntryPoint: Standard UEFI entry point at 0x3B0
*
*This is generated by the UEFI Boot Services Table Library.
*It initializes global ImageHandle, SystemTable, BootServices,
*and RuntimeServices pointers, then calls UefiConfigManagerEntry().
*/
EFI_STATUS EFIAPI ModuleEntryPoint(
EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable)
{
::ImageHandle = ImageHandle;
if (ImageHandle == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)");
}
::SystemTable = SystemTable;
if (SystemTable == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)");
}
::BootServices = SystemTable->BootServices;
if (::BootServices == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)");
}
::RuntimeServices = SystemTable->RuntimeServices;
if (::RuntimeServices == NULL) {
DebugAssert(
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)");
}
// Initialize HOB list GetHobList();
// Call the actual entry point return UefiConfigManagerEntry(ImageHandle, SystemTable);
}