| /* |
| * Copyright (C) 2014 Intel Corporation; author Matt Fleming |
| * |
| * Support for invoking 32-bit EFI runtime services from a 64-bit |
| * kernel. |
| * |
| * The below thunking functions are only used after ExitBootServices() |
| * has been called. This simplifies things considerably as compared with |
| * the early EFI thunking because we can leave all the kernel state |
| * intact (GDT, IDT, etc) and simply invoke the the 32-bit EFI runtime |
| * services from __KERNEL32_CS. This means we can continue to service |
| * interrupts across an EFI mixed mode call. |
| * |
| * We do however, need to handle the fact that we're running in a full |
| * 64-bit virtual address space. Things like the stack and instruction |
| * addresses need to be accessible by the 32-bit firmware, so we rely on |
| * using the identity mappings in the EFI page table to access the stack |
| * and kernel text (see efi_setup_page_tables()). |
| */ |
| |
| #include <linux/linkage.h> |
| #include <asm/page_types.h> |
| #include <asm/segment.h> |
| |
| .text |
| .code64 |
| ENTRY(efi64_thunk) |
| push %rbp |
| push %rbx |
| |
| /* |
| * Switch to 1:1 mapped 32-bit stack pointer. |
| */ |
| movq %rsp, efi_saved_sp(%rip) |
| movq efi_scratch+25(%rip), %rsp |
| |
| /* |
| * Calculate the physical address of the kernel text. |
| */ |
| movq $__START_KERNEL_map, %rax |
| subq phys_base(%rip), %rax |
| |
| /* |
| * Push some physical addresses onto the stack. This is easier |
| * to do now in a code64 section while the assembler can address |
| * 64-bit values. Note that all the addresses on the stack are |
| * 32-bit. |
| */ |
| subq $16, %rsp |
| leaq efi_exit32(%rip), %rbx |
| subq %rax, %rbx |
| movl %ebx, 8(%rsp) |
| |
| leaq __efi64_thunk(%rip), %rbx |
| subq %rax, %rbx |
| call *%rbx |
| |
| movq efi_saved_sp(%rip), %rsp |
| pop %rbx |
| pop %rbp |
| retq |
| ENDPROC(efi64_thunk) |
| |
| /* |
| * We run this function from the 1:1 mapping. |
| * |
| * This function must be invoked with a 1:1 mapped stack. |
| */ |
| ENTRY(__efi64_thunk) |
| movl %ds, %eax |
| push %rax |
| movl %es, %eax |
| push %rax |
| movl %ss, %eax |
| push %rax |
| |
| subq $32, %rsp |
| movl %esi, 0x0(%rsp) |
| movl %edx, 0x4(%rsp) |
| movl %ecx, 0x8(%rsp) |
| movq %r8, %rsi |
| movl %esi, 0xc(%rsp) |
| movq %r9, %rsi |
| movl %esi, 0x10(%rsp) |
| |
| leaq 1f(%rip), %rbx |
| movq %rbx, func_rt_ptr(%rip) |
| |
| /* Switch to 32-bit descriptor */ |
| pushq $__KERNEL32_CS |
| leaq efi_enter32(%rip), %rax |
| pushq %rax |
| lretq |
| |
| 1: addq $32, %rsp |
| |
| pop %rbx |
| movl %ebx, %ss |
| pop %rbx |
| movl %ebx, %es |
| pop %rbx |
| movl %ebx, %ds |
| |
| /* |
| * Convert 32-bit status code into 64-bit. |
| */ |
| test %rax, %rax |
| jz 1f |
| movl %eax, %ecx |
| andl $0x0fffffff, %ecx |
| andl $0xf0000000, %eax |
| shl $32, %rax |
| or %rcx, %rax |
| 1: |
| ret |
| ENDPROC(__efi64_thunk) |
| |
| ENTRY(efi_exit32) |
| movq func_rt_ptr(%rip), %rax |
| push %rax |
| mov %rdi, %rax |
| ret |
| ENDPROC(efi_exit32) |
| |
| .code32 |
| /* |
| * EFI service pointer must be in %edi. |
| * |
| * The stack should represent the 32-bit calling convention. |
| */ |
| ENTRY(efi_enter32) |
| movl $__KERNEL_DS, %eax |
| movl %eax, %ds |
| movl %eax, %es |
| movl %eax, %ss |
| |
| call *%edi |
| |
| /* We must preserve return value */ |
| movl %eax, %edi |
| |
| movl 72(%esp), %eax |
| pushl $__KERNEL_CS |
| pushl %eax |
| |
| lret |
| ENDPROC(efi_enter32) |
| |
| .data |
| .balign 8 |
| func_rt_ptr: .quad 0 |
| efi_saved_sp: .quad 0 |