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