vfs_semihost.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdarg.h>
  7. #include <stdbool.h>
  8. #include <string.h>
  9. #include <sys/errno.h>
  10. #include <sys/stat.h>
  11. #include <fcntl.h>
  12. #include "esp_log.h"
  13. #include "esp_vfs.h"
  14. #include "esp_cpu.h"
  15. #include "openocd_semihosting.h"
  16. #ifndef CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS
  17. #define CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS 1
  18. #endif
  19. const static char *TAG = "esp_semihost";
  20. /* Additional open flags */
  21. /* There is no O_BINARY flag defined in newlib, as well as on Linux,
  22. * but we are leaving it to have the flags table identical to OpenOCD.
  23. */
  24. #define O_BINARY 0
  25. /* The table is identical to the one in OpenOCD semihosting_common.c */
  26. static const int open_modeflags[12] = {
  27. O_RDONLY,
  28. O_RDONLY | O_BINARY,
  29. O_RDWR,
  30. O_RDWR | O_BINARY,
  31. O_WRONLY | O_CREAT | O_TRUNC,
  32. O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
  33. O_RDWR | O_CREAT | O_TRUNC,
  34. O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
  35. O_WRONLY | O_CREAT | O_APPEND,
  36. O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
  37. O_RDWR | O_CREAT | O_APPEND,
  38. O_RDWR | O_CREAT | O_APPEND | O_BINARY
  39. };
  40. /**
  41. * @brief Get the number of appropriate file open mode set from open_modeflags and add some esp flags to them
  42. *
  43. * @param flags value, every bit of which reflects state of some open-file flag
  44. * @return index of the flag from @ref open_modeflags[], or -1 if invalid flags combination is given.
  45. */
  46. static inline int get_o_mode(int flags) {
  47. if (flags & O_EXCL) { // bypassing lacking of this at table above
  48. flags &= ~(O_EXCL);
  49. flags |= O_CREAT;
  50. }
  51. for (int i = 0; i < sizeof(open_modeflags) / sizeof(open_modeflags[0]); i++) {
  52. if (flags == open_modeflags[i]) {
  53. return i;
  54. }
  55. }
  56. return -1; // there is no corresponding mode in the table
  57. }
  58. typedef struct {
  59. char base_path[ESP_VFS_PATH_MAX + 1]; /* base path in VFS where host semihosting dir is mounted */
  60. } vfs_semihost_ctx_t;
  61. static vfs_semihost_ctx_t s_semhost_ctx[CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS];
  62. static inline bool ctx_is_unused(const vfs_semihost_ctx_t* ctx)
  63. {
  64. return ctx->base_path[0] == 0;
  65. }
  66. #define FAIL_IF_NO_DEBUGGER() \
  67. do { \
  68. if (!esp_cpu_dbgr_is_attached()) { \
  69. errno = EIO; \
  70. return -1; \
  71. } \
  72. } while(0)
  73. static esp_err_t vfs_semihost_drvinfo(vfs_semihost_ctx_t *ctx)
  74. {
  75. FAIL_IF_NO_DEBUGGER();
  76. int ret = semihosting_ver_info();
  77. if (ret == -1) {
  78. /* Unsupported syscall - old version of OpenOCD */
  79. return ESP_ERR_INVALID_VERSION;
  80. }
  81. return ESP_OK;
  82. }
  83. static int vfs_semihost_open(void* ctx, const char* path, int flags, int mode)
  84. {
  85. int ret_fd = -1;
  86. FAIL_IF_NO_DEBUGGER();
  87. if (path == NULL) {
  88. errno = ENOENT;
  89. return ret_fd;
  90. }
  91. ESP_LOGV(TAG, "%s: '%s 0x%x 0x%x'", __func__, path, flags, mode);
  92. int o_mode = get_o_mode(flags);
  93. if (o_mode == -1) { /* if wrong flags - error */
  94. errno = EINVAL;
  95. } else {
  96. ret_fd = semihosting_open(path, o_mode, mode);
  97. }
  98. return ret_fd;
  99. }
  100. static ssize_t vfs_semihost_write(void* ctx, int fd, const void * data, size_t size)
  101. {
  102. FAIL_IF_NO_DEBUGGER();
  103. if (data == NULL) {
  104. errno = EINVAL;
  105. return -1;
  106. }
  107. ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size);
  108. return semihosting_write(fd, data, size);
  109. }
  110. static ssize_t vfs_semihost_read(void* ctx, int fd, void* data, size_t size)
  111. {
  112. FAIL_IF_NO_DEBUGGER();
  113. if (data == NULL) {
  114. errno = EINVAL;
  115. return -1;
  116. }
  117. ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size);
  118. return semihosting_read(fd, data, size);
  119. }
  120. static int vfs_semihost_close(void* ctx, int fd)
  121. {
  122. FAIL_IF_NO_DEBUGGER();
  123. ESP_LOGV(TAG, "%s: %d", __func__, fd);
  124. return semihosting_close(fd);
  125. }
  126. static off_t vfs_semihost_lseek(void* ctx, int fd, off_t offset, int mode)
  127. {
  128. FAIL_IF_NO_DEBUGGER();
  129. return semihosting_seek(fd, offset, mode);
  130. }
  131. esp_err_t esp_vfs_semihost_register(const char* base_path)
  132. {
  133. assert(base_path);
  134. const esp_vfs_t vfs = {
  135. .flags = ESP_VFS_FLAG_CONTEXT_PTR,
  136. .write_p = &vfs_semihost_write,
  137. .open_p = &vfs_semihost_open,
  138. .close_p = &vfs_semihost_close,
  139. .read_p = &vfs_semihost_read,
  140. .lseek_p = &vfs_semihost_lseek,
  141. };
  142. ESP_LOGD(TAG, "Register semihosting driver '%s'", base_path);
  143. if (!esp_cpu_dbgr_is_attached()) {
  144. ESP_LOGE(TAG, "OpenOCD is not connected!");
  145. return ESP_ERR_NOT_SUPPORTED;
  146. }
  147. int i = 0;
  148. for (i = 0; i < CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS; i++) {
  149. if (ctx_is_unused(&s_semhost_ctx[i])) {
  150. break;
  151. }
  152. if (strcmp(base_path, s_semhost_ctx[i].base_path) == 0) {
  153. return ESP_ERR_INVALID_STATE;
  154. }
  155. }
  156. if (i == CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS) {
  157. return ESP_ERR_NO_MEM;
  158. }
  159. strlcpy(s_semhost_ctx[i].base_path, base_path, sizeof(s_semhost_ctx[i].base_path) - 1);
  160. ESP_LOGD(TAG, "Register semihosting driver %d %p", i, &s_semhost_ctx[i]);
  161. esp_err_t err;
  162. /* Check for older OpenOCD versions */
  163. err = vfs_semihost_drvinfo(&s_semhost_ctx[i]); // define semihosting version
  164. if (err != ESP_OK) {
  165. ESP_LOGE(TAG, "Incompatible OpenOCD version detected. Please follow the getting started guides to install the required version.");
  166. }
  167. err = esp_vfs_register(base_path, &vfs, &s_semhost_ctx[i]);
  168. if (err != ESP_OK) {
  169. ESP_LOGE(TAG, "Can't register the semihosting! Error: %s", esp_err_to_name(err));
  170. return err;
  171. }
  172. return err;
  173. }
  174. esp_err_t esp_vfs_semihost_unregister(const char* base_path)
  175. {
  176. assert(base_path);
  177. ESP_LOGD(TAG, "Unregister semihosting driver @ '%s'", base_path);
  178. int i = 0;
  179. for (i = 0; i < CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS; i++) {
  180. if (s_semhost_ctx[i].base_path[0] != 0 && strcmp(base_path, s_semhost_ctx[i].base_path) == 0) {
  181. break;
  182. }
  183. }
  184. if (i == CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS) {
  185. return ESP_ERR_INVALID_ARG;
  186. }
  187. esp_err_t ret = esp_vfs_unregister(s_semhost_ctx[i].base_path);
  188. if (ret != ESP_OK) {
  189. return ret;
  190. }
  191. s_semhost_ctx[i].base_path[0] = 0;
  192. ESP_LOGD(TAG, "Unregistered semihosting driver @ '%s'", base_path);
  193. return ESP_OK;
  194. }