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