symbian-qemu-0.9.1-12/qemu-symbian-svp/arm-semi.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  *  Arm "Angel" semihosting syscalls
       
     3  *
       
     4  *  Copyright (c) 2005, 2007 CodeSourcery.
       
     5  *  Written by Paul Brook.
       
     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 <fcntl.h>
       
    25 #include <unistd.h>
       
    26 #include <stdlib.h>
       
    27 #include <stdio.h>
       
    28 #include <time.h>
       
    29 
       
    30 #include "cpu.h"
       
    31 #ifdef CONFIG_USER_ONLY
       
    32 #include "qemu.h"
       
    33 
       
    34 #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
       
    35 #else
       
    36 #include "qemu-common.h"
       
    37 #include "sysemu.h"
       
    38 #include "gdbstub.h"
       
    39 #endif
       
    40 
       
    41 #define SYS_OPEN        0x01
       
    42 #define SYS_CLOSE       0x02
       
    43 #define SYS_WRITEC      0x03
       
    44 #define SYS_WRITE0      0x04
       
    45 #define SYS_WRITE       0x05
       
    46 #define SYS_READ        0x06
       
    47 #define SYS_READC       0x07
       
    48 #define SYS_ISTTY       0x09
       
    49 #define SYS_SEEK        0x0a
       
    50 #define SYS_FLEN        0x0c
       
    51 #define SYS_TMPNAM      0x0d
       
    52 #define SYS_REMOVE      0x0e
       
    53 #define SYS_RENAME      0x0f
       
    54 #define SYS_CLOCK       0x10
       
    55 #define SYS_TIME        0x11
       
    56 #define SYS_SYSTEM      0x12
       
    57 #define SYS_ERRNO       0x13
       
    58 #define SYS_GET_CMDLINE 0x15
       
    59 #define SYS_HEAPINFO    0x16
       
    60 #define SYS_EXIT        0x18
       
    61 
       
    62 #ifndef O_BINARY
       
    63 #define O_BINARY 0
       
    64 #endif
       
    65 
       
    66 #define GDB_O_RDONLY  0x000
       
    67 #define GDB_O_WRONLY  0x001
       
    68 #define GDB_O_RDWR    0x002
       
    69 #define GDB_O_APPEND  0x008
       
    70 #define GDB_O_CREAT   0x200
       
    71 #define GDB_O_TRUNC   0x400
       
    72 #define GDB_O_BINARY  0
       
    73 
       
    74 static int gdb_open_modeflags[12] = {
       
    75     GDB_O_RDONLY,
       
    76     GDB_O_RDONLY | GDB_O_BINARY,
       
    77     GDB_O_RDWR,
       
    78     GDB_O_RDWR | GDB_O_BINARY,
       
    79     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
       
    80     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
       
    81     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
       
    82     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
       
    83     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
       
    84     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
       
    85     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
       
    86     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
       
    87 };
       
    88 
       
    89 static int open_modeflags[12] = {
       
    90     O_RDONLY,
       
    91     O_RDONLY | O_BINARY,
       
    92     O_RDWR,
       
    93     O_RDWR | O_BINARY,
       
    94     O_WRONLY | O_CREAT | O_TRUNC,
       
    95     O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
       
    96     O_RDWR | O_CREAT | O_TRUNC,
       
    97     O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
       
    98     O_WRONLY | O_CREAT | O_APPEND,
       
    99     O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
       
   100     O_RDWR | O_CREAT | O_APPEND,
       
   101     O_RDWR | O_CREAT | O_APPEND | O_BINARY
       
   102 };
       
   103 
       
   104 #ifdef CONFIG_USER_ONLY
       
   105 static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
       
   106 {
       
   107     if (code == (uint32_t)-1)
       
   108         ts->swi_errno = errno;
       
   109     return code;
       
   110 }
       
   111 #else
       
   112 static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
       
   113 {
       
   114     return code;
       
   115 }
       
   116 
       
   117 #include "softmmu-semi.h"
       
   118 #endif
       
   119 
       
   120 static target_ulong arm_semi_syscall_len;
       
   121 
       
   122 #if !defined(CONFIG_USER_ONLY)
       
   123 static target_ulong syscall_err;
       
   124 #endif
       
   125 
       
   126 static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
       
   127 {
       
   128 #ifdef CONFIG_USER_ONLY
       
   129     TaskState *ts = env->opaque;
       
   130 #endif
       
   131 
       
   132     if (ret == (target_ulong)-1) {
       
   133 #ifdef CONFIG_USER_ONLY
       
   134         ts->swi_errno = err;
       
   135 #else
       
   136 	syscall_err = err;
       
   137 #endif
       
   138         env->regs[0] = ret;
       
   139     } else {
       
   140         /* Fixup syscalls that use nonstardard return conventions.  */
       
   141         switch (env->regs[0]) {
       
   142         case SYS_WRITE:
       
   143         case SYS_READ:
       
   144             env->regs[0] = arm_semi_syscall_len - ret;
       
   145             break;
       
   146         case SYS_SEEK:
       
   147             env->regs[0] = 0;
       
   148             break;
       
   149         default:
       
   150             env->regs[0] = ret;
       
   151             break;
       
   152         }
       
   153     }
       
   154 }
       
   155 
       
   156 static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
       
   157 {
       
   158     /* The size is always stored in big-endian order, extract
       
   159        the value. We assume the size always fit in 32 bits.  */
       
   160     uint32_t size;
       
   161     cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
       
   162     env->regs[0] = be32_to_cpu(size);
       
   163 #ifdef CONFIG_USER_ONLY
       
   164     ((TaskState *)env->opaque)->swi_errno = err;
       
   165 #else
       
   166     syscall_err = err;
       
   167 #endif
       
   168 }
       
   169 
       
   170 #define ARG(n)					\
       
   171 ({						\
       
   172     target_ulong __arg;				\
       
   173     /* FIXME - handle get_user() failure */	\
       
   174     get_user_ual(__arg, args + (n) * 4);	\
       
   175     __arg;					\
       
   176 })
       
   177 #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
       
   178 uint32_t do_arm_semihosting(CPUState *env)
       
   179 {
       
   180     target_ulong args;
       
   181     char * s;
       
   182     int nr;
       
   183     uint32_t ret;
       
   184     uint32_t len;
       
   185 #ifdef CONFIG_USER_ONLY
       
   186     TaskState *ts = env->opaque;
       
   187 #else
       
   188     CPUState *ts = env;
       
   189 #endif
       
   190 
       
   191     nr = env->regs[0];
       
   192     args = env->regs[1];
       
   193     switch (nr) {
       
   194     case SYS_OPEN:
       
   195         if (!(s = lock_user_string(ARG(0))))
       
   196             /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   197             return (uint32_t)-1;
       
   198         if (ARG(1) >= 12)
       
   199             return (uint32_t)-1;
       
   200         if (strcmp(s, ":tt") == 0) {
       
   201             if (ARG(1) < 4)
       
   202                 return STDIN_FILENO;
       
   203             else
       
   204                 return STDOUT_FILENO;
       
   205         }
       
   206         if (use_gdb_syscalls()) {
       
   207             gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
       
   208 			   (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
       
   209             return env->regs[0];
       
   210         } else {
       
   211 	    ret = uninterrupted_open(s, open_modeflags[ARG(1)], 0644);
       
   212             ret = set_swi_errno(ts, ret);
       
   213         }
       
   214         unlock_user(s, ARG(0), 0);
       
   215         return ret;
       
   216     case SYS_CLOSE:
       
   217         if (use_gdb_syscalls()) {
       
   218             gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
       
   219             return env->regs[0];
       
   220         } else {
       
   221 	    return set_swi_errno(ts, uninterrupted_close(ARG(0)));
       
   222         }
       
   223     case SYS_WRITEC:
       
   224         {
       
   225           char c;
       
   226 
       
   227           if (get_user_u8(c, args))
       
   228               /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   229               return (uint32_t)-1;
       
   230           /* Write to debug console.  stderr is near enough.  */
       
   231           if (use_gdb_syscalls()) {
       
   232                 gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
       
   233                 return env->regs[0];
       
   234           } else {
       
   235 	      ret = uninterrupted_write(STDERR_FILENO, &c, 1);
       
   236 	      return set_swi_errno(ts, ret);
       
   237           }
       
   238         }
       
   239     case SYS_WRITE0:
       
   240         if (!(s = lock_user_string(args)))
       
   241             /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   242             return (uint32_t)-1;
       
   243         len = strlen(s);
       
   244         if (use_gdb_syscalls()) {
       
   245             gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
       
   246             ret = env->regs[0];
       
   247         } else {
       
   248 	    ret = uninterrupted_write(STDERR_FILENO, s, len);
       
   249 	    ret = set_swi_errno(ts, ret);
       
   250         }
       
   251         unlock_user(s, args, 0);
       
   252         return ret;
       
   253     case SYS_WRITE:
       
   254         len = ARG(2);
       
   255         if (use_gdb_syscalls()) {
       
   256             arm_semi_syscall_len = len;
       
   257             gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
       
   258             return env->regs[0];
       
   259         } else {
       
   260             if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
       
   261                 /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   262                 return (uint32_t)-1;
       
   263 	    ret = set_swi_errno(ts, uninterrupted_write(ARG(0), s, len));
       
   264             unlock_user(s, ARG(1), 0);
       
   265             if (ret == (uint32_t)-1)
       
   266                 return -1;
       
   267             return len - ret;
       
   268         }
       
   269     case SYS_READ:
       
   270         len = ARG(2);
       
   271         if (use_gdb_syscalls()) {
       
   272             arm_semi_syscall_len = len;
       
   273             gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
       
   274             return env->regs[0];
       
   275         } else {
       
   276             if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
       
   277                 /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   278                 return (uint32_t)-1;
       
   279 	    ret = set_swi_errno(ts, uninterrupted_read(ARG(0), s, len));
       
   280             unlock_user(s, ARG(1), len);
       
   281             if (ret == (uint32_t)-1)
       
   282                 return -1;
       
   283             return len - ret;
       
   284         }
       
   285     case SYS_READC:
       
   286        /* XXX: Read from debug cosole. Not implemented.  */
       
   287         return 0;
       
   288     case SYS_ISTTY:
       
   289         if (use_gdb_syscalls()) {
       
   290             gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
       
   291             return env->regs[0];
       
   292         } else {
       
   293             return isatty(ARG(0));
       
   294         }
       
   295     case SYS_SEEK:
       
   296         if (use_gdb_syscalls()) {
       
   297             gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
       
   298             return env->regs[0];
       
   299         } else {
       
   300             ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
       
   301             if (ret == (uint32_t)-1)
       
   302               return -1;
       
   303             return 0;
       
   304         }
       
   305     case SYS_FLEN:
       
   306         if (use_gdb_syscalls()) {
       
   307             gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
       
   308 			   ARG(0), env->regs[13]-64);
       
   309             return env->regs[0];
       
   310         } else {
       
   311             struct stat buf;
       
   312             ret = set_swi_errno(ts, fstat(ARG(0), &buf));
       
   313             if (ret == (uint32_t)-1)
       
   314                 return -1;
       
   315             return buf.st_size;
       
   316         }
       
   317     case SYS_TMPNAM:
       
   318         /* XXX: Not implemented.  */
       
   319         return -1;
       
   320     case SYS_REMOVE:
       
   321         if (use_gdb_syscalls()) {
       
   322             gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
       
   323             ret = env->regs[0];
       
   324         } else {
       
   325             if (!(s = lock_user_string(ARG(0))))
       
   326                 /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   327                 return (uint32_t)-1;
       
   328             ret =  set_swi_errno(ts, remove(s));
       
   329             unlock_user(s, ARG(0), 0);
       
   330         }
       
   331         return ret;
       
   332     case SYS_RENAME:
       
   333         if (use_gdb_syscalls()) {
       
   334             gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
       
   335                            ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
       
   336             return env->regs[0];
       
   337         } else {
       
   338             char *s2;
       
   339             s = lock_user_string(ARG(0));
       
   340             s2 = lock_user_string(ARG(2));
       
   341             if (!s || !s2)
       
   342                 /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   343                 ret = (uint32_t)-1;
       
   344             else
       
   345                 ret = set_swi_errno(ts, rename(s, s2));
       
   346             if (s2)
       
   347                 unlock_user(s2, ARG(2), 0);
       
   348             if (s)
       
   349                 unlock_user(s, ARG(0), 0);
       
   350             return ret;
       
   351         }
       
   352     case SYS_CLOCK:
       
   353         return clock() / (CLOCKS_PER_SEC / 100);
       
   354     case SYS_TIME:
       
   355         return set_swi_errno(ts, time(NULL));
       
   356     case SYS_SYSTEM:
       
   357         if (use_gdb_syscalls()) {
       
   358             gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
       
   359             return env->regs[0];
       
   360         } else {
       
   361             if (!(s = lock_user_string(ARG(0))))
       
   362                 /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   363                 return (uint32_t)-1;
       
   364             ret = set_swi_errno(ts, system(s));
       
   365             unlock_user(s, ARG(0), 0);
       
   366             return ret;
       
   367         }
       
   368     case SYS_ERRNO:
       
   369 #ifdef CONFIG_USER_ONLY
       
   370         return ts->swi_errno;
       
   371 #else
       
   372         return syscall_err;
       
   373 #endif
       
   374     case SYS_GET_CMDLINE:
       
   375 #ifdef CONFIG_USER_ONLY
       
   376         /* Build a commandline from the original argv.  */
       
   377         {
       
   378             char **arg = ts->info->host_argv;
       
   379             int len = ARG(1);
       
   380             /* lock the buffer on the ARM side */
       
   381             char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0);
       
   382 
       
   383             if (!cmdline_buffer)
       
   384                 /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   385                 return (uint32_t)-1;
       
   386 
       
   387             s = cmdline_buffer;
       
   388             while (*arg && len > 2) {
       
   389                 int n = strlen(*arg);
       
   390 
       
   391                 if (s != cmdline_buffer) {
       
   392                     *(s++) = ' ';
       
   393                     len--;
       
   394                 }
       
   395                 if (n >= len)
       
   396                     n = len - 1;
       
   397                 memcpy(s, *arg, n);
       
   398                 s += n;
       
   399                 len -= n;
       
   400                 arg++;
       
   401             }
       
   402             /* Null terminate the string.  */
       
   403             *s = 0;
       
   404             len = s - cmdline_buffer;
       
   405 
       
   406             /* Unlock the buffer on the ARM side.  */
       
   407             unlock_user(cmdline_buffer, ARG(0), len);
       
   408 
       
   409             /* Adjust the commandline length argument.  */
       
   410             SET_ARG(1, len);
       
   411 
       
   412             /* Return success if commandline fit into buffer.  */
       
   413             return *arg ? -1 : 0;
       
   414         }
       
   415 #else
       
   416       return -1;
       
   417 #endif
       
   418     case SYS_HEAPINFO:
       
   419         {
       
   420             uint32_t *ptr;
       
   421             uint32_t limit;
       
   422 
       
   423 #ifdef CONFIG_USER_ONLY
       
   424             /* Some C libraries assume the heap immediately follows .bss, so
       
   425                allocate it using sbrk.  */
       
   426             if (!ts->heap_limit) {
       
   427                 long ret;
       
   428 
       
   429                 ts->heap_base = do_brk(0);
       
   430                 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
       
   431                 /* Try a big heap, and reduce the size if that fails.  */
       
   432                 for (;;) {
       
   433                     ret = do_brk(limit);
       
   434                     if (ret != -1)
       
   435                         break;
       
   436                     limit = (ts->heap_base >> 1) + (limit >> 1);
       
   437                 }
       
   438                 ts->heap_limit = limit;
       
   439             }
       
   440 
       
   441             if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
       
   442                 /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   443                 return (uint32_t)-1;
       
   444             ptr[0] = tswap32(ts->heap_base);
       
   445             ptr[1] = tswap32(ts->heap_limit);
       
   446             ptr[2] = tswap32(ts->stack_base);
       
   447             ptr[3] = tswap32(0); /* Stack limit.  */
       
   448             unlock_user(ptr, ARG(0), 16);
       
   449 #else
       
   450             limit = ram_size;
       
   451             if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
       
   452                 /* FIXME - should this error code be -TARGET_EFAULT ? */
       
   453                 return (uint32_t)-1;
       
   454             /* TODO: Make this use the limit of the loaded application.  */
       
   455             ptr[0] = tswap32(limit / 2);
       
   456             ptr[1] = tswap32(limit);
       
   457             ptr[2] = tswap32(limit); /* Stack base */
       
   458             ptr[3] = tswap32(0); /* Stack limit.  */
       
   459             unlock_user(ptr, ARG(0), 16);
       
   460 #endif
       
   461             return 0;
       
   462         }
       
   463     case SYS_EXIT:
       
   464         /* FIXME: ideally we want to inform gdb about program
       
   465 	   exit whenever gdb is connected, even if syscalls
       
   466 	   are not handled by gdb.  */
       
   467         if (use_gdb_syscalls())
       
   468             gdb_exit(env, 0);
       
   469         exit(0);
       
   470     default:
       
   471         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
       
   472         cpu_dump_state(env, stderr, fprintf, 0);
       
   473         abort();
       
   474     }
       
   475 }