Newer
Older
AMI-Aptio-BIOS-Reversed / AST2500PeiInit / AST2500PeiInit.c
@Ajax Dong Ajax Dong 2 days ago 29 KB Init
/**
 *AST2500PeiInit (0409) - Full Decompilation
 *
 *Module: AST2500PeiInit.efi
 *PE Index: 0409
 *Arch: IA32 (32-bit)
 *Base Address: 0xffe51614
 *Image Size: 0x1200
 *MD5: 105d4b74f818219f209297607f2e1f28
 *SHA256: 40afe5e3b14fb4d12a08eadfe576fefd9798fa88caeb4cc8abc08a4063b084d2
 *
 *This PEIM initializes AST2500 (ASPEED) Super I/O and GPIO configuration
 *during the PEI phase. It detects the AST2500 SIO, programs GPIO
 *configuration registers via PCI CF8/CF9 and SIO PM I/O ports (0x2E/0x2F).
 *
 *Source origin: edk2/MdePkg sub-libraries compiled together:
 * - BaseIoLibIntrinsic (IoLibMsc.c, IoLib.c)
 * - BasePciCf8Lib (PciCf8Lib.c)
 * - PeiServicesTablePointerLibIdt (PeiServicesTablePointer.c)
 * - BaseLib (X86ReadIdtr.c)
 *
 *These edk2 library functions were compiled as static-linked helper
 *functions (rather than importing from other modules), which is unusual
 *for PEI and suggests this module was compiled with specific build flags.
 */

/* ---------------------------------------------------------------------------
 *Forward declarations
 * --------------------------------------------------------------------------- */

int Ast2500PeiInitEntry(void);
char AstSioReadPmReg(int addr, int *out_value);
char AstSioWritePmReg(int addr, int value);

int AstPciCfgRead16(unsigned __int16 port);
unsigned __int16 AstPciCfgWrite16(unsigned __int16 port, unsigned __int16 value);
unsigned int __thiscall AstPciCfgRdAddr(unsigned __int16 port);
unsigned int AstPciCfgWrAddr(unsigned __int16 port, unsigned int value);
int AstPciCfgLibAssert(void);
int AstDebugAssert(int file, int line, int expr);

int AstPciCfgProgramBar(int func_addr, __int16 bar_size);
int AstGpioCfgProgram(int gpio_port, __int16 pin_mask, char flags, int function);
unsigned __int8 AstIoRmwSequence(int base, int count);
__int16 __thiscall AstPciCfgRead(unsigned int pci_addr);
__int16 AstPciCfgWrite(unsigned int pci_addr, unsigned __int16 value);
int AstPciCf8Read(unsigned int pci_addr);
int AstPciCf8Write(unsigned int pci_addr, int value);
__int16 AstIoWrite16(_WORD *addr, __int16 value);
int AstPeiServicesGetPtr(void);
void *__thiscall AstReadIdtr(void *this);

/* ---------------------------------------------------------------------------
 *Data Tables (located in .data section)
 *
 *GPIO_CONFIG_ENTRY: 8 bytes each
 *WORD gpio_num; // GPIO pin number (0-255)
 *BYTE and_mask; // AND mask to clear bits
 *DWORD or_value; // OR value to set bits (little-endian)
 *
 *IO_RMW_ENTRY: 4 bytes each
 *WORD io_port; // I/O port address
 *BYTE and_mask; // AND mask (0 = direct write, non-zero = RMW)
 *BYTE or_value; // OR value to write
 *
 *PM_REG_CONFIG: 12 bytes each
 *BYTE ldn_select; // LDN register to select (at offset i*4)
 *DWORD and_mask; // AND mask applied to register value (at dword_FFE52668[i])
 *DWORD or_value; // OR value applied to register value (at dword_FFE5266C[i])
 * --------------------------------------------------------------------------- */

/*GPIO config table for non-AST2500 SIO (4 entries) at 0xFFE52646:
 *GPIO2 (GPIOE0): and=0xFF, or=0x0A000000 (multi-function pin, SCU3C)
 *GPIO64 (GPIOD0): and=0xFF, or=0x03E00000
 *GPIO16 (GPIOA0): and=0xFF, or=0x02E00000
 *GPIO32 (GPIOB0): and=0xFF, or=0x00000000
 */
static const BYTE GpioTableNonAst[] = {
 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0A,
 0x40, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x03,
 0x10, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x02,
 0x20, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
};

/*GPIO config table for AST2500 SIO ID=0x21 (5 entries) at 0xFFE52716:
 *GPIO2 (GPIOE0): and=0xFF, or=0x02F80000 (multi-function pin)
 *GPIO2 (GPIOE0): and=0x06, or=0x0A000000 (extra config)
 *GPIO64 (GPIOD0): and=0xFF, or=0x03E00000
 *GPIO16 (GPIOA0): and=0xFF, or=0x02E00000
 *GPIO32 (GPIOB0): and=0xFF, or=0x00000000
 */
