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