blob: 6bdc0908a5b828c90934f87a35c27f063ea113f8 [file] [log] [blame]
Vincent Chenfe89bd22020-04-16 10:38:05 +08001// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2020 SiFive
4 */
5
6#include <linux/ptrace.h>
7#include <linux/kdebug.h>
8#include <linux/bug.h>
9#include <linux/kgdb.h>
10#include <linux/irqflags.h>
11#include <linux/string.h>
12#include <asm/cacheflush.h>
13
14enum {
15 NOT_KGDB_BREAK = 0,
16 KGDB_SW_BREAK,
17 KGDB_COMPILED_BREAK,
18};
19
20struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
21 {DBG_REG_ZERO, GDB_SIZEOF_REG, -1},
22 {DBG_REG_RA, GDB_SIZEOF_REG, offsetof(struct pt_regs, ra)},
23 {DBG_REG_SP, GDB_SIZEOF_REG, offsetof(struct pt_regs, sp)},
24 {DBG_REG_GP, GDB_SIZEOF_REG, offsetof(struct pt_regs, gp)},
25 {DBG_REG_TP, GDB_SIZEOF_REG, offsetof(struct pt_regs, tp)},
26 {DBG_REG_T0, GDB_SIZEOF_REG, offsetof(struct pt_regs, t0)},
27 {DBG_REG_T1, GDB_SIZEOF_REG, offsetof(struct pt_regs, t1)},
28 {DBG_REG_T2, GDB_SIZEOF_REG, offsetof(struct pt_regs, t2)},
29 {DBG_REG_FP, GDB_SIZEOF_REG, offsetof(struct pt_regs, s0)},
30 {DBG_REG_S1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)},
31 {DBG_REG_A0, GDB_SIZEOF_REG, offsetof(struct pt_regs, a0)},
32 {DBG_REG_A1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)},
33 {DBG_REG_A2, GDB_SIZEOF_REG, offsetof(struct pt_regs, a2)},
34 {DBG_REG_A3, GDB_SIZEOF_REG, offsetof(struct pt_regs, a3)},
35 {DBG_REG_A4, GDB_SIZEOF_REG, offsetof(struct pt_regs, a4)},
36 {DBG_REG_A5, GDB_SIZEOF_REG, offsetof(struct pt_regs, a5)},
37 {DBG_REG_A6, GDB_SIZEOF_REG, offsetof(struct pt_regs, a6)},
38 {DBG_REG_A7, GDB_SIZEOF_REG, offsetof(struct pt_regs, a7)},
39 {DBG_REG_S2, GDB_SIZEOF_REG, offsetof(struct pt_regs, s2)},
40 {DBG_REG_S3, GDB_SIZEOF_REG, offsetof(struct pt_regs, s3)},
41 {DBG_REG_S4, GDB_SIZEOF_REG, offsetof(struct pt_regs, s4)},
42 {DBG_REG_S5, GDB_SIZEOF_REG, offsetof(struct pt_regs, s5)},
43 {DBG_REG_S6, GDB_SIZEOF_REG, offsetof(struct pt_regs, s6)},
44 {DBG_REG_S7, GDB_SIZEOF_REG, offsetof(struct pt_regs, s7)},
45 {DBG_REG_S8, GDB_SIZEOF_REG, offsetof(struct pt_regs, s8)},
46 {DBG_REG_S9, GDB_SIZEOF_REG, offsetof(struct pt_regs, s9)},
47 {DBG_REG_S10, GDB_SIZEOF_REG, offsetof(struct pt_regs, s10)},
48 {DBG_REG_S11, GDB_SIZEOF_REG, offsetof(struct pt_regs, s11)},
49 {DBG_REG_T3, GDB_SIZEOF_REG, offsetof(struct pt_regs, t3)},
50 {DBG_REG_T4, GDB_SIZEOF_REG, offsetof(struct pt_regs, t4)},
51 {DBG_REG_T5, GDB_SIZEOF_REG, offsetof(struct pt_regs, t5)},
52 {DBG_REG_T6, GDB_SIZEOF_REG, offsetof(struct pt_regs, t6)},
53 {DBG_REG_EPC, GDB_SIZEOF_REG, offsetof(struct pt_regs, epc)},
54};
55
56char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
57{
58 if (regno >= DBG_MAX_REG_NUM || regno < 0)
59 return NULL;
60
61 if (dbg_reg_def[regno].offset != -1)
62 memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
63 dbg_reg_def[regno].size);
64 else
65 memset(mem, 0, dbg_reg_def[regno].size);
66 return dbg_reg_def[regno].name;
67}
68
69int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
70{
71 if (regno >= DBG_MAX_REG_NUM || regno < 0)
72 return -EINVAL;
73
74 if (dbg_reg_def[regno].offset != -1)
75 memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
76 dbg_reg_def[regno].size);
77 return 0;
78}
79
80void
81sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
82{
83 /* Initialize to zero */
84 memset((char *)gdb_regs, 0, NUMREGBYTES);
85
86 gdb_regs[DBG_REG_SP_OFF] = task->thread.sp;
87 gdb_regs[DBG_REG_FP_OFF] = task->thread.s[0];
88 gdb_regs[DBG_REG_S1_OFF] = task->thread.s[1];
89 gdb_regs[DBG_REG_S2_OFF] = task->thread.s[2];
90 gdb_regs[DBG_REG_S3_OFF] = task->thread.s[3];
91 gdb_regs[DBG_REG_S4_OFF] = task->thread.s[4];
92 gdb_regs[DBG_REG_S5_OFF] = task->thread.s[5];
93 gdb_regs[DBG_REG_S6_OFF] = task->thread.s[6];
94 gdb_regs[DBG_REG_S7_OFF] = task->thread.s[7];
95 gdb_regs[DBG_REG_S8_OFF] = task->thread.s[8];
96 gdb_regs[DBG_REG_S9_OFF] = task->thread.s[10];
97 gdb_regs[DBG_REG_S10_OFF] = task->thread.s[11];
98 gdb_regs[DBG_REG_EPC_OFF] = task->thread.ra;
99}
100
101void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
102{
103 regs->epc = pc;
104}
105
106static inline void kgdb_arch_update_addr(struct pt_regs *regs,
107 char *remcom_in_buffer)
108{
109 unsigned long addr;
110 char *ptr;
111
112 ptr = &remcom_in_buffer[1];
113 if (kgdb_hex2long(&ptr, &addr))
114 regs->epc = addr;
115}
116
117int kgdb_arch_handle_exception(int vector, int signo, int err_code,
118 char *remcom_in_buffer, char *remcom_out_buffer,
119 struct pt_regs *regs)
120{
121 int err = 0;
122
123 switch (remcom_in_buffer[0]) {
124 case 'c':
125 case 'D':
126 case 'k':
127 if (remcom_in_buffer[0] == 'c')
128 kgdb_arch_update_addr(regs, remcom_in_buffer);
129 break;
130 default:
131 err = -1;
132 }
133
134 return err;
135}
136
137int kgdb_riscv_kgdbbreak(unsigned long addr)
138{
139 if (atomic_read(&kgdb_setting_breakpoint))
140 if (addr == (unsigned long)&kgdb_compiled_break)
141 return KGDB_COMPILED_BREAK;
142
143 return kgdb_has_hit_break(addr);
144}
145
146static int kgdb_riscv_notify(struct notifier_block *self, unsigned long cmd,
147 void *ptr)
148{
149 struct die_args *args = (struct die_args *)ptr;
150 struct pt_regs *regs = args->regs;
151 unsigned long flags;
152 int type;
153
154 if (user_mode(regs))
155 return NOTIFY_DONE;
156
157 type = kgdb_riscv_kgdbbreak(regs->epc);
158 if (type == NOT_KGDB_BREAK && cmd == DIE_TRAP)
159 return NOTIFY_DONE;
160
161 local_irq_save(flags);
162 if (kgdb_handle_exception(1, args->signr, cmd, regs))
163 return NOTIFY_DONE;
164
165 if (type == KGDB_COMPILED_BREAK)
166 regs->epc += 4;
167
168 local_irq_restore(flags);
169
170 return NOTIFY_STOP;
171}
172
173static struct notifier_block kgdb_notifier = {
174 .notifier_call = kgdb_riscv_notify,
175};
176
177int kgdb_arch_init(void)
178{
179 register_die_notifier(&kgdb_notifier);
180
181 return 0;
182}
183
184void kgdb_arch_exit(void)
185{
186 unregister_die_notifier(&kgdb_notifier);
187}
188
189/*
190 * Global data
191 */
192#ifdef CONFIG_RISCV_ISA_C
193const struct kgdb_arch arch_kgdb_ops = {
194 .gdb_bpt_instr = {0x02, 0x90}, /* c.ebreak */
195};
196#else
197const struct kgdb_arch arch_kgdb_ops = {
198 .gdb_bpt_instr = {0x73, 0x00, 0x10, 0x00}, /* ebreak */
199};
200#endif