efi_thunk_64.S (3622B)
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/msr.h> 19#include <asm/page_types.h> 20#include <asm/processor-flags.h> 21#include <asm/segment.h> 22 23 .code64 24 .text 25SYM_FUNC_START(__efi64_thunk) 26 push %rbp 27 push %rbx 28 29 movl %ds, %eax 30 push %rax 31 movl %es, %eax 32 push %rax 33 movl %ss, %eax 34 push %rax 35 36 /* Copy args passed on stack */ 37 movq 0x30(%rsp), %rbp 38 movq 0x38(%rsp), %rbx 39 movq 0x40(%rsp), %rax 40 41 /* 42 * Convert x86-64 ABI params to i386 ABI 43 */ 44 subq $64, %rsp 45 movl %esi, 0x0(%rsp) 46 movl %edx, 0x4(%rsp) 47 movl %ecx, 0x8(%rsp) 48 movl %r8d, 0xc(%rsp) 49 movl %r9d, 0x10(%rsp) 50 movl %ebp, 0x14(%rsp) 51 movl %ebx, 0x18(%rsp) 52 movl %eax, 0x1c(%rsp) 53 54 leaq 0x20(%rsp), %rbx 55 sgdt (%rbx) 56 57 addq $16, %rbx 58 sidt (%rbx) 59 60 leaq 1f(%rip), %rbp 61 62 /* 63 * Switch to IDT and GDT with 32-bit segments. This is the firmware GDT 64 * and IDT that was installed when the kernel started executing. The 65 * pointers were saved at the EFI stub entry point in head_64.S. 66 * 67 * Pass the saved DS selector to the 32-bit code, and use far return to 68 * restore the saved CS selector. 69 */ 70 leaq efi32_boot_idt(%rip), %rax 71 lidt (%rax) 72 leaq efi32_boot_gdt(%rip), %rax 73 lgdt (%rax) 74 75 movzwl efi32_boot_ds(%rip), %edx 76 movzwq efi32_boot_cs(%rip), %rax 77 pushq %rax 78 leaq efi_enter32(%rip), %rax 79 pushq %rax 80 lretq 81 821: addq $64, %rsp 83 movq %rdi, %rax 84 85 pop %rbx 86 movl %ebx, %ss 87 pop %rbx 88 movl %ebx, %es 89 pop %rbx 90 movl %ebx, %ds 91 /* Clear out 32-bit selector from FS and GS */ 92 xorl %ebx, %ebx 93 movl %ebx, %fs 94 movl %ebx, %gs 95 96 /* 97 * Convert 32-bit status code into 64-bit. 98 */ 99 roll $1, %eax 100 rorq $1, %rax 101 102 pop %rbx 103 pop %rbp 104 RET 105SYM_FUNC_END(__efi64_thunk) 106 107 .code32 108/* 109 * EFI service pointer must be in %edi. 110 * 111 * The stack should represent the 32-bit calling convention. 112 */ 113SYM_FUNC_START_LOCAL(efi_enter32) 114 /* Load firmware selector into data and stack segment registers */ 115 movl %edx, %ds 116 movl %edx, %es 117 movl %edx, %fs 118 movl %edx, %gs 119 movl %edx, %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 and IDTs. 144 */ 145 cli 146 147 lidtl (%ebx) 148 subl $16, %ebx 149 150 lgdtl (%ebx) 151 152 movl %cr4, %eax 153 btsl $(X86_CR4_PAE_BIT), %eax 154 movl %eax, %cr4 155 156 movl %cr3, %eax 157 movl %eax, %cr3 158 159 movl $MSR_EFER, %ecx 160 rdmsr 161 btsl $_EFER_LME, %eax 162 wrmsr 163 164 xorl %eax, %eax 165 lldt %ax 166 167 pushl $__KERNEL_CS 168 pushl %ebp 169 170 /* Enable paging */ 171 movl %cr0, %eax 172 btsl $X86_CR0_PG_BIT, %eax 173 movl %eax, %cr0 174 lret 175SYM_FUNC_END(efi_enter32) 176 177 .data 178 .balign 8 179SYM_DATA_START(efi32_boot_gdt) 180 .word 0 181 .quad 0 182SYM_DATA_END(efi32_boot_gdt) 183 184SYM_DATA_START(efi32_boot_idt) 185 .word 0 186 .quad 0 187SYM_DATA_END(efi32_boot_idt) 188 189SYM_DATA_START(efi32_boot_cs) 190 .word 0 191SYM_DATA_END(efi32_boot_cs) 192 193SYM_DATA_START(efi32_boot_ds) 194 .word 0 195SYM_DATA_END(efi32_boot_ds)