Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / GenericSio / SioDxeInit / SioDxeInit.c
@Ajax Dong Ajax Dong 2 days ago 26 KB Full restructure
/**
 * SioDxeInit.c -- Super I/O DXE Initialization Driver
 *
 * Source: AmiModulePkg/GenericSio/SioDxeInit.c
 * Platform: Lenovo HR650X (Purley/LBG-PCH)
 * Compiler: VS2015, X64
 *
 * This DXE driver initializes the Super I/O controller and configures
 * LPC I/O cycle decoding on the PCH. It manages port ranges for legacy
 * devices (serial, parallel, GPIO, KBC, etc.) connected via the LPC bus
 * to the AST2500 BMC or compatible SIO chipset.
 *
 * Key responsibilities:
 *   1. Initialize UEFI boot/runtime services table pointers
 *   2. Locate and cache the DXE Services Table, HOB list, PCD protocol,
 *      and MM PCI USRA protocol
 *   3. Configure LPC I/O cycle decode registers (PCR and PCI config space)
 *      to route legacy device I/O ranges to the SIO
 *   4. Install the SIO protocol for other drivers to manage SIO devices
 *
 * Dependencies:
 *   gEfiDxeServicesTableGuid
 *   gEfiHobListGuid
 *   gEfiMmPciUsraProtocolGuid
 *   gEfiPcdProtocolGuid
 */

#include "SioDxeInit.h"

//
// ---------------------------------------------------------------------------
// Global variable declarations
// ---------------------------------------------------------------------------

//
// UEFI service table pointers (cached at driver startup)
//
EFI_SYSTEM_TABLE        *gST;              ///< offset 0x2658
EFI_BOOT_SERVICES       *gBS;              ///< offset 0x2660
EFI_RUNTIME_SERVICES    *gRT;              ///< offset 0x2670
EFI_HANDLE              gImageHandle;      ///< offset 0x2668
EFI_DXE_SERVICES        *gDS;              ///< offset 0x2680
VOID                    *mHobList;         ///< offset 0x2690
MM_PCI_USRA_PROTOCOL    *mPciUsra;         ///< offset 0x2688
PCD_PROTOCOL            *mPcd;             ///< offset 0x26F8

//
// Driver instance handle installed protocols on
//
EFI_HANDLE              mDriverHandle = NULL;  ///< offset 0x2650

//
// SIO DXE Init protocol structure (installed as protocol interface)
//
SIO_DXE_INIT_PROTOCOL   mSioDxeInitProtocol;  ///< offset 0x2600

//
// Cached PCH SKU type (detected at runtime)
//   0 = Unknown, 1 = LbgPch, 2 = LbgPchH, 3 = Unsupported
//
UINT32                  mPchSkuType = 3;       ///< offset 0x2644

//
// Debug library state (report status code protocol)
//
VOID                    *mDebugLib = NULL;     ///< offset 0x2678


//
// ---------------------------------------------------------------------------
// Forward declarations of local helper functions
// ---------------------------------------------------------------------------

STATIC
EFI_STATUS
EFIAPI
SioDeviceInit (
  IN SIO_DXE_INIT_PROTOCOL  *This,
  IN UINT16                 DeviceType,
  IN UINT16                 IoBase,
  IN UINT8                  IrqNumber,
  IN UINT8                  DmaChannel
  );

STATIC
VOID
PchPcrWriteReg (
  IN UINT16   PcrAddress,
  IN UINT8    Size,
  IN UINT32   Value
  );

STATIC
EFI_STATUS
PchDetectSku (
  VOID
  );

STATIC
BOOLEAN
IsInterruptsEnabled (
  VOID
  );

STATIC
VOID *
GetDebugLib (
  VOID
  );

STATIC
EFI_STATUS
GetConfigTable (
  IN  EFI_GUID  *TableGuid,
  OUT VOID      **Table
  );

STATIC
PCD_PROTOCOL *
GetPcdProtocol (
  VOID
  );

STATIC
VOID *
GetHobList (
  VOID
  );

STATIC
UINT64
SioMmioBase (
  VOID
  );


