diff options
| -rw-r--r-- | arch/nios2/Kconfig | 1 | ||||
| -rw-r--r-- | arch/nios2/include/asm/kgdb.h | 93 | ||||
| -rw-r--r-- | arch/nios2/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/nios2/kernel/entry.S | 12 | ||||
| -rw-r--r-- | arch/nios2/kernel/kgdb.c | 171 |
5 files changed, 277 insertions, 1 deletions
diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index f77991ecfd8e..437555424bda 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig | |||
| @@ -8,6 +8,7 @@ config NIOS2 | |||
| 8 | select GENERIC_IRQ_PROBE | 8 | select GENERIC_IRQ_PROBE |
| 9 | select GENERIC_IRQ_SHOW | 9 | select GENERIC_IRQ_SHOW |
| 10 | select HAVE_ARCH_TRACEHOOK | 10 | select HAVE_ARCH_TRACEHOOK |
| 11 | select HAVE_ARCH_KGDB | ||
| 11 | select IRQ_DOMAIN | 12 | select IRQ_DOMAIN |
| 12 | select MODULES_USE_ELF_RELA | 13 | select MODULES_USE_ELF_RELA |
| 13 | select OF | 14 | select OF |
diff --git a/arch/nios2/include/asm/kgdb.h b/arch/nios2/include/asm/kgdb.h new file mode 100644 index 000000000000..8fd5e3b66c57 --- /dev/null +++ b/arch/nios2/include/asm/kgdb.h | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2015 Altera Corporation | ||
| 3 | * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> | ||
| 4 | * | ||
| 5 | * Based on the code posted by Kazuyasu on the Altera Forum at: | ||
| 6 | * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef _ASM_NIOS2_KGDB_H | ||
| 24 | #define _ASM_NIOS2_KGDB_H | ||
| 25 | |||
| 26 | #define CACHE_FLUSH_IS_SAFE 1 | ||
| 27 | #define BUFMAX 2048 | ||
| 28 | |||
| 29 | enum regnames { | ||
| 30 | GDB_R0 = 0, | ||
| 31 | GDB_AT, | ||
| 32 | GDB_R2, | ||
| 33 | GDB_R3, | ||
| 34 | GDB_R4, | ||
| 35 | GDB_R5, | ||
| 36 | GDB_R6, | ||
| 37 | GDB_R7, | ||
| 38 | GDB_R8, | ||
| 39 | GDB_R9, | ||
| 40 | GDB_R10, | ||
| 41 | GDB_R11, | ||
| 42 | GDB_R12, | ||
| 43 | GDB_R13, | ||
| 44 | GDB_R14, | ||
| 45 | GDB_R15, | ||
| 46 | GDB_R16, | ||
| 47 | GDB_R17, | ||
| 48 | GDB_R18, | ||
| 49 | GDB_R19, | ||
| 50 | GDB_R20, | ||
| 51 | GDB_R21, | ||
| 52 | GDB_R22, | ||
| 53 | GDB_R23, | ||
| 54 | GDB_ET, | ||
| 55 | GDB_BT, | ||
| 56 | GDB_GP, | ||
| 57 | GDB_SP, | ||
| 58 | GDB_FP, | ||
| 59 | GDB_EA, | ||
| 60 | GDB_BA, | ||
| 61 | GDB_RA, | ||
| 62 | GDB_PC, | ||
| 63 | GDB_STATUS, | ||
| 64 | GDB_ESTATUS, | ||
| 65 | GDB_BSTATUS, | ||
| 66 | GDB_IENABLE, | ||
| 67 | GDB_IPENDING, | ||
| 68 | GDB_CPUID, | ||
| 69 | GDB_CTL6, | ||
| 70 | GDB_EXCEPTION, | ||
| 71 | GDB_PTEADDR, | ||
| 72 | GDB_TLBACC, | ||
| 73 | GDB_TLBMISC, | ||
| 74 | GDB_ECCINJ, | ||
| 75 | GDB_BADADDR, | ||
| 76 | GDB_CONFIG, | ||
| 77 | GDB_MPUBASE, | ||
| 78 | GDB_MPUACC, | ||
| 79 | /* do not change the last entry or anything below! */ | ||
| 80 | GDB_NUMREGBYTES /* number of registers */ | ||
| 81 | }; | ||
| 82 | |||
| 83 | #define GDB_SIZEOF_REG sizeof(u32) | ||
| 84 | #define DBG_MAX_REG_NUM (49) | ||
| 85 | #define NUMREGBYTES (DBG_MAX_REG_NUM * sizeof(GDB_SIZEOF_REG)) | ||
| 86 | |||
| 87 | #define BREAK_INSTR_SIZE 4 | ||
| 88 | static inline void arch_kgdb_breakpoint(void) | ||
| 89 | { | ||
| 90 | __asm__ __volatile__("trap 30\n"); | ||
| 91 | } | ||
| 92 | |||
| 93 | #endif /* _ASM_NIOS2_KGDB_H */ | ||
diff --git a/arch/nios2/kernel/Makefile b/arch/nios2/kernel/Makefile index eaaa894c432f..1aae25703657 100644 --- a/arch/nios2/kernel/Makefile +++ b/arch/nios2/kernel/Makefile | |||
| @@ -21,5 +21,6 @@ obj-y += time.o | |||
| 21 | obj-y += traps.o | 21 | obj-y += traps.o |
| 22 | 22 | ||
| 23 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | 23 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o |
| 24 | obj-$(CONFIG_KGDB) += kgdb.o | ||
| 24 | obj-$(CONFIG_MODULES) += module.o | 25 | obj-$(CONFIG_MODULES) += module.o |
| 25 | obj-$(CONFIG_NIOS2_ALIGNMENT_TRAP) += misaligned.o | 26 | obj-$(CONFIG_NIOS2_ALIGNMENT_TRAP) += misaligned.o |
diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S index 0bdfd13ff98b..7729bd3f2e79 100644 --- a/arch/nios2/kernel/entry.S +++ b/arch/nios2/kernel/entry.S | |||
| @@ -121,7 +121,11 @@ trap_table: | |||
| 121 | .word instruction_trap /* 27 */ | 121 | .word instruction_trap /* 27 */ |
| 122 | .word instruction_trap /* 28 */ | 122 | .word instruction_trap /* 28 */ |
| 123 | .word instruction_trap /* 29 */ | 123 | .word instruction_trap /* 29 */ |
| 124 | .word instruction_trap /* 30 */ | 124 | #ifdef CONFIG_KGDB |
| 125 | .word handle_kgdb_breakpoint /* 30 KGDB breakpoint */ | ||
| 126 | #else | ||
| 127 | .word instruction_trap /* 30 */ | ||
| 128 | #endif | ||
| 125 | .word handle_breakpoint /* 31 */ | 129 | .word handle_breakpoint /* 31 */ |
| 126 | 130 | ||
| 127 | .text | 131 | .text |
| @@ -445,6 +449,12 @@ handle_diverror: | |||
| 445 | call handle_diverror_c | 449 | call handle_diverror_c |
| 446 | br ret_from_exception | 450 | br ret_from_exception |
| 447 | 451 | ||
| 452 | #ifdef CONFIG_KGDB | ||
| 453 | handle_kgdb_breakpoint: | ||
| 454 | call kgdb_breakpoint_c | ||
| 455 | br ret_from_exception | ||
| 456 | #endif | ||
| 457 | |||
| 448 | /* | 458 | /* |
| 449 | * Beware - when entering resume, prev (the current task) is | 459 | * Beware - when entering resume, prev (the current task) is |
| 450 | * in r4, next (the new task) is in r5, don't change these | 460 | * in r4, next (the new task) is in r5, don't change these |
diff --git a/arch/nios2/kernel/kgdb.c b/arch/nios2/kernel/kgdb.c new file mode 100644 index 000000000000..117859122d1c --- /dev/null +++ b/arch/nios2/kernel/kgdb.c | |||
| @@ -0,0 +1,171 @@ | |||
| 1 | /* | ||
| 2 | * Nios2 KGDB support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015 Altera Corporation | ||
| 5 | * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> | ||
| 6 | * | ||
| 7 | * Based on the code posted by Kazuyasu on the Altera Forum at: | ||
| 8 | * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License | ||
| 21 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | #include <linux/ptrace.h> | ||
| 25 | #include <linux/kgdb.h> | ||
| 26 | #include <linux/kdebug.h> | ||
| 27 | #include <linux/io.h> | ||
| 28 | |||
| 29 | static int wait_for_remote_debugger; | ||
| 30 | |||
| 31 | struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = | ||
| 32 | { | ||
| 33 | { "zero", GDB_SIZEOF_REG, -1 }, | ||
| 34 | { "at", GDB_SIZEOF_REG, offsetof(struct pt_regs, r1) }, | ||
| 35 | { "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, r2) }, | ||
| 36 | { "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, r3) }, | ||
| 37 | { "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, r4) }, | ||
| 38 | { "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, r5) }, | ||
| 39 | { "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, r6) }, | ||
| 40 | { "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, r7) }, | ||
| 41 | { "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, r8) }, | ||
| 42 | { "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, r9) }, | ||
| 43 | { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, r10) }, | ||
| 44 | { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, r11) }, | ||
| 45 | { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, r12) }, | ||
| 46 | { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, r13) }, | ||
| 47 | { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, r14) }, | ||
| 48 | { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, r15) }, | ||
| 49 | { "r16", GDB_SIZEOF_REG, -1 }, | ||
| 50 | { "r17", GDB_SIZEOF_REG, -1 }, | ||
| 51 | { "r18", GDB_SIZEOF_REG, -1 }, | ||
| 52 | { "r19", GDB_SIZEOF_REG, -1 }, | ||
| 53 | { "r20", GDB_SIZEOF_REG, -1 }, | ||
| 54 | { "r21", GDB_SIZEOF_REG, -1 }, | ||
| 55 | { "r22", GDB_SIZEOF_REG, -1 }, | ||
| 56 | { "r23", GDB_SIZEOF_REG, -1 }, | ||
| 57 | { "et", GDB_SIZEOF_REG, -1 }, | ||
| 58 | { "bt", GDB_SIZEOF_REG, -1 }, | ||
| 59 | { "gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, gp) }, | ||
| 60 | { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, sp) }, | ||
| 61 | { "fp", GDB_SIZEOF_REG, offsetof(struct pt_regs, fp) }, | ||
| 62 | { "ea", GDB_SIZEOF_REG, -1 }, | ||
| 63 | { "ba", GDB_SIZEOF_REG, -1 }, | ||
| 64 | { "ra", GDB_SIZEOF_REG, offsetof(struct pt_regs, ra) }, | ||
| 65 | { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, ea) }, | ||
| 66 | { "status", GDB_SIZEOF_REG, -1 }, | ||
| 67 | { "estatus", GDB_SIZEOF_REG, offsetof(struct pt_regs, estatus) }, | ||
| 68 | { "bstatus", GDB_SIZEOF_REG, -1 }, | ||
| 69 | { "ienable", GDB_SIZEOF_REG, -1 }, | ||
| 70 | { "ipending", GDB_SIZEOF_REG, -1}, | ||
| 71 | { "cpuid", GDB_SIZEOF_REG, -1 }, | ||
| 72 | { "ctl6", GDB_SIZEOF_REG, -1 }, | ||
| 73 | { "exception", GDB_SIZEOF_REG, -1 }, | ||
| 74 | { "pteaddr", GDB_SIZEOF_REG, -1 }, | ||
| 75 | { "tlbacc", GDB_SIZEOF_REG, -1 }, | ||
| 76 | { "tlbmisc", GDB_SIZEOF_REG, -1 }, | ||
| 77 | { "eccinj", GDB_SIZEOF_REG, -1 }, | ||
| 78 | { "badaddr", GDB_SIZEOF_REG, -1 }, | ||
| 79 | { "config", GDB_SIZEOF_REG, -1 }, | ||
| 80 | { "mpubase", GDB_SIZEOF_REG, -1 }, | ||
| 81 | { "mpuacc", GDB_SIZEOF_REG, -1 }, | ||
| 82 | }; | ||
| 83 | |||
| 84 | char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) | ||
| 85 | { | ||
| 86 | if (regno >= DBG_MAX_REG_NUM || regno < 0) | ||
| 87 | return NULL; | ||
| 88 | |||
| 89 | if (dbg_reg_def[regno].offset != -1) | ||
| 90 | memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, | ||
| 91 | dbg_reg_def[regno].size); | ||
| 92 | else | ||
| 93 | memset(mem, 0, dbg_reg_def[regno].size); | ||
| 94 | |||
| 95 | return dbg_reg_def[regno].name; | ||
| 96 | } | ||
| 97 | |||
| 98 | int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) | ||
| 99 | { | ||
| 100 | if (regno >= DBG_MAX_REG_NUM || regno < 0) | ||
| 101 | return -EINVAL; | ||
| 102 | |||
| 103 | if (dbg_reg_def[regno].offset != -1) | ||
| 104 | memcpy((void *)regs + dbg_reg_def[regno].offset, mem, | ||
| 105 | dbg_reg_def[regno].size); | ||
| 106 | |||
| 107 | return 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) | ||
| 111 | { | ||
| 112 | memset((char *)gdb_regs, 0, NUMREGBYTES); | ||
| 113 | gdb_regs[GDB_SP] = p->thread.kregs->sp; | ||
| 114 | gdb_regs[GDB_PC] = p->thread.kregs->ea; | ||
| 115 | } | ||
| 116 | |||
| 117 | void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) | ||
| 118 | { | ||
| 119 | regs->ea = pc; | ||
| 120 | } | ||
| 121 | |||
| 122 | int kgdb_arch_handle_exception(int vector, int signo, int err_code, | ||
| 123 | char *remcom_in_buffer, char *remcom_out_buffer, | ||
| 124 | struct pt_regs *regs) | ||
| 125 | { | ||
| 126 | char *ptr; | ||
| 127 | unsigned long addr; | ||
| 128 | |||
| 129 | switch (remcom_in_buffer[0]) { | ||
| 130 | case 's': | ||
| 131 | case 'c': | ||
| 132 | /* handle the optional parameters */ | ||
| 133 | ptr = &remcom_in_buffer[1]; | ||
| 134 | if (kgdb_hex2long(&ptr, &addr)) | ||
| 135 | regs->ea = addr; | ||
| 136 | |||
| 137 | return 0; | ||
| 138 | } | ||
| 139 | |||
| 140 | return -1; /* this means that we do not want to exit from the handler */ | ||
| 141 | } | ||
| 142 | |||
| 143 | asmlinkage void kgdb_breakpoint_c(struct pt_regs *regs) | ||
| 144 | { | ||
| 145 | /* | ||
| 146 | * The breakpoint entry code has moved the PC on by 4 bytes, so we must | ||
| 147 | * move it back. This could be done on the host but we do it here | ||
| 148 | */ | ||
| 149 | if (!wait_for_remote_debugger) | ||
| 150 | regs->ea -= 4; | ||
| 151 | else /* pass the first trap 30 code */ | ||
| 152 | wait_for_remote_debugger = 0; | ||
| 153 | |||
| 154 | kgdb_handle_exception(30, SIGTRAP, 0, regs); | ||
| 155 | } | ||
| 156 | |||
| 157 | int kgdb_arch_init(void) | ||
| 158 | { | ||
| 159 | wait_for_remote_debugger = 1; | ||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | void kgdb_arch_exit(void) | ||
| 164 | { | ||
| 165 | /* Nothing to do */ | ||
| 166 | } | ||
| 167 | |||
| 168 | struct kgdb_arch arch_kgdb_ops = { | ||
| 169 | /* Breakpoint instruction: trap 30 */ | ||
| 170 | .gdb_bpt_instr = { 0xba, 0x6f, 0x3b, 0x00 }, | ||
| 171 | }; | ||
