[PATCH] s390: add support for DYNAMIC_FTRACE_WITH_REGS

Vojtech Pavlik
2014-07-03T12:00:52Z

Add support for DYNAMIC_FTRACE_WITH_REGS to 64-bit and 31-bit s390
architectures. This is required for kGraft and kpatch to work on s390.

It's done by adding a _regs variant of ftrace_caller that preserves
registers and puts them on stack in a struct pt_regs layout and
allows modification of return address by changing the PSW (instruction
pointer) member od struct pt_regs.

Signed-off-by: Vojtech Pavlik <vojtech@suse.cz>
Reviewed-by: Jiri Kosina <jkosina@suse.cz>
Cc: Steven Rostedt <rostedt@goodmis.org>

---

arch/s390/Kconfig | 1
arch/s390/include/asm/ftrace.h | 4 ++
arch/s390/include/asm/lowcore.h | 12 ++++---
arch/s390/kernel/asm-offsets.c | 1
arch/s390/kernel/early.c | 3 +
arch/s390/kernel/ftrace.c | 68 +++++++++++++++++++++++++++++++++++-----
arch/s390/kernel/mcount.S | 43 +++++++++++++++++++++++++
arch/s390/kernel/mcount64.S | 38 ++++++++++++++++++++++
arch/s390/kernel/setup.c | 1
arch/s390/kernel/smp.c | 1
include/linux/ftrace.h | 1
11 files changed, 160 insertions(+), 13 deletions(-)

diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index bb63499..1de14d3 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -113,6 +113,7 @@ config S390
select HAVE_C_RECORDMCOUNT
select HAVE_DEBUG_KMEMLEAK
select HAVE_DYNAMIC_FTRACE
+ select HAVE_DYNAMIC_FTRACE_WITH_REGS
select HAVE_FTRACE_MCOUNT_RECORD
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_FUNCTION_TRACER
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index bf246da..df87fd2 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -17,6 +17,10 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)

#endif /* __ASSEMBLY__ */

+#ifdef CONFIG_DYNAMIC_FTRACE
+#define ARCH_SUPPORTS_FTRACE_OPS 1
+#endif
+
#ifdef CONFIG_64BIT
#define MCOUNT_INSN_SIZE 12
#else
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index 4349197..31b064a 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -142,9 +142,10 @@ struct _lowcore {
__u32 percpu_offset; /* 0x02f0 */
__u32 machine_flags; /* 0x02f4 */
__u32 ftrace_func; /* 0x02f8 */
- __u32 spinlock_lockval; /* 0x02fc */
+ __u32 ftrace_regs_func; /* 0x02fc */
+ __u32 spinlock_lockval; /* 0x0300 */

- __u8 pad_0x0300[0x0e00-0x0300]; /* 0x0300 */
+ __u8 pad_0x0304[0x0e00-0x0304]; /* 0x0304 */

/*
* 0xe00 contains the address of the IPL Parameter Information
@@ -287,9 +288,10 @@ struct _lowcore {
__u64 vdso_per_cpu_data; /* 0x0380 */
__u64 machine_flags; /* 0x0388 */
__u64 ftrace_func; /* 0x0390 */
- __u64 gmap; /* 0x0398 */
- __u32 spinlock_lockval; /* 0x03a0 */
- __u8 pad_0x03a0[0x0400-0x03a4]; /* 0x03a4 */
+ __u64 ftrace_regs_func; /* 0x0398 */
+ __u64 gmap; /* 0x03a0 */
+ __u32 spinlock_lockval; /* 0x03a8 */
+ __u8 pad_0x03ac[0x0400-0x03ac]; /* 0x03ac */

/* Per cpu primary space access list */
__u32 paste[16]; /* 0x0400 */
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index afe1715..12ef6ce 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -150,6 +150,7 @@ int main(void)
DEFINE(__LC_MCCK_CLOCK, offsetof(struct _lowcore, mcck_clock));
DEFINE(__LC_MACHINE_FLAGS, offsetof(struct _lowcore, machine_flags));
DEFINE(__LC_FTRACE_FUNC, offsetof(struct _lowcore, ftrace_func));
+ DEFINE(__LC_FTRACE_REGS_FUNC, offsetof(struct _lowcore, ftrace_regs_func));
DEFINE(__LC_DUMP_REIPL, offsetof(struct _lowcore, ipib));
BLANK();
DEFINE(__LC_CPU_TIMER_SAVE_AREA, offsetof(struct _lowcore, cpu_timer_save_area));
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 0dff972..2ab52d2 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -493,5 +493,8 @@ void __init startup_init(void)
#ifdef CONFIG_DYNAMIC_FTRACE
S390_lowcore.ftrace_func = (unsigned long)ftrace_caller;
#endif
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ S390_lowcore.ftrace_regs_func = (unsigned long)ftrace_regs_caller;
+#endif
lockdep_on();
}
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c
index 54d6493..7d1ec79 100644
--- a/arch/s390/kernel/ftrace.c
+++ b/arch/s390/kernel/ftrace.c
@@ -21,6 +21,7 @@

