xref: /linux/arch/loongarch/kernel/unwind_prologue.c (revision 64275e9fda3702bfb5ab3b95f7c2b9b414667164)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/cpumask.h>
6 #include <linux/export.h>
7 #include <linux/ftrace.h>
8 #include <linux/kallsyms.h>
9 
10 #include <asm/inst.h>
11 #include <asm/loongson.h>
12 #include <asm/ptrace.h>
13 #include <asm/setup.h>
14 #include <asm/unwind.h>
15 
16 extern const int unwind_hint_ade;
17 extern const int unwind_hint_ale;
18 extern const int unwind_hint_bp;
19 extern const int unwind_hint_fpe;
20 extern const int unwind_hint_fpu;
21 extern const int unwind_hint_lsx;
22 extern const int unwind_hint_lasx;
23 extern const int unwind_hint_lbt;
24 extern const int unwind_hint_ri;
25 extern const int unwind_hint_watch;
26 
scan_handlers(unsigned long entry_offset)27 static inline bool scan_handlers(unsigned long entry_offset)
28 {
29 	int idx, offset;
30 
31 	if (entry_offset >= EXCCODE_INT_START * VECSIZE)
32 		return false;
33 
34 	idx = entry_offset / VECSIZE;
35 	offset = entry_offset % VECSIZE;
36 	switch (idx) {
37 	case EXCCODE_ADE:
38 		return offset == unwind_hint_ade;
39 	case EXCCODE_ALE:
40 		return offset == unwind_hint_ale;
41 	case EXCCODE_BP:
42 		return offset == unwind_hint_bp;
43 	case EXCCODE_FPE:
44 		return offset == unwind_hint_fpe;
45 	case EXCCODE_FPDIS:
46 		return offset == unwind_hint_fpu;
47 	case EXCCODE_LSXDIS:
48 		return offset == unwind_hint_lsx;
49 	case EXCCODE_LASXDIS:
50 		return offset == unwind_hint_lasx;
51 	case EXCCODE_BTDIS:
52 		return offset == unwind_hint_lbt;
53 	case EXCCODE_INE:
54 		return offset == unwind_hint_ri;
55 	case EXCCODE_WATCH:
56 		return offset == unwind_hint_watch;
57 	default:
58 		return false;
59 	}
60 }
61 
fix_exception(unsigned long pc)62 static inline bool fix_exception(unsigned long pc)
63 {
64 #if defined(CONFIG_NUMA) && !defined(CONFIG_PREEMPT_RT)
65 	int cpu;
66 
67 	for_each_possible_cpu(cpu) {
68 		if (!pcpu_handlers[cpu])
69 			continue;
70 		if (scan_handlers(pc - pcpu_handlers[cpu]))
71 			return true;
72 	}
73 #endif
74 	return scan_handlers(pc - eentry);
75 }
76 
77 /*
78  * As we meet ftrace_regs_entry, reset first flag like first doing
79  * tracing. Prologue analysis will stop soon because PC is at entry.
80  */
fix_ftrace(unsigned long pc)81 static inline bool fix_ftrace(unsigned long pc)
82 {
83 #ifdef CONFIG_DYNAMIC_FTRACE
84 	return pc == (unsigned long)ftrace_call + LOONGARCH_INSN_SIZE;
85 #else
86 	return false;
87 #endif
88 }
89 
unwind_state_fixup(struct unwind_state * state)90 static inline bool unwind_state_fixup(struct unwind_state *state)
91 {
92 	if (!fix_exception(state->pc) && !fix_ftrace(state->pc))
93 		return false;
94 
95 	state->reset = true;
96 	return true;
97 }
98 
99 /*
100  * LoongArch function prologue is like follows,
101  *     [instructions not use stack var]
102  *     addi.d sp, sp, -imm
103  *     st.d   xx, sp, offset <- save callee saved regs and
104  *     st.d   yy, sp, offset    save ra if function is nest.
105  *     [others instructions]
106  */
unwind_by_prologue(struct unwind_state * state)107 static bool unwind_by_prologue(struct unwind_state *state)
108 {
109 	long frame_ra = -1;
110 	unsigned long frame_size = 0;
111 	unsigned long size, offset, pc;
112 	struct pt_regs *regs;
113 	struct stack_info *info = &state->stack_info;
114 	union loongarch_instruction *ip, *ip_end;
115 
116 	if (state->sp >= info->end || state->sp < info->begin)
117 		return false;
118 
119 	if (state->reset) {
120 		regs = (struct pt_regs *)state->sp;
121 		state->first = true;
122 		state->reset = false;
123 		state->pc = regs->csr_era;
124 		state->ra = regs->regs[1];
125 		state->sp = regs->regs[3];
126 		return true;
127 	}
128 
129 	/*
130 	 * When first is not set, the PC is a return address in the previous frame.
131 	 * We need to adjust its value in case overflow to the next symbol.
132 	 */
133 	pc = state->pc - (state->first ? 0 : LOONGARCH_INSN_SIZE);
134 	if (!kallsyms_lookup_size_offset(pc, &size, &offset))
135 		return false;
136 
137 	ip = (union loongarch_instruction *)(pc - offset);
138 	ip_end = (union loongarch_instruction *)pc;
139 
140 	while (ip < ip_end) {
141 		if (is_stack_alloc_ins(ip)) {
142 			frame_size = (1 << 12) - ip->reg2i12_format.immediate;
143 			ip++;
144 			break;
145 		}
146 		ip++;
147 	}
148 
149 	/*
150 	 * Can't find stack alloc action, PC may be in a leaf function. Only the
151 	 * first being true is reasonable, otherwise indicate analysis is broken.
152 	 */
153 	if (!frame_size) {
154 		if (state->first)
155 			goto first;
156 
157 		return false;
158 	}
159 
160 	while (ip < ip_end) {
161 		if (is_ra_save_ins(ip)) {
162 			frame_ra = ip->reg2i12_format.immediate;
163 			break;
164 		}
165 		if (is_branch_ins(ip))
166 			break;
167 		ip++;
168 	}
169 
170 	/* Can't find save $ra action, PC may be in a leaf function, too. */
171 	if (frame_ra < 0) {
172 		if (state->first) {
173 			state->sp = state->sp + frame_size;
174 			goto first;
175 		}
176 		return false;
177 	}
178 
179 	state->pc = *(unsigned long *)(state->sp + frame_ra);
180 	state->sp = state->sp + frame_size;
181 	goto out;
182 
183 first:
184 	state->pc = state->ra;
185 
186 out:
187 	state->first = false;
188 	return unwind_state_fixup(state) || __kernel_text_address(state->pc);
189 }
190 
next_frame(struct unwind_state * state)191 static bool next_frame(struct unwind_state *state)
192 {
193 	unsigned long pc;
194 	struct pt_regs *regs;
195 	struct stack_info *info = &state->stack_info;
196 
197 	if (unwind_done(state))
198 		return false;
199 
200 	do {
201 		if (unwind_by_prologue(state)) {
202 			state->pc = unwind_graph_addr(state, state->pc, state->sp);
203 			return true;
204 		}
205 
206 		if (info->type == STACK_TYPE_IRQ && info->end == state->sp) {
207 			regs = (struct pt_regs *)info->next_sp;
208 			pc = regs->csr_era;
209 
210 			if (user_mode(regs) || !__kernel_text_address(pc))
211 				goto out;
212 
213 			state->first = true;
214 			state->pc = pc;
215 			state->ra = regs->regs[1];
216 			state->sp = regs->regs[3];
217 			get_stack_info(state->sp, state->task, info);
218 
219 			return true;
220 		}
221 
222 		state->sp = info->next_sp;
223 
224 	} while (!get_stack_info(state->sp, state->task, info));
225 
226 out:
227 	state->stack_info.type = STACK_TYPE_UNKNOWN;
228 	return false;
229 }
230 
unwind_get_return_address(struct unwind_state * state)231 unsigned long unwind_get_return_address(struct unwind_state *state)
232 {
233 	return __unwind_get_return_address(state);
234 }
235 EXPORT_SYMBOL_GPL(unwind_get_return_address);
236 
unwind_start(struct unwind_state * state,struct task_struct * task,struct pt_regs * regs)237 void unwind_start(struct unwind_state *state, struct task_struct *task,
238 		    struct pt_regs *regs)
239 {
240 	__unwind_start(state, task, regs);
241 	state->type = UNWINDER_PROLOGUE;
242 	state->first = true;
243 
244 	/*
245 	 * The current PC is not kernel text address, we cannot find its
246 	 * relative symbol. Thus, prologue analysis will be broken. Luckily,
247 	 * we can use the default_next_frame().
248 	 */
249 	if (!__kernel_text_address(state->pc)) {
250 		state->type = UNWINDER_GUESS;
251 		if (!unwind_done(state))
252 			unwind_next_frame(state);
253 	}
254 }
255 EXPORT_SYMBOL_GPL(unwind_start);
256 
unwind_next_frame(struct unwind_state * state)257 bool unwind_next_frame(struct unwind_state *state)
258 {
259 	return state->type == UNWINDER_PROLOGUE ?
260 			next_frame(state) : default_next_frame(state);
261 }
262 EXPORT_SYMBOL_GPL(unwind_next_frame);
263