#ifdef CONFIG_DYNAMIC_FTRACE
+
+/*
+ * We generally only have a single long_branch tramp and at most 2 or 3 plt
+ * tramps generated. But, we don't use the plt tramps currently. We also allot
+ * 2 tramps after .text and .init.text. So, we only end up with around 3 usable
+ * tramps in total. Set aside 8 just to be sure.
+ */
+#define        NUM_FTRACE_TRAMPS       8
+static unsigned long ftrace_tramps[NUM_FTRACE_TRAMPS];
+
 static unsigned int
 ftrace_call_replace(unsigned long ip, unsigned long addr, int link)
 {
        return create_branch((unsigned int *)ip, addr, 0);
 }
 
-#ifdef CONFIG_MODULES
-
 static int is_bl_op(unsigned int op)
 {
        return (op & 0xfc000003) == 0x48000001;
 }
 
+static int is_b_op(unsigned int op)
+{
+       return (op & 0xfc000003) == 0x48000000;
+}
+
 static unsigned long find_bl_target(unsigned long ip, unsigned int op)
 {
        static int offset;
        return ip + (long)offset;
 }
 
+#ifdef CONFIG_MODULES
 #ifdef CONFIG_PPC64
 static int
 __ftrace_make_nop(struct module *mod,
 #endif /* PPC64 */
 #endif /* CONFIG_MODULES */
 
+static unsigned long find_ftrace_tramp(unsigned long ip)
+{
+       int i;
+
+       /*
+        * We have the compiler generated long_branch tramps at the end
+        * and we prefer those
+        */
+       for (i = NUM_FTRACE_TRAMPS - 1; i >= 0; i--)
+               if (!ftrace_tramps[i])
+                       continue;
+               else if (create_branch((void *)ip, ftrace_tramps[i], 0))
+                       return ftrace_tramps[i];
+
+       return 0;
+}
+
+static int add_ftrace_tramp(unsigned long tramp)
+{
+       int i;
+
+       for (i = 0; i < NUM_FTRACE_TRAMPS; i++)
+               if (!ftrace_tramps[i]) {
+                       ftrace_tramps[i] = tramp;
+                       return 0;
+               }
+
+       return -1;
+}
+
+/*
+ * If this is a compiler generated long_branch trampoline (essentially, a
+ * trampoline that has a branch to _mcount()), we re-write the branch to
+ * instead go to ftrace_[regs_]caller() and note down the location of this
+ * trampoline.
+ */
+static int setup_mcount_compiler_tramp(unsigned long tramp)
+{
+       int i, op;
+       unsigned long ptr;
+       static unsigned long ftrace_plt_tramps[NUM_FTRACE_TRAMPS];
+
+       /* Is this a known long jump tramp? */
+       for (i = 0; i < NUM_FTRACE_TRAMPS; i++)
+               if (!ftrace_tramps[i])
+                       break;
+               else if (ftrace_tramps[i] == tramp)
+                       return 0;
+
+       /* Is this a known plt tramp? */
+       for (i = 0; i < NUM_FTRACE_TRAMPS; i++)
+               if (!ftrace_plt_tramps[i])
+                       break;
+               else if (ftrace_plt_tramps[i] == tramp)
+                       return -1;
+
+       /* New trampoline -- read where this goes */
+       if (probe_kernel_read(&op, (void *)tramp, sizeof(int))) {
+               pr_debug("Fetching opcode failed.\n");
+               return -1;
+       }
+
+       /* Is this a 24 bit branch? */
+       if (!is_b_op(op)) {
+               pr_debug("Trampoline is not a long branch tramp.\n");
+               return -1;
+       }
+
+       /* lets find where the pointer goes */
+       ptr = find_bl_target(tramp, op);
+
+       if (ptr != ppc_global_function_entry((void *)_mcount)) {
+               pr_debug("Trampoline target %p is not _mcount\n", (void *)ptr);
+               return -1;
+       }
+
+       /* Let's re-write the tramp to go to ftrace_[regs_]caller */
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       ptr = ppc_global_function_entry((void *)ftrace_regs_caller);
+#else
+       ptr = ppc_global_function_entry((void *)ftrace_caller);
+#endif
+       if (!create_branch((void *)tramp, ptr, 0)) {
+               pr_debug("%ps is not reachable from existing mcount tramp\n",
+                               (void *)ptr);
+               return -1;
+       }
+
+       if (patch_branch((unsigned int *)tramp, ptr, 0)) {
+               pr_debug("REL24 out of range!\n");
+               return -1;
+       }
+
+       if (add_ftrace_tramp(tramp)) {
+               pr_debug("No tramp locations left\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int __ftrace_make_nop_kernel(struct dyn_ftrace *rec, unsigned long addr)
+{
+       unsigned long tramp, ip = rec->ip;
+       unsigned int op;
+
+       /* Read where this goes */
+       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)) {
+               pr_err("Not expected bl: opcode is %x\n", op);
+               return -EINVAL;
+       }
+
+       /* Let's find where the pointer goes */
+       tramp = find_bl_target(ip, op);
+
+       pr_devel("ip:%lx jumps to %lx", ip, tramp);
+
+       if (setup_mcount_compiler_tramp(tramp)) {
+               /* Are other trampolines reachable? */
+               if (!find_ftrace_tramp(ip)) {
+                       pr_err("No ftrace trampolines reachable from %ps\n",
+                                       (void *)ip);
+                       return -EINVAL;
+               }
+       }
+
+       if (patch_instruction((unsigned int *)ip, PPC_INST_NOP)) {
+               pr_err("Patching NOP failed.\n");
+               return -EPERM;
+       }
+
+       return 0;
+}
+
 int ftrace_make_nop(struct module *mod,
                    struct dyn_ftrace *rec, unsigned long addr)
 {
                old = ftrace_call_replace(ip, addr, 1);
                new = PPC_INST_NOP;
                return ftrace_modify_code(ip, old, new);
-       }
+       } else if (core_kernel_text(ip))
+               return __ftrace_make_nop_kernel(rec, addr);
 
 #ifdef CONFIG_MODULES
        /*
 #endif /* CONFIG_PPC64 */
 #endif /* CONFIG_MODULES */
 
+static int __ftrace_make_call_kernel(struct dyn_ftrace *rec, unsigned long addr)
+{
+       unsigned int op;
+       void *ip = (void *)rec->ip;
+       unsigned long tramp, entry, ptr;
+
+       /* Make sure we're being asked to patch branch to a known ftrace addr */
+       entry = ppc_global_function_entry((void *)ftrace_caller);
+       ptr = ppc_global_function_entry((void *)addr);
+
+       if (ptr != entry) {
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+               entry = ppc_global_function_entry((void *)ftrace_regs_caller);
+               if (ptr != entry) {
+#endif
+                       pr_err("Unknown ftrace addr to patch: %ps\n", (void *)ptr);
+                       return -EINVAL;
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+               }
+#endif
+       }
+
+       /* Make sure we have a nop */
+       if (probe_kernel_read(&op, ip, sizeof(op))) {
+               pr_err("Unable to read ftrace location %p\n", ip);
+               return -EFAULT;
+       }
+
+       if (op != PPC_INST_NOP) {
+               pr_err("Unexpected call sequence at %p: %x\n", ip, op);
+               return -EINVAL;
+       }
+
+       tramp = find_ftrace_tramp((unsigned long)ip);
+       if (!tramp) {
+               pr_err("No ftrace trampolines reachable from %ps\n", ip);
+               return -EINVAL;
+       }
+
+       if (patch_branch(ip, tramp, BRANCH_SET_LINK)) {
+               pr_err("Error patching branch to ftrace tramp!\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 {
        unsigned long ip = rec->ip;
                old = PPC_INST_NOP;
                new = ftrace_call_replace(ip, addr, 1);
                return ftrace_modify_code(ip, old, new);
-       }
+       } else if (core_kernel_text(ip))
+               return __ftrace_make_call_kernel(rec, addr);
 
 #ifdef CONFIG_MODULES
        /*
                old = ftrace_call_replace(ip, old_addr, 1);
                new = ftrace_call_replace(ip, addr, 1);
                return ftrace_modify_code(ip, old, new);
+       } else if (core_kernel_text(ip)) {
+               /*
+                * We always patch out of range locations to go to the regs
+                * variant, so there is nothing to do here
+                */
+               return 0;
        }
 
 #ifdef CONFIG_MODULES
        ftrace_modify_all_code(command);
 }
 
