arm_semihosting: fix two dead assignments
[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 } else if (arm->core_state == ARM_STATE_ARM) {
127 /* return value in R0 */
128 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
129 arm->core_cache->reg_list[0].dirty = true;
130
131 uint32_t pc = buf_get_u32(arm->core_cache->reg_list[32].value, 0, 32);
132 buf_set_u32(arm->pc->value, 0, 32, pc + 4);
133 arm->pc->dirty = true;
134 } else if (arm->core_state == ARM_STATE_THUMB) {
135 /* return value in R0 */
136 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
137 arm->core_cache->reg_list[0].dirty = true;
138
139 uint32_t pc = buf_get_u32(arm->core_cache->reg_list[32].value, 0, 32);
140 buf_set_u32(arm->pc->value, 0, 32, pc + 2);
141 arm->pc->dirty = true;
142 }
143 } else {
144 /* resume execution, this will be pc+2 to skip over the
145 * bkpt instruction */
146
147 /* return result in R0 */
148 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
149 arm->core_cache->reg_list[0].dirty = true;
150 }
151
152 return ERROR_OK;
153 }
154
155 /**
156 * Initialize ARM semihosting support.
157 *
158 * @param target Pointer to the ARM target to initialize.
159 * @return An error status if there is a problem during initialization.
160 */
161 int arm_semihosting_init(struct target *target)
162 {
163 struct arm *arm = target_to_arm(target);
164 assert(arm->setup_semihosting);
165 semihosting_common_init(target, arm->setup_semihosting, post_result);
166
167 return ERROR_OK;
168 }
169
170 /**
171 * Checks for and processes an ARM semihosting request. This is meant
172 * to be called when the target is stopped due to a debug mode entry.
173 * If the value 0 is returned then there was nothing to process. A non-zero
174 * return value signifies that a request was processed and the target resumed,
175 * or an error was encountered, in which case the caller must return
176 * immediately.
177 *
178 * @param target Pointer to the ARM target to process. This target must
179 * not represent an ARMv6-M or ARMv7-M processor.
180 * @param retval Pointer to a location where the return code will be stored
181 * @return non-zero value if a request was processed or an error encountered
182 */
183 int arm_semihosting(struct target *target, int *retval)
184 {
185 struct arm *arm = target_to_arm(target);
186 struct armv7a_common *armv7a = target_to_armv7a(target);
187 uint32_t pc, lr, spsr;
188 struct reg *r;
189
190 struct semihosting *semihosting = target->semihosting;
191 if (!semihosting)
192 return 0;
193
194 if (!semihosting->is_active)
195 return 0;
196
197 if (is_arm7_9(target_to_arm7_9(target)) ||
198 is_armv7a(armv7a)) {
199 uint32_t vbar = 0x00000000;
200
201 if (arm->core_mode != ARM_MODE_SVC)
202 return 0;
203
204 if (is_armv7a(armv7a)) {
205 struct arm_dpm *dpm = armv7a->arm.dpm;
206
207 *retval = dpm->prepare(dpm);
208 if (*retval == ERROR_OK) {
209 *retval = dpm->instr_read_data_r0(dpm,
210 ARMV4_5_MRC(15, 0, 0, 12, 0, 0),
211 &vbar);
212
213 dpm->finish(dpm);
214
215 if (*retval != ERROR_OK)
216 return 1;
217 } else {
218 return 1;
219 }
220 }
221
222 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
223 r = arm->pc;
224 pc = buf_get_u32(r->value, 0, 32);
225 if (pc != (vbar + 0x00000008) && pc != 0xffff0008)
226 return 0;
227
228 r = arm_reg_current(arm, 14);
229 lr = buf_get_u32(r->value, 0, 32);
230
231 /* Core-specific code should make sure SPSR is retrieved
232 * when the above checks pass...
233 */
234 if (!arm->spsr->valid) {
235 LOG_ERROR("SPSR not valid!");
236 *retval = ERROR_FAIL;
237 return 1;
238 }
239
240 spsr = buf_get_u32(arm->spsr->value, 0, 32);
241
242 /* check instruction that triggered this trap */
243 if (spsr & (1 << 5)) {
244 /* was in Thumb (or ThumbEE) mode */
245 uint8_t insn_buf[2];
246 uint16_t insn;
247
248 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
249 if (*retval != ERROR_OK)
250 return 1;
251 insn = target_buffer_get_u16(target, insn_buf);
252
253 /* SVC 0xab */
254 if (insn != 0xDFAB)
255 return 0;
256 } else if (spsr & (1 << 24)) {
257 /* was in Jazelle mode */
258 return 0;
259 } else {
260 /* was in ARM mode */
261 uint8_t insn_buf[4];
262 uint32_t insn;
263
264 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
265 if (*retval != ERROR_OK)
266 return 1;
267 insn = target_buffer_get_u32(target, insn_buf);
268
269 /* SVC 0x123456 */
270 if (insn != 0xEF123456)
271 return 0;
272 }
273 } else if (is_armv7m(target_to_armv7m(target))) {
274 uint16_t insn;
275
276 if (target->debug_reason != DBG_REASON_BREAKPOINT)
277 return 0;
278
279 r = arm->pc;
280 pc = buf_get_u32(r->value, 0, 32);
281
282 pc &= ~1;
283 *retval = target_read_u16(target, pc, &insn);
284 if (*retval != ERROR_OK)
285 return 1;
286
287 /* bkpt 0xAB */
288 if (insn != 0xBEAB)
289 return 0;
290 } else if (is_armv8(target_to_armv8(target))) {
291 if (target->debug_reason != DBG_REASON_BREAKPOINT)
292 return 0;
293
294 /* According to ARM Semihosting for AArch32 and AArch64:
295 * The HLT encodings are new in version 2.0 of the semihosting specification.
296 * Where possible, have semihosting callers continue to use the previously
297 * existing trap instructions to ensure compatibility with legacy semihosting
298 * implementations.
299 * These trap instructions are HLT for A64, SVC on A+R profile A32 or T32,
300 * and BKPT on M profile.
301 * However, it is necessary to change from SVC to HLT instructions to support
302 * AArch32 semihosting properly in a mixed AArch32/AArch64 system. */
303
304 if (arm->core_state == ARM_STATE_AARCH64) {
305 uint32_t insn = 0;
306 r = arm->pc;
307 uint64_t pc64 = buf_get_u64(r->value, 0, 64);
308 *retval = target_read_u32(target, pc64, &insn);
309
310 if (*retval != ERROR_OK)
311 return 1;
312
313 /* HLT 0xF000 */
314 if (insn != 0xD45E0000)
315 return 0;
316 } else if (arm->core_state == ARM_STATE_ARM) {
317 r = arm->pc;
318 pc = buf_get_u32(r->value, 0, 32);
319
320 /* A32 instruction => check for HLT 0xF000 (0xE10F0070) */
321 uint32_t insn = 0;
322
323 *retval = target_read_u32(target, pc, &insn);
324
325 if (*retval != ERROR_OK)
326 return 1;
327
328 /* HLT 0xF000*/
329 if (insn != 0xE10F0070)
330 return 0;
331 } else if (arm->core_state == ARM_STATE_THUMB) {
332 r = arm->pc;
333 pc = buf_get_u32(r->value, 0, 32);
334
335 /* T32 instruction => check for HLT 0x3C (0xBABC) */
336 uint16_t insn = 0;
337 *retval = target_read_u16(target, pc, &insn);
338
339 if (*retval != ERROR_OK)
340 return 1;
341
342 /* HLT 0x3C*/
343 if (insn != 0xBABC)
344 return 0;
345 } else
346 return 1;
347 } else {
348 LOG_ERROR("Unsupported semi-hosting Target");
349 return 0;
350 }
351
352 /* Perform semihosting if we are not waiting on a fileio
353 * operation to complete.
354 */
355 if (!semihosting->hit_fileio) {
356 if (is_armv8(target_to_armv8(target)) &&
357 arm->core_state == ARM_STATE_AARCH64) {
358 /* Read op and param from register x0 and x1 respectively. */
359 semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64);
360 semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64);
361 semihosting->word_size_bytes = 8;
362 } else {
363 /* Read op and param from register r0 and r1 respectively. */
364 semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
365 semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
366 semihosting->word_size_bytes = 4;
367 }
368
369 /* Check for ARM operation numbers. */
370 if (semihosting->op >= 0 && semihosting->op <= 0x31) {
371 *retval = semihosting_common(target);
372 if (*retval != ERROR_OK) {
373 LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
374 return 0;
375 }
376 } else {
377 /* Unknown operation number, not a semihosting call. */
378 return 0;
379 }
380 }
381
382 /* Resume if target it is resumable and we are not waiting on a fileio
383 * operation to complete:
384 */
385 if (semihosting->is_resumable && !semihosting->hit_fileio)
386 return arm_semihosting_resume(target, retval);
387
388 return 0;
389 }

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)