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

Message ID 20231030130042.1472535-4-jaydeep.patil@imgtec.com
State New
Headers
Series sim: riscv: Compressed instruction simulation and semi-hosting support |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed

Commit Message

Jaydeep Patil Oct. 30, 2023, 1 p.m. UTC
  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(-)
  

Patch

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 ();
 }
 
 /* Some utils don't like having a NULL environ.  */