ARM: call_with_stack: add unwind support
authorArd Biesheuvel <ardb@kernel.org>
Tue, 5 Oct 2021 07:15:41 +0000 (09:15 +0200)
committerArd Biesheuvel <ardb@kernel.org>
Fri, 3 Dec 2021 14:11:32 +0000 (15:11 +0100)
Restructure the code and add the unwind annotations so that both the
frame pointer unwinder as well as the EHABI unwind info based unwinder
will be able to follow the call stack through call_with_stack().

Since GCC and Clang use different formats for the stack frame, two
methods are implemented: a GCC version that pushes fp, sp, lr and pc for
compatibility with the frame pointer unwinder, and a second version that
works with Clang, as well as with the EHABI unwinder both in ARM and
Thumb2 modes.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Keith Packard <keithpac@amazon.com>
Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
Tested-by: Marc Zyngier <maz@kernel.org>
Tested-by: Vladimir Murzin <vladimir.murzin@arm.com> # ARMv7M
arch/arm/lib/call_with_stack.S

index 28b0341ae786fbd73aa5d4fe9a43e49bee3e910c..0a268a6c513c8f2f369264885181be08f0678ff0 100644 (file)
@@ -8,25 +8,42 @@
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/unwind.h>
 
 /*
  * void call_with_stack(void (*fn)(void *), void *arg, void *sp)
  *
  * Change the stack to that pointed at by sp, then invoke fn(arg) with
  * the new stack.
+ *
+ * The sequence below follows the APCS frame convention for frame pointer
+ * unwinding, and implements the unwinder annotations needed by the EABI
+ * unwinder.
  */
-ENTRY(call_with_stack)
-       str     sp, [r2, #-4]!
-       str     lr, [r2, #-4]!
 
+ENTRY(call_with_stack)
+#if defined(CONFIG_UNWINDER_FRAME_POINTER) && defined(CONFIG_CC_IS_GCC)
+       mov     ip, sp
+       push    {fp, ip, lr, pc}
+       sub     fp, ip, #4
+#else
+UNWIND( .fnstart               )
+UNWIND( .save  {fpreg, lr}     )
+       push    {fpreg, lr}
+UNWIND( .setfp fpreg, sp       )
+       mov     fpreg, sp
+#endif
        mov     sp, r2
        mov     r2, r0
        mov     r0, r1
 
-       badr    lr, 1f
-       ret     r2
+       bl_r    r2
 
-1:     ldr     lr, [sp]
-       ldr     sp, [sp, #4]
-       ret     lr
+#if defined(CONFIG_UNWINDER_FRAME_POINTER) && defined(CONFIG_CC_IS_GCC)
+       ldmdb   fp, {fp, sp, pc}
+#else
+       mov     sp, fpreg
+       pop     {fpreg, pc}
+UNWIND( .fnend                 )
+#endif
 ENDPROC(call_with_stack)