mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
vfs: add support for semihosting on ESP32-C3
This commit is contained in:
parent
991874ae43
commit
876f4d6a1c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -7,6 +7,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_app_trace_membufs_proto.h"
|
||||
#include "esp_app_trace_port.h"
|
||||
#include "riscv/semihosting.h"
|
||||
|
||||
/** RISCV HW transport data */
|
||||
typedef struct {
|
||||
@ -103,7 +104,7 @@ __attribute__((weak)) int esp_apptrace_advertise_ctrl_block(void *ctrl_block_add
|
||||
if (!esp_cpu_in_ocd_debug_mode()) {
|
||||
return 0;
|
||||
}
|
||||
return cpu_hal_syscall(RISCV_APPTRACE_SYSNR, (int)ctrl_block_addr, 0, 0, 0, NULL);
|
||||
return (int) semihosting_call_noerrno(RISCV_APPTRACE_SYSNR, (long*)ctrl_block_addr);
|
||||
}
|
||||
|
||||
/* Returns up buffers config.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -9,9 +9,9 @@
|
||||
//
|
||||
|
||||
#include "esp_cpu.h"
|
||||
#include "hal/cpu_hal.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "riscv/semihosting.h"
|
||||
|
||||
const static char *TAG = "esp_dbg_stubs";
|
||||
|
||||
#define RISCV_DBG_STUBS_SYSNR 0x65
|
||||
@ -22,13 +22,13 @@ static int esp_dbg_stubs_advertise_table(void *stub_table_addr)
|
||||
if (!esp_cpu_in_ocd_debug_mode()) {
|
||||
return 0;
|
||||
}
|
||||
return cpu_hal_syscall(RISCV_DBG_STUBS_SYSNR, (int)stub_table_addr, 0, 0, 0, NULL);
|
||||
return (int) semihosting_call_noerrno(RISCV_DBG_STUBS_SYSNR, (long*)stub_table_addr);
|
||||
}
|
||||
|
||||
void esp_dbg_stubs_ll_init(void *stub_table_addr)
|
||||
{
|
||||
// notify host about control block address
|
||||
int res = esp_dbg_stubs_advertise_table(stub_table_addr);
|
||||
assert(res == 0 && "Falied to send debug stubs table address to host!");
|
||||
assert(res == 0 && "Failed to send debug stubs table address to host!");
|
||||
ESP_LOGV(TAG, "%s stubs %x", __func__, stub_table_addr);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -143,34 +143,6 @@ static inline void cpu_ll_break(void)
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int cpu_ll_syscall(int sys_nr, int arg1, int arg2, int arg3, int arg4, int* ret_errno)
|
||||
{
|
||||
int host_ret, host_errno;
|
||||
|
||||
asm volatile ( \
|
||||
".option push\n" \
|
||||
".option norvc\n" \
|
||||
"mv a0, %[sys_nr]\n" \
|
||||
"mv a1, %[arg1]\n" \
|
||||
"mv a2, %[arg2]\n" \
|
||||
"mv a3, %[arg3]\n" \
|
||||
"mv a4, %[arg4]\n" \
|
||||
"slli zero,zero,0x1f\n" \
|
||||
"ebreak\n" \
|
||||
"srai zero,zero,0x7\n" \
|
||||
"mv %[host_ret], a0\n" \
|
||||
"mv %[host_errno], a1\n" \
|
||||
".option pop\n" \
|
||||
:[host_ret]"=r"(host_ret),[host_errno]"=r"(host_errno)
|
||||
:[sys_nr]"r"(sys_nr),[arg1]"r"(arg1),[arg2]"r"(arg2),[arg3]"r"(arg3),[arg4]"r"(arg4)
|
||||
:"a0","a1","a2","a3","a4");
|
||||
|
||||
if (ret_errno) {
|
||||
*ret_errno = host_errno;
|
||||
}
|
||||
return host_ret;
|
||||
}
|
||||
|
||||
static inline void cpu_ll_set_vecbase(const void* vecbase)
|
||||
{
|
||||
uintptr_t vecbase_int = (uintptr_t)vecbase;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -141,34 +141,6 @@ static inline void cpu_ll_break(void)
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int cpu_ll_syscall(int sys_nr, int arg1, int arg2, int arg3, int arg4, int* ret_errno)
|
||||
{
|
||||
int host_ret, host_errno;
|
||||
|
||||
asm volatile ( \
|
||||
".option push\n" \
|
||||
".option norvc\n" \
|
||||
"mv a0, %[sys_nr]\n" \
|
||||
"mv a1, %[arg1]\n" \
|
||||
"mv a2, %[arg2]\n" \
|
||||
"mv a3, %[arg3]\n" \
|
||||
"mv a4, %[arg4]\n" \
|
||||
"slli zero,zero,0x1f\n" \
|
||||
"ebreak\n" \
|
||||
"srai zero,zero,0x7\n" \
|
||||
"mv %[host_ret], a0\n" \
|
||||
"mv %[host_errno], a1\n" \
|
||||
".option pop\n" \
|
||||
:[host_ret]"=r"(host_ret),[host_errno]"=r"(host_errno)
|
||||
:[sys_nr]"r"(sys_nr),[arg1]"r"(arg1),[arg2]"r"(arg2),[arg3]"r"(arg3),[arg4]"r"(arg4)
|
||||
:"a0","a1","a2","a3","a4");
|
||||
|
||||
if (ret_errno) {
|
||||
*ret_errno = host_errno;
|
||||
}
|
||||
return host_ret;
|
||||
}
|
||||
|
||||
static inline void cpu_ll_set_vecbase(const void* vecbase)
|
||||
{
|
||||
uintptr_t vecbase_int = (uintptr_t)vecbase;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -70,11 +70,6 @@ extern "C" {
|
||||
*/
|
||||
#define cpu_hal_waiti() cpu_ll_waiti()
|
||||
|
||||
/**
|
||||
* Trigger a syscall.
|
||||
*/
|
||||
#define cpu_hal_syscall(sys_nr, arg1, arg2, arg3, arg4, ret_errno) cpu_ll_syscall(sys_nr, arg1, arg2, arg3, arg4, ret_errno)
|
||||
|
||||
#if SOC_CPU_BREAKPOINTS_NUM > 0
|
||||
|
||||
/**
|
||||
|
68
components/riscv/include/riscv/semihosting.h
Normal file
68
components/riscv/include/riscv/semihosting.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Perform semihosting call
|
||||
*
|
||||
* See https://github.com/riscv/riscv-semihosting-spec/ and the linked
|
||||
* ARM semihosting spec for details.
|
||||
*
|
||||
* @param id semihosting call number
|
||||
* @param data data block to pass to the host; number of items and their
|
||||
* meaning depends on the semihosting call. See the spec for
|
||||
* details.
|
||||
*
|
||||
* @return return value from the host
|
||||
*/
|
||||
static inline long semihosting_call_noerrno(long id, long *data)
|
||||
{
|
||||
register long a0 asm ("a0") = id;
|
||||
register long a1 asm ("a1") = (long) data;
|
||||
__asm__ __volatile__ (
|
||||
".option push\n"
|
||||
".option norvc\n"
|
||||
"slli zero, zero, 0x1f\n"
|
||||
"ebreak\n"
|
||||
"srai zero, zero, 0x7\n"
|
||||
".option pop\n"
|
||||
: "+r"(a0) : "r"(a1) : "memory");
|
||||
return a0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform semihosting call and retrieve errno
|
||||
*
|
||||
* @param id semihosting call number
|
||||
* @param data data block to pass to the host; number of items and their
|
||||
* meaning depends on the semihosting call. See the spec for
|
||||
* details.
|
||||
* @param[out] out_errno output, errno value from the host. Only set if
|
||||
* the return value is negative.
|
||||
* @return return value from the host
|
||||
*/
|
||||
static inline long semihosting_call(long id, long *data, int *out_errno)
|
||||
{
|
||||
long ret = semihosting_call_noerrno(id, data);
|
||||
if (ret < 0) {
|
||||
/* Constant also defined in openocd_semihosting.h,
|
||||
* which is common for RISC-V and Xtensa; it is not included here
|
||||
* to avoid a circular dependency.
|
||||
*/
|
||||
const int semihosting_sys_errno = 0x13;
|
||||
*out_errno = (int) semihosting_call_noerrno(semihosting_sys_errno, NULL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
150
components/vfs/openocd_semihosting.h
Normal file
150
components/vfs/openocd_semihosting.h
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#ifdef __XTENSA__
|
||||
#include "xtensa/semihosting.h"
|
||||
#elif __riscv
|
||||
#include "riscv/semihosting.h"
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Semihosting call numbers and functions for OpenOCD.
|
||||
* In OpenOCD, ARM semihosting call numbers and parameters are used for
|
||||
* RISC-V and Xtensa targets.
|
||||
*
|
||||
* These conventions are not compatible with Xtensa ISS and QEMU for Xtensa,
|
||||
* which the actual Xtensa-specific semihosting call numbers and formats;
|
||||
* 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
|
||||
|
||||
/* This call is an Espressif OpenOCD extension to send the version
|
||||
* information to the host. This lets the host support different IDF versions,
|
||||
* allowing semihosting interface to be modified over time.
|
||||
*
|
||||
* The parameters of this call are:
|
||||
* - pointer to the version info structure,
|
||||
* - size of the version info structure.
|
||||
*
|
||||
* At present, the structure should contain a single word, indicating
|
||||
* the semihosting interface version used by the target.
|
||||
*
|
||||
* If the syscall is recognized, the return value is zero.
|
||||
*/
|
||||
#define SEMIHOSTING_SYS_DRVINFO 0xE0
|
||||
|
||||
|
||||
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);
|
||||
if (result < 0) {
|
||||
errno = host_errno;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
errno = host_errno;
|
||||
return ret;
|
||||
}
|
||||
/* On success, write syscall returns the number of bytes NOT written,
|
||||
* adjust the return value to match POSIX.
|
||||
*/
|
||||
return size - (ssize_t)ret;
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
errno = host_errno;
|
||||
return ret;
|
||||
}
|
||||
/* On success, read syscall returns the number of bytes NOT read,
|
||||
* adjust the return value to match POSIX.
|
||||
*/
|
||||
return size - (ssize_t)ret;
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
errno = host_errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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(SEMIHOSTING_SYS_SEEK, args, &host_errno);
|
||||
if (ret == -1) {
|
||||
errno = host_errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int semihosting_ver_info(void)
|
||||
{
|
||||
int host_errno = 0;
|
||||
struct {
|
||||
int version;
|
||||
} ver_info = { 1 };
|
||||
long args[] = {(long) &ver_info, sizeof(ver_info), 0, 0};
|
||||
int ret = (int) semihosting_call(SEMIHOSTING_SYS_DRVINFO, args, &host_errno);
|
||||
(void) host_errno; /* errno not set by this call */
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,19 +1,19 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_vfs.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_cpu.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "hal/cpu_hal.h"
|
||||
#include "openocd_semihosting.h"
|
||||
|
||||
#ifndef CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS
|
||||
#define CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS 1
|
||||
@ -23,33 +23,23 @@
|
||||
#define CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN 128
|
||||
#endif
|
||||
|
||||
#ifdef VFS_SUPPRESS_SEMIHOSTING_LOG
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_NONE
|
||||
#endif //VFS_SUPPRESS_SEMIHOSTING_LOG
|
||||
|
||||
#include "esp_log.h"
|
||||
const static char *TAG = "esp_semihost";
|
||||
|
||||
/* current semihosting implementation version */
|
||||
#define DRIVER_SEMIHOSTING_VERSION 0x1
|
||||
|
||||
/* syscalls */
|
||||
#define SYSCALL_INSTR "break 1,1\n"
|
||||
#define SYS_OPEN 0x01
|
||||
#define SYS_CLOSE 0x02
|
||||
#define SYS_WRITE 0x05
|
||||
#define SYS_READ 0x06
|
||||
#define SYS_SEEK 0x0A
|
||||
/* Additional open flags */
|
||||
|
||||
#define SYS_DRVINFO 0xE0
|
||||
|
||||
/* additional open flags */
|
||||
#define O_BINARY 0 // there is no binary flag in our toolchain, as well as in Linux OS
|
||||
// but we are leaving it to have an identical to OOCD flags table
|
||||
/** ESP-specific file open flag. Indicates that path passed to open() is absolute host path. */
|
||||
/* ESP-specific file open flag.
|
||||
* Indicates that path passed to open() is absolute host path.
|
||||
*/
|
||||
#define ESP_O_SEMIHOST_ABSPATH 0x80000000
|
||||
|
||||
/* The table is identical to semihosting_common's one from OpenOCD */
|
||||
/* There is no O_BINARY flag defined in newlib, as well as on Linux,
|
||||
* but we are leaving it to have the flags table identical to OpenOCD.
|
||||
*/
|
||||
#define O_BINARY 0
|
||||
|
||||
/* The table is identical to the one in OpenOCD semihosting_common.c */
|
||||
static const int open_modeflags[12] = {
|
||||
O_RDONLY,
|
||||
O_RDONLY | O_BINARY,
|
||||
@ -65,33 +55,22 @@ static const int open_modeflags[12] = {
|
||||
O_RDWR | O_CREAT | O_APPEND | O_BINARY
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief semihosting driver information
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int ver;
|
||||
} drv_info_t;
|
||||
|
||||
/**
|
||||
* @brief Get the number of appropriate file open mode set from open_modeflags and add some esp flags to them
|
||||
*
|
||||
* @param flags value, every bit of which reflects state of some open-file flag
|
||||
* @return int
|
||||
* -1 - there is no appropriate entry of open_modeflags[]
|
||||
* esp_flags | (0...11) - esp-specific flags and number of flag set for oocd from @ref open_modeflags[]
|
||||
* @return index of the flag from @ref open_modeflags[], or -1 if invalid flags combination is given.
|
||||
*/
|
||||
static inline int get_o_mode(int flags) {
|
||||
uint32_t esp_flags = flags & 0xfff00000; // that bits are not used, so let's use it for our espressif's purposes
|
||||
uint32_t semi_comm_flags = flags & 0x000fffff;
|
||||
if (semi_comm_flags & O_EXCL) { // bypassing lacking of this at table above
|
||||
semi_comm_flags &= ~(O_EXCL);
|
||||
semi_comm_flags |= O_CREAT;
|
||||
if (flags & O_EXCL) { // bypassing lacking of this at table above
|
||||
flags &= ~(O_EXCL);
|
||||
flags |= O_CREAT;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sizeof(open_modeflags) / sizeof(open_modeflags[0]); i++) {
|
||||
if (semi_comm_flags == open_modeflags[i])
|
||||
return (esp_flags | i);
|
||||
if (flags == open_modeflags[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // there is no corresponding mode in the table
|
||||
}
|
||||
@ -103,91 +82,55 @@ typedef struct {
|
||||
|
||||
static vfs_semihost_ctx_t s_semhost_ctx[CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS];
|
||||
|
||||
|
||||
static inline int generic_syscall(int sys_nr, int arg1, int arg2, int arg3, int arg4, int* ret_errno)
|
||||
{
|
||||
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 && !CONFIG_IDF_TARGET_ESP8684 // TODO ESP32-C3 reenable semihost in C3 IDF-2287
|
||||
int host_ret, host_errno;
|
||||
|
||||
if (!esp_cpu_in_ocd_debug_mode()) {
|
||||
*ret_errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
__asm__ volatile (
|
||||
"mov a2, %[sys_nr]\n" \
|
||||
"mov a3, %[arg1]\n" \
|
||||
"mov a4, %[arg2]\n" \
|
||||
"mov a5, %[arg3]\n" \
|
||||
"mov a6, %[arg4]\n" \
|
||||
SYSCALL_INSTR \
|
||||
"mov %[host_ret], a2\n" \
|
||||
"mov %[host_errno], a3\n" \
|
||||
:[host_ret]"=r"(host_ret),[host_errno]"=r"(host_errno)
|
||||
:[sys_nr]"r"(sys_nr),[arg1]"r"(arg1),[arg2]"r"(arg2),[arg3]"r"(arg3),[arg4]"r"(arg4)
|
||||
:"a2","a3","a4","a5","a6");
|
||||
*ret_errno = host_errno;
|
||||
return host_ret;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
inline bool ctx_is_unused(const vfs_semihost_ctx_t* ctx)
|
||||
static inline bool ctx_is_unused(const vfs_semihost_ctx_t* ctx)
|
||||
{
|
||||
return ctx->base_path[0] == 0;
|
||||
}
|
||||
|
||||
inline bool ctx_uses_abspath(const vfs_semihost_ctx_t* ctx)
|
||||
static inline bool ctx_uses_abspath(const vfs_semihost_ctx_t* ctx)
|
||||
{
|
||||
return ctx->host_path[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a custom syscall SYS_DRVINFO to the host for determining
|
||||
*
|
||||
* @param ctx context
|
||||
* @return error
|
||||
*/
|
||||
static esp_err_t vfs_semihost_drvinfo(vfs_semihost_ctx_t *ctx) {
|
||||
drv_info_t drv_info = {
|
||||
.ver = DRIVER_SEMIHOSTING_VERSION
|
||||
};
|
||||
#define FAIL_IF_NO_DEBUGGER() \
|
||||
do { \
|
||||
if (!cpu_hal_is_debugger_attached()) { \
|
||||
errno = EIO; \
|
||||
return -1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
int host_err = 0;
|
||||
size_t ret = -1;
|
||||
#if __XTENSA__
|
||||
static esp_err_t vfs_semihost_drvinfo(vfs_semihost_ctx_t *ctx)
|
||||
{
|
||||
FAIL_IF_NO_DEBUGGER();
|
||||
|
||||
ESP_LOGV(TAG, "%s: s_ver: %x, flags: %x, par3: %x, par4: %x", __func__, (int)&drv_info, sizeof(drv_info), 0, 0);
|
||||
|
||||
ret = generic_syscall(SYS_DRVINFO, (int)&drv_info, sizeof(drv_info), 0, 0, &host_err);
|
||||
|
||||
/* Recognizing the version */
|
||||
ESP_LOGV(TAG, "Trying to determine semihosting's version...");
|
||||
if (ret == -1) { /* there is no such syscall - old semihosting */
|
||||
ret = ESP_ERR_INVALID_VERSION;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "OpenOCD Semihosting v.%d [Read from an OpenOCD response]", drv_info.ver);
|
||||
ESP_LOGV(TAG, "[Version was read from an OpenOCD response]");
|
||||
int ret = semihosting_ver_info();
|
||||
if (ret == -1) {
|
||||
/* Unsupported syscall - old version of OpenOCD */
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
}
|
||||
return ret;
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif // __XTENSA__
|
||||
|
||||
static int vfs_semihost_open(void* ctx, const char* path, int flags, int mode) {
|
||||
int ret_fd = -1, o_mode = 0, host_err = 0;
|
||||
static int vfs_semihost_open(void* ctx, const char* path, int flags, int mode)
|
||||
{
|
||||
int ret_fd = -1;
|
||||
char *host_path;
|
||||
vfs_semihost_ctx_t *semi_ctx = ctx;
|
||||
FAIL_IF_NO_DEBUGGER();
|
||||
|
||||
ESP_LOGV(TAG, "%s: %p '%s 0x%x 0x%x'", __func__, semi_ctx, path, flags, mode);
|
||||
|
||||
/* flags processing */
|
||||
if (ctx_uses_abspath(semi_ctx)) {
|
||||
flags |= ESP_O_SEMIHOST_ABSPATH;
|
||||
}
|
||||
o_mode = get_o_mode(flags);
|
||||
|
||||
int o_mode = get_o_mode(flags);
|
||||
if (o_mode == -1) { /* if wrong flags - error */
|
||||
errno = EINVAL;
|
||||
} else { /* if ok - host_path processing */
|
||||
} else {
|
||||
if (ctx_uses_abspath(semi_ctx)) {
|
||||
/* Create full absolute path on the host by concatenating host base
|
||||
* path and file path relative to the filesystem root.
|
||||
*/
|
||||
host_path = malloc(strlen(semi_ctx->host_path) + strlen(path) + 1);
|
||||
if (host_path == NULL) { /* if no valid pointer - error and return */
|
||||
errno = ENOMEM;
|
||||
@ -195,14 +138,36 @@ static int vfs_semihost_open(void* ctx, const char* path, int flags, int mode) {
|
||||
}
|
||||
strcpy(host_path, semi_ctx->host_path);
|
||||
strcat(host_path, path);
|
||||
#ifdef __XTENSA__
|
||||
/* By default, OpenOCD for Xtensa prepends ESP_SEMIHOST_BASEDIR to
|
||||
* the path passed from the target. Adding this special flag to o_mode
|
||||
* inhibits this behavior.
|
||||
* This is not necessary for RISC-V since standard semihosting
|
||||
* implementation is used there and paths aren't mangled on OpenOCD side.
|
||||
*/
|
||||
if (ctx_uses_abspath(semi_ctx)) {
|
||||
o_mode |= ESP_O_SEMIHOST_ABSPATH;
|
||||
}
|
||||
#endif // __XTENSA__
|
||||
} else {
|
||||
host_path = (char *)path;
|
||||
/* For Xtensa targets in OpenOCD there is additional logic related to
|
||||
* semihosting paths handling that isn't there for other targets.
|
||||
* When ESP_SEMIHOST_BASEDIR OpenOCD variable is not set, OpenOCD will
|
||||
* by default prepend '.' to the path passed from the target.
|
||||
* By contrast, for RISC-V there is no such logic and the path will be
|
||||
* used as is, no matter whether it is absolute or relative.
|
||||
* See esp_xtensa_semihosting_get_file_name in esp_xtensa_semihosting.c
|
||||
* for details.
|
||||
*/
|
||||
#ifndef __XTENSA__
|
||||
if (*host_path == '/') {
|
||||
++host_path;
|
||||
}
|
||||
#endif // !__XTENSA__
|
||||
}
|
||||
/* everything is ready: syscall and cleanup */
|
||||
ret_fd = generic_syscall(SYS_OPEN, (int)host_path, o_mode, strlen(host_path), mode, &host_err);
|
||||
if (ret_fd == -1) {
|
||||
errno = host_err;
|
||||
}
|
||||
ret_fd = semihosting_open(host_path, o_mode, mode);
|
||||
if (ctx_uses_abspath(semi_ctx)) {
|
||||
free(host_path);
|
||||
}
|
||||
@ -212,55 +177,34 @@ static int vfs_semihost_open(void* ctx, const char* path, int flags, int mode) {
|
||||
|
||||
static ssize_t vfs_semihost_write(void* ctx, int fd, const void * data, size_t size)
|
||||
{
|
||||
int host_err = 0;
|
||||
size_t ret = -1;
|
||||
FAIL_IF_NO_DEBUGGER();
|
||||
|
||||
ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size);
|
||||
ret = generic_syscall(SYS_WRITE, fd, (int)data, size, 0, &host_err);
|
||||
if (ret == -1) {
|
||||
errno = host_err;
|
||||
}
|
||||
return size - (ssize_t)ret; /* Write syscall returns the number of bytes NOT written */
|
||||
return semihosting_write(fd, data, size);
|
||||
}
|
||||
|
||||
static ssize_t vfs_semihost_read(void* ctx, int fd, void* data, size_t size)
|
||||
{
|
||||
int host_err = 0;
|
||||
size_t ret = -1;
|
||||
FAIL_IF_NO_DEBUGGER();
|
||||
|
||||
ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size);
|
||||
ret = generic_syscall(SYS_READ, fd, (int)data, size, 0, &host_err);
|
||||
if (ret == -1) {
|
||||
errno = host_err;
|
||||
return ret;
|
||||
}
|
||||
return size - (ssize_t)ret; /* Read syscall returns the number of bytes NOT read */
|
||||
|
||||
return semihosting_read(fd, data, size);
|
||||
}
|
||||
|
||||
|
||||
static int vfs_semihost_close(void* ctx, int fd)
|
||||
{
|
||||
int ret = -1, host_err = 0;
|
||||
FAIL_IF_NO_DEBUGGER();
|
||||
|
||||
ESP_LOGV(TAG, "%s: %d", __func__, fd);
|
||||
ret = generic_syscall(SYS_CLOSE, fd, 0, 0, 0, &host_err);
|
||||
if (ret == -1) {
|
||||
errno = host_err;
|
||||
}
|
||||
return ret;
|
||||
return semihosting_close(fd);
|
||||
}
|
||||
|
||||
static off_t vfs_semihost_lseek(void* ctx, int fd, off_t offset, int mode)
|
||||
{
|
||||
int ret = -1, host_err = 0;
|
||||
FAIL_IF_NO_DEBUGGER();
|
||||
|
||||
ESP_LOGV(TAG, "%s: %d %ld %d", __func__, fd, offset, mode);
|
||||
ret = generic_syscall(SYS_SEEK, fd, offset, mode, 0, &host_err);
|
||||
if (ret == -1) {
|
||||
errno = host_err;
|
||||
}
|
||||
return (off_t)ret;
|
||||
return semihosting_seek(fd, offset, mode);
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path)
|
||||
@ -274,7 +218,7 @@ esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path
|
||||
.lseek_p = &vfs_semihost_lseek,
|
||||
};
|
||||
ESP_LOGD(TAG, "Register semihosting driver '%s' -> '%s'", base_path, host_path ? host_path : "null");
|
||||
if (!esp_cpu_in_ocd_debug_mode()) {
|
||||
if (!cpu_hal_is_debugger_attached()) {
|
||||
ESP_LOGE(TAG, "OpenOCD is not connected!");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
@ -295,10 +239,16 @@ esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path
|
||||
strlcpy(s_semhost_ctx[i].host_path, host_path, sizeof(s_semhost_ctx[i].host_path) - 1);
|
||||
}
|
||||
ESP_LOGD(TAG, "Register semihosting driver %d %p", i, &s_semhost_ctx[i]);
|
||||
esp_err_t err = vfs_semihost_drvinfo(&s_semhost_ctx[i]); // define semihosting version
|
||||
|
||||
esp_err_t err;
|
||||
#if __XTENSA__
|
||||
/* Check for older OpenOCD versions */
|
||||
err = vfs_semihost_drvinfo(&s_semhost_ctx[i]); // define semihosting version
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Incompatible OpenOCD version detected. Please follow the getting started guides to install the required version.");
|
||||
}
|
||||
#endif // __XTENSA__
|
||||
|
||||
err = esp_vfs_register(base_path, &vfs, &s_semhost_ctx[i]);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Can't register the semihosting! Error: %s", esp_err_to_name(err));
|
||||
|
60
components/xtensa/include/xtensa/semihosting.h
Normal file
60
components/xtensa/include/xtensa/semihosting.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Perform semihosting call and retrieve errno
|
||||
*
|
||||
* @param id semihosting call number
|
||||
* @param data data block to pass to the host; number of items and their
|
||||
* meaning depends on the semihosting call. See the spec for
|
||||
* details.
|
||||
* On Xtensa, this function assumes that the array contains at
|
||||
* least 4 elements, but no effort is made to guarantee that.
|
||||
* Passing a shorter array will still work, as long as it contains
|
||||
* sufficient values for the corresponding semihosting call.
|
||||
* @param[out] out_errno output, errno value from the host. Only set if
|
||||
* the return value is negative.
|
||||
* @return return value from the host
|
||||
*/
|
||||
static inline long semihosting_call(long id, long *data, int *out_errno) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
long host_ret;
|
||||
long host_errno;
|
||||
/* The break instruction operands should be (1, 14) according to the ISA manual.
|
||||
* We keep (1, 1) for compatibility, until OpenOCD is updated to support both
|
||||
* conventions.
|
||||
*/
|
||||
__asm__ __volatile__ (
|
||||
"mov a2, %[sys_nr]\n" \
|
||||
"mov a3, %[arg1]\n" \
|
||||
"mov a4, %[arg2]\n" \
|
||||
"mov a5, %[arg3]\n" \
|
||||
"mov a6, %[arg4]\n" \
|
||||
"break 1, 1\n" \
|
||||
"mov %[host_ret], a2\n" \
|
||||
"mov %[host_errno], a3\n" \
|
||||
:[host_ret]"=r"(host_ret), [host_errno]"=r"(host_errno)
|
||||
:[sys_nr]"r"(id),
|
||||
[arg1]"r"(data[0]),
|
||||
[arg2]"r"(data[1]),
|
||||
[arg3]"r"(data[2]),
|
||||
[arg4]"r"(data[3])
|
||||
:"a2", "a3", "a4", "a5", "a6");
|
||||
if (host_ret < 0) {
|
||||
*out_errno = host_errno;
|
||||
}
|
||||
return host_ret;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -15,65 +15,101 @@ This example demonstrates how to use semihosting VFS driver with ESP32. Example
|
||||
|
||||
### Hardware and tools required
|
||||
|
||||
This example does not require any special hardware, and can be run on any common development board.
|
||||
This example requires [OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#run-openocd).
|
||||
NOTE: In order to run this example you need OpenOCD version `v0.10.0-esp32-20190313` or later.
|
||||
This example requires a development board with JTAG interface, for example:
|
||||
|
||||
Run OpenOCD using command:
|
||||
```
|
||||
bin/openocd -s share/openocd/scripts -c 'set ESP_SEMIHOST_BASEDIR '$IDF_PATH/examples/storage/semihost_vfs/data -f board/esp32-wrover-kit-3.3v.cfg
|
||||
```
|
||||
This command also configures OpenOCD to expose example project `data` subdirectory to the target's semihosting VFS driver.
|
||||
- ESP32-Wrover-Kit, ESP32-Ethernet-Kit for ESP32
|
||||
- ESP32-S2-Kaluga for ESP32-S2
|
||||
- For ESP32-C3 or ESP32-S3, any board with the built-in USB interface (USB_SERIAL_JTAG)
|
||||
- ESP-Prog as an external JTAG adapter with any other development board
|
||||
|
||||
This example also requires [OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#run-openocd) to be set up.
|
||||
|
||||
### Build and flash
|
||||
|
||||
Replace PORT with serial port name:
|
||||
1. Replace PORT with serial port name and run this command to build and flash the example:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash
|
||||
```
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
|
||||
2. Go to `data` subdirectory of the project and run OpenOCD.
|
||||
|
||||
```
|
||||
cd data
|
||||
openocd -f board/esp32-wrover-kit-3.3v.cfg
|
||||
```
|
||||
|
||||
Note that you need to use the correct configuration file for your board after `-f` option in the above command. Please refer to the list of configuration files available for [ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/jtag-debugging/tips-and-quirks.html#jtag-debugging-tip-openocd-configure-target), [ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/jtag-debugging/tips-and-quirks.html#jtag-debugging-tip-openocd-configure-target), [ESP32-S3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-guides/jtag-debugging/tips-and-quirks.html#jtag-debugging-tip-openocd-configure-target), [ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-guides/jtag-debugging/tips-and-quirks.html#jtag-debugging-tip-openocd-configure-target).
|
||||
|
||||
3. With OpenOCD still running, open another console or terminal and run IDF monitor there:
|
||||
|
||||
```
|
||||
idf.py monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
### Overriding the base directory for semihosting
|
||||
|
||||
When the example application calls `esp_vfs_semihost_register("/host", NULL)`, the path `/host` on the ESP target is mapped to the semihosting _base directory_. By default, this is the directory where OpenOCD program is started from. In the instructions above, OpenOCD is started in `data` subdirectory of the example project.
|
||||
|
||||
When debugging with Xtensa based SoCs (ESP32, ESP32-S2, ESP32-S3) it is possible to override the semihosting base directory using an additional flag of `openocd` command. For example, on Linux and macOS:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
openocd -c "set ESP_SEMIHOST_BASEDIR $IDF_PATH/examples/storage/semihost_vfs/data" -f board/esp32-wrover-kit-3.3v.cfg
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
or on Windows:
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
```
|
||||
openocd -c "set ESP_SEMIHOST_BASEDIR %IDF_PATH%/examples/storage/semihost_vfs/data" -f board/esp32-wrover-kit-3.3v.cfg
|
||||
```
|
||||
|
||||
The above command will set `ESP_SEMIHOST_BASEDIR` variable to `examples/storage/semihost_vfs/data` subdirectory of ESP-IDF. With that, it is not necessary to run OpenOCD from that specific directory.
|
||||
|
||||
> Note: This feature is not available for RISC-V based SoCs (ESP32-C3, ESP32-H2). To set the semihosting base directory, change into the required directory before running `openocd` command.
|
||||
|
||||
## Example output
|
||||
|
||||
There are two types of outputs produced by example:
|
||||
1. File `esp32_stdout.txt` in the host directory mounted to the target:
|
||||
There are two outputs produced by example:
|
||||
|
||||
```
|
||||
W (274) example: Switched to semihosted stdout
|
||||
Semihosted stdout write 0
|
||||
Semihosted stdout write 1
|
||||
Semihosted stdout write 2
|
||||
...
|
||||
Semihosted stdout write 98
|
||||
Semihosted stdout write 99
|
||||
W (274) example: Switch to UART stdout
|
||||
```
|
||||
1. The example creates and writes `esp32_stdout.txt` file in the `data` directory of the project:
|
||||
|
||||
2. On the boards console:
|
||||
```
|
||||
W (274) example: Switched to semihosted stdout
|
||||
Semihosted stdout write 0
|
||||
Semihosted stdout write 1
|
||||
Semihosted stdout write 2
|
||||
...
|
||||
Semihosted stdout write 98
|
||||
Semihosted stdout write 99
|
||||
W (274) example: Switch to UART stdout
|
||||
```
|
||||
|
||||
```
|
||||
W (274) example: Switch to semihosted stdout
|
||||
W (274) example: Switched back to UART stdout
|
||||
I (274) example: Wrote 2798 bytes
|
||||
====================== HOST DATA START =========================
|
||||
The following are the graphical (non-control) characters defined by
|
||||
ISO 8859-1 (1987). Descriptions in words aren't all that helpful,
|
||||
but they're the best we can do in text. A graphics file illustrating
|
||||
the character set should be available from the same archive as this
|
||||
file.
|
||||
2. The example reads [data/host_file.txt](data/host_file.txt) and prints its contents to the serial console:
|
||||
|
||||
Hex Description Hex Description
|
||||
|
||||
20 SPACE
|
||||
...
|
||||
7D RIGHT CURLY BRACKET FD SMALL LETTER Y WITH ACUTE
|
||||
7E TILDE FE SMALL LETTER THORN (Icelandic)
|
||||
FF SMALL LETTER Y WITH DIAERESIS
|
||||
====================== HOST DATA END =========================
|
||||
I (694) example: Read 6121 bytes
|
||||
```
|
||||
```
|
||||
W (274) example: Switch to semihosted stdout
|
||||
W (274) example: Switched back to UART stdout
|
||||
I (274) example: Wrote 2798 bytes
|
||||
====================== HOST DATA START =========================
|
||||
The following are the graphical (non-control) characters defined by
|
||||
ISO 8859-1 (1987). Descriptions in words aren't all that helpful,
|
||||
but they're the best we can do in text. A graphics file illustrating
|
||||
the character set should be available from the same archive as this
|
||||
file.
|
||||
|
||||
Hex Description Hex Description
|
||||
|
||||
20 SPACE
|
||||
...
|
||||
7D RIGHT CURLY BRACKET FD SMALL LETTER Y WITH ACUTE
|
||||
7E TILDE FE SMALL LETTER THORN (Icelandic)
|
||||
FF SMALL LETTER Y WITH DIAERESIS
|
||||
====================== HOST DATA END =========================
|
||||
I (694) example: Read 6121 bytes
|
||||
```
|
||||
|
||||
|
@ -52,7 +52,7 @@ void app_main(void)
|
||||
fflush(fout); // ensure that all data are sent to the host file
|
||||
// ftell can also be used, get file size before closing it in `freopen`
|
||||
int count = ftell(fout);
|
||||
stdout = freopen("/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM), "w", fout);
|
||||
stdout = freopen("/dev/console", "w", fout);
|
||||
if (stdout == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to reopen semihosted stdout (%d)!", errno);
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user