Newer
Older
AMI-Aptio-BIOS-Reversed / SmbiosBoard / SmbiosBoard.c
@Ajax Dong Ajax Dong 2 days ago 18 KB Init
/** @file
  SmbiosBoard - Board-specific SMBIOS data driver for HR650X.

  DXE driver that provides board-specific SMBIOS information via a
  private protocol. Handles SMBIOS table lookups, PCIe segment/bus
  configuration, and board identification through CMOS/IO ports.

  Build path:
    e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\AmiCompatibilityPkg\Smbios\SmbiosBoard\SmbiosBoard\DEBUG\AutoGen.c

  PDB: SmbiosBoard.pdb

  Source: HR650X BIOS decompilation, index 0066.
  Module: SmbiosBoard.efi (6.4 KB, 26 functions).
  Compiler: VS2015, X64, DEBUG.
**/

#include "SmbiosBoard.h"

///
/// SmbiosBoard Protocol GUID:
///   {0903DD14-2CA0-458A-b5eb0c0ca30d785c}
///
EFI_GUID gSmbiosBoardProtocolGuid = SMBIOS_BOARD_PROTOCOL_GUID;

///
/// MmPciBase Protocol GUID:
///   {FD480A76-B134-4EF7-ADFE-B0E054639807}
///
EFI_GUID gMmPciBaseProtocolGuid = MMPCI_BASE_PROTOCOL_GUID;

//
// Library instance globals (populated at entry)
//
EFI_HANDLE            gImageHandle       = NULL;
EFI_SYSTEM_TABLE      *gST               = NULL;
EFI_BOOT_SERVICES     *gBS               = NULL;
EFI_RUNTIME_SERVICES  *gRT               = NULL;
EFI_DXE_SERVICES      *gDS               = NULL;
VOID                  *mPciUsra          = NULL;  ///< MmPciBase protocol
VOID                  *mHobList          = NULL;  ///< HOB list
UINT64                mPciExpressBaseAddress = 0; ///< PCIe base address
VOID                  *mPcd              = NULL;  ///< PCD protocol
VOID                  *gDebugPortProtocol = NULL; ///< Debug/StatusCode protocol

