#include <linux/linkage.h>
 
 SYM_FUNC_START(__efi_rt_asm_wrapper)
-       stp     x29, x30, [sp, #-32]!
+       stp     x29, x30, [sp, #-112]!
        mov     x29, sp
 
        /*
         */
        stp     x1, x18, [sp, #16]
 
+       /*
+        * Preserve all callee saved registers and record the stack pointer
+        * value in a per-CPU variable so we can recover from synchronous
+        * exceptions occurring while running 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]
+
+       adr_this_cpu    x8, __efi_rt_asm_recover_sp, x9
+       str             x29, [x8]
+
        /*
         * We are lucky enough that no EFI runtime services take more than
         * 5 arguments, so all are passed in registers rather than via the
 
        ldp     x1, x2, [sp, #16]
        cmp     x2, x18
-       ldp     x29, x30, [sp], #32
+       ldp     x29, x30, [sp], #112
        b.ne    0f
        ret
 0:
        mov     x18, x2
        b       efi_handle_corrupted_x18        // tail call
 SYM_FUNC_END(__efi_rt_asm_wrapper)
+
+SYM_FUNC_START(__efi_rt_asm_recover)
+       ldr_this_cpu    x8, __efi_rt_asm_recover_sp, x9
+       mov             sp, x8
+
+       ldp     x0,  x18, [sp, #16]
+       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
+
+       b       efi_handle_runtime_exception
+SYM_FUNC_END(__efi_rt_asm_recover)
 
 
 #include <linux/efi.h>
 #include <linux/init.h>
+#include <linux/percpu.h>
 
 #include <asm/efi.h>
 
        pr_err_ratelimited(FW_BUG "register x18 corrupted by EFI %s\n", f);
        return s;
 }
+
+asmlinkage DEFINE_PER_CPU(u64, __efi_rt_asm_recover_sp);
+
+asmlinkage efi_status_t __efi_rt_asm_recover(void);
+
+asmlinkage efi_status_t efi_handle_runtime_exception(const char *f)
+{
+       pr_err(FW_BUG "Synchronous exception occurred in EFI runtime service %s()\n", f);
+       clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
+       return EFI_ABORTED;
+}
+
+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);
+       dump_stack();
+
+       regs->pc = (u64)__efi_rt_asm_recover;
+       return true;
+}