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