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/desc_defs.h>
19#include <asm/msr.h>
20#include <asm/page_types.h>
21#include <asm/pgtable_types.h>
22#include <asm/processor-flags.h>
23#include <asm/segment.h>
24
25 .text
26 .code32
27#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
28SYM_FUNC_START(efi32_stub_entry)
29 call 1f
301: popl %ecx
31
32 /* Clear BSS */
33 xorl %eax, %eax
34 leal (_bss - 1b)(%ecx), %edi
35 leal (_ebss - 1b)(%ecx), %ecx
36 subl %edi, %ecx
37 shrl $2, %ecx
38 cld
39 rep stosl
40
41 add $0x4, %esp /* Discard return address */
42 movl 8(%esp), %ebx /* struct boot_params pointer */
43 jmp efi32_startup
44SYM_FUNC_END(efi32_stub_entry)
45#endif
46
47/*
48 * Called using a far call from __efi64_thunk() below, using the x86_64 SysV
49 * ABI (except for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are
50 * used instead). EBP+16 points to the arguments passed via the stack.
51 *
52 * The first argument (EDI) is a pointer to the boot service or protocol, to
53 * which the remaining arguments are passed, each truncated to 32 bits.
54 */
55SYM_FUNC_START_LOCAL(efi_enter32)
56 /*
57 * Convert x86-64 SysV ABI params to i386 ABI
58 */
59 pushl 32(%ebp) /* Up to 3 args passed via the stack */
60 pushl 24(%ebp)
61 pushl 16(%ebp)
62 pushl %ebx /* R9 */
63 pushl %eax /* R8 */
64 pushl %ecx
65 pushl %edx
66 pushl %esi
67
68 /* Disable paging */
69 movl %cr0, %eax
70 btrl $X86_CR0_PG_BIT, %eax
71 movl %eax, %cr0
72
73 /* Disable long mode via EFER */
74 movl $MSR_EFER, %ecx
75 rdmsr
76 btrl $_EFER_LME, %eax
77 wrmsr
78
79 call *%edi
80
81 /* We must preserve return value */
82 movl %eax, %edi
83
84 call efi32_enable_long_mode
85
86 addl $32, %esp
87 movl %edi, %eax
88 lret
89SYM_FUNC_END(efi_enter32)
90
91 .code64
92SYM_FUNC_START(__efi64_thunk)
93 push %rbp
94 movl %esp, %ebp
95 push %rbx
96
97 /* Move args #5 and #6 into 32-bit accessible registers */
98 movl %r8d, %eax
99 movl %r9d, %ebx
100
101 lcalll *efi32_call(%rip)
102
103 pop %rbx
104 pop %rbp
105 RET
106SYM_FUNC_END(__efi64_thunk)
107
108 .code32
109SYM_FUNC_START_LOCAL(efi32_enable_long_mode)
110 movl %cr4, %eax
111 btsl $(X86_CR4_PAE_BIT), %eax
112 movl %eax, %cr4
113
114 movl $MSR_EFER, %ecx
115 rdmsr
116 btsl $_EFER_LME, %eax
117 wrmsr
118
119 /* Disable interrupts - the firmware's IDT does not work in long mode */
120 cli
121
122 /* Enable paging */
123 movl %cr0, %eax
124 btsl $X86_CR0_PG_BIT, %eax
125 movl %eax, %cr0
126 ret
127SYM_FUNC_END(efi32_enable_long_mode)
128
129/*
130 * This is the common EFI stub entry point for mixed mode. It sets up the GDT
131 * and page tables needed for 64-bit execution, after which it calls the
132 * common 64-bit EFI entrypoint efi_stub_entry().
133 *
134 * Arguments: 0(%esp) image handle
135 * 4(%esp) EFI system table pointer
136 * %ebx struct boot_params pointer (or NULL)
137 *
138 * Since this is the point of no return for ordinary execution, no registers
139 * are considered live except for the function parameters. [Note that the EFI
140 * stub may still exit and return to the firmware using the Exit() EFI boot
141 * service.]
142 */
143SYM_FUNC_START_LOCAL(efi32_startup)
144 movl %esp, %ebp
145
146 subl $8, %esp
147 sgdtl (%esp) /* Save GDT descriptor to the stack */
148 movl 2(%esp), %esi /* Existing GDT pointer */
149 movzwl (%esp), %ecx /* Existing GDT limit */
150 inc %ecx /* Existing GDT size */
151 andl $~7, %ecx /* Ensure size is multiple of 8 */
152
153 subl %ecx, %esp /* Allocate new GDT */
154 andl $~15, %esp /* Realign the stack */
155 movl %esp, %edi /* New GDT address */
156 leal 7(%ecx), %eax /* New GDT limit */
157 pushw %cx /* Push 64-bit CS (for LJMP below) */
158 pushl %edi /* Push new GDT address */
159 pushw %ax /* Push new GDT limit */
160
161 /* Copy GDT to the stack and add a 64-bit code segment at the end */
162 movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) & 0xffffffff, (%edi,%ecx)
163 movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) >> 32, 4(%edi,%ecx)
164 shrl $2, %ecx
165 cld
166 rep movsl /* Copy the firmware GDT */
167 lgdtl (%esp) /* Switch to the new GDT */
168
169 call 1f
1701: pop %edi
171
172 /* Record mixed mode entry */
173 movb $0x0, (efi_is64 - 1b)(%edi)
174
175 /* Set up indirect far call to re-enter 32-bit mode */
176 leal (efi32_call - 1b)(%edi), %eax
177 addl %eax, (%eax)
178 movw %cs, 4(%eax)
179
180 /* Disable paging */
181 movl %cr0, %eax
182 btrl $X86_CR0_PG_BIT, %eax
183 movl %eax, %cr0
184
185 /* Set up 1:1 mapping */
186 leal (pte - 1b)(%edi), %eax
187 movl $_PAGE_PRESENT | _PAGE_RW | _PAGE_PSE, %ecx
188 leal (_PAGE_PRESENT | _PAGE_RW)(%eax), %edx
1892: movl %ecx, (%eax)
190 addl $8, %eax
191 addl $PMD_SIZE, %ecx
192 jnc 2b
193
194 movl $PAGE_SIZE, %ecx
195 .irpc l, 0123
196 movl %edx, \l * 8(%eax)
197 addl %ecx, %edx
198 .endr
199 addl %ecx, %eax
200 movl %edx, (%eax)
201 movl %eax, %cr3
202
203 call efi32_enable_long_mode
204
205 /* Set up far jump to 64-bit mode (CS is already on the stack) */
206 leal (efi_stub_entry - 1b)(%edi), %eax
207 movl %eax, 2(%esp)
208
209 movl 0(%ebp), %edi
210 movl 4(%ebp), %esi
211 movl %ebx, %edx
212 ljmpl *2(%esp)
213SYM_FUNC_END(efi32_startup)
214
215/*
216 * efi_status_t efi32_pe_entry(efi_handle_t image_handle,
217 * efi_system_table_32_t *sys_table)
218 */
219SYM_FUNC_START(efi32_pe_entry)
220 pushl %ebx // save callee-save registers
221
222 /* Check whether the CPU supports long mode */
223 movl $0x80000001, %eax // assume extended info support
224 cpuid
225 btl $29, %edx // check long mode bit
226 jnc 1f
227 leal 8(%esp), %esp // preserve stack alignment
228 xor %ebx, %ebx // no struct boot_params pointer
229 jmp efi32_startup // only ESP and EBX remain live
2301: movl $0x80000003, %eax // EFI_UNSUPPORTED
231 popl %ebx
232 RET
233SYM_FUNC_END(efi32_pe_entry)
234
235#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
236 .org efi32_stub_entry + 0x200
237 .code64
238SYM_FUNC_START_NOALIGN(efi64_stub_entry)
239 jmp efi_handover_entry
240SYM_FUNC_END(efi64_stub_entry)
241#endif
242
243 .data
244 .balign 8
245SYM_DATA_START_LOCAL(efi32_call)
246 .long efi_enter32 - .
247 .word 0x0
248SYM_DATA_END(efi32_call)
249SYM_DATA(efi_is64, .byte 1)
250
251 .bss
252 .balign PAGE_SIZE
253SYM_DATA_LOCAL(pte, .fill 6 * PAGE_SIZE, 1, 0)
254

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of linux/arch/x86/boot/startup/efi-mixed.S