summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS8
-rw-r--r--Makefile14
-rw-r--r--arch_init.c2
-rw-r--r--arch_init.h1
-rw-r--r--block.h1
-rw-r--r--block/iscsi.c59
-rw-r--r--block/qcow2.c32
-rw-r--r--block/qed-check.c26
-rw-r--r--block/qed.c11
-rw-r--r--block/qed.h5
-rw-r--r--blockdev.c1
-rwxr-xr-xconfigure39
-rw-r--r--cpu-exec.c1
-rw-r--r--default-configs/unicore32-softmmu.mak4
-rw-r--r--dma-helpers.c1
-rw-r--r--exec.c2
-rw-r--r--hw/Makefile.objs7
-rw-r--r--hw/ide/ahci.c44
-rw-r--r--hw/ide/internal.h1
-rw-r--r--hw/puv3.c131
-rw-r--r--hw/puv3.h49
-rw-r--r--hw/puv3_dma.c109
-rw-r--r--hw/puv3_gpio.c141
-rw-r--r--hw/puv3_intc.c135
-rw-r--r--hw/puv3_ost.c151
-rw-r--r--hw/puv3_pm.c149
-rw-r--r--hw/scsi-disk.c112
-rw-r--r--hw/unicore32/Makefile.objs6
-rw-r--r--hw/virtio-blk.c31
-rw-r--r--hw/virtio-blk.h4
-rw-r--r--hw/virtio-scsi.c10
-rw-r--r--linux-user/main.c3
-rw-r--r--qemu-common.h1
-rw-r--r--qemu-doc.texi5
-rw-r--r--qemu-img.c2
-rw-r--r--qemu-options.hx8
-rw-r--r--qemu-tool.c5
-rw-r--r--target-unicore32/Makefile.objs4
-rw-r--r--target-unicore32/cpu.c19
-rw-r--r--target-unicore32/cpu.h18
-rw-r--r--target-unicore32/helper.c511
-rw-r--r--target-unicore32/helper.h17
-rw-r--r--target-unicore32/machine.c23
-rw-r--r--target-unicore32/op_helper.c44
-rw-r--r--target-unicore32/softmmu.c267
-rw-r--r--target-unicore32/translate.c159
-rw-r--r--target-unicore32/ucf64_helper.c345
-rwxr-xr-xtests/qemu-iotests/0391
-rw-r--r--tests/qemu-iotests/039.out6
-rw-r--r--tests/qemu-iotests/common.rc34
-rw-r--r--vl.c5
51 files changed, 2263 insertions, 501 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 2d219d2ea0..708ad549ab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -405,6 +405,14 @@ M: Alexander Graf <agraf@suse.de>
 S: Maintained
 F: hw/s390-*.c
 
+UniCore32 Machines
+-------------
+PKUnity-3 SoC initramfs-with-busybox
+M: Guan Xuetao <gxt@mprc.pku.edu.cn>
+S: Maintained
+F: hw/puv3*
+F: hw/unicore32/
+
 X86 Machines
 ------------
 PC
diff --git a/Makefile b/Makefile
index 000b46c379..d736ea5311 100644
--- a/Makefile
+++ b/Makefile
@@ -181,24 +181,26 @@ ifneq ($(wildcard config-host.mak),)
 include $(SRC_PATH)/tests/Makefile
 endif
 
+qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
+
 qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
-$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py
+$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
 qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\
-$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py
+$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
 qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
-$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py
+$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
 
 qapi-types.c qapi-types.h :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py
+$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "." < $<, "  GEN   $@")
 qapi-visit.c qapi-visit.h :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py
+$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "."  < $<, "  GEN   $@")
 qmp-commands.h qmp-marshal.c :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py
+$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, "  GEN   $@")
 
 QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
diff --git a/arch_init.c b/arch_init.c
index 60823baabd..7b65c4888b 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -91,6 +91,8 @@ int graphic_depth = 15;
 #define QEMU_ARCH QEMU_ARCH_SPARC
 #elif defined(TARGET_XTENSA)
 #define QEMU_ARCH QEMU_ARCH_XTENSA
+#elif defined(TARGET_UNICORE32)
+#define QEMU_ARCH QEMU_ARCH_UNICORE32
 #endif
 
 const uint32_t arch_type = QEMU_ARCH;
diff --git a/arch_init.h b/arch_init.h
index 3dfea3b4f3..547f93cd1d 100644
--- a/arch_init.h
+++ b/arch_init.h
@@ -17,6 +17,7 @@ enum {
     QEMU_ARCH_SPARC = 2048,
     QEMU_ARCH_XTENSA = 4096,
     QEMU_ARCH_OPENRISC = 8192,
+    QEMU_ARCH_UNICORE32 = 0x4000,
 };
 
 extern const uint32_t arch_type;
diff --git a/block.h b/block.h
index 650d872f46..2e2be11071 100644
--- a/block.h
+++ b/block.h
@@ -79,6 +79,7 @@ typedef struct BlockDevOps {
 #define BDRV_O_NO_FLUSH    0x0200 /* disable flushing on this disk */
 #define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */
 #define BDRV_O_INCOMING    0x0800  /* consistency hint for incoming migration */
+#define BDRV_O_CHECK       0x1000  /* open solely for consistency check */
 
 #define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
 
diff --git a/block/iscsi.c b/block/iscsi.c
index 993a86d829..219f927823 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -896,26 +896,26 @@ static char *parse_initiator_name(const char *target)
     QemuOptsList *list;
     QemuOpts *opts;
     const char *name = NULL;
+    const char *iscsi_name = qemu_get_vm_name();
 
     list = qemu_find_opts("iscsi");
-    if (!list) {
-        return g_strdup("iqn.2008-11.org.linux-kvm");
-    }
-
-    opts = qemu_opts_find(list, target);
-    if (opts == NULL) {
-        opts = QTAILQ_FIRST(&list->head);
+    if (list) {
+        opts = qemu_opts_find(list, target);
         if (!opts) {
-            return g_strdup("iqn.2008-11.org.linux-kvm");
+            opts = QTAILQ_FIRST(&list->head);
+        }
+        if (opts) {
+            name = qemu_opt_get(opts, "initiator-name");
         }
     }
 
-    name = qemu_opt_get(opts, "initiator-name");
-    if (!name) {
-        return g_strdup("iqn.2008-11.org.linux-kvm");
+    if (name) {
+        return g_strdup(name);
+    } else {
+        return g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s",
+                               iscsi_name ? ":" : "",
+                               iscsi_name ? iscsi_name : "");
     }
-
-    return g_strdup(name);
 }
 
 /*
@@ -943,7 +943,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
         error_report("Failed to parse URL : %s %s", filename,
                      iscsi_get_error(iscsi));
         ret = -EINVAL;
-        goto failed;
+        goto out;
     }
 
     memset(iscsilun, 0, sizeof(IscsiLun));
@@ -954,13 +954,13 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
     if (iscsi == NULL) {
         error_report("iSCSI: Failed to create iSCSI context.");
         ret = -ENOMEM;
-        goto failed;
+        goto out;
     }
 
     if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
         error_report("iSCSI: Failed to set target name.");
         ret = -EINVAL;
-        goto failed;
+        goto out;
     }
 
     if (iscsi_url->user != NULL) {
@@ -969,7 +969,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
         if (ret != 0) {
             error_report("Failed to set initiator username and password");
             ret = -EINVAL;
-            goto failed;
+            goto out;
         }
     }
 
@@ -977,13 +977,13 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
     if (parse_chap(iscsi, iscsi_url->target) != 0) {
         error_report("iSCSI: Failed to set CHAP user/password");
         ret = -EINVAL;
-        goto failed;
+        goto out;
     }
 
     if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
         error_report("iSCSI: Failed to set session type to normal.");
         ret = -EINVAL;
-        goto failed;
+        goto out;
     }
 
     iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
@@ -1004,7 +1004,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
         != 0) {
         error_report("iSCSI: Failed to start async connect.");
         ret = -EINVAL;
-        goto failed;
+        goto out;
     }
 
     while (!task.complete) {
@@ -1015,11 +1015,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
         error_report("iSCSI: Failed to connect to LUN : %s",
                      iscsi_get_error(iscsi));
         ret = -EINVAL;
-        goto failed;
-    }
-
-    if (iscsi_url != NULL) {
-        iscsi_destroy_url(iscsi_url);
+        goto out;
     }
 
     /* Medium changer or tape. We dont have any emulation for this so this must
@@ -1031,19 +1027,22 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
         bs->sg = 1;
     }
 
-    return 0;
+    ret = 0;
 
-failed:
+out:
     if (initiator_name != NULL) {
         g_free(initiator_name);
     }
     if (iscsi_url != NULL) {
         iscsi_destroy_url(iscsi_url);
     }
-    if (iscsi != NULL) {
-        iscsi_destroy_context(iscsi);
+
+    if (ret) {
+        if (iscsi != NULL) {
+            iscsi_destroy_context(iscsi);
+        }
+        memset(iscsilun, 0, sizeof(IscsiLun));
     }
-    memset(iscsilun, 0, sizeof(IscsiLun));
     return ret;
 }
 
diff --git a/block/qcow2.c b/block/qcow2.c
index fd5e214431..8f183f1465 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -270,6 +270,20 @@ static int qcow2_mark_clean(BlockDriverState *bs)
     return 0;
 }
 
+static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
+                       BdrvCheckMode fix)
+{
+    int ret = qcow2_check_refcounts(bs, result, fix);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (fix && result->check_errors == 0 && result->corruptions == 0) {
+        return qcow2_mark_clean(bs);
+    }
+    return ret;
+}
+
 static int qcow2_open(BlockDriverState *bs, int flags)
 {
     BDRVQcowState *s = bs->opaque;
@@ -470,16 +484,11 @@ static int qcow2_open(BlockDriverState *bs, int flags)
     qemu_co_mutex_init(&s->lock);
 
     /* Repair image if dirty */
-    if ((s->incompatible_features & QCOW2_INCOMPAT_DIRTY) &&
-        !bs->read_only) {
+    if (!(flags & BDRV_O_CHECK) && !bs->read_only &&
+        (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
         BdrvCheckResult result = {0};
 
-        ret = qcow2_check_refcounts(bs, &result, BDRV_FIX_ERRORS);
-        if (ret < 0) {
-            goto fail;
-        }
-
-        ret = qcow2_mark_clean(bs);
+        ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS);
         if (ret < 0) {
             goto fail;
         }
@@ -1568,13 +1577,6 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
     return 0;
 }
 
-
-static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
-                       BdrvCheckMode fix)
-{
-    return qcow2_check_refcounts(bs, result, fix);
-}
-
 #if 0
 static void dump_refcounts(BlockDriverState *bs)
 {
diff --git a/block/qed-check.c b/block/qed-check.c
index 5edf60775b..b473dcd61f 100644
--- a/block/qed-check.c
+++ b/block/qed-check.c
@@ -194,6 +194,28 @@ static void qed_check_for_leaks(QEDCheck *check)
     }
 }
 
+/**
+ * Mark an image clean once it passes check or has been repaired
+ */
+static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result)
+{
+    /* Skip if there were unfixable corruptions or I/O errors */
+    if (result->corruptions > 0 || result->check_errors > 0) {
+        return;
+    }
+
+    /* Skip if image is already marked clean */
+    if (!(s->header.features & QED_F_NEED_CHECK)) {
+        return;
+    }
+
+    /* Ensure fixes reach storage before clearing check bit */
+    bdrv_flush(s->bs);
+
+    s->header.features &= ~QED_F_NEED_CHECK;
+    qed_write_header_sync(s);
+}
+
 int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
 {
     QEDCheck check = {
@@ -215,6 +237,10 @@ int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
     if (ret == 0) {
         /* Only check for leaks if entire image was scanned successfully */
         qed_check_for_leaks(&check);
+
+        if (fix) {
+            qed_check_mark_clean(s, result);
+        }
     }
 
     g_free(check.used_clusters);
diff --git a/block/qed.c b/block/qed.c
index 5f3eefa3af..a02dbfd72d 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -89,7 +89,7 @@ static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le)
     le->backing_filename_size = cpu_to_le32(cpu->backing_filename_size);
 }
 
-static int qed_write_header_sync(BDRVQEDState *s)
+int qed_write_header_sync(BDRVQEDState *s)
 {
     QEDHeader le;
     int ret;
@@ -477,7 +477,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
     }
 
     /* If image was not closed cleanly, check consistency */
-    if (s->header.features & QED_F_NEED_CHECK) {
+    if (!(flags & BDRV_O_CHECK) && (s->header.features & QED_F_NEED_CHECK)) {
         /* Read-only images cannot be fixed.  There is no risk of corruption
          * since write operations are not possible.  Therefore, allow
          * potentially inconsistent images to be opened read-only.  This can
@@ -491,13 +491,6 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
             if (ret) {
                 goto out;
             }
-            if (!result.corruptions && !result.check_errors) {
-                /* Ensure fixes reach storage before clearing check bit */
-                bdrv_flush(s->bs);
-
-                s->header.features &= ~QED_F_NEED_CHECK;
-                qed_write_header_sync(s);
-            }
         }
     }
 
diff --git a/block/qed.h b/block/qed.h
index c716772ad7..a063bf70af 100644
--- a/block/qed.h
+++ b/block/qed.h
@@ -211,6 +211,11 @@ void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque);
 void gencb_complete(void *opaque, int ret);
 
 /**
+ * Header functions
+ */
+int qed_write_header_sync(BDRVQEDState *s);
+
+/**
  * L2 cache functions
  */
 void qed_init_l2_cache(L2TableCache *l2_cache);
diff --git a/blockdev.c b/blockdev.c
index 8669142704..7c83baa353 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -377,6 +377,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
 	}
     }
 
+    bdrv_flags |= BDRV_O_CACHE_WB;
     if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
         if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) {
             error_report("invalid cache option");
diff --git a/configure b/configure
index 280726c3f8..f0dbc03af2 100755
--- a/configure
+++ b/configure
@@ -27,16 +27,40 @@ printf " '%s'" "$0" "$@" >> config.log
 echo >> config.log
 echo "#" >> config.log
 
+do_cc() {
+    # Run the compiler, capturing its output to the log.
+    echo $cc "$@" >> config.log
+    $cc "$@" >> config.log 2>&1 || return $?
+    # Test passed. If this is an --enable-werror build, rerun
+    # the test with -Werror and bail out if it fails. This
+    # makes warning-generating-errors in configure test code
+    # obvious to developers.
+    if test "$werror" != "yes"; then
+        return 0
+    fi
+    # Don't bother rerunning the compile if we were already using -Werror
+    case "$*" in
+        *-Werror*)
+           return 0
+        ;;
+    esac
+    echo $cc -Werror "$@" >> config.log
+    $cc -Werror "$@" >> config.log 2>&1 && return $?
+    echo "ERROR: configure test passed without -Werror but failed with -Werror."
+    echo "This is probably a bug in the configure script. The failing command"
+    echo "will be at the bottom of config.log."
+    echo "You can run configure with --disable-werror to bypass this check."
+    exit 1
+}
+
 compile_object() {
-  echo $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log
-  $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log 2>&1
+  do_cc $QEMU_CFLAGS -c -o $TMPO $TMPC
 }
 
 compile_prog() {
   local_cflags="$1"
   local_ldflags="$2"
-  echo $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log
-  $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log 2>&1
+  do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags
 }
 
 # symbolically link $1 to $2.  Portable version of "ln -sf".
