Commit 456d3d42 authored by David S. Miller's avatar David S. Miller

sparc64: Fix several bugs in quad floating point emulation.

UltraSPARC-T2 and later do not use the fp_exception_other trap and do
not set the floating point trap type field in the %fsr at all when you
try to execute an unimplemented FPU operation.

Instead, it uses the illegal_instruction trap and it leaves the
floating point trap type field clear.

So we should not validate the %fsr trap type field when do_mathemu()
is invoked from the illegal instruction handler.

Also, the floating point trap type field is 3 bits, not 4 bits.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 07acfc2a
...@@ -2054,7 +2054,7 @@ void do_fpieee(struct pt_regs *regs) ...@@ -2054,7 +2054,7 @@ void do_fpieee(struct pt_regs *regs)
do_fpe_common(regs); do_fpe_common(regs);
} }
extern int do_mathemu(struct pt_regs *, struct fpustate *); extern int do_mathemu(struct pt_regs *, struct fpustate *, bool);
void do_fpother(struct pt_regs *regs) void do_fpother(struct pt_regs *regs)
{ {
...@@ -2068,7 +2068,7 @@ void do_fpother(struct pt_regs *regs) ...@@ -2068,7 +2068,7 @@ void do_fpother(struct pt_regs *regs)
switch ((current_thread_info()->xfsr[0] & 0x1c000)) { switch ((current_thread_info()->xfsr[0] & 0x1c000)) {
case (2 << 14): /* unfinished_FPop */ case (2 << 14): /* unfinished_FPop */
case (3 << 14): /* unimplemented_FPop */ case (3 << 14): /* unimplemented_FPop */
ret = do_mathemu(regs, f); ret = do_mathemu(regs, f, false);
break; break;
} }
if (ret) if (ret)
...@@ -2308,10 +2308,12 @@ void do_illegal_instruction(struct pt_regs *regs) ...@@ -2308,10 +2308,12 @@ void do_illegal_instruction(struct pt_regs *regs)
} else { } else {
struct fpustate *f = FPUSTATE; struct fpustate *f = FPUSTATE;
/* XXX maybe verify XFSR bits like /* On UltraSPARC T2 and later, FPU insns which
* XXX do_fpother() does? * are not implemented in HW signal an illegal
* instruction trap and do not set the FP Trap
* Trap in the %fsr to unimplemented_FPop.
*/ */
if (do_mathemu(regs, f)) if (do_mathemu(regs, f, true))
return; return;
} }
} }
......
...@@ -163,7 +163,7 @@ typedef union { ...@@ -163,7 +163,7 @@ typedef union {
u64 q[2]; u64 q[2];
} *argp; } *argp;
int do_mathemu(struct pt_regs *regs, struct fpustate *f) int do_mathemu(struct pt_regs *regs, struct fpustate *f, bool illegal_insn_trap)
{ {
unsigned long pc = regs->tpc; unsigned long pc = regs->tpc;
unsigned long tstate = regs->tstate; unsigned long tstate = regs->tstate;
...@@ -218,7 +218,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f) ...@@ -218,7 +218,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
case FSQRTS: { case FSQRTS: {
unsigned long x = current_thread_info()->xfsr[0]; unsigned long x = current_thread_info()->xfsr[0];
x = (x >> 14) & 0xf; x = (x >> 14) & 0x7;
TYPE(x,1,1,1,1,0,0); TYPE(x,1,1,1,1,0,0);
break; break;
} }
...@@ -226,7 +226,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f) ...@@ -226,7 +226,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
case FSQRTD: { case FSQRTD: {
unsigned long x = current_thread_info()->xfsr[0]; unsigned long x = current_thread_info()->xfsr[0];
x = (x >> 14) & 0xf; x = (x >> 14) & 0x7;
TYPE(x,2,1,2,1,0,0); TYPE(x,2,1,2,1,0,0);
break; break;
} }
...@@ -357,9 +357,17 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f) ...@@ -357,9 +357,17 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
if (type) { if (type) {
argp rs1 = NULL, rs2 = NULL, rd = NULL; argp rs1 = NULL, rs2 = NULL, rd = NULL;
freg = (current_thread_info()->xfsr[0] >> 14) & 0xf; /* Starting with UltraSPARC-T2, the cpu does not set the FP Trap
if (freg != (type >> 9)) * Type field in the %fsr to unimplemented_FPop. Nor does it
goto err; * use the fp_exception_other trap. Instead it signals an
* illegal instruction and leaves the FP trap type field of
* the %fsr unchanged.
*/
if (!illegal_insn_trap) {
int ftt = (current_thread_info()->xfsr[0] >> 14) & 0x7;
if (ftt != (type >> 9))
goto err;
}
current_thread_info()->xfsr[0] &= ~0x1c000; current_thread_info()->xfsr[0] &= ~0x1c000;
freg = ((insn >> 14) & 0x1f); freg = ((insn >> 14) & 0x1f);
switch (type & 0x3) { switch (type & 0x3) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment