162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2022 Loongson Technology Corporation Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/ftrace.h> 862306a36Sopenharmony_ci#include <linux/syscalls.h> 962306a36Sopenharmony_ci#include <linux/uaccess.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/asm.h> 1262306a36Sopenharmony_ci#include <asm/asm-offsets.h> 1362306a36Sopenharmony_ci#include <asm/cacheflush.h> 1462306a36Sopenharmony_ci#include <asm/inst.h> 1562306a36Sopenharmony_ci#include <asm/loongarch.h> 1662306a36Sopenharmony_ci#include <asm/syscall.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm-generic/sections.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#ifdef CONFIG_FUNCTION_GRAPH_TRACER 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * As `call _mcount` follows LoongArch psABI, ra-saved operation and 2462306a36Sopenharmony_ci * stack operation can be found before this insn. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int ftrace_get_parent_ra_addr(unsigned long insn_addr, int *ra_off) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci int limit = 32; 3062306a36Sopenharmony_ci union loongarch_instruction *insn; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci insn = (union loongarch_instruction *)insn_addr; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci do { 3562306a36Sopenharmony_ci insn--; 3662306a36Sopenharmony_ci limit--; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (is_ra_save_ins(insn)) 3962306a36Sopenharmony_ci *ra_off = -((1 << 12) - insn->reg2i12_format.immediate); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci } while (!is_stack_alloc_ins(insn) && limit); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (!limit) 4462306a36Sopenharmony_ci return -EINVAL; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_civoid prepare_ftrace_return(unsigned long self_addr, 5062306a36Sopenharmony_ci unsigned long callsite_sp, unsigned long old) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int ra_off; 5362306a36Sopenharmony_ci unsigned long return_hooker = (unsigned long)&return_to_handler; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (unlikely(ftrace_graph_is_dead())) 5662306a36Sopenharmony_ci return; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (unlikely(atomic_read(¤t->tracing_graph_pause))) 5962306a36Sopenharmony_ci return; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (ftrace_get_parent_ra_addr(self_addr, &ra_off)) 6262306a36Sopenharmony_ci goto out; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!function_graph_enter(old, self_addr, 0, NULL)) 6562306a36Sopenharmony_ci *(unsigned long *)(callsite_sp + ra_off) = return_hooker; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciout: 7062306a36Sopenharmony_ci ftrace_graph_stop(); 7162306a36Sopenharmony_ci WARN_ON(1); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 74