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 "exec/semihost.h"
  26
  27typedef enum UHIOp {
  28    UHI_exit = 1,
  29    UHI_open = 2,
  30    UHI_close = 3,
  31    UHI_read = 4,
  32    UHI_write = 5,
  33    UHI_lseek = 6,
  34    UHI_unlink = 7,
  35    UHI_fstat = 8,
  36    UHI_argc = 9,
  37    UHI_argnlen = 10,
  38    UHI_argn = 11,
  39    UHI_plog = 13,
  40    UHI_assert = 14,
  41    UHI_pread = 19,
  42    UHI_pwrite = 20,
  43    UHI_link = 22
  44} UHIOp;
  45
  46typedef struct UHIStat {
  47    int16_t uhi_st_dev;
  48    uint16_t uhi_st_ino;
  49    uint32_t uhi_st_mode;
  50    uint16_t uhi_st_nlink;
  51    uint16_t uhi_st_uid;
  52    uint16_t uhi_st_gid;
  53    int16_t uhi_st_rdev;
  54    uint64_t uhi_st_size;
  55    uint64_t uhi_st_atime;
  56    uint64_t uhi_st_spare1;
  57    uint64_t uhi_st_mtime;
  58    uint64_t uhi_st_spare2;
  59    uint64_t uhi_st_ctime;
  60    uint64_t uhi_st_spare3;
  61    uint64_t uhi_st_blksize;
  62    uint64_t uhi_st_blocks;
  63    uint64_t uhi_st_spare4[2];
  64} UHIStat;
  65
  66enum UHIOpenFlags {
  67    UHIOpen_RDONLY = 0x0,
  68    UHIOpen_WRONLY = 0x1,
  69    UHIOpen_RDWR   = 0x2,
  70    UHIOpen_APPEND = 0x8,
  71    UHIOpen_CREAT  = 0x200,
  72    UHIOpen_TRUNC  = 0x400,
  73    UHIOpen_EXCL   = 0x800
  74};
  75
  76/* Errno values taken from asm-mips/errno.h */
  77static uint16_t host_to_mips_errno[] = {
  78    [ENAMETOOLONG] = 78,
  79#ifdef EOVERFLOW
  80    [EOVERFLOW]    = 79,
  81#endif
  82#ifdef ELOOP
  83    [ELOOP]        = 90,
  84#endif
  85};
  86
  87static int errno_mips(int err)
  88{
  89    if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) {
  90        return EINVAL;
  91    } else if (host_to_mips_errno[err]) {
  92        return host_to_mips_errno[err];
  93    } else {
  94        return err;
  95    }
  96}
  97
  98static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
  99                               target_ulong vaddr)
 100{
 101    hwaddr len = sizeof(struct UHIStat);
 102    UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
 103    if (!dst) {
 104        errno = EFAULT;
 105        return -1;
 106    }
 107
 108    dst->uhi_st_dev = tswap16(src->st_dev);
 109    dst->uhi_st_ino = tswap16(src->st_ino);
 110    dst->uhi_st_mode = tswap32(src->st_mode);
 111    dst->uhi_st_nlink = tswap16(src->st_nlink);
 112    dst->uhi_st_uid = tswap16(src->st_uid);
 113    dst->uhi_st_gid = tswap16(src->st_gid);
 114    dst->uhi_st_rdev = tswap16(src->st_rdev);
 115    dst->uhi_st_size = tswap64(src->st_size);
 116    dst->uhi_st_atime = tswap64(src->st_atime);
 117    dst->uhi_st_mtime = tswap64(src->st_mtime);
 118    dst->uhi_st_ctime = tswap64(src->st_ctime);
 119#ifdef _WIN32
 120    dst->uhi_st_blksize = 0;
 121    dst->uhi_st_blocks = 0;
 122#else
 123    dst->uhi_st_blksize = tswap64(src->st_blksize);
 124    dst->uhi_st_blocks = tswap64(src->st_blocks);
 125#endif
 126    unlock_user(dst, vaddr, len);
 127    return 0;
 128}
 129
 130static int get_open_flags(target_ulong target_flags)
 131{
 132    int open_flags = 0;
 133
 134    if (target_flags & UHIOpen_RDWR) {
 135        open_flags |= O_RDWR;
 136    } else if (target_flags & UHIOpen_WRONLY) {
 137        open_flags |= O_WRONLY;
 138    } else {
 139        open_flags |= O_RDONLY;
 140    }
 141
 142    open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
 143    open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0;
 144    open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0;
 145    open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0;
 146
 147    return open_flags;
 148}
 149
 150static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
 151                         target_ulong len, target_ulong offset)
 152{
 153    int num_of_bytes;
 154    void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
 155    if (!dst) {
 156        errno = EFAULT;
 157        return -1;
 158    }
 159
 160    if (offset) {
 161#ifdef _WIN32
 162        num_of_bytes = 0;
 163#else
 164        num_of_bytes = pwrite(fd, dst, len, offset);
 165#endif
 166    } else {
 167        num_of_bytes = write(fd, dst, len);
 168    }
 169
 170    unlock_user(dst, vaddr, 0);
 171    return num_of_bytes;
 172}
 173
 174static int read_from_file(CPUMIPSState *env, target_ulong fd,
 175                          target_ulong vaddr, target_ulong len,
 176                          target_ulong offset)
 177{
 178    int num_of_bytes;
 179    void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
 180    if (!dst) {
 181        errno = EFAULT;
 182        return -1;
 183    }
 184
 185    if (offset) {
 186#ifdef _WIN32
 187        num_of_bytes = 0;
 188#else
 189        num_of_bytes = pread(fd, dst, len, offset);
 190#endif
 191    } else {
 192        num_of_bytes = read(fd, dst, len);
 193    }
 194
 195    unlock_user(dst, vaddr, len);
 196    return num_of_bytes;
 197}
 198
 199static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
 200                               target_ulong vaddr)
 201{
 202    int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
 203    char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
 204    if (!dst) {
 205        return -1;
 206    }
 207
 208    strcpy(dst, semihosting_get_arg(arg_num));
 209
 210    unlock_user(dst, vaddr, strsize);
 211    return 0;
 212}
 213
 214#define GET_TARGET_STRING(p, addr)              \
 215    do {                                        \
 216        p = lock_user_string(addr);             \
 217        if (!p) {                               \
 218            gpr[2] = -1;                        \
 219            gpr[3] = EFAULT;                    \
 220            goto uhi_done;                      \
 221        }                                       \
 222    } while (0)
 223
 224#define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \
 225    do {                                                \
 226        p = lock_user_string(addr);                     \
 227        if (!p) {                                       \
 228            gpr[2] = -1;                                \
 229            gpr[3] = EFAULT;                            \
 230            goto uhi_done;                              \
 231        }                                               \
 232        p2 = lock_user_string(addr2);                   \
 233        if (!p2) {                                      \
 234            unlock_user(p, addr, 0);                    \
 235            gpr[2] = -1;                                \
 236            gpr[3] = EFAULT;                            \
 237            goto uhi_done;                              \
 238        }                                               \
 239    } while (0)
 240
 241#define FREE_TARGET_STRING(p, gpr)              \
 242    do {                                        \
 243        unlock_user(p, gpr, 0);                 \
 244    } while (0)
 245
 246void helper_do_semihosting(CPUMIPSState *env)
 247{
 248    target_ulong *gpr = env->active_tc.gpr;
 249    const UHIOp op = gpr[25];
 250    char *p, *p2;
 251
 252    switch (op) {
 253    case UHI_exit:
 254        qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
 255        exit(gpr[4]);
 256    case UHI_open:
 257        GET_TARGET_STRING(p, gpr[4]);
 258        if (!strcmp("/dev/stdin", p)) {
 259            gpr[2] = 0;
 260        } else if (!strcmp("/dev/stdout", p)) {
 261            gpr[2] = 1;
 262        } else if (!strcmp("/dev/stderr", p)) {
 263            gpr[2] = 2;
 264        } else {
 265            gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
 266            gpr[3] = errno_mips(errno);
 267        }
 268        FREE_TARGET_STRING(p, gpr[4]);
 269        break;
 270    case UHI_close:
 271        if (gpr[4] < 3) {
 272            /* ignore closing stdin/stdout/stderr */
 273            gpr[2] = 0;
 274            goto uhi_done;
 275        }
 276        gpr[2] = close(gpr[4]);
 277        gpr[3] = errno_mips(errno);
 278        break;
 279    case UHI_read:
 280        gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
 281        gpr[3] = errno_mips(errno);
 282        break;
 283    case UHI_write:
 284        gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
 285        gpr[3] = errno_mips(errno);
 286        break;
 287    case UHI_lseek:
 288        gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
 289        gpr[3] = errno_mips(errno);
 290        break;
 291    case UHI_unlink:
 292        GET_TARGET_STRING(p, gpr[4]);
 293        gpr[2] = remove(p);
 294        gpr[3] = errno_mips(errno);
 295        FREE_TARGET_STRING(p, gpr[4]);
 296        break;
 297    case UHI_fstat:
 298        {
 299            struct stat sbuf;
 300            memset(&sbuf, 0, sizeof(sbuf));
 301            gpr[2] = fstat(gpr[4], &sbuf);
 302            gpr[3] = errno_mips(errno);
 303            if (gpr[2]) {
 304                goto uhi_done;
 305            }
 306            gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
 307            gpr[3] = errno_mips(errno);
 308        }
 309        break;
 310    case UHI_argc:
 311        gpr[2] = semihosting_get_argc();
 312        break;
 313    case UHI_argnlen:
 314        if (gpr[4] >= semihosting_get_argc()) {
 315            gpr[2] = -1;
 316            goto uhi_done;
 317        }
 318        gpr[2] = strlen(semihosting_get_arg(gpr[4]));
 319        break;
 320    case UHI_argn:
 321        if (gpr[4] >= semihosting_get_argc()) {
 322            gpr[2] = -1;
 323            goto uhi_done;
 324        }
 325        gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
 326        break;
 327    case UHI_plog:
 328        GET_TARGET_STRING(p, gpr[4]);
 329        p2 = strstr(p, "%d");
 330        if (p2) {
 331            int char_num = p2 - p;
 332            char *buf = g_malloc(char_num + 1);
 333            strncpy(buf, p, char_num);
 334            buf[char_num] = '\0';
 335            gpr[2] = printf("%s%d%s", buf, (int)gpr[5], p2 + 2);
 336            g_free(buf);
 337        } else {
 338            gpr[2] = printf("%s", 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