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