public inbox for newlib@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] RISC-V: Add semihosting support
@ 2020-11-30 11:36 Craig Blackmore
  2020-11-30 14:05 ` Kito Cheng
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Craig Blackmore @ 2020-11-30 11:36 UTC (permalink / raw)
  To: newlib

Add a new library called libsemihost which implements syscalls using
semihosting syscalls. This is intended to be used instead of libgloss
when semihosting support is required. A new semihost.specs file has
been added which will link with -lsemihost instead -lgloss.

The library is implemented following the RISC-V Semihosting
specification available at:

  https://github.com/riscv/riscv-semihosting-spec

Tested on the gcc testsuite on rv32imc and rv64imc using spike and
openocd.

---
 libgloss/riscv/Makefile.in                | 66 +++++++++++++++++-
 libgloss/riscv/machine/syscall.h          | 25 +++++++
 libgloss/riscv/semihost-sys_close.c       | 24 +++++++
 libgloss/riscv/semihost-sys_exit.c        | 19 ++++++
 libgloss/riscv/semihost-sys_fdtable.c     | 81 +++++++++++++++++++++++
 libgloss/riscv/semihost-sys_fstat.c       | 15 +++++
 libgloss/riscv/semihost-sys_ftime.c       | 12 ++++
 libgloss/riscv/semihost-sys_isatty.c      | 17 +++++
 libgloss/riscv/semihost-sys_link.c        |  5 ++
 libgloss/riscv/semihost-sys_lseek.c       | 66 ++++++++++++++++++
 libgloss/riscv/semihost-sys_open.c        | 58 ++++++++++++++++
 libgloss/riscv/semihost-sys_read.c        | 28 ++++++++
 libgloss/riscv/semihost-sys_sbrk.c        | 22 ++++++
 libgloss/riscv/semihost-sys_stat.c        | 32 +++++++++
 libgloss/riscv/semihost-sys_stat_common.c | 32 +++++++++
 libgloss/riscv/semihost-sys_unlink.c      | 11 +++
 libgloss/riscv/semihost-sys_write.c       | 28 ++++++++
 libgloss/riscv/semihost.specs             | 10 +++
 libgloss/riscv/semihost_fdtable.h         | 17 +++++
 libgloss/riscv/semihost_stat.h            | 10 +++
 libgloss/riscv/semihost_syscall.h         | 43 ++++++++++++
 21 files changed, 620 insertions(+), 1 deletion(-)
 create mode 100644 libgloss/riscv/semihost-sys_close.c
 create mode 100644 libgloss/riscv/semihost-sys_exit.c
 create mode 100644 libgloss/riscv/semihost-sys_fdtable.c
 create mode 100644 libgloss/riscv/semihost-sys_fstat.c
 create mode 100644 libgloss/riscv/semihost-sys_ftime.c
 create mode 100644 libgloss/riscv/semihost-sys_isatty.c
 create mode 100644 libgloss/riscv/semihost-sys_link.c
 create mode 100644 libgloss/riscv/semihost-sys_lseek.c
 create mode 100644 libgloss/riscv/semihost-sys_open.c
 create mode 100644 libgloss/riscv/semihost-sys_read.c
 create mode 100644 libgloss/riscv/semihost-sys_sbrk.c
 create mode 100644 libgloss/riscv/semihost-sys_stat.c
 create mode 100644 libgloss/riscv/semihost-sys_stat_common.c
 create mode 100644 libgloss/riscv/semihost-sys_unlink.c
 create mode 100644 libgloss/riscv/semihost-sys_write.c
 create mode 100644 libgloss/riscv/semihost.specs
 create mode 100644 libgloss/riscv/semihost_fdtable.h
 create mode 100644 libgloss/riscv/semihost_stat.h
 create mode 100644 libgloss/riscv/semihost_syscall.h

diff --git a/libgloss/riscv/Makefile.in b/libgloss/riscv/Makefile.in
index 579dd9554..185b6e6d2 100644
--- a/libgloss/riscv/Makefile.in
+++ b/libgloss/riscv/Makefile.in
@@ -40,8 +40,38 @@ gloss_srcs = \
 	sys_wait.c \
 	sys_write.c 
 
+# libsemihost reuses some of the libgloss minimal implementations
+
+semihost_srcs = \
+	nanosleep.c \
+	sys_chdir.c \
+	sys_chmod.c \
+	sys_chown.c \
+	sys_execve.c \
+	sys_fork.c \
+	sys_getcwd.c \
+	sys_getpid.c \
+	sys_kill.c \
+	sys_utime.c \
+	sys_wait.c \
+	semihost-sys_close.c \
+	semihost-sys_exit.c \
+	semihost-sys_fdtable.c \
+	semihost-sys_fstat.c \
+	semihost-sys_ftime.c \
+	semihost-sys_isatty.c \
+	semihost-sys_link.c \
+	semihost-sys_lseek.c \
+	semihost-sys_open.c \
+	semihost-sys_read.c \
+	semihost-sys_sbrk.c \
+	semihost-sys_stat.c \
+	semihost-sys_stat_common.c \
+	semihost-sys_unlink.c \
+	semihost-sys_write.c
+
 gloss_specs = \
-	nano.specs sim.specs
+	nano.specs sim.specs semihost.specs
 
 # Extra files
 
@@ -134,6 +164,17 @@ sim_objs += $(sim_c_objs)
 deps += $(sim_c_deps)
 junk += $(sim_c_deps) $(sim_c_objs)
 
+semihost_c_srcs = $(filter %.c, $(semihost_srcs))
+semihost_c_objs = $(patsubst %.c, semihost-%.o, $(notdir $(semihost_c_srcs)))
+semihost_c_deps = $(patsubst %.c, semihost-%.d, $(notdir $(semihost_c_srcs)))
+
+$(semihost_c_objs): semihost-%.o : %.c
+	$(COMPILE) -c -o $@ $<
+
+semihost_objs += $(semihost_c_objs)
+deps += $(semihost_c_deps)
+junk += $(semihost_c_deps) $(semihost_c_objs)
+
 #-------------------------------------------------------------------------
 # Build Object Files from Assembly Source
 #-------------------------------------------------------------------------
@@ -159,6 +200,16 @@ sim_objs += $(sim_asm_objs)
 deps += $(sim_asm_deps)
 junk += $(sim_asm_deps) $(sim_asm_objs)
 
+semihost_asm_objs = $(patsubst %.S, semihost-%.o, $(notdir $(gloss_asm_srcs)))
+semihost_asm_deps = $(patsubst %.S, semihost-%.d, $(notdir $(gloss_asm_srcs)))
+
+$(semihost_asm_objs) : semihost-%.o : %.S
+	$(COMPILE) -c -DUSING_SEMIHOST_SPECS -o $@ $<
+
+semihost_objs += $(semihost_asm_objs)
+deps += $(semihost_asm_deps)
+junk += $(semihost_asm_deps) $(semihost_asm_objs)
+
 #-------------------------------------------------------------------------
 # Build libgloss.a
 #-------------------------------------------------------------------------
@@ -187,6 +238,19 @@ junk += $(sim_lib)
 
 install_libs += $(sim_lib)
 
+#-------------------------------------------------------------------------
+# Build libsemihost.a
+#-------------------------------------------------------------------------
+
+semihost_lib  = libsemihost.a
+$(semihost_lib) : $(semihost_objs)
+	$(AR) rcv $@ $^
+	$(RANLIB) $@
+
+junk += $(semihost_lib)
+
+install_libs += $(semihost_lib)
+
 #-------------------------------------------------------------------------
 # Build crt0.o
 #-------------------------------------------------------------------------
diff --git a/libgloss/riscv/machine/syscall.h b/libgloss/riscv/machine/syscall.h
index 5cd15b848..88b9fdfda 100644
--- a/libgloss/riscv/machine/syscall.h
+++ b/libgloss/riscv/machine/syscall.h
@@ -54,4 +54,29 @@
 #define SYS_time 1062
 #define SYS_getmainvars 2011
 
+/* Semihosting operations.  */
+#define SEMIHOST_clock 0x10
+#define SEMIHOST_close 0x02
+#define SEMIHOST_elapsed 0x30
+#define SEMIHOST_errno 0x13
+#define SEMIHOST_exit 0x18
+#define SEMIHOST_exit_extended 0x20
+#define SEMIHOST_flen 0x0C
+#define SEMIHOST_get_cmdline 0x15
+#define SEMIHOST_heapinfo 0x16
+#define SEMIHOST_iserror 0x08
+#define SEMIHOST_istty 0x09
+#define SEMIHOST_open 0x01
+#define SEMIHOST_read 0x06
+#define SEMIHOST_readc 0x07
+#define SEMIHOST_remove 0x0E
+#define SEMIHOST_rename 0x0F
+#define SEMIHOST_seek 0x0A
+#define SEMIHOST_system 0x12
+#define SEMIHOST_tickfreq 0x31
+#define SEMIHOST_time 0x11
+#define SEMIHOST_tmpnam 0x0D
+#define SEMIHOST_write 0x05
+#define SEMIHOST_writec 0x03
+#define SEMIHOST_write0 0x04
 #endif
diff --git a/libgloss/riscv/semihost-sys_close.c b/libgloss/riscv/semihost-sys_close.c
new file mode 100644
index 000000000..214052185
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_close.c
@@ -0,0 +1,24 @@
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+/* Close a file.  */
+int
+_close (int file)
+{
+  long res;
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[1];
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+  res = syscall_errno (SEMIHOST_close, data_block);
+
+  if (res != 0)
+    return res;
+
+  __remove_fdentry (file);
+  return 0;
+}
diff --git a/libgloss/riscv/semihost-sys_exit.c b/libgloss/riscv/semihost-sys_exit.c
new file mode 100644
index 000000000..774ec847e
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_exit.c
@@ -0,0 +1,19 @@
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+
+#define ADP_Stopped_ApplicationExit 0x20026
+
+/* Exit a program without cleaning up files.  */
+void
+_exit (int exit_status)
+{
+#if __riscv_xlen == 32
+  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
+#else
+  /* The semihosting exit operation only allows 64-bit targets to report the
+     exit code.  */
+  long data_block[] = {ADP_Stopped_ApplicationExit, exit_status};
+  syscall_errno (SEMIHOST_exit, data_block);
+#endif
+  while (1);
+}
diff --git a/libgloss/riscv/semihost-sys_fdtable.c b/libgloss/riscv/semihost-sys_fdtable.c
new file mode 100644
index 000000000..c9454224e
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_fdtable.c
@@ -0,0 +1,81 @@
+#include "semihost_fdtable.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifndef RISCV_MAX_OPEN_FILES
+#define RISCV_MAX_OPEN_FILES 16
+#endif
+
+extern int errno;
+extern int _open (const char *, int, ...);
+
+/* fdtable keeps track of the position of each file and is used to map stdin,
+   stdout and stderr to STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO.  */
+
+static struct fdentry fdtable[RISCV_MAX_OPEN_FILES];
+
+/* Initialize fdtable.  A handle of -1 denotes an empty entry.  */
+
+void __attribute__ ((constructor))
+init_semihosting ()
+{
+  int handle;
+
+  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
+    fdtable[i].handle = -1;
+
+  /* Set up std streams.  */
+  /* stdin.  */
+  handle = _open (":tt", O_RDONLY);
+  fdtable[STDIN_FILENO].handle = handle;
+  fdtable[STDIN_FILENO].pos = 0;
+
+  /* stdout.  */
+  handle = _open (":tt", O_WRONLY|O_CREAT|O_TRUNC);
+  fdtable[STDOUT_FILENO].handle = handle;
+  fdtable[STDOUT_FILENO].pos = 0;
+
+  /* stderr.  */
+  handle = _open (":tt", O_WRONLY|O_CREAT|O_APPEND);
+  fdtable[STDERR_FILENO].handle = handle;
+  fdtable[STDERR_FILENO].pos = 0;
+}
+
+/* Add entry to fdtable.  */
+
+int
+__add_fdentry (int handle)
+{
+  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
+    if (fdtable[i].handle == -1)
+      {
+	fdtable[i].handle = handle;
+	fdtable[i].pos = 0;
+	return i;
+      }
+  /* Too many open files.  */
+  errno = ENFILE;
+  return -1;
+}
+
+/* Return the fdentry for file or NULL if not found.  */
+
+struct fdentry *
+__get_fdentry (int file)
+{
+  if (file<0 || file>RISCV_MAX_OPEN_FILES || fdtable[file].handle == -1)
+    {
+      errno = EBADF;
+      return NULL;
+    }
+  return &fdtable[file];
+}
+
+/* Remove entry from fdtable.  */
+
+void
+__remove_fdentry (int file)
+{
+  fdtable[file].handle = -1;
+}
diff --git a/libgloss/riscv/semihost-sys_fstat.c b/libgloss/riscv/semihost-sys_fstat.c
new file mode 100644
index 000000000..0c5a8d857
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_fstat.c
@@ -0,0 +1,15 @@
+#include <string.h>
+#include <sys/stat.h>
+#include "semihost_stat.h"
+
+/* Status of an open file.  The sys/stat.h header file required is
+   distributed in the include subdirectory for this C library.  */
+
+int
+_fstat (int file, struct stat *st)
+{
+  /* Initialize st as not all fields will be set.  */
+  memset (st, 0, sizeof (*st));
+
+  return __stat_common (file, st);
+}
diff --git a/libgloss/riscv/semihost-sys_ftime.c b/libgloss/riscv/semihost-sys_ftime.c
new file mode 100644
index 000000000..c8d9a9133
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_ftime.c
@@ -0,0 +1,12 @@
+#include <machine/syscall.h>
+#include <sys/timeb.h>
+#include "semihost_syscall.h"
+
+/* Get the current time.  */
+int
+_ftime (struct timeb *tp)
+{
+  tp->time = syscall_errno (SEMIHOST_time, 0);
+  tp->millitm = 0;
+  return 0;
+}
diff --git a/libgloss/riscv/semihost-sys_isatty.c b/libgloss/riscv/semihost-sys_isatty.c
new file mode 100644
index 000000000..ed1376600
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_isatty.c
@@ -0,0 +1,17 @@
+#include <machine/syscall.h>
+#include <sys/stat.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+int
+_isatty (int file)
+{
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[1];
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+  return syscall_errno (SEMIHOST_istty, data_block);
+}
diff --git a/libgloss/riscv/semihost-sys_link.c b/libgloss/riscv/semihost-sys_link.c
new file mode 100644
index 000000000..d65852e7e
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_link.c
@@ -0,0 +1,5 @@
+/* Establish a new name for an existing file.  */
+int _link (const char *old_name, const char *new_name)
+{
+  return -1;
+}
diff --git a/libgloss/riscv/semihost-sys_lseek.c b/libgloss/riscv/semihost-sys_lseek.c
new file mode 100644
index 000000000..121769243
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_lseek.c
@@ -0,0 +1,66 @@
+#include <machine/syscall.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+extern int errno;
+
+/* Set position in a file.  */
+off_t
+_lseek (int file, off_t offset, int dir)
+{
+  long data_block[2];
+  long flen;
+  long res;
+  struct fdentry *fd;
+  off_t abs_pos;
+
+  fd =__get_fdentry (file);
+  if (fd == NULL)
+    {
+      errno = EBADF;
+      return -1;
+    }
+
+  if (dir == SEEK_CUR && offset == 0)
+    return fd->pos;
+
+  data_block[0] = fd->handle;
+
+  switch (dir)
+    {
+      case SEEK_SET:
+	abs_pos = offset;
+	break;
+      case SEEK_CUR:
+	abs_pos = fd->pos + offset;
+	break;
+      case SEEK_END:
+	data_block[1] = 0;
+	flen = syscall_errno (SEMIHOST_flen, data_block);
+	if (flen == -1)
+	  return -1;
+	abs_pos = flen + offset;
+	break;
+      default:
+	errno = EINVAL;
+	return -1;
+    }
+
+  if (abs_pos < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  data_block[1] = abs_pos;
+  res = syscall_errno (SEMIHOST_seek, data_block);
+  if (res == 0)
+    {
+      fd->pos = abs_pos;
+      return abs_pos;
+    }
+  return res;
+}
diff --git a/libgloss/riscv/semihost-sys_open.c b/libgloss/riscv/semihost-sys_open.c
new file mode 100644
index 000000000..9d456a51e
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_open.c
@@ -0,0 +1,58 @@
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+extern int errno;
+
+#define SEMIHOST_MODE_R 0
+#define SEMIHOST_MODE_RPLUS 2
+#define SEMIHOST_MODE_W 4
+#define SEMIHOST_MODE_WPLUS 6
+#define SEMIHOST_MODE_A 8
+#define SEMIHOST_MODE_APLUS 10
+
+/* Open a file.  */
+int
+_open (const char *name, int flags, ...)
+{
+  int fh;
+  int mode;
+  long data_block[3];
+
+  /* Work out mode from flags.  */
+  if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
+    mode = SEMIHOST_MODE_R;
+  else if ((flags & (O_WRONLY | O_CREAT | O_TRUNC))
+	   == (O_WRONLY | O_CREAT | O_TRUNC))
+    mode = SEMIHOST_MODE_W;
+  else if ((flags & (O_WRONLY | O_CREAT | O_APPEND))
+	   == (O_WRONLY | O_CREAT | O_APPEND))
+    mode = SEMIHOST_MODE_A;
+  else if ((flags & (O_RDWR | O_CREAT | O_TRUNC))
+	   == (O_RDWR | O_CREAT | O_TRUNC))
+    mode = SEMIHOST_MODE_WPLUS;
+  else if ((flags & (O_RDWR | O_CREAT | O_APPEND))
+	   == (O_RDWR | O_CREAT | O_APPEND))
+    mode = SEMIHOST_MODE_APLUS;
+  else if (flags & O_RDWR)
+    mode = SEMIHOST_MODE_RPLUS;
+  else
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  data_block[0] = (long) name;
+  data_block[1] = mode;
+  data_block[2] = strlen (name);
+  fh = syscall_errno (SEMIHOST_open, data_block);
+  /* Failed to open file.  */
+  if (fh == -1)
+    return -1;
+
+  /* Register the file in the fdtable.  */
+  return __add_fdentry (fh);
+}
diff --git a/libgloss/riscv/semihost-sys_read.c b/libgloss/riscv/semihost-sys_read.c
new file mode 100644
index 000000000..b76193301
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_read.c
@@ -0,0 +1,28 @@
+#include <machine/syscall.h>
+#include <errno.h>
+#include <sys/types.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+/* Read from a file.  */
+ssize_t _read (int file, void *ptr, size_t len)
+{
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[3];
+  long res;
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+  data_block[1] = (long) ptr;
+  data_block[2] = len;
+  res = syscall_errno (SEMIHOST_read, data_block);
+  if (res >= 0)
+    {
+      ssize_t bytes_read = len - res;
+      fd->pos += bytes_read;
+      return bytes_read;
+    }
+  return -1;
+}
diff --git a/libgloss/riscv/semihost-sys_sbrk.c b/libgloss/riscv/semihost-sys_sbrk.c
new file mode 100644
index 000000000..0d7cf3754
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_sbrk.c
@@ -0,0 +1,22 @@
+/* Semihosting requires that sbrk be implemented without a syscall.  */
+extern char _end[];               /* _end is set in the linker command file.  */
+char *heap_ptr;
+
+/*
+ * sbrk -- changes heap size size.  Get nbytes more
+ *         RAM.  We just increment a pointer in what's
+ *         left of memory on the board.
+ */
+char *
+_sbrk (nbytes)
+     int nbytes;
+{
+  char *base;
+
+  if (!heap_ptr)
+    heap_ptr = (char *)&_end;
+  base = heap_ptr;
+  heap_ptr += nbytes;
+
+  return base;
+}
diff --git a/libgloss/riscv/semihost-sys_stat.c b/libgloss/riscv/semihost-sys_stat.c
new file mode 100644
index 000000000..09039b6e9
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_stat.c
@@ -0,0 +1,32 @@
+#include <machine/syscall.h>
+#include <string.h>
+#include <fcntl.h>
+#include "semihost_stat.h"
+
+/* Status of a file (by name).  */
+
+int
+_stat (const char *name, struct stat *st)
+{
+  int file;
+  int res;
+
+  /* Initialize st as not all fields will be set.  */
+  memset (st, 0, sizeof (*st));
+
+  /* Try to open file.  */
+  file = _open (name, O_RDONLY);
+  if (file == -1)
+    /* _open should have already set errno.  */
+    return -1;
+
+  /* File opened successfully, infer read permission for owner and assume it is
+     a regular file.  */
+  st->st_mode |= S_IREAD | S_IFREG;
+
+  /* Fill in more info.  */
+  res = __stat_common (file, st);
+
+  _close (file);
+  return res;
+}
diff --git a/libgloss/riscv/semihost-sys_stat_common.c b/libgloss/riscv/semihost-sys_stat_common.c
new file mode 100644
index 000000000..aa7e716c2
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_stat_common.c
@@ -0,0 +1,32 @@
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+#include <sys/stat.h>
+#include "semihost_fdtable.h"
+
+/* Used by _fstat and _stat to fill in some common details.  */
+
+int
+__stat_common (int file, struct stat *st)
+{
+  int flen;
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[1];
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+
+  /* Assume character device and default block size of 4096.  */
+  st->st_mode |= S_IFCHR;
+  st->st_blksize = 4096;
+
+  /* Attempt to get length of file.  */
+  flen = syscall_errno (SEMIHOST_flen, data_block);
+  if (flen == -1)
+    return -1;
+
+  st->st_size = flen;
+
+  return 0;
+}
diff --git a/libgloss/riscv/semihost-sys_unlink.c b/libgloss/riscv/semihost-sys_unlink.c
new file mode 100644
index 000000000..dd0d2e9b1
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_unlink.c
@@ -0,0 +1,11 @@
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+#include <string.h>
+
+/* Remove a file's directory entry.  */
+int
+_unlink (const char *name)
+{
+  long data_block[] = {(long) name, strlen (name)};
+  return syscall_errno (SEMIHOST_remove, data_block);
+}
diff --git a/libgloss/riscv/semihost-sys_write.c b/libgloss/riscv/semihost-sys_write.c
new file mode 100644
index 000000000..0a249c2b6
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_write.c
@@ -0,0 +1,28 @@
+#include <machine/syscall.h>
+#include <sys/types.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+/* Write to a file.  */
+ssize_t
+_write (int file, const void *ptr, size_t len)
+{
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[3];
+  long res;
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+  data_block[1] = (long) ptr;
+  data_block[2] = len;
+  res = syscall_errno (SEMIHOST_write, data_block);
+  if (res >= 0)
+    {
+      ssize_t bytes_written = len - res;
+      fd->pos += bytes_written;
+      return bytes_written;
+    }
+  return -1;
+}
diff --git a/libgloss/riscv/semihost.specs b/libgloss/riscv/semihost.specs
new file mode 100644
index 000000000..1c86c67e4
--- /dev/null
+++ b/libgloss/riscv/semihost.specs
@@ -0,0 +1,10 @@
+# Spec file for semihosting syscalls.
+
+%rename lib	semihost_lib
+%rename link	semihost_link
+
+*lib:
+--start-group -lc -lsemihost --end-group
+
+*link:
+%(semihost_link) %:replace-outfile(-lgloss -lsemihost)
diff --git a/libgloss/riscv/semihost_fdtable.h b/libgloss/riscv/semihost_fdtable.h
new file mode 100644
index 000000000..0fa62dd2d
--- /dev/null
+++ b/libgloss/riscv/semihost_fdtable.h
@@ -0,0 +1,17 @@
+#include <sys/types.h>
+
+#ifndef RISCV_SEMIHOST_FDTABLE_H
+#define RISCV_SEMIHOST_FDTABLE_H
+
+extern void __attribute__ ((constructor)) init_semihosting ();
+extern int __add_fdentry (int);
+extern struct fdentry * __get_fdentry (int);
+extern void __remove_fdentry (int);
+
+struct fdentry
+{
+  int handle;
+  off_t pos;
+};
+
+#endif
diff --git a/libgloss/riscv/semihost_stat.h b/libgloss/riscv/semihost_stat.h
new file mode 100644
index 000000000..2030c7ab2
--- /dev/null
+++ b/libgloss/riscv/semihost_stat.h
@@ -0,0 +1,10 @@
+#include <sys/types.h>
+
+#ifndef RISCV_SEMIHOST_STAT_H
+#define RISCV_SEMIHOST_STAT_H
+
+extern int __stat_common (int, struct stat *);
+extern int _open (const char *, int, ...);
+extern int _close (int);
+
+#endif
diff --git a/libgloss/riscv/semihost_syscall.h b/libgloss/riscv/semihost_syscall.h
new file mode 100644
index 000000000..c8ae9ffaf
--- /dev/null
+++ b/libgloss/riscv/semihost_syscall.h
@@ -0,0 +1,43 @@
+#ifndef _INTERNAL_SYSCALL_H
+#define _INTERNAL_SYSCALL_H
+
+extern int errno;
+
+static inline long
+__semihost_syscall (long id, long *data_block)
+{
+  register long a0 asm ("a0") = id;
+  register long a1 asm ("a1") = (long) data_block;
+
+  /* RISC-V semihosting trap sequence.  Must be uncompressed and must not
+     cross page boundary.  */
+  asm volatile (
+    ".balign 16             \n"
+    ".option push           \n"
+    ".option norvc          \n"
+    "slli zero, zero, 0x1f  \n"
+    "ebreak                 \n"
+    "srai zero, zero, 0x7   \n"
+    ".option pop            \n"
+      : "+r"(a0) : "r"(a1) : "memory");
+
+  return a0;
+}
+
+static inline long
+__syscall_error ()
+{
+  errno = __semihost_syscall (SEMIHOST_errno, 0);
+  return -1;
+}
+
+static inline long
+syscall_errno (long id, long *data_block)
+{
+  long res = __semihost_syscall (id, data_block);
+  if (res < 0)
+    return __syscall_error ();
+  return res;
+}
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] RISC-V: Add semihosting support
  2020-11-30 11:36 [PATCH] RISC-V: Add semihosting support Craig Blackmore
@ 2020-11-30 14:05 ` Kito Cheng
  2020-11-30 19:53 ` Jeff Johnston
  2020-12-09  8:21 ` Kito Cheng
  2 siblings, 0 replies; 9+ messages in thread
