10603839bSHuacai Chen // SPDX-License-Identifier: GPL-2.0 20603839bSHuacai Chen /* 30603839bSHuacai Chen * Author: Huacai Chen <chenhuacai@loongson.cn> 40603839bSHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 50603839bSHuacai Chen */ 60603839bSHuacai Chen #include <linux/bitops.h> 70603839bSHuacai Chen #include <linux/bug.h> 80603839bSHuacai Chen #include <linux/compiler.h> 90603839bSHuacai Chen #include <linux/context_tracking.h> 100603839bSHuacai Chen #include <linux/entry-common.h> 110603839bSHuacai Chen #include <linux/init.h> 120603839bSHuacai Chen #include <linux/kernel.h> 134e62d1d8SYouling Tang #include <linux/kexec.h> 140603839bSHuacai Chen #include <linux/module.h> 150603839bSHuacai Chen #include <linux/extable.h> 160603839bSHuacai Chen #include <linux/mm.h> 170603839bSHuacai Chen #include <linux/sched/mm.h> 180603839bSHuacai Chen #include <linux/sched/debug.h> 190603839bSHuacai Chen #include <linux/smp.h> 200603839bSHuacai Chen #include <linux/spinlock.h> 210603839bSHuacai Chen #include <linux/kallsyms.h> 220603839bSHuacai Chen #include <linux/memblock.h> 230603839bSHuacai Chen #include <linux/interrupt.h> 240603839bSHuacai Chen #include <linux/ptrace.h> 250603839bSHuacai Chen #include <linux/kgdb.h> 260603839bSHuacai Chen #include <linux/kdebug.h> 270603839bSHuacai Chen #include <linux/kprobes.h> 280603839bSHuacai Chen #include <linux/notifier.h> 290603839bSHuacai Chen #include <linux/irq.h> 300603839bSHuacai Chen #include <linux/perf_event.h> 310603839bSHuacai Chen 320603839bSHuacai Chen #include <asm/addrspace.h> 330603839bSHuacai Chen #include <asm/bootinfo.h> 340603839bSHuacai Chen #include <asm/branch.h> 350603839bSHuacai Chen #include <asm/break.h> 360603839bSHuacai Chen #include <asm/cpu.h> 370603839bSHuacai Chen #include <asm/fpu.h> 380603839bSHuacai Chen #include <asm/loongarch.h> 390603839bSHuacai Chen #include <asm/mmu_context.h> 400603839bSHuacai Chen #include <asm/pgtable.h> 410603839bSHuacai Chen #include <asm/ptrace.h> 420603839bSHuacai Chen #include <asm/sections.h> 430603839bSHuacai Chen #include <asm/siginfo.h> 440603839bSHuacai Chen #include <asm/stacktrace.h> 450603839bSHuacai Chen #include <asm/tlb.h> 460603839bSHuacai Chen #include <asm/types.h> 4749232773SQing Zhang #include <asm/unwind.h> 480603839bSHuacai Chen 490603839bSHuacai Chen #include "access-helper.h" 500603839bSHuacai Chen 510603839bSHuacai Chen extern asmlinkage void handle_ade(void); 520603839bSHuacai Chen extern asmlinkage void handle_ale(void); 530603839bSHuacai Chen extern asmlinkage void handle_sys(void); 540603839bSHuacai Chen extern asmlinkage void handle_bp(void); 550603839bSHuacai Chen extern asmlinkage void handle_ri(void); 560603839bSHuacai Chen extern asmlinkage void handle_fpu(void); 570603839bSHuacai Chen extern asmlinkage void handle_fpe(void); 580603839bSHuacai Chen extern asmlinkage void handle_lbt(void); 590603839bSHuacai Chen extern asmlinkage void handle_lsx(void); 600603839bSHuacai Chen extern asmlinkage void handle_lasx(void); 610603839bSHuacai Chen extern asmlinkage void handle_reserved(void); 620603839bSHuacai Chen extern asmlinkage void handle_watch(void); 630603839bSHuacai Chen extern asmlinkage void handle_vint(void); 640603839bSHuacai Chen 650603839bSHuacai Chen static void show_backtrace(struct task_struct *task, const struct pt_regs *regs, 660603839bSHuacai Chen const char *loglvl, bool user) 670603839bSHuacai Chen { 680603839bSHuacai Chen unsigned long addr; 6949232773SQing Zhang struct unwind_state state; 7049232773SQing Zhang struct pt_regs *pregs = (struct pt_regs *)regs; 7149232773SQing Zhang 7249232773SQing Zhang if (!task) 7349232773SQing Zhang task = current; 740603839bSHuacai Chen 750603839bSHuacai Chen printk("%sCall Trace:", loglvl); 7649232773SQing Zhang for (unwind_start(&state, task, pregs); 7749232773SQing Zhang !unwind_done(&state); unwind_next_frame(&state)) { 7849232773SQing Zhang addr = unwind_get_return_address(&state); 790603839bSHuacai Chen print_ip_sym(loglvl, addr); 800603839bSHuacai Chen } 810603839bSHuacai Chen printk("%s\n", loglvl); 820603839bSHuacai Chen } 830603839bSHuacai Chen 840603839bSHuacai Chen static void show_stacktrace(struct task_struct *task, 850603839bSHuacai Chen const struct pt_regs *regs, const char *loglvl, bool user) 860603839bSHuacai Chen { 870603839bSHuacai Chen int i; 880603839bSHuacai Chen const int field = 2 * sizeof(unsigned long); 890603839bSHuacai Chen unsigned long stackdata; 900603839bSHuacai Chen unsigned long *sp = (unsigned long *)regs->regs[3]; 910603839bSHuacai Chen 920603839bSHuacai Chen printk("%sStack :", loglvl); 930603839bSHuacai Chen i = 0; 940603839bSHuacai Chen while ((unsigned long) sp & (PAGE_SIZE - 1)) { 950603839bSHuacai Chen if (i && ((i % (64 / field)) == 0)) { 960603839bSHuacai Chen pr_cont("\n"); 970603839bSHuacai Chen printk("%s ", loglvl); 980603839bSHuacai Chen } 990603839bSHuacai Chen if (i > 39) { 1000603839bSHuacai Chen pr_cont(" ..."); 1010603839bSHuacai Chen break; 1020603839bSHuacai Chen } 1030603839bSHuacai Chen 1040603839bSHuacai Chen if (__get_addr(&stackdata, sp++, user)) { 1050603839bSHuacai Chen pr_cont(" (Bad stack address)"); 1060603839bSHuacai Chen break; 1070603839bSHuacai Chen } 1080603839bSHuacai Chen 1090603839bSHuacai Chen pr_cont(" %0*lx", field, stackdata); 1100603839bSHuacai Chen i++; 1110603839bSHuacai Chen } 1120603839bSHuacai Chen pr_cont("\n"); 1130603839bSHuacai Chen show_backtrace(task, regs, loglvl, user); 1140603839bSHuacai Chen } 1150603839bSHuacai Chen 1160603839bSHuacai Chen void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl) 1170603839bSHuacai Chen { 1180603839bSHuacai Chen struct pt_regs regs; 1190603839bSHuacai Chen 1200603839bSHuacai Chen regs.csr_crmd = 0; 1210603839bSHuacai Chen if (sp) { 1220603839bSHuacai Chen regs.csr_era = 0; 1230603839bSHuacai Chen regs.regs[1] = 0; 1240603839bSHuacai Chen regs.regs[3] = (unsigned long)sp; 1250603839bSHuacai Chen } else { 1260603839bSHuacai Chen if (!task || task == current) 1270603839bSHuacai Chen prepare_frametrace(®s); 1280603839bSHuacai Chen else { 1290603839bSHuacai Chen regs.csr_era = task->thread.reg01; 1300603839bSHuacai Chen regs.regs[1] = 0; 1310603839bSHuacai Chen regs.regs[3] = task->thread.reg03; 1320603839bSHuacai Chen regs.regs[22] = task->thread.reg22; 1330603839bSHuacai Chen } 1340603839bSHuacai Chen } 1350603839bSHuacai Chen 1360603839bSHuacai Chen show_stacktrace(task, ®s, loglvl, false); 1370603839bSHuacai Chen } 1380603839bSHuacai Chen 1390603839bSHuacai Chen static void show_code(unsigned int *pc, bool user) 1400603839bSHuacai Chen { 1410603839bSHuacai Chen long i; 1420603839bSHuacai Chen unsigned int insn; 1430603839bSHuacai Chen 1440603839bSHuacai Chen printk("Code:"); 1450603839bSHuacai Chen 1460603839bSHuacai Chen for(i = -3 ; i < 6 ; i++) { 1470603839bSHuacai Chen if (__get_inst(&insn, pc + i, user)) { 1480603839bSHuacai Chen pr_cont(" (Bad address in era)\n"); 1490603839bSHuacai Chen break; 1500603839bSHuacai Chen } 1510603839bSHuacai Chen pr_cont("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>')); 1520603839bSHuacai Chen } 1530603839bSHuacai Chen pr_cont("\n"); 1540603839bSHuacai Chen } 1550603839bSHuacai Chen 1560603839bSHuacai Chen static void __show_regs(const struct pt_regs *regs) 1570603839bSHuacai Chen { 1580603839bSHuacai Chen const int field = 2 * sizeof(unsigned long); 1590603839bSHuacai Chen unsigned int excsubcode; 1600603839bSHuacai Chen unsigned int exccode; 1610603839bSHuacai Chen int i; 1620603839bSHuacai Chen 1630603839bSHuacai Chen show_regs_print_info(KERN_DEFAULT); 1640603839bSHuacai Chen 1650603839bSHuacai Chen /* 1660603839bSHuacai Chen * Saved main processor registers 1670603839bSHuacai Chen */ 1680603839bSHuacai Chen for (i = 0; i < 32; ) { 1690603839bSHuacai Chen if ((i % 4) == 0) 1700603839bSHuacai Chen printk("$%2d :", i); 1710603839bSHuacai Chen pr_cont(" %0*lx", field, regs->regs[i]); 1720603839bSHuacai Chen 1730603839bSHuacai Chen i++; 1740603839bSHuacai Chen if ((i % 4) == 0) 1750603839bSHuacai Chen pr_cont("\n"); 1760603839bSHuacai Chen } 1770603839bSHuacai Chen 1780603839bSHuacai Chen /* 1790603839bSHuacai Chen * Saved csr registers 1800603839bSHuacai Chen */ 1810603839bSHuacai Chen printk("era : %0*lx %pS\n", field, regs->csr_era, 1820603839bSHuacai Chen (void *) regs->csr_era); 1830603839bSHuacai Chen printk("ra : %0*lx %pS\n", field, regs->regs[1], 1840603839bSHuacai Chen (void *) regs->regs[1]); 1850603839bSHuacai Chen 1860603839bSHuacai Chen printk("CSR crmd: %08lx ", regs->csr_crmd); 1870603839bSHuacai Chen printk("CSR prmd: %08lx ", regs->csr_prmd); 1880603839bSHuacai Chen printk("CSR euen: %08lx ", regs->csr_euen); 1890603839bSHuacai Chen printk("CSR ecfg: %08lx ", regs->csr_ecfg); 1900603839bSHuacai Chen printk("CSR estat: %08lx ", regs->csr_estat); 1910603839bSHuacai Chen 1920603839bSHuacai Chen pr_cont("\n"); 1930603839bSHuacai Chen 1940603839bSHuacai Chen exccode = ((regs->csr_estat) & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHIFT; 1950603839bSHuacai Chen excsubcode = ((regs->csr_estat) & CSR_ESTAT_ESUBCODE) >> CSR_ESTAT_ESUBCODE_SHIFT; 1960603839bSHuacai Chen printk("ExcCode : %x (SubCode %x)\n", exccode, excsubcode); 1970603839bSHuacai Chen 1980603839bSHuacai Chen if (exccode >= EXCCODE_TLBL && exccode <= EXCCODE_ALE) 1990603839bSHuacai Chen printk("BadVA : %0*lx\n", field, regs->csr_badvaddr); 2000603839bSHuacai Chen 2010603839bSHuacai Chen printk("PrId : %08x (%s)\n", read_cpucfg(LOONGARCH_CPUCFG0), 2020603839bSHuacai Chen cpu_family_string()); 2030603839bSHuacai Chen } 2040603839bSHuacai Chen 2050603839bSHuacai Chen void show_regs(struct pt_regs *regs) 2060603839bSHuacai Chen { 2070603839bSHuacai Chen __show_regs((struct pt_regs *)regs); 2080603839bSHuacai Chen dump_stack(); 2090603839bSHuacai Chen } 2100603839bSHuacai Chen 2110603839bSHuacai Chen void show_registers(struct pt_regs *regs) 2120603839bSHuacai Chen { 2130603839bSHuacai Chen __show_regs(regs); 2140603839bSHuacai Chen print_modules(); 2150603839bSHuacai Chen printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", 2160603839bSHuacai Chen current->comm, current->pid, current_thread_info(), current); 2170603839bSHuacai Chen 2180603839bSHuacai Chen show_stacktrace(current, regs, KERN_DEFAULT, user_mode(regs)); 2190603839bSHuacai Chen show_code((void *)regs->csr_era, user_mode(regs)); 2200603839bSHuacai Chen printk("\n"); 2210603839bSHuacai Chen } 2220603839bSHuacai Chen 2230603839bSHuacai Chen static DEFINE_RAW_SPINLOCK(die_lock); 2240603839bSHuacai Chen 2250603839bSHuacai Chen void __noreturn die(const char *str, struct pt_regs *regs) 2260603839bSHuacai Chen { 2270603839bSHuacai Chen static int die_counter; 2280603839bSHuacai Chen int sig = SIGSEGV; 2290603839bSHuacai Chen 2300603839bSHuacai Chen oops_enter(); 2310603839bSHuacai Chen 2320603839bSHuacai Chen if (notify_die(DIE_OOPS, str, regs, 0, current->thread.trap_nr, 2330603839bSHuacai Chen SIGSEGV) == NOTIFY_STOP) 2340603839bSHuacai Chen sig = 0; 2350603839bSHuacai Chen 2360603839bSHuacai Chen console_verbose(); 2370603839bSHuacai Chen raw_spin_lock_irq(&die_lock); 2380603839bSHuacai Chen bust_spinlocks(1); 2390603839bSHuacai Chen 2400603839bSHuacai Chen printk("%s[#%d]:\n", str, ++die_counter); 2410603839bSHuacai Chen show_registers(regs); 2420603839bSHuacai Chen add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); 2430603839bSHuacai Chen raw_spin_unlock_irq(&die_lock); 2440603839bSHuacai Chen 2450603839bSHuacai Chen oops_exit(); 2460603839bSHuacai Chen 2474e62d1d8SYouling Tang if (regs && kexec_should_crash(current)) 2484e62d1d8SYouling Tang crash_kexec(regs); 2494e62d1d8SYouling Tang 2500603839bSHuacai Chen if (in_interrupt()) 2510603839bSHuacai Chen panic("Fatal exception in interrupt"); 2520603839bSHuacai Chen 2530603839bSHuacai Chen if (panic_on_oops) 2540603839bSHuacai Chen panic("Fatal exception"); 2550603839bSHuacai Chen 2560603839bSHuacai Chen make_task_dead(sig); 2570603839bSHuacai Chen } 2580603839bSHuacai Chen 2590603839bSHuacai Chen static inline void setup_vint_size(unsigned int size) 2600603839bSHuacai Chen { 2610603839bSHuacai Chen unsigned int vs; 2620603839bSHuacai Chen 2630603839bSHuacai Chen vs = ilog2(size/4); 2640603839bSHuacai Chen 2650603839bSHuacai Chen if (vs == 0 || vs > 7) 2660603839bSHuacai Chen panic("vint_size %d Not support yet", vs); 2670603839bSHuacai Chen 2680603839bSHuacai Chen csr_xchg32(vs<<CSR_ECFG_VS_SHIFT, CSR_ECFG_VS, LOONGARCH_CSR_ECFG); 2690603839bSHuacai Chen } 2700603839bSHuacai Chen 2710603839bSHuacai Chen /* 2720603839bSHuacai Chen * Send SIGFPE according to FCSR Cause bits, which must have already 2730603839bSHuacai Chen * been masked against Enable bits. This is impotant as Inexact can 2740603839bSHuacai Chen * happen together with Overflow or Underflow, and `ptrace' can set 2750603839bSHuacai Chen * any bits. 2760603839bSHuacai Chen */ 2770603839bSHuacai Chen void force_fcsr_sig(unsigned long fcsr, void __user *fault_addr, 2780603839bSHuacai Chen struct task_struct *tsk) 2790603839bSHuacai Chen { 2800603839bSHuacai Chen int si_code = FPE_FLTUNK; 2810603839bSHuacai Chen 2820603839bSHuacai Chen if (fcsr & FPU_CSR_INV_X) 2830603839bSHuacai Chen si_code = FPE_FLTINV; 2840603839bSHuacai Chen else if (fcsr & FPU_CSR_DIV_X) 2850603839bSHuacai Chen si_code = FPE_FLTDIV; 2860603839bSHuacai Chen else if (fcsr & FPU_CSR_OVF_X) 2870603839bSHuacai Chen si_code = FPE_FLTOVF; 2880603839bSHuacai Chen else if (fcsr & FPU_CSR_UDF_X) 2890603839bSHuacai Chen si_code = FPE_FLTUND; 2900603839bSHuacai Chen else if (fcsr & FPU_CSR_INE_X) 2910603839bSHuacai Chen si_code = FPE_FLTRES; 2920603839bSHuacai Chen 2930603839bSHuacai Chen force_sig_fault(SIGFPE, si_code, fault_addr); 2940603839bSHuacai Chen } 2950603839bSHuacai Chen 2960603839bSHuacai Chen int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcsr) 2970603839bSHuacai Chen { 2980603839bSHuacai Chen int si_code; 2990603839bSHuacai Chen 3000603839bSHuacai Chen switch (sig) { 3010603839bSHuacai Chen case 0: 3020603839bSHuacai Chen return 0; 3030603839bSHuacai Chen 3040603839bSHuacai Chen case SIGFPE: 3050603839bSHuacai Chen force_fcsr_sig(fcsr, fault_addr, current); 3060603839bSHuacai Chen return 1; 3070603839bSHuacai Chen 3080603839bSHuacai Chen case SIGBUS: 3090603839bSHuacai Chen force_sig_fault(SIGBUS, BUS_ADRERR, fault_addr); 3100603839bSHuacai Chen return 1; 3110603839bSHuacai Chen 3120603839bSHuacai Chen case SIGSEGV: 3130603839bSHuacai Chen mmap_read_lock(current->mm); 3140603839bSHuacai Chen if (vma_lookup(current->mm, (unsigned long)fault_addr)) 3150603839bSHuacai Chen si_code = SEGV_ACCERR; 3160603839bSHuacai Chen else 3170603839bSHuacai Chen si_code = SEGV_MAPERR; 3180603839bSHuacai Chen mmap_read_unlock(current->mm); 3190603839bSHuacai Chen force_sig_fault(SIGSEGV, si_code, fault_addr); 3200603839bSHuacai Chen return 1; 3210603839bSHuacai Chen 3220603839bSHuacai Chen default: 3230603839bSHuacai Chen force_sig(sig); 3240603839bSHuacai Chen return 1; 3250603839bSHuacai Chen } 3260603839bSHuacai Chen } 3270603839bSHuacai Chen 3280603839bSHuacai Chen /* 3290603839bSHuacai Chen * Delayed fp exceptions when doing a lazy ctx switch 3300603839bSHuacai Chen */ 3310603839bSHuacai Chen asmlinkage void noinstr do_fpe(struct pt_regs *regs, unsigned long fcsr) 3320603839bSHuacai Chen { 3330603839bSHuacai Chen int sig; 3340603839bSHuacai Chen void __user *fault_addr; 3350603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 3360603839bSHuacai Chen 3370603839bSHuacai Chen if (notify_die(DIE_FP, "FP exception", regs, 0, current->thread.trap_nr, 3380603839bSHuacai Chen SIGFPE) == NOTIFY_STOP) 3390603839bSHuacai Chen goto out; 3400603839bSHuacai Chen 3410603839bSHuacai Chen /* Clear FCSR.Cause before enabling interrupts */ 3420603839bSHuacai Chen write_fcsr(LOONGARCH_FCSR0, fcsr & ~mask_fcsr_x(fcsr)); 3430603839bSHuacai Chen local_irq_enable(); 3440603839bSHuacai Chen 3450603839bSHuacai Chen die_if_kernel("FP exception in kernel code", regs); 3460603839bSHuacai Chen 3470603839bSHuacai Chen sig = SIGFPE; 3480603839bSHuacai Chen fault_addr = (void __user *) regs->csr_era; 3490603839bSHuacai Chen 3500603839bSHuacai Chen /* Send a signal if required. */ 3510603839bSHuacai Chen process_fpemu_return(sig, fault_addr, fcsr); 3520603839bSHuacai Chen 3530603839bSHuacai Chen out: 3540603839bSHuacai Chen local_irq_disable(); 3550603839bSHuacai Chen irqentry_exit(regs, state); 3560603839bSHuacai Chen } 3570603839bSHuacai Chen 3580603839bSHuacai Chen asmlinkage void noinstr do_ade(struct pt_regs *regs) 3590603839bSHuacai Chen { 3600603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 3610603839bSHuacai Chen 3620603839bSHuacai Chen die_if_kernel("Kernel ade access", regs); 3630603839bSHuacai Chen force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)regs->csr_badvaddr); 3640603839bSHuacai Chen 3650603839bSHuacai Chen irqentry_exit(regs, state); 3660603839bSHuacai Chen } 3670603839bSHuacai Chen 36861a6fcccSHuacai Chen /* sysctl hooks */ 36961a6fcccSHuacai Chen int unaligned_enabled __read_mostly = 1; /* Enabled by default */ 37061a6fcccSHuacai Chen int no_unaligned_warning __read_mostly = 1; /* Only 1 warning by default */ 37161a6fcccSHuacai Chen 3720603839bSHuacai Chen asmlinkage void noinstr do_ale(struct pt_regs *regs) 3730603839bSHuacai Chen { 3740603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 3750603839bSHuacai Chen 37641596803SHuacai Chen #ifndef CONFIG_ARCH_STRICT_ALIGN 37741596803SHuacai Chen die_if_kernel("Kernel ale access", regs); 37841596803SHuacai Chen force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)regs->csr_badvaddr); 37941596803SHuacai Chen #else 38041596803SHuacai Chen unsigned int *pc; 38141596803SHuacai Chen 38261a6fcccSHuacai Chen perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, regs->csr_badvaddr); 38361a6fcccSHuacai Chen 38461a6fcccSHuacai Chen /* 38561a6fcccSHuacai Chen * Did we catch a fault trying to load an instruction? 38661a6fcccSHuacai Chen */ 38761a6fcccSHuacai Chen if (regs->csr_badvaddr == regs->csr_era) 38861a6fcccSHuacai Chen goto sigbus; 38961a6fcccSHuacai Chen if (user_mode(regs) && !test_thread_flag(TIF_FIXADE)) 39061a6fcccSHuacai Chen goto sigbus; 39161a6fcccSHuacai Chen if (!unaligned_enabled) 39261a6fcccSHuacai Chen goto sigbus; 39361a6fcccSHuacai Chen if (!no_unaligned_warning) 39461a6fcccSHuacai Chen show_registers(regs); 39561a6fcccSHuacai Chen 39661a6fcccSHuacai Chen pc = (unsigned int *)exception_era(regs); 39761a6fcccSHuacai Chen 39861a6fcccSHuacai Chen emulate_load_store_insn(regs, (void __user *)regs->csr_badvaddr, pc); 39961a6fcccSHuacai Chen 40061a6fcccSHuacai Chen goto out; 40161a6fcccSHuacai Chen 40261a6fcccSHuacai Chen sigbus: 4030603839bSHuacai Chen die_if_kernel("Kernel ale access", regs); 4040603839bSHuacai Chen force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)regs->csr_badvaddr); 40561a6fcccSHuacai Chen out: 40641596803SHuacai Chen #endif 4070603839bSHuacai Chen irqentry_exit(regs, state); 4080603839bSHuacai Chen } 4090603839bSHuacai Chen 4102d2c3952SYouling Tang #ifdef CONFIG_GENERIC_BUG 4112d2c3952SYouling Tang int is_valid_bugaddr(unsigned long addr) 4122d2c3952SYouling Tang { 4132d2c3952SYouling Tang return 1; 4142d2c3952SYouling Tang } 4152d2c3952SYouling Tang #endif /* CONFIG_GENERIC_BUG */ 4162d2c3952SYouling Tang 4172d2c3952SYouling Tang static void bug_handler(struct pt_regs *regs) 4182d2c3952SYouling Tang { 4192d2c3952SYouling Tang switch (report_bug(regs->csr_era, regs)) { 4202d2c3952SYouling Tang case BUG_TRAP_TYPE_BUG: 4212d2c3952SYouling Tang case BUG_TRAP_TYPE_NONE: 4222d2c3952SYouling Tang die_if_kernel("Oops - BUG", regs); 4232d2c3952SYouling Tang force_sig(SIGTRAP); 4242d2c3952SYouling Tang break; 4252d2c3952SYouling Tang 4262d2c3952SYouling Tang case BUG_TRAP_TYPE_WARN: 4272d2c3952SYouling Tang /* Skip the BUG instruction and continue */ 4282d2c3952SYouling Tang regs->csr_era += LOONGARCH_INSN_SIZE; 4292d2c3952SYouling Tang break; 4302d2c3952SYouling Tang } 4312d2c3952SYouling Tang } 4322d2c3952SYouling Tang 4330603839bSHuacai Chen asmlinkage void noinstr do_bp(struct pt_regs *regs) 4340603839bSHuacai Chen { 4350603839bSHuacai Chen bool user = user_mode(regs); 4360603839bSHuacai Chen unsigned int opcode, bcode; 4370603839bSHuacai Chen unsigned long era = exception_era(regs); 4380603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 4390603839bSHuacai Chen 4400603839bSHuacai Chen local_irq_enable(); 4410603839bSHuacai Chen current->thread.trap_nr = read_csr_excode(); 4420603839bSHuacai Chen if (__get_inst(&opcode, (u32 *)era, user)) 4430603839bSHuacai Chen goto out_sigsegv; 4440603839bSHuacai Chen 4450603839bSHuacai Chen bcode = (opcode & 0x7fff); 4460603839bSHuacai Chen 4470603839bSHuacai Chen /* 4480603839bSHuacai Chen * notify the kprobe handlers, if instruction is likely to 4490603839bSHuacai Chen * pertain to them. 4500603839bSHuacai Chen */ 4510603839bSHuacai Chen switch (bcode) { 4520603839bSHuacai Chen case BRK_KPROBE_BP: 4530603839bSHuacai Chen if (notify_die(DIE_BREAK, "Kprobe", regs, bcode, 4540603839bSHuacai Chen current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) 4550603839bSHuacai Chen goto out; 4560603839bSHuacai Chen else 4570603839bSHuacai Chen break; 4580603839bSHuacai Chen case BRK_KPROBE_SSTEPBP: 4590603839bSHuacai Chen if (notify_die(DIE_SSTEPBP, "Kprobe_SingleStep", regs, bcode, 4600603839bSHuacai Chen current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) 4610603839bSHuacai Chen goto out; 4620603839bSHuacai Chen else 4630603839bSHuacai Chen break; 4640603839bSHuacai Chen case BRK_UPROBE_BP: 4650603839bSHuacai Chen if (notify_die(DIE_UPROBE, "Uprobe", regs, bcode, 4660603839bSHuacai Chen current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) 4670603839bSHuacai Chen goto out; 4680603839bSHuacai Chen else 4690603839bSHuacai Chen break; 4700603839bSHuacai Chen case BRK_UPROBE_XOLBP: 4710603839bSHuacai Chen if (notify_die(DIE_UPROBE_XOL, "Uprobe_XOL", regs, bcode, 4720603839bSHuacai Chen current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) 4730603839bSHuacai Chen goto out; 4740603839bSHuacai Chen else 4750603839bSHuacai Chen break; 4760603839bSHuacai Chen default: 4770603839bSHuacai Chen if (notify_die(DIE_TRAP, "Break", regs, bcode, 4780603839bSHuacai Chen current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) 4790603839bSHuacai Chen goto out; 4800603839bSHuacai Chen else 4810603839bSHuacai Chen break; 4820603839bSHuacai Chen } 4830603839bSHuacai Chen 4840603839bSHuacai Chen switch (bcode) { 4850603839bSHuacai Chen case BRK_BUG: 4862d2c3952SYouling Tang bug_handler(regs); 4870603839bSHuacai Chen break; 4880603839bSHuacai Chen case BRK_DIVZERO: 4890603839bSHuacai Chen die_if_kernel("Break instruction in kernel code", regs); 4900603839bSHuacai Chen force_sig_fault(SIGFPE, FPE_INTDIV, (void __user *)regs->csr_era); 4910603839bSHuacai Chen break; 4920603839bSHuacai Chen case BRK_OVERFLOW: 4930603839bSHuacai Chen die_if_kernel("Break instruction in kernel code", regs); 4940603839bSHuacai Chen force_sig_fault(SIGFPE, FPE_INTOVF, (void __user *)regs->csr_era); 4950603839bSHuacai Chen break; 4960603839bSHuacai Chen default: 4970603839bSHuacai Chen die_if_kernel("Break instruction in kernel code", regs); 4980603839bSHuacai Chen force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->csr_era); 4990603839bSHuacai Chen break; 5000603839bSHuacai Chen } 5010603839bSHuacai Chen 5020603839bSHuacai Chen out: 5030603839bSHuacai Chen local_irq_disable(); 5040603839bSHuacai Chen irqentry_exit(regs, state); 5050603839bSHuacai Chen return; 5060603839bSHuacai Chen 5070603839bSHuacai Chen out_sigsegv: 5080603839bSHuacai Chen force_sig(SIGSEGV); 5090603839bSHuacai Chen goto out; 5100603839bSHuacai Chen } 5110603839bSHuacai Chen 5120603839bSHuacai Chen asmlinkage void noinstr do_watch(struct pt_regs *regs) 5130603839bSHuacai Chen { 514edffa33cSQing Zhang irqentry_state_t state = irqentry_enter(regs); 515edffa33cSQing Zhang 516*424421a7SQing Zhang #ifndef CONFIG_HAVE_HW_BREAKPOINT 517*424421a7SQing Zhang pr_warn("Hardware watch point handler not implemented!\n"); 518*424421a7SQing Zhang #else 519*424421a7SQing Zhang if (test_tsk_thread_flag(current, TIF_SINGLESTEP)) { 520*424421a7SQing Zhang int llbit = (csr_read32(LOONGARCH_CSR_LLBCTL) & 0x1); 521*424421a7SQing Zhang unsigned long pc = instruction_pointer(regs); 522*424421a7SQing Zhang union loongarch_instruction *ip = (union loongarch_instruction *)pc; 523*424421a7SQing Zhang 524*424421a7SQing Zhang if (llbit) { 525*424421a7SQing Zhang /* 526*424421a7SQing Zhang * When the ll-sc combo is encountered, it is regarded as an single 527*424421a7SQing Zhang * instruction. So don't clear llbit and reset CSR.FWPS.Skip until 528*424421a7SQing Zhang * the llsc execution is completed. 529*424421a7SQing Zhang */ 530*424421a7SQing Zhang csr_write32(CSR_FWPC_SKIP, LOONGARCH_CSR_FWPS); 531*424421a7SQing Zhang csr_write32(CSR_LLBCTL_KLO, LOONGARCH_CSR_LLBCTL); 532*424421a7SQing Zhang goto out; 533*424421a7SQing Zhang } 534*424421a7SQing Zhang 535*424421a7SQing Zhang if (pc == current->thread.single_step) { 536*424421a7SQing Zhang /* 537*424421a7SQing Zhang * Certain insns are occasionally not skipped when CSR.FWPS.Skip is 538*424421a7SQing Zhang * set, such as fld.d/fst.d. So singlestep needs to compare whether 539*424421a7SQing Zhang * the csr_era is equal to the value of singlestep which last time set. 540*424421a7SQing Zhang */ 541*424421a7SQing Zhang if (!is_self_loop_ins(ip, regs)) { 542*424421a7SQing Zhang /* 543*424421a7SQing Zhang * Check if the given instruction the target pc is equal to the 544*424421a7SQing Zhang * current pc, If yes, then we should not set the CSR.FWPS.SKIP 545*424421a7SQing Zhang * bit to break the original instruction stream. 546*424421a7SQing Zhang */ 547*424421a7SQing Zhang csr_write32(CSR_FWPC_SKIP, LOONGARCH_CSR_FWPS); 548*424421a7SQing Zhang goto out; 549*424421a7SQing Zhang } 550*424421a7SQing Zhang } 551*424421a7SQing Zhang } else { 552edffa33cSQing Zhang breakpoint_handler(regs); 553edffa33cSQing Zhang watchpoint_handler(regs); 554*424421a7SQing Zhang } 555edffa33cSQing Zhang 556*424421a7SQing Zhang force_sig(SIGTRAP); 557*424421a7SQing Zhang out: 558*424421a7SQing Zhang #endif 559edffa33cSQing Zhang irqentry_exit(regs, state); 5600603839bSHuacai Chen } 5610603839bSHuacai Chen 5620603839bSHuacai Chen asmlinkage void noinstr do_ri(struct pt_regs *regs) 5630603839bSHuacai Chen { 56406e76aceSHuacai Chen int status = SIGILL; 5650603839bSHuacai Chen unsigned int opcode = 0; 5660603839bSHuacai Chen unsigned int __user *era = (unsigned int __user *)exception_era(regs); 5670603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 5680603839bSHuacai Chen 5690603839bSHuacai Chen local_irq_enable(); 5700603839bSHuacai Chen current->thread.trap_nr = read_csr_excode(); 5710603839bSHuacai Chen 5720603839bSHuacai Chen if (notify_die(DIE_RI, "RI Fault", regs, 0, current->thread.trap_nr, 5730603839bSHuacai Chen SIGILL) == NOTIFY_STOP) 5740603839bSHuacai Chen goto out; 5750603839bSHuacai Chen 5760603839bSHuacai Chen die_if_kernel("Reserved instruction in kernel code", regs); 5770603839bSHuacai Chen 5780603839bSHuacai Chen if (unlikely(get_user(opcode, era) < 0)) { 5790603839bSHuacai Chen status = SIGSEGV; 5800603839bSHuacai Chen current->thread.error_code = 1; 5810603839bSHuacai Chen } 5820603839bSHuacai Chen 5830603839bSHuacai Chen force_sig(status); 5840603839bSHuacai Chen 5850603839bSHuacai Chen out: 5860603839bSHuacai Chen local_irq_disable(); 5870603839bSHuacai Chen irqentry_exit(regs, state); 5880603839bSHuacai Chen } 5890603839bSHuacai Chen 5900603839bSHuacai Chen static void init_restore_fp(void) 5910603839bSHuacai Chen { 5920603839bSHuacai Chen if (!used_math()) { 5930603839bSHuacai Chen /* First time FP context user. */ 5940603839bSHuacai Chen init_fpu(); 5950603839bSHuacai Chen } else { 5960603839bSHuacai Chen /* This task has formerly used the FP context */ 5970603839bSHuacai Chen if (!is_fpu_owner()) 5980603839bSHuacai Chen own_fpu_inatomic(1); 5990603839bSHuacai Chen } 6000603839bSHuacai Chen 6010603839bSHuacai Chen BUG_ON(!is_fp_enabled()); 6020603839bSHuacai Chen } 6030603839bSHuacai Chen 6040603839bSHuacai Chen asmlinkage void noinstr do_fpu(struct pt_regs *regs) 6050603839bSHuacai Chen { 6060603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 6070603839bSHuacai Chen 6080603839bSHuacai Chen local_irq_enable(); 6090603839bSHuacai Chen die_if_kernel("do_fpu invoked from kernel context!", regs); 6100603839bSHuacai Chen 6110603839bSHuacai Chen preempt_disable(); 6120603839bSHuacai Chen init_restore_fp(); 6130603839bSHuacai Chen preempt_enable(); 6140603839bSHuacai Chen 6150603839bSHuacai Chen local_irq_disable(); 6160603839bSHuacai Chen irqentry_exit(regs, state); 6170603839bSHuacai Chen } 6180603839bSHuacai Chen 6190603839bSHuacai Chen asmlinkage void noinstr do_lsx(struct pt_regs *regs) 6200603839bSHuacai Chen { 6210603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 6220603839bSHuacai Chen 6230603839bSHuacai Chen local_irq_enable(); 6240603839bSHuacai Chen force_sig(SIGILL); 6250603839bSHuacai Chen local_irq_disable(); 6260603839bSHuacai Chen 6270603839bSHuacai Chen irqentry_exit(regs, state); 6280603839bSHuacai Chen } 6290603839bSHuacai Chen 6300603839bSHuacai Chen asmlinkage void noinstr do_lasx(struct pt_regs *regs) 6310603839bSHuacai Chen { 6320603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 6330603839bSHuacai Chen 6340603839bSHuacai Chen local_irq_enable(); 6350603839bSHuacai Chen force_sig(SIGILL); 6360603839bSHuacai Chen local_irq_disable(); 6370603839bSHuacai Chen 6380603839bSHuacai Chen irqentry_exit(regs, state); 6390603839bSHuacai Chen } 6400603839bSHuacai Chen 6410603839bSHuacai Chen asmlinkage void noinstr do_lbt(struct pt_regs *regs) 6420603839bSHuacai Chen { 6430603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 6440603839bSHuacai Chen 6450603839bSHuacai Chen local_irq_enable(); 6460603839bSHuacai Chen force_sig(SIGILL); 6470603839bSHuacai Chen local_irq_disable(); 6480603839bSHuacai Chen 6490603839bSHuacai Chen irqentry_exit(regs, state); 6500603839bSHuacai Chen } 6510603839bSHuacai Chen 6520603839bSHuacai Chen asmlinkage void noinstr do_reserved(struct pt_regs *regs) 6530603839bSHuacai Chen { 6540603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 6550603839bSHuacai Chen 6560603839bSHuacai Chen local_irq_enable(); 6570603839bSHuacai Chen /* 6580603839bSHuacai Chen * Game over - no way to handle this if it ever occurs. Most probably 6590603839bSHuacai Chen * caused by a fatal error after another hardware/software error. 6600603839bSHuacai Chen */ 6610603839bSHuacai Chen pr_err("Caught reserved exception %u on pid:%d [%s] - should not happen\n", 6620603839bSHuacai Chen read_csr_excode(), current->pid, current->comm); 6630603839bSHuacai Chen die_if_kernel("do_reserved exception", regs); 6640603839bSHuacai Chen force_sig(SIGUNUSED); 6650603839bSHuacai Chen 6660603839bSHuacai Chen local_irq_disable(); 6670603839bSHuacai Chen 6680603839bSHuacai Chen irqentry_exit(regs, state); 6690603839bSHuacai Chen } 6700603839bSHuacai Chen 6710603839bSHuacai Chen asmlinkage void cache_parity_error(void) 6720603839bSHuacai Chen { 6730603839bSHuacai Chen /* For the moment, report the problem and hang. */ 6740603839bSHuacai Chen pr_err("Cache error exception:\n"); 6750603839bSHuacai Chen pr_err("csr_merrctl == %08x\n", csr_read32(LOONGARCH_CSR_MERRCTL)); 6760603839bSHuacai Chen pr_err("csr_merrera == %016llx\n", csr_read64(LOONGARCH_CSR_MERRERA)); 6770603839bSHuacai Chen panic("Can't handle the cache error!"); 6780603839bSHuacai Chen } 6790603839bSHuacai Chen 6800603839bSHuacai Chen asmlinkage void noinstr handle_loongarch_irq(struct pt_regs *regs) 6810603839bSHuacai Chen { 6820603839bSHuacai Chen struct pt_regs *old_regs; 6830603839bSHuacai Chen 6840603839bSHuacai Chen irq_enter_rcu(); 6850603839bSHuacai Chen old_regs = set_irq_regs(regs); 6860603839bSHuacai Chen handle_arch_irq(regs); 6870603839bSHuacai Chen set_irq_regs(old_regs); 6880603839bSHuacai Chen irq_exit_rcu(); 6890603839bSHuacai Chen } 6900603839bSHuacai Chen 6910603839bSHuacai Chen asmlinkage void noinstr do_vint(struct pt_regs *regs, unsigned long sp) 6920603839bSHuacai Chen { 6930603839bSHuacai Chen register int cpu; 6940603839bSHuacai Chen register unsigned long stack; 6950603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 6960603839bSHuacai Chen 6970603839bSHuacai Chen cpu = smp_processor_id(); 6980603839bSHuacai Chen 6990603839bSHuacai Chen if (on_irq_stack(cpu, sp)) 7000603839bSHuacai Chen handle_loongarch_irq(regs); 7010603839bSHuacai Chen else { 7020603839bSHuacai Chen stack = per_cpu(irq_stack, cpu) + IRQ_STACK_START; 7030603839bSHuacai Chen 7040603839bSHuacai Chen /* Save task's sp on IRQ stack for unwinding */ 7050603839bSHuacai Chen *(unsigned long *)stack = sp; 7060603839bSHuacai Chen 7070603839bSHuacai Chen __asm__ __volatile__( 7080603839bSHuacai Chen "move $s0, $sp \n" /* Preserve sp */ 7090603839bSHuacai Chen "move $sp, %[stk] \n" /* Switch stack */ 7100603839bSHuacai Chen "move $a0, %[regs] \n" 7110603839bSHuacai Chen "bl handle_loongarch_irq \n" 7120603839bSHuacai Chen "move $sp, $s0 \n" /* Restore sp */ 7130603839bSHuacai Chen : /* No outputs */ 7140603839bSHuacai Chen : [stk] "r" (stack), [regs] "r" (regs) 7150603839bSHuacai Chen : "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7", "$s0", 7160603839bSHuacai Chen "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8", 7170603839bSHuacai Chen "memory"); 7180603839bSHuacai Chen } 7190603839bSHuacai Chen 7200603839bSHuacai Chen irqentry_exit(regs, state); 7210603839bSHuacai Chen } 7220603839bSHuacai Chen 7230603839bSHuacai Chen unsigned long eentry; 7240603839bSHuacai Chen unsigned long tlbrentry; 7250603839bSHuacai Chen 7260603839bSHuacai Chen long exception_handlers[VECSIZE * 128 / sizeof(long)] __aligned(SZ_64K); 7270603839bSHuacai Chen 7280603839bSHuacai Chen static void configure_exception_vector(void) 7290603839bSHuacai Chen { 7300603839bSHuacai Chen eentry = (unsigned long)exception_handlers; 7310603839bSHuacai Chen tlbrentry = (unsigned long)exception_handlers + 80*VECSIZE; 7320603839bSHuacai Chen 7330603839bSHuacai Chen csr_write64(eentry, LOONGARCH_CSR_EENTRY); 7340603839bSHuacai Chen csr_write64(eentry, LOONGARCH_CSR_MERRENTRY); 7350603839bSHuacai Chen csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY); 7360603839bSHuacai Chen } 7370603839bSHuacai Chen 7380603839bSHuacai Chen void per_cpu_trap_init(int cpu) 7390603839bSHuacai Chen { 7400603839bSHuacai Chen unsigned int i; 7410603839bSHuacai Chen 7420603839bSHuacai Chen setup_vint_size(VECSIZE); 7430603839bSHuacai Chen 7440603839bSHuacai Chen configure_exception_vector(); 7450603839bSHuacai Chen 7460603839bSHuacai Chen if (!cpu_data[cpu].asid_cache) 7470603839bSHuacai Chen cpu_data[cpu].asid_cache = asid_first_version(cpu); 7480603839bSHuacai Chen 7490603839bSHuacai Chen mmgrab(&init_mm); 7500603839bSHuacai Chen current->active_mm = &init_mm; 7510603839bSHuacai Chen BUG_ON(current->mm); 7520603839bSHuacai Chen enter_lazy_tlb(&init_mm, current); 7530603839bSHuacai Chen 7540603839bSHuacai Chen /* Initialise exception handlers */ 7550603839bSHuacai Chen if (cpu == 0) 7560603839bSHuacai Chen for (i = 0; i < 64; i++) 7570603839bSHuacai Chen set_handler(i * VECSIZE, handle_reserved, VECSIZE); 7580603839bSHuacai Chen 759d4b6f156SHuacai Chen tlb_init(cpu); 7600603839bSHuacai Chen cpu_cache_init(); 7610603839bSHuacai Chen } 7620603839bSHuacai Chen 7630603839bSHuacai Chen /* Install CPU exception handler */ 7640603839bSHuacai Chen void set_handler(unsigned long offset, void *addr, unsigned long size) 7650603839bSHuacai Chen { 7660603839bSHuacai Chen memcpy((void *)(eentry + offset), addr, size); 7670603839bSHuacai Chen local_flush_icache_range(eentry + offset, eentry + offset + size); 7680603839bSHuacai Chen } 7690603839bSHuacai Chen 7700603839bSHuacai Chen static const char panic_null_cerr[] = 7710603839bSHuacai Chen "Trying to set NULL cache error exception handler\n"; 7720603839bSHuacai Chen 7730603839bSHuacai Chen /* 7740603839bSHuacai Chen * Install uncached CPU exception handler. 7750603839bSHuacai Chen * This is suitable only for the cache error exception which is the only 7760603839bSHuacai Chen * exception handler that is being run uncached. 7770603839bSHuacai Chen */ 7780603839bSHuacai Chen void set_merr_handler(unsigned long offset, void *addr, unsigned long size) 7790603839bSHuacai Chen { 7800603839bSHuacai Chen unsigned long uncached_eentry = TO_UNCACHE(__pa(eentry)); 7810603839bSHuacai Chen 7820603839bSHuacai Chen if (!addr) 7830603839bSHuacai Chen panic(panic_null_cerr); 7840603839bSHuacai Chen 7850603839bSHuacai Chen memcpy((void *)(uncached_eentry + offset), addr, size); 7860603839bSHuacai Chen } 7870603839bSHuacai Chen 7880603839bSHuacai Chen void __init trap_init(void) 7890603839bSHuacai Chen { 7900603839bSHuacai Chen long i; 7910603839bSHuacai Chen 7920603839bSHuacai Chen /* Set interrupt vector handler */ 7930603839bSHuacai Chen for (i = EXCCODE_INT_START; i < EXCCODE_INT_END; i++) 7940603839bSHuacai Chen set_handler(i * VECSIZE, handle_vint, VECSIZE); 7950603839bSHuacai Chen 7960603839bSHuacai Chen set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE); 7970603839bSHuacai Chen set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE); 7980603839bSHuacai Chen set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE); 7990603839bSHuacai Chen set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE); 8000603839bSHuacai Chen set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE); 8010603839bSHuacai Chen set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE); 8020603839bSHuacai Chen set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE); 8030603839bSHuacai Chen set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE); 8040603839bSHuacai Chen set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE); 8050603839bSHuacai Chen set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE); 8060603839bSHuacai Chen set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE); 8070603839bSHuacai Chen set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE); 8080603839bSHuacai Chen 8090603839bSHuacai Chen cache_error_setup(); 8100603839bSHuacai Chen 8110603839bSHuacai Chen local_flush_icache_range(eentry, eentry + 0x400); 8120603839bSHuacai Chen } 813