semihosting armv7a: Add support for ARMv7-A
[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 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
20 ***************************************************************************/
21
22 /**
23 * @file
24 * Hold ARM semihosting support.
25 *
26 * Semihosting enables code running on an ARM target to use the I/O
27 * facilities on the host computer. The target application must be linked
28 * against a library that forwards operation requests by using the SVC
29 * instruction trapped at the Supervisor Call vector by the debugger.
30 * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
31 * from ARM Ltd.
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "arm.h"
39 #include "armv4_5.h"
40 #include "arm7_9_common.h"
41 #include "armv7m.h"
42 #include "armv7a.h"
43 #include "cortex_m.h"
44 #include "register.h"
45 #include "arm_opcodes.h"
46 #include "arm_semihosting.h"
47 #include <helper/binarybuffer.h>
48 #include <helper/log.h>
49 #include <sys/stat.h>
50
51 static const int open_modeflags[12] = {
52 O_RDONLY,
53 O_RDONLY | O_BINARY,
54 O_RDWR,
55 O_RDWR | O_BINARY,
56 O_WRONLY | O_CREAT | O_TRUNC,
57 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
58 O_RDWR | O_CREAT | O_TRUNC,
59 O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
60 O_WRONLY | O_CREAT | O_APPEND,
61 O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
62 O_RDWR | O_CREAT | O_APPEND,
63 O_RDWR | O_CREAT | O_APPEND | O_BINARY
64 };
65
66 static int do_semihosting(struct target *target)
67 {
68 struct arm *arm = target_to_arm(target);
69 uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
70 uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
71 uint8_t params[16];
72 int retval, result;
73
74 /*
75 * TODO: lots of security issues are not considered yet, such as:
76 * - no validation on target provided file descriptors
77 * - no safety checks on opened/deleted/renamed file paths
78 * Beware the target app you use this support with.
79 *
80 * TODO: explore mapping requests to GDB's "File-I/O Remote
81 * Protocol Extension" ... when GDB is active.
82 */
83 switch (r0) {
84 case 0x01: /* SYS_OPEN */
85 retval = target_read_memory(target, r1, 4, 3, params);
86 if (retval != ERROR_OK)
87 return retval;
88 else {
89 uint32_t a = target_buffer_get_u32(target, params+0);
90 uint32_t m = target_buffer_get_u32(target, params+4);
91 uint32_t l = target_buffer_get_u32(target, params+8);
92 if (l <= 255 && m <= 11) {
93 uint8_t fn[256];
94 retval = target_read_memory(target, a, 1, l, fn);
95 if (retval != ERROR_OK)
96 return retval;
97 fn[l] = 0;
98 if (strcmp((char *)fn, ":tt") == 0) {
99 if (m < 4)
100 result = dup(STDIN_FILENO);
101 else
102 result = dup(STDOUT_FILENO);
103 } else {
104 /* cygwin requires the permission setting
105 * otherwise it will fail to reopen a previously
106 * written file */
107 result = open((char *)fn, open_modeflags[m], 0644);
108 }
109 arm->semihosting_errno = errno;
110 } else {
111 result = -1;
112 arm->semihosting_errno = EINVAL;
113 }
114 }
115 break;
116
117 case 0x02: /* SYS_CLOSE */
118 retval = target_read_memory(target, r1, 4, 1, params);
119 if (retval != ERROR_OK)
120 return retval;
121 else {
122 int fd = target_buffer_get_u32(target, params+0);
123 result = close(fd);
124 arm->semihosting_errno = errno;
125 }
126 break;
127
128 case 0x03: /* SYS_WRITEC */
129 {
130 unsigned char c;
131 retval = target_read_memory(target, r1, 1, 1, &c);
132 if (retval != ERROR_OK)
133 return retval;
134 putchar(c);
135 result = 0;
136 }
137 break;
138
139 case 0x04: /* SYS_WRITE0 */
140 do {
141 unsigned char c;
142 retval = target_read_memory(target, r1++, 1, 1, &c);
143 if (retval != ERROR_OK)
144 return retval;
145 if (!c)
146 break;
147 putchar(c);
148 } while (1);
149 result = 0;
150 break;
151
152 case 0x05: /* SYS_WRITE */
153 retval = target_read_memory(target, r1, 4, 3, params);
154 if (retval != ERROR_OK)
155 return retval;
156 else {
157 int fd = target_buffer_get_u32(target, params+0);
158 uint32_t a = target_buffer_get_u32(target, params+4);
159 size_t l = target_buffer_get_u32(target, params+8);
160 uint8_t *buf = malloc(l);
161 if (!buf) {
162 result = -1;
163 arm->semihosting_errno = ENOMEM;
164 } else {
165 retval = target_read_buffer(target, a, l, buf);
166 if (retval != ERROR_OK) {
167 free(buf);
168 return retval;
169 }
170 result = write(fd, buf, l);
171 arm->semihosting_errno = errno;
172 if (result >= 0)
173 result = l - result;
174 free(buf);
175 }
176 }
177 break;
178
179 case 0x06: /* SYS_READ */
180 retval = target_read_memory(target, r1, 4, 3, params);
181 if (retval != ERROR_OK)
182 return retval;
183 else {
184 int fd = target_buffer_get_u32(target, params+0);
185 uint32_t a = target_buffer_get_u32(target, params+4);
186 ssize_t l = target_buffer_get_u32(target, params+8);
187 uint8_t *buf = malloc(l);
188 if (!buf) {
189 result = -1;
190 arm->semihosting_errno = ENOMEM;
191 } else {
192 result = read(fd, buf, l);
193 arm->semihosting_errno = errno;
194 if (result >= 0) {
195 retval = target_write_buffer(target, a, result, buf);
196 if (retval != ERROR_OK) {
197 free(buf);
198 return retval;
199 }
200 result = l - result;
201 }
202 free(buf);
203 }
204 }
205 break;
206
207 case 0x07: /* SYS_READC */
208 result = getchar();
209 break;
210
211 case 0x08: /* SYS_ISERROR */
212 retval = target_read_memory(target, r1, 4, 1, params);
213 if (retval != ERROR_OK)
214 return retval;
215 result = (target_buffer_get_u32(target, params+0) != 0);
216 break;
217
218 case 0x09: /* SYS_ISTTY */
219 retval = target_read_memory(target, r1, 4, 1, params);
220 if (retval != ERROR_OK)
221 return retval;
222 result = isatty(target_buffer_get_u32(target, params+0));
223 break;
224
225 case 0x0a: /* SYS_SEEK */
226 retval = target_read_memory(target, r1, 4, 2, params);
227 if (retval != ERROR_OK)
228 return retval;
229 else {
230 int fd = target_buffer_get_u32(target, params+0);
231 off_t pos = target_buffer_get_u32(target, params+4);
232 result = lseek(fd, pos, SEEK_SET);
233 arm->semihosting_errno = errno;
234 if (result == pos)
235 result = 0;
236 }
237 break;
238
239 case 0x0c: /* SYS_FLEN */
240 retval = target_read_memory(target, r1, 4, 1, params);
241 if (retval != ERROR_OK)
242 return retval;
243 else {
244 int fd = target_buffer_get_u32(target, params+0);
245 struct stat buf;
246 result = fstat(fd, &buf);
247 if (result == -1) {
248 arm->semihosting_errno = errno;
249 result = -1;
250 break;
251 }
252 result = buf.st_size;
253 }
254 break;
255
256 case 0x0e: /* SYS_REMOVE */
257 retval = target_read_memory(target, r1, 4, 2, params);
258 if (retval != ERROR_OK)
259 return retval;
260 else {
261 uint32_t a = target_buffer_get_u32(target, params+0);
262 uint32_t l = target_buffer_get_u32(target, params+4);
263 if (l <= 255) {
264 uint8_t fn[256];
265 retval = target_read_memory(target, a, 1, l, fn);
266 if (retval != ERROR_OK)
267 return retval;
268 fn[l] = 0;
269 result = remove((char *)fn);
270 arm->semihosting_errno = errno;
271 } else {
272 result = -1;
273 arm->semihosting_errno = EINVAL;
274 }
275 }
276 break;
277
278 case 0x0f: /* SYS_RENAME */
279 retval = target_read_memory(target, r1, 4, 4, params);
280 if (retval != ERROR_OK)
281 return retval;
282 else {
283 uint32_t a1 = target_buffer_get_u32(target, params+0);
284 uint32_t l1 = target_buffer_get_u32(target, params+4);
285 uint32_t a2 = target_buffer_get_u32(target, params+8);
286 uint32_t l2 = target_buffer_get_u32(target, params+12);
287 if (l1 <= 255 && l2 <= 255) {
288 uint8_t fn1[256], fn2[256];
289 retval = target_read_memory(target, a1, 1, l1, fn1);
290 if (retval != ERROR_OK)
291 return retval;
292 retval = target_read_memory(target, a2, 1, l2, fn2);
293 if (retval != ERROR_OK)
294 return retval;
295 fn1[l1] = 0;
296 fn2[l2] = 0;
297 result = rename((char *)fn1, (char *)fn2);
298 arm->semihosting_errno = errno;
299 } else {
300 result = -1;
301 arm->semihosting_errno = EINVAL;
302 }
303 }
304 break;
305
306 case 0x11: /* SYS_TIME */
307 result = time(NULL);
308 break;
309
310 case 0x13: /* SYS_ERRNO */
311 result = arm->semihosting_errno;
312 break;
313
314 case 0x15: /* SYS_GET_CMDLINE */
315 retval = target_read_memory(target, r1, 4, 2, params);
316 if (retval != ERROR_OK)
317 return retval;
318 else {
319 uint32_t a = target_buffer_get_u32(target, params+0);
320 uint32_t l = target_buffer_get_u32(target, params+4);
321 char *arg = "foobar";
322 uint32_t s = strlen(arg) + 1;
323 if (l < s)
324 result = -1;
325 else {
326 retval = target_write_buffer(target, a, s, (uint8_t *)arg);
327 if (retval != ERROR_OK)
328 return retval;
329 result = 0;
330 }
331 }
332 break;
333
334 case 0x16: /* SYS_HEAPINFO */
335 retval = target_read_memory(target, r1, 4, 1, params);
336 if (retval != ERROR_OK)
337 return retval;
338 else {
339 uint32_t a = target_buffer_get_u32(target, params+0);
340 /* tell the remote we have no idea */
341 memset(params, 0, 4*4);
342 retval = target_write_memory(target, a, 4, 4, params);
343 if (retval != ERROR_OK)
344 return retval;
345 result = 0;
346 }
347 break;
348
349 case 0x18: /* angel_SWIreason_ReportException */
350 switch (r1) {
351 case 0x20026: /* ADP_Stopped_ApplicationExit */
352 fprintf(stderr, "semihosting: *** application exited ***\n");
353 break;
354 case 0x20000: /* ADP_Stopped_BranchThroughZero */
355 case 0x20001: /* ADP_Stopped_UndefinedInstr */
356 case 0x20002: /* ADP_Stopped_SoftwareInterrupt */
357 case 0x20003: /* ADP_Stopped_PrefetchAbort */
358 case 0x20004: /* ADP_Stopped_DataAbort */
359 case 0x20005: /* ADP_Stopped_AddressException */
360 case 0x20006: /* ADP_Stopped_IRQ */
361 case 0x20007: /* ADP_Stopped_FIQ */
362 case 0x20020: /* ADP_Stopped_BreakPoint */
363 case 0x20021: /* ADP_Stopped_WatchPoint */
364 case 0x20022: /* ADP_Stopped_StepComplete */
365 case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */
366 case 0x20024: /* ADP_Stopped_InternalError */
367 case 0x20025: /* ADP_Stopped_UserInterruption */
368 case 0x20027: /* ADP_Stopped_StackOverflow */
369 case 0x20028: /* ADP_Stopped_DivisionByZero */
370 case 0x20029: /* ADP_Stopped_OSSpecific */
371 default:
372 fprintf(stderr, "semihosting: exception %#x\n",
373 (unsigned) r1);
374 }
375 return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
376
377 case 0x12: /* SYS_SYSTEM */
378 /* Provide SYS_SYSTEM functionality. Uses the
379 * libc system command, there may be a reason *NOT*
380 * to use this, but as I can't think of one, I
381 * implemented it this way.
382 */
383 retval = target_read_memory(target, r1, 4, 2, params);
384 if (retval != ERROR_OK)
385 return retval;
386 else {
387 uint32_t len = target_buffer_get_u32(target, params+4);
388 uint32_t c_ptr = target_buffer_get_u32(target, params);
389 uint8_t cmd[256];
390 if (len > 255) {
391 result = -1;
392 arm->semihosting_errno = EINVAL;
393 } else {
394 memset(cmd, 0x0, 256);
395 retval = target_read_memory(target, c_ptr, 1, len, cmd);
396 if (retval != ERROR_OK)
397 return retval;
398 else
399 result = system((const char *)cmd);
400 }
401 }
402 break;
403 case 0x0d: /* SYS_TMPNAM */
404 case 0x10: /* SYS_CLOCK */
405 case 0x17: /* angel_SWIreason_EnterSVC */
406 case 0x30: /* SYS_ELAPSED */
407 case 0x31: /* SYS_TICKFREQ */
408 default:
409 fprintf(stderr, "semihosting: unsupported call %#x\n",
410 (unsigned) r0);
411 result = -1;
412 arm->semihosting_errno = ENOTSUP;
413 }
414
415 /* resume execution to the original mode */
416
417 /* REVISIT this looks wrong ... ARM11 and Cortex-A8
418 * should work this way at least sometimes.
419 */
420 if (is_arm7_9(target_to_arm7_9(target)) ||
421 is_armv7a(target_to_armv7a(target))) {
422 uint32_t spsr;
423
424 /* return value in R0 */
425 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result);
426 arm->core_cache->reg_list[0].dirty = 1;
427
428 /* LR --> PC */
429 buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32,
430 buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32));
431 arm->core_cache->reg_list[15].dirty = 1;
432
433 /* saved PSR --> current PSR */
434 spsr = buf_get_u32(arm->spsr->value, 0, 32);
435
436 /* REVISIT should this be arm_set_cpsr(arm, spsr)
437 * instead of a partially unrolled version?
438 */
439
440 buf_set_u32(arm->cpsr->value, 0, 32, spsr);
441 arm->cpsr->dirty = 1;
442 arm->core_mode = spsr & 0x1f;
443 if (spsr & 0x20)
444 arm->core_state = ARM_STATE_THUMB;
445
446 } else {
447 /* resume execution, this will be pc+2 to skip over the
448 * bkpt instruction */
449
450 /* return result in R0 */
451 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result);
452 arm->core_cache->reg_list[0].dirty = 1;
453 }
454
455 return target_resume(target, 1, 0, 0, 0);
456 }
457
458 /**
459 * Checks for and processes an ARM semihosting request. This is meant
460 * to be called when the target is stopped due to a debug mode entry.
461 * If the value 0 is returned then there was nothing to process. A non-zero
462 * return value signifies that a request was processed and the target resumed,
463 * or an error was encountered, in which case the caller must return
464 * immediately.
465 *
466 * @param target Pointer to the ARM target to process. This target must
467 * not represent an ARMv6-M or ARMv7-M processor.
468 * @param retval Pointer to a location where the return code will be stored
469 * @return non-zero value if a request was processed or an error encountered
470 */
471 int arm_semihosting(struct target *target, int *retval)
472 {
473 struct arm *arm = target_to_arm(target);
474 struct armv7a_common *armv7a = target_to_armv7a(target);
475 uint32_t pc, lr, spsr;
476 struct reg *r;
477
478 if (!arm->is_semihosting)
479 return 0;
480
481 if (is_arm7_9(target_to_arm7_9(target)) ||
482 is_armv7a(armv7a)) {
483 uint32_t vbar = 0x00000000;
484
485 if (arm->core_mode != ARM_MODE_SVC)
486 return 0;
487
488 if (is_armv7a(armv7a)) {
489 struct arm_dpm *dpm = armv7a->arm.dpm;
490
491 *retval = dpm->prepare(dpm);
492 if (*retval == ERROR_OK) {
493 *retval = dpm->instr_read_data_r0(dpm,
494 ARMV4_5_MRC(15, 0, 0, 12, 0, 0),
495 &vbar);
496
497 dpm->finish(dpm);
498
499 if (*retval != ERROR_OK)
500 return 1;
501 } else {
502 return 1;
503 }
504 }
505
506 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
507 r = arm->pc;
508 pc = buf_get_u32(r->value, 0, 32);
509 if (pc != (vbar + 0x00000008) && pc != 0xffff0008)
510 return 0;
511
512 r = arm_reg_current(arm, 14);
513 lr = buf_get_u32(r->value, 0, 32);
514
515 /* Core-specific code should make sure SPSR is retrieved
516 * when the above checks pass...
517 */
518 if (!arm->spsr->valid) {
519 LOG_ERROR("SPSR not valid!");
520 *retval = ERROR_FAIL;
521 return 1;
522 }
523
524 spsr = buf_get_u32(arm->spsr->value, 0, 32);
525
526 /* check instruction that triggered this trap */
527 if (spsr & (1 << 5)) {
528 /* was in Thumb (or ThumbEE) mode */
529 uint8_t insn_buf[2];
530 uint16_t insn;
531
532 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
533 if (*retval != ERROR_OK)
534 return 1;
535 insn = target_buffer_get_u16(target, insn_buf);
536
537 /* SVC 0xab */
538 if (insn != 0xDFAB)
539 return 0;
540 } else if (spsr & (1 << 24)) {
541 /* was in Jazelle mode */
542 return 0;
543 } else {
544 /* was in ARM mode */
545 uint8_t insn_buf[4];
546 uint32_t insn;
547
548 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
549 if (*retval != ERROR_OK)
550 return 1;
551 insn = target_buffer_get_u32(target, insn_buf);
552
553 /* SVC 0x123456 */
554 if (insn != 0xEF123456)
555 return 0;
556 }
557 } else if (is_armv7m(target_to_armv7m(target))) {
558 uint16_t insn;
559
560 if (target->debug_reason != DBG_REASON_BREAKPOINT)
561 return 0;
562
563 r = arm->pc;
564 pc = buf_get_u32(r->value, 0, 32);
565
566 pc &= ~1;
567 *retval = target_read_u16(target, pc, &insn);
568 if (*retval != ERROR_OK)
569 return 1;
570
571 /* bkpt 0xAB */
572 if (insn != 0xBEAB)
573 return 0;
574 } else {
575 LOG_ERROR("Unsupported semi-hosting Target");
576 return 0;
577 }
578
579 *retval = do_semihosting(target);
580 return 1;
581 }

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)