symbian-qemu-0.9.1-12/qemu-symbian-svp/ppc-semi.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  *  PowerPC Semihosting syscall interface.
       
     3  *  Implements a subset of NetBSD syscalls.
       
     4  *
       
     5  *  Copyright (c) 2007 CodeSourcery.
       
     6  *
       
     7  *  This program is free software; you can redistribute it and/or modify
       
     8  *  it under the terms of the GNU General Public License as published by
       
     9  *  the Free Software Foundation; either version 2 of the License, or
       
    10  *  (at your option) any later version.
       
    11  *
       
    12  *  This program is distributed in the hope that it will be useful,
       
    13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    15  *  GNU General Public License for more details.
       
    16  *
       
    17  *  You should have received a copy of the GNU General Public License
       
    18  *  along with this program; if not, write to the Free Software
       
    19  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
       
    20  */
       
    21 
       
    22 #include <sys/types.h>
       
    23 #include <sys/stat.h>
       
    24 #include <errno.h>
       
    25 #include <fcntl.h>
       
    26 #include <unistd.h>
       
    27 #include <stdlib.h>
       
    28 #include <stdio.h>
       
    29 #include <sys/time.h>
       
    30 #include <time.h>
       
    31 
       
    32 #include "cpu.h"
       
    33 #include "qemu-common.h"
       
    34 #include "sysemu.h"
       
    35 #include "gdbstub.h"
       
    36 #include "softmmu-semi.h"
       
    37 
       
    38 #define HOSTED_EXIT     1
       
    39 #define HOSTED_READ     3
       
    40 #define HOSTED_WRITE    4
       
    41 #define HOSTED_OPEN     5
       
    42 #define HOSTED_CLOSE    6
       
    43 #define HOSTED_UNLINK	10
       
    44 #define HOSTED_BRK      17
       
    45 #define HOSTED_ACCESS   33
       
    46 #define HOSTED_DUP      41
       
    47 /* This is not quite right: 54 is ioctl, but the only thing we use ioctl
       
    48    for right now is to implement isatty.  Their argument lists are
       
    49    compatible as far as the first argument goes, so we just fake it and
       
    50    declare ioctl's syscall number as isatty's.  */
       
    51 #define HOSTED_ISATTY	54
       
    52 #define HOSTED_SYSTEM	77      /* obsolete vlimit */
       
    53 #define HOSTED_GETTIMEOFDAY 116
       
    54 #define HOSTED_RENAME	128
       
    55 #define HOSTED_LSEEK    199
       
    56 #define HOSTED_STAT	387
       
    57 #define HOSTED_FSTAT	388
       
    58 
       
    59 typedef uint32_t gdb_mode_t;
       
    60 typedef uint32_t gdb_time_t;
       
    61 
       
    62 struct ppc_gdb_stat {
       
    63   uint32_t    gdb_st_dev;     /* device */
       
    64   uint32_t    gdb_st_ino;     /* inode */
       
    65   gdb_mode_t  gdb_st_mode;    /* protection */
       
    66   uint32_t    gdb_st_nlink;   /* number of hard links */
       
    67   uint32_t    gdb_st_uid;     /* user ID of owner */
       
    68   uint32_t    gdb_st_gid;     /* group ID of owner */
       
    69   uint32_t    gdb_st_rdev;    /* device type (if inode device) */
       
    70   uint64_t    gdb_st_size;    /* total size, in bytes */
       
    71   uint64_t    gdb_st_blksize; /* blocksize for filesystem I/O */
       
    72   uint64_t    gdb_st_blocks;  /* number of blocks allocated */
       
    73   gdb_time_t  gdb_st_atime;   /* time of last access */
       
    74   gdb_time_t  gdb_st_mtime;   /* time of last modification */
       
    75   gdb_time_t  gdb_st_ctime;   /* time of last change */
       
    76 } __attribute__((packed));
       
    77 
       
    78 struct ppc_netbsd_stat {
       
    79   uint32_t nbsd_st_dev;         /* device */
       
    80   uint32_t nbsd_st_mode;        /* protection */
       
    81   uint64_t nbsd_st_ino;         /* inode */
       
    82   uint32_t nbsd_st_nlink;       /* number of hard links */
       
    83   uint32_t nbsd_st_uid;         /* user ID of owner */
       
    84   uint32_t nbsd_st_gid;         /* group ID of owner */
       
    85   uint32_t nbsd_st_rdev;        /* device type */
       
    86 
       
    87   int32_t nbsd_st_atime;        /* time of last access */
       
    88   int32_t nbsd_st_atimensec;
       
    89   int32_t nbsd_st_mtime;        /* time of last modification */
       
    90   int32_t nbsd_st_mtimensec;
       
    91   int32_t nbsd_st_ctime;        /* time of last change */
       
    92   int32_t nbsd_st_ctimensec;
       
    93 
       
    94   uint64_t nbsd_st_size;        /* total size, in bytes */
       
    95   int64_t nbsd_st_blocks;       /* number of blocks allocated */
       
    96   uint32_t nbsd_st_blksize;      /* blocksize for filesystem I/O */
       
    97   uint32_t nbsd_st_flags;       /* user-defined flags */
       
    98   uint32_t nbsd_st_gen;         /* filesystem generation number */
       
    99   uint32_t nbsd_st_spare[2];
       
   100 };
       
   101 
       
   102 struct gdb_timeval {
       
   103   gdb_time_t tv_sec;  /* second */
       
   104   uint64_t tv_usec;   /* microsecond */
       
   105 } __attribute__((packed));
       
   106 
       
   107 #define TARGET_O_RDONLY   0
       
   108 #define TARGET_O_WRONLY   1
       
   109 #define TARGET_O_RDWR     2
       
   110 #define TARGET_O_ACCMODE  3
       
   111 #define TARGET_O_APPEND   0x0008
       
   112 #define TARGET_O_CREAT    0x0200
       
   113 #define TARGET_O_TRUNC    0x0400
       
   114 #define TARGET_O_EXCL     0x0800
       
   115 
       
   116 #define GDB_O_RDONLY   0x0
       
   117 #define GDB_O_WRONLY   0x1
       
   118 #define GDB_O_RDWR     0x2
       
   119 #define GDB_O_APPEND   0x8
       
   120 #define GDB_O_CREAT  0x200
       
   121 #define GDB_O_TRUNC  0x400
       
   122 #define GDB_O_EXCL   0x800
       
   123 
       
   124 static int translate_openflags_gdb(int flags)
       
   125 {
       
   126     int gf;
       
   127 
       
   128     if ((flags & TARGET_O_ACCMODE) == TARGET_O_WRONLY)
       
   129         gf = GDB_O_WRONLY;
       
   130     else if ((flags & TARGET_O_ACCMODE) == TARGET_O_RDWR)
       
   131         gf = GDB_O_RDWR;
       
   132     else
       
   133         gf = GDB_O_RDONLY;
       
   134 
       
   135     if (flags & TARGET_O_APPEND) gf |= GDB_O_APPEND;
       
   136     if (flags & TARGET_O_CREAT) gf |= GDB_O_CREAT;
       
   137     if (flags & TARGET_O_TRUNC) gf |= GDB_O_TRUNC;
       
   138     if (flags & TARGET_O_EXCL) gf |= GDB_O_EXCL;
       
   139 
       
   140     return gf;
       
   141 }
       
   142 
       
   143 static int translate_openflags_host(int flags)
       
   144 {
       
   145     int hf;
       
   146 
       
   147     if ((flags & TARGET_O_ACCMODE) == TARGET_O_WRONLY)
       
   148         hf = O_WRONLY;
       
   149     else if ((flags & TARGET_O_ACCMODE) == TARGET_O_RDWR)
       
   150         hf = O_RDWR;
       
   151     else
       
   152         hf = O_RDONLY;
       
   153 
       
   154     if (flags & TARGET_O_APPEND) hf |= O_APPEND;
       
   155     if (flags & TARGET_O_CREAT) hf |= O_CREAT;
       
   156     if (flags & TARGET_O_TRUNC) hf |= O_TRUNC;
       
   157     if (flags & TARGET_O_EXCL) hf |= O_EXCL;
       
   158 
       
   159     return hf;
       
   160 }
       
   161 
       
   162 static void translate_to_netbsd_stat(CPUState *env, target_ulong addr,
       
   163                                      struct stat *s, target_ulong saddr)
       
   164 {
       
   165     struct ppc_netbsd_stat *p;
       
   166 
       
   167     if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(*p), 0)))
       
   168         /* FIXME - should this return an error code? */
       
   169         return;
       
   170 
       
   171     if (s) {
       
   172       p->nbsd_st_dev = cpu_to_be32(s->st_dev);
       
   173       p->nbsd_st_ino = cpu_to_be64(s->st_ino);
       
   174       p->nbsd_st_mode = cpu_to_be32(s->st_mode);
       
   175       p->nbsd_st_nlink = cpu_to_be32(s->st_nlink);
       
   176       p->nbsd_st_uid = cpu_to_be32(s->st_uid);
       
   177       p->nbsd_st_gid = cpu_to_be32(s->st_gid);
       
   178       p->nbsd_st_rdev = cpu_to_be32(s->st_rdev);
       
   179       p->nbsd_st_size = cpu_to_be64(s->st_size);
       
   180 #ifdef _WIN32
       
   181       /* Windows stat is missing some fields.  */
       
   182       p->nbsd_st_blksize = 0;
       
   183       p->nbsd_st_blocks = 0;
       
   184 #else
       
   185       p->nbsd_st_blksize = cpu_to_be32(s->st_blksize);
       
   186       p->nbsd_st_blocks = cpu_to_be64(s->st_blocks);
       
   187 #endif
       
   188       p->nbsd_st_atime = cpu_to_be32(s->st_atime);
       
   189       p->nbsd_st_mtime = cpu_to_be32(s->st_mtime);
       
   190       p->nbsd_st_ctime = cpu_to_be32(s->st_ctime);
       
   191     } else {
       
   192       struct ppc_gdb_stat *g;
       
   193 
       
   194       if (g = lock_user (VERIFY_READ, saddr, sizeof(*g), 1)) {
       
   195         p->nbsd_st_dev = cpu_to_be32(g->gdb_st_dev);
       
   196         p->nbsd_st_ino = cpu_to_be64(g->gdb_st_ino);
       
   197         p->nbsd_st_mode = cpu_to_be32(g->gdb_st_mode);
       
   198         p->nbsd_st_nlink = cpu_to_be32(g->gdb_st_nlink);
       
   199         p->nbsd_st_uid = cpu_to_be32(g->gdb_st_uid);
       
   200         p->nbsd_st_gid = cpu_to_be32(g->gdb_st_gid);
       
   201         p->nbsd_st_rdev = cpu_to_be32(g->gdb_st_rdev);
       
   202         p->nbsd_st_size = cpu_to_be64(g->gdb_st_size);
       
   203         p->nbsd_st_blksize = cpu_to_be32(g->gdb_st_blksize);
       
   204         p->nbsd_st_blocks = cpu_to_be64(g->gdb_st_blocks);
       
   205         p->nbsd_st_atime = cpu_to_be32(g->gdb_st_atime);
       
   206         p->nbsd_st_mtime = cpu_to_be32(g->gdb_st_mtime);
       
   207         p->nbsd_st_ctime = cpu_to_be32(g->gdb_st_ctime);
       
   208 
       
   209         unlock_user (g, saddr, 0);
       
   210       }
       
   211     }
       
   212 
       
   213     unlock_user(p, addr, sizeof(*p));
       
   214 }
       
   215 
       
   216 #define ARG(n) env->gpr[3 + (n)]
       
   217 
       
   218 static void ppc_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
       
   219 {
       
   220     if (ret == -1) {
       
   221         env->gpr[3] = errno;
       
   222         env->crf[0] |= 1;
       
   223     } else {
       
   224         env->gpr[3] = ret;
       
   225         env->crf[0] &= ~1;
       
   226     }
       
   227 }
       
   228 
       
   229 static void ppc_stat_cb(CPUState *env, target_ulong ret, target_ulong err)
       
   230 {
       
   231   /* Do this before we overwrite r3 with the return value.  */
       
   232   if (ret != -1) {
       
   233     target_ulong buffer = env->gpr[1] - sizeof(struct ppc_gdb_stat);
       
   234     translate_to_netbsd_stat (env, ARG(1), NULL, buffer);
       
   235   }
       
   236   ppc_semi_cb(env, ret, err);
       
   237 }
       
   238 
       
   239 static void ppc_isatty_cb(CPUState *env, target_ulong ret, target_ulong err)
       
   240 {
       
   241   /* isatty returns 0 or 1; translate that to system call-ish conventions.
       
   242      Don't bother propagating errno.  */
       
   243   env->gpr[3] = ret ? 0 : -1;
       
   244 }
       
   245 
       
   246 void do_ppc_semihosting(CPUPPCState *env)
       
   247 {
       
   248     int nr;
       
   249     void *p, *q;
       
   250     uint32_t len;
       
   251     uint32_t result;
       
   252 
       
   253     nr = env->gpr[0];
       
   254     switch (nr) {
       
   255     case HOSTED_EXIT:
       
   256         /* FIXME: ideally we want to inform gdb about program
       
   257 	   exit whenever gdb is connected, even if syscalls
       
   258 	   are not handled by gdb.  */
       
   259         if (use_gdb_syscalls())
       
   260             gdb_exit(env, ARG(0));
       
   261         exit(ARG(0));
       
   262     case HOSTED_OPEN:
       
   263         if (use_gdb_syscalls()) {
       
   264             gdb_do_syscall(ppc_semi_cb, "open,%s,%x,%x",
       
   265                            ARG(0), target_strlen(ARG(0)),
       
   266                            translate_openflags_gdb(ARG(1)), ARG(2));
       
   267             return;
       
   268         } else {
       
   269             if (!(p = lock_user_string(ARG(0)))) {
       
   270                 /* FIXME - check error code? */
       
   271                 result = -1;
       
   272             } else {
       
   273 	        result = uninterrupted_open(p, translate_openflags_host(ARG(1)), ARG(2));
       
   274                 unlock_user(p, ARG(0), 0);
       
   275             }
       
   276         }
       
   277         break;
       
   278     case HOSTED_CLOSE:
       
   279         {
       
   280             /* Ignore attempts to close stdin/out/err.  */
       
   281             int fd = ARG(0);
       
   282             if (fd > 2) {
       
   283                 if (use_gdb_syscalls()) {
       
   284                     gdb_do_syscall(ppc_semi_cb, "close,%x", ARG(0));
       
   285                     return;
       
   286                 } else {
       
   287 		    result = uninterrupted_close(fd);
       
   288                 }
       
   289             } else {
       
   290                 result = 0;
       
   291             }
       
   292             break;
       
   293         }
       
   294     case HOSTED_READ:
       
   295         len = ARG(2);
       
   296         if (use_gdb_syscalls()) {
       
   297             gdb_do_syscall(ppc_semi_cb, "read,%x,%x,%x",
       
   298                            ARG(0), ARG(1), len);
       
   299             return;
       
   300         } else {
       
   301             if (!(p = lock_user(VERIFY_WRITE, ARG(1), len, 0))) {
       
   302                 /* FIXME - check error code? */
       
   303                 result = -1;
       
   304             } else {
       
   305 	        result = uninterrupted_read(ARG(0), p, len);
       
   306                 unlock_user(p, ARG(1), len);
       
   307             }
       
   308         }
       
   309         break;
       
   310     case HOSTED_WRITE:
       
   311         len = ARG(2);
       
   312         if (use_gdb_syscalls()) {
       
   313             gdb_do_syscall(ppc_semi_cb, "write,%x,%x,%x",
       
   314                            ARG(0), ARG(1), len);
       
   315             return;
       
   316         } else {
       
   317             if (!(p = lock_user(VERIFY_READ, ARG(1), len, 1))) {
       
   318                 /* FIXME - check error code? */
       
   319                 result = -1;
       
   320             } else {
       
   321 	        result = uninterrupted_write(ARG(0), p, len);
       
   322                 unlock_user(p, ARG(0), 0);
       
   323             }
       
   324         }
       
   325         break;
       
   326     case HOSTED_UNLINK:
       
   327       if (use_gdb_syscalls()) {
       
   328         gdb_do_syscall(ppc_semi_cb, "unlink,%s",
       
   329                        ARG(0), (int)ARG(1));
       
   330         return;
       
   331       } else {
       
   332         if (!(p = lock_user_string(ARG(0)))) {
       
   333           /* FIXME - check error code? */
       
   334           result = -1;
       
   335         } else {
       
   336           result = unlink(p);
       
   337           unlock_user(p, ARG(0), 0);
       
   338         }
       
   339       }
       
   340       break;
       
   341     case HOSTED_BRK:
       
   342         /* Nasty hack: use sp -1M as heap limit. */
       
   343         if (ARG(0) < env->gpr[1] - 0x100000) {
       
   344             result = 0;
       
   345         } else {
       
   346             result = -1;
       
   347             errno = ENOMEM;
       
   348         }
       
   349         break;
       
   350     case HOSTED_LSEEK:
       
   351         {
       
   352           uint64_t off;
       
   353           off = (uint32_t)ARG(2) | ((uint64_t)ARG(1) << 32);
       
   354           if (use_gdb_syscalls()) {
       
   355             gdb_do_syscall(ppc_semi_cb, "lseek,%x,%lx,%x",
       
   356                            ARG(0), off, ARG(3));
       
   357             return;
       
   358           } else {
       
   359             off = lseek(ARG(0), off, ARG(3));
       
   360             if (off == (uint64_t) -1) {
       
   361               result = -1;
       
   362             } else {
       
   363               env->gpr[3] = off >> 32;
       
   364               env->gpr[4] = (uint32_t) off;
       
   365               env->crf[0] &= ~1;
       
   366               return;
       
   367             }
       
   368           }
       
   369         }
       
   370         break;
       
   371     case HOSTED_STAT:
       
   372       if (use_gdb_syscalls()) {
       
   373         target_ulong buffer = env->gpr[1] - sizeof (struct ppc_gdb_stat);
       
   374         gdb_do_syscall(ppc_stat_cb, "stat,%s,%x", ARG(0), buffer);
       
   375         return;
       
   376       } else {
       
   377         struct stat s;
       
   378         if (!(p = lock_user_string(ARG(0)))) {
       
   379           /* FIXME - check error code? */
       
   380           result = -1;
       
   381         } else {
       
   382           result = stat(p, &s);
       
   383           unlock_user(p, ARG(0), 0);
       
   384         }
       
   385         if (result == 0) {
       
   386           translate_to_netbsd_stat(env, ARG(1), &s, 0);
       
   387         }
       
   388       }
       
   389       break;
       
   390     case HOSTED_FSTAT:
       
   391       if (use_gdb_syscalls()) {
       
   392         target_ulong buffer = env->gpr[1] - sizeof (struct ppc_gdb_stat);
       
   393         gdb_do_syscall(ppc_stat_cb, "fstat,%x,%x", ARG(0), buffer);
       
   394         return;
       
   395       } else {
       
   396         struct stat s;
       
   397         result = fstat(ARG(0), &s);
       
   398         if (result == 0) {
       
   399           translate_to_netbsd_stat(env, ARG(1), &s, 0);
       
   400         }
       
   401       }
       
   402       break;
       
   403     case HOSTED_RENAME:
       
   404       if (use_gdb_syscalls()) {
       
   405         gdb_do_syscall(ppc_semi_cb, "rename,%s,%s",
       
   406                        ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
       
   407         return;
       
   408       } else {
       
   409         p = lock_user_string(ARG(0));
       
   410         q = lock_user_string(ARG(2));
       
   411         if (!p || !q) {
       
   412           /* FIXME - check error code? */
       
   413           result = -1;
       
   414         } else {
       
   415           result = rename(p, q);
       
   416         }
       
   417         unlock_user(p, ARG(0), 0);
       
   418         unlock_user(q, ARG(2), 0);
       
   419       }
       
   420       break;
       
   421     case HOSTED_ISATTY:
       
   422         if (use_gdb_syscalls()) {
       
   423             gdb_do_syscall(ppc_isatty_cb, "isatty,%x", ARG(0));
       
   424             return;
       
   425         } else {
       
   426             /* Don't bother propagating errno.  */
       
   427             env->gpr[3] = isatty(ARG(0)) ? 0 : -1;
       
   428             return;
       
   429         }
       
   430         break;
       
   431     case HOSTED_SYSTEM:
       
   432         if (use_gdb_syscalls()) {
       
   433             gdb_do_syscall(ppc_semi_cb, "system,%s",
       
   434                            ARG(0), (int)ARG(1));
       
   435             return;
       
   436         } else {
       
   437             if (!(p = lock_user_string(ARG(0)))) {
       
   438                 /* FIXME - check error code? */
       
   439                 result = -1;
       
   440             } else {
       
   441                 result = system(p);
       
   442                 unlock_user(p, ARG(0), 0);
       
   443             }
       
   444         }
       
   445         break;
       
   446     case HOSTED_GETTIMEOFDAY:
       
   447         result = -1;
       
   448         errno = ENOSYS;
       
   449 #ifdef FIXME
       
   450         if (use_gdb_syscalls()) {
       
   451             gdb_do_syscall(ppc_semi_cb, "gettimeofday,%x,%x",
       
   452                            ARG(0), ARG(1));
       
   453             return;
       
   454         } else {
       
   455             qemu_timeval tv;
       
   456             struct gdb_timeval *p;
       
   457             result = qemu_gettimeofday(&tv);
       
   458             if (result != 0) {
       
   459                 if (!(p = lock_user(VERIFY_WRITE,
       
   460                                     ARG(0), sizeof(struct gdb_timeval), 0))) {
       
   461                     /* FIXME - check error code? */
       
   462                     result = -1;
       
   463                 } else {
       
   464                     p->tv_sec = cpu_to_be32(tv.tv_sec);
       
   465                     p->tv_usec = cpu_to_be64(tv.tv_usec);
       
   466                     unlock_user(p, ARG(0), sizeof(struct gdb_timeval));
       
   467                 }
       
   468             }
       
   469         }
       
   470 #endif
       
   471         break;
       
   472     default:
       
   473         cpu_abort(env, "Unsupported semihosting syscall %d\n", nr);
       
   474         result = 0;
       
   475     }
       
   476     if (result == -1) {
       
   477         env->gpr[3] = errno;
       
   478         env->crf[0] |= 1;
       
   479     } else {
       
   480         env->gpr[3] = result;
       
   481         env->crf[0] &= ~1;
       
   482     }
       
   483 }