target/adi_v5_swd: update cached value on write to DP_SELECT
[openocd.git] / src / target / arm_semihosting.c
1 /***************************************************************************
2 * Copyright (C) 2009 by Marvell Technology Group Ltd. *
3 * Written by Nicolas Pitre <nico@marvell.com> *
4 * *
5 * Copyright (C) 2010 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
7 * *
8 * Copyright (C) 2016 by Square, Inc. *
9 * Steven Stallion <stallion@squareup.com> *
10 * *
11 * Copyright (C) 2018 by Liviu Ionescu *
12 * <ilg@livius.net> *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22 * GNU General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU General Public License *
25 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
26 ***************************************************************************/
27
28 /**
29 * @file
30 * Hold ARM semihosting support.
31 *
32 * Semihosting enables code running on an ARM target to use the I/O
33 * facilities on the host computer. The target application must be linked
34 * against a library that forwards operation requests by using the SVC
35 * instruction trapped at the Supervisor Call vector by the debugger.
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 "arm.h"
45 #include "armv4_5.h"
46 #include "arm7_9_common.h"
47 #include "armv7m.h"
48 #include "armv7a.h"
49 #include "armv8.h"
50 #include "cortex_m.h"
51 #include "register.h"
52 #include "arm_opcodes.h"
53 #include "target_type.h"
54 #include "arm_semihosting.h"
55 #include <helper/binarybuffer.h>
56 #include <helper/log.h>
57 #include <sys/stat.h>
58
59 static int arm_semihosting_resume(struct target *target, int *retval)
60 {
61 if (is_armv8(target_to_armv8(target))) {
62 struct armv8_common *armv8 = target_to_armv8(target);
63 if (armv8->last_run_control_op == ARMV8_RUNCONTROL_RESUME) {
64 *retval = target_resume(target, 1, 0, 0, 0);
65 if (*retval != ERROR_OK) {
66 LOG_ERROR("Failed to resume target");
67 return 0;
68 }
69 } else if (armv8->last_run_control_op == ARMV8_RUNCONTROL_STEP)
70 target->debug_reason = DBG_REASON_SINGLESTEP;
71 } else {
72 *retval = target_resume(target, 1, 0, 0, 0);
73 if (*retval != ERROR_OK) {
74 LOG_ERROR("Failed to resume target");
75 return 0;
76 }
77 }
78 return 1;
79 }
80
81 static int post_result(struct target *target)
82 {
83 struct arm *arm = target_to_arm(target);
84
85 if (!target->semihosting)
86 return ERROR_FAIL;
87
88 /* REVISIT this looks wrong ... ARM11 and Cortex-A8
89 * should work this way at least sometimes.
90 */
91 if (is_arm7_9(target_to_arm7_9(target)) ||
92 is_armv7a(target_to_armv7a(target))) {
93 uint32_t spsr;
94
95 /* return value in R0 */
96 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
97 arm->core_cache->reg_list[0].dirty = true;
98
99 /* LR --> PC */
100 buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32,
101 buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32));
102 arm->core_cache->reg_list[15].dirty = true;
103
104 /* saved PSR --> current PSR */
105 spsr = buf_get_u32(arm->spsr->value, 0, 32);
106
107 /* REVISIT should this be arm_set_cpsr(arm, spsr)
108 * instead of a partially unrolled version?
109 */
110
111 buf_set_u32(arm->cpsr->value, 0, 32, spsr);
112 arm->cpsr->dirty = true;
113 arm->core_mode = spsr & 0x1f;
114 if (spsr & 0x20)
115 arm->core_state = ARM_STATE_THUMB;
116
117 } else if (is_armv8(target_to_armv8(target))) {
118 if (arm->core_state == ARM_STATE_AARCH64) {
119 /* return value in R0 */
120 buf_set_u64(arm->core_cache->reg_list[0].value, 0, 64, target->semihosting->result);
121 arm->core_cache->reg_list[0].dirty = true;
122
123 uint64_t pc = buf_get_u64(arm->core_cache->reg_list[32].value, 0, 64);
124 buf_set_u64(arm->pc->value, 0, 64, pc + 4);
125 arm->pc->dirty = true;
126 }
127 } else {
128 /* resume execution, this will be pc+2 to skip over the
129 * bkpt instruction */
130
131 /* return result in R0 */
132 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
133 arm->core_cache->reg_list[0].dirty = true;
134 }
135
136 return ERROR_OK;
137 }
138
139 /**
140 * Initialize ARM semihosting support.
141 *
142 * @param target Pointer to the ARM target to initialize.
143 * @return An error status if there is a problem during initialization.
144 */
145 int arm_semihosting_init(struct target *target)
146 {
147 struct arm *arm = target_to_arm(target);
148 assert(arm->setup_semihosting);
149 semihosting_common_init(target, arm->setup_semihosting, post_result);
150
151 return ERROR_OK;
152 }
153
154 /**
155 * Checks for and processes an ARM semihosting request. This is meant
156 * to be called when the target is stopped due to a debug mode entry.
157 * If the value 0 is returned then there was nothing to process. A non-zero
158 * return value signifies that a request was processed and the target resumed,
159 * or an error was encountered, in which case the caller must return
160 * immediately.
161 *
162 * @param target Pointer to the ARM target to process. This target must
163 * not represent an ARMv6-M or ARMv7-M processor.
164 * @param retval Pointer to a location where the return code will be stored
165 * @return non-zero value if a request was processed or an error encountered
166 */
167 int arm_semihosting(struct target *target, int *retval)
168 {
169 struct arm *arm = target_to_arm(target);
170 struct armv7a_common *armv7a = target_to_armv7a(target);
171 uint32_t pc, lr, spsr;
172 struct reg *r;
173
174 struct semihosting *semihosting = target->semihosting;
175 if (!semihosting)
176 return 0;
177
178 if (!semihosting->is_active)
179 return 0;
180
181 if (is_arm7_9(target_to_arm7_9(target)) ||
182 is_armv7a(armv7a)) {
183 uint32_t vbar = 0x00000000;
184
185 if (arm->core_mode != ARM_MODE_SVC)
186 return 0;
187
188 if (is_armv7a(armv7a)) {
189 struct arm_dpm *dpm = armv7a->arm.dpm;
190
191 *retval = dpm->prepare(dpm);
192 if (*retval == ERROR_OK) {
193 *retval = dpm->instr_read_data_r0(dpm,
194 ARMV4_5_MRC(15, 0, 0, 12, 0, 0),
195 &vbar);
196
197 dpm->finish(dpm);
198
199 if (*retval != ERROR_OK)
200 return 1;
201 } else {
202 return 1;
203 }
204 }
205
206 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
207 r = arm->pc;
208 pc = buf_get_u32(r->value, 0, 32);
209 if (pc != (vbar + 0x00000008) && pc != 0xffff0008)
210 return 0;
211
212 r = arm_reg_current(arm, 14);
213 lr = buf_get_u32(r->value, 0, 32);
214
215 /* Core-specific code should make sure SPSR is retrieved
216 * when the above checks pass...
217 */
218 if (!arm->spsr->valid) {
219 LOG_ERROR("SPSR not valid!");
220 *retval = ERROR_FAIL;
221 return 1;
222 }
223
224 spsr = buf_get_u32(arm->spsr->value, 0, 32);
225
226 /* check instruction that triggered this trap */
227 if (spsr & (1 << 5)) {
228 /* was in Thumb (or ThumbEE) mode */
229 uint8_t insn_buf[2];
230 uint16_t insn;
231
232 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
233 if (*retval != ERROR_OK)
234 return 1;
235 insn = target_buffer_get_u16(target, insn_buf);
236
237 /* SVC 0xab */
238 if (insn != 0xDFAB)
239 return 0;
240 } else if (spsr & (1 << 24)) {
241 /* was in Jazelle mode */
242 return 0;
243 } else {
244 /* was in ARM mode */
245 uint8_t insn_buf[4];
246 uint32_t insn;
247
248 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
249 if (*retval != ERROR_OK)
250 return 1;
251 insn = target_buffer_get_u32(target, insn_buf);
252
253 /* SVC 0x123456 */
254 if (insn != 0xEF123456)
255 return 0;
256 }
257 } else if (is_armv7m(target_to_armv7m(target))) {
258 uint16_t insn;
259
260 if (target->debug_reason != DBG_REASON_BREAKPOINT)
261 return 0;
262
263 r = arm->pc;
264 pc = buf_get_u32(r->value, 0, 32);
265
266 pc &= ~1;
267 *retval = target_read_u16(target, pc, &insn);
268 if (*retval != ERROR_OK)
269 return 1;
270
271 /* bkpt 0xAB */
272 if (insn != 0xBEAB)
273 return 0;
274 } else if (is_armv8(target_to_armv8(target))) {
275 if (target->debug_reason != DBG_REASON_BREAKPOINT)
276 return 0;
277
278 if (arm->core_state == ARM_STATE_AARCH64) {
279 uint32_t insn = 0;
280 r = arm->pc;
281 uint64_t pc64 = buf_get_u64(r->value, 0, 64);
282 *retval = target_read_u32(target, pc64, &insn);
283
284 if (*retval != ERROR_OK)
285 return 1;
286
287 /* bkpt 0xAB */
288 if (insn != 0xD45E0000)
289 return 0;
290 } else
291 return 1;
292 } else {
293 LOG_ERROR("Unsupported semi-hosting Target");
294 return 0;
295 }
296
297 /* Perform semihosting if we are not waiting on a fileio
298 * operation to complete.
299 */
300 if (!semihosting->hit_fileio) {
301 if (is_armv8(target_to_armv8(target)) &&
302 arm->core_state == ARM_STATE_AARCH64) {
303 /* Read op and param from register x0 and x1 respectively. */
304 semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64);
305 semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64);
306 semihosting->word_size_bytes = 8;
307 } else {
308 /* Read op and param from register r0 and r1 respectively. */
309 semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
310 semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
311 semihosting->word_size_bytes = 4;
312 }
313
314 /* Check for ARM operation numbers. */
315 if (0 <= semihosting->op && semihosting->op <= 0x31) {
316 *retval = semihosting_common(target);
317 if (*retval != ERROR_OK) {
318 LOG_ERROR("Failed semihosting operation");
319 return 0;
320 }
321 } else {
322 /* Unknown operation number, not a semihosting call. */
323 return 0;
324 }
325 }
326
327 /* Resume if target it is resumable and we are not waiting on a fileio
328 * operation to complete:
329 */
330 if (semihosting->is_resumable && !semihosting->hit_fileio)
331 return arm_semihosting_resume(target, retval);
332
333 return 0;
334 }

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)