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

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)