Use rsp instead of esp to save 64-bit stack pointer.
[mirror_edk2.git] / MdePkg / Library / BaseLib / X64 / Thunk16.asm
1 \r
2 #include "BaseLibInternals.h"\r
3 \r
4 ;------------------------------------------------------------------------------\r
5 ;\r
6 ; Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>\r
7 ; This program and the accompanying materials\r
8 ; are licensed and made available under the terms and conditions of the BSD License\r
9 ; which accompanies this distribution.  The full text of the license may be found at\r
10 ; http://opensource.org/licenses/bsd-license.php.\r
11 ;\r
12 ; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 ; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14 ;\r
15 ; Module Name:\r
16 ;\r
17 ;   Thunk.asm\r
18 ;\r
19 ; Abstract:\r
20 ;\r
21 ;   Real mode thunk\r
22 ;\r
23 ;------------------------------------------------------------------------------\r
24 \r
25 EXTERNDEF   m16Start:BYTE\r
26 EXTERNDEF   m16Size:WORD\r
27 EXTERNDEF   mThunk16Attr:WORD\r
28 EXTERNDEF   m16Gdt:WORD\r
29 EXTERNDEF   m16GdtrBase:WORD\r
30 EXTERNDEF   mTransition:WORD\r
31 \r
32 IA32_REGS   STRUC   4t\r
33 _EDI        DD      ?\r
34 _ESI        DD      ?\r
35 _EBP        DD      ?\r
36 _ESP        DD      ?\r
37 _EBX        DD      ?\r
38 _EDX        DD      ?\r
39 _ECX        DD      ?\r
40 _EAX        DD      ?\r
41 _DS         DW      ?\r
42 _ES         DW      ?\r
43 _FS         DW      ?\r
44 _GS         DW      ?\r
45 _EFLAGS     DQ      ?\r
46 _EIP        DD      ?\r
47 _CS         DW      ?\r
48 _SS         DW      ?\r
49 IA32_REGS   ENDS\r
50 \r
51     .const\r
52 \r
53 m16Size         DW      InternalAsmThunk16 - m16Start\r
54 mThunk16Attr    DW      _ThunkAttr - m16Start\r
55 m16Gdt          DW      _NullSeg - m16Start\r
56 m16GdtrBase     DW      _16GdtrBase - m16Start\r
57 mTransition     DW      _EntryPoint - m16Start\r
58 \r
59     .code\r
60 \r
61 m16Start    LABEL   BYTE\r
62 \r
63 SavedGdt    LABEL   FWORD\r
64             DW      ?\r
65             DQ      ?\r
66 \r
67 ;------------------------------------------------------------------------------\r
68 ; _BackFromUserCode() takes control in real mode after 'retf' has been executed\r
69 ; by user code. It will be shadowed to somewhere in memory below 1MB.\r
70 ;------------------------------------------------------------------------------\r
71 _BackFromUserCode   PROC\r
72     ;\r
73     ; The order of saved registers on the stack matches the order they appears\r
74     ; in IA32_REGS structure. This facilitates wrapper function to extract them\r
75     ; into that structure.\r
76     ;\r
77     ; Some instructions for manipulation of segment registers have to be written\r
78     ; in opcode since 64-bit MASM prevents accesses to those registers.\r
79     ;\r
80     DB      16h                         ; push ss\r
81     DB      0eh                         ; push cs\r
82     DB      66h\r
83     call    @Base                       ; push eip\r
84 @Base:\r
85     DB      66h\r
86     push    0                           ; reserved high order 32 bits of EFlags\r
87     pushf                               ; pushfd actually\r
88     cli                                 ; disable interrupts\r
89     push    gs\r
90     push    fs\r
91     DB      6                           ; push es\r
92     DB      1eh                         ; push ds\r
93     DB      66h, 60h                    ; pushad\r
94     DB      66h, 0bah                   ; mov edx, imm32\r
95 _ThunkAttr  DD      ?\r
96     test    dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15\r
97     jz      @1\r
98     mov     eax, 15cd2401h              ; mov ax, 2401h & int 15h\r
99     cli                                 ; disable interrupts\r
100     jnc     @2\r
101 @1:\r
102     test    dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL\r
103     jz      @2\r
104     in      al, 92h\r
105     or      al, 2\r
106     out     92h, al                     ; deactivate A20M#\r
107 @2:\r
108     xor     ax, ax                      ; xor eax, eax\r
109     mov     eax, ss                     ; mov ax, ss\r
110     lea     bp, [esp + sizeof (IA32_REGS)]\r
111     ;\r
112     ; rsi in the following 2 instructions is indeed bp in 16-bit code\r
113     ;\r
114     mov     word ptr (IA32_REGS ptr [rsi - sizeof (IA32_REGS)])._ESP, bp\r
115     DB      66h\r
116     mov     ebx, (IA32_REGS ptr [rsi - sizeof (IA32_REGS)])._EIP\r
117     shl     ax, 4                       ; shl eax, 4\r
118     add     bp, ax                      ; add ebp, eax\r
119     mov     ax, cs\r
120     shl     ax, 4\r
121     lea     ax, [eax + ebx + (@64BitCode - @Base)]\r
122     DB      66h, 2eh, 89h, 87h          ; mov cs:[bx + (@64Eip - @Base)], eax\r
123     DW      @64Eip - @Base\r
124     DB      66h, 0b8h                   ; mov eax, imm32\r
125 SavedCr4    DD      ?\r
126     mov     cr4, rax\r
127     ;\r
128     ; rdi in the instruction below is indeed bx in 16-bit code\r
129     ;\r
130     DB      66h, 2eh                    ; 2eh is "cs:" segment override\r
131     lgdt    fword ptr [rdi + (SavedGdt - @Base)]\r
132     DB      66h\r
133     mov     ecx, 0c0000080h\r
134     rdmsr\r
135     or      ah, 1\r
136     wrmsr\r
137     DB      66h, 0b8h                   ; mov eax, imm32\r
138 SavedCr0    DD      ?\r
139     mov     cr0, rax\r
140     DB      66h, 0eah                   ; jmp far cs:@64Bit\r
141 @64Eip      DD      ?\r
142 SavedCs     DW      ?\r
143 @64BitCode:\r
144     db      090h \r
145     db      048h, 0bch                 ; mov rsp, imm64\r
146 SavedSp     DQ   ?                     ; restore stack\r
147     nop\r
148     ret\r
149 _BackFromUserCode   ENDP\r
150 \r
151 _EntryPoint DD      _ToUserCode - m16Start\r
152             DW      CODE16\r
153 _16Gdtr     LABEL   FWORD\r
154             DW      GDT_SIZE - 1\r
155 _16GdtrBase DQ      _NullSeg\r
156 _16Idtr     FWORD   (1 SHL 10) - 1\r
157 \r
158 ;------------------------------------------------------------------------------\r
159 ; _ToUserCode() takes control in real mode before passing control to user code.\r
160 ; It will be shadowed to somewhere in memory below 1MB.\r
161 ;------------------------------------------------------------------------------\r
162 _ToUserCode PROC\r
163     mov     ss, edx                     ; set new segment selectors\r
164     mov     ds, edx\r
165     mov     es, edx\r
166     mov     fs, edx\r
167     mov     gs, edx\r
168     DB      66h\r
169     mov     ecx, 0c0000080h\r
170     mov     cr0, rax                    ; real mode starts at next instruction\r
171     rdmsr\r
172     and     ah, NOT 1\r
173     wrmsr\r
174     mov     cr4, rbp\r
175     mov     ss, esi                     ; set up 16-bit stack segment\r
176     mov     sp, bx                      ; set up 16-bit stack pointer\r
177     DB      66h                         ; make the following call 32-bit\r
178     call    @Base                       ; push eip\r
179 @Base:\r
180     pop     bp                          ; ebp <- address of @Base\r
181     push    [esp + sizeof (IA32_REGS) + 2]\r
182     lea     eax, [rsi + (@RealMode - @Base)]    ; rsi is "bp" in 16-bit code\r
183     push    rax\r
184     retf                                ; execution begins at next instruction\r
185 @RealMode:\r
186     DB      66h, 2eh                    ; CS and operand size override\r
187     lidt    fword ptr [rsi + (_16Idtr - @Base)]\r
188     DB      66h, 61h                    ; popad\r
189     DB      1fh                         ; pop ds\r
190     DB      07h                         ; pop es\r
191     pop     fs\r
192     pop     gs\r
193     popf                                ; popfd\r
194     lea     sp, [esp + 4]               ; skip high order 32 bits of EFlags\r
195     DB      66h                         ; make the following retf 32-bit\r
196     retf                                ; transfer control to user code\r
197 _ToUserCode ENDP\r
198 \r
199 CODE16  = _16Code - $\r
200 DATA16  = _16Data - $\r
201 DATA32  = _32Data - $\r
202 \r
203 _NullSeg    DQ      0\r
204 _16Code     LABEL   QWORD\r
205             DW      -1\r
206             DW      0\r
207             DB      0\r
208             DB      9bh\r
209             DB      8fh                 ; 16-bit segment, 4GB limit\r
210             DB      0\r
211 _16Data     LABEL   QWORD\r
212             DW      -1\r
213             DW      0\r
214             DB      0\r
215             DB      93h\r
216             DB      8fh                 ; 16-bit segment, 4GB limit\r
217             DB      0\r
218 _32Data     LABEL   QWORD\r
219             DW      -1\r
220             DW      0\r
221             DB      0\r
222             DB      93h\r
223             DB      0cfh                ; 16-bit segment, 4GB limit\r
224             DB      0\r
225 \r
226 GDT_SIZE = $ - _NullSeg\r
227 \r
228 ;------------------------------------------------------------------------------\r
229 ; IA32_REGISTER_SET *\r
230 ; EFIAPI\r
231 ; InternalAsmThunk16 (\r
232 ;   IN      IA32_REGISTER_SET         *RegisterSet,\r
233 ;   IN OUT  VOID                      *Transition\r
234 ;   );\r
235 ;------------------------------------------------------------------------------\r
236 InternalAsmThunk16  PROC    USES    rbp rbx rsi rdi\r
237     mov     rbx, ds\r
238     push    rbx          ; Save ds segment register on the stack\r
239     mov     rbx, es\r
240     push    rbx          ; Save es segment register on the stack\r
241     mov     rbx, ss\r
242     push    rbx          ; Save ss segment register on the stack\r
243     \r
244     push    fs\r
245     push    gs\r
246     mov     rsi, rcx\r
247     movzx   r8d, (IA32_REGS ptr [rsi])._SS\r
248     mov     edi, (IA32_REGS ptr [rsi])._ESP\r
249     lea     rdi, [edi - (sizeof (IA32_REGS) + 4)]\r
250     imul    eax, r8d, 16                ; eax <- r8d(stack segment) * 16\r
251     mov     ebx, edi                    ; ebx <- stack for 16-bit code\r
252     push    sizeof (IA32_REGS) / 4\r
253     add     edi, eax                    ; edi <- linear address of 16-bit stack\r
254     pop     rcx\r
255     rep     movsd                       ; copy RegSet\r
256     lea     ecx, [rdx + (SavedCr4 - m16Start)]\r
257     mov     eax, edx                    ; eax <- transition code address\r
258     and     edx, 0fh\r
259     shl     eax, 12                     ; segment address in high order 16 bits\r
260     lea     ax, [rdx + (_BackFromUserCode - m16Start)]  ; offset address\r
261     stosd                               ; [edi] <- return address of user code\r
262   \r
263     sgdt    fword ptr [rsp + 60h]       ; save GDT stack in argument space\r
264     movzx   r10, word ptr [rsp + 60h]   ; r10 <- GDT limit \r
265     lea     r11, [rcx + (InternalAsmThunk16 - SavedCr4) + 0xf]\r
266     and     r11, 0xfffffff0             ; r11 <- 16-byte aligned shadowed GDT table in real mode buffer\r
267     \r
268     mov     word ptr [rcx + (SavedGdt - SavedCr4)], r10w      ; save the limit of shadowed GDT table\r
269     mov     qword ptr [rcx + (SavedGdt - SavedCr4) + 2], r11  ; save the base address of shadowed GDT table\r
270     \r
271     mov     rsi, qword ptr [rsp + 62h]  ; rsi <- the original GDT base address\r
272     xchg    rcx, r10                    ; save rcx to r10 and initialize rcx to be the limit of GDT table\r
273     inc     rcx                         ; rcx <- the size of memory to copy\r
274     xchg    rdi, r11                    ; save rdi to r11 and initialize rdi to the base address of shadowed GDT table\r
275     rep     movsb                       ; perform memory copy to shadow GDT table\r
276     mov     rcx, r10                    ; restore the orignal rcx before memory copy\r
277     mov     rdi, r11                    ; restore the original rdi before memory copy\r
278     \r
279     sidt    fword ptr [rsp + 50h]       ; save IDT stack in argument space\r
280     mov     rax, cr0\r
281     mov     [rcx + (SavedCr0 - SavedCr4)], eax\r
282     and     eax, 7ffffffeh              ; clear PE, PG bits\r
283     mov     rbp, cr4\r
284     mov     [rcx], ebp                  ; save CR4 in SavedCr4\r
285     and     ebp, NOT 30h                ; clear PAE, PSE bits\r
286     mov     esi, r8d                    ; esi <- 16-bit stack segment\r
287     DB      6ah, DATA32                 ; push DATA32\r
288     pop     rdx                         ; rdx <- 32-bit data segment selector\r
289     lgdt    fword ptr [rcx + (_16Gdtr - SavedCr4)]\r
290     mov     ss, edx\r
291     pushfq\r
292     lea     edx, [rdx + DATA16 - DATA32]\r
293     lea     r8, @RetFromRealMode\r
294     push    r8\r
295     mov     r8d, cs\r
296     mov     [rcx + (SavedCs - SavedCr4)], r8w\r
297     mov     [rcx + (SavedSp - SavedCr4)], rsp\r
298     jmp     fword ptr [rcx + (_EntryPoint - SavedCr4)]\r
299 @RetFromRealMode:\r
300     popfq\r
301     lgdt    fword ptr [rsp + 60h]       ; restore protected mode GDTR\r
302     lidt    fword ptr [rsp + 50h]       ; restore protected mode IDTR\r
303     lea     eax, [rbp - sizeof (IA32_REGS)]\r
304     pop     gs\r
305     pop     fs\r
306     pop     rbx\r
307     mov     ss, rbx\r
308     pop     rbx\r
309     mov     es, rbx\r
310     pop     rbx\r
311     mov     ds, rbx\r
312     ret\r
313 InternalAsmThunk16  ENDP\r
314 \r
315     END\r