qemu/target/mips/mips-semi.c
<<
>>
Prefs
   1/*
   2 * Unified Hosting Interface syscalls.
   3 *
   4 * Copyright (c) 2015 Imagination Technologies
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library 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 GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "cpu.h"
  22#include "qemu/log.h"
  23#include "exec/helper-proto.h"
  24#include "exec/softmmu-semi.h"
  25#include "hw/semihosting/semihost.h"
  26#include "hw/semihosting/console.h"
  27
  28typedef enum UHIOp {
  29    UHI_exit = 1,
  30    UHI_open = 2,
  31    UHI_close = 3,
  32    UHI_read = 4,
  33    UHI_write = 5,
  34    UHI_lseek = 6,
  35    UHI_unlink = 7,
  36    UHI_fstat = 8,
  37    UHI_argc = 9,
  38    UHI_argnlen = 10,
  39    UHI_argn = 11,
  40    UHI_plog = 13,
  41    UHI_assert = 14,
  42    UHI_pread = 19,
  43    UHI_pwrite = 20,
  44    UHI_link = 22
  45} UHIOp;
  46
  47typedef struct UHIStat {
  48    int16_t uhi_st_dev;
  49    uint16_t uhi_st_ino;
  50    uint32_t uhi_st_mode;
  51    uint16_t uhi_st_nlink;
  52    uint16_t uhi_st_uid;
  53    uint16_t uhi_st_gid;
  54    int16_t uhi_st_rdev;
  55    uint64_t uhi_st_size;
  56    uint64_t uhi_st_atime;
  57    uint64_t uhi_st_spare1;
  58    uint64_t uhi_st_mtime;
  59    uint64_t uhi_st_spare2;
  60    uint64_t uhi_st_ctime;
  61    uint64_t uhi_st_spare3;
  62    uint64_t uhi_st_blksize;
  63    uint64_t uhi_st_blocks;
  64    uint64_t uhi_st_spare4[2];
  65} UHIStat;
  66
  67enum UHIOpenFlags {
  68    UHIOpen_RDONLY = 0x0,
  69    UHIOpen_WRONLY = 0x1,
  70    UHIOpen_RDWR   = 0x2,
  71    UHIOpen_APPEND = 0x8,
  72    UHIOpen_CREAT  = 0x200,
  73    UHIOpen_TRUNC  = 0x400,
  74    UHIOpen_EXCL   = 0x800
  75};
  76
  77/* Errno values taken from asm-mips/errno.h */
  78static uint16_t host_to_mips_errno[] = {
  79    [ENAMETOOLONG] = 78,
  80#ifdef EOVERFLOW
  81    [EOVERFLOW]    = 79,
  82#endif
  83#ifdef ELOOP
  84    [ELOOP]        = 90,
  85#endif
  86};
  87
  88static int errno_mips(int err)
  89{
  90    if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) {
  91        return EINVAL;
  92    } else if (host_to_mips_errno[err]) {
  93        return host_to_mips_errno[err];
  94    } else {
  95        return err;
  96    }
  97}
  98
  99static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
 100                               target_ulong vaddr)
 101{
 102    hwaddr len = sizeof(struct UHIStat);
 103    UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
 104    if (!dst) {
 105        errno = EFAULT;
 106        return -1;
 107    }
 108
 109    dst->uhi_st_dev = tswap16(src->st_dev);
 110    dst->uhi_st_ino = tswap16(src->st_ino);
 111    dst->uhi_st_mode = tswap32(src->st_mode);
 112    dst->uhi_st_nlink = tswap16(src->st_nlink);
 113    dst->uhi_st_uid = tswap16(src->st_uid);
 114    dst->uhi_st_gid = tswap16(src->st_gid);
 115    dst->uhi_st_rdev = tswap16(src->st_rdev);
 116    dst->uhi_st_size = tswap64(src->st_size);
 117    dst->uhi_st_atime = tswap64(src->st_atime);
 118    dst->uhi_st_mtime = tswap64(src->st_mtime);
 119    dst->uhi_st_ctime = tswap64(src->st_ctime);
 120#ifdef _WIN32
 121    dst->uhi_st_blksize = 0;
 122    dst->uhi_st_blocks = 0;
 123#else
 124    dst->uhi_st_blksize = tswap64(src->st_blksize);
 125    dst->uhi_st_blocks = tswap64(src->st_blocks);
 126#endif
 127    unlock_user(dst, vaddr, len);
 128    return 0;
 129}
 130
 131static int get_open_flags(target_ulong target_flags)
 132{
 133    int open_flags = 0;
 134
 135    if (target_flags & UHIOpen_RDWR) {
 136        open_flags |= O_RDWR;
 137    } else if (target_flags & UHIOpen_WRONLY) {
 138        open_flags |= O_WRONLY;
 139    } else {
 140        open_flags |= O_RDONLY;
 141    }
 142
 143    open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
 144    open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0;
 145    open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0;
 146    open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0;
 147
 148    return open_flags;
 149}
 150
 151static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
 152                         target_ulong len, target_ulong offset)
 153{
 154    int num_of_bytes;
 155    void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
 156    if (!dst) {
 157        errno = EFAULT;
 158        return -1;
 159    }
 160
 161    if (offset) {
 162#ifdef _WIN32
 163        num_of_bytes = 0;
 164#else
 165        num_of_bytes = pwrite(fd, dst, len, offset);
 166#endif
 167    } else {
 168        num_of_bytes = write(fd, dst, len);
 169    }
 170
 171    unlock_user(dst, vaddr, 0);
 172    return num_of_bytes;
 173}
 174
 175static int read_from_file(CPUMIPSState *env, target_ulong fd,
 176                          target_ulong vaddr, target_ulong len,
 177                          target_ulong offset)
 178{
 179    int num_of_bytes;
 180    void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
 181    if (!dst) {
 182        errno = EFAULT;
 183        return -1;
 184    }
 185
 186    if (offset) {
 187#ifdef _WIN32
 188        num_of_bytes = 0;
 189#else
 190        num_of_bytes = pread(fd, dst, len, offset);
 191#endif
 192    } else {
 193        num_of_bytes = read(fd, dst, len);
 194    }
 195
 196    unlock_user(dst, vaddr, len);
 197    return num_of_bytes;
 198}
 199
 200static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
 201                               target_ulong vaddr)
 202{
 203    int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
 204    char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
 205    if (!dst) {
 206        return -1;
 207    }
 208
 209    strcpy(dst, semihosting_get_arg(arg_num));
 210
 211    unlock_user(dst, vaddr, strsize);
 212    return 0;
 213}
 214
 215#define GET_TARGET_STRING(p, addr)              \
 216    do {                                        \
 217        p = lock_user_string(addr);             \
 218        if (!p) {                               \
 219            gpr[2] = -1;                        \
 220            gpr[3] = EFAULT;                    \
 221            goto uhi_done;                      \
 222        }                                       \
 223    } while (0)
 224
 225#define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \
 226    do {                                                \
 227        p = lock_user_string(addr);                     \
 228        if (!p) {                                       \
 229            gpr[2] = -1;                                \
 230            gpr[3] = EFAULT;                            \
 231            goto uhi_done;                              \
 232        }                                               \
 233        p2 = lock_user_string(addr2);                   \
 234        if (!p2) {                                      \
 235            unlock_user(p, addr, 0);                    \
 236            gpr[2] = -1;                                \
 237            gpr[3] = EFAULT;                            \
 238            goto uhi_done;                              \
 239        }                                               \
 240    } while (0)
 241
 242#define FREE_TARGET_STRING(p, gpr)              \
 243    do {                                        \
 244        unlock_user(p, gpr, 0);                 \
 245    } while (0)
 246
 247void helper_do_semihosting(CPUMIPSState *env)
 248{
 249    target_ulong *gpr = env->active_tc.gpr;
 250    const UHIOp op = gpr[25];
 251    char *p, *p2;
 252
 253    switch (op) {
 254    case UHI_exit:
 255        qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
 256        exit(gpr[4]);
 257    case UHI_open:
 258        GET_TARGET_STRING(p, gpr[4]);
 259        if (!strcmp("/dev/stdin", p)) {
 260            gpr[2] = 0;
 261        } else if (!strcmp("/dev/stdout", p)) {
 262            gpr[2] = 1;
 263        } else if (!strcmp("/dev/stderr", p)) {
 264            gpr[2] = 2;
 265        } else {
 266            gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
 267            gpr[3] = errno_mips(errno);
 268        }
 269        FREE_TARGET_STRING(p, gpr[4]);
 270        break;
 271    case UHI_close:
 272        if (gpr[4] < 3) {
 273            /* ignore closing stdin/stdout/stderr */
 274            gpr[2] = 0;
 275            goto uhi_done;
 276        }
 277        gpr[2] = close(gpr[4]);
 278        gpr[3] = errno_mips(errno);
 279        break;
 280    case UHI_read:
 281        gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
 282        gpr[3] = errno_mips(errno);
 283        break;
 284    case UHI_write:
 285        gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
 286        gpr[3] = errno_mips(errno);
 287        break;
 288    case UHI_lseek:
 289        gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
 290        gpr[3] = errno_mips(errno);
 291        break;
 292    case UHI_unlink:
 293        GET_TARGET_STRING(p, gpr[4]);
 294        gpr[2] = remove(p);
 295        gpr[3] = errno_mips(errno);
 296        FREE_TARGET_STRING(p, gpr[4]);
 297        break;
 298    case UHI_fstat:
 299        {
 300            struct stat sbuf;
 301            memset(&sbuf, 0, sizeof(sbuf));
 302            gpr[2] = fstat(gpr[4], &sbuf);
 303            gpr[3] = errno_mips(errno);
 304            if (gpr[2]) {
 305                goto uhi_done;
 306            }
 307            gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
 308            gpr[3] = errno_mips(errno);
 309        }
 310        break;
 311    case UHI_argc:
 312        gpr[2] = semihosting_get_argc();
 313        break;
 314    case UHI_argnlen:
 315        if (gpr[4] >= semihosting_get_argc()) {
 316            gpr[2] = -1;
 317            goto uhi_done;
 318        }
 319        gpr[2] = strlen(semihosting_get_arg(gpr[4]));
 320        break;
 321    case UHI_argn:
 322        if (gpr[4] >= semihosting_get_argc()) {
 323            gpr[2] = -1;
 324            goto uhi_done;
 325        }
 326        gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
 327        break;
 328    case UHI_plog:
 329        GET_TARGET_STRING(p, gpr[4]);
 330        p2 = strstr(p, "%d");
 331        if (p2) {
 332            int char_num = p2 - p;
 333            GString *s = g_string_new_len(p, char_num);
 334            g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
 335            gpr[2] = qemu_semihosting_log_out(s->str, s->len);
 336            g_string_free(s, true);
 337        } else {
 338            gpr[2] = qemu_semihosting_log_out(p, strlen(p));
 339        }
 340        FREE_TARGET_STRING(p, gpr[4]);
 341        break;
 342    case UHI_assert:
 343        GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
 344        printf("assertion '");
 345        printf("\"%s\"", p);
 346        printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
 347        FREE_TARGET_STRING(p2, gpr[5]);
 348        FREE_TARGET_STRING(p, gpr[4]);
 349        abort();
 350        break;
 351    case UHI_pread:
 352        gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
 353        gpr[3] = errno_mips(errno);
 354        break;
 355    case UHI_pwrite:
 356        gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
 357        gpr[3] = errno_mips(errno);
 358        break;
 359#ifndef _WIN32
 360    case UHI_link:
 361        GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
 362        gpr[2] = link(p, p2);
 363        gpr[3] = errno_mips(errno);
 364        FREE_TARGET_STRING(p2, gpr[5]);
 365        FREE_TARGET_STRING(p, gpr[4]);
 366        break;
 367#endif
 368    default:
 369        fprintf(stderr, "Unknown UHI operation %d\n", op);
 370        abort();
 371    }
 372uhi_done:
 373    return;
 374}
 375