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

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)