Add exception entry assembly code, import stackframe.h
from Linux, provide debug prints when exception happens.

Signed-off-by: Jiaxun Yang <jiaxun.y...@flygoat.com>
---
 arch/loongarch/Kconfig                  |   3 +
 arch/loongarch/cpu/Makefile             |   2 +-
 arch/loongarch/cpu/genex.S              |  21 ++++
 arch/loongarch/include/asm/stackframe.h | 175 +++++++++++++++++++++++++++++
 arch/loongarch/lib/interrupts.c         | 189 ++++++++++++++++++++++++++++++++
 5 files changed, 389 insertions(+), 1 deletion(-)

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 4e8e9d4ee88b..109d37d8e2c7 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -30,6 +30,9 @@ config DMA_ADDR_T_64BIT
        bool
        default y if 64BIT
 
+config SHOW_REGS
+       bool "Show registers on unhandled exception"
+
 config STACK_SIZE_SHIFT
        int
        default 14
diff --git a/arch/loongarch/cpu/Makefile b/arch/loongarch/cpu/Makefile
index d3c38a16d057..6b3dd7ad7d69 100644
--- a/arch/loongarch/cpu/Makefile
+++ b/arch/loongarch/cpu/Makefile
@@ -6,4 +6,4 @@
 extra-y = start.o
 
 obj-y += cpu.o
