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