#------------------------------------------------------------------------------
#
# Copyright (c) 2013 - 2016 Intel Corporation.
#
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution.  The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
# Module Name:
#
#  Flat32.S
#
# Abstract:
#
#  This is the code that goes from real-mode to protected mode.
#  It consumes the reset vector, configures the stack.
#
#
#------------------------------------------------------------------------------

.macro RET32
    jmp    *%esp
.endm

#
# ROM/SPI/MEMORY Definitions
#
.equ  QUARK_DDR3_MEM_BASE_ADDRESS, (0x000000000)    # Memory Base Address = 0
.equ  QUARK_MAX_DDR3_MEM_SIZE_BYTES, (0x80000000)    # DDR3 Memory Size = 2GB
.equ  QUARK_ESRAM_MEM_SIZE_BYTES, (0x00080000)    # eSRAM Memory Size = 512K
.equ  QUARK_STACK_SIZE_BYTES, (0x008000)      # Quark stack size = 32K

#
# RTC/CMOS definitions
#
.equ  RTC_INDEX, (0x70)
.equ    NMI_DISABLE, (0x80)  # Bit7=1 disables NMI
.equ    NMI_ENABLE, (0x00)  # Bit7=0 disables NMI
.equ  RTC_DATA, (0x71)

#
# PCI Configuration definitions
#
.equ  PCI_CFG, (0x80000000) # PCI configuration access mechanism
.equ  PCI_ADDRESS_PORT, (0xCF8)
.equ  PCI_DATA_PORT, (0xCFC)

#
# Quark PCI devices
#
.equ  HOST_BRIDGE_PFA, (0x0000)   # B0:D0:F0 (Host Bridge)
.equ  ILB_PFA, (0x00F8)      # B0:D31:F0 (Legacy Block)

#
# ILB PCI Config Registers
#
.equ  BDE, (0x0D4)                                # BIOS Decode Enable register
.equ    DECODE_ALL_REGIONS_ENABLE, (0xFF000000)    # Decode all BIOS decode ranges

#
# iLB Reset Register
#
.equ  ILB_RESET_REG, (0x0CF9)
.equ    CF9_WARM_RESET, (0x02)
.equ    CF9_COLD_RESET, (0x08)

#
# Host Bridge PCI Config Registers
#
.equ  MESSAGE_BUS_CONTROL_REG, (0xD0)       # Message Bus Control Register
.equ    SB_OPCODE_FIELD, (0x18)              # Bit location of Opcode field
.equ      OPCODE_SIDEBAND_REG_READ, (0x10)  # Read opcode
.equ      OPCODE_SIDEBAND_REG_WRITE, (0x11) # Write opcode
.equ      OPCODE_SIDEBAND_ALT_REG_READ, (0x06)  # Alternate Read opcode
.equ      OPCODE_SIDEBAND_ALT_REG_WRITE, (0x07) # Alternate Write opcode
.equ      OPCODE_WARM_RESET_REQUEST, (0xF4)  # Reset Warm
.equ      OPCODE_COLD_RESET_REQUEST, (0xF5) # Reset Cold
.equ    SB_PORT_FIELD, (0x10)               # Bit location of Port ID field
.equ      MEMORY_ARBITER_PORT_ID, (0x00)
.equ      HOST_BRIDGE_PORT_ID, (0x03)
.equ      RMU_PORT_ID, (0x04)
.equ      MEMORY_MANAGER_PORT_ID, (0x05)
.equ      SOC_UNIT_PORT_ID, (0x31)
.equ    SB_ADDR_FIELD, (0x08)               # Bit location of Register field
.equ    SB_BE_FIELD, (0x04)                  # Bit location of Byte Enables field
.equ      ALL_BYTE_EN, (0x0F)                # All Byte Enables
.equ  MESSAGE_DATA_REG, (0xD4)              # Message Data Register

#
# Memory Arbiter Config Registers
#
.equ  AEC_CTRL_OFFSET, (0x00)

#
# Host Bridge Config Registers
#
.equ  HMISC2_OFFSET, (0x03) # PCI configuration access mechanism
.equ    OR_PM_FIELD, (0x10)
.equ      SMI_EN, (0x00080000)

