diff --git a/sys/powerpc/include/reg.h b/sys/powerpc/include/reg.h index a824792b0f12..c0bf807ac75e 100644 --- a/sys/powerpc/include/reg.h +++ b/sys/powerpc/include/reg.h @@ -68,6 +68,11 @@ int set_fpregs(struct thread *, struct fpreg *); int fill_dbregs(struct thread *, struct dbreg *); int set_dbregs(struct thread *, struct dbreg *); +/* + * MD interfaces. + */ +void cpu_save_thread_regs(struct thread *); + #ifdef COMPAT_FREEBSD32 struct image_params; diff --git a/sys/powerpc/powerpc/exec_machdep.c b/sys/powerpc/powerpc/exec_machdep.c index 97e4caba956a..b32fa6c79279 100644 --- a/sys/powerpc/powerpc/exec_machdep.c +++ b/sys/powerpc/powerpc/exec_machdep.c @@ -570,6 +570,74 @@ cleanup_power_extras(struct thread *td) cleanup_fpscr(); } +/* + * Ensure the PCB has been updated in preparation for copying a thread. + * + * This is needed because normally this only happens during switching tasks, + * but when we are cloning a thread, we need the updated state before doing + * the actual copy, so the new thread inherits the current state instead of + * the state at the last task switch. + * + * Keep this in sync with the assembly code in cpu_switch()! + */ +void +cpu_save_thread_regs(struct thread *td) +{ + uint32_t pcb_flags; + struct pcb *pcb; + + KASSERT(td == curthread, + ("cpu_save_thread_regs: td is not curthread")); + + pcb = td->td_pcb; + + pcb_flags = pcb->pcb_flags; + +#if defined(__powerpc64__) + /* Are *any* FSCR flags in use? */ + if (pcb_flags & PCB_CFSCR) { + pcb->pcb_fscr = mfspr(SPR_FSCR); + + if (pcb->pcb_fscr & FSCR_EBB) { + pcb->pcb_ebb.ebbhr = mfspr(SPR_EBBHR); + pcb->pcb_ebb.ebbrr = mfspr(SPR_EBBRR); + pcb->pcb_ebb.bescr = mfspr(SPR_BESCR); + } + if (pcb->pcb_fscr & FSCR_LM) { + pcb->pcb_lm.lmrr = mfspr(SPR_LMRR); + pcb->pcb_lm.lmser = mfspr(SPR_LMSER); + } + if (pcb->pcb_fscr & FSCR_TAR) + pcb->pcb_tar = mfspr(SPR_TAR); + } + + /* + * This is outside of the PCB_CFSCR check because it can be set + * independently when running on POWER7/POWER8. + */ + if (pcb_flags & PCB_CDSCR) + pcb->pcb_dscr = mfspr(SPR_DSCRP); +#endif + +#if defined(__SPE__) + /* + * On E500v2, single-precision scalar instructions and access to + * SPEFSCR may be used without PSL_VEC turned on, as long as they + * limit themselves to the low word of the registers. + * + * As such, we need to unconditionally save SPEFSCR, even though + * it is also updated in save_vec_nodrop(). + */ + pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR); +#endif + + if (pcb_flags & PCB_FPU) + save_fpu_nodrop(td); + + if (pcb_flags & PCB_VEC) + save_vec_nodrop(td); +} + /* * Set set up registers on exec. */ @@ -1028,6 +1096,9 @@ cpu_copy_thread(struct thread *td, struct thread *td0) struct trapframe *tf; struct callframe *cf; + /* Ensure td0 pcb is up to date. */ + cpu_save_thread_regs(td0); + pcb2 = td->td_pcb; /* Copy the upcall pcb */ diff --git a/sys/powerpc/powerpc/swtch32.S b/sys/powerpc/powerpc/swtch32.S index 6ad781895dc3..928610d488e6 100644 --- a/sys/powerpc/powerpc/swtch32.S +++ b/sys/powerpc/powerpc/swtch32.S @@ -104,6 +104,8 @@ ENTRY(cpu_switch) mr %r16,%r5 /* and the new lock */ mr %r17,%r6 /* and the PCB */ + /* Keep this next section in sync with cpu_save_thread_regs()! */ + lwz %r18,PCB_FLAGS(%r17) /* Save FPU context if needed */ andi. %r7, %r18, PCB_FPU diff --git a/sys/powerpc/powerpc/swtch64.S b/sys/powerpc/powerpc/swtch64.S index fe59a166f3cb..49d50ad70f7a 100644 --- a/sys/powerpc/powerpc/swtch64.S +++ b/sys/powerpc/powerpc/swtch64.S @@ -131,6 +131,8 @@ ENTRY(cpu_switch) stdu %r1,-48(%r1) + /* Keep this next section in sync with cpu_save_thread_regs()! */ + lwz %r18, PCB_FLAGS(%r17) andi. %r7, %r18, PCB_CFSCR beq 1f diff --git a/sys/powerpc/powerpc/vm_machdep.c b/sys/powerpc/powerpc/vm_machdep.c index 4aab71ec036c..a1f0b647a92a 100644 --- a/sys/powerpc/powerpc/vm_machdep.c +++ b/sys/powerpc/powerpc/vm_machdep.c @@ -91,6 +91,7 @@ #include #include #include +#include #include @@ -121,6 +122,9 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) if ((flags & RFPROC) == 0) return; + /* Ensure td1 is up to date before copy. */ + cpu_save_thread_regs(td1); + pcb = (struct pcb *)((td2->td_kstack + td2->td_kstack_pages * PAGE_SIZE - sizeof(struct pcb)) & ~0x2fUL); td2->td_pcb = pcb;