semihosting: move semihosting_result_t from riscv.h to the semihosting_common.h
[openocd.git] / src / target / riscv / riscv_semihosting.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4 * Copyright (C) 2018 by Liviu Ionescu *
5 * ilg@livius.net *
6 * *
7 * Copyright (C) 2009 by Marvell Technology Group Ltd. *
8 * Written by Nicolas Pitre <nico@marvell.com> *
9 * *
10 * Copyright (C) 2010 by Spencer Oliver *
11 * spen@spen-soft.co.uk *
12 * *
13 * Copyright (C) 2016 by Square, Inc. *
14 * Steven Stallion <stallion@squareup.com> *
15 * *
16 * This program is free software; you can redistribute it and/or modify *
17 * it under the terms of the GNU General Public License as published by *
18 * the Free Software Foundation; either version 2 of the License, or *
19 * (at your option) any later version. *
20 * *
21 * This program is distributed in the hope that it will be useful, *
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
24 * GNU General Public License for more details. *
25 * *
26 * You should have received a copy of the GNU General Public License *
27 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
28 ***************************************************************************/
29
30 /**
31 * @file
32 * Hold RISC-V semihosting support.
33 *
34 * The RISC-V code is inspired from ARM semihosting.
35 *
36 * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
37 * from ARM Ltd.
38 */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include <helper/log.h>
45
46 #include "target/target.h"
47 #include "riscv.h"
48
49 static int riscv_semihosting_setup(struct target *target, int enable);
50 static int riscv_semihosting_post_result(struct target *target);
51
52 /**
53 * Initialize RISC-V semihosting. Use common ARM code.
54 */
55 void riscv_semihosting_init(struct target *target)
56 {
57 semihosting_common_init(target, riscv_semihosting_setup,
58 riscv_semihosting_post_result);
59 }
60
61 /**
62 * Check for and process a semihosting request using the ARM protocol). This
63 * is meant to be called when the target is stopped due to a debug mode entry.
64 *
65 * @param target Pointer to the target to process.
66 * @param retval Pointer to a location where the return code will be stored
67 * @return non-zero value if a request was processed or an error encountered
68 */
69 enum semihosting_result riscv_semihosting(struct target *target, int *retval)
70 {
71 struct semihosting *semihosting = target->semihosting;
72 if (!semihosting) {
73 LOG_DEBUG(" -> NONE (!semihosting)");
74 return SEMIHOSTING_NONE;
75 }
76
77 if (!semihosting->is_active) {
78 LOG_DEBUG(" -> NONE (!semihosting->is_active)");
79 return SEMIHOSTING_NONE;
80 }
81
82 riscv_reg_t pc;
83 int result = riscv_get_register(target, &pc, GDB_REGNO_PC);
84 if (result != ERROR_OK)
85 return SEMIHOSTING_ERROR;
86
87 uint8_t tmp_buf[12];
88
89 /* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */
90 for (int i = 0; i < 3; i++) {
91 /* Instruction memories may not support arbitrary read size. Use any size that will work. */
92 *retval = riscv_read_by_any_size(target, (pc - 4) + 4 * i, 4, tmp_buf + 4 * i);
93 if (*retval != ERROR_OK)
94 return SEMIHOSTING_ERROR;
95 }
96
97 /*
98 * The instructions that trigger a semihosting call,
99 * always uncompressed, should look like:
100 *
101 * 01f01013 slli zero,zero,0x1f
102 * 00100073 ebreak
103 * 40705013 srai zero,zero,0x7
104 */
105 uint32_t pre = target_buffer_get_u32(target, tmp_buf);
106 uint32_t ebreak = target_buffer_get_u32(target, tmp_buf + 4);
107 uint32_t post = target_buffer_get_u32(target, tmp_buf + 8);
108 LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
109
110 if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
111 /* Not the magic sequence defining semihosting. */
112 LOG_DEBUG(" -> NONE (no magic)");
113 return SEMIHOSTING_NONE;
114 }
115
116 /*
117 * Perform semihosting call if we are not waiting on a fileio
118 * operation to complete.
119 */
120 if (!semihosting->hit_fileio) {
121 /* RISC-V uses A0 and A1 to pass function arguments */
122 riscv_reg_t r0;
123 riscv_reg_t r1;
124
125 result = riscv_get_register(target, &r0, GDB_REGNO_A0);
126 if (result != ERROR_OK) {
127 LOG_DEBUG(" -> ERROR (couldn't read a0)");
128 return SEMIHOSTING_ERROR;
129 }
130
131 result = riscv_get_register(target, &r1, GDB_REGNO_A1);
132 if (result != ERROR_OK) {
133 LOG_DEBUG(" -> ERROR (couldn't read a1)");
134 return SEMIHOSTING_ERROR;
135 }
136
137 semihosting->op = r0;
138 semihosting->param = r1;
139 semihosting->word_size_bytes = riscv_xlen(target) / 8;
140
141 /* Check for ARM operation numbers. */
142 if ((semihosting->op >= 0 && semihosting->op <= 0x31) ||
143 (semihosting->op >= 0x100 && semihosting->op <= 0x107)) {
144
145 *retval = semihosting_common(target);
146 if (*retval != ERROR_OK) {
147 LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
148 return SEMIHOSTING_ERROR;
149 }
150 } else {
151 /* Unknown operation number, not a semihosting call. */
152 LOG_DEBUG(" -> NONE (unknown operation number)");
153 return SEMIHOSTING_NONE;
154 }
155 }
156
157 /*
158 * Resume target if we are not waiting on a fileio
159 * operation to complete.
160 */
161 if (semihosting->is_resumable && !semihosting->hit_fileio) {
162 /* Resume right after the EBREAK 4 bytes instruction. */
163 *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4);
164 if (*retval != ERROR_OK)
165 return SEMIHOSTING_ERROR;
166
167 LOG_DEBUG(" -> HANDLED");
168 return SEMIHOSTING_HANDLED;
169 }
170
171 LOG_DEBUG(" -> WAITING");
172 return SEMIHOSTING_WAITING;
173 }
174
175 /* -------------------------------------------------------------------------
176 * Local functions. */
177
178 /**
179 * Called via semihosting->setup() later, after the target is known,
180 * usually on the first semihosting command.
181 */
182 static int riscv_semihosting_setup(struct target *target, int enable)
183 {
184 LOG_DEBUG("[%s] enable=%d", target_name(target), enable);
185
186 struct semihosting *semihosting = target->semihosting;
187 if (semihosting)
188 semihosting->setup_time = clock();
189
190 return ERROR_OK;
191 }
192
193 static int riscv_semihosting_post_result(struct target *target)
194 {
195 struct semihosting *semihosting = target->semihosting;
196 if (!semihosting) {
197 /* If not enabled, silently ignored. */
198 return 0;
199 }
200
201 LOG_DEBUG("0x%" PRIx64, semihosting->result);
202 riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
203 return 0;
204 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)