xref: /linux/arch/x86/boot/compressed/efi_mixed.S (revision 55ec81f7517fad09135f65552cea0a3ee84fff30)
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