diff options
Diffstat (limited to 'arch/powerpc/kernel/process.c')
| -rw-r--r-- | arch/powerpc/kernel/process.c | 265 | 
1 files changed, 228 insertions, 37 deletions
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 81430674e71..ceb4e7b62cf 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -50,6 +50,7 @@  #include <asm/runlatch.h>  #include <asm/syscalls.h>  #include <asm/switch_to.h> +#include <asm/tm.h>  #include <asm/debug.h>  #ifdef CONFIG_PPC64  #include <asm/firmware.h> @@ -57,6 +58,13 @@  #include <linux/kprobes.h>  #include <linux/kdebug.h> +/* Transactional Memory debug */ +#ifdef TM_DEBUG_SW +#define TM_DEBUG(x...) printk(KERN_INFO x) +#else +#define TM_DEBUG(x...) do { } while(0) +#endif +  extern unsigned long _get_SP(void);  #ifndef CONFIG_SMP @@ -271,7 +279,7 @@ void do_send_trap(struct pt_regs *regs, unsigned long address,  	force_sig_info(SIGTRAP, &info, current);  }  #else	/* !CONFIG_PPC_ADV_DEBUG_REGS */ -void do_dabr(struct pt_regs *regs, unsigned long address, +void do_break (struct pt_regs *regs, unsigned long address,  		    unsigned long error_code)  {  	siginfo_t info; @@ -281,11 +289,11 @@ void do_dabr(struct pt_regs *regs, unsigned long address,  			11, SIGSEGV) == NOTIFY_STOP)  		return; -	if (debugger_dabr_match(regs)) +	if (debugger_break_match(regs))  		return; -	/* Clear the DABR */ -	set_dabr(0, 0); +	/* Clear the breakpoint */ +	hw_breakpoint_disable();  	/* Deliver the signal to userspace */  	info.si_signo = SIGTRAP; @@ -296,7 +304,7 @@ void do_dabr(struct pt_regs *regs, unsigned long address,  }  #endif	/* CONFIG_PPC_ADV_DEBUG_REGS */ -static DEFINE_PER_CPU(unsigned long, current_dabr); +static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk);  #ifdef CONFIG_PPC_ADV_DEBUG_REGS  /* @@ -364,39 +372,216 @@ static void switch_booke_debug_regs(struct thread_struct *new_thread)  #ifndef CONFIG_HAVE_HW_BREAKPOINT  static void set_debug_reg_defaults(struct thread_struct *thread)  { -	if (thread->dabr) { -		thread->dabr = 0; -		thread->dabrx = 0; -		set_dabr(0, 0); -	} +	thread->hw_brk.address = 0; +	thread->hw_brk.type = 0; +	set_breakpoint(&thread->hw_brk);  }  #endif /* !CONFIG_HAVE_HW_BREAKPOINT */  #endif	/* CONFIG_PPC_ADV_DEBUG_REGS */ -int set_dabr(unsigned long dabr, unsigned long dabrx) -{ -	__get_cpu_var(current_dabr) = dabr; - -	if (ppc_md.set_dabr) -		return ppc_md.set_dabr(dabr, dabrx); - -	/* XXX should we have a CPU_FTR_HAS_DABR ? */  #ifdef CONFIG_PPC_ADV_DEBUG_REGS +static inline int __set_dabr(unsigned long dabr, unsigned long dabrx) +{  	mtspr(SPRN_DAC1, dabr);  #ifdef CONFIG_PPC_47x  	isync();  #endif +	return 0; +}  #elif defined(CONFIG_PPC_BOOK3S) +static inline int __set_dabr(unsigned long dabr, unsigned long dabrx) +{  	mtspr(SPRN_DABR, dabr);  	mtspr(SPRN_DABRX, dabrx); +	return 0; +} +#else +static inline int __set_dabr(unsigned long dabr, unsigned long dabrx) +{ +	return -EINVAL; +}  #endif + +static inline int set_dabr(struct arch_hw_breakpoint *brk) +{ +	unsigned long dabr, dabrx; + +	dabr = brk->address | (brk->type & HW_BRK_TYPE_DABR); +	dabrx = ((brk->type >> 3) & 0x7); + +	if (ppc_md.set_dabr) +		return ppc_md.set_dabr(dabr, dabrx); + +	return __set_dabr(dabr, dabrx); +} + +static inline int set_dawr(struct arch_hw_breakpoint *brk) +{ +	unsigned long dawr, dawrx, mrd; + +	dawr = brk->address; + +	dawrx  = (brk->type & (HW_BRK_TYPE_READ | HW_BRK_TYPE_WRITE)) \ +		                   << (63 - 58); //* read/write bits */ +	dawrx |= ((brk->type & (HW_BRK_TYPE_TRANSLATE)) >> 2) \ +		                   << (63 - 59); //* translate */ +	dawrx |= (brk->type & (HW_BRK_TYPE_PRIV_ALL)) \ +		                   >> 3; //* PRIM bits */ +	/* dawr length is stored in field MDR bits 48:53.  Matches range in +	   doublewords (64 bits) baised by -1 eg. 0b000000=1DW and +	   0b111111=64DW. +	   brk->len is in bytes. +	   This aligns up to double word size, shifts and does the bias. +	*/ +	mrd = ((brk->len + 7) >> 3) - 1; +	dawrx |= (mrd & 0x3f) << (63 - 53); + +	if (ppc_md.set_dawr) +		return ppc_md.set_dawr(dawr, dawrx); +	mtspr(SPRN_DAWR, dawr); +	mtspr(SPRN_DAWRX, dawrx);  	return 0;  } +int set_breakpoint(struct arch_hw_breakpoint *brk) +{ +	__get_cpu_var(current_brk) = *brk; + +	if (cpu_has_feature(CPU_FTR_DAWR)) +		return set_dawr(brk); + +	return set_dabr(brk); +} +  #ifdef CONFIG_PPC64  DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);  #endif +static inline bool hw_brk_match(struct arch_hw_breakpoint *a, +			      struct arch_hw_breakpoint *b) +{ +	if (a->address != b->address) +		return false; +	if (a->type != b->type) +		return false; +	if (a->len != b->len) +		return false; +	return true; +} +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +static inline void tm_reclaim_task(struct task_struct *tsk) +{ +	/* We have to work out if we're switching from/to a task that's in the +	 * middle of a transaction. +	 * +	 * In switching we need to maintain a 2nd register state as +	 * oldtask->thread.ckpt_regs.  We tm_reclaim(oldproc); this saves the +	 * checkpointed (tbegin) state in ckpt_regs and saves the transactional +	 * (current) FPRs into oldtask->thread.transact_fpr[]. +	 * +	 * We also context switch (save) TFHAR/TEXASR/TFIAR in here. +	 */ +	struct thread_struct *thr = &tsk->thread; + +	if (!thr->regs) +		return; + +	if (!MSR_TM_ACTIVE(thr->regs->msr)) +		goto out_and_saveregs; + +	/* Stash the original thread MSR, as giveup_fpu et al will +	 * modify it.  We hold onto it to see whether the task used +	 * FP & vector regs. +	 */ +	thr->tm_orig_msr = thr->regs->msr; + +	TM_DEBUG("--- tm_reclaim on pid %d (NIP=%lx, " +		 "ccr=%lx, msr=%lx, trap=%lx)\n", +		 tsk->pid, thr->regs->nip, +		 thr->regs->ccr, thr->regs->msr, +		 thr->regs->trap); + +	tm_reclaim(thr, thr->regs->msr, TM_CAUSE_RESCHED); + +	TM_DEBUG("--- tm_reclaim on pid %d complete\n", +		 tsk->pid); + +out_and_saveregs: +	/* Always save the regs here, even if a transaction's not active. +	 * This context-switches a thread's TM info SPRs.  We do it here to +	 * be consistent with the restore path (in recheckpoint) which +	 * cannot happen later in _switch(). +	 */ +	tm_save_sprs(thr); +} + +static inline void tm_recheckpoint_new_task(struct task_struct *new) +{ +	unsigned long msr; + +	if (!cpu_has_feature(CPU_FTR_TM)) +		return; + +	/* Recheckpoint the registers of the thread we're about to switch to. +	 * +	 * If the task was using FP, we non-lazily reload both the original and +	 * the speculative FP register states.  This is because the kernel +	 * doesn't see if/when a TM rollback occurs, so if we take an FP +	 * unavoidable later, we are unable to determine which set of FP regs +	 * need to be restored. +	 */ +	if (!new->thread.regs) +		return; + +	/* The TM SPRs are restored here, so that TEXASR.FS can be set +	 * before the trecheckpoint and no explosion occurs. +	 */ +	tm_restore_sprs(&new->thread); + +	if (!MSR_TM_ACTIVE(new->thread.regs->msr)) +		return; +	msr = new->thread.tm_orig_msr; +	/* Recheckpoint to restore original checkpointed register state. */ +	TM_DEBUG("*** tm_recheckpoint of pid %d " +		 "(new->msr 0x%lx, new->origmsr 0x%lx)\n", +		 new->pid, new->thread.regs->msr, msr); + +	/* This loads the checkpointed FP/VEC state, if used */ +	tm_recheckpoint(&new->thread, msr); + +	/* This loads the speculative FP/VEC state, if used */ +	if (msr & MSR_FP) { +		do_load_up_transact_fpu(&new->thread); +		new->thread.regs->msr |= +			(MSR_FP | new->thread.fpexc_mode); +	} +#ifdef CONFIG_ALTIVEC +	if (msr & MSR_VEC) { +		do_load_up_transact_altivec(&new->thread); +		new->thread.regs->msr |= MSR_VEC; +	} +#endif +	/* We may as well turn on VSX too since all the state is restored now */ +	if (msr & MSR_VSX) +		new->thread.regs->msr |= MSR_VSX; + +	TM_DEBUG("*** tm_recheckpoint of pid %d complete " +		 "(kernel msr 0x%lx)\n", +		 new->pid, mfmsr()); +} + +static inline void __switch_to_tm(struct task_struct *prev) +{ +	if (cpu_has_feature(CPU_FTR_TM)) { +		tm_enable(); +		tm_reclaim_task(prev); +	} +} +#else +#define tm_recheckpoint_new_task(new) +#define __switch_to_tm(prev) +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ +  struct task_struct *__switch_to(struct task_struct *prev,  	struct task_struct *new)  { @@ -407,6 +592,8 @@ struct task_struct *__switch_to(struct task_struct *prev,  	struct ppc64_tlb_batch *batch;  #endif +	__switch_to_tm(prev); +  #ifdef CONFIG_SMP  	/* avoid complexity of lazy save/restore of fpu  	 * by just saving it every time we switch out if @@ -481,8 +668,8 @@ struct task_struct *__switch_to(struct task_struct *prev,   * schedule DABR   */  #ifndef CONFIG_HAVE_HW_BREAKPOINT -	if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) -		set_dabr(new->thread.dabr, new->thread.dabrx); +	if (unlikely(hw_brk_match(&__get_cpu_var(current_brk), &new->thread.hw_brk))) +		set_breakpoint(&new->thread.hw_brk);  #endif /* CONFIG_HAVE_HW_BREAKPOINT */  #endif @@ -522,6 +709,9 @@ struct task_struct *__switch_to(struct task_struct *prev,  	 * of sync. Hard disable here.  	 */  	hard_irq_disable(); + +	tm_recheckpoint_new_task(new); +  	last = _switch(old_thread, new_thread);  #ifdef CONFIG_PPC_BOOK3S_64 @@ -641,6 +831,8 @@ void show_regs(struct pt_regs * regs)  {  	int i, trap; +	show_regs_print_info(KERN_DEFAULT); +  	printk("NIP: "REG" LR: "REG" CTR: "REG"\n",  	       regs->nip, regs->link, regs->ctr);  	printk("REGS: %p TRAP: %04lx   %s  (%s)\n", @@ -660,12 +852,6 @@ void show_regs(struct pt_regs * regs)  #else  		printk("DAR: "REG", DSISR: %08lx\n", regs->dar, regs->dsisr);  #endif -	printk("TASK = %p[%d] '%s' THREAD: %p", -	       current, task_pid_nr(current), current->comm, task_thread_info(current)); - -#ifdef CONFIG_SMP -	printk(" CPU: %d", raw_smp_processor_id()); -#endif /* CONFIG_SMP */  	for (i = 0;  i < 32;  i++) {  		if ((i % REGS_PER_LINE) == 0) @@ -683,6 +869,9 @@ void show_regs(struct pt_regs * regs)  	printk("NIP ["REG"] %pS\n", regs->nip, (void *)regs->nip);  	printk("LR ["REG"] %pS\n", regs->link, (void *)regs->link);  #endif +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +	printk("PACATMSCRATCH [%llx]\n", get_paca()->tm_scratch); +#endif  	show_stack(current, (unsigned long *) regs->gpr[1]);  	if (!user_mode(regs))  		show_instructions(regs); @@ -719,10 +908,6 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)  	flush_altivec_to_thread(src);  	flush_vsx_to_thread(src);  	flush_spe_to_thread(src); -#ifdef CONFIG_HAVE_HW_BREAKPOINT -	flush_ptrace_hw_breakpoint(src); -#endif /* CONFIG_HAVE_HW_BREAKPOINT */ -  	*dst = *src;  	return 0;  } @@ -793,6 +978,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,  	p->thread.ksp_limit = (unsigned long)task_stack_page(p) +  				_ALIGN_UP(sizeof(struct thread_info), 16); +#ifdef CONFIG_HAVE_HW_BREAKPOINT +	p->thread.ptrace_bps[0] = NULL; +#endif +  #ifdef CONFIG_PPC_STD_MMU_64  	if (mmu_has_feature(MMU_FTR_SLB)) {  		unsigned long sp_vsid; @@ -813,6 +1002,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,  		p->thread.dscr_inherit = current->thread.dscr_inherit;  		p->thread.dscr = current->thread.dscr;  	} +	if (cpu_has_feature(CPU_FTR_HAS_PPR)) +		p->thread.ppr = INIT_PPR;  #endif  	/*  	 * The PPC64 ABI makes use of a TOC to contain function  @@ -892,7 +1083,6 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)  		regs->msr = MSR_USER32;  	}  #endif -  	discard_lazy_cpu_state();  #ifdef CONFIG_VSX  	current->thread.used_vsr = 0; @@ -912,6 +1102,13 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)  	current->thread.spefscr = 0;  	current->thread.used_spe = 0;  #endif /* CONFIG_SPE */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +	if (cpu_has_feature(CPU_FTR_TM)) +		regs->msr |= MSR_TM; +	current->thread.tm_tfhar = 0; +	current->thread.tm_texasr = 0; +	current->thread.tm_tfiar = 0; +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */  }  #define PR_FP_ALL_EXCEPT (PR_FP_EXC_DIV | PR_FP_EXC_OVF | PR_FP_EXC_UND \ @@ -1161,12 +1358,6 @@ void show_stack(struct task_struct *tsk, unsigned long *stack)  	} while (count++ < kstack_depth_to_print);  } -void dump_stack(void) -{ -	show_stack(current, NULL); -} -EXPORT_SYMBOL(dump_stack); -  #ifdef CONFIG_PPC64  /* Called with hard IRQs off */  void __ppc64_runlatch_on(void)  |