@@ -164,3 +164,83 @@ config MIPS_CPS_NS16550_WIDTH
lb/sb instructions.
endif # MIPS_CPS_NS16550_BOOL
+
+# These options are only for real kernel hackers who want to get their hands dirty.
+config DEBUG_LL
+ bool "Kernel low-level debugging functions (read help!)"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here to include definitions of printascii, printch, printhex
+ in the kernel. This is helpful if you are debugging code that
+ executes before the console is initialized.
+
+ Note that selecting this option will limit the kernel to a single
+ UART or semihosting definition, as specified below. Attempting to
+ boot the kernel image on a different platform *will not work*, so
+ this option should not be enabled for kernels that are intended to
+ be portable.
+
+choice
+ prompt "Kernel low-level debugging port"
+ depends on DEBUG_LL
+
+ config DEBUG_MIPS_UHI
+ bool "Kernel low-level debugging via UHI semihosting"
+ help
+ Say Y here if you want kernel low-level debugging support
+ via MIPS UHI semihosting.
+
+ config DEBUG_LL_UART_8250
+ bool "Kernel low-level debugging via 8250 UART"
+ select DEBUG_LL_UART
+ help
+ Say Y here if you wish the debug print routes to direct
+ their output to an 8250 UART. You can use this option
+ to provide the parameters for the 8250 UART rather than
+ selecting one of the platform specific options below if
+ you know the parameters for the port.
+
+endchoice
+
+config DEBUG_LL_INCLUDE
+ string
+ default "debug/8250.S" if DEBUG_LL_UART_8250 || DEBUG_UART_8250
+ default "debug/uhi.S" if DEBUG_MIPS_UHI
+ default "debug-macro.S"
+
+# Compatibility options for 8250
+config DEBUG_UART_8250
+ bool
+ select DEBUG_LL_UART
+
+config DEBUG_LL_UART
+ bool
+
+config DEBUG_UART_FLOW_CONTROL
+ bool "Enable flow control (CTS) for the debug UART"
+ depends on DEBUG_LL_UART
+ default n
+ help
+ Some UART ports are connected to terminals that will use modem
+ control signals to indicate whether they are ready to receive text.
+ In practice this means that the terminal is asserting the special
+ control signal CTS (Clear To Send). If your debug UART supports
+ this and your debug terminal will require it, enable this option.
+
+config DEBUG_UART_PHYS
+ hex "Physical base address of debug UART"
+ depends on DEBUG_LL_UART
+ help
+ This is the physical base address of the debug UART. It must be
+ accessible from unmapped kernel space (i.e. KSEG1 for 32bit kernels
+ or XKPHYS for 64bit kernels).
+
+config DEBUG_UART_8250_SHIFT
+ int "Register offset shift for the 8250 debug UART"
+ depends on DEBUG_LL_UART_8250 || DEBUG_UART_8250
+ default 2
+
+config DEBUG_UART_8250_WIDTH
+ int "Register width for the 8250 debug UART"
+ depends on DEBUG_LL_UART_8250 || DEBUG_UART_8250
+ default 1
new file mode 100644
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ * MIPS Low level debug include file for 8250 UART
+ */
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <linux/serial_reg.h>
+
+#define DEBUG_LL_UART
+
+#define UART_BASE CKSEG1ADDR(CONFIG_DEBUG_UART_PHYS)
+
+#define UART_TX_OFS (UART_TX << CONFIG_DEBUG_UART_8250_SHIFT)
+#define UART_LSR_OFS (UART_LSR << CONFIG_DEBUG_UART_8250_SHIFT)
+#define UART_MSR_OFS (UART_MSR << CONFIG_DEBUG_UART_8250_SHIFT)
+
+#if CONFIG_DEBUG_UART_8250_WIDTH == 1
+# define UART_L lb
+# define UART_S sb
+#elif CONFIG_DEBUG_UART_8250_WIDTH == 2
+# define UART_L lh
+# define UART_S sh
+#elif CONFIG_DEBUG_UART_8250_WIDTH == 4
+# define UART_L lw
+# define UART_S sw
+#elif defined(CONFIG_64BIT) && CONFIG_DEBUG_UART_8250_WIDTH == 8
+# define UART_L ld
+# define UART_S sd
+#else
+# define UART_L lb
+# define UART_S sb
+#endif
+
+ .macro addruart,rd,rx
+ PTR_LA \rd, UART_BASE
+ .endm
+
+ .macro senduart,rd,rx
+ UART_S \rd, UART_TX_OFS(\rx)
+ .endm
+
+ .macro busyuart,rd,rx
+1002:
+ UART_L \rd, UART_LSR_OFS(\rx)
+ andi \rd, \rd, (UART_LSR_TEMT | UART_LSR_THRE)
+ xori \rd, (UART_LSR_TEMT | UART_LSR_THRE)
+ bnez \rd, 1002b
+ .endm
+
+ .macro waituarttxrdy,rd,rx
+ .endm
+
+ .macro waituartcts,rd,rx
+1001:
+ UART_L \rd, UART_MSR_OFS(\rx)
+ andi \rd, UART_MSR_CTS
+ beqz \rd, 1001b
+ .endm
new file mode 100644
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ * MIPS Low level debug include file for UHI semihosting
+ */
+
+#include <asm/asm.h>
+
+/**
+ * printch() - write a character to UHI Plog
+ * @a0: ASCII character to write
+ *
+ * clobbers v0, t9
+ * UHI may clobber k0, k1
+ */
+ .pushsection .bss
+charbuf_addr: .space 8
+ .popsection
+NESTED(printch, 0, ra)
+ PTR_LI t9, charbuf_addr
+ sb a0, 0(t9)
+ sb zero, 1(t9)
+ move a0, t9
+ li v0, 0x1
+ li t9, 13 /* plog */
+ .set push
+ .set MIPS_ISA_LEVEL_RAW
+ sdbbp 1
+ .set pop
+ jr ra
+ END(printch)
+
+/**
+ * printascii() - write a string to UHI Plog
+ * @a0: pointer to NULL-terminated ASCII string
+ *
+ * clobbers v0
+ * UHI may clobber k0, k1
+ */
+NESTED(printascii, 0, ra)
+ li v0, 0x1
+ li t9, 13 /* plog */
+ .set push
+ .set MIPS_ISA_LEVEL_RAW
+ sdbbp 1
+ .set pop
+ jr ra
+ END(printascii)
@@ -78,6 +78,8 @@ obj-$(CONFIG_MIPS32_COMPAT) += linux32.o ptrace32.o signal32.o
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_KGDB) += kgdb.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
new file mode 100644
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ * MIPS Low level debug routines
+ *
+ * Note: Those functions are designed carefully to only clobber
+ * t0, t1, t9, so they can be used without stack. Also they are
+ * position independent to be called from any place.
+ */
+
+#include <linux/compiler.h>
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+
+#include CONFIG_DEBUG_LL_INCLUDE
+
+#ifdef DEBUG_LL_UART
+/**
+ * printch() - write a character to the UART
+ * @a0: ASCII character to write
+ *
+ * clobbers t0, t9
+ */
+NESTED(printch, 0, ra)
+ addruart t9, t0
+#ifdef CONFIG_DEBUG_UART_FLOW_CONTROL
+ waituartcts t0, t9
+#endif
+ waituarttxrdy t0, t9
+ senduart a0, t9
+ busyuart t0, t9
+ jr ra
+ END(printch)
+
+/**
+ * printascii() - write a string to the UART
+ * @a0: pointer to NULL-terminated ASCII string
+ *
+ * clobbers t0, t1, t9
+ *
+ * Write a null-terminated ASCII string to the UART.
+ */
+NESTED(printascii, 0, ra)
+ addruart t9, t0
+ move t1, a0
+
+1: lb a0, 0(t1)
+ beqz a0, 2f
+#ifdef CONFIG_DEBUG_UART_FLOW_CONTROL
+ waituartcts t0, t9
+#endif
+ waituarttxrdy t0, t9
+ senduart a0, t9
+ busyuart t0, t9
+ PTR_ADDIU t1, t1, 1
+ b 1b
+
+2: jr ra
+ END(printascii)
+#endif /* DEBUG_LL_UART */
+
+/**
+ * printhex() - write n byte hex value to the UART
+ * @a0: Hex value to write to the UART
+ * @a1: number of bytes to write to the UART
+ *
+ * clobbers: t0, t1, t9
+ */
+
+ .pushsection .bss
+hexbuf_addr: .space 32
+ .popsection
+
+NESTED(printhex, 0, ra)
+ PTR_LA t9, hexbuf_addr
+ sll a1, a1, 1
+1:
+ subu t0, a1, 1
+ sll t0, t0, 2
+ LONG_SRLV t0, a0, t0
+ andi t0, t0, 0xf
+ li t1, '0'
+ blt t0, 10, 2f
+ li t1, 'a'
+ addiu t0, t0, -10
+2:
+ addu t1, t1, t0
+ sb t1, 0(t9)
+ PTR_ADDIU t9, t9, 1
+ addiu a1, a1, -1
+ bnez a1, 1b
+ sb zero, 0(t9)
+
+ move a1, ra
+ PTR_LA a0, hexbuf_addr
+ bal printascii
+
+ jr a1
+ END(printhex)
+
+NESTED(printhex1, 0, ra)
+ li a1, 1
+ b printhex
+ END(printhex1)
+
+NESTED(printhex2, 0, ra)
+ li a1, 2
+ b printhex
+ END(printhex2)
+
+NESTED(printhex4, 0, ra)
+ li a1, 4
+ b printhex
+ END(printhex4)
+
+#ifdef CONFIG_64BIT
+NESTED(printhex8, 0, ra)
+ li a1, 8
+ b printhex
+ END(printhex8)
+#endif
+
+NESTED(printhexl, 0, ra)
+ li a1, PTRSIZE
+ b printhex
+ END(printhexl)