#------------------------------------------------------------------------------
#
# Copyright (c) 2006 - 2008, Intel Corporation
# All rights reserved. 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:
#
#   Thunk16.S
#
# Abstract:
#
#   Real mode thunk
#
#------------------------------------------------------------------------------

#include <Library/BaseLib.h>

.globl ASM_PFX(m16Start), ASM_PFX(m16Size), ASM_PFX(mThunk16Attr), ASM_PFX(m16Gdt), ASM_PFX(m16GdtrBase), ASM_PFX(mTransition)
.globl ASM_PFX(InternalAsmThunk16)

ASM_PFX(m16Start):

SavedGdt:     .space  6

ASM_PFX(BackFromUserCode):
    push    %ss
    push    %cs
    .byte   0x66
    call    L_Base1                     # push eip
L_Base1:
    pushfw                              # pushfd actually
    cli                                 # disable interrupts
    push    %gs
    push    %fs
    push    %es
    push    %ds
    pushaw                              # pushad actually
    .byte   0x66, 0xba                  # mov edx, imm32
ASM_PFX(ThunkAttr): .space  4
    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15, %dl
    jz      1f
    movl    $0x15cd2401, %eax           # mov ax, 2401h & int 15h
    cli                                 # disable interrupts
    jnc     2f
1:
    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL, %dl
    jz      2f
    inb     $0x92, %al
    orb     $2, %al
    outb    %al, $0x92                  # deactivate A20M#
2:
    movl    %ss, %eax
    .byte   0x67, 0x66, 0x8d, 0x6c, 0x24, 0x34, 0x66
    mov     %ebp, 0xffffffd8(%esi)
    mov     0xfffffff8(%esi), %ebx
    shlw    $4, %ax                     # shl eax, 4
    addw    %ax, %bp                    # add ebp, eax
    .byte   0x66, 0xb8                   # mov eax, imm32
SavedCr4:   .space  4
    movl    %eax, %cr4
    lgdtw   %cs:0xfffffff2(%edi)
    .byte   0x66, 0xb8                   # mov eax, imm32
SavedCr0:   .space  4
    movl    %eax, %cr0
    .byte   0xb8                        # mov ax, imm16
SavedSs:    .space  2
    movl    %eax, %ss
    .byte   0x66, 0xbc                   # mov esp, imm32
SavedEsp:   .space  4
    .byte   0x66
    lret                                # return to protected mode

_EntryPoint:    .long      ASM_PFX(ToUserCode) - ASM_PFX(m16Start)
                .word      0x8
_16Idtr:        .word      0x3ff
                .long      0
_16Gdtr:        .word      GdtEnd - _NullSegDesc - 1
_16GdtrBase:    .long      _NullSegDesc

ASM_PFX(ToUserCode):
    movl    %ss, %edx
    movl    %ecx, %ss                   # set new segment selectors
    movl    %ecx, %ds
    movl    %ecx, %es
    movl    %ecx, %fs
    movl    %ecx, %gs
    movl    %eax, %cr0
    movl    %ebp, %cr4                  # real mode starts at next instruction
    movl    %esi, %ss                   # set up 16-bit stack segment
    xchgw   %bx, %sp                    # set up 16-bit stack pointer
    .byte   0x66
    call    L_Base                      # push eip
L_Base:
    popw    %bp                         # ebp <- offset L_Base
    .byte   0x67;                       # address size override
    push    54(%esp)
    lea     0xc(%esi), %eax
    push    %eax
    lret

L_RealMode:
    mov     %edx, %cs:0xffffffc5(%esi)
    mov     %bx, %cs:0xffffffcb(%esi)
    lidtw   %cs:0xffffffd7(%esi)
    popaw                               # popad actually
    pop     %ds
    pop     %es
    pop     %fs
    pop     %gs
    popfw                               # popfd
    lretw                               # transfer control to user code

_NullSegDesc:   .quad   0
_16CsDesc:
                .word   -1
                .word   0
                .byte   0
                .byte   0x9b
                .byte   0x8f            # 16-bit segment, 4GB limit
                .byte   0
_16DsDesc:
                .word   -1
                .word   0
                .byte   0
                .byte   0x93
                .byte   0x8f            # 16-bit segment, 4GB limit
                .byte   0
GdtEnd:

#
#   @param  RegSet  Pointer to a IA32_DWORD_REGS structure
#   @param  Transition  Pointer to the transition code
#   @return The address of the 16-bit stack after returning from user code
#
ASM_PFX(InternalAsmThunk16):
    push    %ebp
    push    %ebx
    push    %esi
    push    %edi
    push    %ds
    push    %es
    push    %fs
    push    %gs
    movl    36(%esp), %esi              # esi <- RegSet
    movzwl  0x32(%esi), %edx
    mov     0xc(%esi), %edi
    add     $0xffffffc8, %edi
    movl    %edi, %ebx                  # ebx <- stack offset
    imul    $0x10, %edx, %eax
    push    $0xd
    addl    %eax, %edi                  # edi <- linear address of 16-bit stack
    pop     %ecx
    rep
    movsl                               # copy RegSet
    movl    40(%esp), %eax              # eax <- address of transition code
    movl    %edx, %esi                  # esi <- 16-bit stack segment
    lea     0x5e(%eax), %edx
    movl    %eax, %ecx
    andl    $0xf, %ecx
    shll    $12, %eax
    lea     0x6(%ecx), %ecx
    movw    %cx, %ax
    stosl                               # [edi] <- return address of user code
    sgdtl   0xffffffa2(%edx)
    sidtl   0x24(%esp)
    movl    %cr0, %eax
    movl    %eax, (%edx)                # save CR0 in SavedCr0
    andl    $0x7ffffffe, %eax           # clear PE, PG bits
    movl    %cr4, %ebp
    mov     %ebp, 0xfffffff1(%edx)
    andl    $0x300, %ebp                # clear all but PCE and OSFXSR bits
    pushl   $0x10
    pop     %ecx                        # ecx <- selector for data segments
    lgdtl   0x20(%edx)
    pushfl
    lcall   *0x14(%edx)
    popfl
    lidtl   0x24(%esp)
    lea     0xffffffcc(%ebp), %eax
    pop     %gs
    pop     %fs
    pop     %es
    pop     %ds
    pop     %edi
    pop     %esi
    pop     %ebx
    pop     %ebp
    ret

    .const:

ASM_PFX(m16Size):        .word      _InternalAsmThunk16 - ASM_PFX(m16Start)
ASM_PFX(mThunk16Attr):   .word      _ThunkAttr          - ASM_PFX(m16Start)
ASM_PFX(m16Gdt):         .word      _NullSegDesc        - ASM_PFX(m16Start)
ASM_PFX(m16GdtrBase):    .word      _16GdtrBase         - ASM_PFX(m16Start)
ASM_PFX(mTransition):    .word      _EntryPoint         - ASM_PFX(m16Start)
