Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Originally written by Glenn Engel, Lake Stevens Instrument Division |
| 3 | * |
| 4 | * Contributed by HP Systems |
| 5 | * |
| 6 | * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse |
| 7 | * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> |
| 8 | * |
| 9 | * Copyright (C) 1995 Andreas Busse |
| 10 | * |
| 11 | * Copyright (C) 2003 MontaVista Software Inc. |
| 12 | * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net |
| 13 | * |
| 14 | * Copyright (C) 2004-2005 MontaVista Software Inc. |
| 15 | * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com |
| 16 | * |
| 17 | * Copyright (C) 2007-2008 Wind River Systems, Inc. |
| 18 | * Author/Maintainer: Jason Wessel, jason.wessel@windriver.com |
| 19 | * |
| 20 | * This file is licensed under the terms of the GNU General Public License |
| 21 | * version 2. This program is licensed "as is" without any warranty of any |
| 22 | * kind, whether express or implied. |
| 23 | */ |
| 24 | |
| 25 | #include <linux/ptrace.h> /* for linux pt_regs struct */ |
| 26 | #include <linux/kgdb.h> |
| 27 | #include <linux/kdebug.h> |
| 28 | #include <linux/sched.h> |
Ralf Baechle | 631330f | 2009-06-19 14:05:26 +0100 | [diff] [blame] | 29 | #include <linux/smp.h> |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 30 | #include <asm/inst.h> |
| 31 | #include <asm/fpu.h> |
| 32 | #include <asm/cacheflush.h> |
| 33 | #include <asm/processor.h> |
| 34 | #include <asm/sigcontext.h> |
| 35 | |
| 36 | static struct hard_trap_info { |
| 37 | unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ |
| 38 | unsigned char signo; /* Signal that we map this trap into */ |
| 39 | } hard_trap_info[] = { |
| 40 | { 6, SIGBUS }, /* instruction bus error */ |
| 41 | { 7, SIGBUS }, /* data bus error */ |
| 42 | { 9, SIGTRAP }, /* break */ |
Ralf Baechle | 7034228 | 2013-01-22 12:59:30 +0100 | [diff] [blame^] | 43 | /* { 11, SIGILL }, */ /* CPU unusable */ |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 44 | { 12, SIGFPE }, /* overflow */ |
| 45 | { 13, SIGTRAP }, /* trap */ |
| 46 | { 14, SIGSEGV }, /* virtual instruction cache coherency */ |
| 47 | { 15, SIGFPE }, /* floating point exception */ |
| 48 | { 23, SIGSEGV }, /* watch */ |
| 49 | { 31, SIGSEGV }, /* virtual data cache coherency */ |
| 50 | { 0, 0} /* Must be last */ |
| 51 | }; |
| 52 | |
Jason Wessel | 0896a9b | 2010-08-05 09:22:21 -0500 | [diff] [blame] | 53 | struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = |
| 54 | { |
| 55 | { "zero", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0]) }, |
| 56 | { "at", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1]) }, |
| 57 | { "v0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2]) }, |
| 58 | { "v1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3]) }, |
| 59 | { "a0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4]) }, |
| 60 | { "a1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5]) }, |
| 61 | { "a2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6]) }, |
| 62 | { "a3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7]) }, |
| 63 | { "t0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8]) }, |
| 64 | { "t1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9]) }, |
| 65 | { "t2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10]) }, |
| 66 | { "t3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11]) }, |
| 67 | { "t4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12]) }, |
| 68 | { "t5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13]) }, |
| 69 | { "t6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14]) }, |
| 70 | { "t7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15]) }, |
| 71 | { "s0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[16]) }, |
| 72 | { "s1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[17]) }, |
| 73 | { "s2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[18]) }, |
| 74 | { "s3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[19]) }, |
| 75 | { "s4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[20]) }, |
| 76 | { "s5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[21]) }, |
| 77 | { "s6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[22]) }, |
| 78 | { "s7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[23]) }, |
| 79 | { "t8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[24]) }, |
| 80 | { "t9", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[25]) }, |
| 81 | { "k0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[26]) }, |
| 82 | { "k1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[27]) }, |
| 83 | { "gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[28]) }, |
| 84 | { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[29]) }, |
| 85 | { "s8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[30]) }, |
| 86 | { "ra", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[31]) }, |
| 87 | { "sr", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_status) }, |
| 88 | { "lo", GDB_SIZEOF_REG, offsetof(struct pt_regs, lo) }, |
| 89 | { "hi", GDB_SIZEOF_REG, offsetof(struct pt_regs, hi) }, |
| 90 | { "bad", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_badvaddr) }, |
| 91 | { "cause", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_cause) }, |
| 92 | { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_epc) }, |
| 93 | { "f0", GDB_SIZEOF_REG, 0 }, |
| 94 | { "f1", GDB_SIZEOF_REG, 1 }, |
| 95 | { "f2", GDB_SIZEOF_REG, 2 }, |
| 96 | { "f3", GDB_SIZEOF_REG, 3 }, |
| 97 | { "f4", GDB_SIZEOF_REG, 4 }, |
| 98 | { "f5", GDB_SIZEOF_REG, 5 }, |
| 99 | { "f6", GDB_SIZEOF_REG, 6 }, |
| 100 | { "f7", GDB_SIZEOF_REG, 7 }, |
| 101 | { "f8", GDB_SIZEOF_REG, 8 }, |
| 102 | { "f9", GDB_SIZEOF_REG, 9 }, |
| 103 | { "f10", GDB_SIZEOF_REG, 10 }, |
| 104 | { "f11", GDB_SIZEOF_REG, 11 }, |
| 105 | { "f12", GDB_SIZEOF_REG, 12 }, |
| 106 | { "f13", GDB_SIZEOF_REG, 13 }, |
| 107 | { "f14", GDB_SIZEOF_REG, 14 }, |
| 108 | { "f15", GDB_SIZEOF_REG, 15 }, |
| 109 | { "f16", GDB_SIZEOF_REG, 16 }, |
| 110 | { "f17", GDB_SIZEOF_REG, 17 }, |
| 111 | { "f18", GDB_SIZEOF_REG, 18 }, |
| 112 | { "f19", GDB_SIZEOF_REG, 19 }, |
| 113 | { "f20", GDB_SIZEOF_REG, 20 }, |
| 114 | { "f21", GDB_SIZEOF_REG, 21 }, |
| 115 | { "f22", GDB_SIZEOF_REG, 22 }, |
| 116 | { "f23", GDB_SIZEOF_REG, 23 }, |
| 117 | { "f24", GDB_SIZEOF_REG, 24 }, |
| 118 | { "f25", GDB_SIZEOF_REG, 25 }, |
| 119 | { "f26", GDB_SIZEOF_REG, 26 }, |
| 120 | { "f27", GDB_SIZEOF_REG, 27 }, |
| 121 | { "f28", GDB_SIZEOF_REG, 28 }, |
| 122 | { "f29", GDB_SIZEOF_REG, 29 }, |
| 123 | { "f30", GDB_SIZEOF_REG, 30 }, |
| 124 | { "f31", GDB_SIZEOF_REG, 31 }, |
| 125 | { "fsr", GDB_SIZEOF_REG, 0 }, |
| 126 | { "fir", GDB_SIZEOF_REG, 0 }, |
| 127 | }; |
| 128 | |
| 129 | int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) |
| 130 | { |
| 131 | int fp_reg; |
| 132 | |
| 133 | if (regno < 0 || regno >= DBG_MAX_REG_NUM) |
| 134 | return -EINVAL; |
| 135 | |
| 136 | if (dbg_reg_def[regno].offset != -1 && regno < 38) { |
| 137 | memcpy((void *)regs + dbg_reg_def[regno].offset, mem, |
| 138 | dbg_reg_def[regno].size); |
| 139 | } else if (current && dbg_reg_def[regno].offset != -1 && regno < 72) { |
| 140 | /* FP registers 38 -> 69 */ |
| 141 | if (!(regs->cp0_status & ST0_CU1)) |
| 142 | return 0; |
| 143 | if (regno == 70) { |
| 144 | /* Process the fcr31/fsr (register 70) */ |
| 145 | memcpy((void *)¤t->thread.fpu.fcr31, mem, |
| 146 | dbg_reg_def[regno].size); |
| 147 | goto out_save; |
| 148 | } else if (regno == 71) { |
| 149 | /* Ignore the fir (register 71) */ |
| 150 | goto out_save; |
| 151 | } |
| 152 | fp_reg = dbg_reg_def[regno].offset; |
| 153 | memcpy((void *)¤t->thread.fpu.fpr[fp_reg], mem, |
| 154 | dbg_reg_def[regno].size); |
| 155 | out_save: |
| 156 | restore_fp(current); |
| 157 | } |
| 158 | |
| 159 | return 0; |
| 160 | } |
| 161 | |
| 162 | char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) |
| 163 | { |
| 164 | int fp_reg; |
| 165 | |
| 166 | if (regno >= DBG_MAX_REG_NUM || regno < 0) |
| 167 | return NULL; |
| 168 | |
| 169 | if (dbg_reg_def[regno].offset != -1 && regno < 38) { |
| 170 | /* First 38 registers */ |
| 171 | memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, |
| 172 | dbg_reg_def[regno].size); |
| 173 | } else if (current && dbg_reg_def[regno].offset != -1 && regno < 72) { |
| 174 | /* FP registers 38 -> 69 */ |
| 175 | if (!(regs->cp0_status & ST0_CU1)) |
| 176 | goto out; |
| 177 | save_fp(current); |
| 178 | if (regno == 70) { |
| 179 | /* Process the fcr31/fsr (register 70) */ |
| 180 | memcpy(mem, (void *)¤t->thread.fpu.fcr31, |
| 181 | dbg_reg_def[regno].size); |
| 182 | goto out; |
| 183 | } else if (regno == 71) { |
| 184 | /* Ignore the fir (register 71) */ |
| 185 | memset(mem, 0, dbg_reg_def[regno].size); |
| 186 | goto out; |
| 187 | } |
| 188 | fp_reg = dbg_reg_def[regno].offset; |
| 189 | memcpy(mem, (void *)¤t->thread.fpu.fpr[fp_reg], |
| 190 | dbg_reg_def[regno].size); |
| 191 | } |
| 192 | |
| 193 | out: |
| 194 | return dbg_reg_def[regno].name; |
| 195 | |
| 196 | } |
| 197 | |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 198 | void arch_kgdb_breakpoint(void) |
| 199 | { |
| 200 | __asm__ __volatile__( |
| 201 | ".globl breakinst\n\t" |
| 202 | ".set\tnoreorder\n\t" |
| 203 | "nop\n" |
| 204 | "breakinst:\tbreak\n\t" |
| 205 | "nop\n\t" |
| 206 | ".set\treorder"); |
| 207 | } |
| 208 | |
| 209 | static void kgdb_call_nmi_hook(void *ignored) |
| 210 | { |
Ralf Baechle | 9391d6b | 2008-08-05 23:43:35 +0100 | [diff] [blame] | 211 | kgdb_nmicallback(raw_smp_processor_id(), NULL); |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | void kgdb_roundup_cpus(unsigned long flags) |
| 215 | { |
| 216 | local_irq_enable(); |
Ralf Baechle | e522b7c | 2008-08-05 23:42:16 +0100 | [diff] [blame] | 217 | smp_call_function(kgdb_call_nmi_hook, NULL, 0); |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 218 | local_irq_disable(); |
| 219 | } |
| 220 | |
| 221 | static int compute_signal(int tt) |
| 222 | { |
| 223 | struct hard_trap_info *ht; |
| 224 | |
| 225 | for (ht = hard_trap_info; ht->tt && ht->signo; ht++) |
| 226 | if (ht->tt == tt) |
| 227 | return ht->signo; |
| 228 | |
| 229 | return SIGHUP; /* default for things we don't know about */ |
| 230 | } |
| 231 | |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 232 | /* |
| 233 | * Similar to regs_to_gdb_regs() except that process is sleeping and so |
| 234 | * we may not be able to get all the info. |
| 235 | */ |
| 236 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) |
| 237 | { |
| 238 | int reg; |
| 239 | struct thread_info *ti = task_thread_info(p); |
| 240 | unsigned long ksp = (unsigned long)ti + THREAD_SIZE - 32; |
| 241 | struct pt_regs *regs = (struct pt_regs *)ksp - 1; |
| 242 | #if (KGDB_GDB_REG_SIZE == 32) |
| 243 | u32 *ptr = (u32 *)gdb_regs; |
| 244 | #else |
| 245 | u64 *ptr = (u64 *)gdb_regs; |
| 246 | #endif |
| 247 | |
| 248 | for (reg = 0; reg < 16; reg++) |
| 249 | *(ptr++) = regs->regs[reg]; |
| 250 | |
| 251 | /* S0 - S7 */ |
| 252 | for (reg = 16; reg < 24; reg++) |
| 253 | *(ptr++) = regs->regs[reg]; |
| 254 | |
| 255 | for (reg = 24; reg < 28; reg++) |
| 256 | *(ptr++) = 0; |
| 257 | |
| 258 | /* GP, SP, FP, RA */ |
| 259 | for (reg = 28; reg < 32; reg++) |
| 260 | *(ptr++) = regs->regs[reg]; |
| 261 | |
| 262 | *(ptr++) = regs->cp0_status; |
| 263 | *(ptr++) = regs->lo; |
| 264 | *(ptr++) = regs->hi; |
| 265 | *(ptr++) = regs->cp0_badvaddr; |
| 266 | *(ptr++) = regs->cp0_cause; |
| 267 | *(ptr++) = regs->cp0_epc; |
| 268 | } |
| 269 | |
Jason Wessel | dcc7871 | 2010-05-20 21:04:21 -0500 | [diff] [blame] | 270 | void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) |
| 271 | { |
| 272 | regs->cp0_epc = pc; |
| 273 | } |
| 274 | |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 275 | /* |
| 276 | * Calls linux_debug_hook before the kernel dies. If KGDB is enabled, |
| 277 | * then try to fall into the debugger |
| 278 | */ |
| 279 | static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd, |
| 280 | void *ptr) |
| 281 | { |
| 282 | struct die_args *args = (struct die_args *)ptr; |
| 283 | struct pt_regs *regs = args->regs; |
| 284 | int trap = (regs->cp0_cause & 0x7c) >> 2; |
| 285 | |
Jason Wessel | f0a996e | 2012-08-10 12:21:15 -0500 | [diff] [blame] | 286 | #ifdef CONFIG_KPROBES |
| 287 | /* |
| 288 | * Return immediately if the kprobes fault notifier has set |
| 289 | * DIE_PAGE_FAULT. |
| 290 | */ |
| 291 | if (cmd == DIE_PAGE_FAULT) |
| 292 | return NOTIFY_DONE; |
| 293 | #endif /* CONFIG_KPROBES */ |
| 294 | |
Andrea Gelmini | eefc3f3 | 2010-08-05 15:51:25 +0200 | [diff] [blame] | 295 | /* Userspace events, ignore. */ |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 296 | if (user_mode(regs)) |
| 297 | return NOTIFY_DONE; |
| 298 | |
| 299 | if (atomic_read(&kgdb_active) != -1) |
| 300 | kgdb_nmicallback(smp_processor_id(), regs); |
| 301 | |
Jason Wessel | 5dd11d5 | 2010-05-20 21:04:26 -0500 | [diff] [blame] | 302 | if (kgdb_handle_exception(trap, compute_signal(trap), cmd, regs)) |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 303 | return NOTIFY_DONE; |
| 304 | |
| 305 | if (atomic_read(&kgdb_setting_breakpoint)) |
| 306 | if ((trap == 9) && (regs->cp0_epc == (unsigned long)breakinst)) |
| 307 | regs->cp0_epc += 4; |
| 308 | |
| 309 | /* In SMP mode, __flush_cache_all does IPI */ |
| 310 | local_irq_enable(); |
| 311 | __flush_cache_all(); |
| 312 | |
| 313 | return NOTIFY_STOP; |
| 314 | } |
| 315 | |
Jason Wessel | 5dd11d5 | 2010-05-20 21:04:26 -0500 | [diff] [blame] | 316 | #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP |
| 317 | int kgdb_ll_trap(int cmd, const char *str, |
| 318 | struct pt_regs *regs, long err, int trap, int sig) |
| 319 | { |
| 320 | struct die_args args = { |
| 321 | .regs = regs, |
| 322 | .str = str, |
| 323 | .err = err, |
Ralf Baechle | 7034228 | 2013-01-22 12:59:30 +0100 | [diff] [blame^] | 324 | .trapnr = trap, |
Jason Wessel | 5dd11d5 | 2010-05-20 21:04:26 -0500 | [diff] [blame] | 325 | .signr = sig, |
| 326 | |
| 327 | }; |
| 328 | |
| 329 | if (!kgdb_io_module_registered) |
| 330 | return NOTIFY_DONE; |
| 331 | |
| 332 | return kgdb_mips_notify(NULL, cmd, &args); |
| 333 | } |
| 334 | #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */ |
| 335 | |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 336 | static struct notifier_block kgdb_notifier = { |
| 337 | .notifier_call = kgdb_mips_notify, |
| 338 | }; |
| 339 | |
| 340 | /* |
Jason Wessel | 7aeb48b | 2010-08-05 09:22:25 -0500 | [diff] [blame] | 341 | * Handle the 'c' command |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 342 | */ |
| 343 | int kgdb_arch_handle_exception(int vector, int signo, int err_code, |
| 344 | char *remcom_in_buffer, char *remcom_out_buffer, |
| 345 | struct pt_regs *regs) |
| 346 | { |
| 347 | char *ptr; |
| 348 | unsigned long address; |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 349 | |
| 350 | switch (remcom_in_buffer[0]) { |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 351 | case 'c': |
| 352 | /* handle the optional parameter */ |
| 353 | ptr = &remcom_in_buffer[1]; |
| 354 | if (kgdb_hex2long(&ptr, &address)) |
| 355 | regs->cp0_epc = address; |
| 356 | |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 357 | return 0; |
| 358 | } |
| 359 | |
| 360 | return -1; |
| 361 | } |
| 362 | |
| 363 | struct kgdb_arch arch_kgdb_ops; |
| 364 | |
| 365 | /* |
| 366 | * We use kgdb_early_setup so that functions we need to call now don't |
| 367 | * cause trouble when called again later. |
| 368 | */ |
| 369 | int kgdb_arch_init(void) |
| 370 | { |
| 371 | union mips_instruction insn = { |
| 372 | .r_format = { |
| 373 | .opcode = spec_op, |
Ralf Baechle | 7034228 | 2013-01-22 12:59:30 +0100 | [diff] [blame^] | 374 | .func = break_op, |
Jason Wessel | 8854700 | 2008-07-29 15:58:53 -0500 | [diff] [blame] | 375 | } |
| 376 | }; |
| 377 | memcpy(arch_kgdb_ops.gdb_bpt_instr, insn.byte, BREAK_INSTR_SIZE); |
| 378 | |
| 379 | register_die_notifier(&kgdb_notifier); |
| 380 | |
| 381 | return 0; |
| 382 | } |
| 383 | |
| 384 | /* |
| 385 | * kgdb_arch_exit - Perform any architecture specific uninitalization. |
| 386 | * |
| 387 | * This function will handle the uninitalization of any architecture |
| 388 | * specific callbacks, for dynamic registration and unregistration. |
| 389 | */ |
| 390 | void kgdb_arch_exit(void) |
| 391 | { |
| 392 | unregister_die_notifier(&kgdb_notifier); |
| 393 | } |