提交 ea5e1919 编写于 作者: A Ard Biesheuvel 提交者: Ingo Molnar

efi/x86: Simplify mixed mode call wrapper

Calling 32-bit EFI runtime services from a 64-bit OS involves
switching back to the flat mapping with a stack carved out of
memory that is 32-bit addressable.

There is no need to actually execute the 64-bit part of this
routine from the flat mapping as well, as long as the entry
and return address fit in 32 bits. There is also no need to
preserve part of the calling context in global variables: we
can simply push the old stack pointer value to the new stack,
and keep the return address from the code32 section in EBX.

While at it, move the conditional check whether to invoke
the mixed mode version of SetVirtualAddressMap() into the
64-bit implementation of the wrapper routine.
Signed-off-by: NArd 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-11-ardb@kernel.orgSigned-off-by: NIngo Molnar <mingo@kernel.org>
上级 e5f930fe
...@@ -164,12 +164,6 @@ extern void parse_efi_setup(u64 phys_addr, u32 data_len); ...@@ -164,12 +164,6 @@ extern void parse_efi_setup(u64 phys_addr, u32 data_len);
extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt); extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
extern void efi_thunk_runtime_setup(void); extern void efi_thunk_runtime_setup(void);
extern efi_status_t efi_thunk_set_virtual_address_map(
void *phys_set_virtual_address_map,
unsigned long memory_map_size,
unsigned long descriptor_size,
u32 descriptor_version,
efi_memory_desc_t *virtual_map);
efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size, efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size,
unsigned long descriptor_size, unsigned long descriptor_size,
u32 descriptor_version, u32 descriptor_version,
......
...@@ -1015,21 +1015,10 @@ static void __init __efi_enter_virtual_mode(void) ...@@ -1015,21 +1015,10 @@ static void __init __efi_enter_virtual_mode(void)
efi_sync_low_kernel_mappings(); efi_sync_low_kernel_mappings();
if (!efi_is_mixed()) { status = efi_set_virtual_address_map(efi.memmap.desc_size * count,
status = efi_set_virtual_address_map( efi.memmap.desc_size,
efi.memmap.desc_size * count, efi.memmap.desc_version,
efi.memmap.desc_size, (efi_memory_desc_t *)pa);
efi.memmap.desc_version,
(efi_memory_desc_t *)pa);
} else {
status = efi_thunk_set_virtual_address_map(
efi_phys.set_virtual_address_map,
efi.memmap.desc_size * count,
efi.memmap.desc_size,
efi.memmap.desc_version,
(efi_memory_desc_t *)pa);
}
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_alert("Unable to switch EFI into virtual mode (status=%lx)!\n", pr_alert("Unable to switch EFI into virtual mode (status=%lx)!\n",
status); status);
......
...@@ -626,61 +626,74 @@ void efi_switch_mm(struct mm_struct *mm) ...@@ -626,61 +626,74 @@ void efi_switch_mm(struct mm_struct *mm)
switch_mm(efi_scratch.prev_mm, mm, NULL); switch_mm(efi_scratch.prev_mm, mm, NULL);
} }
#ifdef CONFIG_EFI_MIXED
static DEFINE_SPINLOCK(efi_runtime_lock); static DEFINE_SPINLOCK(efi_runtime_lock);
#define runtime_service32(func) \ /*
({ \ * DS and ES contain user values. We need to save them.
u32 table = (u32)(unsigned long)efi.systab; \ * The 32-bit EFI code needs a valid DS, ES, and SS. There's no
u32 *rt, *___f; \ * need to save the old SS: __KERNEL_DS is always acceptable.
\ */
rt = (u32 *)(table + offsetof(efi_system_table_32_t, runtime)); \ #define __efi_thunk(func, ...) \
___f = (u32 *)(*rt + offsetof(efi_runtime_services_32_t, func)); \ ({ \
*___f; \ efi_runtime_services_32_t *__rt; \
unsigned short __ds, __es; \
efi_status_t ____s; \
\
__rt = (void *)(unsigned long)efi.systab->mixed_mode.runtime; \
\
savesegment(ds, __ds); \
savesegment(es, __es); \
\
loadsegment(ss, __KERNEL_DS); \
loadsegment(ds, __KERNEL_DS); \
loadsegment(es, __KERNEL_DS); \
\
____s = efi64_thunk(__rt->func, __VA_ARGS__); \
\
loadsegment(ds, __ds); \
loadsegment(es, __es); \
\
____s ^= (____s & BIT(31)) | (____s & BIT_ULL(31)) << 32; \
____s; \
}) })
/* /*
* Switch to the EFI page tables early so that we can access the 1:1 * Switch to the EFI page tables early so that we can access the 1:1
* runtime services mappings which are not mapped in any other page * runtime services mappings which are not mapped in any other page
* tables. This function must be called before runtime_service32(). * tables.
* *
* Also, disable interrupts because the IDT points to 64-bit handlers, * Also, disable interrupts because the IDT points to 64-bit handlers,
* which aren't going to function correctly when we switch to 32-bit. * which aren't going to function correctly when we switch to 32-bit.
*/ */
#define efi_thunk(f, ...) \ #define efi_thunk(func...) \
({ \ ({ \
efi_status_t __s; \ efi_status_t __s; \
u32 __func; \
\ \
arch_efi_call_virt_setup(); \ arch_efi_call_virt_setup(); \
\ \
__func = runtime_service32(f); \ __s = __efi_thunk(func); \
__s = efi64_thunk(__func, __VA_ARGS__); \
\ \
arch_efi_call_virt_teardown(); \ arch_efi_call_virt_teardown(); \
\ \
__s; \ __s; \
}) })
efi_status_t efi_thunk_set_virtual_address_map( static efi_status_t __init
void *phys_set_virtual_address_map, efi_thunk_set_virtual_address_map(unsigned long memory_map_size,
unsigned long memory_map_size, unsigned long descriptor_size,
unsigned long descriptor_size, u32 descriptor_version,
u32 descriptor_version, efi_memory_desc_t *virtual_map)
efi_memory_desc_t *virtual_map)
{ {
efi_status_t status; efi_status_t status;
unsigned long flags; unsigned long flags;
u32 func;
efi_sync_low_kernel_mappings(); efi_sync_low_kernel_mappings();
local_irq_save(flags); local_irq_save(flags);
efi_switch_mm(&efi_mm); efi_switch_mm(&efi_mm);
func = (u32)(unsigned long)phys_set_virtual_address_map; status = __efi_thunk(set_virtual_address_map, memory_map_size,
status = efi64_thunk(func, memory_map_size, descriptor_size, descriptor_size, descriptor_version, virtual_map);
descriptor_version, virtual_map);
efi_switch_mm(efi_scratch.prev_mm); efi_switch_mm(efi_scratch.prev_mm);
local_irq_restore(flags); local_irq_restore(flags);
...@@ -983,8 +996,11 @@ efi_thunk_query_capsule_caps(efi_capsule_header_t **capsules, ...@@ -983,8 +996,11 @@ efi_thunk_query_capsule_caps(efi_capsule_header_t **capsules,
return EFI_UNSUPPORTED; return EFI_UNSUPPORTED;
} }
void efi_thunk_runtime_setup(void) void __init efi_thunk_runtime_setup(void)
{ {
if (!IS_ENABLED(CONFIG_EFI_MIXED))
return;
efi.get_time = efi_thunk_get_time; efi.get_time = efi_thunk_get_time;
efi.set_time = efi_thunk_set_time; efi.set_time = efi_thunk_set_time;
efi.get_wakeup_time = efi_thunk_get_wakeup_time; efi.get_wakeup_time = efi_thunk_get_wakeup_time;
...@@ -1000,7 +1016,6 @@ void efi_thunk_runtime_setup(void) ...@@ -1000,7 +1016,6 @@ void efi_thunk_runtime_setup(void)
efi.update_capsule = efi_thunk_update_capsule; efi.update_capsule = efi_thunk_update_capsule;
efi.query_capsule_caps = efi_thunk_query_capsule_caps; efi.query_capsule_caps = efi_thunk_query_capsule_caps;
} }
#endif /* CONFIG_EFI_MIXED */
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,
...@@ -1011,6 +1026,12 @@ efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size, ...@@ -1011,6 +1026,12 @@ efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
unsigned long flags; unsigned long flags;
pgd_t *save_pgd = NULL; pgd_t *save_pgd = NULL;
if (efi_is_mixed())
return efi_thunk_set_virtual_address_map(memory_map_size,
descriptor_size,
descriptor_version,
virtual_map);
if (efi_enabled(EFI_OLD_MEMMAP)) { if (efi_enabled(EFI_OLD_MEMMAP)) {
save_pgd = efi_old_memmap_phys_prolog(); save_pgd = efi_old_memmap_phys_prolog();
if (!save_pgd) if (!save_pgd)
......
...@@ -25,15 +25,16 @@ ...@@ -25,15 +25,16 @@
.text .text
.code64 .code64
SYM_FUNC_START(efi64_thunk) SYM_CODE_START(efi64_thunk)
push %rbp push %rbp
push %rbx push %rbx
/* /*
* Switch to 1:1 mapped 32-bit stack pointer. * Switch to 1:1 mapped 32-bit stack pointer.
*/ */
movq %rsp, efi_saved_sp(%rip) movq %rsp, %rax
movq efi_scratch(%rip), %rsp movq efi_scratch(%rip), %rsp
push %rax
/* /*
* Calculate the physical address of the kernel text. * Calculate the physical address of the kernel text.
...@@ -41,113 +42,31 @@ SYM_FUNC_START(efi64_thunk) ...@@ -41,113 +42,31 @@ SYM_FUNC_START(efi64_thunk)
movq $__START_KERNEL_map, %rax movq $__START_KERNEL_map, %rax
subq phys_base(%rip), %rax subq phys_base(%rip), %rax
/* leaq 1f(%rip), %rbp
* Push some physical addresses onto the stack. This is easier leaq 2f(%rip), %rbx
* to do now in a code64 section while the assembler can address subq %rax, %rbp
* 64-bit values. Note that all the addresses on the stack are
* 32-bit.
*/
subq $16, %rsp
leaq efi_exit32(%rip), %rbx
subq %rax, %rbx
movl %ebx, 8(%rsp)
leaq __efi64_thunk(%rip), %rbx
subq %rax, %rbx subq %rax, %rbx
call *%rbx
movq efi_saved_sp(%rip), %rsp
pop %rbx
pop %rbp
retq
SYM_FUNC_END(efi64_thunk)
/* subq $28, %rsp
* We run this function from the 1:1 mapping. movl %ebx, 0x0(%rsp) /* return address */
* movl %esi, 0x4(%rsp)
* This function must be invoked with a 1:1 mapped stack. movl %edx, 0x8(%rsp)
*/ movl %ecx, 0xc(%rsp)
SYM_FUNC_START_LOCAL(__efi64_thunk) movl %r8d, 0x10(%rsp)
movl %ds, %eax movl %r9d, 0x14(%rsp)
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)
leaq 1f(%rip), %rbx
movq %rbx, func_rt_ptr(%rip)
/* Switch to 32-bit descriptor */ /* Switch to 32-bit descriptor */
pushq $__KERNEL32_CS pushq $__KERNEL32_CS
leaq efi_enter32(%rip), %rax pushq %rdi /* EFI runtime service address */
pushq %rax
lretq lretq
1: addq $32, %rsp 1: movq 24(%rsp), %rsp
pop %rbx pop %rbx
movl %ebx, %ss pop %rbp
pop %rbx retq
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
SYM_FUNC_END(__efi64_thunk)
SYM_FUNC_START_LOCAL(efi_exit32)
movq func_rt_ptr(%rip), %rax
push %rax
mov %rdi, %rax
ret
SYM_FUNC_END(efi_exit32)
.code32 .code32
/* 2: pushl $__KERNEL_CS
* EFI service pointer must be in %edi. pushl %ebp
*
* The stack should represent the 32-bit calling convention.
*/
SYM_FUNC_START_LOCAL(efi_enter32)
movl $__KERNEL_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss
call *%edi
/* We must preserve return value */
movl %eax, %edi
movl 72(%esp), %eax
pushl $__KERNEL_CS
pushl %eax
lret lret
SYM_FUNC_END(efi_enter32) SYM_CODE_END(efi64_thunk)
.data
.balign 8
func_rt_ptr: .quad 0
efi_saved_sp: .quad 0
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册