@@ -80,52 +80,36 @@ SYM_FUNC_START_LOCAL_NOALIGN(startup_64_mixed_mode)
SYM_FUNC_END(startup_64_mixed_mode)
SYM_FUNC_START(__efi64_thunk)
- push %rbp
push %rbx
- /* Copy args passed on stack */
- movq 0x18(%rsp), %rbp
- movq 0x20(%rsp), %rbx
- movq 0x28(%rsp), %rax
-
- /*
- * Convert x86-64 ABI params to i386 ABI
- */
- subq $64, %rsp
- movl %esi, 0x0(%rsp)
- movl %edx, 0x4(%rsp)
- movl %ecx, 0x8(%rsp)
- movl %r8d, 0xc(%rsp)
- movl %r9d, 0x10(%rsp)
- movl %ebp, 0x14(%rsp)
- movl %ebx, 0x18(%rsp)
- movl %eax, 0x1c(%rsp)
-
- leaq 0x20(%rsp), %rbx
- sgdt (%rbx)
- sidt 16(%rbx)
-
- leaq 1f(%rip), %rbp
+ /* Store live GDT and IDT descriptors */
+ subq $16, %rsp
+ sgdt (%rsp)
+ sidt 6(%rsp)
/*
* Switch to IDT and GDT with 32-bit segments. These are the firmware
* GDT and IDT that were installed when the kernel started executing.
* The pointers were saved by the efi32_entry() routine below.
- *
- * Pass the saved DS selector to the 32-bit code, and use far return to
- * restore the saved CS selector.
*/
lidt efi32_boot_idt(%rip)
lgdt efi32_boot_gdt(%rip)
- movzwl efi32_boot_ds(%rip), %edx
- movzwq efi32_boot_cs(%rip), %rax
- pushq %rax
- leaq efi_enter32(%rip), %rax
- pushq %rax
- lretq
+ /* Reload firmware's data segment selectors */
+ movw efi32_boot_ds(%rip), %bx
+ movl %ebx, %ds
+ movl %ebx, %es
+ movl %ebx, %ss
+ movl %ebx, %fs
+ movl %ebx, %gs
+
+ /* Move args #5 and #6 into 32-bit accessible registers */
+ movl %r8d, %eax
+ movl %r9d, %ebx
-1: addq $64, %rsp
+ lcalll *efi32_thunk(%rip)
+
+ addq $16, %rsp
movq %rdi, %rax
/* Clear out 32-bit segment selectors */
@@ -137,7 +121,6 @@ SYM_FUNC_START(__efi64_thunk)
movl %ebx, %gs
pop %rbx
- pop %rbp
RET
SYM_FUNC_END(__efi64_thunk)
@@ -163,17 +146,27 @@ SYM_FUNC_END(efi32_stub_entry)
#endif
/*
- * EFI service pointer must be in %edi.
+ * Called using a far call from 64-bit code, using the x86_64 SysV ABI (except
+ * for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are used instead).
+ *
+ * The first argument (EDI) is a pointer to the boot service or protocol, to
+ * which the remaining arguments are passed, each truncated to 32 bits.
*
- * The stack should represent the 32-bit calling convention.
+ * Entered with ESP+40 pointing to the arguments passed via the stack, and with
+ * the 64-bit mode GDT and IDT descriptors at ESP+8 and ESP+14, respectively.
*/
SYM_FUNC_START_LOCAL(efi_enter32)
- /* Load firmware selector into data and stack segment registers */
- movl %edx, %ds
- movl %edx, %es
- movl %edx, %fs
- movl %edx, %gs
- movl %edx, %ss
+ /*
+ * Convert x86-64 SysV ABI params to i386 ABI
+ */
+ pushl 56(%esp) /* Up to 3 args passed via the caller's stack */
+ pushl 52(%esp)
+ pushl 48(%esp)
+ pushl %ebx /* R9 */
+ pushl %eax /* R8 */
+ pushl %ecx
+ pushl %edx
+ pushl %esi
/* Reload pgtables */
movl %cr3, %eax
@@ -201,8 +194,9 @@ SYM_FUNC_START_LOCAL(efi_enter32)
*/
cli
- lidtl 16(%ebx)
- lgdtl (%ebx)
+ addl $32, %esp
+ lidtl 14(%esp)
+ lgdtl 8(%esp)
movl %cr4, %eax
btsl $(X86_CR4_PAE_BIT), %eax
@@ -219,9 +213,6 @@ SYM_FUNC_START_LOCAL(efi_enter32)
xorl %eax, %eax
lldt %ax
- pushl $__KERNEL_CS
- pushl %ebp
-
/* Enable paging */
movl %cr0, %eax
btsl $X86_CR0_PG_BIT, %eax
@@ -250,6 +241,10 @@ SYM_FUNC_START_LOCAL(efi32_entry)
movw %cs, (efi32_boot_cs - 1b)(%ebx)
movw %ds, (efi32_boot_ds - 1b)(%ebx)
+ /* Fix up absolute reference */
+ leal (efi32_thunk - 1b)(%ebx), %eax
+ addl %eax, (%eax)
+
/* Store firmware IDT descriptor */
sidtl (efi32_boot_idt - 1b)(%ebx)
@@ -351,6 +346,7 @@ SYM_DATA_END(efi32_boot_idt)
.data
.balign 4
+SYM_DATA_LOCAL(efi32_thunk, .long efi_enter32 - .)
SYM_DATA_LOCAL(efi32_boot_cs, .word 0)
SYM_DATA_LOCAL(efi32_boot_ds, .word 0)
SYM_DATA(efi_is64, .byte 1)