Skip to content

Commit fe89bd2

Browse files
VincentZWCpalmer-dabbelt
authored andcommitted
riscv: Add KGDB support
The skeleton of RISC-V KGDB port. Signed-off-by: Vincent Chen <[email protected]> Reviewed-by: Palmer Dabbelt <[email protected]> Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent f83b04d commit fe89bd2

File tree

6 files changed

+325
-0
lines changed

6 files changed

+325
-0
lines changed

arch/riscv/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ config RISCV
6868
select ARCH_HAS_GCOV_PROFILE_ALL
6969
select HAVE_COPY_THREAD_TLS
7070
select HAVE_ARCH_KASAN if MMU && 64BIT
71+
select HAVE_ARCH_KGDB
7172

7273
config ARCH_MMAP_RND_BITS_MIN
7374
default 18 if 64BIT

arch/riscv/include/asm/kdebug.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
3+
#ifndef _ASM_ARC_KDEBUG_H
4+
#define _ASM_ARC_KDEBUG_H
5+
6+
enum die_val {
7+
DIE_UNUSED,
8+
DIE_TRAP,
9+
DIE_OOPS
10+
};
11+
12+
#endif

arch/riscv/include/asm/kgdb.h

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
3+
#ifndef __ASM_KGDB_H_
4+
#define __ASM_KGDB_H_
5+
6+
#ifdef __KERNEL__
7+
8+
#define GDB_SIZEOF_REG sizeof(unsigned long)
9+
10+
#define DBG_MAX_REG_NUM (33)
11+
#define NUMREGBYTES ((DBG_MAX_REG_NUM) * GDB_SIZEOF_REG)
12+
#define CACHE_FLUSH_IS_SAFE 1
13+
#define BUFMAX 2048
14+
#ifdef CONFIG_RISCV_ISA_C
15+
#define BREAK_INSTR_SIZE 2
16+
#else
17+
#define BREAK_INSTR_SIZE 4
18+
#endif
19+
20+
#ifndef __ASSEMBLY__
21+
22+
extern int kgdb_has_hit_break(unsigned long addr);
23+
extern unsigned long kgdb_compiled_break;
24+
25+
static inline void arch_kgdb_breakpoint(void)
26+
{
27+
asm(".global kgdb_compiled_break\n"
28+
".option norvc\n"
29+
"kgdb_compiled_break: ebreak\n"
30+
".option rvc\n");
31+
}
32+
33+
#endif /* !__ASSEMBLY__ */
34+
35+
#define DBG_REG_ZERO "zero"
36+
#define DBG_REG_RA "ra"
37+
#define DBG_REG_SP "sp"
38+
#define DBG_REG_GP "gp"
39+
#define DBG_REG_TP "tp"
40+
#define DBG_REG_T0 "t0"
41+
#define DBG_REG_T1 "t1"
42+
#define DBG_REG_T2 "t2"
43+
#define DBG_REG_FP "fp"
44+
#define DBG_REG_S1 "s1"
45+
#define DBG_REG_A0 "a0"
46+
#define DBG_REG_A1 "a1"
47+
#define DBG_REG_A2 "a2"
48+
#define DBG_REG_A3 "a3"
49+
#define DBG_REG_A4 "a4"
50+
#define DBG_REG_A5 "a5"
51+
#define DBG_REG_A6 "a6"
52+
#define DBG_REG_A7 "a7"
53+
#define DBG_REG_S2 "s2"
54+
#define DBG_REG_S3 "s3"
55+
#define DBG_REG_S4 "s4"
56+
#define DBG_REG_S5 "s5"
57+
#define DBG_REG_S6 "s6"
58+
#define DBG_REG_S7 "s7"
59+
#define DBG_REG_S8 "s8"
60+
#define DBG_REG_S9 "s9"
61+
#define DBG_REG_S10 "s10"
62+
#define DBG_REG_S11 "s11"
63+
#define DBG_REG_T3 "t3"
64+
#define DBG_REG_T4 "t4"
65+
#define DBG_REG_T5 "t5"
66+
#define DBG_REG_T6 "t6"
67+
#define DBG_REG_EPC "pc"
68+
69+
#define DBG_REG_ZERO_OFF 0
70+
#define DBG_REG_RA_OFF 1
71+
#define DBG_REG_SP_OFF 2
72+
#define DBG_REG_GP_OFF 3
73+
#define DBG_REG_TP_OFF 4
74+
#define DBG_REG_T0_OFF 5
75+
#define DBG_REG_T1_OFF 6
76+
#define DBG_REG_T2_OFF 7
77+
#define DBG_REG_FP_OFF 8
78+
#define DBG_REG_S1_OFF 9
79+
#define DBG_REG_A0_OFF 10
80+
#define DBG_REG_A1_OFF 11
81+
#define DBG_REG_A2_OFF 12
82+
#define DBG_REG_A3_OFF 13
83+
#define DBG_REG_A4_OFF 14
84+
#define DBG_REG_A5_OFF 15
85+
#define DBG_REG_A6_OFF 16
86+
#define DBG_REG_A7_OFF 17
87+
#define DBG_REG_S2_OFF 18
88+
#define DBG_REG_S3_OFF 19
89+
#define DBG_REG_S4_OFF 20
90+
#define DBG_REG_S5_OFF 21
91+
#define DBG_REG_S6_OFF 22
92+
#define DBG_REG_S7_OFF 23
93+
#define DBG_REG_S8_OFF 24
94+
#define DBG_REG_S9_OFF 25
95+
#define DBG_REG_S10_OFF 26
96+
#define DBG_REG_S11_OFF 27
97+
#define DBG_REG_T3_OFF 28
98+
#define DBG_REG_T4_OFF 29
99+
#define DBG_REG_T5_OFF 30
100+
#define DBG_REG_T6_OFF 31
101+
#define DBG_REG_EPC_OFF 32
102+
#define DBG_REG_STATUS_OFF 33
103+
#define DBG_REG_BADADDR_OFF 34
104+
#define DBG_REG_CAUSE_OFF 35
105+
#endif
106+
#endif