//
// ---------------------------------------------------------------------------
// Interrupt control helpers
// ---------------------------------------------------------------------------

//
// sub_390 -- Enable interrupts (STI)
//
STATIC
VOID
EnableInterrupts (
  VOID
  )
{
  _enable ();
}

//
// sub_3A0 -- Disable interrupts (CLI)
//
STATIC
VOID
DisableInterrupts (
  VOID
  )
{
  _disable ();
}

//
// sub_3B0 -- Read EFLAGS
//
STATIC
UINTN
ReadEflags (
  VOID
  )
{
  return __getcallerseflags ();
}

//
// sub_1AAC -- Check if interrupts are enabled (EFLAGS.IF bit 9)
//
STATIC
BOOLEAN
IsInterruptsEnabled (
  VOID
  )
{
  UINTN Eflags;

  Eflags = ReadEflags ();
  DisableInterrupts ();
  return (Eflags & 0x200) != 0;
}


//
// ---------------------------------------------------------------------------
// I/O Port Access Helpers
// ---------------------------------------------------------------------------

//
// sub_B9C -- Read UINT16 from I/O port (word-aligned check)
//
STATIC
UINT16
EFIAPI
IoRead16 (
  IN UINT16  Port
  )
{
  ASSERT ((Port & 1) == 0);
  return __inword (Port);
}

//
// sub_BCC -- Write UINT16 to I/O port (word-aligned check)
//
STATIC
UINT16
EFIAPI
IoWrite16 (
  IN UINT16  Port,
  IN UINT16  Value
  )
{
  ASSERT ((Port & 1) == 0);
  __outword (Port, Value);
  return Value;
}

//
// sub_C0C -- Read UINT16 from I/O port
//
STATIC
UINT16
EFIAPI
IoRead16Alt (
  IN UINT16  Port
  )
{
  ASSERT ((Port & 1) == 0);
  return __inword (Port);
}

//
// sub_C40 -- Write UINT16 to I/O port
//
STATIC
UINT16
EFIAPI
IoWrite16Alt (
  IN UINT16  Port,
  IN UINT16  Value
  )
{
  ASSERT ((Port & 1) == 0);
  __outword (Port, Value);
  return Value;
}

//
// sub_C80 -- Read UINT32 from I/O port (dword-aligned check)
//
STATIC
UINT32
EFIAPI
IoRead32 (
  IN UINT16  Port
  )
{
  ASSERT ((Port & 3) == 0);
  return __indword (Port);
}

//
// sub_CB0 -- Write UINT32 to I/O port (dword-aligned check)
//
STATIC
UINT32
EFIAPI
IoWrite32 (
  IN UINT16  Port,
  IN UINT32  Value
  )
{
  ASSERT ((Port & 3) == 0);
  __outdword (Port, Value);
  return Value;
}


//
// ---------------------------------------------------------------------------
// PCI Configuration via CF8/CFC (legacy I/O method)
// ---------------------------------------------------------------------------

//
// sub_17F0 -- Read DWORD from PCI config space via CF8/CFC
//
STATIC
UINT32
PciRead32 (
  IN UINT64  PciAddress
  )
{
  BOOLEAN  InterruptsEnabled;
  UINT32   OrigCfgAddr;
  UINT32   Value;

  ASSERT ((PciAddress & ~0xFFFF0FF | 3) == 0);

  InterruptsEnabled = IsInterruptsEnabled ();
  OrigCfgAddr = IoRead32 (0xCF8);

  IoWrite32 (0xCF8, (UINT32)(PciAddress & 0xFC |
                ((PciAddress >> 4) & 0xFFFF00) | 0x80000000));
  Value = IoRead32 (0xCFC);
  IoWrite32 (0xCF8, OrigCfgAddr);

  if (InterruptsEnabled) {
    EnableInterrupts ();
  } else {
    DisableInterrupts ();
  }

  return Value;
}

