summary refs log tree commit diff stats
path: root/target-lm32
diff options
context:
space:
mode:
Diffstat (limited to 'target-lm32')
-rw-r--r--target-lm32/Makefile.objs1
-rw-r--r--target-lm32/README15
-rw-r--r--target-lm32/cpu.h1
-rw-r--r--target-lm32/helper.c14
-rw-r--r--target-lm32/helper.h4
-rw-r--r--target-lm32/lm32-semi.c215
-rw-r--r--target-lm32/op_helper.c14
-rw-r--r--target-lm32/translate.c6
8 files changed, 241 insertions, 29 deletions
diff --git a/target-lm32/Makefile.objs b/target-lm32/Makefile.objs
index 40236876c8..c3e1bd6bd6 100644
--- a/target-lm32/Makefile.objs
+++ b/target-lm32/Makefile.objs
@@ -1,3 +1,4 @@
 obj-y += translate.o op_helper.o helper.o cpu.o
 obj-y += gdbstub.o
+obj-y += lm32-semi.o
 obj-$(CONFIG_SOFTMMU) += machine.o
diff --git a/target-lm32/README b/target-lm32/README
index a1c2c7eb1e..ba3508a711 100644
--- a/target-lm32/README
+++ b/target-lm32/README
@@ -16,14 +16,13 @@ This will make serial0 (the lm32_uart) and serial1 (the JTAG UART)
 available as virtual consoles.
 
 
-Programmatically terminate the emulator
-----------------------------------------
-Originally neither the LatticeMico32 nor its peripherals support a
-mechanism to shut down the machine. Emulation aware programs can write to a
-to a special register within the system control block to shut down the
-virtual machine.  For more details see hw/lm32_sys.c. The lm32-evr is the
-first BSP which instantiate this model. A (32 bit) write to 0xfff0000
-causes a vm shutdown.
+Semihosting
+-----------
+Semihosting on this target is supported. Some system calls like read, write
+and exit are executed on the host if semihosting is enabled. See
+target/lm32-semi.c for all supported system calls. Emulation aware programs
+can use this mechanism to shut down the virtual machine and print to the
+host console. See the tcg tests for an example.
 
 
 Special instructions
diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h
index 24bde78502..70600aa47a 100644
--- a/target-lm32/cpu.h
+++ b/target-lm32/cpu.h
@@ -217,6 +217,7 @@ void lm32_breakpoint_remove(CPULM32State *env, int index);
 void lm32_watchpoint_insert(CPULM32State *env, int index, target_ulong address,
         lm32_wp_t wp_type);
 void lm32_watchpoint_remove(CPULM32State *env, int index);
+bool lm32_cpu_do_semihosting(CPUState *cs);
 
 static inline CPULM32State *cpu_init(const char *cpu_model)
 {
diff --git a/target-lm32/helper.c b/target-lm32/helper.c
index 783aa16a45..1bca1961af 100644
--- a/target-lm32/helper.c
+++ b/target-lm32/helper.c
@@ -1,7 +1,7 @@
 /*
  *  LatticeMico32 helper routines.
  *
- *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
+ *  Copyright (c) 2010-2014 Michael Walle <michael@walle.cc>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -19,6 +19,7 @@
 
 #include "cpu.h"
 #include "qemu/host-utils.h"
+#include "sysemu/sysemu.h"
 
 int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
                               int mmu_idx)
@@ -159,11 +160,20 @@ void lm32_cpu_do_interrupt(CPUState *cs)
             "exception at pc=%x type=%x\n", env->pc, cs->exception_index);
 
     switch (cs->exception_index) {
+    case EXCP_SYSTEMCALL:
+        if (unlikely(semihosting_enabled)) {
+            /* do_semicall() returns true if call was handled. Otherwise
+             * do the normal exception handling. */
+            if (lm32_cpu_do_semihosting(cs)) {
+                env->pc += 4;
+                break;
+            }
+        }
+        /* fall through */
     case EXCP_INSN_BUS_ERROR:
     case EXCP_DATA_BUS_ERROR:
     case EXCP_DIVIDE_BY_ZERO:
     case EXCP_IRQ:
-    case EXCP_SYSTEMCALL:
         /* non-debug exceptions */
         env->regs[R_EA] = env->pc;
         env->ie |= (env->ie & IE_IE) ? IE_EIE : 0;
diff --git a/target-lm32/helper.h b/target-lm32/helper.h
index f4442e0a93..445578c439 100644
--- a/target-lm32/helper.h
+++ b/target-lm32/helper.h
@@ -1,5 +1,3 @@
-#include "exec/def-helper.h"
-
 DEF_HELPER_2(raise_exception, void, env, i32)
 DEF_HELPER_1(hlt, void, env)
 DEF_HELPER_3(wcsr_bp, void, env, i32, i32)
