xref: /qemu/target/m68k/m68k-semi.c (revision 808d77bc)
1fcf5ef2aSThomas Huth /*
2fcf5ef2aSThomas Huth  *  m68k/ColdFire Semihosting syscall interface
3fcf5ef2aSThomas Huth  *
4fcf5ef2aSThomas Huth  *  Copyright (c) 2005-2007 CodeSourcery.
5fcf5ef2aSThomas Huth  *
6fcf5ef2aSThomas Huth  *  This program is free software; you can redistribute it and/or modify
7fcf5ef2aSThomas Huth  *  it under the terms of the GNU General Public License as published by
8fcf5ef2aSThomas Huth  *  the Free Software Foundation; either version 2 of the License, or
9fcf5ef2aSThomas Huth  *  (at your option) any later version.
10fcf5ef2aSThomas Huth  *
11fcf5ef2aSThomas Huth  *  This program is distributed in the hope that it will be useful,
12fcf5ef2aSThomas Huth  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13fcf5ef2aSThomas Huth  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14fcf5ef2aSThomas Huth  *  GNU General Public License for more details.
15fcf5ef2aSThomas Huth  *
16fcf5ef2aSThomas Huth  *  You should have received a copy of the GNU General Public License
17fcf5ef2aSThomas Huth  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18fcf5ef2aSThomas Huth  */
19fcf5ef2aSThomas Huth 
20fcf5ef2aSThomas Huth #include "qemu/osdep.h"
21fcf5ef2aSThomas Huth 
22fcf5ef2aSThomas Huth #include "cpu.h"
23fcf5ef2aSThomas Huth #if defined(CONFIG_USER_ONLY)
24fcf5ef2aSThomas Huth #include "qemu.h"
25fcf5ef2aSThomas Huth #define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024)
26fcf5ef2aSThomas Huth #else
27fcf5ef2aSThomas Huth #include "exec/gdbstub.h"
28fcf5ef2aSThomas Huth #include "exec/softmmu-semi.h"
29fcf5ef2aSThomas Huth #endif
30fcf5ef2aSThomas Huth #include "qemu/log.h"
31fcf5ef2aSThomas Huth #include "sysemu/sysemu.h"
32fcf5ef2aSThomas Huth 
33fcf5ef2aSThomas Huth #define HOSTED_EXIT  0
34fcf5ef2aSThomas Huth #define HOSTED_INIT_SIM 1
35fcf5ef2aSThomas Huth #define HOSTED_OPEN 2
36fcf5ef2aSThomas Huth #define HOSTED_CLOSE 3
37fcf5ef2aSThomas Huth #define HOSTED_READ 4
38fcf5ef2aSThomas Huth #define HOSTED_WRITE 5
39fcf5ef2aSThomas Huth #define HOSTED_LSEEK 6
40fcf5ef2aSThomas Huth #define HOSTED_RENAME 7
41fcf5ef2aSThomas Huth #define HOSTED_UNLINK 8
42fcf5ef2aSThomas Huth #define HOSTED_STAT 9
43fcf5ef2aSThomas Huth #define HOSTED_FSTAT 10
44fcf5ef2aSThomas Huth #define HOSTED_GETTIMEOFDAY 11
45fcf5ef2aSThomas Huth #define HOSTED_ISATTY 12
46fcf5ef2aSThomas Huth #define HOSTED_SYSTEM 13
47fcf5ef2aSThomas Huth 
48fcf5ef2aSThomas Huth typedef uint32_t gdb_mode_t;
49fcf5ef2aSThomas Huth typedef uint32_t gdb_time_t;
50fcf5ef2aSThomas Huth 
51fcf5ef2aSThomas Huth struct m68k_gdb_stat {
52fcf5ef2aSThomas Huth   uint32_t    gdb_st_dev;     /* device */
53fcf5ef2aSThomas Huth   uint32_t    gdb_st_ino;     /* inode */
54fcf5ef2aSThomas Huth   gdb_mode_t  gdb_st_mode;    /* protection */
55fcf5ef2aSThomas Huth   uint32_t    gdb_st_nlink;   /* number of hard links */
56fcf5ef2aSThomas Huth   uint32_t    gdb_st_uid;     /* user ID of owner */
57fcf5ef2aSThomas Huth   uint32_t    gdb_st_gid;     /* group ID of owner */
58fcf5ef2aSThomas Huth   uint32_t    gdb_st_rdev;    /* device type (if inode device) */
59fcf5ef2aSThomas Huth   uint64_t    gdb_st_size;    /* total size, in bytes */
60fcf5ef2aSThomas Huth   uint64_t    gdb_st_blksize; /* blocksize for filesystem I/O */
61fcf5ef2aSThomas Huth   uint64_t    gdb_st_blocks;  /* number of blocks allocated */
62fcf5ef2aSThomas Huth   gdb_time_t  gdb_st_atime;   /* time of last access */
63fcf5ef2aSThomas Huth   gdb_time_t  gdb_st_mtime;   /* time of last modification */
64fcf5ef2aSThomas Huth   gdb_time_t  gdb_st_ctime;   /* time of last change */
65fcf5ef2aSThomas Huth } QEMU_PACKED;
66fcf5ef2aSThomas Huth 
67fcf5ef2aSThomas Huth struct gdb_timeval {
68fcf5ef2aSThomas Huth   gdb_time_t tv_sec;  /* second */
69fcf5ef2aSThomas Huth   uint64_t tv_usec;   /* microsecond */
70fcf5ef2aSThomas Huth } QEMU_PACKED;
71fcf5ef2aSThomas Huth 
72fcf5ef2aSThomas Huth #define GDB_O_RDONLY   0x0
73fcf5ef2aSThomas Huth #define GDB_O_WRONLY   0x1
74fcf5ef2aSThomas Huth #define GDB_O_RDWR     0x2
75fcf5ef2aSThomas Huth #define GDB_O_APPEND   0x8
76fcf5ef2aSThomas Huth #define GDB_O_CREAT  0x200
77fcf5ef2aSThomas Huth #define GDB_O_TRUNC  0x400
78fcf5ef2aSThomas Huth #define GDB_O_EXCL   0x800
79fcf5ef2aSThomas Huth 
80fcf5ef2aSThomas Huth static int translate_openflags(int flags)
81fcf5ef2aSThomas Huth {
82fcf5ef2aSThomas Huth     int hf;
83fcf5ef2aSThomas Huth 
84fcf5ef2aSThomas Huth     if (flags & GDB_O_WRONLY)
85fcf5ef2aSThomas Huth         hf = O_WRONLY;
86fcf5ef2aSThomas Huth     else if (flags & GDB_O_RDWR)
87fcf5ef2aSThomas Huth         hf = O_RDWR;
88fcf5ef2aSThomas Huth     else
89fcf5ef2aSThomas Huth         hf = O_RDONLY;
90fcf5ef2aSThomas Huth 
91fcf5ef2aSThomas Huth     if (flags & GDB_O_APPEND) hf |= O_APPEND;
92fcf5ef2aSThomas Huth     if (flags & GDB_O_CREAT) hf |= O_CREAT;
93fcf5ef2aSThomas Huth     if (flags & GDB_O_TRUNC) hf |= O_TRUNC;
94fcf5ef2aSThomas Huth     if (flags & GDB_O_EXCL) hf |= O_EXCL;
95fcf5ef2aSThomas Huth 
96fcf5ef2aSThomas Huth     return hf;
97fcf5ef2aSThomas Huth }
98fcf5ef2aSThomas Huth 
99fcf5ef2aSThomas Huth static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s)
100fcf5ef2aSThomas Huth {
101fcf5ef2aSThomas Huth     struct m68k_gdb_stat *p;
102fcf5ef2aSThomas Huth 
103fcf5ef2aSThomas Huth     if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(struct m68k_gdb_stat), 0)))
104fcf5ef2aSThomas Huth         /* FIXME - should this return an error code? */
105fcf5ef2aSThomas Huth         return;
106fcf5ef2aSThomas Huth     p->gdb_st_dev = cpu_to_be32(s->st_dev);
107fcf5ef2aSThomas Huth     p->gdb_st_ino = cpu_to_be32(s->st_ino);
108fcf5ef2aSThomas Huth     p->gdb_st_mode = cpu_to_be32(s->st_mode);
109fcf5ef2aSThomas Huth     p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
110fcf5ef2aSThomas Huth     p->gdb_st_uid = cpu_to_be32(s->st_uid);
111fcf5ef2aSThomas Huth     p->gdb_st_gid = cpu_to_be32(s->st_gid);
112fcf5ef2aSThomas Huth     p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
113fcf5ef2aSThomas Huth     p->gdb_st_size = cpu_to_be64(s->st_size);
114fcf5ef2aSThomas Huth #ifdef _WIN32
115fcf5ef2aSThomas Huth     /* Windows stat is missing some fields.  */
116fcf5ef2aSThomas Huth     p->gdb_st_blksize = 0;
117fcf5ef2aSThomas Huth     p->gdb_st_blocks = 0;
118fcf5ef2aSThomas Huth #else
119fcf5ef2aSThomas Huth     p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
120fcf5ef2aSThomas Huth     p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
121fcf5ef2aSThomas Huth #endif
122fcf5ef2aSThomas Huth     p->gdb_st_atime = cpu_to_be32(s->st_atime);
123fcf5ef2aSThomas Huth     p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
124fcf5ef2aSThomas Huth     p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
125fcf5ef2aSThomas Huth     unlock_user(p, addr, sizeof(struct m68k_gdb_stat));
126fcf5ef2aSThomas Huth }
127fcf5ef2aSThomas Huth 
128fcf5ef2aSThomas Huth static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err)
129fcf5ef2aSThomas Huth {
130fcf5ef2aSThomas Huth     target_ulong args = env->dregs[1];
131fcf5ef2aSThomas Huth     if (put_user_u32(ret, args) ||
132fcf5ef2aSThomas Huth         put_user_u32(err, args + 4)) {
133*808d77bcSLucien Murray-Pitts         /*
134*808d77bcSLucien Murray-Pitts          * The m68k semihosting ABI does not provide any way to report this
135fcf5ef2aSThomas Huth          * error to the guest, so the best we can do is log it in qemu.
136fcf5ef2aSThomas Huth          * It is always a guest error not to pass us a valid argument block.
137fcf5ef2aSThomas Huth          */
138fcf5ef2aSThomas Huth         qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
139fcf5ef2aSThomas Huth                       "discarded because argument block not writable\n");
140fcf5ef2aSThomas Huth     }
141fcf5ef2aSThomas Huth }
142fcf5ef2aSThomas Huth 
143fcf5ef2aSThomas Huth static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err)
144fcf5ef2aSThomas Huth {
145fcf5ef2aSThomas Huth     target_ulong args = env->dregs[1];
146fcf5ef2aSThomas Huth     if (put_user_u32(ret >> 32, args) ||
147fcf5ef2aSThomas Huth         put_user_u32(ret, args + 4) ||
148fcf5ef2aSThomas Huth         put_user_u32(err, args + 8)) {
149fcf5ef2aSThomas Huth         /* No way to report this via m68k semihosting ABI; just log it */
150fcf5ef2aSThomas Huth         qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
151fcf5ef2aSThomas Huth                       "discarded because argument block not writable\n");
152fcf5ef2aSThomas Huth     }
153fcf5ef2aSThomas Huth }
154fcf5ef2aSThomas Huth 
155fcf5ef2aSThomas Huth static int m68k_semi_is_fseek;
156fcf5ef2aSThomas Huth 
157fcf5ef2aSThomas Huth static void m68k_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
158fcf5ef2aSThomas Huth {
159fcf5ef2aSThomas Huth     M68kCPU *cpu = M68K_CPU(cs);
160fcf5ef2aSThomas Huth     CPUM68KState *env = &cpu->env;
161fcf5ef2aSThomas Huth 
162fcf5ef2aSThomas Huth     if (m68k_semi_is_fseek) {
163*808d77bcSLucien Murray-Pitts         /*
164*808d77bcSLucien Murray-Pitts          * FIXME: We've already lost the high bits of the fseek
165*808d77bcSLucien Murray-Pitts          * return value.
166*808d77bcSLucien Murray-Pitts          */
167fcf5ef2aSThomas Huth         m68k_semi_return_u64(env, ret, err);
168fcf5ef2aSThomas Huth         m68k_semi_is_fseek = 0;
169fcf5ef2aSThomas Huth     } else {
170fcf5ef2aSThomas Huth         m68k_semi_return_u32(env, ret, err);
171fcf5ef2aSThomas Huth     }
172fcf5ef2aSThomas Huth }
173fcf5ef2aSThomas Huth 
174*808d77bcSLucien Murray-Pitts /*
175*808d77bcSLucien Murray-Pitts  * Read the input value from the argument block; fail the semihosting
176fcf5ef2aSThomas Huth  * call if the memory read fails.
177fcf5ef2aSThomas Huth  */
178fcf5ef2aSThomas Huth #define GET_ARG(n) do {                                 \
179fcf5ef2aSThomas Huth     if (get_user_ual(arg ## n, args + (n) * 4)) {       \
180fcf5ef2aSThomas Huth         result = -1;                                    \
181fcf5ef2aSThomas Huth         errno = EFAULT;                                 \
182fcf5ef2aSThomas Huth         goto failed;                                    \
183fcf5ef2aSThomas Huth     }                                                   \
184fcf5ef2aSThomas Huth } while (0)
185fcf5ef2aSThomas Huth 
186fcf5ef2aSThomas Huth void do_m68k_semihosting(CPUM68KState *env, int nr)
187fcf5ef2aSThomas Huth {
188fcf5ef2aSThomas Huth     uint32_t args;
189fcf5ef2aSThomas Huth     target_ulong arg0, arg1, arg2, arg3;
190fcf5ef2aSThomas Huth     void *p;
191fcf5ef2aSThomas Huth     void *q;
192fcf5ef2aSThomas Huth     uint32_t len;
193fcf5ef2aSThomas Huth     uint32_t result;
194fcf5ef2aSThomas Huth 
195fcf5ef2aSThomas Huth     args = env->dregs[1];
196fcf5ef2aSThomas Huth     switch (nr) {
197fcf5ef2aSThomas Huth     case HOSTED_EXIT:
198fcf5ef2aSThomas Huth         gdb_exit(env, env->dregs[0]);
199fcf5ef2aSThomas Huth         exit(env->dregs[0]);
200fcf5ef2aSThomas Huth     case HOSTED_OPEN:
201fcf5ef2aSThomas Huth         GET_ARG(0);
202fcf5ef2aSThomas Huth         GET_ARG(1);
203fcf5ef2aSThomas Huth         GET_ARG(2);
204fcf5ef2aSThomas Huth         GET_ARG(3);
205fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
206fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", arg0, (int)arg1,
207fcf5ef2aSThomas Huth                            arg2, arg3);
208fcf5ef2aSThomas Huth             return;
209fcf5ef2aSThomas Huth         } else {
210fcf5ef2aSThomas Huth             p = lock_user_string(arg0);
211fcf5ef2aSThomas Huth             if (!p) {
212fcf5ef2aSThomas Huth                 /* FIXME - check error code? */
213fcf5ef2aSThomas Huth                 result = -1;
214fcf5ef2aSThomas Huth             } else {
215fcf5ef2aSThomas Huth                 result = open(p, translate_openflags(arg2), arg3);
216fcf5ef2aSThomas Huth                 unlock_user(p, arg0, 0);
217fcf5ef2aSThomas Huth             }
218fcf5ef2aSThomas Huth         }
219fcf5ef2aSThomas Huth         break;
220fcf5ef2aSThomas Huth     case HOSTED_CLOSE:
221fcf5ef2aSThomas Huth         {
222fcf5ef2aSThomas Huth             /* Ignore attempts to close stdin/out/err.  */
223fcf5ef2aSThomas Huth             GET_ARG(0);
224fcf5ef2aSThomas Huth             int fd = arg0;
225fcf5ef2aSThomas Huth             if (fd > 2) {
226fcf5ef2aSThomas Huth                 if (use_gdb_syscalls()) {
227fcf5ef2aSThomas Huth                     gdb_do_syscall(m68k_semi_cb, "close,%x", arg0);
228fcf5ef2aSThomas Huth                     return;
229fcf5ef2aSThomas Huth                 } else {
230fcf5ef2aSThomas Huth                     result = close(fd);
231fcf5ef2aSThomas Huth                 }
232fcf5ef2aSThomas Huth             } else {
233fcf5ef2aSThomas Huth                 result = 0;
234fcf5ef2aSThomas Huth             }
235fcf5ef2aSThomas Huth             break;
236fcf5ef2aSThomas Huth         }
237fcf5ef2aSThomas Huth     case HOSTED_READ:
238fcf5ef2aSThomas Huth         GET_ARG(0);
239fcf5ef2aSThomas Huth         GET_ARG(1);
240fcf5ef2aSThomas Huth         GET_ARG(2);
241fcf5ef2aSThomas Huth         len = arg2;
242fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
243fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x",
244fcf5ef2aSThomas Huth                            arg0, arg1, len);
245fcf5ef2aSThomas Huth             return;
246fcf5ef2aSThomas Huth         } else {
247fcf5ef2aSThomas Huth             p = lock_user(VERIFY_WRITE, arg1, len, 0);
248fcf5ef2aSThomas Huth             if (!p) {
249fcf5ef2aSThomas Huth                 /* FIXME - check error code? */
250fcf5ef2aSThomas Huth                 result = -1;
251fcf5ef2aSThomas Huth             } else {
252fcf5ef2aSThomas Huth                 result = read(arg0, p, len);
253fcf5ef2aSThomas Huth                 unlock_user(p, arg1, len);
254fcf5ef2aSThomas Huth             }
255fcf5ef2aSThomas Huth         }
256fcf5ef2aSThomas Huth         break;
257fcf5ef2aSThomas Huth     case HOSTED_WRITE:
258fcf5ef2aSThomas Huth         GET_ARG(0);
259fcf5ef2aSThomas Huth         GET_ARG(1);
260fcf5ef2aSThomas Huth         GET_ARG(2);
261fcf5ef2aSThomas Huth         len = arg2;
262fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
263fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x",
264fcf5ef2aSThomas Huth                            arg0, arg1, len);
265fcf5ef2aSThomas Huth             return;
266fcf5ef2aSThomas Huth         } else {
267fcf5ef2aSThomas Huth             p = lock_user(VERIFY_READ, arg1, len, 1);
268fcf5ef2aSThomas Huth             if (!p) {
269fcf5ef2aSThomas Huth                 /* FIXME - check error code? */
270fcf5ef2aSThomas Huth                 result = -1;
271fcf5ef2aSThomas Huth             } else {
272fcf5ef2aSThomas Huth                 result = write(arg0, p, len);
273fcf5ef2aSThomas Huth                 unlock_user(p, arg0, 0);
274fcf5ef2aSThomas Huth             }
275fcf5ef2aSThomas Huth         }
276fcf5ef2aSThomas Huth         break;
277fcf5ef2aSThomas Huth     case HOSTED_LSEEK:
278fcf5ef2aSThomas Huth         {
279fcf5ef2aSThomas Huth             uint64_t off;
280fcf5ef2aSThomas Huth             GET_ARG(0);
281fcf5ef2aSThomas Huth             GET_ARG(1);
282fcf5ef2aSThomas Huth             GET_ARG(2);
283fcf5ef2aSThomas Huth             GET_ARG(3);
284fcf5ef2aSThomas Huth             off = (uint32_t)arg2 | ((uint64_t)arg1 << 32);
285fcf5ef2aSThomas Huth             if (use_gdb_syscalls()) {
286fcf5ef2aSThomas Huth                 m68k_semi_is_fseek = 1;
287fcf5ef2aSThomas Huth                 gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x",
288fcf5ef2aSThomas Huth                                arg0, off, arg3);
289fcf5ef2aSThomas Huth             } else {
290fcf5ef2aSThomas Huth                 off = lseek(arg0, off, arg3);
291fcf5ef2aSThomas Huth                 m68k_semi_return_u64(env, off, errno);
292fcf5ef2aSThomas Huth             }
293fcf5ef2aSThomas Huth             return;
294fcf5ef2aSThomas Huth         }
295fcf5ef2aSThomas Huth     case HOSTED_RENAME:
296fcf5ef2aSThomas Huth         GET_ARG(0);
297fcf5ef2aSThomas Huth         GET_ARG(1);
298fcf5ef2aSThomas Huth         GET_ARG(2);
299fcf5ef2aSThomas Huth         GET_ARG(3);
300fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
301fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "rename,%s,%s",
302fcf5ef2aSThomas Huth                            arg0, (int)arg1, arg2, (int)arg3);
303fcf5ef2aSThomas Huth             return;
304fcf5ef2aSThomas Huth         } else {
305fcf5ef2aSThomas Huth             p = lock_user_string(arg0);
306fcf5ef2aSThomas Huth             q = lock_user_string(arg2);
307fcf5ef2aSThomas Huth             if (!p || !q) {
308fcf5ef2aSThomas Huth                 /* FIXME - check error code? */
309fcf5ef2aSThomas Huth                 result = -1;
310fcf5ef2aSThomas Huth             } else {
311fcf5ef2aSThomas Huth                 result = rename(p, q);
312fcf5ef2aSThomas Huth             }
313fcf5ef2aSThomas Huth             unlock_user(p, arg0, 0);
314fcf5ef2aSThomas Huth             unlock_user(q, arg2, 0);
315fcf5ef2aSThomas Huth         }
316fcf5ef2aSThomas Huth         break;
317fcf5ef2aSThomas Huth     case HOSTED_UNLINK:
318fcf5ef2aSThomas Huth         GET_ARG(0);
319fcf5ef2aSThomas Huth         GET_ARG(1);
320fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
321fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "unlink,%s",
322fcf5ef2aSThomas Huth                            arg0, (int)arg1);
323fcf5ef2aSThomas Huth             return;
324fcf5ef2aSThomas Huth         } else {
325fcf5ef2aSThomas Huth             p = lock_user_string(arg0);
326fcf5ef2aSThomas Huth             if (!p) {
327fcf5ef2aSThomas Huth                 /* FIXME - check error code? */
328fcf5ef2aSThomas Huth                 result = -1;
329fcf5ef2aSThomas Huth             } else {
330fcf5ef2aSThomas Huth                 result = unlink(p);
331fcf5ef2aSThomas Huth                 unlock_user(p, arg0, 0);
332fcf5ef2aSThomas Huth             }
333fcf5ef2aSThomas Huth         }
334fcf5ef2aSThomas Huth         break;
335fcf5ef2aSThomas Huth     case HOSTED_STAT:
336fcf5ef2aSThomas Huth         GET_ARG(0);
337fcf5ef2aSThomas Huth         GET_ARG(1);
338fcf5ef2aSThomas Huth         GET_ARG(2);
339fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
340fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "stat,%s,%x",
341fcf5ef2aSThomas Huth                            arg0, (int)arg1, arg2);
342fcf5ef2aSThomas Huth             return;
343fcf5ef2aSThomas Huth         } else {
344fcf5ef2aSThomas Huth             struct stat s;
345fcf5ef2aSThomas Huth             p = lock_user_string(arg0);
346fcf5ef2aSThomas Huth             if (!p) {
347fcf5ef2aSThomas Huth                 /* FIXME - check error code? */
348fcf5ef2aSThomas Huth                 result = -1;
349fcf5ef2aSThomas Huth             } else {
350fcf5ef2aSThomas Huth                 result = stat(p, &s);
351fcf5ef2aSThomas Huth                 unlock_user(p, arg0, 0);
352fcf5ef2aSThomas Huth             }
353fcf5ef2aSThomas Huth             if (result == 0) {
354fcf5ef2aSThomas Huth                 translate_stat(env, arg2, &s);
355fcf5ef2aSThomas Huth             }
356fcf5ef2aSThomas Huth         }
357fcf5ef2aSThomas Huth         break;
358fcf5ef2aSThomas Huth     case HOSTED_FSTAT:
359fcf5ef2aSThomas Huth         GET_ARG(0);
360fcf5ef2aSThomas Huth         GET_ARG(1);
361fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
362fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x",
363fcf5ef2aSThomas Huth                            arg0, arg1);
364fcf5ef2aSThomas Huth             return;
365fcf5ef2aSThomas Huth         } else {
366fcf5ef2aSThomas Huth             struct stat s;
367fcf5ef2aSThomas Huth             result = fstat(arg0, &s);
368fcf5ef2aSThomas Huth             if (result == 0) {
369fcf5ef2aSThomas Huth                 translate_stat(env, arg1, &s);
370fcf5ef2aSThomas Huth             }
371fcf5ef2aSThomas Huth         }
372fcf5ef2aSThomas Huth         break;
373fcf5ef2aSThomas Huth     case HOSTED_GETTIMEOFDAY:
374fcf5ef2aSThomas Huth         GET_ARG(0);
375fcf5ef2aSThomas Huth         GET_ARG(1);
376fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
377fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x",
378fcf5ef2aSThomas Huth                            arg0, arg1);
379fcf5ef2aSThomas Huth             return;
380fcf5ef2aSThomas Huth         } else {
381fcf5ef2aSThomas Huth             qemu_timeval tv;
382fcf5ef2aSThomas Huth             struct gdb_timeval *p;
383fcf5ef2aSThomas Huth             result = qemu_gettimeofday(&tv);
384fcf5ef2aSThomas Huth             if (result != 0) {
385fcf5ef2aSThomas Huth                 if (!(p = lock_user(VERIFY_WRITE,
386fcf5ef2aSThomas Huth                                     arg0, sizeof(struct gdb_timeval), 0))) {
387fcf5ef2aSThomas Huth                     /* FIXME - check error code? */
388fcf5ef2aSThomas Huth                     result = -1;
389fcf5ef2aSThomas Huth                 } else {
390fcf5ef2aSThomas Huth                     p->tv_sec = cpu_to_be32(tv.tv_sec);
391fcf5ef2aSThomas Huth                     p->tv_usec = cpu_to_be64(tv.tv_usec);
392fcf5ef2aSThomas Huth                     unlock_user(p, arg0, sizeof(struct gdb_timeval));
393fcf5ef2aSThomas Huth                 }
394fcf5ef2aSThomas Huth             }
395fcf5ef2aSThomas Huth         }
396fcf5ef2aSThomas Huth         break;
397fcf5ef2aSThomas Huth     case HOSTED_ISATTY:
398fcf5ef2aSThomas Huth         GET_ARG(0);
399fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
400fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "isatty,%x", arg0);
401fcf5ef2aSThomas Huth             return;
402fcf5ef2aSThomas Huth         } else {
403fcf5ef2aSThomas Huth             result = isatty(arg0);
404fcf5ef2aSThomas Huth         }
405fcf5ef2aSThomas Huth         break;
406fcf5ef2aSThomas Huth     case HOSTED_SYSTEM:
407fcf5ef2aSThomas Huth         GET_ARG(0);
408fcf5ef2aSThomas Huth         GET_ARG(1);
409fcf5ef2aSThomas Huth         if (use_gdb_syscalls()) {
410fcf5ef2aSThomas Huth             gdb_do_syscall(m68k_semi_cb, "system,%s",
411fcf5ef2aSThomas Huth                            arg0, (int)arg1);
412fcf5ef2aSThomas Huth             return;
413fcf5ef2aSThomas Huth         } else {
414fcf5ef2aSThomas Huth             p = lock_user_string(arg0);
415fcf5ef2aSThomas Huth             if (!p) {
416fcf5ef2aSThomas Huth                 /* FIXME - check error code? */
417fcf5ef2aSThomas Huth                 result = -1;
418fcf5ef2aSThomas Huth             } else {
419fcf5ef2aSThomas Huth                 result = system(p);
420fcf5ef2aSThomas Huth                 unlock_user(p, arg0, 0);
421fcf5ef2aSThomas Huth             }
422fcf5ef2aSThomas Huth         }
423fcf5ef2aSThomas Huth         break;
424fcf5ef2aSThomas Huth     case HOSTED_INIT_SIM:
425fcf5ef2aSThomas Huth #if defined(CONFIG_USER_ONLY)
426fcf5ef2aSThomas Huth         {
427a8d92fd8SRichard Henderson         CPUState *cs = env_cpu(env);
428fcf5ef2aSThomas Huth         TaskState *ts = cs->opaque;
429fcf5ef2aSThomas Huth         /* Allocate the heap using sbrk.  */
430fcf5ef2aSThomas Huth         if (!ts->heap_limit) {
431fcf5ef2aSThomas Huth             abi_ulong ret;
432fcf5ef2aSThomas Huth             uint32_t size;
433fcf5ef2aSThomas Huth             uint32_t base;
434fcf5ef2aSThomas Huth 
435fcf5ef2aSThomas Huth             base = do_brk(0);
436fcf5ef2aSThomas Huth             size = SEMIHOSTING_HEAP_SIZE;
437fcf5ef2aSThomas Huth             /* Try a big heap, and reduce the size if that fails.  */
438fcf5ef2aSThomas Huth             for (;;) {
439fcf5ef2aSThomas Huth                 ret = do_brk(base + size);
440fcf5ef2aSThomas Huth                 if (ret >= (base + size)) {
441fcf5ef2aSThomas Huth                     break;
442fcf5ef2aSThomas Huth                 }
443fcf5ef2aSThomas Huth                 size >>= 1;
444fcf5ef2aSThomas Huth             }
445fcf5ef2aSThomas Huth             ts->heap_limit = base + size;
446fcf5ef2aSThomas Huth         }
447*808d77bcSLucien Murray-Pitts         /*
448*808d77bcSLucien Murray-Pitts          * This call may happen before we have writable memory, so return
449*808d77bcSLucien Murray-Pitts          * values directly in registers.
450*808d77bcSLucien Murray-Pitts          */
451fcf5ef2aSThomas Huth         env->dregs[1] = ts->heap_limit;
452fcf5ef2aSThomas Huth         env->aregs[7] = ts->stack_base;
453fcf5ef2aSThomas Huth         }
454fcf5ef2aSThomas Huth #else
455*808d77bcSLucien Murray-Pitts         /*
456*808d77bcSLucien Murray-Pitts          * FIXME: This is wrong for boards where RAM does not start at
457*808d77bcSLucien Murray-Pitts          * address zero.
458*808d77bcSLucien Murray-Pitts          */
459fcf5ef2aSThomas Huth         env->dregs[1] = ram_size;
460fcf5ef2aSThomas Huth         env->aregs[7] = ram_size;
461fcf5ef2aSThomas Huth #endif
462fcf5ef2aSThomas Huth         return;
463fcf5ef2aSThomas Huth     default:
464a8d92fd8SRichard Henderson         cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr);
465fcf5ef2aSThomas Huth         result = 0;
466fcf5ef2aSThomas Huth     }
467fcf5ef2aSThomas Huth failed:
468fcf5ef2aSThomas Huth     m68k_semi_return_u32(env, result, errno);
469fcf5ef2aSThomas Huth }
470