//
// sub_189C -- Write DWORD to PCI config space via CF8/CFC
//
STATIC
VOID
PciWrite32 (
  IN UINT64  PciAddress,
  IN UINT32  Value
  )
{
  BOOLEAN  InterruptsEnabled;
  UINT32   OrigCfgAddr;

  ASSERT ((PciAddress & ~0xFFFF0FF | 3) == 0);

  InterruptsEnabled = IsInterruptsEnabled ();
  OrigCfgAddr = IoRead32 (0xCF8);

  IoWrite32 (0xCF8, (UINT32)(PciAddress & 0xFC |
                ((PciAddress >> 4) & 0xFFFF00) | 0x80000000));
  IoWrite32 (0xCFC, Value);
  IoWrite32 (0xCF8, OrigCfgAddr);

  if (InterruptsEnabled) {
    EnableInterrupts ();
  } else {
    DisableInterrupts ();
  }
}

//
// sub_1668 -- Read WORD from PCI config space via CF8/CFC
//
STATIC
UINT16
PciRead16 (
  IN UINT64  PciAddress
  )
{
  BOOLEAN  InterruptsEnabled;
  UINT32   OrigCfgAddr;
  UINT16   Value;

  ASSERT ((PciAddress & ~0xFFFF0FF | 1) == 0);

  InterruptsEnabled = IsInterruptsEnabled ();
  OrigCfgAddr = IoRead32 (0xCF8);

  IoWrite32 (0xCF8, (UINT32)(PciAddress & 0xFC |
                ((PciAddress >> 4) & 0xFFFF00) | 0x80000000));
  Value = IoRead16Alt ((UINT16)((PciAddress & 2) + 0xCFC));
  IoWrite32 (0xCF8, OrigCfgAddr);

  if (InterruptsEnabled) {
    EnableInterrupts ();
  } else {
    DisableInterrupts ();
  }

  return Value;
}

//
// sub_1724 -- Write WORD to PCI config space via CF8/CFC
//
STATIC
VOID
PciWrite16 (
  IN UINT64  PciAddress,
  IN UINT16  Value
  )
{
  BOOLEAN  InterruptsEnabled;
  UINT32   OrigCfgAddr;

  ASSERT ((PciAddress & ~0xFFFF0FF | 1) == 0);

  InterruptsEnabled = IsInterruptsEnabled ();
  OrigCfgAddr = IoRead32 (0xCF8);

  IoWrite32 (0xCF8, (UINT32)(PciAddress & 0xFC |
                ((PciAddress >> 4) & 0xFFFF00) | 0x80000000));
  IoWrite16Alt ((UINT16)((PciAddress & 2) + 0xCFC), Value);
  IoWrite32 (0xCF8, OrigCfgAddr);

  if (InterruptsEnabled) {
    EnableInterrupts ();
  } else {
    DisableInterrupts ();
  }
}

//
// sub_2E0 -- Internal memory copy with overlap support
//
STATIC
VOID *
EFIAPI
InternalCopyMem (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Length
  )
{
  VOID *DestPtr = Destination;

  if (Source < Destination &&
      (UINT8 *)Source + Length - 1 >= (UINT8 *)Destination) {
    //
    // Overlapping from above: copy backwards
    //
    UINT8 *DstEnd = (UINT8 *)Destination + Length - 1;
    UINT8 *SrcEnd = (UINT8 *)Source + Length - 1;
    while (Length--) {
      *DstEnd-- = *SrcEnd--;
    }
  } else {
    //
    // Non-overlapping: forward copy with 8-byte chunks
    //
    UINTN  Count8 = Length >> 3;
    Length &= 7;

    CopyMem (Destination, Source, Count8 << 3);
    CopyMem ((UINT8 *)Destination + (Count8 << 3),
             (UINT8 *)Source + (Count8 << 3),
             Length);
  }

  return DestPtr;
}


//
// ---------------------------------------------------------------------------
// PCH PCR (Private Config Register) Write
// ---------------------------------------------------------------------------

