qemu/target-lm32/lm32-semi.c
<<
>>
Prefs
   1/*
   2 *  Lattice Mico32 semihosting syscall interface
   3 *
   4 *  Copyright (c) 2014 Michael Walle <michael@walle.cc>
   5 *
   6 * Based on target-m68k/m68k-semi.c, which is
   7 *  Copyright (c) 2005-2007 CodeSourcery.
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "cpu.h"
  15#include "exec/helper-proto.h"
  16#include "qemu/log.h"
  17#include "exec/softmmu-semi.h"
  18
  19enum {
  20    TARGET_SYS_exit    = 1,
  21    TARGET_SYS_open    = 2,
  22    TARGET_SYS_close   = 3,
  23    TARGET_SYS_read    = 4,
  24    TARGET_SYS_write   = 5,
  25    TARGET_SYS_lseek   = 6,
  26    TARGET_SYS_fstat   = 10,
  27    TARGET_SYS_stat    = 15,
  28};
  29
  30enum {
  31    NEWLIB_O_RDONLY    =   0x0,
  32    NEWLIB_O_WRONLY    =   0x1,
  33    NEWLIB_O_RDWR      =   0x2,
  34    NEWLIB_O_APPEND    =   0x8,
  35    NEWLIB_O_CREAT     = 0x200,
  36    NEWLIB_O_TRUNC     = 0x400,
  37    NEWLIB_O_EXCL      = 0x800,
  38};
  39
  40static int translate_openflags(int flags)
  41{
  42    int hf;
  43
  44    if (flags & NEWLIB_O_WRONLY) {
  45        hf = O_WRONLY;
  46    } else if (flags & NEWLIB_O_RDWR) {
  47        hf = O_RDWR;
  48    } else {
  49        hf = O_RDONLY;
  50    }
  51
  52    if (flags & NEWLIB_O_APPEND) {
  53        hf |= O_APPEND;
  54    }
  55
  56    if (flags & NEWLIB_O_CREAT) {
  57        hf |= O_CREAT;
  58    }
  59
  60    if (flags & NEWLIB_O_TRUNC) {
  61        hf |= O_TRUNC;
  62    }
  63
  64    if (flags & NEWLIB_O_EXCL) {
  65        hf |= O_EXCL;
  66    }
  67
  68    return hf;
  69}
  70
  71struct newlib_stat {
  72    int16_t     newlib_st_dev;     /* device */
  73    uint16_t    newlib_st_ino;     /* inode */
  74    uint16_t    newlib_st_mode;    /* protection */
  75    uint16_t    newlib_st_nlink;   /* number of hard links */
  76    uint16_t    newlib_st_uid;     /* user ID of owner */
  77    uint16_t    newlib_st_gid;     /* group ID of owner */
  78    int16_t     newlib_st_rdev;    /* device type (if inode device) */
  79    int32_t     newlib_st_size;    /* total size, in bytes */
  80    int32_t     newlib_st_atime;   /* time of last access */
  81    uint32_t    newlib_st_spare1;
  82    int32_t     newlib_st_mtime;   /* time of last modification */
  83    uint32_t    newlib_st_spare2;
  84    int32_t     newlib_st_ctime;   /* time of last change */
  85    uint32_t    newlib_st_spare3;
  86} QEMU_PACKED;
  87
  88static int translate_stat(CPULM32State *env, target_ulong addr,
  89        struct stat *s)
  90{
  91    struct newlib_stat *p;
  92
  93    p = lock_user(VERIFY_WRITE, addr, sizeof(struct newlib_stat), 0);
  94    if (!p) {
  95        return 0;
  96    }
  97    p->newlib_st_dev = cpu_to_be16(s->st_dev);
  98    p->newlib_st_ino = cpu_to_be16(s->st_ino);
  99    p->newlib_st_mode = cpu_to_be16(s->st_mode);
 100    p->newlib_st_nlink = cpu_to_be16(s->st_nlink);
 101    p->newlib_st_uid = cpu_to_be16(s->st_uid);
 102    p->newlib_st_gid = cpu_to_be16(s->st_gid);
 103    p->newlib_st_rdev = cpu_to_be16(s->st_rdev);
 104    p->newlib_st_size = cpu_to_be32(s->st_size);
 105    p->newlib_st_atime = cpu_to_be32(s->st_atime);
 106    p->newlib_st_mtime = cpu_to_be32(s->st_mtime);
 107    p->newlib_st_ctime = cpu_to_be32(s->st_ctime);
 108    unlock_user(p, addr, sizeof(struct newlib_stat));
 109
 110    return 1;
 111}
 112
 113bool lm32_cpu_do_semihosting(CPUState *cs)
 114{
 115    LM32CPU *cpu = LM32_CPU(cs);
 116    CPULM32State *env = &cpu->env;
 117
 118    int ret = -1;
 119    target_ulong nr, arg0, arg1, arg2;
 120    void *p;
 121    struct stat s;
 122
 123    nr = env->regs[R_R8];
 124    arg0 = env->regs[R_R1];
 125    arg1 = env->regs[R_R2];
 126    arg2 = env->regs[R_R3];
 127
 128    switch (nr) {
 129    case TARGET_SYS_exit:
 130        /* void _exit(int rc) */
 131        exit(arg0);
 132
 133    case TARGET_SYS_open:
 134        /* int open(const char *pathname, int flags) */
 135        p = lock_user_string(arg0);
 136        if (!p) {
 137            ret = -1;
 138        } else {
 139            ret = open(p, translate_openflags(arg2));
 140            unlock_user(p, arg0, 0);
 141        }
 142        break;
 143
 144    case TARGET_SYS_read:
 145        /* ssize_t read(int fd, const void *buf, size_t count) */
 146        p = lock_user(VERIFY_WRITE, arg1, arg2, 0);
 147        if (!p) {
 148            ret = -1;
 149        } else {
 150            ret = read(arg0, p, arg2);
 151            unlock_user(p, arg1, arg2);
 152        }
 153        break;
 154
 155    case TARGET_SYS_write:
 156        /* ssize_t write(int fd, const void *buf, size_t count) */
 157        p = lock_user(VERIFY_READ, arg1, arg2, 1);
 158        if (!p) {
 159            ret = -1;
 160        } else {
 161            ret = write(arg0, p, arg2);
 162            unlock_user(p, arg1, 0);
 163        }
 164        break;
 165
 166    case TARGET_SYS_close:
 167        /* int close(int fd) */
 168        /* don't close stdin/stdout/stderr */
 169        if (arg0 > 2) {
 170            ret = close(arg0);
 171        } else {
 172            ret = 0;
 173        }
 174        break;
 175
 176    case TARGET_SYS_lseek:
 177        /* off_t lseek(int fd, off_t offset, int whence */
 178        ret = lseek(arg0, arg1, arg2);
 179        break;
 180
 181    case TARGET_SYS_stat:
 182        /* int stat(const char *path, struct stat *buf) */
 183        p = lock_user_string(arg0);
 184        if (!p) {
 185            ret = -1;
 186        } else {
 187            ret = stat(p, &s);
 188            unlock_user(p, arg0, 0);
 189            if (translate_stat(env, arg1, &s) == 0) {
 190                ret = -1;
 191            }
 192        }
 193        break;
 194
 195    case TARGET_SYS_fstat:
 196        /* int stat(int fd, struct stat *buf) */
 197        ret = fstat(arg0, &s);
 198        if (ret == 0) {
 199            if (translate_stat(env, arg1, &s) == 0) {
 200                ret = -1;
 201            }
 202        }
 203        break;
 204
 205    default:
 206        /* unhandled */
 207        return false;
 208    }
 209
 210    env->regs[R_R1] = ret;
 211    return true;
 212}
 213