diff options
Diffstat (limited to 'arch/powerpc/kernel/process.c')
| -rw-r--r-- | arch/powerpc/kernel/process.c | 110 |
1 files changed, 93 insertions, 17 deletions
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 9be77e3936fb..e4d71ced97ef 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c | |||
| @@ -245,6 +245,24 @@ void discard_lazy_cpu_state(void) | |||
| 245 | } | 245 | } |
| 246 | #endif /* CONFIG_SMP */ | 246 | #endif /* CONFIG_SMP */ |
| 247 | 247 | ||
| 248 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | ||
| 249 | void do_send_trap(struct pt_regs *regs, unsigned long address, | ||
| 250 | unsigned long error_code, int signal_code, int breakpt) | ||
| 251 | { | ||
| 252 | siginfo_t info; | ||
| 253 | |||
| 254 | if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code, | ||
| 255 | 11, SIGSEGV) == NOTIFY_STOP) | ||
| 256 | return; | ||
| 257 | |||
| 258 | /* Deliver the signal to userspace */ | ||
| 259 | info.si_signo = SIGTRAP; | ||
| 260 | info.si_errno = breakpt; /* breakpoint or watchpoint id */ | ||
| 261 | info.si_code = signal_code; | ||
| 262 | info.si_addr = (void __user *)address; | ||
| 263 | force_sig_info(SIGTRAP, &info, current); | ||
| 264 | } | ||
| 265 | #else /* !CONFIG_PPC_ADV_DEBUG_REGS */ | ||
| 248 | void do_dabr(struct pt_regs *regs, unsigned long address, | 266 | void do_dabr(struct pt_regs *regs, unsigned long address, |
| 249 | unsigned long error_code) | 267 | unsigned long error_code) |
| 250 | { | 268 | { |
| @@ -257,12 +275,6 @@ void do_dabr(struct pt_regs *regs, unsigned long address, | |||
| 257 | if (debugger_dabr_match(regs)) | 275 | if (debugger_dabr_match(regs)) |
| 258 | return; | 276 | return; |
| 259 | 277 | ||
| 260 | /* Clear the DAC and struct entries. One shot trigger */ | ||
| 261 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | ||
| 262 | mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~(DBSR_DAC1R | DBSR_DAC1W | ||
| 263 | | DBCR0_IDM)); | ||
| 264 | #endif | ||
| 265 | |||
| 266 | /* Clear the DABR */ | 278 | /* Clear the DABR */ |
| 267 | set_dabr(0); | 279 | set_dabr(0); |
| 268 | 280 | ||
| @@ -273,9 +285,82 @@ void do_dabr(struct pt_regs *regs, unsigned long address, | |||
| 273 | info.si_addr = (void __user *)address; | 285 | info.si_addr = (void __user *)address; |
| 274 | force_sig_info(SIGTRAP, &info, current); | 286 | force_sig_info(SIGTRAP, &info, current); |
| 275 | } | 287 | } |
| 288 | #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ | ||
| 276 | 289 | ||
| 277 | static DEFINE_PER_CPU(unsigned long, current_dabr); | 290 | static DEFINE_PER_CPU(unsigned long, current_dabr); |
| 278 | 291 | ||
| 292 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | ||
| 293 | /* | ||
| 294 | * Set the debug registers back to their default "safe" values. | ||
| 295 | */ | ||
| 296 | static void set_debug_reg_defaults(struct thread_struct *thread) | ||
| 297 | { | ||
| 298 | thread->iac1 = thread->iac2 = 0; | ||
| 299 | #if CONFIG_PPC_ADV_DEBUG_IACS > 2 | ||
| 300 | thread->iac3 = thread->iac4 = 0; | ||
| 301 | #endif | ||
| 302 | thread->dac1 = thread->dac2 = 0; | ||
| 303 | #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 | ||
| 304 | thread->dvc1 = thread->dvc2 = 0; | ||
| 305 | #endif | ||
| 306 | thread->dbcr0 = 0; | ||
| 307 | #ifdef CONFIG_BOOKE | ||
| 308 | /* | ||
| 309 | * Force User/Supervisor bits to b11 (user-only MSR[PR]=1) | ||
| 310 | */ | ||
| 311 | thread->dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US | \ | ||
| 312 | DBCR1_IAC3US | DBCR1_IAC4US; | ||
| 313 | /* | ||
| 314 | * Force Data Address Compare User/Supervisor bits to be User-only | ||
| 315 | * (0b11 MSR[PR]=1) and set all other bits in DBCR2 register to be 0. | ||
| 316 | */ | ||
| 317 | thread->dbcr2 = DBCR2_DAC1US | DBCR2_DAC2US; | ||
| 318 | #else | ||
| 319 | thread->dbcr1 = 0; | ||
| 320 | #endif | ||
| 321 | } | ||
| 322 | |||
| 323 | static void prime_debug_regs(struct thread_struct *thread) | ||
| 324 | { | ||
| 325 | mtspr(SPRN_IAC1, thread->iac1); | ||
| 326 | mtspr(SPRN_IAC2, thread->iac2); | ||
| 327 | #if CONFIG_PPC_ADV_DEBUG_IACS > 2 | ||
| 328 | mtspr(SPRN_IAC3, thread->iac3); | ||
| 329 | mtspr(SPRN_IAC4, thread->iac4); | ||
| 330 | #endif | ||
| 331 | mtspr(SPRN_DAC1, thread->dac1); | ||
| 332 | mtspr(SPRN_DAC2, thread->dac2); | ||
| 333 | #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 | ||
| 334 | mtspr(SPRN_DVC1, thread->dvc1); | ||
| 335 | mtspr(SPRN_DVC2, thread->dvc2); | ||
| 336 | #endif | ||
| 337 | mtspr(SPRN_DBCR0, thread->dbcr0); | ||
| 338 | mtspr(SPRN_DBCR1, thread->dbcr1); | ||
| 339 | #ifdef CONFIG_BOOKE | ||
| 340 | mtspr(SPRN_DBCR2, thread->dbcr2); | ||
| 341 | #endif | ||
| 342 | } | ||
| 343 | /* | ||
| 344 | * Unless neither the old or new thread are making use of the | ||
| 345 | * debug registers, set the debug registers from the values | ||
| 346 | * stored in the new thread. | ||
| 347 | */ | ||
| 348 | static void switch_booke_debug_regs(struct thread_struct *new_thread) | ||
| 349 | { | ||
| 350 | if ((current->thread.dbcr0 & DBCR0_IDM) | ||
| 351 | || (new_thread->dbcr0 & DBCR0_IDM)) | ||
| 352 | prime_debug_regs(new_thread); | ||
| 353 | } | ||
| 354 | #else /* !CONFIG_PPC_ADV_DEBUG_REGS */ | ||
| 355 | static void set_debug_reg_defaults(struct thread_struct *thread) | ||
| 356 | { | ||
| 357 | if (thread->dabr) { | ||
| 358 | thread->dabr = 0; | ||
| 359 | set_dabr(0); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ | ||
| 363 | |||
| 279 | int set_dabr(unsigned long dabr) | 364 | int set_dabr(unsigned long dabr) |
| 280 | { | 365 | { |
| 281 | __get_cpu_var(current_dabr) = dabr; | 366 | __get_cpu_var(current_dabr) = dabr; |
| @@ -372,9 +457,7 @@ struct task_struct *__switch_to(struct task_struct *prev, | |||
| 372 | #endif /* CONFIG_SMP */ | 457 | #endif /* CONFIG_SMP */ |
| 373 | 458 | ||
| 374 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | 459 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS |
| 375 | /* If new thread DAC (HW breakpoint) is the same then leave it */ | 460 | switch_booke_debug_regs(&new->thread); |
| 376 | if (new->thread.dabr) | ||
| 377 | set_dabr(new->thread.dabr); | ||
| 378 | #else | 461 | #else |
| 379 | if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) | 462 | if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) |
| 380 | set_dabr(new->thread.dabr); | 463 | set_dabr(new->thread.dabr); |
| @@ -556,14 +639,7 @@ void flush_thread(void) | |||
| 556 | { | 639 | { |
| 557 | discard_lazy_cpu_state(); | 640 | discard_lazy_cpu_state(); |
| 558 | 641 | ||
| 559 | if (current->thread.dabr) { | 642 | set_debug_reg_defaults(¤t->thread); |
| 560 | current->thread.dabr = 0; | ||
| 561 | set_dabr(0); | ||
| 562 | |||
| 563 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | ||
| 564 | current->thread.dbcr0 &= ~(DBSR_DAC1R | DBSR_DAC1W); | ||
| 565 | #endif | ||
| 566 | } | ||
| 567 | } | 643 | } |
| 568 | 644 | ||
| 569 | void | 645 | void |