From: Kito Cheng @ 2020-11-30 14:05 UTC (permalink / raw)
  To: Craig Blackmore; +Cc: Newlib, Keith Packard

Hi Craig:

Thanks for your patch! I think the spec is kind of a de-facto
standard, which is implemented in openocd for a long time, so I think
it would be great to have newlib support for that!

I will review and test this patch this week and give feedback soon :)



On Mon, Nov 30, 2020 at 7:38 PM Craig Blackmore
<craig.blackmore@embecosm.com> wrote:
>
> Add a new library called libsemihost which implements syscalls using
> semihosting syscalls. This is intended to be used instead of libgloss
> when semihosting support is required. A new semihost.specs file has
> been added which will link with -lsemihost instead -lgloss.
>
> The library is implemented following the RISC-V Semihosting
> specification available at:
>
>   https://github.com/riscv/riscv-semihosting-spec
>
> Tested on the gcc testsuite on rv32imc and rv64imc using spike and
> openocd.
>
> ---
>  libgloss/riscv/Makefile.in                | 66 +++++++++++++++++-
>  libgloss/riscv/machine/syscall.h          | 25 +++++++
>  libgloss/riscv/semihost-sys_close.c       | 24 +++++++
>  libgloss/riscv/semihost-sys_exit.c        | 19 ++++++
>  libgloss/riscv/semihost-sys_fdtable.c     | 81 +++++++++++++++++++++++
>  libgloss/riscv/semihost-sys_fstat.c       | 15 +++++
>  libgloss/riscv/semihost-sys_ftime.c       | 12 ++++
>  libgloss/riscv/semihost-sys_isatty.c      | 17 +++++
>  libgloss/riscv/semihost-sys_link.c        |  5 ++
>  libgloss/riscv/semihost-sys_lseek.c       | 66 ++++++++++++++++++
>  libgloss/riscv/semihost-sys_open.c        | 58 ++++++++++++++++
>  libgloss/riscv/semihost-sys_read.c        | 28 ++++++++
>  libgloss/riscv/semihost-sys_sbrk.c        | 22 ++++++
>  libgloss/riscv/semihost-sys_stat.c        | 32 +++++++++
>  libgloss/riscv/semihost-sys_stat_common.c | 32 +++++++++
>  libgloss/riscv/semihost-sys_unlink.c      | 11 +++
>  libgloss/riscv/semihost-sys_write.c       | 28 ++++++++
>  libgloss/riscv/semihost.specs             | 10 +++
>  libgloss/riscv/semihost_fdtable.h         | 17 +++++
>  libgloss/riscv/semihost_stat.h            | 10 +++
>  libgloss/riscv/semihost_syscall.h         | 43 ++++++++++++
>  21 files changed, 620 insertions(+), 1 deletion(-)
>  create mode 100644 libgloss/riscv/semihost-sys_close.c
>  create mode 100644 libgloss/riscv/semihost-sys_exit.c
>  create mode 100644 libgloss/riscv/semihost-sys_fdtable.c
>  create mode 100644 libgloss/riscv/semihost-sys_fstat.c
>  create mode 100644 libgloss/riscv/semihost-sys_ftime.c
>  create mode 100644 libgloss/riscv/semihost-sys_isatty.c
>  create mode 100644 libgloss/riscv/semihost-sys_link.c
>  create mode 100644 libgloss/riscv/semihost-sys_lseek.c
>  create mode 100644 libgloss/riscv/semihost-sys_open.c
>  create mode 100644 libgloss/riscv/semihost-sys_read.c
>  create mode 100644 libgloss/riscv/semihost-sys_sbrk.c
>  create mode 100644 libgloss/riscv/semihost-sys_stat.c
>  create mode 100644 libgloss/riscv/semihost-sys_stat_common.c
>  create mode 100644 libgloss/riscv/semihost-sys_unlink.c
>  create mode 100644 libgloss/riscv/semihost-sys_write.c
>  create mode 100644 libgloss/riscv/semihost.specs
>  create mode 100644 libgloss/riscv/semihost_fdtable.h
>  create mode 100644 libgloss/riscv/semihost_stat.h
>  create mode 100644 libgloss/riscv/semihost_syscall.h
>
> diff --git a/libgloss/riscv/Makefile.in b/libgloss/riscv/Makefile.in
> index 579dd9554..185b6e6d2 100644
> --- a/libgloss/riscv/Makefile.in
> +++ b/libgloss/riscv/Makefile.in
> @@ -40,8 +40,38 @@ gloss_srcs = \
>         sys_wait.c \
>         sys_write.c
>
> +# libsemihost reuses some of the libgloss minimal implementations
> +
> +semihost_srcs = \
> +       nanosleep.c \
> +       sys_chdir.c \
> +       sys_chmod.c \
> +       sys_chown.c \
> +       sys_execve.c \
> +       sys_fork.c \
> +       sys_getcwd.c \
> +       sys_getpid.c \
> +       sys_kill.c \
> +       sys_utime.c \
> +       sys_wait.c \
> +       semihost-sys_close.c \
> +       semihost-sys_exit.c \
> +       semihost-sys_fdtable.c \
> +       semihost-sys_fstat.c \
> +       semihost-sys_ftime.c \
> +       semihost-sys_isatty.c \
> +       semihost-sys_link.c \
> +       semihost-sys_lseek.c \
> +       semihost-sys_open.c \
> +       semihost-sys_read.c \
> +       semihost-sys_sbrk.c \
> +       semihost-sys_stat.c \
> +       semihost-sys_stat_common.c \
> +       semihost-sys_unlink.c \
> +       semihost-sys_write.c
> +
>  gloss_specs = \
> -       nano.specs sim.specs
> +       nano.specs sim.specs semihost.specs
>
>  # Extra files
>
> @@ -134,6 +164,17 @@ sim_objs += $(sim_c_objs)
>  deps += $(sim_c_deps)
>  junk += $(sim_c_deps) $(sim_c_objs)
>
> +semihost_c_srcs = $(filter %.c, $(semihost_srcs))
> +semihost_c_objs = $(patsubst %.c, semihost-%.o, $(notdir $(semihost_c_srcs)))
> +semihost_c_deps = $(patsubst %.c, semihost-%.d, $(notdir $(semihost_c_srcs)))
> +
> +$(semihost_c_objs): semihost-%.o : %.c
> +       $(COMPILE) -c -o $@ $<
> +
> +semihost_objs += $(semihost_c_objs)
> +deps += $(semihost_c_deps)
> +junk += $(semihost_c_deps) $(semihost_c_objs)
> +
>  #-------------------------------------------------------------------------
>  # Build Object Files from Assembly Source
>  #-------------------------------------------------------------------------
> @@ -159,6 +200,16 @@ sim_objs += $(sim_asm_objs)
>  deps += $(sim_asm_deps)
>  junk += $(sim_asm_deps) $(sim_asm_objs)
>
> +semihost_asm_objs = $(patsubst %.S, semihost-%.o, $(notdir $(gloss_asm_srcs)))
> +semihost_asm_deps = $(patsubst %.S, semihost-%.d, $(notdir $(gloss_asm_srcs)))
> +
> +$(semihost_asm_objs) : semihost-%.o : %.S
> +       $(COMPILE) -c -DUSING_SEMIHOST_SPECS -o $@ $<
> +
> +semihost_objs += $(semihost_asm_objs)
> +deps += $(semihost_asm_deps)
> +junk += $(semihost_asm_deps) $(semihost_asm_objs)
> +
>  #-------------------------------------------------------------------------
>  # Build libgloss.a
>  #-------------------------------------------------------------------------
> @@ -187,6 +238,19 @@ junk += $(sim_lib)
>
>  install_libs += $(sim_lib)
>
> +#-------------------------------------------------------------------------
> +# Build libsemihost.a
> +#-------------------------------------------------------------------------
> +
> +semihost_lib  = libsemihost.a
> +$(semihost_lib) : $(semihost_objs)
> +       $(AR) rcv $@ $^
> +       $(RANLIB) $@
> +
> +junk += $(semihost_lib)
> +
> +install_libs += $(semihost_lib)
> +
>  #-------------------------------------------------------------------------
>  # Build crt0.o
>  #-------------------------------------------------------------------------
> diff --git a/libgloss/riscv/machine/syscall.h b/libgloss/riscv/machine/syscall.h
> index 5cd15b848..88b9fdfda 100644
> --- a/libgloss/riscv/machine/syscall.h
> +++ b/libgloss/riscv/machine/syscall.h
> @@ -54,4 +54,29 @@
>  #define SYS_time 1062
>  #define SYS_getmainvars 2011
>
> +/* Semihosting operations.  */
> +#define SEMIHOST_clock 0x10
> +#define SEMIHOST_close 0x02
> +#define SEMIHOST_elapsed 0x30
> +#define SEMIHOST_errno 0x13
> +#define SEMIHOST_exit 0x18
> +#define SEMIHOST_exit_extended 0x20
> +#define SEMIHOST_flen 0x0C
> +#define SEMIHOST_get_cmdline 0x15
> +#define SEMIHOST_heapinfo 0x16
> +#define SEMIHOST_iserror 0x08
> +#define SEMIHOST_istty 0x09
> +#define SEMIHOST_open 0x01
> +#define SEMIHOST_read 0x06
> +#define SEMIHOST_readc 0x07
> +#define SEMIHOST_remove 0x0E
> +#define SEMIHOST_rename 0x0F
> +#define SEMIHOST_seek 0x0A
> +#define SEMIHOST_system 0x12
> +#define SEMIHOST_tickfreq 0x31
> +#define SEMIHOST_time 0x11
> +#define SEMIHOST_tmpnam 0x0D
> +#define SEMIHOST_write 0x05
> +#define SEMIHOST_writec 0x03
> +#define SEMIHOST_write0 0x04
>  #endif
> diff --git a/libgloss/riscv/semihost-sys_close.c b/libgloss/riscv/semihost-sys_close.c
> new file mode 100644
> index 000000000..214052185
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_close.c
> @@ -0,0 +1,24 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Close a file.  */
> +int
> +_close (int file)
> +{
> +  long res;
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  res = syscall_errno (SEMIHOST_close, data_block);
> +
> +  if (res != 0)
> +    return res;
> +
> +  __remove_fdentry (file);
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_exit.c b/libgloss/riscv/semihost-sys_exit.c
> new file mode 100644
> index 000000000..774ec847e
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_exit.c
> @@ -0,0 +1,19 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +
> +#define ADP_Stopped_ApplicationExit 0x20026
> +
> +/* Exit a program without cleaning up files.  */
> +void
> +_exit (int exit_status)
> +{
> +#if __riscv_xlen == 32
> +  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
> +#else
> +  /* The semihosting exit operation only allows 64-bit targets to report the
> +     exit code.  */
> +  long data_block[] = {ADP_Stopped_ApplicationExit, exit_status};
> +  syscall_errno (SEMIHOST_exit, data_block);
> +#endif
> +  while (1);
> +}
> diff --git a/libgloss/riscv/semihost-sys_fdtable.c b/libgloss/riscv/semihost-sys_fdtable.c
> new file mode 100644
> index 000000000..c9454224e
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_fdtable.c
> @@ -0,0 +1,81 @@
> +#include "semihost_fdtable.h"
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> +#ifndef RISCV_MAX_OPEN_FILES
> +#define RISCV_MAX_OPEN_FILES 16
> +#endif
> +
> +extern int errno;
> +extern int _open (const char *, int, ...);
> +
> +/* fdtable keeps track of the position of each file and is used to map stdin,
> +   stdout and stderr to STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO.  */
> +
> +static struct fdentry fdtable[RISCV_MAX_OPEN_FILES];
> +
> +/* Initialize fdtable.  A handle of -1 denotes an empty entry.  */
> +
> +void __attribute__ ((constructor))
> +init_semihosting ()
> +{
> +  int handle;
> +
> +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
> +    fdtable[i].handle = -1;
> +
> +  /* Set up std streams.  */
> +  /* stdin.  */
> +  handle = _open (":tt", O_RDONLY);
> +  fdtable[STDIN_FILENO].handle = handle;
> +  fdtable[STDIN_FILENO].pos = 0;
> +
> +  /* stdout.  */
> +  handle = _open (":tt", O_WRONLY|O_CREAT|O_TRUNC);
> +  fdtable[STDOUT_FILENO].handle = handle;
> +  fdtable[STDOUT_FILENO].pos = 0;
> +
> +  /* stderr.  */
> +  handle = _open (":tt", O_WRONLY|O_CREAT|O_APPEND);
> +  fdtable[STDERR_FILENO].handle = handle;
> +  fdtable[STDERR_FILENO].pos = 0;
> +}
> +
> +/* Add entry to fdtable.  */
> +
> +int
> +__add_fdentry (int handle)
> +{
> +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
> +    if (fdtable[i].handle == -1)
> +      {
> +       fdtable[i].handle = handle;
> +       fdtable[i].pos = 0;
> +       return i;
> +      }
> +  /* Too many open files.  */
> +  errno = ENFILE;
> +  return -1;
> +}
> +
> +/* Return the fdentry for file or NULL if not found.  */
> +
> +struct fdentry *
> +__get_fdentry (int file)
> +{
> +  if (file<0 || file>RISCV_MAX_OPEN_FILES || fdtable[file].handle == -1)
> +    {
> +      errno = EBADF;
> +      return NULL;
> +    }
> +  return &fdtable[file];
> +}
> +
> +/* Remove entry from fdtable.  */
> +
> +void
> +__remove_fdentry (int file)
> +{
> +  fdtable[file].handle = -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_fstat.c b/libgloss/riscv/semihost-sys_fstat.c
> new file mode 100644
> index 000000000..0c5a8d857
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_fstat.c
> @@ -0,0 +1,15 @@
> +#include <string.h>
> +#include <sys/stat.h>
> +#include "semihost_stat.h"
> +
> +/* Status of an open file.  The sys/stat.h header file required is
> +   distributed in the include subdirectory for this C library.  */
> +
> +int
> +_fstat (int file, struct stat *st)
> +{
> +  /* Initialize st as not all fields will be set.  */
> +  memset (st, 0, sizeof (*st));
> +
> +  return __stat_common (file, st);
> +}
> diff --git a/libgloss/riscv/semihost-sys_ftime.c b/libgloss/riscv/semihost-sys_ftime.c
> new file mode 100644
> index 000000000..c8d9a9133
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_ftime.c
> @@ -0,0 +1,12 @@
> +#include <machine/syscall.h>
> +#include <sys/timeb.h>
> +#include "semihost_syscall.h"
> +
> +/* Get the current time.  */
> +int
> +_ftime (struct timeb *tp)
> +{
> +  tp->time = syscall_errno (SEMIHOST_time, 0);
> +  tp->millitm = 0;
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_isatty.c b/libgloss/riscv/semihost-sys_isatty.c
> new file mode 100644
> index 000000000..ed1376600
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_isatty.c
> @@ -0,0 +1,17 @@
> +#include <machine/syscall.h>
> +#include <sys/stat.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +int
> +_isatty (int file)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  return syscall_errno (SEMIHOST_istty, data_block);
> +}
> diff --git a/libgloss/riscv/semihost-sys_link.c b/libgloss/riscv/semihost-sys_link.c
> new file mode 100644
> index 000000000..d65852e7e
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_link.c
> @@ -0,0 +1,5 @@
> +/* Establish a new name for an existing file.  */
> +int _link (const char *old_name, const char *new_name)
> +{
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_lseek.c b/libgloss/riscv/semihost-sys_lseek.c
> new file mode 100644
> index 000000000..121769243
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_lseek.c
> @@ -0,0 +1,66 @@
> +#include <machine/syscall.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +extern int errno;
> +
> +/* Set position in a file.  */
> +off_t
> +_lseek (int file, off_t offset, int dir)
> +{
> +  long data_block[2];
> +  long flen;
> +  long res;
> +  struct fdentry *fd;
> +  off_t abs_pos;
> +
> +  fd =__get_fdentry (file);
> +  if (fd == NULL)
> +    {
> +      errno = EBADF;
> +      return -1;
> +    }
> +
> +  if (dir == SEEK_CUR && offset == 0)
> +    return fd->pos;
> +
> +  data_block[0] = fd->handle;
> +
> +  switch (dir)
> +    {
> +      case SEEK_SET:
> +       abs_pos = offset;
> +       break;
> +      case SEEK_CUR:
> +       abs_pos = fd->pos + offset;
> +       break;
> +      case SEEK_END:
> +       data_block[1] = 0;
> +       flen = syscall_errno (SEMIHOST_flen, data_block);
> +       if (flen == -1)
> +         return -1;
> +       abs_pos = flen + offset;
> +       break;
> +      default:
> +       errno = EINVAL;
> +       return -1;
> +    }
> +
> +  if (abs_pos < 0)
> +    {
> +      errno = EINVAL;
> +      return -1;
> +    }
> +
> +  data_block[1] = abs_pos;
> +  res = syscall_errno (SEMIHOST_seek, data_block);
> +  if (res == 0)
> +    {
> +      fd->pos = abs_pos;
> +      return abs_pos;
> +    }
> +  return res;
> +}
> diff --git a/libgloss/riscv/semihost-sys_open.c b/libgloss/riscv/semihost-sys_open.c
> new file mode 100644
> index 000000000..9d456a51e
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_open.c
> @@ -0,0 +1,58 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +#include <errno.h>
> +#include <string.h>
> +#include <fcntl.h>
> +
> +extern int errno;
> +
> +#define SEMIHOST_MODE_R 0
> +#define SEMIHOST_MODE_RPLUS 2
> +#define SEMIHOST_MODE_W 4
> +#define SEMIHOST_MODE_WPLUS 6
> +#define SEMIHOST_MODE_A 8
> +#define SEMIHOST_MODE_APLUS 10
> +
> +/* Open a file.  */
> +int
> +_open (const char *name, int flags, ...)
> +{
> +  int fh;
> +  int mode;
> +  long data_block[3];
> +
> +  /* Work out mode from flags.  */
> +  if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
> +    mode = SEMIHOST_MODE_R;
> +  else if ((flags & (O_WRONLY | O_CREAT | O_TRUNC))
> +          == (O_WRONLY | O_CREAT | O_TRUNC))
> +    mode = SEMIHOST_MODE_W;
> +  else if ((flags & (O_WRONLY | O_CREAT | O_APPEND))
> +          == (O_WRONLY | O_CREAT | O_APPEND))
> +    mode = SEMIHOST_MODE_A;
> +  else if ((flags & (O_RDWR | O_CREAT | O_TRUNC))
> +          == (O_RDWR | O_CREAT | O_TRUNC))
> +    mode = SEMIHOST_MODE_WPLUS;
> +  else if ((flags & (O_RDWR | O_CREAT | O_APPEND))
> +          == (O_RDWR | O_CREAT | O_APPEND))
> +    mode = SEMIHOST_MODE_APLUS;
> +  else if (flags & O_RDWR)
> +    mode = SEMIHOST_MODE_RPLUS;
> +  else
> +    {
> +      errno = EINVAL;
> +      return -1;
> +    }
> +
> +  data_block[0] = (long) name;
> +  data_block[1] = mode;
> +  data_block[2] = strlen (name);
> +  fh = syscall_errno (SEMIHOST_open, data_block);
> +  /* Failed to open file.  */
> +  if (fh == -1)
> +    return -1;
> +
> +  /* Register the file in the fdtable.  */
> +  return __add_fdentry (fh);
> +}
> diff --git a/libgloss/riscv/semihost-sys_read.c b/libgloss/riscv/semihost-sys_read.c
> new file mode 100644
> index 000000000..b76193301
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_read.c
> @@ -0,0 +1,28 @@
> +#include <machine/syscall.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Read from a file.  */
> +ssize_t _read (int file, void *ptr, size_t len)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[3];
> +  long res;
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  data_block[1] = (long) ptr;
> +  data_block[2] = len;
> +  res = syscall_errno (SEMIHOST_read, data_block);
> +  if (res >= 0)
> +    {
> +      ssize_t bytes_read = len - res;
> +      fd->pos += bytes_read;
> +      return bytes_read;
> +    }
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_sbrk.c b/libgloss/riscv/semihost-sys_sbrk.c
> new file mode 100644
> index 000000000..0d7cf3754
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_sbrk.c
> @@ -0,0 +1,22 @@
> +/* Semihosting requires that sbrk be implemented without a syscall.  */
> +extern char _end[];               /* _end is set in the linker command file.  */
> +char *heap_ptr;
> +
> +/*
> + * sbrk -- changes heap size size.  Get nbytes more
> + *         RAM.  We just increment a pointer in what's
> + *         left of memory on the board.
> + */
> +char *
> +_sbrk (nbytes)
> +     int nbytes;
> +{
> +  char *base;
> +
> +  if (!heap_ptr)
> +    heap_ptr = (char *)&_end;
> +  base = heap_ptr;
> +  heap_ptr += nbytes;
> +
> +  return base;
> +}
> diff --git a/libgloss/riscv/semihost-sys_stat.c b/libgloss/riscv/semihost-sys_stat.c
> new file mode 100644
> index 000000000..09039b6e9
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_stat.c
> @@ -0,0 +1,32 @@
> +#include <machine/syscall.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include "semihost_stat.h"
> +
> +/* Status of a file (by name).  */
> +
> +int
> +_stat (const char *name, struct stat *st)
> +{
> +  int file;
> +  int res;
> +
> +  /* Initialize st as not all fields will be set.  */
> +  memset (st, 0, sizeof (*st));
> +
> +  /* Try to open file.  */
> +  file = _open (name, O_RDONLY);
> +  if (file == -1)
> +    /* _open should have already set errno.  */
> +    return -1;
> +
> +  /* File opened successfully, infer read permission for owner and assume it is
> +     a regular file.  */
> +  st->st_mode |= S_IREAD | S_IFREG;
> +
> +  /* Fill in more info.  */
> +  res = __stat_common (file, st);
> +
> +  _close (file);
> +  return res;
> +}
> diff --git a/libgloss/riscv/semihost-sys_stat_common.c b/libgloss/riscv/semihost-sys_stat_common.c
> new file mode 100644
> index 000000000..aa7e716c2
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_stat_common.c
> @@ -0,0 +1,32 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include <sys/stat.h>
> +#include "semihost_fdtable.h"
> +
> +/* Used by _fstat and _stat to fill in some common details.  */
> +
> +int
> +__stat_common (int file, struct stat *st)
> +{
> +  int flen;
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +
> +  /* Assume character device and default block size of 4096.  */
> +  st->st_mode |= S_IFCHR;
> +  st->st_blksize = 4096;
> +
> +  /* Attempt to get length of file.  */
> +  flen = syscall_errno (SEMIHOST_flen, data_block);
> +  if (flen == -1)
> +    return -1;
> +
> +  st->st_size = flen;
> +
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_unlink.c b/libgloss/riscv/semihost-sys_unlink.c
> new file mode 100644
> index 000000000..dd0d2e9b1
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_unlink.c
> @@ -0,0 +1,11 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include <string.h>
> +
> +/* Remove a file's directory entry.  */
> +int
> +_unlink (const char *name)
> +{
> +  long data_block[] = {(long) name, strlen (name)};
> +  return syscall_errno (SEMIHOST_remove, data_block);
> +}
> diff --git a/libgloss/riscv/semihost-sys_write.c b/libgloss/riscv/semihost-sys_write.c
> new file mode 100644
> index 000000000..0a249c2b6
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_write.c
> @@ -0,0 +1,28 @@
> +#include <machine/syscall.h>
> +#include <sys/types.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Write to a file.  */
> +ssize_t
> +_write (int file, const void *ptr, size_t len)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[3];
> +  long res;
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  data_block[1] = (long) ptr;
> +  data_block[2] = len;
> +  res = syscall_errno (SEMIHOST_write, data_block);
> +  if (res >= 0)
> +    {
> +      ssize_t bytes_written = len - res;
> +      fd->pos += bytes_written;
> +      return bytes_written;
> +    }
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost.specs b/libgloss/riscv/semihost.specs
> new file mode 100644
> index 000000000..1c86c67e4
> --- /dev/null
> +++ b/libgloss/riscv/semihost.specs
> @@ -0,0 +1,10 @@
> +# Spec file for semihosting syscalls.
> +
> +%rename lib    semihost_lib
> +%rename link   semihost_link
> +
> +*lib:
> +--start-group -lc -lsemihost --end-group
> +
> +*link:
> +%(semihost_link) %:replace-outfile(-lgloss -lsemihost)
> diff --git a/libgloss/riscv/semihost_fdtable.h b/libgloss/riscv/semihost_fdtable.h
> new file mode 100644
> index 000000000..0fa62dd2d
> --- /dev/null
> +++ b/libgloss/riscv/semihost_fdtable.h
> @@ -0,0 +1,17 @@
> +#include <sys/types.h>
> +
> +#ifndef RISCV_SEMIHOST_FDTABLE_H
> +#define RISCV_SEMIHOST_FDTABLE_H
> +
> +extern void __attribute__ ((constructor)) init_semihosting ();
> +extern int __add_fdentry (int);
> +extern struct fdentry * __get_fdentry (int);
> +extern void __remove_fdentry (int);
> +
> +struct fdentry
> +{
> +  int handle;
> +  off_t pos;
> +};
> +
> +#endif
> diff --git a/libgloss/riscv/semihost_stat.h b/libgloss/riscv/semihost_stat.h
> new file mode 100644
> index 000000000..2030c7ab2
> --- /dev/null
> +++ b/libgloss/riscv/semihost_stat.h
> @@ -0,0 +1,10 @@
> +#include <sys/types.h>
> +
> +#ifndef RISCV_SEMIHOST_STAT_H
> +#define RISCV_SEMIHOST_STAT_H
> +
> +extern int __stat_common (int, struct stat *);
> +extern int _open (const char *, int, ...);
> +extern int _close (int);
> +
> +#endif
> diff --git a/libgloss/riscv/semihost_syscall.h b/libgloss/riscv/semihost_syscall.h
> new file mode 100644
> index 000000000..c8ae9ffaf
> --- /dev/null
> +++ b/libgloss/riscv/semihost_syscall.h
> @@ -0,0 +1,43 @@
> +#ifndef _INTERNAL_SYSCALL_H
> +#define _INTERNAL_SYSCALL_H
> +
> +extern int errno;
> +
> +static inline long
> +__semihost_syscall (long id, long *data_block)
> +{
> +  register long a0 asm ("a0") = id;
> +  register long a1 asm ("a1") = (long) data_block;
> +
> +  /* RISC-V semihosting trap sequence.  Must be uncompressed and must not
> +     cross page boundary.  */
> +  asm volatile (
> +    ".balign 16             \n"
> +    ".option push           \n"
> +    ".option norvc          \n"
> +    "slli zero, zero, 0x1f  \n"
> +    "ebreak                 \n"
> +    "srai zero, zero, 0x7   \n"
> +    ".option pop            \n"
> +      : "+r"(a0) : "r"(a1) : "memory");
> +
> +  return a0;
> +}
> +
> +static inline long
> +__syscall_error ()
> +{
> +  errno = __semihost_syscall (SEMIHOST_errno, 0);
> +  return -1;
> +}
> +
> +static inline long
> +syscall_errno (long id, long *data_block)
> +{
> +  long res = __semihost_syscall (id, data_block);
> +  if (res < 0)
> +    return __syscall_error ();
> +  return res;
> +}
> +
> +#endif
> --
> 2.17.1
>
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] RISC-V: Add semihosting support
  2020-11-30 11:36 [PATCH] RISC-V: Add semihosting support Craig Blackmore
  2020-11-30 14:05 ` Kito Cheng
@ 2020-11-30 19:53 ` Jeff Johnston
  2020-12-09  8:21 ` Kito Cheng
  2 siblings, 0 replies; 9+ messages in thread
From: Jeff Johnston @ 2020-11-30 19:53 UTC (permalink / raw)
  To: Craig Blackmore, Newlib

Hello Craig,

Just a quick observation.  There are no license details in any of the files
you have submitted.  Did you write the code or
take it from somewhere else?  If you wrote it, then please add the license
and if you copied it, please use the license of the
original code.

Regards,

-- Jeff J.

On Mon, Nov 30, 2020 at 6:37 AM Craig Blackmore <
craig.blackmore@embecosm.com> wrote:

> Add a new library called libsemihost which implements syscalls using
> semihosting syscalls. This is intended to be used instead of libgloss
> when semihosting support is required. A new semihost.specs file has
> been added which will link with -lsemihost instead -lgloss.
>
> The library is implemented following the RISC-V Semihosting
> specification available at:
>
>   https://github.com/riscv/riscv-semihosting-spec
>
> Tested on the gcc testsuite on rv32imc and rv64imc using spike and
> openocd.
>
> ---
>  libgloss/riscv/Makefile.in                | 66 +++++++++++++++++-
>  libgloss/riscv/machine/syscall.h          | 25 +++++++
>  libgloss/riscv/semihost-sys_close.c       | 24 +++++++
>  libgloss/riscv/semihost-sys_exit.c        | 19 ++++++
>  libgloss/riscv/semihost-sys_fdtable.c     | 81 +++++++++++++++++++++++
>  libgloss/riscv/semihost-sys_fstat.c       | 15 +++++
>  libgloss/riscv/semihost-sys_ftime.c       | 12 ++++
>  libgloss/riscv/semihost-sys_isatty.c      | 17 +++++
>  libgloss/riscv/semihost-sys_link.c        |  5 ++
>  libgloss/riscv/semihost-sys_lseek.c       | 66 ++++++++++++++++++
>  libgloss/riscv/semihost-sys_open.c        | 58 ++++++++++++++++
>  libgloss/riscv/semihost-sys_read.c        | 28 ++++++++
>  libgloss/riscv/semihost-sys_sbrk.c        | 22 ++++++
>  libgloss/riscv/semihost-sys_stat.c        | 32 +++++++++
>  libgloss/riscv/semihost-sys_stat_common.c | 32 +++++++++
>  libgloss/riscv/semihost-sys_unlink.c      | 11 +++
>  libgloss/riscv/semihost-sys_write.c       | 28 ++++++++
>  libgloss/riscv/semihost.specs             | 10 +++
>  libgloss/riscv/semihost_fdtable.h         | 17 +++++
>  libgloss/riscv/semihost_stat.h            | 10 +++
>  libgloss/riscv/semihost_syscall.h         | 43 ++++++++++++
>  21 files changed, 620 insertions(+), 1 deletion(-)
>  create mode 100644 libgloss/riscv/semihost-sys_close.c
>  create mode 100644 libgloss/riscv/semihost-sys_exit.c
>  create mode 100644 libgloss/riscv/semihost-sys_fdtable.c
>  create mode 100644 libgloss/riscv/semihost-sys_fstat.c
>  create mode 100644 libgloss/riscv/semihost-sys_ftime.c
>  create mode 100644 libgloss/riscv/semihost-sys_isatty.c
>  create mode 100644 libgloss/riscv/semihost-sys_link.c
>  create mode 100644 libgloss/riscv/semihost-sys_lseek.c
>  create mode 100644 libgloss/riscv/semihost-sys_open.c
>  create mode 100644 libgloss/riscv/semihost-sys_read.c
>  create mode 100644 libgloss/riscv/semihost-sys_sbrk.c
>  create mode 100644 libgloss/riscv/semihost-sys_stat.c
>  create mode 100644 libgloss/riscv/semihost-sys_stat_common.c
>  create mode 100644 libgloss/riscv/semihost-sys_unlink.c
>  create mode 100644 libgloss/riscv/semihost-sys_write.c
>  create mode 100644 libgloss/riscv/semihost.specs
>  create mode 100644 libgloss/riscv/semihost_fdtable.h
>  create mode 100644 libgloss/riscv/semihost_stat.h
>  create mode 100644 libgloss/riscv/semihost_syscall.h
>
> diff --git a/libgloss/riscv/Makefile.in b/libgloss/riscv/Makefile.in
> index 579dd9554..185b6e6d2 100644
> --- a/libgloss/riscv/Makefile.in
> +++ b/libgloss/riscv/Makefile.in
> @@ -40,8 +40,38 @@ gloss_srcs = \
>         sys_wait.c \
>         sys_write.c
>
> +# libsemihost reuses some of the libgloss minimal implementations
> +
> +semihost_srcs = \
> +       nanosleep.c \
> +       sys_chdir.c \
> +       sys_chmod.c \
> +       sys_chown.c \
> +       sys_execve.c \
> +       sys_fork.c \
> +       sys_getcwd.c \
> +       sys_getpid.c \
> +       sys_kill.c \
> +       sys_utime.c \
> +       sys_wait.c \
> +       semihost-sys_close.c \
> +       semihost-sys_exit.c \
> +       semihost-sys_fdtable.c \
> +       semihost-sys_fstat.c \
> +       semihost-sys_ftime.c \
> +       semihost-sys_isatty.c \
> +       semihost-sys_link.c \
> +       semihost-sys_lseek.c \
> +       semihost-sys_open.c \
> +       semihost-sys_read.c \
> +       semihost-sys_sbrk.c \
> +       semihost-sys_stat.c \
> +       semihost-sys_stat_common.c \
> +       semihost-sys_unlink.c \
> +       semihost-sys_write.c
> +
>  gloss_specs = \
> -       nano.specs sim.specs
> +       nano.specs sim.specs semihost.specs
>
>  # Extra files
>
> @@ -134,6 +164,17 @@ sim_objs += $(sim_c_objs)
>  deps += $(sim_c_deps)
>  junk += $(sim_c_deps) $(sim_c_objs)
>
> +semihost_c_srcs = $(filter %.c, $(semihost_srcs))
> +semihost_c_objs = $(patsubst %.c, semihost-%.o, $(notdir
> $(semihost_c_srcs)))
> +semihost_c_deps = $(patsubst %.c, semihost-%.d, $(notdir
> $(semihost_c_srcs)))
> +
> +$(semihost_c_objs): semihost-%.o : %.c
> +       $(COMPILE) -c -o $@ $<
> +
> +semihost_objs += $(semihost_c_objs)
> +deps += $(semihost_c_deps)
> +junk += $(semihost_c_deps) $(semihost_c_objs)
> +
>  #-------------------------------------------------------------------------
>  # Build Object Files from Assembly Source
>  #-------------------------------------------------------------------------
> @@ -159,6 +200,16 @@ sim_objs += $(sim_asm_objs)
>  deps += $(sim_asm_deps)
>  junk += $(sim_asm_deps) $(sim_asm_objs)
>
> +semihost_asm_objs = $(patsubst %.S, semihost-%.o, $(notdir
> $(gloss_asm_srcs)))
> +semihost_asm_deps = $(patsubst %.S, semihost-%.d, $(notdir
> $(gloss_asm_srcs)))
> +
> +$(semihost_asm_objs) : semihost-%.o : %.S
> +       $(COMPILE) -c -DUSING_SEMIHOST_SPECS -o $@ $<
> +
> +semihost_objs += $(semihost_asm_objs)
> +deps += $(semihost_asm_deps)
> +junk += $(semihost_asm_deps) $(semihost_asm_objs)
> +
>  #-------------------------------------------------------------------------
>  # Build libgloss.a
>  #-------------------------------------------------------------------------
> @@ -187,6 +238,19 @@ junk += $(sim_lib)
>
>  install_libs += $(sim_lib)
>
> +#-------------------------------------------------------------------------
> +# Build libsemihost.a
> +#-------------------------------------------------------------------------
> +
> +semihost_lib  = libsemihost.a
> +$(semihost_lib) : $(semihost_objs)
> +       $(AR) rcv $@ $^
> +       $(RANLIB) $@
> +
> +junk += $(semihost_lib)
> +
> +install_libs += $(semihost_lib)
> +
>  #-------------------------------------------------------------------------
>  # Build crt0.o
>  #-------------------------------------------------------------------------
> diff --git a/libgloss/riscv/machine/syscall.h
> b/libgloss/riscv/machine/syscall.h
> index 5cd15b848..88b9fdfda 100644
> --- a/libgloss/riscv/machine/syscall.h
> +++ b/libgloss/riscv/machine/syscall.h
> @@ -54,4 +54,29 @@
>  #define SYS_time 1062
>  #define SYS_getmainvars 2011
>
> +/* Semihosting operations.  */
> +#define SEMIHOST_clock 0x10
> +#define SEMIHOST_close 0x02
> +#define SEMIHOST_elapsed 0x30
> +#define SEMIHOST_errno 0x13
> +#define SEMIHOST_exit 0x18
> +#define SEMIHOST_exit_extended 0x20
> +#define SEMIHOST_flen 0x0C
> +#define SEMIHOST_get_cmdline 0x15
> +#define SEMIHOST_heapinfo 0x16
> +#define SEMIHOST_iserror 0x08
> +#define SEMIHOST_istty 0x09
> +#define SEMIHOST_open 0x01
> +#define SEMIHOST_read 0x06
> +#define SEMIHOST_readc 0x07
> +#define SEMIHOST_remove 0x0E
> +#define SEMIHOST_rename 0x0F
> +#define SEMIHOST_seek 0x0A
> +#define SEMIHOST_system 0x12
> +#define SEMIHOST_tickfreq 0x31
> +#define SEMIHOST_time 0x11
> +#define SEMIHOST_tmpnam 0x0D
> +#define SEMIHOST_write 0x05
> +#define SEMIHOST_writec 0x03
> +#define SEMIHOST_write0 0x04
>  #endif
> diff --git a/libgloss/riscv/semihost-sys_close.c
> b/libgloss/riscv/semihost-sys_close.c
> new file mode 100644
> index 000000000..214052185
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_close.c
> @@ -0,0 +1,24 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Close a file.  */
> +int
> +_close (int file)
> +{
> +  long res;
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  res = syscall_errno (SEMIHOST_close, data_block);
> +
> +  if (res != 0)
> +    return res;
> +
> +  __remove_fdentry (file);
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_exit.c
> b/libgloss/riscv/semihost-sys_exit.c
> new file mode 100644
> index 000000000..774ec847e
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_exit.c
> @@ -0,0 +1,19 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +
> +#define ADP_Stopped_ApplicationExit 0x20026
> +
> +/* Exit a program without cleaning up files.  */
> +void
> +_exit (int exit_status)
> +{
> +#if __riscv_xlen == 32
> +  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
> +#else
> +  /* The semihosting exit operation only allows 64-bit targets to report
> the
> +     exit code.  */
> +  long data_block[] = {ADP_Stopped_ApplicationExit, exit_status};
> +  syscall_errno (SEMIHOST_exit, data_block);
> +#endif
> +  while (1);
> +}
> diff --git a/libgloss/riscv/semihost-sys_fdtable.c
> b/libgloss/riscv/semihost-sys_fdtable.c
> new file mode 100644
> index 000000000..c9454224e
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_fdtable.c
> @@ -0,0 +1,81 @@
> +#include "semihost_fdtable.h"
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> +#ifndef RISCV_MAX_OPEN_FILES
> +#define RISCV_MAX_OPEN_FILES 16
> +#endif
> +
> +extern int errno;
> +extern int _open (const char *, int, ...);
> +
> +/* fdtable keeps track of the position of each file and is used to map
> stdin,
> +   stdout and stderr to STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO.  */
> +
> +static struct fdentry fdtable[RISCV_MAX_OPEN_FILES];
> +
> +/* Initialize fdtable.  A handle of -1 denotes an empty entry.  */
> +
> +void __attribute__ ((constructor))
> +init_semihosting ()
> +{
> +  int handle;
> +
> +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
> +    fdtable[i].handle = -1;
> +
> +  /* Set up std streams.  */
> +  /* stdin.  */
> +  handle = _open (":tt", O_RDONLY);
> +  fdtable[STDIN_FILENO].handle = handle;
> +  fdtable[STDIN_FILENO].pos = 0;
> +
> +  /* stdout.  */
> +  handle = _open (":tt", O_WRONLY|O_CREAT|O_TRUNC);
> +  fdtable[STDOUT_FILENO].handle = handle;
> +  fdtable[STDOUT_FILENO].pos = 0;
> +
> +  /* stderr.  */
> +  handle = _open (":tt", O_WRONLY|O_CREAT|O_APPEND);
> +  fdtable[STDERR_FILENO].handle = handle;
> +  fdtable[STDERR_FILENO].pos = 0;
> +}
> +
> +/* Add entry to fdtable.  */
> +
> +int
> +__add_fdentry (int handle)
> +{
> +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
> +    if (fdtable[i].handle == -1)
> +      {
> +       fdtable[i].handle = handle;
> +       fdtable[i].pos = 0;
> +       return i;
> +      }
> +  /* Too many open files.  */
> +  errno = ENFILE;
> +  return -1;
> +}
> +
> +/* Return the fdentry for file or NULL if not found.  */
> +
> +struct fdentry *
> +__get_fdentry (int file)
> +{
> +  if (file<0 || file>RISCV_MAX_OPEN_FILES || fdtable[file].handle == -1)
> +    {
> +      errno = EBADF;
> +      return NULL;
> +    }
> +  return &fdtable[file];
> +}
> +
> +/* Remove entry from fdtable.  */
> +
> +void
> +__remove_fdentry (int file)
> +{
> +  fdtable[file].handle = -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_fstat.c
> b/libgloss/riscv/semihost-sys_fstat.c
> new file mode 100644
> index 000000000..0c5a8d857
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_fstat.c
> @@ -0,0 +1,15 @@
> +#include <string.h>
> +#include <sys/stat.h>
> +#include "semihost_stat.h"
> +
> +/* Status of an open file.  The sys/stat.h header file required is
> +   distributed in the include subdirectory for this C library.  */
> +
> +int
> +_fstat (int file, struct stat *st)
> +{
> +  /* Initialize st as not all fields will be set.  */
> +  memset (st, 0, sizeof (*st));
> +
> +  return __stat_common (file, st);
> +}
> diff --git a/libgloss/riscv/semihost-sys_ftime.c
> b/libgloss/riscv/semihost-sys_ftime.c
> new file mode 100644
> index 000000000..c8d9a9133
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_ftime.c
> @@ -0,0 +1,12 @@
> +#include <machine/syscall.h>
> +#include <sys/timeb.h>
> +#include "semihost_syscall.h"
> +
> +/* Get the current time.  */
> +int
> +_ftime (struct timeb *tp)
> +{
> +  tp->time = syscall_errno (SEMIHOST_time, 0);
> +  tp->millitm = 0;
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_isatty.c
> b/libgloss/riscv/semihost-sys_isatty.c
> new file mode 100644
> index 000000000..ed1376600
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_isatty.c
> @@ -0,0 +1,17 @@
> +#include <machine/syscall.h>
> +#include <sys/stat.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +int
> +_isatty (int file)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  return syscall_errno (SEMIHOST_istty, data_block);
> +}
> diff --git a/libgloss/riscv/semihost-sys_link.c
> b/libgloss/riscv/semihost-sys_link.c
> new file mode 100644
> index 000000000..d65852e7e
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_link.c
> @@ -0,0 +1,5 @@
> +/* Establish a new name for an existing file.  */
> +int _link (const char *old_name, const char *new_name)
> +{
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_lseek.c
> b/libgloss/riscv/semihost-sys_lseek.c
> new file mode 100644
> index 000000000..121769243
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_lseek.c
> @@ -0,0 +1,66 @@
> +#include <machine/syscall.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +extern int errno;
> +
> +/* Set position in a file.  */
> +off_t
> +_lseek (int file, off_t offset, int dir)
> +{
> +  long data_block[2];
> +  long flen;
> +  long res;
> +  struct fdentry *fd;
> +  off_t abs_pos;
> +
> +  fd =__get_fdentry (file);
> +  if (fd == NULL)
> +    {
> +      errno = EBADF;
> +      return -1;
> +    }
> +
> +  if (dir == SEEK_CUR && offset == 0)
> +    return fd->pos;
> +
> +  data_block[0] = fd->handle;
> +
> +  switch (dir)
> +    {
> +      case SEEK_SET:
> +       abs_pos = offset;
> +       break;
> +      case SEEK_CUR:
> +       abs_pos = fd->pos + offset;
> +       break;
> +      case SEEK_END:
> +       data_block[1] = 0;
> +       flen = syscall_errno (SEMIHOST_flen, data_block);
> +       if (flen == -1)
> +         return -1;
> +       abs_pos = flen + offset;
> +       break;
> +      default:
> +       errno = EINVAL;
> +       return -1;
> +    }
> +
> +  if (abs_pos < 0)
> +    {
> +      errno = EINVAL;
> +      return -1;
> +    }
> +
> +  data_block[1] = abs_pos;
> +  res = syscall_errno (SEMIHOST_seek, data_block);
> +  if (res == 0)
> +    {
> +      fd->pos = abs_pos;
> +      return abs_pos;
> +    }
> +  return res;
> +}
> diff --git a/libgloss/riscv/semihost-sys_open.c
> b/libgloss/riscv/semihost-sys_open.c
> new file mode 100644
> index 000000000..9d456a51e
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_open.c
> @@ -0,0 +1,58 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +#include <errno.h>
> +#include <string.h>
> +#include <fcntl.h>
> +
> +extern int errno;
> +
> +#define SEMIHOST_MODE_R 0
> +#define SEMIHOST_MODE_RPLUS 2
> +#define SEMIHOST_MODE_W 4
> +#define SEMIHOST_MODE_WPLUS 6
> +#define SEMIHOST_MODE_A 8
> +#define SEMIHOST_MODE_APLUS 10
> +
> +/* Open a file.  */
> +int
> +_open (const char *name, int flags, ...)
> +{
> +  int fh;
> +  int mode;
> +  long data_block[3];
> +
> +  /* Work out mode from flags.  */
> +  if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
> +    mode = SEMIHOST_MODE_R;
> +  else if ((flags & (O_WRONLY | O_CREAT | O_TRUNC))
> +          == (O_WRONLY | O_CREAT | O_TRUNC))
> +    mode = SEMIHOST_MODE_W;
> +  else if ((flags & (O_WRONLY | O_CREAT | O_APPEND))
> +          == (O_WRONLY | O_CREAT | O_APPEND))
> +    mode = SEMIHOST_MODE_A;
> +  else if ((flags & (O_RDWR | O_CREAT | O_TRUNC))
> +          == (O_RDWR | O_CREAT | O_TRUNC))
> +    mode = SEMIHOST_MODE_WPLUS;
> +  else if ((flags & (O_RDWR | O_CREAT | O_APPEND))
> +          == (O_RDWR | O_CREAT | O_APPEND))
> +    mode = SEMIHOST_MODE_APLUS;
> +  else if (flags & O_RDWR)
> +    mode = SEMIHOST_MODE_RPLUS;
> +  else
> +    {
> +      errno = EINVAL;
> +      return -1;
> +    }
> +
> +  data_block[0] = (long) name;
> +  data_block[1] = mode;
> +  data_block[2] = strlen (name);
> +  fh = syscall_errno (SEMIHOST_open, data_block);
> +  /* Failed to open file.  */
> +  if (fh == -1)
> +    return -1;
> +
> +  /* Register the file in the fdtable.  */
> +  return __add_fdentry (fh);
> +}
> diff --git a/libgloss/riscv/semihost-sys_read.c
> b/libgloss/riscv/semihost-sys_read.c
> new file mode 100644
> index 000000000..b76193301
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_read.c
> @@ -0,0 +1,28 @@
> +#include <machine/syscall.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Read from a file.  */
> +ssize_t _read (int file, void *ptr, size_t len)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[3];
> +  long res;
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  data_block[1] = (long) ptr;
> +  data_block[2] = len;
> +  res = syscall_errno (SEMIHOST_read, data_block);
> +  if (res >= 0)
> +    {
> +      ssize_t bytes_read = len - res;
> +      fd->pos += bytes_read;
> +      return bytes_read;
> +    }
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_sbrk.c
> b/libgloss/riscv/semihost-sys_sbrk.c
> new file mode 100644
> index 000000000..0d7cf3754
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_sbrk.c
> @@ -0,0 +1,22 @@
> +/* Semihosting requires that sbrk be implemented without a syscall.  */
> +extern char _end[];               /* _end is set in the linker command
> file.  */
> +char *heap_ptr;
> +
> +/*
> + * sbrk -- changes heap size size.  Get nbytes more
> + *         RAM.  We just increment a pointer in what's
> + *         left of memory on the board.
> + */
> +char *
> +_sbrk (nbytes)
> +     int nbytes;
> +{
> +  char *base;
> +
> +  if (!heap_ptr)
> +    heap_ptr = (char *)&_end;
> +  base = heap_ptr;
> +  heap_ptr += nbytes;
> +
> +  return base;
> +}
> diff --git a/libgloss/riscv/semihost-sys_stat.c
> b/libgloss/riscv/semihost-sys_stat.c
> new file mode 100644
> index 000000000..09039b6e9
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_stat.c
> @@ -0,0 +1,32 @@
> +#include <machine/syscall.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include "semihost_stat.h"
> +
> +/* Status of a file (by name).  */
> +
> +int
> +_stat (const char *name, struct stat *st)
> +{
> +  int file;
> +  int res;
> +
> +  /* Initialize st as not all fields will be set.  */
> +  memset (st, 0, sizeof (*st));
> +
> +  /* Try to open file.  */
> +  file = _open (name, O_RDONLY);
> +  if (file == -1)
> +    /* _open should have already set errno.  */
> +    return -1;
> +
> +  /* File opened successfully, infer read permission for owner and assume
> it is
> +     a regular file.  */
> +  st->st_mode |= S_IREAD | S_IFREG;
> +
> +  /* Fill in more info.  */
> +  res = __stat_common (file, st);
> +
> +  _close (file);
> +  return res;
> +}
> diff --git a/libgloss/riscv/semihost-sys_stat_common.c
> b/libgloss/riscv/semihost-sys_stat_common.c
> new file mode 100644
> index 000000000..aa7e716c2
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_stat_common.c
> @@ -0,0 +1,32 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include <sys/stat.h>
> +#include "semihost_fdtable.h"
> +
> +/* Used by _fstat and _stat to fill in some common details.  */
> +
> +int
> +__stat_common (int file, struct stat *st)
> +{
> +  int flen;
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +
> +  /* Assume character device and default block size of 4096.  */
> +  st->st_mode |= S_IFCHR;
> +  st->st_blksize = 4096;
> +
> +  /* Attempt to get length of file.  */
> +  flen = syscall_errno (SEMIHOST_flen, data_block);
> +  if (flen == -1)
> +    return -1;
> +
> +  st->st_size = flen;
> +
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_unlink.c
> b/libgloss/riscv/semihost-sys_unlink.c
> new file mode 100644
> index 000000000..dd0d2e9b1
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_unlink.c
> @@ -0,0 +1,11 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include <string.h>
> +
> +/* Remove a file's directory entry.  */
> +int
> +_unlink (const char *name)
> +{
> +  long data_block[] = {(long) name, strlen (name)};
> +  return syscall_errno (SEMIHOST_remove, data_block);
> +}
> diff --git a/libgloss/riscv/semihost-sys_write.c
> b/libgloss/riscv/semihost-sys_write.c
> new file mode 100644
> index 000000000..0a249c2b6
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_write.c
> @@ -0,0 +1,28 @@
> +#include <machine/syscall.h>
> +#include <sys/types.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Write to a file.  */
> +ssize_t
> +_write (int file, const void *ptr, size_t len)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[3];
> +  long res;
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  data_block[1] = (long) ptr;
> +  data_block[2] = len;
> +  res = syscall_errno (SEMIHOST_write, data_block);
> +  if (res >= 0)
> +    {
> +      ssize_t bytes_written = len - res;
> +      fd->pos += bytes_written;
> +      return bytes_written;
> +    }
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost.specs b/libgloss/riscv/semihost.specs
> new file mode 100644
> index 000000000..1c86c67e4
> --- /dev/null
> +++ b/libgloss/riscv/semihost.specs
> @@ -0,0 +1,10 @@
> +# Spec file for semihosting syscalls.
> +
> +%rename lib    semihost_lib
> +%rename link   semihost_link
> +
> +*lib:
> +--start-group -lc -lsemihost --end-group
> +
> +*link:
> +%(semihost_link) %:replace-outfile(-lgloss -lsemihost)
> diff --git a/libgloss/riscv/semihost_fdtable.h
> b/libgloss/riscv/semihost_fdtable.h
> new file mode 100644
> index 000000000..0fa62dd2d
> --- /dev/null
> +++ b/libgloss/riscv/semihost_fdtable.h
> @@ -0,0 +1,17 @@
> +#include <sys/types.h>
> +
> +#ifndef RISCV_SEMIHOST_FDTABLE_H
> +#define RISCV_SEMIHOST_FDTABLE_H
> +
> +extern void __attribute__ ((constructor)) init_semihosting ();
> +extern int __add_fdentry (int);
> +extern struct fdentry * __get_fdentry (int);
> +extern void __remove_fdentry (int);
> +
> +struct fdentry
> +{
> +  int handle;
> +  off_t pos;
> +};
> +
> +#endif
> diff --git a/libgloss/riscv/semihost_stat.h
> b/libgloss/riscv/semihost_stat.h
> new file mode 100644
> index 000000000..2030c7ab2
> --- /dev/null
> +++ b/libgloss/riscv/semihost_stat.h
> @@ -0,0 +1,10 @@
> +#include <sys/types.h>
> +
> +#ifndef RISCV_SEMIHOST_STAT_H
> +#define RISCV_SEMIHOST_STAT_H
> +
> +extern int __stat_common (int, struct stat *);
> +extern int _open (const char *, int, ...);
> +extern int _close (int);
> +
> +#endif
> diff --git a/libgloss/riscv/semihost_syscall.h
> b/libgloss/riscv/semihost_syscall.h
> new file mode 100644
> index 000000000..c8ae9ffaf
> --- /dev/null
> +++ b/libgloss/riscv/semihost_syscall.h
> @@ -0,0 +1,43 @@
> +#ifndef _INTERNAL_SYSCALL_H
> +#define _INTERNAL_SYSCALL_H
> +
> +extern int errno;
> +
> +static inline long
> +__semihost_syscall (long id, long *data_block)
> +{
> +  register long a0 asm ("a0") = id;
> +  register long a1 asm ("a1") = (long) data_block;
> +
> +  /* RISC-V semihosting trap sequence.  Must be uncompressed and must not
> +     cross page boundary.  */
> +  asm volatile (
> +    ".balign 16             \n"
> +    ".option push           \n"
> +    ".option norvc          \n"
> +    "slli zero, zero, 0x1f  \n"
> +    "ebreak                 \n"
> +    "srai zero, zero, 0x7   \n"
> +    ".option pop            \n"
> +      : "+r"(a0) : "r"(a1) : "memory");
> +
> +  return a0;
> +}
> +
> +static inline long
> +__syscall_error ()
> +{
> +  errno = __semihost_syscall (SEMIHOST_errno, 0);
> +  return -1;
> +}
> +
> +static inline long
> +syscall_errno (long id, long *data_block)
> +{
> +  long res = __semihost_syscall (id, data_block);
> +  if (res < 0)
> +    return __syscall_error ();
> +  return res;
> +}
> +
> +#endif
> --
> 2.17.1
>
>
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] RISC-V: Add semihosting support
  2020-11-30 11:36 [PATCH] RISC-V: Add semihosting support Craig Blackmore
  2020-11-30 14:05 ` Kito Cheng
  2020-11-30 19:53 ` Jeff Johnston
@ 2020-12-09  8:21 ` Kito Cheng
  2020-12-15 12:00   ` Craig Blackmore
  2 siblings, 1 reply; 9+ messages in thread
From: Kito Cheng @ 2020-12-09  8:21 UTC (permalink / raw)
  To: Craig Blackmore; +Cc: Newlib

Hi Craig:

I verified with GCC testsuite on qemu with Keith's semihosting patch,
And that's LGTM, only two minor review comment, see below:

> +++ b/libgloss/riscv/semihost-sys_exit.c
> @@ -0,0 +1,19 @@
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +
> +#define ADP_Stopped_ApplicationExit 0x20026
> +
> +/* Exit a program without cleaning up files.  */
> +void
> +_exit (int exit_status)
> +{
> +#if __riscv_xlen == 32
> +  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);

Could you use SEMIHOST_exit_extended here, so that we could have
return value on rv32.

> +/* Return the fdentry for file or NULL if not found.  */
> +
> +struct fdentry *
> +__get_fdentry (int file)
> +{
> +  if (file<0 || file>RISCV_MAX_OPEN_FILES || fdtable[file].handle == -1)

Should be >= RISCV_MAX_OPEN_FILES here?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] RISC-V: Add semihosting support
  2020-12-09  8:21 ` Kito Cheng
@ 2020-12-15 12:00   ` Craig Blackmore
  2020-12-15 22:51     ` Jeff Johnston
  0 siblings, 1 reply; 9+ messages in thread
From: Craig Blackmore @ 2020-12-15 12:00 UTC (permalink / raw)
  To: Kito Cheng; +Cc: Newlib

Hi Kito,

Thanks for the test and review.

On 09/12/2020 08:21, Kito Cheng wrote:

> Hi Craig:
>
> I verified with GCC testsuite on qemu with Keith's semihosting patch,
> And that's LGTM, only two minor review comment, see below:
>
>> +++ b/libgloss/riscv/semihost-sys_exit.c
>> @@ -0,0 +1,19 @@
>> +#include <machine/syscall.h>
>> +#include "semihost_syscall.h"
>> +
>> +#define ADP_Stopped_ApplicationExit 0x20026
>> +
>> +/* Exit a program without cleaning up files.  */
>> +void
>> +_exit (int exit_status)
>> +{
>> +#if __riscv_xlen == 32
>> +  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
> Could you use SEMIHOST_exit_extended here, so that we could have
> return value on rv32.

I can use SEMIHOST_exit_extended, but I will also need to add a check for the
SH_EXT_EXIT_EXTENDED feature. Can I follow up with this in a later patch?

>> +/* Return the fdentry for file or NULL if not found.  */
>> +
>> +struct fdentry *
>> +__get_fdentry (int file)
>> +{
>> +  if (file<0 || file>RISCV_MAX_OPEN_FILES || fdtable[file].handle == -1)
> Should be >= RISCV_MAX_OPEN_FILES here?

Please see updated patch below which fixes this condition and adds a license as
requested by Jeff Johnston.

Best wishes,
Craig

---
 libgloss/riscv/Makefile.in                | 66 +++++++++++++++++-
 libgloss/riscv/machine/syscall.h          | 25 +++++++
 libgloss/riscv/semihost-sys_close.c       | 28 ++++++++
 libgloss/riscv/semihost-sys_exit.c        | 23 ++++++
 libgloss/riscv/semihost-sys_fdtable.c     | 85 +++++++++++++++++++++++
 libgloss/riscv/semihost-sys_fstat.c       | 19 +++++
 libgloss/riscv/semihost-sys_ftime.c       | 16 +++++
 libgloss/riscv/semihost-sys_isatty.c      | 21 ++++++
 libgloss/riscv/semihost-sys_link.c        |  9 +++
 libgloss/riscv/semihost-sys_lseek.c       | 70 +++++++++++++++++++
 libgloss/riscv/semihost-sys_open.c        | 62 +++++++++++++++++
 libgloss/riscv/semihost-sys_read.c        | 32 +++++++++
 libgloss/riscv/semihost-sys_sbrk.c        | 26 +++++++
 libgloss/riscv/semihost-sys_stat.c        | 36 ++++++++++
 libgloss/riscv/semihost-sys_stat_common.c | 36 ++++++++++
 libgloss/riscv/semihost-sys_unlink.c      | 15 ++++
 libgloss/riscv/semihost-sys_write.c       | 32 +++++++++
 libgloss/riscv/semihost.specs             | 10 +++
 libgloss/riscv/semihost_fdtable.h         | 21 ++++++
 libgloss/riscv/semihost_stat.h            | 14 ++++
 libgloss/riscv/semihost_syscall.h         | 47 +++++++++++++
 21 files changed, 692 insertions(+), 1 deletion(-)
 create mode 100644 libgloss/riscv/semihost-sys_close.c
 create mode 100644 libgloss/riscv/semihost-sys_exit.c
 create mode 100644 libgloss/riscv/semihost-sys_fdtable.c
 create mode 100644 libgloss/riscv/semihost-sys_fstat.c
 create mode 100644 libgloss/riscv/semihost-sys_ftime.c
 create mode 100644 libgloss/riscv/semihost-sys_isatty.c
 create mode 100644 libgloss/riscv/semihost-sys_link.c
 create mode 100644 libgloss/riscv/semihost-sys_lseek.c
 create mode 100644 libgloss/riscv/semihost-sys_open.c
 create mode 100644 libgloss/riscv/semihost-sys_read.c
 create mode 100644 libgloss/riscv/semihost-sys_sbrk.c
 create mode 100644 libgloss/riscv/semihost-sys_stat.c
 create mode 100644 libgloss/riscv/semihost-sys_stat_common.c
 create mode 100644 libgloss/riscv/semihost-sys_unlink.c
 create mode 100644 libgloss/riscv/semihost-sys_write.c
 create mode 100644 libgloss/riscv/semihost.specs
 create mode 100644 libgloss/riscv/semihost_fdtable.h
 create mode 100644 libgloss/riscv/semihost_stat.h
 create mode 100644 libgloss/riscv/semihost_syscall.h

diff --git a/libgloss/riscv/Makefile.in b/libgloss/riscv/Makefile.in
index 579dd9554..185b6e6d2 100644
--- a/libgloss/riscv/Makefile.in
+++ b/libgloss/riscv/Makefile.in
@@ -40,8 +40,38 @@ gloss_srcs = \
 	sys_wait.c \
 	sys_write.c 
 
+# libsemihost reuses some of the libgloss minimal implementations
+
+semihost_srcs = \
+	nanosleep.c \
+	sys_chdir.c \
+	sys_chmod.c \
+	sys_chown.c \
+	sys_execve.c \
+	sys_fork.c \
+	sys_getcwd.c \
+	sys_getpid.c \
+	sys_kill.c \
+	sys_utime.c \
+	sys_wait.c \
+	semihost-sys_close.c \
+	semihost-sys_exit.c \
+	semihost-sys_fdtable.c \
+	semihost-sys_fstat.c \
+	semihost-sys_ftime.c \
+	semihost-sys_isatty.c \
+	semihost-sys_link.c \
+	semihost-sys_lseek.c \
+	semihost-sys_open.c \
+	semihost-sys_read.c \
+	semihost-sys_sbrk.c \
+	semihost-sys_stat.c \
+	semihost-sys_stat_common.c \
+	semihost-sys_unlink.c \
+	semihost-sys_write.c
+
 gloss_specs = \
-	nano.specs sim.specs
+	nano.specs sim.specs semihost.specs
 
 # Extra files
 
@@ -134,6 +164,17 @@ sim_objs += $(sim_c_objs)
 deps += $(sim_c_deps)
 junk += $(sim_c_deps) $(sim_c_objs)
 
+semihost_c_srcs = $(filter %.c, $(semihost_srcs))
+semihost_c_objs = $(patsubst %.c, semihost-%.o, $(notdir $(semihost_c_srcs)))
+semihost_c_deps = $(patsubst %.c, semihost-%.d, $(notdir $(semihost_c_srcs)))
+
+$(semihost_c_objs): semihost-%.o : %.c
+	$(COMPILE) -c -o $@ $<
+
+semihost_objs += $(semihost_c_objs)
+deps += $(semihost_c_deps)
+junk += $(semihost_c_deps) $(semihost_c_objs)
+
 #-------------------------------------------------------------------------
 # Build Object Files from Assembly Source
 #-------------------------------------------------------------------------
@@ -159,6 +200,16 @@ sim_objs += $(sim_asm_objs)
 deps += $(sim_asm_deps)
 junk += $(sim_asm_deps) $(sim_asm_objs)
 
+semihost_asm_objs = $(patsubst %.S, semihost-%.o, $(notdir $(gloss_asm_srcs)))
+semihost_asm_deps = $(patsubst %.S, semihost-%.d, $(notdir $(gloss_asm_srcs)))
+
+$(semihost_asm_objs) : semihost-%.o : %.S
+	$(COMPILE) -c -DUSING_SEMIHOST_SPECS -o $@ $<
+
+semihost_objs += $(semihost_asm_objs)
+deps += $(semihost_asm_deps)
+junk += $(semihost_asm_deps) $(semihost_asm_objs)
+
 #-------------------------------------------------------------------------
 # Build libgloss.a
 #-------------------------------------------------------------------------
@@ -187,6 +238,19 @@ junk += $(sim_lib)
 
 install_libs += $(sim_lib)
 
+#-------------------------------------------------------------------------
+# Build libsemihost.a
+#-------------------------------------------------------------------------
+
+semihost_lib  = libsemihost.a
+$(semihost_lib) : $(semihost_objs)
+	$(AR) rcv $@ $^
+	$(RANLIB) $@
+
+junk += $(semihost_lib)
+
+install_libs += $(semihost_lib)
+
 #-------------------------------------------------------------------------
 # Build crt0.o
 #-------------------------------------------------------------------------
diff --git a/libgloss/riscv/machine/syscall.h b/libgloss/riscv/machine/syscall.h
index 5cd15b848..88b9fdfda 100644
--- a/libgloss/riscv/machine/syscall.h
+++ b/libgloss/riscv/machine/syscall.h
@@ -54,4 +54,29 @@
 #define SYS_time 1062
 #define SYS_getmainvars 2011
 
+/* Semihosting operations.  */
+#define SEMIHOST_clock 0x10
+#define SEMIHOST_close 0x02
+#define SEMIHOST_elapsed 0x30
+#define SEMIHOST_errno 0x13
+#define SEMIHOST_exit 0x18
+#define SEMIHOST_exit_extended 0x20
+#define SEMIHOST_flen 0x0C
+#define SEMIHOST_get_cmdline 0x15
+#define SEMIHOST_heapinfo 0x16
+#define SEMIHOST_iserror 0x08
+#define SEMIHOST_istty 0x09
+#define SEMIHOST_open 0x01
+#define SEMIHOST_read 0x06
+#define SEMIHOST_readc 0x07
+#define SEMIHOST_remove 0x0E
+#define SEMIHOST_rename 0x0F
+#define SEMIHOST_seek 0x0A
+#define SEMIHOST_system 0x12
+#define SEMIHOST_tickfreq 0x31
+#define SEMIHOST_time 0x11
+#define SEMIHOST_tmpnam 0x0D
+#define SEMIHOST_write 0x05
+#define SEMIHOST_writec 0x03
+#define SEMIHOST_write0 0x04
 #endif
diff --git a/libgloss/riscv/semihost-sys_close.c b/libgloss/riscv/semihost-sys_close.c
new file mode 100644
index 000000000..47402340c
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_close.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+/* Close a file.  */
+int
+_close (int file)
+{
+  long res;
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[1];
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+  res = syscall_errno (SEMIHOST_close, data_block);
+
+  if (res != 0)
+    return res;
+
+  __remove_fdentry (file);
+  return 0;
+}
diff --git a/libgloss/riscv/semihost-sys_exit.c b/libgloss/riscv/semihost-sys_exit.c
new file mode 100644
index 000000000..626fb6aeb
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_exit.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+
+#define ADP_Stopped_ApplicationExit 0x20026
+
+/* Exit a program without cleaning up files.  */
+void
+_exit (int exit_status)
+{
+#if __riscv_xlen == 32
+  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
+#else
+  /* The semihosting exit operation only allows 64-bit targets to report the
+     exit code.  */
+  long data_block[] = {ADP_Stopped_ApplicationExit, exit_status};
+  syscall_errno (SEMIHOST_exit, data_block);
+#endif
+  while (1);
+}
diff --git a/libgloss/riscv/semihost-sys_fdtable.c b/libgloss/riscv/semihost-sys_fdtable.c
new file mode 100644
index 000000000..152c92d15
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_fdtable.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include "semihost_fdtable.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifndef RISCV_MAX_OPEN_FILES
+#define RISCV_MAX_OPEN_FILES 16
+#endif
+
+extern int errno;
+extern int _open (const char *, int, ...);
+
+/* fdtable keeps track of the position of each file and is used to map stdin,
+   stdout and stderr to STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO.  */
+
+static struct fdentry fdtable[RISCV_MAX_OPEN_FILES];
+
+/* Initialize fdtable.  A handle of -1 denotes an empty entry.  */
+
+void __attribute__ ((constructor))
+init_semihosting ()
+{
+  int handle;
+
+  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
+    fdtable[i].handle = -1;
+
+  /* Set up std streams.  */
+  /* stdin.  */
+  handle = _open (":tt", O_RDONLY);
+  fdtable[STDIN_FILENO].handle = handle;
+  fdtable[STDIN_FILENO].pos = 0;
+
+  /* stdout.  */
+  handle = _open (":tt", O_WRONLY|O_CREAT|O_TRUNC);
+  fdtable[STDOUT_FILENO].handle = handle;
+  fdtable[STDOUT_FILENO].pos = 0;
+
+  /* stderr.  */
+  handle = _open (":tt", O_WRONLY|O_CREAT|O_APPEND);
+  fdtable[STDERR_FILENO].handle = handle;
+  fdtable[STDERR_FILENO].pos = 0;
+}
+
+/* Add entry to fdtable.  */
+
+int
+__add_fdentry (int handle)
+{
+  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
+    if (fdtable[i].handle == -1)
+      {
+	fdtable[i].handle = handle;
+	fdtable[i].pos = 0;
+	return i;
+      }
+  /* Too many open files.  */
+  errno = ENFILE;
+  return -1;
+}
+
+/* Return the fdentry for file or NULL if not found.  */
+
+struct fdentry *
+__get_fdentry (int file)
+{
+  if (file<0 || file>=RISCV_MAX_OPEN_FILES || fdtable[file].handle == -1)
+    {
+      errno = EBADF;
+      return NULL;
+    }
+  return &fdtable[file];
+}
+
+/* Remove entry from fdtable.  */
+
+void
+__remove_fdentry (int file)
+{
+  fdtable[file].handle = -1;
+}
diff --git a/libgloss/riscv/semihost-sys_fstat.c b/libgloss/riscv/semihost-sys_fstat.c
new file mode 100644
index 000000000..f57f0c07f
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_fstat.c
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <string.h>
+#include <sys/stat.h>
+#include "semihost_stat.h"
+
+/* Status of an open file.  The sys/stat.h header file required is
+   distributed in the include subdirectory for this C library.  */
+
+int
+_fstat (int file, struct stat *st)
+{
+  /* Initialize st as not all fields will be set.  */
+  memset (st, 0, sizeof (*st));
+
+  return __stat_common (file, st);
+}
diff --git a/libgloss/riscv/semihost-sys_ftime.c b/libgloss/riscv/semihost-sys_ftime.c
new file mode 100644
index 000000000..aeafc6ca2
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_ftime.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include <sys/timeb.h>
+#include "semihost_syscall.h"
+
+/* Get the current time.  */
+int
+_ftime (struct timeb *tp)
+{
+  tp->time = syscall_errno (SEMIHOST_time, 0);
+  tp->millitm = 0;
+  return 0;
+}
diff --git a/libgloss/riscv/semihost-sys_isatty.c b/libgloss/riscv/semihost-sys_isatty.c
new file mode 100644
index 000000000..02d8e39cb
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_isatty.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include <sys/stat.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+int
+_isatty (int file)
+{
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[1];
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+  return syscall_errno (SEMIHOST_istty, data_block);
+}
diff --git a/libgloss/riscv/semihost-sys_link.c b/libgloss/riscv/semihost-sys_link.c
new file mode 100644
index 000000000..717c5c81c
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_link.c
@@ -0,0 +1,9 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+/* Establish a new name for an existing file.  */
+int _link (const char *old_name, const char *new_name)
+{
+  return -1;
+}
diff --git a/libgloss/riscv/semihost-sys_lseek.c b/libgloss/riscv/semihost-sys_lseek.c
new file mode 100644
index 000000000..68fccf2ff
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_lseek.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+extern int errno;
+
+/* Set position in a file.  */
+off_t
+_lseek (int file, off_t offset, int dir)
+{
+  long data_block[2];
+  long flen;
+  long res;
+  struct fdentry *fd;
+  off_t abs_pos;
+
+  fd =__get_fdentry (file);
+  if (fd == NULL)
+    {
+      errno = EBADF;
+      return -1;
+    }
+
+  if (dir == SEEK_CUR && offset == 0)
+    return fd->pos;
+
+  data_block[0] = fd->handle;
+
+  switch (dir)
+    {
+      case SEEK_SET:
+	abs_pos = offset;
+	break;
+      case SEEK_CUR:
+	abs_pos = fd->pos + offset;
+	break;
+      case SEEK_END:
+	data_block[1] = 0;
+	flen = syscall_errno (SEMIHOST_flen, data_block);
+	if (flen == -1)
+	  return -1;
+	abs_pos = flen + offset;
+	break;
+      default:
+	errno = EINVAL;
+	return -1;
+    }
+
+  if (abs_pos < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  data_block[1] = abs_pos;
+  res = syscall_errno (SEMIHOST_seek, data_block);
+  if (res == 0)
+    {
+      fd->pos = abs_pos;
+      return abs_pos;
+    }
+  return res;
+}
diff --git a/libgloss/riscv/semihost-sys_open.c b/libgloss/riscv/semihost-sys_open.c
new file mode 100644
index 000000000..22f1d8206
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_open.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+extern int errno;
+
+#define SEMIHOST_MODE_R 0
+#define SEMIHOST_MODE_RPLUS 2
+#define SEMIHOST_MODE_W 4
+#define SEMIHOST_MODE_WPLUS 6
+#define SEMIHOST_MODE_A 8
+#define SEMIHOST_MODE_APLUS 10
+
+/* Open a file.  */
+int
+_open (const char *name, int flags, ...)
+{
+  int fh;
+  int mode;
+  long data_block[3];
+
+  /* Work out mode from flags.  */
+  if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
+    mode = SEMIHOST_MODE_R;
+  else if ((flags & (O_WRONLY | O_CREAT | O_TRUNC))
+	   == (O_WRONLY | O_CREAT | O_TRUNC))
+    mode = SEMIHOST_MODE_W;
+  else if ((flags & (O_WRONLY | O_CREAT | O_APPEND))
+	   == (O_WRONLY | O_CREAT | O_APPEND))
+    mode = SEMIHOST_MODE_A;
+  else if ((flags & (O_RDWR | O_CREAT | O_TRUNC))
+	   == (O_RDWR | O_CREAT | O_TRUNC))
+    mode = SEMIHOST_MODE_WPLUS;
+  else if ((flags & (O_RDWR | O_CREAT | O_APPEND))
+	   == (O_RDWR | O_CREAT | O_APPEND))
+    mode = SEMIHOST_MODE_APLUS;
+  else if (flags & O_RDWR)
+    mode = SEMIHOST_MODE_RPLUS;
+  else
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  data_block[0] = (long) name;
+  data_block[1] = mode;
+  data_block[2] = strlen (name);
+  fh = syscall_errno (SEMIHOST_open, data_block);
+  /* Failed to open file.  */
+  if (fh == -1)
+    return -1;
+
+  /* Register the file in the fdtable.  */
+  return __add_fdentry (fh);
+}
diff --git a/libgloss/riscv/semihost-sys_read.c b/libgloss/riscv/semihost-sys_read.c
new file mode 100644
index 000000000..3164eed56
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_read.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include <errno.h>
+#include <sys/types.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+/* Read from a file.  */
+ssize_t _read (int file, void *ptr, size_t len)
+{
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[3];
+  long res;
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+  data_block[1] = (long) ptr;
+  data_block[2] = len;
+  res = syscall_errno (SEMIHOST_read, data_block);
+  if (res >= 0)
+    {
+      ssize_t bytes_read = len - res;
+      fd->pos += bytes_read;
+      return bytes_read;
+    }
+  return -1;
+}
diff --git a/libgloss/riscv/semihost-sys_sbrk.c b/libgloss/riscv/semihost-sys_sbrk.c
new file mode 100644
index 000000000..cbd035832
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_sbrk.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+/* Semihosting requires that sbrk be implemented without a syscall.  */
+extern char _end[];               /* _end is set in the linker command file.  */
+char *heap_ptr;
+
+/*
+ * sbrk -- changes heap size size.  Get nbytes more
+ *         RAM.  We just increment a pointer in what's
+ *         left of memory on the board.
+ */
+char *
+_sbrk (nbytes)
+     int nbytes;
+{
+  char *base;
+
+  if (!heap_ptr)
+    heap_ptr = (char *)&_end;
+  base = heap_ptr;
+  heap_ptr += nbytes;
+
+  return base;
+}
diff --git a/libgloss/riscv/semihost-sys_stat.c b/libgloss/riscv/semihost-sys_stat.c
new file mode 100644
index 000000000..4015801b9
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_stat.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include <string.h>
+#include <fcntl.h>
+#include "semihost_stat.h"
+
+/* Status of a file (by name).  */
+
+int
+_stat (const char *name, struct stat *st)
+{
+  int file;
+  int res;
+
+  /* Initialize st as not all fields will be set.  */
+  memset (st, 0, sizeof (*st));
+
+  /* Try to open file.  */
+  file = _open (name, O_RDONLY);
+  if (file == -1)
+    /* _open should have already set errno.  */
+    return -1;
+
+  /* File opened successfully, infer read permission for owner and assume it is
+     a regular file.  */
+  st->st_mode |= S_IREAD | S_IFREG;
+
+  /* Fill in more info.  */
+  res = __stat_common (file, st);
+
+  _close (file);
+  return res;
+}
diff --git a/libgloss/riscv/semihost-sys_stat_common.c b/libgloss/riscv/semihost-sys_stat_common.c
new file mode 100644
index 000000000..b38eb0863
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_stat_common.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+#include <sys/stat.h>
+#include "semihost_fdtable.h"
+
+/* Used by _fstat and _stat to fill in some common details.  */
+
+int
+__stat_common (int file, struct stat *st)
+{
+  int flen;
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[1];
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+
+  /* Assume character device and default block size of 4096.  */
+  st->st_mode |= S_IFCHR;
+  st->st_blksize = 4096;
+
+  /* Attempt to get length of file.  */
+  flen = syscall_errno (SEMIHOST_flen, data_block);
+  if (flen == -1)
+    return -1;
+
+  st->st_size = flen;
+
+  return 0;
+}
diff --git a/libgloss/riscv/semihost-sys_unlink.c b/libgloss/riscv/semihost-sys_unlink.c
new file mode 100644
index 000000000..1d2a6a0f9
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_unlink.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include "semihost_syscall.h"
+#include <string.h>
+
+/* Remove a file's directory entry.  */
+int
+_unlink (const char *name)
+{
+  long data_block[] = {(long) name, strlen (name)};
+  return syscall_errno (SEMIHOST_remove, data_block);
+}
diff --git a/libgloss/riscv/semihost-sys_write.c b/libgloss/riscv/semihost-sys_write.c
new file mode 100644
index 000000000..9aee6d30b
--- /dev/null
+++ b/libgloss/riscv/semihost-sys_write.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <machine/syscall.h>
+#include <sys/types.h>
+#include "semihost_syscall.h"
+#include "semihost_fdtable.h"
+
+/* Write to a file.  */
+ssize_t
+_write (int file, const void *ptr, size_t len)
+{
+  struct fdentry *fd =__get_fdentry (file);
+  long data_block[3];
+  long res;
+
+  if (fd == NULL)
+    return -1;
+
+  data_block[0] = fd->handle;
+  data_block[1] = (long) ptr;
+  data_block[2] = len;
+  res = syscall_errno (SEMIHOST_write, data_block);
+  if (res >= 0)
+    {
+      ssize_t bytes_written = len - res;
+      fd->pos += bytes_written;
+      return bytes_written;
+    }
+  return -1;
+}
diff --git a/libgloss/riscv/semihost.specs b/libgloss/riscv/semihost.specs
new file mode 100644
index 000000000..1c86c67e4
--- /dev/null
+++ b/libgloss/riscv/semihost.specs
@@ -0,0 +1,10 @@
+# Spec file for semihosting syscalls.
+
+%rename lib	semihost_lib
+%rename link	semihost_link
+
+*lib:
+--start-group -lc -lsemihost --end-group
+
+*link:
+%(semihost_link) %:replace-outfile(-lgloss -lsemihost)
diff --git a/libgloss/riscv/semihost_fdtable.h b/libgloss/riscv/semihost_fdtable.h
new file mode 100644
index 000000000..f596a409a
--- /dev/null
+++ b/libgloss/riscv/semihost_fdtable.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <sys/types.h>
+
+#ifndef RISCV_SEMIHOST_FDTABLE_H
+#define RISCV_SEMIHOST_FDTABLE_H
+
+extern void __attribute__ ((constructor)) init_semihosting ();
+extern int __add_fdentry (int);
+extern struct fdentry * __get_fdentry (int);
+extern void __remove_fdentry (int);
+
+struct fdentry
+{
+  int handle;
+  off_t pos;
+};
+
+#endif
diff --git a/libgloss/riscv/semihost_stat.h b/libgloss/riscv/semihost_stat.h
new file mode 100644
index 000000000..c040fe8e7
--- /dev/null
+++ b/libgloss/riscv/semihost_stat.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <sys/types.h>
+
+#ifndef RISCV_SEMIHOST_STAT_H
+#define RISCV_SEMIHOST_STAT_H
+
+extern int __stat_common (int, struct stat *);
+extern int _open (const char *, int, ...);
+extern int _close (int);
+
+#endif
diff --git a/libgloss/riscv/semihost_syscall.h b/libgloss/riscv/semihost_syscall.h
new file mode 100644
index 000000000..50e731b40
--- /dev/null
+++ b/libgloss/riscv/semihost_syscall.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 Embecosm Limited
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#ifndef _INTERNAL_SYSCALL_H
+#define _INTERNAL_SYSCALL_H
+
+extern int errno;
+
+static inline long
+__semihost_syscall (long id, long *data_block)
+{
+  register long a0 asm ("a0") = id;
+  register long a1 asm ("a1") = (long) data_block;
+
+  /* RISC-V semihosting trap sequence.  Must be uncompressed and must not
+     cross page boundary.  */
+  asm volatile (
+    ".balign 16             \n"
+    ".option push           \n"
+    ".option norvc          \n"
+    "slli zero, zero, 0x1f  \n"
+    "ebreak                 \n"
+    "srai zero, zero, 0x7   \n"
+    ".option pop            \n"
+      : "+r"(a0) : "r"(a1) : "memory");
+
+  return a0;
+}
+
+static inline long
+__syscall_error ()
+{
+  errno = __semihost_syscall (SEMIHOST_errno, 0);
+  return -1;
+}
+
+static inline long
+syscall_errno (long id, long *data_block)
+{
+  long res = __semihost_syscall (id, data_block);
+  if (res < 0)
+    return __syscall_error ();
+  return res;
+}
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] RISC-V: Add semihosting support
  2020-12-15 12:00   ` Craig Blackmore
@ 2020-12-15 22:51     ` Jeff Johnston
  2020-12-16  8:33       ` Craig Blackmore
  0 siblings, 1 reply; 9+ messages in thread
From: Jeff Johnston @ 2020-12-15 22:51 UTC (permalink / raw)
  To: Craig Blackmore; +Cc: Newlib

Hi Craig,

Can you confirm that you have permission from your employer (embecosm) to
contribute the code?  Other than that, I think we're good to merge before
the snapshot.

Thanks,

-- Jeff J.

On Tue, Dec 15, 2020 at 7:00 AM Craig Blackmore <
craig.blackmore@embecosm.com> wrote:

> Hi Kito,
>
> Thanks for the test and review.
>
> On 09/12/2020 08:21, Kito Cheng wrote:
>
> > Hi Craig:
> >
> > I verified with GCC testsuite on qemu with Keith's semihosting patch,
> > And that's LGTM, only two minor review comment, see below:
> >
> >> +++ b/libgloss/riscv/semihost-sys_exit.c
> >> @@ -0,0 +1,19 @@
> >> +#include <machine/syscall.h>
> >> +#include "semihost_syscall.h"
> >> +
> >> +#define ADP_Stopped_ApplicationExit 0x20026
> >> +
> >> +/* Exit a program without cleaning up files.  */
> >> +void
> >> +_exit (int exit_status)
> >> +{
> >> +#if __riscv_xlen == 32
> >> +  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
> > Could you use SEMIHOST_exit_extended here, so that we could have
> > return value on rv32.
>
> I can use SEMIHOST_exit_extended, but I will also need to add a check for
> the
> SH_EXT_EXIT_EXTENDED feature. Can I follow up with this in a later patch?
>
> >> +/* Return the fdentry for file or NULL if not found.  */
> >> +
> >> +struct fdentry *
> >> +__get_fdentry (int file)
> >> +{
> >> +  if (file<0 || file>RISCV_MAX_OPEN_FILES || fdtable[file].handle ==
> -1)
> > Should be >= RISCV_MAX_OPEN_FILES here?
>
> Please see updated patch below which fixes this condition and adds a
> license as
> requested by Jeff Johnston.
>
> Best wishes,
> Craig
>
> ---
>  libgloss/riscv/Makefile.in                | 66 +++++++++++++++++-
>  libgloss/riscv/machine/syscall.h          | 25 +++++++
>  libgloss/riscv/semihost-sys_close.c       | 28 ++++++++
>  libgloss/riscv/semihost-sys_exit.c        | 23 ++++++
>  libgloss/riscv/semihost-sys_fdtable.c     | 85 +++++++++++++++++++++++
>  libgloss/riscv/semihost-sys_fstat.c       | 19 +++++
>  libgloss/riscv/semihost-sys_ftime.c       | 16 +++++
>  libgloss/riscv/semihost-sys_isatty.c      | 21 ++++++
>  libgloss/riscv/semihost-sys_link.c        |  9 +++
>  libgloss/riscv/semihost-sys_lseek.c       | 70 +++++++++++++++++++
>  libgloss/riscv/semihost-sys_open.c        | 62 +++++++++++++++++
>  libgloss/riscv/semihost-sys_read.c        | 32 +++++++++
>  libgloss/riscv/semihost-sys_sbrk.c        | 26 +++++++
>  libgloss/riscv/semihost-sys_stat.c        | 36 ++++++++++
>  libgloss/riscv/semihost-sys_stat_common.c | 36 ++++++++++
>  libgloss/riscv/semihost-sys_unlink.c      | 15 ++++
>  libgloss/riscv/semihost-sys_write.c       | 32 +++++++++
>  libgloss/riscv/semihost.specs             | 10 +++
>  libgloss/riscv/semihost_fdtable.h         | 21 ++++++
>  libgloss/riscv/semihost_stat.h            | 14 ++++
>  libgloss/riscv/semihost_syscall.h         | 47 +++++++++++++
>  21 files changed, 692 insertions(+), 1 deletion(-)
>  create mode 100644 libgloss/riscv/semihost-sys_close.c
>  create mode 100644 libgloss/riscv/semihost-sys_exit.c
>  create mode 100644 libgloss/riscv/semihost-sys_fdtable.c
>  create mode 100644 libgloss/riscv/semihost-sys_fstat.c
>  create mode 100644 libgloss/riscv/semihost-sys_ftime.c
>  create mode 100644 libgloss/riscv/semihost-sys_isatty.c
>  create mode 100644 libgloss/riscv/semihost-sys_link.c
>  create mode 100644 libgloss/riscv/semihost-sys_lseek.c
>  create mode 100644 libgloss/riscv/semihost-sys_open.c
>  create mode 100644 libgloss/riscv/semihost-sys_read.c
>  create mode 100644 libgloss/riscv/semihost-sys_sbrk.c
>  create mode 100644 libgloss/riscv/semihost-sys_stat.c
>  create mode 100644 libgloss/riscv/semihost-sys_stat_common.c
>  create mode 100644 libgloss/riscv/semihost-sys_unlink.c
>  create mode 100644 libgloss/riscv/semihost-sys_write.c
>  create mode 100644 libgloss/riscv/semihost.specs
>  create mode 100644 libgloss/riscv/semihost_fdtable.h
>  create mode 100644 libgloss/riscv/semihost_stat.h
>  create mode 100644 libgloss/riscv/semihost_syscall.h
>
> diff --git a/libgloss/riscv/Makefile.in b/libgloss/riscv/Makefile.in
> index 579dd9554..185b6e6d2 100644
> --- a/libgloss/riscv/Makefile.in
> +++ b/libgloss/riscv/Makefile.in
> @@ -40,8 +40,38 @@ gloss_srcs = \
>         sys_wait.c \
>         sys_write.c
>
> +# libsemihost reuses some of the libgloss minimal implementations
> +
> +semihost_srcs = \
> +       nanosleep.c \
> +       sys_chdir.c \
> +       sys_chmod.c \
> +       sys_chown.c \
> +       sys_execve.c \
> +       sys_fork.c \
> +       sys_getcwd.c \
> +       sys_getpid.c \
> +       sys_kill.c \
> +       sys_utime.c \
> +       sys_wait.c \
> +       semihost-sys_close.c \
> +       semihost-sys_exit.c \
> +       semihost-sys_fdtable.c \
> +       semihost-sys_fstat.c \
> +       semihost-sys_ftime.c \
> +       semihost-sys_isatty.c \
> +       semihost-sys_link.c \
> +       semihost-sys_lseek.c \
> +       semihost-sys_open.c \
> +       semihost-sys_read.c \
> +       semihost-sys_sbrk.c \
> +       semihost-sys_stat.c \
> +       semihost-sys_stat_common.c \
> +       semihost-sys_unlink.c \
> +       semihost-sys_write.c
> +
>  gloss_specs = \
> -       nano.specs sim.specs
> +       nano.specs sim.specs semihost.specs
>
>  # Extra files
>
> @@ -134,6 +164,17 @@ sim_objs += $(sim_c_objs)
>  deps += $(sim_c_deps)
>  junk += $(sim_c_deps) $(sim_c_objs)
>
> +semihost_c_srcs = $(filter %.c, $(semihost_srcs))
> +semihost_c_objs = $(patsubst %.c, semihost-%.o, $(notdir
> $(semihost_c_srcs)))
> +semihost_c_deps = $(patsubst %.c, semihost-%.d, $(notdir
> $(semihost_c_srcs)))
> +
> +$(semihost_c_objs): semihost-%.o : %.c
> +       $(COMPILE) -c -o $@ $<
> +
> +semihost_objs += $(semihost_c_objs)
> +deps += $(semihost_c_deps)
> +junk += $(semihost_c_deps) $(semihost_c_objs)
> +
>  #-------------------------------------------------------------------------
>  # Build Object Files from Assembly Source
>  #-------------------------------------------------------------------------
> @@ -159,6 +200,16 @@ sim_objs += $(sim_asm_objs)
>  deps += $(sim_asm_deps)
>  junk += $(sim_asm_deps) $(sim_asm_objs)
>
> +semihost_asm_objs = $(patsubst %.S, semihost-%.o, $(notdir
> $(gloss_asm_srcs)))
> +semihost_asm_deps = $(patsubst %.S, semihost-%.d, $(notdir
> $(gloss_asm_srcs)))
> +
> +$(semihost_asm_objs) : semihost-%.o : %.S
> +       $(COMPILE) -c -DUSING_SEMIHOST_SPECS -o $@ $<
> +
> +semihost_objs += $(semihost_asm_objs)
> +deps += $(semihost_asm_deps)
> +junk += $(semihost_asm_deps) $(semihost_asm_objs)
> +
>  #-------------------------------------------------------------------------
>  # Build libgloss.a
>  #-------------------------------------------------------------------------
> @@ -187,6 +238,19 @@ junk += $(sim_lib)
>
>  install_libs += $(sim_lib)
>
> +#-------------------------------------------------------------------------
> +# Build libsemihost.a
> +#-------------------------------------------------------------------------
> +
> +semihost_lib  = libsemihost.a
> +$(semihost_lib) : $(semihost_objs)
> +       $(AR) rcv $@ $^
> +       $(RANLIB) $@
> +
> +junk += $(semihost_lib)
> +
> +install_libs += $(semihost_lib)
> +
>  #-------------------------------------------------------------------------
>  # Build crt0.o
>  #-------------------------------------------------------------------------
> diff --git a/libgloss/riscv/machine/syscall.h
> b/libgloss/riscv/machine/syscall.h
> index 5cd15b848..88b9fdfda 100644
> --- a/libgloss/riscv/machine/syscall.h
> +++ b/libgloss/riscv/machine/syscall.h
> @@ -54,4 +54,29 @@
>  #define SYS_time 1062
>  #define SYS_getmainvars 2011
>
> +/* Semihosting operations.  */
> +#define SEMIHOST_clock 0x10
> +#define SEMIHOST_close 0x02
> +#define SEMIHOST_elapsed 0x30
> +#define SEMIHOST_errno 0x13
> +#define SEMIHOST_exit 0x18
> +#define SEMIHOST_exit_extended 0x20
> +#define SEMIHOST_flen 0x0C
> +#define SEMIHOST_get_cmdline 0x15
> +#define SEMIHOST_heapinfo 0x16
> +#define SEMIHOST_iserror 0x08
> +#define SEMIHOST_istty 0x09
> +#define SEMIHOST_open 0x01
> +#define SEMIHOST_read 0x06
> +#define SEMIHOST_readc 0x07
> +#define SEMIHOST_remove 0x0E
> +#define SEMIHOST_rename 0x0F
> +#define SEMIHOST_seek 0x0A
> +#define SEMIHOST_system 0x12
> +#define SEMIHOST_tickfreq 0x31
> +#define SEMIHOST_time 0x11
> +#define SEMIHOST_tmpnam 0x0D
> +#define SEMIHOST_write 0x05
> +#define SEMIHOST_writec 0x03
> +#define SEMIHOST_write0 0x04
>  #endif
> diff --git a/libgloss/riscv/semihost-sys_close.c
> b/libgloss/riscv/semihost-sys_close.c
> new file mode 100644
> index 000000000..47402340c
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_close.c
> @@ -0,0 +1,28 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Close a file.  */
> +int
> +_close (int file)
> +{
> +  long res;
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  res = syscall_errno (SEMIHOST_close, data_block);
> +
> +  if (res != 0)
> +    return res;
> +
> +  __remove_fdentry (file);
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_exit.c
> b/libgloss/riscv/semihost-sys_exit.c
> new file mode 100644
> index 000000000..626fb6aeb
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_exit.c
> @@ -0,0 +1,23 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +
> +#define ADP_Stopped_ApplicationExit 0x20026
> +
> +/* Exit a program without cleaning up files.  */
> +void
> +_exit (int exit_status)
> +{
> +#if __riscv_xlen == 32
> +  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
> +#else
> +  /* The semihosting exit operation only allows 64-bit targets to report
> the
> +     exit code.  */
> +  long data_block[] = {ADP_Stopped_ApplicationExit, exit_status};
> +  syscall_errno (SEMIHOST_exit, data_block);
> +#endif
> +  while (1);
> +}
> diff --git a/libgloss/riscv/semihost-sys_fdtable.c
> b/libgloss/riscv/semihost-sys_fdtable.c
> new file mode 100644
> index 000000000..152c92d15
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_fdtable.c
> @@ -0,0 +1,85 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include "semihost_fdtable.h"
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> +#ifndef RISCV_MAX_OPEN_FILES
> +#define RISCV_MAX_OPEN_FILES 16
> +#endif
> +
> +extern int errno;
> +extern int _open (const char *, int, ...);
> +
> +/* fdtable keeps track of the position of each file and is used to map
> stdin,
> +   stdout and stderr to STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO.  */
> +
> +static struct fdentry fdtable[RISCV_MAX_OPEN_FILES];
> +
> +/* Initialize fdtable.  A handle of -1 denotes an empty entry.  */
> +
> +void __attribute__ ((constructor))
> +init_semihosting ()
> +{
> +  int handle;
> +
> +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
> +    fdtable[i].handle = -1;
> +
> +  /* Set up std streams.  */
> +  /* stdin.  */
> +  handle = _open (":tt", O_RDONLY);
> +  fdtable[STDIN_FILENO].handle = handle;
> +  fdtable[STDIN_FILENO].pos = 0;
> +
> +  /* stdout.  */
> +  handle = _open (":tt", O_WRONLY|O_CREAT|O_TRUNC);
> +  fdtable[STDOUT_FILENO].handle = handle;
> +  fdtable[STDOUT_FILENO].pos = 0;
> +
> +  /* stderr.  */
> +  handle = _open (":tt", O_WRONLY|O_CREAT|O_APPEND);
> +  fdtable[STDERR_FILENO].handle = handle;
> +  fdtable[STDERR_FILENO].pos = 0;
> +}
> +
> +/* Add entry to fdtable.  */
> +
> +int
> +__add_fdentry (int handle)
> +{
> +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
> +    if (fdtable[i].handle == -1)
> +      {
> +       fdtable[i].handle = handle;
> +       fdtable[i].pos = 0;
> +       return i;
> +      }
> +  /* Too many open files.  */
> +  errno = ENFILE;
> +  return -1;
> +}
> +
> +/* Return the fdentry for file or NULL if not found.  */
> +
> +struct fdentry *
> +__get_fdentry (int file)
> +{
> +  if (file<0 || file>=RISCV_MAX_OPEN_FILES || fdtable[file].handle == -1)
> +    {
> +      errno = EBADF;
> +      return NULL;
> +    }
> +  return &fdtable[file];
> +}
> +
> +/* Remove entry from fdtable.  */
> +
> +void
> +__remove_fdentry (int file)
> +{
> +  fdtable[file].handle = -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_fstat.c
> b/libgloss/riscv/semihost-sys_fstat.c
> new file mode 100644
> index 000000000..f57f0c07f
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_fstat.c
> @@ -0,0 +1,19 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <string.h>
> +#include <sys/stat.h>
> +#include "semihost_stat.h"
> +
> +/* Status of an open file.  The sys/stat.h header file required is
> +   distributed in the include subdirectory for this C library.  */
> +
> +int
> +_fstat (int file, struct stat *st)
> +{
> +  /* Initialize st as not all fields will be set.  */
> +  memset (st, 0, sizeof (*st));
> +
> +  return __stat_common (file, st);
> +}
> diff --git a/libgloss/riscv/semihost-sys_ftime.c
> b/libgloss/riscv/semihost-sys_ftime.c
> new file mode 100644
> index 000000000..aeafc6ca2
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_ftime.c
> @@ -0,0 +1,16 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include <sys/timeb.h>
> +#include "semihost_syscall.h"
> +
> +/* Get the current time.  */
> +int
> +_ftime (struct timeb *tp)
> +{
> +  tp->time = syscall_errno (SEMIHOST_time, 0);
> +  tp->millitm = 0;
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_isatty.c
> b/libgloss/riscv/semihost-sys_isatty.c
> new file mode 100644
> index 000000000..02d8e39cb
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_isatty.c
> @@ -0,0 +1,21 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include <sys/stat.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +int
> +_isatty (int file)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  return syscall_errno (SEMIHOST_istty, data_block);
> +}
> diff --git a/libgloss/riscv/semihost-sys_link.c
> b/libgloss/riscv/semihost-sys_link.c
> new file mode 100644
> index 000000000..717c5c81c
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_link.c
> @@ -0,0 +1,9 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +/* Establish a new name for an existing file.  */
> +int _link (const char *old_name, const char *new_name)
> +{
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_lseek.c
> b/libgloss/riscv/semihost-sys_lseek.c
> new file mode 100644
> index 000000000..68fccf2ff
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_lseek.c
> @@ -0,0 +1,70 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +extern int errno;
> +
> +/* Set position in a file.  */
> +off_t
> +_lseek (int file, off_t offset, int dir)
> +{
> +  long data_block[2];
> +  long flen;
> +  long res;
> +  struct fdentry *fd;
> +  off_t abs_pos;
> +
> +  fd =__get_fdentry (file);
> +  if (fd == NULL)
> +    {
> +      errno = EBADF;
> +      return -1;
> +    }
> +
> +  if (dir == SEEK_CUR && offset == 0)
> +    return fd->pos;
> +
> +  data_block[0] = fd->handle;
> +
> +  switch (dir)
> +    {
> +      case SEEK_SET:
> +       abs_pos = offset;
> +       break;
> +      case SEEK_CUR:
> +       abs_pos = fd->pos + offset;
> +       break;
> +      case SEEK_END:
> +       data_block[1] = 0;
> +       flen = syscall_errno (SEMIHOST_flen, data_block);
> +       if (flen == -1)
> +         return -1;
> +       abs_pos = flen + offset;
> +       break;
> +      default:
> +       errno = EINVAL;
> +       return -1;
> +    }
> +
> +  if (abs_pos < 0)
> +    {
> +      errno = EINVAL;
> +      return -1;
> +    }
> +
> +  data_block[1] = abs_pos;
> +  res = syscall_errno (SEMIHOST_seek, data_block);
> +  if (res == 0)
> +    {
> +      fd->pos = abs_pos;
> +      return abs_pos;
> +    }
> +  return res;
> +}
> diff --git a/libgloss/riscv/semihost-sys_open.c
> b/libgloss/riscv/semihost-sys_open.c
> new file mode 100644
> index 000000000..22f1d8206
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_open.c
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +#include <errno.h>
> +#include <string.h>
> +#include <fcntl.h>
> +
> +extern int errno;
> +
> +#define SEMIHOST_MODE_R 0
> +#define SEMIHOST_MODE_RPLUS 2
> +#define SEMIHOST_MODE_W 4
> +#define SEMIHOST_MODE_WPLUS 6
> +#define SEMIHOST_MODE_A 8
> +#define SEMIHOST_MODE_APLUS 10
> +
> +/* Open a file.  */
> +int
> +_open (const char *name, int flags, ...)
> +{
> +  int fh;
> +  int mode;
> +  long data_block[3];
> +
> +  /* Work out mode from flags.  */
> +  if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
> +    mode = SEMIHOST_MODE_R;
> +  else if ((flags & (O_WRONLY | O_CREAT | O_TRUNC))
> +          == (O_WRONLY | O_CREAT | O_TRUNC))
> +    mode = SEMIHOST_MODE_W;
> +  else if ((flags & (O_WRONLY | O_CREAT | O_APPEND))
> +          == (O_WRONLY | O_CREAT | O_APPEND))
> +    mode = SEMIHOST_MODE_A;
> +  else if ((flags & (O_RDWR | O_CREAT | O_TRUNC))
> +          == (O_RDWR | O_CREAT | O_TRUNC))
> +    mode = SEMIHOST_MODE_WPLUS;
> +  else if ((flags & (O_RDWR | O_CREAT | O_APPEND))
> +          == (O_RDWR | O_CREAT | O_APPEND))
> +    mode = SEMIHOST_MODE_APLUS;
> +  else if (flags & O_RDWR)
> +    mode = SEMIHOST_MODE_RPLUS;
> +  else
> +    {
> +      errno = EINVAL;
> +      return -1;
> +    }
> +
> +  data_block[0] = (long) name;
> +  data_block[1] = mode;
> +  data_block[2] = strlen (name);
> +  fh = syscall_errno (SEMIHOST_open, data_block);
> +  /* Failed to open file.  */
> +  if (fh == -1)
> +    return -1;
> +
> +  /* Register the file in the fdtable.  */
> +  return __add_fdentry (fh);
> +}
> diff --git a/libgloss/riscv/semihost-sys_read.c
> b/libgloss/riscv/semihost-sys_read.c
> new file mode 100644
> index 000000000..3164eed56
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_read.c
> @@ -0,0 +1,32 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Read from a file.  */
> +ssize_t _read (int file, void *ptr, size_t len)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[3];
> +  long res;
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  data_block[1] = (long) ptr;
> +  data_block[2] = len;
> +  res = syscall_errno (SEMIHOST_read, data_block);
> +  if (res >= 0)
> +    {
> +      ssize_t bytes_read = len - res;
> +      fd->pos += bytes_read;
> +      return bytes_read;
> +    }
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost-sys_sbrk.c
> b/libgloss/riscv/semihost-sys_sbrk.c
> new file mode 100644
> index 000000000..cbd035832
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_sbrk.c
> @@ -0,0 +1,26 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +/* Semihosting requires that sbrk be implemented without a syscall.  */
> +extern char _end[];               /* _end is set in the linker command
> file.  */
> +char *heap_ptr;
> +
> +/*
> + * sbrk -- changes heap size size.  Get nbytes more
> + *         RAM.  We just increment a pointer in what's
> + *         left of memory on the board.
> + */
> +char *
> +_sbrk (nbytes)
> +     int nbytes;
> +{
> +  char *base;
> +
> +  if (!heap_ptr)
> +    heap_ptr = (char *)&_end;
> +  base = heap_ptr;
> +  heap_ptr += nbytes;
> +
> +  return base;
> +}
> diff --git a/libgloss/riscv/semihost-sys_stat.c
> b/libgloss/riscv/semihost-sys_stat.c
> new file mode 100644
> index 000000000..4015801b9
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_stat.c
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include "semihost_stat.h"
> +
> +/* Status of a file (by name).  */
> +
> +int
> +_stat (const char *name, struct stat *st)
> +{
> +  int file;
> +  int res;
> +
> +  /* Initialize st as not all fields will be set.  */
> +  memset (st, 0, sizeof (*st));
> +
> +  /* Try to open file.  */
> +  file = _open (name, O_RDONLY);
> +  if (file == -1)
> +    /* _open should have already set errno.  */
> +    return -1;
> +
> +  /* File opened successfully, infer read permission for owner and assume
> it is
> +     a regular file.  */
> +  st->st_mode |= S_IREAD | S_IFREG;
> +
> +  /* Fill in more info.  */
> +  res = __stat_common (file, st);
> +
> +  _close (file);
> +  return res;
> +}
> diff --git a/libgloss/riscv/semihost-sys_stat_common.c
> b/libgloss/riscv/semihost-sys_stat_common.c
> new file mode 100644
> index 000000000..b38eb0863
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_stat_common.c
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include <sys/stat.h>
> +#include "semihost_fdtable.h"
> +
> +/* Used by _fstat and _stat to fill in some common details.  */
> +
> +int
> +__stat_common (int file, struct stat *st)
> +{
> +  int flen;
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[1];
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +
> +  /* Assume character device and default block size of 4096.  */
> +  st->st_mode |= S_IFCHR;
> +  st->st_blksize = 4096;
> +
> +  /* Attempt to get length of file.  */
> +  flen = syscall_errno (SEMIHOST_flen, data_block);
> +  if (flen == -1)
> +    return -1;
> +
> +  st->st_size = flen;
> +
> +  return 0;
> +}
> diff --git a/libgloss/riscv/semihost-sys_unlink.c
> b/libgloss/riscv/semihost-sys_unlink.c
> new file mode 100644
> index 000000000..1d2a6a0f9
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_unlink.c
> @@ -0,0 +1,15 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include "semihost_syscall.h"
> +#include <string.h>
> +
> +/* Remove a file's directory entry.  */
> +int
> +_unlink (const char *name)
> +{
> +  long data_block[] = {(long) name, strlen (name)};
> +  return syscall_errno (SEMIHOST_remove, data_block);
> +}
> diff --git a/libgloss/riscv/semihost-sys_write.c
> b/libgloss/riscv/semihost-sys_write.c
> new file mode 100644
> index 000000000..9aee6d30b
> --- /dev/null
> +++ b/libgloss/riscv/semihost-sys_write.c
> @@ -0,0 +1,32 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <machine/syscall.h>
> +#include <sys/types.h>
> +#include "semihost_syscall.h"
> +#include "semihost_fdtable.h"
> +
> +/* Write to a file.  */
> +ssize_t
> +_write (int file, const void *ptr, size_t len)
> +{
> +  struct fdentry *fd =__get_fdentry (file);
> +  long data_block[3];
> +  long res;
> +
> +  if (fd == NULL)
> +    return -1;
> +
> +  data_block[0] = fd->handle;
> +  data_block[1] = (long) ptr;
> +  data_block[2] = len;
> +  res = syscall_errno (SEMIHOST_write, data_block);
> +  if (res >= 0)
> +    {
> +      ssize_t bytes_written = len - res;
> +      fd->pos += bytes_written;
> +      return bytes_written;
> +    }
> +  return -1;
> +}
> diff --git a/libgloss/riscv/semihost.specs b/libgloss/riscv/semihost.specs
> new file mode 100644
> index 000000000..1c86c67e4
> --- /dev/null
> +++ b/libgloss/riscv/semihost.specs
> @@ -0,0 +1,10 @@
> +# Spec file for semihosting syscalls.
> +
> +%rename lib    semihost_lib
> +%rename link   semihost_link
> +
> +*lib:
> +--start-group -lc -lsemihost --end-group
> +
> +*link:
> +%(semihost_link) %:replace-outfile(-lgloss -lsemihost)
> diff --git a/libgloss/riscv/semihost_fdtable.h
> b/libgloss/riscv/semihost_fdtable.h
> new file mode 100644
> index 000000000..f596a409a
> --- /dev/null
> +++ b/libgloss/riscv/semihost_fdtable.h
> @@ -0,0 +1,21 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <sys/types.h>
> +
> +#ifndef RISCV_SEMIHOST_FDTABLE_H
> +#define RISCV_SEMIHOST_FDTABLE_H
> +
> +extern void __attribute__ ((constructor)) init_semihosting ();
> +extern int __add_fdentry (int);
> +extern struct fdentry * __get_fdentry (int);
> +extern void __remove_fdentry (int);
> +
> +struct fdentry
> +{
> +  int handle;
> +  off_t pos;
> +};
> +
> +#endif
> diff --git a/libgloss/riscv/semihost_stat.h
> b/libgloss/riscv/semihost_stat.h
> new file mode 100644
> index 000000000..c040fe8e7
> --- /dev/null
> +++ b/libgloss/riscv/semihost_stat.h
> @@ -0,0 +1,14 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#include <sys/types.h>
> +
> +#ifndef RISCV_SEMIHOST_STAT_H
> +#define RISCV_SEMIHOST_STAT_H
> +
> +extern int __stat_common (int, struct stat *);
> +extern int _open (const char *, int, ...);
> +extern int _close (int);
> +
> +#endif
> diff --git a/libgloss/riscv/semihost_syscall.h
> b/libgloss/riscv/semihost_syscall.h
> new file mode 100644
> index 000000000..50e731b40
> --- /dev/null
> +++ b/libgloss/riscv/semihost_syscall.h
> @@ -0,0 +1,47 @@
> +/*
> + * Copyright (C) 2020 Embecosm Limited
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +#ifndef _INTERNAL_SYSCALL_H
> +#define _INTERNAL_SYSCALL_H
> +
> +extern int errno;
> +
> +static inline long
> +__semihost_syscall (long id, long *data_block)
> +{
> +  register long a0 asm ("a0") = id;
> +  register long a1 asm ("a1") = (long) data_block;
> +
> +  /* RISC-V semihosting trap sequence.  Must be uncompressed and must not
> +     cross page boundary.  */
> +  asm volatile (
> +    ".balign 16             \n"
> +    ".option push           \n"
> +    ".option norvc          \n"
> +    "slli zero, zero, 0x1f  \n"
> +    "ebreak                 \n"
> +    "srai zero, zero, 0x7   \n"
> +    ".option pop            \n"
> +      : "+r"(a0) : "r"(a1) : "memory");
> +
> +  return a0;
> +}
> +
> +static inline long
> +__syscall_error ()
> +{
> +  errno = __semihost_syscall (SEMIHOST_errno, 0);
> +  return -1;
> +}
> +
> +static inline long
> +syscall_errno (long id, long *data_block)
> +{
> +  long res = __semihost_syscall (id, data_block);
> +  if (res < 0)
> +    return __syscall_error ();
> +  return res;
> +}
> +
> +#endif
> --
> 2.17.1
>
>
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] RISC-V: Add semihosting support
  2020-12-15 22:51     ` Jeff Johnston
@ 2020-12-16  8:33       ` Craig Blackmore
  2020-12-16  8:43         ` Kito Cheng
  2020-12-16 23:31         ` Jeff Johnston
  0 siblings, 2 replies; 9+ messages in thread
From: Craig Blackmore @ 2020-12-16  8:33 UTC (permalink / raw)
  To: Jeff Johnston; +Cc: Newlib

Hi Jeff,

On 15/12/2020 22:51, Jeff Johnston wrote:
> Hi Craig,
>
> Can you confirm that you have permission from your employer (embecosm)
> to contribute the code?  Other than that, I think we're good to merge
> before the snapshot.

Yes, I have permission from Embecosm to contribute this.

Thanks,

Craig

>
> Thanks,
>
> -- Jeff J.
>
> On Tue, Dec 15, 2020 at 7:00 AM Craig Blackmore
> <craig.blackmore@embecosm.com <mailto:craig.blackmore@embecosm.com>>
> wrote:
>
>     Hi Kito,
>
>     Thanks for the test and review.
>
>     On 09/12/2020 08:21, Kito Cheng wrote:
>
>     > Hi Craig:
>     >
>     > I verified with GCC testsuite on qemu with Keith's semihosting
>     patch,
>     > And that's LGTM, only two minor review comment, see below:
>     >
>     >> +++ b/libgloss/riscv/semihost-sys_exit.c
>     >> @@ -0,0 +1,19 @@
>     >> +#include <machine/syscall.h>
>     >> +#include "semihost_syscall.h"
>     >> +
>     >> +#define ADP_Stopped_ApplicationExit 0x20026
>     >> +
>     >> +/* Exit a program without cleaning up files.  */
>     >> +void
>     >> +_exit (int exit_status)
>     >> +{
>     >> +#if __riscv_xlen == 32
>     >> +  syscall_errno (SEMIHOST_exit, (long *)
>     ADP_Stopped_ApplicationExit);
>     > Could you use SEMIHOST_exit_extended here, so that we could have
>     > return value on rv32.
>
>     I can use SEMIHOST_exit_extended, but I will also need to add a
>     check for the
>     SH_EXT_EXIT_EXTENDED feature. Can I follow up with this in a later
>     patch?
>
>     >> +/* Return the fdentry for file or NULL if not found.  */
>     >> +
>     >> +struct fdentry *
>     >> +__get_fdentry (int file)
>     >> +{
>     >> +  if (file<0 || file>RISCV_MAX_OPEN_FILES ||
>     fdtable[file].handle == -1)
>     > Should be >= RISCV_MAX_OPEN_FILES here?
>
>     Please see updated patch below which fixes this condition and adds
>     a license as
>     requested by Jeff Johnston.
>
>     Best wishes,
>     Craig
>
>     ---
>      libgloss/riscv/Makefile.in                | 66 +++++++++++++++++-
>      libgloss/riscv/machine/syscall.h          | 25 +++++++
>      libgloss/riscv/semihost-sys_close.c       | 28 ++++++++
>      libgloss/riscv/semihost-sys_exit.c        | 23 ++++++
>      libgloss/riscv/semihost-sys_fdtable.c     | 85
>     +++++++++++++++++++++++
>      libgloss/riscv/semihost-sys_fstat.c       | 19 +++++
>      libgloss/riscv/semihost-sys_ftime.c       | 16 +++++
>      libgloss/riscv/semihost-sys_isatty.c      | 21 ++++++
>      libgloss/riscv/semihost-sys_link.c        |  9 +++
>      libgloss/riscv/semihost-sys_lseek.c       | 70 +++++++++++++++++++
>      libgloss/riscv/semihost-sys_open.c        | 62 +++++++++++++++++
>      libgloss/riscv/semihost-sys_read.c        | 32 +++++++++
>      libgloss/riscv/semihost-sys_sbrk.c        | 26 +++++++
>      libgloss/riscv/semihost-sys_stat.c        | 36 ++++++++++
>      libgloss/riscv/semihost-sys_stat_common.c | 36 ++++++++++
>      libgloss/riscv/semihost-sys_unlink.c      | 15 ++++
>      libgloss/riscv/semihost-sys_write.c       | 32 +++++++++
>      libgloss/riscv/semihost.specs             | 10 +++
>      libgloss/riscv/semihost_fdtable.h         | 21 ++++++
>      libgloss/riscv/semihost_stat.h            | 14 ++++
>      libgloss/riscv/semihost_syscall.h         | 47 +++++++++++++
>      21 files changed, 692 insertions(+), 1 deletion(-)
>      create mode 100644 libgloss/riscv/semihost-sys_close.c
>      create mode 100644 libgloss/riscv/semihost-sys_exit.c
>      create mode 100644 libgloss/riscv/semihost-sys_fdtable.c
>      create mode 100644 libgloss/riscv/semihost-sys_fstat.c
>      create mode 100644 libgloss/riscv/semihost-sys_ftime.c
>      create mode 100644 libgloss/riscv/semihost-sys_isatty.c
>      create mode 100644 libgloss/riscv/semihost-sys_link.c
>      create mode 100644 libgloss/riscv/semihost-sys_lseek.c
>      create mode 100644 libgloss/riscv/semihost-sys_open.c
>      create mode 100644 libgloss/riscv/semihost-sys_read.c
>      create mode 100644 libgloss/riscv/semihost-sys_sbrk.c
>      create mode 100644 libgloss/riscv/semihost-sys_stat.c
>      create mode 100644 libgloss/riscv/semihost-sys_stat_common.c
>      create mode 100644 libgloss/riscv/semihost-sys_unlink.c
>      create mode 100644 libgloss/riscv/semihost-sys_write.c
>      create mode 100644 libgloss/riscv/semihost.specs
>      create mode 100644 libgloss/riscv/semihost_fdtable.h
>      create mode 100644 libgloss/riscv/semihost_stat.h
>      create mode 100644 libgloss/riscv/semihost_syscall.h
>
>     diff --git a/libgloss/riscv/Makefile.in b/libgloss/riscv/Makefile.in
>     index 579dd9554..185b6e6d2 100644
>     --- a/libgloss/riscv/Makefile.in
>     +++ b/libgloss/riscv/Makefile.in
>     @@ -40,8 +40,38 @@ gloss_srcs = \
>             sys_wait.c \
>             sys_write.c
>
>     +# libsemihost reuses some of the libgloss minimal implementations
>     +
>     +semihost_srcs = \
>     +       nanosleep.c \
>     +       sys_chdir.c \
>     +       sys_chmod.c \
>     +       sys_chown.c \
>     +       sys_execve.c \
>     +       sys_fork.c \
>     +       sys_getcwd.c \
>     +       sys_getpid.c \
>     +       sys_kill.c \
>     +       sys_utime.c \
>     +       sys_wait.c \
>     +       semihost-sys_close.c \
>     +       semihost-sys_exit.c \
>     +       semihost-sys_fdtable.c \
>     +       semihost-sys_fstat.c \
>     +       semihost-sys_ftime.c \
>     +       semihost-sys_isatty.c \
>     +       semihost-sys_link.c \
>     +       semihost-sys_lseek.c \
>     +       semihost-sys_open.c \
>     +       semihost-sys_read.c \
>     +       semihost-sys_sbrk.c \
>     +       semihost-sys_stat.c \
>     +       semihost-sys_stat_common.c \
>     +       semihost-sys_unlink.c \
>     +       semihost-sys_write.c
>     +
>      gloss_specs = \
>     -       nano.specs sim.specs
>     +       nano.specs sim.specs semihost.specs
>
>      # Extra files
>
>     @@ -134,6 +164,17 @@ sim_objs += $(sim_c_objs)
>      deps += $(sim_c_deps)
>      junk += $(sim_c_deps) $(sim_c_objs)
>
>     +semihost_c_srcs = $(filter %.c, $(semihost_srcs))
>     +semihost_c_objs = $(patsubst %.c, semihost-%.o, $(notdir
>     $(semihost_c_srcs)))
>     +semihost_c_deps = $(patsubst %.c, semihost-%.d, $(notdir
>     $(semihost_c_srcs)))
>     +
>     +$(semihost_c_objs): semihost-%.o : %.c
>     +       $(COMPILE) -c -o $@ $<
>     +
>     +semihost_objs += $(semihost_c_objs)
>     +deps += $(semihost_c_deps)
>     +junk += $(semihost_c_deps) $(semihost_c_objs)
>     +
>      #-------------------------------------------------------------------------
>      # Build Object Files from Assembly Source
>      #-------------------------------------------------------------------------
>     @@ -159,6 +200,16 @@ sim_objs += $(sim_asm_objs)
>      deps += $(sim_asm_deps)
>      junk += $(sim_asm_deps) $(sim_asm_objs)
>
>     +semihost_asm_objs = $(patsubst %.S, semihost-%.o, $(notdir
>     $(gloss_asm_srcs)))
>     +semihost_asm_deps = $(patsubst %.S, semihost-%.d, $(notdir
>     $(gloss_asm_srcs)))
>     +
>     +$(semihost_asm_objs) : semihost-%.o : %.S
>     +       $(COMPILE) -c -DUSING_SEMIHOST_SPECS -o $@ $<
>     +
>     +semihost_objs += $(semihost_asm_objs)
>     +deps += $(semihost_asm_deps)
>     +junk += $(semihost_asm_deps) $(semihost_asm_objs)
>     +
>      #-------------------------------------------------------------------------
>      # Build libgloss.a
>      #-------------------------------------------------------------------------
>     @@ -187,6 +238,19 @@ junk += $(sim_lib)
>
>      install_libs += $(sim_lib)
>
>     +#-------------------------------------------------------------------------
>     +# Build libsemihost.a
>     +#-------------------------------------------------------------------------
>     +
>     +semihost_lib  = libsemihost.a
>     +$(semihost_lib) : $(semihost_objs)
>     +       $(AR) rcv $@ $^
>     +       $(RANLIB) $@
>     +
>     +junk += $(semihost_lib)
>     +
>     +install_libs += $(semihost_lib)
>     +
>      #-------------------------------------------------------------------------
>      # Build crt0.o
>      #-------------------------------------------------------------------------
>     diff --git a/libgloss/riscv/machine/syscall.h
>     b/libgloss/riscv/machine/syscall.h
>     index 5cd15b848..88b9fdfda 100644
>     --- a/libgloss/riscv/machine/syscall.h
>     +++ b/libgloss/riscv/machine/syscall.h
>     @@ -54,4 +54,29 @@
>      #define SYS_time 1062
>      #define SYS_getmainvars 2011
>
>     +/* Semihosting operations.  */
>     +#define SEMIHOST_clock 0x10
>     +#define SEMIHOST_close 0x02
>     +#define SEMIHOST_elapsed 0x30
>     +#define SEMIHOST_errno 0x13
>     +#define SEMIHOST_exit 0x18
>     +#define SEMIHOST_exit_extended 0x20
>     +#define SEMIHOST_flen 0x0C
>     +#define SEMIHOST_get_cmdline 0x15
>     +#define SEMIHOST_heapinfo 0x16
>     +#define SEMIHOST_iserror 0x08
>     +#define SEMIHOST_istty 0x09
>     +#define SEMIHOST_open 0x01
>     +#define SEMIHOST_read 0x06
>     +#define SEMIHOST_readc 0x07
>     +#define SEMIHOST_remove 0x0E
>     +#define SEMIHOST_rename 0x0F
>     +#define SEMIHOST_seek 0x0A
>     +#define SEMIHOST_system 0x12
>     +#define SEMIHOST_tickfreq 0x31
>     +#define SEMIHOST_time 0x11
>     +#define SEMIHOST_tmpnam 0x0D
>     +#define SEMIHOST_write 0x05
>     +#define SEMIHOST_writec 0x03
>     +#define SEMIHOST_write0 0x04
>      #endif
>     diff --git a/libgloss/riscv/semihost-sys_close.c
>     b/libgloss/riscv/semihost-sys_close.c
>     new file mode 100644
>     index 000000000..47402340c
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_close.c
>     @@ -0,0 +1,28 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include "semihost_syscall.h"
>     +#include "semihost_fdtable.h"
>     +
>     +/* Close a file.  */
>     +int
>     +_close (int file)
>     +{
>     +  long res;
>     +  struct fdentry *fd =__get_fdentry (file);
>     +  long data_block[1];
>     +
>     +  if (fd == NULL)
>     +    return -1;
>     +
>     +  data_block[0] = fd->handle;
>     +  res = syscall_errno (SEMIHOST_close, data_block);
>     +
>     +  if (res != 0)
>     +    return res;
>     +
>     +  __remove_fdentry (file);
>     +  return 0;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_exit.c
>     b/libgloss/riscv/semihost-sys_exit.c
>     new file mode 100644
>     index 000000000..626fb6aeb
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_exit.c
>     @@ -0,0 +1,23 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include "semihost_syscall.h"
>     +
>     +#define ADP_Stopped_ApplicationExit 0x20026
>     +
>     +/* Exit a program without cleaning up files.  */
>     +void
>     +_exit (int exit_status)
>     +{
>     +#if __riscv_xlen == 32
>     +  syscall_errno (SEMIHOST_exit, (long *)
>     ADP_Stopped_ApplicationExit);
>     +#else
>     +  /* The semihosting exit operation only allows 64-bit targets to
>     report the
>     +     exit code.  */
>     +  long data_block[] = {ADP_Stopped_ApplicationExit, exit_status};
>     +  syscall_errno (SEMIHOST_exit, data_block);
>     +#endif
>     +  while (1);
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_fdtable.c
>     b/libgloss/riscv/semihost-sys_fdtable.c
>     new file mode 100644
>     index 000000000..152c92d15
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_fdtable.c
>     @@ -0,0 +1,85 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include "semihost_fdtable.h"
>     +#include <errno.h>
>     +#include <fcntl.h>
>     +#include <unistd.h>
>     +
>     +#ifndef RISCV_MAX_OPEN_FILES
>     +#define RISCV_MAX_OPEN_FILES 16
>     +#endif
>     +
>     +extern int errno;
>     +extern int _open (const char *, int, ...);
>     +
>     +/* fdtable keeps track of the position of each file and is used
>     to map stdin,
>     +   stdout and stderr to STDIN_FILENO, STDOUT_FILENO and
>     STDERR_FILENO.  */
>     +
>     +static struct fdentry fdtable[RISCV_MAX_OPEN_FILES];
>     +
>     +/* Initialize fdtable.  A handle of -1 denotes an empty entry.  */
>     +
>     +void __attribute__ ((constructor))
>     +init_semihosting ()
>     +{
>     +  int handle;
>     +
>     +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
>     +    fdtable[i].handle = -1;
>     +
>     +  /* Set up std streams.  */
>     +  /* stdin.  */
>     +  handle = _open (":tt", O_RDONLY);
>     +  fdtable[STDIN_FILENO].handle = handle;
>     +  fdtable[STDIN_FILENO].pos = 0;
>     +
>     +  /* stdout.  */
>     +  handle = _open (":tt", O_WRONLY|O_CREAT|O_TRUNC);
>     +  fdtable[STDOUT_FILENO].handle = handle;
>     +  fdtable[STDOUT_FILENO].pos = 0;
>     +
>     +  /* stderr.  */
>     +  handle = _open (":tt", O_WRONLY|O_CREAT|O_APPEND);
>     +  fdtable[STDERR_FILENO].handle = handle;
>     +  fdtable[STDERR_FILENO].pos = 0;
>     +}
>     +
>     +/* Add entry to fdtable.  */
>     +
>     +int
>     +__add_fdentry (int handle)
>     +{
>     +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
>     +    if (fdtable[i].handle == -1)
>     +      {
>     +       fdtable[i].handle = handle;
>     +       fdtable[i].pos = 0;
>     +       return i;
>     +      }
>     +  /* Too many open files.  */
>     +  errno = ENFILE;
>     +  return -1;
>     +}
>     +
>     +/* Return the fdentry for file or NULL if not found.  */
>     +
>     +struct fdentry *
>     +__get_fdentry (int file)
>     +{
>     +  if (file<0 || file>=RISCV_MAX_OPEN_FILES ||
>     fdtable[file].handle == -1)
>     +    {
>     +      errno = EBADF;
>     +      return NULL;
>     +    }
>     +  return &fdtable[file];
>     +}
>     +
>     +/* Remove entry from fdtable.  */
>     +
>     +void
>     +__remove_fdentry (int file)
>     +{
>     +  fdtable[file].handle = -1;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_fstat.c
>     b/libgloss/riscv/semihost-sys_fstat.c
>     new file mode 100644
>     index 000000000..f57f0c07f
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_fstat.c
>     @@ -0,0 +1,19 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <string.h>
>     +#include <sys/stat.h>
>     +#include "semihost_stat.h"
>     +
>     +/* Status of an open file.  The sys/stat.h header file required is
>     +   distributed in the include subdirectory for this C library.  */
>     +
>     +int
>     +_fstat (int file, struct stat *st)
>     +{
>     +  /* Initialize st as not all fields will be set.  */
>     +  memset (st, 0, sizeof (*st));
>     +
>     +  return __stat_common (file, st);
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_ftime.c
>     b/libgloss/riscv/semihost-sys_ftime.c
>     new file mode 100644
>     index 000000000..aeafc6ca2
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_ftime.c
>     @@ -0,0 +1,16 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include <sys/timeb.h>
>     +#include "semihost_syscall.h"
>     +
>     +/* Get the current time.  */
>     +int
>     +_ftime (struct timeb *tp)
>     +{
>     +  tp->time = syscall_errno (SEMIHOST_time, 0);
>     +  tp->millitm = 0;
>     +  return 0;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_isatty.c
>     b/libgloss/riscv/semihost-sys_isatty.c
>     new file mode 100644
>     index 000000000..02d8e39cb
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_isatty.c
>     @@ -0,0 +1,21 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include <sys/stat.h>
>     +#include "semihost_syscall.h"
>     +#include "semihost_fdtable.h"
>     +
>     +int
>     +_isatty (int file)
>     +{
>     +  struct fdentry *fd =__get_fdentry (file);
>     +  long data_block[1];
>     +
>     +  if (fd == NULL)
>     +    return -1;
>     +
>     +  data_block[0] = fd->handle;
>     +  return syscall_errno (SEMIHOST_istty, data_block);
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_link.c
>     b/libgloss/riscv/semihost-sys_link.c
>     new file mode 100644
>     index 000000000..717c5c81c
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_link.c
>     @@ -0,0 +1,9 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +/* Establish a new name for an existing file.  */
>     +int _link (const char *old_name, const char *new_name)
>     +{
>     +  return -1;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_lseek.c
>     b/libgloss/riscv/semihost-sys_lseek.c
>     new file mode 100644
>     index 000000000..68fccf2ff
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_lseek.c
>     @@ -0,0 +1,70 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include <errno.h>
>     +#include <sys/types.h>
>     +#include <unistd.h>
>     +#include "semihost_syscall.h"
>     +#include "semihost_fdtable.h"
>     +
>     +extern int errno;
>     +
>     +/* Set position in a file.  */
>     +off_t
>     +_lseek (int file, off_t offset, int dir)
>     +{
>     +  long data_block[2];
>     +  long flen;
>     +  long res;
>     +  struct fdentry *fd;
>     +  off_t abs_pos;
>     +
>     +  fd =__get_fdentry (file);
>     +  if (fd == NULL)
>     +    {
>     +      errno = EBADF;
>     +      return -1;
>     +    }
>     +
>     +  if (dir == SEEK_CUR && offset == 0)
>     +    return fd->pos;
>     +
>     +  data_block[0] = fd->handle;
>     +
>     +  switch (dir)
>     +    {
>     +      case SEEK_SET:
>     +       abs_pos = offset;
>     +       break;
>     +      case SEEK_CUR:
>     +       abs_pos = fd->pos + offset;
>     +       break;
>     +      case SEEK_END:
>     +       data_block[1] = 0;
>     +       flen = syscall_errno (SEMIHOST_flen, data_block);
>     +       if (flen == -1)
>     +         return -1;
>     +       abs_pos = flen + offset;
>     +       break;
>     +      default:
>     +       errno = EINVAL;
>     +       return -1;
>     +    }
>     +
>     +  if (abs_pos < 0)
>     +    {
>     +      errno = EINVAL;
>     +      return -1;
>     +    }
>     +
>     +  data_block[1] = abs_pos;
>     +  res = syscall_errno (SEMIHOST_seek, data_block);
>     +  if (res == 0)
>     +    {
>     +      fd->pos = abs_pos;
>     +      return abs_pos;
>     +    }
>     +  return res;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_open.c
>     b/libgloss/riscv/semihost-sys_open.c
>     new file mode 100644
>     index 000000000..22f1d8206
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_open.c
>     @@ -0,0 +1,62 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include "semihost_syscall.h"
>     +#include "semihost_fdtable.h"
>     +#include <errno.h>
>     +#include <string.h>
>     +#include <fcntl.h>
>     +
>     +extern int errno;
>     +
>     +#define SEMIHOST_MODE_R 0
>     +#define SEMIHOST_MODE_RPLUS 2
>     +#define SEMIHOST_MODE_W 4
>     +#define SEMIHOST_MODE_WPLUS 6
>     +#define SEMIHOST_MODE_A 8
>     +#define SEMIHOST_MODE_APLUS 10
>     +
>     +/* Open a file.  */
>     +int
>     +_open (const char *name, int flags, ...)
>     +{
>     +  int fh;
>     +  int mode;
>     +  long data_block[3];
>     +
>     +  /* Work out mode from flags.  */
>     +  if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
>     +    mode = SEMIHOST_MODE_R;
>     +  else if ((flags & (O_WRONLY | O_CREAT | O_TRUNC))
>     +          == (O_WRONLY | O_CREAT | O_TRUNC))
>     +    mode = SEMIHOST_MODE_W;
>     +  else if ((flags & (O_WRONLY | O_CREAT | O_APPEND))
>     +          == (O_WRONLY | O_CREAT | O_APPEND))
>     +    mode = SEMIHOST_MODE_A;
>     +  else if ((flags & (O_RDWR | O_CREAT | O_TRUNC))
>     +          == (O_RDWR | O_CREAT | O_TRUNC))
>     +    mode = SEMIHOST_MODE_WPLUS;
>     +  else if ((flags & (O_RDWR | O_CREAT | O_APPEND))
>     +          == (O_RDWR | O_CREAT | O_APPEND))
>     +    mode = SEMIHOST_MODE_APLUS;
>     +  else if (flags & O_RDWR)
>     +    mode = SEMIHOST_MODE_RPLUS;
>     +  else
>     +    {
>     +      errno = EINVAL;
>     +      return -1;
>     +    }
>     +
>     +  data_block[0] = (long) name;
>     +  data_block[1] = mode;
>     +  data_block[2] = strlen (name);
>     +  fh = syscall_errno (SEMIHOST_open, data_block);
>     +  /* Failed to open file.  */
>     +  if (fh == -1)
>     +    return -1;
>     +
>     +  /* Register the file in the fdtable.  */
>     +  return __add_fdentry (fh);
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_read.c
>     b/libgloss/riscv/semihost-sys_read.c
>     new file mode 100644
>     index 000000000..3164eed56
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_read.c
>     @@ -0,0 +1,32 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include <errno.h>
>     +#include <sys/types.h>
>     +#include "semihost_syscall.h"
>     +#include "semihost_fdtable.h"
>     +
>     +/* Read from a file.  */
>     +ssize_t _read (int file, void *ptr, size_t len)
>     +{
>     +  struct fdentry *fd =__get_fdentry (file);
>     +  long data_block[3];
>     +  long res;
>     +
>     +  if (fd == NULL)
>     +    return -1;
>     +
>     +  data_block[0] = fd->handle;
>     +  data_block[1] = (long) ptr;
>     +  data_block[2] = len;
>     +  res = syscall_errno (SEMIHOST_read, data_block);
>     +  if (res >= 0)
>     +    {
>     +      ssize_t bytes_read = len - res;
>     +      fd->pos += bytes_read;
>     +      return bytes_read;
>     +    }
>     +  return -1;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_sbrk.c
>     b/libgloss/riscv/semihost-sys_sbrk.c
>     new file mode 100644
>     index 000000000..cbd035832
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_sbrk.c
>     @@ -0,0 +1,26 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +/* Semihosting requires that sbrk be implemented without a
>     syscall.  */
>     +extern char _end[];               /* _end is set in the linker
>     command file.  */
>     +char *heap_ptr;
>     +
>     +/*
>     + * sbrk -- changes heap size size.  Get nbytes more
>     + *         RAM.  We just increment a pointer in what's
>     + *         left of memory on the board.
>     + */
>     +char *
>     +_sbrk (nbytes)
>     +     int nbytes;
>     +{
>     +  char *base;
>     +
>     +  if (!heap_ptr)
>     +    heap_ptr = (char *)&_end;
>     +  base = heap_ptr;
>     +  heap_ptr += nbytes;
>     +
>     +  return base;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_stat.c
>     b/libgloss/riscv/semihost-sys_stat.c
>     new file mode 100644
>     index 000000000..4015801b9
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_stat.c
>     @@ -0,0 +1,36 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include <string.h>
>     +#include <fcntl.h>
>     +#include "semihost_stat.h"
>     +
>     +/* Status of a file (by name).  */
>     +
>     +int
>     +_stat (const char *name, struct stat *st)
>     +{
>     +  int file;
>     +  int res;
>     +
>     +  /* Initialize st as not all fields will be set.  */
>     +  memset (st, 0, sizeof (*st));
>     +
>     +  /* Try to open file.  */
>     +  file = _open (name, O_RDONLY);
>     +  if (file == -1)
>     +    /* _open should have already set errno.  */
>     +    return -1;
>     +
>     +  /* File opened successfully, infer read permission for owner
>     and assume it is
>     +     a regular file.  */
>     +  st->st_mode |= S_IREAD | S_IFREG;
>     +
>     +  /* Fill in more info.  */
>     +  res = __stat_common (file, st);
>     +
>     +  _close (file);
>     +  return res;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_stat_common.c
>     b/libgloss/riscv/semihost-sys_stat_common.c
>     new file mode 100644
>     index 000000000..b38eb0863
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_stat_common.c
>     @@ -0,0 +1,36 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include "semihost_syscall.h"
>     +#include <sys/stat.h>
>     +#include "semihost_fdtable.h"
>     +
>     +/* Used by _fstat and _stat to fill in some common details.  */
>     +
>     +int
>     +__stat_common (int file, struct stat *st)
>     +{
>     +  int flen;
>     +  struct fdentry *fd =__get_fdentry (file);
>     +  long data_block[1];
>     +
>     +  if (fd == NULL)
>     +    return -1;
>     +
>     +  data_block[0] = fd->handle;
>     +
>     +  /* Assume character device and default block size of 4096.  */
>     +  st->st_mode |= S_IFCHR;
>     +  st->st_blksize = 4096;
>     +
>     +  /* Attempt to get length of file.  */
>     +  flen = syscall_errno (SEMIHOST_flen, data_block);
>     +  if (flen == -1)
>     +    return -1;
>     +
>     +  st->st_size = flen;
>     +
>     +  return 0;
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_unlink.c
>     b/libgloss/riscv/semihost-sys_unlink.c
>     new file mode 100644
>     index 000000000..1d2a6a0f9
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_unlink.c
>     @@ -0,0 +1,15 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include "semihost_syscall.h"
>     +#include <string.h>
>     +
>     +/* Remove a file's directory entry.  */
>     +int
>     +_unlink (const char *name)
>     +{
>     +  long data_block[] = {(long) name, strlen (name)};
>     +  return syscall_errno (SEMIHOST_remove, data_block);
>     +}
>     diff --git a/libgloss/riscv/semihost-sys_write.c
>     b/libgloss/riscv/semihost-sys_write.c
>     new file mode 100644
>     index 000000000..9aee6d30b
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost-sys_write.c
>     @@ -0,0 +1,32 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <machine/syscall.h>
>     +#include <sys/types.h>
>     +#include "semihost_syscall.h"
>     +#include "semihost_fdtable.h"
>     +
>     +/* Write to a file.  */
>     +ssize_t
>     +_write (int file, const void *ptr, size_t len)
>     +{
>     +  struct fdentry *fd =__get_fdentry (file);
>     +  long data_block[3];
>     +  long res;
>     +
>     +  if (fd == NULL)
>     +    return -1;
>     +
>     +  data_block[0] = fd->handle;
>     +  data_block[1] = (long) ptr;
>     +  data_block[2] = len;
>     +  res = syscall_errno (SEMIHOST_write, data_block);
>     +  if (res >= 0)
>     +    {
>     +      ssize_t bytes_written = len - res;
>     +      fd->pos += bytes_written;
>     +      return bytes_written;
>     +    }
>     +  return -1;
>     +}
>     diff --git a/libgloss/riscv/semihost.specs
>     b/libgloss/riscv/semihost.specs
>     new file mode 100644
>     index 000000000..1c86c67e4
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost.specs
>     @@ -0,0 +1,10 @@
>     +# Spec file for semihosting syscalls.
>     +
>     +%rename lib    semihost_lib
>     +%rename link   semihost_link
>     +
>     +*lib:
>     +--start-group -lc -lsemihost --end-group
>     +
>     +*link:
>     +%(semihost_link) %:replace-outfile(-lgloss -lsemihost)
>     diff --git a/libgloss/riscv/semihost_fdtable.h
>     b/libgloss/riscv/semihost_fdtable.h
>     new file mode 100644
>     index 000000000..f596a409a
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost_fdtable.h
>     @@ -0,0 +1,21 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <sys/types.h>
>     +
>     +#ifndef RISCV_SEMIHOST_FDTABLE_H
>     +#define RISCV_SEMIHOST_FDTABLE_H
>     +
>     +extern void __attribute__ ((constructor)) init_semihosting ();
>     +extern int __add_fdentry (int);
>     +extern struct fdentry * __get_fdentry (int);
>     +extern void __remove_fdentry (int);
>     +
>     +struct fdentry
>     +{
>     +  int handle;
>     +  off_t pos;
>     +};
>     +
>     +#endif
>     diff --git a/libgloss/riscv/semihost_stat.h
>     b/libgloss/riscv/semihost_stat.h
>     new file mode 100644
>     index 000000000..c040fe8e7
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost_stat.h
>     @@ -0,0 +1,14 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#include <sys/types.h>
>     +
>     +#ifndef RISCV_SEMIHOST_STAT_H
>     +#define RISCV_SEMIHOST_STAT_H
>     +
>     +extern int __stat_common (int, struct stat *);
>     +extern int _open (const char *, int, ...);
>     +extern int _close (int);
>     +
>     +#endif
>     diff --git a/libgloss/riscv/semihost_syscall.h
>     b/libgloss/riscv/semihost_syscall.h
>     new file mode 100644
>     index 000000000..50e731b40
>     --- /dev/null
>     +++ b/libgloss/riscv/semihost_syscall.h
>     @@ -0,0 +1,47 @@
>     +/*
>     + * Copyright (C) 2020 Embecosm Limited
>     + * SPDX-License-Identifier: BSD-2-Clause
>     + */
>     +#ifndef _INTERNAL_SYSCALL_H
>     +#define _INTERNAL_SYSCALL_H
>     +
>     +extern int errno;
>     +
>     +static inline long
>     +__semihost_syscall (long id, long *data_block)
>     +{
>     +  register long a0 asm ("a0") = id;
>     +  register long a1 asm ("a1") = (long) data_block;
>     +
>     +  /* RISC-V semihosting trap sequence.  Must be uncompressed and
>     must not
>     +     cross page boundary.  */
>     +  asm volatile (
>     +    ".balign 16             \n"
>     +    ".option push           \n"
>     +    ".option norvc          \n"
>     +    "slli zero, zero, 0x1f  \n"
>     +    "ebreak                 \n"
>     +    "srai zero, zero, 0x7   \n"
>     +    ".option pop            \n"
>     +      : "+r"(a0) : "r"(a1) : "memory");
>     +
>     +  return a0;
>     +}
>     +
>     +static inline long
>     +__syscall_error ()
>     +{
>     +  errno = __semihost_syscall (SEMIHOST_errno, 0);
>     +  return -1;
>     +}
>     +
>     +static inline long
>     +syscall_errno (long id, long *data_block)
>     +{
>     +  long res = __semihost_syscall (id, data_block);
>     +  if (res < 0)
>     +    return __syscall_error ();
>     +  return res;
>     +}
>     +
>     +#endif
>     -- 
>     2.17.1
>
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] RISC-V: Add semihosting support
  2020-12-16  8:33       ` Craig Blackmore
@ 2020-12-16  8:43         ` Kito Cheng
  2020-12-16 23:31         ` Jeff Johnston
  1 sibling, 0 replies; 9+ messages in thread
From: Kito Cheng @ 2020-12-16  8:43 UTC (permalink / raw)
  To: Craig Blackmore; +Cc: Jeff Johnston, Newlib

Hi Craig:

The v2 patch is LGTM, verified with qemu again, thank you!

> I can use SEMIHOST_exit_extended, but I will also need to add a check for the
> SH_EXT_EXIT_EXTENDED feature. Can I follow up with this in a later patch?

OK to me :)

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] RISC-V: Add semihosting support
  2020-12-16  8:33       ` Craig Blackmore
  2020-12-16  8:43         ` Kito Cheng
@ 2020-12-16 23:31         ` Jeff Johnston
  1 sibling, 0 replies; 9+ messages in thread
From: Jeff Johnston @ 2020-12-16 23:31 UTC (permalink / raw)
  To: Craig Blackmore; +Cc: Newlib

Patch merged to master with your license added to COPYING.LIBGLOSS.

-- Jeff J.

On Wed, Dec 16, 2020 at 3:33 AM Craig Blackmore <
craig.blackmore@embecosm.com> wrote:

> Hi Jeff,
> On 15/12/2020 22:51, Jeff Johnston wrote:
>
> Hi Craig,
>
> Can you confirm that you have permission from your employer (embecosm) to
> contribute the code?  Other than that, I think we're good to merge before
> the snapshot.
>
> Yes, I have permission from Embecosm to contribute this.
>
> Thanks,
>
> Craig
>
>
> Thanks,
>
> -- Jeff J.
>
> On Tue, Dec 15, 2020 at 7:00 AM Craig Blackmore <
> craig.blackmore@embecosm.com> wrote:
>
>> Hi Kito,
>>
>> Thanks for the test and review.
>>
>> On 09/12/2020 08:21, Kito Cheng wrote:
>>
>> > Hi Craig:
>> >
>> > I verified with GCC testsuite on qemu with Keith's semihosting patch,
>> > And that's LGTM, only two minor review comment, see below:
>> >
>> >> +++ b/libgloss/riscv/semihost-sys_exit.c
>> >> @@ -0,0 +1,19 @@
>> >> +#include <machine/syscall.h>
>> >> +#include "semihost_syscall.h"
>> >> +
>> >> +#define ADP_Stopped_ApplicationExit 0x20026
>> >> +
>> >> +/* Exit a program without cleaning up files.  */
>> >> +void
>> >> +_exit (int exit_status)
>> >> +{
>> >> +#if __riscv_xlen == 32
>> >> +  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
>> > Could you use SEMIHOST_exit_extended here, so that we could have
>> > return value on rv32.
>>
>> I can use SEMIHOST_exit_extended, but I will also need to add a check for
>> the
>> SH_EXT_EXIT_EXTENDED feature. Can I follow up with this in a later patch?
>>
>> >> +/* Return the fdentry for file or NULL if not found.  */
>> >> +
>> >> +struct fdentry *
>> >> +__get_fdentry (int file)
>> >> +{
>> >> +  if (file<0 || file>RISCV_MAX_OPEN_FILES || fdtable[file].handle ==
>> -1)
>> > Should be >= RISCV_MAX_OPEN_FILES here?
>>
>> Please see updated patch below which fixes this condition and adds a
>> license as
>> requested by Jeff Johnston.
>>
>> Best wishes,
>> Craig
>>
>> ---
>>  libgloss/riscv/Makefile.in                | 66 +++++++++++++++++-
>>  libgloss/riscv/machine/syscall.h          | 25 +++++++
>>  libgloss/riscv/semihost-sys_close.c       | 28 ++++++++
>>  libgloss/riscv/semihost-sys_exit.c        | 23 ++++++
>>  libgloss/riscv/semihost-sys_fdtable.c     | 85 +++++++++++++++++++++++
>>  libgloss/riscv/semihost-sys_fstat.c       | 19 +++++
>>  libgloss/riscv/semihost-sys_ftime.c       | 16 +++++
>>  libgloss/riscv/semihost-sys_isatty.c      | 21 ++++++
>>  libgloss/riscv/semihost-sys_link.c        |  9 +++
>>  libgloss/riscv/semihost-sys_lseek.c       | 70 +++++++++++++++++++
>>  libgloss/riscv/semihost-sys_open.c        | 62 +++++++++++++++++
>>  libgloss/riscv/semihost-sys_read.c        | 32 +++++++++
>>  libgloss/riscv/semihost-sys_sbrk.c        | 26 +++++++
>>  libgloss/riscv/semihost-sys_stat.c        | 36 ++++++++++
>>  libgloss/riscv/semihost-sys_stat_common.c | 36 ++++++++++
>>  libgloss/riscv/semihost-sys_unlink.c      | 15 ++++
>>  libgloss/riscv/semihost-sys_write.c       | 32 +++++++++
>>  libgloss/riscv/semihost.specs             | 10 +++
>>  libgloss/riscv/semihost_fdtable.h         | 21 ++++++
>>  libgloss/riscv/semihost_stat.h            | 14 ++++
>>  libgloss/riscv/semihost_syscall.h         | 47 +++++++++++++
>>  21 files changed, 692 insertions(+), 1 deletion(-)
>>  create mode 100644 libgloss/riscv/semihost-sys_close.c
>>  create mode 100644 libgloss/riscv/semihost-sys_exit.c
>>  create mode 100644 libgloss/riscv/semihost-sys_fdtable.c
>>  create mode 100644 libgloss/riscv/semihost-sys_fstat.c
>>  create mode 100644 libgloss/riscv/semihost-sys_ftime.c
>>  create mode 100644 libgloss/riscv/semihost-sys_isatty.c
>>  create mode 100644 libgloss/riscv/semihost-sys_link.c
>>  create mode 100644 libgloss/riscv/semihost-sys_lseek.c
>>  create mode 100644 libgloss/riscv/semihost-sys_open.c
>>  create mode 100644 libgloss/riscv/semihost-sys_read.c
>>  create mode 100644 libgloss/riscv/semihost-sys_sbrk.c
>>  create mode 100644 libgloss/riscv/semihost-sys_stat.c
>>  create mode 100644 libgloss/riscv/semihost-sys_stat_common.c
>>  create mode 100644 libgloss/riscv/semihost-sys_unlink.c
>>  create mode 100644 libgloss/riscv/semihost-sys_write.c
>>  create mode 100644 libgloss/riscv/semihost.specs
>>  create mode 100644 libgloss/riscv/semihost_fdtable.h
>>  create mode 100644 libgloss/riscv/semihost_stat.h
>>  create mode 100644 libgloss/riscv/semihost_syscall.h
>>
>> diff --git a/libgloss/riscv/Makefile.in b/libgloss/riscv/Makefile.in
>> index 579dd9554..185b6e6d2 100644
>> --- a/libgloss/riscv/Makefile.in
>> +++ b/libgloss/riscv/Makefile.in
>> @@ -40,8 +40,38 @@ gloss_srcs = \
>>         sys_wait.c \
>>         sys_write.c
>>
>> +# libsemihost reuses some of the libgloss minimal implementations
>> +
>> +semihost_srcs = \
>> +       nanosleep.c \
>> +       sys_chdir.c \
>> +       sys_chmod.c \
>> +       sys_chown.c \
>> +       sys_execve.c \
>> +       sys_fork.c \
>> +       sys_getcwd.c \
>> +       sys_getpid.c \
>> +       sys_kill.c \
>> +       sys_utime.c \
>> +       sys_wait.c \
>> +       semihost-sys_close.c \
>> +       semihost-sys_exit.c \
>> +       semihost-sys_fdtable.c \
>> +       semihost-sys_fstat.c \
>> +       semihost-sys_ftime.c \
>> +       semihost-sys_isatty.c \
>> +       semihost-sys_link.c \
>> +       semihost-sys_lseek.c \
>> +       semihost-sys_open.c \
>> +       semihost-sys_read.c \
>> +       semihost-sys_sbrk.c \
>> +       semihost-sys_stat.c \
>> +       semihost-sys_stat_common.c \
>> +       semihost-sys_unlink.c \
>> +       semihost-sys_write.c
>> +
>>  gloss_specs = \
>> -       nano.specs sim.specs
>> +       nano.specs sim.specs semihost.specs
>>
>>  # Extra files
>>
>> @@ -134,6 +164,17 @@ sim_objs += $(sim_c_objs)
>>  deps += $(sim_c_deps)
>>  junk += $(sim_c_deps) $(sim_c_objs)
>>
>> +semihost_c_srcs = $(filter %.c, $(semihost_srcs))
>> +semihost_c_objs = $(patsubst %.c, semihost-%.o, $(notdir
>> $(semihost_c_srcs)))
>> +semihost_c_deps = $(patsubst %.c, semihost-%.d, $(notdir
>> $(semihost_c_srcs)))
>> +
>> +$(semihost_c_objs): semihost-%.o : %.c
>> +       $(COMPILE) -c -o $@ $<
>> +
>> +semihost_objs += $(semihost_c_objs)
>> +deps += $(semihost_c_deps)
>> +junk += $(semihost_c_deps) $(semihost_c_objs)
>> +
>>
>>  #-------------------------------------------------------------------------
>>  # Build Object Files from Assembly Source
>>
>>  #-------------------------------------------------------------------------
>> @@ -159,6 +200,16 @@ sim_objs += $(sim_asm_objs)
>>  deps += $(sim_asm_deps)
>>  junk += $(sim_asm_deps) $(sim_asm_objs)
>>
>> +semihost_asm_objs = $(patsubst %.S, semihost-%.o, $(notdir
>> $(gloss_asm_srcs)))
>> +semihost_asm_deps = $(patsubst %.S, semihost-%.d, $(notdir
>> $(gloss_asm_srcs)))
>> +
>> +$(semihost_asm_objs) : semihost-%.o : %.S
>> +       $(COMPILE) -c -DUSING_SEMIHOST_SPECS -o $@ $<
>> +
>> +semihost_objs += $(semihost_asm_objs)
>> +deps += $(semihost_asm_deps)
>> +junk += $(semihost_asm_deps) $(semihost_asm_objs)
>> +
>>
>>  #-------------------------------------------------------------------------
>>  # Build libgloss.a
>>
>>  #-------------------------------------------------------------------------
>> @@ -187,6 +238,19 @@ junk += $(sim_lib)
>>
>>  install_libs += $(sim_lib)
>>
>>
>> +#-------------------------------------------------------------------------
>> +# Build libsemihost.a
>>
>> +#-------------------------------------------------------------------------
>> +
>> +semihost_lib  = libsemihost.a
>> +$(semihost_lib) : $(semihost_objs)
>> +       $(AR) rcv $@ $^
>> +       $(RANLIB) $@
>> +
>> +junk += $(semihost_lib)
>> +
>> +install_libs += $(semihost_lib)
>> +
>>
>>  #-------------------------------------------------------------------------
>>  # Build crt0.o
>>
>>  #-------------------------------------------------------------------------
>> diff --git a/libgloss/riscv/machine/syscall.h
>> b/libgloss/riscv/machine/syscall.h
>> index 5cd15b848..88b9fdfda 100644
>> --- a/libgloss/riscv/machine/syscall.h
>> +++ b/libgloss/riscv/machine/syscall.h
>> @@ -54,4 +54,29 @@
>>  #define SYS_time 1062
>>  #define SYS_getmainvars 2011
>>
>> +/* Semihosting operations.  */
>> +#define SEMIHOST_clock 0x10
>> +#define SEMIHOST_close 0x02
>> +#define SEMIHOST_elapsed 0x30
>> +#define SEMIHOST_errno 0x13
>> +#define SEMIHOST_exit 0x18
>> +#define SEMIHOST_exit_extended 0x20
>> +#define SEMIHOST_flen 0x0C
>> +#define SEMIHOST_get_cmdline 0x15
>> +#define SEMIHOST_heapinfo 0x16
>> +#define SEMIHOST_iserror 0x08
>> +#define SEMIHOST_istty 0x09
>> +#define SEMIHOST_open 0x01
>> +#define SEMIHOST_read 0x06
>> +#define SEMIHOST_readc 0x07
>> +#define SEMIHOST_remove 0x0E
>> +#define SEMIHOST_rename 0x0F
>> +#define SEMIHOST_seek 0x0A
>> +#define SEMIHOST_system 0x12
>> +#define SEMIHOST_tickfreq 0x31
>> +#define SEMIHOST_time 0x11
>> +#define SEMIHOST_tmpnam 0x0D
>> +#define SEMIHOST_write 0x05
>> +#define SEMIHOST_writec 0x03
>> +#define SEMIHOST_write0 0x04
>>  #endif
>> diff --git a/libgloss/riscv/semihost-sys_close.c
>> b/libgloss/riscv/semihost-sys_close.c
>> new file mode 100644
>> index 000000000..47402340c
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_close.c
>> @@ -0,0 +1,28 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include "semihost_syscall.h"
>> +#include "semihost_fdtable.h"
>> +
>> +/* Close a file.  */
>> +int
>> +_close (int file)
>> +{
>> +  long res;
>> +  struct fdentry *fd =__get_fdentry (file);
>> +  long data_block[1];
>> +
>> +  if (fd == NULL)
>> +    return -1;
>> +
>> +  data_block[0] = fd->handle;
>> +  res = syscall_errno (SEMIHOST_close, data_block);
>> +
>> +  if (res != 0)
>> +    return res;
>> +
>> +  __remove_fdentry (file);
>> +  return 0;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_exit.c
>> b/libgloss/riscv/semihost-sys_exit.c
>> new file mode 100644
>> index 000000000..626fb6aeb
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_exit.c
>> @@ -0,0 +1,23 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include "semihost_syscall.h"
>> +
>> +#define ADP_Stopped_ApplicationExit 0x20026
>> +
>> +/* Exit a program without cleaning up files.  */
>> +void
>> +_exit (int exit_status)
>> +{
>> +#if __riscv_xlen == 32
>> +  syscall_errno (SEMIHOST_exit, (long *) ADP_Stopped_ApplicationExit);
>> +#else
>> +  /* The semihosting exit operation only allows 64-bit targets to report
>> the
>> +     exit code.  */
>> +  long data_block[] = {ADP_Stopped_ApplicationExit, exit_status};
>> +  syscall_errno (SEMIHOST_exit, data_block);
>> +#endif
>> +  while (1);
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_fdtable.c
>> b/libgloss/riscv/semihost-sys_fdtable.c
>> new file mode 100644
>> index 000000000..152c92d15
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_fdtable.c
>> @@ -0,0 +1,85 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include "semihost_fdtable.h"
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <unistd.h>
>> +
>> +#ifndef RISCV_MAX_OPEN_FILES
>> +#define RISCV_MAX_OPEN_FILES 16
>> +#endif
>> +
>> +extern int errno;
>> +extern int _open (const char *, int, ...);
>> +
>> +/* fdtable keeps track of the position of each file and is used to map
>> stdin,
>> +   stdout and stderr to STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO.
>> */
>> +
>> +static struct fdentry fdtable[RISCV_MAX_OPEN_FILES];
>> +
>> +/* Initialize fdtable.  A handle of -1 denotes an empty entry.  */
>> +
>> +void __attribute__ ((constructor))
>> +init_semihosting ()
>> +{
>> +  int handle;
>> +
>> +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
>> +    fdtable[i].handle = -1;
>> +
>> +  /* Set up std streams.  */
>> +  /* stdin.  */
>> +  handle = _open (":tt", O_RDONLY);
>> +  fdtable[STDIN_FILENO].handle = handle;
>> +  fdtable[STDIN_FILENO].pos = 0;
>> +
>> +  /* stdout.  */
>> +  handle = _open (":tt", O_WRONLY|O_CREAT|O_TRUNC);
>> +  fdtable[STDOUT_FILENO].handle = handle;
>> +  fdtable[STDOUT_FILENO].pos = 0;
>> +
>> +  /* stderr.  */
>> +  handle = _open (":tt", O_WRONLY|O_CREAT|O_APPEND);
>> +  fdtable[STDERR_FILENO].handle = handle;
>> +  fdtable[STDERR_FILENO].pos = 0;
>> +}
>> +
>> +/* Add entry to fdtable.  */
>> +
>> +int
>> +__add_fdentry (int handle)
>> +{
>> +  for (int i=0; i<RISCV_MAX_OPEN_FILES; i++)
>> +    if (fdtable[i].handle == -1)
>> +      {
>> +       fdtable[i].handle = handle;
>> +       fdtable[i].pos = 0;
>> +       return i;
>> +      }
>> +  /* Too many open files.  */
>> +  errno = ENFILE;
>> +  return -1;
>> +}
>> +
>> +/* Return the fdentry for file or NULL if not found.  */
>> +
>> +struct fdentry *
>> +__get_fdentry (int file)
>> +{
>> +  if (file<0 || file>=RISCV_MAX_OPEN_FILES || fdtable[file].handle == -1)
>> +    {
>> +      errno = EBADF;
>> +      return NULL;
>> +    }
>> +  return &fdtable[file];
>> +}
>> +
>> +/* Remove entry from fdtable.  */
>> +
>> +void
>> +__remove_fdentry (int file)
>> +{
>> +  fdtable[file].handle = -1;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_fstat.c
>> b/libgloss/riscv/semihost-sys_fstat.c
>> new file mode 100644
>> index 000000000..f57f0c07f
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_fstat.c
>> @@ -0,0 +1,19 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <string.h>
>> +#include <sys/stat.h>
>> +#include "semihost_stat.h"
>> +
>> +/* Status of an open file.  The sys/stat.h header file required is
>> +   distributed in the include subdirectory for this C library.  */
>> +
>> +int
>> +_fstat (int file, struct stat *st)
>> +{
>> +  /* Initialize st as not all fields will be set.  */
>> +  memset (st, 0, sizeof (*st));
>> +
>> +  return __stat_common (file, st);
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_ftime.c
>> b/libgloss/riscv/semihost-sys_ftime.c
>> new file mode 100644
>> index 000000000..aeafc6ca2
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_ftime.c
>> @@ -0,0 +1,16 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include <sys/timeb.h>
>> +#include "semihost_syscall.h"
>> +
>> +/* Get the current time.  */
>> +int
>> +_ftime (struct timeb *tp)
>> +{
>> +  tp->time = syscall_errno (SEMIHOST_time, 0);
>> +  tp->millitm = 0;
>> +  return 0;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_isatty.c
>> b/libgloss/riscv/semihost-sys_isatty.c
>> new file mode 100644
>> index 000000000..02d8e39cb
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_isatty.c
>> @@ -0,0 +1,21 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include <sys/stat.h>
>> +#include "semihost_syscall.h"
>> +#include "semihost_fdtable.h"
>> +
>> +int
>> +_isatty (int file)
>> +{
>> +  struct fdentry *fd =__get_fdentry (file);
>> +  long data_block[1];
>> +
>> +  if (fd == NULL)
>> +    return -1;
>> +
>> +  data_block[0] = fd->handle;
>> +  return syscall_errno (SEMIHOST_istty, data_block);
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_link.c
>> b/libgloss/riscv/semihost-sys_link.c
>> new file mode 100644
>> index 000000000..717c5c81c
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_link.c
>> @@ -0,0 +1,9 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +/* Establish a new name for an existing file.  */
>> +int _link (const char *old_name, const char *new_name)
>> +{
>> +  return -1;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_lseek.c
>> b/libgloss/riscv/semihost-sys_lseek.c
>> new file mode 100644
>> index 000000000..68fccf2ff
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_lseek.c
>> @@ -0,0 +1,70 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include <errno.h>
>> +#include <sys/types.h>
>> +#include <unistd.h>
>> +#include "semihost_syscall.h"
>> +#include "semihost_fdtable.h"
>> +
>> +extern int errno;
>> +
>> +/* Set position in a file.  */
>> +off_t
>> +_lseek (int file, off_t offset, int dir)
>> +{
>> +  long data_block[2];
>> +  long flen;
>> +  long res;
>> +  struct fdentry *fd;
>> +  off_t abs_pos;
>> +
>> +  fd =__get_fdentry (file);
>> +  if (fd == NULL)
>> +    {
>> +      errno = EBADF;
>> +      return -1;
>> +    }
>> +
>> +  if (dir == SEEK_CUR && offset == 0)
>> +    return fd->pos;
>> +
>> +  data_block[0] = fd->handle;
>> +
>> +  switch (dir)
>> +    {
>> +      case SEEK_SET:
>> +       abs_pos = offset;
>> +       break;
>> +      case SEEK_CUR:
>> +       abs_pos = fd->pos + offset;
>> +       break;
>> +      case SEEK_END:
>> +       data_block[1] = 0;
>> +       flen = syscall_errno (SEMIHOST_flen, data_block);
>> +       if (flen == -1)
>> +         return -1;
>> +       abs_pos = flen + offset;
>> +       break;
>> +      default:
>> +       errno = EINVAL;
>> +       return -1;
>> +    }
>> +
>> +  if (abs_pos < 0)
>> +    {
>> +      errno = EINVAL;
>> +      return -1;
>> +    }
>> +
>> +  data_block[1] = abs_pos;
>> +  res = syscall_errno (SEMIHOST_seek, data_block);
>> +  if (res == 0)
>> +    {
>> +      fd->pos = abs_pos;
>> +      return abs_pos;
>> +    }
>> +  return res;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_open.c
>> b/libgloss/riscv/semihost-sys_open.c
>> new file mode 100644
>> index 000000000..22f1d8206
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_open.c
>> @@ -0,0 +1,62 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include "semihost_syscall.h"
>> +#include "semihost_fdtable.h"
>> +#include <errno.h>
>> +#include <string.h>
>> +#include <fcntl.h>
>> +
>> +extern int errno;
>> +
>> +#define SEMIHOST_MODE_R 0
>> +#define SEMIHOST_MODE_RPLUS 2
>> +#define SEMIHOST_MODE_W 4
>> +#define SEMIHOST_MODE_WPLUS 6
>> +#define SEMIHOST_MODE_A 8
>> +#define SEMIHOST_MODE_APLUS 10
>> +
>> +/* Open a file.  */
>> +int
>> +_open (const char *name, int flags, ...)
>> +{
>> +  int fh;
>> +  int mode;
>> +  long data_block[3];
>> +
>> +  /* Work out mode from flags.  */
>> +  if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
>> +    mode = SEMIHOST_MODE_R;
>> +  else if ((flags & (O_WRONLY | O_CREAT | O_TRUNC))
>> +          == (O_WRONLY | O_CREAT | O_TRUNC))
>> +    mode = SEMIHOST_MODE_W;
>> +  else if ((flags & (O_WRONLY | O_CREAT | O_APPEND))
>> +          == (O_WRONLY | O_CREAT | O_APPEND))
>> +    mode = SEMIHOST_MODE_A;
>> +  else if ((flags & (O_RDWR | O_CREAT | O_TRUNC))
>> +          == (O_RDWR | O_CREAT | O_TRUNC))
>> +    mode = SEMIHOST_MODE_WPLUS;
>> +  else if ((flags & (O_RDWR | O_CREAT | O_APPEND))
>> +          == (O_RDWR | O_CREAT | O_APPEND))
>> +    mode = SEMIHOST_MODE_APLUS;
>> +  else if (flags & O_RDWR)
>> +    mode = SEMIHOST_MODE_RPLUS;
>> +  else
>> +    {
>> +      errno = EINVAL;
>> +      return -1;
>> +    }
>> +
>> +  data_block[0] = (long) name;
>> +  data_block[1] = mode;
>> +  data_block[2] = strlen (name);
>> +  fh = syscall_errno (SEMIHOST_open, data_block);
>> +  /* Failed to open file.  */
>> +  if (fh == -1)
>> +    return -1;
>> +
>> +  /* Register the file in the fdtable.  */
>> +  return __add_fdentry (fh);
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_read.c
>> b/libgloss/riscv/semihost-sys_read.c
>> new file mode 100644
>> index 000000000..3164eed56
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_read.c
>> @@ -0,0 +1,32 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include <errno.h>
>> +#include <sys/types.h>
>> +#include "semihost_syscall.h"
>> +#include "semihost_fdtable.h"
>> +
>> +/* Read from a file.  */
>> +ssize_t _read (int file, void *ptr, size_t len)
>> +{
>> +  struct fdentry *fd =__get_fdentry (file);
>> +  long data_block[3];
>> +  long res;
>> +
>> +  if (fd == NULL)
>> +    return -1;
>> +
>> +  data_block[0] = fd->handle;
>> +  data_block[1] = (long) ptr;
>> +  data_block[2] = len;
>> +  res = syscall_errno (SEMIHOST_read, data_block);
>> +  if (res >= 0)
>> +    {
>> +      ssize_t bytes_read = len - res;
>> +      fd->pos += bytes_read;
>> +      return bytes_read;
>> +    }
>> +  return -1;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_sbrk.c
>> b/libgloss/riscv/semihost-sys_sbrk.c
>> new file mode 100644
>> index 000000000..cbd035832
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_sbrk.c
>> @@ -0,0 +1,26 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +/* Semihosting requires that sbrk be implemented without a syscall.  */
>> +extern char _end[];               /* _end is set in the linker command
>> file.  */
>> +char *heap_ptr;
>> +
>> +/*
>> + * sbrk -- changes heap size size.  Get nbytes more
>> + *         RAM.  We just increment a pointer in what's
>> + *         left of memory on the board.
>> + */
>> +char *
>> +_sbrk (nbytes)
>> +     int nbytes;
>> +{
>> +  char *base;
>> +
>> +  if (!heap_ptr)
>> +    heap_ptr = (char *)&_end;
>> +  base = heap_ptr;
>> +  heap_ptr += nbytes;
>> +
>> +  return base;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_stat.c
>> b/libgloss/riscv/semihost-sys_stat.c
>> new file mode 100644
>> index 000000000..4015801b9
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_stat.c
>> @@ -0,0 +1,36 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include <string.h>
>> +#include <fcntl.h>
>> +#include "semihost_stat.h"
>> +
>> +/* Status of a file (by name).  */
>> +
>> +int
>> +_stat (const char *name, struct stat *st)
>> +{
>> +  int file;
>> +  int res;
>> +
>> +  /* Initialize st as not all fields will be set.  */
>> +  memset (st, 0, sizeof (*st));
>> +
>> +  /* Try to open file.  */
>> +  file = _open (name, O_RDONLY);
>> +  if (file == -1)
>> +    /* _open should have already set errno.  */
>> +    return -1;
>> +
>> +  /* File opened successfully, infer read permission for owner and
>> assume it is
>> +     a regular file.  */
>> +  st->st_mode |= S_IREAD | S_IFREG;
>> +
>> +  /* Fill in more info.  */
>> +  res = __stat_common (file, st);
>> +
>> +  _close (file);
>> +  return res;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_stat_common.c
>> b/libgloss/riscv/semihost-sys_stat_common.c
>> new file mode 100644
>> index 000000000..b38eb0863
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_stat_common.c
>> @@ -0,0 +1,36 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include "semihost_syscall.h"
>> +#include <sys/stat.h>
>> +#include "semihost_fdtable.h"
>> +
>> +/* Used by _fstat and _stat to fill in some common details.  */
>> +
>> +int
>> +__stat_common (int file, struct stat *st)
>> +{
>> +  int flen;
>> +  struct fdentry *fd =__get_fdentry (file);
>> +  long data_block[1];
>> +
>> +  if (fd == NULL)
>> +    return -1;
>> +
>> +  data_block[0] = fd->handle;
>> +
>> +  /* Assume character device and default block size of 4096.  */
>> +  st->st_mode |= S_IFCHR;
>> +  st->st_blksize = 4096;
>> +
>> +  /* Attempt to get length of file.  */
>> +  flen = syscall_errno (SEMIHOST_flen, data_block);
>> +  if (flen == -1)
>> +    return -1;
>> +
>> +  st->st_size = flen;
>> +
>> +  return 0;
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_unlink.c
>> b/libgloss/riscv/semihost-sys_unlink.c
>> new file mode 100644
>> index 000000000..1d2a6a0f9
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_unlink.c
>> @@ -0,0 +1,15 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include "semihost_syscall.h"
>> +#include <string.h>
>> +
>> +/* Remove a file's directory entry.  */
>> +int
>> +_unlink (const char *name)
>> +{
>> +  long data_block[] = {(long) name, strlen (name)};
>> +  return syscall_errno (SEMIHOST_remove, data_block);
>> +}
>> diff --git a/libgloss/riscv/semihost-sys_write.c
>> b/libgloss/riscv/semihost-sys_write.c
>> new file mode 100644
>> index 000000000..9aee6d30b
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost-sys_write.c
>> @@ -0,0 +1,32 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <machine/syscall.h>
>> +#include <sys/types.h>
>> +#include "semihost_syscall.h"
>> +#include "semihost_fdtable.h"
>> +
>> +/* Write to a file.  */
>> +ssize_t
>> +_write (int file, const void *ptr, size_t len)
>> +{
>> +  struct fdentry *fd =__get_fdentry (file);
>> +  long data_block[3];
>> +  long res;
>> +
>> +  if (fd == NULL)
>> +    return -1;
>> +
>> +  data_block[0] = fd->handle;
>> +  data_block[1] = (long) ptr;
>> +  data_block[2] = len;
>> +  res = syscall_errno (SEMIHOST_write, data_block);
>> +  if (res >= 0)
>> +    {
>> +      ssize_t bytes_written = len - res;
>> +      fd->pos += bytes_written;
>> +      return bytes_written;
>> +    }
>> +  return -1;
>> +}
>> diff --git a/libgloss/riscv/semihost.specs b/libgloss/riscv/semihost.specs
>> new file mode 100644
>> index 000000000..1c86c67e4
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost.specs
>> @@ -0,0 +1,10 @@
>> +# Spec file for semihosting syscalls.
>> +
>> +%rename lib    semihost_lib
>> +%rename link   semihost_link
>> +
>> +*lib:
>> +--start-group -lc -lsemihost --end-group
>> +
>> +*link:
>> +%(semihost_link) %:replace-outfile(-lgloss -lsemihost)
>> diff --git a/libgloss/riscv/semihost_fdtable.h
>> b/libgloss/riscv/semihost_fdtable.h
>> new file mode 100644
>> index 000000000..f596a409a
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost_fdtable.h
>> @@ -0,0 +1,21 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <sys/types.h>
>> +
>> +#ifndef RISCV_SEMIHOST_FDTABLE_H
>> +#define RISCV_SEMIHOST_FDTABLE_H
>> +
>> +extern void __attribute__ ((constructor)) init_semihosting ();
>> +extern int __add_fdentry (int);
>> +extern struct fdentry * __get_fdentry (int);
>> +extern void __remove_fdentry (int);
>> +
>> +struct fdentry
>> +{
>> +  int handle;
>> +  off_t pos;
>> +};
>> +
>> +#endif
>> diff --git a/libgloss/riscv/semihost_stat.h
>> b/libgloss/riscv/semihost_stat.h
>> new file mode 100644
>> index 000000000..c040fe8e7
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost_stat.h
>> @@ -0,0 +1,14 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#include <sys/types.h>
>> +
>> +#ifndef RISCV_SEMIHOST_STAT_H
>> +#define RISCV_SEMIHOST_STAT_H
>> +
>> +extern int __stat_common (int, struct stat *);
>> +extern int _open (const char *, int, ...);
>> +extern int _close (int);
>> +
>> +#endif
>> diff --git a/libgloss/riscv/semihost_syscall.h
>> b/libgloss/riscv/semihost_syscall.h
>> new file mode 100644
>> index 000000000..50e731b40
>> --- /dev/null
>> +++ b/libgloss/riscv/semihost_syscall.h
>> @@ -0,0 +1,47 @@
>> +/*
>> + * Copyright (C) 2020 Embecosm Limited
>> + * SPDX-License-Identifier: BSD-2-Clause
>> + */
>> +#ifndef _INTERNAL_SYSCALL_H
>> +#define _INTERNAL_SYSCALL_H
>> +
>> +extern int errno;
>> +
>> +static inline long
>> +__semihost_syscall (long id, long *data_block)
>> +{
>> +  register long a0 asm ("a0") = id;
>> +  register long a1 asm ("a1") = (long) data_block;
>> +
>> +  /* RISC-V semihosting trap sequence.  Must be uncompressed and must not
>> +     cross page boundary.  */
>> +  asm volatile (
>> +    ".balign 16             \n"
>> +    ".option push           \n"
>> +    ".option norvc          \n"
>> +    "slli zero, zero, 0x1f  \n"
>> +    "ebreak                 \n"
>> +    "srai zero, zero, 0x7   \n"
>> +    ".option pop            \n"
>> +      : "+r"(a0) : "r"(a1) : "memory");
>> +
>> +  return a0;
>> +}
>> +
>> +static inline long
>> +__syscall_error ()
>> +{
>> +  errno = __semihost_syscall (SEMIHOST_errno, 0);
>> +  return -1;
>> +}
>> +
>> +static inline long
>> +syscall_errno (long id, long *data_block)
>> +{
>> +  long res = __semihost_syscall (id, data_block);
>> +  if (res < 0)
>> +    return __syscall_error ();
>> +  return res;
>> +}
>> +
>> +#endif
>> --
>> 2.17.1
>>
>>
>>

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2020-12-16 23:31 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-30 11:36 [PATCH] RISC-V: Add semihosting support Craig Blackmore
2020-11-30 14:05 ` Kito Cheng
2020-11-30 19:53 ` Jeff Johnston
2020-12-09  8:21 ` Kito Cheng
2020-12-15 12:00   ` Craig Blackmore
2020-12-15 22:51     ` Jeff Johnston
2020-12-16  8:33       ` Craig Blackmore
2020-12-16  8:43         ` Kito Cheng
2020-12-16 23:31         ` Jeff Johnston

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).