/* * 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 <linux/elf-randomize.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 <asm/exec.h>#include <asm/livepatch.h>#include <asm/cpu_has_feature.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_MEMstatic 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); void 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);} void __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_FPUvoid __giveup_fpu(struct task_struct *tsk){ unsigned long msr; save_fpu(tsk); msr = tsk->thread.regs->msr; msr &= ~MSR_FP;#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){ WARN_ON(preemptible()); msr_check_and_set(MSR_FP); if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) { check_if_tm_restore_required(current); __giveup_fpu(current); }}EXPORT_SYMBOL(enable_kernel_fp); static int restore_fp(struct task_struct *tsk) { if (tsk->thread.load_fp) { load_fp_state(¤t->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){ WARN_ON(preemptible()); msr_check_and_set(MSR_VEC); if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) { check_if_tm_restore_required(current); __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){ if (tsk->thread.regs->msr & MSR_FP) __giveup_fpu(tsk); if (tsk->thread.regs->msr & MSR_VEC) __giveup_altivec(tsk); tsk->thread.regs->msr &= ~MSR_VSX;} 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);} static void save_vsx(struct task_struct *tsk){ if (tsk->thread.regs->msr & MSR_FP) save_fpu(tsk); if (tsk->thread.regs->msr & MSR_VEC) save_altivec(tsk);} void enable_kernel_vsx(void){ WARN_ON(preemptible()); msr_check_and_set(MSR_FP|MSR_VEC|MSR_VSX); if (current->thread.regs && (current->thread.regs->msr & MSR_VSX)) { check_if_tm_restore_required(current); if (current->thread.regs->msr & MSR_FP) __giveup_fpu(current); if (current->thread.regs->msr & MSR_VEC) __giveup_altivec(current); __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) { 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; }static inline void save_vsx(struct task_struct *tsk) { }#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; usermsr = tsk->thread.regs->msr; if ((usermsr & msr_all_available) == 0) return; msr_check_and_set(msr_all_available); #ifdef CONFIG_PPC_FPU if (usermsr & MSR_FP) __giveup_fpu(tsk);#endif#ifdef CONFIG_ALTIVEC if (usermsr & MSR_VEC) __giveup_altivec(tsk);#endif#ifdef CONFIG_VSX if (usermsr & MSR_VSX) __giveup_vsx(tsk);#endif#ifdef CONFIG_SPE if (usermsr & MSR_SPE) __giveup_spe(tsk);#endif msr_check_and_clear(msr_all_available);}EXPORT_SYMBOL(giveup_all); void restore_math(struct pt_regs *regs){ unsigned long msr; if (!current->thread.load_fp && !loadvec(current->thread)) return; msr = regs->msr; msr_check_and_set(msr_all_available); /* * Only reload if the bit is not set in the user MSR, the bit BEING set * indicates that the registers are hot */ if ((!(msr & MSR_FP)) && restore_fp(current)) msr |= MSR_FP | current->thread.fpexc_mode; if ((!(msr & MSR_VEC)) && restore_altivec(current)) msr |= MSR_VEC; if ((msr & (MSR_FP | MSR_VEC)) == (MSR_FP | MSR_VEC) && restore_vsx(current)) { msr |= MSR_VSX; } msr_check_and_clear(msr_all_available); regs->msr = msr;} void save_all(struct task_struct *tsk){ unsigned long usermsr; if (!tsk->thread.regs) return; usermsr = tsk->thread.regs->msr;Lines 1–500 of 1974. Load more
/* * 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 <linux/elf-randomize.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 <asm/exec.h> #include <asm/livepatch.h> #include <asm/cpu_has_feature.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 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); } } #else static 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); void 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); } void __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_FPU void __giveup_fpu(struct task_struct *tsk) { unsigned long msr; save_fpu(tsk); msr = tsk->thread.regs->msr; msr &= ~MSR_FP; #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) { WARN_ON(preemptible()); msr_check_and_set(MSR_FP); if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) { check_if_tm_restore_required(current); __giveup_fpu(current); } } EXPORT_SYMBOL(enable_kernel_fp); static int restore_fp(struct task_struct *tsk) { if (tsk->thread.load_fp) { load_fp_state(¤t->thread.fp_state); current->thread.load_fp++; return 1; } return 0; } #else static 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) { WARN_ON(preemptible()); msr_check_and_set(MSR_VEC); if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) { check_if_tm_restore_required(current); __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) 0 static inline int restore_altivec(struct task_struct *tsk) { return 0; } #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSX static void __giveup_vsx(struct task_struct *tsk) { if (tsk->thread.regs->msr & MSR_FP) __giveup_fpu(tsk); if (tsk->thread.regs->msr & MSR_VEC) __giveup_altivec(tsk); tsk->thread.regs->msr &= ~MSR_VSX; } 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); } static void save_vsx(struct task_struct *tsk) { if (tsk->thread.regs->msr & MSR_FP) save_fpu(tsk); if (tsk->thread.regs->msr & MSR_VEC) save_altivec(tsk); } void enable_kernel_vsx(void) { WARN_ON(preemptible()); msr_check_and_set(MSR_FP|MSR_VEC|MSR_VSX); if (current->thread.regs && (current->thread.regs->msr & MSR_VSX)) { check_if_tm_restore_required(current); if (current->thread.regs->msr & MSR_FP) __giveup_fpu(current); if (current->thread.regs->msr & MSR_VEC) __giveup_altivec(current); __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) { 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; } #else static inline int restore_vsx(struct task_struct *tsk) { return 0; } static inline void save_vsx(struct task_struct *tsk) { } #endif /* CONFIG_VSX */ #ifdef CONFIG_SPE void 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; usermsr = tsk->thread.regs->msr; if ((usermsr & msr_all_available) == 0) return; msr_check_and_set(msr_all_available); #ifdef CONFIG_PPC_FPU if (usermsr & MSR_FP) __giveup_fpu(tsk); #endif #ifdef CONFIG_ALTIVEC if (usermsr & MSR_VEC) __giveup_altivec(tsk); #endif #ifdef CONFIG_VSX if (usermsr & MSR_VSX) __giveup_vsx(tsk); #endif #ifdef CONFIG_SPE if (usermsr & MSR_SPE) __giveup_spe(tsk); #endif msr_check_and_clear(msr_all_available); } EXPORT_SYMBOL(giveup_all); void restore_math(struct pt_regs *regs) { unsigned long msr; if (!current->thread.load_fp && !loadvec(current->thread)) return; msr = regs->msr; msr_check_and_set(msr_all_available); /* * Only reload if the bit is not set in the user MSR, the bit BEING set * indicates that the registers are hot */ if ((!(msr & MSR_FP)) && restore_fp(current)) msr |= MSR_FP | current->thread.fpexc_mode; if ((!(msr & MSR_VEC)) && restore_altivec(current)) msr |= MSR_VEC; if ((msr & (MSR_FP | MSR_VEC)) == (MSR_FP | MSR_VEC) && restore_vsx(current)) { msr |= MSR_VSX; } msr_check_and_clear(msr_all_available); regs->msr = msr; } void save_all(struct task_struct *tsk) { unsigned long usermsr; if (!tsk->thread.regs) return; usermsr = tsk->thread.regs->msr;