//
// sub_195C -- Write to PCH PCR register (base 0xFDEF0000)
//             Handles byte, word, and dword accesses.
//
STATIC
VOID
PchPcrWriteReg (
  IN UINT16   PcrAddress,
  IN UINT8    Size,
  IN UINT32   Value
  )
{
  if (((PcrAddress - 1) & PcrAddress) != 0) {
    DEBUG ((EFI_D_ERROR,
            "PchPcrWrite error. Invalid Offset: %x Size: %x",
            PcrAddress, Size));
    ASSERT (!EFI_ERROR (EFI_INVALID_PARAMETER));
    return;
  }

  switch (Size) {
  case 1:
    *(volatile UINT8 *)(LPC_IO_RANGE_BASE + PcrAddress) = (UINT8)Value;
    break;
  case 2:
    IoWrite16 ((UINT16)(LPC_IO_RANGE_BASE + PcrAddress), (UINT16)Value);
    break;
  case 4:
    *(volatile UINT32 *)(LPC_IO_RANGE_BASE + PcrAddress) = Value;
    break;
  }
}

//
// sub_1A14 -- Detect PCH SKU type from LPC device ID (PCR register)
//
STATIC
VOID
PchDetectSku (
  VOID
  )
{
  if (mPchSkuType == 3) {
    UINTN   MmioBase;
    UINT16  LpcDevId;

    MmioBase = SioMmioBase ();
    LpcDevId = IoRead16 ((UINT16)(MmioBase + 2));

    if ((LpcDevId + 0x5E40) < 0x100 &&
        ((LpcDevId + 0x5E40) == 0 || (LpcDevId + 0x62C0) <= 8)) {
      mPchSkuType = 1;    // LbgPch
    } else if ((LpcDevId + 0x5E40) >= 0x100 &&
               (LpcDevId + 0x62C0) <= 8) {
      mPchSkuType = 2;    // LbgPchH
    } else {
      DEBUG ((EFI_D_ERROR,
              "Unsupported PCH SKU, LpcDeviceId: 0x%04x!\n",
              LpcDevId));
      ASSERT (!EFI_ERROR (EFI_UNSUPPORTED));
    }
  }
}


//
// ---------------------------------------------------------------------------
// Debug Library
// ---------------------------------------------------------------------------

//
// sub_6C8 -- Get the debug library instance (report status code protocol)
//
STATIC
VOID *
GetDebugLib (
  VOID
  )
{
  if (mDebugLib == NULL) {
    UINTN  PoolSize;

    PoolSize = gBS->CalculatePoolSize ();
    if (PoolSize <= 0x10) {
      gBS->AllocatePool (EfiBootServicesData, 0, &mDebugLib);
      if (mDebugLib == NULL) {
        return NULL;
      }
    } else {
      return NULL;
    }
  }

  return mDebugLib;
}

//
// sub_748 -- Debug print with severity mask check
//
STATIC
VOID
EFIAPI
DebugPrint (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  VA_LIST     Marker;
  VOID        *DebugLib;

  DebugLib = GetDebugLib ();
  if (DebugLib != NULL) {
    if (DebugGetMask () & ErrorLevel) {
      VA_START (Marker, Format);
      DebugVPrint (ErrorLevel, Format, Marker);
      VA_END (Marker);
    }
  }
}

//
// sub_790 -- ASSERT check with debug print
//
STATIC
VOID
EFIAPI
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *AssertString
  )
{
  VOID  *DebugLib;

  DebugLib = GetDebugLib ();
  if (DebugLib != NULL) {
    DebugReportAssert (DebugLib, FileName, LineNumber, AssertString);
  }
}


//
// ---------------------------------------------------------------------------
// DXE Services Table Retrieval
// ---------------------------------------------------------------------------

