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