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