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