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