find -type f | xargs sed -i 's/[\t ]$//g' # on most files
[qemu] / m68k-semi.c
1 /*
2  *  m68k/ColdFire Semihosting syscall interface
3  *
4  *  Copyright (c) 2005-2007 CodeSourcery.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <sys/time.h>
29 #include <time.h>
30
31 #include "cpu.h"
32 #if defined(CONFIG_USER_ONLY)
33 #include "qemu.h"
34 #define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024)
35 #else
36 #include "vl.h"
37 #include "softmmu-semi.h"
38 #endif
39
40 #define HOSTED_EXIT  0
41 #define HOSTED_INIT_SIM 1
42 #define HOSTED_OPEN 2
43 #define HOSTED_CLOSE 3
44 #define HOSTED_READ 4
45 #define HOSTED_WRITE 5
46 #define HOSTED_LSEEK 6
47 #define HOSTED_RENAME 7
48 #define HOSTED_UNLINK 8
49 #define HOSTED_STAT 9
50 #define HOSTED_FSTAT 10
51 #define HOSTED_GETTIMEOFDAY 11
52 #define HOSTED_ISATTY 12
53 #define HOSTED_SYSTEM 13
54
55 typedef uint32_t gdb_mode_t;
56 typedef uint32_t gdb_time_t;
57
58 struct m68k_gdb_stat {
59   uint32_t    gdb_st_dev;     /* device */
60   uint32_t    gdb_st_ino;     /* inode */
61   gdb_mode_t  gdb_st_mode;    /* protection */
62   uint32_t    gdb_st_nlink;   /* number of hard links */
63   uint32_t    gdb_st_uid;     /* user ID of owner */
64   uint32_t    gdb_st_gid;     /* group ID of owner */
65   uint32_t    gdb_st_rdev;    /* device type (if inode device) */
66   uint64_t    gdb_st_size;    /* total size, in bytes */
67   uint64_t    gdb_st_blksize; /* blocksize for filesystem I/O */
68   uint64_t    gdb_st_blocks;  /* number of blocks allocated */
69   gdb_time_t  gdb_st_atime;   /* time of last access */
70   gdb_time_t  gdb_st_mtime;   /* time of last modification */
71   gdb_time_t  gdb_st_ctime;   /* time of last change */
72 } __attribute__((packed));
73
74 struct gdb_timeval {
75   gdb_time_t tv_sec;  /* second */
76   uint64_t tv_usec;   /* microsecond */
77 } __attribute__((packed));
78
79 #define GDB_O_RDONLY   0x0
80 #define GDB_O_WRONLY   0x1
81 #define GDB_O_RDWR     0x2
82 #define GDB_O_APPEND   0x8
83 #define GDB_O_CREAT  0x200
84 #define GDB_O_TRUNC  0x400
85 #define GDB_O_EXCL   0x800
86
87 static int translate_openflags(int flags)
88 {
89     int hf;
90
91     if (flags & GDB_O_WRONLY)
92         hf = O_WRONLY;
93     else if (flags & GDB_O_RDWR)
94         hf = O_RDWR;
95     else
96         hf = O_RDONLY;
97
98     if (flags & GDB_O_APPEND) hf |= O_APPEND;
99     if (flags & GDB_O_CREAT) hf |= O_CREAT;
100     if (flags & GDB_O_TRUNC) hf |= O_TRUNC;
101     if (flags & GDB_O_EXCL) hf |= O_EXCL;
102
103     return hf;
104 }
105
106 static void translate_stat(CPUState *env, target_ulong addr, struct stat *s)
107 {
108     struct m68k_gdb_stat *p;
109
110     p = lock_user(addr, sizeof(struct m68k_gdb_stat), 0);
111     p->gdb_st_dev = cpu_to_be32(s->st_dev);
112     p->gdb_st_ino = cpu_to_be32(s->st_ino);
113     p->gdb_st_mode = cpu_to_be32(s->st_mode);
114     p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
115     p->gdb_st_uid = cpu_to_be32(s->st_uid);
116     p->gdb_st_gid = cpu_to_be32(s->st_gid);
117     p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
118     p->gdb_st_size = cpu_to_be64(s->st_size);
119 #ifdef _WIN32
120     /* Windows stat is missing some fields.  */
121     p->gdb_st_blksize = 0;
122     p->gdb_st_blocks = 0;
123 #else
124     p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
125     p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
126 #endif
127     p->gdb_st_atime = cpu_to_be32(s->st_atime);
128     p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
129     p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
130     unlock_user(p, addr, sizeof(struct m68k_gdb_stat));
131 }
132
133 static int m68k_semi_is_fseek;
134
135 static void m68k_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
136 {
137     target_ulong args;
138
139     args = env->dregs[1];
140     if (m68k_semi_is_fseek) {
141         /* FIXME: We've already lost the high bits of the fseek
142            return value.  */
143         tput32(args, 0);
144         args += 4;
145         m68k_semi_is_fseek = 0;
146     }
147     tput32(args, ret);
148     tput32(args + 4, errno);
149 }
150
151 #define ARG(x) tget32(args + (x) * 4)
152 #define PARG(x) ((unsigned long)ARG(x))
153 void do_m68k_semihosting(CPUM68KState *env, int nr)
154 {
155     uint32_t args;
156     void *p;
157     void *q;
158     uint32_t len;
159     uint32_t result;
160
161     args = env->dregs[1];
162     switch (nr) {
163     case HOSTED_EXIT:
164         exit(env->dregs[0]);
165     case HOSTED_OPEN:
166         if (use_gdb_syscalls()) {
167             gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", ARG(0), (int)ARG(1),
168                            ARG(2), ARG(3));
169             return;
170         } else {
171             p = lock_user_string(ARG(0));
172             result = open(p, translate_openflags(ARG(2)), ARG(3));
173             unlock_user(p, ARG(0), 0);
174         }
175         break;
176     case HOSTED_CLOSE:
177         {
178             /* Ignore attempts to close stdin/out/err.  */
179             int fd = ARG(0);
180             if (fd > 2) {
181                 if (use_gdb_syscalls()) {
182                     gdb_do_syscall(m68k_semi_cb, "close,%x", ARG(0));
183                     return;
184                 } else {
185                     result = close(fd);
186                 }
187             } else {
188                 result = 0;
189             }
190             break;
191         }
192     case HOSTED_READ:
193         len = ARG(2);
194         if (use_gdb_syscalls()) {
195             gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x",
196                            ARG(0), ARG(1), len);
197             return;
198         } else {
199             p = lock_user(ARG(1), len, 0);
200             result = read(ARG(0), p, len);
201             unlock_user(p, ARG(1), len);
202         }
203         break;
204     case HOSTED_WRITE:
205         len = ARG(2);
206         if (use_gdb_syscalls()) {
207             gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x",
208                            ARG(0), ARG(1), len);
209             return;
210         } else {
211             p = lock_user(ARG(1), len, 1);
212             result = write(ARG(0), p, len);
213             unlock_user(p, ARG(0), 0);
214         }
215         break;
216     case HOSTED_LSEEK:
217         {
218             uint64_t off;
219             off = (uint32_t)ARG(2) | ((uint64_t)ARG(1) << 32);
220             if (use_gdb_syscalls()) {
221                 m68k_semi_is_fseek = 1;
222                 gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x",
223                                ARG(0), off, ARG(3));
224             } else {
225                 off = lseek(ARG(0), off, ARG(3));
226                 tput32(args, off >> 32);
227                 tput32(args + 4, off);
228                 tput32(args + 8, errno);
229             }
230             return;
231         }
232     case HOSTED_RENAME:
233         if (use_gdb_syscalls()) {
234             gdb_do_syscall(m68k_semi_cb, "rename,%s,%s",
235                            ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
236             return;
237         } else {
238             p = lock_user_string(ARG(0));
239             q = lock_user_string(ARG(2));
240             result = rename(p, q);
241             unlock_user(p, ARG(0), 0);
242             unlock_user(q, ARG(2), 0);
243         }
244         break;
245     case HOSTED_UNLINK:
246         if (use_gdb_syscalls()) {
247             gdb_do_syscall(m68k_semi_cb, "unlink,%s",
248                            ARG(0), (int)ARG(1));
249             return;
250         } else {
251             p = lock_user_string(ARG(0));
252             result = unlink(p);
253             unlock_user(p, ARG(0), 0);
254         }
255         break;
256     case HOSTED_STAT:
257         if (use_gdb_syscalls()) {
258             gdb_do_syscall(m68k_semi_cb, "stat,%s,%x",
259                            ARG(0), (int)ARG(1), ARG(2));
260             return;
261         } else {
262             struct stat s;
263             p = lock_user_string(ARG(0));
264             result = stat(p, &s);
265             unlock_user(p, ARG(0), 0);
266             if (result == 0) {
267                 translate_stat(env, ARG(2), &s);
268             }
269         }
270         break;
271     case HOSTED_FSTAT:
272         if (use_gdb_syscalls()) {
273             gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x",
274                            ARG(0), ARG(1));
275             return;
276         } else {
277             struct stat s;
278             result = fstat(ARG(0), &s);
279             if (result == 0) {
280                 translate_stat(env, ARG(1), &s);
281             }
282         }
283         break;
284     case HOSTED_GETTIMEOFDAY:
285         if (use_gdb_syscalls()) {
286             gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x",
287                            ARG(0), ARG(1));
288             return;
289         } else {
290             qemu_timeval tv;
291             struct gdb_timeval *p;
292             result = qemu_gettimeofday(&tv);
293             if (result != 0) {
294                 p = lock_user(ARG(0), sizeof(struct gdb_timeval), 0);
295                 p->tv_sec = cpu_to_be32(tv.tv_sec);
296                 p->tv_usec = cpu_to_be64(tv.tv_usec);
297                 unlock_user(p, ARG(0), sizeof(struct gdb_timeval));
298             }
299         }
300         break;
301     case HOSTED_ISATTY:
302         if (use_gdb_syscalls()) {
303             gdb_do_syscall(m68k_semi_cb, "isatty,%x", ARG(0));
304             return;
305         } else {
306             result = isatty(ARG(0));
307         }
308         break;
309     case HOSTED_SYSTEM:
310         if (use_gdb_syscalls()) {
311             gdb_do_syscall(m68k_semi_cb, "system,%s",
312                            ARG(0), (int)ARG(1));
313             return;
314         } else {
315             p = lock_user_string(ARG(0));
316             result = system(p);
317             unlock_user(p, ARG(0), 0);
318         }
319         break;
320     case HOSTED_INIT_SIM:
321 #if defined(CONFIG_USER_ONLY)
322         {
323         TaskState *ts = env->opaque;
324         /* Allocate the heap using sbrk.  */
325         if (!ts->heap_limit) {
326             long ret;
327             uint32_t size;
328             uint32_t base;
329
330             base = do_brk(0);
331             size = SEMIHOSTING_HEAP_SIZE;
332             /* Try a big heap, and reduce the size if that fails.  */
333             for (;;) {
334                 ret = do_brk(base + size);
335                 if (ret != -1)
336                     break;
337                 size >>= 1;
338             }
339             ts->heap_limit = base + size;
340         }
341         /* This call may happen before we have writable memory, so return
342            values directly in registers.  */
343         env->dregs[1] = ts->heap_limit;
344         env->aregs[7] = ts->stack_base;
345         }
346 #else
347         /* FIXME: This is wrong for boards where RAM does not start at
348            address zero.  */
349         env->dregs[1] = ram_size;
350         env->aregs[7] = ram_size;
351 #endif
352         return;
353     default:
354         cpu_abort(env, "Unsupported semihosting syscall %d\n", nr);
355         result = 0;
356     }
357     tput32(args, result);
358     tput32(args + 4, errno);
359 }