Merge branch 'master' of /home/nchip/public_html/qemu into garage-push
[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., 51 Franklin Street - Fifth Floor, Boston,
19  *  MA 02110-1301, USA.
20  */
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <sys/time.h>
30 #include <time.h>
31
32 #include "cpu.h"
33 #if defined(CONFIG_USER_ONLY)
34 #include "qemu.h"
35 #define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024)
36 #else
37 #include "qemu-common.h"
38 #include "sysemu.h"
39 #include "gdbstub.h"
40 #include "softmmu-semi.h"
41 #endif
42
43 #define HOSTED_EXIT  0
44 #define HOSTED_INIT_SIM 1
45 #define HOSTED_OPEN 2
46 #define HOSTED_CLOSE 3
47 #define HOSTED_READ 4
48 #define HOSTED_WRITE 5
49 #define HOSTED_LSEEK 6
50 #define HOSTED_RENAME 7
51 #define HOSTED_UNLINK 8
52 #define HOSTED_STAT 9
53 #define HOSTED_FSTAT 10
54 #define HOSTED_GETTIMEOFDAY 11
55 #define HOSTED_ISATTY 12
56 #define HOSTED_SYSTEM 13
57
58 typedef uint32_t gdb_mode_t;
59 typedef uint32_t gdb_time_t;
60
61 struct m68k_gdb_stat {
62   uint32_t    gdb_st_dev;     /* device */
63   uint32_t    gdb_st_ino;     /* inode */
64   gdb_mode_t  gdb_st_mode;    /* protection */
65   uint32_t    gdb_st_nlink;   /* number of hard links */
66   uint32_t    gdb_st_uid;     /* user ID of owner */
67   uint32_t    gdb_st_gid;     /* group ID of owner */
68   uint32_t    gdb_st_rdev;    /* device type (if inode device) */
69   uint64_t    gdb_st_size;    /* total size, in bytes */
70   uint64_t    gdb_st_blksize; /* blocksize for filesystem I/O */
71   uint64_t    gdb_st_blocks;  /* number of blocks allocated */
72   gdb_time_t  gdb_st_atime;   /* time of last access */
73   gdb_time_t  gdb_st_mtime;   /* time of last modification */
74   gdb_time_t  gdb_st_ctime;   /* time of last change */
75 } __attribute__((packed));
76
77 struct gdb_timeval {
78   gdb_time_t tv_sec;  /* second */
79   uint64_t tv_usec;   /* microsecond */
80 } __attribute__((packed));
81
82 #define GDB_O_RDONLY   0x0
83 #define GDB_O_WRONLY   0x1
84 #define GDB_O_RDWR     0x2
85 #define GDB_O_APPEND   0x8
86 #define GDB_O_CREAT  0x200
87 #define GDB_O_TRUNC  0x400
88 #define GDB_O_EXCL   0x800
89
90 static int translate_openflags(int flags)
91 {
92     int hf;
93
94     if (flags & GDB_O_WRONLY)
95         hf = O_WRONLY;
96     else if (flags & GDB_O_RDWR)
97         hf = O_RDWR;
98     else
99         hf = O_RDONLY;
100
101     if (flags & GDB_O_APPEND) hf |= O_APPEND;
102     if (flags & GDB_O_CREAT) hf |= O_CREAT;
103     if (flags & GDB_O_TRUNC) hf |= O_TRUNC;
104     if (flags & GDB_O_EXCL) hf |= O_EXCL;
105
106     return hf;
107 }
108
109 static void translate_stat(CPUState *env, target_ulong addr, struct stat *s)
110 {
111     struct m68k_gdb_stat *p;
112
113     if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(struct m68k_gdb_stat), 0)))
114         /* FIXME - should this return an error code? */
115         return;
116     p->gdb_st_dev = cpu_to_be32(s->st_dev);
117     p->gdb_st_ino = cpu_to_be32(s->st_ino);
118     p->gdb_st_mode = cpu_to_be32(s->st_mode);
119     p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
120     p->gdb_st_uid = cpu_to_be32(s->st_uid);
121     p->gdb_st_gid = cpu_to_be32(s->st_gid);
122     p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
123     p->gdb_st_size = cpu_to_be64(s->st_size);
124 #ifdef _WIN32
125     /* Windows stat is missing some fields.  */
126     p->gdb_st_blksize = 0;
127     p->gdb_st_blocks = 0;
128 #else
129     p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
130     p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
131 #endif
132     p->gdb_st_atime = cpu_to_be32(s->st_atime);
133     p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
134     p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
135     unlock_user(p, addr, sizeof(struct m68k_gdb_stat));
136 }
137
138 static int m68k_semi_is_fseek;
139
140 static void m68k_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
141 {
142     target_ulong args;
143
144     args = env->dregs[1];
145     if (m68k_semi_is_fseek) {
146         /* FIXME: We've already lost the high bits of the fseek
147            return value.  */
148         /* FIXME - handle put_user() failure */
149         put_user_u32(0, args);
150         args += 4;
151         m68k_semi_is_fseek = 0;
152     }
153     /* FIXME - handle put_user() failure */
154     put_user_u32(ret, args);
155     put_user_u32(errno, args + 4);
156 }
157
158 #define ARG(n)                                  \
159 ({                                              \
160     target_ulong __arg;                         \
161     /* FIXME - handle get_user() failure */     \
162     get_user_ual(__arg, args + (n) * 4);        \
163     __arg;                                      \
164 })
165 #define PARG(x) ((unsigned long)ARG(x))
166 void do_m68k_semihosting(CPUM68KState *env, int nr)
167 {
168     uint32_t args;
169     void *p;
170     void *q;
171     uint32_t len;
172     uint32_t result;
173
174     args = env->dregs[1];
175     switch (nr) {
176     case HOSTED_EXIT:
177         exit(env->dregs[0]);
178     case HOSTED_OPEN:
179         if (use_gdb_syscalls()) {
180             gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", ARG(0), (int)ARG(1),
181                            ARG(2), ARG(3));
182             return;
183         } else {
184             if (!(p = lock_user_string(ARG(0)))) {
185                 /* FIXME - check error code? */
186                 result = -1;
187             } else {
188                 result = open(p, translate_openflags(ARG(2)), ARG(3));
189                 unlock_user(p, ARG(0), 0);
190             }
191         }
192         break;
193     case HOSTED_CLOSE:
194         {
195             /* Ignore attempts to close stdin/out/err.  */
196             int fd = ARG(0);
197             if (fd > 2) {
198                 if (use_gdb_syscalls()) {
199                     gdb_do_syscall(m68k_semi_cb, "close,%x", ARG(0));
200                     return;
201                 } else {
202                     result = close(fd);
203                 }
204             } else {
205                 result = 0;
206             }
207             break;
208         }
209     case HOSTED_READ:
210         len = ARG(2);
211         if (use_gdb_syscalls()) {
212             gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x",
213                            ARG(0), ARG(1), len);
214             return;
215         } else {
216             if (!(p = lock_user(VERIFY_WRITE, ARG(1), len, 0))) {
217                 /* FIXME - check error code? */
218                 result = -1;
219             } else {
220                 result = read(ARG(0), p, len);
221                 unlock_user(p, ARG(1), len);
222             }
223         }
224         break;
225     case HOSTED_WRITE:
226         len = ARG(2);
227         if (use_gdb_syscalls()) {
228             gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x",
229                            ARG(0), ARG(1), len);
230             return;
231         } else {
232             if (!(p = lock_user(VERIFY_READ, ARG(1), len, 1))) {
233                 /* FIXME - check error code? */
234                 result = -1;
235             } else {
236                 result = write(ARG(0), p, len);
237                 unlock_user(p, ARG(0), 0);
238             }
239         }
240         break;
241     case HOSTED_LSEEK:
242         {
243             uint64_t off;
244             off = (uint32_t)ARG(2) | ((uint64_t)ARG(1) << 32);
245             if (use_gdb_syscalls()) {
246                 m68k_semi_is_fseek = 1;
247                 gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x",
248                                ARG(0), off, ARG(3));
249             } else {
250                 off = lseek(ARG(0), off, ARG(3));
251                 /* FIXME - handle put_user() failure */
252                 put_user_u32(off >> 32, args);
253                 put_user_u32(off, args + 4);
254                 put_user_u32(errno, args + 8);
255             }
256             return;
257         }
258     case HOSTED_RENAME:
259         if (use_gdb_syscalls()) {
260             gdb_do_syscall(m68k_semi_cb, "rename,%s,%s",
261                            ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
262             return;
263         } else {
264             p = lock_user_string(ARG(0));
265             q = lock_user_string(ARG(2));
266             if (!p || !q) {
267                 /* FIXME - check error code? */
268                 result = -1;
269             } else {
270                 result = rename(p, q);
271             }
272             unlock_user(p, ARG(0), 0);
273             unlock_user(q, ARG(2), 0);
274         }
275         break;
276     case HOSTED_UNLINK:
277         if (use_gdb_syscalls()) {
278             gdb_do_syscall(m68k_semi_cb, "unlink,%s",
279                            ARG(0), (int)ARG(1));
280             return;
281         } else {
282             if (!(p = lock_user_string(ARG(0)))) {
283                 /* FIXME - check error code? */
284                 result = -1;
285             } else {
286                 result = unlink(p);
287                 unlock_user(p, ARG(0), 0);
288             }
289         }
290         break;
291     case HOSTED_STAT:
292         if (use_gdb_syscalls()) {
293             gdb_do_syscall(m68k_semi_cb, "stat,%s,%x",
294                            ARG(0), (int)ARG(1), ARG(2));
295             return;
296         } else {
297             struct stat s;
298             if (!(p = lock_user_string(ARG(0)))) {
299                 /* FIXME - check error code? */
300                 result = -1;
301             } else {
302                 result = stat(p, &s);
303                 unlock_user(p, ARG(0), 0);
304             }
305             if (result == 0) {
306                 translate_stat(env, ARG(2), &s);
307             }
308         }
309         break;
310     case HOSTED_FSTAT:
311         if (use_gdb_syscalls()) {
312             gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x",
313                            ARG(0), ARG(1));
314             return;
315         } else {
316             struct stat s;
317             result = fstat(ARG(0), &s);
318             if (result == 0) {
319                 translate_stat(env, ARG(1), &s);
320             }
321         }
322         break;
323     case HOSTED_GETTIMEOFDAY:
324         if (use_gdb_syscalls()) {
325             gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x",
326                            ARG(0), ARG(1));
327             return;
328         } else {
329             qemu_timeval tv;
330             struct gdb_timeval *p;
331             result = qemu_gettimeofday(&tv);
332             if (result != 0) {
333                 if (!(p = lock_user(VERIFY_WRITE,
334                                     ARG(0), sizeof(struct gdb_timeval), 0))) {
335                     /* FIXME - check error code? */
336                     result = -1;
337                 } else {
338                     p->tv_sec = cpu_to_be32(tv.tv_sec);
339                     p->tv_usec = cpu_to_be64(tv.tv_usec);
340                     unlock_user(p, ARG(0), sizeof(struct gdb_timeval));
341                 }
342             }
343         }
344         break;
345     case HOSTED_ISATTY:
346         if (use_gdb_syscalls()) {
347             gdb_do_syscall(m68k_semi_cb, "isatty,%x", ARG(0));
348             return;
349         } else {
350             result = isatty(ARG(0));
351         }
352         break;
353     case HOSTED_SYSTEM:
354         if (use_gdb_syscalls()) {
355             gdb_do_syscall(m68k_semi_cb, "system,%s",
356                            ARG(0), (int)ARG(1));
357             return;
358         } else {
359             if (!(p = lock_user_string(ARG(0)))) {
360                 /* FIXME - check error code? */
361                 result = -1;
362             } else {
363                 result = system(p);
364                 unlock_user(p, ARG(0), 0);
365             }
366         }
367         break;
368     case HOSTED_INIT_SIM:
369 #if defined(CONFIG_USER_ONLY)
370         {
371         TaskState *ts = env->opaque;
372         /* Allocate the heap using sbrk.  */
373         if (!ts->heap_limit) {
374             long ret;
375             uint32_t size;
376             uint32_t base;
377
378             base = do_brk(0);
379             size = SEMIHOSTING_HEAP_SIZE;
380             /* Try a big heap, and reduce the size if that fails.  */
381             for (;;) {
382                 ret = do_brk(base + size);
383                 if (ret != -1)
384                     break;
385                 size >>= 1;
386             }
387             ts->heap_limit = base + size;
388         }
389         /* This call may happen before we have writable memory, so return
390            values directly in registers.  */
391         env->dregs[1] = ts->heap_limit;
392         env->aregs[7] = ts->stack_base;
393         }
394 #else
395         /* FIXME: This is wrong for boards where RAM does not start at
396            address zero.  */
397         env->dregs[1] = ram_size;
398         env->aregs[7] = ram_size;
399 #endif
400         return;
401     default:
402         cpu_abort(env, "Unsupported semihosting syscall %d\n", nr);
403         result = 0;
404     }
405     /* FIXME - handle put_user() failure */
406     put_user_u32(result, args);
407     put_user_u32(errno, args + 4);
408 }