;
; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
; SPDX-License-Identifier: BSD-2-Clause-Patent
;
;
; Module Name:
;
;    Thunk64To32.nasm
;
; Abstract:
;
;   This is the assembly code to transition from long mode to compatibility mode to execute 32-bit code and then
;   transit back to long mode.
;
;-------------------------------------------------------------------------------
    DEFAULT REL
    SECTION .text
;----------------------------------------------------------------------------
; Procedure:    AsmExecute32BitCode
;
; Input:        None
;
; Output:       None
;
; Prototype:    UINT32
;               AsmExecute32BitCode (
;                 IN UINT64           Function,
;                 IN UINT64           Param1,
;                 IN UINT64           Param2,
;                 IN IA32_DESCRIPTOR  *InternalGdtr
;                 );
;
;
; Description:  A thunk function to execute 32-bit code in long mode.
;
;----------------------------------------------------------------------------
global ASM_PFX(AsmExecute32BitCode)
ASM_PFX(AsmExecute32BitCode):
    ;
    ; save IFLAG and disable it
    ;
    pushfq
    cli

    ;
    ; save original GDTR and CS
    ;
    mov     rax, ds
    push    rax
    mov     rax, cs
    push    rax
    sub     rsp, 0x10
    sgdt    [rsp]
    ;
    ; load internal GDT
    ;
    lgdt    [r9]
    ;
    ; Save general purpose register and rflag register
    ;
    pushfq
    push    rdi
    push    rsi
    push    rbp
    push    rbx

    ;
    ; save CR3
    ;
    mov     rax, cr3
    mov     rbp, rax

    ;
    ; Prepare the CS and return address for the transition from 32-bit to 64-bit mode
    ;
    mov     rax, dword 0x10              ; load long mode selector
    shl     rax, 32
    lea     r9,  [ReloadCS]          ;Assume the ReloadCS is under 4G
    or      rax, r9
    push    rax
    ;
    ; Save parameters for 32-bit function call
    ;
    mov     rax, r8
    shl     rax, 32
    or      rax, rdx
    push    rax
    ;
    ; save the 32-bit function entry and the return address into stack which will be
    ; retrieve in compatibility mode.
    ;
    lea     rax, [ReturnBack]   ;Assume the ReloadCS is under 4G
    shl     rax, 32
    or      rax, rcx
    push    rax

    ;
    ; let rax save DS
    ;
    mov     rax, dword 0x18

    ;
    ; Change to Compatible Segment
    ;
    mov     rcx, dword 0x8               ; load compatible mode selector
    shl     rcx, 32
    lea     rdx, [Compatible] ; assume address < 4G
    or      rcx, rdx
    push    rcx
    retf

Compatible:
    ; reload DS/ES/SS to make sure they are correct referred to current GDT
    mov     ds, ax
    mov     es, ax
    mov     ss, ax

    ;
    ; Disable paging
    ;
    mov     rcx, cr0
    btc     ecx, 31
    mov     cr0, rcx
    ;
    ; Clear EFER.LME
    ;
    mov     ecx, 0xC0000080
    rdmsr
    btc     eax, 8
    wrmsr

; Now we are in protected mode
    ;
    ; Call 32-bit function. Assume the function entry address and parameter value is less than 4G
    ;
    pop    rax                 ; Here is the function entry
    ;
    ; Now the parameter is at the bottom of the stack,  then call in to IA32 function.
    ;
    jmp   rax
ReturnBack:
    mov   ebx, eax             ; save return status
    pop   rcx                  ; drop param1
    pop   rcx                  ; drop param2

    ;
    ; restore CR4
    ;
    mov     rax, cr4
    bts     eax, 5
    mov     cr4, rax

    ;
    ; restore CR3
    ;
    mov     eax, ebp
    mov     cr3, rax

    ;
    ; Set EFER.LME to re-enable ia32-e
    ;
    mov     ecx, 0xC0000080
    rdmsr
    bts     eax, 8
    wrmsr
    ;
    ; Enable paging
    ;
    mov     rax, cr0
    bts     eax, 31
    mov     cr0, rax
; Now we are in compatible mode

    ;
    ; Reload cs register
    ;
    retf
ReloadCS:
    ;
    ; Now we're in Long Mode
    ;
    ;
    ; Restore C register and eax hold the return status from 32-bit function.
    ; Note: Do not touch rax from now which hold the return value from IA32 function
    ;
    mov     eax, ebx ; put return status to EAX
    pop     rbx
    pop     rbp
    pop     rsi
    pop     rdi
    popfq
    ;
    ; Switch to original GDT and CS. here rsp is pointer to the original GDT descriptor.
    ;
    lgdt    [rsp]
    ;
    ; drop GDT descriptor in stack
    ;
    add     rsp, 0x10
    ;
    ; switch to original CS and GDTR
    ;
    pop     r9                 ; get  CS
    shl     r9,  32            ; rcx[32..47] <- Cs
    lea     rcx, [.0]
    or      rcx, r9
    push    rcx
    retf
.0:
    ;
    ; Reload original DS/ES/SS
    ;
    pop     rcx
    mov     ds, rcx
    mov     es, rcx
    mov     ss, rcx

    ;
    ; Restore IFLAG
    ;
    popfq

    ret

