1: /*
2: * Arm "Angel" semihosting syscalls
3: *
4: * Copyright (c) 2005, 2007 CodeSourcery.
5: * Written by Paul Brook.
6: *
7: * This program is free software; you can redistribute it and/or modify
8: * it under the terms of the GNU General Public License as published by
9: * the Free Software Foundation; either version 2 of the License, or
10: * (at your option) any later version.
11: *
12: * This program is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15: * GNU General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public License
18: * along with this program; if not, write to the Free Software
19: * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20: * MA 02110-1301, USA.
21: */
22:
23: #include <sys/types.h>
24: #include <sys/stat.h>
25: #include <fcntl.h>
26: #include <unistd.h>
27: #include <stdlib.h>
28: #include <stdio.h>
29: #include <time.h>
30:
31: #include "cpu.h"
32: #ifdef CONFIG_USER_ONLY
33: #include "qemu.h"
34:
35: #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
36: #else
37: #include "qemu-common.h"
38: #include "sysemu.h"
39: #include "gdbstub.h"
40: #endif
41:
42: #define SYS_OPEN 0x01
43: #define SYS_CLOSE 0x02
44: #define SYS_WRITEC 0x03
45: #define SYS_WRITE0 0x04
46: #define SYS_WRITE 0x05
47: #define SYS_READ 0x06
48: #define SYS_READC 0x07
49: #define SYS_ISTTY 0x09
50: #define SYS_SEEK 0x0a
51: #define SYS_FLEN 0x0c
52: #define SYS_TMPNAM 0x0d
53: #define SYS_REMOVE 0x0e
54: #define SYS_RENAME 0x0f
55: #define SYS_CLOCK 0x10
56: #define SYS_TIME 0x11
57: #define SYS_SYSTEM 0x12
58: #define SYS_ERRNO 0x13
59: #define SYS_GET_CMDLINE 0x15
60: #define SYS_HEAPINFO 0x16
61: #define SYS_EXIT 0x18
62:
63: #ifndef O_BINARY
64: #define O_BINARY 0
65: #endif
66:
67: #define GDB_O_RDONLY 0x000
68: #define GDB_O_WRONLY 0x001
69: #define GDB_O_RDWR 0x002
70: #define GDB_O_APPEND 0x008
71: #define GDB_O_CREAT 0x200
72: #define GDB_O_TRUNC 0x400
73: #define GDB_O_BINARY 0
74:
75: static int gdb_open_modeflags[12] = {
76: GDB_O_RDONLY,
77: GDB_O_RDONLY | GDB_O_BINARY,
78: GDB_O_RDWR,
79: GDB_O_RDWR | GDB_O_BINARY,
80: GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
81: GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
82: GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
83: GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
84: GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
85: GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
86: GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
87: GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
88: };
89:
90: static int open_modeflags[12] = {
91: O_RDONLY,
92: O_RDONLY | O_BINARY,
93: O_RDWR,
94: O_RDWR | O_BINARY,
95: O_WRONLY | O_CREAT | O_TRUNC,
96: O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
97: O_RDWR | O_CREAT | O_TRUNC,
98: O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
99: O_WRONLY | O_CREAT | O_APPEND,
100: O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
101: O_RDWR | O_CREAT | O_APPEND,
102: O_RDWR | O_CREAT | O_APPEND | O_BINARY
103: };
104:
105: #ifdef CONFIG_USER_ONLY
106: static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
107: {
108: if (code == (uint32_t)-1)
109: ts->swi_errno = errno;
110: return code;
111: }
112: #else
113: static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
114: {
115: return code;
116: }
117:
118: #include "softmmu-semi.h"
119: #endif
120:
121: static target_ulong arm_semi_syscall_len;
122:
123: #if !defined(CONFIG_USER_ONLY)
124: static target_ulong syscall_err;
125: #endif
126:
127: static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
128: {
129: #ifdef CONFIG_USER_ONLY
130: TaskState *ts = env->opaque;
131: #endif
132:
133: if (ret == (target_ulong)-1) {
134: #ifdef CONFIG_USER_ONLY
135: ts->swi_errno = err;
136: #else
137: syscall_err = err;
138: #endif
139: env->regs[0] = ret;
140: } else {
141: /* Fixup syscalls that use nonstardard return conventions. */
142: switch (env->regs[0]) {
143: case SYS_WRITE:
144: case SYS_READ:
145: env->regs[0] = arm_semi_syscall_len - ret;
146: break;
147: case SYS_SEEK:
148: env->regs[0] = 0;
149: break;
150: default:
151: env->regs[0] = ret;
152: break;
153: }
154: }
155: }
156:
157: static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
158: {
159: /* The size is always stored in big-endian order, extract
160: the value. We assume the size always fit in 32 bits. */
161: uint32_t size;
162: cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
163: env->regs[0] = be32_to_cpu(size);
164: #ifdef CONFIG_USER_ONLY
165: ((TaskState *)env->opaque)->swi_errno = err;
166: #else
167: syscall_err = err;
168: #endif
169: }
170:
171: #define ARG(n) \
172: ({ \
173: target_ulong __arg; \
174: /* FIXME - handle get_user() failure */ \
175: get_user_ual(__arg, args + (n) * 4); \
176: __arg; \
177: })
178: #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
179: uint32_t do_arm_semihosting(CPUState *env)
180: {
181: target_ulong args;
182: char * s;
183: int nr;
184: uint32_t ret;
185: uint32_t len;
186: #ifdef CONFIG_USER_ONLY
187: TaskState *ts = env->opaque;
188: #else
189: CPUState *ts = env;
190: #endif
191:
192: nr = env->regs[0];
193: args = env->regs[1];
194: switch (nr) {
195: case SYS_OPEN:
196: if (!(s = lock_user_string(ARG(0))))
197: /* FIXME - should this error code be -TARGET_EFAULT ? */
198: return (uint32_t)-1;
199: if (ARG(1) >= 12)
200: return (uint32_t)-1;
201: if (strcmp(s, ":tt") == 0) {
202: if (ARG(1) < 4)
203: return STDIN_FILENO;
204: else
205: return STDOUT_FILENO;
206: }
207: if (use_gdb_syscalls()) {
208: gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
209: (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
210: return env->regs[0];
211: } else {
212: ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
213: }
214: unlock_user(s, ARG(0), 0);
215: return ret;
216: case SYS_CLOSE:
217: if (use_gdb_syscalls()) {
218: gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
219: return env->regs[0];
220: } else {
221: return set_swi_errno(ts, close(ARG(0)));
222: }
223: case SYS_WRITEC:
224: {
225: char c;
226:
227: if (get_user_u8(c, args))
228: /* FIXME - should this error code be -TARGET_EFAULT ? */
229: return (uint32_t)-1;
230: /* Write to debug console. stderr is near enough. */
231: if (use_gdb_syscalls()) {
232: gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
233: return env->regs[0];
234: } else {
235: return write(STDERR_FILENO, &c, 1);
236: }
237: }
238: case SYS_WRITE0:
239: if (!(s = lock_user_string(args)))
240: /* FIXME - should this error code be -TARGET_EFAULT ? */
241: return (uint32_t)-1;
242: len = strlen(s);
243: if (use_gdb_syscalls()) {
244: gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
245: ret = env->regs[0];
246: } else {
247: ret = write(STDERR_FILENO, s, len);
248: }
249: unlock_user(s, args, 0);
250: return ret;
251: case SYS_WRITE:
252: len = ARG(2);
253: if (use_gdb_syscalls()) {
254: arm_semi_syscall_len = len;
255: gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
256: return env->regs[0];
257: } else {
258: if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
259: /* FIXME - should this error code be -TARGET_EFAULT ? */
260: return (uint32_t)-1;
261: ret = set_swi_errno(ts, write(ARG(0), s, len));
262: unlock_user(s, ARG(1), 0);
263: if (ret == (uint32_t)-1)
264: return -1;
265: return len - ret;
266: }
267: case SYS_READ:
268: len = ARG(2);
269: if (use_gdb_syscalls()) {
270: arm_semi_syscall_len = len;
271: gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
272: return env->regs[0];
273: } else {
274: if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
275: /* FIXME - should this error code be -TARGET_EFAULT ? */
276: return (uint32_t)-1;
277: do
278: ret = set_swi_errno(ts, read(ARG(0), s, len));
279: while (ret == -1 && errno == EINTR);
280: unlock_user(s, ARG(1), len);
281: if (ret == (uint32_t)-1)
282: return -1;
283: return len - ret;
284: }
285: case SYS_READC:
286: /* XXX: Read from debug cosole. Not implemented. */
287: return 0;
288: case SYS_ISTTY:
289: if (use_gdb_syscalls()) {
290: gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
291: return env->regs[0];
292: } else {
293: return isatty(ARG(0));
294: }
295: case SYS_SEEK:
296: if (use_gdb_syscalls()) {
297: gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
298: return env->regs[0];
299: } else {
300: ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
301: if (ret == (uint32_t)-1)
302: return -1;
303: return 0;
304: }
305: case SYS_FLEN:
306: if (use_gdb_syscalls()) {
307: gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
308: ARG(0), env->regs[13]-64);
309: return env->regs[0];
310: } else {
311: struct stat buf;
312: ret = set_swi_errno(ts, fstat(ARG(0), &buf));
313: if (ret == (uint32_t)-1)
314: return -1;
315: return buf.st_size;
316: }
317: case SYS_TMPNAM:
318: /* XXX: Not implemented. */
319: return -1;
320: case SYS_REMOVE:
321: if (use_gdb_syscalls()) {
322: gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
323: ret = env->regs[0];
324: } else {
325: if (!(s = lock_user_string(ARG(0))))
326: /* FIXME - should this error code be -TARGET_EFAULT ? */
327: return (uint32_t)-1;
328: ret = set_swi_errno(ts, remove(s));
329: unlock_user(s, ARG(0), 0);
330: }
331: return ret;
332: case SYS_RENAME:
333: if (use_gdb_syscalls()) {
334: gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
335: ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
336: return env->regs[0];
337: } else {
338: char *s2;
339: s = lock_user_string(ARG(0));
340: s2 = lock_user_string(ARG(2));
341: if (!s || !s2)
342: /* FIXME - should this error code be -TARGET_EFAULT ? */
343: ret = (uint32_t)-1;
344: else
345: ret = set_swi_errno(ts, rename(s, s2));
346: if (s2)
347: unlock_user(s2, ARG(2), 0);
348: if (s)
349: unlock_user(s, ARG(0), 0);
350: return ret;
351: }
352: case SYS_CLOCK:
353: return clock() / (CLOCKS_PER_SEC / 100);
354: case SYS_TIME:
355: return set_swi_errno(ts, time(NULL));
356: case SYS_SYSTEM:
357: if (use_gdb_syscalls()) {
358: gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
359: return env->regs[0];
360: } else {
361: if (!(s = lock_user_string(ARG(0))))
362: /* FIXME - should this error code be -TARGET_EFAULT ? */
363: return (uint32_t)-1;
364: ret = set_swi_errno(ts, system(s));
365: unlock_user(s, ARG(0), 0);
366: return ret;
367: }
368: case SYS_ERRNO:
369: #ifdef CONFIG_USER_ONLY
370: return ts->swi_errno;
371: #else
372: return syscall_err;
373: #endif
374: case SYS_GET_CMDLINE:
375: #ifdef CONFIG_USER_ONLY
376: /* Build a commandline from the original argv. */
377: {
378: char **arg = ts->info->host_argv;
379: int len = ARG(1);
380: /* lock the buffer on the ARM side */
381: char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0);
382:
383: if (!cmdline_buffer)
384: /* FIXME - should this error code be -TARGET_EFAULT ? */
385: return (uint32_t)-1;
386:
387: s = cmdline_buffer;
388: while (*arg && len > 2) {
389: int n = strlen(*arg);
390:
391: if (s != cmdline_buffer) {
392: *(s++) = ' ';
393: len--;
394: }
395: if (n >= len)
396: n = len - 1;
397: memcpy(s, *arg, n);
398: s += n;
399: len -= n;
400: arg++;
401: }
402: /* Null terminate the string. */
403: *s = 0;
404: len = s - cmdline_buffer;
405:
406: /* Unlock the buffer on the ARM side. */
407: unlock_user(cmdline_buffer, ARG(0), len);
408:
409: /* Adjust the commandline length argument. */
410: SET_ARG(1, len);
411:
412: /* Return success if commandline fit into buffer. */
413: return *arg ? -1 : 0;
414: }
415: #else
416: return -1;
417: #endif
418: case SYS_HEAPINFO:
419: {
420: uint32_t *ptr;
421: uint32_t limit;
422:
423: #ifdef CONFIG_USER_ONLY
424: /* Some C libraries assume the heap immediately follows .bss, so
425: allocate it using sbrk. */
426: if (!ts->heap_limit) {
427: long ret;
428:
429: ts->heap_base = do_brk(0);
430: limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
431: /* Try a big heap, and reduce the size if that fails. */
432: for (;;) {
433: ret = do_brk(limit);
434: if (ret != -1)
435: break;
436: limit = (ts->heap_base >> 1) + (limit >> 1);
437: }
438: ts->heap_limit = limit;
439: }
440:
441: if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
442: /* FIXME - should this error code be -TARGET_EFAULT ? */
443: return (uint32_t)-1;
444: ptr[0] = tswap32(ts->heap_base);
445: ptr[1] = tswap32(ts->heap_limit);
446: ptr[2] = tswap32(ts->stack_base);
447: ptr[3] = tswap32(0); /* Stack limit. */
448: unlock_user(ptr, ARG(0), 16);
449: #else
450: limit = ram_size;
451: if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
452: /* FIXME - should this error code be -TARGET_EFAULT ? */
453: return (uint32_t)-1;
454: /* TODO: Make this use the limit of the loaded application. */
455: ptr[0] = tswap32(limit / 2);
456: ptr[1] = tswap32(limit);
457: ptr[2] = tswap32(limit); /* Stack base */
458: ptr[3] = tswap32(0); /* Stack limit. */
459: unlock_user(ptr, ARG(0), 16);
460: #endif
461: return 0;
462: }
463: case SYS_EXIT:
464: exit(0);
465: default:
466: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
467: cpu_dump_state(env, stderr, fprintf, 0);
468: abort();
469: }
470: }
unix.superglobalmegacorp.com