@@ -935,6 +959,7 @@ sparc64-softmmu \
 s390x-softmmu \
 xtensa-softmmu \
 xtensaeb-softmmu \
+unicore32-softmmu \
 "
 fi
 # the following are Linux specific
@@ -2231,7 +2256,7 @@ cat > $TMPC <<EOF
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <unistd.h>
-int main(void) { return preadv == preadv; }
+int main(void) { return preadv(0, 0, 0, 0); }
 EOF
 preadv=no
 if compile_prog "" "" ; then
@@ -2527,7 +2552,7 @@ int main(void)
      * warning but not an error, and will proceed to fail the
      * qemu compile where we compile with -Werror.)
      */
-    return epoll_create1 == epoll_create1;
+    return (int)(uintptr_t)&epoll_create1;
 }
 EOF
 if compile_prog "" "" ; then
@@ -2920,7 +2945,7 @@ has_environ=no
 cat > $TMPC << EOF
 #include <unistd.h>
 int main(void) {
-    environ = environ;
+    environ = 0;
     return 0;
 }
 EOF
diff --git a/cpu-exec.c b/cpu-exec.c
index 4fee0618bd..134b3c4fcf 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -444,6 +444,7 @@ int cpu_exec(CPUArchState *env)
 #elif defined(TARGET_UNICORE32)
                     if (interrupt_request & CPU_INTERRUPT_HARD
                         && !(env->uncached_asr & ASR_I)) {
+                        env->exception_index = UC32_EXCP_INTR;
                         do_interrupt(env);
                         next_tb = 0;
                     }
diff --git a/default-configs/unicore32-softmmu.mak b/default-configs/unicore32-softmmu.mak
new file mode 100644
index 0000000000..de38577a35
--- /dev/null
+++ b/default-configs/unicore32-softmmu.mak
@@ -0,0 +1,4 @@
+# Default configuration for unicore32-softmmu
+CONFIG_PUV3=y
+CONFIG_PTIMER=y
+CONFIG_PCKBD=y
diff --git a/dma-helpers.c b/dma-helpers.c
index 35cb500581..13593d1b42 100644
--- a/dma-helpers.c
+++ b/dma-helpers.c
@@ -65,6 +65,7 @@ void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len)
 void qemu_sglist_destroy(QEMUSGList *qsg)
 {
     g_free(qsg->sg);
+    memset(qsg, 0, sizeof(*qsg));
 }
 
 typedef struct {
diff --git a/exec.c b/exec.c
index a42a0b5b78..929db5cf0a 100644
--- a/exec.c
+++ b/exec.c
@@ -2550,6 +2550,8 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
 
     ram_list.phys_dirty = g_realloc(ram_list.phys_dirty,
                                        last_ram_offset() >> TARGET_PAGE_BITS);
+    memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS),
+           0, size >> TARGET_PAGE_BITS);
     cpu_physical_memory_set_dirty_range(new_block->offset, size, 0xff);
 
     if (kvm_enabled())
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 12cc141796..3ba5dd0c6b 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -66,6 +66,13 @@ hw-obj-$(CONFIG_XILINX) += xilinx_uartlite.o
 hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
 hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
 
+# PKUnity SoC devices
+hw-obj-$(CONFIG_PUV3) += puv3_intc.o
+hw-obj-$(CONFIG_PUV3) += puv3_ost.o
+hw-obj-$(CONFIG_PUV3) += puv3_gpio.o
+hw-obj-$(CONFIG_PUV3) += puv3_pm.o
+hw-obj-$(CONFIG_PUV3) += puv3_dma.o
+
 # PCI watchdog devices
 hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
 
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index efea93f0b4..5ea3cadb01 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -636,7 +636,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
     }
 }
 
-static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist)
+static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset)
 {
     AHCICmdHdr *cmd = ad->cur_cmd;
     uint32_t opts = le32_to_cpu(cmd->opts);
@@ -647,6 +647,10 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist)
     uint8_t *prdt;
     int i;
     int r = 0;
+    int sum = 0;
+    int off_idx = -1;
+    int off_pos = -1;
+    int tbl_entry_size;
 
     if (!sglist_alloc_hint) {
         DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
@@ -669,10 +673,31 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist)
     /* Get entries in the PRDT, init a qemu sglist accordingly */
     if (sglist_alloc_hint > 0) {
         AHCI_SG *tbl = (AHCI_SG *)prdt;
-
-        qemu_sglist_init(sglist, sglist_alloc_hint, ad->hba->dma);
+        sum = 0;
         for (i = 0; i < sglist_alloc_hint; i++) {
             /* flags_size is zero-based */
+            tbl_entry_size = (le32_to_cpu(tbl[i].flags_size) + 1);
+            if (offset <= (sum + tbl_entry_size)) {
+                off_idx = i;
+                off_pos = offset - sum;
+                break;
+            }
+            sum += tbl_entry_size;
+        }
+        if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) {
+            DPRINTF(ad->port_no, "%s: Incorrect offset! "
+                            "off_idx: %d, off_pos: %d\n",
+                            __func__, off_idx, off_pos);
+            r = -1;
+            goto out;
+        }
+
+        qemu_sglist_init(sglist, (sglist_alloc_hint - off_idx), ad->hba->dma);
+        qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos),
+                        le32_to_cpu(tbl[off_idx].flags_size) + 1 - off_pos);
+
+        for (i = off_idx + 1; i < sglist_alloc_hint; i++) {
+            /* flags_size is zero-based */
             qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
                             le32_to_cpu(tbl[i].flags_size) + 1);
         }
@@ -745,7 +770,7 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
             ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
             s->dev[port].port.ifs[0].nb_sectors - 1);
 
-    ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist);
+    ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist, 0);
     ncq_tfs->tag = tag;
 
     switch(ncq_fis->command) {
@@ -970,7 +995,7 @@ static int ahci_start_transfer(IDEDMA *dma)
         goto out;
     }
 
-    if (!ahci_populate_sglist(ad, &s->sg)) {
+    if (!ahci_populate_sglist(ad, &s->sg, 0)) {
         has_sglist = 1;
     }
 
@@ -1015,6 +1040,7 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s,
     DPRINTF(ad->port_no, "\n");
     ad->dma_cb = dma_cb;
     ad->dma_status |= BM_STATUS_DMAING;
+    s->io_buffer_offset = 0;
     dma_cb(s, 0);
 }
 
@@ -1023,7 +1049,7 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
     AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
     IDEState *s = &ad->port.ifs[0];
 
-    ahci_populate_sglist(ad, &s->sg);
+    ahci_populate_sglist(ad, &s->sg, 0);
     s->io_buffer_size = s->sg.size;
 
     DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size);
@@ -1037,7 +1063,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
     uint8_t *p = s->io_buffer + s->io_buffer_index;
     int l = s->io_buffer_size - s->io_buffer_index;
 
-    if (ahci_populate_sglist(ad, &s->sg)) {
+    if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) {
         return 0;
     }
 
@@ -1047,9 +1073,13 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
         dma_buf_write(p, l, &s->sg);
     }
 
+    /* free sglist that was created in ahci_populate_sglist() */
+    qemu_sglist_destroy(&s->sg);
+
     /* update number of transferred bytes */
     ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l);
     s->io_buffer_index += l;
+    s->io_buffer_offset += l;
 
     DPRINTF(ad->port_no, "len=%#x\n", l);
 
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 7170bd9cd0..bf7d313cf4 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -393,6 +393,7 @@ struct IDEState {
     struct iovec iov;
     QEMUIOVector qiov;
     /* ATA DMA state */
+    int io_buffer_offset;
     int io_buffer_size;
     QEMUSGList sg;
     /* PIO transfer handling */
diff --git a/hw/puv3.c b/hw/puv3.c
new file mode 100644
index 0000000000..43f7216e4e
--- /dev/null
+++ b/hw/puv3.c
@@ -0,0 +1,131 @@
+/*
+ * Generic PKUnity SoC machine and board descriptor
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "console.h"
+#include "elf.h"
+#include "exec-memory.h"
+#include "sysbus.h"
+#include "boards.h"
+#include "loader.h"
+#include "pc.h"
+
+#undef DEBUG_PUV3
+#include "puv3.h"
+
+#define KERNEL_LOAD_ADDR        0x03000000
+#define KERNEL_MAX_SIZE         0x00800000 /* Just a guess */
+
+static void puv3_intc_cpu_handler(void *opaque, int irq, int level)
+{
+    CPUUniCore32State *env = opaque;
+
+    assert(irq == 0);
+    if (level) {
+        cpu_interrupt(env, CPU_INTERRUPT_HARD);
+    } else {
+        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+    }
+}
+
+static void puv3_soc_init(CPUUniCore32State *env)
+{
+    qemu_irq *cpu_intc, irqs[PUV3_IRQS_NR];
+    DeviceState *dev;
+    MemoryRegion *i8042 = g_new(MemoryRegion, 1);
+    int i;
+
+    /* Initialize interrupt controller */
+    cpu_intc = qemu_allocate_irqs(puv3_intc_cpu_handler, env, 1);
+    dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, *cpu_intc);
+    for (i = 0; i < PUV3_IRQS_NR; i++) {
+        irqs[i] = qdev_get_gpio_in(dev, i);
+    }
+
+    /* Initialize minimal necessary devices for kernel booting */
+    sysbus_create_simple("puv3_pm", PUV3_PM_BASE, NULL);
+    sysbus_create_simple("puv3_dma", PUV3_DMA_BASE, NULL);
+    sysbus_create_simple("puv3_ost", PUV3_OST_BASE, irqs[PUV3_IRQS_OST0]);
+    sysbus_create_varargs("puv3_gpio", PUV3_GPIO_BASE,
+            irqs[PUV3_IRQS_GPIOLOW0], irqs[PUV3_IRQS_GPIOLOW1],
+            irqs[PUV3_IRQS_GPIOLOW2], irqs[PUV3_IRQS_GPIOLOW3],
+            irqs[PUV3_IRQS_GPIOLOW4], irqs[PUV3_IRQS_GPIOLOW5],
+            irqs[PUV3_IRQS_GPIOLOW6], irqs[PUV3_IRQS_GPIOLOW7],
+            irqs[PUV3_IRQS_GPIOHIGH], NULL);
+
+    /* Keyboard (i8042), mouse disabled for nographic */
+    i8042_mm_init(irqs[PUV3_IRQS_PS2_KBD], NULL, i8042, PUV3_REGS_OFFSET, 4);
+    memory_region_add_subregion(get_system_memory(), PUV3_PS2_BASE, i8042);
+}
+
+static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size)
+{
+    MemoryRegion *ram_memory = g_new(MemoryRegion, 1);
+
+    /* SDRAM at address zero.  */
+    memory_region_init_ram(ram_memory, "puv3.ram", ram_size);
+    vmstate_register_ram_global(ram_memory);
+    memory_region_add_subregion(get_system_memory(), 0, ram_memory);
+}
+
+static void puv3_load_kernel(const char *kernel_filename)
+{
+    int size;
+
+    assert(kernel_filename != NULL);
+
+    /* only zImage format supported */
+    size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR,
+            KERNEL_MAX_SIZE);
+    if (size < 0) {
+        hw_error("Load kernel error: '%s'\n", kernel_filename);
+    }
+
+    /* cheat curses that we have a graphic console, only under ocd console */
+    graphic_console_init(NULL, NULL, NULL, NULL, NULL);
+}
+
+static void puv3_init(ram_addr_t ram_size, const char *boot_device,
+                     const char *kernel_filename, const char *kernel_cmdline,
+                     const char *initrd_filename, const char *cpu_model)
+{
+    CPUUniCore32State *env;
+
+    if (initrd_filename) {
+        hw_error("Please use kernel built-in initramdisk.\n");
+    }
+
+    if (!cpu_model) {
+        cpu_model = "UniCore-II";
+    }
+
+    env = cpu_init(cpu_model);
+    if (!env) {
+        hw_error("Unable to find CPU definition\n");
+    }
+
+    puv3_soc_init(env);
+    puv3_board_init(env, ram_size);
+    puv3_load_kernel(kernel_filename);
+}
+
+static QEMUMachine puv3_machine = {
+    .name = "puv3",
+    .desc = "PKUnity Version-3 based on UniCore32",
+    .init = puv3_init,
+    .is_default = 1,
+    .use_scsi = 0,
+};
+
+static void puv3_machine_init(void)
+{
+    qemu_register_machine(&puv3_machine);
+}
+
+machine_init(puv3_machine_init)
diff --git a/hw/puv3.h b/hw/puv3.h
new file mode 100644
index 0000000000..f37adcb665
--- /dev/null
+++ b/hw/puv3.h
@@ -0,0 +1,49 @@
+/*
+ * Misc PKUnity SoC declarations
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef QEMU_HW_PUV3_H
+#define QEMU_HW_PUV3_H
+
+#define PUV3_REGS_OFFSET        (0x1000) /* 4K is reasonable */
+
+/* PKUnity System bus (AHB): 0xc0000000 - 0xedffffff (640MB) */
+#define PUV3_DMA_BASE           (0xc0200000) /* AHB-4 */
+
+/* PKUnity Peripheral bus (APB): 0xee000000 - 0xefffffff (128MB) */
+#define PUV3_GPIO_BASE          (0xee500000) /* APB-5 */
+#define PUV3_INTC_BASE          (0xee600000) /* APB-6 */
+#define PUV3_OST_BASE           (0xee800000) /* APB-8 */
+#define PUV3_PM_BASE            (0xeea00000) /* APB-10 */
+#define PUV3_PS2_BASE           (0xeeb00000) /* APB-11 */
+
+/* Hardware interrupts */
+#define PUV3_IRQS_NR            (32)
+
+#define PUV3_IRQS_GPIOLOW0      (0)
+#define PUV3_IRQS_GPIOLOW1      (1)
+#define PUV3_IRQS_GPIOLOW2      (2)
+#define PUV3_IRQS_GPIOLOW3      (3)
+#define PUV3_IRQS_GPIOLOW4      (4)
+#define PUV3_IRQS_GPIOLOW5      (5)
+#define PUV3_IRQS_GPIOLOW6      (6)
+#define PUV3_IRQS_GPIOLOW7      (7)
+#define PUV3_IRQS_GPIOHIGH      (8)
+#define PUV3_IRQS_PS2_KBD       (22)
+#define PUV3_IRQS_PS2_AUX       (23)
+#define PUV3_IRQS_OST0          (26)
+
+/* All puv3_*.c use DPRINTF for debug. */
+#ifdef DEBUG_PUV3
+#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#endif /* !QEMU_HW_PUV3_H */
diff --git a/hw/puv3_dma.c b/hw/puv3_dma.c
new file mode 100644
index 0000000000..85b97bfdeb
--- /dev/null
+++ b/hw/puv3_dma.c
@@ -0,0 +1,109 @@
+/*
+ * DMA device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw.h"
+#include "sysbus.h"
+
+#undef DEBUG_PUV3
+#include "puv3.h"
+
+#define PUV3_DMA_CH_NR          (6)
+#define PUV3_DMA_CH_MASK        (0xff)
+#define PUV3_DMA_CH(offset)     ((offset) >> 8)
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t reg_CFG[PUV3_DMA_CH_NR];
+} PUV3DMAState;
+
+static uint64_t puv3_dma_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    PUV3DMAState *s = opaque;
+    uint32_t ret = 0;
+
+    assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR);
+
+    switch (offset & PUV3_DMA_CH_MASK) {
+    case 0x10:
+        ret = s->reg_CFG[PUV3_DMA_CH(offset)];
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+
+    return ret;
+}
+
+static void puv3_dma_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    PUV3DMAState *s = opaque;
+
+    assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR);
+
+    switch (offset & PUV3_DMA_CH_MASK) {
+    case 0x10:
+        s->reg_CFG[PUV3_DMA_CH(offset)] = value;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+}
+
+static const MemoryRegionOps puv3_dma_ops = {
+    .read = puv3_dma_read,
+    .write = puv3_dma_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_dma_init(SysBusDevice *dev)
+{
+    PUV3DMAState *s = FROM_SYSBUS(PUV3DMAState, dev);
+    int i;
+
+    for (i = 0; i < PUV3_DMA_CH_NR; i++) {
+        s->reg_CFG[i] = 0x0;
+    }
+
+    memory_region_init_io(&s->iomem, &puv3_dma_ops, s, "puv3_dma",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_dma_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_dma_init;
+}
+
+static const TypeInfo puv3_dma_info = {
+    .name = "puv3_dma",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3DMAState),
+    .class_init = puv3_dma_class_init,
+};
+
+static void puv3_dma_register_type(void)
+{
+    type_register_static(&puv3_dma_info);
+}
+
+type_init(puv3_dma_register_type)
diff --git a/hw/puv3_gpio.c b/hw/puv3_gpio.c
new file mode 100644
index 0000000000..9436e6c62c
--- /dev/null
+++ b/hw/puv3_gpio.c
@@ -0,0 +1,141 @@
+/*
+ * GPIO device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw.h"
+#include "sysbus.h"
+
+#undef DEBUG_PUV3
+#include "puv3.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq[9];
+
+    uint32_t reg_GPLR;
+    uint32_t reg_GPDR;
+    uint32_t reg_GPIR;
+} PUV3GPIOState;
+
+static uint64_t puv3_gpio_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    PUV3GPIOState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x00:
+        ret = s->reg_GPLR;
+        break;
+    case 0x04:
+        ret = s->reg_GPDR;
+        break;
+    case 0x20:
+        ret = s->reg_GPIR;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+
+    return ret;
+}
+
+static void puv3_gpio_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    PUV3GPIOState *s = opaque;
+
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+    switch (offset) {
+    case 0x04:
+        s->reg_GPDR = value;
+        break;
+    case 0x08:
+        if (s->reg_GPDR & value) {
+            s->reg_GPLR |= value;
+        } else {
+            DPRINTF("Write gpio input port error!");
+        }
+        break;
+    case 0x0c:
+        if (s->reg_GPDR & value) {
+            s->reg_GPLR &= ~value;
+        } else {
+            DPRINTF("Write gpio input port error!");
+        }
+        break;
+    case 0x10: /* GRER */
+    case 0x14: /* GFER */
+    case 0x18: /* GEDR */
+        break;
+    case 0x20: /* GPIR */
+        s->reg_GPIR = value;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+}
+
+static const MemoryRegionOps puv3_gpio_ops = {
+    .read = puv3_gpio_read,
+    .write = puv3_gpio_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_gpio_init(SysBusDevice *dev)
+{
+    PUV3GPIOState *s = FROM_SYSBUS(PUV3GPIOState, dev);
+
+    s->reg_GPLR = 0;
+    s->reg_GPDR = 0;
+
+    /* FIXME: these irqs not handled yet */
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW0]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW1]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW2]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW3]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW4]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW5]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW6]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW7]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOHIGH]);
+
+    memory_region_init_io(&s->iomem, &puv3_gpio_ops, s, "puv3_gpio",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_gpio_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_gpio_init;
+}
+
+static const TypeInfo puv3_gpio_info = {
+    .name = "puv3_gpio",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3GPIOState),
+    .class_init = puv3_gpio_class_init,
+};
+
+static void puv3_gpio_register_type(void)
+{
+    type_register_static(&puv3_gpio_info);
+}
+
+type_init(puv3_gpio_register_type)
diff --git a/hw/puv3_intc.c b/hw/puv3_intc.c
new file mode 100644
index 0000000000..9e0b975ea2
--- /dev/null
+++ b/hw/puv3_intc.c
@@ -0,0 +1,135 @@
+/*
+ * INTC device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+
+#undef DEBUG_PUV3
+#include "puv3.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq parent_irq;
+
+    uint32_t reg_ICMR;
+    uint32_t reg_ICPR;
+} PUV3INTCState;
+
+/* Update interrupt status after enabled or pending bits have been changed.  */
+static void puv3_intc_update(PUV3INTCState *s)
+{
+    if (s->reg_ICMR & s->reg_ICPR) {
+        qemu_irq_raise(s->parent_irq);
+    } else {
+        qemu_irq_lower(s->parent_irq);
+    }
+}
+
+/* Process a change in an external INTC input. */
+static void puv3_intc_handler(void *opaque, int irq, int level)
+{
+    PUV3INTCState *s = opaque;
+
+    DPRINTF("irq 0x%x, level 0x%x\n", irq, level);
+    if (level) {
+        s->reg_ICPR |= (1 << irq);
+    } else {
+        s->reg_ICPR &= ~(1 << irq);
+    }
+    puv3_intc_update(s);
+}
+
+static uint64_t puv3_intc_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    PUV3INTCState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x04: /* INTC_ICMR */
+        ret = s->reg_ICMR;
+        break;
+    case 0x0c: /* INTC_ICIP */
+        ret = s->reg_ICPR; /* the same value with ICPR */
+        break;
+    default:
+        DPRINTF("Bad offset %x\n", (int)offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+    return ret;
+}
+
+static void puv3_intc_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    PUV3INTCState *s = opaque;
+
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+    switch (offset) {
+    case 0x00: /* INTC_ICLR */
+    case 0x14: /* INTC_ICCR */
+        break;
+    case 0x04: /* INTC_ICMR */
+        s->reg_ICMR = value;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", (int)offset);
+        return;
+    }
+    puv3_intc_update(s);
+}
+
+static const MemoryRegionOps puv3_intc_ops = {
+    .read = puv3_intc_read,
+    .write = puv3_intc_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_intc_init(SysBusDevice *dev)
+{
+    PUV3INTCState *s = FROM_SYSBUS(PUV3INTCState, dev);
+
+    qdev_init_gpio_in(&s->busdev.qdev, puv3_intc_handler, PUV3_IRQS_NR);
+    sysbus_init_irq(&s->busdev, &s->parent_irq);
+
+    s->reg_ICMR = 0;
+    s->reg_ICPR = 0;
+
+    memory_region_init_io(&s->iomem, &puv3_intc_ops, s, "puv3_intc",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_intc_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_intc_init;
+}
+
+static const TypeInfo puv3_intc_info = {
+    .name = "puv3_intc",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3INTCState),
+    .class_init = puv3_intc_class_init,
+};
+
+static void puv3_intc_register_type(void)
+{
+    type_register_static(&puv3_intc_info);
+}
+
+type_init(puv3_intc_register_type)
diff --git a/hw/puv3_ost.c b/hw/puv3_ost.c
new file mode 100644
index 0000000000..dd30cad0e2
--- /dev/null
+++ b/hw/puv3_ost.c
@@ -0,0 +1,151 @@
+/*
+ * OSTimer device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+#include "ptimer.h"
+
+#undef DEBUG_PUV3
+#include "puv3.h"
+
+/* puv3 ostimer implementation. */
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    QEMUBH *bh;
+    qemu_irq irq;
+    ptimer_state *ptimer;
+
+    uint32_t reg_OSMR0;
+    uint32_t reg_OSCR;
+    uint32_t reg_OSSR;
+    uint32_t reg_OIER;
+} PUV3OSTState;
+
+static uint64_t puv3_ost_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    PUV3OSTState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x10: /* Counter Register */
+        ret = s->reg_OSMR0 - (uint32_t)ptimer_get_count(s->ptimer);
+        break;
+    case 0x14: /* Status Register */
+        ret = s->reg_OSSR;
+        break;
+    case 0x1c: /* Interrupt Enable Register */
+        ret = s->reg_OIER;
+        break;
+    default:
+        DPRINTF("Bad offset %x\n", (int)offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+    return ret;
+}
+
+static void puv3_ost_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    PUV3OSTState *s = opaque;
+
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+    switch (offset) {
+    case 0x00: /* Match Register 0 */
+        s->reg_OSMR0 = value;
+        if (s->reg_OSMR0 > s->reg_OSCR) {
+            ptimer_set_count(s->ptimer, s->reg_OSMR0 - s->reg_OSCR);
+        } else {
+            ptimer_set_count(s->ptimer, s->reg_OSMR0 +
+                    (0xffffffff - s->reg_OSCR));
+        }
+        ptimer_run(s->ptimer, 2);
+        break;
+    case 0x14: /* Status Register */
+        assert(value == 0);
+        if (s->reg_OSSR) {
+            s->reg_OSSR = value;
+            qemu_irq_lower(s->irq);
+        }
+        break;
+    case 0x1c: /* Interrupt Enable Register */
+        s->reg_OIER = value;
+        break;
+    default:
+        DPRINTF("Bad offset %x\n", (int)offset);
+    }
+}
+
+static const MemoryRegionOps puv3_ost_ops = {
+    .read = puv3_ost_read,
+    .write = puv3_ost_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void puv3_ost_tick(void *opaque)
+{
+    PUV3OSTState *s = opaque;
+
+    DPRINTF("ost hit when ptimer counter from 0x%x to 0x%x!\n",
+            s->reg_OSCR, s->reg_OSMR0);
+
+    s->reg_OSCR = s->reg_OSMR0;
+    if (s->reg_OIER) {
+        s->reg_OSSR = 1;
+        qemu_irq_raise(s->irq);
+    }
+}
+
+static int puv3_ost_init(SysBusDevice *dev)
+{
+    PUV3OSTState *s = FROM_SYSBUS(PUV3OSTState, dev);
+
+    s->reg_OIER = 0;
+    s->reg_OSSR = 0;
+    s->reg_OSMR0 = 0;
+    s->reg_OSCR = 0;
+
+    sysbus_init_irq(dev, &s->irq);
+
+    s->bh = qemu_bh_new(puv3_ost_tick, s);
+    s->ptimer = ptimer_init(s->bh);
+    ptimer_set_freq(s->ptimer, 50 * 1000 * 1000);
+
+    memory_region_init_io(&s->iomem, &puv3_ost_ops, s, "puv3_ost",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_ost_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_ost_init;
+}
+
+static const TypeInfo puv3_ost_info = {
+    .name = "puv3_ost",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3OSTState),
+    .class_init = puv3_ost_class_init,
+};
+
+static void puv3_ost_register_type(void)
+{
+    type_register_static(&puv3_ost_info);
+}
+
+type_init(puv3_ost_register_type)
diff --git a/hw/puv3_pm.c b/hw/puv3_pm.c
new file mode 100644
index 0000000000..621c96875c
--- /dev/null
+++ b/hw/puv3_pm.c
@@ -0,0 +1,149 @@
+/*
+ * Power Management device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw.h"
+#include "sysbus.h"
+
+#undef DEBUG_PUV3
+#include "puv3.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    uint32_t reg_PMCR;
+    uint32_t reg_PCGR;
+    uint32_t reg_PLL_SYS_CFG;
+    uint32_t reg_PLL_DDR_CFG;
+    uint32_t reg_PLL_VGA_CFG;
+    uint32_t reg_DIVCFG;
+} PUV3PMState;
+
+static uint64_t puv3_pm_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    PUV3PMState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x14:
+        ret = s->reg_PCGR;
+        break;
+    case 0x18:
+        ret = s->reg_PLL_SYS_CFG;
+        break;
+    case 0x1c:
+        ret = s->reg_PLL_DDR_CFG;
+        break;
+    case 0x20:
+        ret = s->reg_PLL_VGA_CFG;
+        break;
+    case 0x24:
+        ret = s->reg_DIVCFG;
+        break;
+    case 0x28: /* PLL SYS STATUS */
+        ret = 0x00002401;
+        break;
+    case 0x2c: /* PLL DDR STATUS */
+        ret = 0x00100c00;
+        break;
+    case 0x30: /* PLL VGA STATUS */
+        ret = 0x00003801;
+        break;
+    case 0x34: /* DIV STATUS */
+        ret = 0x22f52015;
+        break;
+    case 0x38: /* SW RESET */
+        ret = 0x0;
+        break;
+    case 0x44: /* PLL DFC DONE */
+        ret = 0x7;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+
+    return ret;
+}
+
+static void puv3_pm_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    PUV3PMState *s = opaque;
+
+    switch (offset) {
+    case 0x0:
+        s->reg_PMCR = value;
+        break;
+    case 0x14:
+        s->reg_PCGR = value;
+        break;
+    case 0x18:
+        s->reg_PLL_SYS_CFG = value;
+        break;
+    case 0x1c:
+        s->reg_PLL_DDR_CFG = value;
+        break;
+    case 0x20:
+        s->reg_PLL_VGA_CFG = value;
+        break;
+    case 0x24:
+    case 0x38:
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+}
+
+static const MemoryRegionOps puv3_pm_ops = {
+    .read = puv3_pm_read,
+    .write = puv3_pm_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_pm_init(SysBusDevice *dev)
+{
+    PUV3PMState *s = FROM_SYSBUS(PUV3PMState, dev);
+
+    s->reg_PCGR = 0x0;
+
+    memory_region_init_io(&s->iomem, &puv3_pm_ops, s, "puv3_pm",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_pm_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_pm_init;
+}
+
+static const TypeInfo puv3_pm_info = {
+    .name = "puv3_pm",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3PMState),
+    .class_init = puv3_pm_class_init,
+};
+
+static void puv3_pm_register_type(void)
+{
+    type_register_static(&puv3_pm_info);
+}
+
+type_init(puv3_pm_register_type)
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index c8d5edd86e..409f760ef7 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -175,6 +175,8 @@ static void scsi_aio_complete(void *opaque, int ret)
     SCSIDiskReq *r = (SCSIDiskReq *)opaque;
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
 
+    assert(r->req.aiocb != NULL);
+    r->req.aiocb = NULL;
     bdrv_acct_done(s->qdev.conf.bs, &r->acct);
 
     if (ret < 0) {
@@ -238,10 +240,9 @@ static void scsi_dma_complete(void *opaque, int ret)
     SCSIDiskReq *r = (SCSIDiskReq *)opaque;
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
 
-    if (r->req.aiocb != NULL) {
-        r->req.aiocb = NULL;
-        bdrv_acct_done(s->qdev.conf.bs, &r->acct);
-    }
+    assert(r->req.aiocb != NULL);
+    r->req.aiocb = NULL;
+    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
 
     if (ret < 0) {
         if (scsi_handle_rw_error(r, -ret)) {
@@ -270,10 +271,9 @@ static void scsi_read_complete(void * opaque, int ret)
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
     int n;
 
-    if (r->req.aiocb != NULL) {
-        r->req.aiocb = NULL;
-        bdrv_acct_done(s->qdev.conf.bs, &r->acct);
-    }
+    assert(r->req.aiocb != NULL);
+    r->req.aiocb = NULL;
+    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
 
     if (ret < 0) {
         if (scsi_handle_rw_error(r, -ret)) {
@@ -637,7 +637,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
         {
             buflen = 8;
             outbuf[4] = 0;
-            outbuf[5] = 0x60; /* write_same 10/16 supported */
+            outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
             outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
             outbuf[7] = 0;
             break;
@@ -1449,6 +1449,89 @@ invalid_field:
     return;
 }
 
+typedef struct UnmapCBData {
+    SCSIDiskReq *r;
+    uint8_t *inbuf;
+    int count;
+} UnmapCBData;
+
+static void scsi_unmap_complete(void *opaque, int ret)
+{
+    UnmapCBData *data = opaque;
+    SCSIDiskReq *r = data->r;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    uint64_t sector_num;
+    uint32 nb_sectors;
+
+    r->req.aiocb = NULL;
+    if (ret < 0) {
+        if (scsi_handle_rw_error(r, -ret)) {
+            goto done;
+        }
+    }
+
+    if (data->count > 0 && !r->req.io_canceled) {
+        sector_num = ldq_be_p(&data->inbuf[0]);
+        nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
+        if (sector_num > sector_num + nb_sectors ||
+            sector_num + nb_sectors - 1 > s->qdev.max_lba) {
+            scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
+            goto done;
+        }
+
+        r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs,
+                                        sector_num * (s->qdev.blocksize / 512),
+                                        nb_sectors * (s->qdev.blocksize / 512),
+                                        scsi_unmap_complete, data);
+        data->count--;
+        data->inbuf += 16;
+        return;
+    }
+
+done:
+    if (data->count == 0) {
+        scsi_req_complete(&r->req, GOOD);
+    }
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+    g_free(data);
+}
+
+static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
+{
+    uint8_t *p = inbuf;
+    int len = r->req.cmd.xfer;
+    UnmapCBData *data;
+
+    if (len < 8) {
+        goto invalid_param_len;
+    }
+    if (len < lduw_be_p(&p[0]) + 2) {
+        goto invalid_param_len;
+    }
+    if (len < lduw_be_p(&p[2]) + 8) {
+        goto invalid_param_len;
+    }
+    if (lduw_be_p(&p[2]) & 15) {
+        goto invalid_param_len;
+    }
+
+    data = g_new0(UnmapCBData, 1);
+    data->r = r;
+    data->inbuf = &p[8];
+    data->count = lduw_be_p(&p[2]) >> 4;
+
+    /* The matching unref is in scsi_unmap_complete, before data is freed.  */
+    scsi_req_ref(&r->req);
+    scsi_unmap_complete(data, 0);
+    return;
+
+invalid_param_len:
+    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
+    return;
+}
+
 static void scsi_disk_emulate_write_data(SCSIRequest *req)
 {
     SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
@@ -1468,6 +1551,10 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req)
         scsi_disk_emulate_mode_select(r, r->iov.iov_base);
         break;
 
+    case UNMAP:
+        scsi_disk_emulate_unmap(r, r->iov.iov_base);
+        break;
+
     default:
         abort();
     }
@@ -1702,6 +1789,9 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
     case MODE_SELECT_10:
         DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer);
         break;
+    case UNMAP:
+        DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer);
+        break;
     case WRITE_SAME_10:
         nb_sectors = lduw_be_p(&req->cmd.buf[7]);
         goto write_same;
@@ -1712,7 +1802,8 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
             scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
             return 0;
         }
-        if (r->req.cmd.lba > s->qdev.max_lba) {
+        if (r->req.cmd.lba > r->req.cmd.lba + nb_sectors ||
+            r->req.cmd.lba + nb_sectors - 1 > s->qdev.max_lba) {
             goto illegal_lba;
         }
 
@@ -2067,6 +2158,7 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = {
     [SEEK_10]                         = &scsi_disk_emulate_reqops,
     [MODE_SELECT]                     = &scsi_disk_emulate_reqops,
     [MODE_SELECT_10]                  = &scsi_disk_emulate_reqops,
+    [UNMAP]                           = &scsi_disk_emulate_reqops,
     [WRITE_SAME_10]                   = &scsi_disk_emulate_reqops,
     [WRITE_SAME_16]                   = &scsi_disk_emulate_reqops,
 
diff --git a/hw/unicore32/Makefile.objs b/hw/unicore32/Makefile.objs
new file mode 100644
index 0000000000..0725ce3ca7
--- /dev/null
+++ b/hw/unicore32/Makefile.objs
@@ -0,0 +1,6 @@
+# For UniCore32 machines and boards
+
+# PKUnity-v3 SoC and board information
+obj-${CONFIG_PUV3} += puv3.o
+
+obj-y := $(addprefix ../,$(obj-y))
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index f21757ed55..fd8fa90792 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -254,6 +254,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
 
     virtio_blk_req_complete(req, status);
     g_free(req);
+    return;
 #else
     abort();
 #endif
@@ -509,9 +510,19 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
     blkcfg.size_max = 0;
     blkcfg.physical_block_exp = get_physical_block_exp(s->conf);
     blkcfg.alignment_offset = 0;
+    blkcfg.wce = bdrv_enable_write_cache(s->bs);
     memcpy(config, &blkcfg, sizeof(struct virtio_blk_config));
 }
 
+static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+    VirtIOBlock *s = to_virtio_blk(vdev);
+    struct virtio_blk_config blkcfg;
+
+    memcpy(&blkcfg, config, sizeof(blkcfg));
+    bdrv_set_enable_write_cache(s->bs, blkcfg.wce != 0);
+}
+
 static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
 {
     VirtIOBlock *s = to_virtio_blk(vdev);
@@ -522,15 +533,29 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
     features |= (1 << VIRTIO_BLK_F_BLK_SIZE);
     features |= (1 << VIRTIO_BLK_F_SCSI);
 
+    features |= (1 << VIRTIO_BLK_F_CONFIG_WCE);
     if (bdrv_enable_write_cache(s->bs))
-        features |= (1 << VIRTIO_BLK_F_WCACHE);
-    
+        features |= (1 << VIRTIO_BLK_F_WCE);
+
     if (bdrv_is_read_only(s->bs))
         features |= 1 << VIRTIO_BLK_F_RO;
 
     return features;
 }
 
+static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
+{
+    VirtIOBlock *s = to_virtio_blk(vdev);
+    uint32_t features;
+
+    if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+        return;
+    }
+
+    features = vdev->guest_features;
+    bdrv_set_enable_write_cache(s->bs, !!(features & (1 << VIRTIO_BLK_F_WCE)));
+}
+
 static void virtio_blk_save(QEMUFile *f, void *opaque)
 {
     VirtIOBlock *s = opaque;
@@ -609,7 +634,9 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk)
                                           sizeof(VirtIOBlock));
 
     s->vdev.get_config = virtio_blk_update_config;
