--- /dev/null
+From 8ac4d85ae44fdcd01aae5f4889b08a7600801fd2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 9 Dec 2022 12:10:13 +0100
+Subject: arm64: efi: Account for the EFI runtime stack in stack unwinder
+
+From: Ard Biesheuvel <ardb@kernel.org>
+
+[ Upstream commit 7ea55715c421d22c1b63f7129cae6a654091b695 ]
+
+The EFI runtime services run from a dedicated stack now, and so the
+stack unwinder needs to be informed about this.
+
+Acked-by: Mark Rutland <mark.rutland@arm.com>
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/include/asm/stacktrace.h | 15 +++++++++++++++
+ arch/arm64/kernel/stacktrace.c | 12 ++++++++++++
+ 2 files changed, 27 insertions(+)
+
+diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h
+index 5a0edb064ea4..327cdcfcb1db 100644
+--- a/arch/arm64/include/asm/stacktrace.h
++++ b/arch/arm64/include/asm/stacktrace.h
+@@ -104,4 +104,19 @@ static inline struct stack_info stackinfo_get_sdei_critical(void)
+ #define stackinfo_get_sdei_critical() stackinfo_get_unknown()
+ #endif
+
++#ifdef CONFIG_EFI
++extern u64 *efi_rt_stack_top;
++
++static inline struct stack_info stackinfo_get_efi(void)
++{
++ unsigned long high = (u64)efi_rt_stack_top;
++ unsigned long low = high - THREAD_SIZE;
++
++ return (struct stack_info) {
++ .low = low,
++ .high = high,
++ };
++}
++#endif
++
+ #endif /* __ASM_STACKTRACE_H */
+diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
+index 117e2c180f3c..83154303e682 100644
+--- a/arch/arm64/kernel/stacktrace.c
++++ b/arch/arm64/kernel/stacktrace.c
+@@ -5,6 +5,7 @@
+ * Copyright (C) 2012 ARM Ltd.
+ */
+ #include <linux/kernel.h>
++#include <linux/efi.h>
+ #include <linux/export.h>
+ #include <linux/ftrace.h>
+ #include <linux/sched.h>
+@@ -12,6 +13,7 @@
+ #include <linux/sched/task_stack.h>
+ #include <linux/stacktrace.h>
+
++#include <asm/efi.h>
+ #include <asm/irq.h>
+ #include <asm/stack_pointer.h>
+ #include <asm/stacktrace.h>
+@@ -186,6 +188,13 @@ void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl)
+ : stackinfo_get_unknown(); \
+ })
+
++#define STACKINFO_EFI \
++ ({ \
++ ((task == current) && current_in_efi()) \
++ ? stackinfo_get_efi() \
++ : stackinfo_get_unknown(); \
++ })
++
+ noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry,
+ void *cookie, struct task_struct *task,
+ struct pt_regs *regs)
+@@ -199,6 +208,9 @@ noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry,
+ #if defined(CONFIG_VMAP_STACK) && defined(CONFIG_ARM_SDE_INTERFACE)
+ STACKINFO_SDEI(normal),
+ STACKINFO_SDEI(critical),
++#endif
++#ifdef CONFIG_EFI
++ STACKINFO_EFI,
+ #endif
+ };
+ struct unwind_state state = {
+--
+2.39.0
+
--- /dev/null
+From 42d095c4a8567b8bf8d97c16970f79003a2c1a6c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 28 Oct 2022 16:39:14 +0200
+Subject: arm64: efi: Avoid workqueue to check whether EFI runtime is live
+
+From: Ard Biesheuvel <ardb@kernel.org>
+
+[ Upstream commit 8a9a1a18731eb123e35f48176380a18b9782845e ]
+
+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>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/include/asm/efi.h | 9 +++++++++
+ arch/arm64/kernel/efi-rt-wrapper.S | 6 ++++++
+ arch/arm64/kernel/efi.c | 3 ++-
+ 3 files changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
+index 0edaf8e385b8..b13c22046de5 100644
+--- a/arch/arm64/include/asm/efi.h
++++ b/arch/arm64/include/asm/efi.h
+@@ -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)
+
+ /*
+diff --git a/arch/arm64/kernel/efi-rt-wrapper.S b/arch/arm64/kernel/efi-rt-wrapper.S
+index d872d18101d8..e8ae803662cf 100644
+--- a/arch/arm64/kernel/efi-rt-wrapper.S
++++ b/arch/arm64/kernel/efi-rt-wrapper.S
+@@ -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]
+diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
+index fab05de2e12d..b273900f4566 100644
+--- a/arch/arm64/kernel/efi.c
++++ b/arch/arm64/kernel/efi.c
+@@ -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);
+--
+2.39.0
+
--- /dev/null
+From d6d33475c308f7e389651c291ee31676fe6096d8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 28 Oct 2022 16:39:14 +0200
+Subject: arm64: efi: Recover from synchronous exceptions occurring in firmware
+
+From: Ard Biesheuvel <ardb@kernel.org>
+
+[ Upstream commit e8dfdf3162eb549d064b8c10b1564f7e8ee82591 ]
+
+Unlike x86, which has machinery to deal with page faults that occur
+during the execution of EFI runtime services, arm64 has nothing like
+that, and a synchronous exception raised by firmware code brings down
+the whole system.
+
+With more EFI based systems appearing that were not built to run Linux
+(such as the Windows-on-ARM laptops based on Qualcomm SOCs), as well as
+the introduction of PRM (platform specific firmware routines that are
+callable just like EFI runtime services), we are more likely to run into
+issues of this sort, and it is much more likely that we can identify and
+work around such issues if they don't bring down the system entirely.
+
+Since we already use a EFI runtime services call wrapper in assembler,
+we can quite easily add some code that captures the execution state at
+the point where the call is made, allowing us to revert to this state
+and proceed execution if the call triggered a synchronous exception.
+
+Given that the kernel and the firmware don't share any data structures
+that could end up in an indeterminate state, we can happily continue
+running, as long as we mark the EFI runtime services as unavailable from
+that point on.
+
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Acked-by: Catalin Marinas <catalin.marinas@arm.com>
+Stable-dep-of: 8a9a1a18731e ("arm64: efi: Avoid workqueue to check whether EFI runtime is live")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/include/asm/efi.h | 8 +++++++
+ arch/arm64/kernel/efi-rt-wrapper.S | 32 +++++++++++++++++++++----
+ arch/arm64/kernel/efi.c | 22 +++++++++++++++++
+ arch/arm64/mm/fault.c | 4 ++++
+ drivers/firmware/efi/runtime-wrappers.c | 1 +
+ 5 files changed, 62 insertions(+), 5 deletions(-)
+
+diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
+index b9f3165075c9..0edaf8e385b8 100644
+--- a/arch/arm64/include/asm/efi.h
++++ b/arch/arm64/include/asm/efi.h
+@@ -14,8 +14,16 @@
+
+ #ifdef CONFIG_EFI
+ extern void efi_init(void);
++
++bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg);
+ #else
+ #define efi_init()
++
++static inline
++bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
++{
++ return false;
++}
+ #endif
+
+ int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
+diff --git a/arch/arm64/kernel/efi-rt-wrapper.S b/arch/arm64/kernel/efi-rt-wrapper.S
+index 2d3c4b02393e..d872d18101d8 100644
+--- a/arch/arm64/kernel/efi-rt-wrapper.S
++++ b/arch/arm64/kernel/efi-rt-wrapper.S
+@@ -7,7 +7,7 @@
+ #include <asm/assembler.h>
+
+ SYM_FUNC_START(__efi_rt_asm_wrapper)
+- stp x29, x30, [sp, #-32]!
++ stp x29, x30, [sp, #-112]!
+ mov x29, sp
+
+ /*
+@@ -17,11 +17,21 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
+ */
+ stp x1, x18, [sp, #16]
+
++ /*
++ * Preserve all callee saved registers and preserve the stack pointer
++ * value at the base of the EFI runtime stack so we can recover from
++ * synchronous exceptions occurring while executing the firmware
++ * routines.
++ */
++ stp x19, x20, [sp, #32]
++ stp x21, x22, [sp, #48]
++ stp x23, x24, [sp, #64]
++ stp x25, x26, [sp, #80]
++ stp x27, x28, [sp, #96]
++
+ ldr_l x16, efi_rt_stack_top
+ mov sp, x16
+-#ifdef CONFIG_SHADOW_CALL_STACK
+- str x18, [sp, #-16]!
+-#endif
++ stp x18, x29, [sp, #-16]!
+
+ /*
+ * We are lucky enough that no EFI runtime services take more than
+@@ -39,7 +49,7 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
+ mov sp, x29
+ ldp x1, x2, [sp, #16]
+ cmp x2, x18
+- ldp x29, x30, [sp], #32
++ ldp x29, x30, [sp], #112
+ b.ne 0f
+ ret
+ 0:
+@@ -57,3 +67,15 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
+
+ b efi_handle_corrupted_x18 // tail call
+ SYM_FUNC_END(__efi_rt_asm_wrapper)
++
++SYM_CODE_START(__efi_rt_asm_recover)
++ mov sp, x30
++
++ ldp x19, x20, [sp, #32]
++ ldp x21, x22, [sp, #48]
++ ldp x23, x24, [sp, #64]
++ ldp x25, x26, [sp, #80]
++ ldp x27, x28, [sp, #96]
++ ldp x29, x30, [sp], #112
++ ret
++SYM_CODE_END(__efi_rt_asm_recover)
+diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
+index 386bd81ca12b..fab05de2e12d 100644
+--- a/arch/arm64/kernel/efi.c
++++ b/arch/arm64/kernel/efi.c
+@@ -149,6 +149,28 @@ DEFINE_SPINLOCK(efi_rt_lock);
+
+ asmlinkage u64 *efi_rt_stack_top __ro_after_init;
+
++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)
++ return false;
++
++ pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg);
++ add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
++ clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
++
++ regs->regs[0] = EFI_ABORTED;
++ regs->regs[30] = efi_rt_stack_top[-1];
++ regs->pc = (u64)__efi_rt_asm_recover;
++
++ if (IS_ENABLED(CONFIG_SHADOW_CALL_STACK))
++ regs->regs[18] = efi_rt_stack_top[-2];
++
++ return true;
++}
++
+ /* EFI requires 8 KiB of stack space for runtime services */
+ static_assert(THREAD_SIZE >= SZ_8K);
+
+diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
+index 74f76514a48d..3eb2825d08cf 100644
+--- a/arch/arm64/mm/fault.c
++++ b/arch/arm64/mm/fault.c
+@@ -30,6 +30,7 @@
+ #include <asm/bug.h>
+ #include <asm/cmpxchg.h>
+ #include <asm/cpufeature.h>
++#include <asm/efi.h>
+ #include <asm/exception.h>
+ #include <asm/daifflags.h>
+ #include <asm/debug-monitors.h>
+@@ -397,6 +398,9 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr,
+ msg = "paging request";
+ }
+
++ if (efi_runtime_fixup_exception(regs, msg))
++ return;
++
+ die_kernel_fault(msg, addr, esr, regs);
+ }
+
+diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
+index 60075e0e4943..1fba4e09cdcf 100644
+--- a/drivers/firmware/efi/runtime-wrappers.c
++++ b/drivers/firmware/efi/runtime-wrappers.c
+@@ -84,6 +84,7 @@ struct efi_runtime_work efi_rts_work;
+ else \
+ pr_err("Failed to queue work to efi_rts_wq.\n"); \
+ \
++ WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); \
+ exit: \
+ efi_rts_work.efi_rts_id = EFI_NONE; \
+ efi_rts_work.status; \
+--
+2.39.0
+