1#------------------------------------------------------------------------------
2#
3# Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
4# This program and the accompanying materials
5# are licensed and made available under the terms and conditions of the BSD License
6# which accompanies this distribution.  The full text of the license may be found at
7# http://opensource.org/licenses/bsd-license.php.
8#
9# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11#
12# Module Name:
13#
14#   Thunk16.S
15#
16# Abstract:
17#
18#   Real mode thunk
19#
20#------------------------------------------------------------------------------
21
22#include <Library/BaseLib.h>
23
24ASM_GLOBAL ASM_PFX(m16Start)
25ASM_GLOBAL ASM_PFX(m16Size)
26ASM_GLOBAL ASM_PFX(mThunk16Attr)
27ASM_GLOBAL ASM_PFX(m16Gdt)
28ASM_GLOBAL ASM_PFX(m16GdtrBase)
29ASM_GLOBAL ASM_PFX(mTransition)
30ASM_GLOBAL ASM_PFX(InternalAsmThunk16)
31
32# define the structure of IA32_REGS
33.set  _EDI, 0       #size 4
34.set  _ESI, 4       #size 4
35.set  _EBP, 8       #size 4
36.set  _ESP, 12      #size 4
37.set  _EBX, 16      #size 4
38.set  _EDX, 20      #size 4
39.set  _ECX, 24      #size 4
40.set  _EAX, 28      #size 4
41.set  _DS,  32      #size 2
42.set  _ES,  34      #size 2
43.set  _FS,  36      #size 2
44.set  _GS,  38      #size 2
45.set  _EFLAGS, 40   #size 8
46.set  _EIP, 48      #size 4
47.set  _CS, 52       #size 2
48.set  _SS, 54       #size 2
49.set  IA32_REGS_SIZE, 56
50
51    .data
52
53.set Lm16Size, ASM_PFX(InternalAsmThunk16) - ASM_PFX(m16Start)
54ASM_PFX(m16Size):         .word      Lm16Size
55.set  LmThunk16Attr, L_ThunkAttr - ASM_PFX(m16Start)
56ASM_PFX(mThunk16Attr):    .word      LmThunk16Attr
57.set Lm16Gdt, ASM_PFX(NullSeg) - ASM_PFX(m16Start)
58ASM_PFX(m16Gdt):          .word      Lm16Gdt
59.set Lm16GdtrBase, _16GdtrBase - ASM_PFX(m16Start)
60ASM_PFX(m16GdtrBase):     .word      Lm16GdtrBase
61.set LmTransition, _EntryPoint - ASM_PFX(m16Start)
62ASM_PFX(mTransition):     .word      LmTransition
63
64    .text
65
66ASM_PFX(m16Start):
67
68SavedGdt:    .space 10
69
70#------------------------------------------------------------------------------
71# _BackFromUserCode() takes control in real mode after 'retf' has been executed
72# by user code. It will be shadowed to somewhere in memory below 1MB.
73#------------------------------------------------------------------------------
74ASM_GLOBAL ASM_PFX(BackFromUserCode)
75ASM_PFX(BackFromUserCode):
76    #
77    # The order of saved registers on the stack matches the order they appears
78    # in IA32_REGS structure. This facilitates wrapper function to extract them
79    # into that structure.
80    #
81    # Some instructions for manipulation of segment registers have to be written
82    # in opcode since 64-bit MASM prevents accesses to those registers.
83    #
84    .byte 0x16                          # push ss
85    .byte 0xe                           # push cs
86    .byte 0x66
87    call    L_Base                       # push eip
88L_Base:
89    .byte 0x66
90    pushq   $0                          # reserved high order 32 bits of EFlags
91    .byte 0x66, 0x9c                    # pushfd actually
92    cli                                 # disable interrupts
93    push    %gs
94    push    %fs
95    .byte 6                             # push es
96    .byte 0x1e                          # push ds
97    .byte 0x66,0x60                     # pushad
98    .byte 0x66,0xba                     # mov edx, imm32
99L_ThunkAttr:  .space  4
100    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15, %dl
101    jz      L_1
102    movl    $0x15cd2401,%eax            # mov ax, 2401h & int 15h
103    cli                                 # disable interrupts
104    jnc     L_2
105L_1:
106    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL, %dl
107    jz      L_2
108    inb     $0x92,%al
109    orb     $2,%al
110    outb    %al, $0x92                   # deactivate A20M#
111L_2:
112    xorw    %ax, %ax                     # xor eax, eax
113    movl    %ss, %eax                    # mov ax, ss
114    lea     IA32_REGS_SIZE(%esp), %bp
115    #
116    # rsi in the following 2 instructions is indeed bp in 16-bit code
117    #
118    movw    %bp, (_ESP - IA32_REGS_SIZE)(%rsi)
119    .byte 0x66
120    movl    (_EIP - IA32_REGS_SIZE)(%rsi), %ebx
121    shlw    $4,%ax                      # shl eax, 4
122    addw    %ax,%bp                     # add ebp, eax
123    movw    %cs,%ax
124    shlw    $4,%ax
125    lea     (L_64BitCode - L_Base)(%ebx, %eax), %ax
126    .byte 0x66,0x2e,0x89,0x87           # mov cs:[bx + (L_64Eip - L_Base)], eax
127    .word   L_64Eip - L_Base
128    .byte 0x66,0xb8                     # mov eax, imm32
129L_SavedCr4: .space      4
130    movq    %rax, %cr4
131    #
132    # rdi in the instruction below is indeed bx in 16-bit code
133    #
134    .byte 0x66,0x2e                     # 2eh is "cs:" segment override
135    lgdt    (SavedGdt - L_Base)(%rdi)
136    .byte 0x66
137    movl    $0xc0000080,%ecx
138    rdmsr
139    orb     $1,%ah
140    wrmsr
141    .byte 0x66,0xb8                     # mov eax, imm32
142L_SavedCr0: .space      4
143    movq    %rax, %cr0
144    .byte 0x66,0xea                     # jmp far cs:L_64Bit
145L_64Eip:    .space      4
146L_SavedCs:  .space      2
147L_64BitCode:
148    .byte   0x90
149    .byte   0x48,0xbc                  # mov rsp, imm64
150L_SavedSp:  .space      8              # restore stack
151    nop
152    ret
153
154_EntryPoint: .long      ASM_PFX(ToUserCode) - ASM_PFX(m16Start)
155             .word      CODE16
156_16Gdtr:     .word      GDT_SIZE - 1
157_16GdtrBase: .quad      0
158_16Idtr:     .word      0x3ff
159             .long      0
160
161#------------------------------------------------------------------------------
162# _ToUserCode() takes control in real mode before passing control to user code.
163# It will be shadowed to somewhere in memory below 1MB.
164#------------------------------------------------------------------------------
165ASM_GLOBAL ASM_PFX(ToUserCode)
166ASM_PFX(ToUserCode):
167    movl    %edx,%ss                    # set new segment selectors
168    movl    %edx,%ds
169    movl    %edx,%es
170    movl    %edx,%fs
171    movl    %edx,%gs
172    .byte 0x66
173    movl    $0xc0000080,%ecx
174    movq    %rax, %cr0
175    rdmsr
176    andb    $0xfe, %ah                  # $0b11111110
177    wrmsr
178    movq    %rbp, %cr4
179    movl    %esi,%ss                    # set up 16-bit stack segment
180    movw    %bx,%sp                     # set up 16-bit stack pointer
181    .byte 0x66                          # make the following call 32-bit
182    call    L_Base1                       # push eip
183L_Base1:
184    popw    %bp                         # ebp <- address of L_Base1
185    pushq   (IA32_REGS_SIZE + 2)(%esp)
186    lea     0x0c(%rsi), %eax
187    pushq   %rax
188    lret                                # execution begins at next instruction
189L_RealMode:
190    .byte 0x66,0x2e                     # CS and operand size override
191    lidt    (_16Idtr - L_Base1)(%rsi)
192    .byte 0x66,0x61                     # popad
193    .byte 0x1f                          # pop ds
194    .byte 0x7                           # pop es
195    .byte 0x0f, 0xa1                    # pop fs
196    .byte 0x0f, 0xa9                    # pop gs
197    .byte 0x66, 0x9d                    # popfd
198    leaw    4(%esp),%sp                 # skip high order 32 bits of EFlags
199    .byte 0x66                          # make the following retf 32-bit
200    lret                                # transfer control to user code
201
202.set  CODE16,  ASM_PFX(_16Code) - .
203.set  DATA16,  ASM_PFX(_16Data) - .
204.set  DATA32,  ASM_PFX(_32Data) - .
205
206ASM_PFX(NullSeg):   .quad      0
207ASM_PFX(_16Code):
208            .word -1
209            .word 0
210            .byte 0
211            .byte 0x9b
212            .byte 0x8f                  # 16-bit segment, 4GB limit
213            .byte 0
214ASM_PFX(_16Data):
215            .word -1
216            .word 0
217            .byte 0
218            .byte 0x93
219            .byte 0x8f                  # 16-bit segment, 4GB limit
220            .byte 0
221ASM_PFX(_32Data):
222            .word -1
223            .word 0
224            .byte 0
225            .byte 0x93
226            .byte 0xcf                  # 16-bit segment, 4GB limit
227            .byte 0
228
229.set  GDT_SIZE, . - ASM_PFX(NullSeg)
230
231#------------------------------------------------------------------------------
232# IA32_REGISTER_SET *
233# EFIAPI
234# InternalAsmThunk16 (
235#   IN      IA32_REGISTER_SET         *RegisterSet,
236#   IN OUT  VOID                      *Transition
237#   );
238#------------------------------------------------------------------------------
239
240ASM_GLOBAL ASM_PFX(InternalAsmThunk16)
241ASM_PFX(InternalAsmThunk16):
242    pushq   %rbp
243    pushq   %rbx
244    pushq   %rsi
245    pushq   %rdi
246
247    movl    %ds, %ebx
248    pushq   %rbx      # Save ds segment register on the stack
249    movl    %es, %ebx
250    pushq   %rbx      # Save es segment register on the stack
251    movl    %ss, %ebx
252    pushq   %rbx      # Save ss segment register on the stack
253
254    .byte   0x0f, 0xa0                  #push   fs
255    .byte   0x0f, 0xa8                  #push   gs
256    movq    %rcx, %rsi
257    movzwl  _SS(%rsi), %r8d
258    movl    _ESP(%rsi), %edi
259    lea     -(IA32_REGS_SIZE + 4)(%edi), %rdi
260    imul    $16, %r8d, %eax
261    movl    %edi,%ebx                   # ebx <- stack for 16-bit code
262    pushq   $(IA32_REGS_SIZE / 4)
263    addl    %eax,%edi                   # edi <- linear address of 16-bit stack
264    popq    %rcx
265    rep
266    movsl                               # copy RegSet
267    lea     (L_SavedCr4 - ASM_PFX(m16Start))(%rdx), %ecx
268    movl    %edx,%eax                   # eax <- transition code address
269    andl    $0xf,%edx
270    shll    $12,%eax                    # segment address in high order 16 bits
271    .set LBackFromUserCodeDelta, ASM_PFX(BackFromUserCode) - ASM_PFX(m16Start)
272    lea     (LBackFromUserCodeDelta)(%rdx), %ax
273    stosl                               # [edi] <- return address of user code
274    sgdt    0x60(%rsp)                  # save GDT stack in argument space
275    movzwq  0x60(%rsp), %r10            # r10 <- GDT limit
276    lea     ((ASM_PFX(InternalAsmThunk16) - L_SavedCr4) + 0xf)(%rcx), %r11
277    andq    $0xfffffffffffffff0, %r11   # r11 <- 16-byte aligned shadowed GDT table in real mode buffer
278
279    movw    %r10w, (SavedGdt - L_SavedCr4)(%rcx)       # save the limit of shadowed GDT table
280    movq    %r11, (SavedGdt - L_SavedCr4 + 0x2)(%rcx)  # save the base address of shadowed GDT table
281
282    movq    0x62(%rsp) ,%rsi            # rsi <- the original GDT base address
283    xchg   %r10, %rcx                   # save rcx to r10 and initialize rcx to be the limit of GDT table
284    incq   %rcx                         # rcx <- the size of memory to copy
285    xchg   %r11, %rdi                   # save rdi to r11 and initialize rdi to the base address of shadowed GDT table
286    rep
287    movsb                               # perform memory copy to shadow GDT table
288    movq   %r10, %rcx                   # restore the orignal rcx before memory copy
289    movq   %r11, %rdi                   # restore the original rdi before memory copy
290
291    sidt    0x50(%rsp)
292    movq    %cr0, %rax
293    .set LSavedCrDelta, L_SavedCr0 - L_SavedCr4
294    movl    %eax, (LSavedCrDelta)(%rcx)
295    andl    $0x7ffffffe,%eax            # clear PE, PG bits
296    movq    %cr4, %rbp
297    movl    %ebp, (%rcx)                # save CR4 in SavedCr4
298    andl    $0xffffffcf,%ebp            # clear PAE, PSE bits
299    movl    %r8d, %esi                  # esi <- 16-bit stack segment
300    .byte      0x6a, DATA32
301    popq    %rdx
302    lgdt    (_16Gdtr - L_SavedCr4)(%rcx)
303    movl    %edx,%ss
304    pushfq
305    lea     -8(%rdx), %edx
306    lea     L_RetFromRealMode(%rip), %r8
307    pushq   %r8
308    movl    %cs, %r8d
309    movw    %r8w, (L_SavedCs - L_SavedCr4)(%rcx)
310    movq    %rsp, (L_SavedSp - L_SavedCr4)(%rcx)
311    .byte   0xff, 0x69                  #  jmp (_EntryPoint - L_SavedCr4)(%rcx)
312    .set    Ltemp1, _EntryPoint - L_SavedCr4
313    .byte   Ltemp1
314L_RetFromRealMode:
315    popfq
316    lgdt    0x60(%rsp)                  # restore protected mode GDTR
317    lidt    0x50(%rsp)                  # restore protected mode IDTR
318    lea     -IA32_REGS_SIZE(%rbp), %eax
319    .byte 0x0f, 0xa9                    # pop gs
320    .byte 0x0f, 0xa1                    # pop fs
321
322    popq     %rbx
323    movl     %ebx, %ss
324    popq     %rbx
325    movl     %ebx, %es
326    popq     %rbx
327    movl     %ebx, %ds
328
329    popq    %rdi
330    popq    %rsi
331    popq    %rbx
332    popq    %rbp
333
334    ret
335