From 5a9b2920e872cda1edfadfb5982a65522cf40337 Mon Sep 17 00:00:00 2001 From: Masayuki Ishikawa Date: Mon, 2 May 2022 18:00:26 +0900 Subject: [PATCH] arch: risc-v: Add support for semihosting and hostfs Summary: - This commit adds support for semihosting and hostfs Impact: - None Testing: - Tested with nsh and nsh64 (defconfig will be updated later) Signed-off-by: Masayuki Ishikawa --- arch/risc-v/Kconfig | 19 ++ arch/risc-v/include/syscall.h | 2 + arch/risc-v/src/common/riscv_hostfs.c | 359 ++++++++++++++++++++++++ arch/risc-v/src/common/riscv_semihost.S | 40 +++ arch/risc-v/src/qemu-rv/Make.defs | 5 + 5 files changed, 425 insertions(+) create mode 100644 arch/risc-v/src/common/riscv_hostfs.c create mode 100644 arch/risc-v/src/common/riscv_semihost.S diff --git a/arch/risc-v/Kconfig b/arch/risc-v/Kconfig index 8c4e0d516d..43d28a9787 100644 --- a/arch/risc-v/Kconfig +++ b/arch/risc-v/Kconfig @@ -276,6 +276,25 @@ config RISCV_TOOLCHAIN_GNU_RVG endchoice +config RISCV_SEMIHOSTING_HOSTFS + bool "Semihosting HostFS" + depends on FS_HOSTFS + ---help--- + Mount HostFS through semihosting. + + This doesn't support some directory operations like readdir because + of the limitations of semihosting mechanism. + +if RISCV_SEMIHOSTING_HOSTFS + +config RISCV_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + bool "Cache coherence in semihosting hostfs" + depends on ARCH_DCACHE + ---help--- + Flush & Invalidte cache before & after bkpt instruction. + +endif + source "arch/risc-v/src/opensbi/Kconfig" if ARCH_CHIP_FE310 diff --git a/arch/risc-v/include/syscall.h b/arch/risc-v/include/syscall.h index 9991111acb..ccb751fb07 100644 --- a/arch/risc-v/include/syscall.h +++ b/arch/risc-v/include/syscall.h @@ -150,6 +150,8 @@ extern "C" #define EXTERN extern #endif +long smh_call(unsigned int nbr, void *parm); + #if defined(CONFIG_ARCH_USE_S_MODE) && defined(__KERNEL__) uintptr_t sys_call0(unsigned int nbr); uintptr_t sys_call1(unsigned int nbr, uintptr_t parm1); diff --git a/arch/risc-v/src/common/riscv_hostfs.c b/arch/risc-v/src/common/riscv_hostfs.c new file mode 100644 index 0000000000..d5786e22a2 --- /dev/null +++ b/arch/risc-v/src/common/riscv_hostfs.c @@ -0,0 +1,359 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_hostfs.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HOST_OPEN 0x01 +#define HOST_CLOSE 0x02 +#define HOST_WRITE 0x05 +#define HOST_READ 0x06 +#define HOST_SEEK 0x0a +#define HOST_FLEN 0x0c +#define HOST_REMOVE 0x0e +#define HOST_RENAME 0x0f +#define HOST_ERROR 0x13 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static long host_call(unsigned int nbr, void *parm, size_t size) +{ +#ifdef CONFIG_RISCV_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + up_clean_dcache(parm, parm + size); +#endif + + long ret = smh_call(nbr, parm); + if (ret < 0) + { + long err = smh_call(HOST_ERROR, NULL); + if (err > 0) + { + ret = -err; + } + } + + return ret; +} + +static ssize_t host_flen(long fd) +{ + return host_call(HOST_FLEN, &fd, sizeof(long)); +} + +static int host_flags_to_mode(int flags) +{ + static const int modeflags[] = + { + O_RDONLY, + O_RDONLY | O_BINARY, + O_RDWR, + O_RDWR | O_BINARY, + O_WRONLY | O_CREAT | O_TRUNC, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + O_RDWR | O_CREAT | O_TRUNC, + O_RDWR | O_CREAT | O_TRUNC | O_BINARY, + O_WRONLY | O_CREAT | O_APPEND, + O_WRONLY | O_CREAT | O_APPEND | O_BINARY, + O_RDWR | O_CREAT | O_APPEND, + O_RDWR | O_CREAT | O_APPEND | O_BINARY, + 0, + }; + + int i; + for (i = 0; modeflags[i] != 0; i++) + { + if (modeflags[i] == flags) + { + return i; + } + } + + return -EINVAL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int host_open(const char *pathname, int flags, int mode) +{ + struct + { + const char *pathname; + long mode; + size_t len; + } open = + { + .pathname = pathname, + .mode = host_flags_to_mode(flags), + .len = strlen(pathname), + }; + +#ifdef CONFIG_RISCV_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + up_clean_dcache(pathname, pathname + open.len + 1); +#endif + + return host_call(HOST_OPEN, &open, sizeof(open)); +} + +int host_close(int fd_) +{ + long fd = fd_; + return host_call(HOST_CLOSE, &fd, sizeof(long)); +} + +ssize_t host_read(int fd, void *buf, size_t count) +{ + struct + { + long fd; + void *buf; + size_t count; + } read = + { + .fd = fd, + .buf = buf, + .count = count, + }; + + ssize_t ret; + +#ifdef CONFIG_RISCV_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + up_invalidate_dcache(buf, buf + count); +#endif + + ret = host_call(HOST_READ, &read, sizeof(read)); + + return ret < 0 ? ret : count - ret; +} + +ssize_t host_write(int fd, const void *buf, size_t count) +{ + struct + { + long fd; + const void *buf; + size_t count; + } write = + { + .fd = fd, + .buf = buf, + .count = count, + }; + + ssize_t ret; + +#ifdef CONFIG_RISCV_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + up_clean_dcache(buf, buf + count); +#endif + + ret = host_call(HOST_WRITE, &write, sizeof(write)); + return ret < 0 ? ret : count - ret; +} + +off_t host_lseek(int fd, off_t offset, int whence) +{ + off_t ret = -ENOSYS; + + if (whence == SEEK_END) + { + ret = host_flen(fd); + if (ret >= 0) + { + offset += ret; + whence = SEEK_SET; + } + } + + if (whence == SEEK_SET) + { + struct + { + long fd; + size_t pos; + } seek = + { + .fd = fd, + .pos = offset, + }; + + ret = host_call(HOST_SEEK, &seek, sizeof(seek)); + if (ret >= 0) + { + ret = offset; + } + } + + return ret; +} + +int host_ioctl(int fd, int request, unsigned long arg) +{ + return -ENOSYS; +} + +void host_sync(int fd) +{ +} + +int host_dup(int fd) +{ + return -ENOSYS; +} + +int host_fstat(int fd, struct stat *buf) +{ + memset(buf, 0, sizeof(*buf)); + buf->st_mode = S_IFREG | 0777; + buf->st_size = host_flen(fd); + return buf->st_size < 0 ? buf->st_size : 0; +} + +int host_fchstat(int fd, const struct stat *buf, int flags) +{ + return -ENOSYS; +} + +int host_ftruncate(int fd, off_t length) +{ + return -ENOSYS; +} + +void *host_opendir(const char *name) +{ + return NULL; +} + +int host_readdir(void *dirp, struct dirent *entry) +{ + return -ENOSYS; +} + +void host_rewinddir(void *dirp) +{ +} + +int host_closedir(void *dirp) +{ + return -ENOSYS; +} + +int host_statfs(const char *path, struct statfs *buf) +{ + return 0; +} + +int host_unlink(const char *pathname) +{ + struct + { + const char *pathname; + size_t pathname_len; + } remove = + { + .pathname = pathname, + .pathname_len = strlen(pathname), + }; + +#ifdef CONFIG_RISCV_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + up_clean_dcache(pathname, pathname + + remove.pathname_len + 1); +#endif + + return host_call(HOST_REMOVE, &remove, sizeof(remove)); +} + +int host_mkdir(const char *pathname, mode_t mode) +{ + return -ENOSYS; +} + +int host_rmdir(const char *pathname) +{ + return host_unlink(pathname); +} + +int host_rename(const char *oldpath, const char *newpath) +{ + struct + { + const char *oldpath; + size_t oldpath_len; + const char *newpath; + size_t newpath_len; + } rename = + { + .oldpath = oldpath, + .oldpath_len = strlen(oldpath), + .newpath = newpath, + .newpath_len = strlen(newpath), + }; + +#ifdef CONFIG_RISCV_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + up_clean_dcache(oldpath, oldpath + rename.oldpath_len + 1); + up_clean_dcache(newpath, newpath + rename.newpath_len + 1); +#endif + + return host_call(HOST_RENAME, &rename, sizeof(rename)); +} + +int host_stat(const char *path, struct stat *buf) +{ + int ret = host_open(path, O_RDONLY, 0); + if (ret >= 0) + { + int fd = ret; + ret = host_fstat(fd, buf); + host_close(fd); + } + + if (ret < 0) + { + /* Since semihosting doesn't support directory yet, */ + + ret = 0; /* we have to assume it's a directory here. */ + memset(buf, 0, sizeof(*buf)); + buf->st_mode = S_IFDIR | 0777; + } + + return ret; +} + +int host_chstat(const char *path, const struct stat *buf, int flags) +{ + return -ENOSYS; +} diff --git a/arch/risc-v/src/common/riscv_semihost.S b/arch/risc-v/src/common/riscv_semihost.S new file mode 100644 index 0000000000..0bd9db2f81 --- /dev/null +++ b/arch/risc-v/src/common/riscv_semihost.S @@ -0,0 +1,40 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_semihost.S + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Name: smh_call + * + * Description: + * Semihosting call with call number and one parameter + * + ****************************************************************************/ + + .option norvc + .text + .balign 16 + .global smh_call + .type smh_call @function + +smh_call: + + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + ret diff --git a/arch/risc-v/src/qemu-rv/Make.defs b/arch/risc-v/src/qemu-rv/Make.defs index a222285990..74fc60a903 100644 --- a/arch/risc-v/src/qemu-rv/Make.defs +++ b/arch/risc-v/src/qemu-rv/Make.defs @@ -68,6 +68,11 @@ ifeq ($(CONFIG_ARCH_RV_ISA_A),y) CMN_ASRCS += riscv_testset.S endif +ifeq ($(CONFIG_RISCV_SEMIHOSTING_HOSTFS),y) +CMN_ASRCS += riscv_semihost.S +CMN_CSRCS += riscv_hostfs.c +endif + # Specify our C code within this directory to be included CHIP_CSRCS = qemu_rv_start.c qemu_rv_irq_dispatch.c qemu_rv_irq.c CHIP_CSRCS += qemu_rv_timerisr.c