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/asm-offsets.h> |
19 | #include <asm/msr.h> |
20 | #include <asm/page_types.h> |
21 | #include <asm/processor-flags.h> |
22 | #include <asm/segment.h> |
23 | #include <asm/setup.h> |
24 | |
25 | .code64 |
26 | .text |
27 | /* |
28 | * When booting in 64-bit mode on 32-bit EFI firmware, startup_64_mixed_mode() |
29 | * is the first thing that runs after switching to long mode. Depending on |
30 | * whether the EFI handover protocol or the compat entry point was used to |
31 | * enter the kernel, it will either branch to the common 64-bit EFI stub |
32 | * entrypoint efi_stub_entry() directly, or via the 64-bit EFI PE/COFF |
33 | * entrypoint efi_pe_entry(). In the former case, the bootloader must provide a |
34 | * struct bootparams pointer as the third argument, so the presence of such a |
35 | * pointer is used to disambiguate. |
36 | * |
37 | * +--------------+ |
38 | * +------------------+ +------------+ +------>| efi_pe_entry | |
39 | * | efi32_pe_entry |---->| | | +-----------+--+ |
40 | * +------------------+ | | +------+----------------+ | |
41 | * | startup_32 |---->| startup_64_mixed_mode | | |
42 | * +------------------+ | | +------+----------------+ | |
43 | * | efi32_stub_entry |---->| | | | |
44 | * +------------------+ +------------+ | | |
45 | * V | |
46 | * +------------+ +----------------+ | |
47 | * | startup_64 |<----| efi_stub_entry |<--------+ |
48 | * +------------+ +----------------+ |
49 | */ |
50 | SYM_FUNC_START(startup_64_mixed_mode) |
51 | lea efi32_boot_args(%rip), %rdx |
52 | mov 0(%rdx), %edi |
53 | mov 4(%rdx), %esi |
54 | |
55 | /* Switch to the firmware's stack */ |
56 | movl efi32_boot_sp(%rip), %esp |
57 | andl $~7, %esp |
58 | |
59 | #ifdef CONFIG_EFI_HANDOVER_PROTOCOL |
60 | mov 8(%rdx), %edx // saved bootparams pointer |
61 | test %edx, %edx |
62 | jnz efi_stub_entry |
63 | #endif |
64 | /* |
65 | * efi_pe_entry uses MS calling convention, which requires 32 bytes of |
66 | * shadow space on the stack even if all arguments are passed in |
67 | * registers. We also need an additional 8 bytes for the space that |
68 | * would be occupied by the return address, and this also results in |
69 | * the correct stack alignment for entry. |
70 | */ |
71 | sub $40, %rsp |
72 | mov %rdi, %rcx // MS calling convention |
73 | mov %rsi, %rdx |
74 | jmp efi_pe_entry |
75 | SYM_FUNC_END(startup_64_mixed_mode) |
76 | |
77 | SYM_FUNC_START(__efi64_thunk) |
78 | push %rbp |
79 | push %rbx |
80 | |
81 | movl %ds, %eax |
82 | push %rax |
83 | movl %es, %eax |
84 | push %rax |
85 | movl %ss, %eax |
86 | push %rax |
87 | |
88 | /* Copy args passed on stack */ |
89 | movq 0x30(%rsp), %rbp |
90 | movq 0x38(%rsp), %rbx |
91 | movq 0x40(%rsp), %rax |
92 | |
93 | /* |
94 | * Convert x86-64 ABI params to i386 ABI |
95 | */ |
96 | subq $64, %rsp |
97 | movl %esi, 0x0(%rsp) |
98 | movl %edx, 0x4(%rsp) |
99 | movl %ecx, 0x8(%rsp) |
100 | movl %r8d, 0xc(%rsp) |
101 | movl %r9d, 0x10(%rsp) |
102 | movl %ebp, 0x14(%rsp) |
103 | movl %ebx, 0x18(%rsp) |
104 | movl %eax, 0x1c(%rsp) |
105 | |
106 | leaq 0x20(%rsp), %rbx |
107 | sgdt (%rbx) |
108 | sidt 16(%rbx) |
109 | |
110 | leaq 1f(%rip), %rbp |
111 | |
112 | /* |
113 | * Switch to IDT and GDT with 32-bit segments. These are the firmware |
114 | * GDT and IDT that were installed when the kernel started executing. |
115 | * The pointers were saved by the efi32_entry() routine below. |
116 | * |
117 | * Pass the saved DS selector to the 32-bit code, and use far return to |
118 | * restore the saved CS selector. |
119 | */ |
120 | lidt efi32_boot_idt(%rip) |
121 | lgdt efi32_boot_gdt(%rip) |
122 | |
123 | movzwl efi32_boot_ds(%rip), %edx |
124 | movzwq efi32_boot_cs(%rip), %rax |
125 | pushq %rax |
126 | leaq efi_enter32(%rip), %rax |
127 | pushq %rax |
128 | lretq |
129 | |
130 | 1: addq $64, %rsp |
131 | movq %rdi, %rax |
132 | |
133 | pop %rbx |
134 | movl %ebx, %ss |
135 | pop %rbx |
136 | movl %ebx, %es |
137 | pop %rbx |
138 | movl %ebx, %ds |
139 | /* Clear out 32-bit selector from FS and GS */ |
140 | xorl %ebx, %ebx |
141 | movl %ebx, %fs |
142 | movl %ebx, %gs |
143 | |
144 | pop %rbx |
145 | pop %rbp |
146 | RET |
147 | SYM_FUNC_END(__efi64_thunk) |
148 | |
149 | .code32 |
150 | #ifdef CONFIG_EFI_HANDOVER_PROTOCOL |
151 | SYM_FUNC_START(efi32_stub_entry) |
152 | call 1f |
153 | 1: popl %ecx |
154 | leal (efi32_boot_args - 1b)(%ecx), %ebx |
155 | |
156 | /* Clear BSS */ |
157 | xorl %eax, %eax |
158 | leal (_bss - 1b)(%ecx), %edi |
159 | leal (_ebss - 1b)(%ecx), %ecx |
160 | subl %edi, %ecx |
161 | shrl $2, %ecx |
162 | cld |
163 | rep stosl |
164 | |
165 | add $0x4, %esp /* Discard return address */ |
166 | popl %ecx |
167 | popl %edx |
168 | popl %esi |
169 | movl %esi, 8(%ebx) |
170 | jmp efi32_entry |
171 | SYM_FUNC_END(efi32_stub_entry) |
172 | #endif |
173 | |
174 | /* |
175 | * EFI service pointer must be in %edi. |
176 | * |
177 | * The stack should represent the 32-bit calling convention. |
178 | */ |
179 | SYM_FUNC_START_LOCAL(efi_enter32) |
180 | /* Load firmware selector into data and stack segment registers */ |
181 | movl %edx, %ds |
182 | movl %edx, %es |
183 | movl %edx, %fs |
184 | movl %edx, %gs |
185 | movl %edx, %ss |
186 | |
187 | /* Reload pgtables */ |
188 | movl %cr3, %eax |
189 | movl %eax, %cr3 |
190 | |
191 | /* Disable paging */ |
192 | movl %cr0, %eax |
193 | btrl $X86_CR0_PG_BIT, %eax |
194 | movl %eax, %cr0 |
195 | |
196 | /* Disable long mode via EFER */ |
197 | movl $MSR_EFER, %ecx |
198 | rdmsr |
199 | btrl $_EFER_LME, %eax |
200 | wrmsr |
201 | |
202 | call *%edi |
203 | |
204 | /* We must preserve return value */ |
205 | movl %eax, %edi |
206 | |
207 | /* |
208 | * Some firmware will return with interrupts enabled. Be sure to |
209 | * disable them before we switch GDTs and IDTs. |
210 | */ |
211 | cli |
212 | |
213 | lidtl 16(%ebx) |
214 | lgdtl (%ebx) |
215 | |
216 | movl %cr4, %eax |
217 | btsl $(X86_CR4_PAE_BIT), %eax |
218 | movl %eax, %cr4 |
219 | |
220 | movl %cr3, %eax |
221 | movl %eax, %cr3 |
222 | |
223 | movl $MSR_EFER, %ecx |
224 | rdmsr |
225 | btsl $_EFER_LME, %eax |
226 | wrmsr |
227 | |
228 | xorl %eax, %eax |
229 | lldt %ax |
230 | |
231 | pushl $__KERNEL_CS |
232 | pushl %ebp |
233 | |
234 | /* Enable paging */ |
235 | movl %cr0, %eax |
236 | btsl $X86_CR0_PG_BIT, %eax |
237 | movl %eax, %cr0 |
238 | lret |
239 | SYM_FUNC_END(efi_enter32) |
240 | |
241 | /* |
242 | * This is the common EFI stub entry point for mixed mode. |
243 | * |
244 | * Arguments: %ecx image handle |
245 | * %edx EFI system table pointer |
246 | * |
247 | * Since this is the point of no return for ordinary execution, no registers |
248 | * are considered live except for the function parameters. [Note that the EFI |
249 | * stub may still exit and return to the firmware using the Exit() EFI boot |
250 | * service.] |
251 | */ |
252 | SYM_FUNC_START_LOCAL(efi32_entry) |
253 | call 1f |
254 | 1: pop %ebx |
255 | |
256 | /* Save firmware GDTR and code/data selectors */ |
257 | sgdtl (efi32_boot_gdt - 1b)(%ebx) |
258 | movw %cs, (efi32_boot_cs - 1b)(%ebx) |
259 | movw %ds, (efi32_boot_ds - 1b)(%ebx) |
260 | |
261 | /* Store firmware IDT descriptor */ |
262 | sidtl (efi32_boot_idt - 1b)(%ebx) |
263 | |
264 | /* Store firmware stack pointer */ |
265 | movl %esp, (efi32_boot_sp - 1b)(%ebx) |
266 | |
267 | /* Store boot arguments */ |
268 | leal (efi32_boot_args - 1b)(%ebx), %ebx |
269 | movl %ecx, 0(%ebx) |
270 | movl %edx, 4(%ebx) |
271 | movb $0x0, 12(%ebx) // efi_is64 |
272 | |
273 | /* |
274 | * Allocate some memory for a temporary struct boot_params, which only |
275 | * needs the minimal pieces that startup_32() relies on. |
276 | */ |
277 | subl $PARAM_SIZE, %esp |
278 | movl %esp, %esi |
279 | movl $PAGE_SIZE, BP_kernel_alignment(%esi) |
280 | movl $_end - 1b, BP_init_size(%esi) |
281 | subl $startup_32 - 1b, BP_init_size(%esi) |
282 | |
283 | /* Disable paging */ |
284 | movl %cr0, %eax |
285 | btrl $X86_CR0_PG_BIT, %eax |
286 | movl %eax, %cr0 |
287 | |
288 | jmp startup_32 |
289 | SYM_FUNC_END(efi32_entry) |
290 | |
291 | /* |
292 | * efi_status_t efi32_pe_entry(efi_handle_t image_handle, |
293 | * efi_system_table_32_t *sys_table) |
294 | */ |
295 | SYM_FUNC_START(efi32_pe_entry) |
296 | pushl %ebp |
297 | movl %esp, %ebp |
298 | pushl %ebx // save callee-save registers |
299 | pushl %edi |
300 | |
301 | call verify_cpu // check for long mode support |
302 | testl %eax, %eax |
303 | movl $0x80000003, %eax // EFI_UNSUPPORTED |
304 | jnz 2f |
305 | |
306 | movl 8(%ebp), %ecx // image_handle |
307 | movl 12(%ebp), %edx // sys_table |
308 | jmp efi32_entry // pass %ecx, %edx |
309 | // no other registers remain live |
310 | |
311 | 2: popl %edi // restore callee-save registers |
312 | popl %ebx |
313 | leave |
314 | RET |
315 | SYM_FUNC_END(efi32_pe_entry) |
316 | |
317 | #ifdef CONFIG_EFI_HANDOVER_PROTOCOL |
318 | .org efi32_stub_entry + 0x200 |
319 | .code64 |
320 | SYM_FUNC_START_NOALIGN(efi64_stub_entry) |
321 | jmp efi_handover_entry |
322 | SYM_FUNC_END(efi64_stub_entry) |
323 | #endif |
324 | |
325 | .data |
326 | .balign 8 |
327 | SYM_DATA_START_LOCAL(efi32_boot_gdt) |
328 | .word 0 |
329 | .quad 0 |
330 | SYM_DATA_END(efi32_boot_gdt) |
331 | |
332 | SYM_DATA_START_LOCAL(efi32_boot_idt) |
333 | .word 0 |
334 | .quad 0 |
335 | SYM_DATA_END(efi32_boot_idt) |
336 | |
337 | SYM_DATA_LOCAL(efi32_boot_cs, .word 0) |
338 | SYM_DATA_LOCAL(efi32_boot_ds, .word 0) |
339 | SYM_DATA_LOCAL(efi32_boot_sp, .long 0) |
340 | SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0) |
341 | SYM_DATA(efi_is64, .byte 1) |
342 | |