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