///
/// SmbiosBoard Protocol instance.
/// Contains board data and function dispatch table installed at entry.
///
/// Layout:
///   +0x00  BoardData[24]     - Board type/revision byte sequence
///   +0x18  Reserved          - Padding/reserved
///   +0x20  Callbacks[8]      - Function dispatch table (all point to NullCallback)
///   +0x60  LocateMmPci       - MmPciBase protocol locator (sub_734)
///   +0x68  UnsupportedHandler - Stub returning EFI_UNSUPPORTED (sub_77C)
///   +0x70  MmPciAccess       - MMIO PCI config access (sub_788)
///   +0x78  DestructorDispatch - Destructor dispatch loop (sub_704)
///   +0x80  PcdSizeConstant   - PCD size value (0xC0C)
///
STATIC SMBIOS_BOARD_PROTOCOL mSmbiosBoardProtocol = {
  //
  // BoardData - board type and identification bytes
  //
  { 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x05, 0x01, 0x02, 0x04,
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
  //
  // Reserved
  //
  0,
  //
  // Callbacks[8] - all default to null callback
  //
  { (UINT64)NullCallback, (UINT64)NullCallback,
    (UINT64)NullCallback, (UINT64)NullCallback,
    (UINT64)NullCallback, (UINT64)NullCallback,
    (UINT64)NullCallback, (UINT64)NullCallback },
  //
  // LocateMmPci - LocateMmPciBaseProtocol
  //
  (UINT64)LocateMmPciBaseProtocol,
  //
  // UnsupportedHandler
  //
  (UINT64)UnsupportedStub,
  //
  // MmPciAccess
  //
  (UINT64)MmPciAccess,
  //
  // DestructorDispatch
  //
  (UINT64)DestructorDispatch,
  //
  // PcdSizeConstant
  //
  0xC0C
};

//
// Destructor callback table (terminated by NULL entry)
//
STATIC VOID (*mDestructorTable[])(VOID) = {
  NULL
};

/**
  Entry point for SmbiosBoard driver.

  Initializes UEFI library globals (gImageHandle, gST, gBS, gRT),
  locates DxeServicesTable, MmPciBaseProtocol, and PCD protocols.
  Sets up PCIe segment/bus configuration via CopyMem, writes board
  identification flags, reads CMOS/RTC board type, installs the
  SmbiosBoard protocol, and enters a timed spin-wait loop.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS  The entry point executed successfully.
**/
EFI_STATUS
EFIAPI
SmbiosBoardEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_BOOT_SERVICES     *BootServices;
  EFI_RUNTIME_SERVICES  *RuntimeServices;
  EFI_DXE_SERVICES      *DxeServices;
  EFI_PCD_PROTOCOL      *PcdProtocol;
  UINT64                PciExpressBase;
  UINT64                PciSegBusTableSize;
  UINT8                 BoardFlags;
  UINTN                 Eflags;
  BOOLEAN               InterruptsWereEnabled;
  UINT32                BoardType;
  UINT64                TscStart;
  UINT64                TscNow;
  EFI_HANDLE            NewHandle;

  //
  // Initialize library globals
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  gST = SystemTable;
  ASSERT (gST != NULL);

  gBS = SystemTable->BootServices;
  ASSERT (gBS != NULL);

  gRT = SystemTable->RuntimeServices;
  ASSERT (gRT != NULL);

  //
  // Locate DxeServicesTable from configuration table
  //
  Status = GetConfigTable (&gEfiDxeServicesTableGuid, (VOID **)&gDS);
  ASSERT_EFI_ERROR (Status);
  ASSERT (gDS != NULL);
  ASSERT_EFI_ERROR (Status);   // AutoGen.c:444

  //
  // Locate MmPciBase protocol for PCI configuration space access
  //
  if (mPciUsra == NULL) {
    Status = gBS->LocateProtocol (
                   &gMmPciBaseProtocolGuid,
                   NULL,
                   &mPciUsra
                   );
    ASSERT_EFI_ERROR (Status);
    ASSERT (mPciUsra != NULL);
  }

  //
  // Initialize HOB list
  //
  GetHobList ();

  //
  // Get PCD protocol for PCIe configuration
  //
  PcdProtocol = GetPcdProtocol ();

  //
  // Read PCI Express base address from PCD token 5
  //
  mPciExpressBaseAddress = PcdProtocol->Get64 (5);

  //
  // Get PCIe segment/bus table size from PCD token 7
  //
  PciSegBusTableSize = (UINT64)PcdProtocol->Get64 (7);
  ASSERT (sizeof (PCIE_SEG_BUS_TABLE) >= PcdProtocol->GetSize (7));

  //
  // Copy PCIe segment/bus table to static buffer
  // (address 0x1700 in the .data section)
  //
  CopyMem ((VOID *)(UINTN)&mSmbiosBoardProtocol.PcdSizeConstant,
           (VOID *)(UINTN)PciExpressBase,
           PciSegBusTableSize);

  //
  // Write board configuration flag to PCI config space
  //
  if (GetPciExpressBaseAddress () >= 0) {
    WriteBoardConfig (GetPciExpressBaseAddress ());
  }

  //
  // Read board type from CMOS/RTC
  //
  Eflags = ReadCallerEflags ();
  DisableInterrupts ();
  InterruptsWereEnabled = (Eflags & 0x200) != 0;
  BoardType = IoRead32 (1288) & 0xFFFFFF;

  //
  // Timed spin-wait loop: wait ~2 seconds (33554432 * TSC ticks)
  //
  TscStart = ReadTsc ();
  while ((((UINT32)BoardType + 357 - (UINT32)IoRead32 (1288)) & 0x800000) == 0) {
    CpuPause ();
  }
  ReadTsc ();

  //
  // Restore interrupt state
  //
  if (InterruptsWereEnabled) {
    EnableInterrupts ();
  } else {
    DisableInterrupts ();
  }

  //
  // Install SmbiosBoard protocol
  //
  NewHandle = ImageHandle;
  Status = gBS->InstallProtocolInterface (
                  &NewHandle,
                  &gSmbiosBoardProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &mSmbiosBoardProtocol
                  );

  return Status;
}

