summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r--arch/mips/kernel/traps.c75
1 files changed, 57 insertions, 18 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index d053bf4..e971043 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -29,6 +29,7 @@
#include <linux/notifier.h>
#include <linux/kdb.h>
#include <linux/irq.h>
+#include <linux/perf_event.h>
#include <asm/bootinfo.h>
#include <asm/branch.h>
@@ -82,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);
@@ -576,10 +578,16 @@ static inline int simulate_sc(struct pt_regs *regs, unsigned int opcode)
*/
static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
{
- if ((opcode & OPCODE) == LL)
+ if ((opcode & OPCODE) == LL) {
+ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+ 1, 0, regs, 0);
return simulate_ll(regs, opcode);
- if ((opcode & OPCODE) == SC)
+ }
+ if ((opcode & OPCODE) == SC) {
+ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+ 1, 0, regs, 0);
return simulate_sc(regs, opcode);
+ }
return -1; /* Must be something else ... */
}
@@ -595,6 +603,8 @@ static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode)
if ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR) {
int rd = (opcode & RD) >> 11;
int rt = (opcode & RT) >> 16;
+ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+ 1, 0, regs, 0);
switch (rd) {
case 0: /* CPU number */
regs->regs[rt] = smp_processor_id();
@@ -630,8 +640,11 @@ static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode)
static int simulate_sync(struct pt_regs *regs, unsigned int opcode)
{
- if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC)
+ if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC) {
+ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
+ 1, 0, regs, 0);
return 0;
+ }
return -1; /* Must be something else ... */
}
@@ -649,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)
@@ -663,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
@@ -678,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
@@ -690,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)
@@ -984,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();
}
@@ -1469,6 +1507,7 @@ void __cpuinit per_cpu_trap_init(void)
{
unsigned int cpu = smp_processor_id();
unsigned int status_set = ST0_CU0;
+ unsigned int hwrena = cpu_hwrena_impl_bits;
#ifdef CONFIG_MIPS_MT_SMTC
int secondaryTC = 0;
int bootTC = (cpu == 0);
@@ -1501,14 +1540,14 @@ void __cpuinit per_cpu_trap_init(void)
change_c0_status(ST0_CU|ST0_MX|ST0_RE|ST0_FR|ST0_BEV|ST0_TS|ST0_KX|ST0_SX|ST0_UX,
status_set);
- if (cpu_has_mips_r2) {
- unsigned int enable = 0x0000000f | cpu_hwrena_impl_bits;
+ if (cpu_has_mips_r2)
+ hwrena |= 0x0000000f;
- if (!noulri && cpu_has_userlocal)
- enable |= (1 << 29);
+ if (!noulri && cpu_has_userlocal)
+ hwrena |= (1 << 29);
- write_c0_hwrena(enable);
- }
+ if (hwrena)
+ write_c0_hwrena(hwrena);
#ifdef CONFIG_MIPS_MT_SMTC
if (!secondaryTC) {
OpenPOWER on IntegriCloud