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