static const BYTE GpioTableAst[] = {
 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0x02,
 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x0A,
 0x40, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x03,
 0x10, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x02,
 0x20, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
};

/*I/O RMW Sequence table at 0xFFE526B4 (23 entries, 4 bytes each):
 *SIO unlock x2, select LDN 7, activate, set reg 0x30, clear,
 *repeat for each GPIO group, SIO lock
 *See full documentation in .md file.
 */

/*SIO PM Register Configuration (byte_FFE52664, dword_FFE52668, dword_FFE5266C)
 * ][ byte at i*4 = LDN register select
 * ][ dword at = AND mask
 * ][ dword at = OR value
 *
 *Base address: 0x1E6E2000 (AST2500 SCU register space)
 *9 groups of 3 registers each = 27 total register operations
 */

/* ---------------------------------------------------------------------------
 *PEI Module Entry Point
 * --------------------------------------------------------------------------- */

/**
 *Module entry point - thunk to Ast2500PeiInitEntry.
 *UEFI PEI entry with standard ImageHandle/SystemTable parameters.
 */
EFI_STATUS EFIAPI _ModuleEntryPoint (
 EFI_HANDLE ImageHandle,
 EFI_SYSTEM_TABLE *SystemTable
 )
{
 return Ast2500PeiInitEntry();
}

/**
 *AST2500 PEI Init main routine.
 *
 *1. Probe AST2500 SIO via PM port 0x72/0x73 at register 0x5C
 *2. If SIO ID = 0x21 (AST2500): configure 5 GPIO entries from AST table
 *else: configure 4 GPIO entries from non-AST table
 *3. Run 23-entry I/O RMW sequence (SIO port operations)
 *4. Program 27 PM registers at base 0x1E6E2000 in 3-entry groups
 * (read -> mask -> write pattern)
 *
 *Returns 0 (EFI_SUCCESS).
 */
int Ast2500PeiInitEntry()
{
 unsigned __int8 sio_id;
 char *gpio_ptr;
 int num_entries;
 unsigned int i;
 int pm_base;
 int pm_value;
 int temp; // [esp-4h] [ebp-14h]

 /*Probe AST2500 SIO via PM port 0x72/0x73 at register 0x5C */
 __outbyte(0x72u, 0x5Cu);
 sio_id = __inbyte(0x73u);

 if (sio_id == 33) { /*0x21 = AST2500 */
 gpio_ptr = (char *)&GpioTableAst;
 num_entries = 5;
 do {
 /*Each entry: WORD gpio_num, BYTE and_mask, DWORD or_value */
 AstGpioCfgProgram(temp, *((_WORD *)gpio_ptr - 1), *gpio_ptr, *(_DWORD *)(gpio_ptr + 2));
 gpio_ptr += 8;
 temp = temp; /*compiler artifact from LODWORD optimization */
 --num_entries;
 } while (num_entries);
 /*Use IO RMW table at 0xFFE526B4 (SIO setup) */
 temp = (int)&unk_FFE526B4;
 } else {
 gpio_ptr = (char *)&GpioTableNonAst;
 num_entries = 4;
 do {
 AstGpioCfgProgram(temp, *((_WORD *)gpio_ptr - 1), *gpio_ptr, *(_DWORD *)(gpio_ptr + 2));
 gpio_ptr += 8;
 temp = temp;
 --num_entries;
 } while (num_entries);
 temp = (int)&unk_FFE52744;
 }

 /*Apply 23-entry I/O RMW sequence */
 AstIoRmwSequence(temp, temp);

 /*Program 27 PM registers at base 0x1E6E2000 in 3-entry groups:
 *byte_FFE52664[i*4] = PM sub-register select (added to base 0x1E6E2000)
 *dword_FFE52668[i] = AND mask
 *dword_FFE5266C[i] = OR value
 *i increments by 3, so entries 0,3,6,...,24 (9 groups)
 */
 for (i = 0; i < 27; i += 3) {
 pm_base = (unsigned __int8)byte_FFE52664[i *4] | 0x1E6E2000;
 AstSioReadPmReg(pm_base, &pm_value);
 pm_value = dword_FFE5266C[i] | pm_value & dword_FFE52668[i];
 AstSioWritePmReg(pm_base, pm_value);
 }
 return 0;
}

/* ---------------------------------------------------------------------------
 *AST2500 Super I/O PM Register Access (SIO LPC PM I/O via 0x2E/0x2F)
 * --------------------------------------------------------------------------- */

