1515 */
1616
1717#include <linux/linkage.h>
18- #include <asm/asm-offsets .h>
18+ #include <asm/desc_defs .h>
1919#include <asm/msr.h>
2020#include <asm/page_types.h>
2121#include <asm/pgtable_types.h>
2222#include <asm/processor-flags.h>
2323#include <asm/segment.h>
24- #include <asm/setup.h>
2524
2625 .code64
2726 .text
28- /*
29- * When booting in 64-bit mode on 32-bit EFI firmware, startup_64_mixed_mode()
30- * is the first thing that runs after switching to long mode. Depending on
31- * whether the EFI handover protocol or the compat entry point was used to
32- * enter the kernel, it will either branch to the common 64-bit EFI stub
33- * entrypoint efi_stub_entry() directly, or via the 64-bit EFI PE/COFF
34- * entrypoint efi_pe_entry(). In the former case, the bootloader must provide a
35- * struct bootparams pointer as the third argument, so the presence of such a
36- * pointer is used to disambiguate.
37- *
38- * +--------------+
39- * +------------------+ +------------+ +------>| efi_pe_entry |
40- * | efi32_pe_entry |---->| | | +-----------+--+
41- * +------------------+ | | +------+----------------+ |
42- * | startup_32 |---->| startup_64_mixed_mode | |
43- * +------------------+ | | +------+----------------+ |
44- * | efi32_stub_entry |---->| | | |
45- * +------------------+ +------------+ | |
46- * V |
47- * +------------+ +----------------+ |
48- * | startup_64 |<----| efi_stub_entry |<--------+
49- * +------------+ +----------------+
50- */
51- SYM_FUNC_START(startup_64_mixed_mode)
52- lea efi32_boot_args(%rip ), %rdx
53- mov 0 (%rdx ), %edi
54- mov 4 (%rdx ), %esi
55-
56- leaq (pte + 5 * PAGE_SIZE)(%rip ), %rax
57- movq %rax , %cr3 // reload after startup_32
58-
59- /* Switch to the firmware's stack */
60- movl efi32_boot_sp(%rip ), %esp
61- andl $~7 , %esp
62-
63- mov 8 (%rdx ), %edx // saved bootparams pointer
64- call efi_stub_entry
65- SYM_FUNC_END(startup_64_mixed_mode)
66-
6727SYM_FUNC_START(__efi64_thunk)
6828 push %rbp
6929 push %rbx
7030
71- movl %ds , %eax
72- push %rax
73- movl %es , %eax
74- push %rax
75- movl %ss , %eax
76- push %rax
77-
7831 /* Copy args passed on stack */
79- movq 0x30 (%rsp ), %rbp
80- movq 0x38 (%rsp ), %rbx
81- movq 0x40 (%rsp ), %rax
32+ movq 0x18 (%rsp ), %rbp
33+ movq 0x20 (%rsp ), %rbx
34+ movq 0x28 (%rsp ), %rax
8235
8336 /*
8437 * Convert x86-64 ABI params to i386 ABI
@@ -93,44 +46,14 @@ SYM_FUNC_START(__efi64_thunk)
9346 movl %ebx , 0x18 (%rsp )
9447 movl %eax , 0x1c (%rsp )
9548
96- leaq 0x20 (%rsp ), %rbx
97- sgdt (%rbx )
98- sidt 16 (%rbx )
99-
10049 leaq 1f(%rip ), %rbp
50+ movl %cs , %ebx
10151
102- /*
103- * Switch to IDT and GDT with 32-bit segments. These are the firmware
104- * GDT and IDT that were installed when the kernel started executing.
105- * The pointers were saved by the efi32_entry() routine below.
106- *
107- * Pass the saved DS selector to the 32-bit code, and use far return to
108- * restore the saved CS selector.
109- */
110- lidt efi32_boot_idt(%rip )
111- lgdt efi32_boot_gdt(%rip )
112-
113- movzwl efi32_boot_ds(%rip ), %edx
114- movzwq efi32_boot_cs(%rip ), %rax
115- pushq %rax
116- leaq efi_enter32(%rip ), %rax
117- pushq %rax
118- lretq
52+ ljmpl *efi32_call(%rip )
11953
120541: addq $64 , %rsp
12155 movq %rdi , %rax
12256
123- pop %rbx
124- movl %ebx , %ss
125- pop %rbx
126- movl %ebx , %es
127- pop %rbx
128- movl %ebx , %ds
129- /* Clear out 32-bit selector from FS and GS */
130- xorl %ebx , %ebx
131- movl %ebx , %fs
132- movl %ebx , %gs
133-
13457 pop %rbx
13558 pop %rbp
13659 RET
@@ -141,7 +64,6 @@ SYM_FUNC_END(__efi64_thunk)
14164SYM_FUNC_START(efi32_stub_entry)
14265 call 1f
143661: popl %ecx
144- leal (efi32_boot_args - 1b)(%ecx ), %ebx
14567
14668 /* Clear BSS */
14769 xorl %eax , %eax
@@ -153,11 +75,8 @@ SYM_FUNC_START(efi32_stub_entry)
15375 rep stosl
15476
15577 add $0x4 , %esp /* Discard return address */
156- popl %ecx
157- popl %edx
158- popl %esi
159- movl %esi , 8 (%ebx )
160- jmp efi32_entry
78+ movl 8 (%esp ), %ebx /* struct boot_params pointer */
79+ jmp efi32_startup
16180SYM_FUNC_END(efi32_stub_entry)
16281#endif
16382
@@ -167,13 +86,6 @@ SYM_FUNC_END(efi32_stub_entry)
16786 * The stack should represent the 32-bit calling convention.
16887 */
16988SYM_FUNC_START_LOCAL (efi_enter32)
170- /* Load firmware selector into data and stack segment registers */
171- movl %edx , %ds
172- movl %edx , %es
173- movl %edx , %fs
174- movl %edx , %gs
175- movl %edx , %ss
176-
17789 /* Disable paging */
17890 movl %cr0 , %eax
17991 btrl $X86_CR0_PG_BIT, %eax
@@ -190,21 +102,9 @@ SYM_FUNC_START_LOCAL(efi_enter32)
190102 /* We must preserve return value */
191103 movl %eax , %edi
192104
193- /*
194- * Some firmware will return with interrupts enabled. Be sure to
195- * disable them before we switch GDTs and IDTs.
196- */
197- cli
198-
199- lidtl 16 (%ebx )
200- lgdtl (%ebx )
201-
202- xorl %eax , %eax
203- lldt %ax
204-
205105 call efi32_enable_long_mode
206106
207- pushl $__KERNEL_CS
107+ pushl %ebx
208108 pushl %ebp
209109 lret
210110SYM_FUNC_END(efi_enter32)
@@ -230,50 +130,56 @@ SYM_FUNC_START_LOCAL(efi32_enable_long_mode)
230130SYM_FUNC_END(efi32_enable_long_mode)
231131
232132/*
233- * This is the common EFI stub entry point for mixed mode.
133+ * This is the common EFI stub entry point for mixed mode. It sets up the GDT
134+ * and page tables needed for 64-bit execution, after which it calls the
135+ * common 64-bit EFI entrypoint efi_stub_entry().
234136 *
235- * Arguments: %ecx image handle
236- * %edx EFI system table pointer
137+ * Arguments: 0(%esp) image handle
138+ * 4(%esp) EFI system table pointer
139+ * %ebx struct boot_params pointer (or NULL)
237140 *
238141 * Since this is the point of no return for ordinary execution, no registers
239142 * are considered live except for the function parameters. [Note that the EFI
240143 * stub may still exit and return to the firmware using the Exit() EFI boot
241144 * service.]
242145 */
243- SYM_FUNC_START_LOCAL (efi32_entry)
244- call 1f
245- 1: pop %ebx
246-
247- /* Save firmware GDTR and code/data selectors */
248- sgdtl (efi32_boot_gdt - 1b)(%ebx )
249- movw %cs , (efi32_boot_cs - 1b)(%ebx )
250- movw %ds , (efi32_boot_ds - 1b)(%ebx )
251-
252- /* Store firmware IDT descriptor */
253- sidtl (efi32_boot_idt - 1b)(%ebx )
254-
255- /* Store firmware stack pointer */
256- movl %esp , (efi32_boot_sp - 1b)(%ebx )
257-
258- /* Store boot arguments */
259- leal (efi32_boot_args - 1b)(%ebx ), %ebx
260- movl %ecx , 0 (%ebx )
261- movl %edx , 4 (%ebx )
262- movb $0x0 , 12 (%ebx ) // efi_is64
263-
264- /*
265- * Allocate some memory for a temporary struct boot_params, which only
266- * needs the minimal pieces that startup_32() relies on.
267- */
268- subl $PARAM_SIZE, %esp
269- movl %esp , %esi
270- movl $PAGE_SIZE, BP_kernel_alignment(%esi )
271- movl $_end - 1b, BP_init_size(%esi )
272- subl $startup_32 - 1b, BP_init_size(%esi )
146+ SYM_FUNC_START_LOCAL (efi32_startup)
147+ movl %esp , %ebp
148+
149+ subl $8 , %esp
150+ sgdtl (%esp ) /* Save GDT descriptor to the stack */
151+ movl 2 (%esp ), %esi /* Existing GDT pointer */
152+ movzwl (%esp ), %ecx /* Existing GDT limit */
153+ inc %ecx /* Existing GDT size */
154+ andl $~7 , %ecx /* Ensure size is multiple of 8 */
155+
156+ subl %ecx , %esp /* Allocate new GDT */
157+ andl $~15 , %esp /* Realign the stack */
158+ movl %esp , %edi /* New GDT address */
159+ leal 7 (%ecx ), %eax /* New GDT limit */
160+ pushw %cx /* Push 64-bit CS (for LJMP below) */
161+ pushl %edi /* Push new GDT address */
162+ pushw %ax /* Push new GDT limit */
163+
164+ /* Copy GDT to the stack and add a 64-bit code segment at the end */
165+ movl $GDT_ENTRY(DESC_CODE64, 0 , 0xfffff ) & 0xffffffff , (%edi ,%ecx )
166+ movl $GDT_ENTRY(DESC_CODE64, 0 , 0xfffff ) >> 32 , 4 (%edi ,%ecx )
167+ shrl $2 , %ecx
168+ cld
169+ rep movsl /* Copy the firmware GDT */
170+ lgdtl (%esp ) /* Switch to the new GDT */
273171
274172 call 1f
2751731: pop %edi
276174
175+ /* Record mixed mode entry */
176+ movb $0x0 , (efi_is64 - 1b)(%edi )
177+
178+ /* Set up indirect far call to re-enter 32-bit mode */
179+ leal (efi32_call - 1b)(%edi ), %eax
180+ addl %eax , (%eax )
181+ movw %cs , 4 (%eax )
182+
277183 /* Disable paging */
278184 movl %cr0 , %eax
279185 btrl $X86_CR0_PG_BIT, %eax
@@ -297,8 +203,17 @@ SYM_FUNC_START_LOCAL(efi32_entry)
297203 movl %edx , (%eax )
298204 movl %eax , %cr3
299205
300- jmp startup_32
301- SYM_FUNC_END(efi32_entry)
206+ call efi32_enable_long_mode
207+
208+ /* Set up far jump to 64-bit mode (CS is already on the stack) */
209+ leal (efi_stub_entry - 1b)(%edi ), %eax
210+ movl %eax , 2 (%esp )
211+
212+ movl 0 (%ebp ), %edi
213+ movl 4 (%ebp ), %esi
214+ movl %ebx , %edx
215+ ljmpl *2 (%esp )
216+ SYM_FUNC_END(efi32_startup)
302217
303218/*
304219 * efi_status_t efi32_pe_entry(efi_handle_t image_handle,
@@ -313,10 +228,8 @@ SYM_FUNC_START(efi32_pe_entry)
313228 btl $29 , %edx // check long mode bit
314229 jnc 1f
315230 leal 8 (%esp ), %esp // preserve stack alignment
316- movl (%esp ), %ecx // image_handle
317- movl 4 (%esp ), %edx // sys_table
318- jmp efi32_entry // pass %ecx, %edx
319- // no other registers remain live
231+ xor %ebx , %ebx // no struct boot_params pointer
232+ jmp efi32_startup // only ESP and EBX remain live
3202331: movl $0x80000003 , %eax // EFI_UNSUPPORTED
321234 popl %ebx
322235 RET
@@ -332,20 +245,10 @@ SYM_FUNC_END(efi64_stub_entry)
332245
333246 .data
334247 .balign 8
335- SYM_DATA_START_LOCAL (efi32_boot_gdt)
336- .word 0
337- .quad 0
338- SYM_DATA_END(efi32_boot_gdt)
339-
340- SYM_DATA_START_LOCAL (efi32_boot_idt)
341- .word 0
342- .quad 0
343- SYM_DATA_END(efi32_boot_idt)
344-
345- SYM_DATA_LOCAL (efi32_boot_cs, .word 0 )
346- SYM_DATA_LOCAL (efi32_boot_ds, .word 0 )
347- SYM_DATA_LOCAL (efi32_boot_sp, .long 0 )
348- SYM_DATA_LOCAL (efi32_boot_args, .long 0 , 0 , 0 )
248+ SYM_DATA_START_LOCAL (efi32_call)
249+ .long efi_enter32 - .
250+ .word 0x0
251+ SYM_DATA_END(efi32_call)
349252SYM_DATA(efi_is64, .byte 1 )
350253
351254 .bss
0 commit comments