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