armv7m: add gdb target description 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 * 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, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
22 ***************************************************************************/
23
24 /**
25 * @file
26 * Hold ARM semihosting support.
27 *
28 * Semihosting enables code running on an ARM target to use the I/O
29 * facilities on the host computer. The target application must be linked
30 * against a library that forwards operation requests by using the SVC
31 * instruction trapped at the Supervisor Call vector by the debugger.
32 * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
33 * from ARM Ltd.
34 */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include "arm.h"
41 #include "armv4_5.h"
42 #include "arm7_9_common.h"
43 #include "armv7m.h"
44 #include "cortex_m.h"
45 #include "register.h"
46 #include "arm_semihosting.h"
47 #include <helper/binarybuffer.h>
48 #include <helper/log.h>
49 #include <sys/stat.h>
50
51 static 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, (void *)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 uint32_t spsr;
422
423 /* return value in R0 */
424 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result);
425 arm->core_cache->reg_list[0].dirty = 1;
426
427 /* LR --> PC */
428 buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32,
429 buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32));
430 arm->core_cache->reg_list[15].dirty = 1;
431
432 /* saved PSR --> current PSR */
433 spsr = buf_get_u32(arm->spsr->value, 0, 32);
434
435 /* REVISIT should this be arm_set_cpsr(arm, spsr)
436 * instead of a partially unrolled version?
437 */
438
439 buf_set_u32(arm->cpsr->value, 0, 32, spsr);
440 arm->cpsr->dirty = 1;
441 arm->core_mode = spsr & 0x1f;
442 if (spsr & 0x20)
443 arm->core_state = ARM_STATE_THUMB;
444
445 } else {
446 /* resume execution, this will be pc+2 to skip over the
447 * bkpt instruction */
448
449 /* return result in R0 */
450 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result);
451 arm->core_cache->reg_list[0].dirty = 1;
452 }
453
454 return target_resume(target, 1, 0, 0, 0);
455 }
456
457 /**
458 * Checks for and processes an ARM semihosting request. This is meant
459 * to be called when the target is stopped due to a debug mode entry.
460 * If the value 0 is returned then there was nothing to process. A non-zero
461 * return value signifies that a request was processed and the target resumed,
462 * or an error was encountered, in which case the caller must return
463 * immediately.
464 *
465 * @param target Pointer to the ARM target to process. This target must
466 * not represent an ARMv6-M or ARMv7-M processor.
467 * @param retval Pointer to a location where the return code will be stored
468 * @return non-zero value if a request was processed or an error encountered
469 */
470 int arm_semihosting(struct target *target, int *retval)
471 {
472 struct arm *arm = target_to_arm(target);
473 uint32_t pc, lr, spsr;
474 struct reg *r;
475
476 if (!arm->is_semihosting)
477 return 0;
478
479 if (is_arm7_9(target_to_arm7_9(target))) {
480 if (arm->core_mode != ARM_MODE_SVC)
481 return 0;
482
483 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
484 r = arm->pc;
485 pc = buf_get_u32(r->value, 0, 32);
486 if (pc != 0x00000008 && pc != 0xffff0008)
487 return 0;
488
489 r = arm_reg_current(arm, 14);
490 lr = buf_get_u32(r->value, 0, 32);
491
492 /* Core-specific code should make sure SPSR is retrieved
493 * when the above checks pass...
494 */
495 if (!arm->spsr->valid) {
496 LOG_ERROR("SPSR not valid!");
497 *retval = ERROR_FAIL;
498 return 1;
499 }
500
501 spsr = buf_get_u32(arm->spsr->value, 0, 32);
502
503 /* check instruction that triggered this trap */
504 if (spsr & (1 << 5)) {
505 /* was in Thumb (or ThumbEE) mode */
506 uint8_t insn_buf[2];
507 uint16_t insn;
508
509 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
510 if (*retval != ERROR_OK)
511 return 1;
512 insn = target_buffer_get_u16(target, insn_buf);
513
514 /* SVC 0xab */
515 if (insn != 0xDFAB)
516 return 0;
517 } else if (spsr & (1 << 24)) {
518 /* was in Jazelle mode */
519 return 0;
520 } else {
521 /* was in ARM mode */
522 uint8_t insn_buf[4];
523 uint32_t insn;
524
525 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
526 if (*retval != ERROR_OK)
527 return 1;
528 insn = target_buffer_get_u32(target, insn_buf);
529
530 /* SVC 0x123456 */
531 if (insn != 0xEF123456)
532 return 0;
533 }
534 } else if (is_armv7m(target_to_armv7m(target))) {
535 uint16_t insn;
536
537 if (target->debug_reason != DBG_REASON_BREAKPOINT)
538 return 0;
539
540 r = arm->pc;
541 pc = buf_get_u32(r->value, 0, 32);
542
543 pc &= ~1;
544 *retval = target_read_u16(target, pc, &insn);
545 if (*retval != ERROR_OK)
546 return 1;
547
548 /* bkpt 0xAB */
549 if (insn != 0xBEAB)
550 return 0;
551 } else {
552 LOG_ERROR("Unsupported semi-hosting Target");
553 return 0;
554 }
555
556 *retval = do_semihosting(target);
557 return 1;
558 }

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)