• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 Loongson Technology Co., Ltd.
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 
unwind_get_return_address(struct unwind_state * state)12 unsigned long unwind_get_return_address(struct unwind_state *state)
13 {
14 	if (unwind_done(state))
15 		return 0;
16 
17 	return state->pc;
18 }
19 EXPORT_SYMBOL_GPL(unwind_get_return_address);
20 
is_stack_open_ins(union loongarch_instruction * ip)21 static inline bool is_stack_open_ins(union loongarch_instruction *ip)
22 {
23 	/* addi.d $sp, $sp, -imm */
24 	return ip->reg2i12_format.opcode == addid_op &&
25 		ip->reg2i12_format.rj == 3 &&
26 		ip->reg2i12_format.rd == 3 &&
27 		ip->reg2i12_format.simmediate < 0;
28 }
29 
is_ra_save_ins(union loongarch_instruction * ip)30 static inline bool is_ra_save_ins(union loongarch_instruction *ip)
31 {
32 	/* st.d $ra, $sp, offset */
33 	return ip->reg2i12_format.opcode == std_op &&
34 		  ip->reg2i12_format.rj == 3 &&
35 		  ip->reg2i12_format.rd == 1;
36 }
37 
is_branch_ins(union loongarch_instruction * ip)38 static inline bool is_branch_ins(union loongarch_instruction *ip)
39 {
40 	return is_branch_insn(*ip);
41 }
42 
unwind_state_fixup(struct unwind_state * state)43 static inline void unwind_state_fixup(struct unwind_state *state)
44 {
45 #ifdef CONFIG_DYNAMIC_FTRACE
46 	static unsigned long ftrace_case = (unsigned long)ftrace_call + 4;
47 
48 	if (state->pc == ftrace_case)
49 		state->is_ftrace = true;
50 #endif
51 }
52 
unwind_by_prologue(struct unwind_state * state)53 static bool unwind_by_prologue(struct unwind_state *state)
54 {
55 	struct stack_info *info = &state->stack_info;
56 	union loongarch_instruction *ip, *ip_end;
57 	unsigned long frame_size = 0, frame_ra = -1;
58 	unsigned long size, offset, pc;
59 	struct pt_regs *regs;
60 
61 	if (state->sp >= info->end || state->sp < info->begin)
62 		return false;
63 
64 	if (state->is_ftrace) {
65 		/*
66 		 * As we meet ftrace_regs_entry, reset first flag like first doing
67 		 * tracing, Prologue analysis will stop soon because PC is at entry.
68 		 */
69 		regs = (struct pt_regs *)state->sp;
70 		state->pc = regs->csr_era;
71 		state->ra = regs->regs[1];
72 		state->sp = regs->regs[3];
73 		state->first = true;
74 		state->is_ftrace = false;
75 		return true;
76 	}
77 
78 	/*
79 	 * When first is not set, the PC is a return address in the previous frame.
80 	 * We need to adjust its value in case overflow to the next symbol.
81 	 */
82 	pc = state->pc - (state->first ? 0 : LOONGARCH_INSN_SIZE);
83 	if (!kallsyms_lookup_size_offset(pc, &size, &offset))
84 		return false;
85 
86 	ip = (union loongarch_instruction *)(pc - offset);
87 	ip_end = (union loongarch_instruction *)pc;
88 
89 	while (ip < ip_end) {
90 		if (is_stack_open_ins(ip)) {
91 			frame_size = -ip->reg2i12_format.simmediate;
92 			ip++;
93 			break;
94 		}
95 		ip++;
96 	}
97 
98 	if (!frame_size) {
99 		if (state->first)
100 			goto first;
101 
102 		return false;
103 	}
104 
105 	while (ip < ip_end) {
106 		if (is_ra_save_ins(ip)) {
107 			frame_ra = ip->reg2i12_format.simmediate;
108 			break;
109 		}
110 		if (is_branch_ins(ip))
111 			break;
112 		ip++;
113 	}
114 
115 	if (frame_ra < 0) {
116 		if (state->first) {
117 			state->sp = state->sp + frame_size;
118 			goto first;
119 		}
120 		return false;
121 	}
122 
123 	if (state->first)
124 		state->first = false;
125 
126 	state->pc = *(unsigned long *)(state->sp + frame_ra);
127 	state->sp = state->sp + frame_size;
128 	goto out;
129 
130 first:
131 	state->first = false;
132 	if (state->pc == state->ra)
133 		return false;
134 
135 	state->pc = state->ra;
136 
137 out:
138 	unwind_state_fixup(state);
139 	return !!__kernel_text_address(state->pc);
140 }
141 
unwind_by_guess(struct unwind_state * state)142 static bool unwind_by_guess(struct unwind_state *state)
143 {
144 	struct stack_info *info = &state->stack_info;
145 	unsigned long addr;
146 
147 	for (state->sp += sizeof(unsigned long);
148 	     state->sp < info->end;
149 	     state->sp += sizeof(unsigned long)) {
150 		addr = *(unsigned long *)(state->sp);
151 
152 		state->pc = unwind_graph_addr(state, addr, state->sp + 8);
153 		if (__kernel_text_address(state->pc))
154 			return true;
155 	}
156 
157 	return false;
158 }
159 
unwind_next_frame(struct unwind_state * state)160 bool unwind_next_frame(struct unwind_state *state)
161 {
162 	struct stack_info *info = &state->stack_info;
163 	struct pt_regs *regs;
164 	unsigned long pc;
165 
166 	if (unwind_done(state))
167 		return false;
168 
169 	do {
170 		if (state->enable) {
171 			if (unwind_by_prologue(state)) {
172 				state->pc = unwind_graph_addr(state, state->pc, state->sp);
173 				return true;
174 			}
175 			if (info->type == STACK_TYPE_IRQ &&
176 				info->end == state->sp) {
177 				regs = (struct pt_regs *)info->next_sp;
178 				pc = regs->csr_era;
179 				if (user_mode(regs) || !__kernel_text_address(pc))
180 					goto out;
181 
182 				state->pc = pc;
183 				state->sp = regs->regs[3];
184 				state->ra = regs->regs[1];
185 				state->first = true;
186 				get_stack_info(state->sp, state->task, info);
187 
188 				return true;
189 			}
190 		} else {
191 			if (state->first)
192 				state->first = false;
193 
194 			if (unwind_by_guess(state))
195 				return true;
196 		}
197 
198 		state->sp = info->next_sp;
199 
200 	} while (!get_stack_info(state->sp, state->task, info));
201 
202 out:
203 	state->stack_info.type = STACK_TYPE_UNKNOWN;
204 	return false;
205 }
206 EXPORT_SYMBOL_GPL(unwind_next_frame);
207 
unwind_start(struct unwind_state * state,struct task_struct * task,struct pt_regs * regs)208 void unwind_start(struct unwind_state *state, struct task_struct *task,
209 		    struct pt_regs *regs)
210 {
211 	memset(state, 0, sizeof(*state));
212 
213 	if (__kernel_text_address(regs->csr_era)) {
214 		state->enable = true;
215 	}
216 
217 	state->task = task;
218 	state->pc = regs->csr_era;
219 	state->sp = regs->regs[3];
220 	state->ra = regs->regs[1];
221 	state->first = true;
222 	state->pc = unwind_graph_addr(state, state->pc, state->sp);
223 	get_stack_info(state->sp, state->task, &state->stack_info);
224 
225 	if (!unwind_done(state) && !__kernel_text_address(state->pc))
226 		unwind_next_frame(state);
227 }
228 EXPORT_SYMBOL_GPL(unwind_start);
229