@@ -14,5 +12,3 @@ DEF_HELPER_1(rcsr_ip, i32, env)
 DEF_HELPER_1(rcsr_jtx, i32, env)
 DEF_HELPER_1(rcsr_jrx, i32, env)
 DEF_HELPER_1(ill, void, env)
-
-#include "exec/def-helper.h"
diff --git a/target-lm32/lm32-semi.c b/target-lm32/lm32-semi.c
new file mode 100644
index 0000000000..ec6524f376
--- /dev/null
+++ b/target-lm32/lm32-semi.c
@@ -0,0 +1,215 @@
+/*
+ *  Lattice Mico32 semihosting syscall interface
+ *
+ *  Copyright (c) 2014 Michael Walle <michael@walle.cc>
+ *
+ * Based on target-m68k/m68k-semi.c, which is
+ *  Copyright (c) 2005-2007 CodeSourcery.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stddef.h>
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "qemu/log.h"
+#include "exec/softmmu-semi.h"
+
+enum {
+    TARGET_SYS_exit    = 1,
+    TARGET_SYS_open    = 2,
+    TARGET_SYS_close   = 3,
+    TARGET_SYS_read    = 4,
+    TARGET_SYS_write   = 5,
+    TARGET_SYS_lseek   = 6,
+    TARGET_SYS_fstat   = 10,
+    TARGET_SYS_stat    = 15,
+};
+
+enum {
+    NEWLIB_O_RDONLY    =   0x0,
+    NEWLIB_O_WRONLY    =   0x1,
+    NEWLIB_O_RDWR      =   0x2,
+    NEWLIB_O_APPEND    =   0x8,
+    NEWLIB_O_CREAT     = 0x200,
+    NEWLIB_O_TRUNC     = 0x400,
+    NEWLIB_O_EXCL      = 0x800,
+};
+
+static int translate_openflags(int flags)
+{
+    int hf;
+
+    if (flags & NEWLIB_O_WRONLY) {
+        hf = O_WRONLY;
+    } else if (flags & NEWLIB_O_RDWR) {
+        hf = O_RDWR;
+    } else {
+        hf = O_RDONLY;
+    }
+
+    if (flags & NEWLIB_O_APPEND) {
+        hf |= O_APPEND;
+    }
+
+    if (flags & NEWLIB_O_CREAT) {
+        hf |= O_CREAT;
+    }
+
+    if (flags & NEWLIB_O_TRUNC) {
+        hf |= O_TRUNC;
+    }
+
+    if (flags & NEWLIB_O_EXCL) {
+        hf |= O_EXCL;
+    }
+
+    return hf;
+}
+
+struct newlib_stat {
+    int16_t     newlib_st_dev;     /* device */
+    uint16_t    newlib_st_ino;     /* inode */
+    uint16_t    newlib_st_mode;    /* protection */
+    uint16_t    newlib_st_nlink;   /* number of hard links */
+    uint16_t    newlib_st_uid;     /* user ID of owner */
+    uint16_t    newlib_st_gid;     /* group ID of owner */
+    int16_t     newlib_st_rdev;    /* device type (if inode device) */
+    int32_t     newlib_st_size;    /* total size, in bytes */
+    int32_t     newlib_st_atime;   /* time of last access */
+    uint32_t    newlib_st_spare1;
+    int32_t     newlib_st_mtime;   /* time of last modification */
+    uint32_t    newlib_st_spare2;
+    int32_t     newlib_st_ctime;   /* time of last change */
+    uint32_t    newlib_st_spare3;
+} QEMU_PACKED;
+
+static int translate_stat(CPULM32State *env, target_ulong addr,
+        struct stat *s)
+{
+    struct newlib_stat *p;
+
+    p = lock_user(VERIFY_WRITE, addr, sizeof(struct newlib_stat), 0);
+    if (!p) {
+        return 0;
+    }
+    p->newlib_st_dev = cpu_to_be16(s->st_dev);
+    p->newlib_st_ino = cpu_to_be16(s->st_ino);
+    p->newlib_st_mode = cpu_to_be16(s->st_mode);
+    p->newlib_st_nlink = cpu_to_be16(s->st_nlink);
+    p->newlib_st_uid = cpu_to_be16(s->st_uid);
+    p->newlib_st_gid = cpu_to_be16(s->st_gid);
+    p->newlib_st_rdev = cpu_to_be16(s->st_rdev);
+    p->newlib_st_size = cpu_to_be32(s->st_size);
+    p->newlib_st_atime = cpu_to_be32(s->st_atime);
+    p->newlib_st_mtime = cpu_to_be32(s->st_mtime);
+    p->newlib_st_ctime = cpu_to_be32(s->st_ctime);
+    unlock_user(p, addr, sizeof(struct newlib_stat));
+
+    return 1;
+}
+
+bool lm32_cpu_do_semihosting(CPUState *cs)
+{
+    LM32CPU *cpu = LM32_CPU(cs);
+    CPULM32State *env = &cpu->env;
+
+    int ret = -1;
+    target_ulong nr, arg0, arg1, arg2;
+    void *p;
+    struct stat s;
+
+    nr = env->regs[R_R8];
+    arg0 = env->regs[R_R1];
+    arg1 = env->regs[R_R2];
+    arg2 = env->regs[R_R3];
+
+    switch (nr) {
+    case TARGET_SYS_exit:
+        /* void _exit(int rc) */
+        exit(arg0);
+
+    case TARGET_SYS_open:
+        /* int open(const char *pathname, int flags) */
+        p = lock_user_string(arg0);
+        if (!p) {
+            ret = -1;
+        } else {
+            ret = open(p, translate_openflags(arg2));
+            unlock_user(p, arg0, 0);
+        }
+        break;
+
+    case TARGET_SYS_read:
+        /* ssize_t read(int fd, const void *buf, size_t count) */
+        p = lock_user(VERIFY_WRITE, arg1, arg2, 0);
+        if (!p) {
+            ret = -1;
+        } else {
+            ret = read(arg0, p, arg2);
+            unlock_user(p, arg1, arg2);
+        }
+        break;
+
+    case TARGET_SYS_write:
+        /* ssize_t write(int fd, const void *buf, size_t count) */
+        p = lock_user(VERIFY_READ, arg1, arg2, 1);
+        if (!p) {
+            ret = -1;
+        } else {
+            ret = write(arg0, p, arg2);
+            unlock_user(p, arg1, 0);
+        }
+        break;
+
+    case TARGET_SYS_close:
+        /* int close(int fd) */
+        /* don't close stdin/stdout/stderr */
+        if (arg0 > 2) {
+            ret = close(arg0);
+        } else {
+            ret = 0;
+        }
+        break;
+
+    case TARGET_SYS_lseek:
+        /* off_t lseek(int fd, off_t offset, int whence */
+        ret = lseek(arg0, arg1, arg2);
+        break;
+
+    case TARGET_SYS_stat:
+        /* int stat(const char *path, struct stat *buf) */
+        p = lock_user_string(arg0);
+        if (!p) {
+            ret = -1;
+        } else {
+            ret = stat(p, &s);
+            unlock_user(p, arg0, 0);
+            if (translate_stat(env, arg1, &s) == 0) {
+                ret = -1;
+            }
+        }
+        break;
+
+    case TARGET_SYS_fstat:
+        /* int stat(int fd, struct stat *buf) */
+        ret = fstat(arg0, &s);
+        if (ret == 0) {
+            if (translate_stat(env, arg1, &s) == 0) {
+                ret = -1;
+            }
+        }
+        break;
+
+    default:
+        /* unhandled */
+        return false;
+    }
+
+    env->regs[R_R1] = ret;
+    return true;
+}
diff --git a/target-lm32/op_helper.c b/target-lm32/op_helper.c
index 2f36b7b053..308742a74e 100644
--- a/target-lm32/op_helper.c
+++ b/target-lm32/op_helper.c
@@ -1,28 +1,18 @@
 #include <assert.h>
 #include "cpu.h"
