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 and IDT before we make EFI service 9 * calls. 10 * 11 * On the plus side, we don't have to worry about mangling 64-bit 12 * addresses into 32-bits because we're executing with an identity 13 * mapped pagetable and haven't transitioned to 64-bit virtual addresses 14 * yet. 15 */ 16 17#include <linux/linkage.h> 18#include <asm/desc_defs.h> 19#include <asm/msr.h> 20#include <asm/page_types.h> 21#include <asm/pgtable_types.h> 22#include <asm/processor-flags.h> 23#include <asm/segment.h> 24 25 .text 26 .code32 27#ifdef CONFIG_EFI_HANDOVER_PROTOCOL 28SYM_FUNC_START(efi32_stub_entry) 29 call 1f 301: popl %ecx 31 32 /* Clear BSS */ 33 xorl %eax, %eax 34 leal (_bss - 1b)(%ecx), %edi 35 leal (_ebss - 1b)(%ecx), %ecx 36 subl %edi, %ecx 37 shrl $2, %ecx 38 cld 39 rep stosl 40 41 add $0x4, %esp /* Discard return address */ 42 movl 8(%esp), %ebx /* struct boot_params pointer */ 43 jmp efi32_startup 44SYM_FUNC_END(efi32_stub_entry) 45#endif 46 47/* 48 * Called using a far call from __efi64_thunk() below, using the x86_64 SysV 49 * ABI (except for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are 50 * used instead). EBP+16 points to the arguments passed via the stack. 51 * 52 * The first argument (EDI) is a pointer to the boot service or protocol, to 53 * which the remaining arguments are passed, each truncated to 32 bits. 54 */ 55SYM_FUNC_START_LOCAL(efi_enter32) 56 /* 57 * Convert x86-64 SysV ABI params to i386 ABI 58 */ 59 pushl 32(%ebp) /* Up to 3 args passed via the stack */ 60 pushl 24(%ebp) 61 pushl 16(%ebp) 62 pushl %ebx /* R9 */ 63 pushl %eax /* R8 */ 64 pushl %ecx 65 pushl %edx 66 pushl %esi 67 68 /* Disable paging */ 69 movl %cr0, %eax 70 btrl $X86_CR0_PG_BIT, %eax 71 movl %eax, %cr0 72 73 /* Disable long mode via EFER */ 74 movl $MSR_EFER, %ecx 75 rdmsr 76 btrl $_EFER_LME, %eax 77 wrmsr 78 79 call *%edi 80 81 /* We must preserve return value */ 82 movl %eax, %edi 83 84 call efi32_enable_long_mode 85 86 addl $32, %esp 87 movl %edi, %eax 88 lret 89SYM_FUNC_END(efi_enter32) 90 91 .code64 92SYM_FUNC_START(__efi64_thunk) 93 push %rbp 94 movl %esp, %ebp 95 push %rbx 96 97 /* Move args #5 and #6 into 32-bit accessible registers */ 98 movl %r8d, %eax 99 movl %r9d, %ebx 100 101 lcalll *efi32_call(%rip) 102 103 pop %rbx 104 pop %rbp 105 RET 106SYM_FUNC_END(__efi64_thunk) 107 108 .code32 109SYM_FUNC_START_LOCAL(efi32_enable_long_mode) 110 movl %cr4, %eax 111 btsl $(X86_CR4_PAE_BIT), %eax 112 movl %eax, %cr4 113 114 movl $MSR_EFER, %ecx 115 rdmsr 116 btsl $_EFER_LME, %eax 117 wrmsr 118 119 /* Disable interrupts - the firmware's IDT does not work in long mode */ 120 cli 121 122 /* Enable paging */ 123 movl %cr0, %eax 124 btsl $X86_CR0_PG_BIT, %eax 125 movl %eax, %cr0 126 ret 127SYM_FUNC_END(efi32_enable_long_mode) 128 129/* 130 * This is the common EFI stub entry point for mixed mode. It sets up the GDT 131 * and page tables needed for 64-bit execution, after which it calls the 132 * common 64-bit EFI entrypoint efi_stub_entry(). 133 * 134 * Arguments: 0(%esp) image handle 135 * 4(%esp) EFI system table pointer 136 * %ebx struct boot_params pointer (or NULL) 137 * 138 * Since this is the point of no return for ordinary execution, no registers 139 * are considered live except for the function parameters. [Note that the EFI 140 * stub may still exit and return to the firmware using the Exit() EFI boot 141 * service.] 142 */ 143SYM_FUNC_START_LOCAL(efi32_startup) 144 movl %esp, %ebp 145 146 subl $8, %esp 147 sgdtl (%esp) /* Save GDT descriptor to the stack */ 148 movl 2(%esp), %esi /* Existing GDT pointer */ 149 movzwl (%esp), %ecx /* Existing GDT limit */ 150 inc %ecx /* Existing GDT size */ 151 andl $~7, %ecx /* Ensure size is multiple of 8 */ 152 153 subl %ecx, %esp /* Allocate new GDT */ 154 andl $~15, %esp /* Realign the stack */ 155 movl %esp, %edi /* New GDT address */ 156 leal 7(%ecx), %eax /* New GDT limit */ 157 pushw %cx /* Push 64-bit CS (for LJMP below) */ 158 pushl %edi /* Push new GDT address */ 159 pushw %ax /* Push new GDT limit */ 160 161 /* Copy GDT to the stack and add a 64-bit code segment at the end */ 162 movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) & 0xffffffff, (%edi,%ecx) 163 movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) >> 32, 4(%edi,%ecx) 164 shrl $2, %ecx 165 cld 166 rep movsl /* Copy the firmware GDT */ 167 lgdtl (%esp) /* Switch to the new GDT */ 168 169 call 1f 1701: pop %edi 171 172 /* Record mixed mode entry */ 173 movb $0x0, (efi_is64 - 1b)(%edi) 174 175 /* Set up indirect far call to re-enter 32-bit mode */ 176 leal (efi32_call - 1b)(%edi), %eax 177 addl %eax, (%eax) 178 movw %cs, 4(%eax) 179 180 /* Disable paging */ 181 movl %cr0, %eax 182 btrl $X86_CR0_PG_BIT, %eax 183 movl %eax, %cr0 184 185 /* Set up 1:1 mapping */ 186 leal (pte - 1b)(%edi), %eax 187 movl $_PAGE_PRESENT | _PAGE_RW | _PAGE_PSE, %ecx 188 leal (_PAGE_PRESENT | _PAGE_RW)(%eax), %edx 1892: movl %ecx, (%eax) 190 addl $8, %eax 191 addl $PMD_SIZE, %ecx 192 jnc 2b 193 194 movl $PAGE_SIZE, %ecx 195 .irpc l, 0123 196 movl %edx, \l * 8(%eax) 197 addl %ecx, %edx 198 .endr 199 addl %ecx, %eax 200 movl %edx, (%eax) 201 movl %eax, %cr3 202 203 call efi32_enable_long_mode 204 205 /* Set up far jump to 64-bit mode (CS is already on the stack) */ 206 leal (efi_stub_entry - 1b)(%edi), %eax 207 movl %eax, 2(%esp) 208 209 movl 0(%ebp), %edi 210 movl 4(%ebp), %esi 211 movl %ebx, %edx 212 ljmpl *2(%esp) 213SYM_FUNC_END(efi32_startup) 214 215/* 216 * efi_status_t efi32_pe_entry(efi_handle_t image_handle, 217 * efi_system_table_32_t *sys_table) 218 */ 219SYM_FUNC_START(efi32_pe_entry) 220 pushl %ebx // save callee-save registers 221 222 /* Check whether the CPU supports long mode */ 223 movl $0x80000001, %eax // assume extended info support 224 cpuid 225 btl $29, %edx // check long mode bit 226 jnc 1f 227 leal 8(%esp), %esp // preserve stack alignment 228 xor %ebx, %ebx // no struct boot_params pointer 229 jmp efi32_startup // only ESP and EBX remain live 2301: movl $0x80000003, %eax // EFI_UNSUPPORTED 231 popl %ebx 232 RET 233SYM_FUNC_END(efi32_pe_entry) 234 235#ifdef CONFIG_EFI_HANDOVER_PROTOCOL 236 .org efi32_stub_entry + 0x200 237 .code64 238SYM_FUNC_START_NOALIGN(efi64_stub_entry) 239 jmp efi_handover_entry 240SYM_FUNC_END(efi64_stub_entry) 241#endif 242 243 .data 244 .balign 8 245SYM_DATA_START_LOCAL(efi32_call) 246 .long efi_enter32 - . 247 .word 0x0 248SYM_DATA_END(efi32_call) 249SYM_DATA(efi_is64, .byte 1) 250 251 .bss 252 .balign PAGE_SIZE 253SYM_DATA_LOCAL(pte, .fill 6 * PAGE_SIZE, 1, 0) 254