/**
 *Read a 32-bit value from AST2500 PM register via SIO.
 *
 *Protocol:
 *1. SIO unlock: outb(0x2E, 0xA5) x2
 *2. Select LDN 0x0D (PM I/O device):
 *outb(0x2E, 7); outb(0x2F, 0x0D)
 *3. Enable LDN 0x0D:
 *outb(0x2E, 0x30); reg = inb(0x2F); outb(0x2F, reg | 1)
 *4. Write register address via SIO index registers:
 *F0 = HIBYTE(addr), F1 = BYTE2(addr),
 *F2 = BYTE1(addr), F3 = LOBYTE(addr)
 *5. Trigger read: set F8 to mode 2, dummy read FE
 *6. Read result: F4=byte0, F5=byte1, F6=byte2, F7=byte3
 *7. SIO lock: outb(0x2E, 0xAA)
 *
 * @param addr AST2500 PM register address (SCU space)
 * @param out_value Pointer to receive 32-bit register value
 * @return 0xAA (unlock byte value)
 */
char AstSioReadPmReg(int addr, int *out_value)
{
 unsigned __int8 b0, b1, b2, b3;
 unsigned __int8 reg;

 /*SIO unlock sequence */
 __outbyte(0x2Eu, 0xA5u);
 __outbyte(0x2Eu, 0xA5u);

 /*Select logical device 0x0D (PM I/O device) */
 __outbyte(0x2Eu, 7u);
 __outbyte(0x2Fu, 0xDu);

 /*Enable LDN */
 __outbyte(0x2Eu, 0x30u);
 reg = __inbyte(0x2Fu);
 __outbyte(0x2Fu, reg | 1);

 /*Write register address (32-bit address in 4 bytes) */
 __outbyte(0x2Eu, 0xF0u);
 __outbyte(0x2Fu, HIBYTE(addr));
 __outbyte(0x2Eu, 0xF1u);
 __outbyte(0x2Fu, BYTE2(addr));
 __outbyte(0x2Eu, 0xF2u);
 __outbyte(0x2Fu, BYTE1(addr));
 __outbyte(0x2Eu, 0xF3u);
 __outbyte(0x2Fu, addr);

 /*Trigger read: set F8[1:0] = 0b10 (read mode) */
 __outbyte(0x2Eu, 0xF8u);
 reg = __inbyte(0x2Fu);
 __outbyte(0x2Fu, reg & 0xFC | 2);

 /*Flush / sync */
 __outbyte(0x2Eu, 0xFEu);
 __inbyte(0x2Fu); /*dummy read for synchronization */

 /*Read result bytes: F4-F7 = 4 data bytes */
 __outbyte(0x2Eu, 0xF4u);
 b0 = __inbyte(0x2Fu);
 __outbyte(0x2Eu, 0xF5u);
 b1 = __inbyte(0x2Fu);
 __outbyte(0x2Eu, 0xF6u);
 b2 = __inbyte(0x2Fu);
 __outbyte(0x2Eu, 0xF7u);
 b3 = __inbyte(0x2Fu);

 /*Combine into 32-bit value: b3:b2:b1:b0 (big-endian byte order) */
 *out_value = b3 | ((b2 | ((b1 | (b0 << 8)) << 8)) << 8);

 /*SIO lock */
 __outbyte(0x2Eu, 0xAAu);
 return 0xAA;
}

/**
 *Write a 32-bit value to AST2500 PM register via SIO.
 *
 *Same protocol as read, but:
 *5b. Write value bytes F4-F7
 *6b. Trigger write via F8
 *7b. CF9 flush register via FE
 *
 * @param addr AST2500 PM register address
 * @param value 32-bit value to write
 * @return 0xAA (unlock byte value)
 */
char AstSioWritePmReg(int addr, int value)
{
 unsigned __int8 reg;

 /*SIO unlock sequence */
 __outbyte(0x2Eu, 0xA5u);
 __outbyte(0x2Eu, 0xA5u);

 /*Select logical device 0x0D (PM I/O device) */
 __outbyte(0x2Eu, 7u);
 __outbyte(0x2Fu, 0xDu);

 /*Enable LDN */
 __outbyte(0x2Eu, 0x30u);
 reg = __inbyte(0x2Fu);
 __outbyte(0x2Fu, reg | 1);

 /*Write register address bytes */
 __outbyte(0x2Eu, 0xF0u);
 __outbyte(0x2Fu, HIBYTE(addr));
 __outbyte(0x2Eu, 0xF1u);
 __outbyte(0x2Fu, BYTE2(addr));
 __outbyte(0x2Eu, 0xF2u);
 __outbyte(0x2Fu, BYTE1(addr));
 __outbyte(0x2Eu, 0xF3u);
 __outbyte(0x2Fu, addr);

 /*Write value bytes F4-F7 (big-endian: F4=MSB, F7=LSB) */
 __outbyte(0x2Eu, 0xF4u);
 __outbyte(0x2Fu, HIBYTE(value));
 __outbyte(0x2Eu, 0xF5u);
 __outbyte(0x2Fu, BYTE2(value));
 __outbyte(0x2Eu, 0xF6u);
 __outbyte(0x2Fu, BYTE1(value));
 __outbyte(0x2Eu, 0xF7u);
 __outbyte(0x2Fu, value);

 /*Trigger write via F8 */
 __outbyte(0x2Eu, 0xF8u);
 reg = __inbyte(0x2Fu);
 __outbyte(0x2Fu, reg & 0xFC | 2);

 /*CF9 flush: write 0xCF to FE register */
 __outbyte(0x2Eu, 0xFEu);
 __outbyte(0x2Fu, 0xCFu);

 /*SIO lock */
 __outbyte(0x2Eu, 0xAAu);
 return 0xAA;
}

