|
@@ -13,6 +13,7 @@
|
|
|
*/
|
|
|
#include <linux/bug.h>
|
|
|
#include <linux/compiler.h>
|
|
|
+#include <linux/context_tracking.h>
|
|
|
#include <linux/kexec.h>
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/kernel.h>
|
|
@@ -264,7 +265,7 @@ static void __show_regs(const struct pt_regs *regs)
|
|
|
|
|
|
printk("Status: %08x ", (uint32_t) regs->cp0_status);
|
|
|
|
|
|
- if (current_cpu_data.isa_level == MIPS_CPU_ISA_I) {
|
|
|
+ if (cpu_has_3kex) {
|
|
|
if (regs->cp0_status & ST0_KUO)
|
|
|
printk("KUo ");
|
|
|
if (regs->cp0_status & ST0_IEO)
|
|
@@ -277,7 +278,7 @@ static void __show_regs(const struct pt_regs *regs)
|
|
|
printk("KUc ");
|
|
|
if (regs->cp0_status & ST0_IEC)
|
|
|
printk("IEc ");
|
|
|
- } else {
|
|
|
+ } else if (cpu_has_4kex) {
|
|
|
if (regs->cp0_status & ST0_KX)
|
|
|
printk("KX ");
|
|
|
if (regs->cp0_status & ST0_SX)
|
|
@@ -423,7 +424,9 @@ asmlinkage void do_be(struct pt_regs *regs)
|
|
|
const struct exception_table_entry *fixup = NULL;
|
|
|
int data = regs->cp0_cause & 4;
|
|
|
int action = MIPS_BE_FATAL;
|
|
|
+ enum ctx_state prev_state;
|
|
|
|
|
|
+ prev_state = exception_enter();
|
|
|
/* XXX For now. Fixme, this searches the wrong table ... */
|
|
|
if (data && !user_mode(regs))
|
|
|
fixup = search_dbe_tables(exception_epc(regs));
|
|
@@ -436,11 +439,11 @@ asmlinkage void do_be(struct pt_regs *regs)
|
|
|
|
|
|
switch (action) {
|
|
|
case MIPS_BE_DISCARD:
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
case MIPS_BE_FIXUP:
|
|
|
if (fixup) {
|
|
|
regs->cp0_epc = fixup->nextinsn;
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
}
|
|
|
break;
|
|
|
default:
|
|
@@ -455,10 +458,13 @@ asmlinkage void do_be(struct pt_regs *regs)
|
|
|
field, regs->cp0_epc, field, regs->regs[31]);
|
|
|
if (notify_die(DIE_OOPS, "bus error", regs, 0, regs_to_trapnr(regs), SIGBUS)
|
|
|
== NOTIFY_STOP)
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
|
|
|
die_if_kernel("Oops", regs);
|
|
|
force_sig(SIGBUS, current);
|
|
|
+
|
|
|
+out:
|
|
|
+ exception_exit(prev_state);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -673,8 +679,10 @@ static int simulate_sync(struct pt_regs *regs, unsigned int opcode)
|
|
|
|
|
|
asmlinkage void do_ov(struct pt_regs *regs)
|
|
|
{
|
|
|
+ enum ctx_state prev_state;
|
|
|
siginfo_t info;
|
|
|
|
|
|
+ prev_state = exception_enter();
|
|
|
die_if_kernel("Integer overflow", regs);
|
|
|
|
|
|
info.si_code = FPE_INTOVF;
|
|
@@ -682,6 +690,7 @@ asmlinkage void do_ov(struct pt_regs *regs)
|
|
|
info.si_errno = 0;
|
|
|
info.si_addr = (void __user *) regs->cp0_epc;
|
|
|
force_sig_info(SIGFPE, &info, current);
|
|
|
+ exception_exit(prev_state);
|
|
|
}
|
|
|
|
|
|
int process_fpemu_return(int sig, void __user *fault_addr)
|
|
@@ -713,11 +722,13 @@ int process_fpemu_return(int sig, void __user *fault_addr)
|
|
|
*/
|
|
|
asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
|
|
|
{
|
|
|
+ enum ctx_state prev_state;
|
|
|
siginfo_t info = {0};
|
|
|
|
|
|
+ prev_state = exception_enter();
|
|
|
if (notify_die(DIE_FP, "FP exception", regs, 0, regs_to_trapnr(regs), SIGFPE)
|
|
|
== NOTIFY_STOP)
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
die_if_kernel("FP exception in kernel code", regs);
|
|
|
|
|
|
if (fcr31 & FPU_CSR_UNI_X) {
|
|
@@ -753,7 +764,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
|
|
|
/* If something went wrong, signal */
|
|
|
process_fpemu_return(sig, fault_addr);
|
|
|
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
} else if (fcr31 & FPU_CSR_INV_X)
|
|
|
info.si_code = FPE_FLTINV;
|
|
|
else if (fcr31 & FPU_CSR_DIV_X)
|
|
@@ -770,6 +781,9 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
|
|
|
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,
|
|
@@ -835,9 +849,11 @@ static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
|
|
|
asmlinkage void do_bp(struct pt_regs *regs)
|
|
|
{
|
|
|
unsigned int opcode, bcode;
|
|
|
+ enum ctx_state prev_state;
|
|
|
unsigned long epc;
|
|
|
u16 instr[2];
|
|
|
|
|
|
+ prev_state = exception_enter();
|
|
|
if (get_isa16_mode(regs->cp0_epc)) {
|
|
|
/* Calculate EPC. */
|
|
|
epc = exception_epc(regs);
|
|
@@ -852,7 +868,7 @@ asmlinkage void do_bp(struct pt_regs *regs)
|
|
|
goto out_sigsegv;
|
|
|
bcode = (instr[0] >> 6) & 0x3f;
|
|
|
do_trap_or_bp(regs, bcode, "Break");
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
}
|
|
|
} else {
|
|
|
if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
|
|
@@ -876,12 +892,12 @@ asmlinkage void do_bp(struct pt_regs *regs)
|
|
|
switch (bcode) {
|
|
|
case BRK_KPROBE_BP:
|
|
|
if (notify_die(DIE_BREAK, "debug", regs, bcode, regs_to_trapnr(regs), SIGTRAP) == NOTIFY_STOP)
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
else
|
|
|
break;
|
|
|
case BRK_KPROBE_SSTEPBP:
|
|
|
if (notify_die(DIE_SSTEPBP, "single_step", regs, bcode, regs_to_trapnr(regs), SIGTRAP) == NOTIFY_STOP)
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
else
|
|
|
break;
|
|
|
default:
|
|
@@ -889,18 +905,24 @@ asmlinkage void do_bp(struct pt_regs *regs)
|
|
|
}
|
|
|
|
|
|
do_trap_or_bp(regs, bcode, "Break");
|
|
|
+
|
|
|
+out:
|
|
|
+ exception_exit(prev_state);
|
|
|
return;
|
|
|
|
|
|
out_sigsegv:
|
|
|
force_sig(SIGSEGV, current);
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
asmlinkage void do_tr(struct pt_regs *regs)
|
|
|
{
|
|
|
u32 opcode, tcode = 0;
|
|
|
+ enum ctx_state prev_state;
|
|
|
u16 instr[2];
|
|
|
unsigned long epc = msk_isa16_mode(exception_epc(regs));
|
|
|
|
|
|
+ prev_state = exception_enter();
|
|
|
if (get_isa16_mode(regs->cp0_epc)) {
|
|
|
if (__get_user(instr[0], (u16 __user *)(epc + 0)) ||
|
|
|
__get_user(instr[1], (u16 __user *)(epc + 2)))
|
|
@@ -918,10 +940,14 @@ asmlinkage void do_tr(struct pt_regs *regs)
|
|
|
}
|
|
|
|
|
|
do_trap_or_bp(regs, tcode, "Trap");
|
|
|
+
|
|
|
+out:
|
|
|
+ exception_exit(prev_state);
|
|
|
return;
|
|
|
|
|
|
out_sigsegv:
|
|
|
force_sig(SIGSEGV, current);
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
asmlinkage void do_ri(struct pt_regs *regs)
|
|
@@ -929,17 +955,19 @@ 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];
|
|
|
+ enum ctx_state prev_state;
|
|
|
unsigned int opcode = 0;
|
|
|
int status = -1;
|
|
|
|
|
|
+ prev_state = exception_enter();
|
|
|
if (notify_die(DIE_RI, "RI Fault", regs, 0, regs_to_trapnr(regs), SIGILL)
|
|
|
== NOTIFY_STOP)
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
|
|
|
die_if_kernel("Reserved instruction in kernel code", regs);
|
|
|
|
|
|
if (unlikely(compute_return_epc(regs) < 0))
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
|
|
|
if (get_isa16_mode(regs->cp0_epc)) {
|
|
|
unsigned short mmop[2] = { 0 };
|
|
@@ -974,6 +1002,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
|
|
|
regs->regs[31] = old31;
|
|
|
force_sig(status, current);
|
|
|
}
|
|
|
+
|
|
|
+out:
|
|
|
+ exception_exit(prev_state);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1025,21 +1056,16 @@ static int default_cu2_call(struct notifier_block *nfb, unsigned long action,
|
|
|
{
|
|
|
struct pt_regs *regs = data;
|
|
|
|
|
|
- switch (action) {
|
|
|
- default:
|
|
|
- die_if_kernel("Unhandled kernel unaligned access or invalid "
|
|
|
+ die_if_kernel("COP2: Unhandled kernel unaligned access or invalid "
|
|
|
"instruction", regs);
|
|
|
- /* Fall through */
|
|
|
-
|
|
|
- case CU2_EXCEPTION:
|
|
|
- force_sig(SIGILL, current);
|
|
|
- }
|
|
|
+ force_sig(SIGILL, current);
|
|
|
|
|
|
return NOTIFY_OK;
|
|
|
}
|
|
|
|
|
|
asmlinkage void do_cpu(struct pt_regs *regs)
|
|
|
{
|
|
|
+ enum ctx_state prev_state;
|
|
|
unsigned int __user *epc;
|
|
|
unsigned long old_epc, old31;
|
|
|
unsigned int opcode;
|
|
@@ -1047,10 +1073,12 @@ asmlinkage void do_cpu(struct pt_regs *regs)
|
|
|
int status;
|
|
|
unsigned long __maybe_unused flags;
|
|
|
|
|
|
- die_if_kernel("do_cpu invoked from kernel context!", regs);
|
|
|
-
|
|
|
+ prev_state = exception_enter();
|
|
|
cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;
|
|
|
|
|
|
+ if (cpid != 2)
|
|
|
+ die_if_kernel("do_cpu invoked from kernel context!", regs);
|
|
|
+
|
|
|
switch (cpid) {
|
|
|
case 0:
|
|
|
epc = (unsigned int __user *)exception_epc(regs);
|
|
@@ -1060,7 +1088,7 @@ asmlinkage void do_cpu(struct pt_regs *regs)
|
|
|
status = -1;
|
|
|
|
|
|
if (unlikely(compute_return_epc(regs) < 0))
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
|
|
|
if (get_isa16_mode(regs->cp0_epc)) {
|
|
|
unsigned short mmop[2] = { 0 };
|
|
@@ -1093,7 +1121,7 @@ asmlinkage void do_cpu(struct pt_regs *regs)
|
|
|
force_sig(status, current);
|
|
|
}
|
|
|
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
|
|
|
case 3:
|
|
|
/*
|
|
@@ -1131,19 +1159,26 @@ asmlinkage void do_cpu(struct pt_regs *regs)
|
|
|
mt_ase_fp_affinity();
|
|
|
}
|
|
|
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
|
|
|
case 2:
|
|
|
raw_notifier_call_chain(&cu2_chain, CU2_EXCEPTION, regs);
|
|
|
- return;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
force_sig(SIGILL, current);
|
|
|
+
|
|
|
+out:
|
|
|
+ exception_exit(prev_state);
|
|
|
}
|
|
|
|
|
|
asmlinkage void do_mdmx(struct pt_regs *regs)
|
|
|
{
|
|
|
+ enum ctx_state prev_state;
|
|
|
+
|
|
|
+ prev_state = exception_enter();
|
|
|
force_sig(SIGILL, current);
|
|
|
+ exception_exit(prev_state);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1151,8 +1186,10 @@ asmlinkage void do_mdmx(struct pt_regs *regs)
|
|
|
*/
|
|
|
asmlinkage void do_watch(struct pt_regs *regs)
|
|
|
{
|
|
|
+ enum ctx_state prev_state;
|
|
|
u32 cause;
|
|
|
|
|
|
+ prev_state = exception_enter();
|
|
|
/*
|
|
|
* Clear WP (bit 22) bit of cause register so we don't loop
|
|
|
* forever.
|
|
@@ -1174,13 +1211,16 @@ asmlinkage void do_watch(struct pt_regs *regs)
|
|
|
mips_clear_watch_registers();
|
|
|
local_irq_enable();
|
|
|
}
|
|
|
+ exception_exit(prev_state);
|
|
|
}
|
|
|
|
|
|
asmlinkage void do_mcheck(struct pt_regs *regs)
|
|
|
{
|
|
|
const int field = 2 * sizeof(unsigned long);
|
|
|
int multi_match = regs->cp0_status & ST0_TS;
|
|
|
+ enum ctx_state prev_state;
|
|
|
|
|
|
+ prev_state = exception_enter();
|
|
|
show_regs(regs);
|
|
|
|
|
|
if (multi_match) {
|
|
@@ -1202,6 +1242,7 @@ asmlinkage void do_mcheck(struct pt_regs *regs)
|
|
|
panic("Caught Machine Check exception - %scaused by multiple "
|
|
|
"matching entries in the TLB.",
|
|
|
(multi_match) ? "" : "not ");
|
|
|
+ exception_exit(prev_state);
|
|
|
}
|
|
|
|
|
|
asmlinkage void do_mt(struct pt_regs *regs)
|
|
@@ -1627,7 +1668,6 @@ void *set_vi_handler(int n, vi_handler_t addr)
|
|
|
}
|
|
|
|
|
|
extern void tlb_init(void);
|
|
|
-extern void flush_tlb_handlers(void);
|
|
|
|
|
|
/*
|
|
|
* Timer interrupt
|
|
@@ -1837,6 +1877,15 @@ void __init trap_init(void)
|
|
|
ebase += (read_c0_ebase() & 0x3ffff000);
|
|
|
}
|
|
|
|
|
|
+ if (cpu_has_mmips) {
|
|
|
+ unsigned int config3 = read_c0_config3();
|
|
|
+
|
|
|
+ if (IS_ENABLED(CONFIG_CPU_MICROMIPS))
|
|
|
+ write_c0_config3(config3 | MIPS_CONF3_ISA_OE);
|
|
|
+ else
|
|
|
+ write_c0_config3(config3 & ~MIPS_CONF3_ISA_OE);
|
|
|
+ }
|
|
|
+
|
|
|
if (board_ebase_setup)
|
|
|
board_ebase_setup();
|
|
|
per_cpu_trap_init(true);
|
|
@@ -1956,7 +2005,6 @@ void __init trap_init(void)
|
|
|
set_handler(0x080, &except_vec3_generic, 0x80);
|
|
|
|
|
|
local_flush_icache_range(ebase, ebase + 0x400);
|
|
|
- flush_tlb_handlers();
|
|
|
|
|
|
sort_extable(__start___dbe_table, __stop___dbe_table);
|
|
|
|