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