1#
2# Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
3# This program and the accompanying materials
4# are licensed and made available under the terms and conditions of the BSD License
5# which accompanies this distribution.  The full text of the license may be found at
6# http://opensource.org/licenses/bsd-license.php.
7#
8# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
9# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
10#
11#
12# Module Name:
13#
14#    Thunk64To32.asm
15#
16# Abstract:
17#
18#   This is the assembly code to transition from long mode to compatibility mode to execute 32-bit code and then
19#   transit back to long mode.
20#
21#-------------------------------------------------------------------------------
22
23#----------------------------------------------------------------------------
24# Procedure:    AsmExecute32BitCode
25#
26# Input:        None
27#
28# Output:       None
29#
30# Prototype:    UINT32
31#               AsmExecute32BitCode (
32#                 IN UINT64           Function,
33#                 IN UINT64           Param1,
34#                 IN UINT64           Param2,
35#                 IN IA32_DESCRIPTOR  *InternalGdtr
36#                 );
37#
38#
39# Description:  A thunk function to execute 32-bit code in long mode.
40#
41#----------------------------------------------------------------------------
42
43ASM_GLOBAL ASM_PFX(AsmExecute32BitCode)
44ASM_PFX(AsmExecute32BitCode):
45    #
46    # save IFLAG and disable it
47    #
48    pushfq
49    cli
50
51    #
52    # save orignal GDTR and CS
53    #
54    movl    %ds, %eax
55    push    %rax
56    movl    %cs, %eax
57    push    %rax
58    subq    $0x10, %rsp
59    sgdt    (%rsp)
60    #
61    # load internal GDT
62    #
63    lgdt    (%r9)
64    #
65    # Save general purpose register and rflag register
66    #
67    pushfq
68    push    %rdi
69    push    %rsi
70    push    %rbp
71    push    %rbx
72
73    #
74    # save CR3
75    #
76    movq    %cr3, %rax
77    movq    %rax, %rbp
78
79    #
80    # Prepare the CS and return address for the transition from 32-bit to 64-bit mode
81    #
82    movq    $0x10, %rax              # load long mode selector
83    shl     $32, %rax
84    lea     ReloadCS(%rip), %r9   #Assume the ReloadCS is under 4G
85    orq     %r9, %rax
86    push    %rax
87    #
88    # Save parameters for 32-bit function call
89    #
90    movq    %r8, %rax
91    shl     $32, %rax
92    orq     %rdx, %rax
93    push    %rax
94    #
95    # save the 32-bit function entry and the return address into stack which will be
96    # retrieve in compatibility mode.
97    #
98    lea     ReturnBack(%rip), %rax   #Assume the ReloadCS is under 4G
99    shl     $32, %rax
100    orq     %rcx, %rax
101    push    %rax
102
103    #
104    # let rax save DS
105    #
106    movq    $0x18, %rax
107
108    #
109    # Change to Compatible Segment
110    #
111    movq    $8, %rcx               # load compatible mode selector
112    shl     $32, %rcx
113    lea     Compatible(%rip), %rdx # assume address < 4G
114    orq     %rdx, %rcx
115    push    %rcx
116    .byte   0xcb                # retf
117
118Compatible:
119    # reload DS/ES/SS to make sure they are correct referred to current GDT
120    movw    %ax, %ds
121    movw    %ax, %es
122    movw    %ax, %ss
123
124    #
125    # Disable paging
126    #
127    movq    %cr0, %rcx
128    btc     $31, %ecx
129    movq    %rcx, %cr0
130    #
131    # Clear EFER.LME
132    #
133    movl    $0xC0000080, %ecx
134    rdmsr
135    btc     $8, %eax
136    wrmsr
137
138# Now we are in protected mode
139    #
140    # Call 32-bit function. Assume the function entry address and parameter value is less than 4G
141    #
142    pop    %rax                 # Here is the function entry
143    #
144    # Now the parameter is at the bottom of the stack,  then call in to IA32 function.
145    #
146    jmp   *%rax
147ReturnBack:
148    movl  %eax, %ebx            # save return status
149    pop   %rcx                  # drop param1
150    pop   %rcx                  # drop param2
151
152    #
153    # restore CR4
154    #
155    movq    %cr4, %rax
156    bts     $5, %eax
157    movq    %rax, %cr4
158
159    #
160    # restore CR3
161    #
162    movl    %ebp, %eax
163    movq    %rax, %cr3
164
165    #
166    # Set EFER.LME to re-enable ia32-e
167    #
168    movl    $0xC0000080, %ecx
169    rdmsr
170    bts     $8, %eax
171    wrmsr
172    #
173    # Enable paging
174    #
175    movq    %cr0, %rax
176    bts     $31, %eax
177    mov     %rax, %cr0
178# Now we are in compatible mode
179
180    #
181    # Reload cs register
182    #
183    .byte   0xcb                # retf
184ReloadCS:
185    #
186    # Now we're in Long Mode
187    #
188    #
189    # Restore C register and eax hold the return status from 32-bit function.
190    # Note: Do not touch rax from now which hold the return value from IA32 function
191    #
192    movl    %ebx, %eax # put return status to EAX
193    pop     %rbx
194    pop     %rbp
195    pop     %rsi
196    pop     %rdi
197    popfq
198    #
199    # Switch to orignal GDT and CS. here rsp is pointer to the orignal GDT descriptor.
200    #
201    lgdt    (%rsp)
202    #
203    # drop GDT descriptor in stack
204    #
205    addq    $0x10, %rsp
206    #
207    # switch to orignal CS and GDTR
208    #
209    pop     %r9                 # get  CS
210    shl     $32, %r9            # rcx[32..47] <- Cs
211    lea     ReturnToLongMode(%rip), %rcx
212    orq     %r9, %rcx
213    push    %rcx
214    .byte   0xcb                # retf
215ReturnToLongMode:
216    #
217    # Reload original DS/ES/SS
218    #
219    pop     %rcx
220    movl    %ecx, %ds
221    movl    %ecx, %es
222    movl    %ecx, %ss
223
224    #
225    # Restore IFLAG
226    #
227    popfq
228
229    ret
230
231