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 before we make EFI serivce calls, 9 * since the firmware's 32-bit IDT is still currently installed and it 10 * needs to be able to service interrupts. 11 * 12 * On the plus side, we don't have to worry about mangling 64-bit 13 * addresses into 32-bits because we're executing with an identity 14 * mapped pagetable and haven't transitioned to 64-bit virtual addresses 15 * yet. 16 */ 17 18#include <linux/linkage.h> 19#include <asm/msr.h> 20#include <asm/page_types.h> 21#include <asm/processor-flags.h> 22#include <asm/segment.h> 23 24 .code64 25 .text 26SYM_FUNC_START(__efi64_thunk) 27 push %rbp 28 push %rbx 29 30 leaq 1f(%rip), %rbp 31 32 movl %ds, %eax 33 push %rax 34 movl %es, %eax 35 push %rax 36 movl %ss, %eax 37 push %rax 38 39 /* 40 * Convert x86-64 ABI params to i386 ABI 41 */ 42 subq $32, %rsp 43 movl %esi, 0x0(%rsp) 44 movl %edx, 0x4(%rsp) 45 movl %ecx, 0x8(%rsp) 46 movl %r8d, 0xc(%rsp) 47 movl %r9d, 0x10(%rsp) 48 49 leaq 0x14(%rsp), %rbx 50 sgdt (%rbx) 51 52 /* 53 * Switch to gdt with 32-bit segments. This is the firmware GDT 54 * that was installed when the kernel started executing. This 55 * pointer was saved at the EFI stub entry point in head_64.S. 56 * 57 * Pass the saved DS selector to the 32-bit code, and use far return to 58 * restore the saved CS selector. 59 */ 60 leaq efi32_boot_gdt(%rip), %rax 61 lgdt (%rax) 62 63 movzwl efi32_boot_ds(%rip), %edx 64 movzwq efi32_boot_cs(%rip), %rax 65 pushq %rax 66 leaq efi_enter32(%rip), %rax 67 pushq %rax 68 lretq 69 701: addq $32, %rsp 71 movq %rdi, %rax 72 73 pop %rbx 74 movl %ebx, %ss 75 pop %rbx 76 movl %ebx, %es 77 pop %rbx 78 movl %ebx, %ds 79 /* Clear out 32-bit selector from FS and GS */ 80 xorl %ebx, %ebx 81 movl %ebx, %fs 82 movl %ebx, %gs 83 84 /* 85 * Convert 32-bit status code into 64-bit. 86 */ 87 roll $1, %eax 88 rorq $1, %rax 89 90 pop %rbx 91 pop %rbp 92 RET 93SYM_FUNC_END(__efi64_thunk) 94 95 .code32 96/* 97 * EFI service pointer must be in %edi. 98 * 99 * The stack should represent the 32-bit calling convention. 100 */ 101SYM_FUNC_START_LOCAL(efi_enter32) 102 /* Load firmware selector into data and stack segment registers */ 103 movl %edx, %ds 104 movl %edx, %es 105 movl %edx, %fs 106 movl %edx, %gs 107 movl %edx, %ss 108 109 /* Reload pgtables */ 110 movl %cr3, %eax 111 movl %eax, %cr3 112 113 /* Disable paging */ 114 movl %cr0, %eax 115 btrl $X86_CR0_PG_BIT, %eax 116 movl %eax, %cr0 117 118 /* Disable long mode via EFER */ 119 movl $MSR_EFER, %ecx 120 rdmsr 121 btrl $_EFER_LME, %eax 122 wrmsr 123 124 call *%edi 125 126 /* We must preserve return value */ 127 movl %eax, %edi 128 129 /* 130 * Some firmware will return with interrupts enabled. Be sure to 131 * disable them before we switch GDTs. 132 */ 133 cli 134 135 lgdtl (%ebx) 136 137 movl %cr4, %eax 138 btsl $(X86_CR4_PAE_BIT), %eax 139 movl %eax, %cr4 140 141 movl %cr3, %eax 142 movl %eax, %cr3 143 144 movl $MSR_EFER, %ecx 145 rdmsr 146 btsl $_EFER_LME, %eax 147 wrmsr 148 149 xorl %eax, %eax 150 lldt %ax 151 152 pushl $__KERNEL_CS 153 pushl %ebp 154 155 /* Enable paging */ 156 movl %cr0, %eax 157 btsl $X86_CR0_PG_BIT, %eax 158 movl %eax, %cr0 159 lret 160SYM_FUNC_END(efi_enter32) 161 162 .data 163 .balign 8 164SYM_DATA_START(efi32_boot_gdt) 165 .word 0 166 .quad 0 167SYM_DATA_END(efi32_boot_gdt) 168 169SYM_DATA_START(efi32_boot_cs) 170 .word 0 171SYM_DATA_END(efi32_boot_cs) 172 173SYM_DATA_START(efi32_boot_ds) 174 .word 0 175SYM_DATA_END(efi32_boot_ds) 176