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