ARM: ftrace: avoid redundant loads or clobbering IP
authorArd Biesheuvel <ardb@kernel.org>
Sun, 23 Jan 2022 19:18:33 +0000 (20:18 +0100)
committerArd Biesheuvel <ardb@kernel.org>
Wed, 9 Feb 2022 08:12:32 +0000 (09:12 +0100)
Tweak the ftrace return paths to avoid redundant loads of SP, as well as
unnecessary clobbering of IP.

This also fixes the inconsistency of using MOV to perform a function
return, which is sub-optimal on recent micro-architectures but more
importantly, does not perform an interworking return, unlike compiler
generated function returns in Thumb2 builds.

Let's fix this by popping PC from the stack like most ordinary code
does.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org>
arch/arm/kernel/entry-ftrace.S

index 237d435..67548c3 100644 (file)
  * mcount can be thought of as a function called in the middle of a subroutine
  * call.  As such, it needs to be transparent for both the caller and the
  * callee: the original lr needs to be restored when leaving mcount, and no
- * registers should be clobbered.  (In the __gnu_mcount_nc implementation, we
- * clobber the ip register.  This is OK because the ARM calling convention
- * allows it to be clobbered in subroutines and doesn't use it to hold
- * parameters.)
+ * registers should be clobbered.
  *
  * When using dynamic ftrace, we patch out the mcount call by a "add sp, #4"
  * instead of the __gnu_mcount_nc call (see arch/arm/kernel/ftrace.c).
 
 .macro __ftrace_regs_caller
 
-       sub     sp, sp, #8      @ space for PC and CPSR OLD_R0,
+       str     lr, [sp, #-8]!  @ store LR as PC and make space for CPSR/OLD_R0,
                                @ OLD_R0 will overwrite previous LR
 
-       add     ip, sp, #12     @ move in IP the value of SP as it was
-                               @ before the push {lr} of the mcount mechanism
+       ldr     lr, [sp, #8]    @ get previous LR
 
-       str     lr, [sp, #0]    @ store LR instead of PC
+       str     r0, [sp, #8]    @ write r0 as OLD_R0 over previous LR
 
-       ldr     lr, [sp, #8]    @ get previous LR
+       str     lr, [sp, #-4]!  @ store previous LR as LR
 
-       str     r0, [sp, #8]    @ write r0 as OLD_R0 over previous LR
+       add     lr, sp, #16     @ move in LR the value of SP as it was
+                               @ before the push {lr} of the mcount mechanism
 
-       stmdb   sp!, {ip, lr}
-       stmdb   sp!, {r0-r11, lr}
+       push    {r0-r11, ip, lr}
 
        @ stack content at this point:
        @ 0  4          48   52       56            60   64    68       72
-       @ R0 | R1 | ... | LR | SP + 4 | previous LR | LR | PSR | OLD_R0 |
+       @ R0 | R1 | ... | IP | SP + 4 | previous LR | LR | PSR | OLD_R0 |
 
-       mov r3, sp                              @ struct pt_regs*
+       mov     r3, sp                          @ struct pt_regs*
 
        ldr r2, =function_trace_op
        ldr r2, [r2]                            @ pointer to the current
@@ -112,11 +108,9 @@ ftrace_graph_regs_call:
 #endif
 
        @ pop saved regs
-       ldmia   sp!, {r0-r12}                   @ restore r0 through r12
-       ldr     ip, [sp, #8]                    @ restore PC
-       ldr     lr, [sp, #4]                    @ restore LR
-       ldr     sp, [sp, #0]                    @ restore SP
-       mov     pc, ip                          @ return
+       pop     {r0-r11, ip, lr}                @ restore r0 through r12
+       ldr     lr, [sp], #4                    @ restore LR
+       ldr     pc, [sp], #12
 .endm
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -132,11 +126,9 @@ ftrace_graph_regs_call:
        bl      prepare_ftrace_return
 
        @ pop registers saved in ftrace_regs_caller
-       ldmia   sp!, {r0-r12}                   @ restore r0 through r12
-       ldr     ip, [sp, #8]                    @ restore PC
-       ldr     lr, [sp, #4]                    @ restore LR
-       ldr     sp, [sp, #0]                    @ restore SP
-       mov     pc, ip                          @ return
+       pop     {r0-r11, ip, lr}                @ restore r0 through r12
+       ldr     lr, [sp], #4                    @ restore LR
+       ldr     pc, [sp], #12
 
 .endm
 #endif
@@ -202,16 +194,17 @@ ftrace_graph_call\suffix:
 .endm
 
 .macro mcount_exit
-       ldmia   sp!, {r0-r3, ip, lr}
-       ret     ip
+       ldmia   sp!, {r0-r3}
+       ldr     lr, [sp, #4]
+       ldr     pc, [sp], #8
 .endm
 
 ENTRY(__gnu_mcount_nc)
 UNWIND(.fnstart)
 #ifdef CONFIG_DYNAMIC_FTRACE
-       mov     ip, lr
-       ldmia   sp!, {lr}
-       ret     ip
+       push    {lr}
+       ldr     lr, [sp, #4]
+       ldr     pc, [sp], #8
 #else
        __mcount
 #endif