[riscv] Add support for the SBI debug console
Add the ability to issue Supervisor Binary Interface (SBI) calls via
the ECALL instruction, and use the SBI DBCN extension to implement a
debug console.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/arch/riscv/Makefile b/src/arch/riscv/Makefile
index 668d7db..d3ae4e8 100644
--- a/src/arch/riscv/Makefile
+++ b/src/arch/riscv/Makefile
@@ -10,6 +10,7 @@
# RISCV-specific directories containing source files
#
SRCDIRS += arch/riscv/core
+SRCDIRS += arch/riscv/interface/sbi
# RISCV-specific flags
#
diff --git a/src/arch/riscv/include/ipxe/sbi.h b/src/arch/riscv/include/ipxe/sbi.h
new file mode 100644
index 0000000..529f1d0
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/sbi.h
@@ -0,0 +1,157 @@
+#ifndef _IPXE_SBI_H
+#define _IPXE_SBI_H
+
+/** @file
+ *
+ * Supervisor Binary Interface (SBI)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/** An SBI function return value */
+struct sbi_return {
+ /** Error status (returned in a0) */
+ long error;
+ /** Data value (returned in a1) */
+ long value;
+};
+
+/**
+ * @defgroup sbierrors SBI errors
+ *
+ * *{
+ */
+#define SBI_SUCCESS 0 /**< Completed successfully */
+#define SBI_ERR_FAILED -1 /**< Failed */
+#define SBI_ERR_NOT_SUPPORTED -2 /**< Not supported */
+#define SBI_ERR_INVALID_PARAM -3 /**< Invalid parameter(s) */
+#define SBI_ERR_DENIED -4 /**< Denied or not allowed */
+#define SBI_ERR_INVALID_ADDRESS -5 /**< Invalid address(es) */
+#define SBI_ERR_ALREADY_AVAILABLE -6 /**< Already available */
+#define SBI_ERR_ALREADY_STARTED -7 /**< Already started */
+#define SBI_ERR_ALREADY_STOPPED -8 /**< Already stopped */
+#define SBI_ERR_NO_SHMEM -9 /**< Shared memory not available */
+#define SBI_ERR_INVALID_STATE -10 /**< Invalid state */
+#define SBI_ERR_BAD_RANGE -11 /**< Bad (or invalid) range */
+#define SBI_ERR_TIMEOUT -12 /**< Failed due to timeout */
+#define SBI_ERR_IO -13 /**< Input/output error */
+/** @} */
+
+/** Construct SBI extension ID */
+#define SBI_EID( c1, c2, c3, c4 ) \
+ ( (int) ( ( (c1) << 24 ) | ( (c2) << 16 ) | ( (c3) << 8 ) | (c4) ) )
+
+/**
+ * Call supervisor with no parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_0 ( int eid, int fid ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" );
+ register unsigned long a1 asm ( "a1" );
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "=r" ( a0 ), "=r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with one parameter
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v param0 Parameter 0
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_1 ( int eid, int fid, unsigned long p0 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" );
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "=r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with two parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v param0 Parameter 0
+ * @v param1 Parameter 1
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_2 ( int eid, int fid, unsigned long p0, unsigned long p1 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" ) = p1;
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "+r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with three parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v param0 Parameter 0
+ * @v param1 Parameter 1
+ * @v param2 Parameter 2
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_3 ( int eid, int fid, unsigned long p0, unsigned long p1,
+ unsigned long p2 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" ) = p1;
+ register unsigned long a2 asm ( "a2" ) = p2;
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "+r" ( a1 )
+ : "r" ( a2 ), "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/** Debug console extension */
+#define SBI_DBCN SBI_EID ( 'D', 'B', 'C', 'N' )
+#define SBI_DBCN_WRITE 0x00 /**< Console Write */
+#define SBI_DBCN_READ 0x01 /**< Console Read */
+#define SBI_DBCN_WRITE_BYTE 0x02 /**< Console Write Byte */
+
+#endif /* _IPXE_SBI_H */
diff --git a/src/arch/riscv/interface/sbi/sbi_console.c b/src/arch/riscv/interface/sbi/sbi_console.c
new file mode 100644
index 0000000..8352555
--- /dev/null
+++ b/src/arch/riscv/interface/sbi/sbi_console.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+/** @file
+ *
+ * SBI debug console
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/sbi.h>
+#include <ipxe/io.h>
+#include <ipxe/console.h>
+#include <config/console.h>
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_SBI ) && CONSOLE_EXPLICIT ( CONSOLE_SBI ) )
+#undef CONSOLE_SBI
+#define CONSOLE_SBI ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
+#endif
+
+/** Buffered input character (if any) */
+static unsigned char sbi_console_input;
+
+/**
+ * Print a character to SBI console
+ *
+ * @v character Character to be printed
+ */
+static void sbi_putchar ( int character ) {
+
+ /* Write byte to console */
+ sbi_ecall_1 ( SBI_DBCN, SBI_DBCN_WRITE_BYTE, character );
+}
+
+/**
+ * Get character from SBI console
+ *
+ * @ret character Character read from console, if any
+ */
+static int sbi_getchar ( void ) {
+ int character;
+
+ /* Consume and return buffered character, if any */
+ character = sbi_console_input;
+ sbi_console_input = 0;
+ return character;
+}
+
+/**
+ * Check for character ready to read from SBI console
+ *
+ * @ret True Character available to read
+ * @ret False No character available to read
+ */
+static int sbi_iskey ( void ) {
+ struct sbi_return ret;
+
+ /* Do nothing if we already have a buffered character */
+ if ( sbi_console_input )
+ return sbi_console_input;
+
+ /* Read and buffer byte from console, if any */
+ ret = sbi_ecall_3 ( SBI_DBCN, SBI_DBCN_READ,
+ sizeof ( sbi_console_input ),
+ virt_to_phys ( &sbi_console_input ), 0 );
+ if ( ret.error )
+ return 0;
+
+ /* Return number of characters read and buffered */
+ return ret.value;
+}
+
+/** SBI console */
+struct console_driver sbi_console_driver __console_driver = {
+ .putchar = sbi_putchar,
+ .getchar = sbi_getchar,
+ .iskey = sbi_iskey,
+ .usage = CONSOLE_SBI,
+};
diff --git a/src/config/config.c b/src/config/config.c
index 0f950eb..4cfa5dd 100644
--- a/src/config/config.c
+++ b/src/config/config.c
@@ -78,6 +78,9 @@
#ifdef CONSOLE_DEBUGCON
REQUIRE_OBJECT ( debugcon );
#endif
+#ifdef CONSOLE_SBI
+REQUIRE_OBJECT ( sbi_console );
+#endif
/*
* Drag in all requested network protocols
diff --git a/src/config/console.h b/src/config/console.h
index 9f770d0..0ff328b 100644
--- a/src/config/console.h
+++ b/src/config/console.h
@@ -41,6 +41,7 @@
//#define CONSOLE_VMWARE /* VMware logfile console */
//#define CONSOLE_DEBUGCON /* Bochs/QEMU/KVM debug port console */
//#define CONSOLE_INT13 /* INT13 disk log console */
+//#define CONSOLE_SBI /* RISC-V SBI debug console */
/*
* Very obscure console types