Update Thunk16.S of X64 for BaseLib.
[people/mcb30/edk2.git] / edk2 / MdePkg / Library / BaseLib / X64 / Thunk16.S
1 #------------------------------------------------------------------------------
2 #
3 # Copyright (c) 2006 - 2008, Intel Corporation
4 # All rights reserved. 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
24 .globl ASM_PFX(m16Start)\r
25 .globl ASM_PFX(m16Size)\r
26 .globl ASM_PFX(mThunk16Attr)\r
27 .globl ASM_PFX(m16Gdt)\r
28 .globl ASM_PFX(m16GdtrBase)\r
29 .globl ASM_PFX(mTransition)\r
30 .globl ASM_PFX(InternalAsmThunk16)\r
31
32 # define the structure of IA32_REGS\r
33 .equ  _EDI, 0       #size 4\r
34 .equ  _ESI, 4       #size 4\r
35 .equ  _EBP, 8       #size 4\r
36 .equ  _ESP, 12      #size 4\r
37 .equ  _EBX, 16      #size 4\r
38 .equ  _EDX, 20      #size 4\r
39 .equ  _ECX, 24      #size 4\r
40 .equ  _EAX, 28      #size 4\r
41 .equ  _DS,  32      #size 2\r
42 .equ  _ES,  34      #size 2\r
43 .equ  _FS,  36      #size 2\r
44 .equ  _GS,  38      #size 2\r
45 .equ  _EFLAGS, 40   #size 8\r
46 .equ  _EIP, 48      #size 4\r
47 .equ  _CS, 52       #size 2\r
48 .equ  _SS, 54       #size 2\r
49 .equ  IA32_REGS_SIZE, 56\r
50
51     .data
52
53 ASM_PFX(m16Size):         .word      ASM_PFX(InternalAsmThunk16) - ASM_PFX(m16Start)
54 ASM_PFX(mThunk16Attr):    .word      _ThunkAttr - ASM_PFX(m16Start)
55 ASM_PFX(m16Gdt):          .word      _NullSeg - ASM_PFX(m16Start)
56 ASM_PFX(m16GdtrBase):     .word      _16GdtrBase - ASM_PFX(m16Start)
57 ASM_PFX(mTransition):     .word      _EntryPoint - ASM_PFX(m16Start)
58
59     .text
60
61 ASM_PFX(m16Start):
62
63 SavedGdt:    .space 10
64
65 #------------------------------------------------------------------------------
66 # _BackFromUserCode() takes control in real mode after 'retf' has been executed
67 # by user code. It will be shadowed to somewhere in memory below 1MB.
68 #------------------------------------------------------------------------------
69 .globl ASM_PFX(BackFromUserCode)
70 ASM_PFX(BackFromUserCode):
71     #
72     # The order of saved registers on the stack matches the order they appears
73     # in IA32_REGS structure. This facilitates wrapper function to extract them
74     # into that structure.
75     #
76     # Some instructions for manipulation of segment registers have to be written
77     # in opcode since 64-bit MASM prevents accesses to those registers.
78     #
79     .byte 0x16                          # push ss
80     .byte 0xe                           # push cs
81     .byte 0x66
82     call    @Base                       # push eip
83 @Base: 
84     .byte 0x66
85     pushq   $0                          # reserved high order 32 bits of EFlags
86     .byte 0x66, 0x9c                    # pushfd actually
87     cli                                 # disable interrupts
88     push    %gs
89     push    %fs
90     .byte 6                             # push es
91     .byte 0x1e                          # push ds
92     .byte 0x66,0x60                     # pushad
93     .byte 0x66,0xba                     # mov edx, imm32
94 _ThunkAttr:  .space  4
95     testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15, %dl
96     jz      @1
97     movl    $0x15cd2401,%eax            # mov ax, 2401h & int 15h
98     cli                                 # disable interrupts
99     jnc     @2
100 @1: 
101     testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL, %dl
102     jz      @2
103     inb     $0x92,%al
104     orb     $2,%al
105     outb    %al, $0x92                   # deactivate A20M#
106 @2: 
107     movl    %ss,%eax
108     lea     IA32_REGS_SIZE(%esp), %bp
109     #
110     # rsi in the following 2 instructions is indeed bp in 16-bit code
111     #
112     movw    %bp, (_ESP - IA32_REGS_SIZE)(%rsi)
113     .byte 0x66
114     movl    (_EIP - IA32_REGS_SIZE)(%rsi), %ebx
115     shlw    $4,%ax                      # shl eax, 4
116     addw    %ax,%bp                     # add ebp, eax
117     movw    %cs,%ax
118     shlw    $4,%ax
119     lea     (@64BitCode - @Base)(%ebx, %eax), %ax
120     .byte 0x66,0x2e,0x89,0x87           # mov cs:[bx + (@64Eip - @Base)], eax
121     .word   @64Eip - @Base
122     .byte 0x66,0xb8                     # mov eax, imm32
123 SavedCr4:   .space  4
124     movq    %rax, %cr4
125     #
126     # rdi in the instruction below is indeed bx in 16-bit code
127     #
128     .byte 0x66,0x2e                     # 2eh is "cs:" segment override
129     lgdt    (SavedGdt - @Base)(%rdi)
130     .byte 0x66
131     movl    $0xc0000080,%ecx
132     rdmsr
133     orb     $1,%ah
134     wrmsr
135     .byte 0x66,0xb8                     # mov eax, imm32
136 SavedCr0:    .space      4
137     movq    %rax, %cr0
138     .byte 0x66,0xea                     # jmp far cs:@64Bit
139 @64Eip:      .space      4
140 SavedCs:     .space      2
141 @64BitCode: 
142     movq     %r8, %rsp
143     ret
144
145 _EntryPoint: .long      ASM_PFX(ToUserCode) - ASM_PFX(m16Start)
146              .word      CODE16
147 _16Gdtr:     .word      GDT_SIZE - 1
148 _16GdtrBase: .quad      _NullSeg
149 _16Idtr:     .word      0x3ff
150              .long      0
151
152 #------------------------------------------------------------------------------
153 # _ToUserCode() takes control in real mode before passing control to user code.
154 # It will be shadowed to somewhere in memory below 1MB.
155 #------------------------------------------------------------------------------
156 .globl ASM_PFX(ToUserCode)
157 ASM_PFX(ToUserCode):
158     movl    %edx,%ss                    # set new segment selectors
159     movl    %edx,%ds
160     movl    %edx,%es
161     movl    %edx,%fs
162     movl    %edx,%gs
163     .byte 0x66
164     movl    $0xc0000080,%ecx
165     movq    %rax, %cr0
166     rdmsr
167     andb    $0b11111110, %ah 
168     wrmsr
169     movq    %rbp, %cr4
170     movl    %esi,%ss                    # set up 16-bit stack segment
171     movw    %bx,%sp                     # set up 16-bit stack pointer
172     .byte 0x66                          # make the following call 32-bit
173     call    @Base1                       # push eip
174 @Base1: 
175     popw    %bp                         # ebp <- address of @Base1
176     pushq   (IA32_REGS_SIZE + 2)(%esp)
177     lea     0x0c(%rsi), %eax
178     pushq   %rax
179     lret                                # execution begins at next instruction
180 @RealMode: 
181     .byte 0x66,0x2e                     # CS and operand size override
182     lidt    (_16Idtr - @Base1)(%rsi)
183     .byte 0x66,0x61                     # popad
184     .byte 0x1f                          # pop ds
185     .byte 0x7                           # pop es
186     .byte 0x0f, 0xa1                    # pop fs
187     .byte 0x0f, 0xa9                    # pop gs
188     .byte 0x66, 0x9d                    # popfd
189     leaw    4(%esp),%sp                 # skip high order 32 bits of EFlags
190     .byte 0x66                          # make the following retf 32-bit
191     lret                                # transfer control to user code
192
193 .equ  CODE16,  ASM_PFX(16Code) - .
194 .equ  DATA16,  ASM_PFX(16Data) - .
195 .equ  DATA32,  ASM_PFX(32Data) - .
196
197 _NullSeg:   .quad      0
198 ASM_PFX(16Code):
199             .word -1
200             .word 0
201             .byte 0
202             .byte 0x9b
203             .byte 0x8f                  # 16-bit segment, 4GB limit
204             .byte 0
205 ASM_PFX(16Data):
206             .word -1
207             .word 0
208             .byte 0
209             .byte 0x93
210             .byte 0x8f                  # 16-bit segment, 4GB limit
211             .byte 0
212 ASM_PFX(32Data):
213             .word -1
214             .word 0
215             .byte 0
216             .byte 0x93
217             .byte 0xcf                  # 16-bit segment, 4GB limit
218             .byte 0
219
220 .equ  GDT_SIZE, . - ASM_PFX(NullSeg)
221
222 #------------------------------------------------------------------------------
223 # IA32_REGISTER_SET *
224 # EFIAPI
225 # InternalAsmThunk16 (
226 #   IN      IA32_REGISTER_SET         *RegisterSet,
227 #   IN OUT  VOID                      *Transition
228 #   );
229 #------------------------------------------------------------------------------
230 # MISMATCH: "InternalAsmThunk16  PROC    USES    rbp rbx rsi rdi"
231
232 .globl ASM_PFX(InternalAsmThunk16)
233 ASM_PFX(InternalAsmThunk16):
234     pushq   %rbp
235     pushq   %rbx
236     pushq   %rsi
237     pushq   %rdi
238     
239     movl    %ds, %r10d                  # r9 ~ r11 are not accessible in 16-bit
240     movl    %es, %r11d                  # so use them for saving seg registers
241     movl    %ss, %r9d
242     .byte   0x0f, 0xa0                  #push   fs
243     .byte   0x0f, 0xa8                  #push   gs
244     movq    %rcx, %rsi
245     movzwl  _SS(%rsi), %r8d
246     movl    _ESP(%rsi), %edi
247     lea     -(IA32_REGS_SIZE + 4)(%edi), %rdi
248     imul    $16, %r8d, %eax 
249     movl    %edi,%ebx                   # ebx <- stack for 16-bit code
250     pushq   $(IA32_REGS_SIZE / 4)
251     addl    %eax,%edi                   # edi <- linear address of 16-bit stack
252     popq    %rcx
253     rep
254     movsl                               # copy RegSet
255     lea     (SavedCr4 - ASM_PFX(m16Start))(%rdx), %ecx
256     movl    %edx,%eax                   # eax <- transition code address
257     andl    $0xf,%edx
258     shll    $12,%eax                    # segment address in high order 16 bits
259     lea     (_BackFromUserCode - ASM_PFX(m16Start))(%rdx), %ax
260     stosl                               # [edi] <- return address of user code
261     sgdt    (SavedGdt - SavedCr4)(%rcx) 
262     sidt    0x38(%rsp)
263     movq    %cr0, %rax
264     movl    %eax, (SavedCr0 - SavedCr4)(%rcx)
265     andl    $0x7ffffffe,%eax            # clear PE, PG bits
266     movq    %cr4, %rbp
267     movl    %ebp, (%rcx)                # save CR4 in SavedCr4
268     andl    $0x300,%ebp                 # clear all but PCE and OSFXSR bits
269     movl    %r8d, %esi                  # esi <- 16-bit stack segment
270     .byte      0x6a, DATA32
271     popq    %rdx
272     lgdt    (_16Gdtr - SavedCr4)(%rcx)
273     movl    %edx,%ss
274     pushfq
275     lea     -8(%rdx), %edx
276     lea     @RetFromRealMode, %r8
277     pushq   %r8
278     movl    %cs, %r8d
279     movw    %r8w, (SavedCs - SavedCr4)(%rcx)
280     movq     %rsp, %r8
281     .byte   0xff, 0x69                  #  jmp (_EntryPoint - SavedCr4)(%rcx)
282     .byte   _EntryPoint - SavedCr4
283 @RetFromRealMode: 
284     popfq
285     lidt    0x38(%rsp)
286     lea     -IA32_REGS_SIZE(%rbp), %eax
287     .byte 0x0f, 0xa9                    # pop gs
288     .byte 0x0f, 0xa1                    # pop fs
289     movl    %r9d, %ss
290     movl    %r11d, %es
291     movl    %r10d, %ds
292     
293     popq    %rdi
294     popq    %rsi
295     popq    %rbx
296     popq    %rbp
297
298     ret