forked from Minki/linux
62fa6e69a4
We really only need one phys and one virt function call, and then only one assembly function to make firmware calls. Since we are not using the C type system anyway, we're not really losing much by deleting the macros apart from no longer having a check that we are passing the correct number of parameters. The lack of duplicated code seems like a worthwhile trade-off. Cc: Ricardo Neri <ricardo.neri-calderon@linux.intel.com> Cc: Borislav Petkov <bp@suse.de> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
260 lines
4.4 KiB
ArmAsm
260 lines
4.4 KiB
ArmAsm
/*
|
|
* Function calling ABI conversion from Linux to EFI for x86_64
|
|
*
|
|
* Copyright (C) 2007 Intel Corp
|
|
* Bibo Mao <bibo.mao@intel.com>
|
|
* Huang Ying <ying.huang@intel.com>
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/segment.h>
|
|
#include <asm/msr.h>
|
|
#include <asm/processor-flags.h>
|
|
#include <asm/page_types.h>
|
|
|
|
#define SAVE_XMM \
|
|
mov %rsp, %rax; \
|
|
subq $0x70, %rsp; \
|
|
and $~0xf, %rsp; \
|
|
mov %rax, (%rsp); \
|
|
mov %cr0, %rax; \
|
|
clts; \
|
|
mov %rax, 0x8(%rsp); \
|
|
movaps %xmm0, 0x60(%rsp); \
|
|
movaps %xmm1, 0x50(%rsp); \
|
|
movaps %xmm2, 0x40(%rsp); \
|
|
movaps %xmm3, 0x30(%rsp); \
|
|
movaps %xmm4, 0x20(%rsp); \
|
|
movaps %xmm5, 0x10(%rsp)
|
|
|
|
#define RESTORE_XMM \
|
|
movaps 0x60(%rsp), %xmm0; \
|
|
movaps 0x50(%rsp), %xmm1; \
|
|
movaps 0x40(%rsp), %xmm2; \
|
|
movaps 0x30(%rsp), %xmm3; \
|
|
movaps 0x20(%rsp), %xmm4; \
|
|
movaps 0x10(%rsp), %xmm5; \
|
|
mov 0x8(%rsp), %rsi; \
|
|
mov %rsi, %cr0; \
|
|
mov (%rsp), %rsp
|
|
|
|
/* stolen from gcc */
|
|
.macro FLUSH_TLB_ALL
|
|
movq %r15, efi_scratch(%rip)
|
|
movq %r14, efi_scratch+8(%rip)
|
|
movq %cr4, %r15
|
|
movq %r15, %r14
|
|
andb $0x7f, %r14b
|
|
movq %r14, %cr4
|
|
movq %r15, %cr4
|
|
movq efi_scratch+8(%rip), %r14
|
|
movq efi_scratch(%rip), %r15
|
|
.endm
|
|
|
|
.macro SWITCH_PGT
|
|
cmpb $0, efi_scratch+24(%rip)
|
|
je 1f
|
|
movq %r15, efi_scratch(%rip) # r15
|
|
# save previous CR3
|
|
movq %cr3, %r15
|
|
movq %r15, efi_scratch+8(%rip) # prev_cr3
|
|
movq efi_scratch+16(%rip), %r15 # EFI pgt
|
|
movq %r15, %cr3
|
|
1:
|
|
.endm
|
|
|
|
.macro RESTORE_PGT
|
|
cmpb $0, efi_scratch+24(%rip)
|
|
je 2f
|
|
movq efi_scratch+8(%rip), %r15
|
|
movq %r15, %cr3
|
|
movq efi_scratch(%rip), %r15
|
|
FLUSH_TLB_ALL
|
|
2:
|
|
.endm
|
|
|
|
ENTRY(efi_call)
|
|
SAVE_XMM
|
|
mov (%rsp), %rax
|
|
mov 8(%rax), %rax
|
|
subq $48, %rsp
|
|
mov %r9, 32(%rsp)
|
|
mov %rax, 40(%rsp)
|
|
mov %r8, %r9
|
|
mov %rcx, %r8
|
|
mov %rsi, %rcx
|
|
SWITCH_PGT
|
|
call *%rdi
|
|
RESTORE_PGT
|
|
addq $48, %rsp
|
|
RESTORE_XMM
|
|
ret
|
|
ENDPROC(efi_call)
|
|
|
|
#ifdef CONFIG_EFI_MIXED
|
|
|
|
/*
|
|
* We run this function from the 1:1 mapping.
|
|
*
|
|
* This function must be invoked with a 1:1 mapped stack.
|
|
*/
|
|
ENTRY(__efi64_thunk)
|
|
movl %ds, %eax
|
|
push %rax
|
|
movl %es, %eax
|
|
push %rax
|
|
movl %ss, %eax
|
|
push %rax
|
|
|
|
subq $32, %rsp
|
|
movl %esi, 0x0(%rsp)
|
|
movl %edx, 0x4(%rsp)
|
|
movl %ecx, 0x8(%rsp)
|
|
movq %r8, %rsi
|
|
movl %esi, 0xc(%rsp)
|
|
movq %r9, %rsi
|
|
movl %esi, 0x10(%rsp)
|
|
|
|
sgdt save_gdt(%rip)
|
|
|
|
leaq 1f(%rip), %rbx
|
|
movq %rbx, func_rt_ptr(%rip)
|
|
|
|
/* Switch to gdt with 32-bit segments */
|
|
movl 64(%rsp), %eax
|
|
lgdt (%rax)
|
|
|
|
leaq efi_enter32(%rip), %rax
|
|
pushq $__KERNEL_CS
|
|
pushq %rax
|
|
lretq
|
|
|
|
1: addq $32, %rsp
|
|
|
|
lgdt save_gdt(%rip)
|
|
|
|
pop %rbx
|
|
movl %ebx, %ss
|
|
pop %rbx
|
|
movl %ebx, %es
|
|
pop %rbx
|
|
movl %ebx, %ds
|
|
|
|
/*
|
|
* Convert 32-bit status code into 64-bit.
|
|
*/
|
|
test %rax, %rax
|
|
jz 1f
|
|
movl %eax, %ecx
|
|
andl $0x0fffffff, %ecx
|
|
andl $0xf0000000, %eax
|
|
shl $32, %rax
|
|
or %rcx, %rax
|
|
1:
|
|
ret
|
|
ENDPROC(__efi64_thunk)
|
|
|
|
ENTRY(efi_exit32)
|
|
movq func_rt_ptr(%rip), %rax
|
|
push %rax
|
|
mov %rdi, %rax
|
|
ret
|
|
ENDPROC(efi_exit32)
|
|
|
|
.code32
|
|
/*
|
|
* EFI service pointer must be in %edi.
|
|
*
|
|
* The stack should represent the 32-bit calling convention.
|
|
*/
|
|
ENTRY(efi_enter32)
|
|
movl $__KERNEL_DS, %eax
|
|
movl %eax, %ds
|
|
movl %eax, %es
|
|
movl %eax, %ss
|
|
|
|
/* Reload pgtables */
|
|
movl %cr3, %eax
|
|
movl %eax, %cr3
|
|
|
|
/* Disable paging */
|
|
movl %cr0, %eax
|
|
btrl $X86_CR0_PG_BIT, %eax
|
|
movl %eax, %cr0
|
|
|
|
/* Disable long mode via EFER */
|
|
movl $MSR_EFER, %ecx
|
|
rdmsr
|
|
btrl $_EFER_LME, %eax
|
|
wrmsr
|
|
|
|
call *%edi
|
|
|
|
/* We must preserve return value */
|
|
movl %eax, %edi
|
|
|
|
/*
|
|
* Some firmware will return with interrupts enabled. Be sure to
|
|
* disable them before we switch GDTs.
|
|
*/
|
|
cli
|
|
|
|
movl 68(%esp), %eax
|
|
movl %eax, 2(%eax)
|
|
lgdtl (%eax)
|
|
|
|
movl %cr4, %eax
|
|
btsl $(X86_CR4_PAE_BIT), %eax
|
|
movl %eax, %cr4
|
|
|
|
movl %cr3, %eax
|
|
movl %eax, %cr3
|
|
|
|
movl $MSR_EFER, %ecx
|
|
rdmsr
|
|
btsl $_EFER_LME, %eax
|
|
wrmsr
|
|
|
|
xorl %eax, %eax
|
|
lldt %ax
|
|
|
|
movl 72(%esp), %eax
|
|
pushl $__KERNEL_CS
|
|
pushl %eax
|
|
|
|
/* Enable paging */
|
|
movl %cr0, %eax
|
|
btsl $X86_CR0_PG_BIT, %eax
|
|
movl %eax, %cr0
|
|
lret
|
|
ENDPROC(efi_enter32)
|
|
|
|
.data
|
|
.balign 8
|
|
.global efi32_boot_gdt
|
|
efi32_boot_gdt: .word 0
|
|
.quad 0
|
|
|
|
save_gdt: .word 0
|
|
.quad 0
|
|
func_rt_ptr: .quad 0
|
|
|
|
.global efi_gdt64
|
|
efi_gdt64:
|
|
.word efi_gdt64_end - efi_gdt64
|
|
.long 0 /* Filled out by user */
|
|
.word 0
|
|
.quad 0x0000000000000000 /* NULL descriptor */
|
|
.quad 0x00af9a000000ffff /* __KERNEL_CS */
|
|
.quad 0x00cf92000000ffff /* __KERNEL_DS */
|
|
.quad 0x0080890000000000 /* TS descriptor */
|
|
.quad 0x0000000000000000 /* TS continued */
|
|
efi_gdt64_end:
|
|
#endif /* CONFIG_EFI_MIXED */
|
|
|
|
.data
|
|
ENTRY(efi_scratch)
|
|
.fill 3,8,0
|
|
.byte 0
|
|
.quad 0
|