/**
  Locates a configuration table by GUID.

  @param[in]  TableGuid  Pointer to the GUID to find.
  @param[out] Table      Pointer to receive the table pointer.

  @retval EFI_SUCCESS           Table found.
  @retval EFI_NOT_FOUND         Table not found.
  @retval EFI_INVALID_PARAMETER TableGuid or Table is NULL.
**/
EFI_STATUS
EFIAPI
GetConfigTable (
  IN EFI_GUID  *TableGuid,
  OUT VOID     **Table
  )
{
  UINTN       Index;
  EFI_STATUS  Status;

  ASSERT (TableGuid != NULL);
  ASSERT (Table != NULL);

  *Table = NULL;
  Status = EFI_NOT_FOUND;

  if (gST->NumberOfTableEntries == 0) {
    return EFI_NOT_FOUND;
  }

  for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
    if (CompareGuid (TableGuid, (EFI_GUID *)(gST->ConfigurationTable + Index))) {
      *Table = (VOID *)((EFI_CONFIGURATION_TABLE *)gST->ConfigurationTable)[Index].VendorTable;
      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

/**
  Initializes and returns the HOB list pointer.

  @return Pointer to the HOB list.
**/
VOID *
EFIAPI
GetHobList (
  VOID
  )
{
  EFI_STATUS  Status;

  if (mHobList == NULL) {
    Status = GetConfigTable (&gEfiHobListGuid, &mHobList);
    ASSERT_EFI_ERROR (Status);
    ASSERT (mHobList != NULL);
  }

  return mHobList;
}

/**
  Returns the PCD protocol instance.

  @return Pointer to the EFI_PCD_PROTOCOL instance.
**/
EFI_PCD_PROTOCOL *
EFIAPI
GetPcdProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

  if (mPcd == NULL) {
    Status = gBS->LocateProtocol (
                    &gEfiPcdProtocolGuid,
                    NULL,
                    &mPcd
                    );
    ASSERT_EFI_ERROR (Status);
    ASSERT (mPcd != NULL);
  }

  return (EFI_PCD_PROTOCOL *)mPcd;
}

/**
  Reads a 32-bit value from a CMOS/RTC I/O port.

  @param[in]  Port  The I/O port to read from (must be 4-byte aligned).

  @return The 32-bit value read from the specified port.
**/
UINT32
EFIAPI
IoRead32 (
  IN UINT16  Port
  )
{
  ASSERT ((Port & 3) == 0);
  return __indword (Port);
}

/**
  Reads board identification via CMOS/RTC.

  Reads from CMOS register 0x4B (RTC Index 0x4B, port 0x70/0x71)
  to determine board type/revision. The CMOS routine:
    1. Preserves bit 7 of CMOS index register 0x70
    2. Selects RTC register 0x4B
    3. Reads the value from data port 0x71

  If the CMOS value is 0 (indicating uninitialized/battery lost),
  falls back to reading memory-mapped PCI configuration at address
  0xFDAF0490 (typical for chipset strap configuration).

  @return Board type identifier:
          0x80000004 - Board type 1
          0x8000000C - Board type > 1
          0           - Unknown/unreadable
**/
UINT32
EFIAPI
GetBoardType (
  VOID
  )
{
  UINT8   CmosIndex;
  UINT8   BoardId;
  UINT8   BoardIdAdjusted;

  //
  // Read CMOS register 0x4B
  // Preserve NMI mask (bit 7) in index register 0x70
  //
  CmosIndex = __inbyte (0x70);
  __outbyte (0x70, CmosIndex & 0x80 | 0x4B);
  BoardId = __inbyte (0x71);

  BoardIdAdjusted = BoardId;

  if (BoardId > 3) {
    BoardIdAdjusted = BoardId;
    if (BoardId == 0) {
      //
      // CMOS battery may be dead; read chipset straps from
      // memory-mapped PCI config space at 0xFDAF0490
      //
      BoardIdAdjusted = (*(volatile UINT8 *)(UINTN)0xFDAF0490) & 2 | 1;
    }
  }

  if ((UINT8)(BoardIdAdjusted - 1) > 0xFD) {
    return 0;
  }

  return (BoardIdAdjusted == 1) ? 0x80000004 : 0x8000000C;
}

/**
  Writes an SMBIOS board type flag to the PCIe configuration
  space via I/O ports 0xCF8/0xCFC.

  Writes value 0x0500 (endian: 1280 decimal = 0x500)
  to the PCI config address derived from PciExpressBase.

  @param[in] PciExpressBase  The PCIe base address.
**/
VOID
EFIAPI
WriteBoardConfig (
  IN UINT64  PciExpressBase
  )
{
  ASSERT (((UINTN)PciExpressBase & 1) == 0);
  *(volatile UINT16 *)(UINTN)PciExpressBase = 0x500;
}

/**
  Returns the PCI Express base address from PCD.

  Translates an SMBIOS/PCD address token to a memory-mapped address
  by adding the cached PciExpressBaseAddress.

  @return The full PCI Express MMIO base address.
**/
UINT64
EFIAPI
GetPciExpressBaseAddress (
  VOID
  )
{
  UINT64  Address;

  //
  // Token 1024064 / 1024068 corresponds to the PCIe base address PCD
  //
  if (Address & ~0xFFFFFFF) {
    //
    // In UEFI, the address validation is for addresses with bits beyond
    // the 28-bit PCIe MMIO window -- the ASSERT below would fire
    //
  }

  return Address + mPciExpressBaseAddress;
}

/**
  Locates MmPciBase protocol (gMmPciBaseProtocolGuid) and caches
  the protocol pointer for use by the driver.

  @retval EFI_SUCCESS  Protocol located successfully.
**/
EFI_STATUS
EFIAPI
LocateMmPciBaseProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

  Status = gBS->LocateProtocol (
                  &gMmPciBaseProtocolGuid,
                  NULL,
                  &mPciUsra
                  );
  ASSERT_EFI_ERROR (Status);
  ASSERT (mPciUsra != NULL);

  return Status;
}

