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