Skip to content

Commit f46121b

Browse files
mischajonkervineetgarc
authored andcommitted
ARC: kgdb support
Signed-off-by: Mischa Jonker <mjonker@synopsys.com> Signed-off-by: Vineet Gupta <vgupta@synopsys.com> Cc: Jason Wessel <jason.wessel@windriver.com> Acked-by: Jason Wessel <jason.wessel@windriver.com>
1 parent 2e651ea commit f46121b

5 files changed

Lines changed: 275 additions & 1 deletion

File tree

arch/arc/Kconfig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ config ARC
2222
select GENERIC_PENDING_IRQ if SMP
2323
select GENERIC_SIGALTSTACK
2424
select GENERIC_SMP_IDLE_THREAD
25+
select HAVE_ARCH_KGDB
2526
select HAVE_ARCH_TRACEHOOK
2627
select HAVE_GENERIC_HARDIRQS
2728
select HAVE_KPROBES
@@ -378,7 +379,7 @@ config ARC_DW2_UNWIND
378379

379380
config ARC_DBG_TLB_PARANOIA
380381
bool "Paranoia Checks in Low Level TLB Handlers"
381-
depends on ARC_DBG && !SMP
382+
depends on ARC_DBG
382383
default n
383384

384385
config ARC_DBG_TLB_MISS_COUNT

arch/arc/include/asm/kgdb.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* kgdb support for ARC
3+
*
4+
* Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com)
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License version 2 as
8+
* published by the Free Software Foundation.
9+
*/
10+
11+
#ifndef __ARC_KGDB_H__
12+
#define __ARC_KGDB_H__
13+
14+
#ifdef CONFIG_KGDB
15+
16+
#include <asm/user.h>
17+
18+
/* to ensure compatibility with Linux 2.6.35, we don't implement the get/set
19+
* register API yet */
20+
#undef DBG_MAX_REG_NUM
21+
22+
#define GDB_MAX_REGS 39
23+
24+
#define BREAK_INSTR_SIZE 2
25+
#define CACHE_FLUSH_IS_SAFE 1
26+
#define NUMREGBYTES (GDB_MAX_REGS * 4)
27+
#define BUFMAX 2048
28+
29+
static inline void arch_kgdb_breakpoint(void)
30+
{
31+
__asm__ __volatile__ ("trap_s 0x4\n");
32+
}
33+
34+
extern void kgdb_trap(struct pt_regs *regs, int param);
35+
36+
enum arc700_linux_regnums {
37+
_R0 = 0,
38+
_R1, _R2, _R3, _R4, _R5, _R6, _R7, _R8, _R9, _R10, _R11, _R12, _R13,
39+
_R14, _R15, _R16, _R17, _R18, _R19, _R20, _R21, _R22, _R23, _R24,
40+
_R25, _R26,
41+
_BTA = 27,
42+
_LP_START = 28,
43+
_LP_END = 29,
44+
_LP_COUNT = 30,
45+
_STATUS32 = 31,
46+
_BLINK = 32,
47+
_FP = 33,
48+
__SP = 34,
49+
_EFA = 35,
50+
_RET = 36,
51+
_ORIG_R8 = 37,
52+
_STOP_PC = 38
53+
};
54+
55+
#else
56+
static inline void kgdb_trap(struct pt_regs *regs, int param)
57+
{
58+
}
59+
#endif
60+
61+
#endif /* __ARC_KGDB_H__ */

arch/arc/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ obj-$(CONFIG_SMP) += smp.o
1717
obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o
1818
obj-$(CONFIG_KPROBES) += kprobes.o
1919
obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o
20+
obj-$(CONFIG_KGDB) += kgdb.o
2021

2122
obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
2223
CFLAGS_fpu.o += -mdpfp

arch/arc/kernel/kgdb.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* kgdb support for ARC
3+
*
4+
* Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com)
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License version 2 as
8+
* published by the Free Software Foundation.
9+
*/
10+
11+
#include <linux/kgdb.h>
12+
#include <asm/disasm.h>
13+
#include <asm/cacheflush.h>
14+
15+
static void to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
16+
struct callee_regs *cregs)
17+
{
18+
int regno;
19+
20+
for (regno = 0; regno <= 26; regno++)
21+
gdb_regs[_R0 + regno] = get_reg(regno, kernel_regs, cregs);
22+
23+
for (regno = 27; regno < GDB_MAX_REGS; regno++)
24+
gdb_regs[regno] = 0;
25+
26+
gdb_regs[_FP] = kernel_regs->fp;
27+
gdb_regs[__SP] = kernel_regs->sp;
28+
gdb_regs[_BLINK] = kernel_regs->blink;
29+
gdb_regs[_RET] = kernel_regs->ret;
30+
gdb_regs[_STATUS32] = kernel_regs->status32;
31+
gdb_regs[_LP_COUNT] = kernel_regs->lp_count;
32+
gdb_regs[_LP_END] = kernel_regs->lp_end;
33+
gdb_regs[_LP_START] = kernel_regs->lp_start;
34+
gdb_regs[_BTA] = kernel_regs->bta;
35+
gdb_regs[_STOP_PC] = kernel_regs->ret;
36+
}
37+
38+
static void from_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
39+
struct callee_regs *cregs)
40+
{
41+
int regno;
42+
43+
for (regno = 0; regno <= 26; regno++)
44+
set_reg(regno, gdb_regs[regno + _R0], kernel_regs, cregs);
45+
46+
kernel_regs->fp = gdb_regs[_FP];
47+
kernel_regs->sp = gdb_regs[__SP];
48+
kernel_regs->blink = gdb_regs[_BLINK];
49+
kernel_regs->ret = gdb_regs[_RET];
50+
kernel_regs->status32 = gdb_regs[_STATUS32];
51+
kernel_regs->lp_count = gdb_regs[_LP_COUNT];
52+
kernel_regs->lp_end = gdb_regs[_LP_END];
53+
kernel_regs->lp_start = gdb_regs[_LP_START];
54+
kernel_regs->bta = gdb_regs[_BTA];
55+
}
56+
57+
58+
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
59+
{
60+
to_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
61+
current->thread.callee_reg);
62+
}
63+
64+
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
65+
{
66+
from_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
67+
current->thread.callee_reg);
68+
}
69+
70+
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
71+
struct task_struct *task)
72+
{
73+
if (task)
74+
to_gdb_regs(gdb_regs, task_pt_regs(task),
75+
(struct callee_regs *) task->thread.callee_reg);
76+
}
77+
78+
struct single_step_data_t {
79+
uint16_t opcode[2];
80+
unsigned long address[2];
81+
int is_branch;
82+
int armed;
83+
} single_step_data;
84+
85+
static void undo_single_step(struct pt_regs *regs)
86+
{
87+
if (single_step_data.armed) {
88+
int i;
89+
90+
for (i = 0; i < (single_step_data.is_branch ? 2 : 1); i++) {
91+
memcpy((void *) single_step_data.address[i],
92+
&single_step_data.opcode[i],
93+
BREAK_INSTR_SIZE);
94+
95+
flush_icache_range(single_step_data.address[i],
96+
single_step_data.address[i] +
97+
BREAK_INSTR_SIZE);
98+
}
99+
single_step_data.armed = 0;
100+
}
101+
}
102+
103+
static void place_trap(unsigned long address, void *save)
104+
{
105+
memcpy(save, (void *) address, BREAK_INSTR_SIZE);
106+
memcpy((void *) address, &arch_kgdb_ops.gdb_bpt_instr,
107+
BREAK_INSTR_SIZE);
108+
flush_icache_range(address, address + BREAK_INSTR_SIZE);
109+
}
110+
111+
static void do_single_step(struct pt_regs *regs)
112+
{
113+
single_step_data.is_branch = disasm_next_pc((unsigned long)
114+
regs->ret, regs, (struct callee_regs *)
115+
current->thread.callee_reg,
116+
&single_step_data.address[0],
117+
&single_step_data.address[1]);
118+
119+
place_trap(single_step_data.address[0], &single_step_data.opcode[0]);
120+
121+
if (single_step_data.is_branch) {
122+
place_trap(single_step_data.address[1],
123+
&single_step_data.opcode[1]);
124+
}
125+
126+
single_step_data.armed++;
127+
}
128+
129+
int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
130+
char *remcomInBuffer, char *remcomOutBuffer,
131+
struct pt_regs *regs)
132+
{
133+
unsigned long addr;
134+
char *ptr;
135+
136+
undo_single_step(regs);
137+
138+
switch (remcomInBuffer[0]) {
139+
case 's':
140+
case 'c':
141+
ptr = &remcomInBuffer[1];
142+
if (kgdb_hex2long(&ptr, &addr))
143+
regs->ret = addr;
144+
145+
case 'D':
146+
case 'k':
147+
atomic_set(&kgdb_cpu_doing_single_step, -1);
148+
149+
if (remcomInBuffer[0] == 's') {
150+
do_single_step(regs);
151+
atomic_set(&kgdb_cpu_doing_single_step,
152+
smp_processor_id());
153+
}
154+
155+
return 0;
156+
}
157+
return -1;
158+
}
159+
160+
unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
161+
{
162+
return instruction_pointer(regs);
163+
}
164+
165+
int kgdb_arch_init(void)
166+
{
167+
single_step_data.armed = 0;
168+
return 0;
169+
}
170+
171+
void kgdb_trap(struct pt_regs *regs, int param)
172+
{
173+
/* trap_s 3 is used for breakpoints that overwrite existing
174+
* instructions, while trap_s 4 is used for compiled breakpoints.
175+
*
176+
* with trap_s 3 breakpoints the original instruction needs to be
177+
* restored and continuation needs to start at the location of the
178+
* breakpoint.
179+
*
180+
* with trap_s 4 (compiled) breakpoints, continuation needs to
181+
* start after the breakpoint.
182+
*/
183+
if (param == 3)
184+
instruction_pointer(regs) -= BREAK_INSTR_SIZE;
185+
186+
kgdb_handle_exception(1, SIGTRAP, 0, regs);
187+
}
188+
189+
void kgdb_arch_exit(void)
190+
{
191+
}
192+
193+
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
194+
{
195+
instruction_pointer(regs) = ip;
196+
}
197+
198+
struct kgdb_arch arch_kgdb_ops = {
199+
/* breakpoint instruction: TRAP_S 0x3 */
200+
#ifdef CONFIG_CPU_BIG_ENDIAN
201+
.gdb_bpt_instr = {0x78, 0x7e},
202+
#else
203+
.gdb_bpt_instr = {0x7e, 0x78},
204+
#endif
205+
};

arch/arc/kernel/traps.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <asm/setup.h>
2121
#include <asm/kprobes.h>
2222
#include <asm/unaligned.h>
23+
#include <asm/kgdb.h>
2324

2425
void __init trap_init(void)
2526
{
@@ -141,6 +142,11 @@ void do_non_swi_trap(unsigned long cause, unsigned long address,
141142
trap_is_kprobe(param, address, regs);
142143
break;
143144

145+
case 3:
146+
case 4:
147+
kgdb_trap(regs, param);
148+
break;
149+
144150
default:
145151
break;
146152
}

0 commit comments

Comments
 (0)