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