#------------------------------------------------------------------------------
#
# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>
# 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.
#
# Abstract:
#
#   Provide FSP API entry points.
#
#------------------------------------------------------------------------------


.equ MSR_IA32_PLATFORM_ID,                   0x00000017
.equ MSR_IA32_BIOS_UPDT_TRIG,                0x00000079
.equ MSR_IA32_BIOS_SIGN_ID,                  0x0000008b


MicrocodeHdr:
.equ        MicrocodeHdrVersion,                 0x0000
.equ        MicrocodeHdrRevision,                0x0004
.equ        MicrocodeHdrDate,                    0x0008
.equ        MicrocodeHdrProcessor,               0x000c
.equ        MicrocodeHdrChecksum,                0x0010
.equ        MicrocodeHdrLoader,                  0x0014
.equ        MicrocodeHdrFlags,                   0x0018
.equ        MicrocodeHdrDataSize,                0x001C
.equ        MicrocodeHdrTotalSize,               0x0020
.equ        MicrocodeHdrRsvd,                    0x0024
MicrocodeHdrEnd:
.equ        MicrocodeHdrLength,                  0x0030  # MicrocodeHdrLength = MicrocodeHdrEnd - MicrocodeHdr


ExtSigHdr:
.equ        ExtSigHdrCount,                  0x0000
.equ        ExtSigHdrChecksum,               0x0004
.equ        ExtSigHdrRsvd,                   0x0008
ExtSigHdrEnd:
.equ        ExtSigHdrLength,                 0x0014  #ExtSigHdrLength = ExtSigHdrEnd - ExtSigHdr

ExtSig:
.equ        ExtSigProcessor,                 0x0000
.equ        ExtSigFlags,                     0x0004
.equ        ExtSigChecksum,                  0x0008
ExtSigEnd:
.equ        ExtSigLength,                    0x000C  #ExtSigLength = ExtSigEnd - ExtSig

LoadMicrocodeParams:
.equ        MicrocodeCodeAddr,               0x0000
.equ        MicrocodeCodeSize,               0x0004
LoadMicrocodeParamsEnd:



.macro SAVE_REGS
  pinsrw     $0x00, %ebp, %xmm7
  ror        $0x10, %ebp
  pinsrw     $0x01, %ebp, %xmm7
  ror        $0x10, %ebp
#
  pinsrw     $0x02, %ebx, %xmm7
  ror        $0x10, %ebx
  pinsrw     $0x03, %ebx, %xmm7
  ror        $0x10, %ebx
#
  pinsrw     $0x04, %esi, %xmm7
  ror        $0x10, %esi
  pinsrw     $0x05, %esi, %xmm7
  ror        $0x10, %esi
#
  pinsrw     $0x06, %edi, %xmm7
  ror        $0x10, %edi
  pinsrw     $0x07, %edi, %xmm7
  ror        $0x10, %edi
#
  pinsrw     $0x00, %esp, %xmm6
  ror        $0x10, %esp
  pinsrw     $0x01, %esp, %xmm6
  ror        $0x10, %esp
.endm

.macro LOAD_REGS
  pshufd     $0xe4, %xmm7, %xmm7
  movd       %xmm7, %ebp 
  pshufd     $0xe4, %xmm7, %xmm7
#
  pshufd     $0x39, %xmm7, %xmm7
  movd       %xmm7, %ebx
  pshufd     $0x93, %xmm7, %xmm7
#
  pshufd     $0x4e, %xmm7, %xmm7
  movd       %xmm7, %esi
  pshufd     $0x4e, %xmm7, %xmm7
#
  pshufd     $0x93, %xmm7, %xmm7
  movd       %xmm7, %edi
  pshufd     $0x39, %xmm7, %xmm7
#
  movd       %xmm6, %esp
.endm

.macro LOAD_EAX
  pshufd     $0x39, %xmm6, %xmm6
  movd       %xmm6, %eax
  pshufd     $0x93, %xmm6, %xmm6
.endm

.macro LOAD_EDX
  pshufd     $0xe4, %xmm6, %xmm6
  movd       %xmm6, %edx
  pshufd     $0xe4, %xmm6, %xmm6
.endm

