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

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)