powerpc/ftrace: Activate HAVE_DYNAMIC_FTRACE_WITH_REGS on PPC32

Unlike PPC64, PPC32 doesn't require any special compiler option
to get _mcount() call not clobbering registers.

Provide ftrace_regs_caller() and ftrace_regs_call() and activate
HAVE_DYNAMIC_FTRACE_WITH_REGS.

That's heavily copied from ftrace_64_mprofile.S

For the time being leave livepatching aside, it will come with
following patch.

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/1862dc7719855cc2a4eec80920d94c955877557e.1635423081.git.christophe.leroy@csgroup.eu
This commit is contained in:
Christophe Leroy 2021-10-28 14:24:04 +02:00 committed by Michael Ellerman
parent c93d4f6ecf
commit 7dfbfb87c2
4 changed files with 126 additions and 13 deletions

View file

@ -206,7 +206,7 @@ config PPC
select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_KMEMLEAK
select HAVE_DEBUG_STACKOVERFLOW select HAVE_DEBUG_STACKOVERFLOW
select HAVE_DYNAMIC_FTRACE select HAVE_DYNAMIC_FTRACE
select HAVE_DYNAMIC_FTRACE_WITH_REGS if MPROFILE_KERNEL select HAVE_DYNAMIC_FTRACE_WITH_REGS if MPROFILE_KERNEL || PPC32
select HAVE_EBPF_JIT select HAVE_EBPF_JIT
select HAVE_EFFICIENT_UNALIGNED_ACCESS if !(CPU_LITTLE_ENDIAN && POWER7_CPU) select HAVE_EFFICIENT_UNALIGNED_ACCESS if !(CPU_LITTLE_ENDIAN && POWER7_CPU)
select HAVE_FAST_GUP select HAVE_FAST_GUP
@ -230,7 +230,7 @@ config PPC
select HAVE_KPROBES_ON_FTRACE select HAVE_KPROBES_ON_FTRACE
select HAVE_KRETPROBES select HAVE_KRETPROBES
select HAVE_LD_DEAD_CODE_DATA_ELIMINATION select HAVE_LD_DEAD_CODE_DATA_ELIMINATION
select HAVE_LIVEPATCH if HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_LIVEPATCH if HAVE_DYNAMIC_FTRACE_WITH_REGS && PPC64
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select HAVE_NMI if PERF_EVENTS || (PPC64 && PPC_BOOK3S) select HAVE_NMI if PERF_EVENTS || (PPC64 && PPC_BOOK3S)
select HAVE_OPTPROBES select HAVE_OPTPROBES

View file

@ -306,6 +306,14 @@ int module_finalize_ftrace(struct module *module, const Elf_Shdr *sechdrs)
if (!module->arch.tramp) if (!module->arch.tramp)
return -ENOENT; return -ENOENT;
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
module->arch.tramp_regs = do_plt_call(module->core_layout.base,
(unsigned long)ftrace_regs_caller,
sechdrs, module);
if (!module->arch.tramp_regs)
return -ENOENT;
#endif
return 0; return 0;
} }
#endif #endif

View file

