1cb8bda8aSArd Biesheuvel/* SPDX-License-Identifier: GPL-2.0 */ 2cb8bda8aSArd Biesheuvel/* 3cb8bda8aSArd Biesheuvel * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming 4cb8bda8aSArd Biesheuvel * 5cb8bda8aSArd Biesheuvel * Early support for invoking 32-bit EFI services from a 64-bit kernel. 6cb8bda8aSArd Biesheuvel * 7cb8bda8aSArd Biesheuvel * Because this thunking occurs before ExitBootServices() we have to 8cb8bda8aSArd Biesheuvel * restore the firmware's 32-bit GDT and IDT before we make EFI service 9cb8bda8aSArd Biesheuvel * calls. 10cb8bda8aSArd Biesheuvel * 11cb8bda8aSArd Biesheuvel * On the plus side, we don't have to worry about mangling 64-bit 12cb8bda8aSArd Biesheuvel * addresses into 32-bits because we're executing with an identity 13cb8bda8aSArd Biesheuvel * mapped pagetable and haven't transitioned to 64-bit virtual addresses 14cb8bda8aSArd Biesheuvel * yet. 15cb8bda8aSArd Biesheuvel */ 16cb8bda8aSArd Biesheuvel 17cb8bda8aSArd Biesheuvel#include <linux/linkage.h> 18*01666eecSArd Biesheuvel#include <asm/asm-offsets.h> 19cb8bda8aSArd Biesheuvel#include <asm/msr.h> 20cb8bda8aSArd Biesheuvel#include <asm/page_types.h> 21cb8bda8aSArd Biesheuvel#include <asm/processor-flags.h> 22cb8bda8aSArd Biesheuvel#include <asm/segment.h> 23*01666eecSArd Biesheuvel#include <asm/setup.h> 24cb8bda8aSArd Biesheuvel 25cb8bda8aSArd Biesheuvel .code64 26cb8bda8aSArd Biesheuvel .text 275c3a85f3SArd Biesheuvel/* 285c3a85f3SArd Biesheuvel * When booting in 64-bit mode on 32-bit EFI firmware, startup_64_mixed_mode() 295c3a85f3SArd Biesheuvel * is the first thing that runs after switching to long mode. Depending on 305c3a85f3SArd Biesheuvel * whether the EFI handover protocol or the compat entry point was used to 31df9215f1SArd Biesheuvel * enter the kernel, it will either branch to the common 64-bit EFI stub 32df9215f1SArd Biesheuvel * entrypoint efi_stub_entry() directly, or via the 64-bit EFI PE/COFF 335c3a85f3SArd Biesheuvel * entrypoint efi_pe_entry(). In the former case, the bootloader must provide a 345c3a85f3SArd Biesheuvel * struct bootparams pointer as the third argument, so the presence of such a 355c3a85f3SArd Biesheuvel * pointer is used to disambiguate. 365c3a85f3SArd Biesheuvel * 375c3a85f3SArd Biesheuvel * +--------------+ 385c3a85f3SArd Biesheuvel * +------------------+ +------------+ +------>| efi_pe_entry | 395c3a85f3SArd Biesheuvel * | efi32_pe_entry |---->| | | +-----------+--+ 405c3a85f3SArd Biesheuvel * +------------------+ | | +------+----------------+ | 415c3a85f3SArd Biesheuvel * | startup_32 |---->| startup_64_mixed_mode | | 42df9215f1SArd Biesheuvel * +------------------+ | | +------+----------------+ | 43df9215f1SArd Biesheuvel * | efi32_stub_entry |---->| | | | 44df9215f1SArd Biesheuvel * +------------------+ +------------+ | | 45df9215f1SArd Biesheuvel * V | 46df9215f1SArd Biesheuvel * +------------+ +----------------+ | 47df9215f1SArd Biesheuvel * | startup_64 |<----| efi_stub_entry |<--------+ 48df9215f1SArd Biesheuvel * +------------+ +----------------+ 495c3a85f3SArd Biesheuvel */ 505c3a85f3SArd BiesheuvelSYM_FUNC_START(startup_64_mixed_mode) 515c3a85f3SArd Biesheuvel lea efi32_boot_args(%rip), %rdx 525c3a85f3SArd Biesheuvel mov 0(%rdx), %edi 535c3a85f3SArd Biesheuvel mov 4(%rdx), %esi 5493077506SArd Biesheuvel 5593077506SArd Biesheuvel /* Switch to the firmware's stack */ 5693077506SArd Biesheuvel movl efi32_boot_sp(%rip), %esp 5793077506SArd Biesheuvel andl $~7, %esp 5893077506SArd Biesheuvel 59df9215f1SArd Biesheuvel#ifdef CONFIG_EFI_HANDOVER_PROTOCOL 605c3a85f3SArd Biesheuvel mov 8(%rdx), %edx // saved bootparams pointer 615c3a85f3SArd Biesheuvel test %edx, %edx 62df9215f1SArd Biesheuvel jnz efi_stub_entry 63df9215f1SArd Biesheuvel#endif 645c3a85f3SArd Biesheuvel /* 655c3a85f3SArd Biesheuvel * efi_pe_entry uses MS calling convention, which requires 32 bytes of 665c3a85f3SArd Biesheuvel * shadow space on the stack even if all arguments are passed in 675c3a85f3SArd Biesheuvel * registers. We also need an additional 8 bytes for the space that 685c3a85f3SArd Biesheuvel * would be occupied by the return address, and this also results in 695c3a85f3SArd Biesheuvel * the correct stack alignment for entry. 705c3a85f3SArd Biesheuvel */ 715c3a85f3SArd Biesheuvel sub $40, %rsp 725c3a85f3SArd Biesheuvel mov %rdi, %rcx // MS calling convention 735c3a85f3SArd Biesheuvel mov %rsi, %rdx 745c3a85f3SArd Biesheuvel jmp efi_pe_entry 755c3a85f3SArd BiesheuvelSYM_FUNC_END(startup_64_mixed_mode) 765c3a85f3SArd Biesheuvel 77cb8bda8aSArd BiesheuvelSYM_FUNC_START(__efi64_thunk) 78cb8bda8aSArd Biesheuvel push %rbp 79cb8bda8aSArd Biesheuvel push %rbx 80cb8bda8aSArd Biesheuvel 81cb8bda8aSArd Biesheuvel movl %ds, %eax 82cb8bda8aSArd Biesheuvel push %rax 83cb8bda8aSArd Biesheuvel movl %es, %eax 84cb8bda8aSArd Biesheuvel push %rax 85cb8bda8aSArd Biesheuvel movl %ss, %eax 86cb8bda8aSArd Biesheuvel push %rax 87cb8bda8aSArd Biesheuvel 88cb8bda8aSArd Biesheuvel /* Copy args passed on stack */ 89cb8bda8aSArd Biesheuvel movq 0x30(%rsp), %rbp 90cb8bda8aSArd Biesheuvel movq 0x38(%rsp), %rbx 91cb8bda8aSArd Biesheuvel movq 0x40(%rsp), %rax 92cb8bda8aSArd Biesheuvel 93cb8bda8aSArd Biesheuvel /* 94cb8bda8aSArd Biesheuvel * Convert x86-64 ABI params to i386 ABI 95cb8bda8aSArd Biesheuvel */ 96cb8bda8aSArd Biesheuvel subq $64, %rsp 97cb8bda8aSArd Biesheuvel movl %esi, 0x0(%rsp) 98cb8bda8aSArd Biesheuvel movl %edx, 0x4(%rsp) 99cb8bda8aSArd Biesheuvel movl %ecx, 0x8(%rsp) 100cb8bda8aSArd Biesheuvel movl %r8d, 0xc(%rsp) 101cb8bda8aSArd Biesheuvel movl %r9d, 0x10(%rsp) 102cb8bda8aSArd Biesheuvel movl %ebp, 0x14(%rsp) 103cb8bda8aSArd Biesheuvel movl %ebx, 0x18(%rsp) 104cb8bda8aSArd Biesheuvel movl %eax, 0x1c(%rsp) 105cb8bda8aSArd Biesheuvel 106cb8bda8aSArd Biesheuvel leaq 0x20(%rsp), %rbx 107cb8bda8aSArd Biesheuvel sgdt (%rbx) 108630f337fSArd Biesheuvel sidt 16(%rbx) 109cb8bda8aSArd Biesheuvel 110cb8bda8aSArd Biesheuvel leaq 1f(%rip), %rbp 111cb8bda8aSArd Biesheuvel 112cb8bda8aSArd Biesheuvel /* 113630f337fSArd Biesheuvel * Switch to IDT and GDT with 32-bit segments. These are the firmware 114630f337fSArd Biesheuvel * GDT and IDT that were installed when the kernel started executing. 115630f337fSArd Biesheuvel * The pointers were saved by the efi32_entry() routine below. 116cb8bda8aSArd Biesheuvel * 117cb8bda8aSArd Biesheuvel * Pass the saved DS selector to the 32-bit code, and use far return to 118cb8bda8aSArd Biesheuvel * restore the saved CS selector. 119cb8bda8aSArd Biesheuvel */ 120630f337fSArd Biesheuvel lidt efi32_boot_idt(%rip) 121630f337fSArd Biesheuvel lgdt efi32_boot_gdt(%rip) 122cb8bda8aSArd Biesheuvel 123cb8bda8aSArd Biesheuvel movzwl efi32_boot_ds(%rip), %edx 124cb8bda8aSArd Biesheuvel movzwq efi32_boot_cs(%rip), %rax 125cb8bda8aSArd Biesheuvel pushq %rax 126cb8bda8aSArd Biesheuvel leaq efi_enter32(%rip), %rax 127cb8bda8aSArd Biesheuvel pushq %rax 128cb8bda8aSArd Biesheuvel lretq 129cb8bda8aSArd Biesheuvel 130cb8bda8aSArd Biesheuvel1: addq $64, %rsp 131cb8bda8aSArd Biesheuvel movq %rdi, %rax 132cb8bda8aSArd Biesheuvel 133cb8bda8aSArd Biesheuvel pop %rbx 134cb8bda8aSArd Biesheuvel movl %ebx, %ss 135cb8bda8aSArd Biesheuvel pop %rbx 136cb8bda8aSArd Biesheuvel movl %ebx, %es 137cb8bda8aSArd Biesheuvel pop %rbx 138cb8bda8aSArd Biesheuvel movl %ebx, %ds 139cb8bda8aSArd Biesheuvel /* Clear out 32-bit selector from FS and GS */ 140cb8bda8aSArd Biesheuvel xorl %ebx, %ebx 141cb8bda8aSArd Biesheuvel movl %ebx, %fs 142cb8bda8aSArd Biesheuvel movl %ebx, %gs 143cb8bda8aSArd Biesheuvel 144cb8bda8aSArd Biesheuvel pop %rbx 145cb8bda8aSArd Biesheuvel pop %rbp 146cb8bda8aSArd Biesheuvel RET 147cb8bda8aSArd BiesheuvelSYM_FUNC_END(__efi64_thunk) 148cb8bda8aSArd Biesheuvel 149cb8bda8aSArd Biesheuvel .code32 15012792064SArd Biesheuvel#ifdef CONFIG_EFI_HANDOVER_PROTOCOL 15112792064SArd BiesheuvelSYM_FUNC_START(efi32_stub_entry) 152d7156b98SArd Biesheuvel call 1f 153d7156b98SArd Biesheuvel1: popl %ecx 154*01666eecSArd Biesheuvel leal (efi32_boot_args - 1b)(%ecx), %ebx 155d7156b98SArd Biesheuvel 156d7156b98SArd Biesheuvel /* Clear BSS */ 157d7156b98SArd Biesheuvel xorl %eax, %eax 158d7156b98SArd Biesheuvel leal (_bss - 1b)(%ecx), %edi 159d7156b98SArd Biesheuvel leal (_ebss - 1b)(%ecx), %ecx 160d7156b98SArd Biesheuvel subl %edi, %ecx 161d7156b98SArd Biesheuvel shrl $2, %ecx 162d7156b98SArd Biesheuvel cld 163d7156b98SArd Biesheuvel rep stosl 164d7156b98SArd Biesheuvel 16512792064SArd Biesheuvel add $0x4, %esp /* Discard return address */ 16612792064SArd Biesheuvel popl %ecx 16712792064SArd Biesheuvel popl %edx 16812792064SArd Biesheuvel popl %esi 169*01666eecSArd Biesheuvel movl %esi, 8(%ebx) 17012792064SArd Biesheuvel jmp efi32_entry 17112792064SArd BiesheuvelSYM_FUNC_END(efi32_stub_entry) 17212792064SArd Biesheuvel#endif 17312792064SArd Biesheuvel 174cb8bda8aSArd Biesheuvel/* 175cb8bda8aSArd Biesheuvel * EFI service pointer must be in %edi. 176cb8bda8aSArd Biesheuvel * 177cb8bda8aSArd Biesheuvel * The stack should represent the 32-bit calling convention. 178cb8bda8aSArd Biesheuvel */ 179cb8bda8aSArd BiesheuvelSYM_FUNC_START_LOCAL(efi_enter32) 180cb8bda8aSArd Biesheuvel /* Load firmware selector into data and stack segment registers */ 181cb8bda8aSArd Biesheuvel movl %edx, %ds 182cb8bda8aSArd Biesheuvel movl %edx, %es 183cb8bda8aSArd Biesheuvel movl %edx, %fs 184cb8bda8aSArd Biesheuvel movl %edx, %gs 185cb8bda8aSArd Biesheuvel movl %edx, %ss 186cb8bda8aSArd Biesheuvel 187cb8bda8aSArd Biesheuvel /* Reload pgtables */ 188cb8bda8aSArd Biesheuvel movl %cr3, %eax 189cb8bda8aSArd Biesheuvel movl %eax, %cr3 190cb8bda8aSArd Biesheuvel 191cb8bda8aSArd Biesheuvel /* Disable paging */ 192cb8bda8aSArd Biesheuvel movl %cr0, %eax 193cb8bda8aSArd Biesheuvel btrl $X86_CR0_PG_BIT, %eax 194cb8bda8aSArd Biesheuvel movl %eax, %cr0 195cb8bda8aSArd Biesheuvel 196cb8bda8aSArd Biesheuvel /* Disable long mode via EFER */ 197cb8bda8aSArd Biesheuvel movl $MSR_EFER, %ecx 198cb8bda8aSArd Biesheuvel rdmsr 199cb8bda8aSArd Biesheuvel btrl $_EFER_LME, %eax 200cb8bda8aSArd Biesheuvel wrmsr 201cb8bda8aSArd Biesheuvel 202cb8bda8aSArd Biesheuvel call *%edi 203cb8bda8aSArd Biesheuvel 204cb8bda8aSArd Biesheuvel /* We must preserve return value */ 205cb8bda8aSArd Biesheuvel movl %eax, %edi 206cb8bda8aSArd Biesheuvel 207cb8bda8aSArd Biesheuvel /* 208cb8bda8aSArd Biesheuvel * Some firmware will return with interrupts enabled. Be sure to 209cb8bda8aSArd Biesheuvel * disable them before we switch GDTs and IDTs. 210cb8bda8aSArd Biesheuvel */ 211cb8bda8aSArd Biesheuvel cli 212cb8bda8aSArd Biesheuvel 213630f337fSArd Biesheuvel lidtl 16(%ebx) 214cb8bda8aSArd Biesheuvel lgdtl (%ebx) 215cb8bda8aSArd Biesheuvel 216cb8bda8aSArd Biesheuvel movl %cr4, %eax 217cb8bda8aSArd Biesheuvel btsl $(X86_CR4_PAE_BIT), %eax 218cb8bda8aSArd Biesheuvel movl %eax, %cr4 219cb8bda8aSArd Biesheuvel 220cb8bda8aSArd Biesheuvel movl %cr3, %eax 221cb8bda8aSArd Biesheuvel movl %eax, %cr3 222cb8bda8aSArd Biesheuvel 223cb8bda8aSArd Biesheuvel movl $MSR_EFER, %ecx 224cb8bda8aSArd Biesheuvel rdmsr 225cb8bda8aSArd Biesheuvel btsl $_EFER_LME, %eax 226cb8bda8aSArd Biesheuvel wrmsr 227cb8bda8aSArd Biesheuvel 228cb8bda8aSArd Biesheuvel xorl %eax, %eax 229cb8bda8aSArd Biesheuvel lldt %ax 230cb8bda8aSArd Biesheuvel 231cb8bda8aSArd Biesheuvel pushl $__KERNEL_CS 232cb8bda8aSArd Biesheuvel pushl %ebp 233cb8bda8aSArd Biesheuvel 234cb8bda8aSArd Biesheuvel /* Enable paging */ 235cb8bda8aSArd Biesheuvel movl %cr0, %eax 236cb8bda8aSArd Biesheuvel btsl $X86_CR0_PG_BIT, %eax 237cb8bda8aSArd Biesheuvel movl %eax, %cr0 238cb8bda8aSArd Biesheuvel lret 239cb8bda8aSArd BiesheuvelSYM_FUNC_END(efi_enter32) 240cb8bda8aSArd Biesheuvel 24173a6dec8SArd Biesheuvel/* 24273a6dec8SArd Biesheuvel * This is the common EFI stub entry point for mixed mode. 24373a6dec8SArd Biesheuvel * 24473a6dec8SArd Biesheuvel * Arguments: %ecx image handle 24573a6dec8SArd Biesheuvel * %edx EFI system table pointer 24673a6dec8SArd Biesheuvel * 24773a6dec8SArd Biesheuvel * Since this is the point of no return for ordinary execution, no registers 24873a6dec8SArd Biesheuvel * are considered live except for the function parameters. [Note that the EFI 24973a6dec8SArd Biesheuvel * stub may still exit and return to the firmware using the Exit() EFI boot 25073a6dec8SArd Biesheuvel * service.] 25173a6dec8SArd Biesheuvel */ 25212792064SArd BiesheuvelSYM_FUNC_START_LOCAL(efi32_entry) 25373a6dec8SArd Biesheuvel call 1f 25473a6dec8SArd Biesheuvel1: pop %ebx 25573a6dec8SArd Biesheuvel 25673a6dec8SArd Biesheuvel /* Save firmware GDTR and code/data selectors */ 25773a6dec8SArd Biesheuvel sgdtl (efi32_boot_gdt - 1b)(%ebx) 25873a6dec8SArd Biesheuvel movw %cs, (efi32_boot_cs - 1b)(%ebx) 25973a6dec8SArd Biesheuvel movw %ds, (efi32_boot_ds - 1b)(%ebx) 26073a6dec8SArd Biesheuvel 26173a6dec8SArd Biesheuvel /* Store firmware IDT descriptor */ 26273a6dec8SArd Biesheuvel sidtl (efi32_boot_idt - 1b)(%ebx) 26373a6dec8SArd Biesheuvel 26493077506SArd Biesheuvel /* Store firmware stack pointer */ 26593077506SArd Biesheuvel movl %esp, (efi32_boot_sp - 1b)(%ebx) 26693077506SArd Biesheuvel 26773a6dec8SArd Biesheuvel /* Store boot arguments */ 26873a6dec8SArd Biesheuvel leal (efi32_boot_args - 1b)(%ebx), %ebx 26973a6dec8SArd Biesheuvel movl %ecx, 0(%ebx) 27073a6dec8SArd Biesheuvel movl %edx, 4(%ebx) 27173a6dec8SArd Biesheuvel movb $0x0, 12(%ebx) // efi_is64 27273a6dec8SArd Biesheuvel 273*01666eecSArd Biesheuvel /* 274*01666eecSArd Biesheuvel * Allocate some memory for a temporary struct boot_params, which only 275*01666eecSArd Biesheuvel * needs the minimal pieces that startup_32() relies on. 276*01666eecSArd Biesheuvel */ 277*01666eecSArd Biesheuvel subl $PARAM_SIZE, %esp 278*01666eecSArd Biesheuvel movl %esp, %esi 279*01666eecSArd Biesheuvel movl $PAGE_SIZE, BP_kernel_alignment(%esi) 280*01666eecSArd Biesheuvel movl $_end - 1b, BP_init_size(%esi) 281*01666eecSArd Biesheuvel subl $startup_32 - 1b, BP_init_size(%esi) 282*01666eecSArd Biesheuvel 28373a6dec8SArd Biesheuvel /* Disable paging */ 28473a6dec8SArd Biesheuvel movl %cr0, %eax 28573a6dec8SArd Biesheuvel btrl $X86_CR0_PG_BIT, %eax 28673a6dec8SArd Biesheuvel movl %eax, %cr0 28773a6dec8SArd Biesheuvel 28873a6dec8SArd Biesheuvel jmp startup_32 28973a6dec8SArd BiesheuvelSYM_FUNC_END(efi32_entry) 29073a6dec8SArd Biesheuvel 2917f22ca39SArd Biesheuvel/* 2927f22ca39SArd Biesheuvel * efi_status_t efi32_pe_entry(efi_handle_t image_handle, 2937f22ca39SArd Biesheuvel * efi_system_table_32_t *sys_table) 2947f22ca39SArd Biesheuvel */ 2957f22ca39SArd BiesheuvelSYM_FUNC_START(efi32_pe_entry) 2967f22ca39SArd Biesheuvel pushl %ebp 2977f22ca39SArd Biesheuvel movl %esp, %ebp 2987f22ca39SArd Biesheuvel pushl %ebx // save callee-save registers 2997f22ca39SArd Biesheuvel pushl %edi 3007f22ca39SArd Biesheuvel 3017f22ca39SArd Biesheuvel call verify_cpu // check for long mode support 3027f22ca39SArd Biesheuvel testl %eax, %eax 3037f22ca39SArd Biesheuvel movl $0x80000003, %eax // EFI_UNSUPPORTED 3047f22ca39SArd Biesheuvel jnz 2f 3057f22ca39SArd Biesheuvel 3067f22ca39SArd Biesheuvel movl 8(%ebp), %ecx // image_handle 3077f22ca39SArd Biesheuvel movl 12(%ebp), %edx // sys_table 308*01666eecSArd Biesheuvel jmp efi32_entry // pass %ecx, %edx 3097f22ca39SArd Biesheuvel // no other registers remain live 3107f22ca39SArd Biesheuvel 3117f22ca39SArd Biesheuvel2: popl %edi // restore callee-save registers 3127f22ca39SArd Biesheuvel popl %ebx 3137f22ca39SArd Biesheuvel leave 3147f22ca39SArd Biesheuvel RET 3157f22ca39SArd BiesheuvelSYM_FUNC_END(efi32_pe_entry) 3167f22ca39SArd Biesheuvel 31712792064SArd Biesheuvel#ifdef CONFIG_EFI_HANDOVER_PROTOCOL 31812792064SArd Biesheuvel .org efi32_stub_entry + 0x200 31912792064SArd Biesheuvel .code64 32012792064SArd BiesheuvelSYM_FUNC_START_NOALIGN(efi64_stub_entry) 321d7156b98SArd Biesheuvel jmp efi_handover_entry 32212792064SArd BiesheuvelSYM_FUNC_END(efi64_stub_entry) 32312792064SArd Biesheuvel#endif 32412792064SArd Biesheuvel 325cb8bda8aSArd Biesheuvel .data 326cb8bda8aSArd Biesheuvel .balign 8 32773a6dec8SArd BiesheuvelSYM_DATA_START_LOCAL(efi32_boot_gdt) 328cb8bda8aSArd Biesheuvel .word 0 329cb8bda8aSArd Biesheuvel .quad 0 330cb8bda8aSArd BiesheuvelSYM_DATA_END(efi32_boot_gdt) 331cb8bda8aSArd Biesheuvel 33273a6dec8SArd BiesheuvelSYM_DATA_START_LOCAL(efi32_boot_idt) 333cb8bda8aSArd Biesheuvel .word 0 334cb8bda8aSArd Biesheuvel .quad 0 335cb8bda8aSArd BiesheuvelSYM_DATA_END(efi32_boot_idt) 336cb8bda8aSArd Biesheuvel 33773a6dec8SArd BiesheuvelSYM_DATA_LOCAL(efi32_boot_cs, .word 0) 33873a6dec8SArd BiesheuvelSYM_DATA_LOCAL(efi32_boot_ds, .word 0) 33993077506SArd BiesheuvelSYM_DATA_LOCAL(efi32_boot_sp, .long 0) 34073a6dec8SArd BiesheuvelSYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0) 34173a6dec8SArd BiesheuvelSYM_DATA(efi_is64, .byte 1) 342