summaryrefslogtreecommitdiff
path: root/EdkCompatibilityPkg/Compatibility/BootScriptSaveOnS3SaveStateThunk/X64/AsmDispatchExecute.S
blob: 167e7d4cc1edb0b5b927fbc950a9b574717c080b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#
# Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution.  The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#
# Module Name:
# 
#    AsmDispatchExecute.asm
#
# Abstract:
# 
#   This is the assembly code to transition from long mode to compatibility mode to execute 32-bit code and then 
#   transit back to long mode.
#
#------------------------------------------------------------------------------- 

#----------------------------------------------------------------------------
# Procedure:    AsmExecute32BitCode
#
# Input:        None
#
# Output:       None
#
# Prototype:    EFI_STATUS
#               AsmExecute32BitCode (
#                 IN UINT64           Function,
#                 IN UINT64           Param1,
#                 IN UINT64           Param2,
#                 IN IA32_DESCRIPTOR  *InternalGdtr
#                 );
#
#
# Description:  A thunk function to execute 32-bit code in long mode. 
#
#----------------------------------------------------------------------------

ASM_GLOBAL ASM_PFX(AsmExecute32BitCode)
ASM_PFX(AsmExecute32BitCode):
    #
    # save orignal GDTR and CS
    #
    movq    %ds, %rax
    push    %rax
    movq    %cs, %rax
    push    %rax
    subq    $0x10, %rsp
    sgdt    (%rsp)
    #
    # load internal GDT
    #
    lgdt    (%r9)
    #
    # Save general purpose register and rflag register
    #
    pushfq 
    push    %rdi
    push    %rsi
    push    %rbp
    push    %rbx
    
    #
    # save CR3
    #
    movq    %cr3, %rax
    movq    %rax, %rbp

    #
    # Prepare the CS and return address for the transition from 32-bit to 64-bit mode 
    #        
    movq    $0x10, %rax              # load long mode selector    
    shl     $32, %rax
    lea     ReloadCS(%rip), %r9   #Assume the ReloadCS is under 4G
    orq     %r9, %rax
    push    %rax
    #
    # Save parameters for 32-bit function call
    #   
    movq    %r8, %rax
    shl     $32, %rax
    orq     %rdx, %rax
    push    %rax
    #
    # save the 32-bit function entry and the return address into stack which will be 
    # retrieve in compatibility mode.
    #
    lea     ReturnBack(%rip), %rax   #Assume the ReloadCS is under 4G 
    shl     $32, %rax
    orq     %rcx, %rax
    push    %rax
    
    #
    # let rax save DS
    #
    movq    $0x18, %rax

    #
    # Change to Compatible Segment
    #
    movq    $8, %rcx               # load compatible mode selector 
    shl     $32, %rcx
    lea     Compatible(%rip), %rdx # assume address < 4G
    orq     %rdx, %rcx
    push    %rcx
    .byte   0xcb                # retf

Compatible:
    # reload DS/ES/SS to make sure they are correct referred to current GDT
    movw    %ax, %ds
    movw    %ax, %es
    movw    %ax, %ss

    #
    # Disable paging
    #
    movq    %cr0, %rcx
    btc     $31, %ecx
    movq    %rcx, %cr0
    #
    # Clear EFER.LME
    #
    movl    $0xC0000080, %ecx
    rdmsr
    btc     $8, %eax
    wrmsr

# Now we are in protected mode
    #
    # Call 32-bit function. Assume the function entry address and parameter value is less than 4G
    #    
    pop    %rax                 # Here is the function entry
    #  
    # Now the parameter is at the bottom of the stack,  then call in to IA32 function.
    #
    jmp   *%rax
ReturnBack:  
    pop   %rcx                  # drop param1      
    pop   %rcx                  # drop param2      

    #
    # restore CR4
    #
    movq    %cr4, %rax
    bts     $5, %eax
    movq    %rax, %cr4

    #
    # restore CR3
    #
    movl    %ebp, %eax
    movq    %rax, %cr3

    #
    # Set EFER.LME to re-enable ia32-e
    #
    movl    $0xC0000080, %ecx
    rdmsr
    bts     $8, %eax
    wrmsr
    #
    # Enable paging
    #
    movq    %cr0, %rax
    bts     $31, %eax
    mov     %rax, %cr0
# Now we are in compatible mode

    #
    # Reload cs register 
    #   
    .byte   0xcb                # retf
ReloadCS:   
    #
    # Now we're in Long Mode
    #
    #
    # Restore C register and eax hold the return status from 32-bit function.
    # Note: Do not touch rax from now which hold the return value from IA32 function
    #
    pop     %rbx
    pop     %rbp
    pop     %rsi
    pop     %rdi
    popfq
    #
    # Switch to orignal GDT and CS. here rsp is pointer to the orignal GDT descriptor.
    #
    lgdt    (%rsp)
    #
    # drop GDT descriptor in stack
    #
    addq    $0x10, %rsp
    #
    # switch to orignal CS and GDTR
    #
    pop     %r9                 # get  CS
    shl     $32, %r9            # rcx[32..47] <- Cs       
    lea     ReturnToLongMode(%rip), %rcx
    orq     %r9, %rcx
    push    %rcx
    .byte   0xcb                # retf
ReturnToLongMode:
    #
    # Reload original DS/ES/SS
    #
    pop     %rcx
    movq    %rcx, %ds
    movq    %rcx, %es
    movq    %rcx, %ss
    ret