build: cleanup src/target directory
[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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 0x0d: /* SYS_TMPNAM */
378 case 0x10: /* SYS_CLOCK */
379 case 0x12: /* SYS_SYSTEM */
380 case 0x17: /* angel_SWIreason_EnterSVC */
381 case 0x30: /* SYS_ELAPSED */
382 case 0x31: /* SYS_TICKFREQ */
383 default:
384 fprintf(stderr, "semihosting: unsupported call %#x\n",
385 (unsigned) r0);
386 result = -1;
387 arm->semihosting_errno = ENOTSUP;
388 }
389
390 /* resume execution to the original mode */
391
392 /* REVISIT this looks wrong ... ARM11 and Cortex-A8
393 * should work this way at least sometimes.
394 */
395 if (is_arm7_9(target_to_arm7_9(target))) {
396 uint32_t spsr;
397
398 /* return value in R0 */
399 buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result);
400 arm->core_cache->reg_list[0].dirty = 1;
401
402 /* LR --> PC */
403 buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32,
404 buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32));
405 arm->core_cache->reg_list[15].dirty = 1;
406
407 /* saved PSR --> current PSR */
408 spsr = buf_get_u32(arm->spsr->value, 0, 32);
409
410 /* REVISIT should this be arm_set_cpsr(arm, spsr)
411 * instead of a partially unrolled version?
412 */
413
414 buf_set_u32(arm->cpsr->value, 0, 32, spsr);
415 arm->cpsr->dirty = 1;
416 arm->core_mode = spsr & 0x1f;
417 if (spsr & 0x20)
418 arm->core_state = ARM_STATE_THUMB;
419
420 } else {
421 /* resume execution, this will be pc+2 to skip over the
422 * bkpt instruction */
423
424 /* return result 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
429 return target_resume(target, 1, 0, 0, 0);
430 }
431
432 /**
433 * Checks for and processes an ARM semihosting request. This is meant
434 * to be called when the target is stopped due to a debug mode entry.
435 * If the value 0 is returned then there was nothing to process. A non-zero
436 * return value signifies that a request was processed and the target resumed,
437 * or an error was encountered, in which case the caller must return
438 * immediately.
439 *
440 * @param target Pointer to the ARM target to process. This target must
441 * not represent an ARMv6-M or ARMv7-M processor.
442 * @param retval Pointer to a location where the return code will be stored
443 * @return non-zero value if a request was processed or an error encountered
444 */
445 int arm_semihosting(struct target *target, int *retval)
446 {
447 struct arm *arm = target_to_arm(target);
448 uint32_t pc, lr, spsr;
449 struct reg *r;
450
451 if (!arm->is_semihosting)
452 return 0;
453
454 if (is_arm7_9(target_to_arm7_9(target))) {
455 if (arm->core_mode != ARM_MODE_SVC)
456 return 0;
457
458 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
459 r = arm->pc;
460 pc = buf_get_u32(r->value, 0, 32);
461 if (pc != 0x00000008 && pc != 0xffff0008)
462 return 0;
463
464 r = arm_reg_current(arm, 14);
465 lr = buf_get_u32(r->value, 0, 32);
466
467 /* Core-specific code should make sure SPSR is retrieved
468 * when the above checks pass...
469 */
470 if (!arm->spsr->valid) {
471 LOG_ERROR("SPSR not valid!");
472 *retval = ERROR_FAIL;
473 return 1;
474 }
475
476 spsr = buf_get_u32(arm->spsr->value, 0, 32);
477
478 /* check instruction that triggered this trap */
479 if (spsr & (1 << 5)) {
480 /* was in Thumb (or ThumbEE) mode */
481 uint8_t insn_buf[2];
482 uint16_t insn;
483
484 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
485 if (*retval != ERROR_OK)
486 return 1;
487 insn = target_buffer_get_u16(target, insn_buf);
488
489 /* SVC 0xab */
490 if (insn != 0xDFAB)
491 return 0;
492 } else if (spsr & (1 << 24)) {
493 /* was in Jazelle mode */
494 return 0;
495 } else {
496 /* was in ARM mode */
497 uint8_t insn_buf[4];
498 uint32_t insn;
499
500 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
501 if (*retval != ERROR_OK)
502 return 1;
503 insn = target_buffer_get_u32(target, insn_buf);
504
505 /* SVC 0x123456 */
506 if (insn != 0xEF123456)
507 return 0;
508 }
509 } else if (is_armv7m(target_to_armv7m(target))) {
510 uint16_t insn;
511
512 if (target->debug_reason != DBG_REASON_BREAKPOINT)
513 return 0;
514
515 r = arm->pc;
516 pc = buf_get_u32(r->value, 0, 32);
517
518 pc &= ~1;
519 *retval = target_read_u16(target, pc, &insn);
520 if (*retval != ERROR_OK)
521 return 1;
522
523 /* bkpt 0xAB */
524 if (insn != 0xBEAB)
525 return 0;
526 } else {
527 LOG_ERROR("Unsupported semi-hosting Target");
528 return 0;
529 }
530
531 *retval = do_semihosting(target);
532 return 1;
533 }

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)