1
2#include "BaseLibInternals.h"
3
4;------------------------------------------------------------------------------
5;
6; Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
7; SPDX-License-Identifier: BSD-2-Clause-Patent
8;
9; Module Name:
10;
11;   Thunk.asm
12;
13; Abstract:
14;
15;   Real mode thunk
16;
17;------------------------------------------------------------------------------
18
19global ASM_PFX(m16Size)
20global ASM_PFX(mThunk16Attr)
21global ASM_PFX(m16Gdt)
22global ASM_PFX(m16GdtrBase)
23global ASM_PFX(mTransition)
24global ASM_PFX(m16Start)
25
26struc IA32_REGS
27
28  ._EDI:       resd      1
29  ._ESI:       resd      1
30  ._EBP:       resd      1
31  ._ESP:       resd      1
32  ._EBX:       resd      1
33  ._EDX:       resd      1
34  ._ECX:       resd      1
35  ._EAX:       resd      1
36  ._DS:        resw      1
37  ._ES:        resw      1
38  ._FS:        resw      1
39  ._GS:        resw      1
40  ._EFLAGS:    resd      1
41  ._EIP:       resd      1
42  ._CS:        resw      1
43  ._SS:        resw      1
44  .size:
45
46endstruc
47
48;; .const
49
50SECTION .data
51
52;
53; These are global constant to convey information to C code.
54;
55ASM_PFX(m16Size)         DW      ASM_PFX(InternalAsmThunk16) - ASM_PFX(m16Start)
56ASM_PFX(mThunk16Attr)    DW      _BackFromUserCode.ThunkAttrEnd - 4 - ASM_PFX(m16Start)
57ASM_PFX(m16Gdt)          DW      _NullSegDesc - ASM_PFX(m16Start)
58ASM_PFX(m16GdtrBase)     DW      _16GdtrBase - ASM_PFX(m16Start)
59ASM_PFX(mTransition)     DW      _EntryPoint - ASM_PFX(m16Start)
60
61SECTION .text
62
63ASM_PFX(m16Start):
64
65SavedGdt:
66            dw  0
67            dd  0
68
69;------------------------------------------------------------------------------
70; _BackFromUserCode() takes control in real mode after 'retf' has been executed
71; by user code. It will be shadowed to somewhere in memory below 1MB.
72;------------------------------------------------------------------------------
73_BackFromUserCode:
74    ;
75    ; The order of saved registers on the stack matches the order they appears
76    ; in IA32_REGS structure. This facilitates wrapper function to extract them
77    ; into that structure.
78    ;
79BITS    16
80    push    ss
81    push    cs
82    ;
83    ; Note: We can't use o32 on the next instruction because of a bug
84    ; in NASM 2.09.04 through 2.10rc1.
85    ;
86    call    dword .Base                 ; push eip
87.Base:
88    pushfd
89    cli                                 ; disable interrupts
90    push    gs
91    push    fs
92    push    es
93    push    ds
94    pushad
95    mov     edx, strict dword 0
96.ThunkAttrEnd:
97    test    dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15
98    jz      .1
99    mov     ax, 2401h
100    int     15h
101    cli                                 ; disable interrupts
102    jnc     .2
103.1:
104    test    dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL
105    jz      .2
106    in      al, 92h
107    or      al, 2
108    out     92h, al                     ; deactivate A20M#
109.2:
110    xor     eax, eax
111    mov     ax, ss
112    lea     ebp, [esp + IA32_REGS.size]
113    mov     [bp - IA32_REGS.size + IA32_REGS._ESP], ebp
114    mov     bx, [bp - IA32_REGS.size + IA32_REGS._EIP]
115    shl     eax, 4                      ; shl eax, 4
116    add     ebp, eax                    ; add ebp, eax
117    mov     eax, strict dword 0
118.SavedCr4End:
119    mov     cr4, eax
120o32 lgdt [cs:bx + (SavedGdt - .Base)]
121    mov     eax, strict dword 0
122.SavedCr0End:
123    mov     cr0, eax
124    mov     ax, strict word 0
125.SavedSsEnd:
126    mov     ss, eax
127    mov     esp, strict dword 0
128.SavedEspEnd:
129o32 retf                                ; return to protected mode
130
131_EntryPoint:
132        DD      _ToUserCode - ASM_PFX(m16Start)
133        DW      8h
134_16Idtr:
135        DW      (1 << 10) - 1
136        DD      0
137_16Gdtr:
138        DW      GdtEnd - _NullSegDesc - 1
139_16GdtrBase:
140        DD      0
141
142;------------------------------------------------------------------------------
143; _ToUserCode() takes control in real mode before passing control to user code.
144; It will be shadowed to somewhere in memory below 1MB.
145;------------------------------------------------------------------------------
146_ToUserCode:
147BITS    16
148    mov     dx, ss
149    mov     ss, cx                      ; set new segment selectors
150    mov     ds, cx
151    mov     es, cx
152    mov     fs, cx
153    mov     gs, cx
154    mov     cr0, eax                    ; real mode starts at next instruction
155                                        ;  which (per SDM) *must* be a far JMP.
156    jmp     0:strict word 0
157.RealAddrEnd:
158    mov     cr4, ebp
159    mov     ss, si                      ; set up 16-bit stack segment
160    xchg    esp, ebx                    ; set up 16-bit stack pointer
161    mov     bp, [esp + IA32_REGS.size]
162    mov     [cs:bp + (_BackFromUserCode.SavedSsEnd - 2 - _BackFromUserCode)], dx
163    mov     [cs:bp + (_BackFromUserCode.SavedEspEnd - 4 - _BackFromUserCode)], ebx
164    lidt    [cs:bp + (_16Idtr - _BackFromUserCode)]
165
166    popad
167    pop     ds
168    pop     es
169    pop     fs
170    pop     gs
171    popfd
172
173o32 retf                                ; transfer control to user code
174
175ALIGN   16
176_NullSegDesc    DQ      0
177_16CsDesc:
178                DW      -1
179                DW      0
180                DB      0
181                DB      9bh
182                DB      8fh             ; 16-bit segment, 4GB limit
183                DB      0
184_16DsDesc:
185                DW      -1
186                DW      0
187                DB      0
188                DB      93h
189                DB      8fh             ; 16-bit segment, 4GB limit
190                DB      0
191GdtEnd:
192
193;------------------------------------------------------------------------------
194; IA32_REGISTER_SET *
195; EFIAPI
196; InternalAsmThunk16 (
197;   IN      IA32_REGISTER_SET         *RegisterSet,
198;   IN OUT  VOID                      *Transition
199;   );
200;------------------------------------------------------------------------------
201global ASM_PFX(InternalAsmThunk16)
202ASM_PFX(InternalAsmThunk16):
203BITS    32
204    push    ebp
205    push    ebx
206    push    esi
207    push    edi
208    push    ds
209    push    es
210    push    fs
211    push    gs
212    mov     esi, [esp + 36]             ; esi <- RegSet, the 1st parameter
213    movzx   edx, word [esi + IA32_REGS._SS]
214    mov     edi, [esi + IA32_REGS._ESP]
215    add     edi, - (IA32_REGS.size + 4) ; reserve stack space
216    mov     ebx, edi                    ; ebx <- stack offset
217    imul    eax, edx, 16                ; eax <- edx * 16
218    push    IA32_REGS.size / 4
219    add     edi, eax                    ; edi <- linear address of 16-bit stack
220    pop     ecx
221    rep     movsd                       ; copy RegSet
222    mov     eax, [esp + 40]             ; eax <- address of transition code
223    mov     esi, edx                    ; esi <- 16-bit stack segment
224    lea     edx, [eax + (_BackFromUserCode.SavedCr0End - ASM_PFX(m16Start))]
225    mov     ecx, eax
226    and     ecx, 0fh
227    shl     eax, 12
228    lea     ecx, [ecx + (_BackFromUserCode - ASM_PFX(m16Start))]
229    mov     ax, cx
230    stosd                               ; [edi] <- return address of user code
231    add     eax, _ToUserCode.RealAddrEnd - _BackFromUserCode
232    mov     [edx + (_ToUserCode.RealAddrEnd - 4 - _BackFromUserCode.SavedCr0End)], eax
233    sgdt    [edx + (SavedGdt - _BackFromUserCode.SavedCr0End)]
234    sidt    [esp + 36]        ; save IDT stack in argument space
235    mov     eax, cr0
236    mov     [edx - 4], eax                  ; save CR0 in _BackFromUserCode.SavedCr0End - 4
237    and     eax, 7ffffffeh              ; clear PE, PG bits
238    mov     ebp, cr4
239    mov     [edx + (_BackFromUserCode.SavedCr4End - 4 - _BackFromUserCode.SavedCr0End)], ebp
240    and     ebp, ~30h                ; clear PAE, PSE bits
241    push    10h
242    pop     ecx                         ; ecx <- selector for data segments
243    lgdt    [edx + (_16Gdtr - _BackFromUserCode.SavedCr0End)]
244    pushfd                              ; Save df/if indeed
245    call    dword far [edx + (_EntryPoint - _BackFromUserCode.SavedCr0End)]
246    popfd
247    lidt    [esp + 36]        ; restore protected mode IDTR
248    lea     eax, [ebp - IA32_REGS.size] ; eax <- the address of IA32_REGS
249    pop     gs
250    pop     fs
251    pop     es
252    pop     ds
253    pop     edi
254    pop     esi
255    pop     ebx
256    pop     ebp
257    ret
258