Hi all,
This patchset adds support for MIPS UHI semihosting.
It has been implemented on many debugers such as MIPS CodeScape debugger
and OpenOCD.
It is also supported by QEMU's -semihosting option.
I didn't add MAINTAINERS entry for this driver because I find other
simple earlycon drivers don't have one either. Do let me know if it
should be added.
Thanks
Jiaxun
Jiaxun Yang (3):
MIPS: Add header for UHI semihosting defines
MIPS: zboot: Add UHI semihosting debug print support
serial: Add an earlycon driver for MIPS UHI semihosting
arch/mips/Kconfig.debug | 11 +
arch/mips/boot/compressed/Makefile | 1 +
arch/mips/boot/compressed/dbg-uhi.c | 11 +
arch/mips/boot/compressed/dbg.c | 4 +-
arch/mips/boot/compressed/decompress.c | 4 +
arch/mips/include/asm/uhi.h | 309 +++++++++++++++++++++++++
drivers/tty/serial/Kconfig | 13 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/earlycon-mips-uhi.c | 85 +++++++
9 files changed, 437 insertions(+), 2 deletions(-)
create mode 100644 arch/mips/boot/compressed/dbg-uhi.c
create mode 100644 arch/mips/include/asm/uhi.h
create mode 100644 drivers/tty/serial/earlycon-mips-uhi.c
--
2.34.1
Add a header for MIPS UHI semihosting defines.
Including calling number, data structures and functions
to do UHI calls.
Signed-off-by: Jiaxun Yang <[email protected]>
---
arch/mips/include/asm/uhi.h | 309 ++++++++++++++++++++++++++++++++++++
1 file changed, 309 insertions(+)
create mode 100644 arch/mips/include/asm/uhi.h
diff --git a/arch/mips/include/asm/uhi.h b/arch/mips/include/asm/uhi.h
new file mode 100644
index 000000000000..5e72fd5a67fb
--- /dev/null
+++ b/arch/mips/include/asm/uhi.h
@@ -0,0 +1,309 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MIPS UHI semihosting define and helpers
+ *
+ * Based on MD01069 - Unified Hosting Interface Reference Manual
+ *
+ * Copyright (C) 2023 Jiaxun Yang <[email protected]>
+ */
+
+
+#ifndef _MIPS_UHI_H_
+#define _MIPS_UHI_H_
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+#define UHI_NR_exit 1
+#define UHI_NR_open 2
+#define UHI_NR_close 3
+#define UHI_NR_read 4
+#define UHI_NR_write 5
+#define UHI_NR_lseek 6
+#define UHI_NR_unlink 7
+#define UHI_NR_fstat 8
+#define UHI_NR_argc 9
+#define UHI_NR_argnlen 10
+#define UHI_NR_argn 11
+#define UHI_NR_plog 13
+#define UHI_NR_assert 14
+#define UHI_NR_pread 19
+#define UHI_NR_pwrite 20
+#define UHI_NR_link 22
+#define UHI_NR_bootfailure 23
+
+/* For SDDBP code and R2 */
+#define UHI_CALL_CODE "1"
+#define UHI_CALL_CODE_RAW 1
+
+struct uhi_stat {
+ int16_t st_dev;
+ uint16_t st_ino;
+ uint32_t st_mode;
+ uint16_t st_nlink;
+ uint16_t st_uid;
+ uint16_t st_gid;
+ int16_t st_rdev;
+ uint64_t st_size;
+ uint64_t st_atime;
+ uint64_t st_spare1;
+ uint64_t st_mtime;
+ uint64_t st_spare2;
+ uint64_t st_ctime;
+ uint64_t st_spare3;
+ uint64_t st_blksize;
+ uint64_t st_blocks;
+ uint64_t st_spare4[2];
+};
+
+#define UHI_O_RDONLY 0x0
+#define UHI_O_WRONLY 0x1
+#define UHI_O_RDWR 0x2
+#define UHI_O_APPEND 0x8
+#define UHI_O_CREAT 0x200
+#define UHI_O_TRUNC 0x400
+#define UHI_O_EXCL 0x800
+
+#define UHI_EACCESS 13
+#define UHI_EAGAIN 11
+#define UHI_EBADF 9
+#define UHI_EBADMSG 77
+#define UHI_EBUSY 16
+#define UHI_ECONNRESET 104
+#define UHI_EEXIST 17
+#define UHI_EFBIG 27
+#define UHI_EINTR 4
+#define UHI_EINVAL 22
+#define UHI_EIO 5
+#define UHI_EISDIR 21
+#define UHI_ELOOP 92
+#define UHI_EMFILE 24
+#define UHI_EMLINK 31
+#define UHI_ENAMETOOLONG 91
+#define UHI_ENETDOWN 115
+#define UHI_ENETUNREACH 114
+#define UHI_ENFILE 23
+#define UHI_ENOBUFS 105
+#define UHI_ENOENT 2
+#define UHI_ENOMEM 12
+#define UHI_ENOSPC 28
+#define UHI_ENOSR 63
+#define UHI_ENOTCONN 128
+#define UHI_ENOTDIR 20
+#define UHI_ENXIO 6
+#define UHI_EOVERFLOW 139
+#define UHI_EPERM 1
+#define UHI_EPIPE 32
+#define UHI_ERANGE 34
+#define UHI_EROFS 30
+#define UHI_ESPIPE 29
+#define UHI_ETIMEDOUT 116
+#define UHI_ETXTBSY 26
+#define UHI_EWOULDBLOCK 11
+#define UHI_EXDEV 18
+
+
+struct uhi_gpctx {
+ unsigned long r[31];
+ unsigned long epc;
+ unsigned long badvaddr;
+ unsigned long hi;
+ unsigned long lo;
+ void *link;
+ uint32_t status;
+ uint32_t cause;
+ uint32_t badinstr;
+ uint32_t badpinstr;
+};
+
+static inline long __uhi_call0(long n)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline long __uhi_call1(long n, long a)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r4 __asm__("$4") = a;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25), "r"(r4)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline long __uhi_call2(long n, long a, long b)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r4 __asm__("$4") = a;
+ register long r5 __asm__("$5") = b;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25), "r"(r4), "r"(r5)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline long __uhi_call3(long n, long a, long b, long c)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r4 __asm__("$4") = a;
+ register long r5 __asm__("$5") = b;
+ register long r6 __asm__("$6") = c;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25), "r"(r4), "r"(r5), "r"(r6)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline long __uhi_call4(long n, long a, long b, long c, long d)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r4 __asm__("$4") = a;
+ register long r5 __asm__("$5") = b;
+ register long r6 __asm__("$6") = c;
+ register long r7 __asm__("$7") = d;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25), "r"(r4), "r"(r5), "r"(r6), "r"(r7)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline int32_t uhi_exit(int32_t code)
+{
+ return __uhi_call1(UHI_NR_exit, code);
+}
+
+static inline int32_t uhi_open(const char *path, int32_t flags, int32_t mode)
+{
+ return __uhi_call3(UHI_NR_open, (long)path, flags, mode);
+}
+
+static inline int32_t uhi_close(int32_t fd)
+{
+ return __uhi_call1(UHI_NR_close, fd);
+}
+
+static inline int32_t uhi_read(int32_t fd, char *buf, long num)
+{
+ return __uhi_call3(UHI_NR_read, fd, (long)buf, num);
+}
+
+static inline long uhi_write(int32_t fd, const char *buf, long num)
+{
+ return __uhi_call3(UHI_NR_write, fd, (long)buf, num);
+}
+
+static inline int32_t uhi_lseek(int32_t fd, long offset, int32_t whence)
+{
+ return __uhi_call3(UHI_NR_lseek, fd, offset, whence);
+}
+
+static inline int32_t uhi_unlink(const char *path)
+{
+ return __uhi_call1(UHI_NR_unlink, (long)path);
+}
+
+static inline int32_t uhi_fstat(int32_t fd, struct uhi_stat *buf)
+{
+ return __uhi_call2(UHI_NR_fstat, fd, (long)buf);
+}
+
+static inline int32_t uhi_argc(void)
+{
+ return __uhi_call0(UHI_NR_argc);
+}
+
+static inline int32_t uhi_argnlen(int32_t n)
+{
+ return __uhi_call1(UHI_NR_argnlen, n);
+}
+
+static inline int32_t uhi_argn(int32_t n, char *buf, int32_t num)
+{
+ return __uhi_call3(UHI_NR_argn, n, (long)buf, num);
+}
+
+static inline int32_t uhi_plog(const char *buf, int32_t num)
+{
+ return __uhi_call2(UHI_NR_plog, (long)buf, num);
+}
+
+static inline void uhi_assert(const char *msg, const char *file, int32_t line)
+{
+ __uhi_call3(UHI_NR_assert, (long)msg, (long)file, line);
+}
+
+static inline int32_t uhi_link(const char *oldpath, const char *newpath)
+{
+ return __uhi_call2(UHI_NR_link, (long)oldpath, (long)newpath);
+}
+
+static inline int32_t uhi_pread(int32_t fd, char *buf, long num, long offset)
+{
+ return __uhi_call4(UHI_NR_pread, fd, (long)buf, num, offset);
+}
+
+static inline int32_t uhi_pwrite(int32_t fd, const char *buf, long num, long offset)
+{
+ return __uhi_call4(UHI_NR_pwrite, fd, (long)buf, num, offset);
+}
+
+static inline void uhi_bootfailure(int reason)
+{
+ __uhi_call1(UHI_NR_bootfailure, reason);
+}
+
+#endif
--
2.34.1
Support print debug message via MIPS UHI semihosting Plog
functions.
Signed-off-by: Jiaxun Yang <[email protected]>
---
arch/mips/Kconfig.debug | 11 +++++++++++
arch/mips/boot/compressed/Makefile | 1 +
arch/mips/boot/compressed/dbg-uhi.c | 11 +++++++++++
arch/mips/boot/compressed/dbg.c | 4 ++--
arch/mips/boot/compressed/decompress.c | 4 ++++
5 files changed, 29 insertions(+), 2 deletions(-)
create mode 100644 arch/mips/boot/compressed/dbg-uhi.c
diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug
index f4ae7900fcd3..1393bdb33f5c 100644
--- a/arch/mips/Kconfig.debug
+++ b/arch/mips/Kconfig.debug
@@ -98,6 +98,17 @@ config DEBUG_ZBOOT
to reduce the kernel image size and speed up the booting procedure a
little.
+config ZBOOT_DBG_UHI
+ bool "Enable UHI debugging"
+ depends on DEBUG_ZBOOT
+ default n
+ help
+ Enable this option to debug compressed kernel support via UHI.
+ Logs will be outputed to the host machine via UHI Plog function.
+ You MUST connect system to a debugger with UHI semihosting support
+ or use a boot montor implemented UHI exceptions, otherwise the
+ system will hang.
+
config ZBOOT_INGENIC_UART
int "UART to use for compressed kernel debugging"
depends on DEBUG_ZBOOT && MACH_INGENIC_SOC
diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile
index 6cc28173bee8..5667597c3584 100644
--- a/arch/mips/boot/compressed/Makefile
+++ b/arch/mips/boot/compressed/Makefile
@@ -45,6 +45,7 @@ vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o $(obj)/string.o $(obj)/bswaps
ifdef CONFIG_DEBUG_ZBOOT
vmlinuzobjs-$(CONFIG_DEBUG_ZBOOT) += $(obj)/dbg.o
+vmlinuzobjs-$(CONFIG_ZBOOT_DBG_UHI) += $(obj)/dbg-uhi.o
vmlinuzobjs-$(CONFIG_SYS_SUPPORTS_ZBOOT_UART16550) += $(obj)/uart-16550.o
vmlinuzobjs-$(CONFIG_SYS_SUPPORTS_ZBOOT_UART_PROM) += $(obj)/uart-prom.o
vmlinuzobjs-$(CONFIG_MIPS_ALCHEMY) += $(obj)/uart-alchemy.o
diff --git a/arch/mips/boot/compressed/dbg-uhi.c b/arch/mips/boot/compressed/dbg-uhi.c
new file mode 100644
index 000000000000..7daa8de717b0
--- /dev/null
+++ b/arch/mips/boot/compressed/dbg-uhi.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * zboot debug output for MIPS UHI semihosting
+ */
+
+#include <asm/uhi.h>
+
+void puts(const char *s)
+{
+ uhi_plog(s, 0);
+}
diff --git a/arch/mips/boot/compressed/dbg.c b/arch/mips/boot/compressed/dbg.c
index f6728a8fd1c3..7fa5242e2b7d 100644
--- a/arch/mips/boot/compressed/dbg.c
+++ b/arch/mips/boot/compressed/dbg.c
@@ -4,7 +4,7 @@
*
* NOTE: putc() is board specific, if your board have a 16550 compatible uart,
* please select SYS_SUPPORTS_ZBOOT_UART16550 for your machine. othewise, you
- * need to implement your own putc().
+ * need to implement your own putc() or puts().
*/
#include <linux/compiler.h>
#include <linux/types.h>
@@ -13,7 +13,7 @@ void __weak putc(char c)
{
}
-void puts(const char *s)
+void __weak puts(const char *s)
{
char c;
while ((c = *s++) != '\0') {
diff --git a/arch/mips/boot/compressed/decompress.c b/arch/mips/boot/compressed/decompress.c
index c5dd415254d3..f4e69dfe2923 100644
--- a/arch/mips/boot/compressed/decompress.c
+++ b/arch/mips/boot/compressed/decompress.c
@@ -17,6 +17,7 @@
#include <asm/addrspace.h>
#include <asm/unaligned.h>
+#include <asm/uhi.h>
#include <asm-generic/vmlinux.lds.h>
/*
@@ -46,6 +47,9 @@ void error(char *x)
puts(x);
puts("\n\n -- System halted");
+#ifdef CONFIG_ZBOOT_DBG_UHI
+ uhi_bootfailure(0);
+#endif
while (1)
; /* Halt */
}
--
2.34.1
UHI is MIPS's implementation of semihosting.
Add an earlycon driver to help with debugging on boot.
This driver is capable for print log using UHI's "Plog" or interact
with KGDB using UHI's stdio function.
Signed-off-by: Jiaxun Yang <[email protected]>
---
drivers/tty/serial/Kconfig | 13 ++++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/earlycon-mips-uhi.c | 85 ++++++++++++++++++++++++++
3 files changed, 99 insertions(+)
create mode 100644 drivers/tty/serial/earlycon-mips-uhi.c
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index bdc568a4ab66..04c62c6b45cd 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -85,6 +85,19 @@ config SERIAL_EARLYCON_SEMIHOST
This is enabled with "earlycon=smh" on the kernel command line.
The console is enabled when early_param is processed.
+config SERIAL_EARLYCON_UHI
+ bool "Early console using MIPS UHI semihosting"
+ depends on MIPS
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Support for early debug console using UHI semihosting.
+ This enables the console before standard serial driver is probed.
+ This is enabled with "earlycon=uhi" or "earlycon=uhi_stdio" on the
+ kernel command line.
+ The console is enabled when early_param is processed.
+
config SERIAL_EARLYCON_RISCV_SBI
bool "Early console using RISC-V SBI"
depends on RISCV_SBI_V01
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index f6b8c220dcfb..ef5e9c87aea1 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -9,6 +9,7 @@ serial_base-y := serial_core.o serial_base_bus.o serial_ctrl.o serial_port.o
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o
obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o
+obj-$(CONFIG_SERIAL_EARLYCON_MIPS_UHI) += earlycon-mips-uhi.o
# These Sparc drivers have to appear before others such as 8250
# which share ttySx minor node space. Otherwise console device
diff --git a/drivers/tty/serial/earlycon-mips-uhi.c b/drivers/tty/serial/earlycon-mips-uhi.c
new file mode 100644
index 000000000000..002bb2c37064
--- /dev/null
+++ b/drivers/tty/serial/earlycon-mips-uhi.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MIPS UHI semihosting based earlycon
+ *
+ * Copyright (C) 2023 Jiaxun Yang <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <asm/uhi.h>
+
+static int stdin_fd = -1;
+static int stdout_fd = -1;
+
+static void uhi_plog_write(struct console *con, const char *s, unsigned int n)
+{
+ uhi_plog(s, 0);
+}
+
+static void uhi_stdout_write(struct console *con, const char *s, unsigned int n)
+{
+ if (stdout_fd < 0)
+ return;
+
+ uhi_write(stdout_fd, s, n);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int uhi_stdin_read(struct console *con, char *s, unsigned int n)
+{
+ if (stdin_fd < 0)
+ return 0;
+
+ return uhi_read(stdin_fd, s, n);
+}
+#endif
+
+static int uhi_stdio_fd_open(struct console *co, char *options)
+{
+ /*
+ * You have to open both stdin and stdout to get console work
+ * properly on some old CodeScape debugger.
+ */
+ stdin_fd = uhi_open("/dev/stdin", UHI_O_RDONLY, 0);
+ stdout_fd = uhi_open("/dev/stdout", UHI_O_WRONLY, 0);
+
+ return (stdin_fd < 0 || stdout_fd < 0) ? -ENODEV : 0;
+}
+
+static int uhi_stdio_fd_close(struct console *co)
+{
+ int ret1 = 0, ret2 = 0;
+
+ if (stdin_fd >= 0)
+ ret1 = uhi_close(stdin_fd);
+ if (stdout_fd >= 0)
+ ret2 = uhi_close(stdout_fd);
+
+ return (ret1 < 0 || ret2 < 0) ? -ENODEV : 0;
+}
+
+static int
+__init early_uhi_setup(struct earlycon_device *device, const char *opt)
+{
+ device->con->write = uhi_plog_write;
+ return 0;
+}
+
+static int
+__init early_uhi_stdio_setup(struct earlycon_device *device, const char *opt)
+{
+
+ device->con->setup = uhi_stdio_fd_open;
+ device->con->exit = uhi_stdio_fd_close;
+ device->con->write = uhi_stdout_write;
+#ifdef CONFIG_CONSOLE_POLL
+ device->con->read = uhi_stdin_read;
+#endif
+ return 0;
+}
+
+EARLYCON_DECLARE(uhi, early_uhi_setup);
+EARLYCON_DECLARE(uhi_stdio, early_uhi_stdio_setup);
--
2.34.1
Add a header for MIPS UHI semihosting defines.
Including calling number, data structures and functions
to do UHI calls.
Signed-off-by: Jiaxun Yang <[email protected]>
---
arch/mips/include/asm/uhi.h | 309 ++++++++++++++++++++++++++++++++++++
1 file changed, 309 insertions(+)
create mode 100644 arch/mips/include/asm/uhi.h
diff --git a/arch/mips/include/asm/uhi.h b/arch/mips/include/asm/uhi.h
new file mode 100644
index 000000000000..5e72fd5a67fb
--- /dev/null
+++ b/arch/mips/include/asm/uhi.h
@@ -0,0 +1,309 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MIPS UHI semihosting define and helpers
+ *
+ * Based on MD01069 - Unified Hosting Interface Reference Manual
+ *
+ * Copyright (C) 2023 Jiaxun Yang <[email protected]>
+ */
+
+
+#ifndef _MIPS_UHI_H_
+#define _MIPS_UHI_H_
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+#define UHI_NR_exit 1
+#define UHI_NR_open 2
+#define UHI_NR_close 3
+#define UHI_NR_read 4
+#define UHI_NR_write 5
+#define UHI_NR_lseek 6
+#define UHI_NR_unlink 7
+#define UHI_NR_fstat 8
+#define UHI_NR_argc 9
+#define UHI_NR_argnlen 10
+#define UHI_NR_argn 11
+#define UHI_NR_plog 13
+#define UHI_NR_assert 14
+#define UHI_NR_pread 19
+#define UHI_NR_pwrite 20
+#define UHI_NR_link 22
+#define UHI_NR_bootfailure 23
+
+/* For SDDBP code and R2 */
+#define UHI_CALL_CODE "1"
+#define UHI_CALL_CODE_RAW 1
+
+struct uhi_stat {
+ int16_t st_dev;
+ uint16_t st_ino;
+ uint32_t st_mode;
+ uint16_t st_nlink;
+ uint16_t st_uid;
+ uint16_t st_gid;
+ int16_t st_rdev;
+ uint64_t st_size;
+ uint64_t st_atime;
+ uint64_t st_spare1;
+ uint64_t st_mtime;
+ uint64_t st_spare2;
+ uint64_t st_ctime;
+ uint64_t st_spare3;
+ uint64_t st_blksize;
+ uint64_t st_blocks;
+ uint64_t st_spare4[2];
+};
+
+#define UHI_O_RDONLY 0x0
+#define UHI_O_WRONLY 0x1
+#define UHI_O_RDWR 0x2
+#define UHI_O_APPEND 0x8
+#define UHI_O_CREAT 0x200
+#define UHI_O_TRUNC 0x400
+#define UHI_O_EXCL 0x800
+
+#define UHI_EACCESS 13
+#define UHI_EAGAIN 11
+#define UHI_EBADF 9
+#define UHI_EBADMSG 77
+#define UHI_EBUSY 16
+#define UHI_ECONNRESET 104
+#define UHI_EEXIST 17
+#define UHI_EFBIG 27
+#define UHI_EINTR 4
+#define UHI_EINVAL 22
+#define UHI_EIO 5
+#define UHI_EISDIR 21
+#define UHI_ELOOP 92
+#define UHI_EMFILE 24
+#define UHI_EMLINK 31
+#define UHI_ENAMETOOLONG 91
+#define UHI_ENETDOWN 115
+#define UHI_ENETUNREACH 114
+#define UHI_ENFILE 23
+#define UHI_ENOBUFS 105
+#define UHI_ENOENT 2
+#define UHI_ENOMEM 12
+#define UHI_ENOSPC 28
+#define UHI_ENOSR 63
+#define UHI_ENOTCONN 128
+#define UHI_ENOTDIR 20
+#define UHI_ENXIO 6
+#define UHI_EOVERFLOW 139
+#define UHI_EPERM 1
+#define UHI_EPIPE 32
+#define UHI_ERANGE 34
+#define UHI_EROFS 30
+#define UHI_ESPIPE 29
+#define UHI_ETIMEDOUT 116
+#define UHI_ETXTBSY 26
+#define UHI_EWOULDBLOCK 11
+#define UHI_EXDEV 18
+
+
+struct uhi_gpctx {
+ unsigned long r[31];
+ unsigned long epc;
+ unsigned long badvaddr;
+ unsigned long hi;
+ unsigned long lo;
+ void *link;
+ uint32_t status;
+ uint32_t cause;
+ uint32_t badinstr;
+ uint32_t badpinstr;
+};
+
+static inline long __uhi_call0(long n)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline long __uhi_call1(long n, long a)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r4 __asm__("$4") = a;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25), "r"(r4)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline long __uhi_call2(long n, long a, long b)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r4 __asm__("$4") = a;
+ register long r5 __asm__("$5") = b;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25), "r"(r4), "r"(r5)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline long __uhi_call3(long n, long a, long b, long c)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r4 __asm__("$4") = a;
+ register long r5 __asm__("$5") = b;
+ register long r6 __asm__("$6") = c;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25), "r"(r4), "r"(r5), "r"(r6)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline long __uhi_call4(long n, long a, long b, long c, long d)
+{
+ register long r2 __asm__("$2");
+ register long r25 __asm__("$25") = n;
+ register long r4 __asm__("$4") = a;
+ register long r5 __asm__("$5") = b;
+ register long r6 __asm__("$6") = c;
+ register long r7 __asm__("$7") = d;
+ register long r3 __asm__("$3");
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set " MIPS_ISA_LEVEL " \n"
+ "addu $2,$0,%2 \n"
+ "sdbbp " UHI_CALL_CODE " \n"
+ ".set pop \n"
+ : "=&r"(r2), "=r"(r3)
+ : "ir"(UHI_CALL_CODE_RAW), "0"(r2), "r"(r25), "r"(r4), "r"(r5), "r"(r6), "r"(r7)
+ : "memory");
+
+ return r2 == -1 ? -r3 : r2;
+}
+
+static inline int32_t uhi_exit(int32_t code)
+{
+ return __uhi_call1(UHI_NR_exit, code);
+}
+
+static inline int32_t uhi_open(const char *path, int32_t flags, int32_t mode)
+{
+ return __uhi_call3(UHI_NR_open, (long)path, flags, mode);
+}
+
+static inline int32_t uhi_close(int32_t fd)
+{
+ return __uhi_call1(UHI_NR_close, fd);
+}
+
+static inline int32_t uhi_read(int32_t fd, char *buf, long num)
+{
+ return __uhi_call3(UHI_NR_read, fd, (long)buf, num);
+}
+
+static inline long uhi_write(int32_t fd, const char *buf, long num)
+{
+ return __uhi_call3(UHI_NR_write, fd, (long)buf, num);
+}
+
+static inline int32_t uhi_lseek(int32_t fd, long offset, int32_t whence)
+{
+ return __uhi_call3(UHI_NR_lseek, fd, offset, whence);
+}
+
+static inline int32_t uhi_unlink(const char *path)
+{
+ return __uhi_call1(UHI_NR_unlink, (long)path);
+}
+
+static inline int32_t uhi_fstat(int32_t fd, struct uhi_stat *buf)
+{
+ return __uhi_call2(UHI_NR_fstat, fd, (long)buf);
+}
+
+static inline int32_t uhi_argc(void)
+{
+ return __uhi_call0(UHI_NR_argc);
+}
+
+static inline int32_t uhi_argnlen(int32_t n)
+{
+ return __uhi_call1(UHI_NR_argnlen, n);
+}
+
+static inline int32_t uhi_argn(int32_t n, char *buf, int32_t num)
+{
+ return __uhi_call3(UHI_NR_argn, n, (long)buf, num);
+}
+
+static inline int32_t uhi_plog(const char *buf, int32_t num)
+{
+ return __uhi_call2(UHI_NR_plog, (long)buf, num);
+}
+
+static inline void uhi_assert(const char *msg, const char *file, int32_t line)
+{
+ __uhi_call3(UHI_NR_assert, (long)msg, (long)file, line);
+}
+
+static inline int32_t uhi_link(const char *oldpath, const char *newpath)
+{
+ return __uhi_call2(UHI_NR_link, (long)oldpath, (long)newpath);
+}
+
+static inline int32_t uhi_pread(int32_t fd, char *buf, long num, long offset)
+{
+ return __uhi_call4(UHI_NR_pread, fd, (long)buf, num, offset);
+}
+
+static inline int32_t uhi_pwrite(int32_t fd, const char *buf, long num, long offset)
+{
+ return __uhi_call4(UHI_NR_pwrite, fd, (long)buf, num, offset);
+}
+
+static inline void uhi_bootfailure(int reason)
+{
+ __uhi_call1(UHI_NR_bootfailure, reason);
+}
+
+#endif
--
2.34.1
UHI is MIPS's implementation of semihosting.
Add an earlycon driver to help with debugging on boot.
This driver is capable for print log using UHI's "Plog" or interact
with KGDB using UHI's stdio function.
Signed-off-by: Jiaxun Yang <[email protected]>
---
drivers/tty/serial/Kconfig | 13 ++++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/earlycon-mips-uhi.c | 85 ++++++++++++++++++++++++++
3 files changed, 99 insertions(+)
create mode 100644 drivers/tty/serial/earlycon-mips-uhi.c
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index bdc568a4ab66..04c62c6b45cd 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -85,6 +85,19 @@ config SERIAL_EARLYCON_SEMIHOST
This is enabled with "earlycon=smh" on the kernel command line.
The console is enabled when early_param is processed.
+config SERIAL_EARLYCON_UHI
+ bool "Early console using MIPS UHI semihosting"
+ depends on MIPS
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Support for early debug console using UHI semihosting.
+ This enables the console before standard serial driver is probed.
+ This is enabled with "earlycon=uhi" or "earlycon=uhi_stdio" on the
+ kernel command line.
+ The console is enabled when early_param is processed.
+
config SERIAL_EARLYCON_RISCV_SBI
bool "Early console using RISC-V SBI"
depends on RISCV_SBI_V01
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index f6b8c220dcfb..ef5e9c87aea1 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -9,6 +9,7 @@ serial_base-y := serial_core.o serial_base_bus.o serial_ctrl.o serial_port.o
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o
obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o
+obj-$(CONFIG_SERIAL_EARLYCON_MIPS_UHI) += earlycon-mips-uhi.o
# These Sparc drivers have to appear before others such as 8250
# which share ttySx minor node space. Otherwise console device
diff --git a/drivers/tty/serial/earlycon-mips-uhi.c b/drivers/tty/serial/earlycon-mips-uhi.c
new file mode 100644
index 000000000000..002bb2c37064
--- /dev/null
+++ b/drivers/tty/serial/earlycon-mips-uhi.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MIPS UHI semihosting based earlycon
+ *
+ * Copyright (C) 2023 Jiaxun Yang <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <asm/uhi.h>
+
+static int stdin_fd = -1;
+static int stdout_fd = -1;
+
+static void uhi_plog_write(struct console *con, const char *s, unsigned int n)
+{
+ uhi_plog(s, 0);
+}
+
+static void uhi_stdout_write(struct console *con, const char *s, unsigned int n)
+{
+ if (stdout_fd < 0)
+ return;
+
+ uhi_write(stdout_fd, s, n);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int uhi_stdin_read(struct console *con, char *s, unsigned int n)
+{
+ if (stdin_fd < 0)
+ return 0;
+
+ return uhi_read(stdin_fd, s, n);
+}
+#endif
+
+static int uhi_stdio_fd_open(struct console *co, char *options)
+{
+ /*
+ * You have to open both stdin and stdout to get console work
+ * properly on some old CodeScape debugger.
+ */
+ stdin_fd = uhi_open("/dev/stdin", UHI_O_RDONLY, 0);
+ stdout_fd = uhi_open("/dev/stdout", UHI_O_WRONLY, 0);
+
+ return (stdin_fd < 0 || stdout_fd < 0) ? -ENODEV : 0;
+}
+
+static int uhi_stdio_fd_close(struct console *co)
+{
+ int ret1 = 0, ret2 = 0;
+
+ if (stdin_fd >= 0)
+ ret1 = uhi_close(stdin_fd);
+ if (stdout_fd >= 0)
+ ret2 = uhi_close(stdout_fd);
+
+ return (ret1 < 0 || ret2 < 0) ? -ENODEV : 0;
+}
+
+static int
+__init early_uhi_setup(struct earlycon_device *device, const char *opt)
+{
+ device->con->write = uhi_plog_write;
+ return 0;
+}
+
+static int
+__init early_uhi_stdio_setup(struct earlycon_device *device, const char *opt)
+{
+
+ device->con->setup = uhi_stdio_fd_open;
+ device->con->exit = uhi_stdio_fd_close;
+ device->con->write = uhi_stdout_write;
+#ifdef CONFIG_CONSOLE_POLL
+ device->con->read = uhi_stdin_read;
+#endif
+ return 0;
+}
+
+EARLYCON_DECLARE(uhi, early_uhi_setup);
+EARLYCON_DECLARE(uhi_stdio, early_uhi_stdio_setup);
--
2.34.1
在2023年10月27日十月 下午7:26,Jiaxun Yang写道:
> UHI is MIPS's implementation of semihosting.
> Add an earlycon driver to help with debugging on boot.
>
> This driver is capable for print log using UHI's "Plog" or interact
> with KGDB using UHI's stdio function.
>
> Signed-off-by: Jiaxun Yang <[email protected]>
> ---
> drivers/tty/serial/Kconfig | 13 ++++
> drivers/tty/serial/Makefile | 1 +
> drivers/tty/serial/earlycon-mips-uhi.c | 85 ++++++++++++++++++++++++++
> 3 files changed, 99 insertions(+)
> create mode 100644 drivers/tty/serial/earlycon-mips-uhi.c
>
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index bdc568a4ab66..04c62c6b45cd 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -85,6 +85,19 @@ config SERIAL_EARLYCON_SEMIHOST
> This is enabled with "earlycon=smh" on the kernel command line.
> The console is enabled when early_param is processed.
>
> +config SERIAL_EARLYCON_UHI
^ Oops there is a symbol name mismatch, will fix in v2.
Sorry for the noise.
Thanks
- Jiaxun
> + bool "Early console using MIPS UHI semihosting"
> + depends on MIPS
> + select SERIAL_CORE
> + select SERIAL_CORE_CONSOLE
> + select SERIAL_EARLYCON
> + help
> + Support for early debug console using UHI semihosting.
> + This enables the console before standard serial driver is probed.
> + This is enabled with "earlycon=uhi" or "earlycon=uhi_stdio" on the
> + kernel command line.
> + The console is enabled when early_param is processed.
> +
> config SERIAL_EARLYCON_RISCV_SBI
> bool "Early console using RISC-V SBI"
> depends on RISCV_SBI_V01
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index f6b8c220dcfb..ef5e9c87aea1 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -9,6 +9,7 @@ serial_base-y := serial_core.o serial_base_bus.o
> serial_ctrl.o serial_port.o
> obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
> obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o
> obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o
> +obj-$(CONFIG_SERIAL_EARLYCON_MIPS_UHI) += earlycon-mips-uhi.o
>
> # These Sparc drivers have to appear before others such as 8250
> # which share ttySx minor node space. Otherwise console device
> diff --git a/drivers/tty/serial/earlycon-mips-uhi.c
> b/drivers/tty/serial/earlycon-mips-uhi.c
> new file mode 100644
> index 000000000000..002bb2c37064
> --- /dev/null
> +++ b/drivers/tty/serial/earlycon-mips-uhi.c
> @@ -0,0 +1,85 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MIPS UHI semihosting based earlycon
> + *
> + * Copyright (C) 2023 Jiaxun Yang <[email protected]>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/console.h>
> +#include <linux/init.h>
> +#include <linux/serial_core.h>
> +#include <asm/uhi.h>
> +
> +static int stdin_fd = -1;
> +static int stdout_fd = -1;
> +
> +static void uhi_plog_write(struct console *con, const char *s,
> unsigned int n)
> +{
> + uhi_plog(s, 0);
> +}
> +
> +static void uhi_stdout_write(struct console *con, const char *s,
> unsigned int n)
> +{
> + if (stdout_fd < 0)
> + return;
> +
> + uhi_write(stdout_fd, s, n);
> +}
> +
> +#ifdef CONFIG_CONSOLE_POLL
> +static int uhi_stdin_read(struct console *con, char *s, unsigned int n)
> +{
> + if (stdin_fd < 0)
> + return 0;
> +
> + return uhi_read(stdin_fd, s, n);
> +}
> +#endif
> +
> +static int uhi_stdio_fd_open(struct console *co, char *options)
> +{
> + /*
> + * You have to open both stdin and stdout to get console work
> + * properly on some old CodeScape debugger.
> + */
> + stdin_fd = uhi_open("/dev/stdin", UHI_O_RDONLY, 0);
> + stdout_fd = uhi_open("/dev/stdout", UHI_O_WRONLY, 0);
> +
> + return (stdin_fd < 0 || stdout_fd < 0) ? -ENODEV : 0;
> +}
> +
> +static int uhi_stdio_fd_close(struct console *co)
> +{
> + int ret1 = 0, ret2 = 0;
> +
> + if (stdin_fd >= 0)
> + ret1 = uhi_close(stdin_fd);
> + if (stdout_fd >= 0)
> + ret2 = uhi_close(stdout_fd);
> +
> + return (ret1 < 0 || ret2 < 0) ? -ENODEV : 0;
> +}
> +
> +static int
> +__init early_uhi_setup(struct earlycon_device *device, const char *opt)
> +{
> + device->con->write = uhi_plog_write;
> + return 0;
> +}
> +
> +static int
> +__init early_uhi_stdio_setup(struct earlycon_device *device, const
> char *opt)
> +{
> +
> + device->con->setup = uhi_stdio_fd_open;
> + device->con->exit = uhi_stdio_fd_close;
> + device->con->write = uhi_stdout_write;
> +#ifdef CONFIG_CONSOLE_POLL
> + device->con->read = uhi_stdin_read;
> +#endif
> + return 0;
> +}
> +
> +EARLYCON_DECLARE(uhi, early_uhi_setup);
> +EARLYCON_DECLARE(uhi_stdio, early_uhi_stdio_setup);
> --
> 2.34.1
--
- Jiaxun
Hi Jiaxun,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tty/tty-testing]
[also build test WARNING on tty/tty-next tty/tty-linus linus/master v6.6 next-20231030]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Jiaxun-Yang/MIPS-zboot-Add-UHI-semihosting-debug-print-support/20231028-032719
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link: https://lore.kernel.org/r/20231027182650.281405-7-jiaxun.yang%40flygoat.com
patch subject: [PATCH 2/3] MIPS: zboot: Add UHI semihosting debug print support
config: mips-allmodconfig (https://download.01.org/0day-ci/archive/20231101/[email protected]/config)
compiler: mips-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231101/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
Note: functions only called from assembly code should be annotated with the asmlinkage attribute
All warnings (new ones prefixed by >>):
>> arch/mips/boot/compressed/dbg-uhi.c:8:6: warning: no previous prototype for 'puts' [-Wmissing-prototypes]
8 | void puts(const char *s)
| ^~~~
vim +/puts +8 arch/mips/boot/compressed/dbg-uhi.c
7
> 8 void puts(const char *s)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki