aboutsummaryrefslogtreecommitdiff
path: root/arch/mips/kernel/traps.c
diff options
context:
space:
mode:
authorDavid Daney <ddaney@caviumnetworks.com>2010-10-21 16:32:26 -0700
committerRalf Baechle <ralf@linux-mips.org>2010-12-16 18:10:57 +0000
commit515b029d005b5694cf612a0a5ca6f861a7e45362 (patch)
tree983f7fea48513cfae6c7f96d4d4036475d8130b1 /arch/mips/kernel/traps.c
parent0bc6791707694c77b3543de39f77972a65de917a (diff)
MIPS: Send proper signal and siginfo on FP emulator faults.
We were unconditionally sending SIGBUS with an empty siginfo on FP emulator faults. This differs from what happens when real floating point hardware would get a fault. For most faults we need to send SIGSEGV with the faulting address filled in in the struct siginfo. Reported-by: Camm Maguire <camm@maguirefamily.org> Signed-off-by: David Daney <ddaney@caviumnetworks.com> To: linux-mips@linux-mips.org Cc: Camm Maguire <camm@maguirefamily.org> Patchwork: https://patchwork.linux-mips.org/patch/1727/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r--arch/mips/kernel/traps.c44
1 files changed, 35 insertions, 9 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 8e9fbe75894..e9710430254 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -83,7 +83,8 @@ extern asmlinkage void handle_mcheck(void);
extern asmlinkage void handle_reserved(void);
extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
- struct mips_fpu_struct *ctx, int has_fpu);
+ struct mips_fpu_struct *ctx, int has_fpu,
+ void *__user *fault_addr);
void (*board_be_init)(void);
int (*board_be_handler)(struct pt_regs *regs, int is_fixup);
@@ -661,12 +662,36 @@ asmlinkage void do_ov(struct pt_regs *regs)
force_sig_info(SIGFPE, &info, current);
}
+static int process_fpemu_return(int sig, void __user *fault_addr)
+{
+ if (sig == SIGSEGV || sig == SIGBUS) {
+ struct siginfo si = {0};
+ si.si_addr = fault_addr;
+ si.si_signo = sig;
+ if (sig == SIGSEGV) {
+ if (find_vma(current->mm, (unsigned long)fault_addr))
+ si.si_code = SEGV_ACCERR;
+ else
+ si.si_code = SEGV_MAPERR;
+ } else {
+ si.si_code = BUS_ADRERR;
+ }
+ force_sig_info(sig, &si, current);
+ return 1;
+ } else if (sig) {
+ force_sig(sig, current);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
/*
* XXX Delayed fp exceptions when doing a lazy ctx switch XXX
*/
asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
{
- siginfo_t info;
+ siginfo_t info = {0};
if (notify_die(DIE_FP, "FP exception", regs, 0, regs_to_trapnr(regs), SIGFPE)
== NOTIFY_STOP)
@@ -675,6 +700,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
if (fcr31 & FPU_CSR_UNI_X) {
int sig;
+ void __user *fault_addr = NULL;
/*
* Unimplemented operation exception. If we've got the full
@@ -690,7 +716,8 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
lose_fpu(1);
/* Run the emulator */
- sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1);
+ sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
+ &fault_addr);
/*
* We can't allow the emulated instruction to leave any of
@@ -702,8 +729,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
own_fpu(1); /* Using the FPU again. */
/* If something went wrong, signal */
- if (sig)
- force_sig(sig, current);
+ process_fpemu_return(sig, fault_addr);
return;
} else if (fcr31 & FPU_CSR_INV_X)
@@ -996,11 +1022,11 @@ asmlinkage void do_cpu(struct pt_regs *regs)
if (!raw_cpu_has_fpu) {
int sig;
+ void __user *fault_addr = NULL;
sig = fpu_emulator_cop1Handler(regs,
- &current->thread.fpu, 0);
- if (sig)
- force_sig(sig, current);
- else
+ &current->thread.fpu,
+ 0, &fault_addr);
+ if (!process_fpemu_return(sig, fault_addr))
mt_ase_fp_affinity();
}