Skip to content

Commit

Permalink
MIPS: Set `si_code' for SIGFPE signals sent from emulation too
Browse files Browse the repository at this point in the history
Rework `process_fpemu_return' and move IEEE 754 exception interpretation
there, from `do_fpe'.  Record the cause bits set in FCSR before they are
cleared and pass them through to `process_fpemu_return' so as to set
`si_code' correctly too for SIGFPE signals sent from emulation rather
than those issued by hardware with the FPE processor exception only.

For simplicity `mipsr2_decoder' assumes `*fcr31' has been preinitialised
and only sets it to anything if an FPU instruction has been emulated,
which in turn is the only case SIGFPE can be issued for here.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9705/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
  • Loading branch information
Maciej W. Rozycki authored and Ralf Baechle committed Apr 7, 2015
1 parent 443c440 commit 304acb7
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 73 deletions.
3 changes: 2 additions & 1 deletion arch/mips/include/asm/fpu_emulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ extern int do_dsemulret(struct pt_regs *xcp);
extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
struct mips_fpu_struct *ctx, int has_fpu,
void *__user *fault_addr);
int process_fpemu_return(int sig, void __user *fault_addr);
int process_fpemu_return(int sig, void __user *fault_addr,
unsigned long fcr31);
int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
unsigned long *contpc);

Expand Down
9 changes: 7 additions & 2 deletions arch/mips/include/asm/mips-r2-to-r6-emul.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,16 @@ extern void do_trap_or_bp(struct pt_regs *regs, unsigned int code,

#ifndef CONFIG_MIPSR2_TO_R6_EMULATOR
static int mipsr2_emulation;
static inline int mipsr2_decoder(struct pt_regs *regs, u32 inst) { return 0; };
static inline int mipsr2_decoder(struct pt_regs *regs, u32 inst,
unsigned long *fcr31)
{
return 0;
};
#else
/* MIPS R2 Emulator ON/OFF */
extern int mipsr2_emulation;
extern int mipsr2_decoder(struct pt_regs *regs, u32 inst);
extern int mipsr2_decoder(struct pt_regs *regs, u32 inst,
unsigned long *fcr31);
#endif /* CONFIG_MIPSR2_TO_R6_EMULATOR */

#define NO_R6EMU (cpu_has_mips_r6 && !mipsr2_emulation)
Expand Down
4 changes: 3 additions & 1 deletion arch/mips/kernel/mips-r2-to-r6-emul.c
Original file line number Diff line number Diff line change
Expand Up @@ -898,8 +898,9 @@ static inline int mipsr2_find_op_func(struct pt_regs *regs, u32 inst,
* mipsr2_decoder: Decode and emulate a MIPS R2 instruction
* @regs: Process register set
* @inst: Instruction to decode and emulate
* @fcr31: Floating Point Control and Status Register returned
*/
int mipsr2_decoder(struct pt_regs *regs, u32 inst)
int mipsr2_decoder(struct pt_regs *regs, u32 inst, unsigned long *fcr31)
{
int err = 0;
unsigned long vaddr;
Expand Down Expand Up @@ -1168,6 +1169,7 @@ int mipsr2_decoder(struct pt_regs *regs, u32 inst)

err = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0,
&fault_addr);
*fcr31 = current->thread.fpu.fcr31;

/*
* We can't allow the emulated instruction to leave any of
Expand Down
150 changes: 83 additions & 67 deletions arch/mips/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -700,37 +700,69 @@ asmlinkage void do_ov(struct pt_regs *regs)
exception_exit(prev_state);
}

int process_fpemu_return(int sig, void __user *fault_addr)
int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31)
{
if (sig == SIGSEGV || sig == SIGBUS) {
struct siginfo si = {0};
struct siginfo si = { 0 };

switch (sig) {
case 0:
return 0;

case SIGFPE:
si.si_addr = fault_addr;
si.si_signo = sig;
if (sig == SIGSEGV) {
down_read(&current->mm->mmap_sem);
if (find_vma(current->mm, (unsigned long)fault_addr))
si.si_code = SEGV_ACCERR;
else
si.si_code = SEGV_MAPERR;
up_read(&current->mm->mmap_sem);
} else {
si.si_code = BUS_ADRERR;
}
/*
* Inexact can happen together with Overflow or Underflow.
* Respect the mask to deliver the correct exception.
*/
fcr31 &= (fcr31 & FPU_CSR_ALL_E) <<
(ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E));
if (fcr31 & FPU_CSR_INV_X)
si.si_code = FPE_FLTINV;
else if (fcr31 & FPU_CSR_DIV_X)
si.si_code = FPE_FLTDIV;
else if (fcr31 & FPU_CSR_OVF_X)
si.si_code = FPE_FLTOVF;
else if (fcr31 & FPU_CSR_UDF_X)
si.si_code = FPE_FLTUND;
else if (fcr31 & FPU_CSR_INE_X)
si.si_code = FPE_FLTRES;
else
si.si_code = __SI_FAULT;
force_sig_info(sig, &si, current);
return 1;
} else if (sig) {

case SIGBUS:
si.si_addr = fault_addr;
si.si_signo = sig;
si.si_code = BUS_ADRERR;
force_sig_info(sig, &si, current);
return 1;

case SIGSEGV:
si.si_addr = fault_addr;
si.si_signo = sig;
down_read(&current->mm->mmap_sem);
if (find_vma(current->mm, (unsigned long)fault_addr))
si.si_code = SEGV_ACCERR;
else
si.si_code = SEGV_MAPERR;
up_read(&current->mm->mmap_sem);
force_sig_info(sig, &si, current);
return 1;

default:
force_sig(sig, current);
return 1;
} else {
return 0;
}
}

