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