xref: /linux/arch/x86/boot/startup/efi-mixed.S (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
1*4f2d1bbcSArd Biesheuvel/* SPDX-License-Identifier: GPL-2.0 */
2*4f2d1bbcSArd Biesheuvel/*
3*4f2d1bbcSArd Biesheuvel * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
4*4f2d1bbcSArd Biesheuvel *
5*4f2d1bbcSArd Biesheuvel * Early support for invoking 32-bit EFI services from a 64-bit kernel.
6*4f2d1bbcSArd Biesheuvel *
7*4f2d1bbcSArd Biesheuvel * Because this thunking occurs before ExitBootServices() we have to
8*4f2d1bbcSArd Biesheuvel * restore the firmware's 32-bit GDT and IDT before we make EFI service
9*4f2d1bbcSArd Biesheuvel * calls.
10*4f2d1bbcSArd Biesheuvel *
11*4f2d1bbcSArd Biesheuvel * On the plus side, we don't have to worry about mangling 64-bit
12*4f2d1bbcSArd Biesheuvel * addresses into 32-bits because we're executing with an identity
13*4f2d1bbcSArd Biesheuvel * mapped pagetable and haven't transitioned to 64-bit virtual addresses
14*4f2d1bbcSArd Biesheuvel * yet.
15*4f2d1bbcSArd Biesheuvel */
16*4f2d1bbcSArd Biesheuvel
17*4f2d1bbcSArd Biesheuvel#include <linux/linkage.h>
18*4f2d1bbcSArd Biesheuvel#include <asm/desc_defs.h>
19*4f2d1bbcSArd Biesheuvel#include <asm/msr.h>
20*4f2d1bbcSArd Biesheuvel#include <asm/page_types.h>
21*4f2d1bbcSArd Biesheuvel#include <asm/pgtable_types.h>
22*4f2d1bbcSArd Biesheuvel#include <asm/processor-flags.h>
23*4f2d1bbcSArd Biesheuvel#include <asm/segment.h>
24*4f2d1bbcSArd Biesheuvel
25*4f2d1bbcSArd Biesheuvel	.text
26*4f2d1bbcSArd Biesheuvel	.code32
27*4f2d1bbcSArd Biesheuvel#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
28*4f2d1bbcSArd BiesheuvelSYM_FUNC_START(efi32_stub_entry)
29*4f2d1bbcSArd Biesheuvel	call	1f
30*4f2d1bbcSArd Biesheuvel1:	popl	%ecx
31*4f2d1bbcSArd Biesheuvel
32*4f2d1bbcSArd Biesheuvel	/* Clear BSS */
33*4f2d1bbcSArd Biesheuvel	xorl	%eax, %eax
34*4f2d1bbcSArd Biesheuvel	leal	(_bss - 1b)(%ecx), %edi
35*4f2d1bbcSArd Biesheuvel	leal	(_ebss - 1b)(%ecx), %ecx
36*4f2d1bbcSArd Biesheuvel	subl	%edi, %ecx
37*4f2d1bbcSArd Biesheuvel	shrl	$2, %ecx
38*4f2d1bbcSArd Biesheuvel	cld
39*4f2d1bbcSArd Biesheuvel	rep	stosl
40*4f2d1bbcSArd Biesheuvel
41*4f2d1bbcSArd Biesheuvel	add	$0x4, %esp		/* Discard return address */
42*4f2d1bbcSArd Biesheuvel	movl	8(%esp), %ebx		/* struct boot_params pointer */
43*4f2d1bbcSArd Biesheuvel	jmp	efi32_startup
44*4f2d1bbcSArd BiesheuvelSYM_FUNC_END(efi32_stub_entry)
45*4f2d1bbcSArd Biesheuvel#endif
46*4f2d1bbcSArd Biesheuvel
47*4f2d1bbcSArd Biesheuvel/*
48*4f2d1bbcSArd Biesheuvel * Called using a far call from __efi64_thunk() below, using the x86_64 SysV
49*4f2d1bbcSArd Biesheuvel * ABI (except for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are
50*4f2d1bbcSArd Biesheuvel * used instead).  EBP+16 points to the arguments passed via the stack.
51*4f2d1bbcSArd Biesheuvel *
52*4f2d1bbcSArd Biesheuvel * The first argument (EDI) is a pointer to the boot service or protocol, to
53*4f2d1bbcSArd Biesheuvel * which the remaining arguments are passed, each truncated to 32 bits.
54*4f2d1bbcSArd Biesheuvel */
55*4f2d1bbcSArd BiesheuvelSYM_FUNC_START_LOCAL(efi_enter32)
56*4f2d1bbcSArd Biesheuvel	/*
57*4f2d1bbcSArd Biesheuvel	 * Convert x86-64 SysV ABI params to i386 ABI
58*4f2d1bbcSArd Biesheuvel	 */
59*4f2d1bbcSArd Biesheuvel	pushl	32(%ebp)	/* Up to 3 args passed via the stack */
60*4f2d1bbcSArd Biesheuvel	pushl	24(%ebp)
61*4f2d1bbcSArd Biesheuvel	pushl	16(%ebp)
62*4f2d1bbcSArd Biesheuvel	pushl	%ebx		/* R9 */
63*4f2d1bbcSArd Biesheuvel	pushl	%eax		/* R8 */
64*4f2d1bbcSArd Biesheuvel	pushl	%ecx
65*4f2d1bbcSArd Biesheuvel	pushl	%edx
66*4f2d1bbcSArd Biesheuvel	pushl	%esi
67*4f2d1bbcSArd Biesheuvel
68*4f2d1bbcSArd Biesheuvel	/* Disable paging */
69*4f2d1bbcSArd Biesheuvel	movl	%cr0, %eax
70*4f2d1bbcSArd Biesheuvel	btrl	$X86_CR0_PG_BIT, %eax
71*4f2d1bbcSArd Biesheuvel	movl	%eax, %cr0
72*4f2d1bbcSArd Biesheuvel
73*4f2d1bbcSArd Biesheuvel	/* Disable long mode via EFER */
74*4f2d1bbcSArd Biesheuvel	movl	$MSR_EFER, %ecx
75*4f2d1bbcSArd Biesheuvel	rdmsr
76*4f2d1bbcSArd Biesheuvel	btrl	$_EFER_LME, %eax
77*4f2d1bbcSArd Biesheuvel	wrmsr
78*4f2d1bbcSArd Biesheuvel
79*4f2d1bbcSArd Biesheuvel	call	*%edi
80*4f2d1bbcSArd Biesheuvel
81*4f2d1bbcSArd Biesheuvel	/* We must preserve return value */
82*4f2d1bbcSArd Biesheuvel	movl	%eax, %edi
83*4f2d1bbcSArd Biesheuvel
84*4f2d1bbcSArd Biesheuvel	call	efi32_enable_long_mode
85*4f2d1bbcSArd Biesheuvel
86*4f2d1bbcSArd Biesheuvel	addl	$32, %esp
87*4f2d1bbcSArd Biesheuvel	movl	%edi, %eax
88*4f2d1bbcSArd Biesheuvel	lret
89*4f2d1bbcSArd BiesheuvelSYM_FUNC_END(efi_enter32)
90*4f2d1bbcSArd Biesheuvel
91*4f2d1bbcSArd Biesheuvel	.code64
92*4f2d1bbcSArd BiesheuvelSYM_FUNC_START(__efi64_thunk)
93*4f2d1bbcSArd Biesheuvel	push	%rbp
94*4f2d1bbcSArd Biesheuvel	movl	%esp, %ebp
95*4f2d1bbcSArd Biesheuvel	push	%rbx
96*4f2d1bbcSArd Biesheuvel
97*4f2d1bbcSArd Biesheuvel	/* Move args #5 and #6 into 32-bit accessible registers */
98*4f2d1bbcSArd Biesheuvel	movl	%r8d, %eax
99*4f2d1bbcSArd Biesheuvel	movl	%r9d, %ebx
100*4f2d1bbcSArd Biesheuvel
101*4f2d1bbcSArd Biesheuvel	lcalll	*efi32_call(%rip)
102*4f2d1bbcSArd Biesheuvel
103*4f2d1bbcSArd Biesheuvel	pop	%rbx
104*4f2d1bbcSArd Biesheuvel	pop	%rbp
105*4f2d1bbcSArd Biesheuvel	RET
106*4f2d1bbcSArd BiesheuvelSYM_FUNC_END(__efi64_thunk)
107*4f2d1bbcSArd Biesheuvel
108*4f2d1bbcSArd Biesheuvel	.code32
109*4f2d1bbcSArd BiesheuvelSYM_FUNC_START_LOCAL(efi32_enable_long_mode)
110*4f2d1bbcSArd Biesheuvel	movl	%cr4, %eax
111*4f2d1bbcSArd Biesheuvel	btsl	$(X86_CR4_PAE_BIT), %eax
112*4f2d1bbcSArd Biesheuvel	movl	%eax, %cr4
113*4f2d1bbcSArd Biesheuvel
114*4f2d1bbcSArd Biesheuvel	movl	$MSR_EFER, %ecx
115*4f2d1bbcSArd Biesheuvel	rdmsr
116*4f2d1bbcSArd Biesheuvel	btsl	$_EFER_LME, %eax
117*4f2d1bbcSArd Biesheuvel	wrmsr
118*4f2d1bbcSArd Biesheuvel
119*4f2d1bbcSArd Biesheuvel	/* Disable interrupts - the firmware's IDT does not work in long mode */
120*4f2d1bbcSArd Biesheuvel	cli
121*4f2d1bbcSArd Biesheuvel
122*4f2d1bbcSArd Biesheuvel	/* Enable paging */
123*4f2d1bbcSArd Biesheuvel	movl	%cr0, %eax
124*4f2d1bbcSArd Biesheuvel	btsl	$X86_CR0_PG_BIT, %eax
125*4f2d1bbcSArd Biesheuvel	movl	%eax, %cr0
126*4f2d1bbcSArd Biesheuvel	ret
127*4f2d1bbcSArd BiesheuvelSYM_FUNC_END(efi32_enable_long_mode)
128*4f2d1bbcSArd Biesheuvel
129*4f2d1bbcSArd Biesheuvel/*
130*4f2d1bbcSArd Biesheuvel * This is the common EFI stub entry point for mixed mode. It sets up the GDT
131*4f2d1bbcSArd Biesheuvel * and page tables needed for 64-bit execution, after which it calls the
132*4f2d1bbcSArd Biesheuvel * common 64-bit EFI entrypoint efi_stub_entry().
133*4f2d1bbcSArd Biesheuvel *
134*4f2d1bbcSArd Biesheuvel * Arguments:	0(%esp)	image handle
135*4f2d1bbcSArd Biesheuvel * 		4(%esp)	EFI system table pointer
136*4f2d1bbcSArd Biesheuvel *		%ebx	struct boot_params pointer (or NULL)
137*4f2d1bbcSArd Biesheuvel *
138*4f2d1bbcSArd Biesheuvel * Since this is the point of no return for ordinary execution, no registers
139*4f2d1bbcSArd Biesheuvel * are considered live except for the function parameters. [Note that the EFI
140*4f2d1bbcSArd Biesheuvel * stub may still exit and return to the firmware using the Exit() EFI boot
141*4f2d1bbcSArd Biesheuvel * service.]
142*4f2d1bbcSArd Biesheuvel */
143*4f2d1bbcSArd BiesheuvelSYM_FUNC_START_LOCAL(efi32_startup)
144*4f2d1bbcSArd Biesheuvel	movl	%esp, %ebp
145*4f2d1bbcSArd Biesheuvel
146*4f2d1bbcSArd Biesheuvel	subl	$8, %esp
147*4f2d1bbcSArd Biesheuvel	sgdtl	(%esp)			/* Save GDT descriptor to the stack */
148*4f2d1bbcSArd Biesheuvel	movl	2(%esp), %esi		/* Existing GDT pointer */
149*4f2d1bbcSArd Biesheuvel	movzwl	(%esp), %ecx		/* Existing GDT limit */
150*4f2d1bbcSArd Biesheuvel	inc	%ecx			/* Existing GDT size */
151*4f2d1bbcSArd Biesheuvel	andl	$~7, %ecx		/* Ensure size is multiple of 8 */
152*4f2d1bbcSArd Biesheuvel
153*4f2d1bbcSArd Biesheuvel	subl	%ecx, %esp		/* Allocate new GDT */
154*4f2d1bbcSArd Biesheuvel	andl	$~15, %esp		/* Realign the stack */
155*4f2d1bbcSArd Biesheuvel	movl	%esp, %edi		/* New GDT address */
156*4f2d1bbcSArd Biesheuvel	leal	7(%ecx), %eax		/* New GDT limit */
157*4f2d1bbcSArd Biesheuvel	pushw	%cx			/* Push 64-bit CS (for LJMP below) */
158*4f2d1bbcSArd Biesheuvel	pushl	%edi			/* Push new GDT address */
159*4f2d1bbcSArd Biesheuvel	pushw	%ax			/* Push new GDT limit */
160*4f2d1bbcSArd Biesheuvel
161*4f2d1bbcSArd Biesheuvel	/* Copy GDT to the stack and add a 64-bit code segment at the end */
162*4f2d1bbcSArd Biesheuvel	movl	$GDT_ENTRY(DESC_CODE64, 0, 0xfffff) & 0xffffffff, (%edi,%ecx)
163*4f2d1bbcSArd Biesheuvel	movl	$GDT_ENTRY(DESC_CODE64, 0, 0xfffff) >> 32, 4(%edi,%ecx)
164*4f2d1bbcSArd Biesheuvel	shrl	$2, %ecx
165*4f2d1bbcSArd Biesheuvel	cld
166*4f2d1bbcSArd Biesheuvel	rep	movsl			/* Copy the firmware GDT */
167*4f2d1bbcSArd Biesheuvel	lgdtl	(%esp)			/* Switch to the new GDT */
168*4f2d1bbcSArd Biesheuvel
169*4f2d1bbcSArd Biesheuvel	call	1f
170*4f2d1bbcSArd Biesheuvel1:	pop	%edi
171*4f2d1bbcSArd Biesheuvel
172*4f2d1bbcSArd Biesheuvel	/* Record mixed mode entry */
173*4f2d1bbcSArd Biesheuvel	movb	$0x0, (efi_is64 - 1b)(%edi)
174*4f2d1bbcSArd Biesheuvel
175*4f2d1bbcSArd Biesheuvel	/* Set up indirect far call to re-enter 32-bit mode */
176*4f2d1bbcSArd Biesheuvel	leal	(efi32_call - 1b)(%edi), %eax
177*4f2d1bbcSArd Biesheuvel	addl	%eax, (%eax)
178*4f2d1bbcSArd Biesheuvel	movw	%cs, 4(%eax)
179*4f2d1bbcSArd Biesheuvel
180*4f2d1bbcSArd Biesheuvel	/* Disable paging */
181*4f2d1bbcSArd Biesheuvel	movl	%cr0, %eax
182*4f2d1bbcSArd Biesheuvel	btrl	$X86_CR0_PG_BIT, %eax
183*4f2d1bbcSArd Biesheuvel	movl	%eax, %cr0
184*4f2d1bbcSArd Biesheuvel
185*4f2d1bbcSArd Biesheuvel	/* Set up 1:1 mapping */
186*4f2d1bbcSArd Biesheuvel	leal	(pte - 1b)(%edi), %eax
187*4f2d1bbcSArd Biesheuvel	movl	$_PAGE_PRESENT | _PAGE_RW | _PAGE_PSE, %ecx
188*4f2d1bbcSArd Biesheuvel	leal	(_PAGE_PRESENT | _PAGE_RW)(%eax), %edx
189*4f2d1bbcSArd Biesheuvel2:	movl	%ecx, (%eax)
190*4f2d1bbcSArd Biesheuvel	addl	$8, %eax
191*4f2d1bbcSArd Biesheuvel	addl	$PMD_SIZE, %ecx
192*4f2d1bbcSArd Biesheuvel	jnc	2b
193*4f2d1bbcSArd Biesheuvel
194*4f2d1bbcSArd Biesheuvel	movl	$PAGE_SIZE, %ecx
195*4f2d1bbcSArd Biesheuvel	.irpc	l, 0123
196*4f2d1bbcSArd Biesheuvel	movl	%edx, \l * 8(%eax)
197*4f2d1bbcSArd Biesheuvel	addl	%ecx, %edx
198*4f2d1bbcSArd Biesheuvel	.endr
199*4f2d1bbcSArd Biesheuvel	addl	%ecx, %eax
200*4f2d1bbcSArd Biesheuvel	movl	%edx, (%eax)
201*4f2d1bbcSArd Biesheuvel	movl	%eax, %cr3
202*4f2d1bbcSArd Biesheuvel
203*4f2d1bbcSArd Biesheuvel	call	efi32_enable_long_mode
204*4f2d1bbcSArd Biesheuvel
205*4f2d1bbcSArd Biesheuvel	/* Set up far jump to 64-bit mode (CS is already on the stack) */
206*4f2d1bbcSArd Biesheuvel	leal	(efi_stub_entry - 1b)(%edi), %eax
207*4f2d1bbcSArd Biesheuvel	movl	%eax, 2(%esp)
208*4f2d1bbcSArd Biesheuvel
209*4f2d1bbcSArd Biesheuvel	movl	0(%ebp), %edi
210*4f2d1bbcSArd Biesheuvel	movl	4(%ebp), %esi
211*4f2d1bbcSArd Biesheuvel	movl	%ebx, %edx
212*4f2d1bbcSArd Biesheuvel	ljmpl	*2(%esp)
213*4f2d1bbcSArd BiesheuvelSYM_FUNC_END(efi32_startup)
214*4f2d1bbcSArd Biesheuvel
215*4f2d1bbcSArd Biesheuvel/*
216*4f2d1bbcSArd Biesheuvel * efi_status_t efi32_pe_entry(efi_handle_t image_handle,
217*4f2d1bbcSArd Biesheuvel *			       efi_system_table_32_t *sys_table)
218*4f2d1bbcSArd Biesheuvel */
219*4f2d1bbcSArd BiesheuvelSYM_FUNC_START(efi32_pe_entry)
220*4f2d1bbcSArd Biesheuvel	pushl	%ebx				// save callee-save registers
221*4f2d1bbcSArd Biesheuvel
222*4f2d1bbcSArd Biesheuvel	/* Check whether the CPU supports long mode */
223*4f2d1bbcSArd Biesheuvel	movl	$0x80000001, %eax		// assume extended info support
224*4f2d1bbcSArd Biesheuvel	cpuid
225*4f2d1bbcSArd Biesheuvel	btl	$29, %edx			// check long mode bit
226*4f2d1bbcSArd Biesheuvel	jnc	1f
227*4f2d1bbcSArd Biesheuvel	leal	8(%esp), %esp			// preserve stack alignment
228*4f2d1bbcSArd Biesheuvel	xor	%ebx, %ebx			// no struct boot_params pointer
229*4f2d1bbcSArd Biesheuvel	jmp	efi32_startup			// only ESP and EBX remain live
230*4f2d1bbcSArd Biesheuvel1:	movl	$0x80000003, %eax		// EFI_UNSUPPORTED
231*4f2d1bbcSArd Biesheuvel	popl	%ebx
232*4f2d1bbcSArd Biesheuvel	RET
233*4f2d1bbcSArd BiesheuvelSYM_FUNC_END(efi32_pe_entry)
234*4f2d1bbcSArd Biesheuvel
235*4f2d1bbcSArd Biesheuvel#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
236*4f2d1bbcSArd Biesheuvel	.org	efi32_stub_entry + 0x200
237*4f2d1bbcSArd Biesheuvel	.code64
238*4f2d1bbcSArd BiesheuvelSYM_FUNC_START_NOALIGN(efi64_stub_entry)
239*4f2d1bbcSArd Biesheuvel	jmp	efi_handover_entry
240*4f2d1bbcSArd BiesheuvelSYM_FUNC_END(efi64_stub_entry)
241*4f2d1bbcSArd Biesheuvel#endif
242*4f2d1bbcSArd Biesheuvel
243*4f2d1bbcSArd Biesheuvel	.data
244*4f2d1bbcSArd Biesheuvel	.balign	8
245*4f2d1bbcSArd BiesheuvelSYM_DATA_START_LOCAL(efi32_call)
246*4f2d1bbcSArd Biesheuvel	.long	efi_enter32 - .
247*4f2d1bbcSArd Biesheuvel	.word	0x0
248*4f2d1bbcSArd BiesheuvelSYM_DATA_END(efi32_call)
249*4f2d1bbcSArd BiesheuvelSYM_DATA(efi_is64, .byte 1)
250*4f2d1bbcSArd Biesheuvel
251*4f2d1bbcSArd Biesheuvel	.bss
252*4f2d1bbcSArd Biesheuvel	.balign PAGE_SIZE
253*4f2d1bbcSArd BiesheuvelSYM_DATA_LOCAL(pte, .fill 6 * PAGE_SIZE, 1, 0)
254