diff options
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
| -rw-r--r-- | arch/powerpc/kernel/traps.c | 128 | 
1 files changed, 95 insertions, 33 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index d069ff8a7e0..696626a2e83 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -60,13 +60,13 @@  #endif  #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) -int (*__debugger)(struct pt_regs *regs); -int (*__debugger_ipi)(struct pt_regs *regs); -int (*__debugger_bpt)(struct pt_regs *regs); -int (*__debugger_sstep)(struct pt_regs *regs); -int (*__debugger_iabr_match)(struct pt_regs *regs); -int (*__debugger_dabr_match)(struct pt_regs *regs); -int (*__debugger_fault_handler)(struct pt_regs *regs); +int (*__debugger)(struct pt_regs *regs) __read_mostly; +int (*__debugger_ipi)(struct pt_regs *regs) __read_mostly; +int (*__debugger_bpt)(struct pt_regs *regs) __read_mostly; +int (*__debugger_sstep)(struct pt_regs *regs) __read_mostly; +int (*__debugger_iabr_match)(struct pt_regs *regs) __read_mostly; +int (*__debugger_dabr_match)(struct pt_regs *regs) __read_mostly; +int (*__debugger_fault_handler)(struct pt_regs *regs) __read_mostly;  EXPORT_SYMBOL(__debugger);  EXPORT_SYMBOL(__debugger_ipi); @@ -102,11 +102,11 @@ static inline void pmac_backlight_unblank(void) { }  int die(const char *str, struct pt_regs *regs, long err)  {  	static struct { -		spinlock_t lock; +		raw_spinlock_t lock;  		u32 lock_owner;  		int lock_owner_depth;  	} die = { -		.lock =			__SPIN_LOCK_UNLOCKED(die.lock), +		.lock =			__RAW_SPIN_LOCK_UNLOCKED(die.lock),  		.lock_owner =		-1,  		.lock_owner_depth =	0  	}; @@ -120,7 +120,7 @@ int die(const char *str, struct pt_regs *regs, long err)  	if (die.lock_owner != raw_smp_processor_id()) {  		console_verbose(); -		spin_lock_irqsave(&die.lock, flags); +		raw_spin_lock_irqsave(&die.lock, flags);  		die.lock_owner = smp_processor_id();  		die.lock_owner_depth = 0;  		bust_spinlocks(1); @@ -146,6 +146,11 @@ int die(const char *str, struct pt_regs *regs, long err)  #endif  		printk("%s\n", ppc_md.name ? ppc_md.name : ""); +		sysfs_printk_last_file(); +		if (notify_die(DIE_OOPS, str, regs, err, 255, +			       SIGSEGV) == NOTIFY_STOP) +			return 1; +  		print_modules();  		show_regs(regs);  	} else { @@ -155,7 +160,7 @@ int die(const char *str, struct pt_regs *regs, long err)  	bust_spinlocks(0);  	die.lock_owner = -1;  	add_taint(TAINT_DIE); -	spin_unlock_irqrestore(&die.lock, flags); +	raw_spin_unlock_irqrestore(&die.lock, flags);  	if (kexec_should_crash(current) ||  		kexec_sr_activated(smp_processor_id())) @@ -294,7 +299,7 @@ static inline int check_io_access(struct pt_regs *regs)  	return 0;  } -#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) +#ifdef CONFIG_PPC_ADV_DEBUG_REGS  /* On 4xx, the reason for the machine check or program exception     is in the ESR. */  #define get_reason(regs)	((regs)->dsisr) @@ -478,6 +483,8 @@ void machine_check_exception(struct pt_regs *regs)  {  	int recover = 0; +	__get_cpu_var(irq_stat).mce_exceptions++; +  	/* See if any machine dependent calls. In theory, we would want  	 * to call the CPU first, and call the ppc_md. one if the CPU  	 * one returns a positive number. However there is existing code @@ -960,6 +967,8 @@ void vsx_unavailable_exception(struct pt_regs *regs)  void performance_monitor_exception(struct pt_regs *regs)  { +	__get_cpu_var(irq_stat).pmu_irqs++; +  	perf_irq(regs);  } @@ -1024,10 +1033,69 @@ void SoftwareEmulation(struct pt_regs *regs)  }  #endif /* CONFIG_8xx */ -#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) +#ifdef CONFIG_PPC_ADV_DEBUG_REGS +static void handle_debug(struct pt_regs *regs, unsigned long debug_status) +{ +	int changed = 0; +	/* +	 * Determine the cause of the debug event, clear the +	 * event flags and send a trap to the handler. Torez +	 */ +	if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) { +		dbcr_dac(current) &= ~(DBCR_DAC1R | DBCR_DAC1W); +#ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE +		current->thread.dbcr2 &= ~DBCR2_DAC12MODE; +#endif +		do_send_trap(regs, mfspr(SPRN_DAC1), debug_status, TRAP_HWBKPT, +			     5); +		changed |= 0x01; +	}  else if (debug_status & (DBSR_DAC2R | DBSR_DAC2W)) { +		dbcr_dac(current) &= ~(DBCR_DAC2R | DBCR_DAC2W); +		do_send_trap(regs, mfspr(SPRN_DAC2), debug_status, TRAP_HWBKPT, +			     6); +		changed |= 0x01; +	}  else if (debug_status & DBSR_IAC1) { +		current->thread.dbcr0 &= ~DBCR0_IAC1; +		dbcr_iac_range(current) &= ~DBCR_IAC12MODE; +		do_send_trap(regs, mfspr(SPRN_IAC1), debug_status, TRAP_HWBKPT, +			     1); +		changed |= 0x01; +	}  else if (debug_status & DBSR_IAC2) { +		current->thread.dbcr0 &= ~DBCR0_IAC2; +		do_send_trap(regs, mfspr(SPRN_IAC2), debug_status, TRAP_HWBKPT, +			     2); +		changed |= 0x01; +	}  else if (debug_status & DBSR_IAC3) { +		current->thread.dbcr0 &= ~DBCR0_IAC3; +		dbcr_iac_range(current) &= ~DBCR_IAC34MODE; +		do_send_trap(regs, mfspr(SPRN_IAC3), debug_status, TRAP_HWBKPT, +			     3); +		changed |= 0x01; +	}  else if (debug_status & DBSR_IAC4) { +		current->thread.dbcr0 &= ~DBCR0_IAC4; +		do_send_trap(regs, mfspr(SPRN_IAC4), debug_status, TRAP_HWBKPT, +			     4); +		changed |= 0x01; +	} +	/* +	 * At the point this routine was called, the MSR(DE) was turned off. +	 * Check all other debug flags and see if that bit needs to be turned +	 * back on or not. +	 */ +	if (DBCR_ACTIVE_EVENTS(current->thread.dbcr0, current->thread.dbcr1)) +		regs->msr |= MSR_DE; +	else +		/* Make sure the IDM flag is off */ +		current->thread.dbcr0 &= ~DBCR0_IDM; + +	if (changed & 0x01) +		mtspr(SPRN_DBCR0, current->thread.dbcr0); +}  void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)  { +	current->thread.dbsr = debug_status; +  	/* Hack alert: On BookE, Branch Taken stops on the branch itself, while  	 * on server, it stops on the target of the branch. In order to simulate  	 * the server behaviour, we thus restart right away with a single step @@ -1071,29 +1139,23 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)  		if (debugger_sstep(regs))  			return; -		if (user_mode(regs)) -			current->thread.dbcr0 &= ~(DBCR0_IC); - -		_exception(SIGTRAP, regs, TRAP_TRACE, regs->nip); -	} else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) { -		regs->msr &= ~MSR_DE; -  		if (user_mode(regs)) { -			current->thread.dbcr0 &= ~(DBSR_DAC1R | DBSR_DAC1W | -								DBCR0_IDM); -		} else { -			/* Disable DAC interupts */ -			mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~(DBSR_DAC1R | -						DBSR_DAC1W | DBCR0_IDM)); - -			/* Clear the DAC event */ -			mtspr(SPRN_DBSR, (DBSR_DAC1R | DBSR_DAC1W)); +			current->thread.dbcr0 &= ~DBCR0_IC; +#ifdef CONFIG_PPC_ADV_DEBUG_REGS +			if (DBCR_ACTIVE_EVENTS(current->thread.dbcr0, +					       current->thread.dbcr1)) +				regs->msr |= MSR_DE; +			else +				/* Make sure the IDM bit is off */ +				current->thread.dbcr0 &= ~DBCR0_IDM; +#endif  		} -		/* Setup and send the trap to the handler */ -		do_dabr(regs, mfspr(SPRN_DAC1), debug_status); -	} + +		_exception(SIGTRAP, regs, TRAP_TRACE, regs->nip); +	} else +		handle_debug(regs, debug_status);  } -#endif /* CONFIG_4xx || CONFIG_BOOKE */ +#endif /* CONFIG_PPC_ADV_DEBUG_REGS */  #if !defined(CONFIG_TAU_INT)  void TAUException(struct pt_regs *regs)  |