|
@@ -61,8 +61,11 @@ ftrace_modify_code(unsigned long ip, unsigned int old, unsigned int new)
|
|
|
return -EFAULT;
|
|
|
|
|
|
/* Make sure it is what we expect it to be */
|
|
|
- if (replaced != old)
|
|
|
+ if (replaced != old) {
|
|
|
+ pr_err("%p: replaced (%#x) != old (%#x)",
|
|
|
+ (void *)ip, replaced, old);
|
|
|
return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
/* replace the text with the new text */
|
|
|
if (patch_instruction((unsigned int *)ip, new))
|
|
@@ -108,11 +111,13 @@ __ftrace_make_nop(struct module *mod,
|
|
|
{
|
|
|
unsigned long entry, ptr, tramp;
|
|
|
unsigned long ip = rec->ip;
|
|
|
- unsigned int op;
|
|
|
+ unsigned int op, pop;
|
|
|
|
|
|
/* read where this goes */
|
|
|
- if (probe_kernel_read(&op, (void *)ip, sizeof(int)))
|
|
|
+ if (probe_kernel_read(&op, (void *)ip, sizeof(int))) {
|
|
|
+ pr_err("Fetching opcode failed.\n");
|
|
|
return -EFAULT;
|
|
|
+ }
|
|
|
|
|
|
/* Make sure that that this is still a 24bit jump */
|
|
|
if (!is_bl_op(op)) {
|
|
@@ -152,10 +157,42 @@ __ftrace_make_nop(struct module *mod,
|
|
|
*
|
|
|
* Use a b +8 to jump over the load.
|
|
|
*/
|
|
|
- op = 0x48000008; /* b +8 */
|
|
|
|
|
|
- if (patch_instruction((unsigned int *)ip, op))
|
|
|
+ pop = PPC_INST_BRANCH | 8; /* b +8 */
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check what is in the next instruction. We can see ld r2,40(r1), but
|
|
|
+ * on first pass after boot we will see mflr r0.
|
|
|
+ */
|
|
|
+ if (probe_kernel_read(&op, (void *)(ip+4), MCOUNT_INSN_SIZE)) {
|
|
|
+ pr_err("Fetching op failed.\n");
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (op != PPC_INST_LD_TOC) {
|
|
|
+ unsigned int inst;
|
|
|
+
|
|
|
+ if (probe_kernel_read(&inst, (void *)(ip - 4), 4)) {
|
|
|
+ pr_err("Fetching instruction at %lx failed.\n", ip - 4);
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We expect either a mlfr r0, or a std r0, LRSAVE(r1) */
|
|
|
+ if (inst != PPC_INST_MFLR && inst != PPC_INST_STD_LR) {
|
|
|
+ pr_err("Unexpected instructions around bl _mcount\n"
|
|
|
+ "when enabling dynamic ftrace!\t"
|
|
|
+ "(%08x,bl,%08x)\n", inst, op);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* When using -mkernel_profile there is no load to jump over */
|
|
|
+ pop = PPC_INST_NOP;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (patch_instruction((unsigned int *)ip, pop)) {
|
|
|
+ pr_err("Patching NOP failed.\n");
|
|
|
return -EPERM;
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -281,16 +318,15 @@ int ftrace_make_nop(struct module *mod,
|
|
|
|
|
|
#ifdef CONFIG_MODULES
|
|
|
#ifdef CONFIG_PPC64
|
|
|
+/*
|
|
|
+ * Examine the existing instructions for __ftrace_make_call.
|
|
|
+ * They should effectively be a NOP, and follow formal constraints,
|
|
|
+ * depending on the ABI. Return false if they don't.
|
|
|
+ */
|
|
|
+#ifndef CC_USING_MPROFILE_KERNEL
|
|
|
static int
|
|
|
-__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
|
|
+expected_nop_sequence(void *ip, unsigned int op0, unsigned int op1)
|
|
|
{
|
|
|
- unsigned int op[2];
|
|
|
- void *ip = (void *)rec->ip;
|
|
|
-
|
|
|
- /* read where this goes */
|
|
|
- if (probe_kernel_read(op, ip, sizeof(op)))
|
|
|
- return -EFAULT;
|
|
|
-
|
|
|
/*
|
|
|
* We expect to see:
|
|
|
*
|
|
@@ -300,8 +336,34 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
|
|
* The load offset is different depending on the ABI. For simplicity
|
|
|
* just mask it out when doing the compare.
|
|
|
*/
|
|
|
- if ((op[0] != 0x48000008) || ((op[1] & 0xffff0000) != 0xe8410000)) {
|
|
|
- pr_err("Unexpected call sequence: %x %x\n", op[0], op[1]);
|
|
|
+ if ((op0 != 0x48000008) || ((op1 & 0xffff0000) != 0xe8410000))
|
|
|
+ return 0;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+#else
|
|
|
+static int
|
|
|
+expected_nop_sequence(void *ip, unsigned int op0, unsigned int op1)
|
|
|
+{
|
|
|
+ /* look for patched "NOP" on ppc64 with -mprofile-kernel */
|
|
|
+ if (op0 != PPC_INST_NOP)
|
|
|
+ return 0;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static int
|
|
|
+__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
|
|
+{
|
|
|
+ unsigned int op[2];
|
|
|
+ void *ip = (void *)rec->ip;
|
|
|
+
|
|
|
+ /* read where this goes */
|
|
|
+ if (probe_kernel_read(op, ip, sizeof(op)))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ if (!expected_nop_sequence(ip, op[0], op[1])) {
|
|
|
+ pr_err("Unexpected call sequence at %p: %x %x\n",
|
|
|
+ ip, op[0], op[1]);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
@@ -324,7 +386,16 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
-#else
|
|
|
+
|
|
|
+#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
|
|
|
+
|
|
|
+#else /* !CONFIG_PPC64: */
|
|
|
static int
|
|
|
__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
|
|
{
|