[PATCH v2 4/9] MIPS: debug: Provide an early exception vector for low-level debugging

From: Jiaxun Yang
Date: Tue Mar 26 2024 - 16:39:54 EST


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@xxxxxxxxxxx>
---
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(+)

diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug
index 6ef42edc7d67..323ad3ec643b 100644
--- a/arch/mips/Kconfig.debug
+++ b/arch/mips/Kconfig.debug
@@ -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
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index bfac35a64ae5..6641c3370e68 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -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
diff --git a/arch/mips/kernel/debug-vec.S b/arch/mips/kernel/debug-vec.S
new file mode 100644
index 000000000000..4530bf3d5b75
--- /dev/null
+++ b/arch/mips/kernel/debug-vec.S
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023, Jiaxun Yang <jiaxun.yang@xxxxxxxxxxx>
+ * 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)
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index b825ed4476c7..9be1c4ce6324 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -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

--
2.34.1