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 
arm_semihosting_resume(struct target * target,int * retval)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 
post_result(struct target * target)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  */
arm_semihosting_init(struct target * target)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  */
arm_semihosting(struct target * target,int * retval)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 (0x%02X)", semihosting->op);
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 }
335