+    s->vdev.set_config = virtio_blk_set_config;
     s->vdev.get_features = virtio_blk_get_features;
+    s->vdev.set_status = virtio_blk_set_status;
     s->vdev.reset = virtio_blk_reset;
     s->bs = blk->conf.bs;
     s->conf = &blk->conf;
diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h
index 79ebccc95b..35834cf493 100644
--- a/hw/virtio-blk.h
+++ b/hw/virtio-blk.h
@@ -31,8 +31,9 @@
 #define VIRTIO_BLK_F_BLK_SIZE   6       /* Block size of disk is available*/
 #define VIRTIO_BLK_F_SCSI       7       /* Supports scsi command passthru */
 /* #define VIRTIO_BLK_F_IDENTIFY   8       ATA IDENTIFY supported, DEPRECATED */
-#define VIRTIO_BLK_F_WCACHE     9       /* write cache enabled */
+#define VIRTIO_BLK_F_WCE        9       /* write cache enabled */
 #define VIRTIO_BLK_F_TOPOLOGY   10      /* Topology information is available */
+#define VIRTIO_BLK_F_CONFIG_WCE 11      /* write cache configurable */
 
 #define VIRTIO_BLK_ID_BYTES     20      /* ID string length */
 
@@ -49,6 +50,7 @@ struct virtio_blk_config
     uint8_t alignment_offset;
     uint16_t min_io_size;
     uint32_t opt_io_size;
+    uint8_t wce;
 } QEMU_PACKED;
 
 /* These two define direction. */
diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
index c4a5b22f94..5f737acd97 100644
--- a/hw/virtio-scsi.c
+++ b/hw/virtio-scsi.c
@@ -305,11 +305,17 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
             goto incorrect_lun;
         }
         QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
-            if (r->tag == req->req.tmf->tag) {
+            VirtIOSCSIReq *cmd_req = r->hba_private;
+            if (cmd_req && cmd_req->req.cmd->tag == req->req.tmf->tag) {
                 break;
             }
         }
