arm64: efi: Avoid workqueue to check whether EFI runtime is live
authorArd Biesheuvel <ardb@kernel.org>
Fri, 28 Oct 2022 14:39:14 +0000 (16:39 +0200)
committerArd Biesheuvel <ardb@kernel.org>
Mon, 16 Jan 2023 14:27:31 +0000 (15:27 +0100)
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: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
arch/arm64/include/asm/efi.h
arch/arm64/kernel/efi-rt-wrapper.S
arch/arm64/kernel/efi.c

index 31d13a6001df49c482a6bd426d0a8718288746ad..de4ff90785b2c96a15ad08484dedbda289951fae 100644 (file)
@@ -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 u64 *efi_rt_stack_top;
 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)
 
 /*
index d872d18101d837508d1a4dd2cef742cc7c83e241..e8ae803662cf14d1de635e2f89fe062d3a72209d 100644 (file)
@@ -46,7 +46,10 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
        mov     x4, x6
        blr     x8
 
+       mov     x16, sp
        mov     sp, x29
+       str     xzr, [x16, #8]                  // clear recorded task SP value
+
        ldp     x1, x2, [sp, #16]
        cmp     x2, x18
        ldp     x29, x30, [sp], #112
@@ -71,6 +74,9 @@ SYM_FUNC_END(__efi_rt_asm_wrapper)
 SYM_CODE_START(__efi_rt_asm_recover)
        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     x21, x22, [sp, #48]
        ldp     x23, x24, [sp, #64]
index fab05de2e12dd5d8ad28b1a647fea66c6ac1651e..b273900f4566858710bee8d427721accc55fa4b4 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/init.h>
 
 #include <asm/efi.h>
+#include <asm/stacktrace.h>
 
 static bool region_is_misaligned(const efi_memory_desc_t *md)
 {
@@ -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)
 {
         /* 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;
 
        pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg);