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

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)