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