qemu/target/nios2/nios2-semi.c
<<
>>
Prefs
   1/*
   2 *  Nios II Semihosting syscall interface.
   3 *  This code is derived from m68k-semi.c.
   4 *  The semihosting protocol implemented here is described in the
   5 *  libgloss sources:
   6 *  https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD
   7 *
   8 *  Copyright (c) 2017-2019 Mentor Graphics
   9 *
  10 *  This program is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU General Public License as published by
  12 *  the Free Software Foundation; either version 2 of the License, or
  13 *  (at your option) any later version.
  14 *
  15 *  This program is distributed in the hope that it will be useful,
  16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 *  GNU General Public License for more details.
  19 *
  20 *  You should have received a copy of the GNU General Public License
  21 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  22 */
  23
  24#include "qemu/osdep.h"
  25
  26#include "cpu.h"
  27#if defined(CONFIG_USER_ONLY)
  28#include "qemu.h"
  29#else
  30#include "qemu-common.h"
  31#include "exec/gdbstub.h"
  32#include "exec/softmmu-semi.h"
  33#endif
  34#include "qemu/log.h"
  35#include "sysemu/sysemu.h"
  36
  37#define HOSTED_EXIT  0
  38#define HOSTED_INIT_SIM 1
  39#define HOSTED_OPEN 2
  40#define HOSTED_CLOSE 3
  41#define HOSTED_READ 4
  42#define HOSTED_WRITE 5
  43#define HOSTED_LSEEK 6
  44#define HOSTED_RENAME 7
  45#define HOSTED_UNLINK 8
  46#define HOSTED_STAT 9
  47#define HOSTED_FSTAT 10
  48#define HOSTED_GETTIMEOFDAY 11
  49#define HOSTED_ISATTY 12
  50#define HOSTED_SYSTEM 13
  51
  52typedef uint32_t gdb_mode_t;
  53typedef uint32_t gdb_time_t;
  54
  55struct nios2_gdb_stat {
  56  uint32_t    gdb_st_dev;     /* device */
  57  uint32_t    gdb_st_ino;     /* inode */
  58  gdb_mode_t  gdb_st_mode;    /* protection */
  59  uint32_t    gdb_st_nlink;   /* number of hard links */
  60  uint32_t    gdb_st_uid;     /* user ID of owner */
  61  uint32_t    gdb_st_gid;     /* group ID of owner */
  62  uint32_t    gdb_st_rdev;    /* device type (if inode device) */
  63  uint64_t    gdb_st_size;    /* total size, in bytes */
  64  uint64_t    gdb_st_blksize; /* blocksize for filesystem I/O */
  65  uint64_t    gdb_st_blocks;  /* number of blocks allocated */
  66  gdb_time_t  gdb_st_atime;   /* time of last access */
  67  gdb_time_t  gdb_st_mtime;   /* time of last modification */
  68  gdb_time_t  gdb_st_ctime;   /* time of last change */
  69} QEMU_PACKED;
  70
  71struct gdb_timeval {
  72  gdb_time_t tv_sec;  /* second */
  73  uint64_t tv_usec;   /* microsecond */
  74} QEMU_PACKED;
  75
  76#define GDB_O_RDONLY   0x0
  77#define GDB_O_WRONLY   0x1
  78#define GDB_O_RDWR     0x2
  79#define GDB_O_APPEND   0x8
  80#define GDB_O_CREAT  0x200
  81#define GDB_O_TRUNC  0x400
  82#define GDB_O_EXCL   0x800
  83
  84static int translate_openflags(int flags)
  85{
  86    int hf;
  87
  88    if (flags & GDB_O_WRONLY) {
  89        hf = O_WRONLY;
  90    } else if (flags & GDB_O_RDWR) {
  91        hf = O_RDWR;
  92    } else {
  93        hf = O_RDONLY;
  94    }
  95
  96    if (flags & GDB_O_APPEND) {
  97        hf |= O_APPEND;
  98    }
  99    if (flags & GDB_O_CREAT) {
 100        hf |= O_CREAT;
 101    }
 102    if (flags & GDB_O_TRUNC) {
 103        hf |= O_TRUNC;
 104    }
 105    if (flags & GDB_O_EXCL) {
 106        hf |= O_EXCL;
 107    }
 108
 109    return hf;
 110}
 111
 112static bool translate_stat(CPUNios2State *env, target_ulong addr,
 113                           struct stat *s)
 114{
 115    struct nios2_gdb_stat *p;
 116
 117    p = lock_user(VERIFY_WRITE, addr, sizeof(struct nios2_gdb_stat), 0);
 118
 119    if (!p) {
 120        return false;
 121    }
 122    p->gdb_st_dev = cpu_to_be32(s->st_dev);
 123    p->gdb_st_ino = cpu_to_be32(s->st_ino);
 124    p->gdb_st_mode = cpu_to_be32(s->st_mode);
 125    p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
 126    p->gdb_st_uid = cpu_to_be32(s->st_uid);
 127    p->gdb_st_gid = cpu_to_be32(s->st_gid);
 128    p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
 129    p->gdb_st_size = cpu_to_be64(s->st_size);
 130#ifdef _WIN32
 131    /* Windows stat is missing some fields.  */
 132    p->gdb_st_blksize = 0;
 133    p->gdb_st_blocks = 0;
 134#else
 135    p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
 136    p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
 137#endif
 138    p->gdb_st_atime = cpu_to_be32(s->st_atime);
 139    p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
 140    p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
 141    unlock_user(p, addr, sizeof(struct nios2_gdb_stat));
 142    return true;
 143}
 144
 145static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret,
 146                                  uint32_t err)
 147{
 148    target_ulong args = env->regs[R_ARG1];
 149    if (put_user_u32(ret, args) ||
 150        put_user_u32(err, args + 4)) {
 151        /*
 152         * The nios2 semihosting ABI does not provide any way to report this
 153         * error to the guest, so the best we can do is log it in qemu.
 154         * It is always a guest error not to pass us a valid argument block.
 155         */
 156        qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
 157                      "discarded because argument block not writable\n");
 158    }
 159}
 160
 161static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret,
 162                                  uint32_t err)
 163{
 164    target_ulong args = env->regs[R_ARG1];
 165    if (put_user_u32(ret >> 32, args) ||
 166        put_user_u32(ret, args + 4) ||
 167        put_user_u32(err, args + 8)) {
 168        /* No way to report this via nios2 semihosting ABI; just log it */
 169        qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
 170                      "discarded because argument block not writable\n");
 171    }
 172}
 173
 174static int nios2_semi_is_lseek;
 175
 176static void nios2_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
 177{
 178    Nios2CPU *cpu = NIOS2_CPU(cs);
 179    CPUNios2State *env = &cpu->env;
 180
 181    if (nios2_semi_is_lseek) {
 182        /*
 183         * FIXME: We've already lost the high bits of the lseek
 184         * return value.
 185         */
 186        nios2_semi_return_u64(env, ret, err);
 187        nios2_semi_is_lseek = 0;
 188    } else {
 189        nios2_semi_return_u32(env, ret, err);
 190    }
 191}
 192
 193/*
 194 * Read the input value from the argument block; fail the semihosting
 195 * call if the memory read fails.
 196 */
 197#define GET_ARG(n) do {                                 \
 198    if (get_user_ual(arg ## n, args + (n) * 4)) {       \
 199        result = -1;                                    \
 200        errno = EFAULT;                                 \
 201        goto failed;                                    \
 202    }                                                   \
 203} while (0)
 204
 205void do_nios2_semihosting(CPUNios2State *env)
 206{
 207    int nr;
 208    uint32_t args;
 209    target_ulong arg0, arg1, arg2, arg3;
 210    void *p;
 211    void *q;
 212    uint32_t len;
 213    uint32_t result;
 214
 215    nr = env->regs[R_ARG0];
 216    args = env->regs[R_ARG1];
 217    switch (nr) {
 218    case HOSTED_EXIT:
 219        gdb_exit(env, env->regs[R_ARG0]);
 220        exit(env->regs[R_ARG0]);
 221    case HOSTED_OPEN:
 222        GET_ARG(0);
 223        GET_ARG(1);
 224        GET_ARG(2);
 225        GET_ARG(3);
 226        if (use_gdb_syscalls()) {
 227            gdb_do_syscall(nios2_semi_cb, "open,%s,%x,%x", arg0, (int)arg1,
 228                           arg2, arg3);
 229            return;
 230        } else {
 231            p = lock_user_string(arg0);
 232            if (!p) {
 233                result = -1;
 234                errno = EFAULT;
 235            } else {
 236                result = open(p, translate_openflags(arg2), arg3);
 237                unlock_user(p, arg0, 0);
 238            }
 239        }
 240        break;
 241    case HOSTED_CLOSE:
 242        {
 243            /* Ignore attempts to close stdin/out/err.  */
 244            GET_ARG(0);
 245            int fd = arg0;
 246            if (fd > 2) {
 247                if (use_gdb_syscalls()) {
 248                    gdb_do_syscall(nios2_semi_cb, "close,%x", arg0);
 249                    return;
 250                } else {
 251                    result = close(fd);
 252                }
 253            } else {
 254                result = 0;
 255            }
 256            break;
 257        }
 258    case HOSTED_READ:
 259        GET_ARG(0);
 260        GET_ARG(1);
 261        GET_ARG(2);
 262        len = arg2;
 263        if (use_gdb_syscalls()) {
 264            gdb_do_syscall(nios2_semi_cb, "read,%x,%x,%x",
 265                           arg0, arg1, len);
 266            return;
 267        } else {
 268            p = lock_user(VERIFY_WRITE, arg1, len, 0);
 269            if (!p) {
 270                result = -1;
 271                errno = EFAULT;
 272            } else {
 273                result = read(arg0, p, len);
 274                unlock_user(p, arg1, len);
 275            }
 276        }
 277        break;
 278    case HOSTED_WRITE:
 279        GET_ARG(0);
 280        GET_ARG(1);
 281        GET_ARG(2);
 282        len = arg2;
 283        if (use_gdb_syscalls()) {
 284            gdb_do_syscall(nios2_semi_cb, "write,%x,%x,%x",
 285                           arg0, arg1, len);
 286            return;
 287        } else {
 288            p = lock_user(VERIFY_READ, arg1, len, 1);
 289            if (!p) {
 290                result = -1;
 291                errno = EFAULT;
 292            } else {
 293                result = write(arg0, p, len);
 294                unlock_user(p, arg0, 0);
 295            }
 296        }
 297        break;
 298    case HOSTED_LSEEK:
 299        {
 300            uint64_t off;
 301            GET_ARG(0);
 302            GET_ARG(1);
 303            GET_ARG(2);
 304            GET_ARG(3);
 305            off = (uint32_t)arg2 | ((uint64_t)arg1 << 32);
 306            if (use_gdb_syscalls()) {
 307                nios2_semi_is_lseek = 1;
 308                gdb_do_syscall(nios2_semi_cb, "lseek,%x,%lx,%x",
 309                               arg0, off, arg3);
 310            } else {
 311                off = lseek(arg0, off, arg3);
 312                nios2_semi_return_u64(env, off, errno);
 313            }
 314            return;
 315        }
 316    case HOSTED_RENAME:
 317        GET_ARG(0);
 318        GET_ARG(1);
 319        GET_ARG(2);
 320        GET_ARG(3);
 321        if (use_gdb_syscalls()) {
 322            gdb_do_syscall(nios2_semi_cb, "rename,%s,%s",
 323                           arg0, (int)arg1, arg2, (int)arg3);
 324            return;
 325        } else {
 326            p = lock_user_string(arg0);
 327            q = lock_user_string(arg2);
 328            if (!p || !q) {
 329                result = -1;
 330                errno = EFAULT;
 331            } else {
 332                result = rename(p, q);
 333            }
 334            unlock_user(p, arg0, 0);
 335            unlock_user(q, arg2, 0);
 336        }
 337        break;
 338    case HOSTED_UNLINK:
 339        GET_ARG(0);
 340        GET_ARG(1);
 341        if (use_gdb_syscalls()) {
 342            gdb_do_syscall(nios2_semi_cb, "unlink,%s",
 343                           arg0, (int)arg1);
 344            return;
 345        } else {
 346            p = lock_user_string(arg0);
 347            if (!p) {
 348                result = -1;
 349                errno = EFAULT;
 350            } else {
 351                result = unlink(p);
 352                unlock_user(p, arg0, 0);
 353            }
 354        }
 355        break;
 356    case HOSTED_STAT:
 357        GET_ARG(0);
 358        GET_ARG(1);
 359        GET_ARG(2);
 360        if (use_gdb_syscalls()) {
 361            gdb_do_syscall(nios2_semi_cb, "stat,%s,%x",
 362                           arg0, (int)arg1, arg2);
 363            return;
 364        } else {
 365            struct stat s;
 366            p = lock_user_string(arg0);
 367            if (!p) {
 368                result = -1;
 369                errno = EFAULT;
 370            } else {
 371                result = stat(p, &s);
 372                unlock_user(p, arg0, 0);
 373            }
 374            if (result == 0 && !translate_stat(env, arg2, &s)) {
 375                result = -1;
 376                errno = EFAULT;
 377            }
 378        }
 379        break;
 380    case HOSTED_FSTAT:
 381        GET_ARG(0);
 382        GET_ARG(1);
 383        if (use_gdb_syscalls()) {
 384            gdb_do_syscall(nios2_semi_cb, "fstat,%x,%x",
 385                           arg0, arg1);
 386            return;
 387        } else {
 388            struct stat s;
 389            result = fstat(arg0, &s);
 390            if (result == 0 && !translate_stat(env, arg1, &s)) {
 391                result = -1;
 392                errno = EFAULT;
 393            }
 394        }
 395        break;
 396    case HOSTED_GETTIMEOFDAY:
 397        /* Only the tv parameter is used.  tz is assumed NULL.  */
 398        GET_ARG(0);
 399        if (use_gdb_syscalls()) {
 400            gdb_do_syscall(nios2_semi_cb, "gettimeofday,%x,%x",
 401                           arg0, 0);
 402            return;
 403        } else {
 404            qemu_timeval tv;
 405            struct gdb_timeval *p;
 406            result = qemu_gettimeofday(&tv);
 407            if (result != 0) {
 408                p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval),
 409                              0);
 410                if (!p) {
 411                    result = -1;
 412                    errno = EFAULT;
 413                } else {
 414                    p->tv_sec = cpu_to_be32(tv.tv_sec);
 415                    p->tv_usec = cpu_to_be64(tv.tv_usec);
 416                    unlock_user(p, arg0, sizeof(struct gdb_timeval));
 417                }
 418            }
 419        }
 420        break;
 421    case HOSTED_ISATTY:
 422        GET_ARG(0);
 423        if (use_gdb_syscalls()) {
 424            gdb_do_syscall(nios2_semi_cb, "isatty,%x", arg0);
 425            return;
 426        } else {
 427            result = isatty(arg0);
 428        }
 429        break;
 430    case HOSTED_SYSTEM:
 431        GET_ARG(0);
 432        GET_ARG(1);
 433        if (use_gdb_syscalls()) {
 434            gdb_do_syscall(nios2_semi_cb, "system,%s",
 435                           arg0, (int)arg1);
 436            return;
 437        } else {
 438            p = lock_user_string(arg0);
 439            if (!p) {
 440                result = -1;
 441                errno = EFAULT;
 442            } else {
 443                result = system(p);
 444                unlock_user(p, arg0, 0);
 445            }
 446        }
 447        break;
 448    default:
 449        qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported "
 450                      "semihosting syscall %d\n", nr);
 451        result = 0;
 452    }
 453failed:
 454    nios2_semi_return_u32(env, result, errno);
 455}
 456