From 39ee91af7dcb9dc1d9d265926a1df317726e549a Mon Sep 17 00:00:00 2001 From: gabriel Date: Mon, 26 Feb 2024 20:41:15 +0100 Subject: [PATCH] first semihosting integration --- env/entry.S | 2 +- libwrap/CMakeLists.txt | 54 +++++++++++++++++++ libwrap/semihosting/semihosting.c | 90 +++++++++++++++++++++++++++++++ libwrap/semihosting/semihosting.h | 14 +++++ libwrap/semihosting/trap.c | 60 +++++++++++++++++++++ 5 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 libwrap/CMakeLists.txt create mode 100644 libwrap/semihosting/semihosting.c create mode 100644 libwrap/semihosting/semihosting.h create mode 100644 libwrap/semihosting/trap.c diff --git a/env/entry.S b/env/entry.S index fef8b9e..ae059d4 100644 --- a/env/entry.S +++ b/env/entry.S @@ -41,7 +41,7 @@ trap_entry: csrr a0, mcause csrr a1, mepc mv a2, sp - call handle_trap + call trap csrw mepc, a0 #ifndef __riscv_abi_rve addi sp, sp, -8*REGBYTES diff --git a/libwrap/CMakeLists.txt b/libwrap/CMakeLists.txt new file mode 100644 index 0000000..7af5136 --- /dev/null +++ b/libwrap/CMakeLists.txt @@ -0,0 +1,54 @@ + +IF(NOT DEFINED _MK_LIBWRAP) + +SET(_MK_LIBWRAP TRUE) + +SET(LIBWRAP_DIR ${CMAKE_CURRENT_LIST_DIR}) + +SET(LIBWRAP_SRCS + ${LIBWRAP_DIR}/stdlib/malloc.c + ${LIBWRAP_DIR}/sys/open.c + ${LIBWRAP_DIR}/sys/lseek.c + ${LIBWRAP_DIR}/sys/read.c + ${LIBWRAP_DIR}/sys/write.c + ${LIBWRAP_DIR}/sys/fstat.c + ${LIBWRAP_DIR}/sys/stat.c + ${LIBWRAP_DIR}/sys/close.c + ${LIBWRAP_DIR}/sys/link.c + ${LIBWRAP_DIR}/sys/unlink.c + ${LIBWRAP_DIR}/sys/execve.c + ${LIBWRAP_DIR}/sys/fork.c + ${LIBWRAP_DIR}/sys/getpid.c + ${LIBWRAP_DIR}/sys/kill.c + ${LIBWRAP_DIR}/sys/wait.c + ${LIBWRAP_DIR}/sys/isatty.c + ${LIBWRAP_DIR}/sys/times.c + ${LIBWRAP_DIR}/sys/sbrk.c + ${LIBWRAP_DIR}/sys/_exit.c + ${LIBWRAP_DIR}/misc/write_hex.c + ${LIBWRAP_DIR}/sys/printf.c +) +IF(${SEMIHOSTING}) + SET(LIBWRAP_SRCS ${LIBWRAP_SRCS} ${LIBWRAP_DIR}/semihosting/semihosting.c ${LIBWRAP_DIR}/semihosting/trap.c) +ENDIF() + +SET(LIBWRAP_SYMS malloc free open lseek read write fstat stat close link unlink execve fork getpid jukk wait isatty times sbrk _exit printf puts) +# Includes +INCLUDE_DIRECTORIES( + ${LIBWRAP_DIR} + ${LIBWRAP_DIR}/../include + ${LIBWRAP_DIR}/../drivers + ${LIBWRAP_DIR}/../env + ${LIBWRAP_DIR}/../env/iss +) + +ADD_LIBRARY(LIBWRAP_TGC STATIC ${LIBWRAP_SRCS}) +TARGET_COMPILE_OPTIONS(LIBWRAP_TGC PRIVATE -march=${RISCV_ARCH}_zicsr_zifencei -mabi=${RISCV_ABI} "-DBOARD_${BOARD}") + +FOREACH(SYM ${LIBWRAP_SYMS}) + LIST(APPEND WRAP_LDFLAGS "-Wl,--wrap=${SYM}") +ENDFOREACH() + +SET(LIBWRAP_TGC_LDFLAGS ${WRAP_LDFLAGS} "-Wl,--start-group" "-Wl,--end-group") + +ENDIF(NOT DEFINED _MK_LIBWRAP) diff --git a/libwrap/semihosting/semihosting.c b/libwrap/semihosting/semihosting.c new file mode 100644 index 0000000..5246a11 --- /dev/null +++ b/libwrap/semihosting/semihosting.c @@ -0,0 +1,90 @@ +#include +#include +#include + +#include "semihosting.h" + +#define SEMIHOSTING_SYS_OPEN 0x01 +#define SEMIHOSTING_SYS_CLOSE 0x02 +#define SEMIHOSTING_SYS_WRITEC 0x03 +#define SEMIHOSTING_SYS_WRITE0 0x04 +#define SEMIHOSTING_SYS_WRITE 0x05 +#define SEMIHOSTING_SYS_READ 0x06 +#define SEMIHOSTING_SYS_READC 0x07 +#define SEMIHOSTING_SYS_ISERROR 0x08 +#define SEMIHOSTING_SYS_ISTTY 0x09 +#define SEMIHOSTING_SYS_SEEK 0x0A +#define SEMIHOSTING_SYS_FLEN 0x0C +#define SEMIHOSTING_SYS_TMPNAM 0x0D +#define SEMIHOSTING_SYS_REMOVE 0x0E +#define SEMIHOSTING_SYS_RENAME 0x0F +#define SEMIHOSTING_SYS_CLOCK 0x10 +#define SEMIHOSTING_SYS_TIME 0x11 +#define SEMIHOSTING_SYS_SYSTEM 0x12 +#define SEMIHOSTING_SYS_ERRNO 0x13 +#define SEMIHOSTING_SYS_GET_CMDLINE 0x15 +#define SEMIHOSTING_SYS_HEAPINFO 0x16 +#define SEMIHOSTING_EnterSVC 0x17 +#define SEMIHOSTING_ReportException 0x18 +#define SEMIHOSTING_SYS_ELAPSED 0x30 +#define SEMIHOSTING_SYS_TICKFREQ 0x31 + +#define RISCV_SEMIHOSTING_CALL_NUMBER 7 + + +static inline int __attribute__ ((always_inline)) call_host(int reason, void* arg) { +#if 1 + // This must always be set back to 0 to cover the case where a host wasn't + // initially present, but only connected while the program was already up and + // running. In that case, trap() suddenly won't be called anymore, so we have to + // clear this variable *before* EBREAK is called. + sh_missing_host = 0; + + register int value asm ("a0") = reason; + register void* ptr asm ("a1") = arg; + asm volatile ( + // Workaround for RISC-V lack of multiple EBREAKs. + " .option push \n" + " .option norvc \n" + // Force 16-byte alignment to make sure that the 3 instruction fall + // within the same virtual page. If you the instruction straddle a page boundary + // the debugger fetching the instructions could lead to a page fault. + // Note: align 4 means, align by 2 to the power of 4! + " .align 4 \n" + " slli x0, x0, 0x1f \n" + " ebreak \n" + " srai x0, x0, 0x07 \n" + " .option pop \n" + + : "=r" (value) /* Outputs */ + : "0" (value), "r" (ptr), [swi] "i" (RISCV_SEMIHOSTING_CALL_NUMBER) /* Inputs */ + : "memory" /* Clobbers */ + ); + return value; +#else + return 0; +#endif +} + +void sh_write0(const char* buf) +{ + // Print zero-terminated string + call_host(SEMIHOSTING_SYS_WRITE0, (void*) buf); +} + +void sh_writec(char c) +{ + // Print single character + call_host(SEMIHOSTING_SYS_WRITEC, (void*)&c); +} + +char sh_readc(void) +{ + // Read character from keyboard. (Blocking operation!) + char c = call_host(SEMIHOSTING_SYS_READC, (void*)NULL); + + if (sh_missing_host) + return 0; + else + return c; +} \ No newline at end of file diff --git a/libwrap/semihosting/semihosting.h b/libwrap/semihosting/semihosting.h new file mode 100644 index 0000000..f5b0317 --- /dev/null +++ b/libwrap/semihosting/semihosting.h @@ -0,0 +1,14 @@ +#ifndef SEMIHOSTING_H +#define SEMIHOSTING_H + +//int32_t trace_write(const char* buf, uint32_t nbyte); + +void sh_write0(const char* buf); +void sh_writec(char c); +char sh_readc(void); + +int getchar(void); + +extern int sh_missing_host; + +#endif \ No newline at end of file diff --git a/libwrap/semihosting/trap.c b/libwrap/semihosting/trap.c new file mode 100644 index 0000000..122dae8 --- /dev/null +++ b/libwrap/semihosting/trap.c @@ -0,0 +1,60 @@ +#include +#include +#include + +#include "encoding.h" + +#define EBREAK_OPCODE 0x00100073 +#define EBREAK_MCAUSE 0x00000003 + +#define SLLI_X0_X0_0X1F_OPCODE 0x01f01013 +#define SRAI_X0_X0_0X07_OPCODE 0x40705013 + +int sh_missing_host = 0; + +void trap() +{ + uint32_t mepc = read_csr(0x341); // Address of trap + uint32_t mtval = read_csr(0x343); // Instruction value of trap + uint32_t mcause = read_csr(0x342); // Reason for the trap + + if (mcause == EBREAK_MCAUSE && mtval == EBREAK_OPCODE){ + // This trap was caused by an EBREAK... + + int aligned = ((mepc-4) & 0x0f) == 0; + if (aligned + && *(uint32_t *)mepc == EBREAK_OPCODE + && *(uint32_t *)(mepc-4) == SLLI_X0_X0_0X1F_OPCODE + && *(uint32_t *)(mepc+4) == SRAI_X0_X0_0X07_OPCODE) + { + // The EBREAK was part of the semihosting call. (See semihosting.c) + // + // If a debugger were connected, this would have resulted in a CPU halt, + // and the debugger would have serviced the the semihosting call. + // + // However, the semihosting function was called without a debugger being + // attached. The best course of action is to simply return from the trap + // and let the semihosting function continue after the call to EBREAK to + // prevent the CPU from hanging in the trap handler. + write_csr(mepc, mepc+4); + + // Set a global variable to tell the semihosting code the the semihosting + // call + // didn't execute on the host. + sh_missing_host = 1; + + return; + } + + // EBREAK was not part of a semihosting call. This should not have happened. + // Hang forever. + while(1) + ; + } + + // Trap was issued for another reason than an EBREAK. + // Replace the code below with whatever trap handler you'd normally use. (e.g. interrupt + // processing.) + while(1) + ; +} \ No newline at end of file