.macro SAVE_EAX
  pinsrw     $0x02, %eax, %xmm6
  ror        $0x10, %eax
  pinsrw     $0x03, %eax, %xmm6
  ror        $0x10, %eax
.endm

.macro SAVE_EDX
  pinsrw     $0x04, %edx, %xmm6
  ror        $0x10, %edx
  pinsrw     $0x05, %edx, %xmm6
  ror        $0x10, %edx
.endm

.macro LOAD_ESP
  movd       %xmm6, %esp
.endm

.macro ENABLE_SSE
    jmp     NextAddress
.align 4
    #
    # Float control word initial value:
    # all exceptions masked, double-precision, round-to-nearest
    #
ASM_PFX(mFpuControlWord): .word     0x027F
    #
    # Multimedia-extensions control word:
    # all exceptions masked, round-to-nearest, flush to zero for masked underflow
    #
ASM_PFX(mMmxControlWord): .long     0x01F80
SseError:      
    #
    # Processor has to support SSE
    #
    jmp     SseError      
NextAddress:            
    #
    # Initialize floating point units
    #
    finit
    fldcw   ASM_PFX(mFpuControlWord)

    #
    # Use CpuId instructuion (CPUID.01H:EDX.SSE[bit 25] = 1) to test
    # whether the processor supports SSE instruction.
    #
    movl    $1,  %eax
    cpuid
    btl     $25, %edx
    jnc     SseError

    #
    # Set OSFXSR bit (bit #9) & OSXMMEXCPT bit (bit #10)
    #
    movl    %cr4, %eax
    orl     $BIT9, %eax
    movl    %eax, %cr4

    #
    # The processor should support SSE instruction and we can use
    # ldmxcsr instruction
    #
    ldmxcsr ASM_PFX(mMmxControlWord)
.endm

#Save in ECX-SLOT 3 in xmm6.
.macro SAVE_EAX_MICROCODE_RET_STATUS
  pinsrw     $0x6, %eax, %xmm6
  ror        $0x10, %eax
  pinsrw     $0x7, %eax, %xmm6
  rol        $0x10, %eax
.endm

#Restore from ECX-SLOT 3 in xmm6.
.macro LOAD_EAX_MICROCODE_RET_STATUS
  pshufd     $0x93, %xmm6, %xmm6
  movd       %xmm6, %eax
  pshufd     $0x39, %xmm6, %xmm6
.endm



#
# Following are fixed PCDs
#
ASM_GLOBAL    ASM_PFX(_gPcd_FixedAtBuild_PcdTemporaryRamBase)
ASM_GLOBAL    ASM_PFX(_gPcd_FixedAtBuild_PcdTemporaryRamSize)
ASM_GLOBAL    ASM_PFX(_gPcd_FixedAtBuild_PcdFspTemporaryRamSize)

#
# Following functions will be provided in C
#
ASM_GLOBAL    ASM_PFX(SecStartup)
ASM_GLOBAL    ASM_PFX(FspApiCallingCheck)

#
# Following functions will be provided in PlatformSecLib
#
ASM_GLOBAL    ASM_PFX(AsmGetFspBaseAddress)
ASM_GLOBAL    ASM_PFX(AsmGetFspInfoHeader)
ASM_GLOBAL    ASM_PFX(GetBootFirmwareVolumeOffset)
ASM_GLOBAL    ASM_PFX(Loader2PeiSwitchStack)


#
# Define the data length that we saved on the stack top
#
.equ          DATA_LEN_OF_PER0, 0x018
.equ          DATA_LEN_OF_MCUD, 0x018
.equ          DATA_LEN_AT_STACK_TOP, (DATA_LEN_OF_PER0 + DATA_LEN_OF_MCUD + 4)

#------------------------------------------------------------------------------
# SecPlatformInitDefault
# Inputs:
#   mm7 -> Return address
# Outputs:
#   eax -> 0 - Successful, Non-zero - Failed.
# Register Usage:
#   eax is cleared and ebp is used for return address.
#   All others reserved.
#------------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(SecPlatformInitDefault)
ASM_PFX(SecPlatformInitDefault):
   #
   # Save return address to EBP
   #
   movd   %mm7, %ebp
   xorl   %eax, %eax

SecPlatformInitDefaultExit:
   jmp   *%ebp


#------------------------------------------------------------------------------
# LoadMicrocodeDefault
#
# Inputs:
#   esp -> LoadMicrocodeParams pointer
# Register Usage:
#   esp  Preserved
#   All others destroyed
# Assumptions:
#   No memory available, stack is hard-coded and used for return address
#   Executed by SBSP and NBSP
#   Beginning of microcode update region starts on paragraph boundary
#------------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(LoadMicrocodeDefault)
ASM_PFX(LoadMicrocodeDefault):
   #
   # Save return address to EBP
   #
   movd   %mm7, %ebp

   cmpl   $0x00, %esp
   jz     ParamError
   movl   4(%esp), %eax                       #dword ptr []     Parameter pointer
   cmpl   $0x00, %eax
   jz     ParamError
   movl   %eax, %esp
   movl   MicrocodeCodeAddr(%esp), %esi
   cmpl   $0x00, %esi
   jnz    CheckMainHeader

ParamError:
   movl   $0x080000002, %eax
   jmp    LoadMicrocodeExit

CheckMainHeader:
   #
   # Get processor signature and platform ID from the installed processor
   # and save into registers for later use
   # ebx = processor signature
   # edx = platform ID
   #
   movl   $0x01, %eax
   cpuid
   movl   %eax, %ebx
   movl   $MSR_IA32_PLATFORM_ID, %ecx
   rdmsr
   movl   %edx, %ecx
   shrl   $0x12, %ecx                        # shift (50d-32d=18d=0x12) bits
   andl   $0x07, %ecx                        # platform id at bit[52..50]
   movl   $0x01, %edx
   shll   %cl,%edx

   #
   # Current register usage
   # esp -> stack with paramters
   # esi -> microcode update to check
   # ebx = processor signature
   # edx = platform ID
   #

   #
   # Check for valid microcode header
   # Minimal test checking for header version and loader version as 1
   #
   movl   $0x01, %eax
   cmpl   %eax, MicrocodeHdrVersion(%esi)
   jne    AdvanceFixedSize
   cmpl   %eax, MicrocodeHdrLoader(%esi)
   jne    AdvanceFixedSize

   #
   # Check if signature and plaform ID match
   #
   cmpl   MicrocodeHdrProcessor(%esi), %ebx
   jne    LoadMicrocodeL0
   testl  MicrocodeHdrFlags(%esi), %edx
   jnz    LoadCheck                          #Jif signature and platform ID match

LoadMicrocodeL0:
   #
   # Check if extended header exists
   # First check if MicrocodeHdrTotalSize and MicrocodeHdrDataSize are valid
   #
   xorl   %eax, %eax
   cmpl   %eax, MicrocodeHdrTotalSize(%esi)
   je     NextMicrocode
   cmpl   %eax, MicrocodeHdrDataSize(%esi)
   je     NextMicrocode

   #
   # Then verify total size - sizeof header > data size
   #
   movl   MicrocodeHdrTotalSize(%esi), %ecx
   subl   $MicrocodeHdrLength, %ecx
   cmpl   MicrocodeHdrDataSize(%esi), %ecx
   jle NextMicrocode

   #
   # Set edi -> extended header
   #
   movl   %esi, %edi
   addl   $MicrocodeHdrLength, %edi
   addl   MicrocodeHdrDataSize(%esi), %edi

   #
   # Get count of extended structures
   #
   movl   ExtSigHdrCount(%edi), %ecx

   #
   # Move pointer to first signature structure
   #
   addl   ExtSigHdrLength, %edi

CheckExtSig:
   #
   # Check if extended signature and platform ID match
   #
   cmpl   %ebx, ExtSigProcessor(%edi)
   jne    LoadMicrocodeL1
   test   %edx, ExtSigFlags(%edi)
   jnz    LoadCheck                          # Jif signature and platform ID match
LoadMicrocodeL1:
   #
   # Check if any more extended signatures exist
   #
   addl   $ExtSigLength, %edi
   loop   CheckExtSig

NextMicrocode:
   #
   # Advance just after end of this microcode
   #
   xorl   %eax, %eax
   cmpl   %eax, MicrocodeHdrTotalSize(%esi)
   je     LoadMicrocodeL2
   addl   MicrocodeHdrTotalSize(%esi), %esi
   jmp    CheckAddress
