diff options
author | Jason Wessel <jason.wessel@windriver.com> | 2010-05-20 22:04:30 -0400 |
---|---|---|
committer | Jason Wessel <jason.wessel@windriver.com> | 2010-05-20 22:04:30 -0400 |
commit | 031acd8c42edd61070f81c51edc89e83147a3d0e (patch) | |
tree | f0f848ca6b19ec049b00a430bb0fba1035acb76b /arch/x86/kernel/kgdb.c | |
parent | 0b4b3827db386ec6034a5aba1261025b039440c2 (diff) |
x86,kgdb: Implement early hardware breakpoint debugging
It is not possible to use the hw_breakpoint.c API prior to mm_init(),
but it is possible to use hardware breakpoints with the kernel
debugger.
Prior to smp_init() it is possible to simply write to the dr registers
of the boot cpu directly. This can be used up until the
kgdb_arch_late() is invoked, at which point the standard hw_breakpoint.c
API will get used.
CC: Frederic Weisbecker <fweisbec@gmail.com>
CC: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
Diffstat (limited to 'arch/x86/kernel/kgdb.c')
-rw-r--r-- | arch/x86/kernel/kgdb.c | 30 |
1 files changed, 28 insertions, 2 deletions
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index 2b71ec41869f..4f4af75b9482 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c | |||
@@ -199,6 +199,8 @@ static struct hw_breakpoint { | |||
199 | struct perf_event **pev; | 199 | struct perf_event **pev; |
200 | } breakinfo[4]; | 200 | } breakinfo[4]; |
201 | 201 | ||
202 | static unsigned long early_dr7; | ||
203 | |||
202 | static void kgdb_correct_hw_break(void) | 204 | static void kgdb_correct_hw_break(void) |
203 | { | 205 | { |
204 | int breakno; | 206 | int breakno; |
@@ -210,6 +212,14 @@ static void kgdb_correct_hw_break(void) | |||
210 | int cpu = raw_smp_processor_id(); | 212 | int cpu = raw_smp_processor_id(); |
211 | if (!breakinfo[breakno].enabled) | 213 | if (!breakinfo[breakno].enabled) |
212 | continue; | 214 | continue; |
215 | if (dbg_is_early) { | ||
216 | set_debugreg(breakinfo[breakno].addr, breakno); | ||
217 | early_dr7 |= encode_dr7(breakno, | ||
218 | breakinfo[breakno].len, | ||
219 | breakinfo[breakno].type); | ||
220 | set_debugreg(early_dr7, 7); | ||
221 | continue; | ||
222 | } | ||
213 | bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu); | 223 | bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu); |
214 | info = counter_arch_bp(bp); | 224 | info = counter_arch_bp(bp); |
215 | if (bp->attr.disabled != 1) | 225 | if (bp->attr.disabled != 1) |
@@ -224,7 +234,8 @@ static void kgdb_correct_hw_break(void) | |||
224 | if (!val) | 234 | if (!val) |
225 | bp->attr.disabled = 0; | 235 | bp->attr.disabled = 0; |
226 | } | 236 | } |
227 | hw_breakpoint_restore(); | 237 | if (!dbg_is_early) |
238 | hw_breakpoint_restore(); | ||
228 | } | 239 | } |
229 | 240 | ||
230 | static int hw_break_reserve_slot(int breakno) | 241 | static int hw_break_reserve_slot(int breakno) |
@@ -233,6 +244,9 @@ static int hw_break_reserve_slot(int breakno) | |||
233 | int cnt = 0; | 244 | int cnt = 0; |
234 | struct perf_event **pevent; | 245 | struct perf_event **pevent; |
235 | 246 | ||
247 | if (dbg_is_early) | ||
248 | return 0; | ||
249 | |||
236 | for_each_online_cpu(cpu) { | 250 | for_each_online_cpu(cpu) { |
237 | cnt++; | 251 | cnt++; |
238 | pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); | 252 | pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); |
@@ -258,6 +272,9 @@ static int hw_break_release_slot(int breakno) | |||
258 | struct perf_event **pevent; | 272 | struct perf_event **pevent; |
259 | int cpu; | 273 | int cpu; |
260 | 274 | ||
275 | if (dbg_is_early) | ||
276 | return 0; | ||
277 | |||
261 | for_each_online_cpu(cpu) { | 278 | for_each_online_cpu(cpu) { |
262 | pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); | 279 | pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); |
263 | if (dbg_release_bp_slot(*pevent)) | 280 | if (dbg_release_bp_slot(*pevent)) |
@@ -302,7 +319,11 @@ static void kgdb_remove_all_hw_break(void) | |||
302 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); | 319 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); |
303 | if (bp->attr.disabled == 1) | 320 | if (bp->attr.disabled == 1) |
304 | continue; | 321 | continue; |
305 | arch_uninstall_hw_breakpoint(bp); | 322 | if (dbg_is_early) |
323 | early_dr7 &= ~encode_dr7(i, breakinfo[i].len, | ||
324 | breakinfo[i].type); | ||
325 | else | ||
326 | arch_uninstall_hw_breakpoint(bp); | ||
306 | bp->attr.disabled = 1; | 327 | bp->attr.disabled = 1; |
307 | } | 328 | } |
308 | } | 329 | } |
@@ -379,6 +400,11 @@ void kgdb_disable_hw_debug(struct pt_regs *regs) | |||
379 | for (i = 0; i < 4; i++) { | 400 | for (i = 0; i < 4; i++) { |
380 | if (!breakinfo[i].enabled) | 401 | if (!breakinfo[i].enabled) |
381 | continue; | 402 | continue; |
403 | if (dbg_is_early) { | ||
404 | early_dr7 &= ~encode_dr7(i, breakinfo[i].len, | ||
405 | breakinfo[i].type); | ||
406 | continue; | ||
407 | } | ||
382 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); | 408 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); |
383 | if (bp->attr.disabled == 1) | 409 | if (bp->attr.disabled == 1) |
384 | continue; | 410 | continue; |