]> Git Repo - J-linux.git/blob - arch/x86/boot/compressed/efi_mixed.S
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / arch / x86 / boot / compressed / efi_mixed.S
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/asm-offsets.h>
19 #include <asm/msr.h>
20 #include <asm/page_types.h>
21 #include <asm/processor-flags.h>
22 #include <asm/segment.h>
23 #include <asm/setup.h>
24
25         .code64
26         .text
27 /*
28  * When booting in 64-bit mode on 32-bit EFI firmware, startup_64_mixed_mode()
29  * is the first thing that runs after switching to long mode. Depending on
30  * whether the EFI handover protocol or the compat entry point was used to
31  * enter the kernel, it will either branch to the common 64-bit EFI stub
32  * entrypoint efi_stub_entry() directly, or via the 64-bit EFI PE/COFF
33  * entrypoint efi_pe_entry(). In the former case, the bootloader must provide a
34  * struct bootparams pointer as the third argument, so the presence of such a
35  * pointer is used to disambiguate.
36  *
37  *                                                             +--------------+
38  *  +------------------+     +------------+            +------>| efi_pe_entry |
39  *  | efi32_pe_entry   |---->|            |            |       +-----------+--+
40  *  +------------------+     |            |     +------+----------------+  |
41  *                           | startup_32 |---->| startup_64_mixed_mode |  |
42  *  +------------------+     |            |     +------+----------------+  |
43  *  | efi32_stub_entry |---->|            |            |                   |
44  *  +------------------+     +------------+            |                   |
45  *                                                     V                   |
46  *                           +------------+     +----------------+         |
47  *                           | startup_64 |<----| efi_stub_entry |<--------+
48  *                           +------------+     +----------------+
49  */
50 SYM_FUNC_START(startup_64_mixed_mode)
51         lea     efi32_boot_args(%rip), %rdx
52         mov     0(%rdx), %edi
53         mov     4(%rdx), %esi
54
55         /* Switch to the firmware's stack */
56         movl    efi32_boot_sp(%rip), %esp
57         andl    $~7, %esp
58
59 #ifdef CONFIG_EFI_HANDOVER_PROTOCOL
60         mov     8(%rdx), %edx           // saved bootparams pointer
61         test    %edx, %edx
62         jnz     efi_stub_entry
63 #endif
64         /*
65          * efi_pe_entry uses MS calling convention, which requires 32 bytes of
66          * shadow space on the stack even if all arguments are passed in
67          * registers. We also need an additional 8 bytes for the space that
68          * would be occupied by the return address, and this also results in
69          * the correct stack alignment for entry.
70          */
71         sub     $40, %rsp
72         mov     %rdi, %rcx              // MS calling convention
73         mov     %rsi, %rdx
74         jmp     efi_pe_entry
75 SYM_FUNC_END(startup_64_mixed_mode)
76
77 SYM_FUNC_START(__efi64_thunk)
78         push    %rbp
79         push    %rbx
80
81         movl    %ds, %eax
82         push    %rax
83         movl    %es, %eax
84         push    %rax
85         movl    %ss, %eax
86         push    %rax
87
88         /* Copy args passed on stack */
89         movq    0x30(%rsp), %rbp
90         movq    0x38(%rsp), %rbx
91         movq    0x40(%rsp), %rax
92
93         /*
94          * Convert x86-64 ABI params to i386 ABI
95          */
96         subq    $64, %rsp
97         movl    %esi, 0x0(%rsp)
98         movl    %edx, 0x4(%rsp)
99         movl    %ecx, 0x8(%rsp)
100         movl    %r8d, 0xc(%rsp)
101         movl    %r9d, 0x10(%rsp)
102         movl    %ebp, 0x14(%rsp)
103         movl    %ebx, 0x18(%rsp)
104         movl    %eax, 0x1c(%rsp)
105
106         leaq    0x20(%rsp), %rbx
107         sgdt    (%rbx)
108         sidt    16(%rbx)
109
110         leaq    1f(%rip), %rbp
111
112         /*
113          * Switch to IDT and GDT with 32-bit segments. These are the firmware
114          * GDT and IDT that were installed when the kernel started executing.
115          * The pointers were saved by the efi32_entry() routine below.
116          *
117          * Pass the saved DS selector to the 32-bit code, and use far return to
118          * restore the saved CS selector.
119          */
120         lidt    efi32_boot_idt(%rip)
121         lgdt    efi32_boot_gdt(%rip)
122
123         movzwl  efi32_boot_ds(%rip), %edx
124         movzwq  efi32_boot_cs(%rip), %rax
125         pushq   %rax
126         leaq    efi_enter32(%rip), %rax
127         pushq   %rax
128         lretq
129
130 1:      addq    $64, %rsp
131         movq    %rdi, %rax
132
133         pop     %rbx
134         movl    %ebx, %ss
135         pop     %rbx
136         movl    %ebx, %es
137         pop     %rbx
138         movl    %ebx, %ds
139         /* Clear out 32-bit selector from FS and GS */
140         xorl    %ebx, %ebx
141         movl    %ebx, %fs
142         movl    %ebx, %gs
143
144         pop     %rbx
145         pop     %rbp
146         RET
147 SYM_FUNC_END(__efi64_thunk)
148
149         .code32
150 #ifdef CONFIG_EFI_HANDOVER_PROTOCOL
151 SYM_FUNC_START(efi32_stub_entry)
152         call    1f
153 1:      popl    %ecx
154         leal    (efi32_boot_args - 1b)(%ecx), %ebx
155
156         /* Clear BSS */
157         xorl    %eax, %eax
158         leal    (_bss - 1b)(%ecx), %edi
159         leal    (_ebss - 1b)(%ecx), %ecx
160         subl    %edi, %ecx
161         shrl    $2, %ecx
162         cld
163         rep     stosl
164
165         add     $0x4, %esp              /* Discard return address */
166         popl    %ecx
167         popl    %edx
168         popl    %esi
169         movl    %esi, 8(%ebx)
170         jmp     efi32_entry
171 SYM_FUNC_END(efi32_stub_entry)
172 #endif
173
174 /*
175  * EFI service pointer must be in %edi.
176  *
177  * The stack should represent the 32-bit calling convention.
178  */
179 SYM_FUNC_START_LOCAL(efi_enter32)
180         /* Load firmware selector into data and stack segment registers */
181         movl    %edx, %ds
182         movl    %edx, %es
183         movl    %edx, %fs
184         movl    %edx, %gs
185         movl    %edx, %ss
186
187         /* Reload pgtables */
188         movl    %cr3, %eax
189         movl    %eax, %cr3
190
191         /* Disable paging */
192         movl    %cr0, %eax
193         btrl    $X86_CR0_PG_BIT, %eax
194         movl    %eax, %cr0
195
196         /* Disable long mode via EFER */
197         movl    $MSR_EFER, %ecx
198         rdmsr
199         btrl    $_EFER_LME, %eax
200         wrmsr
201
202         call    *%edi
203
204         /* We must preserve return value */
205         movl    %eax, %edi
206
207         /*
208          * Some firmware will return with interrupts enabled. Be sure to
209          * disable them before we switch GDTs and IDTs.
210          */
211         cli
212
213         lidtl   16(%ebx)
214         lgdtl   (%ebx)
215
216         movl    %cr4, %eax
217         btsl    $(X86_CR4_PAE_BIT), %eax
218         movl    %eax, %cr4
219
220         movl    %cr3, %eax
221         movl    %eax, %cr3
222
223         movl    $MSR_EFER, %ecx
224         rdmsr
225         btsl    $_EFER_LME, %eax
226         wrmsr
227
228         xorl    %eax, %eax
229         lldt    %ax
230
231         pushl   $__KERNEL_CS
232         pushl   %ebp
233
234         /* Enable paging */
235         movl    %cr0, %eax
236         btsl    $X86_CR0_PG_BIT, %eax
237         movl    %eax, %cr0
238         lret
239 SYM_FUNC_END(efi_enter32)
240
241 /*
242  * This is the common EFI stub entry point for mixed mode.
243  *
244  * Arguments:   %ecx    image handle
245  *              %edx    EFI system table pointer
246  *
247  * Since this is the point of no return for ordinary execution, no registers
248  * are considered live except for the function parameters. [Note that the EFI
249  * stub may still exit and return to the firmware using the Exit() EFI boot
250  * service.]
251  */
252 SYM_FUNC_START_LOCAL(efi32_entry)
253         call    1f
254 1:      pop     %ebx
255
256         /* Save firmware GDTR and code/data selectors */
257         sgdtl   (efi32_boot_gdt - 1b)(%ebx)
258         movw    %cs, (efi32_boot_cs - 1b)(%ebx)
259         movw    %ds, (efi32_boot_ds - 1b)(%ebx)
260
261         /* Store firmware IDT descriptor */
262         sidtl   (efi32_boot_idt - 1b)(%ebx)
263
264         /* Store firmware stack pointer */
265         movl    %esp, (efi32_boot_sp - 1b)(%ebx)
266
267         /* Store boot arguments */
268         leal    (efi32_boot_args - 1b)(%ebx), %ebx
269         movl    %ecx, 0(%ebx)
270         movl    %edx, 4(%ebx)
271         movb    $0x0, 12(%ebx)          // efi_is64
272
273         /*
274          * Allocate some memory for a temporary struct boot_params, which only
275          * needs the minimal pieces that startup_32() relies on.
276          */
277         subl    $PARAM_SIZE, %esp
278         movl    %esp, %esi
279         movl    $PAGE_SIZE, BP_kernel_alignment(%esi)
280         movl    $_end - 1b, BP_init_size(%esi)
281         subl    $startup_32 - 1b, BP_init_size(%esi)
282
283         /* Disable paging */
284         movl    %cr0, %eax
285         btrl    $X86_CR0_PG_BIT, %eax
286         movl    %eax, %cr0
287
288         jmp     startup_32
289 SYM_FUNC_END(efi32_entry)
290
291 /*
292  * efi_status_t efi32_pe_entry(efi_handle_t image_handle,
293  *                             efi_system_table_32_t *sys_table)
294  */
295 SYM_FUNC_START(efi32_pe_entry)
296         pushl   %ebp
297         movl    %esp, %ebp
298         pushl   %ebx                            // save callee-save registers
299         pushl   %edi
300
301         call    verify_cpu                      // check for long mode support
302         testl   %eax, %eax
303         movl    $0x80000003, %eax               // EFI_UNSUPPORTED
304         jnz     2f
305
306         movl    8(%ebp), %ecx                   // image_handle
307         movl    12(%ebp), %edx                  // sys_table
308         jmp     efi32_entry                     // pass %ecx, %edx
309                                                 // no other registers remain live
310
311 2:      popl    %edi                            // restore callee-save registers
312         popl    %ebx
313         leave
314         RET
315 SYM_FUNC_END(efi32_pe_entry)
316
317 #ifdef CONFIG_EFI_HANDOVER_PROTOCOL
318         .org    efi32_stub_entry + 0x200
319         .code64
320 SYM_FUNC_START_NOALIGN(efi64_stub_entry)
321         jmp     efi_handover_entry
322 SYM_FUNC_END(efi64_stub_entry)
323 #endif
324
325         .data
326         .balign 8
327 SYM_DATA_START_LOCAL(efi32_boot_gdt)
328         .word   0
329         .quad   0
330 SYM_DATA_END(efi32_boot_gdt)
331
332 SYM_DATA_START_LOCAL(efi32_boot_idt)
333         .word   0
334         .quad   0
335 SYM_DATA_END(efi32_boot_idt)
336
337 SYM_DATA_LOCAL(efi32_boot_cs, .word 0)
338 SYM_DATA_LOCAL(efi32_boot_ds, .word 0)
339 SYM_DATA_LOCAL(efi32_boot_sp, .long 0)
340 SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0)
341 SYM_DATA(efi_is64, .byte 1)
This page took 0.043317 seconds and 4 git commands to generate.