arch/riscv/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@ ifeq ($(CONFIG_RISCV_SBI), y)
5151
obj-$(CONFIG_SMP) += cpu_ops_sbi.o
5252
endif
5353
obj-$(CONFIG_HOTPLUG_CPU) += cpu-hotplug.o
54+
obj-$(CONFIG_KGDB) += kgdb.o
5455

5556
clean:

arch/riscv/kernel/kgdb.c

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// 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+
14+
enum {
15+
NOT_KGDB_BREAK = 0,
16+
KGDB_SW_BREAK,
17+
KGDB_COMPILED_BREAK,
18+
};
19+
20+
struct 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+
56+
char *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+
69+
int 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+
80+
void
81+
sleeping_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+
101+
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
102+
{
103+
regs->epc = pc;
104+
}
105+
106+
static 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+
117+
int 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+
137+
int 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+
146+
static 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+
173+
static struct notifier_block kgdb_notifier = {
174+
.notifier_call = kgdb_riscv_notify,
175+
};
176+
177+
int kgdb_arch_init(void)
178+
{
179+
register_die_notifier(&kgdb_notifier);
180+
181+
return 0;
182+
}
183+
184+
void kgdb_arch_exit(void)
185+
{
186+
unregister_die_notifier(&kgdb_notifier);
187+
}
188+
189+
/*
190+
* Global data
191+
*/
192+
#ifdef CONFIG_RISCV_ISA_C
193+
const struct kgdb_arch arch_kgdb_ops = {
194+
.gdb_bpt_instr = {0x02, 0x90}, /* c.ebreak */
195+
};
196+
#else
197+
const struct kgdb_arch arch_kgdb_ops = {
198+
.gdb_bpt_instr = {0x73, 0x00, 0x10, 0x00}, /* ebreak */
199+
};
200+
#endif

arch/riscv/kernel/traps.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ asmlinkage __visible void do_trap_break(struct pt_regs *regs)
147147
{
148148
if (user_mode(regs))
149149
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->epc);
150+
#ifdef CONFIG_KGDB
151+
else if (notify_die(DIE_TRAP, "EBREAK", regs, 0, regs->cause, SIGTRAP)
152+
== NOTIFY_STOP)
153+
return;
154+
#endif
150155
else if (report_bug(regs->epc, regs) == BUG_TRAP_TYPE_WARN)
151156
regs->epc += get_break_insn_length(regs->epc);
152157
else

0 commit comments

Comments
 (0)