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