Newer
Older
AMI-Aptio-BIOS-Reversed / CpuIoDxe / CpuIoDxe.c
@Ajax Dong Ajax Dong 2 days ago 30 KB Init
/**
 * 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;
}