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