Merge branch 'feature/semihosting_call_number_change' into 'master'

Semihosting V2

Closes IDF-4893

See merge request espressif/esp-idf!17412
This commit is contained in:
Roland Dobai 2022-05-07 00:28:48 +08:00
commit efc3357f1a
11 changed files with 121 additions and 183 deletions

View File

@ -19,7 +19,7 @@ extern "C" {
* @param addr address of apptrace control data block
* @return return 0 on sucess or non-zero error code
*/
#define ESP_SEMIHOSTING_SYS_APPTRACE_INIT 0x64
#define ESP_SEMIHOSTING_SYS_APPTRACE_INIT 0x101
/**
* @brief Initialize debug stubs table at host side
@ -27,7 +27,7 @@ extern "C" {
* @param addr address of debug stubs table
* @return return 0 on sucess or non-zero error code
*/
#define ESP_SEMIHOSTING_SYS_DBG_STUBS_INIT 0x65
#define ESP_SEMIHOSTING_SYS_DBG_STUBS_INIT 0x102
/**
* @brief Set/clear breakpoint
@ -37,7 +37,7 @@ extern "C" {
* @param addr address to set breakpoint at. Ignored if `set` is false.
* @return return 0 on sucess or non-zero error code
*/
#define ESP_SEMIHOSTING_SYS_BREAKPOINT_SET 0x66
#define ESP_SEMIHOSTING_SYS_BREAKPOINT_SET 0x103
/**
* @brief Set/clear watchpoint
@ -49,7 +49,7 @@ extern "C" {
* @param flags watchpoint flags, see description below. Ignored if `set` is false.
* @return return 0 on sucess or non-zero error code
*/
#define ESP_SEMIHOSTING_SYS_WATCHPOINT_SET 0x67
#define ESP_SEMIHOSTING_SYS_WATCHPOINT_SET 0x104
/* bit values for `flags` argument of ESP_SEMIHOSTING_SYS_WATCHPOINT_SET call. Can be ORed. */
/* watch for 'reads' at `addr` */
@ -85,31 +85,6 @@ static inline long semihosting_call_noerrno(long id, long *data)
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

View File

@ -80,15 +80,6 @@ menu "Virtual file system"
default 1
help
Define maximum number of host filesystem mount points.
config VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN
int "Host FS: Maximum path length for the host base directory"
default 128
help
Define maximum path length for the host base directory which is to be mounted.
If host path passed to esp_vfs_semihost_register() is longer than this value
it will be truncated.
endmenu
endmenu

View File

@ -1,16 +1,8 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -24,13 +16,12 @@ extern "C" {
* @brief add virtual filesystem semihosting driver
*
* @param base_path VFS path to mount host directory
* @param host_path host path to mount; if NULL default dirctory will be used (see OpenOCD configuration)
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if esp_vfs_semihost_register was already called for specified VFS path
* - ESP_ERR_NO_MEM if there are no slots to register new mount point
*/
esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path);
esp_err_t esp_vfs_semihost_register(const char* base_path);
/**
* @brief Un-register semihosting driver from VFS

View File

@ -66,8 +66,58 @@ extern "C" {
*
* If the syscall is recognized, the return value is zero.
*/
#define SEMIHOSTING_SYS_DRVINFO 0xE0
#define ESP_SEMIHOSTING_SYS_DRV_INFO 0x100
/* 0x101...0x104 used by RiscV for custom semihosting calls */
/* Other Espressif extension sys calls */
#define ESP_SEMIHOSTING_SYS_SEEK 0x105 /* custom lseek with whence */
/* not implemented yet */
#define ESP_SEMIHOSTING_SYS_MKDIR 0x106
#define ESP_SEMIHOSTING_SYS_OPENDIR 0x107
#define ESP_SEMIHOSTING_SYS_READDIR 0x108
#define ESP_SEMIHOSTING_SYS_READDIR_R 0x109
#define ESP_SEMIHOSTING_SYS_SEEKDIR 0x10A
#define ESP_SEMIHOSTING_SYS_TELLDIR 0x10B
#define ESP_SEMIHOSTING_SYS_CLOSEDIR 0x10C
#define ESP_SEMIHOSTING_SYS_RMDIR 0x10D
#define ESP_SEMIHOSTING_SYS_ACCESS 0x10E
#define ESP_SEMIHOSTING_SYS_TRUNCATE 0x10F
#define ESP_SEMIHOSTING_SYS_UTIME 0x110
#define ESP_SEMIHOSTING_SYS_FSTAT 0x111
#define ESP_SEMIHOSTING_SYS_STAT 0x112
#define ESP_SEMIHOSTING_SYS_FSYNC 0x113
#define ESP_SEMIHOSTING_SYS_LINK 0x114
#define ESP_SEMIHOSTING_SYS_UNLINK 0x115
/* Semihosting version bumped to 2. Changelog;
1 - Memory based approach with 2 registers implemented as defined in the ARM standard.
2 - User defined syscall numbers located between 0x100-0x1FF
3 - The break instruction operands updated to (1, 14)
4 - Absolute path support is dropped
*/
#define SEMIHOSTING_DRV_VERSION 2
/**
* @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) {
const int semihosting_sys_errno = SEMIHOSTING_SYS_ERRNO;
*out_errno = (int) semihosting_call_noerrno(semihosting_sys_errno, NULL);
}
return ret;
}
static inline int semihosting_open(const char *path, int open_mode, int mode)
{
@ -126,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(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;
}
@ -138,9 +188,9 @@ static inline int semihosting_ver_info(void)
int host_errno = 0;
struct {
int version;
} ver_info = { 1 };
} ver_info = { SEMIHOSTING_DRV_VERSION };
long args[] = {(long) &ver_info, sizeof(ver_info), 0, 0};
int ret = (int) semihosting_call(SEMIHOSTING_SYS_DRVINFO, 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;
}

View File

@ -19,21 +19,11 @@
#define CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS 1
#endif
#ifndef CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN
#define CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN 128
#endif
const static char *TAG = "esp_semihost";
/* Additional open flags */
/* ESP-specific file open flag.
* Indicates that path passed to open() is absolute host path.
*/
#define ESP_O_SEMIHOST_ABSPATH 0x80000000
/* 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.
*/
@ -41,18 +31,18 @@ const static char *TAG = "esp_semihost";
/* The table is identical to the one in OpenOCD semihosting_common.c */
static const int open_modeflags[12] = {
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
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
};
/**
@ -77,7 +67,6 @@ static inline int get_o_mode(int flags) {
typedef struct {
char base_path[ESP_VFS_PATH_MAX + 1]; /* base path in VFS where host semihosting dir is mounted */
char host_path[CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN + 1]; /* host path to use as base dir for open files */
} vfs_semihost_ctx_t;
static vfs_semihost_ctx_t s_semhost_ctx[CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS];
@ -87,11 +76,6 @@ static inline bool ctx_is_unused(const vfs_semihost_ctx_t* ctx)
return ctx->base_path[0] == 0;
}
static inline bool ctx_uses_abspath(const vfs_semihost_ctx_t* ctx)
{
return ctx->host_path[0];
}
#define FAIL_IF_NO_DEBUGGER() \
do { \
if (!cpu_hal_is_debugger_attached()) { \
@ -100,7 +84,6 @@ static inline bool ctx_uses_abspath(const vfs_semihost_ctx_t* ctx)
} \
} while(0)
#if __XTENSA__
static esp_err_t vfs_semihost_drvinfo(vfs_semihost_ctx_t *ctx)
{
FAIL_IF_NO_DEBUGGER();
@ -112,65 +95,25 @@ static esp_err_t vfs_semihost_drvinfo(vfs_semihost_ctx_t *ctx)
}
return ESP_OK;
}
#endif // __XTENSA__
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);
if (path == NULL) {
errno = ENOENT;
return ret_fd;
}
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 {
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;
return -1;
}
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 = semihosting_open(host_path, o_mode, mode);
if (ctx_uses_abspath(semi_ctx)) {
free(host_path);
}
ret_fd = semihosting_open(path, o_mode, mode);
}
return ret_fd;
}
@ -179,6 +122,11 @@ static ssize_t vfs_semihost_write(void* ctx, int fd, const void * data, size_t s
{
FAIL_IF_NO_DEBUGGER();
if (data == NULL) {
errno = EINVAL;
return -1;
}
ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size);
return semihosting_write(fd, data, size);
}
@ -187,11 +135,15 @@ static ssize_t vfs_semihost_read(void* ctx, int fd, void* data, size_t size)
{
FAIL_IF_NO_DEBUGGER();
if (data == NULL) {
errno = EINVAL;
return -1;
}
ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size);
return semihosting_read(fd, data, size);
}
static int vfs_semihost_close(void* ctx, int fd)
{
FAIL_IF_NO_DEBUGGER();
@ -207,8 +159,10 @@ static off_t vfs_semihost_lseek(void* ctx, int fd, off_t offset, int mode)
return semihosting_seek(fd, offset, mode);
}
esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path)
esp_err_t esp_vfs_semihost_register(const char* base_path)
{
assert(base_path);
const esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
.write_p = &vfs_semihost_write,
@ -217,7 +171,7 @@ esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path
.read_p = &vfs_semihost_read,
.lseek_p = &vfs_semihost_lseek,
};
ESP_LOGD(TAG, "Register semihosting driver '%s' -> '%s'", base_path, host_path ? host_path : "null");
ESP_LOGD(TAG, "Register semihosting driver '%s'", base_path);
if (!cpu_hal_is_debugger_attached()) {
ESP_LOGE(TAG, "OpenOCD is not connected!");
return ESP_ERR_NOT_SUPPORTED;
@ -235,19 +189,15 @@ esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path
return ESP_ERR_NO_MEM;
}
strlcpy(s_semhost_ctx[i].base_path, base_path, sizeof(s_semhost_ctx[i].base_path) - 1);
if (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;
#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) {
@ -259,6 +209,8 @@ esp_err_t esp_vfs_semihost_register(const char* base_path, const char* host_path
esp_err_t esp_vfs_semihost_unregister(const char* base_path)
{
assert(base_path);
ESP_LOGD(TAG, "Unregister semihosting driver @ '%s'", base_path);
int i = 0;
for (i = 0; i < CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS; i++) {
@ -274,7 +226,6 @@ esp_err_t esp_vfs_semihost_unregister(const char* base_path)
return ret;
}
s_semhost_ctx[i].base_path[0] = 0;
s_semhost_ctx[i].host_path[0] = 0;
ESP_LOGD(TAG, "Unregistered semihosting driver @ '%s'", base_path);
return ESP_OK;
}

View File

@ -11,52 +11,28 @@ extern "C" {
#endif
/**
* @brief Perform semihosting call and retrieve errno
* @brief Perform semihosting call
*
* See ARM semihosting spec for details.
* https://static.docs.arm.com/100863/0200/semihosting.pdf
*
* @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
*
* @return return value from the host
*/
static inline long semihosting_call(long id, long *data, int *out_errno) // NOLINT(readability-non-const-parameter)
static inline long semihosting_call_noerrno(long id, long *data)
{
/* GCC doesn't allow using specific register names in constraints for Xtensa.
* For this case, GCC extended inline assembly manual says the following:
* If you must use a specific register, but your Machine Constraints do not provide
* sufficient control to select the specific register you want, local register variables
* may provide a solution.
* Using local register variables results in simpler generated code than
* the previous implementation which listed a2-a6 as clobbered registers.
*/
register long a2 asm ("a2") = id;
register long a3 asm ("a3") = (long) data[0];
register long a4 asm ("a4") = (long) data[1];
register long a5 asm ("a5") = (long) data[2];
register long a6 asm ("a6") = (long) data[3];
register long a3 asm ("a3") = (long)data;
/* 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__ (
"break 1, 1\n"
: "+r"(a2), "+r"(a3)
: "r"(a4), "r"(a5), "r"(a6)
"break 1, 14\n"
: "+r"(a2) : "r"(a3)
: "memory");
long host_ret = a2;
long host_errno = a3;
if (host_ret < 0) {
*out_errno = host_errno;
}
return host_ret;
return a2;
}
#ifdef __cplusplus

View File

@ -12,4 +12,9 @@ New signature is ``FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void
Partition table generation no longer supports misaligned partitions
-------------------------------------------------------------------
When generating a partiton table, ``esp-idf`` will no longer accept partitions which offset does not align to 4kB. This change only affects generating new partition tables, reading and writing to already existing partitions remains unchanged.
When generating a partiton table, ``esp-idf`` will no longer accept partitions which offset does not align to 4kB. This change only affects generating new partition tables, reading and writing to already existing partitions remains unchanged.
esp_vfs_semihost_register() signature change
--------------------------------------------
New signature is ``esp_err_t esp_vfs_semihost_register(const char* base_path);`` Absolute path as a second parameter will no longer in use. Instead, the OpenOCD command ``ESP_SEMIHOST_BASEDIR`` should be used to set the full path on the host.

View File

@ -47,7 +47,7 @@ static void initialise_mdns(void)
#if CONFIG_EXAMPLE_WEB_DEPLOY_SEMIHOST
esp_err_t init_fs(void)
{
esp_err_t ret = esp_vfs_semihost_register(CONFIG_EXAMPLE_WEB_MOUNT_POINT, CONFIG_EXAMPLE_HOST_PATH_TO_MOUNT);
esp_err_t ret = esp_vfs_semihost_register(CONFIG_EXAMPLE_WEB_MOUNT_POINT);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to register semihost driver (%s)!", esp_err_to_name(ret));
return ESP_FAIL;

View File

@ -54,7 +54,7 @@ This example also requires [OpenOCD](https://docs.espressif.com/projects/esp-idf
### 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 the example application calls `esp_vfs_semihost_register("/host")`, 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:

View File

@ -26,7 +26,7 @@ static uint8_t s_buf[512];
void app_main(void)
{
// Register host FS at '/host'. On the host file will be written/read in the current semihosting dir of OpenOCD
esp_err_t ret = esp_vfs_semihost_register("/host", NULL);
esp_err_t ret = esp_vfs_semihost_register("/host");
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to register semihost driver (%s)!", esp_err_to_name(ret));
return;

View File

@ -1639,7 +1639,6 @@ components/unity/unity_runner.c
components/usb/test/hcd/test_hcd_ctrl.c
components/vfs/include/esp_vfs_common.h
components/vfs/include/esp_vfs_eventfd.h
components/vfs/include/esp_vfs_semihost.h
components/vfs/test/test_vfs_lwip.c
components/vfs/test/test_vfs_paths.c
components/vfs/test/test_vfs_uart.c