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