]> git.ipfire.org Git - thirdparty/linux.git/blob - arch/x86/boot/compressed/efi_thunk_64.S
Merge tag 'x86-fpu-2020-06-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
[thirdparty/linux.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 identity
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 SYM_FUNC_START(__efi64_thunk)
27 push %rbp
28 push %rbx
29
30 leaq 1f(%rip), %rbp
31
32 movl %ds, %eax
33 push %rax
34 movl %es, %eax
35 push %rax
36 movl %ss, %eax
37 push %rax
38
39 /*
40 * Convert x86-64 ABI params to i386 ABI
41 */
42 subq $32, %rsp
43 movl %esi, 0x0(%rsp)
44 movl %edx, 0x4(%rsp)
45 movl %ecx, 0x8(%rsp)
46 movl %r8d, 0xc(%rsp)
47 movl %r9d, 0x10(%rsp)
48
49 leaq 0x14(%rsp), %rbx
50 sgdt (%rbx)
51
52 /*
53 * Switch to gdt with 32-bit segments. This is the firmware GDT
54 * that was installed when the kernel started executing. This
55 * pointer was saved at the EFI stub entry point in head_64.S.
56 *
57 * Pass the saved DS selector to the 32-bit code, and use far return to
58 * restore the saved CS selector.
59 */
60 leaq efi32_boot_gdt(%rip), %rax
61 lgdt (%rax)
62
63 movzwl efi32_boot_ds(%rip), %edx
64 movzwq efi32_boot_cs(%rip), %rax
65 pushq %rax
66 leaq efi_enter32(%rip), %rax
67 pushq %rax
68 lretq
69
70 1: addq $32, %rsp
71 movq %rdi, %rax
72
73 pop %rbx
74 movl %ebx, %ss
75 pop %rbx
76 movl %ebx, %es
77 pop %rbx
78 movl %ebx, %ds
79 /* Clear out 32-bit selector from FS and GS */
80 xorl %ebx, %ebx
81 movl %ebx, %fs
82 movl %ebx, %gs
83
84 /*
85 * Convert 32-bit status code into 64-bit.
86 */
87 roll $1, %eax
88 rorq $1, %rax
89
90 pop %rbx
91 pop %rbp
92 ret
93 SYM_FUNC_END(__efi64_thunk)
94
95 .code32
96 /*
97 * EFI service pointer must be in %edi.
98 *
99 * The stack should represent the 32-bit calling convention.
100 */
101 SYM_FUNC_START_LOCAL(efi_enter32)
102 /* Load firmware selector into data and stack segment registers */
103 movl %edx, %ds
104 movl %edx, %es
105 movl %edx, %fs
106 movl %edx, %gs
107 movl %edx, %ss
108
109 /* Reload pgtables */
110 movl %cr3, %eax
111 movl %eax, %cr3
112
113 /* Disable paging */
114 movl %cr0, %eax
115 btrl $X86_CR0_PG_BIT, %eax
116 movl %eax, %cr0
117
118 /* Disable long mode via EFER */
119 movl $MSR_EFER, %ecx
120 rdmsr
121 btrl $_EFER_LME, %eax
122 wrmsr
123
124 call *%edi
125
126 /* We must preserve return value */
127 movl %eax, %edi
128
129 /*
130 * Some firmware will return with interrupts enabled. Be sure to
131 * disable them before we switch GDTs.
132 */
133 cli
134
135 lgdtl (%ebx)
136
137 movl %cr4, %eax
138 btsl $(X86_CR4_PAE_BIT), %eax
139 movl %eax, %cr4
140
141 movl %cr3, %eax
142 movl %eax, %cr3
143
144 movl $MSR_EFER, %ecx
145 rdmsr
146 btsl $_EFER_LME, %eax
147 wrmsr
148
149 xorl %eax, %eax
150 lldt %ax
151
152 pushl $__KERNEL_CS
153 pushl %ebp
154
155 /* Enable paging */
156 movl %cr0, %eax
157 btsl $X86_CR0_PG_BIT, %eax
158 movl %eax, %cr0
159 lret
160 SYM_FUNC_END(efi_enter32)
161
162 .data
163 .balign 8
164 SYM_DATA_START(efi32_boot_gdt)
165 .word 0
166 .quad 0
167 SYM_DATA_END(efi32_boot_gdt)
168
169 SYM_DATA_START(efi32_boot_cs)
170 .word 0
171 SYM_DATA_END(efi32_boot_cs)
172
173 SYM_DATA_START(efi32_boot_ds)
174 .word 0
175 SYM_DATA_END(efi32_boot_ds)