xref: /illumos-kvm-cmd/m68k-semi.c (revision 68396ea9)
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 "gdbstub.h"
37 #include "softmmu-semi.h"
38 #endif
39 #include "sysemu.h"
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 
translate_openflags(int flags)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 
translate_stat(CPUState * env,target_ulong addr,struct stat * s)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 
m68k_semi_cb(CPUState * env,target_ulong ret,target_ulong err)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))
do_m68k_semihosting(CPUM68KState * env,int nr)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         gdb_exit(env, env->dregs[0]);
176         exit(env->dregs[0]);
177     case HOSTED_OPEN:
178         if (use_gdb_syscalls()) {
179             gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", ARG(0), (int)ARG(1),
180                            ARG(2), ARG(3));
181             return;
182         } else {
183             if (!(p = lock_user_string(ARG(0)))) {
184                 /* FIXME - check error code? */
185                 result = -1;
186             } else {
187                 result = open(p, translate_openflags(ARG(2)), ARG(3));
188                 unlock_user(p, ARG(0), 0);
189             }
190         }
191         break;
192     case HOSTED_CLOSE:
193         {
194             /* Ignore attempts to close stdin/out/err.  */
195             int fd = ARG(0);
196             if (fd > 2) {
197                 if (use_gdb_syscalls()) {
198                     gdb_do_syscall(m68k_semi_cb, "close,%x", ARG(0));
199                     return;
200                 } else {
201                     result = close(fd);
202                 }
203             } else {
204                 result = 0;
205             }
206             break;
207         }
208     case HOSTED_READ:
209         len = ARG(2);
210         if (use_gdb_syscalls()) {
211             gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x",
212                            ARG(0), ARG(1), len);
213             return;
214         } else {
215             if (!(p = lock_user(VERIFY_WRITE, ARG(1), len, 0))) {
216                 /* FIXME - check error code? */
217                 result = -1;
218             } else {
219                 result = read(ARG(0), p, len);
220                 unlock_user(p, ARG(1), len);
221             }
222         }
223         break;
224     case HOSTED_WRITE:
225         len = ARG(2);
226         if (use_gdb_syscalls()) {
227             gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x",
228                            ARG(0), ARG(1), len);
229             return;
230         } else {
231             if (!(p = lock_user(VERIFY_READ, ARG(1), len, 1))) {
232                 /* FIXME - check error code? */
233                 result = -1;
234             } else {
235                 result = write(ARG(0), p, len);
236                 unlock_user(p, ARG(0), 0);
237             }
238         }
239         break;
240     case HOSTED_LSEEK:
241         {
242             uint64_t off;
243             off = (uint32_t)ARG(2) | ((uint64_t)ARG(1) << 32);
244             if (use_gdb_syscalls()) {
245                 m68k_semi_is_fseek = 1;
246                 gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x",
247                                ARG(0), off, ARG(3));
248             } else {
249                 off = lseek(ARG(0), off, ARG(3));
250                 /* FIXME - handle put_user() failure */
251                 put_user_u32(off >> 32, args);
252                 put_user_u32(off, args + 4);
253                 put_user_u32(errno, args + 8);
254             }
255             return;
256         }
257     case HOSTED_RENAME:
258         if (use_gdb_syscalls()) {
259             gdb_do_syscall(m68k_semi_cb, "rename,%s,%s",
260                            ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
261             return;
262         } else {
263             p = lock_user_string(ARG(0));
264             q = lock_user_string(ARG(2));
265             if (!p || !q) {
266                 /* FIXME - check error code? */
267                 result = -1;
268             } else {
269                 result = rename(p, q);
270             }
271             unlock_user(p, ARG(0), 0);
272             unlock_user(q, ARG(2), 0);
273         }
274         break;
275     case HOSTED_UNLINK:
276         if (use_gdb_syscalls()) {
277             gdb_do_syscall(m68k_semi_cb, "unlink,%s",
278                            ARG(0), (int)ARG(1));
279             return;
280         } else {
281             if (!(p = lock_user_string(ARG(0)))) {
282                 /* FIXME - check error code? */
283                 result = -1;
284             } else {
285                 result = unlink(p);
286                 unlock_user(p, ARG(0), 0);
287             }
288         }
289         break;
290     case HOSTED_STAT:
291         if (use_gdb_syscalls()) {
292             gdb_do_syscall(m68k_semi_cb, "stat,%s,%x",
293                            ARG(0), (int)ARG(1), ARG(2));
294             return;
295         } else {
296             struct stat s;
297             if (!(p = lock_user_string(ARG(0)))) {
298                 /* FIXME - check error code? */
299                 result = -1;
300             } else {
301                 result = stat(p, &s);
302                 unlock_user(p, ARG(0), 0);
303             }
304             if (result == 0) {
305                 translate_stat(env, ARG(2), &s);
306             }
307         }
308         break;
309     case HOSTED_FSTAT:
310         if (use_gdb_syscalls()) {
311             gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x",
312                            ARG(0), ARG(1));
313             return;
314         } else {
315             struct stat s;
316             result = fstat(ARG(0), &s);
317             if (result == 0) {
318                 translate_stat(env, ARG(1), &s);
319             }
320         }
321         break;
322     case HOSTED_GETTIMEOFDAY:
323         if (use_gdb_syscalls()) {
324             gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x",
325                            ARG(0), ARG(1));
326             return;
327         } else {
328             qemu_timeval tv;
329             struct gdb_timeval *p;
330             result = qemu_gettimeofday(&tv);
331             if (result != 0) {
332                 if (!(p = lock_user(VERIFY_WRITE,
333                                     ARG(0), sizeof(struct gdb_timeval), 0))) {
334                     /* FIXME - check error code? */
335                     result = -1;
336                 } else {
337                     p->tv_sec = cpu_to_be32(tv.tv_sec);
338                     p->tv_usec = cpu_to_be64(tv.tv_usec);
339                     unlock_user(p, ARG(0), sizeof(struct gdb_timeval));
340                 }
341             }
342         }
343         break;
344     case HOSTED_ISATTY:
345         if (use_gdb_syscalls()) {
346             gdb_do_syscall(m68k_semi_cb, "isatty,%x", ARG(0));
347             return;
348         } else {
349             result = isatty(ARG(0));
350         }
351         break;
352     case HOSTED_SYSTEM:
353         if (use_gdb_syscalls()) {
354             gdb_do_syscall(m68k_semi_cb, "system,%s",
355                            ARG(0), (int)ARG(1));
356             return;
357         } else {
358             if (!(p = lock_user_string(ARG(0)))) {
359                 /* FIXME - check error code? */
360                 result = -1;
361             } else {
362                 result = system(p);
363                 unlock_user(p, ARG(0), 0);
364             }
365         }
366         break;
367     case HOSTED_INIT_SIM:
368 #if defined(CONFIG_USER_ONLY)
369         {
370         TaskState *ts = env->opaque;
371         /* Allocate the heap using sbrk.  */
372         if (!ts->heap_limit) {
373             long ret;
374             uint32_t size;
375             uint32_t base;
376 
377             base = do_brk(0);
378             size = SEMIHOSTING_HEAP_SIZE;
379             /* Try a big heap, and reduce the size if that fails.  */
380             for (;;) {
381                 ret = do_brk(base + size);
382                 if (ret != -1)
383                     break;
384                 size >>= 1;
385             }
386             ts->heap_limit = base + size;
387         }
388         /* This call may happen before we have writable memory, so return
389            values directly in registers.  */
390         env->dregs[1] = ts->heap_limit;
391         env->aregs[7] = ts->stack_base;
392         }
393 #else
394         /* FIXME: This is wrong for boards where RAM does not start at
395            address zero.  */
396         env->dregs[1] = ram_size;
397         env->aregs[7] = ram_size;
398 #endif
399         return;
400     default:
401         cpu_abort(env, "Unsupported semihosting syscall %d\n", nr);
402         result = 0;
403     }
404     /* FIXME - handle put_user() failure */
405     put_user_u32(result, args);
406     put_user_u32(errno, args + 4);
407 }
408