summaryrefslogtreecommitdiffstats
path: root/IntelFsp2WrapperPkg/Library/BaseFspWrapperApiLib/X64/Thunk64To32.nasm
blob: 45c8f21255e36c46e1729ab974d11aeb561481d4 (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
217
218
219
220
221
222
223
224
;
; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
; SPDX-License-Identifier: BSD-2-Clause-Patent
;
;
; Module Name:
;
;    Thunk64To32.nasm
;
; 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.
;
;-------------------------------------------------------------------------------
    DEFAULT REL
    SECTION .text
;----------------------------------------------------------------------------
; Procedure:    AsmExecute32BitCode
;
; Input:        None
;
; Output:       None
;
; Prototype:    UINT32
;               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.
;
;----------------------------------------------------------------------------
global ASM_PFX(AsmExecute32BitCode)
ASM_PFX(AsmExecute32BitCode):
    ;
    ; save IFLAG and disable it
    ;
    pushfq
    cli

    ;
    ; save orignal GDTR and CS
    ;
    mov     rax, ds
    push    rax
    mov     rax, cs
    push    rax
    sub     rsp, 0x10
    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
    ;
    mov     rax, cr3
    mov     rbp, rax

    ;
    ; Prepare the CS and return address for the transition from 32-bit to 64-bit mode
    ;
    mov     rax, dword 0x10              ; load long mode selector
    shl     rax, 32
    lea     r9,  [ReloadCS]          ;Assume the ReloadCS is under 4G
    or      rax, r9
    push    rax
    ;
    ; Save parameters for 32-bit function call
    ;
    mov     rax, r8
    shl     rax, 32
    or      rax, rdx
    push    rax
    ;
    ; save the 32-bit function entry and the return address into stack which will be
    ; retrieve in compatibility mode.
    ;
    lea     rax, [ReturnBack]   ;Assume the ReloadCS is under 4G
    shl     rax, 32
    or      rax, rcx
    push    rax

    ;
    ; let rax save DS
    ;
    mov     rax, dword 0x18

    ;
    ; Change to Compatible Segment
    ;
    mov     rcx, dword 0x8               ; load compatible mode selector
    shl     rcx, 32
    lea     rdx, [Compatible] ; assume address < 4G
    or      rcx, rdx
    push    rcx
    retf

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

    ;
    ; Disable paging
    ;
    mov     rcx, cr0
    btc     ecx, 31
    mov     cr0, rcx
    ;
    ; Clear EFER.LME
    ;
    mov     ecx, 0xC0000080
    rdmsr
    btc     eax, 8
    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:
    mov   ebx, eax             ; save return status
    pop   rcx                  ; drop param1
    pop   rcx                  ; drop param2

    ;
    ; restore CR4
    ;
    mov     rax, cr4
    bts     eax, 5
    mov     cr4, rax

    ;
    ; restore CR3
    ;
    mov     eax, ebp
    mov     cr3, rax

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

    ;
    ; Reload cs register
    ;
    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
    ;
    mov     eax, ebx ; put return status to EAX
    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
    ;
    add     rsp, 0x10
    ;
    ; switch to orignal CS and GDTR
    ;
    pop     r9                 ; get  CS
    shl     r9,  32            ; rcx[32..47] <- Cs
    lea     rcx, [.0]
    or      rcx, r9
    push    rcx
    retf
.0:
    ;
    ; Reload original DS/ES/SS
    ;
    pop     rcx
    mov     ds, rcx
    mov     es, rcx
    mov     ss, rcx

    ;
    ; Restore IFLAG
    ;
    popfq

    ret