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 */
48SYM_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
68SYM_FUNC_END(startup_64_mixed_mode)
69
70SYM_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
1231: 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
140SYM_FUNC_END(__efi64_thunk)
141
142 .code32
143#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
144SYM_FUNC_START(efi32_stub_entry)
145 call 1f
1461: 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
162SYM_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 */
170SYM_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
230SYM_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 */
245SYM_FUNC_START_LOCAL(efi32_entry)
246 call 1f
2471: 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
270SYM_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 */
276SYM_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
2932: popl %edi // restore callee-save registers
294 popl %ebx
295 leave
296 RET
297SYM_FUNC_END(efi32_pe_entry)
298
299#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
300 .org efi32_stub_entry + 0x200
301 .code64
302SYM_FUNC_START_NOALIGN(efi64_stub_entry)
303 jmp efi_handover_entry
304SYM_FUNC_END(efi64_stub_entry)
305#endif
306
307 .data
308 .balign 8
309SYM_DATA_START_LOCAL(efi32_boot_gdt)
310 .word 0
311 .quad 0
312SYM_DATA_END(efi32_boot_gdt)
313
314SYM_DATA_START_LOCAL(efi32_boot_idt)
315 .word 0
316 .quad 0
317SYM_DATA_END(efi32_boot_idt)
318
319SYM_DATA_LOCAL(efi32_boot_cs, .word 0)
320SYM_DATA_LOCAL(efi32_boot_ds, .word 0)
321SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0)
322SYM_DATA(efi_is64, .byte 1)
323

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