-#include "helper.h"
+#include "exec/helper-proto.h"
 #include "qemu/host-utils.h"
 
 #include "hw/lm32/lm32_pic.h"
 #include "hw/char/lm32_juart.h"
 
-#include "exec/softmmu_exec.h"
+#include "exec/cpu_ldst.h"
 
 #ifndef CONFIG_USER_ONLY
 #include "sysemu/sysemu.h"
 #endif
 
 #if !defined(CONFIG_USER_ONLY)
-#define MMUSUFFIX _mmu
-#define SHIFT 0
-#include "exec/softmmu_template.h"
-#define SHIFT 1
-#include "exec/softmmu_template.h"
-#define SHIFT 2
-#include "exec/softmmu_template.h"
-#define SHIFT 3
-#include "exec/softmmu_template.h"
-
 void raise_exception(CPULM32State *env, int index)
 {
     CPUState *cs = CPU(lm32_env_get_cpu(env));
diff --git a/target-lm32/translate.c b/target-lm32/translate.c
index c8abd1f27e..a51ade9a15 100644
--- a/target-lm32/translate.c
+++ b/target-lm32/translate.c
@@ -19,13 +19,13 @@
 
 #include "cpu.h"
 #include "disas/disas.h"
-#include "helper.h"
+#include "exec/helper-proto.h"
 #include "tcg-op.h"
 
+#include "exec/cpu_ldst.h"
 #include "hw/lm32/lm32_pic.h"
 
-#define GEN_HELPER 1
-#include "helper.h"
+#include "exec/helper-gen.h"
 
 #define DISAS_LM32 1
 #if DISAS_LM32