cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

m68k-semi.c (14218B)


      1/*
      2 *  m68k/ColdFire Semihosting syscall interface
      3 *
      4 *  Copyright (c) 2005-2007 CodeSourcery.
      5 *
      6 *  This program is free software; you can redistribute it and/or modify
      7 *  it under the terms of the GNU General Public License as published by
      8 *  the Free Software Foundation; either version 2 of the License, or
      9 *  (at your option) any later version.
     10 *
     11 *  This program is distributed in the hope that it will be useful,
     12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 *  GNU General Public License for more details.
     15 *
     16 *  You should have received a copy of the GNU General Public License
     17 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
     18 */
     19
     20#include "qemu/osdep.h"
     21
     22#include "cpu.h"
     23#include "exec/gdbstub.h"
     24#if defined(CONFIG_USER_ONLY)
     25#include "qemu.h"
     26#define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024)
     27#else
     28#include "exec/softmmu-semi.h"
     29#include "hw/boards.h"
     30#endif
     31#include "qemu/log.h"
     32
     33#define HOSTED_EXIT  0
     34#define HOSTED_INIT_SIM 1
     35#define HOSTED_OPEN 2
     36#define HOSTED_CLOSE 3
     37#define HOSTED_READ 4
     38#define HOSTED_WRITE 5
     39#define HOSTED_LSEEK 6
     40#define HOSTED_RENAME 7
     41#define HOSTED_UNLINK 8
     42#define HOSTED_STAT 9
     43#define HOSTED_FSTAT 10
     44#define HOSTED_GETTIMEOFDAY 11
     45#define HOSTED_ISATTY 12
     46#define HOSTED_SYSTEM 13
     47
     48typedef uint32_t gdb_mode_t;
     49typedef uint32_t gdb_time_t;
     50
     51struct m68k_gdb_stat {
     52  uint32_t    gdb_st_dev;     /* device */
     53  uint32_t    gdb_st_ino;     /* inode */
     54  gdb_mode_t  gdb_st_mode;    /* protection */
     55  uint32_t    gdb_st_nlink;   /* number of hard links */
     56  uint32_t    gdb_st_uid;     /* user ID of owner */
     57  uint32_t    gdb_st_gid;     /* group ID of owner */
     58  uint32_t    gdb_st_rdev;    /* device type (if inode device) */
     59  uint64_t    gdb_st_size;    /* total size, in bytes */
     60  uint64_t    gdb_st_blksize; /* blocksize for filesystem I/O */
     61  uint64_t    gdb_st_blocks;  /* number of blocks allocated */
     62  gdb_time_t  gdb_st_atime;   /* time of last access */
     63  gdb_time_t  gdb_st_mtime;   /* time of last modification */
     64  gdb_time_t  gdb_st_ctime;   /* time of last change */
     65} QEMU_PACKED;
     66
     67struct gdb_timeval {
     68  gdb_time_t tv_sec;  /* second */
     69  uint64_t tv_usec;   /* microsecond */
     70} QEMU_PACKED;
     71
     72#define GDB_O_RDONLY   0x0
     73#define GDB_O_WRONLY   0x1
     74#define GDB_O_RDWR     0x2
     75#define GDB_O_APPEND   0x8
     76#define GDB_O_CREAT  0x200
     77#define GDB_O_TRUNC  0x400
     78#define GDB_O_EXCL   0x800
     79
     80static int translate_openflags(int flags)
     81{
     82    int hf;
     83
     84    if (flags & GDB_O_WRONLY)
     85        hf = O_WRONLY;
     86    else if (flags & GDB_O_RDWR)
     87        hf = O_RDWR;
     88    else
     89        hf = O_RDONLY;
     90
     91    if (flags & GDB_O_APPEND) hf |= O_APPEND;
     92    if (flags & GDB_O_CREAT) hf |= O_CREAT;
     93    if (flags & GDB_O_TRUNC) hf |= O_TRUNC;
     94    if (flags & GDB_O_EXCL) hf |= O_EXCL;
     95
     96    return hf;
     97}
     98
     99static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s)
    100{
    101    struct m68k_gdb_stat *p;
    102
    103    if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(struct m68k_gdb_stat), 0)))
    104        /* FIXME - should this return an error code? */
    105        return;
    106    p->gdb_st_dev = cpu_to_be32(s->st_dev);
    107    p->gdb_st_ino = cpu_to_be32(s->st_ino);
    108    p->gdb_st_mode = cpu_to_be32(s->st_mode);
    109    p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
    110    p->gdb_st_uid = cpu_to_be32(s->st_uid);
    111    p->gdb_st_gid = cpu_to_be32(s->st_gid);
    112    p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
    113    p->gdb_st_size = cpu_to_be64(s->st_size);
    114#ifdef _WIN32
    115    /* Windows stat is missing some fields.  */
    116    p->gdb_st_blksize = 0;
    117    p->gdb_st_blocks = 0;
    118#else
    119    p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
    120    p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
    121#endif
    122    p->gdb_st_atime = cpu_to_be32(s->st_atime);
    123    p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
    124    p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
    125    unlock_user(p, addr, sizeof(struct m68k_gdb_stat));
    126}
    127
    128static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err)
    129{
    130    target_ulong args = env->dregs[1];
    131    if (put_user_u32(ret, args) ||
    132        put_user_u32(err, args + 4)) {
    133        /*
    134         * The m68k semihosting ABI does not provide any way to report this
    135         * error to the guest, so the best we can do is log it in qemu.
    136         * It is always a guest error not to pass us a valid argument block.
    137         */
    138        qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
    139                      "discarded because argument block not writable\n");
    140    }
    141}
    142
    143static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err)
    144{
    145    target_ulong args = env->dregs[1];
    146    if (put_user_u32(ret >> 32, args) ||
    147        put_user_u32(ret, args + 4) ||
    148        put_user_u32(err, args + 8)) {
    149        /* No way to report this via m68k semihosting ABI; just log it */
    150        qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
    151                      "discarded because argument block not writable\n");
    152    }
    153}
    154
    155static int m68k_semi_is_fseek;
    156
    157static void m68k_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
    158{
    159    M68kCPU *cpu = M68K_CPU(cs);
    160    CPUM68KState *env = &cpu->env;
    161
    162    if (m68k_semi_is_fseek) {
    163        /*
    164         * FIXME: We've already lost the high bits of the fseek
    165         * return value.
    166         */
    167        m68k_semi_return_u64(env, ret, err);
    168        m68k_semi_is_fseek = 0;
    169    } else {
    170        m68k_semi_return_u32(env, ret, err);
    171    }
    172}
    173
    174/*
    175 * Read the input value from the argument block; fail the semihosting
    176 * call if the memory read fails.
    177 */
    178#define GET_ARG(n) do {                                 \
    179    if (get_user_ual(arg ## n, args + (n) * 4)) {       \
    180        result = -1;                                    \
    181        errno = EFAULT;                                 \
    182        goto failed;                                    \
    183    }                                                   \
    184} while (0)
    185
    186void do_m68k_semihosting(CPUM68KState *env, int nr)
    187{
    188    uint32_t args;
    189    target_ulong arg0, arg1, arg2, arg3;
    190    void *p;
    191    void *q;
    192    uint32_t len;
    193    uint32_t result;
    194
    195    args = env->dregs[1];
    196    switch (nr) {
    197    case HOSTED_EXIT:
    198        gdb_exit(env->dregs[0]);
    199        exit(env->dregs[0]);
    200    case HOSTED_OPEN:
    201        GET_ARG(0);
    202        GET_ARG(1);
    203        GET_ARG(2);
    204        GET_ARG(3);
    205        if (use_gdb_syscalls()) {
    206            gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", arg0, (int)arg1,
    207                           arg2, arg3);
    208            return;
    209        } else {
    210            p = lock_user_string(arg0);
    211            if (!p) {
    212                /* FIXME - check error code? */
    213                result = -1;
    214            } else {
    215                result = open(p, translate_openflags(arg2), arg3);
    216                unlock_user(p, arg0, 0);
    217            }
    218        }
    219        break;
    220    case HOSTED_CLOSE:
    221        {
    222            /* Ignore attempts to close stdin/out/err.  */
    223            GET_ARG(0);
    224            int fd = arg0;
    225            if (fd > 2) {
    226                if (use_gdb_syscalls()) {
    227                    gdb_do_syscall(m68k_semi_cb, "close,%x", arg0);
    228                    return;
    229                } else {
    230                    result = close(fd);
    231                }
    232            } else {
    233                result = 0;
    234            }
    235            break;
    236        }
    237    case HOSTED_READ:
    238        GET_ARG(0);
    239        GET_ARG(1);
    240        GET_ARG(2);
    241        len = arg2;
    242        if (use_gdb_syscalls()) {
    243            gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x",
    244                           arg0, arg1, len);
    245            return;
    246        } else {
    247            p = lock_user(VERIFY_WRITE, arg1, len, 0);
    248            if (!p) {
    249                /* FIXME - check error code? */
    250                result = -1;
    251            } else {
    252                result = read(arg0, p, len);
    253                unlock_user(p, arg1, len);
    254            }
    255        }
    256        break;
    257    case HOSTED_WRITE:
    258        GET_ARG(0);
    259        GET_ARG(1);
    260        GET_ARG(2);
    261        len = arg2;
    262        if (use_gdb_syscalls()) {
    263            gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x",
    264                           arg0, arg1, len);
    265            return;
    266        } else {
    267            p = lock_user(VERIFY_READ, arg1, len, 1);
    268            if (!p) {
    269                /* FIXME - check error code? */
    270                result = -1;
    271            } else {
    272                result = write(arg0, p, len);
    273                unlock_user(p, arg0, 0);
    274            }
    275        }
    276        break;
    277    case HOSTED_LSEEK:
    278        {
    279            uint64_t off;
    280            GET_ARG(0);
    281            GET_ARG(1);
    282            GET_ARG(2);
    283            GET_ARG(3);
    284            off = (uint32_t)arg2 | ((uint64_t)arg1 << 32);
    285            if (use_gdb_syscalls()) {
    286                m68k_semi_is_fseek = 1;
    287                gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x",
    288                               arg0, off, arg3);
    289            } else {
    290                off = lseek(arg0, off, arg3);
    291                m68k_semi_return_u64(env, off, errno);
    292            }
    293            return;
    294        }
    295    case HOSTED_RENAME:
    296        GET_ARG(0);
    297        GET_ARG(1);
    298        GET_ARG(2);
    299        GET_ARG(3);
    300        if (use_gdb_syscalls()) {
    301            gdb_do_syscall(m68k_semi_cb, "rename,%s,%s",
    302                           arg0, (int)arg1, arg2, (int)arg3);
    303            return;
    304        } else {
    305            p = lock_user_string(arg0);
    306            q = lock_user_string(arg2);
    307            if (!p || !q) {
    308                /* FIXME - check error code? */
    309                result = -1;
    310            } else {
    311                result = rename(p, q);
    312            }
    313            unlock_user(p, arg0, 0);
    314            unlock_user(q, arg2, 0);
    315        }
    316        break;
    317    case HOSTED_UNLINK:
    318        GET_ARG(0);
    319        GET_ARG(1);
    320        if (use_gdb_syscalls()) {
    321            gdb_do_syscall(m68k_semi_cb, "unlink,%s",
    322                           arg0, (int)arg1);
    323            return;
    324        } else {
    325            p = lock_user_string(arg0);
    326            if (!p) {
    327                /* FIXME - check error code? */
    328                result = -1;
    329            } else {
    330                result = unlink(p);
    331                unlock_user(p, arg0, 0);
    332            }
    333        }
    334        break;
    335    case HOSTED_STAT:
    336        GET_ARG(0);
    337        GET_ARG(1);
    338        GET_ARG(2);
    339        if (use_gdb_syscalls()) {
    340            gdb_do_syscall(m68k_semi_cb, "stat,%s,%x",
    341                           arg0, (int)arg1, arg2);
    342            return;
    343        } else {
    344            struct stat s;
    345            p = lock_user_string(arg0);
    346            if (!p) {
    347                /* FIXME - check error code? */
    348                result = -1;
    349            } else {
    350                result = stat(p, &s);
    351                unlock_user(p, arg0, 0);
    352            }
    353            if (result == 0) {
    354                translate_stat(env, arg2, &s);
    355            }
    356        }
    357        break;
    358    case HOSTED_FSTAT:
    359        GET_ARG(0);
    360        GET_ARG(1);
    361        if (use_gdb_syscalls()) {
    362            gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x",
    363                           arg0, arg1);
    364            return;
    365        } else {
    366            struct stat s;
    367            result = fstat(arg0, &s);
    368            if (result == 0) {
    369                translate_stat(env, arg1, &s);
    370            }
    371        }
    372        break;
    373    case HOSTED_GETTIMEOFDAY:
    374        GET_ARG(0);
    375        GET_ARG(1);
    376        if (use_gdb_syscalls()) {
    377            gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x",
    378                           arg0, arg1);
    379            return;
    380        } else {
    381            qemu_timeval tv;
    382            struct gdb_timeval *p;
    383            result = qemu_gettimeofday(&tv);
    384            if (result != 0) {
    385                if (!(p = lock_user(VERIFY_WRITE,
    386                                    arg0, sizeof(struct gdb_timeval), 0))) {
    387                    /* FIXME - check error code? */
    388                    result = -1;
    389                } else {
    390                    p->tv_sec = cpu_to_be32(tv.tv_sec);
    391                    p->tv_usec = cpu_to_be64(tv.tv_usec);
    392                    unlock_user(p, arg0, sizeof(struct gdb_timeval));
    393                }
    394            }
    395        }
    396        break;
    397    case HOSTED_ISATTY:
    398        GET_ARG(0);
    399        if (use_gdb_syscalls()) {
    400            gdb_do_syscall(m68k_semi_cb, "isatty,%x", arg0);
    401            return;
    402        } else {
    403            result = isatty(arg0);
    404        }
    405        break;
    406    case HOSTED_SYSTEM:
    407        GET_ARG(0);
    408        GET_ARG(1);
    409        if (use_gdb_syscalls()) {
    410            gdb_do_syscall(m68k_semi_cb, "system,%s",
    411                           arg0, (int)arg1);
    412            return;
    413        } else {
    414            p = lock_user_string(arg0);
    415            if (!p) {
    416                /* FIXME - check error code? */
    417                result = -1;
    418            } else {
    419                result = system(p);
    420                unlock_user(p, arg0, 0);
    421            }
    422        }
    423        break;
    424    case HOSTED_INIT_SIM:
    425#if defined(CONFIG_USER_ONLY)
    426        {
    427        CPUState *cs = env_cpu(env);
    428        TaskState *ts = cs->opaque;
    429        /* Allocate the heap using sbrk.  */
    430        if (!ts->heap_limit) {
    431            abi_ulong ret;
    432            uint32_t size;
    433            uint32_t base;
    434
    435            base = do_brk(0);
    436            size = SEMIHOSTING_HEAP_SIZE;
    437            /* Try a big heap, and reduce the size if that fails.  */
    438            for (;;) {
    439                ret = do_brk(base + size);
    440                if (ret >= (base + size)) {
    441                    break;
    442                }
    443                size >>= 1;
    444            }
    445            ts->heap_limit = base + size;
    446        }
    447        /*
    448         * This call may happen before we have writable memory, so return
    449         * values directly in registers.
    450         */
    451        env->dregs[1] = ts->heap_limit;
    452        env->aregs[7] = ts->stack_base;
    453        }
    454#else
    455        /*
    456         * FIXME: This is wrong for boards where RAM does not start at
    457         * address zero.
    458         */
    459        env->dregs[1] = current_machine->ram_size;
    460        env->aregs[7] = current_machine->ram_size;
    461#endif
    462        return;
    463    default:
    464        cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr);
    465        result = 0;
    466    }
    467failed:
    468    m68k_semi_return_u32(env, result, errno);
    469}