| 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* |
| 3 | * Copyright (C) 2014 Intel Corporation; author Matt Fleming |
| 4 | * |
| 5 | * Support for invoking 32-bit EFI runtime services from a 64-bit |
| 6 | * kernel. |
| 7 | * |
| 8 | * The below thunking functions are only used after ExitBootServices() |
| 9 | * has been called. This simplifies things considerably as compared with |
| 10 | * the early EFI thunking because we can leave all the kernel state |
| 11 | * intact (GDT, IDT, etc) and simply invoke the 32-bit EFI runtime |
| 12 | * services from __KERNEL32_CS. This means we can continue to service |
| 13 | * interrupts across an EFI mixed mode call. |
| 14 | * |
| 15 | * We do however, need to handle the fact that we're running in a full |
| 16 | * 64-bit virtual address space. Things like the stack and instruction |
| 17 | * addresses need to be accessible by the 32-bit firmware, so we rely on |
| 18 | * using the identity mappings in the EFI page table to access the stack |
| 19 | * and kernel text (see efi_setup_page_tables()). |
| 20 | */ |
| 21 | |
| 22 | #include <linux/linkage.h> |
| 23 | #include <linux/objtool.h> |
| 24 | #include <asm/page_types.h> |
| 25 | #include <asm/segment.h> |
| 26 | |
| 27 | .text |
| 28 | .code64 |
| 29 | SYM_FUNC_START(__efi64_thunk) |
| 30 | STACK_FRAME_NON_STANDARD __efi64_thunk |
| 31 | push %rbp |
| 32 | push %rbx |
| 33 | |
| 34 | /* |
| 35 | * Switch to 1:1 mapped 32-bit stack pointer. |
| 36 | */ |
| 37 | movq %rsp, %rax |
| 38 | movq efi_mixed_mode_stack_pa(%rip), %rsp |
| 39 | push %rax |
| 40 | |
| 41 | /* |
| 42 | * Copy args passed via the stack |
| 43 | */ |
| 44 | subq $0x24, %rsp |
| 45 | movq 0x18(%rax), %rbp |
| 46 | movq 0x20(%rax), %rbx |
| 47 | movq 0x28(%rax), %rax |
| 48 | movl %ebp, 0x18(%rsp) |
| 49 | movl %ebx, 0x1c(%rsp) |
| 50 | movl %eax, 0x20(%rsp) |
| 51 | |
| 52 | /* |
| 53 | * Calculate the physical address of the kernel text. |
| 54 | */ |
| 55 | movq $__START_KERNEL_map, %rax |
| 56 | subq phys_base(%rip), %rax |
| 57 | |
| 58 | leaq 1f(%rip), %rbp |
| 59 | leaq 2f(%rip), %rbx |
| 60 | subq %rax, %rbp |
| 61 | subq %rax, %rbx |
| 62 | |
| 63 | movl %ebx, 0x0(%rsp) /* return address */ |
| 64 | movl %esi, 0x4(%rsp) |
| 65 | movl %edx, 0x8(%rsp) |
| 66 | movl %ecx, 0xc(%rsp) |
| 67 | movl %r8d, 0x10(%rsp) |
| 68 | movl %r9d, 0x14(%rsp) |
| 69 | |
| 70 | /* Switch to 32-bit descriptor */ |
| 71 | pushq $__KERNEL32_CS |
| 72 | pushq %rdi /* EFI runtime service address */ |
| 73 | lretq |
| 74 | |
| 75 | // This return instruction is not needed for correctness, as it will |
| 76 | // never be reached. It only exists to make objtool happy, which will |
| 77 | // otherwise complain about unreachable instructions in the callers. |
| 78 | RET |
| 79 | SYM_FUNC_END(__efi64_thunk) |
| 80 | |
| 81 | .section ".rodata" , "a" , @progbits |
| 82 | .balign 16 |
| 83 | SYM_DATA_START(__efi64_thunk_ret_tramp) |
| 84 | 1: movq 0x20(%rsp), %rsp |
| 85 | pop %rbx |
| 86 | pop %rbp |
| 87 | ret |
| 88 | int3 |
| 89 | |
| 90 | .code32 |
| 91 | 2: pushl $__KERNEL_CS |
| 92 | pushl %ebp |
| 93 | lret |
| 94 | SYM_DATA_END(__efi64_thunk_ret_tramp) |
| 95 | |
| 96 | .bss |
| 97 | .balign 8 |
| 98 | SYM_DATA(efi_mixed_mode_stack_pa, .quad 0) |
| 99 | |