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#include "cpu.h"
  26#include "exec/gdbstub.h"
  27#include "semihosting/softmmu-uaccess.h"
  28#include "qemu/log.h"
  29
  30#define HOSTED_EXIT  0
  31#define HOSTED_INIT_SIM 1
  32#define HOSTED_OPEN 2
  33#define HOSTED_CLOSE 3
  34#define HOSTED_READ 4
  35#define HOSTED_WRITE 5
  36#define HOSTED_LSEEK 6
  37#define HOSTED_RENAME 7
  38#define HOSTED_UNLINK 8
  39#define HOSTED_STAT 9
  40#define HOSTED_FSTAT 10
  41#define HOSTED_GETTIMEOFDAY 11
  42#define HOSTED_ISATTY 12
  43#define HOSTED_SYSTEM 13
  44
  45static int translate_openflags(int flags)
  46{
  47    int hf;
  48
  49    if (flags & GDB_O_WRONLY) {
  50        hf = O_WRONLY;
  51    } else if (flags & GDB_O_RDWR) {
  52        hf = O_RDWR;
  53    } else {
  54        hf = O_RDONLY;
  55    }
  56
  57    if (flags & GDB_O_APPEND) {
  58        hf |= O_APPEND;
  59    }
  60    if (flags & GDB_O_CREAT) {
  61        hf |= O_CREAT;
  62    }
  63    if (flags & GDB_O_TRUNC) {
  64        hf |= O_TRUNC;
  65    }
  66    if (flags & GDB_O_EXCL) {
  67        hf |= O_EXCL;
  68    }
  69
  70    return hf;
  71}
  72
  73static bool translate_stat(CPUNios2State *env, target_ulong addr,
  74                           struct stat *s)
  75{
  76    struct gdb_stat *p;
  77
  78    p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0);
  79
  80    if (!p) {
  81        return false;
  82    }
  83    p->gdb_st_dev = cpu_to_be32(s->st_dev);
  84    p->gdb_st_ino = cpu_to_be32(s->st_ino);
  85    p->gdb_st_mode = cpu_to_be32(s->st_mode);
  86    p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
  87    p->gdb_st_uid = cpu_to_be32(s->st_uid);
  88    p->gdb_st_gid = cpu_to_be32(s->st_gid);
  89    p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
  90    p->gdb_st_size = cpu_to_be64(s->st_size);
  91#ifdef _WIN32
  92    /* Windows stat is missing some fields.  */
  93    p->gdb_st_blksize = 0;
  94    p->gdb_st_blocks = 0;
  95#else
  96    p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
  97    p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
  98#endif
  99    p->gdb_st_atime = cpu_to_be32(s->st_atime);
 100    p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
 101    p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
 102    unlock_user(p, addr, sizeof(struct gdb_stat));
 103    return true;
 104}
 105
 106static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err)
 107{
 108    Nios2CPU *cpu = NIOS2_CPU(cs);
 109    CPUNios2State *env = &cpu->env;
 110    target_ulong args = env->regs[R_ARG1];
 111
 112    if (put_user_u32(ret, args) ||
 113        put_user_u32(err, args + 4)) {
 114        /*
 115         * The nios2 semihosting ABI does not provide any way to report this
 116         * error to the guest, so the best we can do is log it in qemu.
 117         * It is always a guest error not to pass us a valid argument block.
 118         */
 119        qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
 120                      "discarded because argument block not writable\n");
 121    }
 122}
 123
 124static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err)
 125{
 126    Nios2CPU *cpu = NIOS2_CPU(cs);
 127    CPUNios2State *env = &cpu->env;
 128    target_ulong args = env->regs[R_ARG1];
 129
 130    if (put_user_u32(ret >> 32, args) ||
 131        put_user_u32(ret, args + 4) ||
 132        put_user_u32(err, args + 8)) {
 133        /* No way to report this via nios2 semihosting ABI; just log it */
 134        qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
 135                      "discarded because argument block not writable\n");
 136    }
 137}
 138
 139/*
 140 * Read the input value from the argument block; fail the semihosting
 141 * call if the memory read fails.
 142 */
 143#define GET_ARG(n) do {                                 \
 144    if (get_user_ual(arg ## n, args + (n) * 4)) {       \
 145        result = -1;                                    \
 146        errno = EFAULT;                                 \
 147        goto failed;                                    \
 148    }                                                   \
 149} while (0)
 150
 151void do_nios2_semihosting(CPUNios2State *env)
 152{
 153    CPUState *cs = env_cpu(env);
 154    int nr;
 155    uint32_t args;
 156    target_ulong arg0, arg1, arg2, arg3;
 157    void *p;
 158    void *q;
 159    uint32_t len;
 160    uint32_t result;
 161
 162    nr = env->regs[R_ARG0];
 163    args = env->regs[R_ARG1];
 164    switch (nr) {
 165    case HOSTED_EXIT:
 166        gdb_exit(env->regs[R_ARG0]);
 167        exit(env->regs[R_ARG0]);
 168    case HOSTED_OPEN:
 169        GET_ARG(0);
 170        GET_ARG(1);
 171        GET_ARG(2);
 172        GET_ARG(3);
 173        if (use_gdb_syscalls()) {
 174            gdb_do_syscall(nios2_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1,
 175                           arg2, arg3);
 176            return;
 177        } else {
 178            p = lock_user_string(arg0);
 179            if (!p) {
 180                result = -1;
 181                errno = EFAULT;
 182            } else {
 183                result = open(p, translate_openflags(arg2), arg3);
 184                unlock_user(p, arg0, 0);
 185            }
 186        }
 187        break;
 188    case HOSTED_CLOSE:
 189        {
 190            /* Ignore attempts to close stdin/out/err.  */
 191            GET_ARG(0);
 192            int fd = arg0;
 193            if (fd > 2) {
 194                if (use_gdb_syscalls()) {
 195                    gdb_do_syscall(nios2_semi_u32_cb, "close,%x", arg0);
 196                    return;
 197                } else {
 198                    result = close(fd);
 199                }
 200            } else {
 201                result = 0;
 202            }
 203            break;
 204        }
 205    case HOSTED_READ:
 206        GET_ARG(0);
 207        GET_ARG(1);
 208        GET_ARG(2);
 209        len = arg2;
 210        if (use_gdb_syscalls()) {
 211            gdb_do_syscall(nios2_semi_u32_cb, "read,%x,%x,%x",
 212                           arg0, arg1, len);
 213            return;
 214        } else {
 215            p = lock_user(VERIFY_WRITE, arg1, len, 0);
 216            if (!p) {
 217                result = -1;
 218                errno = EFAULT;
 219            } else {
 220                result = read(arg0, p, len);
 221                unlock_user(p, arg1, len);
 222            }
 223        }
 224        break;
 225    case HOSTED_WRITE:
 226        GET_ARG(0);
 227        GET_ARG(1);
 228        GET_ARG(2);
 229        len = arg2;
 230        if (use_gdb_syscalls()) {
 231            gdb_do_syscall(nios2_semi_u32_cb, "write,%x,%x,%x",
 232                           arg0, arg1, len);
 233            return;
 234        } else {
 235            p = lock_user(VERIFY_READ, arg1, len, 1);
 236            if (!p) {
 237                result = -1;
 238                errno = EFAULT;
 239            } else {
 240                result = write(arg0, p, len);
 241                unlock_user(p, arg0, 0);
 242            }
 243        }
 244        break;
 245    case HOSTED_LSEEK:
 246        {
 247            uint64_t off;
 248            GET_ARG(0);
 249            GET_ARG(1);
 250            GET_ARG(2);
 251            GET_ARG(3);
 252            off = (uint32_t)arg2 | ((uint64_t)arg1 << 32);
 253            if (use_gdb_syscalls()) {
 254                gdb_do_syscall(nios2_semi_u64_cb, "lseek,%x,%lx,%x",
 255                               arg0, off, arg3);
 256            } else {
 257                off = lseek(arg0, off, arg3);
 258                nios2_semi_u64_cb(cs, off, errno);
 259            }
 260            return;
 261        }
 262    case HOSTED_RENAME:
 263        GET_ARG(0);
 264        GET_ARG(1);
 265        GET_ARG(2);
 266        GET_ARG(3);
 267        if (use_gdb_syscalls()) {
 268            gdb_do_syscall(nios2_semi_u32_cb, "rename,%s,%s",
 269                           arg0, (int)arg1, arg2, (int)arg3);
 270            return;
 271        } else {
 272            p = lock_user_string(arg0);
 273            q = lock_user_string(arg2);
 274            if (!p || !q) {
 275                result = -1;
 276                errno = EFAULT;
 277            } else {
 278                result = rename(p, q);
 279            }
 280            unlock_user(p, arg0, 0);
 281            unlock_user(q, arg2, 0);
 282        }
 283        break;
 284    case HOSTED_UNLINK:
 285        GET_ARG(0);
 286        GET_ARG(1);
 287        if (use_gdb_syscalls()) {
 288            gdb_do_syscall(nios2_semi_u32_cb, "unlink,%s",
 289                           arg0, (int)arg1);
 290            return;
 291        } else {
 292            p = lock_user_string(arg0);
 293            if (!p) {
 294                result = -1;
 295                errno = EFAULT;
 296            } else {
 297                result = unlink(p);
 298                unlock_user(p, arg0, 0);
 299            }
 300        }
 301        break;
 302    case HOSTED_STAT:
 303        GET_ARG(0);
 304        GET_ARG(1);
 305        GET_ARG(2);
 306        if (use_gdb_syscalls()) {
 307            gdb_do_syscall(nios2_semi_u32_cb, "stat,%s,%x",
 308                           arg0, (int)arg1, arg2);
 309            return;
 310        } else {
 311            struct stat s;
 312            p = lock_user_string(arg0);
 313            if (!p) {
 314                result = -1;
 315                errno = EFAULT;
 316            } else {
 317                result = stat(p, &s);
 318                unlock_user(p, arg0, 0);
 319            }
 320            if (result == 0 && !translate_stat(env, arg2, &s)) {
 321                result = -1;
 322                errno = EFAULT;
 323            }
 324        }
 325        break;
 326    case HOSTED_FSTAT:
 327        GET_ARG(0);
 328        GET_ARG(1);
 329        if (use_gdb_syscalls()) {
 330            gdb_do_syscall(nios2_semi_u32_cb, "fstat,%x,%x",
 331                           arg0, arg1);
 332            return;
 333        } else {
 334            struct stat s;
 335            result = fstat(arg0, &s);
 336            if (result == 0 && !translate_stat(env, arg1, &s)) {
 337                result = -1;
 338                errno = EFAULT;
 339            }
 340        }
 341        break;
 342    case HOSTED_GETTIMEOFDAY:
 343        /* Only the tv parameter is used.  tz is assumed NULL.  */
 344        GET_ARG(0);
 345        if (use_gdb_syscalls()) {
 346            gdb_do_syscall(nios2_semi_u32_cb, "gettimeofday,%x,%x",
 347                           arg0, 0);
 348            return;
 349        } else {
 350            struct gdb_timeval *p;
 351            int64_t rt = g_get_real_time();
 352            p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0);
 353            if (!p) {
 354                result = -1;
 355                errno = EFAULT;
 356            } else {
 357                result = 0;
 358                p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC);
 359                p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC);
 360                unlock_user(p, arg0, sizeof(struct gdb_timeval));
 361            }
 362        }
 363        break;
 364    case HOSTED_ISATTY:
 365        GET_ARG(0);
 366        if (use_gdb_syscalls()) {
 367            gdb_do_syscall(nios2_semi_u32_cb, "isatty,%x", arg0);
 368            return;
 369        } else {
 370            result = isatty(arg0);
 371        }
 372        break;
 373    case HOSTED_SYSTEM:
 374        GET_ARG(0);
 375        GET_ARG(1);
 376        if (use_gdb_syscalls()) {
 377            gdb_do_syscall(nios2_semi_u32_cb, "system,%s",
 378                           arg0, (int)arg1);
 379            return;
 380        } else {
 381            p = lock_user_string(arg0);
 382            if (!p) {
 383                result = -1;
 384                errno = EFAULT;
 385            } else {
 386                result = system(p);
 387                unlock_user(p, arg0, 0);
 388            }
 389        }
 390        break;
 391    default:
 392        qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported "
 393                      "semihosting syscall %d\n", nr);
 394        result = 0;
 395    }
 396failed:
 397    nios2_semi_u32_cb(cs, result, errno);
 398}
 399