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 9extern void my_direct_func1(unsigned long ip); 10extern void my_direct_func2(unsigned long ip); 11 12void my_direct_func1(unsigned long ip) 13{ 14 trace_printk("my direct func1 ip %lx\n", ip); 15} 16 17void my_direct_func2(unsigned long ip) 18{ 19 trace_printk("my direct func2 ip %lx\n", ip); 20} 21 22extern void my_tramp1(void *); 23extern void my_tramp2(void *); 24 25#ifdef CONFIG_RISCV 26#include <asm/asm.h> 27 28asm ( 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 71asm ( 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 109asm ( 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 151asm ( 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 190asm ( 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 228static unsigned long my_tramp = (unsigned long)my_tramp1; 229static unsigned long tramps[2] = { 230 (unsigned long)my_tramp1, 231 (unsigned long)my_tramp2, 232}; 233 234static struct ftrace_ops direct; 235 236static 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 257static struct task_struct *simple_tsk; 258 259static 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 273static void __exit ftrace_direct_multi_exit(void) 274{ 275 kthread_stop(simple_tsk); 276 unregister_ftrace_direct(&direct, my_tramp, true); 277} 278 279module_init(ftrace_direct_multi_init); 280module_exit(ftrace_direct_multi_exit); 281 282MODULE_AUTHOR("Jiri Olsa"); 283MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct()"); 284MODULE_LICENSE("GPL"); 285