18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/sched.h> 38c2ecf20Sopenharmony_ci#include <linux/sched/debug.h> 48c2ecf20Sopenharmony_ci#include <linux/stacktrace.h> 58c2ecf20Sopenharmony_ci#include <linux/thread_info.h> 68c2ecf20Sopenharmony_ci#include <linux/ftrace.h> 78c2ecf20Sopenharmony_ci#include <linux/export.h> 88c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 98c2ecf20Sopenharmony_ci#include <asm/stacktrace.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "kstack.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic void __save_stack_trace(struct thread_info *tp, 148c2ecf20Sopenharmony_ci struct stack_trace *trace, 158c2ecf20Sopenharmony_ci bool skip_sched) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci unsigned long ksp, fp; 188c2ecf20Sopenharmony_ci#ifdef CONFIG_FUNCTION_GRAPH_TRACER 198c2ecf20Sopenharmony_ci struct task_struct *t; 208c2ecf20Sopenharmony_ci int graph = 0; 218c2ecf20Sopenharmony_ci#endif 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci if (tp == current_thread_info()) { 248c2ecf20Sopenharmony_ci stack_trace_flush(); 258c2ecf20Sopenharmony_ci __asm__ __volatile__("mov %%fp, %0" : "=r" (ksp)); 268c2ecf20Sopenharmony_ci } else { 278c2ecf20Sopenharmony_ci ksp = tp->ksp; 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci fp = ksp + STACK_BIAS; 318c2ecf20Sopenharmony_ci#ifdef CONFIG_FUNCTION_GRAPH_TRACER 328c2ecf20Sopenharmony_ci t = tp->task; 338c2ecf20Sopenharmony_ci#endif 348c2ecf20Sopenharmony_ci do { 358c2ecf20Sopenharmony_ci struct sparc_stackf *sf; 368c2ecf20Sopenharmony_ci struct pt_regs *regs; 378c2ecf20Sopenharmony_ci unsigned long pc; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (!kstack_valid(tp, fp)) 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci sf = (struct sparc_stackf *) fp; 438c2ecf20Sopenharmony_ci regs = (struct pt_regs *) (sf + 1); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (kstack_is_trap_frame(tp, regs)) { 468c2ecf20Sopenharmony_ci if (!(regs->tstate & TSTATE_PRIV)) 478c2ecf20Sopenharmony_ci break; 488c2ecf20Sopenharmony_ci pc = regs->tpc; 498c2ecf20Sopenharmony_ci fp = regs->u_regs[UREG_I6] + STACK_BIAS; 508c2ecf20Sopenharmony_ci } else { 518c2ecf20Sopenharmony_ci pc = sf->callers_pc; 528c2ecf20Sopenharmony_ci fp = (unsigned long)sf->fp + STACK_BIAS; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (trace->skip > 0) 568c2ecf20Sopenharmony_ci trace->skip--; 578c2ecf20Sopenharmony_ci else if (!skip_sched || !in_sched_functions(pc)) { 588c2ecf20Sopenharmony_ci trace->entries[trace->nr_entries++] = pc; 598c2ecf20Sopenharmony_ci#ifdef CONFIG_FUNCTION_GRAPH_TRACER 608c2ecf20Sopenharmony_ci if ((pc + 8UL) == (unsigned long) &return_to_handler) { 618c2ecf20Sopenharmony_ci struct ftrace_ret_stack *ret_stack; 628c2ecf20Sopenharmony_ci ret_stack = ftrace_graph_get_ret_stack(t, 638c2ecf20Sopenharmony_ci graph); 648c2ecf20Sopenharmony_ci if (ret_stack) { 658c2ecf20Sopenharmony_ci pc = ret_stack->ret; 668c2ecf20Sopenharmony_ci if (trace->nr_entries < 678c2ecf20Sopenharmony_ci trace->max_entries) 688c2ecf20Sopenharmony_ci trace->entries[trace->nr_entries++] = pc; 698c2ecf20Sopenharmony_ci graph++; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci#endif 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci } while (trace->nr_entries < trace->max_entries); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_civoid save_stack_trace(struct stack_trace *trace) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci __save_stack_trace(current_thread_info(), trace, false); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(save_stack_trace); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_civoid save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct thread_info *tp = task_thread_info(tsk); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci __save_stack_trace(tp, trace, true); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(save_stack_trace_tsk); 90