Commit 8a9a1a18 authored by Ard Biesheuvel's avatar Ard Biesheuvel

arm64: efi: Avoid workqueue to check whether EFI runtime is live

Comparing current_work() against efi_rts_work.work is sufficient to
decide whether current is currently running EFI runtime services code at
any level in its call stack.

However, there are other potential users of the EFI runtime stack, such
as the ACPI subsystem, which may invoke efi_call_virt_pointer()
directly, and so any sync exceptions occurring in firmware during those
calls are currently misidentified.

So instead, let's check whether the stashed value of the thread stack
pointer points into current's thread stack. This can only be the case if
current was interrupted while running EFI runtime code. Note that this
implies that we should clear the stashed value after switching back, to
avoid false positives.
Reviewed-by: default avatarMark Rutland <mark.rutland@arm.com>
Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
parent d3f45053
...@@ -48,8 +48,17 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); ...@@ -48,8 +48,17 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
}) })
extern spinlock_t efi_rt_lock; extern spinlock_t efi_rt_lock;
extern u64 *efi_rt_stack_top;
efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...); efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...);
/*
* efi_rt_stack_top[-1] contains the value the stack pointer had before
* switching to the EFI runtime stack.
*/
#define current_in_efi() \
(!preemptible() && efi_rt_stack_top != NULL && \
on_task_stack(current, READ_ONCE(efi_rt_stack_top[-1]), 1))
#define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) #define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
/* /*
......
...@@ -46,7 +46,10 @@ SYM_FUNC_START(__efi_rt_asm_wrapper) ...@@ -46,7 +46,10 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
mov x4, x6 mov x4, x6
blr x8 blr x8
mov x16, sp
mov sp, x29 mov sp, x29
str xzr, [x16, #8] // clear recorded task SP value
ldp x1, x2, [sp, #16] ldp x1, x2, [sp, #16]
cmp x2, x18 cmp x2, x18
ldp x29, x30, [sp], #112 ldp x29, x30, [sp], #112
...@@ -71,6 +74,9 @@ SYM_FUNC_END(__efi_rt_asm_wrapper) ...@@ -71,6 +74,9 @@ SYM_FUNC_END(__efi_rt_asm_wrapper)
SYM_CODE_START(__efi_rt_asm_recover) SYM_CODE_START(__efi_rt_asm_recover)
mov sp, x30 mov sp, x30
ldr_l x16, efi_rt_stack_top // clear recorded task SP value
str xzr, [x16, #-8]
ldp x19, x20, [sp, #32] ldp x19, x20, [sp, #32]
ldp x21, x22, [sp, #48] ldp x21, x22, [sp, #48]
ldp x23, x24, [sp, #64] ldp x23, x24, [sp, #64]
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <asm/efi.h> #include <asm/efi.h>
#include <asm/stacktrace.h>
static bool region_is_misaligned(const efi_memory_desc_t *md) static bool region_is_misaligned(const efi_memory_desc_t *md)
{ {
...@@ -154,7 +155,7 @@ asmlinkage efi_status_t __efi_rt_asm_recover(void); ...@@ -154,7 +155,7 @@ asmlinkage efi_status_t __efi_rt_asm_recover(void);
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg) bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
{ {
/* Check whether the exception occurred while running the firmware */ /* Check whether the exception occurred while running the firmware */
if (current_work() != &efi_rts_work.work || regs->pc >= TASK_SIZE_64) if (!current_in_efi() || regs->pc >= TASK_SIZE_64)
return false; return false;
pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg); pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment