diff options
-rwxr-xr-x | debug/gdbserver.py | 32 | ||||
-rw-r--r-- | debug/programs/semihosting.c | 73 | ||||
-rw-r--r-- | debug/programs/semihosting.h | 82 | ||||
-rw-r--r-- | debug/targets.py | 4 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike-1.cfg | 2 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike-2-hwthread.cfg | 6 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike-2.cfg | 9 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike32-2-rtos.py | 1 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike64-2-rtos.py | 1 |
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, |