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 */
50SYM_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
75SYM_FUNC_END(startup_64_mixed_mode)
76
77SYM_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
1301: 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
147SYM_FUNC_END(__efi64_thunk)
148
149 .code32
150#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
151SYM_FUNC_START(efi32_stub_entry)
152 call 1f
1531: 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
171SYM_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 */
179SYM_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
239SYM_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 */
252SYM_FUNC_START_LOCAL(efi32_entry)
253 call 1f
2541: 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
289SYM_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 */
295SYM_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
3112: popl %edi // restore callee-save registers
312 popl %ebx
313 leave
314 RET
315SYM_FUNC_END(efi32_pe_entry)
316
317#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
318 .org efi32_stub_entry + 0x200
319 .code64
320SYM_FUNC_START_NOALIGN(efi64_stub_entry)
321 jmp efi_handover_entry
322SYM_FUNC_END(efi64_stub_entry)
323#endif
324
325 .data
326 .balign 8
327SYM_DATA_START_LOCAL(efi32_boot_gdt)
328 .word 0
329 .quad 0
330SYM_DATA_END(efi32_boot_gdt)
331
332SYM_DATA_START_LOCAL(efi32_boot_idt)
333 .word 0
334 .quad 0
335SYM_DATA_END(efi32_boot_idt)
336
337SYM_DATA_LOCAL(efi32_boot_cs, .word 0)
338SYM_DATA_LOCAL(efi32_boot_ds, .word 0)
339SYM_DATA_LOCAL(efi32_boot_sp, .long 0)
340SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0)
341SYM_DATA(efi_is64, .byte 1)
342

source code of linux/arch/x86/boot/compressed/efi_mixed.S