+#ifdef CONFIG_PPC64
+#define PACATOC offsetof(struct paca_struct, kernel_toc)
+
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
+extern unsigned int ftrace_tramp_text[], ftrace_tramp_init[];
+
+int __init ftrace_dyn_arch_init(void)
+{
+       int i;
+       unsigned int *tramp[] = { ftrace_tramp_text, ftrace_tramp_init };
+       u32 stub_insns[] = {
+               0xe98d0000 | PACATOC,   /* ld      r12,PACATOC(r13)     */
+               0x3d8c0000,             /* addis   r12,r12,<high>       */
+               0x398c0000,             /* addi    r12,r12,<low>        */
+               0x7d8903a6,             /* mtctr   r12                  */
+               0x4e800420,             /* bctr                         */
+       };
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       unsigned long addr = ppc_global_function_entry((void *)ftrace_regs_caller);
+#else
+       unsigned long addr = ppc_global_function_entry((void *)ftrace_caller);
+#endif
+       long reladdr = addr - kernel_toc_addr();
+
+       if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
+               pr_err("Address of %ps out of range of kernel_toc.\n",
+                               (void *)addr);
+               return -1;
+       }
+
+       for (i = 0; i < 2; i++) {
+               memcpy(tramp[i], stub_insns, sizeof(stub_insns));
+               tramp[i][1] |= PPC_HA(reladdr);
+               tramp[i][2] |= PPC_LO(reladdr);
+               add_ftrace_tramp((unsigned long)tramp[i]);
+       }
+
+       return 0;
+}
+#else
 int __init ftrace_dyn_arch_init(void)
 {
        return 0;
 }
+#endif
 #endif /* CONFIG_DYNAMIC_FTRACE */
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER