[PATCH] RISC-V: Add semihosting support

Jeff Johnston jjohnstn@redhat.com
Mon Nov 30 19:53:35 GMT 2020


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
>
>
>


More information about the Newlib mailing list