aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Hibbits <jhibbits@FreeBSD.org>2019-04-03 04:01:08 +0000
committerJustin Hibbits <jhibbits@FreeBSD.org>2019-04-03 04:01:08 +0000
commit62c7ea1f1d3693ec35e78ce16f0c0c260100b1ce (patch)
tree50f32fca027f243e1a6d0eb0d0bd18f6d747ef6f
parentb0fefb25c558179e9f9c7f0d375c6a03fb567eb9 (diff)
powerpc: Allow emulating optional FPU instructions on CPUs with an FPU
The e5500 has an FPU, but lacks the optional fsqrt instruction. This instruction gets emulated in the kernel, but the emulation uses stale data, from the last switch out, and does not return the result of the operation immediately. Fix both of these conditions by saving and restoring the FPRs around the emulation point. MFC after: 1 week MFC with: r345829
Notes
Notes: svn path=/head/; revision=345831
-rw-r--r--sys/powerpc/include/trap.h4
-rw-r--r--sys/powerpc/powerpc/exec_machdep.c9
-rw-r--r--sys/powerpc/powerpc/trap.c2
3 files changed, 10 insertions, 5 deletions
diff --git a/sys/powerpc/include/trap.h b/sys/powerpc/include/trap.h
index fc927419db68..5017a7535d29 100644
--- a/sys/powerpc/include/trap.h
+++ b/sys/powerpc/include/trap.h
@@ -152,10 +152,10 @@
#ifndef LOCORE
struct trapframe;
-struct pcb;
+struct thread;
extern int (*hmi_handler)(struct trapframe *);
void trap(struct trapframe *);
-int ppc_instr_emulate(struct trapframe *, struct pcb *);
+int ppc_instr_emulate(struct trapframe *, struct thread *);
#endif
#endif /* _POWERPC_TRAP_H_ */
diff --git a/sys/powerpc/powerpc/exec_machdep.c b/sys/powerpc/powerpc/exec_machdep.c
index 3ae1640b2642..0ce6f0e26869 100644
--- a/sys/powerpc/powerpc/exec_machdep.c
+++ b/sys/powerpc/powerpc/exec_machdep.c
@@ -1081,8 +1081,9 @@ emulate_mtspr(int spr, int reg, struct trapframe *frame){
#define XFX 0xFC0007FF
int
-ppc_instr_emulate(struct trapframe *frame, struct pcb *pcb)
+ppc_instr_emulate(struct trapframe *frame, struct thread *td)
{
+ struct pcb *pcb;
uint32_t instr;
int reg, sig;
int rs, spr;
@@ -1109,12 +1110,16 @@ ppc_instr_emulate(struct trapframe *frame, struct pcb *pcb)
return (0);
}
+ pcb = td->td_pcb;
#ifdef FPU_EMU
if (!(pcb->pcb_flags & PCB_FPREGS)) {
bzero(&pcb->pcb_fpu, sizeof(pcb->pcb_fpu));
pcb->pcb_flags |= PCB_FPREGS;
- }
+ } else if (pcb->pcb_flags & PCB_FPU)
+ save_fpu(td);
sig = fpu_emulate(frame, &pcb->pcb_fpu);
+ if ((sig == 0 || sig == SIGFPE) && pcb->pcb_flags & PCB_FPU)
+ enable_fpu(td);
#endif
if (sig == SIGILL) {
if (pcb->pcb_lastill != frame->srr0) {
diff --git a/sys/powerpc/powerpc/trap.c b/sys/powerpc/powerpc/trap.c
index 111e69a7b32e..2a63f47b71a1 100644
--- a/sys/powerpc/powerpc/trap.c
+++ b/sys/powerpc/powerpc/trap.c
@@ -363,7 +363,7 @@ trap(struct trapframe *frame)
sig = SIGTRAP;
ucode = TRAP_BRKPT;
} else {
- sig = ppc_instr_emulate(frame, td->td_pcb);
+ sig = ppc_instr_emulate(frame, td);
if (sig == SIGILL) {
if (frame->srr1 & EXC_PGM_PRIV)
ucode = ILL_PRVOPC;