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
25global ASM_PFX(m16Size)
26global ASM_PFX(mThunk16Attr)
27global ASM_PFX(m16Gdt)
28global ASM_PFX(m16GdtrBase)
29global ASM_PFX(mTransition)
30global ASM_PFX(m16Start)
31
32struc 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
52endstruc
53
54SECTION .data
55
56;
57; These are global constant to convey information to C code.
58;
59ASM_PFX(m16Size)         DW      ASM_PFX(InternalAsmThunk16) - ASM_PFX(m16Start)
60ASM_PFX(mThunk16Attr)    DW      _BackFromUserCode.ThunkAttrEnd - 4 - ASM_PFX(m16Start)
61ASM_PFX(m16Gdt)          DW      _NullSeg - ASM_PFX(m16Start)
62ASM_PFX(m16GdtrBase)     DW      _16GdtrBase - ASM_PFX(m16Start)
63ASM_PFX(mTransition)     DW      _EntryPoint - ASM_PFX(m16Start)
64
65SECTION .text
66
67ASM_PFX(m16Start):
68
69SavedGdt:
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    ;
83BITS    16
84    push    ss
85    push    cs
86    ;
87    ; Note: We can't use o32 on the next instruction because of a bug
88    ; in NASM 2.09.04 through 2.10rc1.
89    ;
90    call    dword .Base                 ; push eip
91.Base:
92    push    dword 0                     ; reserved high order 32 bits of EFlags
93    pushfd
94    cli                                 ; disable interrupts
95    push    gs
96    push    fs
97    push    es
98    push    ds
99    pushad
100    mov     edx, strict dword 0
101.ThunkAttrEnd:
102    test    dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15
103    jz      .1
104    mov     ax, 2401h
105    int     15h
106    cli                                 ; disable interrupts
107    jnc     .2
108.1:
109    test    dl, THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL
110    jz      .2
111    in      al, 92h
112    or      al, 2
113    out     92h, al                     ; deactivate A20M#
114.2:
115    xor     eax, eax
116    mov     ax, ss
117    lea     ebp, [esp + IA32_REGS.size]
118    mov     [bp - IA32_REGS.size + IA32_REGS._ESP], ebp
119    mov     ebx, [bp - IA32_REGS.size + IA32_REGS._EIP]
120    shl     eax, 4                      ; shl eax, 4
121    add     ebp, eax                    ; add ebp, eax
122    mov     eax, cs
123    shl     eax, 4
124    lea     eax, [eax + ebx + (.X64JmpEnd - .Base)]
125    mov     [cs:bx + (.X64JmpEnd - 6 - .Base)], eax
126    mov     eax, strict dword 0
127.SavedCr4End:
128    mov     cr4, eax
129o32 lgdt [cs:bx + (SavedGdt - .Base)]
130    mov     ecx, 0c0000080h
131    rdmsr
132    or      ah, 1
133    wrmsr
134    mov     eax, strict dword 0
135.SavedCr0End:
136    mov     cr0, eax
137    jmp     0:strict dword 0
138.X64JmpEnd:
139BITS    64
140    nop
141    mov rsp, strict qword 0
142.SavedSpEnd:
143    nop
144    ret
145
146_EntryPoint:
147        DD      _ToUserCode - ASM_PFX(m16Start)
148        DW      CODE16
149_16Gdtr:
150        DW      GDT_SIZE - 1
151_16GdtrBase:
152        DQ      0
153_16Idtr:
154        DW      (1 << 10) - 1
155        DD      0
156
157;------------------------------------------------------------------------------
158; _ToUserCode() takes control in real mode before passing control to user code.
159; It will be shadowed to somewhere in memory below 1MB.
160;------------------------------------------------------------------------------
161_ToUserCode:
162BITS    16
163    mov     ss, dx                      ; set new segment selectors
164    mov     ds, dx
165    mov     es, dx
166    mov     fs, dx
167    mov     gs, dx
168    mov     ecx, 0c0000080h
169    mov     cr0, eax                    ; real mode starts at next instruction
170    rdmsr
171    and     ah, ~1
172    wrmsr
173    mov     cr4, ebp
174    mov     ss, si                      ; set up 16-bit stack segment
175    mov     esp, ebx                    ; set up 16-bit stack pointer
176    call    dword .Base                 ; push eip
177.Base:
178    pop     ebp                         ; ebp <- address of .Base
179    push    word [dword esp + IA32_REGS.size + 2]
180    lea     ax, [bp + (.RealMode - .Base)]
181    push    ax
182    retf                                ; execution begins at next instruction
183.RealMode:
184
185o32 lidt    [cs:bp + (_16Idtr - .Base)]
186
187    popad
188    pop     ds
189    pop     es
190    pop     fs
191    pop     gs
192    popfd
193    lea     esp, [esp + 4]        ; skip high order 32 bits of EFlags
194
195o32 retf                                ; transfer control to user code
196
197ALIGN   8
198
199CODE16  equ _16Code - $
200DATA16  equ _16Data - $
201DATA32  equ _32Data - $
202
203_NullSeg    DQ      0
204_16Code:
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:
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:
219            DW      -1
220            DW      0
221            DB      0
222            DB      93h
223            DB      0cfh                ; 16-bit segment, 4GB limit
224            DB      0
225
226GDT_SIZE equ $ - _NullSeg
227
228;------------------------------------------------------------------------------
229; IA32_REGISTER_SET *
230; EFIAPI
231; InternalAsmThunk16 (
232;   IN      IA32_REGISTER_SET         *RegisterSet,
233;   IN OUT  VOID                      *Transition
234;   );
235;------------------------------------------------------------------------------
236global ASM_PFX(InternalAsmThunk16)
237ASM_PFX(InternalAsmThunk16):
238BITS    64
239    push    rbp
240    push    rbx
241    push    rsi
242    push    rdi
243
244    mov     ebx, ds
245    push    rbx          ; Save ds segment register on the stack
246    mov     ebx, es
247    push    rbx          ; Save es segment register on the stack
248    mov     ebx, ss
249    push    rbx          ; Save ss segment register on the stack
250
251    push    fs
252    push    gs
253    mov     rsi, rcx
254    movzx   r8d, word [rsi + IA32_REGS._SS]
255    mov     edi, [rsi + IA32_REGS._ESP]
256    lea     rdi, [edi - (IA32_REGS.size + 4)]
257    imul    eax, r8d, 16                ; eax <- r8d(stack segment) * 16
258    mov     ebx, edi                    ; ebx <- stack for 16-bit code
259    push    IA32_REGS.size / 4
260    add     edi, eax                    ; edi <- linear address of 16-bit stack
261    pop     rcx
262    rep     movsd                       ; copy RegSet
263    lea     ecx, [rdx + (_BackFromUserCode.SavedCr4End - ASM_PFX(m16Start))]
264    mov     eax, edx                    ; eax <- transition code address
265    and     edx, 0fh
266    shl     eax, 12                     ; segment address in high order 16 bits
267    lea     ax, [rdx + (_BackFromUserCode - ASM_PFX(m16Start))]  ; offset address
268    stosd                               ; [edi] <- return address of user code
269
270    sgdt    [rsp + 60h]       ; save GDT stack in argument space
271    movzx   r10, word [rsp + 60h]   ; r10 <- GDT limit
272    lea     r11, [rcx + (ASM_PFX(InternalAsmThunk16) - _BackFromUserCode.SavedCr4End) + 0xf]
273    and     r11, ~0xf            ; r11 <- 16-byte aligned shadowed GDT table in real mode buffer
274
275    mov     [rcx + (SavedGdt - _BackFromUserCode.SavedCr4End)], r10w      ; save the limit of shadowed GDT table
276    mov     [rcx + (SavedGdt - _BackFromUserCode.SavedCr4End) + 2], r11  ; save the base address of shadowed GDT table
277
278    mov     rsi, [rsp + 62h]  ; rsi <- the original GDT base address
279    xchg    rcx, r10                    ; save rcx to r10 and initialize rcx to be the limit of GDT table
280    inc     rcx                         ; rcx <- the size of memory to copy
281    xchg    rdi, r11                    ; save rdi to r11 and initialize rdi to the base address of shadowed GDT table
282    rep     movsb                       ; perform memory copy to shadow GDT table
283    mov     rcx, r10                    ; restore the orignal rcx before memory copy
284    mov     rdi, r11                    ; restore the original rdi before memory copy
285
286    sidt    [rsp + 50h]       ; save IDT stack in argument space
287    mov     rax, cr0
288    mov     [rcx + (_BackFromUserCode.SavedCr0End - 4 - _BackFromUserCode.SavedCr4End)], eax
289    and     eax, 7ffffffeh              ; clear PE, PG bits
290    mov     rbp, cr4
291    mov     [rcx - 4], ebp              ; save CR4 in _BackFromUserCode.SavedCr4End - 4
292    and     ebp, ~30h                ; clear PAE, PSE bits
293    mov     esi, r8d                    ; esi <- 16-bit stack segment
294    push    DATA32
295    pop     rdx                         ; rdx <- 32-bit data segment selector
296    lgdt    [rcx + (_16Gdtr - _BackFromUserCode.SavedCr4End)]
297    mov     ss, edx
298    pushfq
299    lea     edx, [rdx + DATA16 - DATA32]
300    lea     r8, [REL .RetFromRealMode]
301    push    r8
302    mov     r8d, cs
303    mov     [rcx + (_BackFromUserCode.X64JmpEnd - 2 - _BackFromUserCode.SavedCr4End)], r8w
304    mov     [rcx + (_BackFromUserCode.SavedSpEnd - 8 - _BackFromUserCode.SavedCr4End)], rsp
305    jmp     dword far [rcx + (_EntryPoint - _BackFromUserCode.SavedCr4End)]
306.RetFromRealMode:
307    popfq
308    lgdt    [rsp + 60h]       ; restore protected mode GDTR
309    lidt    [rsp + 50h]       ; restore protected mode IDTR
310    lea     eax, [rbp - IA32_REGS.size]
311    pop     gs
312    pop     fs
313    pop     rbx
314    mov     ss, ebx
315    pop     rbx
316    mov     es, ebx
317    pop     rbx
318    mov     ds, ebx
319
320    pop     rdi
321    pop     rsi
322    pop     rbx
323    pop     rbp
324
325    ret
326