[POWERPC] Fixup error handling when emulating a floating point instruction

When we do full FP emulation its possible that we need to post a SIGFPE based
on the results of the emulation.  The previous code ignored this case completely.

Additionally, the Soft_emulate_8xx case had two issues.  One, we should never
generate a SIGFPE since the code only does data movement.  Second, we were
interpreting the return codes incorrectly, it returns 0 on success, 1 on
illop and -EFAULT on a data access error.

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
This commit is contained in:
Kumar Gala 2007-02-07 01:47:59 -06:00
Родитель 04903a30a3
Коммит 5fad293bcb
1 изменённых файлов: 60 добавлений и 22 удалений

Просмотреть файл

@ -535,34 +535,40 @@ static void emulate_single_step(struct pt_regs *regs)
} }
} }
static void parse_fpe(struct pt_regs *regs) static inline int __parse_fpscr(unsigned long fpscr)
{ {
int code = 0; int ret = 0;
unsigned long fpscr;
flush_fp_to_thread(current);
fpscr = current->thread.fpscr.val;
/* Invalid operation */ /* Invalid operation */
if ((fpscr & FPSCR_VE) && (fpscr & FPSCR_VX)) if ((fpscr & FPSCR_VE) && (fpscr & FPSCR_VX))
code = FPE_FLTINV; ret = FPE_FLTINV;
/* Overflow */ /* Overflow */
else if ((fpscr & FPSCR_OE) && (fpscr & FPSCR_OX)) else if ((fpscr & FPSCR_OE) && (fpscr & FPSCR_OX))
code = FPE_FLTOVF; ret = FPE_FLTOVF;
/* Underflow */ /* Underflow */
else if ((fpscr & FPSCR_UE) && (fpscr & FPSCR_UX)) else if ((fpscr & FPSCR_UE) && (fpscr & FPSCR_UX))
code = FPE_FLTUND; ret = FPE_FLTUND;
/* Divide by zero */ /* Divide by zero */
else if ((fpscr & FPSCR_ZE) && (fpscr & FPSCR_ZX)) else if ((fpscr & FPSCR_ZE) && (fpscr & FPSCR_ZX))
code = FPE_FLTDIV; ret = FPE_FLTDIV;
/* Inexact result */ /* Inexact result */
else if ((fpscr & FPSCR_XE) && (fpscr & FPSCR_XX)) else if ((fpscr & FPSCR_XE) && (fpscr & FPSCR_XX))
code = FPE_FLTRES; ret = FPE_FLTRES;
return ret;
}
static void parse_fpe(struct pt_regs *regs)
{
int code = 0;
flush_fp_to_thread(current);
code = __parse_fpscr(current->thread.fpscr.val);
_exception(SIGFPE, regs, code, regs->nip); _exception(SIGFPE, regs, code, regs->nip);
} }
@ -773,10 +779,21 @@ void __kprobes program_check_exception(struct pt_regs *regs)
* hardware people - not sure if it can happen on any illegal * hardware people - not sure if it can happen on any illegal
* instruction or only on FP instructions, whether there is a * instruction or only on FP instructions, whether there is a
* pattern to occurences etc. -dgibson 31/Mar/2003 */ * pattern to occurences etc. -dgibson 31/Mar/2003 */
if (do_mathemu(regs) == 0) { switch (do_mathemu(regs)) {
case 0:
emulate_single_step(regs); emulate_single_step(regs);
return; return;
case 1: {
int code = 0;
code = __parse_fpscr(current->thread.fpscr.val);
_exception(SIGFPE, regs, code, regs->nip);
return;
}
case -EFAULT:
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
return;
} }
/* fall through on any other errors */
#endif /* CONFIG_MATH_EMULATION */ #endif /* CONFIG_MATH_EMULATION */
/* Try to emulate it if we should. */ /* Try to emulate it if we should. */
@ -892,18 +909,39 @@ void SoftwareEmulation(struct pt_regs *regs)
#ifdef CONFIG_MATH_EMULATION #ifdef CONFIG_MATH_EMULATION
errcode = do_mathemu(regs); errcode = do_mathemu(regs);
switch (errcode) {
case 0:
emulate_single_step(regs);
return;
case 1: {
int code = 0;
code = __parse_fpscr(current->thread.fpscr.val);
_exception(SIGFPE, regs, code, regs->nip);
return;
}
case -EFAULT:
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
return;
default:
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
return;
}
#else #else
errcode = Soft_emulate_8xx(regs); errcode = Soft_emulate_8xx(regs);
#endif switch (errcode) {
if (errcode) { case 0:
if (errcode > 0)
_exception(SIGFPE, regs, 0, 0);
else if (errcode == -EFAULT)
_exception(SIGSEGV, regs, 0, 0);
else
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
} else
emulate_single_step(regs); emulate_single_step(regs);
return;
case 1:
_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
return;
case -EFAULT:
_exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
return;
}
#endif
} }
#endif /* CONFIG_8xx */ #endif /* CONFIG_8xx */