2190 lines · 54.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *  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) */ #include <linux/errno.h>#include <linux/sched.h>#include <linux/sched/debug.h>#include <linux/sched/task.h>#include <linux/sched/task_stack.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 <linux/elf-randomize.h>#include <linux/pkeys.h>#include <linux/seq_buf.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>#include <asm/hw_irq.h>#endif#include <asm/code-patching.h>#include <asm/exec.h>#include <asm/livepatch.h>#include <asm/cpu_has_feature.h>#include <asm/asm-prototypes.h>#include <asm/stacktrace.h>#include <asm/hw_breakpoint.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); #ifdef CONFIG_PPC_TRANSACTIONAL_MEM/* * Are we running in "Suspend disabled" mode? If so we have to block any * sigreturn that would get us into suspended state, and we also warn in some * other paths that we should never reach with suspend disabled. */bool tm_suspend_disabled __ro_after_init = false; static void check_if_tm_restore_required(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.ckpt_regs.msr = tsk->thread.regs->msr;		set_thread_flag(TIF_RESTORE_TM);	}} #elsestatic inline void check_if_tm_restore_required(struct task_struct *tsk) { }#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ bool strict_msr_control;EXPORT_SYMBOL(strict_msr_control); static int __init enable_strict_msr_control(char *str){	strict_msr_control = true;	pr_info("Enabling strict facility control\n"); 	return 0;}early_param("ppc_strict_facility_enable", enable_strict_msr_control); /* notrace because it's called by restore_math */unsigned long notrace msr_check_and_set(unsigned long bits){	unsigned long oldmsr = mfmsr();	unsigned long newmsr; 	newmsr = oldmsr | bits; #ifdef CONFIG_VSX	if (cpu_has_feature(CPU_FTR_VSX) && (bits & MSR_FP))		newmsr |= MSR_VSX;#endif 	if (oldmsr != newmsr)		mtmsr_isync(newmsr); 	return newmsr;}EXPORT_SYMBOL_GPL(msr_check_and_set); /* notrace because it's called by restore_math */void notrace __msr_check_and_clear(unsigned long bits){	unsigned long oldmsr = mfmsr();	unsigned long newmsr; 	newmsr = oldmsr & ~bits; #ifdef CONFIG_VSX	if (cpu_has_feature(CPU_FTR_VSX) && (bits & MSR_FP))		newmsr &= ~MSR_VSX;#endif 	if (oldmsr != newmsr)		mtmsr_isync(newmsr);}EXPORT_SYMBOL(__msr_check_and_clear); #ifdef CONFIG_PPC_FPUstatic void __giveup_fpu(struct task_struct *tsk){	unsigned long msr; 	save_fpu(tsk);	msr = tsk->thread.regs->msr;	msr &= ~(MSR_FP|MSR_FE0|MSR_FE1);#ifdef CONFIG_VSX	if (cpu_has_feature(CPU_FTR_VSX))		msr &= ~MSR_VSX;#endif	tsk->thread.regs->msr = msr;} void giveup_fpu(struct task_struct *tsk){	check_if_tm_restore_required(tsk); 	msr_check_and_set(MSR_FP);	__giveup_fpu(tsk);	msr_check_and_clear(MSR_FP);}EXPORT_SYMBOL(giveup_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) {			/*			 * 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,			 * there is something wrong if a stopped child appears			 * to still have its FP state in the CPU registers.			 */			BUG_ON(tsk != current);			giveup_fpu(tsk);		}		preempt_enable();	}}EXPORT_SYMBOL_GPL(flush_fp_to_thread); void enable_kernel_fp(void){	unsigned long cpumsr; 	WARN_ON(preemptible()); 	cpumsr = msr_check_and_set(MSR_FP); 	if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) {		check_if_tm_restore_required(current);		/*		 * If a thread has already been reclaimed then the		 * checkpointed registers are on the CPU but have definitely		 * been saved by the reclaim code. Don't need to and *cannot*		 * giveup as this would save  to the 'live' structure not the		 * checkpointed structure.		 */		if (!MSR_TM_ACTIVE(cpumsr) &&		     MSR_TM_ACTIVE(current->thread.regs->msr))			return;		__giveup_fpu(current);	}}EXPORT_SYMBOL(enable_kernel_fp); static int restore_fp(struct task_struct *tsk){	if (tsk->thread.load_fp) {		load_fp_state(&current->thread.fp_state);		current->thread.load_fp++;		return 1;	}	return 0;}#elsestatic int restore_fp(struct task_struct *tsk) { return 0; }#endif /* CONFIG_PPC_FPU */ #ifdef CONFIG_ALTIVEC#define loadvec(thr) ((thr).load_vec) static void __giveup_altivec(struct task_struct *tsk){	unsigned long msr; 	save_altivec(tsk);	msr = tsk->thread.regs->msr;	msr &= ~MSR_VEC;#ifdef CONFIG_VSX	if (cpu_has_feature(CPU_FTR_VSX))		msr &= ~MSR_VSX;#endif	tsk->thread.regs->msr = msr;} void giveup_altivec(struct task_struct *tsk){	check_if_tm_restore_required(tsk); 	msr_check_and_set(MSR_VEC);	__giveup_altivec(tsk);	msr_check_and_clear(MSR_VEC);}EXPORT_SYMBOL(giveup_altivec); void enable_kernel_altivec(void){	unsigned long cpumsr; 	WARN_ON(preemptible()); 	cpumsr = msr_check_and_set(MSR_VEC); 	if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) {		check_if_tm_restore_required(current);		/*		 * If a thread has already been reclaimed then the		 * checkpointed registers are on the CPU but have definitely		 * been saved by the reclaim code. Don't need to and *cannot*		 * giveup as this would save  to the 'live' structure not the		 * checkpointed structure.		 */		if (!MSR_TM_ACTIVE(cpumsr) &&		     MSR_TM_ACTIVE(current->thread.regs->msr))			return;		__giveup_altivec(current);	}}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) {			BUG_ON(tsk != current);			giveup_altivec(tsk);		}		preempt_enable();	}}EXPORT_SYMBOL_GPL(flush_altivec_to_thread); static int restore_altivec(struct task_struct *tsk){	if (cpu_has_feature(CPU_FTR_ALTIVEC) && (tsk->thread.load_vec)) {		load_vr_state(&tsk->thread.vr_state);		tsk->thread.used_vr = 1;		tsk->thread.load_vec++; 		return 1;	}	return 0;}#else#define loadvec(thr) 0static inline int restore_altivec(struct task_struct *tsk) { return 0; }#endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSXstatic void __giveup_vsx(struct task_struct *tsk){	unsigned long msr = tsk->thread.regs->msr; 	/*	 * We should never be ssetting MSR_VSX without also setting	 * MSR_FP and MSR_VEC	 */	WARN_ON((msr & MSR_VSX) && !((msr & MSR_FP) && (msr & MSR_VEC))); 	/* __giveup_fpu will clear MSR_VSX */	if (msr & MSR_FP)		__giveup_fpu(tsk);	if (msr & MSR_VEC)		__giveup_altivec(tsk);} static void giveup_vsx(struct task_struct *tsk){	check_if_tm_restore_required(tsk); 	msr_check_and_set(MSR_FP|MSR_VEC|MSR_VSX);	__giveup_vsx(tsk);	msr_check_and_clear(MSR_FP|MSR_VEC|MSR_VSX);} void enable_kernel_vsx(void){	unsigned long cpumsr; 	WARN_ON(preemptible()); 	cpumsr = msr_check_and_set(MSR_FP|MSR_VEC|MSR_VSX); 	if (current->thread.regs &&	    (current->thread.regs->msr & (MSR_VSX|MSR_VEC|MSR_FP))) {		check_if_tm_restore_required(current);		/*		 * If a thread has already been reclaimed then the		 * checkpointed registers are on the CPU but have definitely		 * been saved by the reclaim code. Don't need to and *cannot*		 * giveup as this would save  to the 'live' structure not the		 * checkpointed structure.		 */		if (!MSR_TM_ACTIVE(cpumsr) &&		     MSR_TM_ACTIVE(current->thread.regs->msr))			return;		__giveup_vsx(current);	}}EXPORT_SYMBOL(enable_kernel_vsx); void flush_vsx_to_thread(struct task_struct *tsk){	if (tsk->thread.regs) {		preempt_disable();		if (tsk->thread.regs->msr & (MSR_VSX|MSR_VEC|MSR_FP)) {			BUG_ON(tsk != current);			giveup_vsx(tsk);		}		preempt_enable();	}}EXPORT_SYMBOL_GPL(flush_vsx_to_thread); static int restore_vsx(struct task_struct *tsk){	if (cpu_has_feature(CPU_FTR_VSX)) {		tsk->thread.used_vsr = 1;		return 1;	} 	return 0;}#elsestatic inline int restore_vsx(struct task_struct *tsk) { return 0; }#endif /* CONFIG_VSX */ #ifdef CONFIG_SPEvoid giveup_spe(struct task_struct *tsk){	check_if_tm_restore_required(tsk); 	msr_check_and_set(MSR_SPE);	__giveup_spe(tsk);	msr_check_and_clear(MSR_SPE);}EXPORT_SYMBOL(giveup_spe); void enable_kernel_spe(void){	WARN_ON(preemptible()); 	msr_check_and_set(MSR_SPE); 	if (current->thread.regs && (current->thread.regs->msr & MSR_SPE)) {		check_if_tm_restore_required(current);		__giveup_spe(current);	}}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) {			BUG_ON(tsk != current);			tsk->thread.spefscr = mfspr(SPRN_SPEFSCR);			giveup_spe(tsk);		}		preempt_enable();	}}#endif /* CONFIG_SPE */ static unsigned long msr_all_available; static int __init init_msr_all_available(void){#ifdef CONFIG_PPC_FPU	msr_all_available |= MSR_FP;#endif#ifdef CONFIG_ALTIVEC	if (cpu_has_feature(CPU_FTR_ALTIVEC))		msr_all_available |= MSR_VEC;#endif#ifdef CONFIG_VSX	if (cpu_has_feature(CPU_FTR_VSX))		msr_all_available |= MSR_VSX;#endif#ifdef CONFIG_SPE	if (cpu_has_feature(CPU_FTR_SPE))		msr_all_available |= MSR_SPE;#endif 	return 0;}early_initcall(init_msr_all_available); void giveup_all(struct task_struct *tsk){	unsigned long usermsr; 	if (!tsk->thread.regs)		return; 	check_if_tm_restore_required(tsk); 	usermsr = tsk->thread.regs->msr; 	if ((usermsr & msr_all_available) == 0)		return; 	msr_check_and_set(msr_all_available); 	WARN_ON((usermsr & MSR_VSX) && !((usermsr & MSR_FP) && (usermsr & MSR_VEC))); #ifdef CONFIG_PPC_FPU	if (usermsr & MSR_FP)		__giveup_fpu(tsk);#endif

Lines 1–500 of 2190.