Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arc/kernel/kgdb.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* kgdb support for ARC
4
*
5
* Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com)
6
*/
7
8
#include <linux/kgdb.h>
9
#include <linux/sched.h>
10
#include <linux/sched/task_stack.h>
11
#include <asm/disasm.h>
12
#include <asm/cacheflush.h>
13
14
static void to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
15
struct callee_regs *cregs)
16
{
17
int regno;
18
19
for (regno = 0; regno <= 26; regno++)
20
gdb_regs[_R0 + regno] = get_reg(regno, kernel_regs, cregs);
21
22
for (regno = 27; regno < GDB_MAX_REGS; regno++)
23
gdb_regs[regno] = 0;
24
25
gdb_regs[_FP] = kernel_regs->fp;
26
gdb_regs[__SP] = kernel_regs->sp;
27
gdb_regs[_BLINK] = kernel_regs->blink;
28
gdb_regs[_RET] = kernel_regs->ret;
29
gdb_regs[_STATUS32] = kernel_regs->status32;
30
gdb_regs[_LP_COUNT] = kernel_regs->lp_count;
31
gdb_regs[_LP_END] = kernel_regs->lp_end;
32
gdb_regs[_LP_START] = kernel_regs->lp_start;
33
gdb_regs[_BTA] = kernel_regs->bta;
34
gdb_regs[_STOP_PC] = kernel_regs->ret;
35
}
36
37
static void from_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
38
struct callee_regs *cregs)
39
{
40
int regno;
41
42
for (regno = 0; regno <= 26; regno++)
43
set_reg(regno, gdb_regs[regno + _R0], kernel_regs, cregs);
44
45
kernel_regs->fp = gdb_regs[_FP];
46
kernel_regs->sp = gdb_regs[__SP];
47
kernel_regs->blink = gdb_regs[_BLINK];
48
kernel_regs->ret = gdb_regs[_RET];
49
kernel_regs->status32 = gdb_regs[_STATUS32];
50
kernel_regs->lp_count = gdb_regs[_LP_COUNT];
51
kernel_regs->lp_end = gdb_regs[_LP_END];
52
kernel_regs->lp_start = gdb_regs[_LP_START];
53
kernel_regs->bta = gdb_regs[_BTA];
54
}
55
56
57
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
58
{
59
to_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
60
current->thread.callee_reg);
61
}
62
63
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
64
{
65
from_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
66
current->thread.callee_reg);
67
}
68
69
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
70
struct task_struct *task)
71
{
72
if (task)
73
to_gdb_regs(gdb_regs, task_pt_regs(task),
74
(struct callee_regs *) task->thread.callee_reg);
75
}
76
77
struct single_step_data_t {
78
uint16_t opcode[2];
79
unsigned long address[2];
80
int is_branch;
81
int armed;
82
} single_step_data;
83
84
static void undo_single_step(struct pt_regs *regs)
85
{
86
if (single_step_data.armed) {
87
int i;
88
89
for (i = 0; i < (single_step_data.is_branch ? 2 : 1); i++) {
90
memcpy((void *) single_step_data.address[i],
91
&single_step_data.opcode[i],
92
BREAK_INSTR_SIZE);
93
94
flush_icache_range(single_step_data.address[i],
95
single_step_data.address[i] +
96
BREAK_INSTR_SIZE);
97
}
98
single_step_data.armed = 0;
99
}
100
}
101
102
static void place_trap(unsigned long address, void *save)
103
{
104
memcpy(save, (void *) address, BREAK_INSTR_SIZE);
105
memcpy((void *) address, &arch_kgdb_ops.gdb_bpt_instr,
106
BREAK_INSTR_SIZE);
107
flush_icache_range(address, address + BREAK_INSTR_SIZE);
108
}
109
110
static void do_single_step(struct pt_regs *regs)
111
{
112
single_step_data.is_branch = disasm_next_pc((unsigned long)
113
regs->ret, regs, (struct callee_regs *)
114
current->thread.callee_reg,
115
&single_step_data.address[0],
116
&single_step_data.address[1]);
117
118
place_trap(single_step_data.address[0], &single_step_data.opcode[0]);
119
120
if (single_step_data.is_branch) {
121
place_trap(single_step_data.address[1],
122
&single_step_data.opcode[1]);
123
}
124
125
single_step_data.armed++;
126
}
127
128
int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
129
char *remcomInBuffer, char *remcomOutBuffer,
130
struct pt_regs *regs)
131
{
132
unsigned long addr;
133
char *ptr;
134
135
undo_single_step(regs);
136
137
switch (remcomInBuffer[0]) {
138
case 's':
139
case 'c':
140
ptr = &remcomInBuffer[1];
141
if (kgdb_hex2long(&ptr, &addr))
142
regs->ret = addr;
143
fallthrough;
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
int kgdb_arch_init(void)
161
{
162
single_step_data.armed = 0;
163
return 0;
164
}
165
166
void kgdb_trap(struct pt_regs *regs)
167
{
168
/* trap_s 3 is used for breakpoints that overwrite existing
169
* instructions, while trap_s 4 is used for compiled breakpoints.
170
*
171
* with trap_s 3 breakpoints the original instruction needs to be
172
* restored and continuation needs to start at the location of the
173
* breakpoint.
174
*
175
* with trap_s 4 (compiled) breakpoints, continuation needs to
176
* start after the breakpoint.
177
*/
178
if (regs->ecr.param == 3)
179
instruction_pointer(regs) -= BREAK_INSTR_SIZE;
180
181
kgdb_handle_exception(1, SIGTRAP, 0, regs);
182
}
183
184
void kgdb_arch_exit(void)
185
{
186
}
187
188
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
189
{
190
instruction_pointer(regs) = ip;
191
}
192
193
void kgdb_call_nmi_hook(void *ignored)
194
{
195
/* Default implementation passes get_irq_regs() but we don't */
196
kgdb_nmicallback(raw_smp_processor_id(), NULL);
197
}
198
199
const struct kgdb_arch arch_kgdb_ops = {
200
/* breakpoint instruction: TRAP_S 0x3 */
201
#ifdef CONFIG_CPU_BIG_ENDIAN
202
.gdb_bpt_instr = {0x78, 0x7e},
203
#else
204
.gdb_bpt_instr = {0x7e, 0x78},
205
#endif
206
};
207
208