.equ  HMBOUND_OFFSET, (0x08)
.equ    HMBOUND_ADDRESS, (QUARK_DDR3_MEM_BASE_ADDRESS + QUARK_MAX_DDR3_MEM_SIZE_BYTES + QUARK_ESRAM_MEM_SIZE_BYTES)
.equ    HMBOUND_LOCK, (0x01)
.equ  HECREG_OFFSET, (0x09)
.equ    EC_BASE, (0xE0000000)
.equ    EC_ENABLE, (0x01)
.equ  HLEGACY_OFFSET, (0x0A)
.equ    NMI, (0x00004000)
.equ    SMI, (0x00001000)
.equ    INTR, (0x00000400)

#
# Memory Manager Config Registers
#
.equ  ESRAMPGCTRL_BLOCK_OFFSET, (0x82)
.equ    BLOCK_ENABLE_PG, (0x10000000)
.equ  BIMRVCTL_OFFSET, (0x19)
.equ    ENABLE_IMR_INTERRUPT, (0x80000000)

#
# SOC UNIT Debug Registers
#
.equ  CFGSTICKY_W1_OFFSET, (0x50)
.equ    FORCE_COLD_RESET, (0x00000001)
.equ  CFGSTICKY_RW_OFFSET, (0x51)
.equ    RESET_FOR_ESRAM_LOCK, (0x00000020)
.equ    RESET_FOR_HMBOUND_LOCK, (0x00000040)
.equ  CFGNONSTICKY_W1_OFFSET, (0x52)
.equ    FORCE_WARM_RESET, (0x00000001)

#
# CR0 cache control bit definition
#
.equ                    CR0_CACHE_DISABLE, 0x040000000
.equ                    CR0_NO_WRITE,      0x020000000

ASM_GLOBAL  ASM_PFX(PcdGet32(PcdEsramStage1Base))


#
# Contrary to the name, this file contains 16 bit code as well.
#
.text
#----------------------------------------------------------------------------
#
# Procedure:    _ModuleEntryPoint
#
# Input:        None
#
# Output:       None
#
# Destroys:     Assume all registers
#
# Description:
#
#   Transition to non-paged flat-model protected mode from a
#   hard-coded GDT that provides exactly two descriptors.
#   This is a bare bones transition to protected mode only
#   used for a while in PEI and possibly DXE.
#
#   After enabling protected mode, a far jump is executed to
#   transfer to PEI using the newly loaded GDT.
#
# Return:       None
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(_ModuleEntryPoint)
ASM_PFX(_ModuleEntryPoint):

  #
  # Warm Reset (INIT#) check.
  #
  .byte   0xbe,0x00,0xf0   #movw    $0xF000, %si
  .byte   0x8e,0xde        #movw    %si, %ds
  .byte   0xbe,0xf0,0xff   #movw    $0xFFF0, %si
  .byte   0x80,0x3c,0xea   #cmpb    $0xEA, (%si)          # Is it warm reset ?
  jne     NotWarmReset     # Jump if not.
  .byte   0xb0,0x08        #movb    $0x08, %al
  .byte   0xba,0xf9,0x0c   #movw    $0xcf9, %dx
  .byte   0xee             #outb    %al, %dx
  .byte   0xb0,0x55        #movb    $0x55, %al
  .byte   0xe6,0x80        #outb    %al, $0x80
  jmp     .
