jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / target / arm_semihosting.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 * Copyright (C) 2009 by Marvell Technology Group Ltd. *
5 * Written by Nicolas Pitre <nico@marvell.com> *
6 * *
7 * Copyright (C) 2010 by Spencer Oliver *
8 * spen@spen-soft.co.uk *
9 * *
10 * Copyright (C) 2016 by Square, Inc. *
11 * Steven Stallion <stallion@squareup.com> *
12 * *
13 * Copyright (C) 2018 by Liviu Ionescu *
14 * <ilg@livius.net> *
15 ***************************************************************************/
16
17 /**
18 * @file
19 * Hold ARM semihosting support.
20 *
21 * Semihosting enables code running on an ARM target to use the I/O
22 * facilities on the host computer. The target application must be linked
23 * against a library that forwards operation requests by using the SVC
24 * instruction trapped at the Supervisor Call vector by the debugger.
25 * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
26 * from ARM Ltd.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "arm.h"
34 #include "armv4_5.h"
35 #include "arm7_9_common.h"
36 #include "armv7m.h"
37 #include "armv7a.h"
38 #include "armv8.h"
39 #include "cortex_m.h"
40 #include "register.h"
41 #include "arm_opcodes.h"
42 #include "target_type.h"
43 #include "arm_semihosting.h"
44 #include <helper/binarybuffer.h>
45 #include <helper/log.h>
46 #include <sys/stat.h>
47
48 static int arm_semihosting_resume(struct target *target, int *retval)
49 {
50 if (is_armv8(target_to_armv8(target))) {
51 struct armv8_common *armv8 = target_to_armv8(target);
52 if (armv8->last_run_control_op == ARMV8_RUNCONTROL_RESUME) {
53 *retval = target_resume(target, 1, 0, 0, 0);
54 if (*retval != ERROR_OK) {
55 LOG_ERROR("Failed to resume target");
56 return 0;
57 }
58 } else if (armv8->last_run_control_op == ARMV8_RUNCONTROL_STEP)
59 target->debug_reason = DBG_REASON_SINGLESTEP;
60 } else {
61 *retval = target_resume(target, 1, 0, 0, 0);
62 if (*retval != ERROR_OK) {
63 LOG_ERROR("Failed to resume target");
64 return 0;
65 }
66 }
67 return 1;
68 }
69
70 static int post_result(struct target *target)
71 {
72 struct arm *arm = target_to_arm(target);
73
74 if (!target->semihosting)
75 return ERROR_FAIL;
76
77 /* REVISIT this looks wrong ... ARM11 and Cortex-A8
78 * should work this way at least sometimes.
79 */
80 if (is_arm7_9(target_to_arm7_9(target)) ||
81 is_armv7a(target_to_armv7a(target))) {
82 uint32_t spsr;
83
84 /* return value in R0 */
85 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
86 arm->core_cache->reg_list[0].dirty = true;
87
88 /* LR --> PC */
89 buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32,
90 buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32));
91 arm->core_cache->reg_list[15].dirty = true;
92
93 /* saved PSR --> current PSR */
94 spsr = buf_get_u32(arm->spsr->value, 0, 32);
95
96 /* REVISIT should this be arm_set_cpsr(arm, spsr)
97 * instead of a partially unrolled version?
98 */
99
100 buf_set_u32(arm->cpsr->value, 0, 32, spsr);
101 arm->cpsr->dirty = true;
102 arm->core_mode = spsr & 0x1f;
103 if (spsr & 0x20)
104 arm->core_state = ARM_STATE_THUMB;
105
106 } else if (is_armv8(target_to_armv8(target))) {
107 if (arm->core_state == ARM_STATE_AARCH64) {
108 /* return value in R0 */
109 buf_set_u64(arm->core_cache->reg_list[0].value, 0, 64, target->semihosting->result);
110 arm->core_cache->reg_list[0].dirty = true;
111
112 uint64_t pc = buf_get_u64(arm->core_cache->reg_list[32].value, 0, 64);
113 buf_set_u64(arm->pc->value, 0, 64, pc + 4);
114 arm->pc->dirty = true;
115 } else if (arm->core_state == ARM_STATE_ARM) {
116 /* return value in R0 */
117 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
118 arm->core_cache->reg_list[0].dirty = true;
119
120 uint32_t pc = buf_get_u32(arm->core_cache->reg_list[32].value, 0, 32);
121 buf_set_u32(arm->pc->value, 0, 32, pc + 4);
122 arm->pc->dirty = true;
123 } else if (arm->core_state == ARM_STATE_THUMB) {
124 /* return value in R0 */
125 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
126 arm->core_cache->reg_list[0].dirty = true;
127
128 uint32_t pc = buf_get_u32(arm->core_cache->reg_list[32].value, 0, 32);
129 buf_set_u32(arm->pc->value, 0, 32, pc + 2);
130 arm->pc->dirty = true;
131 }
132 } else {
133 /* resume execution, this will be pc+2 to skip over the
134 * bkpt instruction */
135
136 /* return result in R0 */
137 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
138 arm->core_cache->reg_list[0].dirty = true;
139 }
140
141 return ERROR_OK;
142 }
143
144 /**
145 * Initialize ARM semihosting support.
146 *
147 * @param target Pointer to the ARM target to initialize.
148 * @return An error status if there is a problem during initialization.
149 */
150 int arm_semihosting_init(struct target *target)
151 {
152 struct arm *arm = target_to_arm(target);
153 assert(arm->setup_semihosting);
154 semihosting_common_init(target, arm->setup_semihosting, post_result);
155
156 return ERROR_OK;
157 }
158
159 /**
160 * Checks for and processes an ARM semihosting request. This is meant
161 * to be called when the target is stopped due to a debug mode entry.
162 * If the value 0 is returned then there was nothing to process. A non-zero
163 * return value signifies that a request was processed and the target resumed,
164 * or an error was encountered, in which case the caller must return
165 * immediately.
166 *
167 * @param target Pointer to the ARM target to process. This target must
168 * not represent an ARMv6-M or ARMv7-M processor.
169 * @param retval Pointer to a location where the return code will be stored
170 * @return non-zero value if a request was processed or an error encountered
171 */
172 int arm_semihosting(struct target *target, int *retval)
173 {
174 struct arm *arm = target_to_arm(target);
175 struct armv7a_common *armv7a = target_to_armv7a(target);
176 uint32_t pc, lr, spsr;
177 struct reg *r;
178
179 struct semihosting *semihosting = target->semihosting;
180 if (!semihosting)
181 return 0;
182
183 if (!semihosting->is_active)
184 return 0;
185
186 if (is_arm7_9(target_to_arm7_9(target)) ||
187 is_armv7a(armv7a)) {
188 uint32_t vbar = 0x00000000;
189
190 if (arm->core_mode != ARM_MODE_SVC)
191 return 0;
192
193 if (is_armv7a(armv7a)) {
194 struct arm_dpm *dpm = armv7a->arm.dpm;
195
196 *retval = dpm->prepare(dpm);
197 if (*retval == ERROR_OK) {
198 *retval = dpm->instr_read_data_r0(dpm,
199 ARMV4_5_MRC(15, 0, 0, 12, 0, 0),
200 &vbar);
201
202 dpm->finish(dpm);
203
204 if (*retval != ERROR_OK)
205 return 1;
206 } else {
207 return 1;
208 }
209 }
210
211 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
212 r = arm->pc;
213 pc = buf_get_u32(r->value, 0, 32);
214 if (pc != (vbar + 0x00000008) && pc != 0xffff0008)
215 return 0;
216
217 r = arm_reg_current(arm, 14);
218 lr = buf_get_u32(r->value, 0, 32);
219
220 /* Core-specific code should make sure SPSR is retrieved
221 * when the above checks pass...
222 */
223 if (!arm->spsr->valid) {
224 LOG_ERROR("SPSR not valid!");
225 *retval = ERROR_FAIL;
226 return 1;
227 }
228
229 spsr = buf_get_u32(arm->spsr->value, 0, 32);
230
231 /* check instruction that triggered this trap */
232 if (spsr & (1 << 5)) {
233 /* was in Thumb (or ThumbEE) mode */
234 uint8_t insn_buf[2];
235 uint16_t insn;
236
237 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
238 if (*retval != ERROR_OK)
239 return 1;
240 insn = target_buffer_get_u16(target, insn_buf);
241
242 /* SVC 0xab */
243 if (insn != 0xDFAB)
244 return 0;
245 } else if (spsr & (1 << 24)) {
246 /* was in Jazelle mode */
247 return 0;
248 } else {
249 /* was in ARM mode */
250 uint8_t insn_buf[4];
251 uint32_t insn;
252
253 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
254 if (*retval != ERROR_OK)
255 return 1;
256 insn = target_buffer_get_u32(target, insn_buf);
257
258 /* SVC 0x123456 */
259 if (insn != 0xEF123456)
260 return 0;
261 }
262 } else if (is_armv7m(target_to_armv7m(target))) {
263 uint16_t insn;
264
265 if (target->debug_reason != DBG_REASON_BREAKPOINT)
266 return 0;
267
268 r = arm->pc;
269 pc = buf_get_u32(r->value, 0, 32);
270
271 pc &= ~1;
272 *retval = target_read_u16(target, pc, &insn);
273 if (*retval != ERROR_OK)
274 return 1;
275
276 /* bkpt 0xAB */
277 if (insn != 0xBEAB)
278 return 0;
279 } else if (is_armv8(target_to_armv8(target))) {
280 if (target->debug_reason != DBG_REASON_BREAKPOINT)
281 return 0;
282
283 /* According to ARM Semihosting for AArch32 and AArch64:
284 * The HLT encodings are new in version 2.0 of the semihosting specification.
285 * Where possible, have semihosting callers continue to use the previously
286 * existing trap instructions to ensure compatibility with legacy semihosting
287 * implementations.
288 * These trap instructions are HLT for A64, SVC on A+R profile A32 or T32,
289 * and BKPT on M profile.
290 * However, it is necessary to change from SVC to HLT instructions to support
291 * AArch32 semihosting properly in a mixed AArch32/AArch64 system. */
292
293 if (arm->core_state == ARM_STATE_AARCH64) {
294 uint32_t insn = 0;
295 r = arm->pc;
296 uint64_t pc64 = buf_get_u64(r->value, 0, 64);
297 *retval = target_read_u32(target, pc64, &insn);
298
299 if (*retval != ERROR_OK)
300 return 1;
301
302 /* HLT 0xF000 */
303 if (insn != 0xD45E0000)
304 return 0;
305 } else if (arm->core_state == ARM_STATE_ARM) {
306 r = arm->pc;
307 pc = buf_get_u32(r->value, 0, 32);
308
309 /* A32 instruction => check for HLT 0xF000 (0xE10F0070) */
310 uint32_t insn = 0;
311
312 *retval = target_read_u32(target, pc, &insn);
313
314 if (*retval != ERROR_OK)
315 return 1;
316
317 /* HLT 0xF000*/
318 if (insn != 0xE10F0070)
319 return 0;
320 } else if (arm->core_state == ARM_STATE_THUMB) {
321 r = arm->pc;
322 pc = buf_get_u32(r->value, 0, 32);
323
324 /* T32 instruction => check for HLT 0x3C (0xBABC) */
325 uint16_t insn = 0;
326 *retval = target_read_u16(target, pc, &insn);
327
328 if (*retval != ERROR_OK)
329 return 1;
330
331 /* HLT 0x3C*/
332 if (insn != 0xBABC)
333 return 0;
334 } else
335 return 1;
336 } else {
337 LOG_ERROR("Unsupported semi-hosting Target");
338 return 0;
339 }
340
341 /* Perform semihosting if we are not waiting on a fileio
342 * operation to complete.
343 */
344 if (!semihosting->hit_fileio) {
345 if (is_armv8(target_to_armv8(target)) &&
346 arm->core_state == ARM_STATE_AARCH64) {
347 /* Read op and param from register x0 and x1 respectively. */
348 semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64);
349 semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64);
350 semihosting->word_size_bytes = 8;
351 } else {
352 /* Read op and param from register r0 and r1 respectively. */
353 semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
354 semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
355 semihosting->word_size_bytes = 4;
356 }
357
358 /* Check for ARM operation numbers. */
359 if ((semihosting->op >= 0 && semihosting->op <= 0x31) ||
360 (semihosting->op >= 0x100 && semihosting->op <= 0x107)) {
361
362 *retval = semihosting_common(target);
363 if (*retval != ERROR_OK) {
364 LOG_ERROR("Failed semihosting operation (0x%02X)",
365 semihosting->op);
366 return 0;
367 }
368 } else {
369 /* Unknown operation number, not a semihosting call. */
370 return 0;
371 }
372 }
373
374 /* Resume if target it is resumable and we are not waiting on a fileio
375 * operation to complete:
376 */
377 if (semihosting->is_resumable && !semihosting->hit_fileio)
378 return arm_semihosting_resume(target, retval);
379
380 return 0;
381 }

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)