diff --git a/sys/powerpc/include/reg.h b/sys/powerpc/include/reg.h index a824792b0f12..bb02a0646aae 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 *, register_t msr); + #ifdef COMPAT_FREEBSD32 struct image_params; diff --git a/sys/powerpc/powerpc/exec_machdep.c b/sys/powerpc/powerpc/exec_machdep.c index 97e4caba956a..87123357b660 100644 --- a/sys/powerpc/powerpc/exec_machdep.c +++ b/sys/powerpc/powerpc/exec_machdep.c @@ -570,6 +570,58 @@ 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. + */ +void +cpu_save_thread_regs(struct thread *td, register_t msr) +{ + 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; + + /* 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); + } + + if (pcb_flags & PCB_CDSCR) { + if (msr & PSL_PR) + td->td_pcb->pcb_dscr = mfspr(SPR_DSCR); + else + td->td_pcb->pcb_dscr = mfspr(SPR_DSCRP); + } + + 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 +1080,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, td0->td_frame->srr1); + pcb2 = td->td_pcb; /* Copy the upcall pcb */ diff --git a/sys/powerpc/powerpc/swtch64.S b/sys/powerpc/powerpc/swtch64.S index fe59a166f3cb..272f7ecc7826 100644 --- a/sys/powerpc/powerpc/swtch64.S +++ b/sys/powerpc/powerpc/swtch64.S @@ -117,6 +117,8 @@ ENTRY(cpu_switch) std %r30,PCB_CONTEXT+18*8(%r6) std %r31,PCB_CONTEXT+19*8(%r6) + /* Keep this next section in sync with cpu_save_thread_regs()! */ + mfcr %r16 /* Save the condition register */ std %r16,PCB_CR(%r6) mflr %r16 /* Save the link register */ diff --git a/sys/powerpc/powerpc/vm_machdep.c b/sys/powerpc/powerpc/vm_machdep.c index 4aab71ec036c..34dfde8c6632 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, td1->td_frame->srr1); + pcb = (struct pcb *)((td2->td_kstack + td2->td_kstack_pages * PAGE_SIZE - sizeof(struct pcb)) & ~0x2fUL); td2->td_pcb = pcb;