/* ---------------------------------------------------------------------------
 *I/O Port Primitives (edk2 IoLibMsc.c wrappers)
 *These are inline I/O port access with alignment assertions.
 * --------------------------------------------------------------------------- */

/**
 *Read 16-bit value from I/O port.
 *From BaseIoLibIntrinsic/IoLibMsc.c line 0x85.
 */
int AstPciCfgRead16(unsigned __int16 port)
{
 int Device;
 int result;

 if ((port & 1) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BaseIoLibIntrinsic\\IoLibMsc.c",
 133,
 "(Port & 1) == 0");
 }
 }
 LOWORD(result) = __inword(port);
 return (unsigned __int16)result;
}

/**
 *Write 16-bit value to I/O port.
 *From BaseIoLibIntrinsic/IoLibMsc.c line 0xA3.
 */
unsigned __int16 AstPciCfgWrite16(unsigned __int16 port, unsigned __int16 value)
{
 int Device;

 if ((port & 1) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BaseIoLibIntrinsic\\IoLibMsc.c",
 163,
 "(Port & 1) == 0");
 }
 }
 __outword(port, value);
 return value;
}

/**
 *Read 32-bit from I/O port (used for PCI CF8 address register).
 *From BaseIoLibIntrinsic/IoLibMsc.c line 0xC1.
 */
unsigned int __thiscall AstPciCfgRdAddr(unsigned __int16 port)
{
 int Device;

 if ((port & 3) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BaseIoLibIntrinsic\\IoLibMsc.c",
 193,
 "(Port & 3) == 0");
 }
 }
 return __indword(port);
}

/**
 *Write 32-bit to I/O port (used for PCI CF8 address register).
 *From BaseIoLibIntrinsic/IoLibMsc.c line 0xDF.
 */
unsigned int AstPciCfgWrAddr(unsigned __int16 port, unsigned int value)
{
 int Device;

 if ((port & 3) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BaseIoLibIntrinsic\\IoLibMsc.c",
 223,
 "(Port & 3) == 0");
 }
 }
 __outdword(port, value);
 return value;
}

/* ---------------------------------------------------------------------------
 *PEI Assert / Debug Helpers
 * --------------------------------------------------------------------------- */

/**
 *Get PEI Services pointer and locate a PPI by GUID for assertion reporting.
 *
 *Locates PPI referenced by unk_FFE52634 (which appears to be a GUID)
 *via EFI_PEI_SERVICES->LocatePpi (offset +0x20 from service table).
 *Returns pointer to the found PPI instance's protocol interface.
 *
 *GUID at 0xFFE52634: 36 29 23 36 76 0E C8 31 A1 3A 3A F2 FC 1C 39 32
 *
 * @return Pointer to PPI protocol interface, or 0 on failure.
 */
int AstPciCfgLibAssert()
{
 int pei_services;
 int v2; // [esp+0h] [ebp-8h] PPI GUID ptr int Result; // [esp+4h] [ebp-4h] found PPI instance pei_services = AstPeiServicesGetPtr();
 if ((*(int ( **)(int, void *, _DWORD, int *, int *))(*(_DWORD *)pei_services + 32))(
 pei_services, &unk_FFE52634, 0, &v2, &Result) >= 0) {
 return Result; /*Return PPI protocol pointer */
 } else {
 return 0;
 }
}

/**
 *Debug assertion handler - reports assertion through PEI services PPI.
 *
 *Calls ReportStatusCode-style function at offset +4 in the found PPI
 *instance's interface.
 *
 * @param file Source file path string
 * @param line Line number
 * @param expr Failed expression string
 * @return Result from status code reporter, or 0.
 */
int AstDebugAssert(int file, int line, int expr)
{
 int result;

 result = AstPciCfgLibAssert();
 if (result) {
 return (*(int ( **)(int, int, int))(result + 4))(file, line, expr);
 }
 return result;
}

/* ---------------------------------------------------------------------------
 *GPIO / PCI BAR Programming
 * --------------------------------------------------------------------------- */

/**
 *Program a PCI BAR register with the given size mask.
 *
 *Searches 4 BAR registers at function-relative PCI config space,
 *computes alignment from size, writes BAR value both to PCI CF8
 *and to memory-mapped I/O at 0xFDEF0000+offset.
 *
 * @param func_addr PCI function address (bus/dev/func)
 * @param bar_size Desired BAR size in bytes
 * @return 0 on success, -2147483639 (EFI_NOT_FOUND) if no free BAR
 */
