Newer
Older
AMI-Aptio-BIOS-Reversed / PciOutOfResourceSetupPage / PciOutOfResourceSetupPage.c
@Ajax Dong Ajax Dong 2 days ago 12 KB Init
/**
 * 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
//