/**
  Stub handler that returns EFI_UNSUPPORTED.

  @return EFI_UNSUPPORTED (0x8000000000000003).
**/
UINT64
EFIAPI
UnsupportedStub (
  VOID
  )
{
  return EFI_UNSUPPORTED;
}

/**
  Accesses MmPciBase protocol to perform an MMIO PCI configuration
  read/write operation.

  @param[in]      Handle    Protocol handle.
  @param[in]      Operation Operation type.
  @param[in]      Address   PCI configuration address.
  @param[in,out]  Buffer    Data buffer.

  @retval EFI_SUCCESS           Operation completed.
  @retval EFI_UNSUPPORTED       Protocol not located.
**/
EFI_STATUS
EFIAPI
MmPciAccess (
  IN     VOID   *Handle,
  IN     UINT64 Operation,
  IN     UINT64 Address,
  IN OUT VOID   *Buffer
  )
{
  MMPCI_BASE_PROTOCOL  *MmPci;
  UINT64               Result;

  if (mPciUsra == NULL) {
    return EFI_UNSUPPORTED;
  }

  MmPci = (MMPCI_BASE_PROTOCOL *)mPciUsra;

  //
  // The protocol's entry at offset 0x20 (index 4 into vtable)
  // is expected to be a function with:
  //   MmPci->Function[4](MmPci, Handle, Operation, ..., &Result, Address)
  //
  // For this driver, Operation=4, with a result buffer.
  //
  Result = 1;
  return MmPci->Function[4] (
                  MmPci,
                  Handle,
                  Operation,
                  4,
                  0,
                  &Result,
                  Address
                  );
}

/**
  Iterates over the destructor callback table and invokes
  each non-NULL entry.

  Called as part of driver dispatch cleanup.
**/
VOID
EFIAPI
DestructorDispatch (
  VOID
  )
{
  UINTN  Index;

  for (Index = 0; mDestructorTable[Index] != NULL; Index++) {
    mDestructorTable[Index] ();
  }
}

/**
  Locates the DebugLib (StatusCode Runtime Protocol) for debug output.

  The StatusCode Runtime Protocol GUID
  {36232936-0E76-31C8-A13A-3AF2FC1C3932} is used by DebugLib to
  output debug/assert messages. On first call, allocates a page
  and locates the protocol.

  @return Pointer to the protocol instance, or NULL on failure.
**/
VOID *
EFIAPI
GetDebugProtocol (
  VOID
  )
{
  EFI_STATUS  Status;
  UINTN       Pages;

  if (gDebugPortProtocol == NULL) {
    Pages = EFI_SIZE_TO_PAGES (31);
    Pages = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, Pages);
    if (Pages <= EFI_SIZE_TO_PAGES (0x10)) {
      //
      // Locate StatusCode Runtime Protocol for debug output
      //
      Status = gBS->LocateProtocol (
                      &gEfiStatusCodeRuntimeProtocolGuid,
                      NULL,
                      &gDebugPortProtocol
                      );
      if (EFI_ERROR (Status)) {
        gDebugPortProtocol = NULL;
      }
    } else {
      return NULL;
    }
  }

  return gDebugPortProtocol;
}

