[PATCH v2 3/3] [sim/riscv] Add semi-hosting support

jaydeep.patil
2023-10-30T13:01:34Z

From: Jaydeep Patil <jaydeep.patil@imgtec.com>

Added support for all semi-hosting calls.
Enable prints in gdb.arch/riscv-insn-simulation.c.
---
.../gdb.arch/riscv-insn-simulation.c | 2 -
.../gdb.arch/riscv-insn-simulation.exp | 3 +-
sim/riscv/riscv-sim.h | 21 +
sim/riscv/sim-main.c | 494 ++++++++++++++++++
4 files changed, 517 insertions(+), 3 deletions(-)

diff --git a/gdb/testsuite/gdb.arch/riscv-insn-simulation.c b/gdb/testsuite/gdb.arch/riscv-insn-simulation.c
index db2b8b4f3d8..12334bdd89e 100755
--- a/gdb/testsuite/gdb.arch/riscv-insn-simulation.c
+++ b/gdb/testsuite/gdb.arch/riscv-insn-simulation.c
@@ -51,8 +51,6 @@
#define SKIP_c_fsdsp
#endif

-#define DISABLE_PRINTS
-
#if defined (DISABLE_PRINTS)
#define print(...) ;
#else
diff --git a/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp b/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
index f94a31cc236..9ffa526b318 100755
--- a/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
+++ b/gdb/testsuite/gdb.arch/riscv-insn-simulation.exp
@@ -28,4 +28,5 @@ if { ![runto_main] } {
return -1
}

-gdb_continue_to_end ".*All.*tests pass.*Inferior.*process.*exited normally.*"
+gdb_continue_to_end "continue parent to end" "continue" 1
+
diff --git a/sim/riscv/riscv-sim.h b/sim/riscv/riscv-sim.h
index 6d9a719eadf..8e80a4f7374 100644
--- a/sim/riscv/riscv-sim.h
+++ b/sim/riscv/riscv-sim.h
@@ -78,8 +78,29 @@ extern void initialize_env (SIM_DESC, const char * const *argv,
#define APPLICATION_EXIT 0x20026

#define SYS_OPEN 0x01
+#define SYS_CLOSE 0x02
+#define SYS_WRITEC 0x03
+#define SYS_WRITE0 0x04
+#define SYS_WRITE 0x05
+#define SYS_READ 0x06
+#define SYS_READC 0x07
+#define SYS_ISERROR 0x08
+#define SYS_ISTTY 0x09
+#define SYS_SEEK 0x0A
+#define SYS_FLEN 0x0C
+#define SYS_TMPNAM 0x0D
+#define SYS_REMOVE 0x0E
+#define SYS_RENAME 0x0F
+#define SYS_CLOCK 0x10
+#define SYS_TIME 0x11
+#define SYS_SYSTEM 0x12
+#define SYS_ERRNO 0x13
#define SYS_GET_CMDLINE 0x15
+#define SYS_HEAPINFO 0x16
#define SYS_EXIT 0x18
+#define SYS_EXIT_EXTENDED 0x20
+#define SYS_ELAPSED 0x30
+#define SYS_TICKFREQ 0x31

#define GDB_O_RDONLY 0x000
#define GDB_O_WRONLY 0x001
diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
index da9a1aa4cdf..8362d85522a 100644
--- a/sim/riscv/sim-main.c
+++ b/sim/riscv/sim-main.c
@@ -26,7 +26,11 @@

#include <inttypes.h>
#include <time.h>
+#include <sys/stat.h>
+#include <sys/unistd.h>
+#include <sys/fcntl.h>

+#include "bfd.h"
#include "sim-main.h"
#include "sim-signal.h"
#include "sim-syscall.h"
@@ -66,6 +70,8 @@ static const struct riscv_opcode *riscv_hash[OP_MASK_OP + 1];
} \
} while (0)

