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