| 1 | // SPDX-License-Identifier: GPL-2.0 |
|---|---|
| 2 | /* |
| 3 | * Copyright (C) 2022 Loongson Technology Corporation Limited |
| 4 | */ |
| 5 | |
| 6 | #include <linux/init.h> |
| 7 | #include <linux/ftrace.h> |
| 8 | #include <linux/syscalls.h> |
| 9 | #include <linux/uaccess.h> |
| 10 | |
| 11 | #include <asm/asm.h> |
| 12 | #include <asm/asm-offsets.h> |
| 13 | #include <asm/cacheflush.h> |
| 14 | #include <asm/inst.h> |
| 15 | #include <asm/loongarch.h> |
| 16 | #include <asm/syscall.h> |
| 17 | |
| 18 | #include <asm-generic/sections.h> |
| 19 | |
| 20 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 21 | |
| 22 | /* |
| 23 | * As `call _mcount` follows LoongArch psABI, ra-saved operation and |
| 24 | * stack operation can be found before this insn. |
| 25 | */ |
| 26 | |
| 27 | static int ftrace_get_parent_ra_addr(unsigned long insn_addr, int *ra_off) |
| 28 | { |
| 29 | int limit = 32; |
| 30 | union loongarch_instruction *insn; |
| 31 | |
| 32 | insn = (union loongarch_instruction *)insn_addr; |
| 33 | |
| 34 | do { |
| 35 | insn--; |
| 36 | limit--; |
| 37 | |
| 38 | if (is_ra_save_ins(insn)) |
| 39 | *ra_off = -((1 << 12) - insn->reg2i12_format.immediate); |
| 40 | |
| 41 | } while (!is_stack_alloc_ins(insn) && limit); |
| 42 | |
| 43 | if (!limit) |
| 44 | return -EINVAL; |
| 45 | |
| 46 | return 0; |
| 47 | } |
| 48 | |
| 49 | void prepare_ftrace_return(unsigned long self_addr, |
| 50 | unsigned long callsite_sp, unsigned long old) |
| 51 | { |
| 52 | int ra_off; |
| 53 | unsigned long return_hooker = (unsigned long)&return_to_handler; |
| 54 | |
| 55 | if (unlikely(ftrace_graph_is_dead())) |
| 56 | return; |
| 57 | |
| 58 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) |
| 59 | return; |
| 60 | |
| 61 | if (ftrace_get_parent_ra_addr(insn_addr: self_addr, ra_off: &ra_off)) |
| 62 | goto out; |
| 63 | |
| 64 | if (!function_graph_enter(ret: old, func: self_addr, fp: 0, NULL)) |
| 65 | *(unsigned long *)(callsite_sp + ra_off) = return_hooker; |
| 66 | |
| 67 | return; |
| 68 | |
| 69 | out: |
| 70 | ftrace_graph_stop(); |
| 71 | WARN_ON(1); |
| 72 | } |
| 73 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ |
| 74 |
