aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/kgdb.c138
-rw-r--r--arch/x86/kernel/setup64.c16
-rw-r--r--kernel/kgdb.c4
3 files changed, 156 insertions, 2 deletions
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
index 5d7a21119bf8..7d651adcb222 100644
--- a/arch/x86/kernel/kgdb.c
+++ b/arch/x86/kernel/kgdb.c
@@ -182,6 +182,122 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
182#endif 182#endif
183} 183}
184 184
185static struct hw_breakpoint {
186 unsigned enabled;
187 unsigned type;
188 unsigned len;
189 unsigned long addr;
190} breakinfo[4];
191
192static void kgdb_correct_hw_break(void)
193{
194 unsigned long dr7;
195 int correctit = 0;
196 int breakbit;
197 int breakno;
198
199 get_debugreg(dr7, 7);
200 for (breakno = 0; breakno < 4; breakno++) {
201 breakbit = 2 << (breakno << 1);
202 if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
203 correctit = 1;
204 dr7 |= breakbit;
205 dr7 &= ~(0xf0000 << (breakno << 2));
206 dr7 |= ((breakinfo[breakno].len << 2) |
207 breakinfo[breakno].type) <<
208 ((breakno << 2) + 16);
209 if (breakno >= 0 && breakno <= 3)
210 set_debugreg(breakinfo[breakno].addr, breakno);
211
212 } else {
213 if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
214 correctit = 1;
215 dr7 &= ~breakbit;
216 dr7 &= ~(0xf0000 << (breakno << 2));
217 }
218 }
219 }
220 if (correctit)
221 set_debugreg(dr7, 7);
222}
223
224static int
225kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
226{
227 int i;
228
229 for (i = 0; i < 4; i++)
230 if (breakinfo[i].addr == addr && breakinfo[i].enabled)
231 break;
232 if (i == 4)
233 return -1;
234
235 breakinfo[i].enabled = 0;
236
237 return 0;
238}
239
240static void kgdb_remove_all_hw_break(void)
241{
242 int i;
243
244 for (i = 0; i < 4; i++)
245 memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
246}
247
248static int
249kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
250{
251 unsigned type;
252 int i;
253
254 for (i = 0; i < 4; i++)
255 if (!breakinfo[i].enabled)
256 break;
257 if (i == 4)
258 return -1;
259
260 switch (bptype) {
261 case BP_HARDWARE_BREAKPOINT:
262 type = 0;
263 len = 1;
264 break;
265 case BP_WRITE_WATCHPOINT:
266 type = 1;
267 break;
268 case BP_ACCESS_WATCHPOINT:
269 type = 3;
270 break;
271 default:
272 return -1;
273 }
274
275 if (len == 1 || len == 2 || len == 4)
276 breakinfo[i].len = len - 1;
277 else
278 return -1;
279
280 breakinfo[i].enabled = 1;
281 breakinfo[i].addr = addr;
282 breakinfo[i].type = type;
283
284 return 0;
285}
286
287/**
288 * kgdb_disable_hw_debug - Disable hardware debugging while we in kgdb.
289 * @regs: Current &struct pt_regs.
290 *
291 * This function will be called if the particular architecture must
292 * disable hardware debugging while it is processing gdb packets or
293 * handling exception.
294 */
295void kgdb_disable_hw_debug(struct pt_regs *regs)
296{
297 /* Disable hardware debugging while we are in kgdb: */
298 set_debugreg(0UL, 7);
299}
300
185/** 301/**
186 * kgdb_post_primary_code - Save error vector/code numbers. 302 * kgdb_post_primary_code - Save error vector/code numbers.
187 * @regs: Original pt_regs. 303 * @regs: Original pt_regs.
@@ -243,6 +359,7 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
243 struct pt_regs *linux_regs) 359 struct pt_regs *linux_regs)
244{ 360{
245 unsigned long addr; 361 unsigned long addr;
362 unsigned long dr6;
246 char *ptr; 363 char *ptr;
247 int newPC; 364 int newPC;
248 365
@@ -269,6 +386,22 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
269 } 386 }
270 } 387 }
271 388
389 get_debugreg(dr6, 6);
390 if (!(dr6 & 0x4000)) {
391 int breakno;
392
393 for (breakno = 0; breakno < 4; breakno++) {
394 if (dr6 & (1 << breakno) &&
395 breakinfo[breakno].type == 0) {
396 /* Set restore flag: */
397 linux_regs->flags |= X86_EFLAGS_RF;
398 break;
399 }
400 }
401 }
402 set_debugreg(0UL, 6);
403 kgdb_correct_hw_break();
404
272 return 0; 405 return 0;
273 } 406 }
274 407
@@ -426,4 +559,9 @@ unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
426struct kgdb_arch arch_kgdb_ops = { 559struct kgdb_arch arch_kgdb_ops = {
427 /* Breakpoint instruction: */ 560 /* Breakpoint instruction: */
428 .gdb_bpt_instr = { 0xcc }, 561 .gdb_bpt_instr = { 0xcc },
562 .flags = KGDB_HW_BREAKPOINT,
563 .set_hw_breakpoint = kgdb_set_hw_break,
564 .remove_hw_breakpoint = kgdb_remove_hw_break,
565 .remove_all_hw_break = kgdb_remove_all_hw_break,
566 .correct_hw_break = kgdb_correct_hw_break,
429}; 567};
diff --git a/arch/x86/kernel/setup64.c b/arch/x86/kernel/setup64.c
index e24c45677094..143aa78c566b 100644
--- a/arch/x86/kernel/setup64.c
+++ b/arch/x86/kernel/setup64.c
@@ -11,6 +11,7 @@
11#include <linux/bootmem.h> 11#include <linux/bootmem.h>
12#include <linux/bitops.h> 12#include <linux/bitops.h>
13#include <linux/module.h> 13#include <linux/module.h>
14#include <linux/kgdb.h>
14#include <asm/pda.h> 15#include <asm/pda.h>
15#include <asm/pgtable.h> 16#include <asm/pgtable.h>
16#include <asm/processor.h> 17#include <asm/processor.h>
@@ -327,6 +328,17 @@ void __cpuinit cpu_init (void)
327 load_TR_desc(); 328 load_TR_desc();
328 load_LDT(&init_mm.context); 329 load_LDT(&init_mm.context);
329 330
331#ifdef CONFIG_KGDB
332 /*
333 * If the kgdb is connected no debug regs should be altered. This
334 * is only applicable when KGDB and a KGDB I/O module are built
335 * into the kernel and you are using early debugging with
336 * kgdbwait. KGDB will control the kernel HW breakpoint registers.
337 */
338 if (kgdb_connected && arch_kgdb_ops.correct_hw_break)
339 arch_kgdb_ops.correct_hw_break();
340 else {
341#endif
330 /* 342 /*
331 * Clear all 6 debug registers: 343 * Clear all 6 debug registers:
332 */ 344 */
@@ -337,6 +349,10 @@ void __cpuinit cpu_init (void)
337 set_debugreg(0UL, 3); 349 set_debugreg(0UL, 3);
338 set_debugreg(0UL, 6); 350 set_debugreg(0UL, 6);
339 set_debugreg(0UL, 7); 351 set_debugreg(0UL, 7);
352#ifdef CONFIG_KGDB
353 /* If the kgdb is connected no debug regs should be altered. */
354 }
355#endif
340 356
341 fpu_init(); 357 fpu_init();
342 358
diff --git a/kernel/kgdb.c b/kernel/kgdb.c
index 319c08c92ee2..68aea78407e4 100644
--- a/kernel/kgdb.c
+++ b/kernel/kgdb.c
@@ -1139,10 +1139,10 @@ static void gdb_cmd_break(struct kgdb_state *ks)
1139 error = kgdb_remove_sw_break(addr); 1139 error = kgdb_remove_sw_break(addr);
1140 else if (remcom_in_buffer[0] == 'Z') 1140 else if (remcom_in_buffer[0] == 'Z')
1141 error = arch_kgdb_ops.set_hw_breakpoint(addr, 1141 error = arch_kgdb_ops.set_hw_breakpoint(addr,
1142 (int)length, *bpt_type); 1142 (int)length, *bpt_type - '0');
1143 else if (remcom_in_buffer[0] == 'z') 1143 else if (remcom_in_buffer[0] == 'z')
1144 error = arch_kgdb_ops.remove_hw_breakpoint(addr, 1144 error = arch_kgdb_ops.remove_hw_breakpoint(addr,
1145 (int) length, *bpt_type); 1145 (int) length, *bpt_type - '0');
1146 1146
1147 if (error == 0) 1147 if (error == 0)
1148 strcpy(remcom_out_buffer, "OK"); 1148 strcpy(remcom_out_buffer, "OK");