@@ -180,6 +180,15 @@ config DEBUG_LL
this option should not be enabled for kernels that are intended to
be portable.
+config DEBUG_LL_EXCEPT
+ bool "Kernel low-level debugging of exceptions"
+ depends on DEBUG_LL
+ help
+ Say Y here to enable debugging prints for low-level exceptions.
+ This is helpful if you are debugging a early boot crash when
+ exceptions happens before trap_init(), or if you are debugging
+ SMP bringup code.
+
choice
prompt "Kernel low-level debugging port"
depends on DEBUG_LL
@@ -79,6 +79,7 @@ obj-$(CONFIG_MIPS32_N32) += scall64-n32.o signal_n32.o
obj-$(CONFIG_MIPS32_O32) += scall64-o32.o signal_o32.o
obj-$(CONFIG_DEBUG_LL) += debug.o
+obj-$(CONFIG_DEBUG_LL_EXCEPT) += debug-vec.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_PROC_FS) += proc.o
new file mode 100644
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ * MIPS Low level exception vectors and handler
+ */
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/asmmacro.h>
+
+
+.balign 0x1000
+LEAF(debug_ll_vecs)
+ .set push
+ .set noreorder
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x80
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x100
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x180
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x200
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x280
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x300
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x380
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x400
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+.org 0x480
+ move a0, zero
+ PTR_LA t9, debug_ll_exception
+ jalr t9
+ nop
+ b .
+ nop
+
+ .set pop
+END(debug_ll_vecs)
+
+
+/**
+ * debug_ll_exception() - dump relevant exception state to LL outputs
+ * @a0: pointer to NULL-terminated ASCII string naming the exception
+ *
+ * If a0 is NULL, it will print out exception vector number calculated
+ * from the ra insted. This is useful for debugging exceptions that
+ * happen before the exception handler is set up, or routed from BEV.
+ */
+LEAF(debug_ll_exception)
+ move k0, ra
+ move k1, a0
+ PTR_LA t3, printascii
+
+ PTR_LA a0, str_newline
+ jalr t3 /* Call printascii */
+ PTR_LA a0, str_exp
+ jalr t3 /* Call printascii */
+ beqz k1, 1f
+ move a0, k1
+ jalr t3 /* Call printascii */
+ b 2f
+1:
+ PTR_SRL a0, k0, 7
+ PTR_SLL a0, a0, 7
+ PTR_LA t2, printhexl
+ jalr t2
+2:
+ PTR_LA a0, str_newline
+ jalr t3 /* Call printascii */
+ PTR_LA a0, str_newline
+ jalr t3 /* Call printascii */
+
+#define DUMP_COP0_REG(reg, name, sz, _mfc0) \
+ PTR_LA a0, 8f; \
+ jalr t3; \
+ _mfc0 a0, reg; \
+ PTR_LA t2, printhex##sz; \
+ jalr t2; \
+ PTR_LA a0, str_newline; \
+ jalr t3; \
+ TEXT(name)
+
+#if defined(CONFIG_SMP) && defined(CONFIG_CPU_MIPSR6)
+ DUMP_COP0_REG(CP0_GLOBALNUMBER, "GlobalNumber: 0x", 4, mfc0)
+#endif
+#if MIPS_ISA_REV >= 2
+ DUMP_COP0_REG(CP0_EBASE, "EBase: 0x", l, MFC0)
+#endif
+#ifdef CONFIG_64BIT
+ DUMP_COP0_REG(CP0_XCONTEXT, "Xcontext: 0x", 8, dmfc0)
+#else
+ DUMP_COP0_REG(CP0_CONTEXT, "Context: 0x", 4, mfc0)
+#endif
+ DUMP_COP0_REG(CP0_CAUSE, "Cause: 0x", 4, mfc0)
+ DUMP_COP0_REG(CP0_STATUS, "Status: 0x", 4, mfc0)
+ DUMP_COP0_REG(CP0_EPC, "EPC: 0x", l, MFC0)
+ DUMP_COP0_REG(CP0_BADVADDR, "BadVAddr: 0x", l, MFC0)
+#if MIPS_ISA_REV >= 6
+ DUMP_COP0_REG(CP0_BADINSTR, "BadInstr: 0x", 4, mfc0)
+#endif
+ PTR_LA a0, str_newline
+ jalr t3 /* Call printascii */
+ jr k0
+ END(debug_ll_exception)
+
+.pushsection .rodata.str
+str_exp: .asciiz "Low level exception: "
+str_newline: .asciiz "\r\n"
+.popsection
+
+NESTED(setup_debug_ll_exception, 0, ra)
+ PTR_LA t0, debug_ll_vecs
+#if MIPS_ISA_REV >= 2
+ /* Set ebase to debug_ll_vecs */
+#if defined (CONFIG_64BIT) && !defined(KBUILD_64BIT_SYM32)
+ ori t0, MIPS_EBASE_WG
+#endif
+ MTC0 t0, CP0_EBASE
+#else
+ /* Copy debug_ll_vecs to start of KSEG0 */
+ PTR_LI t1, CKSEG0
+ PTR_ADDIU t2, t0, 0x400 /* Only copy 0x400 as that is what reserved on old systems */
+copy_word:
+ PTR_LW t3, 0(t0)
+ PTR_SW t3, 0(t1)
+ PTR_ADDIU t0, t0, PTRSIZE
+ PTR_ADDIU t1, t1, PTRSIZE
+ PTR_BNE t0, t2, copy_word
+#endif
+ /* Clear BEV bit in status register */
+ mfc0 t0, CP0_STATUS
+ and t0, t0, ~ST0_BEV
+ mtc0 t0, CP0_STATUS
+ jr ra
+ END(setup_debug_ll_exception)
@@ -106,6 +106,10 @@ NESTED(kernel_entry, 16, sp) # kernel entry point
LONG_S a2, fw_arg2
LONG_S a3, fw_arg3
+#ifdef CONFIG_DEBUG_LL_EXCEPT
+ jal setup_debug_ll_exception
+#endif
+
MTC0 zero, CP0_CONTEXT # clear context register
#ifdef CONFIG_64BIT
MTC0 zero, CP0_XCONTEXT
This is helpful for debugging of early boot crash when exceptions happens before trap_init(), or for debugging SMP bringup code. It will install exception handler very early on kernel_entry by setting ebase or copy itself to start of CKSEG0, and print off exception vectors and other status registers like: Low level exception: ffffffff8011e180 GlobalNumber: 0x00000000 EBase: 0xffffffff8011e000 Xcontext: 0x0000000000000000 Cause: 0x0000000c Status: 0x14000082 EPC: 0xffffffff80d324e0 BadVAddr: 0x0000000000000000 BadInstr: 0xac000000 Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com> --- arch/mips/Kconfig.debug | 9 ++ arch/mips/kernel/Makefile | 1 + arch/mips/kernel/debug-vec.S | 194 +++++++++++++++++++++++++++++++++++++++++++ arch/mips/kernel/head.S | 4 + 4 files changed, 208 insertions(+)