Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/efi: Add early thunk code to go from 64-bit to 32-bit

Implement the transition code to go from IA32e mode to protected mode in
the EFI boot stub. This is required to use 32-bit EFI services from a
64-bit kernel.

Since EFI boot stub is executed in an identity-mapped region, there's
not much we need to do before invoking the 32-bit EFI boot services.
However, we do reload the firmware's global descriptor table
(efi32_boot_gdt) in case things like timer events are still running in
the firmware.

Signed-off-by: Matt Fleming <matt.fleming@intel.com>

+179
+29
arch/x86/boot/compressed/efi_stub_64.S
··· 1 + #include <asm/segment.h> 2 + #include <asm/msr.h> 3 + #include <asm/processor-flags.h> 4 + 1 5 #include "../../platform/efi/efi_stub_64.S" 6 + 7 + #ifdef CONFIG_EFI_MIXED 8 + .code64 9 + .text 10 + ENTRY(efi64_thunk) 11 + push %rbp 12 + push %rbx 13 + 14 + subq $16, %rsp 15 + leaq efi_exit32(%rip), %rax 16 + movl %eax, 8(%rsp) 17 + leaq efi_gdt64(%rip), %rax 18 + movl %eax, 4(%rsp) 19 + movl %eax, 2(%rax) /* Fixup the gdt base address */ 20 + leaq efi32_boot_gdt(%rip), %rax 21 + movl %eax, (%rsp) 22 + 23 + call __efi64_thunk 24 + 25 + addq $16, %rsp 26 + pop %rbx 27 + pop %rbp 28 + ret 29 + ENDPROC(efi64_thunk) 30 + #endif /* CONFIG_EFI_MIXED */
+150
arch/x86/platform/efi/efi_stub_64.S
··· 7 7 */ 8 8 9 9 #include <linux/linkage.h> 10 + #include <asm/segment.h> 11 + #include <asm/msr.h> 12 + #include <asm/processor-flags.h> 13 + #include <asm/page_types.h> 10 14 11 15 #define SAVE_XMM \ 12 16 mov %rsp, %rax; \ ··· 167 163 RESTORE_XMM 168 164 ret 169 165 ENDPROC(efi_call6) 166 + 167 + #ifdef CONFIG_EFI_MIXED 168 + 169 + /* 170 + * We run this function from the 1:1 mapping. 171 + * 172 + * This function must be invoked with a 1:1 mapped stack. 173 + */ 174 + ENTRY(__efi64_thunk) 175 + subq $32, %rsp 176 + movl %esi, 0x0(%rsp) 177 + movl %edx, 0x4(%rsp) 178 + movl %ecx, 0x8(%rsp) 179 + movq %r8, %rsi 180 + movl %esi, 0xc(%rsp) 181 + movq %r9, %rsi 182 + movl %esi, 0x10(%rsp) 183 + 184 + sgdt save_gdt(%rip) 185 + 186 + leaq 1f(%rip), %rbx 187 + movq %rbx, func_rt_ptr(%rip) 188 + 189 + /* Switch to gdt with 32-bit segments */ 190 + movl 40(%rsp), %eax 191 + lgdt (%rax) 192 + 193 + leaq efi_enter32(%rip), %rax 194 + pushq $__KERNEL_CS 195 + pushq %rax 196 + lretq 197 + 198 + 1: addq $32, %rsp 199 + 200 + lgdt save_gdt(%rip) 201 + 202 + /* 203 + * Convert 32-bit status code into 64-bit. 204 + */ 205 + test %rax, %rax 206 + jz 1f 207 + movl %eax, %ecx 208 + andl $0x0fffffff, %ecx 209 + andl $0xf0000000, %eax 210 + shl $32, %rax 211 + or %rcx, %rax 212 + 1: 213 + ret 214 + ENDPROC(__efi64_thunk) 215 + 216 + ENTRY(efi_exit32) 217 + xorq %rax, %rax 218 + movl %eax, %ds 219 + movl %eax, %es 220 + movl %eax, %ss 221 + 222 + movq func_rt_ptr(%rip), %rax 223 + push %rax 224 + mov %rdi, %rax 225 + ret 226 + ENDPROC(efi_exit32) 227 + 228 + .code32 229 + /* 230 + * EFI service pointer must be in %edi. 231 + * 232 + * The stack should represent the 32-bit calling convention. 233 + */ 234 + ENTRY(efi_enter32) 235 + movl $__KERNEL_DS, %eax 236 + movl %eax, %ds 237 + movl %eax, %es 238 + movl %eax, %ss 239 + 240 + /* Reload pgtables */ 241 + movl %cr3, %eax 242 + movl %eax, %cr3 243 + 244 + /* Disable paging */ 245 + movl %cr0, %eax 246 + btrl $X86_CR0_PG_BIT, %eax 247 + movl %eax, %cr0 248 + 249 + /* Disable long mode via EFER */ 250 + movl $MSR_EFER, %ecx 251 + rdmsr 252 + btrl $_EFER_LME, %eax 253 + wrmsr 254 + 255 + call *%edi 256 + 257 + /* We must preserve return value */ 258 + movl %eax, %edi 259 + 260 + movl 44(%esp), %eax 261 + movl %eax, 2(%eax) 262 + lgdtl (%eax) 263 + 264 + movl %cr4, %eax 265 + btsl $(X86_CR4_PAE_BIT), %eax 266 + movl %eax, %cr4 267 + 268 + movl %cr3, %eax 269 + movl %eax, %cr3 270 + 271 + movl $MSR_EFER, %ecx 272 + rdmsr 273 + btsl $_EFER_LME, %eax 274 + wrmsr 275 + 276 + xorl %eax, %eax 277 + lldt %ax 278 + 279 + movl 48(%esp), %eax 280 + pushl $__KERNEL_CS 281 + pushl %eax 282 + 283 + /* Enable paging */ 284 + movl %cr0, %eax 285 + btsl $X86_CR0_PG_BIT, %eax 286 + movl %eax, %cr0 287 + lret 288 + ENDPROC(efi_enter32) 289 + 290 + .data 291 + .balign 8 292 + .global efi32_boot_gdt 293 + efi32_boot_gdt: .word 0 294 + .quad 0 295 + 296 + save_gdt: .word 0 297 + .quad 0 298 + func_rt_ptr: .quad 0 299 + 300 + .global efi_gdt64 301 + efi_gdt64: 302 + .word efi_gdt64_end - efi_gdt64 303 + .long 0 /* Filled out by user */ 304 + .word 0 305 + .quad 0x0000000000000000 /* NULL descriptor */ 306 + .quad 0x00af9a000000ffff /* __KERNEL_CS */ 307 + .quad 0x00cf92000000ffff /* __KERNEL_DS */ 308 + .quad 0x0080890000000000 /* TS descriptor */ 309 + .quad 0x0000000000000000 /* TS continued */ 310 + efi_gdt64_end: 311 + #endif /* CONFIG_EFI_MIXED */ 170 312 171 313 .data 172 314 ENTRY(efi_scratch)