Add ARM v8 AArch64 semihosting support
[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 /* REVISIT this looks wrong ... ARM11 and Cortex-A8
86 * should work this way at least sometimes.
87 */
88 if (is_arm7_9(target_to_arm7_9(target)) ||
89 is_armv7a(target_to_armv7a(target))) {
90 uint32_t spsr;
91
92 /* return value in R0 */
93 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
94 arm->core_cache->reg_list[0].dirty = 1;
95
96 /* LR --> PC */
97 buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32,
98 buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32));
99 arm->core_cache->reg_list[15].dirty = 1;
100
101 /* saved PSR --> current PSR */
102 spsr = buf_get_u32(arm->spsr->value, 0, 32);
103
104 /* REVISIT should this be arm_set_cpsr(arm, spsr)
105 * instead of a partially unrolled version?
106 */
107
108 buf_set_u32(arm->cpsr->value, 0, 32, spsr);
109 arm->cpsr->dirty = 1;
110 arm->core_mode = spsr & 0x1f;
111 if (spsr & 0x20)
112 arm->core_state = ARM_STATE_THUMB;
113
114 } else if (is_armv8(target_to_armv8(target))) {
115 if (arm->core_state == ARM_STATE_AARCH64) {
116 /* return value in R0 */
117 buf_set_u64(arm->core_cache->reg_list[0].value, 0, 64, target->semihosting->result);
118 arm->core_cache->reg_list[0].dirty = 1;
119
120 uint64_t pc = buf_get_u64(arm->core_cache->reg_list[32].value, 0, 64);
121 buf_set_u64(arm->pc->value, 0, 64, pc + 4);
122 arm->pc->dirty = 1;
123 }
124 } else {
125 /* resume execution, this will be pc+2 to skip over the
126 * bkpt instruction */
127
128 /* return result in R0 */
129 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
130 arm->core_cache->reg_list[0].dirty = 1;
131 }
132
133 return ERROR_OK;
134 }
135
136 /**
137 * Initialize ARM semihosting support.
138 *
139 * @param target Pointer to the ARM target to initialize.
140 * @return An error status if there is a problem during initialization.
141 */
142 int arm_semihosting_init(struct target *target)
143 {
144 struct arm *arm = target_to_arm(target);
145 assert(arm->setup_semihosting);
146 semihosting_common_init(target, arm->setup_semihosting, post_result);
147
148 return ERROR_OK;
149 }
150
151 /**
152 * Checks for and processes an ARM semihosting request. This is meant
153 * to be called when the target is stopped due to a debug mode entry.
154 * If the value 0 is returned then there was nothing to process. A non-zero
155 * return value signifies that a request was processed and the target resumed,
156 * or an error was encountered, in which case the caller must return
157 * immediately.
158 *
159 * @param target Pointer to the ARM target to process. This target must
160 * not represent an ARMv6-M or ARMv7-M processor.
161 * @param retval Pointer to a location where the return code will be stored
162 * @return non-zero value if a request was processed or an error encountered
163 */
164 int arm_semihosting(struct target *target, int *retval)
165 {
166 struct arm *arm = target_to_arm(target);
167 struct armv7a_common *armv7a = target_to_armv7a(target);
168 uint32_t pc, lr, spsr;
169 struct reg *r;
170
171 struct semihosting *semihosting = target->semihosting;
172 if (!semihosting)
173 return 0;
174
175 if (!semihosting->is_active)
176 return 0;
177
178 if (is_arm7_9(target_to_arm7_9(target)) ||
179 is_armv7a(armv7a)) {
180 uint32_t vbar = 0x00000000;
181
182 if (arm->core_mode != ARM_MODE_SVC)
183 return 0;
184
185 if (is_armv7a(armv7a)) {
186 struct arm_dpm *dpm = armv7a->arm.dpm;
187
188 *retval = dpm->prepare(dpm);
189 if (*retval == ERROR_OK) {
190 *retval = dpm->instr_read_data_r0(dpm,
191 ARMV4_5_MRC(15, 0, 0, 12, 0, 0),
192 &vbar);
193
194 dpm->finish(dpm);
195
196 if (*retval != ERROR_OK)
197 return 1;
198 } else {
199 return 1;
200 }
201 }
202
203 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
204 r = arm->pc;
205 pc = buf_get_u32(r->value, 0, 32);
206 if (pc != (vbar + 0x00000008) && pc != 0xffff0008)
207 return 0;
208
209 r = arm_reg_current(arm, 14);
210 lr = buf_get_u32(r->value, 0, 32);
211
212 /* Core-specific code should make sure SPSR is retrieved
213 * when the above checks pass...
214 */
215 if (!arm->spsr->valid) {
216 LOG_ERROR("SPSR not valid!");
217 *retval = ERROR_FAIL;
218 return 1;
219 }
220
221 spsr = buf_get_u32(arm->spsr->value, 0, 32);
222
223 /* check instruction that triggered this trap */
224 if (spsr & (1 << 5)) {
225 /* was in Thumb (or ThumbEE) mode */
226 uint8_t insn_buf[2];
227 uint16_t insn;
228
229 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
230 if (*retval != ERROR_OK)
231 return 1;
232 insn = target_buffer_get_u16(target, insn_buf);
233
234 /* SVC 0xab */
235 if (insn != 0xDFAB)
236 return 0;
237 } else if (spsr & (1 << 24)) {
238 /* was in Jazelle mode */
239 return 0;
240 } else {
241 /* was in ARM mode */
242 uint8_t insn_buf[4];
243 uint32_t insn;
244
245 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
246 if (*retval != ERROR_OK)
247 return 1;
248 insn = target_buffer_get_u32(target, insn_buf);
249
250 /* SVC 0x123456 */
251 if (insn != 0xEF123456)
252 return 0;
253 }
254 } else if (is_armv7m(target_to_armv7m(target))) {
255 uint16_t insn;
256
257 if (target->debug_reason != DBG_REASON_BREAKPOINT)
258 return 0;
259
260 r = arm->pc;
261 pc = buf_get_u32(r->value, 0, 32);
262
263 pc &= ~1;
264 *retval = target_read_u16(target, pc, &insn);
265 if (*retval != ERROR_OK)
266 return 1;
267
268 /* bkpt 0xAB */
269 if (insn != 0xBEAB)
270 return 0;
271 } else if (is_armv8(target_to_armv8(target))) {
272 if (target->debug_reason != DBG_REASON_BREAKPOINT)
273 return 0;
274
275 if (arm->core_state == ARM_STATE_AARCH64) {
276 uint32_t insn = 0;
277 r = arm->pc;
278 uint64_t pc64 = buf_get_u64(r->value, 0, 64);
279 *retval = target_read_u32(target, pc64, &insn);
280
281 if (*retval != ERROR_OK)
282 return 1;
283
284 /* bkpt 0xAB */
285 if (insn != 0xD45E0000)
286 return 0;
287 } else
288 return 1;
289 } else {
290 LOG_ERROR("Unsupported semi-hosting Target");
291 return 0;
292 }
293
294 /* Perform semihosting if we are not waiting on a fileio
295 * operation to complete.
296 */
297 if (!semihosting->hit_fileio) {
298 if (is_armv8(target_to_armv8(target)) &&
299 arm->core_state == ARM_STATE_AARCH64) {
300 /* Read op and param from register x0 and x1 respectively. */
301 semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64);
302 semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64);
303 semihosting->word_size_bytes = 8;
304 } else {
305 /* Read op and param from register r0 and r1 respectively. */
306 semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
307 semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
308 semihosting->word_size_bytes = 4;
309 }
310
311 /* Check for ARM operation numbers. */
312 if (0 <= semihosting->op && semihosting->op <= 0x31) {
313 *retval = semihosting_common(target);
314 if (*retval != ERROR_OK) {
315 LOG_ERROR("Failed semihosting operation");
316 return 0;
317 }
318 } else {
319 /* Unknown operation number, not a semihosting call. */
320 return 0;
321 }
322 }
323
324 /* Resume if target it is resumable and we are not waiting on a fileio
325 * operation to complete:
326 */
327 if (semihosting->is_resumable && !semihosting->hit_fileio)
328 return arm_semihosting_resume(target, retval);
329
330 return 0;
331 }

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)