Linux-libre 5.3.12-gnu
[librecmc/linux-libre.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 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
26 ENTRY(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
74 1:      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
95 1:
96         addq    $8, %rsp
97         pop     %rbx
98         pop     %rbp
99         ret
100 ENDPROC(efi64_thunk)
101
102 ENTRY(efi_exit32)
103         movq    func_rt_ptr(%rip), %rax
104         push    %rax
105         mov     %rdi, %rax
106         ret
107 ENDPROC(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  */
115 ENTRY(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
175 ENDPROC(efi_enter32)
176
177         .data
178         .balign 8
179         .global efi32_boot_gdt
180 efi32_boot_gdt: .word   0
181                 .quad   0
182
183 save_gdt:       .word   0
184                 .quad   0
185 func_rt_ptr:    .quad   0
186
187         .global efi_gdt64
188 efi_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 */
197 efi_gdt64_end: