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"); |