LoadMicrocodeL2:
   addl   $0x800, %esi                       #add   esi, dword ptr 2048
   jmp    CheckAddress

AdvanceFixedSize:
   #
   # Advance by 4X dwords
   #
   addl   $0x400, %esi                       #add   esi, dword ptr 1024

CheckAddress:
   #
   # Is valid Microcode start point ?
   #
   cmpl   $0x0ffffffff, MicrocodeHdrVersion(%esi)

   #
   # Is automatic size detection ?
   #
   movl   MicrocodeCodeSize(%esp), %eax
   cmpl   $0x0ffffffff, %eax
   jz     LoadMicrocodeL3
   #
   # Address >= microcode region address + microcode region size?
   #
   addl   MicrocodeCodeAddr(%esp), %eax

   cmpl   %eax, %esi
   jae    Done                               #Jif address is outside of microcode region
   jmp    CheckMainHeader

LoadMicrocodeL3:
LoadCheck:
   #
   # Get the revision of the current microcode update loaded
   #
   movl   $MSR_IA32_BIOS_SIGN_ID, %ecx
   xorl   %eax, %eax                         # Clear EAX
   xorl   %edx, %edx                         # Clear EDX
   wrmsr                                     # Load 0 to MSR at 8Bh

   movl   $0x01, %eax
   cpuid
   movl   $MSR_IA32_BIOS_SIGN_ID, %ecx
   rdmsr                                     # Get current microcode signature

   #
   # Verify this microcode update is not already loaded
   #
   cmpl   %edx, MicrocodeHdrRevision(%esi)
   je     Continue

LoadMicrocode0:
   #
   # EAX contains the linear address of the start of the Update Data
   # EDX contains zero
   # ECX contains 79h (IA32_BIOS_UPDT_TRIG)
   # Start microcode load with wrmsr
   #
   movl   %esi, %eax
   addl   $MicrocodeHdrLength, %eax
   xorl   %edx, %edx
   movl   $MSR_IA32_BIOS_UPDT_TRIG, %ecx
   wrmsr
   movl   $0x01, %eax
   cpuid

Continue:
   jmp    NextMicrocode

Done:
   movl   $0x01, %eax
   cpuid
   movl   $MSR_IA32_BIOS_SIGN_ID, %ecx
   rdmsr                                     # Get current microcode signature
   xorl   %eax, %eax
   cmpl   $0x00, %edx
   jnz    LoadMicrocodeExit
   movl   $0x08000000E, %eax

LoadMicrocodeExit:
   jmp   *%ebp


#----------------------------------------------------------------------------
# EstablishStackFsp
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(EstablishStackFsp)
ASM_PFX(EstablishStackFsp):
  #
  # Save parameter pointer in edx
  #
  movl    4(%esp), %edx

  #
  # Enable FSP STACK
  #
  movl    PcdGet32(PcdTemporaryRamBase), %esp
  addl    PcdGet32(PcdTemporaryRamSize), %esp

  pushl   $DATA_LEN_OF_MCUD                  # Size of the data region
  pushl   $0x4455434D                        # Signature of the  data region 'MCUD'
  pushl   12(%edx)                           # Code size
  pushl   8(%edx)                            # Code base
  pushl   4(%edx)                            # Microcode size
  pushl   (%edx)                             # Microcode base

  #
  # Save API entry/exit timestamp into stack
  #
  pushl   $DATA_LEN_OF_PER0                  # Size of the data region
  pushl   $0x30524550                        # Signature of the  data region 'PER0'
  LOAD_EDX
  pushl   %edx
  LOAD_EAX
  pushl   %eax
  rdtsc
  pushl   %edx
  pushl   %eax

  #
  # Terminator for the data on stack
  #
  push    $0x00

  #
  # Set ECX/EDX to the BootLoader temporary memory range
  #
  movl       PcdGet32 (PcdTemporaryRamBase), %ecx
  movl       %ecx, %edx
  addl       PcdGet32 (PcdTemporaryRamSize), %edx
  subl       PcdGet32 (PcdFspTemporaryRamSize), %edx

  xorl       %eax, %eax

  movd       %mm7, %esi                      #RET_ESI
  jmp        *%esi

