Home | History | Annotate | Download | only in target-arm
      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 "exec/gdbstub.h"
     37 #include "hw/arm/arm.h"
     38 #endif
     39 
     40 #define TARGET_SYS_OPEN        0x01
     41 #define TARGET_SYS_CLOSE       0x02
     42 #define TARGET_SYS_WRITEC      0x03
     43 #define TARGET_SYS_WRITE0      0x04
     44 #define TARGET_SYS_WRITE       0x05
     45 #define TARGET_SYS_READ        0x06
     46 #define TARGET_SYS_READC       0x07
     47 #define TARGET_SYS_ISTTY       0x09
     48 #define TARGET_SYS_SEEK        0x0a
     49 #define TARGET_SYS_FLEN        0x0c
     50 #define TARGET_SYS_TMPNAM      0x0d
     51 #define TARGET_SYS_REMOVE      0x0e
     52 #define TARGET_SYS_RENAME      0x0f
     53 #define TARGET_SYS_CLOCK       0x10
     54 #define TARGET_SYS_TIME        0x11
     55 #define TARGET_SYS_SYSTEM      0x12
     56 #define TARGET_SYS_ERRNO       0x13
     57 #define TARGET_SYS_GET_CMDLINE 0x15
     58 #define TARGET_SYS_HEAPINFO    0x16
     59 #define TARGET_SYS_EXIT        0x18
     60 
     61 #ifndef O_BINARY
     62 #define O_BINARY 0
     63 #endif
     64 
     65 #define GDB_O_RDONLY  0x000
     66 #define GDB_O_WRONLY  0x001
     67 #define GDB_O_RDWR    0x002
     68 #define GDB_O_APPEND  0x008
     69 #define GDB_O_CREAT   0x200
     70 #define GDB_O_TRUNC   0x400
     71 #define GDB_O_BINARY  0
     72 
     73 static int gdb_open_modeflags[12] = {
     74     GDB_O_RDONLY,
     75     GDB_O_RDONLY | GDB_O_BINARY,
     76     GDB_O_RDWR,
     77     GDB_O_RDWR | GDB_O_BINARY,
     78     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
     79     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
     80     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
     81     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
     82     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
     83     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
     84     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
     85     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
     86 };
     87 
     88 static int open_modeflags[12] = {
     89     O_RDONLY,
     90     O_RDONLY | O_BINARY,
     91     O_RDWR,
     92     O_RDWR | O_BINARY,
     93     O_WRONLY | O_CREAT | O_TRUNC,
     94     O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
     95     O_RDWR | O_CREAT | O_TRUNC,
     96     O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
     97     O_WRONLY | O_CREAT | O_APPEND,
     98     O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
     99     O_RDWR | O_CREAT | O_APPEND,
    100     O_RDWR | O_CREAT | O_APPEND | O_BINARY
    101 };
    102 
    103 #ifdef CONFIG_USER_ONLY
    104 static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
    105 {
    106     if (code == (uint32_t)-1)
    107         ts->swi_errno = errno;
    108     return code;
    109 }
    110 #else
    111 static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
    112 {
    113     return code;
    114 }
    115 
    116 #include "exec/softmmu-semi.h"
    117 #endif
    118 
    119 static target_ulong arm_semi_syscall_len;
    120 
    121 #if !defined(CONFIG_USER_ONLY)
    122 static target_ulong syscall_err;
    123 #endif
    124 
    125 static void arm_semi_cb(CPUState *cpu, target_ulong ret, target_ulong err)
    126 {
    127     CPUARMState *env = cpu->env_ptr;
    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 TARGET_SYS_WRITE:
    143         case TARGET_SYS_READ:
    144             env->regs[0] = arm_semi_syscall_len - ret;
    145             break;
    146         case TARGET_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 *cpu, 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     CPUARMState *env = cpu->env_ptr;
    161     uint32_t size;
    162     cpu_memory_rw_debug(cpu, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
    163     env->regs[0] = be32_to_cpu(size);
    164 #ifdef CONFIG_USER_ONLY
    165     ((TaskState *)env->opaque)->swi_errno = err;
    166 #else
    167     syscall_err = err;
    168 #endif
    169 }
    170 
    171 /* Read the input value from the argument block; fail the semihosting
    172  * call if the memory read fails.
    173  */
    174 #define GET_ARG(n) do {                                 \
    175     if (get_user_ual(arg ## n, args + (n) * 4)) {       \
    176         return (uint32_t)-1;                            \
    177     }                                                   \
    178 } while (0)
    179 
    180 #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
    181 uint32_t do_arm_semihosting(CPUARMState *env)
    182 {
    183     target_ulong args;
    184     target_ulong arg0, arg1, arg2, arg3;
    185     char * s;
    186     int nr;
    187     uint32_t ret;
    188     uint32_t len;
    189 #ifdef CONFIG_USER_ONLY
    190     TaskState *ts = env->opaque;
    191 #else
    192     CPUARMState *ts = env;
    193 #endif
    194 
    195     nr = env->regs[0];
    196     args = env->regs[1];
    197     switch (nr) {
    198     case TARGET_SYS_OPEN:
    199         GET_ARG(0);
    200         GET_ARG(1);
    201         GET_ARG(2);
    202         s = lock_user_string(arg0);
    203         if (!s) {
    204             /* FIXME - should this error code be -TARGET_EFAULT ? */
    205             return (uint32_t)-1;
    206         }
    207         if (arg1 >= 12) {
    208             unlock_user(s, arg0, 0);
    209             return (uint32_t)-1;
    210         }
    211         if (strcmp(s, ":tt") == 0) {
    212             int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
    213             unlock_user(s, arg0, 0);
    214             return result_fileno;
    215         }
    216         if (use_gdb_syscalls()) {
    217             gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
    218                            (int)arg2+1, gdb_open_modeflags[arg1]);
    219             ret = env->regs[0];
    220         } else {
    221             ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
    222         }
    223         unlock_user(s, arg0, 0);
    224         return ret;
    225     case TARGET_SYS_CLOSE:
    226         GET_ARG(0);
    227         if (use_gdb_syscalls()) {
    228             gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
    229             return env->regs[0];
    230         } else {
    231             return set_swi_errno(ts, close(arg0));
    232         }
    233     case TARGET_SYS_WRITEC:
    234         {
    235           char c;
    236 
    237           if (get_user_u8(c, args))
    238               /* FIXME - should this error code be -TARGET_EFAULT ? */
    239               return (uint32_t)-1;
    240           /* Write to debug console.  stderr is near enough.  */
    241           if (use_gdb_syscalls()) {
    242                 gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
    243                 return env->regs[0];
    244           } else {
    245                 return write(STDERR_FILENO, &c, 1);
    246           }
    247         }
    248     case TARGET_SYS_WRITE0:
    249         if (!(s = lock_user_string(args)))
    250             /* FIXME - should this error code be -TARGET_EFAULT ? */
    251             return (uint32_t)-1;
    252         len = strlen(s);
    253         if (use_gdb_syscalls()) {
    254             gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
    255             ret = env->regs[0];
    256         } else {
    257             ret = write(STDERR_FILENO, s, len);
    258         }
    259         unlock_user(s, args, 0);
    260         return ret;
    261     case TARGET_SYS_WRITE:
    262         GET_ARG(0);
    263         GET_ARG(1);
    264         GET_ARG(2);
    265         len = arg2;
    266         if (use_gdb_syscalls()) {
    267             arm_semi_syscall_len = len;
    268             gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
    269             return env->regs[0];
    270         } else {
    271             s = lock_user(VERIFY_READ, arg1, len, 1);
    272             if (!s) {
    273                 /* FIXME - should this error code be -TARGET_EFAULT ? */
    274                 return (uint32_t)-1;
    275             }
    276             ret = set_swi_errno(ts, write(arg0, s, len));
    277             unlock_user(s, arg1, 0);
    278             if (ret == (uint32_t)-1)
    279                 return -1;
    280             return len - ret;
    281         }
    282     case TARGET_SYS_READ:
    283         GET_ARG(0);
    284         GET_ARG(1);
    285         GET_ARG(2);
    286         len = arg2;
    287         if (use_gdb_syscalls()) {
    288             arm_semi_syscall_len = len;
    289             gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
    290             return env->regs[0];
    291         } else {
    292             s = lock_user(VERIFY_WRITE, arg1, len, 0);
    293             if (!s) {
    294                 /* FIXME - should this error code be -TARGET_EFAULT ? */
    295                 return (uint32_t)-1;
    296             }
    297             do {
    298                 ret = set_swi_errno(ts, read(arg0, s, len));
    299             } while (ret == -1 && errno == EINTR);
    300             unlock_user(s, arg1, len);
    301             if (ret == (uint32_t)-1)
    302                 return -1;
    303             return len - ret;
    304         }
    305     case TARGET_SYS_READC:
    306        /* XXX: Read from debug console. Not implemented.  */
    307         return 0;
    308     case TARGET_SYS_ISTTY:
    309         GET_ARG(0);
    310         if (use_gdb_syscalls()) {
    311             gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
    312             return env->regs[0];
    313         } else {
    314             return isatty(arg0);
    315         }
    316     case TARGET_SYS_SEEK:
    317         GET_ARG(0);
    318         GET_ARG(1);
    319         if (use_gdb_syscalls()) {
    320             gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
    321             return env->regs[0];
    322         } else {
    323             ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
    324             if (ret == (uint32_t)-1)
    325               return -1;
    326             return 0;
    327         }
    328     case TARGET_SYS_FLEN:
    329         GET_ARG(0);
    330         if (use_gdb_syscalls()) {
    331             gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
    332                            arg0, env->regs[13]-64);
    333             return env->regs[0];
    334         } else {
    335             struct stat buf;
    336             ret = set_swi_errno(ts, fstat(arg0, &buf));
    337             if (ret == (uint32_t)-1)
    338                 return -1;
    339             return buf.st_size;
    340         }
    341     case TARGET_SYS_TMPNAM:
    342         /* XXX: Not implemented.  */
    343         return -1;
    344     case TARGET_SYS_REMOVE:
    345         GET_ARG(0);
    346         GET_ARG(1);
    347         if (use_gdb_syscalls()) {
    348             gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
    349             ret = env->regs[0];
    350         } else {
    351             s = lock_user_string(arg0);
    352             if (!s) {
    353                 /* FIXME - should this error code be -TARGET_EFAULT ? */
    354                 return (uint32_t)-1;
    355             }
    356             ret =  set_swi_errno(ts, remove(s));
    357             unlock_user(s, arg0, 0);
    358         }
    359         return ret;
    360     case TARGET_SYS_RENAME:
    361         GET_ARG(0);
    362         GET_ARG(1);
    363         GET_ARG(2);
    364         GET_ARG(3);
    365         if (use_gdb_syscalls()) {
    366             gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
    367                            arg0, (int)arg1+1, arg2, (int)arg3+1);
    368             return env->regs[0];
    369         } else {
    370             char *s2;
    371             s = lock_user_string(arg0);
    372             s2 = lock_user_string(arg2);
    373             if (!s || !s2)
    374                 /* FIXME - should this error code be -TARGET_EFAULT ? */
    375                 ret = (uint32_t)-1;
    376             else
    377                 ret = set_swi_errno(ts, rename(s, s2));
    378             if (s2)
    379                 unlock_user(s2, arg2, 0);
    380             if (s)
    381                 unlock_user(s, arg0, 0);
    382             return ret;
    383         }
    384     case TARGET_SYS_CLOCK:
    385         return clock() / (CLOCKS_PER_SEC / 100);
    386     case TARGET_SYS_TIME:
    387         return set_swi_errno(ts, time(NULL));
    388     case TARGET_SYS_SYSTEM:
    389         GET_ARG(0);
    390         GET_ARG(1);
    391         if (use_gdb_syscalls()) {
    392             gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
    393             return env->regs[0];
    394         } else {
    395             s = lock_user_string(arg0);
    396             if (!s) {
    397                 /* FIXME - should this error code be -TARGET_EFAULT ? */
    398                 return (uint32_t)-1;
    399             }
    400             ret = set_swi_errno(ts, system(s));
    401             unlock_user(s, arg0, 0);
    402             return ret;
    403         }
    404     case TARGET_SYS_ERRNO:
    405 #ifdef CONFIG_USER_ONLY
    406         return ts->swi_errno;
    407 #else
    408         return syscall_err;
    409 #endif
    410     case TARGET_SYS_GET_CMDLINE:
    411         {
    412             /* Build a command-line from the original argv.
    413              *
    414              * The inputs are:
    415              *     * arg0, pointer to a buffer of at least the size
    416              *               specified in arg1.
    417              *     * arg1, size of the buffer pointed to by arg0 in
    418              *               bytes.
    419              *
    420              * The outputs are:
    421              *     * arg0, pointer to null-terminated string of the
    422              *               command line.
    423              *     * arg1, length of the string pointed to by arg0.
    424              */
    425 
    426             char *output_buffer;
    427             size_t input_size;
    428             size_t output_size;
    429             int status = 0;
    430             GET_ARG(0);
    431             GET_ARG(1);
    432             input_size = arg1;
    433             /* Compute the size of the output string.  */
    434 #if !defined(CONFIG_USER_ONLY)
    435             output_size = strlen(ts->boot_info->kernel_filename)
    436                         + 1  /* Separating space.  */
    437                         + strlen(ts->boot_info->kernel_cmdline)
    438                         + 1; /* Terminating null byte.  */
    439 #else
    440             unsigned int i;
    441 
    442             output_size = ts->info->arg_end - ts->info->arg_start;
    443             if (!output_size) {
    444                 /* We special-case the "empty command line" case (argc==0).
    445                    Just provide the terminating 0. */
    446                 output_size = 1;
    447             }
    448 #endif
    449 
    450             if (output_size > input_size) {
    451                  /* Not enough space to store command-line arguments.  */
    452                 return -1;
    453             }
    454 
    455             /* Adjust the command-line length.  */
    456             if (SET_ARG(1, output_size - 1)) {
    457                 /* Couldn't write back to argument block */
    458                 return -1;
    459             }
    460 
    461             /* Lock the buffer on the ARM side.  */
    462             output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
    463             if (!output_buffer) {
    464                 return -1;
    465             }
    466 
    467             /* Copy the command-line arguments.  */
    468 #if !defined(CONFIG_USER_ONLY)
    469             pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
    470             pstrcat(output_buffer, output_size, " ");
    471             pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
    472 #else
    473             if (output_size == 1) {
    474                 /* Empty command-line.  */
    475                 output_buffer[0] = '\0';
    476                 goto out;
    477             }
    478 
    479             if (copy_from_user(output_buffer, ts->info->arg_start,
    480                                output_size)) {
    481                 status = -1;
    482                 goto out;
    483             }
    484 
    485             /* Separate arguments by white spaces.  */
    486             for (i = 0; i < output_size - 1; i++) {
    487                 if (output_buffer[i] == 0) {
    488                     output_buffer[i] = ' ';
    489                 }
    490             }
    491         out:
    492 #endif
    493             /* Unlock the buffer on the ARM side.  */
    494             unlock_user(output_buffer, arg0, output_size);
    495 
    496             return status;
    497         }
    498     case TARGET_SYS_HEAPINFO:
    499         {
    500             uint32_t *ptr;
    501             uint32_t limit;
    502             GET_ARG(0);
    503 
    504 #ifdef CONFIG_USER_ONLY
    505             /* Some C libraries assume the heap immediately follows .bss, so
    506                allocate it using sbrk.  */
    507             if (!ts->heap_limit) {
    508                 abi_ulong ret;
    509 
    510                 ts->heap_base = do_brk(0);
    511                 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
    512                 /* Try a big heap, and reduce the size if that fails.  */
    513                 for (;;) {
    514                     ret = do_brk(limit);
    515                     if (ret >= limit) {
    516                         break;
    517                     }
    518                     limit = (ts->heap_base >> 1) + (limit >> 1);
    519                 }
    520                 ts->heap_limit = limit;
    521             }
    522 
    523             ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
    524             if (!ptr) {
    525                 /* FIXME - should this error code be -TARGET_EFAULT ? */
    526                 return (uint32_t)-1;
    527             }
    528             ptr[0] = tswap32(ts->heap_base);
    529             ptr[1] = tswap32(ts->heap_limit);
    530             ptr[2] = tswap32(ts->stack_base);
    531             ptr[3] = tswap32(0); /* Stack limit.  */
    532             unlock_user(ptr, arg0, 16);
    533 #else
    534             limit = ram_size;
    535             ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
    536             if (!ptr) {
    537                 /* FIXME - should this error code be -TARGET_EFAULT ? */
    538                 return (uint32_t)-1;
    539             }
    540             /* TODO: Make this use the limit of the loaded application.  */
    541             ptr[0] = tswap32(limit / 2);
    542             ptr[1] = tswap32(limit);
    543             ptr[2] = tswap32(limit); /* Stack base */
    544             ptr[3] = tswap32(0); /* Stack limit.  */
    545             unlock_user(ptr, arg0, 16);
    546 #endif
    547             return 0;
    548         }
    549     case TARGET_SYS_EXIT:
    550         //gdb_exit(env, 0);
    551         exit(0);
    552     default:
    553         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
    554         cpu_dump_state(ENV_GET_CPU(env), stderr, fprintf, 0);
    555         abort();
    556     }
    557 }
    558