1/* 2 * Copyright (C) 2014 Intel Corporation; author Matt Fleming 3 * 4 * Support for invoking 32-bit EFI runtime services from a 64-bit 5 * kernel. 6 * 7 * The below thunking functions are only used after ExitBootServices() 8 * has been called. This simplifies things considerably as compared with 9 * the early EFI thunking because we can leave all the kernel state 10 * intact (GDT, IDT, etc) and simply invoke the the 32-bit EFI runtime 11 * services from __KERNEL32_CS. This means we can continue to service 12 * interrupts across an EFI mixed mode call. 13 * 14 * We do however, need to handle the fact that we're running in a full 15 * 64-bit virtual address space. Things like the stack and instruction 16 * addresses need to be accessible by the 32-bit firmware, so we rely on 17 * using the identity mappings in the EFI page table to access the stack 18 * and kernel text (see efi_setup_page_tables()). 19 */ 20 21#include <linux/linkage.h> 22#include <asm/page_types.h> 23#include <asm/segment.h> 24 25 .text 26 .code64 27ENTRY(efi64_thunk) 28 push %rbp 29 push %rbx 30 31 /* 32 * Switch to 1:1 mapped 32-bit stack pointer. 33 */ 34 movq %rsp, efi_saved_sp(%rip) 35 movq efi_scratch+25(%rip), %rsp 36 37 /* 38 * Calculate the physical address of the kernel text. 39 */ 40 movq $__START_KERNEL_map, %rax 41 subq phys_base(%rip), %rax 42 43 /* 44 * Push some physical addresses onto the stack. This is easier 45 * to do now in a code64 section while the assembler can address 46 * 64-bit values. Note that all the addresses on the stack are 47 * 32-bit. 48 */ 49 subq $16, %rsp 50 leaq efi_exit32(%rip), %rbx 51 subq %rax, %rbx 52 movl %ebx, 8(%rsp) 53 54 leaq __efi64_thunk(%rip), %rbx 55 subq %rax, %rbx 56 call *%rbx 57 58 movq efi_saved_sp(%rip), %rsp 59 pop %rbx 60 pop %rbp 61 retq 62ENDPROC(efi64_thunk) 63 64/* 65 * We run this function from the 1:1 mapping. 66 * 67 * This function must be invoked with a 1:1 mapped stack. 68 */ 69ENTRY(__efi64_thunk) 70 movl %ds, %eax 71 push %rax 72 movl %es, %eax 73 push %rax 74 movl %ss, %eax 75 push %rax 76 77 subq $32, %rsp 78 movl %esi, 0x0(%rsp) 79 movl %edx, 0x4(%rsp) 80 movl %ecx, 0x8(%rsp) 81 movq %r8, %rsi 82 movl %esi, 0xc(%rsp) 83 movq %r9, %rsi 84 movl %esi, 0x10(%rsp) 85 86 leaq 1f(%rip), %rbx 87 movq %rbx, func_rt_ptr(%rip) 88 89 /* Switch to 32-bit descriptor */ 90 pushq $__KERNEL32_CS 91 leaq efi_enter32(%rip), %rax 92 pushq %rax 93 lretq 94 951: addq $32, %rsp 96 97 pop %rbx 98 movl %ebx, %ss 99 pop %rbx 100 movl %ebx, %es 101 pop %rbx 102 movl %ebx, %ds 103 104 /* 105 * Convert 32-bit status code into 64-bit. 106 */ 107 test %rax, %rax 108 jz 1f 109 movl %eax, %ecx 110 andl $0x0fffffff, %ecx 111 andl $0xf0000000, %eax 112 shl $32, %rax 113 or %rcx, %rax 1141: 115 ret 116ENDPROC(__efi64_thunk) 117 118ENTRY(efi_exit32) 119 movq func_rt_ptr(%rip), %rax 120 push %rax 121 mov %rdi, %rax 122 ret 123ENDPROC(efi_exit32) 124 125 .code32 126/* 127 * EFI service pointer must be in %edi. 128 * 129 * The stack should represent the 32-bit calling convention. 130 */ 131ENTRY(efi_enter32) 132 movl $__KERNEL_DS, %eax 133 movl %eax, %ds 134 movl %eax, %es 135 movl %eax, %ss 136 137 call *%edi 138 139 /* We must preserve return value */ 140 movl %eax, %edi 141 142 movl 72(%esp), %eax 143 pushl $__KERNEL_CS 144 pushl %eax 145 146 lret 147ENDPROC(efi_enter32) 148 149 .data 150 .balign 8 151func_rt_ptr: .quad 0 152efi_saved_sp: .quad 0 153