#----------------------------------------------------------------------------
# TempRamInit API
#
# This FSP API will load the microcode update, enable code caching for the
# region specified by the boot loader and also setup a temporary stack to be
# used till main memory is initialized.
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(TempRamInitApi)
ASM_PFX(TempRamInitApi):
  #
  # Ensure SSE is enabled
  #
  ENABLE_SSE

  #
  # Save EBP, EBX, ESI, EDI & ESP in XMM7 & XMM6
  #
  SAVE_REGS

  #
  # Save timestamp into XMM6
  #
  rdtsc
  SAVE_EAX
  SAVE_EDX

  #
  # Check Parameter
  #
  movl    4(%esp), %eax
  cmpl    $0x00, %eax
  movl    $0x80000002, %eax
  jz      NemInitExit

  #
  # Sec Platform Init
  #
  movl    $TempRamInitApiL1, %esi            #CALL_MMX  SecPlatformInit
  movd    %esi, %mm7
  .weak   ASM_PFX(SecPlatformInit)
  .set    ASM_PFX(SecPlatformInit), ASM_PFX(SecPlatformInitDefault)
  jmp     ASM_PFX(SecPlatformInit)
TempRamInitApiL1:
  cmpl    $0x00, %eax
  jnz     NemInitExit

  #
  # Load microcode
  #
  LOAD_ESP
  movl    $TempRamInitApiL2, %esi            #CALL_MMX  LoadMicrocode
  movd    %esi, %mm7
  .weak   ASM_PFX(LoadMicrocode)
  .set    ASM_PFX(LoadMicrocode), ASM_PFX(LoadMicrocodeDefault)
  jmp     ASM_PFX(LoadMicrocode)
TempRamInitApiL2:
  SAVE_EAX_MICROCODE_RET_STATUS              #Save microcode return status in ECX-SLOT 3 in xmm6.
  #@note If return value eax is not 0, microcode did not load, but continue and attempt to boot from ECX-SLOT 3 in xmm6.

  #
  # Call Sec CAR Init
  #
  LOAD_ESP
  movl    $TempRamInitApiL3, %esi            #CALL_MMX  SecCarInit
  movd    %esi, %mm7
  jmp     ASM_PFX(SecCarInit)
TempRamInitApiL3:
  cmpl    $0x00, %eax
  jnz     NemInitExit

  #
  # EstablishStackFsp
  #
  LOAD_ESP
  movl    $TempRamInitApiL4, %esi            #CALL_MMX  EstablishStackFsp
  movd    %esi, %mm7
  jmp     ASM_PFX(EstablishStackFsp)
TempRamInitApiL4:

  LOAD_EAX_MICROCODE_RET_STATUS              #Restore microcode status if no CAR init error.

NemInitExit:
  #
  # Load EBP, EBX, ESI, EDI & ESP from XMM7 & XMM6
  #
  LOAD_REGS
  ret


#----------------------------------------------------------------------------
# FspInit API
#
# This FSP API will perform the processor and chipset initialization.
# This API will not return.  Instead, it transfers the control to the
# ContinuationFunc provided in the parameter.
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(FspInitApi)
ASM_PFX(FspInitApi):
  movl   $0x01, %eax
  jmp    FspApiCommon

#----------------------------------------------------------------------------
# NotifyPhase API
#
# This FSP API will notify the FSP about the different phases in the boot
# process
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(NotifyPhaseApi)
ASM_PFX(NotifyPhaseApi):
  movl   $0x02, %eax
  jmp    FspApiCommon

#----------------------------------------------------------------------------
# FspMemoryInit API
#
# This FSP API is called after TempRamInit and initializes the memory.
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(FspMemoryInitApi)
ASM_PFX(FspMemoryInitApi):
  movl   $0x03, %eax
  jmp    FspApiCommon

#----------------------------------------------------------------------------
# TempRamExitApi API
#
# This API tears down temporary RAM
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(TempRamExitApi)
ASM_PFX(TempRamExitApi):
  movl   $0x04, %eax
  jmp    FspApiCommon

