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