1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/kernel.h>
6 #include <linux/ftrace.h>
7 
8 #include <asm/unwind.h>
9 
10 unsigned long unwind_get_return_address(struct unwind_state *state)
11 {
12 	if (unwind_done(state))
13 		return 0;
14 	else if (state->first)
15 		return state->pc;
16 
17 	return *(unsigned long *)(state->sp);
18 }
19 EXPORT_SYMBOL_GPL(unwind_get_return_address);
20 
21 void unwind_start(struct unwind_state *state, struct task_struct *task,
22 		    struct pt_regs *regs)
23 {
24 	memset(state, 0, sizeof(*state));
25 
26 	if (regs) {
27 		state->sp = regs->regs[3];
28 		state->pc = regs->csr_era;
29 	}
30 
31 	state->task = task;
32 	state->first = true;
33 
34 	get_stack_info(state->sp, state->task, &state->stack_info);
35 
36 	if (!unwind_done(state) && !__kernel_text_address(state->pc))
37 		unwind_next_frame(state);
38 }
39 EXPORT_SYMBOL_GPL(unwind_start);
40 
41 bool unwind_next_frame(struct unwind_state *state)
42 {
43 	struct stack_info *info = &state->stack_info;
44 	unsigned long addr;
45 
46 	if (unwind_done(state))
47 		return false;
48 
49 	if (state->first)
50 		state->first = false;
51 
52 	do {
53 		for (state->sp += sizeof(unsigned long);
54 		     state->sp < info->end;
55 		     state->sp += sizeof(unsigned long)) {
56 			addr = *(unsigned long *)(state->sp);
57 			state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
58 					addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
59 			if (__kernel_text_address(addr))
60 				return true;
61 		}
62 
63 		state->sp = info->next_sp;
64 
65 	} while (!get_stack_info(state->sp, state->task, info));
66 
67 	return false;
68 }
69 EXPORT_SYMBOL_GPL(unwind_next_frame);
70