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