-obj-y += smp_secondary.o
+obj-y += smp_secondary.o genex.o
diff --git a/arch/loongarch/cpu/genex.S b/arch/loongarch/cpu/genex.S
new file mode 100644
index 000000000000..18d183a352b9
--- /dev/null
+++ b/arch/loongarch/cpu/genex.S
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Exception entry for LoongArch CPU
+ *
+ * Copyright (C) 2024 Jiaxun Yang <jiaxun.y...@flygoat.com>
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/loongarch.h>
+#include <asm/stackframe.h>
+
+.align 12
+ENTRY(exception_entry)
+       BACKUP_T0T1
+       SAVE_ALL
+       move            a0, sp
+       la.pcrel        t0, do_exceptions
+       jirl            ra, t0, 0
+       RESTORE_ALL_AND_RET
+END(exception_entry)
diff --git a/arch/loongarch/include/asm/stackframe.h 
b/arch/loongarch/include/asm/stackframe.h
new file mode 100644
index 000000000000..932150afdfaf
--- /dev/null
+++ b/arch/loongarch/include/asm/stackframe.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ * Copyright (C) 2024 Jiaxun Yang <jiaxun.y...@flygoat.com>
+ */
+#ifndef _ASM_STACKFRAME_H
+#define _ASM_STACKFRAME_H
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <generated/asm-offsets.h>
+#include <asm/loongarch.h>
+
+/* Make the addition of cfi info a little easier. */
+       .macro cfi_rel_offset reg offset=0 docfi=0
+       .if \docfi
+       .cfi_rel_offset \reg, \offset
+       .endif
+       .endm
+
+       .macro cfi_st reg offset=0 docfi=0
+       cfi_rel_offset \reg, \offset, \docfi
+       LONG_S  \reg, sp, \offset
+       .endm
+
+       .macro cfi_restore reg offset=0 docfi=0
+       .if \docfi
+       .cfi_restore \reg
+       .endif
+       .endm
+
+       .macro cfi_ld reg offset=0 docfi=0
+       LONG_L  \reg, sp, \offset
+       cfi_restore \reg \offset \docfi
+       .endm
+
+       .macro BACKUP_T0T1
+       csrwr   t0, EXCEPTION_KS0
+       csrwr   t1, EXCEPTION_KS1
+       .endm
+
+       .macro RELOAD_T0T1
+       csrrd   t0, EXCEPTION_KS0
+       csrrd   t1, EXCEPTION_KS1
+       .endm
+
+       .macro  SAVE_TEMP docfi=0
+       RELOAD_T0T1
+       cfi_st  t0, PT_R12, \docfi
+       cfi_st  t1, PT_R13, \docfi
+       cfi_st  t2, PT_R14, \docfi
+       cfi_st  t3, PT_R15, \docfi
+       cfi_st  t4, PT_R16, \docfi
+       cfi_st  t5, PT_R17, \docfi
+       cfi_st  t6, PT_R18, \docfi
+       cfi_st  t7, PT_R19, \docfi
+       cfi_st  t8, PT_R20, \docfi
+       .endm
+
+       .macro  SAVE_STATIC docfi=0
+       cfi_st  s0, PT_R23, \docfi
+       cfi_st  s1, PT_R24, \docfi
+       cfi_st  s2, PT_R25, \docfi
+       cfi_st  s3, PT_R26, \docfi
+       cfi_st  s4, PT_R27, \docfi
+       cfi_st  s5, PT_R28, \docfi
+       cfi_st  s6, PT_R29, \docfi
+       cfi_st  s7, PT_R30, \docfi
+       cfi_st  s8, PT_R31, \docfi
+       .endm
+
+       .macro  SAVE_SOME docfi=0
+       PTR_ADDI sp, sp, -PT_SIZE
+       .if \docfi
+       .cfi_def_cfa sp, 0
+       .endif
+       cfi_st  t0, PT_R3, \docfi
+       cfi_rel_offset  sp, PT_R3, \docfi
+       LONG_S  zero, sp, PT_R0
+       csrrd   t0, LOONGARCH_CSR_PRMD
+       LONG_S  t0, sp, PT_PRMD
+       csrrd   t0, LOONGARCH_CSR_CRMD
+       LONG_S  t0, sp, PT_CRMD
+       csrrd   t0, LOONGARCH_CSR_EUEN
+       LONG_S  t0, sp, PT_EUEN
+       csrrd   t0, LOONGARCH_CSR_ECFG
+       LONG_S  t0, sp, PT_ECFG
+       csrrd   t0, LOONGARCH_CSR_ESTAT
+       PTR_S   t0, sp, PT_ESTAT
+       cfi_st  ra, PT_R1, \docfi
+       cfi_st  a0, PT_R4, \docfi
+       cfi_st  a1, PT_R5, \docfi
+       cfi_st  a2, PT_R6, \docfi
+       cfi_st  a3, PT_R7, \docfi
+       cfi_st  a4, PT_R8, \docfi
+       cfi_st  a5, PT_R9, \docfi
+       cfi_st  a6, PT_R10, \docfi
+       cfi_st  a7, PT_R11, \docfi
+       csrrd   ra, LOONGARCH_CSR_ERA
+       LONG_S  ra, sp, PT_ERA
+       .if \docfi
+       .cfi_rel_offset ra, PT_ERA
+       .endif
+       cfi_st  tp, PT_R2, \docfi
+       cfi_st  fp, PT_R22, \docfi
+       /* Save U0 for sanity */
+       cfi_st  u0, PT_R21, \docfi
+       .endm
+
+       .macro  SAVE_ALL docfi=0
+       SAVE_SOME \docfi
+       SAVE_TEMP \docfi
+       SAVE_STATIC \docfi
+       .endm
+
+       .macro  RESTORE_TEMP docfi=0
+       cfi_ld  t0, PT_R12, \docfi
+       cfi_ld  t1, PT_R13, \docfi
+       cfi_ld  t2, PT_R14, \docfi
+       cfi_ld  t3, PT_R15, \docfi
+       cfi_ld  t4, PT_R16, \docfi
+       cfi_ld  t5, PT_R17, \docfi
+       cfi_ld  t6, PT_R18, \docfi
+       cfi_ld  t7, PT_R19, \docfi
+       cfi_ld  t8, PT_R20, \docfi
+       .endm
+
+       .macro  RESTORE_STATIC docfi=0
+       cfi_ld  s0, PT_R23, \docfi
+       cfi_ld  s1, PT_R24, \docfi
+       cfi_ld  s2, PT_R25, \docfi
+       cfi_ld  s3, PT_R26, \docfi
+       cfi_ld  s4, PT_R27, \docfi
+       cfi_ld  s5, PT_R28, \docfi
+       cfi_ld  s6, PT_R29, \docfi
+       cfi_ld  s7, PT_R30, \docfi
+       cfi_ld  s8, PT_R31, \docfi
+       .endm
+
+       .macro  RESTORE_SOME docfi=0
+       LONG_L  a0, sp, PT_PRMD
+       andi    a0, a0, 0x3     /* extract pplv bit */
+       beqz    a0, 8f
+       cfi_ld  u0, PT_R21, \docfi
+8:
+       LONG_L  a0, sp, PT_ERA
+       csrwr   a0, LOONGARCH_CSR_ERA
+       LONG_L  a0, sp, PT_PRMD
+       csrwr   a0, LOONGARCH_CSR_PRMD
+       cfi_ld  ra, PT_R1, \docfi
+       cfi_ld  a0, PT_R4, \docfi
+       cfi_ld  a1, PT_R5, \docfi
+       cfi_ld  a2, PT_R6, \docfi
+       cfi_ld  a3, PT_R7, \docfi
+       cfi_ld  a4, PT_R8, \docfi
+       cfi_ld  a5, PT_R9, \docfi
+       cfi_ld  a6, PT_R10, \docfi
+       cfi_ld  a7, PT_R11, \docfi
+       cfi_ld  tp, PT_R2, \docfi
+       cfi_ld  fp, PT_R22, \docfi
+       .endm
+
+       .macro  RESTORE_SP_AND_RET docfi=0
+       cfi_ld  sp, PT_R3, \docfi
+       ertn
+       .endm
+
+       .macro  RESTORE_ALL_AND_RET docfi=0
+       RESTORE_STATIC \docfi
+       RESTORE_TEMP \docfi
+       RESTORE_SOME \docfi
+       RESTORE_SP_AND_RET \docfi
+       .endm
+
+#endif /* _ASM_STACKFRAME_H */
diff --git a/arch/loongarch/lib/interrupts.c b/arch/loongarch/lib/interrupts.c
new file mode 100644
index 000000000000..886c71034c3c
--- /dev/null
+++ b/arch/loongarch/lib/interrupts.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <jiaxun.y...@flygoat.com>
+ */
+
+#include <linux/compat.h>
+#include <linux/bitfield.h>
+#include <linux/linkage.h>
+#include <hang.h>
+#include <interrupt.h>
+#include <irq_func.h>
+#include <asm/global_data.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/regdef.h>
+#include <asm/loongarch.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct resume_data *resume;
+
+void set_resume(struct resume_data *data)
+{
+       resume = data;
+}
+
+static void show_regs(struct pt_regs *regs)
+{
+#if IS_ENABLED(CONFIG_SHOW_REGS)
+       const int field = 2 * sizeof(unsigned long);
+
+#define GPR_FIELD(x) field, regs->regs[x]
+       printf("pc %0*lx ra %0*lx tp %0*lx sp %0*lx\n",
+              field, regs->csr_era, GPR_FIELD(1), GPR_FIELD(2), GPR_FIELD(3));
+       printf("a0 %0*lx a1 %0*lx a2 %0*lx a3 %0*lx\n",
+              GPR_FIELD(4), GPR_FIELD(5), GPR_FIELD(6), GPR_FIELD(7));
+       printf("a4 %0*lx a5 %0*lx a6 %0*lx a7 %0*lx\n",
+              GPR_FIELD(8), GPR_FIELD(9), GPR_FIELD(10), GPR_FIELD(11));
+       printf("t0 %0*lx t1 %0*lx t2 %0*lx t3 %0*lx\n",
+              GPR_FIELD(12), GPR_FIELD(13), GPR_FIELD(14), GPR_FIELD(15));
+       printf("t4 %0*lx t5 %0*lx t6 %0*lx t7 %0*lx\n",
+              GPR_FIELD(16), GPR_FIELD(17), GPR_FIELD(18), GPR_FIELD(19));
+       printf("t8 %0*lx u0 %0*lx s9 %0*lx s0 %0*lx\n",
+              GPR_FIELD(20), GPR_FIELD(21), GPR_FIELD(22), GPR_FIELD(23));
+       printf("s1 %0*lx s2 %0*lx s3 %0*lx s4 %0*lx\n",
+              GPR_FIELD(24), GPR_FIELD(25), GPR_FIELD(26), GPR_FIELD(27));
+       printf("s5 %0*lx s6 %0*lx s7 %0*lx s8 %0*lx\n",
+              GPR_FIELD(28), GPR_FIELD(29), GPR_FIELD(30), GPR_FIELD(31));
+#endif
+}
+
+static void __maybe_unused show_backtrace(struct pt_regs *regs)
+{
+       uintptr_t *fp = (uintptr_t *)regs->regs[0x16];
+       unsigned int count = 0;
+       ulong ra;
+
+       printf("\nbacktrace:\n");
+
+       /*
+        * there are a few entry points where the s0 register is
+        * set to gd, so to avoid changing those, just abort if
+        * the value is the same.
+        */
+       while (fp != NULL && fp != (uintptr_t *)gd) {
+               ra = fp[-1];
+               printf("%3d: fp: " REG_FMT " ra: " REG_FMT,
+                      count, (ulong)fp, ra);
+
+               if (gd && gd->flags & GD_FLG_RELOC)
+                       printf(" - ra: " REG_FMT " reloc adjusted\n",
+                              ra - gd->reloc_off);
+               else
+                       printf("\n");
+
+               fp = (uintptr_t *)fp[-2];
+               count++;
+       }
+}
+
+static const char *humanize_exc_name(unsigned int ecode, unsigned int esubcode)
+{
+       /*
+        * LoongArch users and developers are probably more familiar with
+        * those names found in the ISA manual, so we are going to print out
+        * the latter. This will require some mapping.
+        */
+       switch (ecode) {
+       case EXCCODE_RSV: return "INT";
+       case EXCCODE_TLBL: return "PIL";
+       case EXCCODE_TLBS: return "PIS";
+       case EXCCODE_TLBI: return "PIF";
+       case EXCCODE_TLBM: return "PME";
+       case EXCCODE_TLBNR: return "PNR";
+       case EXCCODE_TLBNX: return "PNX";
+       case EXCCODE_TLBPE: return "PPI";
+       case EXCCODE_ADE:
+               switch (esubcode) {
+               case EXSUBCODE_ADEF: return "ADEF";
+               case EXSUBCODE_ADEM: return "ADEM";
+               }
+               break;
+       case EXCCODE_ALE: return "ALE";
+       case EXCCODE_BCE: return "BCE";
+       case EXCCODE_SYS: return "SYS";
+       case EXCCODE_BP: return "BRK";
+       case EXCCODE_INE: return "INE";
+       case EXCCODE_IPE: return "IPE";
+       case EXCCODE_FPDIS: return "FPD";
+       case EXCCODE_LSXDIS: return "SXD";
+       case EXCCODE_LASXDIS: return "ASXD";
+       case EXCCODE_FPE:
+               switch (esubcode) {
+               case EXCSUBCODE_FPE: return "FPE";
+               case EXCSUBCODE_VFPE: return "VFPE";
+               }
+               break;
+       case EXCCODE_WATCH:
+               switch (esubcode) {
+               case EXCSUBCODE_WPEF: return "WPEF";
+               case EXCSUBCODE_WPEM: return "WPEM";
+               }
+               break;
+       case EXCCODE_BTDIS: return "BTD";
+       case EXCCODE_BTE: return "BTE";
+       case EXCCODE_GSPR: return "GSPR";
+       case EXCCODE_HVC: return "HVC";
+       case EXCCODE_GCM:
+               switch (esubcode) {
+               case EXCSUBCODE_GCSC: return "GCSC";
+               case EXCSUBCODE_GCHC: return "GCHC";
+               }
+               break;
+       /*
+        * The manual did not mention the EXCCODE_SE case, but print out it
+        * nevertheless.
+        */
+       case EXCCODE_SE: return "SE";
+       }
+
+       return "???";
+}
+
+asmlinkage void do_exceptions(struct pt_regs *regs)
+{
+       unsigned int ecode = FIELD_GET(CSR_ESTAT_EXC, regs->csr_estat);
+       unsigned int esubcode = FIELD_GET(CSR_ESTAT_ESUBCODE, regs->csr_estat);
+
+       printf("Unhandled exception: %s\n", humanize_exc_name(ecode, esubcode));
+
+       printf("ERA: " REG_FMT " ra: " REG_FMT "\n",
+              regs->csr_era, regs->regs[1]);
+       /* Print relocation adjustments, but only if gd is initialized */
+       if (gd && gd->flags & GD_FLG_RELOC)
+               printf("ERA: " REG_FMT " ra: " REG_FMT " reloc adjusted\n",
+                      regs->csr_era - gd->reloc_off, regs->regs[1] - 
gd->reloc_off);
+
+       printf("CRMD: " REG_FMT "\n", regs->csr_crmd);
+       if (ecode >= EXCCODE_TLBL && ecode <= EXCCODE_ALE)
+               printf("BADV: " REG_FMT "\n", regs->csr_badvaddr);
+
+       printf("\n");
+       show_regs(regs);
+
+       if (CONFIG_IS_ENABLED(FRAMEPOINTER))
+               show_backtrace(regs);
+
+       panic("\n");
+}
+
+int interrupt_init(void)
+{
+       return 0;
+}
+
+/*
+ * enable interrupts
+ */
+void enable_interrupts(void)
+{
+}
+
+/*
+ * disable interrupts
+ */
+int disable_interrupts(void)
+{
+       return 0;
+}

-- 
2.43.0

Reply via email to