Rework/update ARM semihosting
[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 "cortex_m.h"
50 #include "register.h"
51 #include "arm_opcodes.h"
52 #include "target_type.h"
53 #include "arm_semihosting.h"
54 #include <helper/binarybuffer.h>
55 #include <helper/log.h>
56 #include <sys/stat.h>
57
58 static int post_result(struct target *target)
59 {
60 struct arm *arm = target_to_arm(target);
61
62 /* REVISIT this looks wrong ... ARM11 and Cortex-A8
63 * should work this way at least sometimes.
64 */
65 if (is_arm7_9(target_to_arm7_9(target)) ||
66 is_armv7a(target_to_armv7a(target))) {
67 uint32_t spsr;
68
69 /* return value in R0 */
70 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
71 arm->core_cache->reg_list[0].dirty = 1;
72
73 /* LR --> PC */
74 buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32,
75 buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32));
76 arm->core_cache->reg_list[15].dirty = 1;
77
78 /* saved PSR --> current PSR */
79 spsr = buf_get_u32(arm->spsr->value, 0, 32);
80
81 /* REVISIT should this be arm_set_cpsr(arm, spsr)
82 * instead of a partially unrolled version?
83 */
84
85 buf_set_u32(arm->cpsr->value, 0, 32, spsr);
86 arm->cpsr->dirty = 1;
87 arm->core_mode = spsr & 0x1f;
88 if (spsr & 0x20)
89 arm->core_state = ARM_STATE_THUMB;
90
91 } else {
92 /* resume execution, this will be pc+2 to skip over the
93 * bkpt instruction */
94
95 /* return result 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 = 1;
98 }
99
100 return ERROR_OK;
101 }
102
103 /**
104 * Initialize ARM semihosting support.
105 *
106 * @param target Pointer to the ARM target to initialize.
107 * @return An error status if there is a problem during initialization.
108 */
109 int arm_semihosting_init(struct target *target)
110 {
111 struct arm *arm = target_to_arm(target);
112 assert(arm->setup_semihosting);
113 semihosting_common_init(target, arm->setup_semihosting, post_result);
114
115 return ERROR_OK;
116 }
117
118 /**
119 * Checks for and processes an ARM semihosting request. This is meant
120 * to be called when the target is stopped due to a debug mode entry.
121 * If the value 0 is returned then there was nothing to process. A non-zero
122 * return value signifies that a request was processed and the target resumed,
123 * or an error was encountered, in which case the caller must return
124 * immediately.
125 *
126 * @param target Pointer to the ARM target to process. This target must
127 * not represent an ARMv6-M or ARMv7-M processor.
128 * @param retval Pointer to a location where the return code will be stored
129 * @return non-zero value if a request was processed or an error encountered
130 */
131 int arm_semihosting(struct target *target, int *retval)
132 {
133 struct arm *arm = target_to_arm(target);
134 struct armv7a_common *armv7a = target_to_armv7a(target);
135 uint32_t pc, lr, spsr;
136 struct reg *r;
137
138 struct semihosting *semihosting = target->semihosting;
139 if (!semihosting)
140 return 0;
141
142 if (!semihosting->is_active)
143 return 0;
144
145 if (is_arm7_9(target_to_arm7_9(target)) ||
146 is_armv7a(armv7a)) {
147 uint32_t vbar = 0x00000000;
148
149 if (arm->core_mode != ARM_MODE_SVC)
150 return 0;
151
152 if (is_armv7a(armv7a)) {
153 struct arm_dpm *dpm = armv7a->arm.dpm;
154
155 *retval = dpm->prepare(dpm);
156 if (*retval == ERROR_OK) {
157 *retval = dpm->instr_read_data_r0(dpm,
158 ARMV4_5_MRC(15, 0, 0, 12, 0, 0),
159 &vbar);
160
161 dpm->finish(dpm);
162
163 if (*retval != ERROR_OK)
164 return 1;
165 } else {
166 return 1;
167 }
168 }
169
170 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
171 r = arm->pc;
172 pc = buf_get_u32(r->value, 0, 32);
173 if (pc != (vbar + 0x00000008) && pc != 0xffff0008)
174 return 0;
175
176 r = arm_reg_current(arm, 14);
177 lr = buf_get_u32(r->value, 0, 32);
178
179 /* Core-specific code should make sure SPSR is retrieved
180 * when the above checks pass...
181 */
182 if (!arm->spsr->valid) {
183 LOG_ERROR("SPSR not valid!");
184 *retval = ERROR_FAIL;
185 return 1;
186 }
187
188 spsr = buf_get_u32(arm->spsr->value, 0, 32);
189
190 /* check instruction that triggered this trap */
191 if (spsr & (1 << 5)) {
192 /* was in Thumb (or ThumbEE) mode */
193 uint8_t insn_buf[2];
194 uint16_t insn;
195
196 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
197 if (*retval != ERROR_OK)
198 return 1;
199 insn = target_buffer_get_u16(target, insn_buf);
200
201 /* SVC 0xab */
202 if (insn != 0xDFAB)
203 return 0;
204 } else if (spsr & (1 << 24)) {
205 /* was in Jazelle mode */
206 return 0;
207 } else {
208 /* was in ARM mode */
209 uint8_t insn_buf[4];
210 uint32_t insn;
211
212 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
213 if (*retval != ERROR_OK)
214 return 1;
215 insn = target_buffer_get_u32(target, insn_buf);
216
217 /* SVC 0x123456 */
218 if (insn != 0xEF123456)
219 return 0;
220 }
221 } else if (is_armv7m(target_to_armv7m(target))) {
222 uint16_t insn;
223
224 if (target->debug_reason != DBG_REASON_BREAKPOINT)
225 return 0;
226
227 r = arm->pc;
228 pc = buf_get_u32(r->value, 0, 32);
229
230 pc &= ~1;
231 *retval = target_read_u16(target, pc, &insn);
232 if (*retval != ERROR_OK)
233 return 1;
234
235 /* bkpt 0xAB */
236 if (insn != 0xBEAB)
237 return 0;
238 } else {
239 LOG_ERROR("Unsupported semi-hosting Target");
240 return 0;
241 }
242
243 /* Perform semihosting if we are not waiting on a fileio
244 * operation to complete.
245 */
246 if (!semihosting->hit_fileio) {
247 /* TODO: update for 64-bits */
248 uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
249 uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
250
251 semihosting->op = r0;
252 semihosting->param = r1;
253 semihosting->word_size_bytes = 4;
254
255 /* Check for ARM operation numbers. */
256 if (0 <= semihosting->op && semihosting->op <= 0x31) {
257 *retval = semihosting_common(target);
258 if (*retval != ERROR_OK) {
259 LOG_ERROR("Failed semihosting operation");
260 return 0;
261 }
262 } else {
263 /* Unknown operation number, not a semihosting call. */
264 return 0;
265 }
266 }
267
268 /* Post result to target if we are not waiting on a fileio
269 * operation to complete:
270 */
271 if (semihosting->is_resumable && !semihosting->hit_fileio) {
272 /* Resume right after the BRK instruction. */
273 *retval = target_resume(target, 1, 0, 0, 0);
274 if (*retval != ERROR_OK) {
275 LOG_ERROR("Failed to resume target");
276 return 0;
277 }
278
279 return 1;
280 }
281
282 return 0;
283 }

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)