#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 |