198944Sobrien// SPDX-License-Identifier: GPL-2.0-only 298944Sobrien#include <linux/sched.h> 398944Sobrien#include <linux/sched/debug.h> 498944Sobrien#include <linux/stacktrace.h> 598944Sobrien#include <linux/thread_info.h> 698944Sobrien#include <linux/ftrace.h> 798944Sobrien#include <linux/export.h> 898944Sobrien#include <asm/ptrace.h> 998944Sobrien#include <asm/stacktrace.h> 1098944Sobrien 1198944Sobrien#include "kstack.h" 1298944Sobrien 1398944Sobrienstatic void __save_stack_trace(struct thread_info *tp, 1498944Sobrien struct stack_trace *trace, 1598944Sobrien bool skip_sched) 1698944Sobrien{ 1798944Sobrien unsigned long ksp, fp; 1898944Sobrien#ifdef CONFIG_FUNCTION_GRAPH_TRACER 1998944Sobrien struct task_struct *t; 2098944Sobrien int graph = 0; 2198944Sobrien#endif 2298944Sobrien 2398944Sobrien if (tp == current_thread_info()) { 2498944Sobrien stack_trace_flush(); 2598944Sobrien __asm__ __volatile__("mov %%fp, %0" : "=r" (ksp)); 2698944Sobrien } else { 2798944Sobrien ksp = tp->ksp; 2898944Sobrien } 2998944Sobrien 3098944Sobrien fp = ksp + STACK_BIAS; 3198944Sobrien#ifdef CONFIG_FUNCTION_GRAPH_TRACER 3298944Sobrien t = tp->task; 3398944Sobrien#endif 3498944Sobrien do { 3598944Sobrien struct sparc_stackf *sf; 3698944Sobrien struct pt_regs *regs; 3798944Sobrien unsigned long pc; 3898944Sobrien 3998944Sobrien if (!kstack_valid(tp, fp)) 4098944Sobrien break; 4198944Sobrien 4298944Sobrien sf = (struct sparc_stackf *) fp; 4398944Sobrien regs = (struct pt_regs *) (sf + 1); 4498944Sobrien 4598944Sobrien if (kstack_is_trap_frame(tp, regs)) { 4698944Sobrien if (!(regs->tstate & TSTATE_PRIV)) 4798944Sobrien break; 4898944Sobrien pc = regs->tpc; 4998944Sobrien fp = regs->u_regs[UREG_I6] + STACK_BIAS; 5098944Sobrien } else { 5198944Sobrien pc = sf->callers_pc; 5298944Sobrien fp = (unsigned long)sf->fp + STACK_BIAS; 5398944Sobrien } 5498944Sobrien 5598944Sobrien if (trace->skip > 0) 5698944Sobrien trace->skip--; 5798944Sobrien else if (!skip_sched || !in_sched_functions(pc)) { 5898944Sobrien trace->entries[trace->nr_entries++] = pc; 5998944Sobrien#ifdef CONFIG_FUNCTION_GRAPH_TRACER 6098944Sobrien if ((pc + 8UL) == (unsigned long) &return_to_handler) { 6198944Sobrien struct ftrace_ret_stack *ret_stack; 6298944Sobrien ret_stack = ftrace_graph_get_ret_stack(t, 6398944Sobrien graph); 6498944Sobrien if (ret_stack) { 6598944Sobrien pc = ret_stack->ret; 6698944Sobrien if (trace->nr_entries < 6798944Sobrien trace->max_entries) 6898944Sobrien trace->entries[trace->nr_entries++] = pc; 6998944Sobrien graph++; 7098944Sobrien } 7198944Sobrien } 7298944Sobrien#endif 7398944Sobrien } 7498944Sobrien } while (trace->nr_entries < trace->max_entries); 7598944Sobrien} 7698944Sobrien 7798944Sobrienvoid save_stack_trace(struct stack_trace *trace) 7898944Sobrien{ 7998944Sobrien __save_stack_trace(current_thread_info(), trace, false); 8098944Sobrien} 8198944SobrienEXPORT_SYMBOL_GPL(save_stack_trace); 8298944Sobrien 8398944Sobrienvoid save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 8498944Sobrien{ 8598944Sobrien struct thread_info *tp = task_thread_info(tsk); 8698944Sobrien 8798944Sobrien __save_stack_trace(tp, trace, true); 8898944Sobrien} 8998944SobrienEXPORT_SYMBOL_GPL(save_stack_trace_tsk); 9098944Sobrien