ARM: use <target/arm.h> not armv4_5.h
[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 off_t cur = lseek(fd, 0, SEEK_CUR);
234 if (cur == (off_t)-1) {
235 armv4_5->semihosting_errno = errno;
236 result = -1;
237 break;
238 }
239 result = lseek(fd, 0, SEEK_END);
240 armv4_5->semihosting_errno = errno;
241 if (lseek(fd, cur, SEEK_SET) == (off_t)-1) {
242 armv4_5->semihosting_errno = errno;
243 result = -1;
244 }
245 }
246 break;
247
248 case 0x0e: /* SYS_REMOVE */
249 retval = target_read_memory(target, r1, 4, 2, params);
250 if (retval != ERROR_OK)
251 return retval;
252 else {
253 uint32_t a = target_buffer_get_u32(target, params+0);
254 uint32_t l = target_buffer_get_u32(target, params+4);
255 if (l <= 255) {
256 uint8_t fn[256];
257 retval = target_read_memory(target, a, 1, l, fn);
258 if (retval != ERROR_OK)
259 return retval;
260 fn[l] = 0;
261 result = remove((char *)fn);
262 armv4_5->semihosting_errno = errno;
263 } else {
264 result = -1;
265 armv4_5->semihosting_errno = EINVAL;
266 }
267 }
268 break;
269
270 case 0x0f: /* SYS_RENAME */
271 retval = target_read_memory(target, r1, 4, 4, params);
272 if (retval != ERROR_OK)
273 return retval;
274 else {
275 uint32_t a1 = target_buffer_get_u32(target, params+0);
276 uint32_t l1 = target_buffer_get_u32(target, params+4);
277 uint32_t a2 = target_buffer_get_u32(target, params+8);
278 uint32_t l2 = target_buffer_get_u32(target, params+12);
279 if (l1 <= 255 && l2 <= 255) {
280 uint8_t fn1[256], fn2[256];
281 retval = target_read_memory(target, a1, 1, l1, fn1);
282 if (retval != ERROR_OK)
283 return retval;
284 retval = target_read_memory(target, a2, 1, l2, fn2);
285 if (retval != ERROR_OK)
286 return retval;
287 fn1[l1] = 0;
288 fn2[l2] = 0;
289 result = rename((char *)fn1, (char *)fn2);
290 armv4_5->semihosting_errno = errno;
291 } else {
292 result = -1;
293 armv4_5->semihosting_errno = EINVAL;
294 }
295 }
296 break;
297
298 case 0x11: /* SYS_TIME */
299 result = time(NULL);
300 break;
301
302 case 0x13: /* SYS_ERRNO */
303 result = armv4_5->semihosting_errno;
304 break;
305
306 case 0x15: /* SYS_GET_CMDLINE */
307 retval = target_read_memory(target, r1, 4, 2, params);
308 if (retval != ERROR_OK)
309 return retval;
310 else {
311 uint32_t a = target_buffer_get_u32(target, params+0);
312 uint32_t l = target_buffer_get_u32(target, params+4);
313 char *arg = "foobar";
314 uint32_t s = strlen(arg) + 1;
315 if (l < s)
316 result = -1;
317 else {
318 retval = target_write_buffer(target, a, s, (void*)arg);
319 if (retval != ERROR_OK)
320 return retval;
321 result = 0;
322 }
323 }
324 break;
325
326 case 0x16: /* SYS_HEAPINFO */
327 retval = target_read_memory(target, r1, 4, 1, params);
328 if (retval != ERROR_OK)
329 return retval;
330 else {
331 uint32_t a = target_buffer_get_u32(target, params+0);
332 /* tell the remote we have no idea */
333 memset(params, 0, 4*4);
334 retval = target_write_memory(target, a, 4, 4, params);
335 if (retval != ERROR_OK)
336 return retval;
337 result = 0;
338 }
339 break;
340
341 case 0x18: /* angel_SWIreason_ReportException */
342 switch (r1) {
343 case 0x20026: /* ADP_Stopped_ApplicationExit */
344 fprintf(stderr, "semihosting: *** application exited ***\n");
345 break;
346 case 0x20000: /* ADP_Stopped_BranchThroughZero */
347 case 0x20001: /* ADP_Stopped_UndefinedInstr */
348 case 0x20002: /* ADP_Stopped_SoftwareInterrupt */
349 case 0x20003: /* ADP_Stopped_PrefetchAbort */
350 case 0x20004: /* ADP_Stopped_DataAbort */
351 case 0x20005: /* ADP_Stopped_AddressException */
352 case 0x20006: /* ADP_Stopped_IRQ */
353 case 0x20007: /* ADP_Stopped_FIQ */
354 case 0x20020: /* ADP_Stopped_BreakPoint */
355 case 0x20021: /* ADP_Stopped_WatchPoint */
356 case 0x20022: /* ADP_Stopped_StepComplete */
357 case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */
358 case 0x20024: /* ADP_Stopped_InternalError */
359 case 0x20025: /* ADP_Stopped_UserInterruption */
360 case 0x20027: /* ADP_Stopped_StackOverflow */
361 case 0x20028: /* ADP_Stopped_DivisionByZero */
362 case 0x20029: /* ADP_Stopped_OSSpecific */
363 default:
364 fprintf(stderr, "semihosting: exception %#x\n",
365 (unsigned) r1);
366 }
367 return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
368
369 case 0x0d: /* SYS_TMPNAM */
370 case 0x10: /* SYS_CLOCK */
371 case 0x12: /* SYS_SYSTEM */
372 case 0x17: /* angel_SWIreason_EnterSVC */
373 case 0x30: /* SYS_ELAPSED */
374 case 0x31: /* SYS_TICKFREQ */
375 default:
376 fprintf(stderr, "semihosting: unsupported call %#x\n",
377 (unsigned) r0);
378 result = -1;
379 armv4_5->semihosting_errno = ENOTSUP;
380 }
381
382 /* resume execution to the original mode */
383
384 /* return value in R0 */
385 buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, result);
386 armv4_5->core_cache->reg_list[0].dirty = 1;
387
388 /* LR --> PC */
389 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, lr);
390 armv4_5->core_cache->reg_list[15].dirty = 1;
391
392 /* saved PSR --> current PSR */
393 buf_set_u32(armv4_5->cpsr->value, 0, 32, spsr);
394 armv4_5->cpsr->dirty = 1;
395 armv4_5->core_mode = spsr & 0x1f;
396 if (spsr & 0x20)
397 armv4_5->core_state = ARM_STATE_THUMB;
398
399 return target_resume(target, 1, 0, 0, 0);
400 }
401
402 /**
403 * Checks for and processes an ARM semihosting request. This is meant
404 * to be called when the target is stopped due to a debug mode entry.
405 * If the value 0 is returned then there was nothing to process. A non-zero
406 * return value signifies that a request was processed and the target resumed,
407 * or an error was encountered, in which case the caller must return
408 * immediately.
409 *
410 * @param target Pointer to the ARM target to process. This target must
411 * not represent an ARMv6-M or ARMv7-M processor.
412 * @param retval Pointer to a location where the return code will be stored
413 * @return non-zero value if a request was processed or an error encountered
414 */
415 int arm_semihosting(struct target *target, int *retval)
416 {
417 struct arm *arm = target_to_arm(target);
418 uint32_t pc, lr, spsr;
419 struct reg *r;
420
421 if (!arm->is_semihosting || arm->core_mode != ARM_MODE_SVC)
422 return 0;
423
424 /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */
425 r = arm->core_cache->reg_list + 15;
426 pc = buf_get_u32(r->value, 0, 32);
427 if (pc != 0x00000008 && pc != 0xffff0008)
428 return 0;
429
430 r = arm_reg_current(arm, 14);
431 lr = buf_get_u32(r->value, 0, 32);
432
433 /* Core-specific code should make sure SPSR is retrieved
434 * when the above checks pass...
435 */
436 if (!arm->spsr->valid) {
437 LOG_ERROR("SPSR not valid!");
438 *retval = ERROR_FAIL;
439 return 1;
440 }
441
442 spsr = buf_get_u32(arm->spsr->value, 0, 32);
443
444 /* check instruction that triggered this trap */
445 if (spsr & (1 << 5)) {
446 /* was in Thumb (or ThumbEE) mode */
447 uint8_t insn_buf[2];
448 uint16_t insn;
449
450 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
451 if (*retval != ERROR_OK)
452 return 1;
453 insn = target_buffer_get_u16(target, insn_buf);
454
455 /* SVC 0xab */
456 if (insn != 0xDFAB)
457 return 0;
458 } else if (spsr & (1 << 24)) {
459 /* was in Jazelle mode */
460 return 0;
461 } else {
462 /* was in ARM mode */
463 uint8_t insn_buf[4];
464 uint32_t insn;
465
466 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
467 if (*retval != ERROR_OK)
468 return 1;
469 insn = target_buffer_get_u32(target, insn_buf);
470
471 /* SVC 0x123456 */
472 if (insn != 0xEF123456)
473 return 0;
474 }
475
476 *retval = do_semihosting(target);
477 return 1;
478 }

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)