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

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)