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