diff options
-rw-r--r-- | arch/x86/kernel/kgdb.c | 138 | ||||
-rw-r--r-- | arch/x86/kernel/setup64.c | 16 | ||||
-rw-r--r-- | kernel/kgdb.c | 4 |
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 | ||
185 | static struct hw_breakpoint { | ||
186 | unsigned enabled; | ||
187 | unsigned type; | ||
188 | unsigned len; | ||
189 | unsigned long addr; | ||
190 | } breakinfo[4]; | ||
191 | |||
192 | static 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 | |||
224 | static int | ||
225 | kgdb_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 | |||
240 | static 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 | |||
248 | static int | ||
249 | kgdb_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 | */ | ||
295 | void 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) | |||
426 | struct kgdb_arch arch_kgdb_ops = { | 559 | struct 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"); |