1660 lines · 42.9 KB · C
RawDownload
/* *  Derived from "arch/i386/kernel/process.c" *    Copyright (C) 1995  Linus Torvalds * *  Updated and modified by Cort Dougan (cort@cs.nmt.edu) and *  Paul Mackerras (paulus@cs.anu.edu.au) * *  PowerPC version *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * *  This program is free software; you can redistribute it and/or *  modify it under the terms of the GNU General Public License *  as published by the Free Software Foundation; either version *  2 of the License, or (at your option) any later version. */ #include <linux/errno.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/smp.h>#include <linux/stddef.h>#include <linux/unistd.h>#include <linux/ptrace.h>#include <linux/slab.h>#include <linux/user.h>#include <linux/elf.h>#include <linux/prctl.h>#include <linux/init_task.h>#include <linux/export.h>#include <linux/kallsyms.h>#include <linux/mqueue.h>#include <linux/hardirq.h>#include <linux/utsname.h>#include <linux/ftrace.h>#include <linux/kernel_stat.h>#include <linux/personality.h>#include <linux/random.h>#include <linux/hw_breakpoint.h>#include <linux/uaccess.h> #include <asm/pgtable.h>#include <asm/io.h>#include <asm/processor.h>#include <asm/mmu.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/time.h>#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>#endif#include <asm/code-patching.h>#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_SMPstruct task_struct *last_task_used_math = NULL;struct task_struct *last_task_used_altivec = NULL;struct task_struct *last_task_used_vsx = NULL;struct task_struct *last_task_used_spe = NULL;#endif #ifdef CONFIG_PPC_TRANSACTIONAL_MEMvoid giveup_fpu_maybe_transactional(struct task_struct *tsk){	/*	 * If we are saving the current thread's registers, and the	 * thread is in a transactional state, set the TIF_RESTORE_TM	 * bit so that we know to restore the registers before	 * returning to userspace.	 */	if (tsk == current && tsk->thread.regs &&	    MSR_TM_ACTIVE(tsk->thread.regs->msr) &&	    !test_thread_flag(TIF_RESTORE_TM)) {		tsk->thread.tm_orig_msr = tsk->thread.regs->msr;		set_thread_flag(TIF_RESTORE_TM);	} 	giveup_fpu(tsk);} void giveup_altivec_maybe_transactional(struct task_struct *tsk){	/*	 * If we are saving the current thread's registers, and the	 * thread is in a transactional state, set the TIF_RESTORE_TM	 * bit so that we know to restore the registers before	 * returning to userspace.	 */	if (tsk == current && tsk->thread.regs &&	    MSR_TM_ACTIVE(tsk->thread.regs->msr) &&	    !test_thread_flag(TIF_RESTORE_TM)) {		tsk->thread.tm_orig_msr = tsk->thread.regs->msr;		set_thread_flag(TIF_RESTORE_TM);	} 	giveup_altivec(tsk);} #else#define giveup_fpu_maybe_transactional(tsk)	giveup_fpu(tsk)#define giveup_altivec_maybe_transactional(tsk)	giveup_altivec(tsk)#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ #ifdef CONFIG_PPC_FPU/* * Make sure the floating-point register state in the * the thread_struct is up to date for task tsk. */void flush_fp_to_thread(struct task_struct *tsk){	if (tsk->thread.regs) {		/*		 * We need to disable preemption here because if we didn't,		 * another process could get scheduled after the regs->msr		 * test but before we have finished saving the FP registers		 * to the thread_struct.  That process could take over the		 * FPU, and then when we get scheduled again we would store		 * bogus values for the remaining FP registers.		 */		preempt_disable();		if (tsk->thread.regs->msr & MSR_FP) {#ifdef CONFIG_SMP			/*			 * This should only ever be called for current or			 * for a stopped child process.  Since we save away			 * the FP register state on context switch on SMP,			 * there is something wrong if a stopped child appears			 * to still have its FP state in the CPU registers.			 */			BUG_ON(tsk != current);#endif			giveup_fpu_maybe_transactional(tsk);		}		preempt_enable();	}}EXPORT_SYMBOL_GPL(flush_fp_to_thread);#endif /* CONFIG_PPC_FPU */ void enable_kernel_fp(void){	WARN_ON(preemptible()); #ifdef CONFIG_SMP	if (current->thread.regs && (current->thread.regs->msr & MSR_FP))		giveup_fpu_maybe_transactional(current);	else		giveup_fpu(NULL);	/* just enables FP for kernel */#else	giveup_fpu_maybe_transactional(last_task_used_math);#endif /* CONFIG_SMP */}EXPORT_SYMBOL(enable_kernel_fp); #ifdef CONFIG_ALTIVECvoid enable_kernel_altivec(void){	WARN_ON(preemptible()); #ifdef CONFIG_SMP	if (current->thread.regs && (current->thread.regs->msr & MSR_VEC))		giveup_altivec_maybe_transactional(current);	else		giveup_altivec_notask();#else	giveup_altivec_maybe_transactional(last_task_used_altivec);#endif /* CONFIG_SMP */}EXPORT_SYMBOL(enable_kernel_altivec); /* * Make sure the VMX/Altivec register state in the * the thread_struct is up to date for task tsk. */void flush_altivec_to_thread(struct task_struct *tsk){	if (tsk->thread.regs) {		preempt_disable();		if (tsk->thread.regs->msr & MSR_VEC) {#ifdef CONFIG_SMP			BUG_ON(tsk != current);#endif			giveup_altivec_maybe_transactional(tsk);		}		preempt_enable();	}}EXPORT_SYMBOL_GPL(flush_altivec_to_thread);#endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSX#if 0/* not currently used, but some crazy RAID module might want to later */void enable_kernel_vsx(void){	WARN_ON(preemptible()); #ifdef CONFIG_SMP	if (current->thread.regs && (current->thread.regs->msr & MSR_VSX))		giveup_vsx(current);	else		giveup_vsx(NULL);	/* just enable vsx for kernel - force */#else	giveup_vsx(last_task_used_vsx);#endif /* CONFIG_SMP */}EXPORT_SYMBOL(enable_kernel_vsx);#endif void giveup_vsx(struct task_struct *tsk){	giveup_fpu_maybe_transactional(tsk);	giveup_altivec_maybe_transactional(tsk);	__giveup_vsx(tsk);}EXPORT_SYMBOL(giveup_vsx); void flush_vsx_to_thread(struct task_struct *tsk){	if (tsk->thread.regs) {		preempt_disable();		if (tsk->thread.regs->msr & MSR_VSX) {#ifdef CONFIG_SMP			BUG_ON(tsk != current);#endif			giveup_vsx(tsk);		}		preempt_enable();	}}EXPORT_SYMBOL_GPL(flush_vsx_to_thread);#endif /* CONFIG_VSX */ #ifdef CONFIG_SPE void enable_kernel_spe(void){	WARN_ON(preemptible()); #ifdef CONFIG_SMP	if (current->thread.regs && (current->thread.regs->msr & MSR_SPE))		giveup_spe(current);	else		giveup_spe(NULL);	/* just enable SPE for kernel - force */#else	giveup_spe(last_task_used_spe);#endif /* __SMP __ */}EXPORT_SYMBOL(enable_kernel_spe); void flush_spe_to_thread(struct task_struct *tsk){	if (tsk->thread.regs) {		preempt_disable();		if (tsk->thread.regs->msr & MSR_SPE) {#ifdef CONFIG_SMP			BUG_ON(tsk != current);#endif			tsk->thread.spefscr = mfspr(SPRN_SPEFSCR);			giveup_spe(tsk);		}		preempt_enable();	}}#endif /* CONFIG_SPE */ #ifndef CONFIG_SMP/* * If we are doing lazy switching of CPU state (FP, altivec or SPE), * and the current task has some state, discard it. */void discard_lazy_cpu_state(void){	preempt_disable();	if (last_task_used_math == current)		last_task_used_math = NULL;#ifdef CONFIG_ALTIVEC	if (last_task_used_altivec == current)		last_task_used_altivec = NULL;#endif /* CONFIG_ALTIVEC */#ifdef CONFIG_VSX	if (last_task_used_vsx == current)		last_task_used_vsx = NULL;#endif /* CONFIG_VSX */#ifdef CONFIG_SPE	if (last_task_used_spe == current)		last_task_used_spe = NULL;#endif	preempt_enable();}#endif /* CONFIG_SMP */ #ifdef CONFIG_PPC_ADV_DEBUG_REGSvoid do_send_trap(struct pt_regs *regs, unsigned long address,		  unsigned long error_code, int signal_code, int breakpt){	siginfo_t info; 	current->thread.trap_nr = signal_code;	if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,			11, SIGSEGV) == NOTIFY_STOP)		return; 	/* Deliver the signal to userspace */	info.si_signo = SIGTRAP;	info.si_errno = breakpt;	/* breakpoint or watchpoint id */	info.si_code = signal_code;	info.si_addr = (void __user *)address;	force_sig_info(SIGTRAP, &info, current);}#else	/* !CONFIG_PPC_ADV_DEBUG_REGS */void do_break (struct pt_regs *regs, unsigned long address,		    unsigned long error_code){	siginfo_t info; 	current->thread.trap_nr = TRAP_HWBKPT;	if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,			11, SIGSEGV) == NOTIFY_STOP)		return; 	if (debugger_break_match(regs))		return; 	/* Clear the breakpoint */	hw_breakpoint_disable(); 	/* Deliver the signal to userspace */	info.si_signo = SIGTRAP;	info.si_errno = 0;	info.si_code = TRAP_HWBKPT;	info.si_addr = (void __user *)address;	force_sig_info(SIGTRAP, &info, current);}#endif	/* CONFIG_PPC_ADV_DEBUG_REGS */ static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk); #ifdef CONFIG_PPC_ADV_DEBUG_REGS/* * Set the debug registers back to their default "safe" values. */static void set_debug_reg_defaults(struct thread_struct *thread){	thread->debug.iac1 = thread->debug.iac2 = 0;#if CONFIG_PPC_ADV_DEBUG_IACS > 2	thread->debug.iac3 = thread->debug.iac4 = 0;#endif	thread->debug.dac1 = thread->debug.dac2 = 0;#if CONFIG_PPC_ADV_DEBUG_DVCS > 0	thread->debug.dvc1 = thread->debug.dvc2 = 0;#endif	thread->debug.dbcr0 = 0;#ifdef CONFIG_BOOKE	/*	 * Force User/Supervisor bits to b11 (user-only MSR[PR]=1)	 */	thread->debug.dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US |			DBCR1_IAC3US | DBCR1_IAC4US;	/*	 * Force Data Address Compare User/Supervisor bits to be User-only	 * (0b11 MSR[PR]=1) and set all other bits in DBCR2 register to be 0.	 */	thread->debug.dbcr2 = DBCR2_DAC1US | DBCR2_DAC2US;#else	thread->debug.dbcr1 = 0;#endif} static void prime_debug_regs(struct debug_reg *debug){	/*	 * We could have inherited MSR_DE from userspace, since	 * it doesn't get cleared on exception entry.  Make sure	 * MSR_DE is clear before we enable any debug events.	 */	mtmsr(mfmsr() & ~MSR_DE); 	mtspr(SPRN_IAC1, debug->iac1);	mtspr(SPRN_IAC2, debug->iac2);#if CONFIG_PPC_ADV_DEBUG_IACS > 2	mtspr(SPRN_IAC3, debug->iac3);	mtspr(SPRN_IAC4, debug->iac4);#endif	mtspr(SPRN_DAC1, debug->dac1);	mtspr(SPRN_DAC2, debug->dac2);#if CONFIG_PPC_ADV_DEBUG_DVCS > 0	mtspr(SPRN_DVC1, debug->dvc1);	mtspr(SPRN_DVC2, debug->dvc2);#endif	mtspr(SPRN_DBCR0, debug->dbcr0);	mtspr(SPRN_DBCR1, debug->dbcr1);#ifdef CONFIG_BOOKE	mtspr(SPRN_DBCR2, debug->dbcr2);#endif}/* * Unless neither the old or new thread are making use of the * debug registers, set the debug registers from the values * stored in the new thread. */void switch_booke_debug_regs(struct debug_reg *new_debug){	if ((current->thread.debug.dbcr0 & DBCR0_IDM)		|| (new_debug->dbcr0 & DBCR0_IDM))			prime_debug_regs(new_debug);}EXPORT_SYMBOL_GPL(switch_booke_debug_regs);#else	/* !CONFIG_PPC_ADV_DEBUG_REGS */#ifndef CONFIG_HAVE_HW_BREAKPOINTstatic void set_debug_reg_defaults(struct thread_struct *thread){	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 */ #ifdef CONFIG_PPC_ADV_DEBUG_REGSstatic 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);	if (cpu_has_feature(CPU_FTR_DABRX))		mtspr(SPRN_DABRX, dabrx);	return 0;}#elsestatic 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;} void __set_breakpoint(struct arch_hw_breakpoint *brk)

Lines 1–500 of 1660.