void ftrace_disable_code(void);
void ftrace_enable_insn(void);
+void ftrace_enable_regs_insn(void);

#ifdef CONFIG_64BIT
/*
@@ -56,7 +57,13 @@ asm(
"0:\n"
" .align 4\n"
"ftrace_enable_insn:\n"
- " lg %r1,"__stringify(__LC_FTRACE_FUNC)"\n");
+ " lg %r1,"__stringify(__LC_FTRACE_FUNC)"\n"
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ " .align 4\n"
+ "ftrace_enable_regs_insn:\n"
+ " lg %r1,"__stringify(__LC_FTRACE_REGS_FUNC)"\n"
+#endif
+);

#define FTRACE_INSN_SIZE 6

@@ -101,7 +108,13 @@ asm(
"1:\n"
" .align 4\n"
"ftrace_enable_insn:\n"
- " l %r14,"__stringify(__LC_FTRACE_FUNC)"\n");
+ " l %r14,"__stringify(__LC_FTRACE_FUNC)"\n"
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ " .align 4\n"
+ "ftrace_enable_regs_insn:\n"
+ " l %r14,"__stringify(__LC_FTRACE_REGS_FUNC)"\n"
+#endif
+);

#define FTRACE_INSN_SIZE 4

@@ -119,12 +132,29 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,

int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
- if (probe_kernel_write((void *) rec->ip, ftrace_enable_insn,
- FTRACE_INSN_SIZE))
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ if (rec->flags & FTRACE_FL_REGS) {
+ if (probe_kernel_write((void *) rec->ip, ftrace_enable_regs_insn,
+ FTRACE_INSN_SIZE))
return -EPERM;
+ } else
+#endif
+ {
+ if (probe_kernel_write((void *) rec->ip, ftrace_enable_insn,
+ FTRACE_INSN_SIZE))
+ return -EPERM;
+ }
return 0;
}

+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
+ unsigned long addr)
+{
+ return ftrace_make_call(rec, addr);
+}
+#endif
+
int ftrace_update_ftrace_func(ftrace_func_t func)
{
return 0;
@@ -173,19 +203,41 @@ out:
int ftrace_enable_ftrace_graph_caller(void)
{
unsigned short offset;
+ int ret;
+
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ offset = ((void *) prepare_ftrace_return -
+ (void *) ftrace_regs_graph_caller) / 2;
+ if ((ret = probe_kernel_write((void *) ftrace_regs_graph_caller + 2,
+ &offset, sizeof(offset))))
+ return ret;
+#endif

offset = ((void *) prepare_ftrace_return -
(void *) ftrace_graph_caller) / 2;
- return probe_kernel_write((void *) ftrace_graph_caller + 2,
- &offset, sizeof(offset));
+ if ((ret = probe_kernel_write((void *) ftrace_graph_caller + 2,
+ &offset, sizeof(offset))))
+ return ret;
+
+ return 0;
}

int ftrace_disable_ftrace_graph_caller(void)
{
static unsigned short offset = 0x0002;
+ int ret;

- return probe_kernel_write((void *) ftrace_graph_caller + 2,
- &offset, sizeof(offset));
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ if ((ret = probe_kernel_write((void *) ftrace_regs_graph_caller + 2,
+ &offset, sizeof(offset))))
+ return ret;
+#endif
+
+ if ((ret = probe_kernel_write((void *) ftrace_graph_caller + 2,
+ &offset, sizeof(offset))))
+ return ret;
+
+ return 0;
}

#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
index 08dcf21..e4322cf 100644
--- a/arch/s390/kernel/mcount.S
+++ b/arch/s390/kernel/mcount.S
@@ -53,6 +53,49 @@ ENTRY(ftrace_graph_caller)
3: lm %r2,%r5,16(%r15)
br %r14

+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ENTRY(ftrace_regs_caller)
+ bras %r1,1f
+0: .long function_trace_stop
+1: l %r1,0(%r1)
+ icm %r1,0xf,0(%r1)
+ bnzr %r14
+ lr %r1,%r15
+ ahi %r15,-188
+ st %r1,__SF_BACKCHAIN(%r15)
+ st %r1,168(%r15)
+ stm %r0,%r13,108(%r15)
+ st %r14,104(%r15)
+ lr %r2,%r14
+ ahi %r2,-MCOUNT_INSN_SIZE
+ l %r3,192(%r15)
+ st %r3,164(%r15)
+ bras %r1,1f
+0: .long ftrace_trace_function
+1: .long function_trace_op
+ l %r4,1b-0b(%r1)
+ l %r4,0(%r4)
+ lr %r5, %r15
+ ahi %r5, 96
+ l %r14,0(%r1)
+ l %r14,0(%r14)
+ basr %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ l %r2,192(%r15)
+ l %r3,244(%r15)
+ENTRY(ftrace_regs_graph_caller)
+# The bras instruction gets runtime patched to call prepare_ftrace_return.
+# See ftrace_enable_ftrace_graph_caller. The patched instruction is:
+# bras %r14,prepare_ftrace_return
+ bras %r14,0f
+0: st %r2,192(%r15)
+#endif
+ lm %r0,%r13,108(%r15)
+ l %r14,104(%r15)
+ ahi %r15,188
+ br %r14
+#endif
+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER

ENTRY(return_to_handler)
diff --git a/arch/s390/kernel/mcount64.S b/arch/s390/kernel/mcount64.S
index 1c52eae..bcad958 100644
--- a/arch/s390/kernel/mcount64.S
+++ b/arch/s390/kernel/mcount64.S
@@ -49,6 +49,44 @@ ENTRY(ftrace_graph_caller)
lg %r14,112(%r15)
br %r14

+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ENTRY(ftrace_regs_caller)
+ larl %r1,function_trace_stop
+ icm %r1,0xf,0(%r1)
+ bnzr %r14
+ lgr %r1,%r15
+ aghi %r15,-336
+ stg %r1,__SF_BACKCHAIN(%r15)
+ stg %r1,304(%r15)
+ stmg %r0,%r13,184(%r15)
+ stg %r14,176(%r15)
+ lgr %r2,%r14
+ aghi %r2,-MCOUNT_INSN_SIZE
+ lg %r3,344(%r15)
+ stg %r3,296(%r15)
+ larl %r4,function_trace_op
+ lg %r4,0(%r4)
+ lgr %r5, %r15
+ aghi %r5, 160
+ larl %r14,ftrace_trace_function
+ lg %r14,0(%r14)
+ basr %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ lg %r2,344(%r15)
+ lg %r3,448(%r15)
+ENTRY(ftrace_regs_graph_caller)
+# The bras instruction gets runtime patched to call prepare_ftrace_return.
+# See ftrace_enable_ftrace_graph_caller. The patched instruction is:
+# bras %r14,prepare_ftrace_return
+ bras %r14,0f
+0: stg %r2,344(%r15)
+#endif
+ lmg %r0,%r13,184(%r15)
+ lg %r14,176(%r15)
+ aghi %r15,336
+ br %r14
+#endif
+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER

ENTRY(return_to_handler)
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 1e2264b..6cdf2a1 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -352,6 +352,7 @@ static void __init setup_lowcore(void)
lc->last_update_timer = S390_lowcore.last_update_timer;
lc->last_update_clock = S390_lowcore.last_update_clock;
lc->ftrace_func = S390_lowcore.ftrace_func;
+ lc->ftrace_regs_func = S390_lowcore.ftrace_regs_func;

restart_stack = __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0);
restart_stack += ASYNC_SIZE;
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 243c7e5..152967a 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -232,6 +232,7 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
lc->kernel_asce = S390_lowcore.kernel_asce;
lc->machine_flags = S390_lowcore.machine_flags;
lc->ftrace_func = S390_lowcore.ftrace_func;
+ lc->ftrace_regs_func = S390_lowcore.ftrace_regs_func;
lc->user_timer = lc->system_timer = lc->steal_timer = 0;
__ctl_store(lc->cregs_save_area, 0, 15);
save_access_regs((unsigned int *) lc->access_regs_save_area);
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 404a686..70ccc82 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -441,6 +441,7 @@ void ftrace_modify_all_code(int command);

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
extern void ftrace_graph_caller(void);
+extern void ftrace_regs_graph_caller(void);
extern int ftrace_enable_ftrace_graph_caller(void);
extern int ftrace_disable_ftrace_graph_caller(void);
#else