Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/boot/startup/efi-mixed.S
26535 views
1
/* SPDX-License-Identifier: GPL-2.0 */
2
/*
3
* Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
4
*
5
* Early support for invoking 32-bit EFI services from a 64-bit kernel.
6
*
7
* Because this thunking occurs before ExitBootServices() we have to
8
* restore the firmware's 32-bit GDT and IDT before we make EFI service
9
* calls.
10
*
11
* On the plus side, we don't have to worry about mangling 64-bit
12
* addresses into 32-bits because we're executing with an identity
13
* mapped pagetable and haven't transitioned to 64-bit virtual addresses
14
* yet.
15
*/
16
17
#include <linux/linkage.h>
18
#include <asm/desc_defs.h>
19
#include <asm/msr.h>
20
#include <asm/page_types.h>
21
#include <asm/pgtable_types.h>
22
#include <asm/processor-flags.h>
23
#include <asm/segment.h>
24
25
.text
26
.code32
27
#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
28
SYM_FUNC_START(efi32_stub_entry)
29
call 1f
30
1: popl %ecx
31
32
/* Clear BSS */
33
xorl %eax, %eax
34
leal (_bss - 1b)(%ecx), %edi
35
leal (_ebss - 1b)(%ecx), %ecx
36
subl %edi, %ecx
37
shrl $2, %ecx
38
cld
39
rep stosl
40
41
add $0x4, %esp /* Discard return address */
42
movl 8(%esp), %ebx /* struct boot_params pointer */
43
jmp efi32_startup
44
SYM_FUNC_END(efi32_stub_entry)
45
#endif
46
47
/*
48
* Called using a far call from __efi64_thunk() below, using the x86_64 SysV
49
* ABI (except for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are
50
* used instead). EBP+16 points to the arguments passed via the stack.
51
*
52
* The first argument (EDI) is a pointer to the boot service or protocol, to
53
* which the remaining arguments are passed, each truncated to 32 bits.
54
*/
55
SYM_FUNC_START_LOCAL(efi_enter32)
56
/*
57
* Convert x86-64 SysV ABI params to i386 ABI
58
*/
59
pushl 32(%ebp) /* Up to 3 args passed via the stack */
60
pushl 24(%ebp)
61
pushl 16(%ebp)
62
pushl %ebx /* R9 */
63
pushl %eax /* R8 */
64
pushl %ecx
65
pushl %edx
66
pushl %esi
67
68
/* Disable paging */
69
movl %cr0, %eax
70
btrl $X86_CR0_PG_BIT, %eax
71
movl %eax, %cr0
72
73
/* Disable long mode via EFER */
74
movl $MSR_EFER, %ecx
75
rdmsr
76
btrl $_EFER_LME, %eax
77
wrmsr
78
79
call *%edi
80
81
/* We must preserve return value */
82
movl %eax, %edi
83
84
call efi32_enable_long_mode
85
86
addl $32, %esp
87
movl %edi, %eax
88
lret
89
SYM_FUNC_END(efi_enter32)
90
91
.code64
92
SYM_FUNC_START(__efi64_thunk)
93
push %rbp
94
movl %esp, %ebp
95
push %rbx
96
97
/* Move args #5 and #6 into 32-bit accessible registers */
98
movl %r8d, %eax
99
movl %r9d, %ebx
100
101
lcalll *efi32_call(%rip)
102
103
pop %rbx
104
pop %rbp
105
RET
106
SYM_FUNC_END(__efi64_thunk)
107
108
.code32
109
SYM_FUNC_START_LOCAL(efi32_enable_long_mode)
110
movl %cr4, %eax
111
btsl $(X86_CR4_PAE_BIT), %eax
112
movl %eax, %cr4
113
114
movl $MSR_EFER, %ecx
115
rdmsr
116
btsl $_EFER_LME, %eax
117
wrmsr
118
119
/* Disable interrupts - the firmware's IDT does not work in long mode */
120
cli
121
122
/* Enable paging */
123
movl %cr0, %eax
124
btsl $X86_CR0_PG_BIT, %eax
125
movl %eax, %cr0
126
ret
127
SYM_FUNC_END(efi32_enable_long_mode)
128
129
/*
130
* This is the common EFI stub entry point for mixed mode. It sets up the GDT
131
* and page tables needed for 64-bit execution, after which it calls the
132
* common 64-bit EFI entrypoint efi_stub_entry().
133
*
134
* Arguments: 0(%esp) image handle
135
* 4(%esp) EFI system table pointer
136
* %ebx struct boot_params pointer (or NULL)
137
*
138
* Since this is the point of no return for ordinary execution, no registers
139
* are considered live except for the function parameters. [Note that the EFI
140
* stub may still exit and return to the firmware using the Exit() EFI boot
141
* service.]
142
*/
143
SYM_FUNC_START_LOCAL(efi32_startup)
144
movl %esp, %ebp
145
146
subl $8, %esp
147
sgdtl (%esp) /* Save GDT descriptor to the stack */
148
movl 2(%esp), %esi /* Existing GDT pointer */
149
movzwl (%esp), %ecx /* Existing GDT limit */
150
inc %ecx /* Existing GDT size */
151
andl $~7, %ecx /* Ensure size is multiple of 8 */
152
153
subl %ecx, %esp /* Allocate new GDT */
154
andl $~15, %esp /* Realign the stack */
155
movl %esp, %edi /* New GDT address */
156
leal 7(%ecx), %eax /* New GDT limit */
157
pushw %cx /* Push 64-bit CS (for LJMP below) */
158
pushl %edi /* Push new GDT address */
159
pushw %ax /* Push new GDT limit */
160
161
/* Copy GDT to the stack and add a 64-bit code segment at the end */
162
movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) & 0xffffffff, (%edi,%ecx)
163
movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) >> 32, 4(%edi,%ecx)
164
shrl $2, %ecx
165
cld
166
rep movsl /* Copy the firmware GDT */
167
lgdtl (%esp) /* Switch to the new GDT */
168
169
call 1f
170
1: pop %edi
171
172
/* Record mixed mode entry */
173
movb $0x0, (efi_is64 - 1b)(%edi)
174
175
/* Set up indirect far call to re-enter 32-bit mode */
176
leal (efi32_call - 1b)(%edi), %eax
177
addl %eax, (%eax)
178
movw %cs, 4(%eax)
179
180
/* Disable paging */
181
movl %cr0, %eax
182
btrl $X86_CR0_PG_BIT, %eax
183
movl %eax, %cr0
184
185
/* Set up 1:1 mapping */
186
leal (pte - 1b)(%edi), %eax
187
movl $_PAGE_PRESENT | _PAGE_RW | _PAGE_PSE, %ecx
188
leal (_PAGE_PRESENT | _PAGE_RW)(%eax), %edx
189
2: movl %ecx, (%eax)
190
addl $8, %eax
191
addl $PMD_SIZE, %ecx
192
jnc 2b
193
194
movl $PAGE_SIZE, %ecx
195
.irpc l, 0123
196
movl %edx, \l * 8(%eax)
197
addl %ecx, %edx
198
.endr
199
addl %ecx, %eax
200
movl %edx, (%eax)
201
movl %eax, %cr3
202
203
call efi32_enable_long_mode
204
205
/* Set up far jump to 64-bit mode (CS is already on the stack) */
206
leal (efi_stub_entry - 1b)(%edi), %eax
207
movl %eax, 2(%esp)
208
209
movl 0(%ebp), %edi
210
movl 4(%ebp), %esi
211
movl %ebx, %edx
212
ljmpl *2(%esp)
213
SYM_FUNC_END(efi32_startup)
214
215
/*
216
* efi_status_t efi32_pe_entry(efi_handle_t image_handle,
217
* efi_system_table_32_t *sys_table)
218
*/
219
SYM_FUNC_START(efi32_pe_entry)
220
pushl %ebx // save callee-save registers
221
222
/* Check whether the CPU supports long mode */
223
movl $0x80000001, %eax // assume extended info support
224
cpuid
225
btl $29, %edx // check long mode bit
226
jnc 1f
227
leal 8(%esp), %esp // preserve stack alignment
228
xor %ebx, %ebx // no struct boot_params pointer
229
jmp efi32_startup // only ESP and EBX remain live
230
1: movl $0x80000003, %eax // EFI_UNSUPPORTED
231
popl %ebx
232
RET
233
SYM_FUNC_END(efi32_pe_entry)
234
235
#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
236
.org efi32_stub_entry + 0x200
237
.code64
238
SYM_FUNC_START_NOALIGN(efi64_stub_entry)
239
jmp efi_handover_entry
240
SYM_FUNC_END(efi64_stub_entry)
241
#endif
242
243
.data
244
.balign 8
245
SYM_DATA_START_LOCAL(efi32_call)
246
.long efi_enter32 - .
247
.word 0x0
248
SYM_DATA_END(efi32_call)
249
SYM_DATA(efi_is64, .byte 1)
250
251
.bss
252
.balign PAGE_SIZE
253
SYM_DATA_LOCAL(pte, .fill 6 * PAGE_SIZE, 1, 0)
254
255