int AstPciCfgProgramBar(int func_addr, __int16 bar_size)
{
 unsigned __int8 Size;
 unsigned __int16 i;
 __int16 bar_val;
 unsigned __int8 bit_count;
 unsigned __int16 n4;
 int bar_cfg;

 /*Search 4 BARs for a match or empty slot */
 for (i = 0; i < 4u; ++i) {
 bar_val = AstPciCf8Read(4 *i + 1015940); /*PCI func base + 4 (BAR0-3) */
 if ((bar_val & 1) == 0) /*BAR not enabled - empty slot */
 break;
 if ((bar_val & 0xFFFC) == bar_size) /*BAR address match */
 break;
 }

 if (i == 4)
 return -2147483639; /*EFI_NOT_FOUND */

 /*Calculate alignment bits from size */
 bit_count = 0;
 n4 = 4;
 do {
 n4 >>= 1;
 ++bit_count;
 } while ((n4 & 1) == 0);

 Size = 0;
 n4 = 4;
 do {
 ++Size;
 n4 >>= 1;
 } while (n4);

 if (bit_count == Size - 1)
 --Size;

 /*Build BAR value: [31:16]=upper bits of size mask, [15:2]=aligned address */
 bar_cfg = ((((1 << Size) - 1) & 0xFFFC) << 16) |
 (unsigned __int16)(bar_size & ~((1 << Size) - 1)) | 1;

 AstPciCf8Write(4 *i + 1015940, bar_cfg);
 /*Write to MMIO copy at 0xFDEF0000 + BAR offset */
 *(_DWORD *)((unsigned __int16)(4 *i + 10032) | 0xFDEF0000) = bar_cfg;

 return 0;
}

/**
 *Program AST2500 GPIO configuration via PCI CF8 at bus 0xF8, dev 0x80.
 *
 *Configures GPIO pin/pad function, direction, drive strength, pull-up/down,
 *and Schmitt trigger based on function code (n5):
 *
 *n5=1: GPIO direction control (input/output)
 * - pin_mask=0: returns error EFI_INVALID_PARAMETER (-9)
 * - pin_mask >0: searches table of valid GPIO directions (1008, 1016, 888, 512...)
 *
 *n5=2..5: Simple GPIO config (drive type)
 * - pin_mask depends on table entries
 *
 *n5=6: Drive strength control (2mA/4mA/8mA/12mA)
 * - Searches entry table at 0xFFE5266C
 * - a3=0: strength mode select, a3!=0: per-pin config
 *
 *n5=7: Internal pull-up/pull-down
 * - Searches entry table at 0xFFE526C0
 *
 *n5=8: Schmitt trigger enable/disable
 * - 520 = special register
 *
 *n5=255: Special modes
 * - 46 = SCU config (4096)
 * - 78 = SCU config (8192)
 * - 98 = SCU config (2048)
 * - others: AstPciCfgProgramBar
 *
 *All paths apply config via PCI at 0xF8080/0xF8082 (AST2500 SCU PCI
 *config) and MMIO at 0xFDEF2770/0xFDEF2774.
 */
