1/* 2 * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming 3 * 4 * Early support for invoking 32-bit EFI services from a 64-bit kernel. 5 * 6 * Because this thunking occurs before ExitBootServices() we have to 7 * restore the firmware's 32-bit GDT before we make EFI serivce calls, 8 * since the firmware's 32-bit IDT is still currently installed and it 9 * needs to be able to service interrupts. 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 identify 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 25ENTRY(efi64_thunk) 26 push %rbp 27 push %rbx 28 29 subq $8, %rsp 30 leaq efi_exit32(%rip), %rax 31 movl %eax, 4(%rsp) 32 leaq efi_gdt64(%rip), %rax 33 movl %eax, (%rsp) 34 movl %eax, 2(%rax) /* Fixup the gdt base address */ 35 36 movl %ds, %eax 37 push %rax 38 movl %es, %eax 39 push %rax 40 movl %ss, %eax 41 push %rax 42 43 /* 44 * Convert x86-64 ABI params to i386 ABI 45 */ 46 subq $32, %rsp 47 movl %esi, 0x0(%rsp) 48 movl %edx, 0x4(%rsp) 49 movl %ecx, 0x8(%rsp) 50 movq %r8, %rsi 51 movl %esi, 0xc(%rsp) 52 movq %r9, %rsi 53 movl %esi, 0x10(%rsp) 54 55 sgdt save_gdt(%rip) 56 57 leaq 1f(%rip), %rbx 58 movq %rbx, func_rt_ptr(%rip) 59 60 /* 61 * Switch to gdt with 32-bit segments. This is the firmware GDT 62 * that was installed when the kernel started executing. This 63 * pointer was saved at the EFI stub entry point in head_64.S. 64 */ 65 leaq efi32_boot_gdt(%rip), %rax 66 lgdt (%rax) 67 68 pushq $__KERNEL_CS 69 leaq efi_enter32(%rip), %rax 70 pushq %rax 71 lretq 72 731: addq $32, %rsp 74 75 lgdt save_gdt(%rip) 76 77 pop %rbx 78 movl %ebx, %ss 79 pop %rbx 80 movl %ebx, %es 81 pop %rbx 82 movl %ebx, %ds 83 84 /* 85 * Convert 32-bit status code into 64-bit. 86 */ 87 test %rax, %rax 88 jz 1f 89 movl %eax, %ecx 90 andl $0x0fffffff, %ecx 91 andl $0xf0000000, %eax 92 shl $32, %rax 93 or %rcx, %rax 941: 95 addq $8, %rsp 96 pop %rbx 97 pop %rbp 98 ret 99ENDPROC(efi64_thunk) 100 101ENTRY(efi_exit32) 102 movq func_rt_ptr(%rip), %rax 103 push %rax 104 mov %rdi, %rax 105 ret 106ENDPROC(efi_exit32) 107 108 .code32 109/* 110 * EFI service pointer must be in %edi. 111 * 112 * The stack should represent the 32-bit calling convention. 113 */ 114ENTRY(efi_enter32) 115 movl $__KERNEL_DS, %eax 116 movl %eax, %ds 117 movl %eax, %es 118 movl %eax, %ss 119 120 /* Reload pgtables */ 121 movl %cr3, %eax 122 movl %eax, %cr3 123 124 /* Disable paging */ 125 movl %cr0, %eax 126 btrl $X86_CR0_PG_BIT, %eax 127 movl %eax, %cr0 128 129 /* Disable long mode via EFER */ 130 movl $MSR_EFER, %ecx 131 rdmsr 132 btrl $_EFER_LME, %eax 133 wrmsr 134 135 call *%edi 136 137 /* We must preserve return value */ 138 movl %eax, %edi 139 140 /* 141 * Some firmware will return with interrupts enabled. Be sure to 142 * disable them before we switch GDTs. 143 */ 144 cli 145 146 movl 56(%esp), %eax 147 movl %eax, 2(%eax) 148 lgdtl (%eax) 149 150 movl %cr4, %eax 151 btsl $(X86_CR4_PAE_BIT), %eax 152 movl %eax, %cr4 153 154 movl %cr3, %eax 155 movl %eax, %cr3 156 157 movl $MSR_EFER, %ecx 158 rdmsr 159 btsl $_EFER_LME, %eax 160 wrmsr 161 162 xorl %eax, %eax 163 lldt %ax 164 165 movl 60(%esp), %eax 166 pushl $__KERNEL_CS 167 pushl %eax 168 169 /* Enable paging */ 170 movl %cr0, %eax 171 btsl $X86_CR0_PG_BIT, %eax 172 movl %eax, %cr0 173 lret 174ENDPROC(efi_enter32) 175 176 .data 177 .balign 8 178 .global efi32_boot_gdt 179efi32_boot_gdt: .word 0 180 .quad 0 181 182save_gdt: .word 0 183 .quad 0 184func_rt_ptr: .quad 0 185 186 .global efi_gdt64 187efi_gdt64: 188 .word efi_gdt64_end - efi_gdt64 189 .long 0 /* Filled out by user */ 190 .word 0 191 .quad 0x0000000000000000 /* NULL descriptor */ 192 .quad 0x00af9a000000ffff /* __KERNEL_CS */ 193 .quad 0x00cf92000000ffff /* __KERNEL_DS */ 194 .quad 0x0080890000000000 /* TS descriptor */ 195 .quad 0x0000000000000000 /* TS continued */ 196efi_gdt64_end: 197