static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
unsigned long old_epc, unsigned long old_ra)
{
union mips_instruction inst = { .word = opcode };
void __user *fault_addr = NULL;
void __user *fault_addr;
unsigned long fcr31;
int sig;

/* If it's obviously not an FP instruction, skip it */
Expand Down Expand Up @@ -760,19 +792,20 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
/* Run the emulator */
sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
&fault_addr);
fcr31 = current->thread.fpu.fcr31;

/*
* We can't allow the emulated instruction to leave any of
* the cause bits set in $fcr31.
*/
current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;

/* If something went wrong, signal */
process_fpemu_return(sig, fault_addr);

/* Restore the hardware register state */
own_fpu(1);

/* Send a signal if required. */
process_fpemu_return(sig, fault_addr, fcr31);

return 0;
}

Expand All @@ -782,7 +815,8 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
{
enum ctx_state prev_state;
siginfo_t info = {0};
void __user *fault_addr;
int sig;

prev_state = exception_enter();
if (notify_die(DIE_FP, "FP exception", regs, 0, regs_to_trapnr(regs),
Expand All @@ -791,9 +825,6 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
die_if_kernel("FP exception in kernel code", regs);

if (fcr31 & FPU_CSR_UNI_X) {
int sig;
void __user *fault_addr = NULL;

/*
* Unimplemented operation exception. If we've got the full
* software emulator on-board, let's use it...
Expand All @@ -810,6 +841,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
/* Run the emulator */
sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
&fault_addr);
fcr31 = current->thread.fpu.fcr31;

/*
* We can't allow the emulated instruction to leave any of
Expand All @@ -819,35 +851,13 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)

/* Restore the hardware register state */
own_fpu(1); /* Using the FPU again. */

/* If something went wrong, signal */
process_fpemu_return(sig, fault_addr);

goto out;
} else {
sig = SIGFPE;
fault_addr = (void __user *) regs->cp0_epc;
}

/*
* Inexact can happen together with Overflow or Underflow.
* Respect the mask to deliver the correct exception.
*/
fcr31 &= (fcr31 & FPU_CSR_ALL_E) <<
(ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E));
if (fcr31 & FPU_CSR_INV_X)
info.si_code = FPE_FLTINV;
else if (fcr31 & FPU_CSR_DIV_X)
info.si_code = FPE_FLTDIV;
else if (fcr31 & FPU_CSR_OVF_X)
info.si_code = FPE_FLTOVF;
else if (fcr31 & FPU_CSR_UDF_X)
info.si_code = FPE_FLTUND;
else if (fcr31 & FPU_CSR_INE_X)
info.si_code = FPE_FLTRES;
else
info.si_code = __SI_FAULT;
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_addr = (void __user *) regs->cp0_epc;
force_sig_info(SIGFPE, &info, current);
/* Send a signal if required. */
process_fpemu_return(sig, fault_addr, fcr31);

out:
exception_exit(prev_state);
Expand Down Expand Up @@ -1050,7 +1060,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
if (mipsr2_emulation && cpu_has_mips_r6 &&
likely(user_mode(regs)) &&
likely(get_user(opcode, epc) >= 0)) {
status = mipsr2_decoder(regs, opcode);
unsigned long fcr31 = 0;

status = mipsr2_decoder(regs, opcode, &fcr31);
switch (status) {
case 0:
case SIGEMT:
Expand All @@ -1060,7 +1072,8 @@ asmlinkage void do_ri(struct pt_regs *regs)
goto no_r2_instr;
default:
process_fpemu_return(status,
&current->thread.cp0_baduaddr);
&current->thread.cp0_baduaddr,
fcr31);
task_thread_info(current)->r2_emul_return = 1;
return;
}
Expand Down Expand Up @@ -1307,10 +1320,13 @@ asmlinkage void do_cpu(struct pt_regs *regs)
enum ctx_state prev_state;
unsigned int __user *epc;
unsigned long old_epc, old31;
void __user *fault_addr;
unsigned int opcode;
unsigned long fcr31;
unsigned int cpid;
int status, err;
unsigned long __maybe_unused flags;
int sig;

prev_state = exception_enter();
cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;
Expand Down Expand Up @@ -1384,22 +1400,22 @@ asmlinkage void do_cpu(struct pt_regs *regs)
case 1:
err = enable_restore_fp_context(0);

if (!raw_cpu_has_fpu || err) {
int sig;
void __user *fault_addr = NULL;
sig = fpu_emulator_cop1Handler(regs,
&current->thread.fpu,
0, &fault_addr);
if (raw_cpu_has_fpu && !err)
break;

/*
* We can't allow the emulated instruction to leave
* any of the cause bits set in $fcr31.
*/
current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 0,
&fault_addr);
fcr31 = current->thread.fpu.fcr31;

if (!process_fpemu_return(sig, fault_addr) && !err)
mt_ase_fp_affinity();
}
/*
* We can't allow the emulated instruction to leave
* any of the cause bits set in $fcr31.
*/
current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;

/* Send a signal if required. */
if (!process_fpemu_return(sig, fault_addr, fcr31) && !err)
mt_ase_fp_affinity();

break;

Expand Down
4 changes: 2 additions & 2 deletions arch/mips/kernel/unaligned.c
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
own_fpu(1); /* Restore FPU state. */

/* Signal if something went wrong. */
process_fpemu_return(res, fault_addr);
process_fpemu_return(res, fault_addr, 0);

if (res == 0)
break;
Expand Down Expand Up @@ -1511,7 +1511,7 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
own_fpu(1); /* restore FPU state */

/* If something went wrong, signal */
process_fpemu_return(res, fault_addr);
process_fpemu_return(res, fault_addr, 0);

if (res == 0)
goto success;
Expand Down

0 comments on commit 304acb7

Please sign in to comment.