mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
0bc6791707
commit
515b029d00
|
@ -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, ¤t->thread.fpu, 1);
|
||||
sig = fpu_emulator_cop1Handler(regs, ¤t->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,
|
||||
¤t->thread.fpu, 0);
|
||||
if (sig)
|
||||
force_sig(sig, current);
|
||||
else
|
||||
¤t->thread.fpu,
|
||||
0, &fault_addr);
|
||||
if (!process_fpemu_return(sig, fault_addr))
|
||||
mt_ase_fp_affinity();
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *,
|
|||
|
||||
#if __mips >= 4 && __mips != 32
|
||||
static int fpux_emu(struct pt_regs *,
|
||||
struct mips_fpu_struct *, mips_instruction);
|
||||
struct mips_fpu_struct *, mips_instruction, void *__user *);
|
||||
#endif
|
||||
|
||||
/* Further private data for which no space exists in mips_fpu_struct */
|
||||
|
@ -208,16 +208,23 @@ static inline int cop1_64bit(struct pt_regs *xcp)
|
|||
* Two instructions if the instruction is in a branch delay slot.
|
||||
*/
|
||||
|
||||
static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
||||
static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
||||
void *__user *fault_addr)
|
||||
{
|
||||
mips_instruction ir;
|
||||
unsigned long emulpc, contpc;
|
||||
unsigned int cond;
|
||||
|
||||
if (get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
|
||||
if (!access_ok(VERIFY_READ, xcp->cp0_epc, sizeof(mips_instruction))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
||||
return SIGSEGV;
|
||||
}
|
||||
|
||||
/* XXX NEC Vr54xx bug workaround */
|
||||
if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir))
|
||||
|
@ -245,10 +252,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|||
#endif
|
||||
return SIGILL;
|
||||
}
|
||||
if (get_user(ir, (mips_instruction __user *) emulpc)) {
|
||||
if (!access_ok(VERIFY_READ, emulpc, sizeof(mips_instruction))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = (mips_instruction __user *)emulpc;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__get_user(ir, (mips_instruction __user *) emulpc)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = (mips_instruction __user *)emulpc;
|
||||
return SIGSEGV;
|
||||
}
|
||||
/* __compute_return_epc() will have updated cp0_epc */
|
||||
contpc = xcp->cp0_epc;
|
||||
/* In order not to confuse ptrace() et al, tweak context */
|
||||
|
@ -269,10 +282,17 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|||
u64 val;
|
||||
|
||||
MIPS_FPU_EMU_INC_STATS(loads);
|
||||
if (get_user(val, va)) {
|
||||
|
||||
if (!access_ok(VERIFY_READ, va, sizeof(u64))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__get_user(val, va)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGSEGV;
|
||||
}
|
||||
DITOREG(val, MIPSInst_RT(ir));
|
||||
break;
|
||||
}
|
||||
|
@ -284,10 +304,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|||
|
||||
MIPS_FPU_EMU_INC_STATS(stores);
|
||||
DIFROMREG(val, MIPSInst_RT(ir));
|
||||
if (put_user(val, va)) {
|
||||
if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__put_user(val, va)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGSEGV;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -297,10 +323,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|||
u32 val;
|
||||
|
||||
MIPS_FPU_EMU_INC_STATS(loads);
|
||||
if (get_user(val, va)) {
|
||||
if (!access_ok(VERIFY_READ, va, sizeof(u32))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__get_user(val, va)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGSEGV;
|
||||
}
|
||||
SITOREG(val, MIPSInst_RT(ir));
|
||||
break;
|
||||
}
|
||||
|
@ -312,10 +344,16 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|||
|
||||
MIPS_FPU_EMU_INC_STATS(stores);
|
||||
SIFROMREG(val, MIPSInst_RT(ir));
|
||||
if (put_user(val, va)) {
|
||||
if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__put_user(val, va)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGSEGV;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -440,10 +478,17 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|||
contpc = (xcp->cp0_epc +
|
||||
(MIPSInst_SIMM(ir) << 2));
|
||||
|
||||
if (get_user(ir,
|
||||
if (!access_ok(VERIFY_READ, xcp->cp0_epc,
|
||||
sizeof(mips_instruction))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__get_user(ir,
|
||||
(mips_instruction __user *) xcp->cp0_epc)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
return SIGBUS;
|
||||
*fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
||||
return SIGSEGV;
|
||||
}
|
||||
|
||||
switch (MIPSInst_OPCODE(ir)) {
|
||||
|
@ -506,9 +551,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx)
|
|||
|
||||
#if __mips >= 4 && __mips != 32
|
||||
case cop1x_op:{
|
||||
int sig;
|
||||
|
||||
if ((sig = fpux_emu(xcp, ctx, ir)))
|
||||
int sig = fpux_emu(xcp, ctx, ir, fault_addr);
|
||||
if (sig)
|
||||
return sig;
|
||||
break;
|
||||
}
|
||||
|
@ -604,7 +648,7 @@ DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg);
|
|||
DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg);
|
||||
|
||||
static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
||||
mips_instruction ir)
|
||||
mips_instruction ir, void *__user *fault_addr)
|
||||
{
|
||||
unsigned rcsr = 0; /* resulting csr */
|
||||
|
||||
|
@ -624,10 +668,16 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|||
xcp->regs[MIPSInst_FT(ir)]);
|
||||
|
||||
MIPS_FPU_EMU_INC_STATS(loads);
|
||||
if (get_user(val, va)) {
|
||||
if (!access_ok(VERIFY_READ, va, sizeof(u32))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__get_user(val, va)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGSEGV;
|
||||
}
|
||||
SITOREG(val, MIPSInst_FD(ir));
|
||||
break;
|
||||
|
||||
|
@ -638,9 +688,15 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|||
MIPS_FPU_EMU_INC_STATS(stores);
|
||||
|
||||
SIFROMREG(val, MIPSInst_FS(ir));
|
||||
if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (put_user(val, va)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
return SIGBUS;
|
||||
*fault_addr = va;
|
||||
return SIGSEGV;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -701,10 +757,16 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|||
xcp->regs[MIPSInst_FT(ir)]);
|
||||
|
||||
MIPS_FPU_EMU_INC_STATS(loads);
|
||||
if (get_user(val, va)) {
|
||||
if (!access_ok(VERIFY_READ, va, sizeof(u64))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__get_user(val, va)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGSEGV;
|
||||
}
|
||||
DITOREG(val, MIPSInst_FD(ir));
|
||||
break;
|
||||
|
||||
|
@ -714,10 +776,16 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|||
|
||||
MIPS_FPU_EMU_INC_STATS(stores);
|
||||
DIFROMREG(val, MIPSInst_FS(ir));
|
||||
if (put_user(val, va)) {
|
||||
if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__put_user(val, va)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = va;
|
||||
return SIGSEGV;
|
||||
}
|
||||
break;
|
||||
|
||||
case madd_d_op:
|
||||
|
@ -1242,7 +1310,7 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|||
}
|
||||
|
||||
int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
||||
int has_fpu)
|
||||
int has_fpu, void *__user *fault_addr)
|
||||
{
|
||||
unsigned long oldepc, prevepc;
|
||||
mips_instruction insn;
|
||||
|
@ -1252,10 +1320,16 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|||
do {
|
||||
prevepc = xcp->cp0_epc;
|
||||
|
||||
if (get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
|
||||
if (!access_ok(VERIFY_READ, xcp->cp0_epc, sizeof(mips_instruction))) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
||||
return SIGBUS;
|
||||
}
|
||||
if (__get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) {
|
||||
MIPS_FPU_EMU_INC_STATS(errors);
|
||||
*fault_addr = (mips_instruction __user *)xcp->cp0_epc;
|
||||
return SIGSEGV;
|
||||
}
|
||||
if (insn == 0)
|
||||
xcp->cp0_epc += 4; /* skip nops */
|
||||
else {
|
||||
|
@ -1267,7 +1341,7 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
|
|||
*/
|
||||
/* convert to ieee library modes */
|
||||
ieee754_csr.rm = ieee_rm[ieee754_csr.rm];
|
||||
sig = cop1Emulate(xcp, ctx);
|
||||
sig = cop1Emulate(xcp, ctx, fault_addr);
|
||||
/* revert to mips rounding mode */
|
||||
ieee754_csr.rm = mips_rm[ieee754_csr.rm];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue