[newlib-cygwin] RISC-V: Add semihosting support

Jeff Johnston jjohnstn@sourceware.org
Wed Dec 16 21:41:02 GMT 2020


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=865cd30dcc2f00c81c8b3624a9f3464138cd24a5

commit 865cd30dcc2f00c81c8b3624a9f3464138cd24a5
Author: Craig Blackmore <craig.blackmore@embecosm.com>
Date:   Tue Dec 15 12:00:27 2020 +0000

    RISC-V: Add semihosting support

Diff:
---
 COPYING.LIBGLOSS                          |  5 ++
 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 +++++++++++++++++
 22 files changed, 697 insertions(+), 1 deletion(-)

diff --git a/COPYING.LIBGLOSS b/COPYING.LIBGLOSS
index 2c64ed2b2..7ae0cd9a4 100644
--- a/COPYING.LIBGLOSS
+++ b/COPYING.LIBGLOSS
@@ -478,3 +478,8 @@ including the implied warranties of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  A copy of this license is available at
 http://www.opensource.org/licenses.
 
+(24) - RISC-V Semihosting (riscv-* targets)
+
+Copyright (C) 2020 Embecosm Limited
+SPDX-License-Identifier: BSD-2-Clause
+
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


More information about the Cygwin-cvs mailing list