-        if (r && r->hba_private) {
+        if (r) {
+            /*
+             * Assert that the request has not been completed yet, we
+             * check for it in the loop above.
+             */
+            assert(r->hba_private);
             if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {
                 /* "If the specified command is present in the task set, then
                  * return a service response set to FUNCTION SUCCEEDED".
diff --git a/linux-user/main.c b/linux-user/main.c
index 53714de0d4..9d921aa4f0 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -958,7 +958,8 @@ void cpu_loop(CPUUniCore32State *env)
                 }
             }
             break;
-        case UC32_EXCP_TRAP:
+        case UC32_EXCP_DTRAP:
+        case UC32_EXCP_ITRAP:
             info.si_signo = SIGSEGV;
             info.si_errno = 0;
             /* XXX: check env->error_code */
diff --git a/qemu-common.h b/qemu-common.h
index f16079f432..f9deca6f86 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -376,6 +376,7 @@ bool buffer_is_zero(const void *buf, size_t len);
 void qemu_progress_init(int enabled, float min_skip);
 void qemu_progress_end(void);
 void qemu_progress_print(float delta, int max);
+const char *qemu_get_vm_name(void);
 
 #define QEMU_FILE_TYPE_BIOS   0
 #define QEMU_FILE_TYPE_KEYMAP 1
diff --git a/qemu-doc.texi b/qemu-doc.texi
index f32e9e2fb9..35cabbcb9e 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -734,6 +734,11 @@ Various session related parameters can be set via special options, either
 in a configuration file provided via '-readconfig' or directly on the
 command line.
 
+If the initiator-name is not specified qemu will use a default name
+of 'iqn.2008-11.org.linux-kvm[:<name>'] where <name> is the name of the
+virtual machine.
+
+
 @example
 Setting a specific initiator name to use when logging in to the target
 -iscsi initiator-name=iqn.qemu.test:my-initiator
diff --git a/qemu-img.c b/qemu-img.c
index 94a31ad9f0..b41e670a61 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -379,7 +379,7 @@ static int img_check(int argc, char **argv)
     BlockDriverState *bs;
     BdrvCheckResult result;
     int fix = 0;
-    int flags = BDRV_O_FLAGS;
+    int flags = BDRV_O_FLAGS | BDRV_O_CHECK;
 
     fmt = NULL;
     for(;;) {
diff --git a/qemu-options.hx b/qemu-options.hx
index 5e7d0dc035..47cb5bd311 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1897,6 +1897,11 @@ images for the guest storage. Both disk and cdrom images are supported.
 Syntax for specifying iSCSI LUNs is
 ``iscsi://<target-ip>[:<port>]/<target-iqn>/<lun>''
 
+By default qemu will use the iSCSI initiator-name
+'iqn.2008-11.org.linux-kvm[:<name>]' but this can also be set from the command
+line or a configuration file.
+
+
 Example (without authentication):
 @example
 qemu-system-i386 -iscsi initiator-name=iqn.2001-04.com.example:my-initiator \
@@ -1926,6 +1931,9 @@ DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi,
     "                iSCSI session parameters\n", QEMU_ARCH_ALL)
 STEXI
 
+iSCSI parameters such as username and password can also be specified via
+a configuration file. See qemu-doc for more information and examples.
+
 @item NBD
 QEMU supports NBD (Network Block Devices) both using TCP protocol as well
 as Unix Domain Sockets.
diff --git a/qemu-tool.c b/qemu-tool.c
index 318c5fcbca..64b5e88bc7 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -30,6 +30,11 @@ struct QEMUBH
     void *opaque;
 };
 
+const char *qemu_get_vm_name(void)
+{
+    return NULL;
+}
+
 Monitor *cur_mon;
 
 int monitor_cur_is_qmp(void)
diff --git a/target-unicore32/Makefile.objs b/target-unicore32/Makefile.objs
index 2e0e093e1f..777f01fef8 100644
--- a/target-unicore32/Makefile.objs
+++ b/target-unicore32/Makefile.objs
@@ -1,4 +1,6 @@
 obj-y += translate.o op_helper.o helper.o cpu.o
-obj-$(CONFIG_SOFTMMU) += machine.o
+obj-y += ucf64_helper.o
+
+obj-$(CONFIG_SOFTMMU) += machine.o softmmu.o
 
 $(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c
index de63f58dda..3425bbeac9 100644
--- a/target-unicore32/cpu.c
+++ b/target-unicore32/cpu.c
@@ -1,7 +1,7 @@
 /*
  * QEMU UniCore32 CPU
  *
- * Copyright (c) 2010-2011 GUAN Xue-tao
+ * Copyright (c) 2010-2012 Guan Xuetao
  * Copyright (c) 2012 SUSE LINUX Products GmbH
  *
  * This program is free software; you can redistribute it and/or modify
@@ -32,13 +32,16 @@ static void unicore_ii_cpu_initfn(Object *obj)
     UniCore32CPU *cpu = UNICORE32_CPU(obj);
     CPUUniCore32State *env = &cpu->env;
 
-    env->cp0.c0_cpuid = 0x40010863;
+    env->cp0.c0_cpuid = 0x4d000863;
+    env->cp0.c0_cachetype = 0x0d152152;
+    env->cp0.c1_sys = 0x2000;
+    env->cp0.c2_base = 0x0;
+    env->cp0.c3_faultstatus = 0x0;
+    env->cp0.c4_faultaddr = 0x0;
+    env->ucf64.xregs[UC32_UCF64_FPSCR] = 0;
 
     set_feature(env, UC32_HWCAP_CMOV);
     set_feature(env, UC32_HWCAP_UCF64);
-    env->ucf64.xregs[UC32_UCF64_FPSCR] = 0;
-    env->cp0.c0_cachetype = 0x1dd20d2;
-    env->cp0.c1_sys = 0x00090078;
 }
 
 static void uc32_any_cpu_initfn(Object *obj)
@@ -47,6 +50,7 @@ static void uc32_any_cpu_initfn(Object *obj)
     CPUUniCore32State *env = &cpu->env;
 
     env->cp0.c0_cpuid = 0xffffffff;
+    env->ucf64.xregs[UC32_UCF64_FPSCR] = 0;
 
     set_feature(env, UC32_HWCAP_CMOV);
     set_feature(env, UC32_HWCAP_UCF64);
@@ -65,8 +69,13 @@ static void uc32_cpu_initfn(Object *obj)
     cpu_exec_init(env);
     env->cpu_model_str = object_get_typename(obj);
 
+#ifdef CONFIG_USER_ONLY
     env->uncached_asr = ASR_MODE_USER;
     env->regs[31] = 0;
+#else
+    env->uncached_asr = ASR_MODE_PRIV;
+    env->regs[31] = 0x03000000;
+#endif
 
     tlb_flush(env, 1);
 }
diff --git a/target-unicore32/cpu.h b/target-unicore32/cpu.h
index 81c14ffd77..06508a1278 100644
--- a/target-unicore32/cpu.h
+++ b/target-unicore32/cpu.h
@@ -1,15 +1,15 @@
 /*
  * UniCore32 virtual CPU header
  *
- * Copyright (C) 2010-2011 GUAN Xue-tao
+ * Copyright (C) 2010-2012 Guan Xuetao
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation, or (at your option) any
  * later version. See the COPYING file in the top-level directory.
  */
-#ifndef __CPU_UC32_H__
-#define __CPU_UC32_H__
+#ifndef QEMU_UNICORE32_CPU_H
+#define QEMU_UNICORE32_CPU_H
 
 #define TARGET_LONG_BITS                32
 #define TARGET_PAGE_BITS                12
@@ -89,8 +89,10 @@ typedef struct CPUUniCore32State {
 #define ASR_NZCV                (ASR_N | ASR_Z | ASR_C | ASR_V)
 #define ASR_RESERVED            (~(ASR_M | ASR_I | ASR_NZCV))
 
-#define UC32_EXCP_PRIV          (ASR_MODE_PRIV)
-#define UC32_EXCP_TRAP          (ASR_MODE_TRAP)
+#define UC32_EXCP_PRIV          (1)
+#define UC32_EXCP_ITRAP         (2)
+#define UC32_EXCP_DTRAP         (3)
+#define UC32_EXCP_INTR          (4)
 
 /* Return the current ASR value.  */
 target_ulong cpu_asr_read(CPUUniCore32State *env1);
@@ -120,10 +122,6 @@ void cpu_asr_write(CPUUniCore32State *env1, target_ulong val, target_ulong mask)
 #define UC32_HWCAP_CMOV                 4 /* 1 << 2 */
 #define UC32_HWCAP_UCF64                8 /* 1 << 3 */
 
-#define UC32_CPUID(env)                 (env->cp0.c0_cpuid)
-#define UC32_CPUID_UCV2                 0x40010863
-#define UC32_CPUID_ANY                  0xffffffff
-
 #define cpu_init                        uc32_cpu_init
 #define cpu_exec                        uc32_cpu_exec
 #define cpu_signal_handler              uc32_cpu_signal_handler
@@ -189,4 +187,4 @@ static inline bool cpu_has_work(CPUUniCore32State *env)
         (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB);
 }
 
-#endif /* __CPU_UC32_H__ */
+#endif /* QEMU_UNICORE32_CPU_H */
diff --git a/target-unicore32/helper.c b/target-unicore32/helper.c
index 9fe4a375e4..a9e226bde4 100644
--- a/target-unicore32/helper.c
+++ b/target-unicore32/helper.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2011 GUAN Xue-tao
+ * Copyright (C) 2010-2012 Guan Xuetao
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -13,6 +13,15 @@
 #include "gdbstub.h"
 #include "helper.h"
 #include "host-utils.h"
+#include "console.h"
+
+#undef DEBUG_UC32
+
+#ifdef DEBUG_UC32
+#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
 
 CPUUniCore32State *uc32_cpu_init(const char *cpu_model)
 {
@@ -45,389 +54,203 @@ uint32_t HELPER(clz)(uint32_t x)
     return clz32(x);
 }
 
-void do_interrupt(CPUUniCore32State *env)
-{
-    env->exception_index = -1;
-}
-
-int uc32_cpu_handle_mmu_fault(CPUUniCore32State *env, target_ulong address, int rw,
-                              int mmu_idx)
-{
-    env->exception_index = UC32_EXCP_TRAP;
-    env->cp0.c4_faultaddr = address;
-    return 1;
-}
-
-/* These should probably raise undefined insn exceptions.  */
-void HELPER(set_cp)(CPUUniCore32State *env, uint32_t insn, uint32_t val)
-{
-    int op1 = (insn >> 8) & 0xf;
-    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
-    return;
-}
-
-uint32_t HELPER(get_cp)(CPUUniCore32State *env, uint32_t insn)
-{
-    int op1 = (insn >> 8) & 0xf;
-    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
-    return 0;
-}
-
-void HELPER(set_cp0)(CPUUniCore32State *env, uint32_t insn, uint32_t val)
-{
-    cpu_abort(env, "cp0 insn %08x\n", insn);
-}
-
-uint32_t HELPER(get_cp0)(CPUUniCore32State *env, uint32_t insn)
-{
-    cpu_abort(env, "cp0 insn %08x\n", insn);
-    return 0;
-}
-
-void switch_mode(CPUUniCore32State *env, int mode)
-{
-    if (mode != ASR_MODE_USER) {
-        cpu_abort(env, "Tried to switch out of user mode\n");
-    }
-}
-
-void HELPER(set_r29_banked)(CPUUniCore32State *env, uint32_t mode, uint32_t val)
-{
-    cpu_abort(env, "banked r29 write\n");
-}
-
-uint32_t HELPER(get_r29_banked)(CPUUniCore32State *env, uint32_t mode)
-{
-    cpu_abort(env, "banked r29 read\n");
-    return 0;
-}
-
-/* UniCore-F64 support.  We follow the convention used for F64 instrunctions:
-   Single precition routines have a "s" suffix, double precision a
-   "d" suffix.  */
-
-/* Convert host exception flags to f64 form.  */
-static inline int ucf64_exceptbits_from_host(int host_bits)
-{
-    int target_bits = 0;
-
-    if (host_bits & float_flag_invalid) {
-        target_bits |= UCF64_FPSCR_FLAG_INVALID;
-    }
-    if (host_bits & float_flag_divbyzero) {
-        target_bits |= UCF64_FPSCR_FLAG_DIVZERO;
-    }
-    if (host_bits & float_flag_overflow) {
-        target_bits |= UCF64_FPSCR_FLAG_OVERFLOW;
-    }
-    if (host_bits & float_flag_underflow) {
-        target_bits |= UCF64_FPSCR_FLAG_UNDERFLOW;
-    }
-    if (host_bits & float_flag_inexact) {
-        target_bits |= UCF64_FPSCR_FLAG_INEXACT;
-    }
-    return target_bits;
-}
-
-uint32_t HELPER(ucf64_get_fpscr)(CPUUniCore32State *env)
-{
-    int i;
-    uint32_t fpscr;
-
-    fpscr = (env->ucf64.xregs[UC32_UCF64_FPSCR] & UCF64_FPSCR_MASK);
-    i = get_float_exception_flags(&env->ucf64.fp_status);
-    fpscr |= ucf64_exceptbits_from_host(i);
-    return fpscr;
-}
-
-/* Convert ucf64 exception flags to target form.  */
-static inline int ucf64_exceptbits_to_host(int target_bits)
-{
-    int host_bits = 0;
-
-    if (target_bits & UCF64_FPSCR_FLAG_INVALID) {
-        host_bits |= float_flag_invalid;
-    }
-    if (target_bits & UCF64_FPSCR_FLAG_DIVZERO) {
-        host_bits |= float_flag_divbyzero;
-    }
-    if (target_bits & UCF64_FPSCR_FLAG_OVERFLOW) {
-        host_bits |= float_flag_overflow;
-    }
-    if (target_bits & UCF64_FPSCR_FLAG_UNDERFLOW) {
-        host_bits |= float_flag_underflow;
-    }
-    if (target_bits & UCF64_FPSCR_FLAG_INEXACT) {
-        host_bits |= float_flag_inexact;
-    }
-    return host_bits;
-}
-
-void HELPER(ucf64_set_fpscr)(CPUUniCore32State *env, uint32_t val)
-{
-    int i;
-    uint32_t changed;
-
-    changed = env->ucf64.xregs[UC32_UCF64_FPSCR];
-    env->ucf64.xregs[UC32_UCF64_FPSCR] = (val & UCF64_FPSCR_MASK);
-
-    changed ^= val;
-    if (changed & (UCF64_FPSCR_RND_MASK)) {
-        i = UCF64_FPSCR_RND(val);
-        switch (i) {
-        case 0:
-            i = float_round_nearest_even;
-            break;
-        case 1:
-            i = float_round_to_zero;
-            break;
-        case 2:
-            i = float_round_up;
-            break;
-        case 3:
-            i = float_round_down;
-            break;
-        default: /* 100 and 101 not implement */
-            cpu_abort(env, "Unsupported UniCore-F64 round mode");
-        }
-        set_float_rounding_mode(i, &env->ucf64.fp_status);
-    }
-
-    i = ucf64_exceptbits_to_host(UCF64_FPSCR_TRAPEN(val));
-    set_float_exception_flags(i, &env->ucf64.fp_status);
-}
-
-float32 HELPER(ucf64_adds)(float32 a, float32 b, CPUUniCore32State *env)
-{
-    return float32_add(a, b, &env->ucf64.fp_status);
-}
-
-float64 HELPER(ucf64_addd)(float64 a, float64 b, CPUUniCore32State *env)
-{
-    return float64_add(a, b, &env->ucf64.fp_status);
-}
-
-float32 HELPER(ucf64_subs)(float32 a, float32 b, CPUUniCore32State *env)
-{
-    return float32_sub(a, b, &env->ucf64.fp_status);
-}
-
-float64 HELPER(ucf64_subd)(float64 a, float64 b, CPUUniCore32State *env)
-{
-    return float64_sub(a, b, &env->ucf64.fp_status);
-}
-
-float32 HELPER(ucf64_muls)(float32 a, float32 b, CPUUniCore32State *env)
-{
-    return float32_mul(a, b, &env->ucf64.fp_status);
-}
-
-float64 HELPER(ucf64_muld)(float64 a, float64 b, CPUUniCore32State *env)
-{
-    return float64_mul(a, b, &env->ucf64.fp_status);
-}
-
-float32 HELPER(ucf64_divs)(float32 a, float32 b, CPUUniCore32State *env)
-{
-    return float32_div(a, b, &env->ucf64.fp_status);
-}
-
-float64 HELPER(ucf64_divd)(float64 a, float64 b, CPUUniCore32State *env)
-{
-    return float64_div(a, b, &env->ucf64.fp_status);
-}
-
-float32 HELPER(ucf64_negs)(float32 a)
-{
-    return float32_chs(a);
-}
-
-float64 HELPER(ucf64_negd)(float64 a)
-{
-    return float64_chs(a);
-}
-
-float32 HELPER(ucf64_abss)(float32 a)
-{
-    return float32_abs(a);
-}
-
-float64 HELPER(ucf64_absd)(float64 a)
-{
-    return float64_abs(a);
-}
-
-/* XXX: check quiet/signaling case */
-void HELPER(ucf64_cmps)(float32 a, float32 b, uint32_t c, CPUUniCore32State *env)
-{
-    int flag;
-    flag = float32_compare_quiet(a, b, &env->ucf64.fp_status);
-    env->CF = 0;
-    switch (c & 0x7) {
-    case 0: /* F */
-        break;
-    case 1: /* UN */
-        if (flag == 2) {
-            env->CF = 1;
+#ifndef CONFIG_USER_ONLY
+void helper_cp0_set(CPUUniCore32State *env, uint32_t val, uint32_t creg,
+        uint32_t cop)
+{
+    /*
+     * movc pp.nn, rn, #imm9
+     *      rn: UCOP_REG_D
+     *      nn: UCOP_REG_N
+     *          1: sys control reg.
+     *          2: page table base reg.
+     *          3: data fault status reg.
+     *          4: insn fault status reg.
+     *          5: cache op. reg.
+     *          6: tlb op. reg.
+     *      imm9: split UCOP_IMM10 with bit5 is 0
+     */
+    switch (creg) {
+    case 1:
+        if (cop != 0) {
+            goto unrecognized;
         }
+        env->cp0.c1_sys = val;
         break;
-    case 2: /* EQ */
-        if (flag == 0) {
-            env->CF = 1;
+    case 2:
+        if (cop != 0) {
+            goto unrecognized;
         }
+        env->cp0.c2_base = val;
         break;
-    case 3: /* UEQ */
-        if ((flag == 0) || (flag == 2)) {
-            env->CF = 1;
+    case 3:
+        if (cop != 0) {
+            goto unrecognized;
         }
+        env->cp0.c3_faultstatus = val;
         break;
-    case 4: /* OLT */
-        if (flag == -1) {
-            env->CF = 1;
+    case 4:
+        if (cop != 0) {
+            goto unrecognized;
         }
+        env->cp0.c4_faultaddr = val;
         break;
-    case 5: /* ULT */
-        if ((flag == -1) || (flag == 2)) {
-            env->CF = 1;
+    case 5:
+        switch (cop) {
+        case 28:
+            DPRINTF("Invalidate Entire I&D cache\n");
+            return;
+        case 20:
+            DPRINTF("Invalidate Entire Icache\n");
+            return;
+        case 12:
+            DPRINTF("Invalidate Entire Dcache\n");
+            return;
+        case 10:
+            DPRINTF("Clean Entire Dcache\n");
+            return;
+        case 14:
+            DPRINTF("Flush Entire Dcache\n");
+            return;
+        case 13:
+            DPRINTF("Invalidate Dcache line\n");
+            return;
+        case 11:
+            DPRINTF("Clean Dcache line\n");
+            return;
+        case 15:
+            DPRINTF("Flush Dcache line\n");
+            return;
         }
         break;
-    case 6: /* OLE */
-        if ((flag == -1) || (flag == 0)) {
-            env->CF = 1;
-        }
-        break;
-    case 7: /* ULE */
-        if (flag != 1) {
-            env->CF = 1;
+    case 6:
+        if ((cop <= 6) && (cop >= 2)) {
+            /* invalid all tlb */
+            tlb_flush(env, 1);
+            return;
         }
         break;
+    default:
+        goto unrecognized;
     }
-    env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29)
-                    | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff);
-}
-
-void HELPER(ucf64_cmpd)(float64 a, float64 b, uint32_t c, CPUUniCore32State *env)
-{
-    int flag;
-    flag = float64_compare_quiet(a, b, &env->ucf64.fp_status);
-    env->CF = 0;
-    switch (c & 0x7) {
-    case 0: /* F */
-        break;
-    case 1: /* UN */
-        if (flag == 2) {
-            env->CF = 1;
-        }
-        break;
-    case 2: /* EQ */
-        if (flag == 0) {
-            env->CF = 1;
-        }
-        break;
-    case 3: /* UEQ */
-        if ((flag == 0) || (flag == 2)) {
-            env->CF = 1;
+    return;
+unrecognized:
+    DPRINTF("Wrong register (%d) or wrong operation (%d) in cp0_set!\n",
+            creg, cop);
+}
+
+uint32_t helper_cp0_get(CPUUniCore32State *env, uint32_t creg, uint32_t cop)
+{
+    /*
+     * movc rd, pp.nn, #imm9
+     *      rd: UCOP_REG_D
+     *      nn: UCOP_REG_N
+     *          0: cpuid and cachetype
+     *          1: sys control reg.
+     *          2: page table base reg.
+     *          3: data fault status reg.
+     *          4: insn fault status reg.
+     *      imm9: split UCOP_IMM10 with bit5 is 0
+     */
+    switch (creg) {
+    case 0:
+        switch (cop) {
+        case 0:
+            return env->cp0.c0_cpuid;
+        case 1:
+            return env->cp0.c0_cachetype;
         }
         break;
-    case 4: /* OLT */
-        if (flag == -1) {
-            env->CF = 1;
+    case 1:
+        if (cop == 0) {
+            return env->cp0.c1_sys;
         }
         break;
-    case 5: /* ULT */
-        if ((flag == -1) || (flag == 2)) {
-            env->CF = 1;
+    case 2:
+        if (cop == 0) {
+            return env->cp0.c2_base;
         }
         break;
-    case 6: /* OLE */
-        if ((flag == -1) || (flag == 0)) {
-            env->CF = 1;
+    case 3:
+        if (cop == 0) {
+            return env->cp0.c3_faultstatus;
         }
         break;
-    case 7: /* ULE */
-        if (flag != 1) {
-            env->CF = 1;
+    case 4:
+        if (cop == 0) {
+            return env->cp0.c4_faultaddr;
         }
         break;
     }
-    env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29)
-                    | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff);
-}
-
-/* Helper routines to perform bitwise copies between float and int.  */
-static inline float32 ucf64_itos(uint32_t i)
-{
-    union {
-        uint32_t i;
-        float32 s;
-    } v;
-
-    v.i = i;
-    return v.s;
-}
-
-static inline uint32_t ucf64_stoi(float32 s)
-{
-    union {
-        uint32_t i;
-        float32 s;
-    } v;
-
-    v.s = s;
-    return v.i;
-}
-
-static inline float64 ucf64_itod(uint64_t i)
-{
-    union {
-        uint64_t i;
-        float64 d;
-    } v;
-
-    v.i = i;
-    return v.d;
+    DPRINTF("Wrong register (%d) or wrong operation (%d) in cp0_set!\n",
+            creg, cop);
+    return 0;
 }
 
-static inline uint64_t ucf64_dtoi(float64 d)
+#ifdef CONFIG_CURSES
+/*
+ * FIXME:
+ *     1. curses windows will be blank when switching back
+ *     2. backspace is not handled yet
+ */
+static void putc_on_screen(unsigned char ch)
 {
-    union {
-        uint64_t i;
-        float64 d;
-    } v;
+    static WINDOW *localwin;
+    static int init;
 
-    v.d = d;
-    return v.i;
-}
+    if (!init) {
+        /* Assume 80 * 30 screen to minimize the implementation */
+        localwin = newwin(30, 80, 0, 0);
+        scrollok(localwin, TRUE);
+        init = TRUE;
+    }
 
-/* Integer to float conversion.  */
-float32 HELPER(ucf64_si2sf)(float32 x, CPUUniCore32State *env)
-{
-    return int32_to_float32(ucf64_stoi(x), &env->ucf64.fp_status);
-}
+    if (isprint(ch)) {
+        wprintw(localwin, "%c", ch);
+    } else {
+        switch (ch) {
+        case '\n':
+            wprintw(localwin, "%c", ch);
+            break;
+        case '\r':
+            /* If '\r' is put before '\n', the curses window will destroy the
+             * last print line. And meanwhile, '\n' implifies '\r' inside. */
+            break;
+        default: /* Not handled, so just print it hex code */
+            wprintw(localwin, "-- 0x%x --", ch);
+        }
+    }
 
-float64 HELPER(ucf64_si2df)(float32 x, CPUUniCore32State *env)
-{
-    return int32_to_float64(ucf64_stoi(x), &env->ucf64.fp_status);
+    wrefresh(localwin);
 }
+#else
+#define putc_on_screen(c)               do { } while (0)
+#endif
 
-/* Float to integer conversion.  */
-float32 HELPER(ucf64_sf2si)(float32 x, CPUUniCore32State *env)
+void helper_cp1_putc(target_ulong x)
 {
-    return ucf64_itos(float32_to_int32(x, &env->ucf64.fp_status));
+    putc_on_screen((unsigned char)x);   /* Output to screen */
+    DPRINTF("%c", x);                   /* Output to stdout */
 }
+#endif
 
-float32 HELPER(ucf64_df2si)(float64 x, CPUUniCore32State *env)
+#ifdef CONFIG_USER_ONLY
+void switch_mode(CPUUniCore32State *env, int mode)
 {
-    return ucf64_itos(float64_to_int32(x, &env->ucf64.fp_status));
+    if (mode != ASR_MODE_USER) {
+        cpu_abort(env, "Tried to switch out of user mode\n");
+    }
 }
 
-/* floating point conversion */
-float64 HELPER(ucf64_sf2df)(float32 x, CPUUniCore32State *env)
+void do_interrupt(CPUUniCore32State *env)
 {
-    return float32_to_float64(x, &env->ucf64.fp_status);
+    cpu_abort(env, "NO interrupt in user mode\n");
 }
 
-float32 HELPER(ucf64_df2sf)(float64 x, CPUUniCore32State *env)
+int uc32_cpu_handle_mmu_fault(CPUUniCore32State *env, target_ulong address,
+                              int access_type, int mmu_idx)
 {
-    return float64_to_float32(x, &env->ucf64.fp_status);
+    cpu_abort(env, "NO mmu fault in user mode\n");
+    return 1;
 }
+#endif
diff --git a/target-unicore32/helper.h b/target-unicore32/helper.h
index 5a3b8a41ea..305318ae59 100644
--- a/target-unicore32/helper.h
+++ b/target-unicore32/helper.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2011 GUAN Xue-tao
+ * Copyright (C) 2010-2012 Guan Xuetao
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -8,6 +8,12 @@
  */
 #include "def-helper.h"
 
+#ifndef CONFIG_USER_ONLY
+DEF_HELPER_4(cp0_set, void, env, i32, i32, i32)
+DEF_HELPER_3(cp0_get, i32, env, i32, i32)
+DEF_HELPER_1(cp1_putc, void, i32)
+#endif
+
 DEF_HELPER_1(clz, i32, i32)
 DEF_HELPER_1(clo, i32, i32)
 
@@ -16,12 +22,6 @@ DEF_HELPER_1(exception, void, i32)
 DEF_HELPER_2(asr_write, void, i32, i32)
 DEF_HELPER_0(asr_read, i32)
 
-DEF_HELPER_3(set_cp0, void, env, i32, i32)
-DEF_HELPER_2(get_cp0, i32, env, i32)
-
-DEF_HELPER_3(set_cp, void, env, i32, i32)
-DEF_HELPER_2(get_cp, i32, env, i32)
-
 DEF_HELPER_1(get_user_reg, i32, i32)
 DEF_HELPER_2(set_user_reg, void, i32, i32)
 
@@ -38,9 +38,6 @@ DEF_HELPER_2(shr_cc, i32, i32, i32)
 DEF_HELPER_2(sar_cc, i32, i32, i32)
 DEF_HELPER_2(ror_cc, i32, i32, i32)
 
-DEF_HELPER_2(get_r29_banked, i32, env, i32)
-DEF_HELPER_3(set_r29_banked, void, env, i32, i32)
-
 DEF_HELPER_1(ucf64_get_fpscr, i32, env)
 DEF_HELPER_2(ucf64_set_fpscr, void, env, i32)
 
diff --git a/target-unicore32/machine.c b/target-unicore32/machine.c
new file mode 100644
index 0000000000..60b2ec1771
--- /dev/null
+++ b/target-unicore32/machine.c
@@ -0,0 +1,23 @@
+/*
+ * Generic machine functions for UniCore32 ISA
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw/hw.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+    hw_error("%s not supported yet.\n", __func__);
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    hw_error("%s not supported yet.\n", __func__);
+
+    return 0;
+}
diff --git a/target-unicore32/op_helper.c b/target-unicore32/op_helper.c
index b954c30a84..c63789d6cb 100644
--- a/target-unicore32/op_helper.c
+++ b/target-unicore32/op_helper.c
@@ -1,7 +1,7 @@
 /*
  *  UniCore32 helper routines
  *
- * Copyright (C) 2010-2011 GUAN Xue-tao
+ * Copyright (C) 2010-2012 Guan Xuetao
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -248,3 +248,45 @@ uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i)
         return ((uint32_t)x >> shift) | (x << (32 - shift));
     }
 }
+
+#ifndef CONFIG_USER_ONLY
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+void tlb_fill(CPUUniCore32State *env1, target_ulong addr, int is_write,
+        int mmu_idx, uintptr_t retaddr)
+{
+    TranslationBlock *tb;
+    CPUUniCore32State *saved_env;
+    unsigned long pc;
+    int ret;
+
+    saved_env = env;
+    env = env1;
+    ret = uc32_cpu_handle_mmu_fault(env, addr, is_write, mmu_idx);
+    if (unlikely(ret)) {
+        if (retaddr) {
+            /* now we have a real cpu fault */
+            pc = (unsigned long)retaddr;
+            tb = tb_find_pc(pc);
+            if (tb) {/* the PC is inside the translated code.
+                        It means that we have a virtual CPU fault */
+                cpu_restore_state(tb, env, pc);
+            }
+        }
+        cpu_loop_exit(env);
+    }
+    env = saved_env;
+}
+#endif
diff --git a/target-unicore32/softmmu.c b/target-unicore32/softmmu.c
new file mode 100644
index 0000000000..373f94b274
--- /dev/null
+++ b/target-unicore32/softmmu.c
@@ -0,0 +1,267 @@
+/*
+ * Softmmu related functions
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#ifdef CONFIG_USER_ONLY
+#error This file only exist under softmmu circumstance
+#endif
+
+#include <cpu.h>
+
+#undef DEBUG_UC32
+
+#ifdef DEBUG_UC32
+#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define SUPERPAGE_SIZE             (1 << 22)
+#define UC32_PAGETABLE_READ        (1 << 8)
+#define UC32_PAGETABLE_WRITE       (1 << 7)
+#define UC32_PAGETABLE_EXEC        (1 << 6)
+#define UC32_PAGETABLE_EXIST       (1 << 2)
+#define PAGETABLE_TYPE(x)          ((x) & 3)
+
+
+/* Map CPU modes onto saved register banks.  */
+static inline int bank_number(int mode)
+{
+    switch (mode) {
+    case ASR_MODE_USER:
+    case ASR_MODE_SUSR:
+        return 0;
+    case ASR_MODE_PRIV:
+        return 1;
+    case ASR_MODE_TRAP:
+        return 2;
+    case ASR_MODE_EXTN:
+        return 3;
+    case ASR_MODE_INTR:
+        return 4;
+    }
+    cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
+    return -1;
+}
+
+void switch_mode(CPUUniCore32State *env, int mode)
+{
+    int old_mode;
+    int i;
+
+    old_mode = env->uncached_asr & ASR_M;
+    if (mode == old_mode) {
+        return;
+    }
+
+    i = bank_number(old_mode);
+    env->banked_r29[i] = env->regs[29];
+    env->banked_r30[i] = env->regs[30];
+    env->banked_bsr[i] = env->bsr;
+
+    i = bank_number(mode);
+    env->regs[29] = env->banked_r29[i];
+    env->regs[30] = env->banked_r30[i];
+    env->bsr = env->banked_bsr[i];
+}
+
+/* Handle a CPU exception.  */
+void do_interrupt(CPUUniCore32State *env)
+{
+    uint32_t addr;
+    int new_mode;
+
+    switch (env->exception_index) {
+    case UC32_EXCP_PRIV:
+        new_mode = ASR_MODE_PRIV;
+        addr = 0x08;
+        break;
+    case UC32_EXCP_ITRAP:
+        DPRINTF("itrap happened at %x\n", env->regs[31]);
+        new_mode = ASR_MODE_TRAP;
+        addr = 0x0c;
+        break;
+    case UC32_EXCP_DTRAP:
+        DPRINTF("dtrap happened at %x\n", env->regs[31]);
+        new_mode = ASR_MODE_TRAP;
+        addr = 0x10;
+        break;
+    case UC32_EXCP_INTR:
+        new_mode = ASR_MODE_INTR;
+        addr = 0x18;
+        break;
+    default:
+        cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
+        return;
+    }
+    /* High vectors.  */
+    if (env->cp0.c1_sys & (1 << 13)) {
+        addr += 0xffff0000;
+    }
+
+    switch_mode(env, new_mode);
+    env->bsr = cpu_asr_read(env);
+    env->uncached_asr = (env->uncached_asr & ~ASR_M) | new_mode;
+    env->uncached_asr |= ASR_I;
+    /* The PC already points to the proper instruction.  */
+    env->regs[30] = env->regs[31];
+    env->regs[31] = addr;
+    env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+}
+
+static int get_phys_addr_ucv2(CPUUniCore32State *env, uint32_t address,
+        int access_type, int is_user, uint32_t *phys_ptr, int *prot,
+        target_ulong *page_size)
+{
+    int code;
+    uint32_t table;
+    uint32_t desc;
+    uint32_t phys_addr;
+
+    /* Pagetable walk.  */
+    /* Lookup l1 descriptor.  */
+    table = env->cp0.c2_base & 0xfffff000;
+    table |= (address >> 20) & 0xffc;
+    desc = ldl_phys(table);
+    code = 0;
+    switch (PAGETABLE_TYPE(desc)) {
+    case 3:
+        /* Superpage  */
+        if (!(desc & UC32_PAGETABLE_EXIST)) {
+            code = 0x0b; /* superpage miss */
+            goto do_fault;
+        }
+        phys_addr = (desc & 0xffc00000) | (address & 0x003fffff);
+        *page_size = SUPERPAGE_SIZE;
+        break;
+    case 0:
+        /* Lookup l2 entry.  */
+        if (is_user) {
+            DPRINTF("PGD address %x, desc %x\n", table, desc);
+        }
+        if (!(desc & UC32_PAGETABLE_EXIST)) {
+            code = 0x05; /* second pagetable miss */
+            goto do_fault;
+        }
+        table = (desc & 0xfffff000) | ((address >> 10) & 0xffc);
+        desc = ldl_phys(table);
+        /* 4k page.  */
+        if (is_user) {
+            DPRINTF("PTE address %x, desc %x\n", table, desc);
+        }
+        if (!(desc & UC32_PAGETABLE_EXIST)) {
+            code = 0x08; /* page miss */
+            goto do_fault;
+        }
+        switch (PAGETABLE_TYPE(desc)) {
+        case 0:
+            phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+            *page_size = TARGET_PAGE_SIZE;
+            break;
+        default:
+            cpu_abort(env, "wrong page type!");
+        }
+        break;
+    default:
+        cpu_abort(env, "wrong page type!");
+    }
+
+    *phys_ptr = phys_addr;
+    *prot = 0;
+    /* Check access permissions.  */
+    if (desc & UC32_PAGETABLE_READ) {
+        *prot |= PAGE_READ;
+    } else {
+        if (is_user && (access_type == 0)) {
+            code = 0x11; /* access unreadable area */
+            goto do_fault;
+        }
+    }
+
+    if (desc & UC32_PAGETABLE_WRITE) {
+        *prot |= PAGE_WRITE;
+    } else {
+        if (is_user && (access_type == 1)) {
+            code = 0x12; /* access unwritable area */
+            goto do_fault;
+        }
+    }
+
+    if (desc & UC32_PAGETABLE_EXEC) {
+        *prot |= PAGE_EXEC;
+    } else {
+        if (is_user && (access_type == 2)) {
+            code = 0x13; /* access unexecutable area */
+            goto do_fault;
+        }
+    }
+
+do_fault:
+    return code;
+}
+
+int uc32_cpu_handle_mmu_fault(CPUUniCore32State *env, target_ulong address,
+                              int access_type, int mmu_idx)
+{
+    uint32_t phys_addr;
+    target_ulong page_size;
+    int prot;
+    int ret, is_user;
+
+    ret = 1;
+    is_user = mmu_idx == MMU_USER_IDX;
+
+    if ((env->cp0.c1_sys & 1) == 0) {
+        /* MMU disabled.  */
+        phys_addr = address;
+        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+        page_size = TARGET_PAGE_SIZE;
+        ret = 0;
+    } else {
+        if ((address & (1 << 31)) || (is_user)) {
+            ret = get_phys_addr_ucv2(env, address, access_type, is_user,
+                                    &phys_addr, &prot, &page_size);
+            if (is_user) {
+                DPRINTF("user space access: ret %x, address %x, "
+                        "access_type %x, phys_addr %x, prot %x\n",
+                        ret, address, access_type, phys_addr, prot);
+            }
+        } else {
+            /*IO memory */
+            phys_addr = address | (1 << 31);
+            prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+            page_size = TARGET_PAGE_SIZE;
+            ret = 0;
+        }
+    }
+
+    if (ret == 0) {
+        /* Map a single page.  */
+        phys_addr &= TARGET_PAGE_MASK;
+        address &= TARGET_PAGE_MASK;
+        tlb_set_page(env, address, phys_addr, prot, mmu_idx, page_size);
+        return 0;
+    }
+
+    env->cp0.c3_faultstatus = ret;
+    env->cp0.c4_faultaddr = address;
+    if (access_type == 2) {
+        env->exception_index = UC32_EXCP_ITRAP;
+    } else {
+        env->exception_index = UC32_EXCP_DTRAP;
+    }
+    return ret;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUUniCore32State *env,
+        target_ulong addr)
+{
+    cpu_abort(env, "%s not supported yet\n", __func__);
+    return addr;
+}
diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c
index 9793d14c1b..188bf8c52a 100644
--- a/target-unicore32/translate.c
+++ b/target-unicore32/translate.c
@@ -1,7 +1,7 @@
 /*
  *  UniCore32 translation
  *
- * Copyright (C) 2010-2011 GUAN Xue-tao
+ * Copyright (C) 2010-2012 Guan Xuetao
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -33,9 +33,16 @@ typedef struct DisasContext {
     int condlabel;
     struct TranslationBlock *tb;
     int singlestep_enabled;
+#ifndef CONFIG_USER_ONLY
+    int user;
+#endif
 } DisasContext;
 
-#define IS_USER(s) 1
+#ifndef CONFIG_USER_ONLY
+#define IS_USER(s)      (s->user)
+#else
+#define IS_USER(s)      1
+#endif
 
 /* These instructions trap after executing, so defer them until after the
    conditional executions state has been updated.  */
@@ -176,6 +183,73 @@ static void store_reg(DisasContext *s, int reg, TCGv var)
                         "Illegal UniCore32 instruction %x at line %d!", \
                         insn, __LINE__)
 
+#ifndef CONFIG_USER_ONLY
+static void disas_cp0_insn(CPUUniCore32State *env, DisasContext *s,
+        uint32_t insn)
+{
+    TCGv tmp, tmp2, tmp3;
+    if ((insn & 0xfe000000) == 0xe0000000) {
+        tmp2 = new_tmp();
+        tmp3 = new_tmp();
+        tcg_gen_movi_i32(tmp2, UCOP_REG_N);
+        tcg_gen_movi_i32(tmp3, UCOP_IMM10);
+        if (UCOP_SET_L) {
+            tmp = new_tmp();
+            gen_helper_cp0_get(tmp, cpu_env, tmp2, tmp3);
+            store_reg(s, UCOP_REG_D, tmp);
+        } else {
+            tmp = load_reg(s, UCOP_REG_D);
+            gen_helper_cp0_set(cpu_env, tmp, tmp2, tmp3);
+            dead_tmp(tmp);
+        }
+        dead_tmp(tmp2);
+        dead_tmp(tmp3);
+        return;
+    }
+    ILLEGAL;
+}
+
+static void disas_ocd_insn(CPUUniCore32State *env, DisasContext *s,
+        uint32_t insn)
+{
+    TCGv tmp;
+
+    if ((insn & 0xff003fff) == 0xe1000400) {
+        /*
+         * movc rd, pp.nn, #imm9
+         *      rd: UCOP_REG_D
+         *      nn: UCOP_REG_N (must be 0)
+         *      imm9: 0
+         */
+        if (UCOP_REG_N == 0) {
+            tmp = new_tmp();
+            tcg_gen_movi_i32(tmp, 0);
+            store_reg(s, UCOP_REG_D, tmp);
+            return;
+        } else {
+            ILLEGAL;
+        }
+    }
+    if ((insn & 0xff003fff) == 0xe0000401) {
+        /*
+         * movc pp.nn, rn, #imm9
+         *      rn: UCOP_REG_D
+         *      nn: UCOP_REG_N (must be 1)
+         *      imm9: 1
+         */
+        if (UCOP_REG_N == 1) {
+            tmp = load_reg(s, UCOP_REG_D);
+            gen_helper_cp1_putc(tmp);
+            dead_tmp(tmp);
+            return;
+        } else {
+            ILLEGAL;
+        }
+    }
+    ILLEGAL;
+}
+#endif
+
 static inline void gen_set_asr(TCGv var, uint32_t mask)
 {
     TCGv tmp_mask = tcg_const_i32(mask);
@@ -1124,9 +1198,18 @@ static void gen_exception_return(DisasContext *s, TCGv pc)
     s->is_jmp = DISAS_UPDATE;
 }
 
-static void disas_coproc_insn(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
+static void disas_coproc_insn(CPUUniCore32State *env, DisasContext *s,
+        uint32_t insn)
 {
     switch (UCOP_CPNUM) {
+#ifndef CONFIG_USER_ONLY
+    case 0:
+        disas_cp0_insn(env, s, insn);
+        break;
+    case 1:
+        disas_ocd_insn(env, s, insn);
+        break;
+#endif
     case 2:
         disas_ucf64_insn(env, s, insn);
         break;
@@ -1478,12 +1561,12 @@ static void do_misc(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
 /* load/store I_offset and R_offset */
 static void do_ldst_ir(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
 {
-    unsigned int i;
+    unsigned int mmu_idx;
     TCGv tmp;
     TCGv tmp2;
 
     tmp2 = load_reg(s, UCOP_REG_N);
-    i = (IS_USER(s) || (!UCOP_SET_P && UCOP_SET_W));
+    mmu_idx = (IS_USER(s) || (!UCOP_SET_P && UCOP_SET_W));
 
     /* immediate */
     if (UCOP_SET_P) {
@@ -1493,17 +1576,17 @@ static void do_ldst_ir(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
     if (UCOP_SET_L) {
         /* load */
         if (UCOP_SET_B) {
-            tmp = gen_ld8u(tmp2, i);
+            tmp = gen_ld8u(tmp2, mmu_idx);
         } else {
-            tmp = gen_ld32(tmp2, i);
+            tmp = gen_ld32(tmp2, mmu_idx);
         }
     } else {
         /* store */
         tmp = load_reg(s, UCOP_REG_D);
         if (UCOP_SET_B) {
-            gen_st8(tmp, tmp2, i);
+            gen_st8(tmp, tmp2, mmu_idx);
         } else {
-            gen_st32(tmp, tmp2, i);
+            gen_st32(tmp, tmp2, mmu_idx);
         }
     }
     if (!UCOP_SET_P) {
@@ -1606,7 +1689,7 @@ static void do_ldst_hwsb(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
 /* load/store multiple words */
 static void do_ldst_m(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
 {
-    unsigned int val, i;
+    unsigned int val, i, mmu_idx;
     int j, n, reg, user, loaded_base;
     TCGv tmp;
     TCGv tmp2;
@@ -1627,6 +1710,7 @@ static void do_ldst_m(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
         }
     }
 
+    mmu_idx = (IS_USER(s) || (!UCOP_SET_P && UCOP_SET_W));
     addr = load_reg(s, UCOP_REG_N);
 
     /* compute total size */
@@ -1671,7 +1755,7 @@ static void do_ldst_m(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
         }
         if (UCOP_SET(i)) {
             if (UCOP_SET_L) { /* load */
-                tmp = gen_ld32(addr, IS_USER(s));
+                tmp = gen_ld32(addr, mmu_idx);
                 if (reg == 31) {
                     gen_bx(s, tmp);
                 } else if (user) {
@@ -1699,7 +1783,7 @@ static void do_ldst_m(CPUUniCore32State *env, DisasContext *s, uint32_t insn)
                 } else {
                     tmp = load_reg(s, reg);
                 }
-                gen_st32(tmp, addr, IS_USER(s));
+                gen_st32(tmp, addr, mmu_idx);
             }
             j++;
             /* no need to add after the last transfer */
@@ -1888,6 +1972,14 @@ static inline void gen_intermediate_code_internal(CPUUniCore32State *env,
         max_insns = CF_COUNT_MASK;
     }
 
+#ifndef CONFIG_USER_ONLY
+    if ((env->uncached_asr & ASR_M) == ASR_MODE_USER) {
+        dc->user = 1;
+    } else {
+        dc->user = 0;
+    }
+#endif
+
     gen_icount_start();
     do {
         if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
@@ -2046,12 +2138,12 @@ static const char *cpu_mode_names[16] = {
     "UM18", "UM19", "UM1A", "EXTN", "UM1C", "UM1D", "UM1E", "SUSR"
 };
 
-#define UCF64_DUMP_STATE
-void cpu_dump_state(CPUUniCore32State *env, FILE *f, fprintf_function cpu_fprintf,
-        int flags)
+#undef UCF64_DUMP_STATE
+#ifdef UCF64_DUMP_STATE
+static void cpu_dump_state_ucf64(CPUUniCore32State *env, FILE *f,
+        fprintf_function cpu_fprintf, int flags)
 {
     int i;
-#ifdef UCF64_DUMP_STATE
     union {
         uint32_t i;
         float s;
@@ -2063,7 +2155,28 @@ void cpu_dump_state(CPUUniCore32State *env, FILE *f, fprintf_function cpu_fprint
         float64 f64;
         double d;
     } d0;
+
+    for (i = 0; i < 16; i++) {
+        d.d = env->ucf64.regs[i];
+        s0.i = d.l.lower;
+        s1.i = d.l.upper;
+        d0.f64 = d.d;
+        cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g)",
+                    i * 2, (int)s0.i, s0.s,
+                    i * 2 + 1, (int)s1.i, s1.s);
+        cpu_fprintf(f, " d%02d=%" PRIx64 "(%8g)\n",
+                    i, (uint64_t)d0.f64, d0.d);
+    }
+    cpu_fprintf(f, "FPSCR: %08x\n", (int)env->ucf64.xregs[UC32_UCF64_FPSCR]);
+}
+#else
+#define cpu_dump_state_ucf64(env, file, pr, flags)      do { } while (0)
 #endif
+
+void cpu_dump_state(CPUUniCore32State *env, FILE *f,
+        fprintf_function cpu_fprintf, int flags)
+{
+    int i;
     uint32_t psr;
 
     for (i = 0; i < 32; i++) {
@@ -2083,19 +2196,7 @@ void cpu_dump_state(CPUUniCore32State *env, FILE *f, fprintf_function cpu_fprint
                 psr & (1 << 28) ? 'V' : '-',
                 cpu_mode_names[psr & 0xf]);
 
-#ifdef UCF64_DUMP_STATE
-    for (i = 0; i < 16; i++) {
-        d.d = env->ucf64.regs[i];
-        s0.i = d.l.lower;
-        s1.i = d.l.upper;
-        d0.f64 = d.d;
-        cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g) d%02d=%" PRIx64 "(%8g)\n",
-                    i * 2, (int)s0.i, s0.s,
-                    i * 2 + 1, (int)s1.i, s1.s,
-                    i, (uint64_t)d0.f64, d0.d);
-    }
-    cpu_fprintf(f, "FPSCR: %08x\n", (int)env->ucf64.xregs[UC32_UCF64_FPSCR]);
-#endif
+    cpu_dump_state_ucf64(env, f, cpu_fprintf, flags);
 }
 
 void restore_state_to_opc(CPUUniCore32State *env, TranslationBlock *tb, int pc_pos)
diff --git a/target-unicore32/ucf64_helper.c b/target-unicore32/ucf64_helper.c
new file mode 100644
index 0000000000..a516edd319
--- /dev/null
+++ b/target-unicore32/ucf64_helper.c
@@ -0,0 +1,345 @@
+/*
+ * UniCore-F64 simulation helpers for QEMU.
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "cpu.h"
+#include "helper.h"
+
+/*
+ * The convention used for UniCore-F64 instructions:
+ *  Single precition routines have a "s" suffix
+ *  Double precision routines have a "d" suffix.
+ */
+
+/* Convert host exception flags to f64 form.  */
+static inline int ucf64_exceptbits_from_host(int host_bits)
+{
+    int target_bits = 0;
+
+    if (host_bits & float_flag_invalid) {
+        target_bits |= UCF64_FPSCR_FLAG_INVALID;
+    }
+    if (host_bits & float_flag_divbyzero) {
+        target_bits |= UCF64_FPSCR_FLAG_DIVZERO;
+    }
+    if (host_bits & float_flag_overflow) {
+        target_bits |= UCF64_FPSCR_FLAG_OVERFLOW;
+    }
+    if (host_bits & float_flag_underflow) {
+        target_bits |= UCF64_FPSCR_FLAG_UNDERFLOW;
+    }
+    if (host_bits & float_flag_inexact) {
+        target_bits |= UCF64_FPSCR_FLAG_INEXACT;
+    }
+    return target_bits;
+}
+
+uint32_t HELPER(ucf64_get_fpscr)(CPUUniCore32State *env)
+{
+    int i;
+    uint32_t fpscr;
+
+    fpscr = (env->ucf64.xregs[UC32_UCF64_FPSCR] & UCF64_FPSCR_MASK);
+    i = get_float_exception_flags(&env->ucf64.fp_status);
+    fpscr |= ucf64_exceptbits_from_host(i);
+    return fpscr;
+}
+
+/* Convert ucf64 exception flags to target form.  */
+static inline int ucf64_exceptbits_to_host(int target_bits)
+{
+    int host_bits = 0;
+
+    if (target_bits & UCF64_FPSCR_FLAG_INVALID) {
+        host_bits |= float_flag_invalid;
+    }
+    if (target_bits & UCF64_FPSCR_FLAG_DIVZERO) {
+        host_bits |= float_flag_divbyzero;
+    }
+    if (target_bits & UCF64_FPSCR_FLAG_OVERFLOW) {
+        host_bits |= float_flag_overflow;
+    }
+    if (target_bits & UCF64_FPSCR_FLAG_UNDERFLOW) {
+        host_bits |= float_flag_underflow;
+    }
+    if (target_bits & UCF64_FPSCR_FLAG_INEXACT) {
+        host_bits |= float_flag_inexact;
+    }
+    return host_bits;
+}
+
+void HELPER(ucf64_set_fpscr)(CPUUniCore32State *env, uint32_t val)
+{
+    int i;
+    uint32_t changed;
+
+    changed = env->ucf64.xregs[UC32_UCF64_FPSCR];
+    env->ucf64.xregs[UC32_UCF64_FPSCR] = (val & UCF64_FPSCR_MASK);
+
+    changed ^= val;
+    if (changed & (UCF64_FPSCR_RND_MASK)) {
+        i = UCF64_FPSCR_RND(val);
+        switch (i) {
+        case 0:
+            i = float_round_nearest_even;
+            break;
+        case 1:
+            i = float_round_to_zero;
+            break;
+        case 2:
+            i = float_round_up;
+            break;
+        case 3:
+            i = float_round_down;
+            break;
+        default: /* 100 and 101 not implement */
+            cpu_abort(env, "Unsupported UniCore-F64 round mode");
+        }
+        set_float_rounding_mode(i, &env->ucf64.fp_status);
+    }
+
+    i = ucf64_exceptbits_to_host(UCF64_FPSCR_TRAPEN(val));
+    set_float_exception_flags(i, &env->ucf64.fp_status);
+}
+
+float32 HELPER(ucf64_adds)(float32 a, float32 b, CPUUniCore32State *env)
+{
+    return float32_add(a, b, &env->ucf64.fp_status);
+}
+
+float64 HELPER(ucf64_addd)(float64 a, float64 b, CPUUniCore32State *env)
+{
+    return float64_add(a, b, &env->ucf64.fp_status);
+}
+
+float32 HELPER(ucf64_subs)(float32 a, float32 b, CPUUniCore32State *env)
+{
+    return float32_sub(a, b, &env->ucf64.fp_status);
+}
+
+float64 HELPER(ucf64_subd)(float64 a, float64 b, CPUUniCore32State *env)
+{
+    return float64_sub(a, b, &env->ucf64.fp_status);
+}
+
+float32 HELPER(ucf64_muls)(float32 a, float32 b, CPUUniCore32State *env)
+{
+    return float32_mul(a, b, &env->ucf64.fp_status);
+}
+
+float64 HELPER(ucf64_muld)(float64 a, float64 b, CPUUniCore32State *env)
+{
+    return float64_mul(a, b, &env->ucf64.fp_status);
+}
+
+float32 HELPER(ucf64_divs)(float32 a, float32 b, CPUUniCore32State *env)
+{
+    return float32_div(a, b, &env->ucf64.fp_status);
+}
+
+float64 HELPER(ucf64_divd)(float64 a, float64 b, CPUUniCore32State *env)
+{
+    return float64_div(a, b, &env->ucf64.fp_status);
+}
+
+float32 HELPER(ucf64_negs)(float32 a)
+{
+    return float32_chs(a);
+}
+
+float64 HELPER(ucf64_negd)(float64 a)
+{
+    return float64_chs(a);
+}
+
+float32 HELPER(ucf64_abss)(float32 a)
+{
+    return float32_abs(a);
+}
+
+float64 HELPER(ucf64_absd)(float64 a)
+{
+    return float64_abs(a);
+}
+
+void HELPER(ucf64_cmps)(float32 a, float32 b, uint32_t c,
+        CPUUniCore32State *env)
+{
+    int flag;
+    flag = float32_compare_quiet(a, b, &env->ucf64.fp_status);
+    env->CF = 0;
+    switch (c & 0x7) {
+    case 0: /* F */
+        break;
+    case 1: /* UN */
+        if (flag == 2) {
+            env->CF = 1;
+        }
+        break;
+    case 2: /* EQ */
+        if (flag == 0) {
+            env->CF = 1;
+        }
+        break;
+    case 3: /* UEQ */
+        if ((flag == 0) || (flag == 2)) {
+            env->CF = 1;
+        }
+        break;
+    case 4: /* OLT */
+        if (flag == -1) {
+            env->CF = 1;
+        }
+        break;
+    case 5: /* ULT */
+        if ((flag == -1) || (flag == 2)) {
+            env->CF = 1;
+        }
+        break;
+    case 6: /* OLE */
+        if ((flag == -1) || (flag == 0)) {
+            env->CF = 1;
+        }
+        break;
+    case 7: /* ULE */
+        if (flag != 1) {
+            env->CF = 1;
+        }
+        break;
+    }
+    env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29)
+                    | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff);
+}
+
+void HELPER(ucf64_cmpd)(float64 a, float64 b, uint32_t c,
+        CPUUniCore32State *env)
+{
+    int flag;
+    flag = float64_compare_quiet(a, b, &env->ucf64.fp_status);
+    env->CF = 0;
+    switch (c & 0x7) {
+    case 0: /* F */
+        break;
+    case 1: /* UN */
+        if (flag == 2) {
+            env->CF = 1;
+        }
+        break;
+    case 2: /* EQ */
+        if (flag == 0) {
+            env->CF = 1;
+        }
+        break;
+    case 3: /* UEQ */
+        if ((flag == 0) || (flag == 2)) {
+            env->CF = 1;
+        }
+        break;
+    case 4: /* OLT */
+        if (flag == -1) {
+            env->CF = 1;
+        }
+        break;
+    case 5: /* ULT */
+        if ((flag == -1) || (flag == 2)) {
+            env->CF = 1;
+        }
+        break;
+    case 6: /* OLE */
+        if ((flag == -1) || (flag == 0)) {
+            env->CF = 1;
+        }
+        break;
+    case 7: /* ULE */
+        if (flag != 1) {
+            env->CF = 1;
+        }
+        break;
+    }
+    env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29)
+                    | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff);
+}
+
+/* Helper routines to perform bitwise copies between float and int.  */
+static inline float32 ucf64_itos(uint32_t i)
+{
+    union {
+        uint32_t i;
+        float32 s;
+    } v;
+
+    v.i = i;
+    return v.s;
+}
+
+static inline uint32_t ucf64_stoi(float32 s)
+{
+    union {
+        uint32_t i;
+        float32 s;
+    } v;
+
+    v.s = s;
+    return v.i;
+}
+
+static inline float64 ucf64_itod(uint64_t i)
+{
+    union {
+        uint64_t i;
+        float64 d;
+    } v;
+
+    v.i = i;
+    return v.d;
+}
+
+static inline uint64_t ucf64_dtoi(float64 d)
+{
+    union {
+        uint64_t i;
+        float64 d;
+    } v;
+
+    v.d = d;
+    return v.i;
+}
+
+/* Integer to float conversion.  */
+float32 HELPER(ucf64_si2sf)(float32 x, CPUUniCore32State *env)
+{
+    return int32_to_float32(ucf64_stoi(x), &env->ucf64.fp_status);
+}
+
+float64 HELPER(ucf64_si2df)(float32 x, CPUUniCore32State *env)
+{
+    return int32_to_float64(ucf64_stoi(x), &env->ucf64.fp_status);
+}
+
+/* Float to integer conversion.  */
+float32 HELPER(ucf64_sf2si)(float32 x, CPUUniCore32State *env)
+{
+    return ucf64_itos(float32_to_int32(x, &env->ucf64.fp_status));
+}
+
+float32 HELPER(ucf64_df2si)(float64 x, CPUUniCore32State *env)
+{
+    return ucf64_itos(float64_to_int32(x, &env->ucf64.fp_status));
+}
+
+/* floating point conversion */
+float64 HELPER(ucf64_sf2df)(float32 x, CPUUniCore32State *env)
+{
+    return float32_to_float64(x, &env->ucf64.fp_status);
+}
+
+float32 HELPER(ucf64_df2sf)(float64 x, CPUUniCore32State *env)
+{
+    return float64_to_float32(x, &env->ucf64.fp_status);
+}
diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039
index a749fcf23b..c5ae806ecb 100755
--- a/tests/qemu-iotests/039
+++ b/tests/qemu-iotests/039
@@ -44,6 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
 _supported_fmt qcow2
 _supported_proto generic
 _supported_os Linux
+_unsupported_qemu_io_options --nocache
 
 size=128M
 
diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out
index 155a05e109..cb510d6716 100644
--- a/tests/qemu-iotests/039.out
+++ b/tests/qemu-iotests/039.out
@@ -26,6 +26,12 @@ incompatible_features     0x1
 == Repairing the image file must succeed ==
 ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0
 Repairing cluster 5 refcount=0 reference=1
+The following inconsistencies were found and repaired:
+
+    0 leaked clusters
+    1 corruptions
+
+Double checking the fixed image now...
 No errors were found on the image.
 incompatible_features     0x0
 
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 7782808a26..d534e9466d 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -105,16 +105,16 @@ _make_test_img()
 
     # XXX(hch): have global image options?
     $QEMU_IMG create -f $IMGFMT $extra_img_options $TEST_IMG $image_size | \
-    	sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" | \
-    	sed -e "s#$TEST_DIR#TEST_DIR#g" | \
-    	sed -e "s#$IMGFMT#IMGFMT#g" | \
-	sed -e "s# encryption=off##g" | \
-	sed -e "s# cluster_size=[0-9]\\+##g" | \
-	sed -e "s# table_size=[0-9]\\+##g" | \
-	sed -e "s# compat='[^']*'##g" | \
-	sed -e "s# compat6=\\(on\\|off\\)##g" | \
-	sed -e "s# static=\\(on\\|off\\)##g" | \
-	sed -e "s# lazy_refcounts=\\(on\\|off\\)##g"
+        sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
+            -e "s#$TEST_DIR#TEST_DIR#g" \
+            -e "s#$IMGFMT#IMGFMT#g" \
+            -e "s# encryption=off##g" \
+            -e "s# cluster_size=[0-9]\\+##g" \
+            -e "s# table_size=[0-9]\\+##g" \
+            -e "s# compat='[^']*'##g" \
+            -e "s# compat6=\\(on\\|off\\)##g" \
+            -e "s# static=\\(on\\|off\\)##g" \
+            -e "s# lazy_refcounts=\\(on\\|off\\)##g"
 }
 
 _cleanup_test_img()
@@ -297,6 +297,20 @@ _supported_os()
     _notrun "not suitable for this OS: $HOSTOS"
 }
 
+_unsupported_qemu_io_options()
+{
+    for bad_opt
+    do
+        for opt in $QEMU_IO_OPTIONS
+        do
+            if [ "$bad_opt" = "$opt" ]
+            then
+                _notrun "not suitable for qemu-io option: $bad_opt"
+            fi
+        done
+    done
+}
+
 # this test requires that a specified command (executable) exists
 #
 _require_command()
diff --git a/vl.c b/vl.c
index 4871428864..91076f0e7c 100644
--- a/vl.c
+++ b/vl.c
@@ -293,6 +293,11 @@ static struct {
     { .driver = "qxl-vga",              .flag = &default_vga       },
 };
 
+const char *qemu_get_vm_name(void)
+{
+    return qemu_name;
+}
+
 static void res_free(void)
 {
     if (boot_splash_filedata != NULL) {