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/s390/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_S390X_SUPPORT 14 #include <libunwind-s390x.h> 15 #endif 16 17 int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused) 18 { 19 #ifndef HAVE_LIBUNWIND_S390X_SUPPORT 20 return -EINVAL; 21 #else 22 switch (unw_regnum) { 23 case UNW_S390X_R0 ... UNW_S390X_R15: 24 return unw_regnum - UNW_S390X_R0 + PERF_REG_S390_R0; 25 case UNW_S390X_F0 ... UNW_S390X_F15: 26 return unw_regnum - UNW_S390X_F0 + PERF_REG_S390_FP0; 27 case UNW_S390X_IP: 28 return PERF_REG_S390_PC; 29 default: 30 pr_err("unwind: invalid reg id %d\n", unw_regnum); 31 return -EINVAL; 32 } 33 #endif // HAVE_LIBUNWIND_S390X_SUPPORT 34 } 35 36 void __libunwind_arch__flush_access_s390(struct maps *maps __maybe_unused) 37 { 38 #ifdef HAVE_LIBUNWIND_S390X_SUPPORT 39 unw_flush_cache(maps__addr_space(maps), 0, 0); 40 #endif 41 } 42 43 void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused) 44 { 45 #ifdef HAVE_LIBUNWIND_S390X_SUPPORT 46 unw_destroy_addr_space(maps__addr_space(maps)); 47 #endif 48 } 49 50 #ifdef HAVE_LIBUNWIND_S390X_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_s390(void) 112 { 113 #ifdef HAVE_LIBUNWIND_S390X_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_S390X_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_s390(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_S390X_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_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X) 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_s390(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_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X) 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_s390(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_S390X_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_S390; 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_s390(struct unwind_info *ui __maybe_unused) 271 { 272 #ifdef HAVE_LIBUNWIND_S390X_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