/**
*CpuIo2Dxe.c -- EFI CPU I/O 2 Protocol DXE Driver
*
*This driver implements and produces the EFI_CPU_IO2_PROTOCOL for a UEFI
*system. It provides validated access to:
* - Memory-mapped I/O (MMIO) space
* - Port I/O space
*
*Each access is parameter-validated for Width, alignment, address range,
*and buffer alignment before dispatch.
*
*Source: e:\hs\UefiCpuPkg\CpuIo2Dxe\CpuIo2Dxe.c
* (EDK2 / Intel UEFI CPU package)
*/
#include "CpuIo2Dxe.h"
/* =========================================================================
*Local Data (.rdata section)
* ========================================================================= */
/**
*I/O access width and stride table at 0xEF8.
*
*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), byte_EF8[Width] = 0 and the module
*accesses the FIFO variants directly (Width & 3 determines the element
*size, and the access width is hardcoded as 1 via the a4 = 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)
* ========================================================================= */
/*0x13B8 */ EFI_SYSTEM_TABLE *gST = NULL;
/*0x13C0 */ EFI_BOOT_SERVICES *gBS = NULL;
/*0x13C8 */ EFI_HANDLE gImageHandle = NULL;
/*0x13D0 */ EFI_RUNTIME_SERVICES *gRT = NULL;
/*0x13D8 */ VOID *gDebugProtocol = NULL;
/*0x13E0 */ VOID *gHobList = NULL;
/*0x13E8 -- stack-local temporary for ASSERT processing (not a global) */
/* =========================================================================
*GUID instances (.rdata section)
* ========================================================================= */
static const EFI_GUID mStatusCodeRuntimeProtocolGuid =
EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID; /*0x1360 */
static const EFI_GUID mEfiHobListGuid =
EFI_HOB_LIST_GUID; /*0x1370 */
static const EFI_GUID mEfiCpuIo2ProtocolGuid =
EFI_CPU_IO2_PROTOCOL_GUID; /*0x1380 */
/**
*Function pointer table for the EFI_CPU_IO2_PROTOCOL interface.
*
*At 0x1390, 48 bytes (room for 6 function pointers, only 4 used):
* [0] 0x061C = CpuMemoryServiceRead
* [1] 0x0714 = CpuMemoryServiceWrite
* [2] 0x0818 = CpuIoServiceRead
* [3] 0x0970 = CpuIoServiceWrite
* [4] zero (reserved)
* [5] zero (reserved)
*
*These are installed as the .Mem.Read, .Mem.Write, .Io.Read, .Io.Write
*entries of the EFI_CPU_IO2_PROTOCOL.
*/
static const VOID *mCpuIo2FunctionTable[6] = {
CpuMemoryServiceRead, /*0x61C */
CpuMemoryServiceWrite, /*0x714 */
CpuIoServiceRead, /*0x818 */
CpuIoServiceWrite, /*0x970 */
NULL,
NULL
};
/* =========================================================================
*Intrinsic Port I/O String Functions (.text section)
*
*These are compiler intrinsics / library routines that provide string
* (repetitive) I/O access. Each uses the x86 REP-prefixed I/O instruction.
*
*Addresses 0x2C0-0x30F
* ========================================================================= */
/**
*Read Count bytes from Port into Buffer using rep insb.
*Address: 0x2C0
*
*Implementation:
*push rdi
*mov rax, r8 ; Count
*mov rdi, rcx ; Buffer (destination)
*xchg rcx, rdx ; rcx = Count, rdx = Port
*rep stosb ; No, this is NOT rep insb -- this is rep stosb?
* ... This function is actually a rep insb wrapper.
*mov rax, rdx
*pop rdi
*ret
*
*NOTE: The decompiler shows __inbytestring() -- the disassembly shows
*this is generated inline by the compiler using rep insb/stosb patterns.
*The exact instruction sequence:
*rep insb BYTE PTR [rdi], dx ; Read from port DX into [rdi]
*/
#pragma intrinsic(__inbytestring)
void __inbytestring(UINT16 Port, OUT VOID *Buffer, IN UINTN Count)
{
__inbytestring_w(Port, Buffer, Count);
}
/**
*Read Count words from Port into Buffer using rep insw.
*Address: 0x2CD
*/
#pragma intrinsic(__inwordstring)
void __inwordstring(UINT16 Port, OUT VOID *Buffer, IN UINTN Count)
{
__inwordstring_w(Port, Buffer, Count);
}
/**
*Read Count dwords from Port into Buffer using rep insd.
*Address: 0x2DB
*/
#pragma intrinsic(__indwordstring)
void __indwordstring(UINT16 Port, OUT VOID *Buffer, IN UINTN Count)
{
__indwordstring_w(Port, Buffer, Count);
}
/**
*Write Count bytes from Buffer to Port using rep outsb.
*Address: 0x2E8
*/
#pragma intrinsic(__outbytestring)
void __outbytestring(UINT16 Port, IN VOID *Buffer, IN UINTN Count)
{
__outbytestring_w(Port, Buffer, Count);
}
/**
*Write Count words from Buffer to Port using rep outsw.
*Address: 0x2F5
*/
#pragma intrinsic(__outwordstring)
void __outwordstring(UINT16 Port, IN VOID *Buffer, IN UINTN Count)
{
__outwordstring_w(Port, Buffer, Count);
}
/**
*Write Count dwords from Buffer to Port using rep outsd.
*Address: 0x303
*/
#pragma intrinsic(__outdwordstring)
void __outdwordstring(UINT16 Port, IN VOID *Buffer, IN UINTN Count)
{
__outdwordstring_w(Port, Buffer, Count);
}
/* =========================================================================
*64-bit Shift Operations
* ========================================================================= */
/**
*64-bit left shift with count validation.
*Address: 0xC24
*
*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: 0xC68
*
*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: 0xDF4
*
*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: 0xADC
*
*Uses a pool alloc/free canary to verify UEFI boot services are functional:
*1. AllocatePool(EfiBootServicesData, 0, &TempBuffer) -- [gBS+0x18]
*2. FreePool(TempBuffer) -- [gBS+0x20]
*3. If (UINTN)TempBuffer <= 0x10 -> boot services unavailable, return NULL
*4. gBS->LocateProtocol(&gEfiStatusCodeRuntimeProtocolGuid, NULL, &gDebugProtocol)
*
*The canary at step 3 detects whether AllocatePool returned a real pointer.
*On a functioning UEFI, a 0-byte AllocatePool returns a valid non-NULL
*pointer with an address > 0x10. On broken/missing boot services, it
*returns NULL or a very low value.
*
*The function calls:
* [gBS + 0x18] -- AllocatePool? No, actually this is RaiseTPL with (31).
* [gBS + 0x20] -- RestoreTPL to restore original TPL.
*
*Wait -- the disassembly shows:
*lea ecx, [rax+1Fh] ; ecx = 0 + 31 = 31 (TPL_HIGH_LEVEL)
*call [BootServices + 18h] ; gBS->RaiseTPL(TPL_HIGH_LEVEL)
*call [BootServices + 20h] ; gBS->RestoreTPL(result)
*
*This is actually a TPL manipulation check that verifies
*RaiseTPL/RestoreTPL are functional (the return value of RaiseTPL is the
*previous TPL, which is then checked: if previous TPL <= TPL_NOTIFY (0x10),
*boot services are active).
*
*Then: call [BootServices + 140h] -- gBS->LocateProtocol()
*
* @return Cached pointer to the StatusCodeRuntimeProtocol, or NULL.
*/
VOID *GetDebugProtocol(VOID)
{
EFI_STATUS Status;
UINTN OldTpl;
VOID *TempBuffer;
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 -- this can happen in runtime phase.
*Do not attempt protocol resolution.
*/
return NULL;
}
/*
*Resolve the StatusCodeRuntimeProtocol.
*gBS->LocateProtocol(&gEfiStatusCodeRuntimeProtocolGuid, NULL, &Protocol)
*/
Status = gBS->LocateProtocol(
(EFI_GUID *)&mStatusCodeRuntimeProtocolGuid,
NULL,
&Protocol
);
if (!EFI_ERROR(Status)) {
gDebugProtocol = Protocol;
} else {
gDebugProtocol = NULL;
}
return gDebugProtocol;
}
/* =========================================================================
*Debug Output Support
* ========================================================================= */
/**
*Debug assert handler.
*Address: 0xBE4
*
*Resolves the StatusCodeRuntimeProtocol (if not already cached) and
*calls the assertion handler at protocol interface offset +0x08.
*
*This corresponds to:
*Protocol->ReportStatusCode(EFI_ERROR_CODE | EFI_ERROR_MAJOR, ...)
*followed by a CpuDeadLoop() in the full EDK2 implementation (not
*included in this binary -- the caller loops or halts).
*
* @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, or
*ReportStatusCodeEx)
*/
((EFI_STATUS (EFIAPI *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))
((UINT8 *)Protocol + 8))(FileName, LineNumber, Description);
}
}
/**
*Debug print through StatusCodeRuntimeProtocol.
*Address: 0xB5C
*
*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
*
*Fallback at 0xFDAF0490:
*if (CMOSLevel == 0) -> read byte from (0xFDAF0490)
*if (byte & 2) -> set ErrorLevel to 0x80000000 | (byte & 1 ? 0xC : 0x4)
*else -> default to ErrorLevel = 4
*
*Calls the protocol's ReportStatusCode at offset +0x00 if the error
*level mask matches.
*
* @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.
*/
__outbyte(0x70, (__inbyte(0x70) & 0x80) | 0x4B);
CmosDebugLevel = __inbyte(0x71);
/*
*Determine the effective debug level mask.
*/
if (CmosDebugLevel > 3) {
/*Level is already valid (> 3 means non-standard; use as-is) */
DebugLevelMask = CmosDebugLevel;
if (CmosDebugLevel == 0) {
/*Level 0: fallback to MMIO-based detection */
UINT8 BoardDebugReg = *(volatile UINT8 *)0xFDAF0490;
if (BoardDebugReg & 2) {
DebugLevelMask = EFI_ERROR_CODE | EFI_ERROR_MAJOR;
if (!(BoardDebugReg & 1)) {
DebugLevelMask = EFI_ERROR_CODE;
}
} else {
DebugLevelMask = 4;
}
}
} else {
/*Level 1 -> mask 0x80000004, other -> mask 0x80000006 */
if (CmosDebugLevel == 1) {
DebugLevelMask = 0x80000004;
} else {
DebugLevelMask = 0x80000006;
}
}
/*
*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);
}
}
/* =========================================================================
*HOB List Resolution
* ========================================================================= */
/**
*Compares two GUID pointers by reading two 8-byte halves.
*Address: 0xD84
*
*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: 0xCAC
*
*Iterates over SystemTable->ConfigurationTable[] looking for
*gEfiHobListGuid. Caches the result in gHobList.
*
*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 (gHobList != NULL) {
return;
}
gHobList = 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 */
{
gHobList = *(VOID **)((UINT8 *)ConfigEntry + sizeof(EFI_GUID));
return;
}
}
/*
*HOB list GUID not found in configuration table.
*/
ASSERT_EFI_ERROR(EFI_NOT_FOUND);
ASSERT(gHobList != NULL);
}
/* =========================================================================
*Parameter Validation
* ========================================================================= */
/**
*Validates I/O access parameters.
*Address: 0x518
*
*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 (the stride remains the element size).
*/
if (Width > 3) {
AccessSize = 1; /*FIFO modes access one element at a time, but
*a4 = AccessSize is set to 1 for bounds checking */
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)) {
/*Address + (Count - 1) * (1 << Width) */
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;
}
/* =========================================================================
*CpuIoServiceRead -- Port I/O Read
* ========================================================================= */
/**
*Port I/O Read dispatcher.
*Address: 0x818
*
*Handles port I/O reads for EFI_CPU_IO_PROTOCOL_WIDTH values 0-3.
*For Width=0 (byte), uses direct __inbyte() for single reads or
*__inbytestring() for bulk reads.
*
*Width=0 (byte):
* - single: __inbyte(Port) -> *Buffer = result
* - bulk: __inbytestring(Port, Buffer, Count)
*
*Width=1 (word):
* - single: __inword(Port) with (Port & 1) alignment check -> *Buffer
* - bulk: __inwordstring(Port, Buffer, Count)
*
*Width=2 (dword):
* - single: __indword(Port) with (Port & 3) alignment check -> *Buffer
* - bulk: __indwordstring(Port, Buffer, Count)
*
*For Width=0 non-bulk mode (Count=1):
* - direct __inbyte call
*
*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] This Pointer to the EFI_CPU_IO2_PROTOCOL instance.
* @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_IO2_PROTOCOL *This,
IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
IN UINTN Port,
IN UINTN Count,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
UINT8 AccessWidth; /*In bytes per element */
UINT8 Stride; /*Buffer advance per element */
Status = CpuIoCheckParameter(0, 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;
}
if (AccessWidth == 0) {
/*
*Bulk I/O string read.
*For byte access: __inbytestring(Port, Buffer, Count).
*/
UINTN BulkWidth = Width & 3;
if (BulkWidth == 0) {
/*Count byte reads */
if (Count == 1) {
*(UINT8 *)Buffer = __inbyte((UINT16)Port);
} else {
__inbytestring((UINT16)Port, Buffer, Count);
}
} else if (BulkWidth == 1) {
/*Count word reads */
if (Count == 1) {
ASSERT(((UINTN)Port & 1) == 0);
*(UINT16 *)Buffer = __inword((UINT16)Port);
} else {
__inwordstring((UINT16)Port, Buffer, Count);
}
} else if (BulkWidth == 2) {
/*Count dword reads */
if (Count == 1) {
ASSERT(((UINTN)Port & 3) == 0);
*(UINT32 *)Buffer = __indword((UINT16)Port);
} else {
__indwordstring((UINT16)Port, Buffer, Count);
}
}
return EFI_SUCCESS;
}
/*
*Element-by-element access with stride.
*Each iteration reads one element, advances the port by AccessWidth,
*and advances the buffer by Stride bytes.
*/
do {
UINT8 *BufPtr = (UINT8 *)Buffer;
UINT16 Port16 = (UINT16)Port;
if (AccessWidth == 1) {
*BufPtr = __inbyte(Port16);
Buffer = (VOID *)(BufPtr + 1);
} else if (AccessWidth == 2) {
ASSERT(((UINTN)Port & 1) == 0);
*(UINT16 *)BufPtr = __inword(Port16);
Buffer = (VOID *)((UINT16 *)BufPtr + 1);
} else if (AccessWidth == 4) {
ASSERT(((UINTN)Port & 3) == 0);
*(UINT32 *)BufPtr = __indword(Port16);
Buffer = (VOID *)((UINT32 *)BufPtr + 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: 0x970
*
*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: __outqword? Not supported by port I/O.
*
*Bulk operations use __outbytestring/__outwordstring/__outdwordstring.
*
* @param[in] This Pointer to the EFI_CPU_IO2_PROTOCOL instance.
* @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_IO2_PROTOCOL *This,
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(0, 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;
}
if (AccessWidth == 0) {
/*Bulk I/O string writes */
UINTN BulkWidth = Width & 3;
if (BulkWidth == 0) {
__outbytestring((UINT16)Port, Buffer, Count);
} else if (BulkWidth == 1) {
__outwordstring((UINT16)Port, Buffer, Count);
} else if (BulkWidth == 2) {
__outdwordstring((UINT16)Port, Buffer, Count);
}
return EFI_SUCCESS;
}
/*
*Element-by-element write.
*/
do {
UINT8 *BufPtr = (UINT8 *)Buffer;
UINT16 Port16 = (UINT16)Port;
if (AccessWidth == 1) {
__outbyte(Port16, *BufPtr);
Buffer = (VOID *)(BufPtr + Stride);
} else if (AccessWidth == 2) {
ASSERT(((UINTN)Port & 1) == 0);
__outword(Port16, *(UINT16 *)BufPtr);
Buffer = (VOID *)(BufPtr + Stride);
} else if (AccessWidth == 4) {
ASSERT(((UINTN)Port & 3) == 0);
__outdword(Port16, *(UINT32 *)BufPtr);
Buffer = (VOID *)(BufPtr + Stride);
} else if (AccessWidth == 8) {
ASSERT(FALSE);
}
Port += AccessWidth;
Count -= 1;
} while (Count > 0);
return EFI_SUCCESS;
}
/* =========================================================================
*CpuMemoryServiceRead -- MMIO Read
* ========================================================================= */
/**
*MMIO Read dispatcher.
*Address: 0x61C
*
*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.
*
*The MMIO read is a direct memory read from Address:
*Width=0: *(volatile UINT8 *)Address
*Width=1: *(volatile UINT16 *)Address
*Width=2: *(volatile UINT32 *)Address
*Width=3: *(volatile UINT64 *)Address
*
*For Count > 1, iterates Count times, advancing the address by the
*access width and the buffer by the stride per element.
*
* @param[in] This Pointer to the EFI_CPU_IO2_PROTOCOL instance.
* @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_IO2_PROTOCOL *This,
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(1, 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((Address & 1) == 0); */
*(UINT16 *)BufPtr = *(volatile UINT16 *)Address;
Buffer = (VOID *)((UINT16 *)BufPtr + 1); /*Stride varies */
} else if (AccessWidth == 4) {
*(UINT32 *)BufPtr = *(volatile UINT32 *)Address;
Buffer = (VOID *)((UINT32 *)BufPtr + 1);
} else if (AccessWidth == 8) {
/*ASSERT((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: 0x714
*
*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
*Width=2: *(volatile UINT32 *)Address = *(UINT32 *)Buffer
*Width=3: *(volatile UINT64 *)Address = *(UINT64 *)Buffer
*
* @param[in] This Pointer to the EFI_CPU_IO2_PROTOCOL instance.
* @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_IO2_PROTOCOL *This,
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(1, 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((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((Address & 7) == 0); */
*(volatile UINT64 *)Address = *(UINT64 *)BufPtr;
Buffer = (VOID *)((UINT64 *)BufPtr + 1);
}
Address += AccessWidth;
Count -= 1;
} while (Count > 0);
return EFI_SUCCESS;
}
/* =========================================================================
*Module Entry Point
* ========================================================================= */
/**
*Driver entry point -- ModuleEntryPoint (_ModuleEntryPoint).
*Address: 0x3E0
*
*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. Checks if gEfiCpuIo2ProtocolGuid is already installed in the
*protocol database. If so, ASSERTs (single-instance driver).
*7. Installs the EFI_CPU_IO2_PROTOCOL on a new handle via
*gBS->InstallMultipleProtocolInterfaces():
*Handle = NULL
*Protocol: gEfiCpuIo2ProtocolGuid
*Interface: protocol instance with function table
*8. 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_IO2_PROTOCOL CpuIo2Protocol;
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);
/*
*Locate and cache the HOB list.
*/
GetHobList();
/*
*Check that gEfiCpuIo2ProtocolGuid is NOT already installed.
*Only one instance should exist system-wide.
*/
Handle = NULL;
CheckStatus = gBS->LocateProtocol(
(EFI_GUID *)&mEfiCpuIo2ProtocolGuid,
NULL,
&Handle
);
if (!EFI_ERROR(CheckStatus)) {
/*Protocol already installed -- this should not happen */
ASSERT(FALSE);
}
/*
*Build the CpuIo2Protocol instance.
*/
CpuIo2Protocol.Revision = EFI_CPU_IO2_PROTOCOL_REVISION;
CpuIo2Protocol.Mem.Read = CpuMemoryServiceRead;
CpuIo2Protocol.Mem.Write = CpuMemoryServiceWrite;
CpuIo2Protocol.Io.Read = CpuIoServiceRead;
CpuIo2Protocol.Io.Write = CpuIoServiceWrite;
/*
*Install the protocol on a new handle.
*/
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces(
&Handle,
&mEfiCpuIo2ProtocolGuid,
&CpuIo2Protocol,
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;
}