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

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)