Add qemu 2.4.0
[kvmfornfv.git] / qemu / target-mips / mips-semi.c
diff --git a/qemu/target-mips/mips-semi.c b/qemu/target-mips/mips-semi.c
new file mode 100644 (file)
index 0000000..5050940
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Unified Hosting Interface syscalls.
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/stat.h>
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/softmmu-semi.h"
+#include "exec/semihost.h"
+
+typedef enum UHIOp {
+    UHI_exit = 1,
+    UHI_open = 2,
+    UHI_close = 3,
+    UHI_read = 4,
+    UHI_write = 5,
+    UHI_lseek = 6,
+    UHI_unlink = 7,
+    UHI_fstat = 8,
+    UHI_argc = 9,
+    UHI_argnlen = 10,
+    UHI_argn = 11,
+    UHI_plog = 13,
+    UHI_assert = 14,
+    UHI_pread = 19,
+    UHI_pwrite = 20,
+    UHI_link = 22
+} UHIOp;
+
+typedef struct UHIStat {
+    int16_t uhi_st_dev;
+    uint16_t uhi_st_ino;
+    uint32_t uhi_st_mode;
+    uint16_t uhi_st_nlink;
+    uint16_t uhi_st_uid;
+    uint16_t uhi_st_gid;
+    int16_t uhi_st_rdev;
+    uint64_t uhi_st_size;
+    uint64_t uhi_st_atime;
+    uint64_t uhi_st_spare1;
+    uint64_t uhi_st_mtime;
+    uint64_t uhi_st_spare2;
+    uint64_t uhi_st_ctime;
+    uint64_t uhi_st_spare3;
+    uint64_t uhi_st_blksize;
+    uint64_t uhi_st_blocks;
+    uint64_t uhi_st_spare4[2];
+} UHIStat;
+
+enum UHIOpenFlags {
+    UHIOpen_RDONLY = 0x0,
+    UHIOpen_WRONLY = 0x1,
+    UHIOpen_RDWR   = 0x2,
+    UHIOpen_APPEND = 0x8,
+    UHIOpen_CREAT  = 0x200,
+    UHIOpen_TRUNC  = 0x400,
+    UHIOpen_EXCL   = 0x800
+};
+
+/* Errno values taken from asm-mips/errno.h */
+static uint16_t host_to_mips_errno[] = {
+    [ENAMETOOLONG] = 78,
+#ifdef EOVERFLOW
+    [EOVERFLOW]    = 79,
+#endif
+#ifdef ELOOP
+    [ELOOP]        = 90,
+#endif
+};
+
+static int errno_mips(int err)
+{
+    if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) {
+        return EINVAL;
+    } else if (host_to_mips_errno[err]) {
+        return host_to_mips_errno[err];
+    } else {
+        return err;
+    }
+}
+
+static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
+                               target_ulong vaddr)
+{
+    hwaddr len = sizeof(struct UHIStat);
+    UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
+    if (!dst) {
+        errno = EFAULT;
+        return -1;
+    }
+
+    dst->uhi_st_dev = tswap16(src->st_dev);
+    dst->uhi_st_ino = tswap16(src->st_ino);
+    dst->uhi_st_mode = tswap32(src->st_mode);
+    dst->uhi_st_nlink = tswap16(src->st_nlink);
+    dst->uhi_st_uid = tswap16(src->st_uid);
+    dst->uhi_st_gid = tswap16(src->st_gid);
+    dst->uhi_st_rdev = tswap16(src->st_rdev);
+    dst->uhi_st_size = tswap64(src->st_size);
+    dst->uhi_st_atime = tswap64(src->st_atime);
+    dst->uhi_st_mtime = tswap64(src->st_mtime);
+    dst->uhi_st_ctime = tswap64(src->st_ctime);
+#ifdef _WIN32
+    dst->uhi_st_blksize = 0;
+    dst->uhi_st_blocks = 0;
+#else
+    dst->uhi_st_blksize = tswap64(src->st_blksize);
+    dst->uhi_st_blocks = tswap64(src->st_blocks);
+#endif
+    unlock_user(dst, vaddr, len);
+    return 0;
+}
+
+static int get_open_flags(target_ulong target_flags)
+{
+    int open_flags = 0;
+
+    if (target_flags & UHIOpen_RDWR) {
+        open_flags |= O_RDWR;
+    } else if (target_flags & UHIOpen_WRONLY) {
+        open_flags |= O_WRONLY;
+    } else {
+        open_flags |= O_RDONLY;
+    }
+
+    open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
+    open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0;
+    open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0;
+    open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0;
+
+    return open_flags;
+}
+
+static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
+                         target_ulong len, target_ulong offset)
+{
+    int num_of_bytes;
+    void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
+    if (!dst) {
+        errno = EFAULT;
+        return -1;
+    }
+
+    if (offset) {
+#ifdef _WIN32
+        num_of_bytes = 0;
+#else
+        num_of_bytes = pwrite(fd, dst, len, offset);
+#endif
+    } else {
+        num_of_bytes = write(fd, dst, len);
+    }
+
+    unlock_user(dst, vaddr, 0);
+    return num_of_bytes;
+}
+
+static int read_from_file(CPUMIPSState *env, target_ulong fd,
+                          target_ulong vaddr, target_ulong len,
+                          target_ulong offset)
+{
+    int num_of_bytes;
+    void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
+    if (!dst) {
+        errno = EFAULT;
+        return -1;
+    }
+
+    if (offset) {
+#ifdef _WIN32
+        num_of_bytes = 0;
+#else
+        num_of_bytes = pread(fd, dst, len, offset);
+#endif
+    } else {
+        num_of_bytes = read(fd, dst, len);
+    }
+
+    unlock_user(dst, vaddr, len);
+    return num_of_bytes;
+}
+
+static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
+                               target_ulong vaddr)
+{
+    int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
+    char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
+    if (!dst) {
+        return -1;
+    }
+
+    strcpy(dst, semihosting_get_arg(arg_num));
+
+    unlock_user(dst, vaddr, strsize);
+    return 0;
+}
+
+#define GET_TARGET_STRING(p, addr)              \
+    do {                                        \
+        p = lock_user_string(addr);             \
+        if (!p) {                               \
+            gpr[2] = -1;                        \
+            gpr[3] = EFAULT;                    \
+            goto uhi_done;                      \
+        }                                       \
+    } while (0)
+
+#define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \
+    do {                                                \
+        p = lock_user_string(addr);                     \
+        if (!p) {                                       \
+            gpr[2] = -1;                                \
+            gpr[3] = EFAULT;                            \
+            goto uhi_done;                              \
+        }                                               \
+        p2 = lock_user_string(addr2);                   \
+        if (!p2) {                                      \
+            unlock_user(p, addr, 0);                    \
+            gpr[2] = -1;                                \
+            gpr[3] = EFAULT;                            \
+            goto uhi_done;                              \
+        }                                               \
+    } while (0)
+
+#define FREE_TARGET_STRING(p, gpr)              \
+    do {                                        \
+        unlock_user(p, gpr, 0);                 \
+    } while (0)
+
+void helper_do_semihosting(CPUMIPSState *env)
+{
+    target_ulong *gpr = env->active_tc.gpr;
+    const UHIOp op = gpr[25];
+    char *p, *p2;
+
+    switch (op) {
+    case UHI_exit:
+        qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
+        exit(gpr[4]);
+    case UHI_open:
+        GET_TARGET_STRING(p, gpr[4]);
+        if (!strcmp("/dev/stdin", p)) {
+            gpr[2] = 0;
+        } else if (!strcmp("/dev/stdout", p)) {
+            gpr[2] = 1;
+        } else if (!strcmp("/dev/stderr", p)) {
+            gpr[2] = 2;
+        } else {
+            gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
+            gpr[3] = errno_mips(errno);
+        }
+        FREE_TARGET_STRING(p, gpr[4]);
+        break;
+    case UHI_close:
+        if (gpr[4] < 3) {
+            /* ignore closing stdin/stdout/stderr */
+            gpr[2] = 0;
+            goto uhi_done;
+        }
+        gpr[2] = close(gpr[4]);
+        gpr[3] = errno_mips(errno);
+        break;
+    case UHI_read:
+        gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
+        gpr[3] = errno_mips(errno);
+        break;
+    case UHI_write:
+        gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
+        gpr[3] = errno_mips(errno);
+        break;
+    case UHI_lseek:
+        gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
+        gpr[3] = errno_mips(errno);
+        break;
+    case UHI_unlink:
+        GET_TARGET_STRING(p, gpr[4]);
+        gpr[2] = remove(p);
+        gpr[3] = errno_mips(errno);
+        FREE_TARGET_STRING(p, gpr[4]);
+        break;
+    case UHI_fstat:
+        {
+            struct stat sbuf;
+            memset(&sbuf, 0, sizeof(sbuf));
+            gpr[2] = fstat(gpr[4], &sbuf);
+            gpr[3] = errno_mips(errno);
+            if (gpr[2]) {
+                goto uhi_done;
+            }
+            gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
+            gpr[3] = errno_mips(errno);
+        }
+        break;
+    case UHI_argc:
+        gpr[2] = semihosting_get_argc();
+        break;
+    case UHI_argnlen:
+        if (gpr[4] >= semihosting_get_argc()) {
+            gpr[2] = -1;
+            goto uhi_done;
+        }
+        gpr[2] = strlen(semihosting_get_arg(gpr[4]));
+        break;
+    case UHI_argn:
+        if (gpr[4] >= semihosting_get_argc()) {
+            gpr[2] = -1;
+            goto uhi_done;
+        }
+        gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
+        break;
+    case UHI_plog:
+        GET_TARGET_STRING(p, gpr[4]);
+        p2 = strstr(p, "%d");
+        if (p2) {
+            int char_num = p2 - p;
+            char *buf = g_malloc(char_num + 1);
+            strncpy(buf, p, char_num);
+            buf[char_num] = '\0';
+            gpr[2] = printf("%s%d%s", buf, (int)gpr[5], p2 + 2);
+            g_free(buf);
+        } else {
+            gpr[2] = printf("%s", p);
+        }
+        FREE_TARGET_STRING(p, gpr[4]);
+        break;
+    case UHI_assert:
+        GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
+        printf("assertion '");
+        printf("\"%s\"", p);
+        printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
+        FREE_TARGET_STRING(p2, gpr[5]);
+        FREE_TARGET_STRING(p, gpr[4]);
+        abort();
+        break;
+    case UHI_pread:
+        gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
+        gpr[3] = errno_mips(errno);
+        break;
+    case UHI_pwrite:
+        gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
+        gpr[3] = errno_mips(errno);
+        break;
+#ifndef _WIN32
+    case UHI_link:
+        GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
+        gpr[2] = link(p, p2);
+        gpr[3] = errno_mips(errno);
+        FREE_TARGET_STRING(p2, gpr[5]);
+        FREE_TARGET_STRING(p, gpr[4]);
+        break;
+#endif
+    default:
+        fprintf(stderr, "Unknown UHI operation %d\n", op);
+        abort();
+    }
+uhi_done:
+    return;
+}