NotWarmReset:
  .byte   0x66,0x8b,0xe8   #movl    %eax, %ebp

  #
  # Load the GDT table in GdtDesc
  #
  .byte   0x66,0xbe        #movl    $GdtDesc, %esi
  .long   GdtDesc

  .byte   0x66,0x2e,0x0f,0x01,0x14   #lgdt    %cs:(%si)

  #
  # Transition to 16 bit protected mode
  #
  .byte   0x0f,0x20,0xc0       #movl    %cr0, %eax                  # Get control register 0
  .byte   0x66,0x83,0xc8,0x03  #orl     $0x0000003, %eax           # Set PE bit (bit #0) & MP bit (bit #1)
  .byte   0x0f,0x22,0xc0       #movl    %eax, %cr0                  # Activate protected mode

  #
  # Now we're in 16 bit protected mode
  # Set up the selectors for 32 bit protected mode entry
  #
  .byte   0xb8                 #movw    SYS_DATA_SEL, %ax
  .word   SYS_DATA_SEL

  .byte   0x8e,0xd8            #movw    %ax, %ds
  .byte   0x8e,0xc0            #movw    %ax, %es
  .byte   0x8e,0xe0            #movw    %ax, %fs
  .byte   0x8e,0xe8            #movw    %ax, %gs
  .byte   0x8e,0xd0            #movw    %ax, %ss

  #
  # Transition to Flat 32 bit protected mode
  # The jump to a far pointer causes the transition to 32 bit mode
  #
  .byte   0x66,0xbe            #movl   ProtectedModeEntryLinearAddress, %esi
  .long   ProtectedModeEntryLinearAddress
  .byte   0x66,0x2e,0xff,0x2c  #jmp    %cs:(%esi)

#
# Protected mode portion initializes stack, configures cache, and calls C entry point
#

#----------------------------------------------------------------------------
#
# Procedure:    ProtectedModeEntryPoint
#
# Input:        Executing in 32 Bit Protected (flat) mode
#                cs: 0-4GB
#                ds: 0-4GB
#                es: 0-4GB
#                fs: 0-4GB
#                gs: 0-4GB
#                ss: 0-4GB
#
# Output:       This function never returns
#
# Destroys:
#               ecx
#               edi
#                esi
#                esp
#
# Description:
#                Perform any essential early platform initilaisation
#               Setup a stack
#               Transfer control to EDKII code in eSRAM
#
#----------------------------------------------------------------------------
ProtectedModeEntryPoint:
  leal  L0, %esp
  jmp  stackless_EarlyPlatformInit
L0:

  #
  # Set up stack pointer
  #
  movl    ASM_PFX(PcdGet32(PcdEsramStage1Base)), %esp
  movl    $QUARK_ESRAM_MEM_SIZE_BYTES, %esi
  addl    %esi, %esp                          # ESP = top of stack (stack grows downwards).

  #
  # Store the the BIST value in EBP
  #
  movl    $0, %ebp    # No processor BIST on Quark

  #
  # Push processor count to stack first, then BIST status (AP then BSP)
  #
  movl    $1, %eax
  cpuid
  shrl    $16, %ebx
  andl    $0x000000FF, %ebx
  cmpb    $1, %bl
  jae     PushProcessorCount

  #
  # Some processors report 0 logical processors.  Effectively 0 = 1.
  # So we fix up the processor count
  #
  incl    %ebx

PushProcessorCount:
  pushl   %ebx

  #
  # We need to implement a long-term solution for BIST capture.  For now, we just copy BSP BIST
  # for all processor threads
  #
  xorl    %ecx, %ecx
  movb    %bl, %cl

PushBist:
  pushl   %ebp
  loop    PushBist

  #
  # Pass Control into the PEI Core
  #
  call PlatformSecLibStartup

  #
  # PEI Core should never return to here, this is just to capture an invalid return.
  #
  jmp     .

