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
+
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
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
#-------------------------------------------------------------------------
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
#-------------------------------------------------------------------------
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
#-------------------------------------------------------------------------
#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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+# 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)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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