196 lines
3.5 KiB
ArmAsm
196 lines
3.5 KiB
ArmAsm
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
/*
|
||
|
* Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
|
||
|
*
|
||
|
* Early support for invoking 32-bit EFI services from a 64-bit kernel.
|
||
|
*
|
||
|
* Because this thunking occurs before ExitBootServices() we have to
|
||
|
* restore the firmware's 32-bit GDT and IDT before we make EFI service
|
||
|
* calls.
|
||
|
*
|
||
|
* On the plus side, we don't have to worry about mangling 64-bit
|
||
|
* addresses into 32-bits because we're executing with an identity
|
||
|
* mapped pagetable and haven't transitioned to 64-bit virtual addresses
|
||
|
* yet.
|
||
|
*/
|
||
|
|
||
|
#include <linux/linkage.h>
|
||
|
#include <asm/msr.h>
|
||
|
#include <asm/page_types.h>
|
||
|
#include <asm/processor-flags.h>
|
||
|
#include <asm/segment.h>
|
||
|
|
||
|
.code64
|
||
|
.text
|
||
|
SYM_FUNC_START(__efi64_thunk)
|
||
|
push %rbp
|
||
|
push %rbx
|
||
|
|
||
|
movl %ds, %eax
|
||
|
push %rax
|
||
|
movl %es, %eax
|
||
|
push %rax
|
||
|
movl %ss, %eax
|
||
|
push %rax
|
||
|
|
||
|
/* Copy args passed on stack */
|
||
|
movq 0x30(%rsp), %rbp
|
||
|
movq 0x38(%rsp), %rbx
|
||
|
movq 0x40(%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)
|
||
|
|
||
|
addq $16, %rbx
|
||
|
sidt (%rbx)
|
||
|
|
||
|
leaq 1f(%rip), %rbp
|
||
|
|
||
|
/*
|
||
|
* Switch to IDT and GDT with 32-bit segments. This is the firmware GDT
|
||
|
* and IDT that was installed when the kernel started executing. The
|
||
|
* pointers were saved at the EFI stub entry point in head_64.S.
|
||
|
*
|
||
|
* Pass the saved DS selector to the 32-bit code, and use far return to
|
||
|
* restore the saved CS selector.
|
||
|
*/
|
||
|
leaq efi32_boot_idt(%rip), %rax
|
||
|
lidt (%rax)
|
||
|
leaq efi32_boot_gdt(%rip), %rax
|
||
|
lgdt (%rax)
|
||
|
|
||
|
movzwl efi32_boot_ds(%rip), %edx
|
||
|
movzwq efi32_boot_cs(%rip), %rax
|
||
|
pushq %rax
|
||
|
leaq efi_enter32(%rip), %rax
|
||
|
pushq %rax
|
||
|
lretq
|
||
|
|
||
|
1: addq $64, %rsp
|
||
|
movq %rdi, %rax
|
||
|
|
||
|
pop %rbx
|
||
|
movl %ebx, %ss
|
||
|
pop %rbx
|
||
|
movl %ebx, %es
|
||
|
pop %rbx
|
||
|
movl %ebx, %ds
|
||
|
/* Clear out 32-bit selector from FS and GS */
|
||
|
xorl %ebx, %ebx
|
||
|
movl %ebx, %fs
|
||
|
movl %ebx, %gs
|
||
|
|
||
|
/*
|
||
|
* Convert 32-bit status code into 64-bit.
|
||
|
*/
|
||
|
roll $1, %eax
|
||
|
rorq $1, %rax
|
||
|
|
||
|
pop %rbx
|
||
|
pop %rbp
|
||
|
RET
|
||
|
SYM_FUNC_END(__efi64_thunk)
|
||
|
|
||
|
.code32
|
||
|
/*
|
||
|
* EFI service pointer must be in %edi.
|
||
|
*
|
||
|
* The stack should represent the 32-bit calling convention.
|
||
|
*/
|
||
|
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
|
||
|
|
||
|
/* 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 and IDTs.
|
||
|
*/
|
||
|
cli
|
||
|
|
||
|
lidtl (%ebx)
|
||
|
subl $16, %ebx
|
||
|
|
||
|
lgdtl (%ebx)
|
||
|
|
||
|
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
|
||
|
|
||
|
pushl $__KERNEL_CS
|
||
|
pushl %ebp
|
||
|
|
||
|
/* Enable paging */
|
||
|
movl %cr0, %eax
|
||
|
btsl $X86_CR0_PG_BIT, %eax
|
||
|
movl %eax, %cr0
|
||
|
lret
|
||
|
SYM_FUNC_END(efi_enter32)
|
||
|
|
||
|
.data
|
||
|
.balign 8
|
||
|
SYM_DATA_START(efi32_boot_gdt)
|
||
|
.word 0
|
||
|
.quad 0
|
||
|
SYM_DATA_END(efi32_boot_gdt)
|
||
|
|
||
|
SYM_DATA_START(efi32_boot_idt)
|
||
|
.word 0
|
||
|
.quad 0
|
||
|
SYM_DATA_END(efi32_boot_idt)
|
||
|
|
||
|
SYM_DATA_START(efi32_boot_cs)
|
||
|
.word 0
|
||
|
SYM_DATA_END(efi32_boot_cs)
|
||
|
|
||
|
SYM_DATA_START(efi32_boot_ds)
|
||
|
.word 0
|
||
|
SYM_DATA_END(efi32_boot_ds)
|