Newer
Older
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1994 - 1999, 2000, 01, 06 Ralf Baechle
* Copyright (C) 1995, 1996 Paul M. Antoine
* Copyright (C) 1998 Ulf Carlsson
* Copyright (C) 1999 Silicon Graphics, Inc.
* Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
* Copyright (C) 2002, 2003, 2004, 2005, 2007 Maciej W. Rozycki
* Copyright (C) 2000, 2001, 2012 MIPS Technologies, Inc. All rights reserved.
* Copyright (C) 2014, Imagination Technologies Ltd.
#include <linux/bug.h>
#include <linux/compiler.h>
#include <linux/context_tracking.h>
#include <linux/cpu_pm.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/kallsyms.h>
#include <linux/bootmem.h>
#include <linux/kgdb.h>
#include <linux/kdebug.h>
#include <linux/kdb.h>
#include <linux/perf_event.h>
#include <asm/bootinfo.h>
#include <asm/branch.h>
#include <asm/break.h>
#include <asm/cpu-type.h>
#include <asm/fpu_emulator.h>
#include <asm/idle.h>
#include <asm/mipsregs.h>
#include <asm/mipsmtregs.h>
#include <asm/pgtable.h>
#include <asm/ptrace.h>
#include <asm/sections.h>
#include <asm/tlbdebug.h>
#include <asm/traps.h>
#include <asm/uaccess.h>
#include <asm/watch.h>
#include <asm/stacktrace.h>
extern void check_wait(void);
extern asmlinkage void rollback_handle_int(void);
extern asmlinkage void handle_int(void);
extern u32 handle_tlbl[];
extern u32 handle_tlbs[];
extern u32 handle_tlbm[];
extern asmlinkage void handle_adel(void);
extern asmlinkage void handle_ades(void);
extern asmlinkage void handle_ibe(void);
extern asmlinkage void handle_dbe(void);
extern asmlinkage void handle_sys(void);
extern asmlinkage void handle_bp(void);
extern asmlinkage void handle_ri(void);
extern asmlinkage void handle_ri_rdhwr_vivt(void);
extern asmlinkage void handle_ri_rdhwr(void);
extern asmlinkage void handle_cpu(void);
extern asmlinkage void handle_ov(void);
extern asmlinkage void handle_tr(void);
extern asmlinkage void handle_msa_fpe(void);
extern asmlinkage void handle_msa(void);
extern asmlinkage void handle_mdmx(void);
extern asmlinkage void handle_watch(void);
extern asmlinkage void handle_dsp(void);
extern asmlinkage void handle_mcheck(void);
extern asmlinkage void handle_reserved(void);
extern void tlb_do_page_fault_0(void);
void (*board_be_init)(void);
int (*board_be_handler)(struct pt_regs *regs, int is_fixup);
void (*board_nmi_handler_setup)(void);
void (*board_ejtag_handler_setup)(void);
void (*board_bind_eic_interrupt)(int irq, int regset);
void(*board_cache_error_setup)(void);
static void show_raw_backtrace(unsigned long reg29)
unsigned long *sp = (unsigned long *)(reg29 & ~3);
unsigned long addr;
printk("Call Trace:");
#ifdef CONFIG_KALLSYMS
printk("\n");
#endif
while (!kstack_end(sp)) {
unsigned long __user *p =
(unsigned long __user *)(unsigned long)sp++;
if (__get_user(addr, p)) {
printk(" (Bad stack address)");
break;
if (__kernel_text_address(addr))
print_ip_sym(addr);
printk("\n");
#ifdef CONFIG_KALLSYMS
int raw_show_trace;
static int __init set_raw_show_trace(char *str)
{
raw_show_trace = 1;
return 1;
}
__setup("raw_show_trace", set_raw_show_trace);
#endif
static void show_backtrace(struct task_struct *task, const struct pt_regs *regs)
unsigned long sp = regs->regs[29];
unsigned long ra = regs->regs[31];
unsigned long pc = regs->cp0_epc;
if (raw_show_trace || !__kernel_text_address(pc)) {
show_raw_backtrace(sp);
return;
}
printk("Call Trace:\n");
print_ip_sym(pc);
pc = unwind_stack(task, &sp, pc, &ra);
printk("\n");
}
/*
* This routine abuses get_user()/put_user() to reference pointers
* with at least a bit of error checking ...
*/
static void show_stacktrace(struct task_struct *task,
const struct pt_regs *regs)
{
const int field = 2 * sizeof(unsigned long);
long stackdata;
int i;
unsigned long __user *sp = (unsigned long __user *)regs->regs[29];
printk("Stack :");
i = 0;
while ((unsigned long) sp & (PAGE_SIZE - 1)) {
if (i && ((i % (64 / field)) == 0))
if (i > 39) {
printk(" ...");
break;
}
if (__get_user(stackdata, sp++)) {
printk(" (Bad stack address)");
break;
}
printk(" %0*lx", field, stackdata);
i++;
}
printk("\n");
show_backtrace(task, regs);
}
void show_stack(struct task_struct *task, unsigned long *sp)
{
struct pt_regs regs;
if (sp) {
regs.regs[29] = (unsigned long)sp;
regs.regs[31] = 0;
regs.cp0_epc = 0;
} else {
if (task && task != current) {
regs.regs[29] = task->thread.reg29;
regs.regs[31] = 0;
regs.cp0_epc = task->thread.reg31;
#ifdef CONFIG_KGDB_KDB
} else if (atomic_read(&kgdb_active) != -1 &&
kdb_current_regs) {
memcpy(®s, kdb_current_regs, sizeof(regs));
#endif /* CONFIG_KGDB_KDB */
} else {
prepare_frametrace(®s);
}
}
show_stacktrace(task, ®s);
static void show_code(unsigned int __user *pc)
unsigned short __user *pc16 = NULL;
if ((unsigned long)pc & 1)
pc16 = (unsigned short __user *)((unsigned long)pc & ~1);
if (pc16 ? __get_user(insn, pc16 + i) : __get_user(insn, pc + i)) {
printk("%c%0*x%c", (i?' ':'<'), pc16 ? 4 : 8, insn, (i?' ':'>'));
static void __show_regs(const struct pt_regs *regs)
{
const int field = 2 * sizeof(unsigned long);
unsigned int cause = regs->cp0_cause;
int i;
show_regs_print_info(KERN_DEFAULT);
/*
* Saved main processor registers
*/
for (i = 0; i < 32; ) {
if ((i % 4) == 0)
printk("$%2d :", i);
if (i == 0)
printk(" %0*lx", field, 0UL);
else if (i == 26 || i == 27)
printk(" %*s", field, "");
else
printk(" %0*lx", field, regs->regs[i]);
i++;
if ((i % 4) == 0)
printk("\n");
}
#ifdef CONFIG_CPU_HAS_SMARTMIPS
printk("Acx : %0*lx\n", field, regs->acx);
#endif
printk("Hi : %0*lx\n", field, regs->hi);
printk("Lo : %0*lx\n", field, regs->lo);
/*
* Saved cp0 registers
*/
printk("epc : %0*lx %pS\n", field, regs->cp0_epc,
(void *) regs->cp0_epc);
printk("ra : %0*lx %pS\n", field, regs->regs[31],
(void *) regs->regs[31]);
printk("Status: %08x ", (uint32_t) regs->cp0_status);
if (regs->cp0_status & ST0_KUO)
printk("KUo ");
if (regs->cp0_status & ST0_IEO)
printk("IEo ");
if (regs->cp0_status & ST0_KUP)
printk("KUp ");
if (regs->cp0_status & ST0_IEP)
printk("IEp ");
if (regs->cp0_status & ST0_KUC)
printk("KUc ");
if (regs->cp0_status & ST0_IEC)
printk("IEc ");
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
if (regs->cp0_status & ST0_KX)
printk("KX ");
if (regs->cp0_status & ST0_SX)
printk("SX ");
if (regs->cp0_status & ST0_UX)
printk("UX ");
switch (regs->cp0_status & ST0_KSU) {
case KSU_USER:
printk("USER ");
break;
case KSU_SUPERVISOR:
printk("SUPERVISOR ");
break;
case KSU_KERNEL:
printk("KERNEL ");
break;
default:
printk("BAD_MODE ");
break;
}
if (regs->cp0_status & ST0_ERL)
printk("ERL ");
if (regs->cp0_status & ST0_EXL)
printk("EXL ");
if (regs->cp0_status & ST0_IE)
printk("IE ");
}
printk("\n");
printk("Cause : %08x\n", cause);
cause = (cause & CAUSEF_EXCCODE) >> CAUSEB_EXCCODE;
if (1 <= cause && cause <= 5)
printk("BadVA : %0*lx\n", field, regs->cp0_badvaddr);
printk("PrId : %08x (%s)\n", read_c0_prid(),
cpu_name_string());
/*
* FIXME: really the generic show_regs should take a const pointer argument.
*/
void show_regs(struct pt_regs *regs)
{
__show_regs((struct pt_regs *)regs);
}
const int field = 2 * sizeof(unsigned long);
mm_segment_t old_fs = get_fs();
printk("Process %s (pid: %d, threadinfo=%p, task=%p, tls=%0*lx)\n",
current->comm, current->pid, current_thread_info(), current,
field, current_thread_info()->tp_value);
if (cpu_has_userlocal) {
unsigned long tls;
tls = read_c0_userlocal();
if (tls != current_thread_info()->tp_value)
printk("*HwTLS: %0*lx\n", field, tls);
}
if (!user_mode(regs))
/* Necessary for getting the correct stack content */
set_fs(KERNEL_DS);
show_stacktrace(current, regs);
show_code((unsigned int __user *) regs->cp0_epc);
set_fs(old_fs);
static int regs_to_trapnr(struct pt_regs *regs)
{
return (regs->cp0_cause >> 2) & 0x1f;
}
void __noreturn die(const char *str, struct pt_regs *regs)
if (notify_die(DIE_OOPS, str, regs, 0, regs_to_trapnr(regs),
SIGSEGV) == NOTIFY_STOP)
printk("%s[#%d]:\n", str, ++die_counter);
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops) {
printk(KERN_EMERG "Fatal exception: panic in 5 seconds");
ssleep(5);
panic("Fatal exception");
}
if (regs && kexec_should_crash(current))
crash_kexec(regs);
extern struct exception_table_entry __start___dbe_table[];
extern struct exception_table_entry __stop___dbe_table[];
__asm__(
" .section __dbe_table, \"a\"\n"
" .previous \n");
/* Given an address, look for it in the exception tables. */
static const struct exception_table_entry *search_dbe_tables(unsigned long addr)
{
const struct exception_table_entry *e;
e = search_extable(__start___dbe_table, __stop___dbe_table - 1, addr);
if (!e)
e = search_module_dbetables(addr);
return e;
}
asmlinkage void do_be(struct pt_regs *regs)
{
const int field = 2 * sizeof(unsigned long);
const struct exception_table_entry *fixup = NULL;
int data = regs->cp0_cause & 4;
int action = MIPS_BE_FATAL;
/* XXX For now. Fixme, this searches the wrong table ... */
if (data && !user_mode(regs))
fixup = search_dbe_tables(exception_epc(regs));
if (fixup)
action = MIPS_BE_FIXUP;
if (board_be_handler)
action = board_be_handler(regs, fixup != NULL);
case MIPS_BE_FIXUP:
if (fixup) {
regs->cp0_epc = fixup->nextinsn;
}
break;
default:
break;
}
/*
* Assume it would be too dangerous to continue ...
*/
printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n",
data ? "Data" : "Instruction",
field, regs->cp0_epc, field, regs->regs[31]);
if (notify_die(DIE_OOPS, "bus error", regs, 0, regs_to_trapnr(regs),
SIGBUS) == NOTIFY_STOP)
die_if_kernel("Oops", regs);
force_sig(SIGBUS, current);
out:
exception_exit(prev_state);
* ll/sc, rdhwr, sync emulation
*/
#define OPCODE 0xfc000000
#define BASE 0x03e00000
#define RT 0x001f0000
#define OFFSET 0x0000ffff
#define LL 0xc0000000
#define SC 0xe0000000
#define SPEC0 0x00000000
#define SPEC3 0x7c000000
#define RD 0x0000f800
#define FUNC 0x0000003f
/* microMIPS definitions */
#define MM_POOL32A_FUNC 0xfc00ffff
#define MM_RDHWR 0x00006b3c
#define MM_RS 0x001f0000
#define MM_RT 0x03e00000
/*
* The ll_bit is cleared by r*_switch.S
*/
unsigned int ll_bit;
struct task_struct *ll_task;
static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode)
long offset;
/*
* analyse the ll instruction that just caused a ri exception
* and put the referenced address to addr.
*/
/* sign extend offset */
offset = opcode & OFFSET;
offset <<= 16;
offset >>= 16;
((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset);
if ((unsigned long)vaddr & 3)
return SIGBUS;
if (get_user(value, vaddr))
return SIGSEGV;
preempt_disable();
if (ll_task == NULL || ll_task == current) {
ll_bit = 1;
} else {
ll_bit = 0;
}
ll_task = current;
preempt_enable();
regs->regs[(opcode & RT) >> 16] = value;
static inline int simulate_sc(struct pt_regs *regs, unsigned int opcode)
long offset;
/*
* analyse the sc instruction that just caused a ri exception
* and put the referenced address to addr.
*/
/* sign extend offset */
offset = opcode & OFFSET;
offset <<= 16;
offset >>= 16;
((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset);
if ((unsigned long)vaddr & 3)
return SIGBUS;
preempt_disable();
if (ll_bit == 0 || ll_task != current) {
regs->regs[reg] = 0;
preempt_enable();
if (put_user(regs->regs[reg], vaddr))
return SIGSEGV;
}
/*
* ll uses the opcode of lwc0 and sc uses the opcode of swc0. That is both
* opcodes are supposed to result in coprocessor unusable exceptions if
* executed on ll/sc-less processors. That's the theory. In practice a
* few processors such as NEC's VR4100 throw reserved instruction exceptions
* instead, so we're doing the emulation thing in both exception handlers.
*/
static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
if ((opcode & OPCODE) == LL) {
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
1, regs, 0);
return simulate_ll(regs, opcode);
}
if ((opcode & OPCODE) == SC) {
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
1, regs, 0);
return simulate_sc(regs, opcode);
return -1; /* Must be something else ... */
/*
* Simulate trapping 'rdhwr' instructions to provide user accessible
* registers not implemented in hardware.
static int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
struct thread_info *ti = task_thread_info(current);
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
1, regs, 0);
switch (rd) {
case 0: /* CPU number */
regs->regs[rt] = smp_processor_id();
return 0;
case 1: /* SYNCI length */
regs->regs[rt] = min(current_cpu_data.dcache.linesz,
current_cpu_data.icache.linesz);
return 0;
case 2: /* Read count register */
regs->regs[rt] = read_c0_count();
return 0;
case 3: /* Count register resolution */
switch (current_cpu_type()) {
case CPU_20KC:
case CPU_25KF:
regs->regs[rt] = 1;
break;
default:
regs->regs[rt] = 2;
}
return 0;
case 29:
regs->regs[rt] = ti->tp_value;
return 0;
default:
return -1;
}
}
static int simulate_rdhwr_normal(struct pt_regs *regs, unsigned int opcode)
{
if ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR) {
int rd = (opcode & RD) >> 11;
int rt = (opcode & RT) >> 16;
simulate_rdhwr(regs, rd, rt);
return 0;
}
/* Not ours. */
return -1;
}
static int simulate_rdhwr_mm(struct pt_regs *regs, unsigned short opcode)
{
if ((opcode & MM_POOL32A_FUNC) == MM_RDHWR) {
int rd = (opcode & MM_RS) >> 16;
int rt = (opcode & MM_RT) >> 21;
simulate_rdhwr(regs, rd, rt);
return 0;
static int simulate_sync(struct pt_regs *regs, unsigned int opcode)
{
if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC) {
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
1, regs, 0);
return -1; /* Must be something else ... */
die_if_kernel("Integer overflow", regs);
info.si_code = FPE_INTOVF;
info.si_signo = SIGFPE;
info.si_errno = 0;
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) {
down_read(¤t->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(¤t->mm->mmap_sem);
} 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 = {0};
if (notify_die(DIE_FP, "FP exception", regs, 0, regs_to_trapnr(regs),
SIGFPE) == NOTIFY_STOP)
die_if_kernel("FP exception in kernel code", regs);
void __user *fault_addr = NULL;
* Unimplemented operation exception. If we've got the full
* software emulator on-board, let's use it...
*
* Force FPU to dump state into task/thread context. We're
* moving a lot of data here for what is probably a single
* instruction, but the alternative is to pre-decode the FP
* register operands before invoking the emulator, which seems
* a bit extreme for what should be an infrequent event.
*/
/* Ensure 'resume' not overwrite saved fp context again. */
sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
&fault_addr);
/*
* We can't allow the emulated instruction to leave any of
* the cause bit set in $fcr31.
*/
current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
process_fpemu_return(sig, fault_addr);
} else 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);
out:
exception_exit(prev_state);
static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
const char *str)
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
if (kgdb_ll_trap(DIE_TRAP, str, regs, code, regs_to_trapnr(regs), SIGTRAP) == NOTIFY_STOP)
return;
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
if (notify_die(DIE_TRAP, str, regs, code, regs_to_trapnr(regs),
SIGTRAP) == NOTIFY_STOP)
* A short test says that IRIX 5.3 sends SIGTRAP for all trap
* insns, even for trap and break codes that indicate arithmetic
* failures. Weird ...
* But should we continue the brokenness??? --macro
*/
switch (code) {
case BRK_OVERFLOW:
case BRK_DIVZERO:
scnprintf(b, sizeof(b), "%s instruction in kernel code", str);
die_if_kernel(b, regs);
if (code == BRK_DIVZERO)
info.si_code = FPE_INTDIV;
else
info.si_code = FPE_INTOVF;
info.si_signo = SIGFPE;
info.si_errno = 0;
case BRK_BUG:
die_if_kernel("Kernel bug detected", regs);
force_sig(SIGTRAP, current);
case BRK_MEMU:
/*
* Address errors may be deliberately induced by the FPU
* emulator to retake control of the CPU after executing the
* instruction in the delay slot of an emulated branch.
*
* Terminate if exception was recognized as a delay slot return
* otherwise handle as normal.
*/
if (do_dsemulret(regs))
return;
die_if_kernel("Math emu break/trap", regs);
force_sig(SIGTRAP, current);
break;
scnprintf(b, sizeof(b), "%s instruction in kernel code", str);
die_if_kernel(b, regs);
}
asmlinkage void do_bp(struct pt_regs *regs)
{
unsigned int opcode, bcode;
unsigned long epc;
u16 instr[2];
mm_segment_t seg;
seg = get_fs();
if (!user_mode(regs))
set_fs(KERNEL_DS);
if (get_isa16_mode(regs->cp0_epc)) {
/* Calculate EPC. */
epc = exception_epc(regs);
if (cpu_has_mmips) {
if ((__get_user(instr[0], (u16 __user *)msk_isa16_mode(epc)) ||
(__get_user(instr[1], (u16 __user *)msk_isa16_mode(epc + 2)))))
goto out_sigsegv;
opcode = (instr[0] << 16) | instr[1];
/* MIPS16e mode */
if (__get_user(instr[0],
(u16 __user *)msk_isa16_mode(epc)))
bcode = (instr[0] >> 6) & 0x3f;
do_trap_or_bp(regs, bcode, "Break");
goto out;
if (__get_user(opcode,
(unsigned int __user *) exception_epc(regs)))
goto out_sigsegv;
}
/*
* There is the ancient bug in the MIPS assemblers that the break
* code starts left to bit 16 instead to bit 6 in the opcode.
* Gas is bug-compatible, but not always, grrr...
* We handle both cases with a simple heuristics. --macro
*/
bcode = ((opcode >> 6) & ((1 << 20) - 1));
if (bcode >= (1 << 10))
bcode >>= 10;
/*
* notify the kprobe handlers, if instruction is likely to
* pertain to them.
*/
switch (bcode) {
case BRK_KPROBE_BP:
if (notify_die(DIE_BREAK, "debug", regs, bcode,
regs_to_trapnr(regs), SIGTRAP) == NOTIFY_STOP)
if (notify_die(DIE_SSTEPBP, "single_step", regs, bcode,
regs_to_trapnr(regs), SIGTRAP) == NOTIFY_STOP)
else
break;
default:
break;
}
do_trap_or_bp(regs, bcode, "Break");
set_fs(seg);
out_sigsegv:
force_sig(SIGSEGV, current);
}
asmlinkage void do_tr(struct pt_regs *regs)
{
mm_segment_t seg;
unsigned long epc = msk_isa16_mode(exception_epc(regs));
seg = get_fs();
if (!user_mode(regs))
set_fs(get_ds());
if (get_isa16_mode(regs->cp0_epc)) {
if (__get_user(instr[0], (u16 __user *)(epc + 0)) ||
__get_user(instr[1], (u16 __user *)(epc + 2)))
opcode = (instr[0] << 16) | instr[1];
/* Immediate versions don't provide a code. */
if (!(opcode & OPCODE))
tcode = (opcode >> 12) & ((1 << 4) - 1);
} else {
if (__get_user(opcode, (u32 __user *)epc))
goto out_sigsegv;
/* Immediate versions don't provide a code. */
if (!(opcode & OPCODE))
tcode = (opcode >> 6) & ((1 << 10) - 1);
do_trap_or_bp(regs, tcode, "Trap");
set_fs(seg);
out_sigsegv:
force_sig(SIGSEGV, current);
}
asmlinkage void do_ri(struct pt_regs *regs)
{
unsigned int __user *epc = (unsigned int __user *)exception_epc(regs);
unsigned long old_epc = regs->cp0_epc;
unsigned long old31 = regs->regs[31];
unsigned int opcode = 0;
int status = -1;
if (notify_die(DIE_RI, "RI Fault", regs, 0, regs_to_trapnr(regs),
SIGILL) == NOTIFY_STOP)
die_if_kernel("Reserved instruction in kernel code", regs);
if (unlikely(compute_return_epc(regs) < 0))
if (get_isa16_mode(regs->cp0_epc)) {
unsigned short mmop[2] = { 0 };
if (unlikely(get_user(mmop[0], epc) < 0))
status = SIGSEGV;