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 */ 6efada2afSWANG Xuerui #include <linux/bitfield.h> 70603839bSHuacai Chen #include <linux/bitops.h> 80603839bSHuacai Chen #include <linux/bug.h> 90603839bSHuacai Chen #include <linux/compiler.h> 100603839bSHuacai Chen #include <linux/context_tracking.h> 110603839bSHuacai Chen #include <linux/entry-common.h> 120603839bSHuacai Chen #include <linux/init.h> 130603839bSHuacai Chen #include <linux/kernel.h> 144e62d1d8SYouling Tang #include <linux/kexec.h> 150603839bSHuacai Chen #include <linux/module.h> 160603839bSHuacai Chen #include <linux/extable.h> 170603839bSHuacai Chen #include <linux/mm.h> 180603839bSHuacai Chen #include <linux/sched/mm.h> 190603839bSHuacai Chen #include <linux/sched/debug.h> 200603839bSHuacai Chen #include <linux/smp.h> 210603839bSHuacai Chen #include <linux/spinlock.h> 220603839bSHuacai Chen #include <linux/kallsyms.h> 230603839bSHuacai Chen #include <linux/memblock.h> 240603839bSHuacai Chen #include <linux/interrupt.h> 250603839bSHuacai Chen #include <linux/ptrace.h> 260603839bSHuacai Chen #include <linux/kgdb.h> 270603839bSHuacai Chen #include <linux/kdebug.h> 280603839bSHuacai Chen #include <linux/kprobes.h> 290603839bSHuacai Chen #include <linux/notifier.h> 300603839bSHuacai Chen #include <linux/irq.h> 310603839bSHuacai Chen #include <linux/perf_event.h> 320603839bSHuacai Chen 330603839bSHuacai Chen #include <asm/addrspace.h> 340603839bSHuacai Chen #include <asm/bootinfo.h> 350603839bSHuacai Chen #include <asm/branch.h> 360603839bSHuacai Chen #include <asm/break.h> 370603839bSHuacai Chen #include <asm/cpu.h> 380603839bSHuacai Chen #include <asm/fpu.h> 39c23e7f01SWANG Xuerui #include <asm/inst.h> 400603839bSHuacai Chen #include <asm/loongarch.h> 410603839bSHuacai Chen #include <asm/mmu_context.h> 420603839bSHuacai Chen #include <asm/pgtable.h> 430603839bSHuacai Chen #include <asm/ptrace.h> 440603839bSHuacai Chen #include <asm/sections.h> 450603839bSHuacai Chen #include <asm/siginfo.h> 460603839bSHuacai Chen #include <asm/stacktrace.h> 470603839bSHuacai Chen #include <asm/tlb.h> 480603839bSHuacai Chen #include <asm/types.h> 4949232773SQing Zhang #include <asm/unwind.h> 5019bc6cb6STiezhu Yang #include <asm/uprobes.h> 510603839bSHuacai Chen 520603839bSHuacai Chen #include "access-helper.h" 530603839bSHuacai Chen 540603839bSHuacai Chen extern asmlinkage void handle_ade(void); 550603839bSHuacai Chen extern asmlinkage void handle_ale(void); 56c23e7f01SWANG Xuerui extern asmlinkage void handle_bce(void); 570603839bSHuacai Chen extern asmlinkage void handle_sys(void); 580603839bSHuacai Chen extern asmlinkage void handle_bp(void); 590603839bSHuacai Chen extern asmlinkage void handle_ri(void); 600603839bSHuacai Chen extern asmlinkage void handle_fpu(void); 610603839bSHuacai Chen extern asmlinkage void handle_fpe(void); 620603839bSHuacai Chen extern asmlinkage void handle_lbt(void); 630603839bSHuacai Chen extern asmlinkage void handle_lsx(void); 640603839bSHuacai Chen extern asmlinkage void handle_lasx(void); 650603839bSHuacai Chen extern asmlinkage void handle_reserved(void); 660603839bSHuacai Chen extern asmlinkage void handle_watch(void); 670603839bSHuacai Chen extern asmlinkage void handle_vint(void); 680603839bSHuacai Chen 690603839bSHuacai Chen static void show_backtrace(struct task_struct *task, const struct pt_regs *regs, 700603839bSHuacai Chen const char *loglvl, bool user) 710603839bSHuacai Chen { 720603839bSHuacai Chen unsigned long addr; 7349232773SQing Zhang struct unwind_state state; 7449232773SQing Zhang struct pt_regs *pregs = (struct pt_regs *)regs; 7549232773SQing Zhang 7649232773SQing Zhang if (!task) 7749232773SQing Zhang task = current; 780603839bSHuacai Chen 790603839bSHuacai Chen printk("%sCall Trace:", loglvl); 8049232773SQing Zhang for (unwind_start(&state, task, pregs); 8149232773SQing Zhang !unwind_done(&state); unwind_next_frame(&state)) { 8249232773SQing Zhang addr = unwind_get_return_address(&state); 830603839bSHuacai Chen print_ip_sym(loglvl, addr); 840603839bSHuacai Chen } 850603839bSHuacai Chen printk("%s\n", loglvl); 860603839bSHuacai Chen } 870603839bSHuacai Chen 880603839bSHuacai Chen static void show_stacktrace(struct task_struct *task, 890603839bSHuacai Chen const struct pt_regs *regs, const char *loglvl, bool user) 900603839bSHuacai Chen { 910603839bSHuacai Chen int i; 920603839bSHuacai Chen const int field = 2 * sizeof(unsigned long); 930603839bSHuacai Chen unsigned long stackdata; 940603839bSHuacai Chen unsigned long *sp = (unsigned long *)regs->regs[3]; 950603839bSHuacai Chen 960603839bSHuacai Chen printk("%sStack :", loglvl); 970603839bSHuacai Chen i = 0; 980603839bSHuacai Chen while ((unsigned long) sp & (PAGE_SIZE - 1)) { 990603839bSHuacai Chen if (i && ((i % (64 / field)) == 0)) { 1000603839bSHuacai Chen pr_cont("\n"); 1010603839bSHuacai Chen printk("%s ", loglvl); 1020603839bSHuacai Chen } 1030603839bSHuacai Chen if (i > 39) { 1040603839bSHuacai Chen pr_cont(" ..."); 1050603839bSHuacai Chen break; 1060603839bSHuacai Chen } 1070603839bSHuacai Chen 1080603839bSHuacai Chen if (__get_addr(&stackdata, sp++, user)) { 1090603839bSHuacai Chen pr_cont(" (Bad stack address)"); 1100603839bSHuacai Chen break; 1110603839bSHuacai Chen } 1120603839bSHuacai Chen 1130603839bSHuacai Chen pr_cont(" %0*lx", field, stackdata); 1140603839bSHuacai Chen i++; 1150603839bSHuacai Chen } 1160603839bSHuacai Chen pr_cont("\n"); 1170603839bSHuacai Chen show_backtrace(task, regs, loglvl, user); 1180603839bSHuacai Chen } 1190603839bSHuacai Chen 1200603839bSHuacai Chen void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl) 1210603839bSHuacai Chen { 1220603839bSHuacai Chen struct pt_regs regs; 1230603839bSHuacai Chen 1240603839bSHuacai Chen regs.csr_crmd = 0; 1250603839bSHuacai Chen if (sp) { 1260603839bSHuacai Chen regs.csr_era = 0; 1270603839bSHuacai Chen regs.regs[1] = 0; 1280603839bSHuacai Chen regs.regs[3] = (unsigned long)sp; 1290603839bSHuacai Chen } else { 1300603839bSHuacai Chen if (!task || task == current) 1310603839bSHuacai Chen prepare_frametrace(®s); 1320603839bSHuacai Chen else { 1330603839bSHuacai Chen regs.csr_era = task->thread.reg01; 1340603839bSHuacai Chen regs.regs[1] = 0; 1350603839bSHuacai Chen regs.regs[3] = task->thread.reg03; 1360603839bSHuacai Chen regs.regs[22] = task->thread.reg22; 1370603839bSHuacai Chen } 1380603839bSHuacai Chen } 1390603839bSHuacai Chen 1400603839bSHuacai Chen show_stacktrace(task, ®s, loglvl, false); 1410603839bSHuacai Chen } 1420603839bSHuacai Chen 1430603839bSHuacai Chen static void show_code(unsigned int *pc, bool user) 1440603839bSHuacai Chen { 1450603839bSHuacai Chen long i; 1460603839bSHuacai Chen unsigned int insn; 1470603839bSHuacai Chen 1480603839bSHuacai Chen printk("Code:"); 1490603839bSHuacai Chen 1500603839bSHuacai Chen for(i = -3 ; i < 6 ; i++) { 1510603839bSHuacai Chen if (__get_inst(&insn, pc + i, user)) { 1520603839bSHuacai Chen pr_cont(" (Bad address in era)\n"); 1530603839bSHuacai Chen break; 1540603839bSHuacai Chen } 1550603839bSHuacai Chen pr_cont("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>')); 1560603839bSHuacai Chen } 1570603839bSHuacai Chen pr_cont("\n"); 1580603839bSHuacai Chen } 1590603839bSHuacai Chen 160efada2afSWANG Xuerui static void print_bool_fragment(const char *key, unsigned long val, bool first) 161efada2afSWANG Xuerui { 162efada2afSWANG Xuerui /* e.g. "+PG", "-DA" */ 163efada2afSWANG Xuerui pr_cont("%s%c%s", first ? "" : " ", val ? '+' : '-', key); 164efada2afSWANG Xuerui } 165efada2afSWANG Xuerui 166efada2afSWANG Xuerui static void print_plv_fragment(const char *key, int val) 167efada2afSWANG Xuerui { 168efada2afSWANG Xuerui /* e.g. "PLV0", "PPLV3" */ 169efada2afSWANG Xuerui pr_cont("%s%d", key, val); 170efada2afSWANG Xuerui } 171efada2afSWANG Xuerui 172efada2afSWANG Xuerui static void print_memory_type_fragment(const char *key, unsigned long val) 173efada2afSWANG Xuerui { 174efada2afSWANG Xuerui const char *humanized_type; 175efada2afSWANG Xuerui 176efada2afSWANG Xuerui switch (val) { 177efada2afSWANG Xuerui case 0: 178efada2afSWANG Xuerui humanized_type = "SUC"; 179efada2afSWANG Xuerui break; 180efada2afSWANG Xuerui case 1: 181efada2afSWANG Xuerui humanized_type = "CC"; 182efada2afSWANG Xuerui break; 183efada2afSWANG Xuerui case 2: 184efada2afSWANG Xuerui humanized_type = "WUC"; 185efada2afSWANG Xuerui break; 186efada2afSWANG Xuerui default: 187efada2afSWANG Xuerui pr_cont(" %s=Reserved(%lu)", key, val); 188efada2afSWANG Xuerui return; 189efada2afSWANG Xuerui } 190efada2afSWANG Xuerui 191efada2afSWANG Xuerui /* e.g. " DATM=WUC" */ 192efada2afSWANG Xuerui pr_cont(" %s=%s", key, humanized_type); 193efada2afSWANG Xuerui } 194efada2afSWANG Xuerui 1955e3e784dSWANG Xuerui static void print_intr_fragment(const char *key, unsigned long val) 1965e3e784dSWANG Xuerui { 1975e3e784dSWANG Xuerui /* e.g. "LIE=0-1,3,5-7" */ 1985e3e784dSWANG Xuerui pr_cont("%s=%*pbl", key, EXCCODE_INT_NUM, &val); 1995e3e784dSWANG Xuerui } 2005e3e784dSWANG Xuerui 201efada2afSWANG Xuerui static void print_crmd(unsigned long x) 202efada2afSWANG Xuerui { 203efada2afSWANG Xuerui printk(" CRMD: %08lx (", x); 204efada2afSWANG Xuerui print_plv_fragment("PLV", (int) FIELD_GET(CSR_CRMD_PLV, x)); 205efada2afSWANG Xuerui print_bool_fragment("IE", FIELD_GET(CSR_CRMD_IE, x), false); 206efada2afSWANG Xuerui print_bool_fragment("DA", FIELD_GET(CSR_CRMD_DA, x), false); 207efada2afSWANG Xuerui print_bool_fragment("PG", FIELD_GET(CSR_CRMD_PG, x), false); 208efada2afSWANG Xuerui print_memory_type_fragment("DACF", FIELD_GET(CSR_CRMD_DACF, x)); 209efada2afSWANG Xuerui print_memory_type_fragment("DACM", FIELD_GET(CSR_CRMD_DACM, x)); 210efada2afSWANG Xuerui print_bool_fragment("WE", FIELD_GET(CSR_CRMD_WE, x), false); 211efada2afSWANG Xuerui pr_cont(")\n"); 212efada2afSWANG Xuerui } 213efada2afSWANG Xuerui 214ce7f0b18SWANG Xuerui static void print_prmd(unsigned long x) 215ce7f0b18SWANG Xuerui { 216ce7f0b18SWANG Xuerui printk(" PRMD: %08lx (", x); 217ce7f0b18SWANG Xuerui print_plv_fragment("PPLV", (int) FIELD_GET(CSR_PRMD_PPLV, x)); 218ce7f0b18SWANG Xuerui print_bool_fragment("PIE", FIELD_GET(CSR_PRMD_PIE, x), false); 219ce7f0b18SWANG Xuerui print_bool_fragment("PWE", FIELD_GET(CSR_PRMD_PWE, x), false); 220ce7f0b18SWANG Xuerui pr_cont(")\n"); 221ce7f0b18SWANG Xuerui } 222ce7f0b18SWANG Xuerui 2239718d96cSWANG Xuerui static void print_euen(unsigned long x) 2249718d96cSWANG Xuerui { 2259718d96cSWANG Xuerui printk(" EUEN: %08lx (", x); 2269718d96cSWANG Xuerui print_bool_fragment("FPE", FIELD_GET(CSR_EUEN_FPEN, x), true); 2279718d96cSWANG Xuerui print_bool_fragment("SXE", FIELD_GET(CSR_EUEN_LSXEN, x), false); 2289718d96cSWANG Xuerui print_bool_fragment("ASXE", FIELD_GET(CSR_EUEN_LASXEN, x), false); 2299718d96cSWANG Xuerui print_bool_fragment("BTE", FIELD_GET(CSR_EUEN_LBTEN, x), false); 2309718d96cSWANG Xuerui pr_cont(")\n"); 2319718d96cSWANG Xuerui } 2329718d96cSWANG Xuerui 2335e3e784dSWANG Xuerui static void print_ecfg(unsigned long x) 2345e3e784dSWANG Xuerui { 2355e3e784dSWANG Xuerui printk(" ECFG: %08lx (", x); 2365e3e784dSWANG Xuerui print_intr_fragment("LIE", FIELD_GET(CSR_ECFG_IM, x)); 2375e3e784dSWANG Xuerui pr_cont(" VS=%d)\n", (int) FIELD_GET(CSR_ECFG_VS, x)); 2385e3e784dSWANG Xuerui } 2395e3e784dSWANG Xuerui 24098b90edeSWANG Xuerui static const char *humanize_exc_name(unsigned int ecode, unsigned int esubcode) 24198b90edeSWANG Xuerui { 24298b90edeSWANG Xuerui /* 24398b90edeSWANG Xuerui * LoongArch users and developers are probably more familiar with 24498b90edeSWANG Xuerui * those names found in the ISA manual, so we are going to print out 24598b90edeSWANG Xuerui * the latter. This will require some mapping. 24698b90edeSWANG Xuerui */ 24798b90edeSWANG Xuerui switch (ecode) { 24898b90edeSWANG Xuerui case EXCCODE_RSV: return "INT"; 24998b90edeSWANG Xuerui case EXCCODE_TLBL: return "PIL"; 25098b90edeSWANG Xuerui case EXCCODE_TLBS: return "PIS"; 25198b90edeSWANG Xuerui case EXCCODE_TLBI: return "PIF"; 25298b90edeSWANG Xuerui case EXCCODE_TLBM: return "PME"; 25398b90edeSWANG Xuerui case EXCCODE_TLBNR: return "PNR"; 25498b90edeSWANG Xuerui case EXCCODE_TLBNX: return "PNX"; 25598b90edeSWANG Xuerui case EXCCODE_TLBPE: return "PPI"; 25698b90edeSWANG Xuerui case EXCCODE_ADE: 25798b90edeSWANG Xuerui switch (esubcode) { 25898b90edeSWANG Xuerui case EXSUBCODE_ADEF: return "ADEF"; 25998b90edeSWANG Xuerui case EXSUBCODE_ADEM: return "ADEM"; 26098b90edeSWANG Xuerui } 26198b90edeSWANG Xuerui break; 26298b90edeSWANG Xuerui case EXCCODE_ALE: return "ALE"; 26398b90edeSWANG Xuerui case EXCCODE_BCE: return "BCE"; 26498b90edeSWANG Xuerui case EXCCODE_SYS: return "SYS"; 26598b90edeSWANG Xuerui case EXCCODE_BP: return "BRK"; 26698b90edeSWANG Xuerui case EXCCODE_INE: return "INE"; 26798b90edeSWANG Xuerui case EXCCODE_IPE: return "IPE"; 26898b90edeSWANG Xuerui case EXCCODE_FPDIS: return "FPD"; 26998b90edeSWANG Xuerui case EXCCODE_LSXDIS: return "SXD"; 27098b90edeSWANG Xuerui case EXCCODE_LASXDIS: return "ASXD"; 27198b90edeSWANG Xuerui case EXCCODE_FPE: 27298b90edeSWANG Xuerui switch (esubcode) { 27398b90edeSWANG Xuerui case EXCSUBCODE_FPE: return "FPE"; 27498b90edeSWANG Xuerui case EXCSUBCODE_VFPE: return "VFPE"; 27598b90edeSWANG Xuerui } 27698b90edeSWANG Xuerui break; 27798b90edeSWANG Xuerui case EXCCODE_WATCH: 27898b90edeSWANG Xuerui switch (esubcode) { 27998b90edeSWANG Xuerui case EXCSUBCODE_WPEF: return "WPEF"; 28098b90edeSWANG Xuerui case EXCSUBCODE_WPEM: return "WPEM"; 28198b90edeSWANG Xuerui } 28298b90edeSWANG Xuerui break; 28398b90edeSWANG Xuerui case EXCCODE_BTDIS: return "BTD"; 28498b90edeSWANG Xuerui case EXCCODE_BTE: return "BTE"; 28598b90edeSWANG Xuerui case EXCCODE_GSPR: return "GSPR"; 28698b90edeSWANG Xuerui case EXCCODE_HVC: return "HVC"; 28798b90edeSWANG Xuerui case EXCCODE_GCM: 28898b90edeSWANG Xuerui switch (esubcode) { 28998b90edeSWANG Xuerui case EXCSUBCODE_GCSC: return "GCSC"; 29098b90edeSWANG Xuerui case EXCSUBCODE_GCHC: return "GCHC"; 29198b90edeSWANG Xuerui } 29298b90edeSWANG Xuerui break; 29398b90edeSWANG Xuerui /* 29498b90edeSWANG Xuerui * The manual did not mention the EXCCODE_SE case, but print out it 29598b90edeSWANG Xuerui * nevertheless. 29698b90edeSWANG Xuerui */ 29798b90edeSWANG Xuerui case EXCCODE_SE: return "SE"; 29898b90edeSWANG Xuerui } 29998b90edeSWANG Xuerui 30098b90edeSWANG Xuerui return "???"; 30198b90edeSWANG Xuerui } 30298b90edeSWANG Xuerui 30398b90edeSWANG Xuerui static void print_estat(unsigned long x) 30498b90edeSWANG Xuerui { 30598b90edeSWANG Xuerui unsigned int ecode = FIELD_GET(CSR_ESTAT_EXC, x); 30698b90edeSWANG Xuerui unsigned int esubcode = FIELD_GET(CSR_ESTAT_ESUBCODE, x); 30798b90edeSWANG Xuerui 30898b90edeSWANG Xuerui printk("ESTAT: %08lx [%s] (", x, humanize_exc_name(ecode, esubcode)); 30998b90edeSWANG Xuerui print_intr_fragment("IS", FIELD_GET(CSR_ESTAT_IS, x)); 31098b90edeSWANG Xuerui pr_cont(" ECode=%d EsubCode=%d)\n", (int) ecode, (int) esubcode); 31198b90edeSWANG Xuerui } 31298b90edeSWANG Xuerui 3130603839bSHuacai Chen static void __show_regs(const struct pt_regs *regs) 3140603839bSHuacai Chen { 3150603839bSHuacai Chen const int field = 2 * sizeof(unsigned long); 31698b90edeSWANG Xuerui unsigned int exccode = FIELD_GET(CSR_ESTAT_EXC, regs->csr_estat); 3170603839bSHuacai Chen 3180603839bSHuacai Chen show_regs_print_info(KERN_DEFAULT); 3190603839bSHuacai Chen 320f6a79b60SWANG Xuerui /* Print saved GPRs except $zero (substituting with PC/ERA) */ 321f6a79b60SWANG Xuerui #define GPR_FIELD(x) field, regs->regs[x] 322f6a79b60SWANG Xuerui printk("pc %0*lx ra %0*lx tp %0*lx sp %0*lx\n", 323f6a79b60SWANG Xuerui field, regs->csr_era, GPR_FIELD(1), GPR_FIELD(2), GPR_FIELD(3)); 324f6a79b60SWANG Xuerui printk("a0 %0*lx a1 %0*lx a2 %0*lx a3 %0*lx\n", 325f6a79b60SWANG Xuerui GPR_FIELD(4), GPR_FIELD(5), GPR_FIELD(6), GPR_FIELD(7)); 326f6a79b60SWANG Xuerui printk("a4 %0*lx a5 %0*lx a6 %0*lx a7 %0*lx\n", 327f6a79b60SWANG Xuerui GPR_FIELD(8), GPR_FIELD(9), GPR_FIELD(10), GPR_FIELD(11)); 328f6a79b60SWANG Xuerui printk("t0 %0*lx t1 %0*lx t2 %0*lx t3 %0*lx\n", 329f6a79b60SWANG Xuerui GPR_FIELD(12), GPR_FIELD(13), GPR_FIELD(14), GPR_FIELD(15)); 330f6a79b60SWANG Xuerui printk("t4 %0*lx t5 %0*lx t6 %0*lx t7 %0*lx\n", 331f6a79b60SWANG Xuerui GPR_FIELD(16), GPR_FIELD(17), GPR_FIELD(18), GPR_FIELD(19)); 332f6a79b60SWANG Xuerui printk("t8 %0*lx u0 %0*lx s9 %0*lx s0 %0*lx\n", 333f6a79b60SWANG Xuerui GPR_FIELD(20), GPR_FIELD(21), GPR_FIELD(22), GPR_FIELD(23)); 334f6a79b60SWANG Xuerui printk("s1 %0*lx s2 %0*lx s3 %0*lx s4 %0*lx\n", 335f6a79b60SWANG Xuerui GPR_FIELD(24), GPR_FIELD(25), GPR_FIELD(26), GPR_FIELD(27)); 336f6a79b60SWANG Xuerui printk("s5 %0*lx s6 %0*lx s7 %0*lx s8 %0*lx\n", 337f6a79b60SWANG Xuerui GPR_FIELD(28), GPR_FIELD(29), GPR_FIELD(30), GPR_FIELD(31)); 3380603839bSHuacai Chen 339f6a79b60SWANG Xuerui /* The slot for $zero is reused as the syscall restart flag */ 340f6a79b60SWANG Xuerui if (regs->regs[0]) 341f6a79b60SWANG Xuerui printk("syscall restart flag: %0*lx\n", GPR_FIELD(0)); 342863b3795SWANG Xuerui 343863b3795SWANG Xuerui if (user_mode(regs)) { 344863b3795SWANG Xuerui printk(" ra: %0*lx\n", GPR_FIELD(1)); 345863b3795SWANG Xuerui printk(" ERA: %0*lx\n", field, regs->csr_era); 346863b3795SWANG Xuerui } else { 347863b3795SWANG Xuerui printk(" ra: %0*lx %pS\n", GPR_FIELD(1), (void *) regs->regs[1]); 348863b3795SWANG Xuerui printk(" ERA: %0*lx %pS\n", field, regs->csr_era, (void *) regs->csr_era); 349863b3795SWANG Xuerui } 350f6a79b60SWANG Xuerui #undef GPR_FIELD 3510603839bSHuacai Chen 35205fa8d49SWANG Xuerui /* Print saved important CSRs */ 353efada2afSWANG Xuerui print_crmd(regs->csr_crmd); 354ce7f0b18SWANG Xuerui print_prmd(regs->csr_prmd); 3559718d96cSWANG Xuerui print_euen(regs->csr_euen); 3565e3e784dSWANG Xuerui print_ecfg(regs->csr_ecfg); 35798b90edeSWANG Xuerui print_estat(regs->csr_estat); 3580603839bSHuacai Chen 3590603839bSHuacai Chen if (exccode >= EXCCODE_TLBL && exccode <= EXCCODE_ALE) 360325a38b5SWANG Xuerui printk(" BADV: %0*lx\n", field, regs->csr_badvaddr); 3610603839bSHuacai Chen 362325a38b5SWANG Xuerui printk(" PRID: %08x (%s, %s)\n", read_cpucfg(LOONGARCH_CPUCFG0), 363325a38b5SWANG Xuerui cpu_family_string(), cpu_full_name_string()); 3640603839bSHuacai Chen } 3650603839bSHuacai Chen 3660603839bSHuacai Chen void show_regs(struct pt_regs *regs) 3670603839bSHuacai Chen { 3680603839bSHuacai Chen __show_regs((struct pt_regs *)regs); 3690603839bSHuacai Chen dump_stack(); 3700603839bSHuacai Chen } 3710603839bSHuacai Chen 3720603839bSHuacai Chen void show_registers(struct pt_regs *regs) 3730603839bSHuacai Chen { 3740603839bSHuacai Chen __show_regs(regs); 3750603839bSHuacai Chen print_modules(); 3760603839bSHuacai Chen printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", 3770603839bSHuacai Chen current->comm, current->pid, current_thread_info(), current); 3780603839bSHuacai Chen 3790603839bSHuacai Chen show_stacktrace(current, regs, KERN_DEFAULT, user_mode(regs)); 3800603839bSHuacai Chen show_code((void *)regs->csr_era, user_mode(regs)); 3810603839bSHuacai Chen printk("\n"); 3820603839bSHuacai Chen } 3830603839bSHuacai Chen 3840603839bSHuacai Chen static DEFINE_RAW_SPINLOCK(die_lock); 3850603839bSHuacai Chen 386*6933c11fSTiezhu Yang void die(const char *str, struct pt_regs *regs) 3870603839bSHuacai Chen { 388*6933c11fSTiezhu Yang int ret; 3890603839bSHuacai Chen static int die_counter; 3900603839bSHuacai Chen 3910603839bSHuacai Chen oops_enter(); 3920603839bSHuacai Chen 393*6933c11fSTiezhu Yang ret = notify_die(DIE_OOPS, str, regs, 0, 394*6933c11fSTiezhu Yang current->thread.trap_nr, SIGSEGV); 3950603839bSHuacai Chen 3960603839bSHuacai Chen console_verbose(); 3970603839bSHuacai Chen raw_spin_lock_irq(&die_lock); 3980603839bSHuacai Chen bust_spinlocks(1); 3990603839bSHuacai Chen 4000603839bSHuacai Chen printk("%s[#%d]:\n", str, ++die_counter); 4010603839bSHuacai Chen show_registers(regs); 4020603839bSHuacai Chen add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); 4030603839bSHuacai Chen raw_spin_unlock_irq(&die_lock); 4040603839bSHuacai Chen 4050603839bSHuacai Chen oops_exit(); 4060603839bSHuacai Chen 4074e62d1d8SYouling Tang if (regs && kexec_should_crash(current)) 4084e62d1d8SYouling Tang crash_kexec(regs); 4094e62d1d8SYouling Tang 4100603839bSHuacai Chen if (in_interrupt()) 4110603839bSHuacai Chen panic("Fatal exception in interrupt"); 4120603839bSHuacai Chen 4130603839bSHuacai Chen if (panic_on_oops) 4140603839bSHuacai Chen panic("Fatal exception"); 4150603839bSHuacai Chen 416*6933c11fSTiezhu Yang if (ret != NOTIFY_STOP) 417*6933c11fSTiezhu Yang make_task_dead(SIGSEGV); 4180603839bSHuacai Chen } 4190603839bSHuacai Chen 4200603839bSHuacai Chen static inline void setup_vint_size(unsigned int size) 4210603839bSHuacai Chen { 4220603839bSHuacai Chen unsigned int vs; 4230603839bSHuacai Chen 4240603839bSHuacai Chen vs = ilog2(size/4); 4250603839bSHuacai Chen 4260603839bSHuacai Chen if (vs == 0 || vs > 7) 4270603839bSHuacai Chen panic("vint_size %d Not support yet", vs); 4280603839bSHuacai Chen 4290603839bSHuacai Chen csr_xchg32(vs<<CSR_ECFG_VS_SHIFT, CSR_ECFG_VS, LOONGARCH_CSR_ECFG); 4300603839bSHuacai Chen } 4310603839bSHuacai Chen 4320603839bSHuacai Chen /* 4330603839bSHuacai Chen * Send SIGFPE according to FCSR Cause bits, which must have already 4340603839bSHuacai Chen * been masked against Enable bits. This is impotant as Inexact can 4350603839bSHuacai Chen * happen together with Overflow or Underflow, and `ptrace' can set 4360603839bSHuacai Chen * any bits. 4370603839bSHuacai Chen */ 4380603839bSHuacai Chen void force_fcsr_sig(unsigned long fcsr, void __user *fault_addr, 4390603839bSHuacai Chen struct task_struct *tsk) 4400603839bSHuacai Chen { 4410603839bSHuacai Chen int si_code = FPE_FLTUNK; 4420603839bSHuacai Chen 4430603839bSHuacai Chen if (fcsr & FPU_CSR_INV_X) 4440603839bSHuacai Chen si_code = FPE_FLTINV; 4450603839bSHuacai Chen else if (fcsr & FPU_CSR_DIV_X) 4460603839bSHuacai Chen si_code = FPE_FLTDIV; 4470603839bSHuacai Chen else if (fcsr & FPU_CSR_OVF_X) 4480603839bSHuacai Chen si_code = FPE_FLTOVF; 4490603839bSHuacai Chen else if (fcsr & FPU_CSR_UDF_X) 4500603839bSHuacai Chen si_code = FPE_FLTUND; 4510603839bSHuacai Chen else if (fcsr & FPU_CSR_INE_X) 4520603839bSHuacai Chen si_code = FPE_FLTRES; 4530603839bSHuacai Chen 4540603839bSHuacai Chen force_sig_fault(SIGFPE, si_code, fault_addr); 4550603839bSHuacai Chen } 4560603839bSHuacai Chen 4570603839bSHuacai Chen int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcsr) 4580603839bSHuacai Chen { 4590603839bSHuacai Chen int si_code; 4600603839bSHuacai Chen 4610603839bSHuacai Chen switch (sig) { 4620603839bSHuacai Chen case 0: 4630603839bSHuacai Chen return 0; 4640603839bSHuacai Chen 4650603839bSHuacai Chen case SIGFPE: 4660603839bSHuacai Chen force_fcsr_sig(fcsr, fault_addr, current); 4670603839bSHuacai Chen return 1; 4680603839bSHuacai Chen 4690603839bSHuacai Chen case SIGBUS: 4700603839bSHuacai Chen force_sig_fault(SIGBUS, BUS_ADRERR, fault_addr); 4710603839bSHuacai Chen return 1; 4720603839bSHuacai Chen 4730603839bSHuacai Chen case SIGSEGV: 4740603839bSHuacai Chen mmap_read_lock(current->mm); 4750603839bSHuacai Chen if (vma_lookup(current->mm, (unsigned long)fault_addr)) 4760603839bSHuacai Chen si_code = SEGV_ACCERR; 4770603839bSHuacai Chen else 4780603839bSHuacai Chen si_code = SEGV_MAPERR; 4790603839bSHuacai Chen mmap_read_unlock(current->mm); 4800603839bSHuacai Chen force_sig_fault(SIGSEGV, si_code, fault_addr); 4810603839bSHuacai Chen return 1; 4820603839bSHuacai Chen 4830603839bSHuacai Chen default: 4840603839bSHuacai Chen force_sig(sig); 4850603839bSHuacai Chen return 1; 4860603839bSHuacai Chen } 4870603839bSHuacai Chen } 4880603839bSHuacai Chen 4890603839bSHuacai Chen /* 4900603839bSHuacai Chen * Delayed fp exceptions when doing a lazy ctx switch 4910603839bSHuacai Chen */ 4920603839bSHuacai Chen asmlinkage void noinstr do_fpe(struct pt_regs *regs, unsigned long fcsr) 4930603839bSHuacai Chen { 4940603839bSHuacai Chen int sig; 4950603839bSHuacai Chen void __user *fault_addr; 4960603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 4970603839bSHuacai Chen 4980603839bSHuacai Chen if (notify_die(DIE_FP, "FP exception", regs, 0, current->thread.trap_nr, 4990603839bSHuacai Chen SIGFPE) == NOTIFY_STOP) 5000603839bSHuacai Chen goto out; 5010603839bSHuacai Chen 5020603839bSHuacai Chen /* Clear FCSR.Cause before enabling interrupts */ 5030603839bSHuacai Chen write_fcsr(LOONGARCH_FCSR0, fcsr & ~mask_fcsr_x(fcsr)); 5040603839bSHuacai Chen local_irq_enable(); 5050603839bSHuacai Chen 5060603839bSHuacai Chen die_if_kernel("FP exception in kernel code", regs); 5070603839bSHuacai Chen 5080603839bSHuacai Chen sig = SIGFPE; 5090603839bSHuacai Chen fault_addr = (void __user *) regs->csr_era; 5100603839bSHuacai Chen 5110603839bSHuacai Chen /* Send a signal if required. */ 5120603839bSHuacai Chen process_fpemu_return(sig, fault_addr, fcsr); 5130603839bSHuacai Chen 5140603839bSHuacai Chen out: 5150603839bSHuacai Chen local_irq_disable(); 5160603839bSHuacai Chen irqentry_exit(regs, state); 5170603839bSHuacai Chen } 5180603839bSHuacai Chen 5190603839bSHuacai Chen asmlinkage void noinstr do_ade(struct pt_regs *regs) 5200603839bSHuacai Chen { 5210603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 5220603839bSHuacai Chen 5230603839bSHuacai Chen die_if_kernel("Kernel ade access", regs); 5240603839bSHuacai Chen force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)regs->csr_badvaddr); 5250603839bSHuacai Chen 5260603839bSHuacai Chen irqentry_exit(regs, state); 5270603839bSHuacai Chen } 5280603839bSHuacai Chen 52961a6fcccSHuacai Chen /* sysctl hooks */ 53061a6fcccSHuacai Chen int unaligned_enabled __read_mostly = 1; /* Enabled by default */ 53161a6fcccSHuacai Chen int no_unaligned_warning __read_mostly = 1; /* Only 1 warning by default */ 53261a6fcccSHuacai Chen 5330603839bSHuacai Chen asmlinkage void noinstr do_ale(struct pt_regs *regs) 5340603839bSHuacai Chen { 5350603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 5360603839bSHuacai Chen 53741596803SHuacai Chen #ifndef CONFIG_ARCH_STRICT_ALIGN 53841596803SHuacai Chen die_if_kernel("Kernel ale access", regs); 53941596803SHuacai Chen force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)regs->csr_badvaddr); 54041596803SHuacai Chen #else 54141596803SHuacai Chen unsigned int *pc; 54241596803SHuacai Chen 54361a6fcccSHuacai Chen perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, regs->csr_badvaddr); 54461a6fcccSHuacai Chen 54561a6fcccSHuacai Chen /* 54661a6fcccSHuacai Chen * Did we catch a fault trying to load an instruction? 54761a6fcccSHuacai Chen */ 54861a6fcccSHuacai Chen if (regs->csr_badvaddr == regs->csr_era) 54961a6fcccSHuacai Chen goto sigbus; 55061a6fcccSHuacai Chen if (user_mode(regs) && !test_thread_flag(TIF_FIXADE)) 55161a6fcccSHuacai Chen goto sigbus; 55261a6fcccSHuacai Chen if (!unaligned_enabled) 55361a6fcccSHuacai Chen goto sigbus; 55461a6fcccSHuacai Chen if (!no_unaligned_warning) 55561a6fcccSHuacai Chen show_registers(regs); 55661a6fcccSHuacai Chen 55761a6fcccSHuacai Chen pc = (unsigned int *)exception_era(regs); 55861a6fcccSHuacai Chen 55961a6fcccSHuacai Chen emulate_load_store_insn(regs, (void __user *)regs->csr_badvaddr, pc); 56061a6fcccSHuacai Chen 56161a6fcccSHuacai Chen goto out; 56261a6fcccSHuacai Chen 56361a6fcccSHuacai Chen sigbus: 5640603839bSHuacai Chen die_if_kernel("Kernel ale access", regs); 5650603839bSHuacai Chen force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)regs->csr_badvaddr); 56661a6fcccSHuacai Chen out: 56741596803SHuacai Chen #endif 5680603839bSHuacai Chen irqentry_exit(regs, state); 5690603839bSHuacai Chen } 5700603839bSHuacai Chen 5712d2c3952SYouling Tang #ifdef CONFIG_GENERIC_BUG 5722d2c3952SYouling Tang int is_valid_bugaddr(unsigned long addr) 5732d2c3952SYouling Tang { 5742d2c3952SYouling Tang return 1; 5752d2c3952SYouling Tang } 5762d2c3952SYouling Tang #endif /* CONFIG_GENERIC_BUG */ 5772d2c3952SYouling Tang 5782d2c3952SYouling Tang static void bug_handler(struct pt_regs *regs) 5792d2c3952SYouling Tang { 5802d2c3952SYouling Tang switch (report_bug(regs->csr_era, regs)) { 5812d2c3952SYouling Tang case BUG_TRAP_TYPE_BUG: 5822d2c3952SYouling Tang case BUG_TRAP_TYPE_NONE: 5832d2c3952SYouling Tang die_if_kernel("Oops - BUG", regs); 5842d2c3952SYouling Tang force_sig(SIGTRAP); 5852d2c3952SYouling Tang break; 5862d2c3952SYouling Tang 5872d2c3952SYouling Tang case BUG_TRAP_TYPE_WARN: 5882d2c3952SYouling Tang /* Skip the BUG instruction and continue */ 5892d2c3952SYouling Tang regs->csr_era += LOONGARCH_INSN_SIZE; 5902d2c3952SYouling Tang break; 5912d2c3952SYouling Tang } 5922d2c3952SYouling Tang } 5932d2c3952SYouling Tang 594c23e7f01SWANG Xuerui asmlinkage void noinstr do_bce(struct pt_regs *regs) 595c23e7f01SWANG Xuerui { 596c23e7f01SWANG Xuerui bool user = user_mode(regs); 597c23e7f01SWANG Xuerui unsigned long era = exception_era(regs); 598c23e7f01SWANG Xuerui u64 badv = 0, lower = 0, upper = ULONG_MAX; 599c23e7f01SWANG Xuerui union loongarch_instruction insn; 600c23e7f01SWANG Xuerui irqentry_state_t state = irqentry_enter(regs); 601c23e7f01SWANG Xuerui 602c23e7f01SWANG Xuerui if (regs->csr_prmd & CSR_PRMD_PIE) 603c23e7f01SWANG Xuerui local_irq_enable(); 604c23e7f01SWANG Xuerui 605c23e7f01SWANG Xuerui current->thread.trap_nr = read_csr_excode(); 606c23e7f01SWANG Xuerui 607c23e7f01SWANG Xuerui die_if_kernel("Bounds check error in kernel code", regs); 608c23e7f01SWANG Xuerui 609c23e7f01SWANG Xuerui /* 610c23e7f01SWANG Xuerui * Pull out the address that failed bounds checking, and the lower / 611c23e7f01SWANG Xuerui * upper bound, by minimally looking at the faulting instruction word 612c23e7f01SWANG Xuerui * and reading from the correct register. 613c23e7f01SWANG Xuerui */ 614c23e7f01SWANG Xuerui if (__get_inst(&insn.word, (u32 *)era, user)) 615c23e7f01SWANG Xuerui goto bad_era; 616c23e7f01SWANG Xuerui 617c23e7f01SWANG Xuerui switch (insn.reg3_format.opcode) { 618c23e7f01SWANG Xuerui case asrtle_op: 619c23e7f01SWANG Xuerui if (insn.reg3_format.rd != 0) 620c23e7f01SWANG Xuerui break; /* not asrtle */ 621c23e7f01SWANG Xuerui badv = regs->regs[insn.reg3_format.rj]; 622c23e7f01SWANG Xuerui upper = regs->regs[insn.reg3_format.rk]; 623c23e7f01SWANG Xuerui break; 624c23e7f01SWANG Xuerui 625c23e7f01SWANG Xuerui case asrtgt_op: 626c23e7f01SWANG Xuerui if (insn.reg3_format.rd != 0) 627c23e7f01SWANG Xuerui break; /* not asrtgt */ 628c23e7f01SWANG Xuerui badv = regs->regs[insn.reg3_format.rj]; 629c23e7f01SWANG Xuerui lower = regs->regs[insn.reg3_format.rk]; 630c23e7f01SWANG Xuerui break; 631c23e7f01SWANG Xuerui 632c23e7f01SWANG Xuerui case ldleb_op: 633c23e7f01SWANG Xuerui case ldleh_op: 634c23e7f01SWANG Xuerui case ldlew_op: 635c23e7f01SWANG Xuerui case ldled_op: 636c23e7f01SWANG Xuerui case stleb_op: 637c23e7f01SWANG Xuerui case stleh_op: 638c23e7f01SWANG Xuerui case stlew_op: 639c23e7f01SWANG Xuerui case stled_op: 640c23e7f01SWANG Xuerui case fldles_op: 641c23e7f01SWANG Xuerui case fldled_op: 642c23e7f01SWANG Xuerui case fstles_op: 643c23e7f01SWANG Xuerui case fstled_op: 644c23e7f01SWANG Xuerui badv = regs->regs[insn.reg3_format.rj]; 645c23e7f01SWANG Xuerui upper = regs->regs[insn.reg3_format.rk]; 646c23e7f01SWANG Xuerui break; 647c23e7f01SWANG Xuerui 648c23e7f01SWANG Xuerui case ldgtb_op: 649c23e7f01SWANG Xuerui case ldgth_op: 650c23e7f01SWANG Xuerui case ldgtw_op: 651c23e7f01SWANG Xuerui case ldgtd_op: 652c23e7f01SWANG Xuerui case stgtb_op: 653c23e7f01SWANG Xuerui case stgth_op: 654c23e7f01SWANG Xuerui case stgtw_op: 655c23e7f01SWANG Xuerui case stgtd_op: 656c23e7f01SWANG Xuerui case fldgts_op: 657c23e7f01SWANG Xuerui case fldgtd_op: 658c23e7f01SWANG Xuerui case fstgts_op: 659c23e7f01SWANG Xuerui case fstgtd_op: 660c23e7f01SWANG Xuerui badv = regs->regs[insn.reg3_format.rj]; 661c23e7f01SWANG Xuerui lower = regs->regs[insn.reg3_format.rk]; 662c23e7f01SWANG Xuerui break; 663c23e7f01SWANG Xuerui } 664c23e7f01SWANG Xuerui 665c23e7f01SWANG Xuerui force_sig_bnderr((void __user *)badv, (void __user *)lower, (void __user *)upper); 666c23e7f01SWANG Xuerui 667c23e7f01SWANG Xuerui out: 668c23e7f01SWANG Xuerui if (regs->csr_prmd & CSR_PRMD_PIE) 669c23e7f01SWANG Xuerui local_irq_disable(); 670c23e7f01SWANG Xuerui 671c23e7f01SWANG Xuerui irqentry_exit(regs, state); 672c23e7f01SWANG Xuerui return; 673c23e7f01SWANG Xuerui 674c23e7f01SWANG Xuerui bad_era: 675c23e7f01SWANG Xuerui /* 676c23e7f01SWANG Xuerui * Cannot pull out the instruction word, hence cannot provide more 677c23e7f01SWANG Xuerui * info than a regular SIGSEGV in this case. 678c23e7f01SWANG Xuerui */ 679c23e7f01SWANG Xuerui force_sig(SIGSEGV); 680c23e7f01SWANG Xuerui goto out; 681c23e7f01SWANG Xuerui } 682c23e7f01SWANG Xuerui 6830603839bSHuacai Chen asmlinkage void noinstr do_bp(struct pt_regs *regs) 6840603839bSHuacai Chen { 6850603839bSHuacai Chen bool user = user_mode(regs); 6860603839bSHuacai Chen unsigned int opcode, bcode; 6870603839bSHuacai Chen unsigned long era = exception_era(regs); 6880603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 6890603839bSHuacai Chen 6906d4cc40fSTiezhu Yang if (regs->csr_prmd & CSR_PRMD_PIE) 6910603839bSHuacai Chen local_irq_enable(); 6926d4cc40fSTiezhu Yang 6930603839bSHuacai Chen if (__get_inst(&opcode, (u32 *)era, user)) 6940603839bSHuacai Chen goto out_sigsegv; 6950603839bSHuacai Chen 6960603839bSHuacai Chen bcode = (opcode & 0x7fff); 6970603839bSHuacai Chen 6980603839bSHuacai Chen /* 6990603839bSHuacai Chen * notify the kprobe handlers, if instruction is likely to 7000603839bSHuacai Chen * pertain to them. 7010603839bSHuacai Chen */ 7020603839bSHuacai Chen switch (bcode) { 7030603839bSHuacai Chen case BRK_KPROBE_BP: 7046d4cc40fSTiezhu Yang if (kprobe_breakpoint_handler(regs)) 7050603839bSHuacai Chen goto out; 7060603839bSHuacai Chen else 7070603839bSHuacai Chen break; 7080603839bSHuacai Chen case BRK_KPROBE_SSTEPBP: 7096d4cc40fSTiezhu Yang if (kprobe_singlestep_handler(regs)) 7100603839bSHuacai Chen goto out; 7110603839bSHuacai Chen else 7120603839bSHuacai Chen break; 7130603839bSHuacai Chen case BRK_UPROBE_BP: 71419bc6cb6STiezhu Yang if (uprobe_breakpoint_handler(regs)) 7150603839bSHuacai Chen goto out; 7160603839bSHuacai Chen else 7170603839bSHuacai Chen break; 7180603839bSHuacai Chen case BRK_UPROBE_XOLBP: 71919bc6cb6STiezhu Yang if (uprobe_singlestep_handler(regs)) 7200603839bSHuacai Chen goto out; 7210603839bSHuacai Chen else 7220603839bSHuacai Chen break; 7230603839bSHuacai Chen default: 72419bc6cb6STiezhu Yang current->thread.trap_nr = read_csr_excode(); 7250603839bSHuacai Chen if (notify_die(DIE_TRAP, "Break", regs, bcode, 7260603839bSHuacai Chen current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP) 7270603839bSHuacai Chen goto out; 7280603839bSHuacai Chen else 7290603839bSHuacai Chen break; 7300603839bSHuacai Chen } 7310603839bSHuacai Chen 7320603839bSHuacai Chen switch (bcode) { 7330603839bSHuacai Chen case BRK_BUG: 7342d2c3952SYouling Tang bug_handler(regs); 7350603839bSHuacai Chen break; 7360603839bSHuacai Chen case BRK_DIVZERO: 7370603839bSHuacai Chen die_if_kernel("Break instruction in kernel code", regs); 7380603839bSHuacai Chen force_sig_fault(SIGFPE, FPE_INTDIV, (void __user *)regs->csr_era); 7390603839bSHuacai Chen break; 7400603839bSHuacai Chen case BRK_OVERFLOW: 7410603839bSHuacai Chen die_if_kernel("Break instruction in kernel code", regs); 7420603839bSHuacai Chen force_sig_fault(SIGFPE, FPE_INTOVF, (void __user *)regs->csr_era); 7430603839bSHuacai Chen break; 7440603839bSHuacai Chen default: 7450603839bSHuacai Chen die_if_kernel("Break instruction in kernel code", regs); 7460603839bSHuacai Chen force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->csr_era); 7470603839bSHuacai Chen break; 7480603839bSHuacai Chen } 7490603839bSHuacai Chen 7500603839bSHuacai Chen out: 7516d4cc40fSTiezhu Yang if (regs->csr_prmd & CSR_PRMD_PIE) 7520603839bSHuacai Chen local_irq_disable(); 7536d4cc40fSTiezhu Yang 7540603839bSHuacai Chen irqentry_exit(regs, state); 7550603839bSHuacai Chen return; 7560603839bSHuacai Chen 7570603839bSHuacai Chen out_sigsegv: 7580603839bSHuacai Chen force_sig(SIGSEGV); 7590603839bSHuacai Chen goto out; 7600603839bSHuacai Chen } 7610603839bSHuacai Chen 7620603839bSHuacai Chen asmlinkage void noinstr do_watch(struct pt_regs *regs) 7630603839bSHuacai Chen { 764edffa33cSQing Zhang irqentry_state_t state = irqentry_enter(regs); 765edffa33cSQing Zhang 766424421a7SQing Zhang #ifndef CONFIG_HAVE_HW_BREAKPOINT 767424421a7SQing Zhang pr_warn("Hardware watch point handler not implemented!\n"); 768424421a7SQing Zhang #else 769424421a7SQing Zhang if (test_tsk_thread_flag(current, TIF_SINGLESTEP)) { 770424421a7SQing Zhang int llbit = (csr_read32(LOONGARCH_CSR_LLBCTL) & 0x1); 771424421a7SQing Zhang unsigned long pc = instruction_pointer(regs); 772424421a7SQing Zhang union loongarch_instruction *ip = (union loongarch_instruction *)pc; 773424421a7SQing Zhang 774424421a7SQing Zhang if (llbit) { 775424421a7SQing Zhang /* 776424421a7SQing Zhang * When the ll-sc combo is encountered, it is regarded as an single 777424421a7SQing Zhang * instruction. So don't clear llbit and reset CSR.FWPS.Skip until 778424421a7SQing Zhang * the llsc execution is completed. 779424421a7SQing Zhang */ 780424421a7SQing Zhang csr_write32(CSR_FWPC_SKIP, LOONGARCH_CSR_FWPS); 781424421a7SQing Zhang csr_write32(CSR_LLBCTL_KLO, LOONGARCH_CSR_LLBCTL); 782424421a7SQing Zhang goto out; 783424421a7SQing Zhang } 784424421a7SQing Zhang 785424421a7SQing Zhang if (pc == current->thread.single_step) { 786424421a7SQing Zhang /* 787424421a7SQing Zhang * Certain insns are occasionally not skipped when CSR.FWPS.Skip is 788424421a7SQing Zhang * set, such as fld.d/fst.d. So singlestep needs to compare whether 789424421a7SQing Zhang * the csr_era is equal to the value of singlestep which last time set. 790424421a7SQing Zhang */ 791424421a7SQing Zhang if (!is_self_loop_ins(ip, regs)) { 792424421a7SQing Zhang /* 793424421a7SQing Zhang * Check if the given instruction the target pc is equal to the 794424421a7SQing Zhang * current pc, If yes, then we should not set the CSR.FWPS.SKIP 795424421a7SQing Zhang * bit to break the original instruction stream. 796424421a7SQing Zhang */ 797424421a7SQing Zhang csr_write32(CSR_FWPC_SKIP, LOONGARCH_CSR_FWPS); 798424421a7SQing Zhang goto out; 799424421a7SQing Zhang } 800424421a7SQing Zhang } 801424421a7SQing Zhang } else { 802edffa33cSQing Zhang breakpoint_handler(regs); 803edffa33cSQing Zhang watchpoint_handler(regs); 804424421a7SQing Zhang } 805edffa33cSQing Zhang 806424421a7SQing Zhang force_sig(SIGTRAP); 807424421a7SQing Zhang out: 808424421a7SQing Zhang #endif 809edffa33cSQing Zhang irqentry_exit(regs, state); 8100603839bSHuacai Chen } 8110603839bSHuacai Chen 8120603839bSHuacai Chen asmlinkage void noinstr do_ri(struct pt_regs *regs) 8130603839bSHuacai Chen { 81406e76aceSHuacai Chen int status = SIGILL; 8150603839bSHuacai Chen unsigned int opcode = 0; 8160603839bSHuacai Chen unsigned int __user *era = (unsigned int __user *)exception_era(regs); 8170603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 8180603839bSHuacai Chen 8190603839bSHuacai Chen local_irq_enable(); 8200603839bSHuacai Chen current->thread.trap_nr = read_csr_excode(); 8210603839bSHuacai Chen 8220603839bSHuacai Chen if (notify_die(DIE_RI, "RI Fault", regs, 0, current->thread.trap_nr, 8230603839bSHuacai Chen SIGILL) == NOTIFY_STOP) 8240603839bSHuacai Chen goto out; 8250603839bSHuacai Chen 8260603839bSHuacai Chen die_if_kernel("Reserved instruction in kernel code", regs); 8270603839bSHuacai Chen 8280603839bSHuacai Chen if (unlikely(get_user(opcode, era) < 0)) { 8290603839bSHuacai Chen status = SIGSEGV; 8300603839bSHuacai Chen current->thread.error_code = 1; 8310603839bSHuacai Chen } 8320603839bSHuacai Chen 8330603839bSHuacai Chen force_sig(status); 8340603839bSHuacai Chen 8350603839bSHuacai Chen out: 8360603839bSHuacai Chen local_irq_disable(); 8370603839bSHuacai Chen irqentry_exit(regs, state); 8380603839bSHuacai Chen } 8390603839bSHuacai Chen 8400603839bSHuacai Chen static void init_restore_fp(void) 8410603839bSHuacai Chen { 8420603839bSHuacai Chen if (!used_math()) { 8430603839bSHuacai Chen /* First time FP context user. */ 8440603839bSHuacai Chen init_fpu(); 8450603839bSHuacai Chen } else { 8460603839bSHuacai Chen /* This task has formerly used the FP context */ 8470603839bSHuacai Chen if (!is_fpu_owner()) 8480603839bSHuacai Chen own_fpu_inatomic(1); 8490603839bSHuacai Chen } 8500603839bSHuacai Chen 8510603839bSHuacai Chen BUG_ON(!is_fp_enabled()); 8520603839bSHuacai Chen } 8530603839bSHuacai Chen 85461650023SHuacai Chen static void init_restore_lsx(void) 85561650023SHuacai Chen { 85661650023SHuacai Chen enable_lsx(); 85761650023SHuacai Chen 85861650023SHuacai Chen if (!thread_lsx_context_live()) { 85961650023SHuacai Chen /* First time LSX context user */ 86061650023SHuacai Chen init_restore_fp(); 86161650023SHuacai Chen init_lsx_upper(); 86261650023SHuacai Chen set_thread_flag(TIF_LSX_CTX_LIVE); 86361650023SHuacai Chen } else { 86461650023SHuacai Chen if (!is_simd_owner()) { 86561650023SHuacai Chen if (is_fpu_owner()) { 86661650023SHuacai Chen restore_lsx_upper(current); 86761650023SHuacai Chen } else { 86861650023SHuacai Chen __own_fpu(); 86961650023SHuacai Chen restore_lsx(current); 87061650023SHuacai Chen } 87161650023SHuacai Chen } 87261650023SHuacai Chen } 87361650023SHuacai Chen 87461650023SHuacai Chen set_thread_flag(TIF_USEDSIMD); 87561650023SHuacai Chen 87661650023SHuacai Chen BUG_ON(!is_fp_enabled()); 87761650023SHuacai Chen BUG_ON(!is_lsx_enabled()); 87861650023SHuacai Chen } 87961650023SHuacai Chen 88061650023SHuacai Chen static void init_restore_lasx(void) 88161650023SHuacai Chen { 88261650023SHuacai Chen enable_lasx(); 88361650023SHuacai Chen 88461650023SHuacai Chen if (!thread_lasx_context_live()) { 88561650023SHuacai Chen /* First time LASX context user */ 88661650023SHuacai Chen init_restore_lsx(); 88761650023SHuacai Chen init_lasx_upper(); 88861650023SHuacai Chen set_thread_flag(TIF_LASX_CTX_LIVE); 88961650023SHuacai Chen } else { 89061650023SHuacai Chen if (is_fpu_owner() || is_simd_owner()) { 89161650023SHuacai Chen init_restore_lsx(); 89261650023SHuacai Chen restore_lasx_upper(current); 89361650023SHuacai Chen } else { 89461650023SHuacai Chen __own_fpu(); 89561650023SHuacai Chen enable_lsx(); 89661650023SHuacai Chen restore_lasx(current); 89761650023SHuacai Chen } 89861650023SHuacai Chen } 89961650023SHuacai Chen 90061650023SHuacai Chen set_thread_flag(TIF_USEDSIMD); 90161650023SHuacai Chen 90261650023SHuacai Chen BUG_ON(!is_fp_enabled()); 90361650023SHuacai Chen BUG_ON(!is_lsx_enabled()); 90461650023SHuacai Chen BUG_ON(!is_lasx_enabled()); 90561650023SHuacai Chen } 90661650023SHuacai Chen 9070603839bSHuacai Chen asmlinkage void noinstr do_fpu(struct pt_regs *regs) 9080603839bSHuacai Chen { 9090603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 9100603839bSHuacai Chen 9110603839bSHuacai Chen local_irq_enable(); 9120603839bSHuacai Chen die_if_kernel("do_fpu invoked from kernel context!", regs); 91361650023SHuacai Chen BUG_ON(is_lsx_enabled()); 91461650023SHuacai Chen BUG_ON(is_lasx_enabled()); 9150603839bSHuacai Chen 9160603839bSHuacai Chen preempt_disable(); 9170603839bSHuacai Chen init_restore_fp(); 9180603839bSHuacai Chen preempt_enable(); 9190603839bSHuacai Chen 9200603839bSHuacai Chen local_irq_disable(); 9210603839bSHuacai Chen irqentry_exit(regs, state); 9220603839bSHuacai Chen } 9230603839bSHuacai Chen 9240603839bSHuacai Chen asmlinkage void noinstr do_lsx(struct pt_regs *regs) 9250603839bSHuacai Chen { 9260603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 9270603839bSHuacai Chen 9280603839bSHuacai Chen local_irq_enable(); 92961650023SHuacai Chen if (!cpu_has_lsx) { 9300603839bSHuacai Chen force_sig(SIGILL); 93161650023SHuacai Chen goto out; 93261650023SHuacai Chen } 9330603839bSHuacai Chen 93461650023SHuacai Chen die_if_kernel("do_lsx invoked from kernel context!", regs); 93561650023SHuacai Chen BUG_ON(is_lasx_enabled()); 93661650023SHuacai Chen 93761650023SHuacai Chen preempt_disable(); 93861650023SHuacai Chen init_restore_lsx(); 93961650023SHuacai Chen preempt_enable(); 94061650023SHuacai Chen 94161650023SHuacai Chen out: 94261650023SHuacai Chen local_irq_disable(); 9430603839bSHuacai Chen irqentry_exit(regs, state); 9440603839bSHuacai Chen } 9450603839bSHuacai Chen 9460603839bSHuacai Chen asmlinkage void noinstr do_lasx(struct pt_regs *regs) 9470603839bSHuacai Chen { 9480603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 9490603839bSHuacai Chen 9500603839bSHuacai Chen local_irq_enable(); 95161650023SHuacai Chen if (!cpu_has_lasx) { 9520603839bSHuacai Chen force_sig(SIGILL); 95361650023SHuacai Chen goto out; 95461650023SHuacai Chen } 9550603839bSHuacai Chen 95661650023SHuacai Chen die_if_kernel("do_lasx invoked from kernel context!", regs); 95761650023SHuacai Chen 95861650023SHuacai Chen preempt_disable(); 95961650023SHuacai Chen init_restore_lasx(); 96061650023SHuacai Chen preempt_enable(); 96161650023SHuacai Chen 96261650023SHuacai Chen out: 96361650023SHuacai Chen local_irq_disable(); 9640603839bSHuacai Chen irqentry_exit(regs, state); 9650603839bSHuacai Chen } 9660603839bSHuacai Chen 9670603839bSHuacai Chen asmlinkage void noinstr do_lbt(struct pt_regs *regs) 9680603839bSHuacai Chen { 9690603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 9700603839bSHuacai Chen 9710603839bSHuacai Chen local_irq_enable(); 9720603839bSHuacai Chen force_sig(SIGILL); 9730603839bSHuacai Chen local_irq_disable(); 9740603839bSHuacai Chen 9750603839bSHuacai Chen irqentry_exit(regs, state); 9760603839bSHuacai Chen } 9770603839bSHuacai Chen 9780603839bSHuacai Chen asmlinkage void noinstr do_reserved(struct pt_regs *regs) 9790603839bSHuacai Chen { 9800603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 9810603839bSHuacai Chen 9820603839bSHuacai Chen local_irq_enable(); 9830603839bSHuacai Chen /* 9840603839bSHuacai Chen * Game over - no way to handle this if it ever occurs. Most probably 9850603839bSHuacai Chen * caused by a fatal error after another hardware/software error. 9860603839bSHuacai Chen */ 9870603839bSHuacai Chen pr_err("Caught reserved exception %u on pid:%d [%s] - should not happen\n", 9880603839bSHuacai Chen read_csr_excode(), current->pid, current->comm); 9890603839bSHuacai Chen die_if_kernel("do_reserved exception", regs); 9900603839bSHuacai Chen force_sig(SIGUNUSED); 9910603839bSHuacai Chen 9920603839bSHuacai Chen local_irq_disable(); 9930603839bSHuacai Chen 9940603839bSHuacai Chen irqentry_exit(regs, state); 9950603839bSHuacai Chen } 9960603839bSHuacai Chen 9970603839bSHuacai Chen asmlinkage void cache_parity_error(void) 9980603839bSHuacai Chen { 9990603839bSHuacai Chen /* For the moment, report the problem and hang. */ 10000603839bSHuacai Chen pr_err("Cache error exception:\n"); 10010603839bSHuacai Chen pr_err("csr_merrctl == %08x\n", csr_read32(LOONGARCH_CSR_MERRCTL)); 100253a4858cSWANG Xuerui pr_err("csr_merrera == %016lx\n", csr_read64(LOONGARCH_CSR_MERRERA)); 10030603839bSHuacai Chen panic("Can't handle the cache error!"); 10040603839bSHuacai Chen } 10050603839bSHuacai Chen 10060603839bSHuacai Chen asmlinkage void noinstr handle_loongarch_irq(struct pt_regs *regs) 10070603839bSHuacai Chen { 10080603839bSHuacai Chen struct pt_regs *old_regs; 10090603839bSHuacai Chen 10100603839bSHuacai Chen irq_enter_rcu(); 10110603839bSHuacai Chen old_regs = set_irq_regs(regs); 10120603839bSHuacai Chen handle_arch_irq(regs); 10130603839bSHuacai Chen set_irq_regs(old_regs); 10140603839bSHuacai Chen irq_exit_rcu(); 10150603839bSHuacai Chen } 10160603839bSHuacai Chen 10170603839bSHuacai Chen asmlinkage void noinstr do_vint(struct pt_regs *regs, unsigned long sp) 10180603839bSHuacai Chen { 10190603839bSHuacai Chen register int cpu; 10200603839bSHuacai Chen register unsigned long stack; 10210603839bSHuacai Chen irqentry_state_t state = irqentry_enter(regs); 10220603839bSHuacai Chen 10230603839bSHuacai Chen cpu = smp_processor_id(); 10240603839bSHuacai Chen 10250603839bSHuacai Chen if (on_irq_stack(cpu, sp)) 10260603839bSHuacai Chen handle_loongarch_irq(regs); 10270603839bSHuacai Chen else { 10280603839bSHuacai Chen stack = per_cpu(irq_stack, cpu) + IRQ_STACK_START; 10290603839bSHuacai Chen 10300603839bSHuacai Chen /* Save task's sp on IRQ stack for unwinding */ 10310603839bSHuacai Chen *(unsigned long *)stack = sp; 10320603839bSHuacai Chen 10330603839bSHuacai Chen __asm__ __volatile__( 10340603839bSHuacai Chen "move $s0, $sp \n" /* Preserve sp */ 10350603839bSHuacai Chen "move $sp, %[stk] \n" /* Switch stack */ 10360603839bSHuacai Chen "move $a0, %[regs] \n" 10370603839bSHuacai Chen "bl handle_loongarch_irq \n" 10380603839bSHuacai Chen "move $sp, $s0 \n" /* Restore sp */ 10390603839bSHuacai Chen : /* No outputs */ 10400603839bSHuacai Chen : [stk] "r" (stack), [regs] "r" (regs) 10410603839bSHuacai Chen : "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7", "$s0", 10420603839bSHuacai Chen "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8", 10430603839bSHuacai Chen "memory"); 10440603839bSHuacai Chen } 10450603839bSHuacai Chen 10460603839bSHuacai Chen irqentry_exit(regs, state); 10470603839bSHuacai Chen } 10480603839bSHuacai Chen 10490603839bSHuacai Chen unsigned long eentry; 10500603839bSHuacai Chen unsigned long tlbrentry; 10510603839bSHuacai Chen 10520603839bSHuacai Chen long exception_handlers[VECSIZE * 128 / sizeof(long)] __aligned(SZ_64K); 10530603839bSHuacai Chen 10540603839bSHuacai Chen static void configure_exception_vector(void) 10550603839bSHuacai Chen { 10560603839bSHuacai Chen eentry = (unsigned long)exception_handlers; 10570603839bSHuacai Chen tlbrentry = (unsigned long)exception_handlers + 80*VECSIZE; 10580603839bSHuacai Chen 10590603839bSHuacai Chen csr_write64(eentry, LOONGARCH_CSR_EENTRY); 10600603839bSHuacai Chen csr_write64(eentry, LOONGARCH_CSR_MERRENTRY); 10610603839bSHuacai Chen csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY); 10620603839bSHuacai Chen } 10630603839bSHuacai Chen 10640603839bSHuacai Chen void per_cpu_trap_init(int cpu) 10650603839bSHuacai Chen { 10660603839bSHuacai Chen unsigned int i; 10670603839bSHuacai Chen 10680603839bSHuacai Chen setup_vint_size(VECSIZE); 10690603839bSHuacai Chen 10700603839bSHuacai Chen configure_exception_vector(); 10710603839bSHuacai Chen 10720603839bSHuacai Chen if (!cpu_data[cpu].asid_cache) 10730603839bSHuacai Chen cpu_data[cpu].asid_cache = asid_first_version(cpu); 10740603839bSHuacai Chen 10750603839bSHuacai Chen mmgrab(&init_mm); 10760603839bSHuacai Chen current->active_mm = &init_mm; 10770603839bSHuacai Chen BUG_ON(current->mm); 10780603839bSHuacai Chen enter_lazy_tlb(&init_mm, current); 10790603839bSHuacai Chen 10800603839bSHuacai Chen /* Initialise exception handlers */ 10810603839bSHuacai Chen if (cpu == 0) 10820603839bSHuacai Chen for (i = 0; i < 64; i++) 10830603839bSHuacai Chen set_handler(i * VECSIZE, handle_reserved, VECSIZE); 10840603839bSHuacai Chen 1085d4b6f156SHuacai Chen tlb_init(cpu); 10860603839bSHuacai Chen cpu_cache_init(); 10870603839bSHuacai Chen } 10880603839bSHuacai Chen 10890603839bSHuacai Chen /* Install CPU exception handler */ 10900603839bSHuacai Chen void set_handler(unsigned long offset, void *addr, unsigned long size) 10910603839bSHuacai Chen { 10920603839bSHuacai Chen memcpy((void *)(eentry + offset), addr, size); 10930603839bSHuacai Chen local_flush_icache_range(eentry + offset, eentry + offset + size); 10940603839bSHuacai Chen } 10950603839bSHuacai Chen 10960603839bSHuacai Chen static const char panic_null_cerr[] = 10970603839bSHuacai Chen "Trying to set NULL cache error exception handler\n"; 10980603839bSHuacai Chen 10990603839bSHuacai Chen /* 11000603839bSHuacai Chen * Install uncached CPU exception handler. 11010603839bSHuacai Chen * This is suitable only for the cache error exception which is the only 11020603839bSHuacai Chen * exception handler that is being run uncached. 11030603839bSHuacai Chen */ 11040603839bSHuacai Chen void set_merr_handler(unsigned long offset, void *addr, unsigned long size) 11050603839bSHuacai Chen { 11060603839bSHuacai Chen unsigned long uncached_eentry = TO_UNCACHE(__pa(eentry)); 11070603839bSHuacai Chen 11080603839bSHuacai Chen if (!addr) 11090603839bSHuacai Chen panic(panic_null_cerr); 11100603839bSHuacai Chen 11110603839bSHuacai Chen memcpy((void *)(uncached_eentry + offset), addr, size); 11120603839bSHuacai Chen } 11130603839bSHuacai Chen 11140603839bSHuacai Chen void __init trap_init(void) 11150603839bSHuacai Chen { 11160603839bSHuacai Chen long i; 11170603839bSHuacai Chen 11180603839bSHuacai Chen /* Set interrupt vector handler */ 11199e36fa42SWANG Xuerui for (i = EXCCODE_INT_START; i <= EXCCODE_INT_END; i++) 11200603839bSHuacai Chen set_handler(i * VECSIZE, handle_vint, VECSIZE); 11210603839bSHuacai Chen 11220603839bSHuacai Chen set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE); 11230603839bSHuacai Chen set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE); 1124c23e7f01SWANG Xuerui set_handler(EXCCODE_BCE * VECSIZE, handle_bce, VECSIZE); 11250603839bSHuacai Chen set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE); 11260603839bSHuacai Chen set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE); 11270603839bSHuacai Chen set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE); 11280603839bSHuacai Chen set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE); 11290603839bSHuacai Chen set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE); 11300603839bSHuacai Chen set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE); 11310603839bSHuacai Chen set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE); 11320603839bSHuacai Chen set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE); 11330603839bSHuacai Chen set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE); 11340603839bSHuacai Chen set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE); 11350603839bSHuacai Chen 11360603839bSHuacai Chen cache_error_setup(); 11370603839bSHuacai Chen 11380603839bSHuacai Chen local_flush_icache_range(eentry, eentry + 0x400); 11390603839bSHuacai Chen } 1140