#----------------------------------------------------------------------------
# FspSiliconInit API
#
# This FSP API initializes the CPU and the chipset including the IO
# controllers in the chipset to enable normal operation of these devices.
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(FspSiliconInitApi)
ASM_PFX(FspSiliconInitApi):
  movl   $0x05, %eax
  jmp    FspApiCommon

#----------------------------------------------------------------------------
# FspApiCommon API
#
# This is the FSP API common entry point to resume the FSP execution
#
#----------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(FspApiCommon)
ASM_PFX(FspApiCommon):
  #
  # EAX holds the API index
  #

  #
  # Stack must be ready
  #  
  pushl   %eax
  addl    $0x04, %esp
  cmpl    -4(%esp), %eax
  jz      FspApiCommonL0
  movl    $0x080000003, %eax
  jmp     FspApiCommonExit

FspApiCommonL0:
  #
  # Verify the calling condition
  #
  pushal
  pushl   36(%esp)  #push ApiParam  [esp + 4 * 8 + 4]
  pushl   %eax      #push ApiIdx
  call    ASM_PFX(FspApiCallingCheck)
  addl    $0x08, %esp
  cmpl    $0x00, %eax
  jz      FspApiCommonL1
  movl    %eax, 0x1C(%esp)                   # mov    dword ptr [esp + 4 * 7], eax
  popal
  ret

FspApiCommonL1:
  popal
  cmpl    $0x01, %eax                        # FspInit API
  jz      FspApiCommonL2
  cmpl    $0x03, %eax                        # FspMemoryInit API
  jz      FspApiCommonL2
  call    ASM_PFX(AsmGetFspInfoHeader)
  jmp     Loader2PeiSwitchStack

FspApiCommonL2:
  #
  # FspInit and FspMemoryInit APIs, setup the initial stack frame
  #  
  
  #
  # Place holder to store the FspInfoHeader pointer
  #
  pushl  %eax

  #
  # Update the FspInfoHeader pointer
  #
  pushl  %eax
  call   ASM_PFX(AsmGetFspInfoHeader)
  movl   %eax, 4(%esp)
  popl   %eax

  #
  # Create a Task Frame in the stack for the Boot Loader
  #
  pushfl                                     # 2 pushf for 4 byte alignment
  cli
  pushal

  #
  # Reserve 8 bytes for IDT save/restore
  #
  subl    $0x08, %esp
  sidt    (%esp)

  #
  # Setup new FSP stack
  #
  movl    %esp, %edi
  movl    PcdGet32(PcdTemporaryRamBase), %esp
  addl    PcdGet32(PcdTemporaryRamSize), %esp
  subl    $(DATA_LEN_AT_STACK_TOP + 0x40), %esp

  #
  # Pass the API Idx to SecStartup
  #
  pushl   %eax
  
  #
  # Pass the BootLoader stack to SecStartup
  #
  pushl   %edi

  #
  # Pass entry point of the PEI core
  #
  call    ASM_PFX(AsmGetFspBaseAddress)
  movl    %eax, %edi
  addl    PcdGet32(PcdFspAreaSize), %edi
  subl    $0x20, %edi
  addl    %ds:(%edi), %eax
  pushl   %eax

  #
  # Pass BFV into the PEI Core
  # It uses relative address to calucate the actual boot FV base
  # For FSP implementation with single FV, PcdFspBootFirmwareVolumeBase and
  # PcdFspAreaBaseAddress are the same. For FSP with mulitple FVs,
  # they are different. The code below can handle both cases.
  #
  call    ASM_PFX(AsmGetFspBaseAddress)
  movl    %eax, %edi
  call    ASM_PFX(GetBootFirmwareVolumeOffset)
  addl    %edi, %eax
  pushl   %eax

  #
  # Pass stack base and size into the PEI Core
  #
  movl    PcdGet32(PcdTemporaryRamBase), %eax
  addl    PcdGet32(PcdTemporaryRamSize), %eax
  subl    PcdGet32(PcdFspTemporaryRamSize), %eax
  pushl   %eax
  pushl   PcdGet32(PcdFspTemporaryRamSize)

  #
  # Pass Control into the PEI Core
  #
  call    ASM_PFX(SecStartup)
  addl    $4, %esp
FspApiCommonExit:
  ret

