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 leaq 1f(%rip), %rbp 30 31 movl %ds, %eax 32 push %rax 33 movl %es, %eax 34 push %rax 35 movl %ss, %eax 36 push %rax 37 38 /* 39 * Convert x86-64 ABI params to i386 ABI 40 */ 41 subq $64, %rsp 42 movl %esi, 0x0(%rsp) 43 movl %edx, 0x4(%rsp) 44 movl %ecx, 0x8(%rsp) 45 movl %r8d, 0xc(%rsp) 46 movl %r9d, 0x10(%rsp) 47 48 leaq 0x14(%rsp), %rbx 49 sgdt (%rbx) 50 51 addq $16, %rbx 52 sidt (%rbx) 53 54 /* 55 * Switch to IDT and GDT with 32-bit segments. This is the firmware GDT 56 * and IDT that was installed when the kernel started executing. The 57 * pointers were saved at the EFI stub entry point in head_64.S. 58 * 59 * Pass the saved DS selector to the 32-bit code, and use far return to 60 * restore the saved CS selector. 61 */ 62 leaq efi32_boot_idt(%rip), %rax 63 lidt (%rax) 64 leaq efi32_boot_gdt(%rip), %rax 65 lgdt (%rax) 66 67 movzwl efi32_boot_ds(%rip), %edx 68 movzwq efi32_boot_cs(%rip), %rax 69 pushq %rax 70 leaq efi_enter32(%rip), %rax 71 pushq %rax 72 lretq 73 741: addq $64, %rsp 75 movq %rdi, %rax 76 77 pop %rbx 78 movl %ebx, %ss 79 pop %rbx 80 movl %ebx, %es 81 pop %rbx 82 movl %ebx, %ds 83 /* Clear out 32-bit selector from FS and GS */ 84 xorl %ebx, %ebx 85 movl %ebx, %fs 86 movl %ebx, %gs 87 88 /* 89 * Convert 32-bit status code into 64-bit. 90 */ 91 roll $1, %eax 92 rorq $1, %rax 93 94 pop %rbx 95 pop %rbp 96 ret 97SYM_FUNC_END(__efi64_thunk) 98 99 .code32 100/* 101 * EFI service pointer must be in %edi. 102 * 103 * The stack should represent the 32-bit calling convention. 104 */ 105SYM_FUNC_START_LOCAL(efi_enter32) 106 /* Load firmware selector into data and stack segment registers */ 107 movl %edx, %ds 108 movl %edx, %es 109 movl %edx, %fs 110 movl %edx, %gs 111 movl %edx, %ss 112 113 /* Reload pgtables */ 114 movl %cr3, %eax 115 movl %eax, %cr3 116 117 /* Disable paging */ 118 movl %cr0, %eax 119 btrl $X86_CR0_PG_BIT, %eax 120 movl %eax, %cr0 121 122 /* Disable long mode via EFER */ 123 movl $MSR_EFER, %ecx 124 rdmsr 125 btrl $_EFER_LME, %eax 126 wrmsr 127 128 call *%edi 129 130 /* We must preserve return value */ 131 movl %eax, %edi 132 133 /* 134 * Some firmware will return with interrupts enabled. Be sure to 135 * disable them before we switch GDTs and IDTs. 136 */ 137 cli 138 139 lidtl (%ebx) 140 subl $16, %ebx 141 142 lgdtl (%ebx) 143 144 movl %cr4, %eax 145 btsl $(X86_CR4_PAE_BIT), %eax 146 movl %eax, %cr4 147 148 movl %cr3, %eax 149 movl %eax, %cr3 150 151 movl $MSR_EFER, %ecx 152 rdmsr 153 btsl $_EFER_LME, %eax 154 wrmsr 155 156 xorl %eax, %eax 157 lldt %ax 158 159 pushl $__KERNEL_CS 160 pushl %ebp 161 162 /* Enable paging */ 163 movl %cr0, %eax 164 btsl $X86_CR0_PG_BIT, %eax 165 movl %eax, %cr0 166 lret 167SYM_FUNC_END(efi_enter32) 168 169 .data 170 .balign 8 171SYM_DATA_START(efi32_boot_gdt) 172 .word 0 173 .quad 0 174SYM_DATA_END(efi32_boot_gdt) 175 176SYM_DATA_START(efi32_boot_idt) 177 .word 0 178 .quad 0 179SYM_DATA_END(efi32_boot_idt) 180 181SYM_DATA_START(efi32_boot_cs) 182 .word 0 183SYM_DATA_END(efi32_boot_cs) 184 185SYM_DATA_START(efi32_boot_ds) 186 .word 0 187SYM_DATA_END(efi32_boot_ds) 188