blob: 03a300dc68640b7c3d651f9f1b9df0c921d14ecb [file] [log] [blame]
#include "BaseLibInternals.h"
;------------------------------------------------------------------------------
;
; Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
; SPDX-License-Identifier: BSD-2-Clause-Patent
;
; Module Name:
;
; Thunk.asm
;
; Abstract:
;
; Real mode thunk
;
;------------------------------------------------------------------------------
global ASM_PFX(m16Size)
global ASM_PFX(mThunk16Attr)
global ASM_PFX(m16Gdt)
global ASM_PFX(m16GdtrBase)
global ASM_PFX(mTransition)
global ASM_PFX(m16Start)
struc IA32_REGS
._EDI: resd 1
._ESI: resd 1
._EBP: resd 1
._ESP: resd 1
._EBX: resd 1
._EDX: resd 1
._ECX: resd 1
._EAX: resd 1
._DS: resw 1
._ES: resw 1
._FS: resw 1
._GS: resw 1
._EFLAGS: resd 1
._EIP: resd 1
._CS: resw 1
._SS: resw 1
.size:
endstruc
;; .const
SECTION .data
;
; These are global constant to convey information to C code.
;
ASM_PFX(m16Size) DW ASM_PFX(InternalAsmThunk16) - ASM_PFX(m16Start)
ASM_PFX(mThunk16Attr) DW _BackFromUserCode.ThunkAttrEnd - 4 - ASM_PFX(m16Start)
ASM_PFX(m16Gdt) DW _NullSegDesc - ASM_PFX(m16Start)
ASM_PFX(m16GdtrBase) DW _16GdtrBase - ASM_PFX(m16Start)
ASM_PFX(mTransition) DW _EntryPoint - ASM_PFX(m16Start)
SECTION .text
ASM_PFX(m16Start):
SavedGdt:
dw 0
dd 0
;------------------------------------------------------------------------------
; _BackFromUserCode() takes control in real mode after 'retf' has been executed
; by user code. It will be shadowed to somewhere in memory below 1MB.
;------------------------------------------------------------------------------
_BackFromUserCode:
;
; The order of saved registers on the stack matches the order they appears
; in IA32_REGS structure. This facilitates wrapper function to extract them
; into that structure.
;
BITS 16
push ss
push cs
;
; Note: We can't use o32 on the next instruction because of a bug
; in NASM 2.09.04 through 2.10rc1.
;
call dword .Base ; push eip
.Base:
pushfd
cli ; disable interrupts
push gs
push fs
push es
push ds
pushad
mov edx, strict dword 0
.ThunkAttrEnd:
test dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15
jz .1
mov ax, 2401h
int 15h
cli ; disable interrupts
jnc .2
.1:
test dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL
jz .2
in al, 92h
or al, 2
out 92h, al ; deactivate A20M#
.2:
xor eax, eax
mov ax, ss
lea ebp, [esp + IA32_REGS.size]
mov [bp - IA32_REGS.size + IA32_REGS._ESP], ebp
mov bx, [bp - IA32_REGS.size + IA32_REGS._EIP]
shl eax, 4 ; shl eax, 4
add ebp, eax ; add ebp, eax
mov eax, strict dword 0
.SavedCr4End:
mov cr4, eax
o32 lgdt [cs:bx + (SavedGdt - .Base)]
mov eax, strict dword 0
.SavedCr0End:
mov cr0, eax
mov ax, strict word 0
.SavedSsEnd:
mov ss, eax
mov esp, strict dword 0
.SavedEspEnd:
o32 retf ; return to protected mode
_EntryPoint:
DD _ToUserCode - ASM_PFX(m16Start)
DW 8h
_16Idtr:
DW (1 << 10) - 1
DD 0
_16Gdtr:
DW GdtEnd - _NullSegDesc - 1
_16GdtrBase:
DD 0
;------------------------------------------------------------------------------
; _ToUserCode() takes control in real mode before passing control to user code.
; It will be shadowed to somewhere in memory below 1MB.
;------------------------------------------------------------------------------
_ToUserCode:
BITS 16
mov dx, ss
mov ss, cx ; set new segment selectors
mov ds, cx
mov es, cx
mov fs, cx
mov gs, cx
mov cr0, eax ; real mode starts at next instruction
; which (per SDM) *must* be a far JMP.
jmp 0:strict word 0
.RealAddrEnd:
mov cr4, ebp
mov ss, si ; set up 16-bit stack segment
xchg esp, ebx ; set up 16-bit stack pointer
mov bp, [esp + IA32_REGS.size]
mov [cs:bp + (_BackFromUserCode.SavedSsEnd - 2 - _BackFromUserCode)], dx
mov [cs:bp + (_BackFromUserCode.SavedEspEnd - 4 - _BackFromUserCode)], ebx
lidt [cs:bp + (_16Idtr - _BackFromUserCode)]
popad
pop ds
pop es
pop fs
pop gs
popfd
o32 retf ; transfer control to user code
ALIGN 16
_NullSegDesc DQ 0
_16CsDesc:
DW -1
DW 0
DB 0
DB 9bh
DB 8fh ; 16-bit segment, 4GB limit
DB 0
_16DsDesc:
DW -1
DW 0
DB 0
DB 93h
DB 8fh ; 16-bit segment, 4GB limit
DB 0
GdtEnd:
;------------------------------------------------------------------------------
; IA32_REGISTER_SET *
; EFIAPI
; InternalAsmThunk16 (
; IN IA32_REGISTER_SET *RegisterSet,
; IN OUT VOID *Transition
; );
;------------------------------------------------------------------------------
global ASM_PFX(InternalAsmThunk16)
ASM_PFX(InternalAsmThunk16):
BITS 32
push ebp
push ebx
push esi
push edi
push ds
push es
push fs
push gs
mov esi, [esp + 36] ; esi <- RegSet, the 1st parameter
movzx edx, word [esi + IA32_REGS._SS]
mov edi, [esi + IA32_REGS._ESP]
add edi, - (IA32_REGS.size + 4) ; reserve stack space
mov ebx, edi ; ebx <- stack offset
imul eax, edx, 16 ; eax <- edx * 16
push IA32_REGS.size / 4
add edi, eax ; edi <- linear address of 16-bit stack
pop ecx
rep movsd ; copy RegSet
mov eax, [esp + 40] ; eax <- address of transition code
mov esi, edx ; esi <- 16-bit stack segment
lea edx, [eax + (_BackFromUserCode.SavedCr0End - ASM_PFX(m16Start))]
mov ecx, eax
and ecx, 0fh
shl eax, 12
lea ecx, [ecx + (_BackFromUserCode - ASM_PFX(m16Start))]
mov ax, cx
stosd ; [edi] <- return address of user code
add eax, _ToUserCode.RealAddrEnd - _BackFromUserCode
mov [edx + (_ToUserCode.RealAddrEnd - 4 - _BackFromUserCode.SavedCr0End)], eax
sgdt [edx + (SavedGdt - _BackFromUserCode.SavedCr0End)]
sidt [esp + 36] ; save IDT stack in argument space
mov eax, cr0
mov [edx - 4], eax ; save CR0 in _BackFromUserCode.SavedCr0End - 4
and eax, 7ffffffeh ; clear PE, PG bits
mov ebp, cr4
mov [edx + (_BackFromUserCode.SavedCr4End - 4 - _BackFromUserCode.SavedCr0End)], ebp
and ebp, ~30h ; clear PAE, PSE bits
push 10h
pop ecx ; ecx <- selector for data segments
lgdt [edx + (_16Gdtr - _BackFromUserCode.SavedCr0End)]
pushfd ; Save df/if indeed
call dword far [edx + (_EntryPoint - _BackFromUserCode.SavedCr0End)]
popfd
lidt [esp + 36] ; restore protected mode IDTR
lea eax, [ebp - IA32_REGS.size] ; eax <- the address of IA32_REGS
pop gs
pop fs
pop es
pop ds
pop edi
pop esi
pop ebx
pop ebp
ret