/** @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 ();
}