1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/module.h> 3 #include <linux/kthread.h> 4 #include <linux/ftrace.h> 5 #ifndef CONFIG_ARM64 6 #include <asm/asm-offsets.h> 7 #endif 8 9 extern void my_direct_func1(unsigned long ip); 10 extern void my_direct_func2(unsigned long ip); 11 12 void my_direct_func1(unsigned long ip) 13 { 14 trace_printk("my direct func1 ip %lx\n", ip); 15 } 16 17 void my_direct_func2(unsigned long ip) 18 { 19 trace_printk("my direct func2 ip %lx\n", ip); 20 } 21 22 extern void my_tramp1(void *); 23 extern void my_tramp2(void *); 24 25 #ifdef CONFIG_RISCV 26 #include <asm/asm.h> 27 28 asm ( 29 " .pushsection .text, \"ax\", @progbits\n" 30 " .type my_tramp1, @function\n" 31 " .globl my_tramp1\n" 32 " my_tramp1:\n" 33 " addi sp,sp,-3*"SZREG"\n" 34 " "REG_S" a0,0*"SZREG"(sp)\n" 35 " "REG_S" t0,1*"SZREG"(sp)\n" 36 " "REG_S" ra,2*"SZREG"(sp)\n" 37 " mv a0,t0\n" 38 " call my_direct_func1\n" 39 " "REG_L" a0,0*"SZREG"(sp)\n" 40 " "REG_L" t0,1*"SZREG"(sp)\n" 41 " "REG_L" ra,2*"SZREG"(sp)\n" 42 " addi sp,sp,3*"SZREG"\n" 43 " jr t0\n" 44 " .size my_tramp1, .-my_tramp1\n" 45 46 " .type my_tramp2, @function\n" 47 " .globl my_tramp2\n" 48 " my_tramp2:\n" 49 " addi sp,sp,-3*"SZREG"\n" 50 " "REG_S" a0,0*"SZREG"(sp)\n" 51 " "REG_S" t0,1*"SZREG"(sp)\n" 52 " "REG_S" ra,2*"SZREG"(sp)\n" 53 " mv a0,t0\n" 54 " call my_direct_func2\n" 55 " "REG_L" a0,0*"SZREG"(sp)\n" 56 " "REG_L" t0,1*"SZREG"(sp)\n" 57 " "REG_L" ra,2*"SZREG"(sp)\n" 58 " addi sp,sp,3*"SZREG"\n" 59 " jr t0\n" 60 " .size my_tramp2, .-my_tramp2\n" 61 " .popsection\n" 62 ); 63 64 #endif /* CONFIG_RISCV */ 65 66 #ifdef CONFIG_X86_64 67 68 #include <asm/ibt.h> 69 #include <asm/nospec-branch.h> 70 71 asm ( 72 " .pushsection .text, \"ax\", @progbits\n" 73 " .type my_tramp1, @function\n" 74 " .globl my_tramp1\n" 75 " my_tramp1:" 76 ASM_ENDBR 77 " pushq %rbp\n" 78 " movq %rsp, %rbp\n" 79 CALL_DEPTH_ACCOUNT 80 " pushq %rdi\n" 81 " movq 8(%rbp), %rdi\n" 82 " call my_direct_func1\n" 83 " popq %rdi\n" 84 " leave\n" 85 ASM_RET 86 " .size my_tramp1, .-my_tramp1\n" 87 88 " .type my_tramp2, @function\n" 89 " .globl my_tramp2\n" 90 " my_tramp2:" 91 ASM_ENDBR 92 " pushq %rbp\n" 93 " movq %rsp, %rbp\n" 94 CALL_DEPTH_ACCOUNT 95 " pushq %rdi\n" 96 " movq 8(%rbp), %rdi\n" 97 " call my_direct_func2\n" 98 " popq %rdi\n" 99 " leave\n" 100 ASM_RET 101 " .size my_tramp2, .-my_tramp2\n" 102 " .popsection\n" 103 ); 104 105 #endif /* CONFIG_X86_64 */ 106 107 #ifdef CONFIG_S390 108 109 asm ( 110 " .pushsection .text, \"ax\", @progbits\n" 111 " .type my_tramp1, @function\n" 112 " .globl my_tramp1\n" 113 " my_tramp1:" 114 " lgr %r1,%r15\n" 115 " stmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" 116 " stg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" 117 " aghi %r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n" 118 " stg %r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n" 119 " lgr %r2,%r0\n" 120 " brasl %r14,my_direct_func1\n" 121 " aghi %r15,"__stringify(STACK_FRAME_OVERHEAD)"\n" 122 " lmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" 123 " lg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" 124 " lgr %r1,%r0\n" 125 " br %r1\n" 126 " .size my_tramp1, .-my_tramp1\n" 127 "\n" 128 " .type my_tramp2, @function\n" 129 " .globl my_tramp2\n" 130 " my_tramp2:" 131 " lgr %r1,%r15\n" 132 " stmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" 133 " stg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" 134 " aghi %r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n" 135 " stg %r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n" 136 " lgr %r2,%r0\n" 137 " brasl %r14,my_direct_func2\n" 138 " aghi %r15,"__stringify(STACK_FRAME_OVERHEAD)"\n" 139 " lmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" 140 " lg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" 141 " lgr %r1,%r0\n" 142 " br %r1\n" 143 " .size my_tramp2, .-my_tramp2\n" 144 " .popsection\n" 145 ); 146 147 #endif /* CONFIG_S390 */ 148 149 #ifdef CONFIG_ARM64 150 151 asm ( 152 " .pushsection .text, \"ax\", @progbits\n" 153 " .type my_tramp1, @function\n" 154 " .globl my_tramp1\n" 155 " my_tramp1:" 156 " hint 34\n" // bti c 157 " sub sp, sp, #32\n" 158 " stp x9, x30, [sp]\n" 159 " str x0, [sp, #16]\n" 160 " mov x0, x30\n" 161 " bl my_direct_func1\n" 162 " ldp x30, x9, [sp]\n" 163 " ldr x0, [sp, #16]\n" 164 " add sp, sp, #32\n" 165 " ret x9\n" 166 " .size my_tramp1, .-my_tramp1\n" 167 168 " .type my_tramp2, @function\n" 169 " .globl my_tramp2\n" 170 " my_tramp2:" 171 " hint 34\n" // bti c 172 " sub sp, sp, #32\n" 173 " stp x9, x30, [sp]\n" 174 " str x0, [sp, #16]\n" 175 " mov x0, x30\n" 176 " bl my_direct_func2\n" 177 " ldp x30, x9, [sp]\n" 178 " ldr x0, [sp, #16]\n" 179 " add sp, sp, #32\n" 180 " ret x9\n" 181 " .size my_tramp2, .-my_tramp2\n" 182 " .popsection\n" 183 ); 184 185 #endif /* CONFIG_ARM64 */ 186 187 #ifdef CONFIG_LOONGARCH 188 #include <asm/asm.h> 189 190 asm ( 191 " .pushsection .text, \"ax\", @progbits\n" 192 " .type my_tramp1, @function\n" 193 " .globl my_tramp1\n" 194 " my_tramp1:\n" 195 " addi.d $sp, $sp, -32\n" 196 " st.d $a0, $sp, 0\n" 197 " st.d $t0, $sp, 8\n" 198 " st.d $ra, $sp, 16\n" 199 " move $a0, $t0\n" 200 " bl my_direct_func1\n" 201 " ld.d $a0, $sp, 0\n" 202 " ld.d $t0, $sp, 8\n" 203 " ld.d $ra, $sp, 16\n" 204 " addi.d $sp, $sp, 32\n" 205 " jr $t0\n" 206 " .size my_tramp1, .-my_tramp1\n" 207 208 " .type my_tramp2, @function\n" 209 " .globl my_tramp2\n" 210 " my_tramp2:\n" 211 " addi.d $sp, $sp, -32\n" 212 " st.d $a0, $sp, 0\n" 213 " st.d $t0, $sp, 8\n" 214 " st.d $ra, $sp, 16\n" 215 " move $a0, $t0\n" 216 " bl my_direct_func2\n" 217 " ld.d $a0, $sp, 0\n" 218 " ld.d $t0, $sp, 8\n" 219 " ld.d $ra, $sp, 16\n" 220 " addi.d $sp, $sp, 32\n" 221 " jr $t0\n" 222 " .size my_tramp2, .-my_tramp2\n" 223 " .popsection\n" 224 ); 225 226 #endif /* CONFIG_LOONGARCH */ 227 228 static unsigned long my_tramp = (unsigned long)my_tramp1; 229 static unsigned long tramps[2] = { 230 (unsigned long)my_tramp1, 231 (unsigned long)my_tramp2, 232 }; 233 234 static struct ftrace_ops direct; 235 236 static int simple_thread(void *arg) 237 { 238 static int t; 239 int ret = 0; 240 241 while (!kthread_should_stop()) { 242 set_current_state(TASK_INTERRUPTIBLE); 243 schedule_timeout(2 * HZ); 244 245 if (ret) 246 continue; 247 t ^= 1; 248 ret = modify_ftrace_direct(&direct, tramps[t]); 249 if (!ret) 250 my_tramp = tramps[t]; 251 WARN_ON_ONCE(ret); 252 } 253 254 return 0; 255 } 256 257 static struct task_struct *simple_tsk; 258 259 static int __init ftrace_direct_multi_init(void) 260 { 261 int ret; 262 263 ftrace_set_filter_ip(&direct, (unsigned long) wake_up_process, 0, 0); 264 ftrace_set_filter_ip(&direct, (unsigned long) schedule, 0, 0); 265 266 ret = register_ftrace_direct(&direct, my_tramp); 267 268 if (!ret) 269 simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn"); 270 return ret; 271 } 272 273 static void __exit ftrace_direct_multi_exit(void) 274 { 275 kthread_stop(simple_tsk); 276 unregister_ftrace_direct(&direct, my_tramp, true); 277 } 278 279 module_init(ftrace_direct_multi_init); 280 module_exit(ftrace_direct_multi_exit); 281 282 MODULE_AUTHOR("Jiri Olsa"); 283 MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct()"); 284 MODULE_LICENSE("GPL"); 285