aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/kernel/kgdb.c171
1 files changed, 114 insertions, 57 deletions
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
index dd74fe7273b1..62bea7307eaa 100644
--- a/arch/x86/kernel/kgdb.c
+++ b/arch/x86/kernel/kgdb.c
@@ -42,6 +42,7 @@
42#include <linux/init.h> 42#include <linux/init.h>
43#include <linux/smp.h> 43#include <linux/smp.h>
44#include <linux/nmi.h> 44#include <linux/nmi.h>
45#include <linux/hw_breakpoint.h>
45 46
46#include <asm/debugreg.h> 47#include <asm/debugreg.h>
47#include <asm/apicdef.h> 48#include <asm/apicdef.h>
@@ -204,40 +205,38 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
204 205
205static struct hw_breakpoint { 206static struct hw_breakpoint {
206 unsigned enabled; 207 unsigned enabled;
207 unsigned type;
208 unsigned len;
209 unsigned long addr; 208 unsigned long addr;
209 int len;
210 int type;
211 struct perf_event **pev;
210} breakinfo[4]; 212} breakinfo[4];
211 213
212static void kgdb_correct_hw_break(void) 214static void kgdb_correct_hw_break(void)
213{ 215{
214 unsigned long dr7;
215 int correctit = 0;
216 int breakbit;
217 int breakno; 216 int breakno;
218 217
219 get_debugreg(dr7, 7);
220 for (breakno = 0; breakno < 4; breakno++) { 218 for (breakno = 0; breakno < 4; breakno++) {
221 breakbit = 2 << (breakno << 1); 219 struct perf_event *bp;
222 if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { 220 struct arch_hw_breakpoint *info;
223 correctit = 1; 221 int val;
224 dr7 |= breakbit; 222 int cpu = raw_smp_processor_id();
225 dr7 &= ~(0xf0000 << (breakno << 2)); 223 if (!breakinfo[breakno].enabled)
226 dr7 |= ((breakinfo[breakno].len << 2) | 224 continue;
227 breakinfo[breakno].type) << 225 bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu);
228 ((breakno << 2) + 16); 226 info = counter_arch_bp(bp);
229 set_debugreg(breakinfo[breakno].addr, breakno); 227 if (bp->attr.disabled != 1)
230 228 continue;
231 } else { 229 bp->attr.bp_addr = breakinfo[breakno].addr;
232 if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { 230 bp->attr.bp_len = breakinfo[breakno].len;
233 correctit = 1; 231 bp->attr.bp_type = breakinfo[breakno].type;
234 dr7 &= ~breakbit; 232 info->address = breakinfo[breakno].addr;
235 dr7 &= ~(0xf0000 << (breakno << 2)); 233 info->len = breakinfo[breakno].len;
236 } 234 info->type = breakinfo[breakno].type;
237 } 235 val = arch_install_hw_breakpoint(bp);
236 if (!val)
237 bp->attr.disabled = 0;
238 } 238 }
239 if (correctit) 239 hw_breakpoint_restore();
240 set_debugreg(dr7, 7);
241} 240}
242 241
243static int 242static int
@@ -259,15 +258,23 @@ kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
259static void kgdb_remove_all_hw_break(void) 258static void kgdb_remove_all_hw_break(void)
260{ 259{
261 int i; 260 int i;
261 int cpu = raw_smp_processor_id();
262 struct perf_event *bp;
262 263
263 for (i = 0; i < 4; i++) 264 for (i = 0; i < 4; i++) {
264 memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint)); 265 if (!breakinfo[i].enabled)
266 continue;
267 bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
268 if (bp->attr.disabled == 1)
269 continue;
270 arch_uninstall_hw_breakpoint(bp);
271 bp->attr.disabled = 1;
272 }
265} 273}
266 274
267static int 275static int
268kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype) 276kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
269{ 277{
270 unsigned type;
271 int i; 278 int i;
272 279
273 for (i = 0; i < 4; i++) 280 for (i = 0; i < 4; i++)
@@ -278,27 +285,38 @@ kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
278 285
279 switch (bptype) { 286 switch (bptype) {
280 case BP_HARDWARE_BREAKPOINT: 287 case BP_HARDWARE_BREAKPOINT:
281 type = 0; 288 len = 1;
282 len = 1; 289 breakinfo[i].type = X86_BREAKPOINT_EXECUTE;
283 break; 290 break;
284 case BP_WRITE_WATCHPOINT: 291 case BP_WRITE_WATCHPOINT:
285 type = 1; 292 breakinfo[i].type = X86_BREAKPOINT_WRITE;
286 break; 293 break;
287 case BP_ACCESS_WATCHPOINT: 294 case BP_ACCESS_WATCHPOINT:
288 type = 3; 295 breakinfo[i].type = X86_BREAKPOINT_RW;
289 break; 296 break;
290 default: 297 default:
291 return -1; 298 return -1;
292 } 299 }
293 300 switch (len) {
294 if (len == 1 || len == 2 || len == 4) 301 case 1:
295 breakinfo[i].len = len - 1; 302 breakinfo[i].len = X86_BREAKPOINT_LEN_1;
296 else 303 break;
304 case 2:
305 breakinfo[i].len = X86_BREAKPOINT_LEN_2;
306 break;
307 case 4:
308 breakinfo[i].len = X86_BREAKPOINT_LEN_4;
309 break;
310#ifdef CONFIG_X86_64
311 case 8:
312 breakinfo[i].len = X86_BREAKPOINT_LEN_8;
313 break;
314#endif
315 default:
297 return -1; 316 return -1;
298 317 }
299 breakinfo[i].enabled = 1;
300 breakinfo[i].addr = addr; 318 breakinfo[i].addr = addr;
301 breakinfo[i].type = type; 319 breakinfo[i].enabled = 1;
302 320
303 return 0; 321 return 0;
304} 322}
@@ -313,8 +331,21 @@ kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
313 */ 331 */
314void kgdb_disable_hw_debug(struct pt_regs *regs) 332void kgdb_disable_hw_debug(struct pt_regs *regs)
315{ 333{
334 int i;
335 int cpu = raw_smp_processor_id();
336 struct perf_event *bp;
337
316 /* Disable hardware debugging while we are in kgdb: */ 338 /* Disable hardware debugging while we are in kgdb: */
317 set_debugreg(0UL, 7); 339 set_debugreg(0UL, 7);
340 for (i = 0; i < 4; i++) {
341 if (!breakinfo[i].enabled)
342 continue;
343 bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
344 if (bp->attr.disabled == 1)
345 continue;
346 arch_uninstall_hw_breakpoint(bp);
347 bp->attr.disabled = 1;
348 }
318} 349}
319 350
320/** 351/**
@@ -378,7 +409,6 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
378 struct pt_regs *linux_regs) 409 struct pt_regs *linux_regs)
379{ 410{
380 unsigned long addr; 411 unsigned long addr;
381 unsigned long dr6;
382 char *ptr; 412 char *ptr;
383 int newPC; 413 int newPC;
384 414
@@ -404,20 +434,6 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
404 raw_smp_processor_id()); 434 raw_smp_processor_id());
405 } 435 }
406 436
407 get_debugreg(dr6, 6);
408 if (!(dr6 & 0x4000)) {
409 int breakno;
410
411 for (breakno = 0; breakno < 4; breakno++) {
412 if (dr6 & (1 << breakno) &&
413 breakinfo[breakno].type == 0) {
414 /* Set restore flag: */
415 linux_regs->flags |= X86_EFLAGS_RF;
416 break;
417 }
418 }
419 }
420 set_debugreg(0UL, 6);
421 kgdb_correct_hw_break(); 437 kgdb_correct_hw_break();
422 438
423 return 0; 439 return 0;
@@ -485,8 +501,7 @@ static int __kgdb_notify(struct die_args *args, unsigned long cmd)
485 break; 501 break;
486 502
487 case DIE_DEBUG: 503 case DIE_DEBUG:
488 if (atomic_read(&kgdb_cpu_doing_single_step) == 504 if (atomic_read(&kgdb_cpu_doing_single_step) != -1) {
489 raw_smp_processor_id()) {
490 if (user_mode(regs)) 505 if (user_mode(regs))
491 return single_step_cont(regs, args); 506 return single_step_cont(regs, args);
492 break; 507 break;
@@ -539,7 +554,42 @@ static struct notifier_block kgdb_notifier = {
539 */ 554 */
540int kgdb_arch_init(void) 555int kgdb_arch_init(void)
541{ 556{
542 return register_die_notifier(&kgdb_notifier); 557 int i, cpu;
558 int ret;
559 struct perf_event_attr attr;
560 struct perf_event **pevent;
561
562 ret = register_die_notifier(&kgdb_notifier);
563 if (ret != 0)
564 return ret;
565 /*
566 * Pre-allocate the hw breakpoint structions in the non-atomic
567 * portion of kgdb because this operation requires mutexs to
568 * complete.
569 */
570 attr.bp_addr = (unsigned long)kgdb_arch_init;
571 attr.type = PERF_TYPE_BREAKPOINT;
572 attr.bp_len = HW_BREAKPOINT_LEN_1;
573 attr.bp_type = HW_BREAKPOINT_W;
574 attr.disabled = 1;
575 for (i = 0; i < 4; i++) {
576 breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
577 if (IS_ERR(breakinfo[i].pev)) {
578 printk(KERN_ERR "kgdb: Could not allocate hw breakpoints\n");
579 breakinfo[i].pev = NULL;
580 kgdb_arch_exit();
581 return -1;
582 }
583 for_each_online_cpu(cpu) {
584 pevent = per_cpu_ptr(breakinfo[i].pev, cpu);
585 pevent[0]->hw.sample_period = 1;
586 if (pevent[0]->destroy != NULL) {
587 pevent[0]->destroy = NULL;
588 release_bp_slot(*pevent);
589 }
590 }
591 }
592 return ret;
543} 593}
544 594
545/** 595/**
@@ -550,6 +600,13 @@ int kgdb_arch_init(void)
550 */ 600 */
551void kgdb_arch_exit(void) 601void kgdb_arch_exit(void)
552{ 602{
603 int i;
604 for (i = 0; i < 4; i++) {
605 if (breakinfo[i].pev) {
606 unregister_wide_hw_breakpoint(breakinfo[i].pev);
607 breakinfo[i].pev = NULL;
608 }
609 }
553 unregister_die_notifier(&kgdb_notifier); 610 unregister_die_notifier(&kgdb_notifier);
554} 611}
555 612