xref: /linux/arch/loongarch/kernel/unwind_prologue.c (revision 9f2c9170934eace462499ba0bfe042cc72900173)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/ftrace.h>
6 #include <linux/kallsyms.h>
7 
8 #include <asm/inst.h>
9 #include <asm/ptrace.h>
10 #include <asm/unwind.h>
11 
12 static inline void unwind_state_fixup(struct unwind_state *state)
13 {
14 #ifdef CONFIG_DYNAMIC_FTRACE
15 	static unsigned long ftrace = (unsigned long)ftrace_call + 4;
16 
17 	if (state->pc == ftrace)
18 		state->is_ftrace = true;
19 #endif
20 }
21 
22 unsigned long unwind_get_return_address(struct unwind_state *state)
23 {
24 
25 	if (unwind_done(state))
26 		return 0;
27 	else if (state->type)
28 		return state->pc;
29 	else if (state->first)
30 		return state->pc;
31 
32 	return *(unsigned long *)(state->sp);
33 
34 }
35 EXPORT_SYMBOL_GPL(unwind_get_return_address);
36 
37 static bool unwind_by_guess(struct unwind_state *state)
38 {
39 	struct stack_info *info = &state->stack_info;
40 	unsigned long addr;
41 
42 	for (state->sp += sizeof(unsigned long);
43 	     state->sp < info->end;
44 	     state->sp += sizeof(unsigned long)) {
45 		addr = *(unsigned long *)(state->sp);
46 		state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
47 				addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
48 		if (__kernel_text_address(addr))
49 			return true;
50 	}
51 
52 	return false;
53 }
54 
55 static bool unwind_by_prologue(struct unwind_state *state)
56 {
57 	long frame_ra = -1;
58 	unsigned long frame_size = 0;
59 	unsigned long size, offset, pc = state->pc;
60 	struct pt_regs *regs;
61 	struct stack_info *info = &state->stack_info;
62 	union loongarch_instruction *ip, *ip_end;
63 
64 	if (state->sp >= info->end || state->sp < info->begin)
65 		return false;
66 
67 	if (state->is_ftrace) {
68 		/*
69 		 * As we meet ftrace_regs_entry, reset first flag like first doing
70 		 * tracing. Prologue analysis will stop soon because PC is at entry.
71 		 */
72 		regs = (struct pt_regs *)state->sp;
73 		state->first = true;
74 		state->is_ftrace = false;
75 		state->pc = regs->csr_era;
76 		state->ra = regs->regs[1];
77 		state->sp = regs->regs[3];
78 		return true;
79 	}
80 
81 	if (!kallsyms_lookup_size_offset(pc, &size, &offset))
82 		return false;
83 
84 	ip = (union loongarch_instruction *)(pc - offset);
85 	ip_end = (union loongarch_instruction *)pc;
86 
87 	while (ip < ip_end) {
88 		if (is_stack_alloc_ins(ip)) {
89 			frame_size = (1 << 12) - ip->reg2i12_format.immediate;
90 			ip++;
91 			break;
92 		}
93 		ip++;
94 	}
95 
96 	if (!frame_size) {
97 		if (state->first)
98 			goto first;
99 
100 		return false;
101 	}
102 
103 	while (ip < ip_end) {
104 		if (is_ra_save_ins(ip)) {
105 			frame_ra = ip->reg2i12_format.immediate;
106 			break;
107 		}
108 		if (is_branch_ins(ip))
109 			break;
110 		ip++;
111 	}
112 
113 	if (frame_ra < 0) {
114 		if (state->first) {
115 			state->sp = state->sp + frame_size;
116 			goto first;
117 		}
118 		return false;
119 	}
120 
121 	if (state->first)
122 		state->first = false;
123 
124 	state->pc = *(unsigned long *)(state->sp + frame_ra);
125 	state->sp = state->sp + frame_size;
126 	goto out;
127 
128 first:
129 	state->first = false;
130 	if (state->pc == state->ra)
131 		return false;
132 
133 	state->pc = state->ra;
134 
135 out:
136 	unwind_state_fixup(state);
137 	return !!__kernel_text_address(state->pc);
138 }
139 
140 void unwind_start(struct unwind_state *state, struct task_struct *task,
141 		    struct pt_regs *regs)
142 {
143 	memset(state, 0, sizeof(*state));
144 
145 	if (regs &&  __kernel_text_address(regs->csr_era)) {
146 		state->pc = regs->csr_era;
147 		state->sp = regs->regs[3];
148 		state->ra = regs->regs[1];
149 		state->type = UNWINDER_PROLOGUE;
150 	}
151 
152 	state->task = task;
153 	state->first = true;
154 
155 	get_stack_info(state->sp, state->task, &state->stack_info);
156 
157 	if (!unwind_done(state) && !__kernel_text_address(state->pc))
158 		unwind_next_frame(state);
159 }
160 EXPORT_SYMBOL_GPL(unwind_start);
161 
162 bool unwind_next_frame(struct unwind_state *state)
163 {
164 	struct stack_info *info = &state->stack_info;
165 	struct pt_regs *regs;
166 	unsigned long pc;
167 
168 	if (unwind_done(state))
169 		return false;
170 
171 	do {
172 		switch (state->type) {
173 		case UNWINDER_GUESS:
174 			state->first = false;
175 			if (unwind_by_guess(state))
176 				return true;
177 			break;
178 
179 		case UNWINDER_PROLOGUE:
180 			if (unwind_by_prologue(state)) {
181 				state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
182 						state->pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
183 				return true;
184 			}
185 
186 			if (info->type == STACK_TYPE_IRQ &&
187 				info->end == state->sp) {
188 				regs = (struct pt_regs *)info->next_sp;
189 				pc = regs->csr_era;
190 
191 				if (user_mode(regs) || !__kernel_text_address(pc))
192 					return false;
193 
194 				state->first = true;
195 				state->ra = regs->regs[1];
196 				state->sp = regs->regs[3];
197 				state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
198 						pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
199 				get_stack_info(state->sp, state->task, info);
200 
201 				return true;
202 			}
203 		}
204 
205 		state->sp = info->next_sp;
206 
207 	} while (!get_stack_info(state->sp, state->task, info));
208 
209 	return false;
210 }
211 EXPORT_SYMBOL_GPL(unwind_next_frame);
212