target/espressif: add semihosting support
[fw/openocd] / src / target / espressif / esp_xtensa_semihosting.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4  *   Copyright (c) 2020 Espressif Systems (Shanghai) Co. Ltd.              *
5  ***************************************************************************/
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include <target/semihosting_common.h>
12 #include <target/xtensa/xtensa_regs.h>
13 #include <target/xtensa/xtensa.h>
14 #include "esp_xtensa.h"
15 #include "esp_xtensa_semihosting.h"
16
17 #define ESP_XTENSA_SYSCALL              0x41E0  /* XT_INS_BREAK(1, 14) */
18 #define ESP_XTENSA_SYSCALL_SZ           3
19
20 #define XTENSA_SYSCALL_OP_REG           XT_REG_IDX_A2
21 #define XTENSA_SYSCALL_RETVAL_REG       XT_REG_IDX_A2
22 #define XTENSA_SYSCALL_ERRNO_REG        XT_REG_IDX_A3
23
24 static int esp_xtensa_semihosting_setup(struct target *target, int enable)
25 {
26         LOG_TARGET_DEBUG(target, "semihosting enable=%d", enable);
27
28         return ERROR_OK;
29 }
30
31 static int esp_xtensa_semihosting_post_result(struct target *target)
32 {
33         /* Even with the v2 and later, errno will not retrieved from A3 reg, it is safe to set */
34         xtensa_reg_set(target, XTENSA_SYSCALL_RETVAL_REG, target->semihosting->result);
35         xtensa_reg_set(target, XTENSA_SYSCALL_ERRNO_REG, target->semihosting->sys_errno);
36         return ERROR_OK;
37 }
38
39 /**
40  * Checks and processes an ESP Xtensa semihosting request. This is meant
41  * to be called when the target is stopped due to a debug mode entry.
42  * If the value 0 is returned then there was nothing to process. A non-zero
43  * return value signifies that a request was processed and the target resumed,
44  * or an error was encountered, in which case the caller must return immediately.
45  *
46  * @param target Pointer to the ESP Xtensa target to process.
47  * @param retval Pointer to a location where the return code will be stored
48  * @return SEMIHOSTING_HANDLED if a request was processed or SEMIHOSTING_NONE with the proper retval
49  */
50 int esp_xtensa_semihosting(struct target *target, int *retval)
51 {
52         struct esp_xtensa_common *esp_xtensa = target_to_esp_xtensa(target);
53
54         xtensa_reg_val_t dbg_cause = xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
55         if ((dbg_cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN)) == 0)
56                 return SEMIHOSTING_NONE;
57
58         uint8_t brk_insn_buf[sizeof(uint32_t)] = { 0 };
59         xtensa_reg_val_t pc = xtensa_reg_get(target, XT_REG_IDX_PC);
60         *retval = target_read_memory(target, pc, ESP_XTENSA_SYSCALL_SZ, 1, brk_insn_buf);
61         if (*retval != ERROR_OK) {
62                 LOG_TARGET_ERROR(target, "Failed to read break instruction!");
63                 return SEMIHOSTING_NONE;
64         }
65
66         uint32_t syscall_ins = buf_get_u32(brk_insn_buf, 0, 32);
67         if (syscall_ins != ESP_XTENSA_SYSCALL) {
68                 *retval = ERROR_OK;
69                 return SEMIHOSTING_NONE;
70         }
71
72         if (esp_xtensa->semihost.ops && esp_xtensa->semihost.ops->prepare)
73                 esp_xtensa->semihost.ops->prepare(target);
74
75         xtensa_reg_val_t a2 = xtensa_reg_get(target, XT_REG_IDX_A2);
76         xtensa_reg_val_t a3 = xtensa_reg_get(target, XT_REG_IDX_A3);
77         LOG_TARGET_DEBUG(target, "Semihosting call 0x%" PRIx32 " 0x%" PRIx32 " Base dir '%s'",
78                 a2,
79                 a3,
80                 target->semihosting->basedir ? target->semihosting->basedir : "");
81
82         target->semihosting->op = a2;
83         target->semihosting->param = a3;
84
85         *retval = semihosting_common(target);
86
87         /* Most operations are resumable, except the two exit calls. */
88         if (*retval != ERROR_OK) {
89                 LOG_TARGET_ERROR(target, "Semihosting operation (op: 0x%x) error! Code: %d",
90                         target->semihosting->op,
91                         *retval);
92         }
93
94         /* Resume if target it is resumable and we are not waiting on a fileio operation to complete. */
95         if (target->semihosting->is_resumable && !target->semihosting->hit_fileio)
96                 target_to_esp_xtensa(target)->semihost.need_resume = true;
97
98         return SEMIHOSTING_HANDLED;
99 }
100
101 static int xtensa_semihosting_init(struct target *target)
102 {
103         return semihosting_common_init(target, esp_xtensa_semihosting_setup, esp_xtensa_semihosting_post_result);
104 }
105
106 int esp_xtensa_semihosting_init(struct target *target)
107 {
108         int retval = xtensa_semihosting_init(target);
109         if (retval != ERROR_OK)
110                 return retval;
111         target->semihosting->word_size_bytes = 4;                       /* 32 bits */
112         target->semihosting->user_command_extension = esp_semihosting_common;
113         return ERROR_OK;
114 }