/**
  Wrapper for debug assertion output.

  Calls GetDebugProtocol internally and invokes the debug print
  routine if the protocol is available and the error level mask
  matches.

  @param[in]  ErrorLevel  The error level of the debug message.
  @param[in]  Format      Format string.
  @param[in]  ...         Variable arguments.
**/
VOID
EFIAPI
DebugAssert (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST                                Args;
  EFI_STATUS_CODE_PROTOCOL               *StatusCodeProtocol;
  UINT32                                 BoardType;

  VA_START (Args, Format);

  StatusCodeProtocol = (EFI_STATUS_CODE_PROTOCOL *)GetDebugProtocol ();
  if (StatusCodeProtocol != NULL) {
    BoardType = GetBoardType ();
    if ((BoardType & (UINT32)ErrorLevel) != 0) {
      //
      // Report status code with the assertion message
      //
      StatusCodeProtocol->ReportStatusCode (
                            (EFI_STATUS_CODE_TYPE)ErrorLevel,
                            (EFI_STATUS_CODE_VALUE)Format,
                            0,
                            NULL,
                            &Args
                            );
    }
  }

  VA_END (Args);
}

/**
  Reads a 64-bit value from a potentially unaligned buffer.

  @param[in]  Buffer  Pointer to the buffer to read from.

  @return The 64-bit value read.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}

/**
  Compares two GUIDs by comparing their 64-bit halves.

  @param[in]  Guid1  Pointer to the first GUID.
  @param[in]  Guid2  Pointer to the second GUID.

  @retval TRUE   The GUIDs match.
  @retval FALSE  The GUIDs do not match.
**/
BOOLEAN
EFIAPI
CompareGuid (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  UINT64  Data1;
  UINT64  Data2;
  UINT64  Data3;
  UINT64  Data4;

  Data1 = ReadUnaligned64 (Guid1);
  Data2 = ReadUnaligned64 (Guid1 + 8);
  Data3 = ReadUnaligned64 (Guid2);
  Data4 = ReadUnaligned64 (Guid2 + 8);

  return (Data1 == Data3) && (Data2 == Data4);
}

/**
  Copies a memory buffer, handling overlapping regions correctly.

  When Source < Destination and ranges overlap, copies backwards
  from the end. Otherwise copies forward in 8-byte chunks with
  qmemcpy, then handles the remainder.

  @param[in]  Destination  Pointer to the destination buffer.
  @param[in]  Source       Pointer to the source buffer.
  @param[in]  Length       Number of bytes to copy.

  @return Pointer to the destination buffer.
**/
VOID *
EFIAPI
CopyMem (
  OUT VOID       *Destination,
  IN CONST VOID  *Source,
  IN UINTN       Length
  )
{
  CHAR8   *Dst;
  CHAR8   *Src;
  UINTN   Count;

  Dst = (CHAR8 *)Destination;
  Src = (CHAR8 *)Source;

  if (Src < Dst && &Src[Length - 1] >= Dst) {
    //
    // Overlapping: copy backwards from the end
    //
    Src = &Src[Length - 1];
    Dst = &Dst[Length - 1];
    //
    // Copy remainder byte-by-byte (backwards)
    //
    for ( ; Length != 0; Length--) {
      *Dst-- = *Src--;
    }
  } else {
    //
    // No overlap or Source after Destination: copy forwards
    //
    Count = Length;
    Count &= ~7;
    Count >>= 3;
    qmemcpy (Dst, Src, 8 * Count);
    Src = &Src[8 * Count];
    Dst = &Dst[8 * Count];
    Length &= 7;
    //
    // Copy remaining bytes
    //
    for ( ; Length != 0; Length--) {
      *Dst++ = *Src++;
    }
  }

  return Destination;
}

/**
  Returns EFI_UNSUPPORTED as a thunk for unimplemented callback slots.

  @return EFI_UNSUPPORTED (0x8000000000000003).
**/
UINT64
EFIAPI
NullCallback (
  VOID
  )
{
  return 0;
}

/**
  Reads the CPU timestamp counter.

  @return The current 64-bit timestamp counter value.
**/
UINT64
EFIAPI
ReadTsc (
  VOID
  )
{
  return __rdtsc ();
}

/**
  Reads the caller's EFLAGS register.

  @return The EFLAGS value at the call site.
**/
UINTN
EFIAPI
ReadCallerEflags (
  VOID
  )
{
  return __getcallerseflags ();
}

/**
  Executes a CPU PAUSE instruction (yield hint for spin-wait).
**/
VOID
EFIAPI
CpuPause (
  VOID
  )
{
  _mm_pause ();
}

/**
  Enables CPU interrupts (STI).
**/
VOID
EFIAPI
EnableInterrupts (
  VOID
  )
{
  _enable ();
}

/**
  Disables CPU interrupts (CLI).
**/
VOID
EFIAPI
DisableInterrupts (
  VOID
  )
{
  _disable ();
}