Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 304acb71 authored by Maciej W. Rozycki's avatar Maciej W. Rozycki Committed by Ralf Baechle
Browse files

MIPS: Set `si_code' for SIGFPE signals sent from emulation too



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: default avatarMaciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9705/


Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 443c4403
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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);

+7 −2
Original line number Diff line number Diff line
@@ -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)
+3 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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
+83 −67
Original line number Diff line number Diff line
@@ -700,29 +700,60 @@ 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 };

	switch (sig) {
	case 0:
		return 0;

	case SIGFPE:
		si.si_addr = fault_addr;
		si.si_signo = sig;
		/*
		 * 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;

	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;
		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;
		}
		force_sig_info(sig, &si, current);
		return 1;
	} else if (sig) {

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

@@ -730,7 +761,8 @@ 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 */
@@ -760,6 +792,7 @@ 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
@@ -767,12 +800,12 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
	 */
	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;
}

@@ -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),
@@ -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...
@@ -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
@@ -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);
@@ -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:
@@ -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;
		}
@@ -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;
@@ -1384,12 +1400,12 @@ 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;

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

		/*
		 * We can't allow the emulated instruction to leave
@@ -1397,9 +1413,9 @@ asmlinkage void do_cpu(struct pt_regs *regs)
		 */
		current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;

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

		break;

+2 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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;