/* * 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/init.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 <asm/pgtable.h>#include <asm/uaccess.h>#include <asm/system.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/syscalls.h>#ifdef CONFIG_PPC64#include <asm/firmware.h>#endif#include <linux/kprobes.h>#include <linux/kdebug.h> 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 /* * 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(tsk); } preempt_enable(); }}EXPORT_SYMBOL_GPL(flush_fp_to_thread); void enable_kernel_fp(void){ WARN_ON(preemptible()); #ifdef CONFIG_SMP if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) giveup_fpu(current); else giveup_fpu(NULL); /* just enables FP for kernel */#else giveup_fpu(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(current); else giveup_altivec(NULL); /* just enable AltiVec for kernel - force */#else giveup_altivec(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(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(tsk); giveup_altivec(tsk); __giveup_vsx(tsk);} 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; 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_dabr(struct pt_regs *regs, unsigned long address, unsigned long error_code){ siginfo_t info; if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code, 11, SIGSEGV) == NOTIFY_STOP) return; if (debugger_dabr_match(regs)) return; /* Clear the DABR */ set_dabr(0); /* 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(unsigned long, current_dabr); #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->iac1 = thread->iac2 = 0;#if CONFIG_PPC_ADV_DEBUG_IACS > 2 thread->iac3 = thread->iac4 = 0;#endif thread->dac1 = thread->dac2 = 0;#if CONFIG_PPC_ADV_DEBUG_DVCS > 0 thread->dvc1 = thread->dvc2 = 0;#endif thread->dbcr0 = 0;#ifdef CONFIG_BOOKE /* * Force User/Supervisor bits to b11 (user-only MSR[PR]=1) */ thread->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;#else thread->dbcr1 = 0;#endif} static void prime_debug_regs(struct thread_struct *thread){ mtspr(SPRN_IAC1, thread->iac1); mtspr(SPRN_IAC2, thread->iac2);#if CONFIG_PPC_ADV_DEBUG_IACS > 2 mtspr(SPRN_IAC3, thread->iac3); mtspr(SPRN_IAC4, thread->iac4);#endif mtspr(SPRN_DAC1, thread->dac1); mtspr(SPRN_DAC2, thread->dac2);#if CONFIG_PPC_ADV_DEBUG_DVCS > 0 mtspr(SPRN_DVC1, thread->dvc1); mtspr(SPRN_DVC2, thread->dvc2);#endif mtspr(SPRN_DBCR0, thread->dbcr0); mtspr(SPRN_DBCR1, thread->dbcr1);#ifdef CONFIG_BOOKE mtspr(SPRN_DBCR2, thread->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. */static void switch_booke_debug_regs(struct thread_struct *new_thread){ if ((current->thread.dbcr0 & DBCR0_IDM) || (new_thread->dbcr0 & DBCR0_IDM)) prime_debug_regs(new_thread);}#else /* !CONFIG_PPC_ADV_DEBUG_REGS */#ifndef CONFIG_HAVE_HW_BREAKPOINTstatic void set_debug_reg_defaults(struct thread_struct *thread){ if (thread->dabr) { thread->dabr = 0; set_dabr(0); }}#endif /* !CONFIG_HAVE_HW_BREAKPOINT */#endif /* CONFIG_PPC_ADV_DEBUG_REGS */ int set_dabr(unsigned long dabr){ __get_cpu_var(current_dabr) = dabr; if (ppc_md.set_dabr) return ppc_md.set_dabr(dabr); /* XXX should we have a CPU_FTR_HAS_DABR ? */#ifdef CONFIG_PPC_ADV_DEBUG_REGS mtspr(SPRN_DAC1, dabr);#ifdef CONFIG_PPC_47x isync();#endif#elif defined(CONFIG_PPC_BOOK3S) mtspr(SPRN_DABR, dabr);#endif return 0;} #ifdef CONFIG_PPC64DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);#endif 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 #ifdef CONFIG_SMP /* avoid complexity of lazy save/restore of fpu * by just saving it every time we switch out if * this task used the fpu during the last quantum. * * If it tries to use the fpu again, it'll trap and * reload its fp regs. So we don't have to do a restore * every switch, just a save. * -- Cort */ if (prev->thread.regs && (prev->thread.regs->msr & MSR_FP)) giveup_fpu(prev);#ifdef CONFIG_ALTIVEC /* * If the previous thread used altivec in the last quantum * (thus changing altivec regs) then save them. * We used to check the VRSAVE register but not all apps * set it, so we don't rely on it now (and in fact we need * to save & restore VSCR even if VRSAVE == 0). -- paulus * * On SMP we always save/restore altivec regs just to avoid the * complexity of changing processors. * -- Cort */ if (prev->thread.regs && (prev->thread.regs->msr & MSR_VEC)) giveup_altivec(prev);#endif /* CONFIG_ALTIVEC */#ifdef CONFIG_VSX if (prev->thread.regs && (prev->thread.regs->msr & MSR_VSX)) /* VMX and FPU registers are already save here */ __giveup_vsx(prev);#endif /* CONFIG_VSX */#ifdef CONFIG_SPE /* * If the previous thread used spe in the last quantum * (thus changing spe regs) then save them. * * On SMP we always save/restore spe regs just to avoid the * complexity of changing processors. */ if ((prev->thread.regs && (prev->thread.regs->msr & MSR_SPE))) giveup_spe(prev);#endif /* CONFIG_SPE */ #else /* CONFIG_SMP */#ifdef CONFIG_ALTIVEC /* Avoid the trap. On smp this this never happens since * we don't set last_task_used_altivec -- Cort */ if (new->thread.regs && last_task_used_altivec == new) new->thread.regs->msr |= MSR_VEC;#endif /* CONFIG_ALTIVEC */#ifdef CONFIG_VSX if (new->thread.regs && last_task_used_vsx == new) new->thread.regs->msr |= MSR_VSX;#endif /* CONFIG_VSX */#ifdef CONFIG_SPE /* Avoid the trap. On smp this this never happens since * we don't set last_task_used_spe */ if (new->thread.regs && last_task_used_spe == new) new->thread.regs->msr |= MSR_SPE;#endif /* CONFIG_SPE */ #endif /* CONFIG_SMP */ #ifdef CONFIG_PPC_ADV_DEBUG_REGS switch_booke_debug_regs(&new->thread);#else/* * For PPC_BOOK3S_64, we use the hw-breakpoint interfaces that would * schedule DABR */#ifndef CONFIG_HAVE_HW_BREAKPOINT if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) set_dabr(new->thread.dabr);#endif /* CONFIG_HAVE_HW_BREAKPOINT */#endif new_thread = &new->thread; old_thread = ¤t->thread; #ifdef CONFIG_PPC64 /* * Collect processor utilization data per process */ if (firmware_has_feature(FW_FEATURE_SPLPAR)) { struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array); long unsigned start_tb, current_tb; start_tb = old_thread->start_tb; cu->current_tb = current_tb = mfspr(SPRN_PURR); old_thread->accum_tb += (current_tb - start_tb); new_thread->start_tb = current_tb; }Lines 1–500 of 1336. 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/init.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 <asm/pgtable.h> #include <asm/uaccess.h> #include <asm/system.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/syscalls.h> #ifdef CONFIG_PPC64 #include <asm/firmware.h> #endif #include <linux/kprobes.h> #include <linux/kdebug.h> extern unsigned long _get_SP(void); #ifndef CONFIG_SMP struct 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 /* * 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(tsk); } preempt_enable(); } } EXPORT_SYMBOL_GPL(flush_fp_to_thread); void enable_kernel_fp(void) { WARN_ON(preemptible()); #ifdef CONFIG_SMP if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) giveup_fpu(current); else giveup_fpu(NULL); /* just enables FP for kernel */ #else giveup_fpu(last_task_used_math); #endif /* CONFIG_SMP */ } EXPORT_SYMBOL(enable_kernel_fp); #ifdef CONFIG_ALTIVEC void enable_kernel_altivec(void) { WARN_ON(preemptible()); #ifdef CONFIG_SMP if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) giveup_altivec(current); else giveup_altivec(NULL); /* just enable AltiVec for kernel - force */ #else giveup_altivec(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(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(tsk); giveup_altivec(tsk); __giveup_vsx(tsk); } 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_REGS void do_send_trap(struct pt_regs *regs, unsigned long address, unsigned long error_code, int signal_code, int breakpt) { siginfo_t info; 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_dabr(struct pt_regs *regs, unsigned long address, unsigned long error_code) { siginfo_t info; if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code, 11, SIGSEGV) == NOTIFY_STOP) return; if (debugger_dabr_match(regs)) return; /* Clear the DABR */ set_dabr(0); /* 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(unsigned long, current_dabr); #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->iac1 = thread->iac2 = 0; #if CONFIG_PPC_ADV_DEBUG_IACS > 2 thread->iac3 = thread->iac4 = 0; #endif thread->dac1 = thread->dac2 = 0; #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 thread->dvc1 = thread->dvc2 = 0; #endif thread->dbcr0 = 0; #ifdef CONFIG_BOOKE /* * Force User/Supervisor bits to b11 (user-only MSR[PR]=1) */ thread->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; #else thread->dbcr1 = 0; #endif } static void prime_debug_regs(struct thread_struct *thread) { mtspr(SPRN_IAC1, thread->iac1); mtspr(SPRN_IAC2, thread->iac2); #if CONFIG_PPC_ADV_DEBUG_IACS > 2 mtspr(SPRN_IAC3, thread->iac3); mtspr(SPRN_IAC4, thread->iac4); #endif mtspr(SPRN_DAC1, thread->dac1); mtspr(SPRN_DAC2, thread->dac2); #if CONFIG_PPC_ADV_DEBUG_DVCS > 0 mtspr(SPRN_DVC1, thread->dvc1); mtspr(SPRN_DVC2, thread->dvc2); #endif mtspr(SPRN_DBCR0, thread->dbcr0); mtspr(SPRN_DBCR1, thread->dbcr1); #ifdef CONFIG_BOOKE mtspr(SPRN_DBCR2, thread->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. */ static void switch_booke_debug_regs(struct thread_struct *new_thread) { if ((current->thread.dbcr0 & DBCR0_IDM) || (new_thread->dbcr0 & DBCR0_IDM)) prime_debug_regs(new_thread); } #else /* !CONFIG_PPC_ADV_DEBUG_REGS */ #ifndef CONFIG_HAVE_HW_BREAKPOINT static void set_debug_reg_defaults(struct thread_struct *thread) { if (thread->dabr) { thread->dabr = 0; set_dabr(0); } } #endif /* !CONFIG_HAVE_HW_BREAKPOINT */ #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ int set_dabr(unsigned long dabr) { __get_cpu_var(current_dabr) = dabr; if (ppc_md.set_dabr) return ppc_md.set_dabr(dabr); /* XXX should we have a CPU_FTR_HAS_DABR ? */ #ifdef CONFIG_PPC_ADV_DEBUG_REGS mtspr(SPRN_DAC1, dabr); #ifdef CONFIG_PPC_47x isync(); #endif #elif defined(CONFIG_PPC_BOOK3S) mtspr(SPRN_DABR, dabr); #endif return 0; } #ifdef CONFIG_PPC64 DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array); #endif 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 #ifdef CONFIG_SMP /* avoid complexity of lazy save/restore of fpu * by just saving it every time we switch out if * this task used the fpu during the last quantum. * * If it tries to use the fpu again, it'll trap and * reload its fp regs. So we don't have to do a restore * every switch, just a save. * -- Cort */ if (prev->thread.regs && (prev->thread.regs->msr & MSR_FP)) giveup_fpu(prev); #ifdef CONFIG_ALTIVEC /* * If the previous thread used altivec in the last quantum * (thus changing altivec regs) then save them. * We used to check the VRSAVE register but not all apps * set it, so we don't rely on it now (and in fact we need * to save & restore VSCR even if VRSAVE == 0). -- paulus * * On SMP we always save/restore altivec regs just to avoid the * complexity of changing processors. * -- Cort */ if (prev->thread.regs && (prev->thread.regs->msr & MSR_VEC)) giveup_altivec(prev); #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSX if (prev->thread.regs && (prev->thread.regs->msr & MSR_VSX)) /* VMX and FPU registers are already save here */ __giveup_vsx(prev); #endif /* CONFIG_VSX */ #ifdef CONFIG_SPE /* * If the previous thread used spe in the last quantum * (thus changing spe regs) then save them. * * On SMP we always save/restore spe regs just to avoid the * complexity of changing processors. */ if ((prev->thread.regs && (prev->thread.regs->msr & MSR_SPE))) giveup_spe(prev); #endif /* CONFIG_SPE */ #else /* CONFIG_SMP */ #ifdef CONFIG_ALTIVEC /* Avoid the trap. On smp this this never happens since * we don't set last_task_used_altivec -- Cort */ if (new->thread.regs && last_task_used_altivec == new) new->thread.regs->msr |= MSR_VEC; #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSX if (new->thread.regs && last_task_used_vsx == new) new->thread.regs->msr |= MSR_VSX; #endif /* CONFIG_VSX */ #ifdef CONFIG_SPE /* Avoid the trap. On smp this this never happens since * we don't set last_task_used_spe */ if (new->thread.regs && last_task_used_spe == new) new->thread.regs->msr |= MSR_SPE; #endif /* CONFIG_SPE */ #endif /* CONFIG_SMP */ #ifdef CONFIG_PPC_ADV_DEBUG_REGS switch_booke_debug_regs(&new->thread); #else /* * For PPC_BOOK3S_64, we use the hw-breakpoint interfaces that would * schedule DABR */ #ifndef CONFIG_HAVE_HW_BREAKPOINT if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) set_dabr(new->thread.dabr); #endif /* CONFIG_HAVE_HW_BREAKPOINT */ #endif new_thread = &new->thread; old_thread = ¤t->thread; #ifdef CONFIG_PPC64 /* * Collect processor utilization data per process */ if (firmware_has_feature(FW_FEATURE_SPLPAR)) { struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array); long unsigned start_tb, current_tb; start_tb = old_thread->start_tb; cu->current_tb = current_tb = mfspr(SPRN_PURR); old_thread->accum_tb += (current_tb - start_tb); new_thread->start_tb = current_tb; }