1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/kallsyms.h>
6 
7 #include <asm/inst.h>
8 #include <asm/ptrace.h>
9 #include <asm/unwind.h>
10 
11 unsigned long unwind_get_return_address(struct unwind_state *state)
12 {
13 
14 	if (unwind_done(state))
15 		return 0;
16 	else if (state->type)
17 		return state->pc;
18 	else if (state->first)
19 		return state->pc;
20 
21 	return *(unsigned long *)(state->sp);
22 
23 }
24 EXPORT_SYMBOL_GPL(unwind_get_return_address);
25 
26 static bool unwind_by_guess(struct unwind_state *state)
27 {
28 	struct stack_info *info = &state->stack_info;
29 	unsigned long addr;
30 
31 	for (state->sp += sizeof(unsigned long);
32 	     state->sp < info->end;
33 	     state->sp += sizeof(unsigned long)) {
34 		addr = *(unsigned long *)(state->sp);
35 		if (__kernel_text_address(addr))
36 			return true;
37 	}
38 
39 	return false;
40 }
41 
42 static bool unwind_by_prologue(struct unwind_state *state)
43 {
44 	struct stack_info *info = &state->stack_info;
45 	union loongarch_instruction *ip, *ip_end;
46 	unsigned long frame_size = 0, frame_ra = -1;
47 	unsigned long size, offset, pc = state->pc;
48 
49 	if (state->sp >= info->end || state->sp < info->begin)
50 		return false;
51 
52 	if (!kallsyms_lookup_size_offset(pc, &size, &offset))
53 		return false;
54 
55 	ip = (union loongarch_instruction *)(pc - offset);
56 	ip_end = (union loongarch_instruction *)pc;
57 
58 	while (ip < ip_end) {
59 		if (is_stack_alloc_ins(ip)) {
60 			frame_size = (1 << 12) - ip->reg2i12_format.immediate;
61 			ip++;
62 			break;
63 		}
64 		ip++;
65 	}
66 
67 	if (!frame_size) {
68 		if (state->first)
69 			goto first;
70 
71 		return false;
72 	}
73 
74 	while (ip < ip_end) {
75 		if (is_ra_save_ins(ip)) {
76 			frame_ra = ip->reg2i12_format.immediate;
77 			break;
78 		}
79 		if (is_branch_ins(ip))
80 			break;
81 		ip++;
82 	}
83 
84 	if (frame_ra < 0) {
85 		if (state->first) {
86 			state->sp = state->sp + frame_size;
87 			goto first;
88 		}
89 		return false;
90 	}
91 
92 	if (state->first)
93 		state->first = false;
94 
95 	state->pc = *(unsigned long *)(state->sp + frame_ra);
96 	state->sp = state->sp + frame_size;
97 	return !!__kernel_text_address(state->pc);
98 
99 first:
100 	state->first = false;
101 	if (state->pc == state->ra)
102 		return false;
103 
104 	state->pc = state->ra;
105 
106 	return !!__kernel_text_address(state->ra);
107 }
108 
109 void unwind_start(struct unwind_state *state, struct task_struct *task,
110 		    struct pt_regs *regs)
111 {
112 	memset(state, 0, sizeof(*state));
113 
114 	if (regs &&  __kernel_text_address(regs->csr_era)) {
115 		state->pc = regs->csr_era;
116 		state->sp = regs->regs[3];
117 		state->ra = regs->regs[1];
118 		state->type = UNWINDER_PROLOGUE;
119 	}
120 
121 	state->task = task;
122 	state->first = true;
123 
124 	get_stack_info(state->sp, state->task, &state->stack_info);
125 
126 	if (!unwind_done(state) && !__kernel_text_address(state->pc))
127 		unwind_next_frame(state);
128 }
129 EXPORT_SYMBOL_GPL(unwind_start);
130 
131 bool unwind_next_frame(struct unwind_state *state)
132 {
133 	struct stack_info *info = &state->stack_info;
134 	struct pt_regs *regs;
135 	unsigned long pc;
136 
137 	if (unwind_done(state))
138 		return false;
139 
140 	do {
141 		switch (state->type) {
142 		case UNWINDER_GUESS:
143 			state->first = false;
144 			if (unwind_by_guess(state))
145 				return true;
146 			break;
147 
148 		case UNWINDER_PROLOGUE:
149 			if (unwind_by_prologue(state))
150 				return true;
151 
152 			if (info->type == STACK_TYPE_IRQ &&
153 				info->end == state->sp) {
154 				regs = (struct pt_regs *)info->next_sp;
155 				pc = regs->csr_era;
156 
157 				if (user_mode(regs) || !__kernel_text_address(pc))
158 					return false;
159 
160 				state->pc = pc;
161 				state->sp = regs->regs[3];
162 				state->ra = regs->regs[1];
163 				state->first = true;
164 				get_stack_info(state->sp, state->task, info);
165 
166 				return true;
167 			}
168 		}
169 
170 		state->sp = info->next_sp;
171 
172 	} while (!get_stack_info(state->sp, state->task, info));
173 
174 	return false;
175 }
176 EXPORT_SYMBOL_GPL(unwind_next_frame);
177