ARM: remove semihosting globals
[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_armv4_5(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, ARMV4_5_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 switch (r0) {
61 case 0x01: /* SYS_OPEN */
62 retval = target_read_memory(target, r1, 4, 3, params);
63 if (retval != ERROR_OK)
64 return retval;
65 else {
66 uint32_t a = target_buffer_get_u32(target, params+0);
67 uint32_t m = target_buffer_get_u32(target, params+4);
68 uint32_t l = target_buffer_get_u32(target, params+8);
69 if (l <= 255 && m <= 11) {
70 uint8_t fn[256];
71 int mode;
72 retval = target_read_memory(target, a, 1, l, fn);
73 if (retval != ERROR_OK)
74 return retval;
75 fn[l] = 0;
76 if (m & 0x2)
77 mode = O_RDWR;
78 else if (m & 0xc)
79 mode = O_WRONLY;
80 else
81 mode = O_RDONLY;
82 if (m >= 8)
83 mode |= O_CREAT|O_APPEND;
84 else if (m >= 4)
85 mode |= O_CREAT|O_TRUNC;
86 if (strcmp((char *)fn, ":tt") == 0) {
87 if ((mode & 3) == 0)
88 result = dup(0);
89 else
90 result = dup(1);
91 } else
92 result = open((char *)fn, mode);
93 armv4_5->semihosting_errno = errno;
94 } else {
95 result = -1;
96 armv4_5->semihosting_errno = EINVAL;
97 }
98 }
99 break;
100
101 case 0x02: /* SYS_CLOSE */
102 retval = target_read_memory(target, r1, 4, 1, params);
103 if (retval != ERROR_OK)
104 return retval;
105 else {
106 int fd = target_buffer_get_u32(target, params+0);
107 result = close(fd);
108 armv4_5->semihosting_errno = errno;
109 }
110 break;
111
112 case 0x03: /* SYS_WRITEC */
113 {
114 unsigned char c;
115 retval = target_read_memory(target, r1, 1, 1, &c);
116 if (retval != ERROR_OK)
117 return retval;
118 putchar(c);
119 result = 0;
120 }
121 break;
122
123 case 0x04: /* SYS_WRITE0 */
124 do {
125 unsigned char c;
126 retval = target_read_memory(target, r1, 1, 1, &c);
127 if (retval != ERROR_OK)
128 return retval;
129 if (!c)
130 break;
131 putchar(c);
132 } while (1);
133 result = 0;
134 break;
135
136 case 0x05: /* SYS_WRITE */
137 retval = target_read_memory(target, r1, 4, 3, params);
138 if (retval != ERROR_OK)
139 return retval;
140 else {
141 int fd = target_buffer_get_u32(target, params+0);
142 uint32_t a = target_buffer_get_u32(target, params+4);
143 size_t l = target_buffer_get_u32(target, params+8);
144 uint8_t *buf = malloc(l);
145 if (!buf) {
146 result = -1;
147 armv4_5->semihosting_errno = ENOMEM;
148 } else {
149 retval = target_read_buffer(target, a, l, buf);
150 if (retval != ERROR_OK) {
151 free(buf);
152 return retval;
153 }
154 result = write(fd, buf, l);
155 armv4_5->semihosting_errno = errno;
156 if (result >= 0)
157 result = l - result;
158 free(buf);
159 }
160 }
161 break;
162
163 case 0x06: /* SYS_READ */
164 retval = target_read_memory(target, r1, 4, 3, params);
165 if (retval != ERROR_OK)
166 return retval;
167 else {
168 int fd = target_buffer_get_u32(target, params+0);
169 uint32_t a = target_buffer_get_u32(target, params+4);
170 ssize_t l = target_buffer_get_u32(target, params+8);
171 uint8_t *buf = malloc(l);
172 if (!buf) {
173 result = -1;
174 armv4_5->semihosting_errno = ENOMEM;
175 } else {
176 result = read(fd, buf, l);
177 armv4_5->semihosting_errno = errno;
178 if (result > 0) {
179 retval = target_write_buffer(target, a, result, buf);
180 if (retval != ERROR_OK) {
181 free(buf);
182 return retval;
183 }
184 result = l - result;
185 }
186 free(buf);
187 }
188 }
189 break;
190
191 case 0x07: /* SYS_READC */
192 result = getchar();
193 break;
194
195 case 0x08: /* SYS_ISERROR */
196 retval = target_read_memory(target, r1, 4, 1, params);
197 if (retval != ERROR_OK)
198 return retval;
199 result = (target_buffer_get_u32(target, params+0) != 0);
200 break;
201
202 case 0x09: /* SYS_ISTTY */
203 retval = target_read_memory(target, r1, 4, 1, params);
204 if (retval != ERROR_OK)
205 return retval;
206 result = isatty(target_buffer_get_u32(target, params+0));
207 break;
208
209 case 0x0a: /* SYS_SEEK */
210 retval = target_read_memory(target, r1, 4, 2, params);
211 if (retval != ERROR_OK)
212 return retval;
213 else {
214 int fd = target_buffer_get_u32(target, params+0);
215 off_t pos = target_buffer_get_u32(target, params+4);
216 result = lseek(fd, pos, SEEK_SET);
217 armv4_5->semihosting_errno = errno;
218 if (result == pos)
219 result = 0;
220 }
221 break;
222
223 case 0x0c: /* SYS_FLEN */
224 retval = target_read_memory(target, r1, 4, 1, params);
225 if (retval != ERROR_OK)
226 return retval;
227 else {
228 int fd = target_buffer_get_u32(target, params+0);
229 off_t cur = lseek(fd, 0, SEEK_CUR);
230 if (cur == (off_t)-1) {
231 armv4_5->semihosting_errno = errno;
232 result = -1;
233 break;
234 }
235 result = lseek(fd, 0, SEEK_END);
236 armv4_5->semihosting_errno = errno;
237 if (lseek(fd, cur, SEEK_SET) == (off_t)-1) {
238 armv4_5->semihosting_errno = errno;
239 result = -1;
240 }
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 buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, result);
380 armv4_5->core_cache->reg_list[0].dirty = 1;
381 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, lr);
382 armv4_5->core_cache->reg_list[15].dirty = 1;
383 buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, spsr);
384 armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1;
385 armv4_5->core_mode = spsr & 0x1f;
386 if (spsr & 0x20)
387 armv4_5->core_state = ARMV4_5_STATE_THUMB;
388 return target_resume(target, 1, 0, 0, 0);
389 }
390
391 /**
392 * Checks for and processes an ARM semihosting request. This is meant
393 * to be called when the target is stopped due to a debug mode entry.
394 * If the value 0 is returned then there was nothing to process. A non-zero
395 * return value signifies that a request was processed and the target resumed,
396 * or an error was encountered, in which case the caller must return
397 * immediately.
398 *
399 * @param target Pointer to the ARM target to process
400 * @param retval Pointer to a location where the return code will be stored
401 * @return non-zero value if a request was processed or an error encountered
402 */
403 int arm_semihosting(struct target *target, int *retval)
404 {
405 struct arm *armv4_5 = target_to_armv4_5(target);
406 uint32_t lr, spsr;
407
408 if (!armv4_5->is_semihosting ||
409 armv4_5->core_mode != ARMV4_5_MODE_SVC ||
410 buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) != 0x08)
411 return 0;
412
413 lr = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, ARMV4_5_MODE_SVC, 14).value, 0, 32);
414 spsr = buf_get_u32(armv4_5->spsr->value, 0, 32);
415
416 /* check instruction that triggered this trap */
417 if (spsr & (1 << 5)) {
418 /* was in Thumb mode */
419 uint8_t insn_buf[2];
420 uint16_t insn;
421 *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
422 if (*retval != ERROR_OK)
423 return 1;
424 insn = target_buffer_get_u16(target, insn_buf);
425 if (insn != 0xDFAB)
426 return 0;
427 } else {
428 /* was in ARM mode */
429 uint8_t insn_buf[4];
430 uint32_t insn;
431 *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
432 if (*retval != ERROR_OK)
433 return 1;
434 insn = target_buffer_get_u32(target, insn_buf);
435 if (insn != 0xEF123456)
436 return 0;
437 }
438
439 *retval = do_semihosting(target);
440 return 1;
441 }

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)