int AstGpioCfgProgram(
 int a1,
 __int16 n1008, /*GPIO pin number or special selector */
 char a3, /*Sub-flag (drive strength mode, etc.) */
 int n5) /*Function code: 1=dir, 6=drive, 7=pull, 8=schmitt, 255=special */
{
 __int16 v4; /*Combined bit mask for or_mask */
 __int16 v5; /*Combined AND mask */
 __int16 n8_1; /*Data value for CF9 write (low 16 bits) */
 __int16 v7; /*AND mask for CF9 data (high 16 bits) */
 unsigned __int8 v9;
 __int16 n888;
 __int16 v11;
 unsigned __int8 v12;
 __int16 n1016;
 unsigned __int8 v14;
 __int16 n1008_1;
 __int16 n8; /*Shifted OR value component */
 int n57672688; /*GPIO direction table at 0xFFE526C0 */
 __int16 v18; /*Reserved/padding */
 _DWORD v19[2]; /*Pull-up table entries (888, ...) */
 _DWORD v20[5]; /*Drive strength table (1016, 1008, 888, other, ...) */
 __int16 v21; /*Reserved */
 int n5a; /*PCI CF8 value for 0xF8080 */
 int n5b; /*PCI CF8 value for 0xF8082 */

 /*Initialize lookup tables (these are stack-local copies of const data) */
 v20[0] = 49808376; /*GPIO direction-related constants */
 v21 = 0;
 v4 = 0;
 v18 = 0;
 v5 = -1;
 n8_1 = 0;
 v20[1] = 36176416;
 v20[2] = 48759352;
 v20[3] = 65536824;
 v20[4] = 48235248;
 v19[0] = 41419640;
 v19[1] = 956;
 n57672688 = 57672688;
 v7 = -1;

 if (n5 == 1) {
 /*GPIO Direction mode */
 if (!n1008) {
 v7 = -9; /*EFI_INVALID_PARAMETER */
 goto WRITE_CONFIG;
 }
 /*Search valid GPIO direction table */
 v14 = 0;
 n1008_1 = 1008;
 do {
 if (n1008_1 == n1008)
 break;
 n1008_1 = *((_WORD *)&n57672688 + ++v14);
 } while (n1008_1);

 if (!*((_WORD *)&n57672688 + v14))
 return -2147483645; /*EFI_INVALID_PARAMETER */

 v5 = -4097;
 n8 = 8;
 v11 = v14 << 12;
 goto COMBINE_MASK;
 }

 if (n5 <= 1)
 return -2147483645;

 if (n5 <= 5) {
 /*Simple GPIO config modes 2-5 */
 v7 = n1008 == 0 ? -1025 : -1;
 n8_1 = n1008 != 0 ? 0x400 : 0;
 goto WRITE_CONFIG;
 }

 switch (n5) {
 case 6: /*Drive Strength mode */
 if (!n1008) {
 v7 = (a3 == 0) - 3;
 goto WRITE_CONFIG;
 }
 /*Search drive strength table */
 v12 = 0;
 n1016 = 1016;
 do {
 if (n1016 == n1008)
 break;
 n1016 = *((_WORD *)v20 + ++v12);
 } while (n1016);

 if (!*((_WORD *)v20 + v12))
 return -2147483645;

 if (!a3) {
 v4 = v12;
 n8_1 = 1;
 v5 = -8;
 goto WRITE_CONFIG;
 }
 v5 = -113;
 n8 = 2;
 v11 = 16 *v12;
 goto COMBINE_MASK;

 case 7: /*Internal Pull-up/Pull-down mode */
 if (!n1008) {
 v7 = -5;
 goto WRITE_CONFIG;
 }
 v9 = 0;
 n888 = 888;
 do {
 if (n888 == n1008)
 break;
 n888 = *((_WORD *)v19 + ++v9);
 } while (n888);

 if (!*((_WORD *)v19 + v9))
 return -2147483645;

 v5 = -769;
 n8 = 4;
 v11 = v9 << 8;

COMBINE_MASK:
 n8_1 = n8;
 v4 = v11;
 goto WRITE_CONFIG;

 case 8: /*Schmitt Trigger mode */
 if (!n1008) {
 v7 = -769;
 goto WRITE_CONFIG;
 }
 n8_1 = 512;
 if (n1008 == 512) {
 n8_1 = 256;
 goto WRITE_CONFIG;
 }
 if (n1008 == 520)
 goto WRITE_CONFIG;
 return -2147483645;
 }

 if (n5 != 255)
 return -2147483645;

 /*Special modes */
 if (n1008 != 46) {
 if (n1008 == 78) {
 n8_1 = 0x2000;
 goto WRITE_CONFIG;
 }
 if (n1008 == 98) {
 n8_1 = 2048;
 goto WRITE_CONFIG;
 }
 if (n1008) {
 AstPciCfgProgramBar(a1, n1008);
 goto WRITE_CONFIG;
 }
 return -2147483645;
 }
 n8_1 = 4096; /*GPIO46 = 0x1000 */

WRITE_CONFIG:
 /*Apply configuration via PCI CF8 at 0xF8080 (AST SCU config port) */
 n5a = (unsigned __int16)(v4 | v5 & AstPciCfgRead(0xF8080u));
 AstPciCfgWrite(0xF8080u, n5a);
 AstIoWrite16((_WORD *)0xFDEF2770, n5a);

 n5b = (unsigned __int16)(n8_1 | v7 & AstPciCfgRead(0xF8082u));
 AstPciCfgWrite(0xF8082u, n5b);
 AstIoWrite16((_WORD *)0xFDEF2774, n5b);

 return 0;
}

/* ---------------------------------------------------------------------------
 *I/O Register Read-Modify-Write Sequence
 * --------------------------------------------------------------------------- */

/**
 *Execute a sequence of 23 I/O register operations from a table.
 *
 *Each table entry is a 4-byte structure:
 *WORD io_port; // Target I/O port
 *BYTE and_mask; // AND mask (0 = direct write, non-zero = RMW)
 *BYTE or_value; // OR value to apply
 *
 *If and_mask == 0: direct write of or_value to io_port
 *If and_mask != 0: read io_port, AND with and_mask, OR with or_value, write back
 *
 *The table at 0xFFE526B4 performs SIO LDN mode setup:
 * - Unlocks SIO (0xA5 to port 0x2E, twice)
 * - Selects various logical devices via LDN setup
 * - Programs activation register (0x30) for GPIO groups
 * - Locks SIO (0xAA to port 0x2E)
 *
 * @param base Unused (first parameter, passed through)
 * @param count Used as pointer offset to table (adjusted by +3)
 * @return Last written value
 */
unsigned __int8 AstIoRmwSequence(int base, int count)
{
 unsigned __int8 *v2; /*Pointer into table, starting at count+3 */
 int n23; /*23 entries */
 unsigned __int8 Result;
 unsigned __int8 v5;
 unsigned __int8 result;

 v2 = (unsigned __int8 *)(count + 3);
 n23 = 23;
 do {
 /*v2[-3..-2] = port (WORD), v2[-1] = and_mask (BYTE), v2[0] = or_value (BYTE) */
 if (*(v2 - 1)) {
 /*RMW: read current port value, mask, then OR */
 v5 = __inbyte(*(_WORD *)(v2 - 3));
 Result = *v2 | v5 & *(v2 - 1);
 } else {
 /*Direct write */
 Result = *v2;
 }
 result = Result;
 __outbyte(*(_WORD *)(v2 - 3), Result);
 v2 += 4;
 --n23;
 } while (n23);

 return result;
}

/* ---------------------------------------------------------------------------
 *PCI CF8/CF9 Access Layer (edk2 BasePciCf8Lib)
 *
 *Standard PCI Configuration Mechanism #1 via ports 0xCF8/0xCFC.
 *CF8 (0xCF8) = PCI Configuration Address
 *CFC (0xCFC) = PCI Configuration Data
 *
 *CF8 format (PCI Enhanced Addressing):
 *bit 31 = 1 (enable)
 *bits 30:24 = reserved
 *bits 23:16 = bus
 *bits 15:11 = device
 *bits 10:8 = function
 *bits 7:0 = register (with bit 0 = byte enable for 16-bit access)
 *
 *These functions save/restore the CF8 register and manage EFLAGS.IF.
 * --------------------------------------------------------------------------- */

/**
 *Read 16-bit PCI configuration register via CF8/CF9 protocol.
 *From BasePciCf8Lib/PciCf8Lib.c line 613.
 *
 * @param pci_addr PCI config address (bus/dev/func/reg encoded)
 * @return 16-bit register value
 */
__int16 __thiscall AstPciCfgRead(unsigned int pci_addr)
{
 int Device;
 __int16 eflags;
 unsigned int saved_cfg_addr;
 __int16 result;
 __int16 saved_eflags;

 /*Assert proper address format (bit 0 and bits 24+0 clear) */
 if ((pci_addr & 0xF0000F01) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BasePciCf8Lib\\PciCf8Lib.c",
 613,
 "((Address) & (~0xffff0ff | (1))) == 0");
 }
 }

 /*Disable interrupts for atomic PCI config cycle */
 eflags = __readeflags();
 saved_eflags = eflags;
 _disable();

 /*Save CF8, then write new address */
 saved_cfg_addr = AstPciCfgRdAddr(3320); /*0xCF8 */
 AstPciCfgWrAddr(3320, pci_addr & 0xFC | (pci_addr >> 4) & 0xFFFF00 | 0x80000000);

 /*Read from CF9 (0xCFC + byte offset = 0xCFC or 0xCFE) */
 result = AstPciCfgRead16((pci_addr & 2) + 3324); /*0xCFC or 0xCFE */

 /*Restore CF8 */
 AstPciCfgWrAddr(3320, saved_cfg_addr);

 /*Restore interrupt flag */
 if ((saved_eflags & 0x200) != 0)
 _enable();
 else _disable();

 return result;
}

/**
 *Write 16-bit PCI configuration register via CF8/CF9 protocol.
 *From BasePciCf8Lib/PciCf8Lib.c line 652.
 *
 * @param pci_addr PCI config address
 * @param value 16-bit value to write
 * @return Written value
 */
__int16 AstPciCfgWrite(unsigned int pci_addr, unsigned __int16 value)
{
 int Device;
 __int16 eflags;
 unsigned int saved_cfg_addr;
 __int16 result;
 __int16 saved_eflags;

 if ((pci_addr & 0xF0000F01) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BasePciCf8Lib\\PciCf8Lib.c",
 652,
 "((Address) & (~0xffff0ff | (1))) == 0");
 }
 }

 eflags = __readeflags();
 saved_eflags = eflags;
 _disable();

 saved_cfg_addr = AstPciCfgRdAddr(3320);
 AstPciCfgWrAddr(3320, pci_addr & 0xFC | (pci_addr >> 4) & 0xFFFF00 | 0x80000000);

 result = AstPciCfgWrite16((pci_addr & 2) + 3324, value);

 AstPciCfgWrAddr(3320, saved_cfg_addr);

 if ((saved_eflags & 0x200) != 0)
 _enable();
 else _disable();

 return result;
}

/**
 *Read 32-bit DWORD PCI configuration via CF8/CF9.
 *From BasePciCf8Lib/PciCf8Lib.c line 1114.
 *
 * @param pci_addr PCI config address (DWORD-aligned in low bits)
 * @return 32-bit register value
 */
