/** @file
IioInit.c -- Integrated IO (IIO) Initialization DXE driver
This DXE driver initializes the IIO (Integrated IO) subsystem on the Purley
platform. It performs PCIe link initialization, IIO stack setup, IO fabric
configuration, S3 boot script management, IOAT initialization, and late
platform security (TXT) setup. It also manages ReadyToBoot and
ExitBootServices events for the IIO hardware.
Source tree path: PurleySktPkg/Dxe/IioInit/IioInit.c
Build path: Build/HR6N0XMLK/DEBUG_VS2015/X64/PurleySktPkg/Dxe/IioInit/IioInit/DEBUG/AutoGen.c
MD5: 123a999d39d274b161482592b152ec76
SHA256: 27b9ba6c6a83f26893d071ec93d110c9490fe7b06ea0eaa86e2040d5abfb6086
Copyright (C) 2025 Intel Corporation
SPDX-License-Identifier: Intel
**/
#include "IioInit.h"
//
// Global EFI protocol pointers
//
EFI_HANDLE ImageHandle = NULL;
EFI_SYSTEM_TABLE *SystemTable = NULL;
EFI_BOOT_SERVICES *BootServices = NULL;
EFI_RUNTIME_SERVICES *RuntimeServices = NULL;
VOID *mPciUsra = NULL;
VOID *gDS = NULL;
VOID *mPcd = NULL;
//
// Global IIO data
//
UINT8 byte_DD59 = 0; // S3 boot script flag
UINT64 qword_DD20 = 0; // PCD token for PcdIioRevision
UINT64 qword_DE18 = 0; // PCD token for PcdIioPostInitDone
UINT64 qword_DE20 = 0; // Boot script label pointer
UINT64 qword_DE28 = 0; // Boot script label entry
UINT32 dword_DCA8 = 0; // MCFG table
//
// Access width/stride table: byte pairs of (element_size, stride_size) for
// access widths 0..3, plus two more entries at +16 for the receive stride.
//
UINT8 byte_A7C0[32] = { 0 };
//
// GUID definitions for IIO protocols
//
EFI_GUID gIioStackProtocolGuid = { 0x0 }; // unk_AA70 - IIO Stack Protocol
EFI_GUID gMmPciBaseProtocolGuid = { 0x0 }; // unk_AAB0 - MM PCIe Base Protocol
EFI_GUID gPcdProtocolGuid = { 0x0 }; // unk_AAE0 - PCD Protocol
//
// Forward declarations of static helper functions
//
STATIC
VOID
IioInitAssert (
IN UINT64 ErrorLevel,
IN CHAR8 *Message,
...
);
STATIC
EFI_STATUS
IioInitGetMmPciBaseProtocol (
VOID
);
STATIC
EFI_STATUS
IioInitGetStackProtocol (
OUT IIO_STACK_PROTOCOL **StackProtocol
);
STATIC
IIO_SOCKET_DATA*
IioInitGetSocketData (
OUT IIO_STACK_PROTOCOL **StackProtocol OPTIONAL
);
//
// ---------------------------------------------------------------------------
// Low-level utility functions (from BaseLib / IoLib)
// ---------------------------------------------------------------------------
/**
Generates a breakpoint trap (INT 3).
**/
VOID
EFIAPI
IioInitBreakpoint (
VOID
)
{
__debugbreak ();
}
/**
No-op wait loop (PAUSE / HLT hint).
**/
VOID
EFIAPI
IioInitHalt (
VOID
)
{
__halt ();
}
/**
Call stack back trace (TRAP).
Generates a stack backtrace via debug register.
**/
VOID
EFIAPI
IioInitStackTrace (
VOID
)
{
// Implementation: stack capture primitive
}
/**
Enable interrupts (STI).
**/
VOID
EFIAPI
IioInitEnableInterrupts (
VOID
)
{
__enable_interrupts ();
}
/**
Disable interrupts (CLI).
**/
VOID
EFIAPI
IioInitDisableInterrupts (
VOID
)
{
__disable_interrupts ();
}
/**
Read the RFLAGS register.
**/
UINT16
EFIAPI
IioInitReadEflags (
VOID
)
{
return __readeflags ();
}
/**
Zero-fill a memory buffer.
@param[in] Buffer Pointer to buffer to zero.
@param[in] Length Number of bytes to zero.
**/
VOID *
EFIAPI
IioInitZeroMem (
IN VOID *Buffer,
IN UINTN Length
)
{
return memset (Buffer, 0, Length);
}
/**
Copy a memory buffer.
@param[in] Destination Pointer to destination buffer.
@param[in] Source Pointer to source buffer.
@param[in] Length Number of bytes to copy.
**/
VOID *
EFIAPI
IioInitCopyMem (
IN VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
return memcpy (Destination, Source, Length);
}
//
// ---------------------------------------------------------------------------
// ASSERT / Debug helper
// ---------------------------------------------------------------------------
/**
Conditional assertion failure handler.
Logs a debug message to the UEFI console and triggers a breakpoint.
@param[in] ErrorLevel Debug error level.
@param[in] Message Format string.
@param[in] ... Variable arguments.
**/
STATIC
VOID
EFIAPI
IioInitDebugAssert (
IN UINT64 ErrorLevel,
IN CHAR8 *Message,
...
)
{
VA_LIST Args;
VA_START (Args, Message);
DebugPrint (ErrorLevel, Message, Args);
VA_END (Args);
if ((ErrorLevel & DEBUG_ERROR) != 0) {
__debugbreak ();
}
}
/**
Dead-loop assertion handler.
Prints the assertion expression and source location, then enters an
infinite breakpoint loop.
@param[in] FileName Source file name.
@param[in] LineNumber Source line number.
@param[in] Description Assertion expression.
**/
STATIC
VOID
EFIAPI
IioInitAssertDeadLoop (
IN CHAR8 *FileName,
IN UINTN LineNumber,
IN CHAR8 *Description
)
{
DebugPrint (DEBUG_ERROR,
"ASSERT [%s(%d)]: %s\n",
FileName, LineNumber, Description);
while (TRUE) {
__debugbreak ();
}
}
//
// ---------------------------------------------------------------------------
// IIO initialization core (entry logic)
// ---------------------------------------------------------------------------
/**
IIO Driver initialization routine.
Initializes global protocol pointers, locates the IIO Stack Protocol and
MM PCIe Base Protocol, sets up the IO fabric, configures PCIe hot-plug
and IOAT, and registers ReadyToBoot and ExitBootServices callbacks.
@param[in] ImageHandle Handle for this image.
@param[in] SystemTable Pointer to EFI system table.
@return EFI_SUCCESS Initialization successful.
@return EFI_UNSUPPORTED Platform not supported.
@return EFI_NOT_FOUND Required protocol not found.
**/
STATIC
EFI_STATUS
EFIAPI
IioInitCore (
IN EFI_HANDLE ImageHandleParam,
IN EFI_SYSTEM_TABLE *SystemTableParam
)
{
EFI_STATUS Status;
UINT16 Flags;
BOOLEAN InterruptsEnabled;
UINT32 Timestamp;
UINT64 PcdInterface;
UINT8 *IioRevision;
IIO_STACK_PROTOCOL *StackProtocol;
//
// Save image handle and system table globally
//
ImageHandle = ImageHandleParam;
ASSERT (ImageHandle != NULL);
SystemTable = (EFI_SYSTEM_TABLE *)SystemTableParam;
ASSERT (SystemTable != NULL);
//
// Get boot services and runtime services tables
//
BootServices = SystemTable->BootServices;
ASSERT (BootServices != NULL);
RuntimeServices = SystemTable->RuntimeServices;
ASSERT (RuntimeServices != NULL);
//
// Locate the DxeServicesTable (gDS)
//
Status = gBS->LocateProtocol (&gDxeServicesTableGuid,
NULL, (VOID **)&gDS);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
ASSERT (gDS != NULL);
//
// AutoGen initialization
//
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
//
// Locate the MM PCIe Base protocol
//
if (mPciUsra == NULL) {
Status = gBS->LocateProtocol (&gMmPciBaseProtocolGuid,
NULL, &mPciUsra);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
ASSERT (mPciUsra != NULL);
}
//
// Platform-specific early init
//
IioPlatformEarlyInit ();
//
// Get PCD interface and read IIO revision token
//
PcdInterface = (UINT64)GetPcdProtocol ();
qword_DD20 = ((PCD_PROTOCOL *)PcdInterface)->Get64 (PcdIioRevision);
//
// Check IIO revision and optionally fix up IioRevision
//
IioRevision = (UINT8 *)PcdGetPtr (PcdIioRevision);
if ((INT8)*IioRevision >= 0) {
IioRevision = PcdGetPtr (PcdIioRevision);
ZeroMem (IioRevision, 1280);
IioRevision = PcdGetPtr (PcdIioRevision);
*IioRevision |= 0x80;
}
//
// Save interrupt state then call initialization routines
//
Flags = SaveAndDisableInterrupts ();
DisableInterrupts ();
InterruptsEnabled = (Flags & 0x200) != 0;
Timestamp = IoRead32 (PCH_LPC_DEFAULT_BASE + R_PCH_LPC_RTC_INDEX) & 0x00FFFFFF;
ReadTimestamp ();
//
// Wait loop for timestamp to advance (small delay)
//
while (((Timestamp + 357 - (UINT32)ReadTimestamp ()) & 0x800000) == 0) {
CpuPause ();
}
ReadTimestamp ();
//
// Restore interrupt state
//
if (InterruptsEnabled) {
EnableInterrupts ();
}
//
// Register protocols / call main IIO initialization chain
//
Status = IioInitEntry ();
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
//
// Signal that IIO post-init is done
//
PcdInterface = (UINT64)GetPcdProtocol ();
qword_DE18 = ((PCD_PROTOCOL *)PcdInterface)->Get64 (PcdIioPostInitDone);
return EFI_SUCCESS;
}
//
// ---------------------------------------------------------------------------
// IIO Stack access helpers
// ---------------------------------------------------------------------------
/**
Locate the IIO Stack Protocol.
@param[out] StackProtocol Returns the IIO stack protocol instance.
@return EFI_SUCCESS Protocol located.
**/
STATIC
EFI_STATUS
EFIAPI
IioInitGetStackProtocol (
OUT IIO_STACK_PROTOCOL **StackProtocol
)
{
EFI_STATUS Status;
*StackProtocol = NULL;
Status = gBS->LocateProtocol (&gIioStackProtocolGuid,
NULL, (VOID **)StackProtocol);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
return Status;
}
/**
Get the IIO socket data block and optionally the stack protocol.
@param[out] StackProtocol Optional pointer to receive the stack protocol.
@return Pointer to the IIO socket data structure.
**/
STATIC
IIO_SOCKET_DATA*
EFIAPI
IioInitGetSocketData (
OUT IIO_STACK_PROTOCOL **StackProtocol OPTIONAL
)
{
EFI_STATUS Status;
IIO_STACK_PROTOCOL *Protocol;
IIO_SOCKET_DATA *SocketData;
Status = IioInitGetStackProtocol (&Protocol);
if (EFI_ERROR (Status)) {
return NULL;
}
SocketData = (IIO_SOCKET_DATA *)Protocol;
if (StackProtocol != NULL) {
*StackProtocol = Protocol;
}
return SocketData;
}
//
// ---------------------------------------------------------------------------
// PCD Protocol helper
// ---------------------------------------------------------------------------
/**
Locate and cache the PCD Protocol.
@return Pointer to the PCD protocol interface.
**/
STATIC
PCD_PROTOCOL*
EFIAPI
IioInitGetPcdProtocol (
VOID
)
{
EFI_STATUS Status;
if (mPcd == NULL) {
Status = gBS->LocateProtocol (&gPcdProtocolGuid,
NULL, &mPcd);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
ASSERT (mPcd != NULL);
}
return (PCD_PROTOCOL *)mPcd;
}
//
// ---------------------------------------------------------------------------
// Boot script S3 support
// ---------------------------------------------------------------------------
/**
Save S3 boot script label entries.
Manages a circular buffer of boot script label entries for S3 resume.
@return 0 on success.
**/
UINTN
EFIAPI
IioInitS3BootScriptLabel (
VOID
)
{
if (qword_DE20 != qword_DE28) {
S3BootScriptLabel (0, 0);
if (*(UINT64 *)qword_DE28 == 0) {
CopyMem ((VOID *)qword_DE28, (VOID *)qword_DE20, 32);
*(UINT8 *)(qword_DE28 + 14) = 1;
}
qword_DE20 = qword_DE28;
}
return 0;
}
/**
Write S3 boot script entries for PCIe configuration.
Writes a PCIe register into the boot script table for S3 restore.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
IioInitS3BootScriptPciCfg (
VOID
)
{
IIO_SOCKET_DATA *SocketData;
UINT32 DataSize;
EFI_STATUS Status;
SocketData = IioInitGetSocketData (NULL);
if (SocketData == NULL) {
return EFI_UNSUPPORTED;
}
if (!*(UINT8 *)((UINT8 *)SocketData + 15)) {
*(UINT32 *)((UINT8 *)SocketData + 16) = *(UINT32 *)((UINT8 *)SocketData + 8) + 3;
Status = S3BootScriptSavePciCfg (S3BootScriptWidthUint32,
(UINT64)SocketData, 0);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
Status = S3BootScriptLabel (S3BootScriptWidthUint32,
*(UINT64 *)SocketData,
*(UINT32 *)((UINT8 *)SocketData + 16));
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
*(UINT8 *)((UINT8 *)SocketData + 15) = 1;
S3BootScriptDone ();
}
return 0;
}
//
// ---------------------------------------------------------------------------
// IOAT / VT-d initialization
// ---------------------------------------------------------------------------
/**
Initialize IOAT (I/O Acceleration Technology) for all IIO sockets.
Checks the socket state and enables IOAT features based on platform
configuration.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
IioInitIoatInit (
VOID
)
{
EFI_STATUS Status;
IIO_STACK_PROTOCOL *StackProtocol;
IIO_SOCKET_DATA *SocketData;
StackProtocol = NULL;
Status = IioInitGetStackProtocol (&StackProtocol);
if (EFI_ERROR (Status)) {
return Status;
}
if (!byte_DD59) {
SocketData = (IIO_SOCKET_DATA *)StackProtocol;
if (*(UINT8 *)((UINT8 *)SocketData + 4855)) {
IioIoatInit ();
byte_DD59 = TRUE;
}
}
return EFI_SUCCESS;
}
//
// ---------------------------------------------------------------------------
// PCIe port configuration
// ---------------------------------------------------------------------------
/**
Enable/disable PCIe hot-plug and link configuration for active IIO ports.
Iterates over all 4 IIO sockets and configures the active PCIe ports,
setting up hot-plug support and link training parameters.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
IioInitPciePortConfig (
VOID
)
{
EFI_STATUS Status;
IIO_STACK_PROTOCOL *StackProtocol;
IIO_SOCKET_DATA *SocketData;
UINT8 SocketIndex;
UINT8 PortIndex;
UINT16 HotPlugReg;
UINT16 LinkStatusReg;
UINT16 LinkCtrl2Reg;
UINT16 NewHotPlugReg;
UINT16 NewLinkStatusReg;
Status = IioInitGetStackProtocol (&StackProtocol);
if (EFI_ERROR (Status)) {
return Status;
}
SocketData = (IIO_SOCKET_DATA *)StackProtocol;
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
if (*(UINT8 *)((UINT8 *)&SocketData->SocketIndexArray[SocketIndex])) {
//
// Read current hot-plug and link status registers
//
HotPlugReg = StackProtocol->Read (SocketIndex, 0, PCIE_HP_REG);
LinkStatusReg = StackProtocol->Read (SocketIndex, 0, PCIE_LINK_STATUS_REG);
if (SocketData->PlatformLateInit && !SocketData->S3DataActive) {
//
// Enable hot-plug detect on this port
//
NewHotPlugReg = HotPlugReg | 1;
for (PortIndex = 0; PortIndex < 6; PortIndex++) {
if ((1 << PortIndex) & SocketData->PortActive[SocketIndex]) {
//
// Enable slot capability
//
StackProtocol->Write (SocketIndex, PortIndex, PCIE_SLOT_CAP_REG,
StackProtocol->Read (SocketIndex, PortIndex, PCIE_SLOT_CAP_REG) | 1);
}
}
}
//
// Write back link control registers
//
NewLinkStatusReg = LinkStatusReg;
IioPcieWriteConfig (2, (UINT64)StackProtocol->Read (SocketIndex, 0, PCIE_LINK_STATUS_REG),
NULL, &NewLinkStatusReg);
IioPcieWriteConfig (2, (UINT64)StackProtocol->Read (SocketIndex, 0, PCIE_HP_REG),
NULL, &NewHotPlugReg);
}
}
return EFI_SUCCESS;
}
/**
ExitBootServices notification callback.
Called when the OS loader is about to take control. Performs late secure
platform actions (TXT) and disables IIO stacks.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
IioInitExitBootServices (
VOID
)
{
EFI_STATUS Status;
IIO_STACK_PROTOCOL *StackProtocol;
IIO_SOCKET_DATA *SocketData;
UINT8 SocketIndex;
Status = IioInitGetStackProtocol (&StackProtocol);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG ((DEBUG_INFO, "\nOnExitBootServices..\n"));
SocketData = (IIO_SOCKET_DATA *)StackProtocol;
if (SocketData->PlatformLateInit) {
if (!SocketData->S3DataActive) {
DEBUG ((DEBUG_INFO, "IioInit Late Secure the Platform (TXT)..\n"));
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
if (SocketData->SocketIndexArray[SocketIndex]) {
LinkStatusReg = StackProtocol->Read (SocketIndex, 0, PCIE_LINK_STATUS_REG);
StackProtocol->Write (SocketIndex, 0, PCIE_LINK_STATUS_REG,
LinkStatusReg | 1);
}
}
}
}
return EFI_SUCCESS;
}
/**
Check if VGA is present on a given IIO socket and port.
@param[in] SocketIndex IIO socket index.
@param[out] VendorId Buffer for 16-bit vendor ID.
@param[out] DeviceId Buffer for 16-bit device ID.
@param[out] RevisionId Buffer for 8-bit revision ID.
@param[out] IsVga Boolean indicating if a VGA device was found.
@return EFI_SUCCESS Port state determined.
**/
EFI_STATUS
EFIAPI
IioInitCheckVgaPresent (
IN UINT8 SocketIndex,
OUT UINT16 *VendorId,
OUT BOOLEAN *IsVga,
OUT UINT8 *RevisionInfo
)
{
EFI_STATUS Status;
IIO_STACK_PROTOCOL *StackProtocol;
IIO_SOCKET_DATA *SocketData;
UINT8 StackId;
UINT16 VendorReg;
UINT16 ClassReg;
UINT16 SubClassReg;
Status = IioInitGetStackProtocol (&StackProtocol);
if (EFI_ERROR (Status)) {
return Status;
}
SocketData = (IIO_SOCKET_DATA *)StackProtocol;
IioPcieGetStackInfo (SocketData, SocketIndex, &StackId, VendorId,
(UINT8 *)VendorId + 2, (UINT8 *)VendorId + 3);
if (SocketIndex == (UINT8)-1 && StackId == 0xFF) {
return EFI_NOT_FOUND;
}
if (StackId >= 0x15) {
return EFI_INVALID_PARAMETER;
}
*VendorId = StackProtocol->Read (SocketIndex, StackId, PCIE_VENDOR_ID_REG);
VendorId[1] = StackProtocol->Read (SocketIndex, StackId, PCIE_DEVICE_ID_REG);
*RevisionInfo = (UINT8)(StackProtocol->Read (SocketIndex, StackId, PCIE_REVISION_REG) >> 8);
//
// Extract sub-class from class code register
//
ClassReg = StackProtocol->Read (SocketIndex, StackId, PCIE_CLASS_REG);
SubClassReg = StackProtocol->Read (SocketIndex, StackId, PCIE_SUBCLASS_REG);
*IsVga = ((ClassReg & 0xFF) == PCI_CLASS_DISPLAY &&
(SubClassReg & 0xFF) == PCI_SUBCLASS_VGA);
return EFI_SUCCESS;
}
/**
ReadyToBoot notification callback.
Called before invoking the boot option. Programs IOAPIC, initializes IOAT
engines, and performs NTB late initialization for all active IIO sockets.
@param[in] Event The ReadyToBoot event.
@param[in] Context Not used.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
IioInitReadyToBoot (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
IIO_STACK_PROTOCOL *StackProtocol;
IIO_SOCKET_DATA *SocketData;
UINT8 SocketIndex;
UINT8 PortIndex;
UINT16 IoApicReg;
Status = IioInitGetStackProtocol (&StackProtocol);
if (EFI_ERROR (Status)) {
return Status;
}
SocketData = (IIO_SOCKET_DATA *)StackProtocol;
Status = gBS->CloseEvent (Event);
for (SocketIndex = 0; SocketIndex < 4; SocketIndex++) {
if (SocketData->SocketIndexArray[SocketIndex]) {
DEBUG ((DEBUG_INFO,
"Calling IioIoatReadyToBootEvent IioIndex: %d\n",
SocketIndex));
//
// Configure IOAPIC redirection for all 8 ports
//
for (PortIndex = 0; PortIndex < 8; PortIndex++) {
IoApicReg = StackProtocol->Read (SocketIndex, 0,
IOAPIC_REDIR_TABLE | (PortIndex << 16));
if (*(UINT8 *)(PortIndex + 8 * SocketIndex + (UINT8 *)SocketData + 4188) ||
SocketData->IoatCapable) {
IoApicReg |= 4;
} else {
IoApicReg &= ~4;
}
StackProtocol->Write (SocketIndex, 0,
IOAPIC_REDIR_TABLE | (PortIndex << 16), IoApicReg);
}
DEBUG ((DEBUG_INFO,
"Calling IioIoApicInitBootEvent IioReadyToBoot IioIndex: %d\n",
SocketIndex));
IioIoApicInitBootEvent (SocketData, SocketIndex, 11);
DEBUG ((DEBUG_INFO,
"Calling NtbLateInit IioReadyToBoot IioIndex: %d\n",
SocketIndex));
IioNtbLateInit (SocketData, SocketIndex, 11);
}
}
return EFI_SUCCESS;
}
/**
IIO socket initialization - late secure platform (TXT) handler.
Called during exit boot services to apply late secure platform
configurations when TXT is enabled.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
IioInitLateSecurePlatform (
VOID
)
{
EFI_STATUS Status;
IIO_STACK_PROTOCOL *StackProtocol;
IIO_SOCKET_DATA *SocketData;
Status = IioInitGetStackProtocol (&StackProtocol);
if (EFI_ERROR (Status)) {
return Status;
}
SocketData = (IIO_SOCKET_DATA *)StackProtocol;
if (SocketData->InitComplete != 1) {
StackProtocol->WriteWidth ((UINT8)-17, 0, 255);
IioLateSecurePlatformInit (SocketData);
SocketData->InitComplete = 1;
}
return EFI_SUCCESS;
}
//
// ---------------------------------------------------------------------------
// PCIe MMCFG / Address library routines (from PcieCommonInitLib / PcieAddressLib)
// ---------------------------------------------------------------------------
/**
IO-port read double-word (32-bit).
@param[in] Port IO port to read from (must be 4-byte aligned).
@return Value read from the port.
**/
UINT32
EFIAPI
IioInitIoPortRead32 (
IN UINT16 Port
)
{
ASSERT ((Port & 3) == 0);
return IoRead32 (Port);
}
/**
Read unaligned 64-bit value from buffer.
@param[in] Buffer Pointer to possibly-unaligned buffer.
@return 64-bit value at Buffer.
**/
UINT64
EFIAPI
IioInitReadUnaligned64 (
IN VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(UINT64 *)Buffer;
}
/**
64-bit right shift.
@param[in] Value Value to shift.
@param[in] Count Number of bits (must be < 64).
@return Value >> Count.
**/
UINT64
EFIAPI
IioInitRShiftU64 (
IN UINT64 Value,
IN UINTN Count
)
{
ASSERT (Count < 64);
return Value >> (UINT8)Count;
}
/**
64-bit left shift.
@param[in] Value Value to shift.
@param[in] Count Number of bits (must be < 64).
@return Value << Count.
**/
UINT64
EFIAPI
IioInitLShiftU64 (
IN UINT64 Value,
IN UINTN Count
)
{
ASSERT (Count < 64);
return Value << (UINT8)Count;
}
/**
Zero-fill a memory buffer (wrapper with assertion).
@param[in] Buffer Pointer to buffer.
@param[in] Length Number of bytes.
@return Pointer to Buffer.
**/
VOID *
EFIAPI
IioInitSafeZeroMem (
IN VOID *Buffer,
IN UINTN Length
)
{
if (Length == 0) {
return Buffer;
}
if (Length - 1 > ~(UINTN)Buffer) {
ASSERT (
(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)Buffer)
);
}
return IioInitZeroMem (Buffer, Length, 0);
}
/**
Copy memory buffer (wrapper with assertions).
@param[in] Destination Destination buffer.
@param[in] Source Source buffer.
@param[in] Length Number of bytes.
@return Pointer to Destination.
**/
VOID *
EFIAPI
IioInitSafeCopyMem (
IN VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
if (Length == 0) {
return Destination;
}
ASSERT (
(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)Destination)
);
ASSERT (
(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)Source)
);
if (Destination == Source) {
return Destination;
}
return IioInitCopyMem (Destination, Source, Length);
}
//
// ---------------------------------------------------------------------------
// PCIe address translation (from PcieAddressLib / CpRcPkg)
// ---------------------------------------------------------------------------
/**
Validate parameters for a DMA/PCIe memory access.
Checks alignment, access width, and size constraints.
@param[in] AccessWidth Encoded access width (0..3) mapped via byte_A7C0.
@param[in] Address Base address.
@param[in] Count Number of elements.
@param[in] ElementSize Element size override (0 = auto).
@param[in] Buffer Destination buffer.
@return EFI_SUCCESS Parameters valid.
@return EFI_INVALID_PARAMETER Invalid width or null buffer.
@return EFI_BAD_BUFFER_SIZE Alignment or overflow violation.
**/
EFI_STATUS
EFIAPI
IioInitValidatePcieAccess (
IN UINT8 AccessWidth,
IN UINTN Count,
IN UINT64 Address,
IN UINTN ElementSize,
IN VOID *Buffer
)
{
UINT8 WidthIndex;
UINTN ElementAlignment;
UINTN MaxElements;
if (Buffer == NULL || AccessWidth >= 0xC) {
return EFI_INVALID_PARAMETER;
}
WidthIndex = AccessWidth & 3;
//
// Force element size to 1 for access widths 4..7
//
if (AccessWidth - 4 <= 3) {
ElementSize = 1;
}
//
// Check alignment
//
ElementAlignment = byte_A7C0[WidthIndex];
if ((Address & (ElementAlignment - 1)) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
//
// Check address range
//
if (Count > 0) {
MaxElements = RShiftU64 (0xFFFFFFFFFFFFFFFF, WidthIndex);
if (Count < ElementSize || Address > LShiftU64 (MaxElements - Count + 1, WidthIndex)) {
return EFI_BAD_BUFFER_SIZE;
}
}
//
// Check buffer alignment
//
{
UINTN Stride = byte_A7C0[WidthIndex];
if (Stride > 8) {
Stride = 8;
}
if ((Buffer & (Stride - 1)) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
}
return EFI_SUCCESS;
}
/**
Read memory from MMIO space into a buffer.
Validates parameters, then reads Count elements of ElementSize from Address
into Buffer using the appropriate access width.
@param[in] BufferDesc Buffer descriptor (contains info, width, count).
@param[out] Buffer Destination buffer.
@return EFI_SUCCESS Read successful.
**/
EFI_STATUS
EFIAPI
IioInitMmioRead (
IN VOID *BufferDesc,
OUT VOID *Buffer
)
{
UINTN Count;
UINT64 CurrentAddress;
EFI_STATUS Status;
UINT8 WidthIndex;
UINTN ElementStride;
UINTN BufferStride;
Count = *(UINT16 *)((UINT8 *)BufferDesc + 6);
PcieGetElementInfo (0, 0, BufferDesc, &CurrentAddress);
WidthIndex = (*(UINT32 *)((UINT8 *)BufferDesc + 8) >> 8) & 0xF;
Status = IioInitValidatePcieAccess (WidthIndex, CurrentAddress, Count, (UINTN)Buffer);
if (EFI_ERROR (Status)) {
return Status;
}
if (Count > 0) {
ElementStride = byte_A7C0[WidthIndex];
BufferStride = byte_A7C0[WidthIndex + 16];
do {
MmioReadBuffer (WidthIndex & 3, CurrentAddress, Buffer);
CurrentAddress += ElementStride;
Buffer = (VOID *)((UINT8 *)Buffer + BufferStride);
} while (--Count);
}
return EFI_SUCCESS;
}
/**
Write memory from a buffer into MMIO space.
Validates parameters, then writes Count elements of ElementSize from Buffer
into Address.
@param[in] BufferDesc Buffer descriptor (contains info, width, count).
@param[in] Buffer Source buffer.
@return EFI_SUCCESS Write successful.
**/
EFI_STATUS
EFIAPI
IioInitMmioWrite (
IN VOID *BufferDesc,
IN VOID *Buffer
)
{
UINTN Count;
UINT64 CurrentAddress;
EFI_STATUS Status;
UINT8 WidthIndex;
UINTN ElementStride;
UINTN BufferStride;
UINT32 DescFlags;
Count = *(UINT16 *)((UINT8 *)BufferDesc + 6);
PcieGetElementInfo (0, 0, BufferDesc, &CurrentAddress);
WidthIndex = (*(UINT32 *)((UINT8 *)BufferDesc + 8) >> 8) & 0xF;
Status = IioInitValidatePcieAccess (WidthIndex, CurrentAddress, Count, (UINTN)Buffer);
if (EFI_ERROR (Status)) {
return Status;
}
if (Count > 0) {
ElementStride = byte_A7C0[WidthIndex];
BufferStride = byte_A7C0[WidthIndex + 16];
do {
MmioWriteBuffer (WidthIndex & 3, CurrentAddress, Buffer);
DescFlags = *(UINT32 *)((UINT8 *)BufferDesc + 8);
if ((DescFlags & 0x2000) != 0) {
IioPcieWriteConfig (WidthIndex & 3, CurrentAddress, NULL, Buffer);
}
CurrentAddress += ElementStride;
Buffer = (VOID *)((UINT8 *)Buffer + BufferStride);
} while (--Count);
}
return EFI_SUCCESS;
}
/**
MMIO read helper with element size decode.
@param[in] BufferDesc Buffer descriptor.
@param[in] Address MMIO address.
@param[out] Buffer Destination buffer.
@param[in] ElementSize Logical element size.
@return 0 on success.
**/
UINTN
EFIAPI
IioInitPcieMmioRead (
IN VOID *BufferDesc,
IN UINT64 Address,
OUT VOID *Buffer,
IN UINT8 ElementSize
)
{
UINT8 WidthIndex;
PCMCI_STATE PciState;
INT32 AccessWidthEncoded = 0x08040001;
PciState = PcieGetState (BufferDesc);
WidthIndex = *((UINT8 *)&AccessWidthEncoded + ((*(UINT32 *)((UINT8 *)BufferDesc + 8) >> 8) & 0xF));
PcieSetupAccess (PciState, Address, Buffer, (UINT8)WidthIndex);
PcieCleanupAccess (BufferDesc, PciState);
return 0;
}
/**
Initialize the PCIe MMCFG table from given header.
@param[in] TableHeader MCFG table descriptor pointer.
@param[in] SegmentCount Number of PCI segments.
@return EFI_SUCCESS Table initialized.
**/
EFI_STATUS
EFIAPI
IioInitInitMmcfgTable (
IN MCFG_TABLE_HEADER *TableHeader,
IN UINT32 SegmentCount
)
{
UINT32 TotalSize;
MMCFG_TABLE_DESCRIPTOR *MmcfgTable;
PCD_PROTOCOL *PcdProtocol;
TotalSize = 16 * SegmentCount + 16;
PcdProtocol = (PCD_PROTOCOL *)sub_4500 (6);
//
// Validate that MMCFG table fits in PCD-allocated space
//
if (sub_CF0 () && TotalSize >= (UINTN)PcdProtocol->GetSize (6)) {
ASSERT (
"MmcfgTableSize < LibPcdGetSize(6U)"
);
}
MmcfgTable = (MMCFG_TABLE_DESCRIPTOR *)PcdProtocol->GetSize (6);
IioInitCopyMem (MmcfgTable, TableHeader, (UINTN)PcdProtocol->GetSize (6));
MmcfgTable->TableSize = TotalSize;
//
// If base address not provided, use PCD default
//
if (!TableHeader->OemId && !TableHeader->Reserved) {
MmcfgTable->BaseAddresses[0] = sub_44E4 (5);
}
return EFI_SUCCESS;
}
/**
Resolve the MMCFG base address for a given PCIe address.
@param[in] Address Pointer to PCIe address structure.
@return MMCFG base address for the segment.
**/
UINT64
EFIAPI
IioInitGetMmcfgBaseAddress (
IN PCIE_ADDRESS *Address
)
{
MMCFG_TABLE_DESCRIPTOR *MmcfgTable;
UINT64 TableHandle;
TableHandle = 0;
if (Address->Bus != 0) {
TableHandle = Address->Bus;
} else {
TableHandle = (UINT64)sub_4500 (6);
if (*(UINT32 *)(TableHandle + 4) == 0) {
IioInitInitMmcfgTable (&dword_DCA8, 8);
}
}
MmcfgTable = (MMCFG_TABLE_DESCRIPTOR *)TableHandle;
if (sub_CF0 () && Address->Segment >= MmcfgTable->SegmentMax) {
ASSERT (
"Address->Pcie.Seg < MmcfgTable->Header.SegMax"
);
}
if (sub_CF0 () && (MmcfgTable->ValidSegMap & (1 << Address->Segment)) == 0) {
ASSERT (
"(1<<Address->Pcie.Seg) & MmcfgTable->Header.ValidSegMap"
);
}
return MmcfgTable->BaseAddresses[Address->Segment];
}
//
// ---------------------------------------------------------------------------
// Module Entry Point
// ---------------------------------------------------------------------------
/**
IIO Initialization driver entry point.
Calls the main initialization routine and returns its status.
@param[in] ImageHandle EFI image handle.
@param[in] SystemTable Pointer to EFI system table.
@return EFI_SUCCESS IIO initialization completed.
@return EFI_UNSUPPORTED Platform or configuration unsupported.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = IioInitCore (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
IioInitErrorHandler ();
}
return Status;
}