/**
* CpuIoDxe.c -- EFI CPU I/O Protocol DXE Driver
*
* This driver implements and produces the EFI_CPU_IO_PROTOCOL for a UEFI
* system. It provides validated access to:
* - Memory-mapped I/O (MMIO) space
* - Port I/O space
*
* This is the older (Framework) CPU I/O protocol from the
* IntelFrameworkModulePkg/Universal/CpuIoDxe/ directory. Unlike the
* newer EFI_CPU_IO2_PROTOCOL (in UefiCpuPkg), the access function signatures
* do NOT include a 'This' pointer as the first parameter.
*
* Source: e:\hs\IntelFrameworkModulePkg\Universal\CpuIoDxe\CpuIo.c
* (EDK2 Intel Framework module)
*/
#include "CpuIoDxe.h"
/* =========================================================================
* Local Data (.rdata section)
* ========================================================================= */
/**
* I/O access width and stride table at 0x20B8.
*
* Maps EFI_CPU_IO_PROTOCOL_WIDTH index values to byte widths and strides.
* Only indices 0-3 (masked by Width & 3) are used for non-FIFO access.
*
* When Width > 3 (FIFO/Fill modes), the module accesses the FIFO variants
* directly (Width & 3 determines the element size, and the access width is
* hardcoded as 1 via AccessSize = 1 in CpuIoCheckParameter).
*
* Offsets:
* [0..3] Access width in bytes (1, 2, 4, 8)
* [4..7] Reserved (zero)
* [8..11] Access width in bytes (duplicate for alternate stride calc)
* [16..19] Destination stride in bytes for Read operations
* [20..23] Destination stride in bytes for Write operations
* [24..31] Reserved (zero)
*/
const UINT8 mIoWidthTable[32] = {
1, 2, 4, 8, /* [0..3] Access width per Width index */
0, 0, 0, 0, /* [4..7] Reserved */
1, 2, 4, 8, /* [8..11] Access width (duplicate) */
0, 0, 0, 0, /* [12..15] Reserved */
1, 2, 4, 8, /* [16..19] Read buffer stride */
1, 2, 4, 8, /* [20..23] Write buffer stride */
0, 0, 0, 0, /* [24..27] Reserved */
0, 0, 0, 0 /* [28..31] Reserved */
};
/* =========================================================================
* Global Variables (.data section)
* ========================================================================= */
/* Address 0x3068 */ EFI_HANDLE gImageHandle = NULL;
/* Address 0x3058 */ EFI_SYSTEM_TABLE *gST = NULL;
/* Address 0x3060 */ EFI_BOOT_SERVICES *gBS = NULL;
/* Address 0x3070 */ EFI_RUNTIME_SERVICES *gRT = NULL;
/* Address 0x3090 */ VOID *gBootServicesCache = NULL; /* Cached gBS for event callbacks */
/* Address 0x3078 */ VOID *gRuntimeServicesCache = NULL;
/* Address 0x3098 */ VOID *gDebugProtocol = NULL;
/* Address 0x30A0 */ VOID *mHobList = NULL;
/* Function table for protocol interface (at unk_3030/off_3030) */
/* =========================================================================
* GUID instances (.rdata section)
* ========================================================================= */
static const EFI_GUID mEfiCpuIoProtocolGuid =
EFI_CPU_IO_PROTOCOL_GUID; /* 0x3020 */
static const EFI_GUID mStatusCodeRuntimeProtocolGuid =
EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID; /* 0x3000 */
static const EFI_GUID mEfiHobListGuid =
EFI_HOB_LIST_GUID; /* 0x3010, 0x3018 */
/* =========================================================================
* Intrinsic Port I/O Functions
*
* These are MSVC compiler intrinsics that generate x86 IN/OUT instructions.
* ========================================================================= */
#pragma intrinsic(__inbyte)
#pragma intrinsic(__inword)
#pragma intrinsic(__indword)
#pragma intrinsic(__outbyte)
#pragma intrinsic(__outword)
#pragma intrinsic(__outdword)
/* =========================================================================
* 64-bit Shift Operations
* ========================================================================= */
/**
* 64-bit left shift with count validation.
* Address: 0x179C
*
* Asserts if Count >= 64 (from BaseLib\LShiftU64.c line 39).
*
* @param[in] Value Value to shift.
* @param[in] Count Shift count (must be < 64).
*
* @return Value << Count.
*/
UINT64 EFIAPI LShiftU64(IN UINT64 Value, IN UINTN Count)
{
ASSERT(Count < 64);
return Value << Count;
}
/**
* 64-bit right shift with count validation.
* Address: 0x17E0
*
* Asserts if Count >= 64 (from BaseLib\RShiftU64.c line 39).
*
* @param[in] Value Value to shift.
* @param[in] Count Shift count (must be < 64).
*
* @return Value >> Count.
*/
UINT64 EFIAPI RShiftU64(IN UINT64 Value, IN UINTN Count)
{
ASSERT(Count < 64);
return Value >> Count;
}
/* =========================================================================
* Unaligned Memory Access
* ========================================================================= */
/**
* Reads a UINT64 from a pointer with NULL assertion.
* Address: 0x1AE8
*
* Assertion from MdePkg\Library\BaseLib\Unaligned.c line 192:
* "Buffer != ((void *) 0)"
*
* @param[in] Buffer Pointer to read from (must not be NULL).
*
* @return The 64-bit value at Buffer.
*/
UINT64 EFIAPI ReadUnaligned64(IN CONST VOID *Buffer)
{
ASSERT(Buffer != NULL);
return *(const UINT64 *)Buffer;
}
/* =========================================================================
* Protocol Helper: GetDebugProtocol
* ========================================================================= */
/**
* Lazily resolves the EFI_STATUS_CODE_RUNTIME_PROTOCOL.
* Address: 0x1824
*
* Uses a RaiseTPL/RestoreTPL canary to verify UEFI boot services are
* functional:
* 1. gBS->RaiseTPL(TPL_HIGH_LEVEL) -> returns OldTpl
* 2. gBS->RestoreTPL(OldTpl)
* 3. If (OldTpl <= TPL_NOTIFY (0x10)), boot services unavailable, return NULL
* 4. gBS->LocateProtocol(&gEfiStatusCodeRuntimeProtocolGuid, NULL, &gDebugProtocol)
*
* @return Cached pointer to the StatusCodeRuntimeProtocol, or NULL.
*/
VOID *GetDebugProtocol(VOID)
{
EFI_STATUS Status;
UINTN OldTpl;
VOID *Protocol;
/* Check cache first */
if (gDebugProtocol != NULL) {
return gDebugProtocol;
}
/*
* Validate boot services via RaiseTPL/RestoreTPL.
* If boot services are functional, RaiseTPL returns the previous TPL,
* which should be a value > TPL_NOTIFY (0x10) on a properly initialized
* system. If the return is <= 0x10, something is wrong.
*/
OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL); /* TPL_HIGH_LEVEL = 31 */
gBS->RestoreTPL(OldTpl);
if (OldTpl <= TPL_NOTIFY) { /* TPL_NOTIFY = 16 = 0x10 */
/* Boot services appear unavailable -- runtime phase */
return NULL;
}
/*
* Resolve the StatusCodeRuntimeProtocol.
*/
Status = gBS->LocateProtocol(
(EFI_GUID *)&mStatusCodeRuntimeProtocolGuid,
NULL,
&Protocol
);
if (!EFI_ERROR(Status)) {
gDebugProtocol = Protocol;
} else {
gDebugProtocol = NULL;
}
return gDebugProtocol;
}
/* =========================================================================
* Debug Output Support
* ========================================================================= */
/**
* Debug print through StatusCodeRuntimeProtocol.
* Address: 0x18AC
*
* Before calling the protocol, reads the CMOS debug level register to
* determine the current verbosity mask. Falls back to MMIO 0xFDAF0490
* if CMOS level is 0.
*
* CMOS RTC register 0x4B:
* out 0x70, 0x4B -- select register 0x4B
* in 0x71 -- read debug level
*
* The register index is written with NMI bit preserved:
* in 0x70 -- read current CMOS index (includes NMI bit 7)
* and al, 0x80 -- preserve only NMI bit
* or al, 0x4B -- set register to 0x4B
* out 0x70, al -- select
* in 0x71 -- read value
*
* @param[in] ErrorLevel Severity/class mask.
* @param[in] Format Format string.
* @param[in] ... Variable arguments.
*/
VOID EFIAPI DebugPrint(
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VOID *Protocol;
UINT8 CmosDebugLevel;
UINT32 DebugLevelMask;
VA_LIST VaList;
Protocol = GetDebugProtocol();
if (Protocol == NULL) {
return;
}
/*
* Read CMOS debug level register 0x4B.
* Preserve the NMI mask bit (bit 7) of CMOS address port 0x70.
*/
__outbyte(0x70, (__inbyte(0x70) & 0x80) | 0x4B);
CmosDebugLevel = __inbyte(0x71);
/*
* Determine the effective debug level mask.
*/
DebugLevelMask = 4; /* Default: EFI_ERROR_CODE | EFI_ERROR_MINOR */
if (CmosDebugLevel <= 3) {
/* Standard EDK2 debug level */
if (CmosDebugLevel == 1) {
DebugLevelMask = 0x80000004; /* EFI_D_ERROR */
} else if (CmosDebugLevel != 0) {
DebugLevelMask = 0x80000006; /* EFI_D_WARN */
}
/* CmosDebugLevel == 0: use default (4) */
} else {
/* Non-standard level -- use raw value */
DebugLevelMask = CmosDebugLevel;
if (CmosDebugLevel == 0) {
/* Level 0: fallback to MMIO-based detection at 0xFDAF0490 */
UINT8 BoardDebugReg = *(volatile UINT8 *)0xFDAF0490;
if (BoardDebugReg & 2) {
DebugLevelMask = 0x80000000 | ((BoardDebugReg & 1) ? 0xC : 0x4);
} else {
DebugLevelMask = 4;
}
}
}
/*
* If the error level matches, call the protocol's report function.
*/
if (DebugLevelMask & ErrorLevel) {
VA_START(VaList, Format);
/*
* Call the report function at protocol interface offset +0x00.
* Signature: ReportStatusCode(ErrorLevel, Format, VaList)
*/
((EFI_STATUS (EFIAPI *)(UINTN, CONST CHAR8 *, VA_LIST))
Protocol)(ErrorLevel, Format, VaList);
VA_END(VaList);
}
}
/**
* Debug assert handler.
* Address: 0x192C
*
* Resolves the StatusCodeRuntimeProtocol (if not already cached) and
* calls the assertion handler at protocol interface offset +0x08.
*
* @param[in] FileName Source file name string.
* @param[in] LineNumber Line number of the assertion.
* @param[in] Description Assertion description string.
*/
VOID EFIAPI DebugAssert(
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
VOID *Protocol;
Protocol = GetDebugProtocol();
if (Protocol != NULL) {
/*
* Call the assertion handler at interface offset +0x08.
* The StatusCodeRuntimeProtocol interface has:
* +0x00: ReportStatusCode (print/format)
* +0x08: ReportStatusCode (assert handler variant)
*/
((EFI_STATUS (EFIAPI *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))
((UINT8 *)Protocol + 8))(FileName, LineNumber, Description);
}
}
/* =========================================================================
* HOB List Resolution
* ========================================================================= */
/**
* Compares two GUID pointers by reading two 8-byte halves.
* Address: 0x1A78
*
* Avoids a full CompareGuid() call by reading the first 8 bytes and
* second 8 bytes of each GUID as UINT64 values, then comparing both pairs.
*
* @param[in] Guid1 Pointer to the reference GUID.
* @param[in] Guid2 Pointer to the candidate GUID.
*
* @return TRUE if both 64-bit halves match, FALSE otherwise.
*/
BOOLEAN CompareGuidByUnaligned64(
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
return (ReadUnaligned64(Guid1) == ReadUnaligned64(Guid2)) &&
(ReadUnaligned64((UINT8 *)Guid1 + 8) == ReadUnaligned64((UINT8 *)Guid2 + 8));
}
/**
* Locates the HOB list by scanning the system configuration table.
* Address: 0x19A0
*
* Iterates over SystemTable->ConfigurationTable[] looking for
* gEfiHobListGuid. Caches the result in mHobList.
*
* The configuration table is an array of:
* typedef struct {
* EFI_GUID VendorGuid; /* 16 bytes */
* VOID *VendorTable; /* 8 bytes */
* } EFI_CONFIGURATION_TABLE;
* Total per entry: 24 (0x18) bytes.
*
* If the GUID is not found, triggers ASSERT_EFI_ERROR(EFI_NOT_FOUND).
*
* Source: e:\hs\MdePkg\Library\DxeHobLib\HobLib.c lines 54-55
*/
VOID GetHobList(VOID)
{
UINTN Index;
EFI_STATUS Status;
EFI_GUID **ConfigEntry;
if (mHobList != NULL) {
return;
}
mHobList = NULL;
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
ConfigEntry = (EFI_GUID **)((UINT8 *)gST->ConfigurationTable +
Index * sizeof(EFI_CONFIGURATION_TABLE));
if (CompareGuidByUnaligned64(
(EFI_GUID *)&mEfiHobListGuid,
ConfigEntry[0])) /* ConfigEntry[0] = VendorGuid field */
{
mHobList = *(VOID **)((UINT8 *)ConfigEntry + sizeof(EFI_GUID));
return;
}
}
/*
* HOB list GUID not found in configuration table.
*/
ASSERT_EFI_ERROR(EFI_NOT_FOUND);
ASSERT(mHobList != NULL);
}
/* =========================================================================
* Event Notification Callbacks
* ========================================================================= */
/**
* Notification callback for ExitBootServices event.
* Address: 0x196C
*
* Clears the cached boot services pointer to NULL when boot services
* are exiting, preventing further gBS usage after ExitBootServices().
*
* Registered via gBS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_NOTIFY, OnExitBootServices).
*/
VOID EFIAPI OnExitBootServices(VOID)
{
gBootServicesCache = NULL;
}
/**
* Notification callback for virtual address change event.
* Address: 0x1978
*
* Converts the debug protocol pointer for runtime use.
* Calls gRT->ConvertPointer() to translate the cached pointer.
*
* Registered via gBS->CreateEventEx(EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
* OnVirtualAddressChange, &gEfiEventVirtualAddressChangeGuid).
*/
VOID EFIAPI OnVirtualAddressChange(VOID)
{
if (gDebugProtocol != NULL) {
gRT->ConvertPointer(0, &gDebugProtocol);
}
}
/* =========================================================================
* Parameter Validation
* ========================================================================= */
/**
* Validates I/O access parameters.
* Address: 0x12A0
*
* Checks:
* 1. Buffer != NULL (return EFI_INVALID_PARAMETER if NULL)
* 2. Width < 12 (EFI_CPU_IO_PROTOCOL_WIDTH_MAXIMUM)
* 3. For non-FIFO/Fill modes (Width <= 3), AccessType must be valid:
* - If AccessType == IO and Width == 3 (UINT64):
* EFI_INVALID_PARAMETER (64-bit port I/O not supported)
* 4. Address alignment matches Width requirement
* 5. Address + Count*Width doesn't overflow architectural limit
* 6. Buffer alignment matches access width
*
* The Width value is masked as (Width & 3) when Width > 3 (FIFO/Fill
* modes use the same alignment rules as the base width).
*
* @param[in] AccessType EFI_CPU_IO_PROTOCOL_IO (0) or _MEM (1).
* @param[in] Width One of EFI_CPU_IO_PROTOCOL_WIDTH (0-11).
* @param[in] Address Starting I/O or MMIO address.
* @param[in] Count Number of elements to access.
* @param[in] Buffer Pointer to the data buffer.
*
* @return EFI_SUCCESS Parameters are valid.
* @return EFI_INVALID_PARAMETER Width >= 12, or invalid combo.
* @return EFI_UNSUPPORTED Address alignment mismatch.
* @return EFI_BAD_BUFFER_SIZE Address range overflow.
*/
EFI_STATUS
CpuIoCheckParameter(
UINT8 AccessType,
UINT32 Width,
UINT64 Address,
UINT64 Count,
VOID *Buffer
)
{
UINT64 MaxAddress;
UINT64 AccessSize;
UINT64 Alignment;
/*
* 1. Validate Buffer and Width.
*/
if (Buffer == NULL || Width >= EfiCpuIoWidthMaximum) {
return EFI_INVALID_PARAMETER;
}
/*
* 2. For FIFO/Fill modes, the access width is effectively 1 element
* of the base type (AccessSize = 1 for bounds checking).
*/
if (Width > 3) {
AccessSize = 1; /* FIFO modes access one element at a time */
Width = Width & 3;
} else {
AccessSize = Count;
}
/*
* 3. For port IO (AccessType == 0), 64-bit access (Width == 3) is
* not supported on x64 port I/O.
*/
if (AccessType == 0 && Width == 3) {
return EFI_INVALID_PARAMETER;
}
/*
* 4. Check address alignment.
* Width 0 (byte) -> alignment 1, passes for any address.
* Width 1 (word) -> alignment 2 (must be even).
* Width 2 (dword) -> alignment 4.
* Width 3 (qword) -> alignment 8.
*/
Alignment = mIoWidthTable[Width] - 1;
if (Alignment & Address) {
return EFI_UNSUPPORTED;
}
/*
* 5. Check that the address range fits within the architectural limit.
* For I/O space: limit is 0xFFFF (16-bit port address space).
* For MMIO: limit is MAX_UINT64 (full 64-bit address space).
*/
if (AccessType == 0) {
MaxAddress = 0xFFFF;
} else {
MaxAddress = MAX_UINT64;
}
if (AccessSize > 0) {
/*
* Compute whether Address + (AccessSize - 1) * elementWidth fits.
* Uses shift operations to avoid overflow.
*/
if ((Count > 1) && (Width > 0)) {
if (RShiftU64(MaxAddress - Address, Width) < (Count - 1)) {
return EFI_UNSUPPORTED;
}
} else {
if (Address > MaxAddress) {
return EFI_UNSUPPORTED;
}
}
}
/*
* 6. Check buffer alignment.
*/
if (Buffer != NULL) {
UINT64 BufAlignment = mIoWidthTable[Width] - 1;
if (BufAlignment & (UINT64)Buffer) {
return EFI_UNSUPPORTED;
}
}
return EFI_SUCCESS;
}
/* =========================================================================
* CpuMemoryServiceRead -- MMIO Read
* ========================================================================= */
/**
* MMIO Read dispatcher.
* Address: 0x13AC
*
* Handles memory-mapped I/O reads. Parameters are first validated via
* CpuIoCheckParameter with AccessType=MEM (1), then the read is
* performed directly from the MMIO address with alignment checks.
*
* The MMIO read is a direct memory read from Address:
* Width=0: *(volatile UINT8 *)Address (with (Address & 1) assert)
* Width=1: *(volatile UINT16 *)Address
* Width=2: *(volatile UINT32 *)Address
* Width=3: *(volatile UINT64 *)Address (with (Address & 7) assert)
*
* For Count > 1, iterates Count times, advancing the address by the
* access width and the buffer by the stride per element.
*
* @param[in] Width Width of the access.
* @param[in] Address Starting MMIO address.
* @param[in] Count Number of elements.
* @param[out] Buffer Destination buffer.
*
* @return EFI_SUCCESS or error from CpuIoCheckParameter.
*/
EFI_STATUS EFIAPI CpuMemoryServiceRead(
IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
IN UINTN Address,
IN UINTN Count,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
UINT8 AccessWidth;
UINT8 Stride;
Status = CpuIoCheckParameter(EFI_CPU_IO_PROTOCOL_MEM, Width, Address, Count, Buffer);
if (EFI_ERROR(Status)) {
return Status;
}
AccessWidth = mIoWidthTable[Width & 3];
Stride = mIoWidthTable[(Width & 3) + 16]; /* Read stride */
if (Count == 0) {
return EFI_SUCCESS;
}
do {
UINT8 *BufPtr = (UINT8 *)Buffer;
if (AccessWidth == 1) {
*BufPtr = *(volatile UINT8 *)Address;
Buffer = (VOID *)(BufPtr + Stride);
} else if (AccessWidth == 2) {
ASSERT(((UINTN)Address & 1) == 0);
*(UINT16 *)BufPtr = *(volatile UINT16 *)Address;
Buffer = (VOID *)((UINT16 *)BufPtr + 1);
} else if (AccessWidth == 4) {
*(UINT32 *)BufPtr = *(volatile UINT32 *)Address;
Buffer = (VOID *)((UINT32 *)BufPtr + 1);
} else if (AccessWidth == 8) {
ASSERT(((UINTN)Address & 7) == 0);
*(UINT64 *)BufPtr = *(volatile UINT64 *)Address;
Buffer = (VOID *)((UINT64 *)BufPtr + 1);
}
Address += AccessWidth;
Count -= 1;
} while (Count > 0);
return EFI_SUCCESS;
}
/* =========================================================================
* CpuMemoryServiceWrite -- MMIO Write
* ========================================================================= */
/**
* MMIO Write dispatcher.
* Address: 0x14A4
*
* Handles memory-mapped I/O writes. Parameters are first validated via
* CpuIoCheckParameter with AccessType=MEM (1), then the write is
* performed directly to the MMIO address.
*
* The MMIO write is a direct memory write to Address:
* Width=0: *(volatile UINT8 *)Address = *Buffer
* Width=1: *(volatile UINT16 *)Address = *(UINT16 *)Buffer (with alignment assert)
* Width=2: *(volatile UINT32 *)Address = *(UINT32 *)Buffer
* Width=3: *(volatile UINT64 *)Address = *(UINT64 *)Buffer (with alignment assert)
*
* @param[in] Width Width of the access.
* @param[in] Address Starting MMIO address.
* @param[in] Count Number of elements.
* @param[in] Buffer Source buffer.
*
* @return EFI_SUCCESS or error from CpuIoCheckParameter.
*/
EFI_STATUS EFIAPI CpuMemoryServiceWrite(
IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
IN UINTN Address,
IN UINTN Count,
IN VOID *Buffer
)
{
EFI_STATUS Status;
UINT8 AccessWidth;
UINT8 Stride;
Status = CpuIoCheckParameter(EFI_CPU_IO_PROTOCOL_MEM, Width, Address, Count, Buffer);
if (EFI_ERROR(Status)) {
return Status;
}
AccessWidth = mIoWidthTable[Width & 3];
Stride = mIoWidthTable[(Width & 3) + 20]; /* Write stride */
if (Count == 0) {
return EFI_SUCCESS;
}
do {
UINT8 *BufPtr = (UINT8 *)Buffer;
if (AccessWidth == 1) {
*(volatile UINT8 *)Address = *BufPtr;
Buffer = (VOID *)(BufPtr + Stride);
} else if (AccessWidth == 2) {
ASSERT(((UINTN)Address & 1) == 0);
*(volatile UINT16 *)Address = *(UINT16 *)BufPtr;
Buffer = (VOID *)((UINT16 *)BufPtr + 1);
} else if (AccessWidth == 4) {
*(volatile UINT32 *)Address = *(UINT32 *)BufPtr;
Buffer = (VOID *)((UINT32 *)BufPtr + 1);
} else if (AccessWidth == 8) {
ASSERT(((UINTN)Address & 7) == 0);
*(volatile UINT64 *)Address = *(UINT64 *)BufPtr;
Buffer = (VOID *)((UINT64 *)BufPtr + 1);
}
Address += AccessWidth;
Count -= 1;
} while (Count > 0);
return EFI_SUCCESS;
}
/* =========================================================================
* CpuIoServiceRead -- Port I/O Read
* ========================================================================= */
/**
* Port I/O Read dispatcher.
* Address: 0x15A8
*
* Handles port I/O reads for EFI_CPU_IO_PROTOCOL_WIDTH values 0-3.
*
* Width=0 (byte):
* - __inbyte(Port) -> *Buffer = result
*
* Width=1 (word):
* - __inword(Port) with (Port & 1) alignment check -> *Buffer
*
* Width=2 (dword):
* - __indword(Port) with (Port & 3) alignment check -> *Buffer
*
* After validation, iterates Count times, reading each element with
* the appropriate I/O instruction. The port is advanced by the access
* width after each read. The buffer is advanced by the stride.
*
* @param[in] Width Width of the access.
* @param[in] Port Starting I/O port.
* @param[in] Count Number of elements.
* @param[out] Buffer Destination buffer.
*
* @return EFI_SUCCESS or error from CpuIoCheckParameter.
*/
EFI_STATUS EFIAPI CpuIoServiceRead(
IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
IN UINTN Port,
IN UINTN Count,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
UINT8 AccessWidth;
UINT8 Stride;
Status = CpuIoCheckParameter(EFI_CPU_IO_PROTOCOL_IO, Width, Port, Count, Buffer);
if (EFI_ERROR(Status)) {
return Status;
}
AccessWidth = mIoWidthTable[Width & 3];
Stride = mIoWidthTable[(Width & 3) + 16]; /* Read stride */
if (Count == 0) {
return EFI_SUCCESS;
}
do {
UINT16 Port16 = (UINT16)Port;
if (AccessWidth == 1) {
*(UINT8 *)Buffer = __inbyte(Port16);
Buffer = (VOID *)((UINT8 *)Buffer + Stride);
} else if (AccessWidth == 2) {
ASSERT(((UINTN)Port & 1) == 0);
*(UINT16 *)Buffer = __inword(Port16);
Buffer = (VOID *)((UINT16 *)Buffer + 1);
} else if (AccessWidth == 4) {
ASSERT(((UINTN)Port & 3) == 0);
*(UINT32 *)Buffer = __indword(Port16);
Buffer = (VOID *)((UINT32 *)Buffer + 1);
} else if (AccessWidth == 8) {
/* 8-byte port IO is not supported (caught by validation) */
ASSERT(FALSE);
}
Port += AccessWidth;
Count -= 1;
} while (Count > 0);
return EFI_SUCCESS;
}
/* =========================================================================
* CpuIoServiceWrite -- Port I/O Write
* ========================================================================= */
/**
* Port I/O Write dispatcher.
* Address: 0x1698
*
* Handles port I/O writes for EFI_CPU_IO_PROTOCOL_WIDTH values 0-3.
*
* For each element:
* Width=0: __outbyte(Port, *Buffer)
* Width=1: __outword(Port, *(UINT16 *)Buffer) with alignment check
* Width=2: __outdword(Port, *(UINT32 *)Buffer) with alignment check
* Width=3: Not supported by port I/O.
*
* @param[in] Width Width of the access.
* @param[in] Port Starting I/O port.
* @param[in] Count Number of elements.
* @param[in] Buffer Source buffer.
*
* @return EFI_SUCCESS or error from CpuIoCheckParameter.
*/
EFI_STATUS EFIAPI CpuIoServiceWrite(
IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
IN UINTN Port,
IN UINTN Count,
IN VOID *Buffer
)
{
EFI_STATUS Status;
UINT8 AccessWidth;
UINT8 Stride;
Status = CpuIoCheckParameter(EFI_CPU_IO_PROTOCOL_IO, Width, Port, Count, Buffer);
if (EFI_ERROR(Status)) {
return Status;
}
AccessWidth = mIoWidthTable[Width & 3];
Stride = mIoWidthTable[(Width & 3) + 20]; /* Write stride */
if (Count == 0) {
return EFI_SUCCESS;
}
do {
UINT16 Port16 = (UINT16)Port;
if (AccessWidth == 1) {
__outbyte(Port16, *(UINT8 *)Buffer);
Buffer = (VOID *)((UINT8 *)Buffer + Stride);
} else if (AccessWidth == 2) {
ASSERT(((UINTN)Port & 1) == 0);
__outword(Port16, *(UINT16 *)Buffer);
Buffer = (VOID *)((UINT16 *)Buffer + 1);
} else if (AccessWidth == 4) {
ASSERT(((UINTN)Port & 3) == 0);
__outdword(Port16, *(UINT32 *)Buffer);
Buffer = (VOID *)((UINT32 *)Buffer + 1);
} else if (AccessWidth == 8) {
ASSERT(FALSE);
}
Port += AccessWidth;
Count -= 1;
} while (Count > 0);
return EFI_SUCCESS;
}
/* =========================================================================
* Module Entry Point
* ========================================================================= */
/**
* Driver entry point -- ModuleEntryPoint (_ModuleEntryPoint).
* Address: 0x10D0
*
* 1. Saves ImageHandle and validates it.
* 2. Saves SystemTable (gST) and validates it.
* 3. Extracts gBS from gST->BootServices and validates.
* 4. Extracts gRT from gST->RuntimeServices and validates.
* 5. Calls GetHobList() to locate and cache the HOB list pointer.
* 6. Creates a notification event for ExitBootServices.
* 7. Creates a notification event for VirtualAddressChange.
* 8. Checks if gEfiCpuIoProtocolGuid is already installed in the
* protocol database. If so, ASSERTs (single-instance driver).
* 9. Installs the EFI_CPU_IO_PROTOCOL on a new handle via
* gBS->InstallMultipleProtocolInterfaces():
* Handle = NULL
* Protocol: gEfiCpuIoProtocolGuid
* Interface: CpuIo protocol instance
* 10. Returns EFI_STATUS from InstallMultipleProtocolInterfaces.
*
* @param[in] ImageHandle Handle for this driver image.
* @param[in] SystemTable Pointer to the UEFI system table.
*
* @return EFI_SUCCESS Protocol installed.
* @return EFI_INVALID_PARAMETER NULL ImageHandle or SystemTable.
* @return EFI_ALREADY_STARTED Protocol already installed (asserted).
*/
EFI_STATUS EFIAPI ModuleEntryPoint(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_CPU_IO_PROTOCOL CpuIoProtocol;
EFI_HANDLE Handle;
EFI_STATUS CheckStatus;
/*
* Save and validate global image handle.
*/
gImageHandle = ImageHandle;
ASSERT(gImageHandle != NULL);
/*
* Save and validate system table.
*/
gST = SystemTable;
ASSERT(gST != NULL);
/*
* Save and validate boot services pointer.
*/
gBS = gST->BootServices;
ASSERT(gBS != NULL);
/*
* Save and validate runtime services pointer.
*/
gRT = gST->RuntimeServices;
ASSERT(gRT != NULL);
/*
* Cache pointers for event callbacks.
*/
gBootServicesCache = (VOID *)gBS;
gRuntimeServicesCache = (VOID *)gRT;
/*
* Locate and cache the HOB list.
*/
GetHobList();
/*
* Register notification events.
*/
gBS->CreateEvent(
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
OnExitBootServices,
NULL,
NULL
);
/*
* Register virtual address change event.
*/
gBS->CreateEventEx(
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
OnVirtualAddressChange,
&gEfiEventVirtualAddressChangeGuid,
NULL,
NULL
);
/*
* Check that gEfiCpuIoProtocolGuid is NOT already installed.
* Only one instance should exist system-wide.
*/
Handle = NULL;
CheckStatus = gBS->LocateProtocol(
(EFI_GUID *)&mEfiCpuIoProtocolGuid,
NULL,
&Handle
);
if (!EFI_ERROR(CheckStatus)) {
/* Protocol already installed -- this should not happen */
DEBUG((EFI_ERROR_CODE, "&gEfiCpuIoProtocolGuid already installed in database\n"));
ASSERT(FALSE);
}
/*
* Build the CpuIoProtocol instance.
*
* NOTE: Unlike EFI_CPU_IO2_PROTOCOL, the EFI_CPU_IO_PROTOCOL access
* functions do NOT take a 'This' parameter as the first argument.
*/
CpuIoProtocol.Revision = 0;
CpuIoProtocol.Mem.Read = CpuMemoryServiceRead;
CpuIoProtocol.Mem.Write = CpuMemoryServiceWrite;
CpuIoProtocol.Io.Read = CpuIoServiceRead;
CpuIoProtocol.Io.Write = CpuIoServiceWrite;
/*
* Install the protocol on a new handle.
*/
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces(
&Handle,
&mEfiCpuIoProtocolGuid,
&CpuIoProtocol,
NULL
);
/*
* This is an ASSERT_EFI_ERROR -- the module asserts on failure.
*/
if (EFI_ERROR(Status)) {
DEBUG((EFI_ERROR_CODE, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR(FALSE);
}
return Status;
}