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