GNU Linux-libre 5.19-rc6-gnu
[releases.git] / arch / x86 / boot / compressed / efi_thunk_64.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/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 SYM_FUNC_START(__efi64_thunk)
26         push    %rbp
27         push    %rbx
28
29         movl    %ds, %eax
30         push    %rax
31         movl    %es, %eax
32         push    %rax
33         movl    %ss, %eax
34         push    %rax
35
36         /* Copy args passed on stack */
37         movq    0x30(%rsp), %rbp
38         movq    0x38(%rsp), %rbx
39         movq    0x40(%rsp), %rax
40
41         /*
42          * Convert x86-64 ABI params to i386 ABI
43          */
44         subq    $64, %rsp
45         movl    %esi, 0x0(%rsp)
46         movl    %edx, 0x4(%rsp)
47         movl    %ecx, 0x8(%rsp)
48         movl    %r8d, 0xc(%rsp)
49         movl    %r9d, 0x10(%rsp)
50         movl    %ebp, 0x14(%rsp)
51         movl    %ebx, 0x18(%rsp)
52         movl    %eax, 0x1c(%rsp)
53
54         leaq    0x20(%rsp), %rbx
55         sgdt    (%rbx)
56
57         addq    $16, %rbx
58         sidt    (%rbx)
59
60         leaq    1f(%rip), %rbp
61
62         /*
63          * Switch to IDT and GDT with 32-bit segments. This is the firmware GDT
64          * and IDT that was installed when the kernel started executing. The
65          * pointers were saved at the EFI stub entry point in head_64.S.
66          *
67          * Pass the saved DS selector to the 32-bit code, and use far return to
68          * restore the saved CS selector.
69          */
70         leaq    efi32_boot_idt(%rip), %rax
71         lidt    (%rax)
72         leaq    efi32_boot_gdt(%rip), %rax
73         lgdt    (%rax)
74
75         movzwl  efi32_boot_ds(%rip), %edx
76         movzwq  efi32_boot_cs(%rip), %rax
77         pushq   %rax
78         leaq    efi_enter32(%rip), %rax
79         pushq   %rax
80         lretq
81
82 1:      addq    $64, %rsp
83         movq    %rdi, %rax
84
85         pop     %rbx
86         movl    %ebx, %ss
87         pop     %rbx
88         movl    %ebx, %es
89         pop     %rbx
90         movl    %ebx, %ds
91         /* Clear out 32-bit selector from FS and GS */
92         xorl    %ebx, %ebx
93         movl    %ebx, %fs
94         movl    %ebx, %gs
95
96         /*
97          * Convert 32-bit status code into 64-bit.
98          */
99         roll    $1, %eax
100         rorq    $1, %rax
101
102         pop     %rbx
103         pop     %rbp
104         RET
105 SYM_FUNC_END(__efi64_thunk)
106
107         .code32
108 /*
109  * EFI service pointer must be in %edi.
110  *
111  * The stack should represent the 32-bit calling convention.
112  */
113 SYM_FUNC_START_LOCAL(efi_enter32)
114         /* Load firmware selector into data and stack segment registers */
115         movl    %edx, %ds
116         movl    %edx, %es
117         movl    %edx, %fs
118         movl    %edx, %gs
119         movl    %edx, %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 and IDTs.
144          */
145         cli
146
147         lidtl   (%ebx)
148         subl    $16, %ebx
149
150         lgdtl   (%ebx)
151
152         movl    %cr4, %eax
153         btsl    $(X86_CR4_PAE_BIT), %eax
154         movl    %eax, %cr4
155
156         movl    %cr3, %eax
157         movl    %eax, %cr3
158
159         movl    $MSR_EFER, %ecx
160         rdmsr
161         btsl    $_EFER_LME, %eax
162         wrmsr
163
164         xorl    %eax, %eax
165         lldt    %ax
166
167         pushl   $__KERNEL_CS
168         pushl   %ebp
169
170         /* Enable paging */
171         movl    %cr0, %eax
172         btsl    $X86_CR0_PG_BIT, %eax
173         movl    %eax, %cr0
174         lret
175 SYM_FUNC_END(efi_enter32)
176
177         .data
178         .balign 8
179 SYM_DATA_START(efi32_boot_gdt)
180         .word   0
181         .quad   0
182 SYM_DATA_END(efi32_boot_gdt)
183
184 SYM_DATA_START(efi32_boot_idt)
185         .word   0
186         .quad   0
187 SYM_DATA_END(efi32_boot_idt)
188
189 SYM_DATA_START(efi32_boot_cs)
190         .word   0
191 SYM_DATA_END(efi32_boot_cs)
192
193 SYM_DATA_START(efi32_boot_ds)
194         .word   0
195 SYM_DATA_END(efi32_boot_ds)