diff options
Diffstat (limited to 'arch/powerpc/kernel/process.c')
| -rw-r--r-- | arch/powerpc/kernel/process.c | 409 | 
1 files changed, 306 insertions, 103 deletions
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 6f428da53e2..be99774d3f4 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -25,7 +25,6 @@  #include <linux/slab.h>  #include <linux/user.h>  #include <linux/elf.h> -#include <linux/init.h>  #include <linux/prctl.h>  #include <linux/init_task.h>  #include <linux/export.h> @@ -55,6 +54,7 @@  #ifdef CONFIG_PPC64  #include <asm/firmware.h>  #endif +#include <asm/code-patching.h>  #include <linux/kprobes.h>  #include <linux/kdebug.h> @@ -74,6 +74,48 @@ struct task_struct *last_task_used_vsx = NULL;  struct task_struct *last_task_used_spe = NULL;  #endif +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +void 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 @@ -102,13 +144,13 @@ void flush_fp_to_thread(struct task_struct *tsk)  			 */  			BUG_ON(tsk != current);  #endif -			giveup_fpu(tsk); +			giveup_fpu_maybe_transactional(tsk);  		}  		preempt_enable();  	}  }  EXPORT_SYMBOL_GPL(flush_fp_to_thread); -#endif +#endif /* CONFIG_PPC_FPU */  void enable_kernel_fp(void)  { @@ -116,11 +158,11 @@ void enable_kernel_fp(void)  #ifdef CONFIG_SMP  	if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) -		giveup_fpu(current); +		giveup_fpu_maybe_transactional(current);  	else  		giveup_fpu(NULL);	/* just enables FP for kernel */  #else -	giveup_fpu(last_task_used_math); +	giveup_fpu_maybe_transactional(last_task_used_math);  #endif /* CONFIG_SMP */  }  EXPORT_SYMBOL(enable_kernel_fp); @@ -132,11 +174,11 @@ void enable_kernel_altivec(void)  #ifdef CONFIG_SMP  	if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) -		giveup_altivec(current); +		giveup_altivec_maybe_transactional(current);  	else  		giveup_altivec_notask();  #else -	giveup_altivec(last_task_used_altivec); +	giveup_altivec_maybe_transactional(last_task_used_altivec);  #endif /* CONFIG_SMP */  }  EXPORT_SYMBOL(enable_kernel_altivec); @@ -153,7 +195,7 @@ void flush_altivec_to_thread(struct task_struct *tsk)  #ifdef CONFIG_SMP  			BUG_ON(tsk != current);  #endif -			giveup_altivec(tsk); +			giveup_altivec_maybe_transactional(tsk);  		}  		preempt_enable();  	} @@ -182,8 +224,8 @@ EXPORT_SYMBOL(enable_kernel_vsx);  void giveup_vsx(struct task_struct *tsk)  { -	giveup_fpu(tsk); -	giveup_altivec(tsk); +	giveup_fpu_maybe_transactional(tsk); +	giveup_altivec_maybe_transactional(tsk);  	__giveup_vsx(tsk);  } @@ -314,32 +356,32 @@ static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk);   */  static void set_debug_reg_defaults(struct thread_struct *thread)  { -	thread->iac1 = thread->iac2 = 0; +	thread->debug.iac1 = thread->debug.iac2 = 0;  #if CONFIG_PPC_ADV_DEBUG_IACS > 2 -	thread->iac3 = thread->iac4 = 0; +	thread->debug.iac3 = thread->debug.iac4 = 0;  #endif -	thread->dac1 = thread->dac2 = 0; +	thread->debug.dac1 = thread->debug.dac2 = 0;  #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 -	thread->dvc1 = thread->dvc2 = 0; +	thread->debug.dvc1 = thread->debug.dvc2 = 0;  #endif -	thread->dbcr0 = 0; +	thread->debug.dbcr0 = 0;  #ifdef CONFIG_BOOKE  	/*  	 * Force User/Supervisor bits to b11 (user-only MSR[PR]=1)  	 */ -	thread->dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US |	\ +	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->dbcr2 = DBCR2_DAC1US | DBCR2_DAC2US; +	thread->debug.dbcr2 = DBCR2_DAC1US | DBCR2_DAC2US;  #else -	thread->dbcr1 = 0; +	thread->debug.dbcr1 = 0;  #endif  } -static void prime_debug_regs(struct thread_struct *thread) +static void prime_debug_regs(struct debug_reg *debug)  {  	/*  	 * We could have inherited MSR_DE from userspace, since @@ -348,22 +390,22 @@ static void prime_debug_regs(struct thread_struct *thread)  	 */  	mtmsr(mfmsr() & ~MSR_DE); -	mtspr(SPRN_IAC1, thread->iac1); -	mtspr(SPRN_IAC2, thread->iac2); +	mtspr(SPRN_IAC1, debug->iac1); +	mtspr(SPRN_IAC2, debug->iac2);  #if CONFIG_PPC_ADV_DEBUG_IACS > 2 -	mtspr(SPRN_IAC3, thread->iac3); -	mtspr(SPRN_IAC4, thread->iac4); +	mtspr(SPRN_IAC3, debug->iac3); +	mtspr(SPRN_IAC4, debug->iac4);  #endif -	mtspr(SPRN_DAC1, thread->dac1); -	mtspr(SPRN_DAC2, thread->dac2); +	mtspr(SPRN_DAC1, debug->dac1); +	mtspr(SPRN_DAC2, debug->dac2);  #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 -	mtspr(SPRN_DVC1, thread->dvc1); -	mtspr(SPRN_DVC2, thread->dvc2); +	mtspr(SPRN_DVC1, debug->dvc1); +	mtspr(SPRN_DVC2, debug->dvc2);  #endif -	mtspr(SPRN_DBCR0, thread->dbcr0); -	mtspr(SPRN_DBCR1, thread->dbcr1); +	mtspr(SPRN_DBCR0, debug->dbcr0); +	mtspr(SPRN_DBCR1, debug->dbcr1);  #ifdef CONFIG_BOOKE -	mtspr(SPRN_DBCR2, thread->dbcr2); +	mtspr(SPRN_DBCR2, debug->dbcr2);  #endif  }  /* @@ -371,12 +413,13 @@ static void prime_debug_regs(struct thread_struct *thread)   * debug registers, set the debug registers from the values   * stored in the new thread.   */ -static void switch_booke_debug_regs(struct thread_struct *new_thread) +void switch_booke_debug_regs(struct debug_reg *new_debug)  { -	if ((current->thread.dbcr0 & DBCR0_IDM) -		|| (new_thread->dbcr0 & DBCR0_IDM)) -			prime_debug_regs(new_thread); +	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_BREAKPOINT  static void set_debug_reg_defaults(struct thread_struct *thread) @@ -453,14 +496,21 @@ static inline int set_dawr(struct arch_hw_breakpoint *brk)  	return 0;  } -int set_breakpoint(struct arch_hw_breakpoint *brk) +void __set_breakpoint(struct arch_hw_breakpoint *brk)  {  	__get_cpu_var(current_brk) = *brk;  	if (cpu_has_feature(CPU_FTR_DAWR)) -		return set_dawr(brk); +		set_dawr(brk); +	else +		set_dabr(brk); +} -	return set_dabr(brk); +void set_breakpoint(struct arch_hw_breakpoint *brk) +{ +	preempt_disable(); +	__set_breakpoint(brk); +	preempt_enable();  }  #ifdef CONFIG_PPC64 @@ -478,7 +528,48 @@ static inline bool hw_brk_match(struct arch_hw_breakpoint *a,  		return false;  	return true;  } +  #ifdef CONFIG_PPC_TRANSACTIONAL_MEM +static void tm_reclaim_thread(struct thread_struct *thr, +			      struct thread_info *ti, uint8_t cause) +{ +	unsigned long msr_diff = 0; + +	/* +	 * If FP/VSX registers have been already saved to the +	 * thread_struct, move them to the transact_fp array. +	 * We clear the TIF_RESTORE_TM bit since after the reclaim +	 * the thread will no longer be transactional. +	 */ +	if (test_ti_thread_flag(ti, TIF_RESTORE_TM)) { +		msr_diff = thr->tm_orig_msr & ~thr->regs->msr; +		if (msr_diff & MSR_FP) +			memcpy(&thr->transact_fp, &thr->fp_state, +			       sizeof(struct thread_fp_state)); +		if (msr_diff & MSR_VEC) +			memcpy(&thr->transact_vr, &thr->vr_state, +			       sizeof(struct thread_vr_state)); +		clear_ti_thread_flag(ti, TIF_RESTORE_TM); +		msr_diff &= MSR_FP | MSR_VEC | MSR_VSX | MSR_FE0 | MSR_FE1; +	} + +	tm_reclaim(thr, thr->regs->msr, cause); + +	/* Having done the reclaim, we now have the checkpointed +	 * FP/VSX values in the registers.  These might be valid +	 * even if we have previously called enable_kernel_fp() or +	 * flush_fp_to_thread(), so update thr->regs->msr to +	 * indicate their current validity. +	 */ +	thr->regs->msr |= msr_diff; +} + +void tm_reclaim_current(uint8_t cause) +{ +	tm_enable(); +	tm_reclaim_thread(¤t->thread, current_thread_info(), cause); +} +  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 @@ -501,9 +592,11 @@ static inline void tm_reclaim_task(struct task_struct *tsk)  	/* 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. +	 * FP & vector regs.  If the TIF_RESTORE_TM flag is set, +	 * tm_orig_msr is already set.  	 */ -	thr->tm_orig_msr = thr->regs->msr; +	if (!test_ti_thread_flag(task_thread_info(tsk), TIF_RESTORE_TM)) +		thr->tm_orig_msr = thr->regs->msr;  	TM_DEBUG("--- tm_reclaim on pid %d (NIP=%lx, "  		 "ccr=%lx, msr=%lx, trap=%lx)\n", @@ -511,7 +604,7 @@ static inline void tm_reclaim_task(struct task_struct *tsk)  		 thr->regs->ccr, thr->regs->msr,  		 thr->regs->trap); -	tm_reclaim(thr, thr->regs->msr, TM_CAUSE_RESCHED); +	tm_reclaim_thread(thr, task_thread_info(tsk), TM_CAUSE_RESCHED);  	TM_DEBUG("--- tm_reclaim on pid %d complete\n",  		 tsk->pid); @@ -525,6 +618,31 @@ out_and_saveregs:  	tm_save_sprs(thr);  } +extern void __tm_recheckpoint(struct thread_struct *thread, +			      unsigned long orig_msr); + +void tm_recheckpoint(struct thread_struct *thread, +		     unsigned long orig_msr) +{ +	unsigned long flags; + +	/* We really can't be interrupted here as the TEXASR registers can't +	 * change and later in the trecheckpoint code, we have a userspace R1. +	 * So let's hard disable over this region. +	 */ +	local_irq_save(flags); +	hard_irq_disable(); + +	/* The TM SPRs are restored here, so that TEXASR.FS can be set +	 * before the trecheckpoint and no explosion occurs. +	 */ +	tm_restore_sprs(thread); + +	__tm_recheckpoint(thread, orig_msr); + +	local_irq_restore(flags); +} +  static inline void tm_recheckpoint_new_task(struct task_struct *new)  {  	unsigned long msr; @@ -543,13 +661,10 @@ static inline void tm_recheckpoint_new_task(struct task_struct *new)  	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)) +	if (!MSR_TM_ACTIVE(new->thread.regs->msr)){ +		tm_restore_sprs(&new->thread);  		return; +	}  	msr = new->thread.tm_orig_msr;  	/* Recheckpoint to restore original checkpointed register state. */  	TM_DEBUG("*** tm_recheckpoint of pid %d " @@ -587,6 +702,43 @@ static inline void __switch_to_tm(struct task_struct *prev)  		tm_reclaim_task(prev);  	}  } + +/* + * This is called if we are on the way out to userspace and the + * TIF_RESTORE_TM flag is set.  It checks if we need to reload + * FP and/or vector state and does so if necessary. + * If userspace is inside a transaction (whether active or + * suspended) and FP/VMX/VSX instructions have ever been enabled + * inside that transaction, then we have to keep them enabled + * and keep the FP/VMX/VSX state loaded while ever the transaction + * continues.  The reason is that if we didn't, and subsequently + * got a FP/VMX/VSX unavailable interrupt inside a transaction, + * we don't know whether it's the same transaction, and thus we + * don't know which of the checkpointed state and the transactional + * state to use. + */ +void restore_tm_state(struct pt_regs *regs) +{ +	unsigned long msr_diff; + +	clear_thread_flag(TIF_RESTORE_TM); +	if (!MSR_TM_ACTIVE(regs->msr)) +		return; + +	msr_diff = current->thread.tm_orig_msr & ~regs->msr; +	msr_diff &= MSR_FP | MSR_VEC | MSR_VSX; +	if (msr_diff & MSR_FP) { +		fp_enable(); +		load_fp_state(¤t->thread.fp_state); +		regs->msr |= current->thread.fpexc_mode; +	} +	if (msr_diff & MSR_VEC) { +		vec_enable(); +		load_vr_state(¤t->thread.vr_state); +	} +	regs->msr |= msr_diff; +} +  #else  #define tm_recheckpoint_new_task(new)  #define __switch_to_tm(prev) @@ -596,21 +748,22 @@ struct task_struct *__switch_to(struct task_struct *prev,  	struct task_struct *new)  {  	struct thread_struct *new_thread, *old_thread; -	unsigned long flags;  	struct task_struct *last;  #ifdef CONFIG_PPC_BOOK3S_64  	struct ppc64_tlb_batch *batch;  #endif -	/* Back up the TAR across context switches. +	WARN_ON(!irqs_disabled()); + +	/* Back up the TAR and DSCR across context switches.  	 * Note that the TAR is not available for use in the kernel.  (To  	 * provide this, the TAR should be backed up/restored on exception  	 * entry/exit instead, and be in pt_regs.  FIXME, this should be in  	 * pt_regs anyway (for debug).) -	 * Save the TAR here before we do treclaim/trecheckpoint as these -	 * will change the TAR. +	 * Save the TAR and DSCR here before we do treclaim/trecheckpoint as +	 * these will change them.  	 */ -	save_tar(&prev->thread); +	save_early_sprs(&prev->thread);  	__switch_to_tm(prev); @@ -681,15 +834,15 @@ struct task_struct *__switch_to(struct task_struct *prev,  #endif /* CONFIG_SMP */  #ifdef CONFIG_PPC_ADV_DEBUG_REGS -	switch_booke_debug_regs(&new->thread); +	switch_booke_debug_regs(&new->thread.debug);  #else  /*   * For PPC_BOOK3S_64, we use the hw-breakpoint interfaces that would   * schedule DABR   */  #ifndef CONFIG_HAVE_HW_BREAKPOINT -	if (unlikely(hw_brk_match(&__get_cpu_var(current_brk), &new->thread.hw_brk))) -		set_breakpoint(&new->thread.hw_brk); +	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 @@ -721,8 +874,6 @@ struct task_struct *__switch_to(struct task_struct *prev,  	}  #endif /* CONFIG_PPC_BOOK3S_64 */ -	local_irq_save(flags); -  	/*  	 * We can't take a PMU exception inside _switch() since there is a  	 * window where the kernel stack SLB and the kernel stack are out @@ -742,8 +893,6 @@ struct task_struct *__switch_to(struct task_struct *prev,  	}  #endif /* CONFIG_PPC_BOOK3S_64 */ -	local_irq_restore(flags); -  	return last;  } @@ -860,17 +1009,21 @@ void show_regs(struct pt_regs * regs)  	printk("MSR: "REG" ", regs->msr);  	printbits(regs->msr, msr_bits);  	printk("  CR: %08lx  XER: %08lx\n", regs->ccr, regs->xer); -#ifdef CONFIG_PPC64 -	printk("SOFTE: %ld\n", regs->softe); -#endif  	trap = TRAP(regs);  	if ((regs->trap != 0xc00) && cpu_has_feature(CPU_FTR_CFAR)) -		printk("CFAR: "REG"\n", regs->orig_gpr3); -	if (trap == 0x300 || trap == 0x600) +		printk("CFAR: "REG" ", regs->orig_gpr3); +	if (trap == 0x200 || trap == 0x300 || trap == 0x600)  #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) -		printk("DEAR: "REG", ESR: "REG"\n", regs->dar, regs->dsisr); +		printk("DEAR: "REG" ESR: "REG" ", regs->dar, regs->dsisr);  #else -		printk("DAR: "REG", DSISR: %08lx\n", regs->dar, regs->dsisr); +		printk("DAR: "REG" DSISR: %08lx ", regs->dar, regs->dsisr); +#endif +#ifdef CONFIG_PPC64 +	printk("SOFTE: %ld ", regs->softe); +#endif +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +	if (MSR_TM_ACTIVE(regs->msr)) +		printk("\nPACATMSCRATCH: %016llx ", get_paca()->tm_scratch);  #endif  	for (i = 0;  i < 32;  i++) { @@ -889,9 +1042,6 @@ 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); @@ -928,6 +1078,15 @@ 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); +	/* +	 * Flush TM state out so we can copy it.  __switch_to_tm() does this +	 * flush but it removes the checkpointed state from the current CPU and +	 * transitions the CPU out of TM mode.  Hence we need to call +	 * tm_recheckpoint_new_task() (on the same task) to restore the +	 * checkpointed state back and the TM mode. +	 */ +	__switch_to_tm(src); +	tm_recheckpoint_new_task(src);  	*dst = *src; @@ -957,7 +1116,9 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,  		struct thread_info *ti = (void *)task_stack_page(p);  		memset(childregs, 0, sizeof(struct pt_regs));  		childregs->gpr[1] = sp + sizeof(struct pt_regs); -		childregs->gpr[14] = usp;	/* function */ +		/* function */ +		if (usp) +			childregs->gpr[14] = ppc_function_entry((void *)usp);  #ifdef CONFIG_PPC64  		clear_tsk_thread_flag(p, TIF_32BIT);  		childregs->softe = 1; @@ -1000,13 +1161,19 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,  	kregs = (struct pt_regs *) sp;  	sp -= STACK_FRAME_OVERHEAD;  	p->thread.ksp = sp; +#ifdef CONFIG_PPC32  	p->thread.ksp_limit = (unsigned long)task_stack_page(p) +  				_ALIGN_UP(sizeof(struct thread_info), 16); - +#endif  #ifdef CONFIG_HAVE_HW_BREAKPOINT  	p->thread.ptrace_bps[0] = NULL;  #endif +	p->thread.fp_save_area = NULL; +#ifdef CONFIG_ALTIVEC +	p->thread.vr_save_area = NULL; +#endif +  #ifdef CONFIG_PPC_STD_MMU_64  	if (mmu_has_feature(MMU_FTR_SLB)) {  		unsigned long sp_vsid; @@ -1030,17 +1197,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,  	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  -	 * pointers.  The function (ret_from_except) is actually a pointer -	 * to the TOC entry.  The first entry is a pointer to the actual -	 * function. -	 */ -#ifdef CONFIG_PPC64 -	kregs->nip = *((unsigned long *)f); -#else -	kregs->nip = (unsigned long)f; -#endif +	kregs->nip = ppc_function_entry(f);  	return 0;  } @@ -1082,25 +1239,45 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)  	regs->msr = MSR_USER;  #else  	if (!is_32bit_task()) { -		unsigned long entry, toc; +		unsigned long entry; -		/* start is a relocated pointer to the function descriptor for -		 * the elf _start routine.  The first entry in the function -		 * descriptor is the entry address of _start and the second -		 * entry is the TOC value we need to use. -		 */ -		__get_user(entry, (unsigned long __user *)start); -		__get_user(toc, (unsigned long __user *)start+1); +		if (is_elf2_task()) { +			/* Look ma, no function descriptors! */ +			entry = start; -		/* Check whether the e_entry function descriptor entries -		 * need to be relocated before we can use them. -		 */ -		if (load_addr != 0) { -			entry += load_addr; -			toc   += load_addr; +			/* +			 * Ulrich says: +			 *   The latest iteration of the ABI requires that when +			 *   calling a function (at its global entry point), +			 *   the caller must ensure r12 holds the entry point +			 *   address (so that the function can quickly +			 *   establish addressability). +			 */ +			regs->gpr[12] = start; +			/* Make sure that's restored on entry to userspace. */ +			set_thread_flag(TIF_RESTOREALL); +		} else { +			unsigned long toc; + +			/* start is a relocated pointer to the function +			 * descriptor for the elf _start routine.  The first +			 * entry in the function descriptor is the entry +			 * address of _start and the second entry is the TOC +			 * value we need to use. +			 */ +			__get_user(entry, (unsigned long __user *)start); +			__get_user(toc, (unsigned long __user *)start+1); + +			/* Check whether the e_entry function descriptor entries +			 * need to be relocated before we can use them. +			 */ +			if (load_addr != 0) { +				entry += load_addr; +				toc   += load_addr; +			} +			regs->gpr[2] = toc;  		}  		regs->nip = entry; -		regs->gpr[2] = toc;  		regs->msr = MSR_USER64;  	} else {  		regs->nip = start; @@ -1112,12 +1289,12 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)  #ifdef CONFIG_VSX  	current->thread.used_vsr = 0;  #endif -	memset(current->thread.fpr, 0, sizeof(current->thread.fpr)); -	current->thread.fpscr.val = 0; +	memset(¤t->thread.fp_state, 0, sizeof(current->thread.fp_state)); +	current->thread.fp_save_area = NULL;  #ifdef CONFIG_ALTIVEC -	memset(current->thread.vr, 0, sizeof(current->thread.vr)); -	memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr)); -	current->thread.vscr.u[3] = 0x00010000; /* Java mode disabled */ +	memset(¤t->thread.vr_state, 0, sizeof(current->thread.vr_state)); +	current->thread.vr_state.vscr.u[3] = 0x00010000; /* Java mode disabled */ +	current->thread.vr_save_area = NULL;  	current->thread.vrsave = 0;  	current->thread.used_vr = 0;  #endif /* CONFIG_ALTIVEC */ @@ -1150,6 +1327,19 @@ int set_fpexc_mode(struct task_struct *tsk, unsigned int val)  	if (val & PR_FP_EXC_SW_ENABLE) {  #ifdef CONFIG_SPE  		if (cpu_has_feature(CPU_FTR_SPE)) { +			/* +			 * When the sticky exception bits are set +			 * directly by userspace, it must call prctl +			 * with PR_GET_FPEXC (with PR_FP_EXC_SW_ENABLE +			 * in the existing prctl settings) or +			 * PR_SET_FPEXC (with PR_FP_EXC_SW_ENABLE in +			 * the bits being set).  <fenv.h> functions +			 * saving and restoring the whole +			 * floating-point environment need to do so +			 * anyway to restore the prctl settings from +			 * the saved environment. +			 */ +			tsk->thread.spefscr_last = mfspr(SPRN_SPEFSCR);  			tsk->thread.fpexc_mode = val &  				(PR_FP_EXC_SW_ENABLE | PR_FP_ALL_EXCEPT);  			return 0; @@ -1181,9 +1371,22 @@ int get_fpexc_mode(struct task_struct *tsk, unsigned long adr)  	if (tsk->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE)  #ifdef CONFIG_SPE -		if (cpu_has_feature(CPU_FTR_SPE)) +		if (cpu_has_feature(CPU_FTR_SPE)) { +			/* +			 * When the sticky exception bits are set +			 * directly by userspace, it must call prctl +			 * with PR_GET_FPEXC (with PR_FP_EXC_SW_ENABLE +			 * in the existing prctl settings) or +			 * PR_SET_FPEXC (with PR_FP_EXC_SW_ENABLE in +			 * the bits being set).  <fenv.h> functions +			 * saving and restoring the whole +			 * floating-point environment need to do so +			 * anyway to restore the prctl settings from +			 * the saved environment. +			 */ +			tsk->thread.spefscr_last = mfspr(SPRN_SPEFSCR);  			val = tsk->thread.fpexc_mode; -		else +		} else  			return -EINVAL;  #else  		return -EINVAL;  | 
