xref: /linux/tools/perf/util/libunwind-arch/libunwind-mips.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "libunwind-arch.h"
3 #include "../debug.h"
4 #include "../maps.h"
5 #include "../thread.h"
6 #include "../../../arch/mips/include/uapi/asm/perf_regs.h"
7 #include <linux/compiler.h>
8 #include <linux/kernel.h>
9 #include <linux/zalloc.h>
10 #include <elf.h>
11 #include <errno.h>
12 
13 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
14 #include <libunwind-mips.h>
15 #endif
16 
17 int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused)
18 {
19 #ifndef HAVE_LIBUNWIND_MIPS_SUPPORT
20 	return -EINVAL;
21 #else
22 	switch (unw_regnum) {
23 	case UNW_MIPS_R1 ... UNW_MIPS_R25:
24 		return unw_regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1;
25 	case UNW_MIPS_R28 ... UNW_MIPS_R31:
26 		return unw_regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28;
27 	case UNW_MIPS_PC:
28 		return PERF_REG_MIPS_PC;
29 	default:
30 		pr_err("unwind: invalid reg id %d\n", unw_regnum);
31 		return -EINVAL;
32 	}
33 #endif // HAVE_LIBUNWIND_MIPS_SUPPORT
34 }
35 
36 void __libunwind_arch__flush_access_mips(struct maps *maps __maybe_unused)
37 {
38 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
39 	unw_flush_cache(maps__addr_space(maps), 0, 0);
40 #endif
41 }
42 
43 void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused)
44 {
45 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
46 	unw_destroy_addr_space(maps__addr_space(maps));
47 #endif
48 }
49 
50 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
51 static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
52 			  int need_unwind_info, void *arg)
53 {
54 	return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
55 }
56 
57 static void put_unwind_info(unw_addr_space_t __maybe_unused as,
58 			    unw_proc_info_t *pi __maybe_unused,
59 			    void *arg __maybe_unused)
60 {
61 	pr_debug("unwind: put_unwind_info called\n");
62 }
63 
64 static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
65 				  unw_word_t __maybe_unused *dil_addr,
66 				  void __maybe_unused *arg)
67 {
68 	return -UNW_ENOINFO;
69 }
70 
71 static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
72 		      int __write, void *arg)
73 {
74 	return __libunwind__access_mem(as, addr, valp, __write, arg);
75 }
76 
77 static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
78 		      int __write, void *arg)
79 {
80 	return __libunwind__access_reg(as, regnum, valp, __write, arg);
81 }
82 
83 static int access_fpreg(unw_addr_space_t __maybe_unused as,
84 			unw_regnum_t __maybe_unused num,
85 			unw_fpreg_t __maybe_unused *val,
86 			int __maybe_unused __write,
87 			void __maybe_unused *arg)
88 {
89 	pr_err("unwind: access_fpreg unsupported\n");
90 	return -UNW_EINVAL;
91 }
92 
93 static int resume(unw_addr_space_t __maybe_unused as,
94 		  unw_cursor_t __maybe_unused *cu,
95 		  void __maybe_unused *arg)
96 {
97 	pr_err("unwind: resume unsupported\n");
98 	return -UNW_EINVAL;
99 }
100 
101 static int get_proc_name(unw_addr_space_t __maybe_unused as,
102 			 unw_word_t __maybe_unused addr,
103 			 char __maybe_unused *bufp, size_t __maybe_unused buf_len,
104 			 unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
105 {
106 	pr_err("unwind: get_proc_name unsupported\n");
107 	return -UNW_EINVAL;
108 }
109 #endif
110 
111 void *__libunwind_arch__create_addr_space_mips(void)
112 {
113 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
114 	static unw_accessors_t accessors = {
115 		.find_proc_info		= find_proc_info,
116 		.put_unwind_info	= put_unwind_info,
117 		.get_dyn_info_list_addr	= get_dyn_info_list_addr,
118 		.access_mem		= access_mem,
119 		.access_reg		= access_reg,
120 		.access_fpreg		= access_fpreg,
121 		.resume			= resume,
122 		.get_proc_name		= get_proc_name,
123 	};
124 	unw_addr_space_t addr_space;
125 
126 	addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
127 	unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
128 	return addr_space;
129 #else
130 	return NULL;
131 #endif
132 }
133 
134 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
135 extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
136 					       unw_word_t ip,
137 					       unw_dyn_info_t *di,
138 					       unw_proc_info_t *pi,
139 					       int need_unwind_info, void *arg);
140 #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
141 #endif
142 
143 int __libunwind_arch__dwarf_search_unwind_table_mips(void *as __maybe_unused,
144 						       uint64_t ip __maybe_unused,
145 						       struct libarch_unwind__dyn_info *_di __maybe_unused,
146 						       void *pi __maybe_unused,
147 						       int need_unwind_info __maybe_unused,
148 						       void *arg __maybe_unused)
149 {
150 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
151 	unw_dyn_info_t di = {
152 		.format     = UNW_INFO_FORMAT_REMOTE_TABLE,
153 		.start_ip   = _di->start_ip,
154 		.end_ip     = _di->end_ip,
155 		.u = {
156 			.rti = {
157 				.segbase    = _di->segbase,
158 				.table_data = _di->table_data,
159 				.table_len  = _di->table_len,
160 			},
161 		},
162 	};
163 	int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
164 
165 	_di->start_ip = di.start_ip;
166 	_di->end_ip = di.end_ip;
167 	_di->segbase = di.u.rti.segbase;
168 	_di->table_data = di.u.rti.table_data;
169 	_di->table_len = di.u.rti.table_len;
170 	return ret;
171 #else
172 	return -EINVAL;
173 #endif
174 }
175 
176 #if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS)
177 extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
178 					    unw_word_t ip,
179 					    unw_word_t segbase,
180 					    const char *obj_name, unw_word_t start,
181 					    unw_word_t end);
182 #define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
183 #endif
184 
185 int __libunwind_arch__dwarf_find_debug_frame_mips(int found __maybe_unused,
186 						 struct libarch_unwind__dyn_info *_di __maybe_unused,
187 						 uint64_t ip __maybe_unused,
188 						 uint64_t segbase __maybe_unused,
189 						 const char *obj_name __maybe_unused,
190 						 uint64_t start __maybe_unused,
191 						 uint64_t end __maybe_unused)
192 {
193 #if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS)
194 	unw_dyn_info_t di = {
195 		.format     = UNW_INFO_FORMAT_REMOTE_TABLE,
196 		.start_ip   = _di->start_ip,
197 		.end_ip     = _di->end_ip,
198 		.u = {
199 			.rti = {
200 				.segbase    = _di->segbase,
201 				.table_data = _di->table_data,
202 				.table_len  = _di->table_len,
203 			},
204 		},
205 	};
206 	int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
207 
208 	_di->start_ip = di.start_ip;
209 	_di->end_ip = di.end_ip;
210 	_di->segbase = di.u.ti.segbase;
211 	_di->table_data = di.u.ti.table_data;
212 	_di->table_len = di.u.ti.table_len;
213 	return ret;
214 #else
215 	return -EINVAL;
216 #endif
217 }
218 
219 struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread __maybe_unused,
220 							     struct perf_sample *sample  __maybe_unused,
221 							     int max_stack __maybe_unused,
222 							     bool best_effort  __maybe_unused,
223 							     uint64_t first_ip  __maybe_unused)
224 {
225 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
226 	struct arch_unwind_info {
227 		struct unwind_info ui;
228 		unw_cursor_t _cursor;
229 		uint64_t _ips[];
230 	};
231 
232 	struct maps *maps = thread__maps(thread);
233 	void *addr_space = maps__addr_space(maps);
234 	struct arch_unwind_info *ui;
235 	int ret;
236 
237 	if (addr_space == NULL)
238 		return NULL;
239 
240 	ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
241 	if (!ui)
242 		return NULL;
243 
244 	ui->ui.machine = maps__machine(maps);
245 	ui->ui.thread = thread;
246 	ui->ui.sample = sample;
247 	ui->ui.cursor = &ui->_cursor;
248 	ui->ui.ips = &ui->_ips[0];
249 	ui->ui.ips[0] = first_ip;
250 	ui->ui.cur_ip = 1;
251 	ui->ui.max_ips = max_stack;
252 	ui->ui.unw_word_t_size = sizeof(unw_word_t);
253 	ui->ui.e_machine = EM_MIPS;
254 	ui->ui.best_effort = best_effort;
255 
256 	ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
257 	if (ret) {
258 		if (!best_effort)
259 			pr_err("libunwind: %s\n", unw_strerror(ret));
260 		free(ui);
261 		return NULL;
262 	}
263 
264 	return &ui->ui;
265 #else
266 	return NULL;
267 #endif
268 }
269 
270 int __libunwind_arch__unwind_step_mips(struct unwind_info *ui __maybe_unused)
271 {
272 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
273 	int ret;
274 
275 	if (ui->cur_ip >= ui->max_ips)
276 		return 0;
277 
278 	ret = unw_step(ui->cursor);
279 	if (ret > 0) {
280 		uint64_t ip;
281 
282 		unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
283 
284 		if (unw_is_signal_frame(ui->cursor) <= 0) {
285 			/*
286 			 * Decrement the IP for any non-activation frames. This
287 			 * is required to properly find the srcline for caller
288 			 * frames.  See also the documentation for
289 			 * dwfl_frame_pc(), which this code tries to replicate.
290 			 */
291 			--ip;
292 		}
293 		ui->ips[ui->cur_ip++] = ip;
294 	}
295 	return ret;
296 #else
297 	return -EINVAL;
298 #endif
299 }
300