+static clock_t clock_start = 0;
+
static INLINE void
store_rd (SIM_CPU *cpu, int rd, unsigned_word val)
{
@@ -187,6 +193,430 @@ get_core_string_with_len (SIM_CPU *cpu, unsigned_word addr,
return str;
}

+/* Write VALUE at specified address. ADDR and (INDEX * XLEN)
+ form the base address. */
+static void
+set_core_data (SIM_CPU *cpu, unsigned_word addr, unsigned_word index,
+ uintptr_t value)
+{
+ int xlen = RISCV_XLEN (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ if (xlen == 64)
+ sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, read_map,
+ addr + (index * 8), value);
+ else
+ sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, read_map,
+ addr + (index * 4), (uint32_t) value);
+}
+
+/* Read data of length SLEN and address ADDR. */
+static char *
+get_core_string (SIM_CPU *cpu, unsigned_word addr, int *slen)
+{
+ int len = 0;
+ char * str;
+ const int chunk_size = 128; /* allocate buffer in chunks. */
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ str = (char *) malloc (chunk_size);
+
+ while (1)
+ {
+ uint8_t ch = sim_core_read_unaligned_1 (cpu, riscv_cpu->pc, read_map,
+ addr + len);
+ str[len] = ch;
+ if (ch == 0)
+ break;
+ len++;
+ if ((len % chunk_size) == 0)
+ str = (char *) realloc (str, len + chunk_size);
+ }
+
+ *slen = len;
+ return str;
+}
+
+/* Find address of the symbol in SYMNAME. */
+static uintptr_t
+get_symbol_value (SIM_CPU *cpu, const char *symname)
+{
+ struct bfd *abfd = STATE_PROG_BFD (CPU_STATE (cpu));
+ static asymbol **symbol_table = NULL;
+ static long number_of_symbols = 0;
+
+ if (symbol_table == NULL)
+ {
+ long storage_needed;
+ storage_needed = bfd_get_symtab_upper_bound (abfd);
+ if (storage_needed <= 0)
+ return 0;
+ symbol_table = (asymbol **) malloc (storage_needed);
+ if (symbol_table == NULL)
+ return 0;
+ number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);
+ if (number_of_symbols <= 0)
+ return 0;
+ }
+
+ for (long i = 0; i < number_of_symbols; i++)
+ {
+ asymbol *sym = symbol_table[i];
+ if (!strcmp (sym->name, symname))
+ return bfd_asymbol_value (sym);
+ }
+
+ return 0;
+}
+
+/* SYS_FLEN
+ Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ The file size in bytes is returned through register a0. */
+static void
+semihosting_flen (SIM_CPU *cpu)
+{
+ int fd;
+ struct stat sb;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+
+ if (fd > STDERR_FILENO)
+ {
+ fstat (fd, &sb);
+ riscv_cpu->a0 = sb.st_size;
+ }
+ else
+ riscv_cpu->a0 = 0;
+}
+
+/* SYS_WRITE
+ Write data to a file. Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ Index 1: Pointer: Pointer to buffer.
+ Index 2: Integer: Number of bytes to write.
+ Number of bytes written is returned through register a0. */
+static void
+semihosting_write (SIM_CPU *cpu)
+{
+ int i, fd;
+ uintptr_t buf;
+ uintptr_t count;
+ char *str;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ buf = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
+ count = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 2);
+
+ if (count <= 0)
+ {
+ riscv_cpu->a0 = -1;
+ return;
+ }
+
+ str = get_core_string_with_len (cpu, buf, count);
+ riscv_cpu->a0 = sim_io_write (CPU_STATE (cpu), fd, str, count);
+ free (str);
+}
+
+/* SYS_WRITEC
+ Register a1 points to a buffer containing:
+ Index 0: Integer: Character to write to console.
+ Number of bytes written is returned through register a0. */
+static void
+semihosting_writec (SIM_CPU *cpu)
+{
+ char ch;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ ch = (char) get_core_data (cpu, riscv_cpu->a1, 0);
+ riscv_cpu->a0 = sim_io_write_stdout (CPU_STATE (cpu), &ch, 1);
+}
+
+/* SYS_WRITE0
+ Register a1 points to a buffer containing:
+ Index 0: Integer: Null terminated string.
+ Number of bytes written is returned through register a0. */
+static void
+semihosting_write0 (SIM_CPU *cpu)
+{
+ int len;
+ char *str;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ str = get_core_string (cpu, riscv_cpu->a1, &len);
+ riscv_cpu->a0 = sim_io_write_stdout (CPU_STATE (cpu), str, len);
+}
+
+/* SYS_READ
+ Read data from a file. Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ Index 1: Pointer: Pointer to destination buffer.
+ Index 2: Integer: Number of bytes to read.
+ Number of bytes read is returned through register a0. */
+static void
+semihosting_read (SIM_CPU *cpu)
+{
+ int i, fd, read_len;
+ uintptr_t dst_buf;
+ uintptr_t count;
+ char *host_buf;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ dst_buf = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
+ count = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 2);
+
+ if (count <= 0)
+ {
+ riscv_cpu->a0 = 0;
+ return;
+ }
+
+ host_buf = (char *) malloc (count);
+ read_len = sim_io_read (CPU_STATE (cpu), fd, host_buf, count);
+ if (read_len > 0)
+ set_core_string (cpu, dst_buf, host_buf, read_len);
+ riscv_cpu->a0 = read_len;
+ free (host_buf);
+}
+
+/* SYS_READC
+ Read a char from console and return it through register a0. */
+static void
+semihosting_readc (SIM_CPU *cpu)
+{
+ char ch;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ sim_io_read_stdin (CPU_STATE (cpu), &ch, 1);
+ riscv_cpu->a0 = ch;
+}
+
+/* SYS_CLOSE
+ Close a file. Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ Return status in register a0. */
+static void
+semihosting_close (SIM_CPU *cpu)
+{
+ int fd;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ riscv_cpu->a0 = sim_io_close (CPU_STATE (cpu), fd);
+}
+
+/* SYS_SEEK
+ Seek in a file. Register a1 points to a buffer containing:
+ Index 0: Integer: File handle.
+ Index 1: Integer: Number of bytes to seek in the file.
+ Return status in register a0. */
+static void
+semihosting_seek (SIM_CPU *cpu)
+{
+ int fd;
+ uintptr_t pos;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ pos = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
+ riscv_cpu->a0 = sim_io_lseek (CPU_STATE (cpu), fd, pos, 0);
+}
+
+/* SYS_EXIT_EXTENDED
+ Register a1 points to a buffer containing:
+ Index 0: Integer: Application code.
+ Index 1: Integer: Exit code. */
+static void
+semihosting_exit_extended (SIM_CPU *cpu)
+{
+ int ret;
+ uintptr_t app_code, exit_status;
+ SIM_DESC sd = CPU_STATE (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ app_code = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
+ exit_status = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
+ if (app_code == APPLICATION_EXIT)
+ ret = exit_status;
+ else
+ ret = 1;
+ riscv_cpu->a0 = ret;
+ sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_exited, ret);
+}
+
+/* SYS_ISERROR
+ Register a1 points to a buffer containing:
+ Index 0: Integer: Status code. */
+static void
+semihosting_iserror (SIM_CPU *cpu)
+{
+ intptr_t status;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ status = (intptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
+ riscv_cpu->a0 = (status < 0);
+}
+
+/* SYS_ISTTY
+ Register a1 points to a buffer containing:
+ Index 0: Integer: File handle. */
+static void
+semihosting_istty (SIM_CPU *cpu)
+{
+ int fd;
+ SIM_DESC sd = CPU_STATE (cpu);
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ fd = (int) get_core_data (cpu, riscv_cpu->a1, 0);
+ riscv_cpu->a0 = sim_io_isatty (sd, fd);
+}
+
+/* SYS_TMPNAM
+ Register a1 points to a buffer containing:
+ Index 0: Pointer: Destination buffer address.
+ Index 1: Integer: ID (ignored).
+ Index 2: Integer: Maximum length of the buffer.
+ Pointer to temporary name is returned through a0. */
+static void
+semihosting_tmpnam (SIM_CPU *cpu)
+{
+ uintptr_t t_pname;
+ int len, id, maxpath;
+ char *pname;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ t_pname = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
+ id = (int) get_core_data (cpu, riscv_cpu->a1, 1);
+ maxpath = (int) get_core_data (cpu, riscv_cpu->a1, 2);
+
+ pname = tmpnam (NULL);
+
+ if (pname == NULL)
+ {
+ riscv_cpu->a0 = 0;
+ return;
+ }
+
+ len = strlen (pname);
+ if (maxpath > len)
+ {
+ riscv_cpu->a0 = 0;
+ return;
+ }
+
+ set_core_string (cpu, t_pname, pname, len + 1);
+ riscv_cpu->a0 = t_pname;
+}
+
+/* SYS_REMOVE
+ Register a1 points to a buffer containing:
+ Index 0: Pointer: Name of the file to remove.
+ Index 1: Integer: Length of the file name. */
+static void
+semihosting_remove (SIM_CPU *cpu)
+{
+ uintptr_t t_pname;
+ int len;
+ char *pname;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ t_pname = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
+ len = (int) get_core_data (cpu, riscv_cpu->a1, 1);
+ pname = get_core_string_with_len (cpu, t_pname, len);
+ riscv_cpu->a0 = sim_io_unlink (CPU_STATE (cpu), pname);
+ free (pname);
+}
+
+/* SYS_RENAME
+ Register a1 points to a buffer containing:
+ Index 0: Pointer: Old file path.
+ Index 1: Integer: Length of the old file name.
+ Index 2: Pointer: New file name.
+ Index 3: Integer: Length of the new file name. */
+static void
+semihosting_rename (SIM_CPU *cpu)
+{
+ uintptr_t old_name_addr, new_name_addr;
+ uintptr_t old_len, new_len;
+ char *old_host_name, *new_host_name;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ old_name_addr = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
+ old_len = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
+ new_name_addr = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 2);
+ new_len = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 3);
+ old_host_name = get_core_string_with_len (cpu, old_name_addr, old_len);
+ new_host_name = get_core_string_with_len (cpu, new_name_addr, new_len);
+ riscv_cpu->a0 = sim_io_rename (CPU_STATE (cpu), old_host_name,
+ new_host_name);
+ free (old_host_name);
+ free (new_host_name);
+}
+
+/* SYS_SYSTEM
+ Register a1 points to a buffer containing:
+ Index 0: Pointer: Command string.
+ Index 1: Integer: Length of the command string.
+ Status of the command is returned through a0. */
+static void
+semihosting_system (SIM_CPU *cpu)
+{
+ uintptr_t cmd_addr;
+ uintptr_t cmd_len;
+ char *cmd_host;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ cmd_addr = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 0);
+ cmd_len = (uintptr_t) get_core_data (cpu, riscv_cpu->a1, 1);
+ cmd_host = get_core_string_with_len (cpu, cmd_addr, cmd_len);
+ riscv_cpu->a0 = sim_io_system (CPU_STATE (cpu), cmd_host);
+ free (cmd_host);
+}
+
+/* SYS_ELAPSED
+ Return the elapsed seconds in a buffer pointed by register a1. */
+static void
+semihosting_elapsed (SIM_CPU *cpu)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ clock_t elapsed = clock () - clock_start;
+ if (RISCV_XLEN (cpu) == 32)
+ {
+ sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
+ riscv_cpu->a1, (uint32_t) elapsed);
+ sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
+ riscv_cpu->a1 + 4, (uint32_t) (elapsed >> 32));
+ }
+ else
+ sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map, riscv_cpu->a1,
+ elapsed);
+}
+
+/* SYS_HEAPINFO
+ Return the heap start, heap end, stack start and stack end
+ in a buffer pointed by register a1. */
+static void
+semihosting_heapinfo (SIM_CPU *cpu)
+{
+ static uintptr_t heap_base = 0, heap_limit = 0,
+ stack_base = 0, stack_limit = 0, stack_size = 0;
+ static bool have_heap = false, have_stack = false;
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+
+ if (have_heap == false)
+ {
+ heap_base = get_symbol_value (cpu, "__heap_start");
+ heap_limit = get_symbol_value (cpu, "__heap_end");
+ have_heap = true;
+ }
+
+ if (have_stack == false)
+ {
+ stack_base = get_symbol_value (cpu, "__stack");
+ stack_size = get_symbol_value (cpu, "__stack_size");
+ stack_limit = stack_base + stack_size;
+ have_stack = true;
+ }
+
+ set_core_data (cpu, riscv_cpu->a1, 0, heap_base);
+ set_core_data (cpu, riscv_cpu->a1, 1, heap_limit);
+ set_core_data (cpu, riscv_cpu->a1, 2, stack_base);
+ set_core_data (cpu, riscv_cpu->a1, 3, stack_limit);
+}
+
/* SYS_OPEN
Register a1 points to a buffer containing:
Index 0: Pointer: Address of the file name string.
@@ -299,6 +729,69 @@ do_semihosting (SIM_CPU *cpu)
case SYS_EXIT:
semihosting_exit (cpu);
break;
+ case SYS_CLOSE:
+ semihosting_close (cpu);
+ break;
+ case SYS_WRITEC:
+ semihosting_writec (cpu);
+ break;
+ case SYS_WRITE0:
+ semihosting_write0 (cpu);
+ break;
+ case SYS_WRITE:
+ semihosting_write (cpu);
+ break;
+ case SYS_READ:
+ semihosting_read (cpu);
+ break;
+ case SYS_READC:
+ semihosting_readc (cpu);
+ break;
+ case SYS_ISERROR:
+ semihosting_iserror (cpu);
+ break;
+ case SYS_ISTTY:
+ semihosting_istty (cpu);
+ break;
+ case SYS_SEEK:
+ semihosting_seek (cpu);
+ break;
+ case SYS_FLEN:
+ semihosting_flen (cpu);
+ break;
+ case SYS_TMPNAM:
+ semihosting_tmpnam (cpu);
+ break;
+ case SYS_REMOVE:
+ semihosting_remove (cpu);
+ break;
+ case SYS_RENAME:
+ semihosting_rename (cpu);
+ break;
+ case SYS_CLOCK:
+ riscv_cpu->a0 = (clock () / (CLOCKS_PER_SEC / 100));
+ break;
+ case SYS_TIME:
+ riscv_cpu->a0 = sim_io_time (CPU_STATE (cpu));
+ break;
+ case SYS_SYSTEM:
+ semihosting_system (cpu);
+ break;
+ case SYS_ERRNO:
+ riscv_cpu->a0 = sim_io_get_errno (CPU_STATE (cpu));
+ break;
+ case SYS_HEAPINFO:
+ semihosting_heapinfo (cpu);
+ break;
+ case SYS_EXIT_EXTENDED:
+ semihosting_exit_extended (cpu);
+ break;
+ case SYS_ELAPSED:
+ semihosting_elapsed (cpu);
+ break;
+ case SYS_TICKFREQ:
+ riscv_cpu->a0 = 1000000000;
+ break;
default:
return -1; /* Semi-hosting call not supported. */
}
@@ -1727,6 +2220,7 @@ initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid)

riscv_cpu->csr.mimpid = 0x8000;
riscv_cpu->csr.mhartid = mhartid;
+ clock_start = clock ();
}
\f
/* Some utils don't like having a NULL environ. */
--
2.25.1