int AstPciCf8Read(unsigned int pci_addr)
{
 int Device;
 __int16 eflags;
 unsigned int saved_cfg_addr;
 int result;
 __int16 saved_eflags;

 if ((pci_addr & 0xF0000F03) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BasePciCf8Lib\\PciCf8Lib.c",
 1114,
 "((Address) & (~0xffff0ff | (3))) == 0");
 }
 }

 eflags = __readeflags();
 saved_eflags = eflags;
 _disable();

 saved_cfg_addr = AstPciCfgRdAddr(3320);
 AstPciCfgWrAddr(3320, pci_addr & 0xFC | (pci_addr >> 4) & 0xFFFF00 | 0x80000000);

 result = AstPciCfgRdAddr(3324); /*0xCFC */

 AstPciCfgWrAddr(3320, saved_cfg_addr);

 if ((saved_eflags & 0x200) != 0)
 _enable();
 else _disable();

 return result;
}

/**
 *Write 32-bit DWORD PCI configuration via CF8/CF9.
 *From BasePciCf8Lib/PciCf8Lib.c line 1153.
 *
 * @param pci_addr PCI config address (DWORD-aligned in low bits)
 * @param value 32-bit value to write
 * @return Written address value
 */
int AstPciCf8Write(unsigned int pci_addr, int value)
{
 int Device;
 __int16 eflags;
 unsigned int saved_cfg_addr;
 int result;
 __int16 saved_eflags;

 if ((pci_addr & 0xF0000F03) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BasePciCf8Lib\\PciCf8Lib.c",
 1153,
 "((Address) & (~0xffff0ff | (3))) == 0");
 }
 }

 eflags = __readeflags();
 saved_eflags = eflags;
 _disable();

 saved_cfg_addr = AstPciCfgRdAddr(3320);
 AstPciCfgWrAddr(3320, pci_addr & 0xFC | (pci_addr >> 4) & 0xFFFF00 | 0x80000000);

 result = AstPciCfgWrAddr(3324, value); /*0xCFC */

 AstPciCfgWrAddr(3320, saved_cfg_addr);

 if ((saved_eflags & 0x200) != 0)
 _enable();
 else _disable();

 return result;
}

/* ---------------------------------------------------------------------------
 *16-bit Memory-Mapped I/O Write (IoLib)
 * --------------------------------------------------------------------------- */

/**
 *Write 16-bit value to memory-mapped I/O address with alignment check.
 *From BaseIoLibIntrinsic/IoLib.c line 183.
 *
 * @param addr I/O address (must be 2-byte aligned)
 * @param value 16-bit value to write
 * @return Written value
 */
__int16 AstIoWrite16(_WORD *addr, __int16 value)
{
 int Device;

 if (((unsigned __int8)addr & 1) != 0) {
 Device = AstPciCfgLibAssert();
 if (Device) {
 (*(void ( **)(const char *, int, const char *))(Device + 4))(
 "e:\\hs\\MdePkg\\Library\\BaseIoLibIntrinsic\\IoLib.c",
 183,
 "(Address & 1) == 0");
 }
 }
 *addr = value;
 return value;
}

/* ---------------------------------------------------------------------------
 *PEI Services / Interrupt Descriptor Table Access
 * --------------------------------------------------------------------------- */

/**
 *Get PEI Services pointer from IDT-based location.
 *
 *The PEI Services table pointer is stored at IDT base - 4 in memory.
 *On IA32, the IDTR (interrupt descriptor table register) contains
 *the base address of the IDT; the PEI services pointer is stored
 *in the 4 bytes immediately preceding the IDT base.
 *
 *From PeiServicesTablePointerLibIdt/PeiServicesTablePointer.c.
 *
 * @return Pointer to EFI_PEI_SERVICES, or asserts if NULL
 */
int AstPeiServicesGetPtr()
{
 int pei_services;
 _BYTE idtr[8]; /*IDTR storage: 2 bytes limit + 4 bytes base */

 AstReadIdtr(idtr); /*SIDT instruction */
 pei_services = *(_DWORD *)(*(_DWORD *)&idtr[2] - 4); /*IDT base - 4 = PeiServices */

 if (!pei_services) {
 AstDebugAssert(
 (int)"e:\\hs\\MdePkg\\Library\\PeiServicesTablePointerLibIdt\\PeiServicesTablePointer.c",
 48,
 (int)"PeiServices != ((void *) 0)");
 }
 return pei_services;
}

/**
 *Read Interrupt Descriptor Table Register.
 *
 *On IA32, the SIDT instruction stores 6 bytes:
 *bytes 0-1: IDT limit (size - 1)
 *bytes 2-5: IDT base address (linear address)
 *
 *From BaseLib/X86ReadIdtr.c.
 *
 * @param this Pointer to 8-byte buffer for IDT register
 * @return Pointer to buffer
 */
void *__thiscall AstReadIdtr(void *this)
{
 if (!this) {
 AstDebugAssert(
 (int)"e:\\hs\\MdePkg\\Library\\BaseLib\\X86ReadIdtr.c",
 37,
 (int)"Idtr != ((void *) 0)");
 }
 __sidt(this);
 return this;
}