Patchwork [3/6] add implementation of MIPS semihosting

login
register
mail settings
Submitter Nathan Froyd
Date Aug. 3, 2009, 2:45 p.m.
Message ID <1249310711-8873-4-git-send-email-froydnj@codesourcery.com>
Download mbox | patch
Permalink /patch/30601/
State Superseded
Headers show

Comments

Nathan Froyd - Aug. 3, 2009, 2:45 p.m.
Later patches will call into this file via do_mips_semihosting.

Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
 Makefile.target   |    1 +
 mips-semi.c       |  216 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 target-mips/cpu.h |    1 +
 3 files changed, 218 insertions(+), 0 deletions(-)
 create mode 100644 mips-semi.c

Patch

diff --git a/Makefile.target b/Makefile.target
index 49ba08d..676a9f9 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -370,6 +370,7 @@  obj-mips-y += piix_pci.o parallel.o cirrus_vga.o pcspk.o $(sound-obj-y)
 obj-mips-y += mipsnet.o
 obj-mips-y += pflash_cfi01.o
 obj-mips-y += vmware_vga.o
+obj-mips-y += mips-semi.o
 
 ifeq ($(TARGET_BASE_ARCH), mips)
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
diff --git a/mips-semi.c b/mips-semi.c
new file mode 100644
index 0000000..77ecff3
--- /dev/null
+++ b/mips-semi.c
@@ -0,0 +1,216 @@ 
+
+/*
+ * MIPS MDI semihosting syscalls
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Written by Nathan Froyd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "cpu.h"
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "gdbstub.h"
+#include "softmmu-semi.h"
+
+#define HOSTED_OPEN 0
+#define HOSTED_CLOSE 1
+#define HOSTED_READ 2
+#define HOSTED_WRITE 3
+#define HOSTED_GETCHAR 4
+#define HOSTED_PUTCHAR 5
+#define HOSTED_LSEEK32 6
+#define HOSTED_GETTIME 7
+#define HOSTED_EXIT 8
+#define HOSTED_MOVED 9
+#define HOSTED_GETARGS 10
+#define HOSTED_ISATTY 11
+#define HOSTED_PROFIL 12
+#define HOSTED_SIGHOOK 13
+
+#define ARG(n) env->active_tc.gpr[4 + n]
+
+static void mips_store_result(CPUState *env, target_ulong ret, target_ulong err)
+{
+    env->active_tc.PC = env->active_tc.gpr[31];
+    env->active_tc.gpr[2] = ret;
+    env->active_tc.gpr[3] = err;
+}
+
+static void mips_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+    mips_store_result(env, ret, err);
+}
+
+#define GDB_O_RDONLY   0x0
+#define GDB_O_WRONLY   0x1
+#define GDB_O_RDWR     0x2
+#define GDB_O_APPEND   0x8
+#define GDB_O_CREAT  0x200
+#define GDB_O_TRUNC  0x400
+#define GDB_O_EXCL   0x800
+
+static int translate_openflags(int flags)
+{
+    int hf;
+
+    if (flags & GDB_O_WRONLY)
+        hf = O_WRONLY;
+    else if (flags & GDB_O_RDWR)
+        hf = O_RDWR;
+    else
+        hf = O_RDONLY;
+
+    if (flags & GDB_O_APPEND) hf |= O_APPEND;
+    if (flags & GDB_O_CREAT) hf |= O_CREAT;
+    if (flags & GDB_O_TRUNC) hf |= O_TRUNC;
+    if (flags & GDB_O_EXCL) hf |= O_EXCL;
+
+    return hf;
+}
+
+void do_mips_semihosting(CPUState *env)
+{
+    target_ulong result;
+    void *p;
+    uint32_t len;
+    target_ulong err = 0;
+    char *s;
+
+    switch (env->active_tc.gpr[2]) {
+    case HOSTED_OPEN:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(mips_semi_cb, "open,%s,%x,%x", ARG(0),
+                           target_strlen(ARG(0))+1, ARG(1), ARG(2));
+            return;
+        } else {
+            if (!(s = lock_user_string(ARG(0)))) {
+                result = -1;
+            } else {
+                result = open(s, translate_openflags(ARG(1)), ARG(2));
+            }
+            unlock_user(s, ARG(0), 0);
+        }
+        break;
+    case HOSTED_CLOSE:
+        /* Ignore attempts to close stdin/out/err */
+        if (ARG(0) > 2) {
+            if (use_gdb_syscalls()) {
+                gdb_do_syscall(mips_semi_cb, "close,%x", ARG(0));
+                return;
+            } else {
+                result = close(ARG(0));
+            }
+        } else {
+            result = 0;
+        }
+        break;
+    case HOSTED_READ:
+        len = ARG(2);
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(mips_semi_cb, "read,%x,%x,%x",
+                           ARG(0), ARG(1), len);
+            return;
+        } else {
+            if (!(p = lock_user(VERIFY_WRITE, ARG(1), len, 0))) {
+                result = -1;
+            } else {
+                result = read(ARG(0), p, len);
+                unlock_user(p, ARG(1), len);
+            }
+        }
+        break;
+    case HOSTED_WRITE:
+        len = ARG(2);
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(mips_semi_cb, "write,%x,%x,%x",
+                           ARG(0), ARG(1), len);
+            return;
+        } else {
+            if (!(p = lock_user(VERIFY_READ, ARG(1), len, 1))) {
+                result = -1;
+            } else {
+                result = write(ARG(0), p, len);
+                unlock_user(p, ARG(1), len);
+            }
+        }
+        break;
+    case HOSTED_LSEEK32:
+        {
+            off_t off = (target_long) ARG(1);
+            if (use_gdb_syscalls()) {
+                gdb_do_syscall(mips_semi_cb, "lseek,%x,%lx,%x",
+                               ARG(0), off, ARG(2));
+                return;
+            } else {
+                off = lseek(ARG(0), off, ARG(2));
+                result = (uint32_t) off;
+            }
+        }
+        break;
+    case HOSTED_GETTIME:
+        {
+            qemu_timeval tv;
+            result = qemu_gettimeofday(&tv);
+            if (!result) {
+                result = tv.tv_sec;
+                err = tv.tv_usec;
+            } else {
+                result = -1;
+                err = errno;
+            }
+        }
+        break;
+    case HOSTED_EXIT:
+        exit(ARG(0));
+        break;
+    case HOSTED_ISATTY:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(mips_semi_cb, "isatty,%x", ARG(0));
+            return;
+        } else {
+            result = isatty(ARG(0));
+        }
+        break;
+    case HOSTED_GETARGS:
+        /* argc gets placed in A0, argv gets copied onto the stack and
+           the address of the copy placed in A1.  We have nothing to
+           provide in terms of argc/argv, so just stuff NULL in
+           each.  */
+        ARG(1) = ARG(0) = 0;
+        result = 0;
+        break;
+    case HOSTED_GETCHAR:
+    case HOSTED_PUTCHAR:
+    case HOSTED_MOVED:
+    case HOSTED_PROFIL:
+    case HOSTED_SIGHOOK:
+    default:
+        result = -1;
+        err = 88;               /* ENOSYS */
+        break;
+    }
+
+    mips_store_result(env, result, err);
+}
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 789176b..1510244 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -587,6 +587,7 @@  int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
 void do_interrupt (CPUState *env);
 void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra);
 
+void do_mips_semihosting(CPUState *env);
 static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
 {
     env->active_tc.PC = tb->pc;