//
// sub_7D0 -- Locate a configuration table by GUID from the system table
//
STATIC
EFI_STATUS
GetConfigTable (
  IN  EFI_GUID  *TableGuid,
  OUT VOID      **Table
  )
{
  UINTN  Index;

  ASSERT (TableGuid != NULL);
  ASSERT (Table != NULL);

  *Table = NULL;

  if (gST->ConfigurationTable == NULL) {
    return EFI_NOT_FOUND;
  }

  for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
    if (CompareGuid (
          &gST->ConfigurationTable[Index].VendorGuid,
          TableGuid)) {
      *Table = gST->ConfigurationTable[Index].VendorTable;
      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

//
// sub_D40 -- Read 8 bytes unaligned
//
STATIC
UINT64
ReadUnaligned64 (
  IN  CONST VOID      *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(volatile UINT64 *)Buffer;
}

//
// sub_E18 -- Compare two GUIDs (two 64-bit compare)
//
STATIC
BOOLEAN
CompareGuid (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  return ReadUnaligned64 (Guid1) == ReadUnaligned64 (Guid2) &&
         ReadUnaligned64 ((UINT8 *)Guid1 + 8) ==
           ReadUnaligned64 ((UINT8 *)Guid2 + 8);
}


//
// ---------------------------------------------------------------------------
// DXE Services / HOB / Protocol retrieval helpers
// ---------------------------------------------------------------------------

//
// sub_8C4 -- Get the HOB list pointer from the configuration table
//
STATIC
VOID *
GetHobList (
  VOID
  )
{
  if (mHobList == NULL) {
    EFI_STATUS  Status;

    Status = GetConfigTable (&gEfiHobListGuid, &mHobList);
    if (EFI_ERROR (Status)) {
      ASSERT_EFI_ERROR (Status);
    }

    ASSERT (mHobList != NULL);
  }

  return mHobList;
}

//
// sub_E80 -- Locate PCD protocol (lazy init)
//
STATIC
PCD_PROTOCOL *
GetPcdProtocol (
  VOID
  )
{
  if (mPcd == NULL) {
    EFI_STATUS  Status;

    Status = gBS->LocateProtocol (
                    &gEfiPcdProtocolGuid,
                    NULL,
                    (VOID **)&mPcd
                    );
    if (EFI_ERROR (Status)) {
      ASSERT_EFI_ERROR (Status);
    }
    ASSERT (mPcd != NULL);
  }

  return mPcd;
}

//
// sub_894 -- Get SIO MMIO base via MM PCI USRA protocol
//            Accesses the LPC bridge (B0 D31 F0) to get the
//            memory-mapped register base.
//
STATIC
UINT64
SioMmioBase (
  VOID
  )
{
  MM_PCI_USRA_ADDRESS  PciAddress;

  PciAddress.Address        = 1015808;    // B0 D31 F0
  PciAddress.AddressSpace   = 512;        // MMIO
  PciAddress.RegisterOffset = 0;

  mPciUsra->UsraAddress (mPciUsra, &PciAddress);
  return PciAddress.Address;
}


//
// ---------------------------------------------------------------------------
// Debug Level Filter (sub_CF0)
// ---------------------------------------------------------------------------

//
// sub_CF0 -- Read the current debug level from CMOS (RTC register 0x4B)
//            Returns EFI_D_INFO, EFI_D_WARN, or 0.
//
STATIC
UINT32
DebugGetLevel (
  VOID
  )
{
  UINT8   CmosIndex;
  UINT8   DebugLevel;
  UINT8   ExtendedDebugLevel;

  //
  // Read CMOS index, set to debug register 0x4B
  //
  CmosIndex = __inbyte (0x70);
  __outbyte (0x70, CmosIndex & 0x80 | 0x4B);

  DebugLevel = __inbyte (0x71);

  if (DebugLevel > 3) {
    if (DebugLevel == 0) {
      //
      // Check platform debug enable (0xFDAF0490 register bit 1)
      //
      ExtendedDebugLevel = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
      DebugLevel = ExtendedDebugLevel;
    }
  }

  //
  // Map debug level to EFI_D_ mask values
  //
  if (DebugLevel == 1) {
    return EFI_D_INFO;    // 0x00000004
  }
  if (DebugLevel == 2) {
    return EFI_D_WARN;    // 0x80000006
  }

  return 0;
}


//
// ---------------------------------------------------------------------------
// LPC I/O Cycle Decode Configuration (sub_1450 / sub_1038 / sub_F0C)
// ---------------------------------------------------------------------------

//
// sub_1450 -- Configure a new LPC I/O range decode entry.
//             Manages the 4-entry I/O range decode table in SIO MMIO.
//             Finds an available slot or resizes an existing overlapping range.
//
STATIC
EFI_STATUS
ConfigureLpcIoRange (
  IN UINT16   IoBase,
  IN UINT16   Size,
  IN UINT8    DeviceType
  )
{
  UINTN   EntryIndex;
  UINT32  IoRangeEntry;
  UINT64  MmioBase;

  MmioBase = SioMmioBase ();

  //
  // Check existing ranges for overlap or empty slot
  //
  for (EntryIndex = 0; EntryIndex < 4; EntryIndex++) {
    UINT32  CurrentEntry;

    CurrentEntry = *(UINT32 *)(MmioBase + 0x84 + EntryIndex * 4);

    if ((CurrentEntry & LPC_IO_PORT_ENTRY_BASE_MASK) == 0) {
      //
      // Empty slot found -- create entry
      //
      IoRangeEntry = (IoBase & LPC_IO_PORT_ENTRY_BASE_MASK) |
                     (((Size - 1) << 16) & 0xFC0000) |
                     LPC_IO_PORT_ENTRY_ENABLE_BIT;

      *(UINT32 *)(MmioBase + 0x84 + EntryIndex * 4) = IoRangeEntry;
      PchPcrWriteReg ((UINT16)(4 * (EntryIndex + 2508)), 4, IoRangeEntry);
      return EFI_SUCCESS;
    }

    //
    // Check IoBase within range
    //
    {
      UINT16  RangeBase;
      UINT16  RangeLimit;

      RangeBase  = (UINT16)(CurrentEntry & LPC_IO_PORT_ENTRY_BASE_MASK);
      RangeLimit = (UINT16)((CurrentEntry >> 16) & 0x7FFF);

      if (IoBase >= RangeBase && IoBase < RangeBase + RangeLimit) {
        break;
      }
    }
  }

  if (EntryIndex < 4) {
    //
    // Overlap detected -- update existing range
    //
    IoRangeEntry = (IoBase & LPC_IO_PORT_ENTRY_BASE_MASK) |
                   (((Size - 1) << 16) & 0xFC0000) |
                   LPC_IO_PORT_ENTRY_ENABLE_BIT;

    *(UINT32 *)(MmioBase + 0x84 + EntryIndex * 4) = IoRangeEntry;
    PchPcrWriteReg ((UINT16)(4 * (EntryIndex + 2508)), 4, IoRangeEntry);
    return EFI_SUCCESS;
  }

  return EFI_OUT_OF_RESOURCES;
}


//
// sub_F0C -- Set up a custom IO range entry when DeviceType == 0xFF.
//            Handles LPC IO decode for a specific I/O address.
//            Called by sub_1038 for raw register access configurations.
//
STATIC
EFI_STATUS
SetLpcRegisterRaw (
  IN UINT64  PciCf8Address,
  IN UINT16  Register,
  IN UINT8   Size
  )
{
  UINT32  Value;
  UINT16  SlotBase;
  UINT16  SlotMask;
  UINT8   ShiftN;
  UINT8   ShiftM;

  //
  // Scan LPC IO decode registers at the given Cf8 address
  // to find which slot matches the requested register.
  //
  SlotBase = Register;
  SlotMask = (Size == 4) ? 0 : ((Size == 1) ? 0xFFFC : 0xFFF8);

  // ... register decoding and slot programming ...

  return EFI_SUCCESS;
}


//
// sub_1038 -- Core LPC IO cycle decode configuration.
//             Routes legacy device I/O ranges through the PCH LPC bridge.
//
//             @param[in] a1           SIO protocol instance or NULL
//             @param[in] IoConfig     Device-specific configuration
//             @param[in] SubType      Sub-type or size parameter
//             @param[in] DeviceType   Device type index (1-255)
//
STATIC
EFI_STATUS
ConfigureLpcDecode (
  IN  VOID    *SioProtocol,
  IN  UINT16  IoConfig,
  IN  UINT8   SubType,
  IN  UINT8   DeviceType
  )
{
  UINT64  MmioBase;

  MmioBase = SioMmioBase ();

  switch (DeviceType) {
  case 1:      // UART
    if (IoConfig != 0) {
      IoWrite16 ((UINT16)(MmioBase + 0x84), IoConfig);
    }
    break;

  case 2:      // LPT
  case 3:      // GPIO
  case 4:      // KBC
    IoWrite16 (SIO_CONFIG_PORT, SIO_UNLOCK_KEY);
    IoWrite16 (SIO_CONFIG_PORT, SIO_UNLOCK_KEY);
    IoWrite16 (SIO_CONFIG_PORT, SIO_REG_LDN);
    IoWrite16 (SIO_DATA_PORT, (UINT8)(MmioBase >> 8));
    IoWrite16 (SIO_CONFIG_PORT, SIO_REG_IO_BASE_LOW);
    IoWrite16 (SIO_DATA_PORT, 0);
    IoWrite16 (SIO_CONFIG_PORT, SIO_REG_IO_BASE_HIGH);
    IoWrite16 (SIO_DATA_PORT, 0);
    IoWrite16 (SIO_CONFIG_PORT, SIO_LOCK_DEVICE);
    break;

  case 5:      // PMC
    IoWrite16 (SIO_CONFIG_PORT, SIO_GLOBAL_LOCK);
    break;

  case 6:      // PME
    break;

  case 7:      // RTCT
    break;

  case 8:      // SUS
    break;

  case 0xFF:   // Raw register access
    SetLpcRegisterRaw (SioProtocol, IoConfig, SubType);
    break;

  default:
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}


//
// ---------------------------------------------------------------------------
// SIO Protocol Implementation (sub_948)
// ---------------------------------------------------------------------------

//
// sub_948 -- Initialize a specific SIO device (AST2500 DxeInit path)
//            Handles device configuration based on type.
//
STATIC
EFI_STATUS
EFIAPI
SioDeviceInit (
  IN SIO_DXE_INIT_PROTOCOL  *This,
  IN UINT16                 DeviceType,
  IN UINT16                 IoBase,
  IN UINT8                  IrqNumber,
  IN UINT8                  DmaChannel
  )
{
  return EFI_SUCCESS;
}


//
// ---------------------------------------------------------------------------
// Module Entry Point (sub_45C + _ModuleEntryPoint)
// ---------------------------------------------------------------------------

//
// sub_45C -- Driver entry-point initialization.
//            Sets up service table pointers, locates protocols,
//            configures PCH LPC I/O decode range, and installs
//            the SIO protocol interface.
//
STATIC
EFI_STATUS
EFIAPI
SioDxeInitPre (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Initialize UEFI service table pointers
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  gST = SystemTable;
  ASSERT (gST != NULL);

  gBS = SystemTable->BootServices;
  ASSERT (gBS != NULL);

  gRT = SystemTable->RuntimeServices;
  ASSERT (gRT != NULL);

  //
  // Locate DXE Services Table
  //
  Status = GetConfigTable (&gEfiDxeServicesTableGuid, (VOID **)&gDS);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
  }
  ASSERT (gDS != NULL);

  //
  // Locate MM PCI USRA protocol
  //
  if (mPciUsra == NULL) {
    Status = gBS->LocateProtocol (
                    &gEfiMmPciUsraProtocolGuid,
                    NULL,
                    (VOID **)&mPciUsra
                    );
    if (EFI_ERROR (Status)) {
      ASSERT_EFI_ERROR (Status);
    }
    ASSERT (mPciUsra != NULL);
  }

  //
  // Initialize HOB list
  //
  GetHobList ();

  //
  // Get PCD protocol and configure PCIe segment bus
  //
  GetPcdProtocol ();

  //
  // Install SIO protocol interface
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mDriverHandle,
                  &gEfiGenericSioProtocolGuid,
                  &mSioDxeInitProtocol,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
  }

  return Status;
}


//
// ---------------------------------------------------------------------------
// UEFI Module Entry Point (sub_3C0 -> _ModuleEntryPoint)
// ---------------------------------------------------------------------------

//
// _ModuleEntryPoint (at 0x3C0)
//   The UEFI DXE driver entry point.
//   Calls SioDxeInitPre to initialize, then installs the SIO protocol.
//
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS        Status;
  EFI_BOOT_SERVICES *BootServices;

  //
  // Initialize driver
  //
  Status = SioDxeInitPre (ImageHandle, SystemTable);

  //
  // Install protocol interface
  //
  BootServices = gBS;
  Status = BootServices->InstallMultipleProtocolInterfaces (
                           &mDriverHandle,
                           &gEfiGenericSioProtocolGuid,
                           &mSioDxeInitProtocol,
                           NULL
                           );

  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
  }

  return Status;
}


