Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
4 *
5 * Early support for invoking 32-bit EFI services from a 64-bit kernel.
6 *
7 * Because this thunking occurs before ExitBootServices() we have to
8 * restore the firmware's 32-bit GDT and IDT before we make EFI service
9 * calls.
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 identity
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
25SYM_FUNC_START(__efi64_thunk)
26 push %rbp
27 push %rbx
28
29 movl %ds, %eax
30 push %rax
31 movl %es, %eax
32 push %rax
33 movl %ss, %eax
34 push %rax
35
36 /* Copy args passed on stack */
37 movq 0x30(%rsp), %rbp
38 movq 0x38(%rsp), %rbx
39 movq 0x40(%rsp), %rax
40
41 /*
42 * Convert x86-64 ABI params to i386 ABI
43 */
44 subq $64, %rsp
45 movl %esi, 0x0(%rsp)
46 movl %edx, 0x4(%rsp)
47 movl %ecx, 0x8(%rsp)
48 movl %r8d, 0xc(%rsp)
49 movl %r9d, 0x10(%rsp)
50 movl %ebp, 0x14(%rsp)
51 movl %ebx, 0x18(%rsp)
52 movl %eax, 0x1c(%rsp)
53
54 leaq 0x20(%rsp), %rbx
55 sgdt (%rbx)
56
57 addq $16, %rbx
58 sidt (%rbx)
59
60 leaq 1f(%rip), %rbp
61
62 /*
63 * Switch to IDT and GDT with 32-bit segments. This is the firmware GDT
64 * and IDT that was installed when the kernel started executing. The
65 * pointers were saved at the EFI stub entry point in head_64.S.
66 *
67 * Pass the saved DS selector to the 32-bit code, and use far return to
68 * restore the saved CS selector.
69 */
70 leaq efi32_boot_idt(%rip), %rax
71 lidt (%rax)
72 leaq efi32_boot_gdt(%rip), %rax
73 lgdt (%rax)
74
75 movzwl efi32_boot_ds(%rip), %edx
76 movzwq efi32_boot_cs(%rip), %rax
77 pushq %rax
78 leaq efi_enter32(%rip), %rax
79 pushq %rax
80 lretq
81
821: addq $64, %rsp
83 movq %rdi, %rax
84
85 pop %rbx
86 movl %ebx, %ss
87 pop %rbx
88 movl %ebx, %es
89 pop %rbx
90 movl %ebx, %ds
91 /* Clear out 32-bit selector from FS and GS */
92 xorl %ebx, %ebx
93 movl %ebx, %fs
94 movl %ebx, %gs
95
96 /*
97 * Convert 32-bit status code into 64-bit.
98 */
99 roll $1, %eax
100 rorq $1, %rax
101
102 pop %rbx
103 pop %rbp
104 RET
105SYM_FUNC_END(__efi64_thunk)
106
107 .code32
108/*
109 * EFI service pointer must be in %edi.
110 *
111 * The stack should represent the 32-bit calling convention.
112 */
113SYM_FUNC_START_LOCAL(efi_enter32)
114 /* Load firmware selector into data and stack segment registers */
115 movl %edx, %ds
116 movl %edx, %es
117 movl %edx, %fs
118 movl %edx, %gs
119 movl %edx, %ss
120
121 /* Reload pgtables */
122 movl %cr3, %eax
123 movl %eax, %cr3
124
125 /* Disable paging */
126 movl %cr0, %eax
127 btrl $X86_CR0_PG_BIT, %eax
128 movl %eax, %cr0
129
130 /* Disable long mode via EFER */
131 movl $MSR_EFER, %ecx
132 rdmsr
133 btrl $_EFER_LME, %eax
134 wrmsr
135
136 call *%edi
137
138 /* We must preserve return value */
139 movl %eax, %edi
140
141 /*
142 * Some firmware will return with interrupts enabled. Be sure to
143 * disable them before we switch GDTs and IDTs.
144 */
145 cli
146
147 lidtl (%ebx)
148 subl $16, %ebx
149
150 lgdtl (%ebx)
151
152 movl %cr4, %eax
153 btsl $(X86_CR4_PAE_BIT), %eax
154 movl %eax, %cr4
155
156 movl %cr3, %eax
157 movl %eax, %cr3
158
159 movl $MSR_EFER, %ecx
160 rdmsr
161 btsl $_EFER_LME, %eax
162 wrmsr
163
164 xorl %eax, %eax
165 lldt %ax
166
167 pushl $__KERNEL_CS
168 pushl %ebp
169
170 /* Enable paging */
171 movl %cr0, %eax
172 btsl $X86_CR0_PG_BIT, %eax
173 movl %eax, %cr0
174 lret
175SYM_FUNC_END(efi_enter32)
176
177 .data
178 .balign 8
179SYM_DATA_START(efi32_boot_gdt)
180 .word 0
181 .quad 0
182SYM_DATA_END(efi32_boot_gdt)
183
184SYM_DATA_START(efi32_boot_idt)
185 .word 0
186 .quad 0
187SYM_DATA_END(efi32_boot_idt)
188
189SYM_DATA_START(efi32_boot_cs)
190 .word 0
191SYM_DATA_END(efi32_boot_cs)
192
193SYM_DATA_START(efi32_boot_ds)
194 .word 0
195SYM_DATA_END(efi32_boot_ds)