@ -561,6 +561,8 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
int err; int err;
struct ppc_inst op; struct ppc_inst op;
u32 *ip = (u32 *)rec->ip; u32 *ip = (u32 *)rec->ip;
struct module *mod = rec->arch.mod;
unsigned long tramp;
/* read where this goes */ /* read where this goes */
if (copy_inst_from_kernel_nofault(&op, ip)) if (copy_inst_from_kernel_nofault(&op, ip))
@ -573,13 +575,23 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
} }
/* If we never set up a trampoline to ftrace_caller, then bail */ /* If we never set up a trampoline to ftrace_caller, then bail */
if (!rec->arch.mod->arch.tramp) { #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
if (!mod->arch.tramp || !mod->arch.tramp_regs) {
#else
if (!mod->arch.tramp) {
#endif
pr_err("No ftrace trampoline\n"); pr_err("No ftrace trampoline\n");
return -EINVAL; return -EINVAL;
} }
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
if (rec->flags & FTRACE_FL_REGS)
tramp = mod->arch.tramp_regs;
else
#endif
tramp = mod->arch.tramp;
/* create the branch to the trampoline */ /* create the branch to the trampoline */
err = create_branch(&op, ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK); err = create_branch(&op, ip, tramp, BRANCH_SET_LINK);
if (err) { if (err) {
pr_err("REL24 out of range!\n"); pr_err("REL24 out of range!\n");
return -EINVAL; return -EINVAL;

View file

@ -9,6 +9,7 @@
#include <asm/asm-offsets.h> #include <asm/asm-offsets.h>
#include <asm/ftrace.h> #include <asm/ftrace.h>
#include <asm/export.h> #include <asm/export.h>
#include <asm/ptrace.h>
_GLOBAL(mcount) _GLOBAL(mcount)
_GLOBAL(_mcount) _GLOBAL(_mcount)
@ -29,17 +30,21 @@ _GLOBAL(ftrace_caller)
MCOUNT_SAVE_FRAME MCOUNT_SAVE_FRAME
/* r3 ends up with link register */ /* r3 ends up with link register */
subi r3, r3, MCOUNT_INSN_SIZE subi r3, r3, MCOUNT_INSN_SIZE
lis r5,function_trace_op@ha
lwz r5,function_trace_op@l(r5)
li r6, 0
.globl ftrace_call .globl ftrace_call
ftrace_call: ftrace_call:
bl ftrace_stub bl ftrace_stub
nop nop
MCOUNT_RESTORE_FRAME
ftrace_caller_common:
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
.globl ftrace_graph_call .globl ftrace_graph_call
ftrace_graph_call: ftrace_graph_call:
b ftrace_graph_stub b ftrace_graph_stub
_GLOBAL(ftrace_graph_stub) _GLOBAL(ftrace_graph_stub)
#endif #endif
MCOUNT_RESTORE_FRAME
/* old link register ends up in ctr reg */ /* old link register ends up in ctr reg */
bctr bctr
@ -47,15 +52,91 @@ _GLOBAL(ftrace_graph_stub)
_GLOBAL(ftrace_stub) _GLOBAL(ftrace_stub)
blr blr
_GLOBAL(ftrace_regs_caller)
/* Save the original return address in A's stack frame */
stw r0,LRSAVE(r1)
/* Create our stack frame + pt_regs */
stwu r1,-INT_FRAME_SIZE(r1)
/* Save all gprs to pt_regs */
stw r0, GPR0(r1)
stmw r2, GPR2(r1)
/* Save previous stack pointer (r1) */
addi r8, r1, INT_FRAME_SIZE
stw r8, GPR1(r1)
/* Load special regs for save below */
mfmsr r8
mfctr r9
mfxer r10
mfcr r11
/* Get the _mcount() call site out of LR */
mflr r7
/* Save it as pt_regs->nip */
stw r7, _NIP(r1)
/* Save the read LR in pt_regs->link */
stw r0, _LINK(r1)
lis r3,function_trace_op@ha
lwz r5,function_trace_op@l(r3)
/* Calculate ip from nip-4 into r3 for call below */
subi r3, r7, MCOUNT_INSN_SIZE
/* Put the original return address in r4 as parent_ip */
mr r4, r0
/* Save special regs */
stw r8, _MSR(r1)
stw r9, _CTR(r1)
stw r10, _XER(r1)
stw r11, _CCR(r1)
/* Load &pt_regs in r6 for call below */
addi r6, r1, STACK_FRAME_OVERHEAD
/* ftrace_call(r3, r4, r5, r6) */
.globl ftrace_regs_call
ftrace_regs_call:
bl ftrace_stub
nop
/* Load ctr with the possibly modified NIP */
lwz r3, _NIP(r1)
mtctr r3
/* Restore gprs */
lmw r2, GPR2(r1)
/* Restore possibly modified LR */
lwz r0, _LINK(r1)
mtlr r0
/* Pop our stack frame */
addi r1, r1, INT_FRAME_SIZE
b ftrace_caller_common
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
_GLOBAL(ftrace_graph_caller) _GLOBAL(ftrace_graph_caller)
addi r5, r1, 48 stwu r1,-48(r1)
/* load r4 with local address */ stw r3, 12(r1)
lwz r4, 44(r1) stw r4, 16(r1)
subi r4, r4, MCOUNT_INSN_SIZE stw r5, 20(r1)
stw r6, 24(r1)
stw r7, 28(r1)
stw r8, 32(r1)
stw r9, 36(r1)
stw r10,40(r1)
/* Grab the LR out of the caller stack frame */ addi r5, r1, 48
lwz r3,52(r1) mfctr r4 /* ftrace_caller has moved local addr here */
stw r4, 44(r1)
mflr r3 /* ftrace_caller has restored LR from stack */
subi r4, r4, MCOUNT_INSN_SIZE
bl prepare_ftrace_return bl prepare_ftrace_return
nop nop
@ -65,9 +146,21 @@ _GLOBAL(ftrace_graph_caller)
* Change the LR in the callers stack frame to this. * Change the LR in the callers stack frame to this.
*/ */
stw r3,52(r1) stw r3,52(r1)
mtlr r3
lwz r0,44(r1)
mtctr r0
lwz r3, 12(r1)
lwz r4, 16(r1)
lwz r5, 20(r1)
lwz r6, 24(r1)
lwz r7, 28(r1)
lwz r8, 32(r1)
lwz r9, 36(r1)
lwz r10,40(r1)
addi r1, r1, 48
MCOUNT_RESTORE_FRAME
/* old link register ends up in ctr reg */
bctr bctr
_GLOBAL(return_to_handler) _GLOBAL(return_to_handler)