mirror of https://gitee.com/openkylin/linux.git
efi/x86: Simplify i386 efi_call_phys() firmware call wrapper
The variadic efi_call_phys() wrapper that exists on i386 was originally created to call into any EFI firmware runtime service, but in practice, we only use it once, to call SetVirtualAddressMap() during early boot. The flexibility provided by the variadic nature also makes it type unsafe, and makes the assembler code more complicated than needed, since it has to deal with an unknown number of arguments living on the stack. So clean this up, by renaming the helper to efi_call_svam(), and dropping the unneeded complexity. Let's also drop the reference to the efi_phys struct and grab the address from the EFI system table directly. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Cc: Andy Lutomirski <luto@kernel.org> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Arvind Sankar <nivedita@alum.mit.edu> Cc: Matthew Garrett <mjg59@google.com> Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20200103113953.9571-9-ardb@kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
6982947045
commit
a46d674068
|
@ -35,9 +35,6 @@
|
||||||
#define ARCH_EFI_IRQ_FLAGS_MASK X86_EFLAGS_IF
|
#define ARCH_EFI_IRQ_FLAGS_MASK X86_EFLAGS_IF
|
||||||
|
|
||||||
#ifdef CONFIG_X86_32
|
#ifdef CONFIG_X86_32
|
||||||
|
|
||||||
extern asmlinkage unsigned long efi_call_phys(void *, ...);
|
|
||||||
|
|
||||||
#define arch_efi_call_virt_setup() \
|
#define arch_efi_call_virt_setup() \
|
||||||
({ \
|
({ \
|
||||||
kernel_fpu_begin(); \
|
kernel_fpu_begin(); \
|
||||||
|
|
|
@ -66,7 +66,8 @@ void __init efi_map_region(efi_memory_desc_t *md)
|
||||||
void __init efi_map_region_fixed(efi_memory_desc_t *md) {}
|
void __init efi_map_region_fixed(efi_memory_desc_t *md) {}
|
||||||
void __init parse_efi_setup(u64 phys_addr, u32 data_len) {}
|
void __init parse_efi_setup(u64 phys_addr, u32 data_len) {}
|
||||||
|
|
||||||
extern struct efi efi_phys;
|
efi_status_t efi_call_svam(efi_set_virtual_address_map_t *__efiapi *,
|
||||||
|
u32, u32, u32, void *);
|
||||||
|
|
||||||
efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
|
efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
|
||||||
unsigned long descriptor_size,
|
unsigned long descriptor_size,
|
||||||
|
@ -89,7 +90,7 @@ efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
|
||||||
|
|
||||||
/* Disable interrupts around EFI calls: */
|
/* Disable interrupts around EFI calls: */
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
status = efi_call_phys(efi_phys.set_virtual_address_map,
|
status = efi_call_svam(&efi.systab->runtime->set_virtual_address_map,
|
||||||
memory_map_size, descriptor_size,
|
memory_map_size, descriptor_size,
|
||||||
descriptor_version, virtual_map);
|
descriptor_version, virtual_map);
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
|
|
|
@ -7,118 +7,43 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/linkage.h>
|
#include <linux/linkage.h>
|
||||||
|
#include <linux/init.h>
|
||||||
#include <asm/page_types.h>
|
#include <asm/page_types.h>
|
||||||
|
|
||||||
/*
|
__INIT
|
||||||
* efi_call_phys(void *, ...) is a function with variable parameters.
|
SYM_FUNC_START(efi_call_svam)
|
||||||
* All the callers of this function assure that all the parameters are 4-bytes.
|
push 8(%esp)
|
||||||
*/
|
push 8(%esp)
|
||||||
|
push %ecx
|
||||||
/*
|
push %edx
|
||||||
* In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
|
|
||||||
* So we'd better save all of them at the beginning of this function and restore
|
|
||||||
* at the end no matter how many we use, because we can not assure EFI runtime
|
|
||||||
* service functions will comply with gcc calling convention, too.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.text
|
|
||||||
SYM_FUNC_START(efi_call_phys)
|
|
||||||
/*
|
|
||||||
* 0. The function can only be called in Linux kernel. So CS has been
|
|
||||||
* set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
|
|
||||||
* the values of these registers are the same. And, the corresponding
|
|
||||||
* GDT entries are identical. So I will do nothing about segment reg
|
|
||||||
* and GDT, but change GDT base register in prolog and epilog.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 1. Now I am running with EIP = <physical address> + PAGE_OFFSET.
|
* Switch to the flat mapped alias of this routine, by jumping to the
|
||||||
* But to make it smoothly switch from virtual mode to flat mode.
|
* address of label '1' after subtracting PAGE_OFFSET from it.
|
||||||
* The mapping of lower virtual memory has been created in prolog and
|
|
||||||
* epilog.
|
|
||||||
*/
|
*/
|
||||||
movl $1f, %edx
|
movl $1f, %edx
|
||||||
subl $__PAGE_OFFSET, %edx
|
subl $__PAGE_OFFSET, %edx
|
||||||
jmp *%edx
|
jmp *%edx
|
||||||
1:
|
1:
|
||||||
|
|
||||||
/*
|
/* disable paging */
|
||||||
* 2. Now on the top of stack is the return
|
|
||||||
* address in the caller of efi_call_phys(), then parameter 1,
|
|
||||||
* parameter 2, ..., param n. To make things easy, we save the return
|
|
||||||
* address of efi_call_phys in a global variable.
|
|
||||||
*/
|
|
||||||
popl %edx
|
|
||||||
movl %edx, saved_return_addr
|
|
||||||
/* get the function pointer into ECX*/
|
|
||||||
popl %ecx
|
|
||||||
movl %ecx, efi_rt_function_ptr
|
|
||||||
movl $2f, %edx
|
|
||||||
subl $__PAGE_OFFSET, %edx
|
|
||||||
pushl %edx
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 3. Clear PG bit in %CR0.
|
|
||||||
*/
|
|
||||||
movl %cr0, %edx
|
movl %cr0, %edx
|
||||||
andl $0x7fffffff, %edx
|
andl $0x7fffffff, %edx
|
||||||
movl %edx, %cr0
|
movl %edx, %cr0
|
||||||
jmp 1f
|
|
||||||
1:
|
|
||||||
|
|
||||||
/*
|
/* convert the stack pointer to a flat mapped address */
|
||||||
* 4. Adjust stack pointer.
|
|
||||||
*/
|
|
||||||
subl $__PAGE_OFFSET, %esp
|
subl $__PAGE_OFFSET, %esp
|
||||||
|
|
||||||
/*
|
/* call the EFI routine */
|
||||||
* 5. Call the physical function.
|
call *(%eax)
|
||||||
*/
|
|
||||||
jmp *%ecx
|
|
||||||
|
|
||||||
2:
|
/* convert ESP back to a kernel VA, and pop the outgoing args */
|
||||||
/*
|
addl $__PAGE_OFFSET + 16, %esp
|
||||||
* 6. After EFI runtime service returns, control will return to
|
|
||||||
* following instruction. We'd better readjust stack pointer first.
|
|
||||||
*/
|
|
||||||
addl $__PAGE_OFFSET, %esp
|
|
||||||
|
|
||||||
/*
|
/* re-enable paging */
|
||||||
* 7. Restore PG bit
|
|
||||||
*/
|
|
||||||
movl %cr0, %edx
|
movl %cr0, %edx
|
||||||
orl $0x80000000, %edx
|
orl $0x80000000, %edx
|
||||||
movl %edx, %cr0
|
movl %edx, %cr0
|
||||||
jmp 1f
|
|
||||||
1:
|
|
||||||
/*
|
|
||||||
* 8. Now restore the virtual mode from flat mode by
|
|
||||||
* adding EIP with PAGE_OFFSET.
|
|
||||||
*/
|
|
||||||
movl $1f, %edx
|
|
||||||
jmp *%edx
|
|
||||||
1:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 9. Balance the stack. And because EAX contain the return value,
|
|
||||||
* we'd better not clobber it.
|
|
||||||
*/
|
|
||||||
leal efi_rt_function_ptr, %edx
|
|
||||||
movl (%edx), %ecx
|
|
||||||
pushl %ecx
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 10. Push the saved return address onto the stack and return.
|
|
||||||
*/
|
|
||||||
leal saved_return_addr, %edx
|
|
||||||
movl (%edx), %ecx
|
|
||||||
pushl %ecx
|
|
||||||
ret
|
ret
|
||||||
SYM_FUNC_END(efi_call_phys)
|
SYM_FUNC_END(efi_call_svam)
|
||||||
.previous
|
|
||||||
|
|
||||||
.data
|
|
||||||
saved_return_addr:
|
|
||||||
.long 0
|
|
||||||
efi_rt_function_ptr:
|
|
||||||
.long 0
|
|
||||||
|
|
Loading…
Reference in New Issue