#----------------------------------------------------------------------------
#
# Procedure:    stackless_EarlyPlatformInit
#
# Input:        esp - Return address
#
# Output:       None
#
# Destroys:     Assume all registers
#
# Description:
#        Any early platform initialisation required
#
# Return:
#      None
#
#----------------------------------------------------------------------------
stackless_EarlyPlatformInit:

  #
  # Save return address
  #
  movl  %esp, %ebp

  #
  # Ensure cache is disabled.
  #
  movl %cr0, %eax
  orl $(CR0_CACHE_DISABLE + CR0_NO_WRITE), %eax
  invd
  movl    %eax, %cr0

  #
  # Disable NMI operation
  # Good convention suggests you should read back RTC data port after
  # accessing the RTC index port.
  #
  movb $(NMI_DISABLE), %al
  movw $(RTC_INDEX), %dx
  outb %al, %dx
  movw $(RTC_DATA), %dx
  inb  %dx, %al

  #
  # Disable SMI (Disables SMI wire, not SMI messages)
  #
  movl  $((OPCODE_SIDEBAND_REG_READ << SB_OPCODE_FIELD) | (HOST_BRIDGE_PORT_ID << SB_PORT_FIELD) | (HMISC2_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L1, %esp
  jmp  stackless_SideBand_Read
L1:
  andl $(~SMI_EN), %eax
  movl  $((OPCODE_SIDEBAND_REG_WRITE << SB_OPCODE_FIELD) | (HOST_BRIDGE_PORT_ID << SB_PORT_FIELD) | (HMISC2_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L2, %esp
  jmp  stackless_SideBand_Write
L2:

  #
  # Before we get going, check SOC Unit Registers to see if we are required to issue a warm/cold reset
  #
  movl  $((OPCODE_SIDEBAND_ALT_REG_READ << SB_OPCODE_FIELD) | (SOC_UNIT_PORT_ID << SB_PORT_FIELD) | (CFGNONSTICKY_W1_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L3, %esp
  jmp  stackless_SideBand_Read
L3:
  andl $(FORCE_WARM_RESET), %eax
  jz TestForceColdReset    # Zero means bit clear, we're not requested to warm reset so continue as normal
  jmp IssueWarmReset

TestForceColdReset:
  movl  $((OPCODE_SIDEBAND_ALT_REG_READ << SB_OPCODE_FIELD) | (SOC_UNIT_PORT_ID << SB_PORT_FIELD) | (CFGNONSTICKY_W1_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L4, %esp
  jmp  stackless_SideBand_Read
L4:
  andl $(FORCE_COLD_RESET), %eax
  jz TestHmboundLock    # Zero means bit clear, we're not requested to cold reset so continue as normal
  jmp IssueColdReset

  #
  # Before setting HMBOUND, check it's not locked
  #
TestHmboundLock:
  movl  $((OPCODE_SIDEBAND_REG_READ << SB_OPCODE_FIELD) | (HOST_BRIDGE_PORT_ID << SB_PORT_FIELD) | (HMBOUND_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L5, %esp
  jmp  stackless_SideBand_Read
L5:
  andl $(HMBOUND_LOCK), %eax
  jz ConfigHmbound  # Zero means bit clear, we have the config we want so continue as normal
  #
  # Failed to config - store sticky bit debug
  #
  movl  $((OPCODE_SIDEBAND_ALT_REG_READ << SB_OPCODE_FIELD) | (SOC_UNIT_PORT_ID << SB_PORT_FIELD) | (CFGSTICKY_RW_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L6, %esp
  jmp  stackless_SideBand_Read
L6:
  orl $(RESET_FOR_HMBOUND_LOCK), %eax
  movl  $((OPCODE_SIDEBAND_ALT_REG_WRITE << SB_OPCODE_FIELD) | (SOC_UNIT_PORT_ID << SB_PORT_FIELD) | (CFGSTICKY_RW_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L7, %esp
  jmp  stackless_SideBand_Write
L7:
  jmp IssueWarmReset

  #
  # Set up the HMBOUND register
  #
ConfigHmbound:
  movl $(HMBOUND_ADDRESS), %eax      # Data (Set HMBOUND location)
  movl  $((OPCODE_SIDEBAND_REG_WRITE << SB_OPCODE_FIELD) | (HOST_BRIDGE_PORT_ID << SB_PORT_FIELD) | (HMBOUND_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L8, %esp
  jmp  stackless_SideBand_Write
L8:

  #
  # Enable interrupts to Remote Management Unit when a IMR/SMM/HMBOUND violation occurs.
  #
  movl $(ENABLE_IMR_INTERRUPT), %eax      # Data (Set interrupt enable mask)
  movl  $((OPCODE_SIDEBAND_REG_WRITE << SB_OPCODE_FIELD) | (MEMORY_MANAGER_PORT_ID << SB_PORT_FIELD) | (BIMRVCTL_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L9, %esp
  jmp  stackless_SideBand_Write
L9:

  #
  # Set eSRAM address
  #
  movl    ASM_PFX(PcdGet32(PcdEsramStage1Base)), %eax   # Data (Set eSRAM location)
  shr   $(0x18), %eax
  addl  $(BLOCK_ENABLE_PG), %eax
  movl  $((OPCODE_SIDEBAND_REG_WRITE << SB_OPCODE_FIELD) | (MEMORY_MANAGER_PORT_ID << SB_PORT_FIELD) | (ESRAMPGCTRL_BLOCK_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L10, %esp
  jmp  stackless_SideBand_Write
L10:

  #
  # Check that we're not blocked from setting the config that we want.
  #
  movl  $((OPCODE_SIDEBAND_REG_READ << SB_OPCODE_FIELD) | (MEMORY_MANAGER_PORT_ID << SB_PORT_FIELD) | (ESRAMPGCTRL_BLOCK_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L11, %esp
  jmp  stackless_SideBand_Read
L11:
  andl $(BLOCK_ENABLE_PG), %eax
  jnz ConfigPci  # Non-zero means bit set, we have the config we want so continue as normal
  #
  # Failed to config - store sticky bit debug
  #
  movl  $((OPCODE_SIDEBAND_ALT_REG_READ << SB_OPCODE_FIELD) | (SOC_UNIT_PORT_ID << SB_PORT_FIELD) | (CFGSTICKY_RW_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L12, %esp
  jmp  stackless_SideBand_Read
L12:
  orl $(RESET_FOR_ESRAM_LOCK), %eax     # Set the bit we're interested in
  movl  $((OPCODE_SIDEBAND_ALT_REG_WRITE << SB_OPCODE_FIELD) | (SOC_UNIT_PORT_ID << SB_PORT_FIELD) | (CFGSTICKY_RW_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L13, %esp
  jmp  stackless_SideBand_Write
L13:
  jmp IssueWarmReset

  #
  # Enable PCIEXBAR
  #
ConfigPci:
  movl $(EC_BASE + EC_ENABLE), %eax      # Data
  movl  $((OPCODE_SIDEBAND_REG_WRITE << SB_OPCODE_FIELD) | (MEMORY_ARBITER_PORT_ID << SB_PORT_FIELD) | (AEC_CTRL_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L14, %esp
  jmp  stackless_SideBand_Write
L14:

  movl $(EC_BASE + EC_ENABLE), %eax      # Data
  movl  $((OPCODE_SIDEBAND_REG_WRITE << SB_OPCODE_FIELD) | (HOST_BRIDGE_PORT_ID << SB_PORT_FIELD) | (HECREG_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L15, %esp
  jmp  stackless_SideBand_Write
L15:

  #
  #  Open up full 8MB SPI decode
  #
  movl  $(PCI_CFG | (ILB_PFA << 8) | BDE), %ebx  # PCI Configuration address
  movl $(DECODE_ALL_REGIONS_ENABLE), %eax
  leal  L16, %esp
  jmp  stackless_PCIConfig_Write
L16:

  #
  # Enable NMI operation
  # Good convention suggests you should read back RTC data port after
  # accessing the RTC index port.
  #
  movb $(NMI_ENABLE), %al
  movw $(RTC_INDEX), %dx
  outb %al, %dx
  movw $(RTC_DATA), %dx
  inb  %dx, %al

  #
  # Clear Host Bridge SMI, NMI, INTR fields
  #
  movl  $((OPCODE_SIDEBAND_REG_READ << SB_OPCODE_FIELD) | (HOST_BRIDGE_PORT_ID << SB_PORT_FIELD) | (HLEGACY_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L21, %esp
  jmp  stackless_SideBand_Read
L21:
  andl $~(NMI + SMI + INTR), %eax      # Clear NMI, SMI, INTR fields
  movl  $((OPCODE_SIDEBAND_REG_WRITE << SB_OPCODE_FIELD) | (HOST_BRIDGE_PORT_ID << SB_PORT_FIELD) | (HLEGACY_OFFSET << SB_ADDR_FIELD)), %ecx
  leal  L22, %esp
  jmp  stackless_SideBand_Write
L22:

  #
  # Restore return address
  #
  movl  %ebp, %esp
  RET32

IssueWarmReset:
  #
  # Issue Warm Reset request to Remote Management Unit via iLB
  #
  movw  $(CF9_WARM_RESET), %ax
  movw  $(ILB_RESET_REG), %dx
  outw  %ax, %dx
  jmp  .  # Stay here until we are reset.

IssueColdReset:
  #
  # Issue Cold Reset request to Remote Management Unit via iLB
  #
  movw  $(CF9_COLD_RESET), %ax
  movw  $(ILB_RESET_REG), %dx
  outw  %ax, %dx
  jmp  .  # Stay here until we are reset.

#----------------------------------------------------------------------------
#
# Procedure:    stackless_SideBand_Read
#
# Input:        esp - return address
#                ecx[15:8] - Register offset
#                ecx[23:16] - Port ID
#                ecx[31:24] - Opcode
#
# Output:       eax - Data read
#
# Destroys:
#                eax
#                ebx
#                cl
#                esi
#
# Description:
#        Perform requested sideband read
#
#----------------------------------------------------------------------------
stackless_SideBand_Read:

  movl  %esp, %esi      # Save the return address

  #
  # Load the SideBand Packet Register to generate the transaction
  #
  movl  $((PCI_CFG) | (HOST_BRIDGE_PFA << 8) | (MESSAGE_BUS_CONTROL_REG)), %ebx   # PCI Configuration address
  movb  $(ALL_BYTE_EN << SB_BE_FIELD), %cl      # Set all Byte Enable bits
  xchgl  %ecx, %eax
  leal  L17, %esp
  jmp  stackless_PCIConfig_Write
L17:
  xchgl  %ecx, %eax

  #
  # Read the SideBand Data Register
  #
  movl  $((PCI_CFG) | (HOST_BRIDGE_PFA << 8) | (MESSAGE_DATA_REG)), %ebx   # PCI Configuration address
  leal  L18, %esp
  jmp  stackless_PCIConfig_Read
L18:

  movl  %esi, %esp      # Restore the return address
  RET32


#----------------------------------------------------------------------------
#
# Procedure:    stackless_SideBand_Write
#
# Input:        esp - return address
#                eax - Data
#                ecx[15:8] - Register offset
#                ecx[23:16] - Port ID
#                ecx[31:24] - Opcode
#
# Output:       None
#
# Destroys:
#                ebx
#                cl
#                esi
#
# Description:
#        Perform requested sideband write
#
#
#----------------------------------------------------------------------------
stackless_SideBand_Write:

  movl  %esp, %esi      # Save the return address

  #
  # Load the SideBand Data Register with the data
  #
  movl  $((PCI_CFG) | (HOST_BRIDGE_PFA << 8) | (MESSAGE_DATA_REG)), %ebx   # PCI Configuration address
  leal  L19, %esp
  jmp  stackless_PCIConfig_Write
L19:

  #
  # Load the SideBand Packet Register to generate the transaction
  #
  movl  $((PCI_CFG) | (HOST_BRIDGE_PFA << 8) | (MESSAGE_BUS_CONTROL_REG)), %ebx   # PCI Configuration address
  movb  $(ALL_BYTE_EN << SB_BE_FIELD), %cl      # Set all Byte Enable bits
  xchgl  %ecx, %eax
  leal  L20, %esp
  jmp  stackless_PCIConfig_Write
L20:
  xchgl  %ecx, %eax

  movl  %esi, %esp      # Restore the return address
  RET32


#----------------------------------------------------------------------------
#
# Procedure:    stackless_PCIConfig_Write
#
# Input:        esp - return address
#                eax - Data to write
#                ebx - PCI Config Address
#
# Output:       None
#
# Destroys:
#                dx
#
# Description:
#        Perform a DWORD PCI Configuration write
#
#----------------------------------------------------------------------------
stackless_PCIConfig_Write:

  #
  # Write the PCI Config Address to the address port
  #
  xchgl  %ebx, %eax
  movw  $(PCI_ADDRESS_PORT), %dx
  outl  %eax, %dx
  xchgl  %ebx, %eax

  #
  # Write the PCI DWORD Data to the data port
  #
  movw  $(PCI_DATA_PORT), %dx
  outl  %eax, %dx

  RET32


#----------------------------------------------------------------------------
#
# Procedure:    stackless_PCIConfig_Read
#
# Input:        esp - return address
#                ebx - PCI Config Address
#
# Output:       eax - Data read
#
# Destroys:
#                eax
#                dx
#
# Description:
#        Perform a DWORD PCI Configuration read
#
#----------------------------------------------------------------------------
stackless_PCIConfig_Read:

  #
  # Write the PCI Config Address to the address port
  #
  xchgl  %ebx, %eax
  movw  $(PCI_ADDRESS_PORT), %dx
  outl  %eax, %dx
  xchgl  %ebx, %eax

  #
  # Read the PCI DWORD Data from the data port
  #
  movw  $(PCI_DATA_PORT), %dx
  inl  %dx, %eax

  RET32


#
# ROM-based Global-Descriptor Table for the Tiano PEI Phase
#
.align 16
#
# GDT[0]: 000h: Null entry, never used.
#

GDT_BASE:
BootGdtTable:
# null descriptor
.equ                NULL_SEL, . - GDT_BASE # Selector [0]
        .word 0         # limit 15:0
        .word 0         # base 15:0
        .byte 0         # base 23:16
        .byte 0         # type
        .byte 0         # limit 19:16, flags
        .byte 0         # base 31:24

# linear data segment descriptor
.equ            LINEAR_SEL, . - GDT_BASE # Selector [0x8]
        .word 0xFFFF    # limit 0xFFFFF
        .word 0         # base 0
        .byte 0
        .byte 0x92      # present, ring 0, data, expand-up, writable
        .byte 0xCF              # page-granular, 32-bit
        .byte 0

# linear code segment descriptor
.equ            LINEAR_CODE_SEL, . - GDT_BASE # Selector [0x10]
        .word 0xFFFF    # limit 0xFFFFF
        .word 0         # base 0
        .byte 0
        .byte 0x9A      # present, ring 0, data, expand-up, writable
        .byte 0xCF              # page-granular, 32-bit
        .byte 0

# system data segment descriptor
.equ            SYS_DATA_SEL, . - GDT_BASE # Selector [0x18]
        .word 0xFFFF    # limit 0xFFFFF
        .word 0         # base 0
        .byte 0
        .byte 0x92      # present, ring 0, data, expand-up, writable
        .byte 0xCF              # page-granular, 32-bit
        .byte 0

# system code segment descriptor
.equ            SYS_CODE_SEL, . - GDT_BASE
        .word 0xFFFF    # limit 0xFFFFF
        .word 0         # base 0
        .byte 0
        .byte 0x9A      # present, ring 0, data, expand-up, writable
        .byte 0xCF              # page-granular, 32-bit
        .byte 0

# spare segment descriptor
.equ        SYS16_CODE_SEL, . - GDT_BASE
        .word 0xffff    # limit 0xFFFFF
        .word 0         # base 0
        .byte 0x0f
        .byte 0x9b      # present, ring 0, data, expand-up, writable
        .byte 0         # page-granular, 32-bit
        .byte 0

# spare segment descriptor
.equ        SYS16_DATA_SEL, . - GDT_BASE
        .word 0xffff    # limit 0xFFFFF
        .word 0         # base 0
        .byte 0
        .byte 0x93      # present, ring 0, data, expand-up, not-writable
        .byte 0         # page-granular, 32-bit
        .byte 0

# spare segment descriptor
.equ        SPARE5_SEL, . - GDT_BASE
        .word 0         # limit 0xFFFFF
        .word 0         # base 0
        .byte 0
        .byte 0         # present, ring 0, data, expand-up, writable
        .byte 0         # page-granular, 32-bit
        .byte 0
.equ        GDT_SIZE, . - GDT_BASE

#
# GDT Descriptor
#
GdtDesc:                                     # GDT descriptor
       .word    GDT_SIZE - 1
       .long    BootGdtTable

ProtectedModeEntryLinearAddress:
ProtectedModeEntryLinearOffset:
       .long    ProtectedModeEntryPoint
       .word    LINEAR_CODE_SEL
