blr
 
 _GLOBAL(return_to_handler)
+       /* need to save return values */
+       std     r4,  -24(r1)
+       std     r3,  -16(r1)
+       std     r31, -8(r1)
+       mr      r31, r1
+       stdu    r1, -112(r1)
+
+       bl      .ftrace_return_to_handler
+       nop
+
+       /* return value has real return address */
+       mtlr    r3
+
+       ld      r1, 0(r1)
+       ld      r4,  -24(r1)
+       ld      r3,  -16(r1)
+       ld      r31, -8(r1)
+
+       /* Jump back to real return address */
+       blr
+
+_GLOBAL(mod_return_to_handler)
        /* need to save return values */
        std     r4,  -32(r1)
        std     r3,  -24(r1)
        mr      r31, r1
        stdu    r1, -112(r1)
 
-       /* update the TOC */
+       /*
+        * We are in a module using the module's TOC.
+        * Switch to our TOC to run inside the core kernel.
+        */
        LOAD_REG_IMMEDIATE(r4,ftrace_return_to_handler)
        ld      r2, 8(r4)
 
 
 }
 #endif /* CONFIG_DYNAMIC_FTRACE */
 
+#ifdef CONFIG_PPC64
+extern void mod_return_to_handler(void);
+#endif
+
 /*
  * Hook the return address and push it in the stack of return addrs
  * in current thread info.
        unsigned long long calltime;
        int faulted;
        struct ftrace_graph_ent trace;
-       unsigned long return_hooker = (unsigned long)
-                               &return_to_handler;
+       unsigned long return_hooker = (unsigned long)&return_to_handler;
 
        if (unlikely(atomic_read(¤t->tracing_graph_pause)))
                return;
 
+#if CONFIG_PPC64
+       /* non core kernel code needs to save and restore the TOC */
+       if (REGION_ID(self_addr) != KERNEL_REGION_ID)
+               return_hooker = (unsigned long)&mod_return_to_handler;
+#endif
+
        return_hooker = GET_ADDR(return_hooker);
 
        /*