//
// ---------------------------------------------------------------------------
// SIO Protocol: SIO Device Read (SioDeviceRead stub)
// ---------------------------------------------------------------------------

STATIC
EFI_STATUS
EFIAPI
SioDeviceRead (
  IN  SIO_DXE_INIT_PROTOCOL *This,
  IN  UINT16                DeviceType,
  OUT VOID                  *Config
  )
{
  return EFI_UNSUPPORTED;
}


//
// ---------------------------------------------------------------------------
// SIO Protocol: SIO Device Write (SioDeviceWrite stub)
// ---------------------------------------------------------------------------

STATIC
EFI_STATUS
EFIAPI
SioDeviceWrite (
  IN SIO_DXE_INIT_PROTOCOL  *This,
  IN UINT16                 DeviceType,
  IN VOID                   *Config
  )
{
  return EFI_UNSUPPORTED;
}


//
// ---------------------------------------------------------------------------
// SIO Protocol: SIO Device Register (SioDeviceRegister stub)
// ---------------------------------------------------------------------------

STATIC
EFI_STATUS
EFIAPI
SioDeviceRegister (
  IN SIO_DXE_INIT_PROTOCOL  *This,
  IN UINT16                 DeviceType
  )
{
  return EFI_UNSUPPORTED;
}


//
// ---------------------------------------------------------------------------
// Protocol structure initialization
// ---------------------------------------------------------------------------

SIO_DXE_INIT_PROTOCOL  mSioDxeInitProtocol = {
  .Signature      = SIO_DXE_INIT_PROTOCOL_SIGNATURE,
  .Revision       = 0x00020000,
  .Reserved0      = 0,
  .Version        = sizeof (SIO_DXE_INIT_PROTOCOL),
  .Reserved1      = 0,
  .DeviceInit     = SioDeviceInit,
  .DeviceRead     = SioDeviceRead,
  .DeviceWrite    = SioDeviceWrite,
  .DeviceRegister = SioDeviceRegister,
  .IoRangeRegs    = { 0, 0, 0, 0 },
  .IoPortRegs     = { 0, 0, 0, 0, 0, 0, 0, 0 },
};


//
// ---------------------------------------------------------------------------
// GUID definitions and protocol references
// ---------------------------------------------------------------------------

//
// SIO Protocol GUID: {9D36F7EF-6078-4419-8C46-2BBDB0E0C7B3}
//
EFI_GUID gEfiGenericSioProtocolGuid = SIO_PROTOCOL_GUID;


//
// ---------------------------------------------------------------------------
// sub_680 -- SIO Driver Binding protocol "Start" stub
// ---------------------------------------------------------------------------

//
// sub_680 -- Driver binding Start() function.
//            Verifies controller handle and initializes SIO.
//
STATIC
EFI_STATUS
EFIAPI
SioDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  if (ControllerHandle == NULL ||
      *((EFI_HANDLE *)RemainingDevicePath) != NULL ||
      ControllerHandle == NULL ||
      *((UINT32 *)RemainingDevicePath + 2) != 0) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Initialize SIO protocol instance
  //
  ((EFI_DRIVER_BINDING_PROTOCOL *)ControllerHandle)->DriverBindingHandle =
    (EFI_HANDLE)((UINT8 *)ControllerHandle + 8);

  ((SIO_DXE_INIT_PROTOCOL *)ControllerHandle)->DeviceInit =
    (SIO_DEVICE_INIT)&unk_2600;

  ((SIO_DXE_INIT_PROTOCOL *)ControllerHandle)->DeviceInit (
    (SIO_DXE_INIT_PROTOCOL *)ControllerHandle, 0, 0, 0, 0);

  ((SIO_DXE_INIT_PROTOCOL *)ControllerHandle)->DeviceRegister =
    (SIO_DEVICE_REGISTER)"Generic SIO Driver";

  return EFI_SUCCESS;
}