M68k system mode semihosting.
[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     p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
120     p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
121     p->gdb_st_atime = cpu_to_be32(s->st_atime);
122     p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
123     p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
124     unlock_user(p, addr, sizeof(struct m68k_gdb_stat));
125 }
126
127 static int m68k_semi_is_fseek;
128
129 static void m68k_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
130 {
131     target_ulong args;
132
133     args = env->dregs[1];
134     if (m68k_semi_is_fseek) {
135         /* FIXME: We've already lost the high bits of the fseek
136            return value.  */
137         tput32(args, 0);
138         args += 4;
139         m68k_semi_is_fseek = 0;
140     }
141     tput32(args, ret);
142     tput32(args + 4, errno);
143 }
144
145 #define ARG(x) tget32(args + (x) * 4)
146 #define PARG(x) ((unsigned long)ARG(x))
147 void do_m68k_semihosting(CPUM68KState *env, int nr)
148 {
149     uint32_t args;
150     void *p;
151     void *q;
152     uint32_t len;
153     uint32_t result;
154
155     args = env->dregs[1];
156     switch (nr) {
157     case HOSTED_EXIT:
158         exit(env->dregs[0]);
159     case HOSTED_OPEN:
160         if (use_gdb_syscalls()) {
161             gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", ARG(0), (int)ARG(1),
162                            ARG(2), ARG(3));
163             return;
164         } else {
165             p = lock_user_string(ARG(0));
166             result = open(p, translate_openflags(ARG(2)), ARG(3));
167             unlock_user(p, ARG(0), 0);
168         }
169         break;
170     case HOSTED_CLOSE:
171         {
172             /* Ignore attempts to close stdin/out/err.  */
173             int fd = ARG(0);
174             if (fd > 2) {
175                 if (use_gdb_syscalls()) {
176                     gdb_do_syscall(m68k_semi_cb, "close,%x", ARG(0));
177                     return;
178                 } else {
179                     result = close(fd);
180                 }
181             } else {
182                 result = 0;
183             }
184             break;
185         }
186     case HOSTED_READ:
187         len = ARG(2);
188         if (use_gdb_syscalls()) {
189             gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x",
190                            ARG(0), ARG(1), len);
191             return;
192         } else {
193             p = lock_user(ARG(1), len, 0);
194             result = read(ARG(0), p, len);
195             unlock_user(p, ARG(1), len);
196         }
197         break;
198     case HOSTED_WRITE:
199         len = ARG(2);
200         if (use_gdb_syscalls()) {
201             gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x",
202                            ARG(0), ARG(1), len);
203             return;
204         } else {
205             p = lock_user(ARG(1), len, 1);
206             result = write(ARG(0), p, len);
207             unlock_user(p, ARG(0), 0);
208         }
209         break;
210     case HOSTED_LSEEK:
211         {
212             uint64_t off;
213             off = (uint32_t)ARG(2) | ((uint64_t)ARG(1) << 32);
214             if (use_gdb_syscalls()) {
215                 m68k_semi_is_fseek = 1;
216                 gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x",
217                                ARG(0), off, ARG(3));
218             } else {
219                 off = lseek(ARG(0), off, ARG(3));
220                 tput32(args, off >> 32);
221                 tput32(args + 4, off);
222                 tput32(args + 8, errno);
223             }
224             return;
225         }
226     case HOSTED_RENAME:
227         if (use_gdb_syscalls()) {
228             gdb_do_syscall(m68k_semi_cb, "rename,%s,%s", 
229                            ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
230             return;
231         } else {
232             p = lock_user_string(ARG(0));
233             q = lock_user_string(ARG(2));
234             result = rename(p, q);
235             unlock_user(p, ARG(0), 0);
236             unlock_user(q, ARG(2), 0);
237         }
238         break;
239     case HOSTED_UNLINK:
240         if (use_gdb_syscalls()) {
241             gdb_do_syscall(m68k_semi_cb, "unlink,%s",
242                            ARG(0), (int)ARG(1));
243             return;
244         } else {
245             p = lock_user_string(ARG(0));
246             result = unlink(p);
247             unlock_user(p, ARG(0), 0);
248         }
249         break;
250     case HOSTED_STAT:
251         if (use_gdb_syscalls()) {
252             gdb_do_syscall(m68k_semi_cb, "stat,%s,%x",
253                            ARG(0), (int)ARG(1), ARG(2));
254             return;
255         } else {
256             struct stat s;
257             p = lock_user_string(ARG(0));
258             result = stat(p, &s);
259             unlock_user(p, ARG(0), 0);
260             if (result == 0) {
261                 translate_stat(env, ARG(2), &s);
262             }
263         }
264         break;
265     case HOSTED_FSTAT:
266         if (use_gdb_syscalls()) {
267             gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x",
268                            ARG(0), ARG(1));
269             return;
270         } else {
271             struct stat s;
272             result = fstat(ARG(0), &s);
273             if (result == 0) {
274                 translate_stat(env, ARG(1), &s);
275             }
276         }
277         break;
278     case HOSTED_GETTIMEOFDAY:
279         if (use_gdb_syscalls()) {
280             gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x",
281                            ARG(0), ARG(1));
282             return;
283         } else {
284             struct timeval tv;
285             struct gdb_timeval *p;
286             result = gettimeofday(&tv, NULL);
287             if (result != 0) {
288                 p = lock_user(ARG(0), sizeof(struct gdb_timeval), 0);
289                 p->tv_sec = cpu_to_be32(tv.tv_sec);
290                 p->tv_usec = cpu_to_be64(tv.tv_usec);
291                 unlock_user(p, ARG(0), sizeof(struct gdb_timeval));
292             }
293         }
294         break;
295     case HOSTED_ISATTY:
296         if (use_gdb_syscalls()) {
297             gdb_do_syscall(m68k_semi_cb, "isatty,%x", ARG(0));
298             return;
299         } else {
300             result = isatty(ARG(0));
301         }
302         break;
303     case HOSTED_SYSTEM:
304         if (use_gdb_syscalls()) {
305             gdb_do_syscall(m68k_semi_cb, "system,%s",
306                            ARG(0), (int)ARG(1));
307             return;
308         } else {
309             p = lock_user_string(ARG(0));
310             result = system(p);
311             unlock_user(p, ARG(0), 0);
312         }
313         break;
314     case HOSTED_INIT_SIM:
315 #if defined(CONFIG_USER_ONLY)
316         {
317         TaskState *ts = env->opaque;
318         /* Allocate the heap using sbrk.  */
319         if (!ts->heap_limit) {
320             long ret;
321             uint32_t size;
322             uint32_t base;
323
324             base = do_brk(0);
325             size = SEMIHOSTING_HEAP_SIZE;
326             /* Try a big heap, and reduce the size if that fails.  */
327             for (;;) {
328                 ret = do_brk(base + size);
329                 if (ret != -1)
330                     break;
331                 size >>= 1;
332             }
333             ts->heap_limit = base + size;
334         }
335         /* This call may happen before we have writable memory, so return
336            values directly in registers.  */
337         env->dregs[1] = ts->heap_limit;
338         env->aregs[7] = ts->stack_base;
339         }
340 #else
341         /* FIXME: This is wrong for boards where RAM does not start at
342            address zero.  */
343         env->dregs[1] = ram_size;
344         env->aregs[7] = ram_size;
345 #endif
346         return;
347     default:
348         cpu_abort(env, "Unsupported semihosting syscall %d\n", nr);
349         result = 0;
350     }
351     tput32(args, result);
352     tput32(args + 4, errno);
353 }