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