esp/vfs: VFS semihosting interface support

This commit is contained in:
Eren Terzioglu 2022-01-26 10:32:40 +03:00
parent b0fa5c7c2d
commit f6248ebf0d
2 changed files with 491 additions and 35 deletions

View File

@ -31,27 +31,27 @@ extern "C" {
* these are not supported in ESP-IDF yet.
*/
#define SEMIHOSTING_SYS_CLOCK 0x10
#define SEMIHOSTING_SYS_CLOSE 0x02
#define SEMIHOSTING_SYS_ERRNO 0x13
#define SEMIHOSTING_SYS_EXIT 0x18
#define SEMIHOSTING_SYS_EXIT_EXTENDED 0x20
#define SEMIHOSTING_SYS_FLEN 0x0C
#define SEMIHOSTING_SYS_GET_CMDLINE 0x15
#define SEMIHOSTING_SYS_HEAPINFO 0x16
#define SEMIHOSTING_SYS_ISERROR 0x08
#define SEMIHOSTING_SYS_ISTTY 0x09
#define SEMIHOSTING_SYS_OPEN 0x01
#define SEMIHOSTING_SYS_READ 0x06
#define SEMIHOSTING_SYS_READC 0x07
#define SEMIHOSTING_SYS_REMOVE 0x0E
#define SEMIHOSTING_SYS_RENAME 0x0F
#define SEMIHOSTING_SYS_SEEK 0x0A
#define SEMIHOSTING_SYS_SYSTEM 0x12
#define SEMIHOSTING_SYS_TIME 0x11
#define SEMIHOSTING_SYS_WRITE 0x05
#define SEMIHOSTING_SYS_WRITEC 0x03
#define SEMIHOSTING_SYS_WRITE0 0x04
#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_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_SYS_EXIT 0x18
#define SEMIHOSTING_SYS_EXIT_EXTENDED 0x20
/* This call is an Espressif OpenOCD extension to send the version
* information to the host. This lets the host support different IDF versions,
@ -114,7 +114,7 @@ static inline long semihosting_call(long id, long *data, int *out_errno)
long ret = semihosting_call_noerrno(id, data);
if (ret < 0) {
const int semihosting_sys_errno = SEMIHOSTING_SYS_ERRNO;
*out_errno = (int) semihosting_call_noerrno(semihosting_sys_errno, NULL);
*out_errno = (int)semihosting_call_noerrno(semihosting_sys_errno, NULL);
}
return ret;
}
@ -124,7 +124,7 @@ static inline int semihosting_open(const char *path, int open_mode, int mode)
int host_errno = 0;
long args[] = {(long) path, open_mode, strlen(path), 0};
(void) mode; // unused in OpenOCD
int result = (int) semihosting_call(SEMIHOSTING_SYS_OPEN, args, &host_errno);
int result = (int)semihosting_call(SEMIHOSTING_SYS_OPEN, args, &host_errno);
if (result < 0) {
errno = host_errno;
}
@ -135,7 +135,7 @@ static inline ssize_t semihosting_write(int fd, const void *data, size_t size)
{
int host_errno = 0;
long args[] = {fd, (long) data, size, 0};
ssize_t ret = (ssize_t) semihosting_call(SEMIHOSTING_SYS_WRITE, args, &host_errno);
ssize_t ret = (ssize_t)semihosting_call(SEMIHOSTING_SYS_WRITE, args, &host_errno);
if (ret < 0) {
errno = host_errno;
return ret;
@ -150,7 +150,7 @@ static inline ssize_t semihosting_read(int fd, void *data, size_t size)
{
int host_errno = 0;
long args[] = {fd, (long) data, size, 0};
ssize_t ret = (ssize_t) semihosting_call(SEMIHOSTING_SYS_READ, args, &host_errno);
ssize_t ret = (ssize_t)semihosting_call(SEMIHOSTING_SYS_READ, args, &host_errno);
if (ret < 0) {
errno = host_errno;
return ret;
@ -165,7 +165,7 @@ static inline int semihosting_close(int fd)
{
int host_errno = 0;
long args[] = {fd, 0, 0, 0};
int ret = (int) semihosting_call(SEMIHOSTING_SYS_CLOSE, args, &host_errno);
int ret = (int)semihosting_call(SEMIHOSTING_SYS_CLOSE, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
@ -176,7 +176,7 @@ static inline off_t semihosting_seek(int fd, off_t offset, int mode)
{
int host_errno = 0;
long args[] = {fd, offset, mode, 0};
off_t ret = (off_t) semihosting_call(ESP_SEMIHOSTING_SYS_SEEK, args, &host_errno);
off_t ret = (off_t)semihosting_call(ESP_SEMIHOSTING_SYS_SEEK, args, &host_errno);
if (ret == -1) {
errno = host_errno;
}
@ -190,11 +190,188 @@ static inline int semihosting_ver_info(void)
int version;
} ver_info = { SEMIHOSTING_DRV_VERSION };
long args[] = {(long) &ver_info, sizeof(ver_info), 0, 0};
int ret = (int) semihosting_call(ESP_SEMIHOSTING_SYS_DRV_INFO, args, &host_errno);
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_DRV_INFO, args, &host_errno);
(void) host_errno; /* errno not set by this call */
return ret;
}
static inline int semihosting_fstat(int fd, struct stat *restrict statbuf)
{
int host_errno = 0;
long args[] = {fd, (int)statbuf, 0, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_FSTAT, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_fsync(int fd)
{
int host_errno = 0;
long args[] = {fd, 0, 0, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_FSYNC, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
#ifdef CONFIG_VFS_SUPPORT_DIR
static inline int semihosting_mkdir(const char *host_path, mode_t mode)
{
int host_errno = 0;
long args[] = {(long)host_path, mode, strlen(host_path), 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_MKDIR, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_rmdir(const char *host_path)
{
int host_errno = 0;
long args[] = {(long)host_path, strlen(host_path), 0, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_RMDIR, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_access(const char *host_path, int mode)
{
int host_errno = 0;
long args[] = {(long)host_path, strlen(host_path), mode, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_ACCESS, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_truncate(const char *host_path, off_t truncate_length)
{
int host_errno = 0;
long args[] = {(long)host_path, strlen(host_path), truncate_length, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_TRUNCATE, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_utime(const char *host_path, const struct utimbuf *times)
{
int host_errno = 0;
long args[] = {(long)host_path, strlen(host_path), times->actime, times->modtime};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_UTIME, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_stat(const char *host_path, struct stat *restrict statbuf)
{
int host_errno = 0;
long args[] = {(long)host_path, strlen(host_path), (int)statbuf, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_STAT, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_rename(const char *old_path, const char *new_path)
{
int host_errno = 0;
long args[] = {(long)old_path, strlen(old_path), (long)new_path, strlen(new_path)};
int ret = (int)semihosting_call(SEMIHOSTING_SYS_RENAME, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_link(const char *path1, const char *path2)
{
int host_errno = 0;
long args[] = {(long)path1, strlen(path1), (long)path2, strlen(path2)};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_LINK, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_unlink(const char *path)
{
int host_errno = 0;
long args[] = {(long)path, strlen(path), 0, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_UNLINK, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_opendir(const char *path, long offset)
{
int host_errno = 0;
long args[] = {(long)path, strlen(path), offset, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_OPENDIR, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_readdir(int struct_dirent_ptr, long offset)
{
int host_errno = 0;
long args[] = {struct_dirent_ptr, offset, 0, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_READDIR, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_closedir(long id)
{
int host_errno = 0;
long args[] = {id, 0, 0, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_CLOSEDIR, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline long semihosting_telldir(long id)
{
int host_errno = 0;
long args[] = {id, 0, 0, 0};
long ret = semihosting_call(ESP_SEMIHOSTING_SYS_TELLDIR, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
static inline int semihosting_seekdir(long id, long offset)
{
int host_errno = 0;
long args[] = {id, offset, 0, 0};
int ret = (int)semihosting_call(ESP_SEMIHOSTING_SYS_SEEKDIR, args, &host_errno);
if (ret < 0) {
errno = host_errno;
}
return ret;
}
#endif
#ifdef __cplusplus
}
#endif

View File

@ -19,8 +19,17 @@
#define CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS 1
#endif
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
const static char *TAG = "esp_semihost";
typedef struct {
char path[256]; /*!< VFS DIR stream path */
struct dirent e; /*!< Last open dirent */
long id; /*!< DIR* unique id */
} vfs_semihost_dir_t;
/* Additional open flags */
@ -98,24 +107,20 @@ static esp_err_t vfs_semihost_drvinfo(vfs_semihost_ctx_t *ctx)
static int vfs_semihost_open(void* ctx, const char* path, int flags, int mode)
{
int ret_fd = -1;
FAIL_IF_NO_DEBUGGER();
if (path == NULL) {
errno = ENOENT;
return ret_fd;
return -1;
}
ESP_LOGV(TAG, "%s: '%s 0x%x 0x%x'", __func__, path, flags, mode);
int o_mode = get_o_mode(flags);
if (o_mode == -1) { /* if wrong flags - error */
errno = EINVAL;
} else {
ret_fd = semihosting_open(path, o_mode, mode);
return -1;
}
return ret_fd;
return semihosting_open(path, o_mode, mode);
}
static ssize_t vfs_semihost_write(void* ctx, int fd, const void * data, size_t size)
@ -159,6 +164,261 @@ static off_t vfs_semihost_lseek(void* ctx, int fd, off_t offset, int mode)
return semihosting_seek(fd, offset, mode);
}
static int vfs_semihost_fstat(void* ctx, int fd, struct stat *restrict statbuf)
{
FAIL_IF_NO_DEBUGGER();
if (statbuf == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '0x%x'", __func__, fd);
return semihosting_fstat(fd, statbuf);
}
static int vfs_semihost_fsync(void* ctx, int fd)
{
FAIL_IF_NO_DEBUGGER();
ESP_LOGV(TAG, "%s: '0x%x'", __func__, fd);
return semihosting_fsync(fd);
}
#ifdef CONFIG_VFS_SUPPORT_DIR
static int vfs_semihost_mkdir(void* ctx, const char* path, mode_t mode)
{
FAIL_IF_NO_DEBUGGER();
if (path == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s 0x%x'", __func__, path, mode);
return semihosting_mkdir(path, mode);
}
static int vfs_semihost_rmdir(void* ctx, const char* path)
{
FAIL_IF_NO_DEBUGGER();
if (path == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s'", __func__, path);
return semihosting_rmdir(path);
}
static int vfs_semihost_access(void* ctx, const char* path, int mode)
{
FAIL_IF_NO_DEBUGGER();
if (path == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s 0x%x'", __func__, path, mode);
return semihosting_access(path, mode);
}
static int vfs_semihost_truncate(void* ctx, const char* path, off_t length)
{
FAIL_IF_NO_DEBUGGER();
if (length < 0) {
errno = EINVAL;
return -1;
}
if (path == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s after %ld bytes'", __func__, path, length);
return semihosting_truncate(path, length);
}
static int vfs_semihost_utime(void* ctx, const char* path, const struct utimbuf *times)
{
FAIL_IF_NO_DEBUGGER();
if (path == NULL || times == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s'", __func__, path);
return semihosting_utime(path, times);
}
static int vfs_semihost_stat(void* ctx, const char *restrict path, struct stat *restrict statbuf)
{
FAIL_IF_NO_DEBUGGER();
if (statbuf == NULL || path == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s'", __func__, path);
return semihosting_stat(path, statbuf);
}
static int vfs_semihost_rename(void* ctx, const char *restrict old_name, const char *restrict new_name)
{
FAIL_IF_NO_DEBUGGER();
if (old_name == NULL || new_name == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s -> %s'", __func__, old_name, new_name);
return semihosting_rename(old_name, new_name);
}
static int vfs_semihost_link(void* ctx, const char *restrict path1, const char *restrict path2)
{
FAIL_IF_NO_DEBUGGER();
if (path1 == NULL || path2 == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s <-> %s'", __func__, path1, path2);
return semihosting_link(path1, path2);
}
static int vfs_semihost_unlink(void* ctx, const char *restrict path)
{
FAIL_IF_NO_DEBUGGER();
if (path == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: '%s'", __func__, path);
return semihosting_unlink(path);
}
static DIR* vfs_semihost_opendir(void* ctx, const char *restrict path)
{
if (!cpu_hal_is_debugger_attached()) {
return NULL;
}
if (path == NULL) {
errno = ENOENT;
return NULL;
}
vfs_semihost_dir_t *semihost_dirp = (vfs_semihost_dir_t*)calloc(1, sizeof(vfs_semihost_dir_t));
if (semihost_dirp == NULL) {
ESP_LOGE("Error", "Error on vfs_semihost_dir_t creation");
errno = ENOMEM;
return NULL;
}
strncpy(semihost_dirp->path, path, MIN(strlen(path), sizeof(semihost_dirp->path) - 1));
ESP_LOGV(TAG, "%s: '%s'", __func__, path);
int ret_fd = semihosting_opendir(path, (int)&semihost_dirp->id);
if (ret_fd < 0) {
free(semihost_dirp);
return NULL;
}
return (DIR *)semihost_dirp;
}
static int vfs_semihost_closedir(void* ctx, DIR* dirp)
{
FAIL_IF_NO_DEBUGGER();
vfs_semihost_dir_t *semihost_dirp = (vfs_semihost_dir_t*)dirp;
if (semihost_dirp == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: %s %ld", __func__, semihost_dirp->path, semihost_dirp->id);
int ret_fd = semihosting_closedir(semihost_dirp->id);
free(semihost_dirp);
return ret_fd;
}
static long vfs_semihost_telldir(void* ctx, DIR* dirp)
{
FAIL_IF_NO_DEBUGGER();
vfs_semihost_dir_t *semihost_dirp = (vfs_semihost_dir_t*)dirp;
if (semihost_dirp == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: %s %ld", __func__, semihost_dirp->path, semihost_dirp->id);
return semihosting_telldir(semihost_dirp->id);
}
static int vfs_semihost_readdir_r(void* ctx, DIR* dirp, struct dirent* entry, struct dirent** out_dirent)
{
FAIL_IF_NO_DEBUGGER();
vfs_semihost_dir_t *semihost_dirp = (vfs_semihost_dir_t *)dirp;
if (semihost_dirp == NULL || entry == NULL || out_dirent == NULL) {
errno = ENOENT;
return -1;
}
ESP_LOGV(TAG, "%s: %s %ld", __func__, semihost_dirp->path, semihost_dirp->id);
int ret_fd = semihosting_readdir((int)entry, semihost_dirp->id);
if (ret_fd < 0) {
if (errno == 0) { /* end of directory */
*out_dirent = NULL;
return 0;
}
return errno;
}
*out_dirent = entry;
return 0;
}
static struct dirent* vfs_semihost_readdir(void* ctx, DIR* dirp)
{
if (!cpu_hal_is_debugger_attached()) {
return NULL;
}
vfs_semihost_dir_t *semihost_dirp = (vfs_semihost_dir_t *)dirp;
struct dirent *dir_ptr;
ESP_LOGV(TAG, "%s: %s %ld", __func__, semihost_dirp->path, semihost_dirp->id);
int ret = vfs_semihost_readdir_r(ctx, (DIR*)semihost_dirp, &semihost_dirp->e, &dir_ptr);
if (ret != 0) {
errno = ret;
return NULL;
}
return dir_ptr;
}
static void vfs_semihost_seekdir(void* ctx, DIR* pdir, long offset)
{
if (!cpu_hal_is_debugger_attached()) {
return;
}
vfs_semihost_dir_t *semihost_dirp = (vfs_semihost_dir_t *) pdir;
if (semihost_dirp != NULL) {
ESP_LOGV(TAG, "%s: %s %ld '%ld' bytes", __func__, semihost_dirp->path, semihost_dirp->id, offset);
semihosting_seekdir(semihost_dirp->id, offset);
}
}
#endif
esp_err_t esp_vfs_semihost_register(const char* base_path)
{
assert(base_path);
@ -170,6 +430,25 @@ esp_err_t esp_vfs_semihost_register(const char* base_path)
.close_p = &vfs_semihost_close,
.read_p = &vfs_semihost_read,
.lseek_p = &vfs_semihost_lseek,
.fsync_p = &vfs_semihost_fsync,
.fstat_p = &vfs_semihost_fstat,
#ifdef CONFIG_VFS_SUPPORT_DIR
.mkdir_p = &vfs_semihost_mkdir,
.rmdir_p = &vfs_semihost_rmdir,
.access_p = &vfs_semihost_access,
.truncate_p = &vfs_semihost_truncate,
.utime_p = &vfs_semihost_utime,
.stat_p = &vfs_semihost_stat,
.rename_p = &vfs_semihost_rename,
.link_p = &vfs_semihost_link,
.unlink_p = &vfs_semihost_unlink,
.opendir_p = &vfs_semihost_opendir,
.closedir_p = &vfs_semihost_closedir,
.telldir_p = &vfs_semihost_telldir,
.readdir_p = &vfs_semihost_readdir,
.readdir_r_p = &vfs_semihost_readdir_r,
.seekdir_p = &vfs_semihost_seekdir,
#endif
};
ESP_LOGD(TAG, "Register semihosting driver '%s'", base_path);
if (!cpu_hal_is_debugger_attached()) {