1
2#include "BaseLibInternals.h"
3
4;------------------------------------------------------------------------------
5;
6; Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
7; This program and the accompanying materials
8; are licensed and made available under the terms and conditions of the BSD License
9; which accompanies this distribution.  The full text of the license may be found at
10; http://opensource.org/licenses/bsd-license.php.
11;
12; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14;
15; Module Name:
16;
17;   Thunk.asm
18;
19; Abstract:
20;
21;   Real mode thunk
22;
23;------------------------------------------------------------------------------
24
25    .686p
26    .model  flat,C
27
28EXTERNDEF   C   m16Start:BYTE
29EXTERNDEF   C   m16Size:WORD
30EXTERNDEF   C   mThunk16Attr:WORD
31EXTERNDEF   C   m16Gdt:WORD
32EXTERNDEF   C   m16GdtrBase:WORD
33EXTERNDEF   C   mTransition:WORD
34
35;
36; Here is the layout of the real mode stack. _ToUserCode() is responsible for
37; loading all these registers from real mode stack.
38;
39IA32_REGS   STRUC   4t
40_EDI        DD      ?
41_ESI        DD      ?
42_EBP        DD      ?
43_ESP        DD      ?
44_EBX        DD      ?
45_EDX        DD      ?
46_ECX        DD      ?
47_EAX        DD      ?
48_DS         DW      ?
49_ES         DW      ?
50_FS         DW      ?
51_GS         DW      ?
52_EFLAGS     DD      ?
53_EIP        DD      ?
54_CS         DW      ?
55_SS         DW      ?
56IA32_REGS   ENDS
57
58    .const
59
60;
61; These are global constant to convey information to C code.
62;
63m16Size         DW      InternalAsmThunk16 - m16Start
64mThunk16Attr    DW      _ThunkAttr - m16Start
65m16Gdt          DW      _NullSegDesc - m16Start
66m16GdtrBase     DW      _16GdtrBase - m16Start
67mTransition     DW      _EntryPoint - m16Start
68
69    .code
70
71m16Start    LABEL   BYTE
72
73SavedGdt    LABEL   FWORD
74            DW      ?
75            DD      ?
76;------------------------------------------------------------------------------
77; _BackFromUserCode() takes control in real mode after 'retf' has been executed
78; by user code. It will be shadowed to somewhere in memory below 1MB.
79;------------------------------------------------------------------------------
80_BackFromUserCode   PROC
81    ;
82    ; The order of saved registers on the stack matches the order they appears
83    ; in IA32_REGS structure. This facilitates wrapper function to extract them
84    ; into that structure.
85    ;
86    push    ss
87    push    cs
88    DB      66h
89    call    @Base                       ; push eip
90@Base:
91    pushf                               ; pushfd actually
92    cli                                 ; disable interrupts
93    push    gs
94    push    fs
95    push    es
96    push    ds
97    pushaw                              ; pushad actually
98    DB      66h, 0bah                   ; mov edx, imm32
99_ThunkAttr  DD      ?
100    test    dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15
101    jz      @1
102    mov     eax, 15cd2401h              ; mov ax, 2401h & int 15h
103    cli                                 ; disable interrupts
104    jnc     @2
105@1:
106    test    dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL
107    jz      @2
108    in      al, 92h
109    or      al, 2
110    out     92h, al                     ; deactivate A20M#
111@2:
112    xor     ax, ax                      ; xor eax, eax
113    mov     eax, ss                     ; mov ax, ss
114    DB      67h
115    lea     bp, [esp + sizeof (IA32_REGS)]
116    ;
117    ; esi's in the following 2 instructions are indeed bp in 16-bit code. Fact
118    ; is "esi" in 32-bit addressing mode has the same encoding of "bp" in 16-
119    ; bit addressing mode.
120    ;
121    mov     word ptr (IA32_REGS ptr [esi - sizeof (IA32_REGS)])._ESP, bp
122    mov     ebx, (IA32_REGS ptr [esi - sizeof (IA32_REGS)])._EIP
123    shl     ax, 4                       ; shl eax, 4
124    add     bp, ax                      ; add ebp, eax
125    DB      66h, 0b8h                   ; mov eax, imm32
126SavedCr4    DD      ?
127    mov     cr4, eax
128    DB      66h
129    lgdt    fword ptr cs:[edi + (SavedGdt - @Base)]
130    DB      66h, 0b8h                   ; mov eax, imm32
131SavedCr0    DD      ?
132    mov     cr0, eax
133    DB      0b8h                        ; mov ax, imm16
134SavedSs     DW      ?
135    mov     ss, eax
136    DB      66h, 0bch                   ; mov esp, imm32
137SavedEsp    DD      ?
138    DB      66h
139    retf                                ; return to protected mode
140_BackFromUserCode   ENDP
141
142_EntryPoint DD      _ToUserCode - m16Start
143            DW      8h
144_16Idtr     FWORD   (1 SHL 10) - 1
145_16Gdtr     LABEL   FWORD
146            DW      GdtEnd - _NullSegDesc - 1
147_16GdtrBase DD      _NullSegDesc
148
149;------------------------------------------------------------------------------
150; _ToUserCode() takes control in real mode before passing control to user code.
151; It will be shadowed to somewhere in memory below 1MB.
152;------------------------------------------------------------------------------
153_ToUserCode PROC
154    mov     edx, ss
155    mov     ss, ecx                     ; set new segment selectors
156    mov     ds, ecx
157    mov     es, ecx
158    mov     fs, ecx
159    mov     gs, ecx
160    mov     cr0, eax                    ; real mode starts at next instruction
161                                        ;  which (per SDM) *must* be a far JMP.
162    DB      0eah
163_RealAddr DW 0,0                       ; filled in by InternalAsmThunk16
164
165    mov     cr4, ebp
166    mov     ss, esi                     ; set up 16-bit stack segment
167    xchg    sp, bx                      ; set up 16-bit stack pointer
168
169;   mov     bp, [esp + sizeof(IA32_REGS)
170    DB      67h
171    mov     ebp, [esp + sizeof(IA32_REGS)] ; BackFromUserCode address from stack
172
173;   mov     cs:[bp + (SavedSs - _BackFromUserCode)], dx
174    mov     cs:[esi + (SavedSs - _BackFromUserCode)], edx
175
176;   mov     cs:[bp + (SavedEsp - _BackFromUserCode)], ebx
177    DB      2eh, 66h, 89h, 9eh
178    DW      SavedEsp - _BackFromUserCode
179
180;   lidt    cs:[bp + (_16Idtr - _BackFromUserCode)]
181    DB      2eh, 66h, 0fh, 01h, 9eh
182    DW      _16Idtr - _BackFromUserCode
183
184    popaw                               ; popad actually
185    pop     ds
186    pop     es
187    pop     fs
188    pop     gs
189    popf                                ; popfd
190    DB      66h                         ; Use 32-bit addressing for "retf" below
191    retf                                ; transfer control to user code
192_ToUserCode ENDP
193
194_NullSegDesc    DQ      0
195_16CsDesc       LABEL   QWORD
196                DW      -1
197                DW      0
198                DB      0
199                DB      9bh
200                DB      8fh             ; 16-bit segment, 4GB limit
201                DB      0
202_16DsDesc       LABEL   QWORD
203                DW      -1
204                DW      0
205                DB      0
206                DB      93h
207                DB      8fh             ; 16-bit segment, 4GB limit
208                DB      0
209GdtEnd          LABEL   QWORD
210
211;------------------------------------------------------------------------------
212; IA32_REGISTER_SET *
213; EFIAPI
214; InternalAsmThunk16 (
215;   IN      IA32_REGISTER_SET         *RegisterSet,
216;   IN OUT  VOID                      *Transition
217;   );
218;------------------------------------------------------------------------------
219InternalAsmThunk16  PROC    USES    ebp ebx esi edi ds  es  fs  gs
220    mov     esi, [esp + 36]             ; esi <- RegSet, the 1st parameter
221    movzx   edx, (IA32_REGS ptr [esi])._SS
222    mov     edi, (IA32_REGS ptr [esi])._ESP
223    add     edi, - (sizeof (IA32_REGS) + 4) ; reserve stack space
224    mov     ebx, edi                    ; ebx <- stack offset
225    imul    eax, edx, 16                ; eax <- edx * 16
226    push    sizeof (IA32_REGS) / 4
227    add     edi, eax                    ; edi <- linear address of 16-bit stack
228    pop     ecx
229    rep     movsd                       ; copy RegSet
230    mov     eax, [esp + 40]             ; eax <- address of transition code
231    mov     esi, edx                    ; esi <- 16-bit stack segment
232    lea     edx, [eax + (SavedCr0 - m16Start)]
233    mov     ecx, eax
234    and     ecx, 0fh
235    shl     eax, 12
236    lea     ecx, [ecx + (_BackFromUserCode - m16Start)]
237    mov     ax, cx
238    stosd                               ; [edi] <- return address of user code
239    add     eax, _RealAddr + 4 - _BackFromUserCode
240    mov     dword ptr [edx + (_RealAddr - SavedCr0)], eax
241    sgdt    fword ptr [edx + (SavedGdt - SavedCr0)]
242    sidt    fword ptr [esp + 36]        ; save IDT stack in argument space
243    mov     eax, cr0
244    mov     [edx], eax                  ; save CR0 in SavedCr0
245    and     eax, 7ffffffeh              ; clear PE, PG bits
246    mov     ebp, cr4
247    mov     [edx + (SavedCr4 - SavedCr0)], ebp
248    and     ebp, NOT 30h                ; clear PAE, PSE bits
249    push    10h
250    pop     ecx                         ; ecx <- selector for data segments
251    lgdt    fword ptr [edx + (_16Gdtr - SavedCr0)]
252    pushfd                              ; Save df/if indeed
253    call    fword ptr [edx + (_EntryPoint - SavedCr0)]
254    popfd
255    lidt    fword ptr [esp + 36]        ; restore protected mode IDTR
256    lea     eax, [ebp - sizeof (IA32_REGS)] ; eax <- the address of IA32_REGS
257    ret
258InternalAsmThunk16  ENDP
259
260    END
261