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