ARM: rename ARMV4_5_MODE_* as ARM_MODE_*
[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 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21 /**
22 * @file
23 * Hold ARM semihosting support.
24 *
25 * Semihosting enables code running on an ARM target to use the I/O
26 * facilities on the host computer. The target application must be linked
27 * against a library that forwards operation requests by using the SVC
28 * instruction trapped at the Supervisor Call vector by the debugger.
29 * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
30 * from ARM Ltd.
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "armv4_5.h"
38 #include "register.h"
39 #include "arm_semihosting.h"
40 #include <helper/binarybuffer.h>
41 #include <helper/log.h>
42
43
44 static int do_semihosting(struct target *target)
45 {
46 struct arm *armv4_5 = target_to_armv4_5(target);
47 uint32_t r0 = buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32);
48 uint32_t r1 = buf_get_u32(armv4_5->core_cache->reg_list[1].value, 0, 32);
49 uint32_t lr = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, ARM_MODE_SVC, 14).value, 0, 32);
50 uint32_t spsr = buf_get_u32(armv4_5->spsr->value, 0, 32);;
51 uint8_t params[16];
52 int retval, result;
53
54 /*
55 * TODO: lots of security issues are not considered yet, such as:
56 * - no validation on target provided file descriptors
57 * - no safety checks on opened/deleted/renamed file paths
58 * Beware the target app you use this support with.
59 *
60 * TODO: explore mapping requests to GDB's "File-I/O Remote
61 * Protocol Extension" ... when GDB is active.
62 */
63 switch (r0) {
64 case 0x01: /* SYS_OPEN */
65 retval = target_read_memory(target, r1, 4, 3, params);
66 if (retval != ERROR_OK)
67 return retval;
68 else {
69 uint32_t a = target_buffer_get_u32(target, params+0);
70 uint32_t m = target_buffer_get_u32(target, params+4);
71 uint32_t l = target_buffer_get_u32(target, params+8);
72 if (l <= 255 && m <= 11) {
73 uint8_t fn[256];
74 int mode;
75 retval = target_read_memory(target, a, 1, l, fn);
76 if (retval != ERROR_OK)
77 return retval;
78 fn[l] = 0;
79 if (m & 0x2)
80 mode = O_RDWR;
81 else if (m & 0xc)
82 mode = O_WRONLY;
83 else
84 mode = O_RDONLY;
85 if (m >= 8)
86 mode |= O_CREAT|O_APPEND;
87 else if (m >= 4)
88 mode |= O_CREAT|O_TRUNC;
89 if (strcmp((char *)fn, ":tt") == 0) {
90 if ((mode & 3) == 0)
91 result = dup(0);
92 else
93 result = dup(1);
94 } else
95 result = open((char *)fn, mode);
96 armv4_5->semihosting_errno = errno;
97 } else {
98 result = -1;
99 armv4_5->semihosting_errno = EINVAL;
100 }
101 }
102 break;
103
104 case 0x02: /* SYS_CLOSE */
105 retval = target_read_memory(target, r1, 4, 1, params);
106 if (retval != ERROR_OK)
107 return retval;
108 else {
109 int fd = target_buffer_get_u32(target, params+0);
110 result = close(fd);
111 armv4_5->semihosting_errno = errno;
112 }
113 break;
114
115 case 0x03: /* SYS_WRITEC */
116 {
117 unsigned char c;
118 retval = target_read_memory(target, r1, 1, 1, &c);
119 if (retval != ERROR_OK)
120 return retval;
121 putchar(c);
122 result = 0;
123 }
124 break;
125
126 case 0x04: /* SYS_WRITE0 */
127 do {
128 unsigned char c;
129 retval = target_read_memory(target, r1, 1, 1, &c);
130 if (retval != ERROR_OK)
131 return retval;
132 if (!c)
133 break;
134 putchar(c);
135 } while (1);
136 result = 0;
137 break;
138
139 case 0x05: /* SYS_WRITE */
140 retval = target_read_memory(target, r1, 4, 3, params);
141 if (retval != ERROR_OK)
142 return retval;
143 else {
144 int fd = target_buffer_get_u32(target, params+0);
145 uint32_t a = target_buffer_get_u32(target, params+4);
146 size_t l = target_buffer_get_u32(target, params+8);
147 uint8_t *buf = malloc(l);
148 if (!buf) {
149 result = -1;
150 armv4_5->semihosting_errno = ENOMEM;
151 } else {
152 retval = target_read_buffer(target, a, l, buf);
153 if (retval != ERROR_OK) {
154 free(buf);
155 return retval;
156 }
157 result = write(fd, buf, l);
158 armv4_5->semihosting_errno = errno;
159 if (result >= 0)
160 result = l - result;
161 free(buf);
162 }
163 }
164 break;
165
166 case 0x06: /* SYS_READ */
167 retval = target_read_memory(target, r1, 4, 3, params);
168 if (retval != ERROR_OK)
169 return retval;
170 else {
171 int fd = target_buffer_get_u32(target, params+0);
172 uint32_t a = target_buffer_get_u32(target, params+4);
173 ssize_t l = target_buffer_get_u32(target, params+8);
174 uint8_t *buf = malloc(l);
175 if (!buf) {
176 result = -1;
177 armv4_5->semihosting_errno = ENOMEM;
178 } else {
179 result = read(fd, buf, l);
180 armv4_5->semihosting_errno = errno;
181 if (result > 0) {
182 retval = target_write_buffer(target, a, result, buf);
183 if (retval != ERROR_OK) {
184 free(buf);
185 return retval;
186 }
187 result = l - result;
188 }
189 free(buf);
190 }
191 }
192 break;
193
194 case 0x07: /* SYS_READC */
195 result = getchar();
196 break;
197
198 case 0x08: /* SYS_ISERROR */
199 retval = target_read_memory(target, r1, 4, 1, params);
200 if (retval != ERROR_OK)
201 return retval;
202 result = (target_buffer_get_u32(target, params+0) != 0);
203 break;
204
205 case 0x09: /* SYS_ISTTY */
206 retval = target_read_memory(target, r1, 4, 1, params);
207 if (retval != ERROR_OK)
208 return retval;
209 result = isatty(target_buffer_get_u32(target, params+0));
210 break;
211
212 case 0x0a: /* SYS_SEEK */
213 retval = target_read_memory(target, r1, 4, 2, params);
214 if (retval != ERROR_OK)
215 return retval;
216 else {
217 int fd = target_buffer_get_u32(target, params+0);
218 off_t pos = target_buffer_get_u32(target, params+4);
219 result = lseek(fd, pos, SEEK_SET);
220 armv4_5->semihosting_errno = errno;
221 if (result == pos)
222 result = 0;
223 }
224 break;
225
226 case 0x0c: /* SYS_FLEN */
227 retval = target_read_memory(target, r1, 4, 1, params);
228 if (retval != ERROR_OK)
229 return retval;
230 else {
231 int fd = target_buffer_get_u32(target, params+0);
232 off_t cur = lseek(fd, 0, SEEK_CUR);
233 if (cur == (off_t)-1) {
234 armv4_5->semihosting_errno = errno;
235 result = -1;
236 break;
237 }
238 result = lseek(fd, 0, SEEK_END);
239 armv4_5->semihosting_errno = errno;
240 if (lseek(fd, cur, SEEK_SET) == (off_t)-1) {
241 armv4_5->semihosting_errno = errno;
242 result = -1;
243 }
244 }
245 break;
246
247 case 0x0e: /* SYS_REMOVE */
248 retval = target_read_memory(target, r1, 4, 2, params);
249 if (retval != ERROR_OK)
250 return retval;
251 else {
252 uint32_t a = target_buffer_get_u32(target, params+0);
253 uint32_t l = target_buffer_get_u32(target, params+4);
254 if (l <= 255) {
255 uint8_t fn[256];
256 retval = target_read_memory(target, a, 1, l, fn);
257 if (retval != ERROR_OK)
258 return retval;
259 fn[l] = 0;
260 result = remove((char *)fn);
261 armv4_5->semihosting_errno = errno;
262 } else {
263 result = -1;
264 armv4_5->semihosting_errno = EINVAL;
265 }
266 }
267 break;
268
269 case 0x0f: /* SYS_RENAME */
270 retval = target_read_memory(target, r1, 4, 4, params);
271 if (retval != ERROR_OK)
272 return retval;
273 else {
274 uint32_t a1 = target_buffer_get_u32(target, params+0);
275 uint32_t l1 = target_buffer_get_u32(target, params+4);
276 uint32_t a2 = target_buffer_get_u32(target, params+8);
277 uint32_t l2 = target_buffer_get_u32(target, params+12);
278 if (l1 <= 255 && l2 <= 255) {
279 uint8_t fn1[256], fn2[256];
280 retval = target_read_memory(target, a1, 1, l1, fn1);
281 if (retval != ERROR_OK)
282 return retval;
283 retval = target_read_memory(target, a2, 1, l2, fn2);
284 if (retval != ERROR_OK)
285 return retval;
286 fn1[l1] = 0;
287 fn2[l2] = 0;
288 result = rename((char *)fn1, (char *)fn2);
289 armv4_5->semihosting_errno = errno;
290 } else {
291 result = -1;
292 armv4_5->semihosting_errno = EINVAL;
293 }
294 }
295 break;
296
297 case 0x11: /* SYS_TIME */
298 result = time(NULL);
299 break;
300
301 case 0x13: /* SYS_ERRNO */
302 result = armv4_5->semihosting_errno;
303 break;
304
305 case 0x15: /* SYS_GET_CMDLINE */
306 retval = target_read_memory(target, r1, 4, 2, params);
307 if (retval != ERROR_OK)
308 return retval;
309 else {
310 uint32_t a = target_buffer_get_u32(target, params+0);
311 uint32_t l = target_buffer_get_u32(target, params+4);
312 char *arg = "foobar";
313 uint32_t s = strlen(arg) + 1;
314 if (l < s)
315 result = -1;
316 else {
317 retval = target_write_buffer(target, a, s, (void*)arg);
318 if (retval != ERROR_OK)
319 return retval;
320 result = 0;
321 }
322 }
323 break;
324
325 case 0x16: /* SYS_HEAPINFO */
326 retval = target_read_memory(target, r1, 4, 1, params);
327 if (retval != ERROR_OK)
328 return retval;
329 else {
330 uint32_t a = target_buffer_get_u32(target, params+0);
331 /* tell the remote we have no idea */
332 memset(params, 0, 4*4);
333 retval = target_write_memory(target, a, 4, 4, params);
334 if (retval != ERROR_OK)
335 return retval;
336 result = 0;
337 }
338 break;
339
340 case 0x18: /* angel_SWIreason_ReportException */
341 switch (r1) {
342 case 0x20026: /* ADP_Stopped_ApplicationExit */
343 fprintf(stderr, "semihosting: *** application exited ***\n");
344 break;
345 case 0x20000: /* ADP_Stopped_BranchThroughZero */
346 case 0x20001: /* ADP_Stopped_UndefinedInstr */
347 case 0x20002: /* ADP_Stopped_SoftwareInterrupt */
348 case 0x20003: /* ADP_Stopped_PrefetchAbort */
349 case 0x20004: /* ADP_Stopped_DataAbort */
350 case 0x20005: /* ADP_Stopped_AddressException */
351 case 0x20006: /* ADP_Stopped_IRQ */
352 case 0x20007: /* ADP_Stopped_FIQ */
353 case 0x20020: /* ADP_Stopped_BreakPoint */
354 case 0x20021: /* ADP_Stopped_WatchPoint */
355 case 0x20022: /* ADP_Stopped_StepComplete */
356 case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */
357 case 0x20024: /* ADP_Stopped_InternalError */
358 case 0x20025: /* ADP_Stopped_UserInterruption */
359 case 0x20027: /* ADP_Stopped_StackOverflow */
360 case 0x20028: /* ADP_Stopped_DivisionByZero */
361 case 0x20029: /* ADP_Stopped_OSSpecific */
362 default:
363 fprintf(stderr, "semihosting: exception %#x\n",
364 (unsigned) r1);
365 }
366 return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
367
368 case 0x0d: /* SYS_TMPNAM */
369 case 0x10: /* SYS_CLOCK */
370 case 0x12: /* SYS_SYSTEM */
371 case 0x17: /* angel_SWIreason_EnterSVC */
372 case 0x30: /* SYS_ELAPSED */
373 case 0x31: /* SYS_TICKFREQ */
374 default:
375 fprintf(stderr, "semihosting: unsupported call %#x\n",
376 (unsigned) r0);
377 result = -1;
378 armv4_5->semihosting_errno = ENOTSUP;
379 }
380
381 /* resume execution to the original mode */
382 buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, result);
383 armv4_5->core_cache->reg_list[0].dirty = 1;
384 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, lr);
385 armv4_5->core_cache->reg_list[15].dirty = 1;
386 buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, spsr);
387 armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1;
388 armv4_5->core_mode = spsr & 0x1f;
389 if (spsr & 0x20)
390 armv4_5->core_state = ARM_STATE_THUMB;
391 return target_resume(target, 1, 0, 0, 0);
392 }
393
394 /**
395 * Checks for and processes an ARM semihosting request. This is meant
396 * to be called when the target is stopped due to a debug mode entry.
397 * If the value 0 is returned then there was nothing to process. A non-zero
398 * return value signifies that a request was processed and the target resumed,
399 * or an error was encountered, in which case the caller must return
400 * immediately.
401 *
402 * @param target Pointer to the ARM target to process. This target must
403 * not represent an ARMv6-M or ARMv7-M processor.
404 * @param retval Pointer to a location where the return code will be stored
405 * @return non-zero value if a request was processed or an error encountered
406 */
407 int arm_semihosting(struct target *target, int *retval)
408 {
409 struct arm *arm = target_to_armv4_5(target);
410 uint32_t lr, spsr;
411 struct reg *r;
412
413 if (!arm->is_semihosting || arm->core_mode != ARM_MODE_SVC)
414 return 0;
415
416 /* Check for PC == 8: Supervisor Call vector
417 * REVISIT: assumes low exception vectors, not hivecs...
418 * safer to test "was this entry from a vector catch".
419 */
420 r = arm->core_cache->reg_list + 15;
421 if (buf_get_u32(r->value, 0, 32) != 0x08)
422 return 0;
423
424 r = arm_reg_current(arm, 14);
425 lr = buf_get_u32(r->value, 0, 32);
426
427 /* Core-specific code should make sure SPSR is retrieved
428 * when the above checks pass...
429 */
430 if (!arm->spsr->valid) {
431 LOG_ERROR("SPSR not valid!");
432 *retval = ERROR_FAIL;
433 return 1;
434 }
435
436 spsr = buf_get_u32(arm->spsr->value, 0, 32);
437
438 /* check instruction that triggered this trap */
439 if (spsr & (1 << 5)) {
440 /* was in Thumb (or ThumbEE) mode */
441 uint8_t insn_buf[2];
442 uint16_t insn;
443
444 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
445 if (*retval != ERROR_OK)
446 return 1;
447 insn = target_buffer_get_u16(target, insn_buf);
448
449 /* SVC 0xab */
450 if (insn != 0xDFAB)
451 return 0;
452 } else if (spsr & (1 << 24)) {
453 /* was in Jazelle mode */
454 return 0;
455 } else {
456 /* was in ARM mode */
457 uint8_t insn_buf[4];
458 uint32_t insn;
459
460 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
461 if (*retval != ERROR_OK)
462 return 1;
463 insn = target_buffer_get_u32(target, insn_buf);
464
465 /* SVC 0x123456 */
466 if (insn != 0xEF123456)
467 return 0;
468 }
469
470 *retval = do_semihosting(target);
471 return 1;
472 }

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)