aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2020-05-26 12:37:09 -0700
committerGitHub <noreply@github.com>2020-05-26 12:37:09 -0700
commitfbe74f48e16be28a2b360e8a9e845b01d9e4b167 (patch)
tree869b79d740a4c5f405f35eb0cab9275ea7da47e0
parentbc4c1d2866b896f6234d19111993fae4a9f47d74 (diff)
downloadriscv-tests-fbe74f48e16be28a2b360e8a9e845b01d9e4b167.zip
riscv-tests-fbe74f48e16be28a2b360e8a9e845b01d9e4b167.tar.gz
riscv-tests-fbe74f48e16be28a2b360e8a9e845b01d9e4b167.tar.bz2
Test semihosting calls (#280)
* Add a basic semihosting test. * Need to configure semihosting on each target. * WIP * Parse "cannot insert breakpoint" message. Also use sys.exit instead of exit, per new pylint's suggestion.
-rwxr-xr-xdebug/gdbserver.py32
-rw-r--r--debug/programs/semihosting.c73
-rw-r--r--debug/programs/semihosting.h82
-rw-r--r--debug/targets.py4
-rw-r--r--debug/targets/RISC-V/spike-1.cfg2
-rw-r--r--debug/targets/RISC-V/spike-2-hwthread.cfg6
-rw-r--r--debug/targets/RISC-V/spike-2.cfg9
-rw-r--r--debug/targets/RISC-V/spike32-2-rtos.py1
-rw-r--r--debug/targets/RISC-V/spike64-2-rtos.py1
9 files changed, 205 insertions, 5 deletions
diff --git a/debug/gdbserver.py b/debug/gdbserver.py
index ca5855a..dd9e129 100755
--- a/debug/gdbserver.py
+++ b/debug/gdbserver.py
@@ -672,6 +672,38 @@ class UserInterrupt(DebugTest):
self.gdb.p("i=0")
self.exit()
+class Semihosting(GdbSingleHartTest):
+ # Include malloc so that gdb can assign a string.
+ compile_args = ("programs/semihosting.c", "programs/tiny-malloc.c",
+ "-DDEFINE_MALLOC", "-DDEFINE_FREE")
+
+ def early_applicable(self):
+ return self.target.test_semihosting
+
+ def setup(self):
+ self.gdb.load()
+ self.parkOtherHarts()
+ self.gdb.b("_exit")
+
+ def exit(self, expected_result=0):
+ output = self.gdb.c()
+ assertIn("Breakpoint", output)
+ assertIn("_exit", output)
+ assertEqual(self.gdb.p("status"), expected_result)
+
+ def test(self):
+ """Sending gdb ^C while the program is running should cause it to
+ halt."""
+ temp = tempfile.NamedTemporaryFile(suffix=".data")
+
+ self.gdb.b("main:begin")
+ self.gdb.c()
+ self.gdb.p('filename="%s"' % temp.name)
+ self.exit()
+
+ contents = open(temp.name, "r").readlines()
+ assertIn("Hello, world!\n", contents)
+
class InterruptTest(GdbSingleHartTest):
compile_args = ("programs/interrupt.c",)
diff --git a/debug/programs/semihosting.c b/debug/programs/semihosting.c
new file mode 100644
index 0000000..2ceea8c
--- /dev/null
+++ b/debug/programs/semihosting.c
@@ -0,0 +1,73 @@
+#include <stdint.h>
+
+#include "semihosting.h"
+
+size_t strlen(const char *buf)
+{
+ int len = 0;
+ while (buf[len])
+ len++;
+ return len;
+}
+
+#define O_RDONLY 0
+#define O_WRONLY 1
+#define O_RDWR 2
+#define O_RDWR 2
+#define O_TRUNC 0x0800
+
+int errno;
+
+/* These semihosting functions came from the Freedom Metal source. */
+static int open(const char *name, int flags, int mode) {
+ int semiflags = 0;
+
+ switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) {
+ case O_RDONLY:
+ semiflags = 0; /* 'r' */
+ break;
+ case O_WRONLY:
+ if (flags & O_TRUNC)
+ semiflags = 4; /* 'w' */
+ else
+ semiflags = 8; /* 'a' */
+ break;
+ default:
+ if (flags & O_TRUNC)
+ semiflags = 6; /* 'w+' */
+ else
+ semiflags = 10; /* 'a+' */
+ break;
+ }
+
+ volatile semihostparam_t arg = {.param1 = (uintptr_t)name,
+ .param2 = (uintptr_t)semiflags,
+ .param3 = (uintptr_t)strlen(name)};
+
+ int ret = (int)semihost_call_host(SEMIHOST_SYS_OPEN, (uintptr_t)&arg);
+ if (ret == -1)
+ errno = semihost_call_host(SEMIHOST_SYS_ERRNO, 0);
+ return ret;
+}
+
+static ssize_t write(int file, const void *ptr, size_t len)
+{
+ volatile semihostparam_t arg = {.param1 = (uintptr_t)file,
+ .param2 = (uintptr_t)ptr,
+ .param3 = (uintptr_t)len};
+ ssize_t ret =
+ (ssize_t)semihost_call_host(SEMIHOST_SYS_WRITE, (uintptr_t)&arg);
+
+ return (len - ret);
+}
+
+int main()
+{
+ char *filename = NULL;
+ const char *message = "Hello, world!\n";
+ int fd;
+
+begin:
+ fd = open(filename, O_WRONLY, 0644);
+ write(fd, message, strlen(message));
+} \ No newline at end of file
diff --git a/debug/programs/semihosting.h b/debug/programs/semihosting.h
new file mode 100644
index 0000000..201e414
--- /dev/null
+++ b/debug/programs/semihosting.h
@@ -0,0 +1,82 @@
+#include <sys/types.h>
+
+#ifndef _SEMIHOSTING_H_
+#define _SEMIHOSTING_H_
+
+// ----------------------------------------------------------------------------
+
+// Semihosting operations.
+enum Semihost_Sys_Op {
+ // Regular operations
+ SEMIHOST_SYS_CLOCK = 0x10,
+ SEMIHOST_SYS_CLOSE = 0x02,
+ SEMIHOST_SYS_ELAPSED = 0x30,
+ SEMIHOST_SYS_ERRNO = 0x13,
+ SEMIHOST_SYS_EXIT = 0x18,
+ SEMIHOST_SYS_EXIT_EXTENDED = 0x20,
+ SEMIHOST_SYS_FLEN = 0x0C,
+ SEMIHOST_SYS_GET_CMDLINE = 0x15,
+ SEMIHOST_SYS_HEAPINFO = 0x16,
+ SEMIHOST_SYS_ISERROR = 0x08,
+ SEMIHOST_SYS_ISTTY = 0x09,
+ SEMIHOST_SYS_OPEN = 0x01,
+ SEMIHOST_SYS_READ = 0x06,
+ SEMIHOST_SYS_READC = 0x07,
+ SEMIHOST_SYS_REMOVE = 0x0E,
+ SEMIHOST_SYS_RENAME = 0x0F,
+ SEMIHOST_SYS_SEEK = 0x0A,
+ SEMIHOST_SYS_SYSTEM = 0x12,
+ SEMIHOST_SYS_TICKFREQ = 0x31,
+ SEMIHOST_SYS_TIME = 0x11,
+ SEMIHOST_SYS_TMPNAM = 0x0D,
+ SEMIHOST_SYS_WRITE = 0x05,
+ SEMIHOST_SYS_WRITEC = 0x03,
+ SEMIHOST_SYS_WRITE0 = 0x04,
+};
+
+enum ADP_Code {
+ ADP_Stopped_BranchThroughZero = 0x20000,
+ ADP_Stopped_UndefinedInstr = 0x20001,
+ ADP_Stopped_SoftwareInterrupt = 0x20002,
+ ADP_Stopped_PrefetchAbort = 0x20003,
+ ADP_Stopped_DataAbort = 0x20004,
+ ADP_Stopped_AddressException = 0x20005,
+ ADP_Stopped_IRQ = 0x20006,
+ ADP_Stopped_FIQ = 0x20007,
+ ADP_Stopped_BreakPoint = 0x20020,
+ ADP_Stopped_WatchPoint = 0x20021,
+ ADP_Stopped_StepComplete = 0x20022,
+ ADP_Stopped_RunTimeErrorUnknown = 0x20023,
+ ADP_Stopped_InternalError = 0x20024,
+ ADP_Stopped_UserInterruption = 0x20025,
+ ADP_Stopped_ApplicationExit = 0x20026,
+ ADP_Stopped_StackOverflow = 0x20027,
+ ADP_Stopped_DivisionByZero = 0x20028,
+ ADP_Stopped_OSSpecific = 0x20029,
+};
+
+typedef struct {
+ uintptr_t param1;
+ uintptr_t param2;
+ uintptr_t param3;
+} semihostparam_t;
+
+static inline uintptr_t __attribute__((always_inline))
+semihost_call_host(uintptr_t op, uintptr_t arg) {
+ register uintptr_t r0 asm("a0") = op;
+ register uintptr_t r1 asm("a1") = arg;
+
+ asm volatile(".option push \n"
+ ".option norvc \n"
+ " slli zero,zero,0x1f \n"
+ " ebreak \n"
+ " srai zero,zero,0x7 \n"
+ ".option pop \n"
+
+ : "=r"(r0) /* Outputs */
+ : "r"(r0), "r"(r1) /* Inputs */
+ : "memory");
+ return r0;
+}
+
+#endif \ No newline at end of file
diff --git a/debug/targets.py b/debug/targets.py
index f4192b6..9c1ccf0 100644
--- a/debug/targets.py
+++ b/debug/targets.py
@@ -91,6 +91,10 @@ class Target:
# whether they are applicable or not.
skip_tests = []
+ # Set False if semihosting should not be tested in this configuration,
+ # because it doesn't work and isn't expected to work.
+ test_semihosting = True
+
# Internal variables:
directory = None
temporary_files = []
diff --git a/debug/targets/RISC-V/spike-1.cfg b/debug/targets/RISC-V/spike-1.cfg
index d1ed60e..3bc32d1 100644
--- a/debug/targets/RISC-V/spike-1.cfg
+++ b/debug/targets/RISC-V/spike-1.cfg
@@ -26,3 +26,5 @@ set challenge [riscv authdata_read]
riscv authdata_write [expr $challenge + 1]
halt
+
+arm semihosting enable
diff --git a/debug/targets/RISC-V/spike-2-hwthread.cfg b/debug/targets/RISC-V/spike-2-hwthread.cfg
index 31a5f68..94bac00 100644
--- a/debug/targets/RISC-V/spike-2-hwthread.cfg
+++ b/debug/targets/RISC-V/spike-2-hwthread.cfg
@@ -11,7 +11,6 @@ jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
set _TARGETNAME_0 $_CHIPNAME.cpu0
set _TARGETNAME_1 $_CHIPNAME.cpu1
target create $_TARGETNAME_0 riscv -chain-position $_CHIPNAME.cpu -rtos hwthread
-#target create $_TARGETNAME_0 riscv -chain-position $_CHIPNAME.cpu -coreid 0 -rtos hwthread
target create $_TARGETNAME_1 riscv -chain-position $_CHIPNAME.cpu -coreid 1
target smp $_TARGETNAME_0 $_TARGETNAME_1
@@ -29,3 +28,8 @@ set challenge [riscv authdata_read]
riscv authdata_write [expr $challenge + 1]
halt
+
+foreach t [target names] {
+ targets $t
+ arm semihosting enable
+}
diff --git a/debug/targets/RISC-V/spike-2.cfg b/debug/targets/RISC-V/spike-2.cfg
index c9de7d2..0eadb89 100644
--- a/debug/targets/RISC-V/spike-2.cfg
+++ b/debug/targets/RISC-V/spike-2.cfg
@@ -26,7 +26,8 @@ init
set challenge [riscv authdata_read]
riscv authdata_write [expr $challenge + 1]
-targets $_TARGETNAME_0
-halt
-targets $_TARGETNAME_1
-halt
+foreach t [target names] {
+ targets $t
+ halt
+ arm semihosting enable
+}
diff --git a/debug/targets/RISC-V/spike32-2-rtos.py b/debug/targets/RISC-V/spike32-2-rtos.py
index 8872739..e221353 100644
--- a/debug/targets/RISC-V/spike32-2-rtos.py
+++ b/debug/targets/RISC-V/spike32-2-rtos.py
@@ -10,6 +10,7 @@ class spike32_2(targets.Target):
timeout_sec = 30
implements_custom_test = True
support_hasel = False
+ test_semihosting = False
def create(self):
return testlib.Spike(self, progbufsize=0, dmi_rti=4,
diff --git a/debug/targets/RISC-V/spike64-2-rtos.py b/debug/targets/RISC-V/spike64-2-rtos.py
index db6263a..3b9c1d8 100644
--- a/debug/targets/RISC-V/spike64-2-rtos.py
+++ b/debug/targets/RISC-V/spike64-2-rtos.py
@@ -10,6 +10,7 @@ class spike64_2_rtos(targets.Target):
timeout_sec = 60
implements_custom_test = True
support_hasel = False
+ test_semihosting = False
def create(self):
return testlib.Spike(self, abstract_rti=30, support_hasel=False,