1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Nios2 KGDB support |
4 | * |
5 | * Copyright (C) 2015 Altera Corporation |
6 | * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> |
7 | * |
8 | * Based on the code posted by Kazuyasu on the Altera Forum at: |
9 | * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 |
10 | */ |
11 | #include <linux/ptrace.h> |
12 | #include <linux/kgdb.h> |
13 | #include <linux/kdebug.h> |
14 | #include <linux/io.h> |
15 | |
16 | static int wait_for_remote_debugger; |
17 | |
18 | struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = |
19 | { |
20 | { "zero" , GDB_SIZEOF_REG, -1 }, |
21 | { "at" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r1) }, |
22 | { "r2" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r2) }, |
23 | { "r3" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r3) }, |
24 | { "r4" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r4) }, |
25 | { "r5" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r5) }, |
26 | { "r6" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r6) }, |
27 | { "r7" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r7) }, |
28 | { "r8" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r8) }, |
29 | { "r9" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r9) }, |
30 | { "r10" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r10) }, |
31 | { "r11" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r11) }, |
32 | { "r12" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r12) }, |
33 | { "r13" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r13) }, |
34 | { "r14" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r14) }, |
35 | { "r15" , GDB_SIZEOF_REG, offsetof(struct pt_regs, r15) }, |
36 | { "r16" , GDB_SIZEOF_REG, -1 }, |
37 | { "r17" , GDB_SIZEOF_REG, -1 }, |
38 | { "r18" , GDB_SIZEOF_REG, -1 }, |
39 | { "r19" , GDB_SIZEOF_REG, -1 }, |
40 | { "r20" , GDB_SIZEOF_REG, -1 }, |
41 | { "r21" , GDB_SIZEOF_REG, -1 }, |
42 | { "r22" , GDB_SIZEOF_REG, -1 }, |
43 | { "r23" , GDB_SIZEOF_REG, -1 }, |
44 | { "et" , GDB_SIZEOF_REG, -1 }, |
45 | { "bt" , GDB_SIZEOF_REG, -1 }, |
46 | { "gp" , GDB_SIZEOF_REG, offsetof(struct pt_regs, gp) }, |
47 | { "sp" , GDB_SIZEOF_REG, offsetof(struct pt_regs, sp) }, |
48 | { "fp" , GDB_SIZEOF_REG, offsetof(struct pt_regs, fp) }, |
49 | { "ea" , GDB_SIZEOF_REG, -1 }, |
50 | { "ba" , GDB_SIZEOF_REG, -1 }, |
51 | { "ra" , GDB_SIZEOF_REG, offsetof(struct pt_regs, ra) }, |
52 | { "pc" , GDB_SIZEOF_REG, offsetof(struct pt_regs, ea) }, |
53 | { "status" , GDB_SIZEOF_REG, -1 }, |
54 | { "estatus" , GDB_SIZEOF_REG, offsetof(struct pt_regs, estatus) }, |
55 | { "bstatus" , GDB_SIZEOF_REG, -1 }, |
56 | { "ienable" , GDB_SIZEOF_REG, -1 }, |
57 | { "ipending" , GDB_SIZEOF_REG, -1}, |
58 | { "cpuid" , GDB_SIZEOF_REG, -1 }, |
59 | { "ctl6" , GDB_SIZEOF_REG, -1 }, |
60 | { "exception" , GDB_SIZEOF_REG, -1 }, |
61 | { "pteaddr" , GDB_SIZEOF_REG, -1 }, |
62 | { "tlbacc" , GDB_SIZEOF_REG, -1 }, |
63 | { "tlbmisc" , GDB_SIZEOF_REG, -1 }, |
64 | { "eccinj" , GDB_SIZEOF_REG, -1 }, |
65 | { "badaddr" , GDB_SIZEOF_REG, -1 }, |
66 | { "config" , GDB_SIZEOF_REG, -1 }, |
67 | { "mpubase" , GDB_SIZEOF_REG, -1 }, |
68 | { "mpuacc" , GDB_SIZEOF_REG, -1 }, |
69 | }; |
70 | |
71 | char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) |
72 | { |
73 | if (regno >= DBG_MAX_REG_NUM || regno < 0) |
74 | return NULL; |
75 | |
76 | if (dbg_reg_def[regno].offset != -1) |
77 | memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, |
78 | dbg_reg_def[regno].size); |
79 | else |
80 | memset(mem, 0, dbg_reg_def[regno].size); |
81 | |
82 | return dbg_reg_def[regno].name; |
83 | } |
84 | |
85 | int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) |
86 | { |
87 | if (regno >= DBG_MAX_REG_NUM || regno < 0) |
88 | return -EINVAL; |
89 | |
90 | if (dbg_reg_def[regno].offset != -1) |
91 | memcpy((void *)regs + dbg_reg_def[regno].offset, mem, |
92 | dbg_reg_def[regno].size); |
93 | |
94 | return 0; |
95 | } |
96 | |
97 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) |
98 | { |
99 | memset((char *)gdb_regs, 0, NUMREGBYTES); |
100 | gdb_regs[GDB_SP] = p->thread.kregs->sp; |
101 | gdb_regs[GDB_PC] = p->thread.kregs->ea; |
102 | } |
103 | |
104 | void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) |
105 | { |
106 | regs->ea = pc; |
107 | } |
108 | |
109 | int kgdb_arch_handle_exception(int vector, int signo, int err_code, |
110 | char *remcom_in_buffer, char *remcom_out_buffer, |
111 | struct pt_regs *regs) |
112 | { |
113 | char *ptr; |
114 | unsigned long addr; |
115 | |
116 | switch (remcom_in_buffer[0]) { |
117 | case 's': |
118 | case 'c': |
119 | /* handle the optional parameters */ |
120 | ptr = &remcom_in_buffer[1]; |
121 | if (kgdb_hex2long(ptr: &ptr, long_val: &addr)) |
122 | regs->ea = addr; |
123 | |
124 | return 0; |
125 | } |
126 | |
127 | return -1; /* this means that we do not want to exit from the handler */ |
128 | } |
129 | |
130 | asmlinkage void kgdb_breakpoint_c(struct pt_regs *regs) |
131 | { |
132 | /* |
133 | * The breakpoint entry code has moved the PC on by 4 bytes, so we must |
134 | * move it back. This could be done on the host but we do it here |
135 | */ |
136 | if (!wait_for_remote_debugger) |
137 | regs->ea -= 4; |
138 | else /* pass the first trap 30 code */ |
139 | wait_for_remote_debugger = 0; |
140 | |
141 | kgdb_handle_exception(ex_vector: 30, SIGTRAP, err_code: 0, regs); |
142 | } |
143 | |
144 | int kgdb_arch_init(void) |
145 | { |
146 | wait_for_remote_debugger = 1; |
147 | return 0; |
148 | } |
149 | |
150 | void kgdb_arch_exit(void) |
151 | { |
152 | /* Nothing to do */ |
153 | } |
154 | |
155 | const struct kgdb_arch arch_kgdb_ops = { |
156 | /* Breakpoint instruction: trap 30 */ |
157 | .gdb_bpt_instr = { 0xba, 0x6f, 0x3b, 0x00 }, |
158 | }; |
159 | |