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 before we make EFI serivce calls,
9 * since the firmware's 32-bit IDT is still currently installed and it
10 * needs to be able to service interrupts.
11 *
12 * On the plus side, we don't have to worry about mangling 64-bit
13 * addresses into 32-bits because we're executing with an identify
14 * mapped pagetable and haven't transitioned to 64-bit virtual addresses
15 * yet.
16 */
17
18#include <linux/linkage.h>
19#include <asm/msr.h>
20#include <asm/page_types.h>
21#include <asm/processor-flags.h>
22#include <asm/segment.h>
23
24	.code64
25	.text
26ENTRY(efi64_thunk)
27	push	%rbp
28	push	%rbx
29
30	subq	$8, %rsp
31	leaq	efi_exit32(%rip), %rax
32	movl	%eax, 4(%rsp)
33	leaq	efi_gdt64(%rip), %rax
34	movl	%eax, (%rsp)
35	movl	%eax, 2(%rax)		/* Fixup the gdt base address */
36
37	movl	%ds, %eax
38	push	%rax
39	movl	%es, %eax
40	push	%rax
41	movl	%ss, %eax
42	push	%rax
43
44	/*
45	 * Convert x86-64 ABI params to i386 ABI
46	 */
47	subq	$32, %rsp
48	movl	%esi, 0x0(%rsp)
49	movl	%edx, 0x4(%rsp)
50	movl	%ecx, 0x8(%rsp)
51	movq	%r8, %rsi
52	movl	%esi, 0xc(%rsp)
53	movq	%r9, %rsi
54	movl	%esi,  0x10(%rsp)
55
56	sgdt	save_gdt(%rip)
57
58	leaq	1f(%rip), %rbx
59	movq	%rbx, func_rt_ptr(%rip)
60
61	/*
62	 * Switch to gdt with 32-bit segments. This is the firmware GDT
63	 * that was installed when the kernel started executing. This
64	 * pointer was saved at the EFI stub entry point in head_64.S.
65	 */
66	leaq	efi32_boot_gdt(%rip), %rax
67	lgdt	(%rax)
68
69	pushq	$__KERNEL_CS
70	leaq	efi_enter32(%rip), %rax
71	pushq	%rax
72	lretq
73
741:	addq	$32, %rsp
75
76	lgdt	save_gdt(%rip)
77
78	pop	%rbx
79	movl	%ebx, %ss
80	pop	%rbx
81	movl	%ebx, %es
82	pop	%rbx
83	movl	%ebx, %ds
84
85	/*
86	 * Convert 32-bit status code into 64-bit.
87	 */
88	test	%rax, %rax
89	jz	1f
90	movl	%eax, %ecx
91	andl	$0x0fffffff, %ecx
92	andl	$0xf0000000, %eax
93	shl	$32, %rax
94	or	%rcx, %rax
951:
96	addq	$8, %rsp
97	pop	%rbx
98	pop	%rbp
99	ret
100ENDPROC(efi64_thunk)
101
102ENTRY(efi_exit32)
103	movq	func_rt_ptr(%rip), %rax
104	push	%rax
105	mov	%rdi, %rax
106	ret
107ENDPROC(efi_exit32)
108
109	.code32
110/*
111 * EFI service pointer must be in %edi.
112 *
113 * The stack should represent the 32-bit calling convention.
114 */
115ENTRY(efi_enter32)
116	movl	$__KERNEL_DS, %eax
117	movl	%eax, %ds
118	movl	%eax, %es
119	movl	%eax, %ss
120
121	/* Reload pgtables */
122	movl	%cr3, %eax
123	movl	%eax, %cr3
124
125	/* Disable paging */
126	movl	%cr0, %eax
127	btrl	$X86_CR0_PG_BIT, %eax
128	movl	%eax, %cr0
129
130	/* Disable long mode via EFER */
131	movl	$MSR_EFER, %ecx
132	rdmsr
133	btrl	$_EFER_LME, %eax
134	wrmsr
135
136	call	*%edi
137
138	/* We must preserve return value */
139	movl	%eax, %edi
140
141	/*
142	 * Some firmware will return with interrupts enabled. Be sure to
143	 * disable them before we switch GDTs.
144	 */
145	cli
146
147	movl	56(%esp), %eax
148	movl	%eax, 2(%eax)
149	lgdtl	(%eax)
150
151	movl	%cr4, %eax
152	btsl	$(X86_CR4_PAE_BIT), %eax
153	movl	%eax, %cr4
154
155	movl	%cr3, %eax
156	movl	%eax, %cr3
157
158	movl	$MSR_EFER, %ecx
159	rdmsr
160	btsl	$_EFER_LME, %eax
161	wrmsr
162
163	xorl	%eax, %eax
164	lldt	%ax
165
166	movl	60(%esp), %eax
167	pushl	$__KERNEL_CS
168	pushl	%eax
169
170	/* Enable paging */
171	movl	%cr0, %eax
172	btsl	$X86_CR0_PG_BIT, %eax
173	movl	%eax, %cr0
174	lret
175ENDPROC(efi_enter32)
176
177	.data
178	.balign	8
179	.global	efi32_boot_gdt
180efi32_boot_gdt:	.word	0
181		.quad	0
182
183save_gdt:	.word	0
184		.quad	0
185func_rt_ptr:	.quad	0
186
187	.global efi_gdt64
188efi_gdt64:
189	.word	efi_gdt64_end - efi_gdt64
190	.long	0			/* Filled out by user */
191	.word	0
192	.quad	0x0000000000000000	/* NULL descriptor */
193	.quad	0x00af9a000000ffff	/* __KERNEL_CS */
194	.quad	0x00cf92000000ffff	/* __KERNEL_DS */
195	.quad	0x0080890000000000	/* TS descriptor */
196	.quad   0x0000000000000000	/* TS continued */
197efi_gdt64_end:
198