aboutsummaryrefslogtreecommitdiff
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/qcow2-cluster.c5
-rw-r--r--block/qcow2.c123
-rw-r--r--block/qcow2.h21
-rw-r--r--block_int.h26
-rwxr-xr-xconfigure39
-rw-r--r--cpu-exec.c6
-rw-r--r--default-configs/pci.mak1
-rw-r--r--default-configs/unicore32-softmmu.mak4
-rw-r--r--docs/specs/qcow2.txt14
-rw-r--r--exec-all.h2
-rw-r--r--exec.c2
-rw-r--r--hmp-commands.hx2
-rw-r--r--hw/Makefile.objs8
-rw-r--r--hw/esp-pci.c518
-rw-r--r--hw/esp.c487
-rw-r--r--hw/esp.h119
-rw-r--r--hw/ide/qdev.c3
-rw-r--r--hw/pci-stub.c15
-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-bus.c1
-rw-r--r--hw/scsi-disk.c3
-rw-r--r--hw/sun4m.c18
-rw-r--r--hw/unicore32/Makefile.objs6
-rw-r--r--hw/usb/dev-storage.c11
-rw-r--r--hw/xilinx_axienet.c1
-rw-r--r--hw/xtensa_lx60.c6
-rw-r--r--hw/xtensa_sim.c5
-rw-r--r--linux-user/main.c3
-rw-r--r--linux-user/signal.c8
-rw-r--r--net/slirp.c6
-rw-r--r--qapi-schema.json17
-rw-r--r--qemu-img.c28
-rw-r--r--qemu-io.c12
-rw-r--r--qemu-timer.c10
-rw-r--r--scripts/qapi.py16
-rw-r--r--target-i386/cpu.c10
-rw-r--r--target-i386/cpu.h1
-rw-r--r--target-i386/helper.c16
-rw-r--r--target-mips/translate.c1
-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
-rw-r--r--target-xtensa/cpu.h6
-rw-r--r--target-xtensa/helper.c8
-rw-r--r--tests/qemu-iotests/031.out20
-rw-r--r--tests/qemu-iotests/036.out4
-rwxr-xr-xtests/qemu-iotests/039136
-rw-r--r--tests/qemu-iotests/039.out53
-rw-r--r--tests/qemu-iotests/common.rc7
-rw-r--r--tests/qemu-iotests/group1
-rwxr-xr-xtests/qemu-iotests/qed.py235
-rw-r--r--user-exec.c17
-rw-r--r--vl.c8
70 files changed, 3334 insertions, 1032 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/qcow2-cluster.c b/block/qcow2-cluster.c
index d7e0e19d9c..e179211c57 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -662,7 +662,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
qcow2_cache_depends_on_flush(s->l2_table_cache);
}
- qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
+ if (qcow2_need_accurate_refcounts(s)) {
+ qcow2_cache_set_dependency(bs, s->l2_table_cache,
+ s->refcount_block_cache);
+ }
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index);
if (ret < 0) {
goto err;
diff --git a/block/qcow2.c b/block/qcow2.c
index 870148ddf8..fd5e214431 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -214,6 +214,62 @@ static void report_unsupported_feature(BlockDriverState *bs,
}
}
+/*
+ * Sets the dirty bit and flushes afterwards if necessary.
+ *
+ * The incompatible_features bit is only set if the image file header was
+ * updated successfully. Therefore it is not required to check the return
+ * value of this function.
+ */
+static int qcow2_mark_dirty(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t val;
+ int ret;
+
+ assert(s->qcow_version >= 3);
+
+ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
+ return 0; /* already dirty */
+ }
+
+ val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY);
+ ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features),
+ &val, sizeof(val));
+ if (ret < 0) {
+ return ret;
+ }
+ ret = bdrv_flush(bs->file);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Only treat image as dirty if the header was updated successfully */
+ s->incompatible_features |= QCOW2_INCOMPAT_DIRTY;
+ return 0;
+}
+
+/*
+ * Clears the dirty bit and flushes before if necessary. Only call this
+ * function when there are no pending requests, it does not guard against
+ * concurrent requests dirtying the image.
+ */
+static int qcow2_mark_clean(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+
+ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
+ int ret = bdrv_flush(bs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
+ return qcow2_update_header(bs);
+ }
+ return 0;
+}
+
static int qcow2_open(BlockDriverState *bs, int flags)
{
BDRVQcowState *s = bs->opaque;
@@ -287,12 +343,13 @@ static int qcow2_open(BlockDriverState *bs, int flags)
s->compatible_features = header.compatible_features;
s->autoclear_features = header.autoclear_features;
- if (s->incompatible_features != 0) {
+ if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
void *feature_table = NULL;
qcow2_read_extensions(bs, header.header_length, ext_end,
&feature_table);
report_unsupported_feature(bs, feature_table,
- s->incompatible_features);
+ s->incompatible_features &
+ ~QCOW2_INCOMPAT_MASK);
ret = -ENOTSUP;
goto fail;
}
@@ -412,6 +469,22 @@ static int qcow2_open(BlockDriverState *bs, int flags)
/* Initialise locks */
qemu_co_mutex_init(&s->lock);
+ /* Repair image if dirty */
+ if ((s->incompatible_features & QCOW2_INCOMPAT_DIRTY) &&
+ !bs->read_only) {
+ BdrvCheckResult result = {0};
+
+ ret = qcow2_check_refcounts(bs, &result, BDRV_FIX_ERRORS);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = qcow2_mark_clean(bs);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+
#ifdef DEBUG_ALLOC
{
BdrvCheckResult result = {0};
@@ -714,6 +787,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
goto fail;
}
+ if (l2meta.nb_clusters > 0 &&
+ (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)) {
+ qcow2_mark_dirty(bs);
+ }
+
cluster_offset = l2meta.cluster_offset;
assert((cluster_offset & 511) == 0);
@@ -785,6 +863,8 @@ static void qcow2_close(BlockDriverState *bs)
qcow2_cache_flush(bs, s->l2_table_cache);
qcow2_cache_flush(bs, s->refcount_block_cache);
+ qcow2_mark_clean(bs);
+
qcow2_cache_destroy(bs, s->l2_table_cache);
qcow2_cache_destroy(bs, s->refcount_block_cache);
@@ -949,7 +1029,16 @@ int qcow2_update_header(BlockDriverState *bs)
/* Feature table */
Qcow2Feature features[] = {
- /* no feature defined yet */
+ {
+ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
+ .bit = QCOW2_INCOMPAT_DIRTY_BITNR,
+ .name = "dirty bit",
+ },
+ {
+ .type = QCOW2_FEAT_TYPE_COMPATIBLE,
+ .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
+ .name = "lazy refcounts",
+ },
};
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
@@ -1132,6 +1221,11 @@ static int qcow2_create2(const char *filename, int64_t total_size,
header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
}
+ if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
+ header.compatible_features |=
+ cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
+ }
+
ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
if (ret < 0) {
goto out;
@@ -1245,6 +1339,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
options->value.s);
return -EINVAL;
}
+ } else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
+ flags |= options->value.n ? BLOCK_FLAG_LAZY_REFCOUNTS : 0;
}
options++;
}
@@ -1255,6 +1351,12 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
return -EINVAL;
}
+ if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
+ fprintf(stderr, "Lazy refcounts only supported with compatibility "
+ "level 1.1 and above (use compat=1.1 or greater)\n");
+ return -EINVAL;
+ }
+
return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
cluster_size, prealloc, options, version);
}
@@ -1441,10 +1543,12 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
return ret;
}
- ret = qcow2_cache_flush(bs, s->refcount_block_cache);
- if (ret < 0) {
- qemu_co_mutex_unlock(&s->lock);
- return ret;
+ if (qcow2_need_accurate_refcounts(s)) {
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ qemu_co_mutex_unlock(&s->lock);
+ return ret;
+ }
}
qemu_co_mutex_unlock(&s->lock);
@@ -1559,6 +1663,11 @@ static QEMUOptionParameter qcow2_create_options[] = {
.type = OPT_STRING,
.help = "Preallocation mode (allowed values: off, metadata)"
},
+ {
+ .name = BLOCK_OPT_LAZY_REFCOUNTS,
+ .type = OPT_FLAG,
+ .help = "Postpone refcount updates",
+ },
{ NULL }
};
diff --git a/block/qcow2.h b/block/qcow2.h
index 455b6d7cfe..b4eb65470e 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -110,6 +110,22 @@ enum {
QCOW2_FEAT_TYPE_AUTOCLEAR = 2,
};
+/* Incompatible feature bits */
+enum {
+ QCOW2_INCOMPAT_DIRTY_BITNR = 0,
+ QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
+
+ QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY,
+};
+
+/* Compatible feature bits */
+enum {
+ QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR = 0,
+ QCOW2_COMPAT_LAZY_REFCOUNTS = 1 << QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
+
+ QCOW2_COMPAT_FEAT_MASK = QCOW2_COMPAT_LAZY_REFCOUNTS,
+};
+
typedef struct Qcow2Feature {
uint8_t type;
uint8_t bit;
@@ -237,6 +253,11 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry)
}
}
+/* Check whether refcounts are eager or lazy */
+static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s)
+{
+ return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY);
+}
// FIXME Need qcow2_ prefix to global functions
diff --git a/block_int.h b/block_int.h
index d72317fbe3..6c1d9cafb1 100644
--- a/block_int.h
+++ b/block_int.h
@@ -31,8 +31,9 @@
#include "qemu-timer.h"
#include "qapi-types.h"
-#define BLOCK_FLAG_ENCRYPT 1
-#define BLOCK_FLAG_COMPAT6 4
+#define BLOCK_FLAG_ENCRYPT 1
+#define BLOCK_FLAG_COMPAT6 4
+#define BLOCK_FLAG_LAZY_REFCOUNTS 8
#define BLOCK_IO_LIMIT_READ 0
#define BLOCK_IO_LIMIT_WRITE 1
@@ -41,16 +42,17 @@
#define BLOCK_IO_SLICE_TIME 100000000
#define NANOSECONDS_PER_SECOND 1000000000.0
-#define BLOCK_OPT_SIZE "size"
-#define BLOCK_OPT_ENCRYPT "encryption"
-#define BLOCK_OPT_COMPAT6 "compat6"
-#define BLOCK_OPT_BACKING_FILE "backing_file"
-#define BLOCK_OPT_BACKING_FMT "backing_fmt"
-#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
-#define BLOCK_OPT_TABLE_SIZE "table_size"
-#define BLOCK_OPT_PREALLOC "preallocation"
-#define BLOCK_OPT_SUBFMT "subformat"
-#define BLOCK_OPT_COMPAT_LEVEL "compat"
+#define BLOCK_OPT_SIZE "size"
+#define BLOCK_OPT_ENCRYPT "encryption"
+#define BLOCK_OPT_COMPAT6 "compat6"
+#define BLOCK_OPT_BACKING_FILE "backing_file"
+#define BLOCK_OPT_BACKING_FMT "backing_fmt"
+#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
+#define BLOCK_OPT_TABLE_SIZE "table_size"
+#define BLOCK_OPT_PREALLOC "preallocation"
+#define BLOCK_OPT_SUBFMT "subformat"
+#define BLOCK_OPT_COMPAT_LEVEL "compat"
+#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts"
typedef struct BdrvTrackedRequest BdrvTrackedRequest;
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 543460c34c..134b3c4fcf 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -156,12 +156,9 @@ static inline TranslationBlock *tb_find_fast(CPUArchState *env)
static CPUDebugExcpHandler *debug_excp_handler;
-CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)
+void cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)
{
- CPUDebugExcpHandler *old_handler = debug_excp_handler;
-
debug_excp_handler = handler;
- return old_handler;
}
static void cpu_handle_debug_exception(CPUArchState *env)
@@ -447,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/pci.mak b/default-configs/pci.mak
index 9febb47ae6..69e18f1428 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -18,3 +18,4 @@ CONFIG_IDE_QDEV=y
CONFIG_IDE_PCI=y
CONFIG_AHCI=y
CONFIG_ESP=y
+CONFIG_ESP_PCI=y
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/docs/specs/qcow2.txt b/docs/specs/qcow2.txt
index 87bf785fe0..36a559d886 100644
--- a/docs/specs/qcow2.txt
+++ b/docs/specs/qcow2.txt
@@ -75,13 +75,23 @@ in the description of a field.
Bitmask of incompatible features. An implementation must
fail to open an image if an unknown bit is set.
- Bits 0-63: Reserved (set to 0)
+ Bit 0: Dirty bit. If this bit is set then refcounts
+ may be inconsistent, make sure to scan L1/L2
+ tables to repair refcounts before accessing the
+ image.
+
+ Bits 1-63: Reserved (set to 0)
80 - 87: compatible_features
Bitmask of compatible features. An implementation can
safely ignore any unknown bits that are set.
- Bits 0-63: Reserved (set to 0)
+ Bit 0: Lazy refcounts bit. If this bit is set then
+ lazy refcount updates can be used. This means
+ marking the image file dirty and postponing
+ refcount metadata updates.
+
+ Bits 1-63: Reserved (set to 0)
88 - 95: autoclear_features
Bitmask of auto-clear features. An implementation may only
diff --git a/exec-all.h b/exec-all.h
index 9bda7f7354..c5ec8e1158 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -357,7 +357,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr);
typedef void (CPUDebugExcpHandler)(CPUArchState *env);
-CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler);
+void cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler);
/* vl.c */
extern int singlestep;
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/hmp-commands.hx b/hmp-commands.hx
index eea8b32894..9bbc7f7555 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -101,7 +101,7 @@ ETEXI
.name = "block_job_cancel",
.args_type = "device:B",
.params = "device",
- .help = "stop an active block streaming operation",
+ .help = "stop an active background block operation",
.mhandler.cmd = hmp_block_job_cancel,
},
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 8327e55c03..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
@@ -88,6 +95,7 @@ hw-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
hw-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
hw-obj-$(CONFIG_ESP) += esp.o
+hw-obj-$(CONFIG_ESP_PCI) += esp-pci.o
hw-obj-y += sysbus.o isa-bus.o
hw-obj-y += qdev-addr.o
diff --git a/hw/esp-pci.c b/hw/esp-pci.c
new file mode 100644
index 0000000000..170e007be9
--- /dev/null
+++ b/hw/esp-pci.c
@@ -0,0 +1,518 @@
+/*
+ * QEMU ESP/NCR53C9x emulation
+ *
+ * Copyright (c) 2005-2006 Fabrice Bellard
+ * Copyright (c) 2012 Herve Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "pci.h"
+#include "eeprom93xx.h"
+#include "esp.h"
+#include "trace.h"
+#include "qemu-log.h"
+
+#define TYPE_AM53C974_DEVICE "am53c974"
+
+#define DMA_CMD 0x0
+#define DMA_STC 0x1
+#define DMA_SPA 0x2
+#define DMA_WBC 0x3
+#define DMA_WAC 0x4
+#define DMA_STAT 0x5
+#define DMA_SMDLA 0x6
+#define DMA_WMAC 0x7
+
+#define DMA_CMD_MASK 0x03
+#define DMA_CMD_DIAG 0x04
+#define DMA_CMD_MDL 0x10
+#define DMA_CMD_INTE_P 0x20
+#define DMA_CMD_INTE_D 0x40
+#define DMA_CMD_DIR 0x80
+
+#define DMA_STAT_PWDN 0x01
+#define DMA_STAT_ERROR 0x02
+#define DMA_STAT_ABORT 0x04
+#define DMA_STAT_DONE 0x08
+#define DMA_STAT_SCSIINT 0x10
+#define DMA_STAT_BCMBLT 0x20
+
+#define SBAC_STATUS 0x1000
+
+typedef struct PCIESPState {
+ PCIDevice dev;
+ MemoryRegion io;
+ uint32_t dma_regs[8];
+ uint32_t sbac;
+ ESPState esp;
+} PCIESPState;
+
+static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val)
+{
+ trace_esp_pci_dma_idle(val);
+ esp_dma_enable(&pci->esp, 0, 0);
+}
+
+static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val)
+{
+ trace_esp_pci_dma_blast(val);
+ qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n");
+}
+
+static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val)
+{
+ trace_esp_pci_dma_abort(val);
+ if (pci->esp.current_req) {
+ scsi_req_cancel(pci->esp.current_req);
+ }
+}
+
+static void esp_pci_handle_start(PCIESPState *pci, uint32_t val)
+{
+ trace_esp_pci_dma_start(val);
+
+ pci->dma_regs[DMA_WBC] = pci->dma_regs[DMA_STC];
+ pci->dma_regs[DMA_WAC] = pci->dma_regs[DMA_SPA];
+ pci->dma_regs[DMA_WMAC] = pci->dma_regs[DMA_SMDLA];
+
+ pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT
+ | DMA_STAT_DONE | DMA_STAT_ABORT
+ | DMA_STAT_ERROR | DMA_STAT_PWDN);
+
+ esp_dma_enable(&pci->esp, 0, 1);
+}
+
+static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val)
+{
+ trace_esp_pci_dma_write(saddr, pci->dma_regs[saddr], val);
+ switch (saddr) {
+ case DMA_CMD:
+ pci->dma_regs[saddr] = val;
+ switch (val & DMA_CMD_MASK) {
+ case 0x0: /* IDLE */
+ esp_pci_handle_idle(pci, val);
+ break;
+ case 0x1: /* BLAST */
+ esp_pci_handle_blast(pci, val);
+ break;
+ case 0x2: /* ABORT */
+ esp_pci_handle_abort(pci, val);
+ break;
+ case 0x3: /* START */
+ esp_pci_handle_start(pci, val);
+ break;
+ default: /* can't happen */
+ abort();
+ }
+ break;
+ case DMA_STC:
+ case DMA_SPA:
+ case DMA_SMDLA:
+ pci->dma_regs[saddr] = val;
+ break;
+ case DMA_STAT:
+ if (!(pci->sbac & SBAC_STATUS)) {
+ /* clear some bits on write */
+ uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE;
+ pci->dma_regs[DMA_STAT] &= ~(val & mask);
+ }
+ break;
+ default:
+ trace_esp_pci_error_invalid_write_dma(val, saddr);
+ return;
+ }
+}
+
+static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr)
+{
+ uint32_t val;
+
+ val = pci->dma_regs[saddr];
+ if (saddr == DMA_STAT) {
+ if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) {
+ val |= DMA_STAT_SCSIINT;
+ }
+ if (pci->sbac & SBAC_STATUS) {
+ pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT |
+ DMA_STAT_DONE);
+ }
+ }
+
+ trace_esp_pci_dma_read(saddr, val);
+ return val;
+}
+
+static void esp_pci_io_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned int size)
+{
+ PCIESPState *pci = opaque;
+
+ if (size < 4 || addr & 3) {
+ /* need to upgrade request: we only support 4-bytes accesses */
+ uint32_t current = 0, mask;
+ int shift;
+
+ if (addr < 0x40) {
+ current = pci->esp.wregs[addr >> 2];
+ } else if (addr < 0x60) {
+ current = pci->dma_regs[(addr - 0x40) >> 2];
+ } else if (addr < 0x74) {
+ current = pci->sbac;
+ }
+
+ shift = (4 - size) * 8;
+ mask = (~(uint32_t)0 << shift) >> shift;
+
+ shift = ((4 - (addr & 3)) & 3) * 8;
+ val <<= shift;
+ val |= current & ~(mask << shift);
+ addr &= ~3;
+ size = 4;
+ }
+
+ if (addr < 0x40) {
+ /* SCSI core reg */
+ esp_reg_write(&pci->esp, addr >> 2, val);
+ } else if (addr < 0x60) {
+ /* PCI DMA CCB */
+ esp_pci_dma_write(pci, (addr - 0x40) >> 2, val);
+ } else if (addr == 0x70) {
+ /* DMA SCSI Bus and control */
+ trace_esp_pci_sbac_write(pci->sbac, val);
+ pci->sbac = val;
+ } else {
+ trace_esp_pci_error_invalid_write((int)addr);
+ }
+}
+
+static uint64_t esp_pci_io_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ PCIESPState *pci = opaque;
+ uint32_t ret;
+
+ if (addr < 0x40) {
+ /* SCSI core reg */
+ ret = esp_reg_read(&pci->esp, addr >> 2);
+ } else if (addr < 0x60) {
+ /* PCI DMA CCB */
+ ret = esp_pci_dma_read(pci, (addr - 0x40) >> 2);
+ } else if (addr == 0x70) {
+ /* DMA SCSI Bus and control */
+ trace_esp_pci_sbac_read(pci->sbac);
+ ret = pci->sbac;
+ } else {
+ /* Invalid region */
+ trace_esp_pci_error_invalid_read((int)addr);
+ ret = 0;
+ }
+
+ /* give only requested data */
+ ret >>= (addr & 3) * 8;
+ ret &= ~(~(uint64_t)0 << (8 * size));
+
+ return ret;
+}
+
+static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
+ DMADirection dir)
+{
+ dma_addr_t addr;
+ DMADirection expected_dir;
+
+ if (pci->dma_regs[DMA_CMD] & DMA_CMD_DIR) {
+ expected_dir = DMA_DIRECTION_FROM_DEVICE;
+ } else {
+ expected_dir = DMA_DIRECTION_TO_DEVICE;
+ }
+
+ if (dir != expected_dir) {
+ trace_esp_pci_error_invalid_dma_direction();
+ return;
+ }
+
+ if (pci->dma_regs[DMA_STAT] & DMA_CMD_MDL) {
+ qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n");
+ }
+
+ addr = pci->dma_regs[DMA_SPA];
+ if (pci->dma_regs[DMA_WBC] < len) {
+ len = pci->dma_regs[DMA_WBC];
+ }
+
+ pci_dma_rw(&pci->dev, addr, buf, len, dir);
+
+ /* update status registers */
+ pci->dma_regs[DMA_WBC] -= len;
+ pci->dma_regs[DMA_WAC] += len;
+}
+
+static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len)
+{
+ PCIESPState *pci = opaque;
+ esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_TO_DEVICE);
+}
+
+static void esp_pci_dma_memory_write(void *opaque, uint8_t *buf, int len)
+{
+ PCIESPState *pci = opaque;
+ esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_FROM_DEVICE);
+}
+
+static const MemoryRegionOps esp_pci_io_ops = {
+ .read = esp_pci_io_read,
+ .write = esp_pci_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void esp_pci_hard_reset(DeviceState *dev)
+{
+ PCIESPState *pci = DO_UPCAST(PCIESPState, dev.qdev, dev);
+ esp_hard_reset(&pci->esp);
+ pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P
+ | DMA_CMD_MDL | DMA_CMD_DIAG | DMA_CMD_MASK);
+ pci->dma_regs[DMA_WBC] &= ~0xffff;
+ pci->dma_regs[DMA_WAC] = 0xffffffff;
+ pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT
+ | DMA_STAT_DONE | DMA_STAT_ABORT
+ | DMA_STAT_ERROR);
+ pci->dma_regs[DMA_WMAC] = 0xfffffffd;
+}
+
+static const VMStateDescription vmstate_esp_pci_scsi = {
+ .name = "pciespscsi",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, PCIESPState),
+ VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)),
+ VMSTATE_STRUCT(esp, PCIESPState, 0, vmstate_esp, ESPState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void esp_pci_command_complete(SCSIRequest *req, uint32_t status,
+ size_t resid)
+{
+ ESPState *s = req->hba_private;
+ PCIESPState *pci = container_of(s, PCIESPState, esp);
+
+ esp_command_complete(req, status, resid);
+ pci->dma_regs[DMA_WBC] = 0;
+ pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
+}
+
+static const struct SCSIBusInfo esp_pci_scsi_info = {
+ .tcq = false,
+ .max_target = ESP_MAX_DEVS,
+ .max_lun = 7,
+
+ .transfer_data = esp_transfer_data,
+ .complete = esp_pci_command_complete,
+ .cancel = esp_request_cancelled,
+};
+
+static int esp_pci_scsi_init(PCIDevice *dev)
+{
+ PCIESPState *pci = DO_UPCAST(PCIESPState, dev, dev);
+ ESPState *s = &pci->esp;
+ uint8_t *pci_conf;
+
+ pci_conf = pci->dev.config;
+
+ /* Interrupt pin A */
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01;
+
+ s->dma_memory_read = esp_pci_dma_memory_read;
+ s->dma_memory_write = esp_pci_dma_memory_write;
+ s->dma_opaque = pci;
+ s->chip_id = TCHI_AM53C974;
+ memory_region_init_io(&pci->io, &esp_pci_io_ops, pci, "esp-io", 0x80);
+
+ pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io);
+ s->irq = pci->dev.irq[0];
+
+ scsi_bus_new(&s->bus, &dev->qdev, &esp_pci_scsi_info);
+ if (!dev->qdev.hotplugged) {
+ return scsi_bus_legacy_handle_cmdline(&s->bus);
+ }
+ return 0;
+}
+
+static void esp_pci_scsi_uninit(PCIDevice *d)
+{
+ PCIESPState *pci = DO_UPCAST(PCIESPState, dev, d);
+
+ memory_region_destroy(&pci->io);
+}
+
+static void esp_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = esp_pci_scsi_init;
+ k->exit = esp_pci_scsi_uninit;
+ k->vendor_id = PCI_VENDOR_ID_AMD;
+ k->device_id = PCI_DEVICE_ID_AMD_SCSI;
+ k->revision = 0x10;
+ k->class_id = PCI_CLASS_STORAGE_SCSI;
+ dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter";
+ dc->reset = esp_pci_hard_reset;
+ dc->vmsd = &vmstate_esp_pci_scsi;
+}
+
+static const TypeInfo esp_pci_info = {
+ .name = TYPE_AM53C974_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIESPState),
+ .class_init = esp_pci_class_init,
+};
+
+typedef struct {
+ PCIESPState pci;
+ eeprom_t *eeprom;
+} DC390State;
+
+#define TYPE_DC390_DEVICE "dc390"
+#define DC390(obj) \
+ OBJECT_CHECK(DC390State, obj, TYPE_DC390_DEVICE)
+
+#define EE_ADAPT_SCSI_ID 64
+#define EE_MODE2 65
+#define EE_DELAY 66
+#define EE_TAG_CMD_NUM 67
+#define EE_ADAPT_OPTIONS 68
+#define EE_BOOT_SCSI_ID 69
+#define EE_BOOT_SCSI_LUN 70
+#define EE_CHKSUM1 126
+#define EE_CHKSUM2 127
+
+#define EE_ADAPT_OPTION_F6_F8_AT_BOOT 0x01
+#define EE_ADAPT_OPTION_BOOT_FROM_CDROM 0x02
+#define EE_ADAPT_OPTION_INT13 0x04
+#define EE_ADAPT_OPTION_SCAM_SUPPORT 0x08
+
+
+static uint32_t dc390_read_config(PCIDevice *dev, uint32_t addr, int l)
+{
+ DC390State *pci = DC390(dev);
+ uint32_t val;
+
+ val = pci_default_read_config(dev, addr, l);
+
+ if (addr == 0x00 && l == 1) {
+ /* First byte of address space is AND-ed with EEPROM DO line */
+ if (!eeprom93xx_read(pci->eeprom)) {
+ val &= ~0xff;
+ }
+ }
+
+ return val;
+}
+
+static void dc390_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int l)
+{
+ DC390State *pci = DC390(dev);
+ if (addr == 0x80) {
+ /* EEPROM write */
+ int eesk = val & 0x80 ? 1 : 0;
+ int eedi = val & 0x40 ? 1 : 0;
+ eeprom93xx_write(pci->eeprom, 1, eesk, eedi);
+ } else if (addr == 0xc0) {
+ /* EEPROM CS low */
+ eeprom93xx_write(pci->eeprom, 0, 0, 0);
+ } else {
+ pci_default_write_config(dev, addr, val, l);
+ }
+}
+
+static int dc390_scsi_init(PCIDevice *dev)
+{
+ DC390State *pci = DC390(dev);
+ uint8_t *contents;
+ uint16_t chksum = 0;
+ int i, ret;
+
+ /* init base class */
+ ret = esp_pci_scsi_init(dev);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* EEPROM */
+ pci->eeprom = eeprom93xx_new(DEVICE(dev), 64);
+
+ /* set default eeprom values */
+ contents = (uint8_t *)eeprom93xx_data(pci->eeprom);
+
+ for (i = 0; i < 16; i++) {
+ contents[i * 2] = 0x57;
+ contents[i * 2 + 1] = 0x00;
+ }
+ contents[EE_ADAPT_SCSI_ID] = 7;
+ contents[EE_MODE2] = 0x0f;
+ contents[EE_TAG_CMD_NUM] = 0x04;
+ contents[EE_ADAPT_OPTIONS] = EE_ADAPT_OPTION_F6_F8_AT_BOOT
+ | EE_ADAPT_OPTION_BOOT_FROM_CDROM
+ | EE_ADAPT_OPTION_INT13;
+
+ /* update eeprom checksum */
+ for (i = 0; i < EE_CHKSUM1; i += 2) {
+ chksum += contents[i] + (((uint16_t)contents[i + 1]) << 8);
+ }
+ chksum = 0x1234 - chksum;
+ contents[EE_CHKSUM1] = chksum & 0xff;
+ contents[EE_CHKSUM2] = chksum >> 8;
+
+ return 0;
+}
+
+static void dc390_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = dc390_scsi_init;
+ k->config_read = dc390_read_config;
+ k->config_write = dc390_write_config;
+ dc->desc = "Tekram DC-390 SCSI adapter";
+}
+
+static const TypeInfo dc390_info = {
+ .name = "dc390",
+ .parent = TYPE_AM53C974_DEVICE,
+ .instance_size = sizeof(DC390State),
+ .class_init = dc390_class_init,
+};
+
+static void esp_pci_register_types(void)
+{
+ type_register_static(&esp_pci_info);
+ type_register_static(&dc390_info);
+}
+
+type_init(esp_pci_register_types)
diff --git a/hw/esp.c b/hw/esp.c
index 77f57076c7..52c46e615f 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -24,8 +24,6 @@
*/
#include "sysbus.h"
-#include "pci.h"
-#include "scsi.h"
#include "esp.h"
#include "trace.h"
#include "qemu-log.h"
@@ -38,114 +36,6 @@
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
*/
-#define ESP_REGS 16
-#define TI_BUFSZ 16
-
-typedef struct ESPState ESPState;
-
-struct ESPState {
- uint8_t rregs[ESP_REGS];
- uint8_t wregs[ESP_REGS];
- qemu_irq irq;
- uint8_t chip_id;
- int32_t ti_size;
- uint32_t ti_rptr, ti_wptr;
- uint32_t status;
- uint32_t dma;
- uint8_t ti_buf[TI_BUFSZ];
- SCSIBus bus;
- SCSIDevice *current_dev;
- SCSIRequest *current_req;
- uint8_t cmdbuf[TI_BUFSZ];
- uint32_t cmdlen;
- uint32_t do_cmd;
-
- /* The amount of data left in the current DMA transfer. */
- uint32_t dma_left;
- /* The size of the current DMA transfer. Zero if no transfer is in
- progress. */
- uint32_t dma_counter;
- int dma_enabled;
-
- uint32_t async_len;
- uint8_t *async_buf;
-
- ESPDMAMemoryReadWriteFunc dma_memory_read;
- ESPDMAMemoryReadWriteFunc dma_memory_write;
- void *dma_opaque;
- void (*dma_cb)(ESPState *s);
-};
-
-#define ESP_TCLO 0x0
-#define ESP_TCMID 0x1
-#define ESP_FIFO 0x2
-#define ESP_CMD 0x3
-#define ESP_RSTAT 0x4
-#define ESP_WBUSID 0x4
-#define ESP_RINTR 0x5
-#define ESP_WSEL 0x5
-#define ESP_RSEQ 0x6
-#define ESP_WSYNTP 0x6
-#define ESP_RFLAGS 0x7
-#define ESP_WSYNO 0x7
-#define ESP_CFG1 0x8
-#define ESP_RRES1 0x9
-#define ESP_WCCF 0x9
-#define ESP_RRES2 0xa
-#define ESP_WTEST 0xa
-#define ESP_CFG2 0xb
-#define ESP_CFG3 0xc
-#define ESP_RES3 0xd
-#define ESP_TCHI 0xe
-#define ESP_RES4 0xf
-
-#define CMD_DMA 0x80
-#define CMD_CMD 0x7f
-
-#define CMD_NOP 0x00
-#define CMD_FLUSH 0x01
-#define CMD_RESET 0x02
-#define CMD_BUSRESET 0x03
-#define CMD_TI 0x10
-#define CMD_ICCS 0x11
-#define CMD_MSGACC 0x12
-#define CMD_PAD 0x18
-#define CMD_SATN 0x1a
-#define CMD_RSTATN 0x1b
-#define CMD_SEL 0x41
-#define CMD_SELATN 0x42
-#define CMD_SELATNS 0x43
-#define CMD_ENSEL 0x44
-#define CMD_DISSEL 0x45
-
-#define STAT_DO 0x00
-#define STAT_DI 0x01
-#define STAT_CD 0x02
-#define STAT_ST 0x03
-#define STAT_MO 0x06
-#define STAT_MI 0x07
-#define STAT_PIO_MASK 0x06
-
-#define STAT_TC 0x10
-#define STAT_PE 0x20
-#define STAT_GE 0x40
-#define STAT_INT 0x80
-
-#define BUSID_DID 0x07
-
-#define INTR_FC 0x08
-#define INTR_BS 0x10
-#define INTR_DC 0x20
-#define INTR_RST 0x80
-
-#define SEQ_0 0x0
-#define SEQ_CD 0x4
-
-#define CFG1_RESREPT 0x40
-
-#define TCHI_FAS100A 0x4
-#define TCHI_AM53C974 0x12
-
static void esp_raise_irq(ESPState *s)
{
if (!(s->rregs[ESP_RSTAT] & STAT_INT)) {
@@ -164,7 +54,7 @@ static void esp_lower_irq(ESPState *s)
}
}
-static void esp_dma_enable(ESPState *s, int irq, int level)
+void esp_dma_enable(ESPState *s, int irq, int level)
{
if (level) {
s->dma_enabled = 1;
@@ -179,7 +69,7 @@ static void esp_dma_enable(ESPState *s, int irq, int level)
}
}
-static void esp_request_cancelled(SCSIRequest *req)
+void esp_request_cancelled(SCSIRequest *req)
{
ESPState *s = req->hba_private;
@@ -388,7 +278,7 @@ static void esp_do_dma(ESPState *s)
esp_dma_done(s);
}
-static void esp_command_complete(SCSIRequest *req, uint32_t status,
+void esp_command_complete(SCSIRequest *req, uint32_t status,
size_t resid)
{
ESPState *s = req->hba_private;
@@ -413,7 +303,7 @@ static void esp_command_complete(SCSIRequest *req, uint32_t status,
}
}
-static void esp_transfer_data(SCSIRequest *req, uint32_t len)
+void esp_transfer_data(SCSIRequest *req, uint32_t len)
{
ESPState *s = req->hba_private;
@@ -465,7 +355,7 @@ static void handle_ti(ESPState *s)
}
}
-static void esp_hard_reset(ESPState *s)
+void esp_hard_reset(ESPState *s)
{
memset(s->rregs, 0, ESP_REGS);
memset(s->wregs, 0, ESP_REGS);
@@ -493,7 +383,7 @@ static void parent_esp_reset(ESPState *s, int irq, int level)
}
}
-static uint64_t esp_reg_read(ESPState *s, uint32_t saddr)
+uint64_t esp_reg_read(ESPState *s, uint32_t saddr)
{
uint32_t old_val;
@@ -533,7 +423,7 @@ static uint64_t esp_reg_read(ESPState *s, uint32_t saddr)
return s->rregs[saddr];
}
-static void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
+void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
{
trace_esp_mem_writeb(saddr, s->wregs[saddr], val);
switch (saddr) {
@@ -660,7 +550,7 @@ static bool esp_mem_accepts(void *opaque, target_phys_addr_t addr,
return (size == 1) || (is_write && size == 4);
}
-static const VMStateDescription vmstate_esp = {
+const VMStateDescription vmstate_esp = {
.name ="esp",
.version_id = 3,
.minimum_version_id = 3,
@@ -823,370 +713,9 @@ static const TypeInfo sysbus_esp_info = {
.class_init = sysbus_esp_class_init,
};
-#define DMA_CMD 0x0
-#define DMA_STC 0x1
-#define DMA_SPA 0x2
-#define DMA_WBC 0x3
-#define DMA_WAC 0x4
-#define DMA_STAT 0x5
-#define DMA_SMDLA 0x6
-#define DMA_WMAC 0x7
-
-#define DMA_CMD_MASK 0x03
-#define DMA_CMD_DIAG 0x04
-#define DMA_CMD_MDL 0x10
-#define DMA_CMD_INTE_P 0x20
-#define DMA_CMD_INTE_D 0x40
-#define DMA_CMD_DIR 0x80
-
-#define DMA_STAT_PWDN 0x01
-#define DMA_STAT_ERROR 0x02
-#define DMA_STAT_ABORT 0x04
-#define DMA_STAT_DONE 0x08
-#define DMA_STAT_SCSIINT 0x10
-#define DMA_STAT_BCMBLT 0x20
-
-#define SBAC_STATUS 0x1000
-
-typedef struct PCIESPState {
- PCIDevice dev;
- MemoryRegion io;
- uint32_t dma_regs[8];
- uint32_t sbac;
- ESPState esp;
-} PCIESPState;
-
-static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val)
-{
- trace_esp_pci_dma_idle(val);
- esp_dma_enable(&pci->esp, 0, 0);
-}
-
-static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val)
-{
- trace_esp_pci_dma_blast(val);
- qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n");
-}
-
-static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val)
-{
- trace_esp_pci_dma_abort(val);
- if (pci->esp.current_req) {
- scsi_req_cancel(pci->esp.current_req);
- }
-}
-
-static void esp_pci_handle_start(PCIESPState *pci, uint32_t val)
-{
- trace_esp_pci_dma_start(val);
-
- pci->dma_regs[DMA_WBC] = pci->dma_regs[DMA_STC];
- pci->dma_regs[DMA_WAC] = pci->dma_regs[DMA_SPA];
- pci->dma_regs[DMA_WMAC] = pci->dma_regs[DMA_SMDLA];
-
- pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT
- | DMA_STAT_DONE | DMA_STAT_ABORT
- | DMA_STAT_ERROR | DMA_STAT_PWDN);
-
- esp_dma_enable(&pci->esp, 0, 1);
-}
-
-static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val)
-{
- trace_esp_pci_dma_write(saddr, pci->dma_regs[saddr], val);
- switch (saddr) {
- case DMA_CMD:
- pci->dma_regs[saddr] = val;
- switch (val & DMA_CMD_MASK) {
- case 0x0: /* IDLE */
- esp_pci_handle_idle(pci, val);
- break;
- case 0x1: /* BLAST */
- esp_pci_handle_blast(pci, val);
- break;
- case 0x2: /* ABORT */
- esp_pci_handle_abort(pci, val);
- break;
- case 0x3: /* START */
- esp_pci_handle_start(pci, val);
- break;
- default: /* can't happen */
- abort();
- }
- break;
- case DMA_STC:
- case DMA_SPA:
- case DMA_SMDLA:
- pci->dma_regs[saddr] = val;
- break;
- case DMA_STAT:
- if (!(pci->sbac & SBAC_STATUS)) {
- /* clear some bits on write */
- uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE;
- pci->dma_regs[DMA_STAT] &= ~(val & mask);
- }
- break;
- default:
- trace_esp_pci_error_invalid_write_dma(val, saddr);
- return;
- }
-}
-
-static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr)
-{
- uint32_t val;
-
- val = pci->dma_regs[saddr];
- if (saddr == DMA_STAT) {
- if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) {
- val |= DMA_STAT_SCSIINT;
- }
- if (pci->sbac & SBAC_STATUS) {
- pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT |
- DMA_STAT_DONE);
- }
- }
-
- trace_esp_pci_dma_read(saddr, val);
- return val;
-}
-
-static void esp_pci_io_write(void *opaque, target_phys_addr_t addr,
- uint64_t val, unsigned int size)
-{
- PCIESPState *pci = opaque;
-
- if (size < 4 || addr & 3) {
- /* need to upgrade request: we only support 4-bytes accesses */
- uint32_t current = 0, mask;
- int shift;
-
- if (addr < 0x40) {
- current = pci->esp.wregs[addr >> 2];
- } else if (addr < 0x60) {
- current = pci->dma_regs[(addr - 0x40) >> 2];
- } else if (addr < 0x74) {
- current = pci->sbac;
- }
-
- shift = (4 - size) * 8;
- mask = (~(uint32_t)0 << shift) >> shift;
-
- shift = ((4 - (addr & 3)) & 3) * 8;
- val <<= shift;
- val |= current & ~(mask << shift);
- addr &= ~3;
- size = 4;
- }
-
- if (addr < 0x40) {
- /* SCSI core reg */
- esp_reg_write(&pci->esp, addr >> 2, val);
- } else if (addr < 0x60) {
- /* PCI DMA CCB */
- esp_pci_dma_write(pci, (addr - 0x40) >> 2, val);
- } else if (addr == 0x70) {
- /* DMA SCSI Bus and control */
- trace_esp_pci_sbac_write(pci->sbac, val);
- pci->sbac = val;
- } else {
- trace_esp_pci_error_invalid_write((int)addr);
- }
-}
-
-static uint64_t esp_pci_io_read(void *opaque, target_phys_addr_t addr,
- unsigned int size)
-{
- PCIESPState *pci = opaque;
- uint32_t ret;
-
- if (addr < 0x40) {
- /* SCSI core reg */
- ret = esp_reg_read(&pci->esp, addr >> 2);
- } else if (addr < 0x60) {
- /* PCI DMA CCB */
- ret = esp_pci_dma_read(pci, (addr - 0x40) >> 2);
- } else if (addr == 0x70) {
- /* DMA SCSI Bus and control */
- trace_esp_pci_sbac_read(pci->sbac);
- ret = pci->sbac;
- } else {
- /* Invalid region */
- trace_esp_pci_error_invalid_read((int)addr);
- ret = 0;
- }
-
- /* give only requested data */
- ret >>= (addr & 3) * 8;
- ret &= ~(~(uint64_t)0 << (8 * size));
-
- return ret;
-}
-
-static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
- DMADirection dir)
-{
- dma_addr_t addr;
- DMADirection expected_dir;
-
- if (pci->dma_regs[DMA_CMD] & DMA_CMD_DIR) {
- expected_dir = DMA_DIRECTION_FROM_DEVICE;
- } else {
- expected_dir = DMA_DIRECTION_TO_DEVICE;
- }
-
- if (dir != expected_dir) {
- trace_esp_pci_error_invalid_dma_direction();
- return;
- }
-
- if (pci->dma_regs[DMA_STAT] & DMA_CMD_MDL) {
- qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n");
- }
-
- addr = pci->dma_regs[DMA_SPA];
- if (pci->dma_regs[DMA_WBC] < len) {
- len = pci->dma_regs[DMA_WBC];
- }
-
- pci_dma_rw(&pci->dev, addr, buf, len, dir);
-
- /* update status registers */
- pci->dma_regs[DMA_WBC] -= len;
- pci->dma_regs[DMA_WAC] += len;
-}
-
-static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len)
-{
- PCIESPState *pci = opaque;
- esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_TO_DEVICE);
-}
-
-static void esp_pci_dma_memory_write(void *opaque, uint8_t *buf, int len)
-{
- PCIESPState *pci = opaque;
- esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_FROM_DEVICE);
-}
-
-static const MemoryRegionOps esp_pci_io_ops = {
- .read = esp_pci_io_read,
- .write = esp_pci_io_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .impl = {
- .min_access_size = 1,
- .max_access_size = 4,
- },
-};
-
-static void esp_pci_hard_reset(DeviceState *dev)
-{
- PCIESPState *pci = DO_UPCAST(PCIESPState, dev.qdev, dev);
- esp_hard_reset(&pci->esp);
- pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P
- | DMA_CMD_MDL | DMA_CMD_DIAG | DMA_CMD_MASK);
- pci->dma_regs[DMA_WBC] &= ~0xffff;
- pci->dma_regs[DMA_WAC] = 0xffffffff;
- pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT
- | DMA_STAT_DONE | DMA_STAT_ABORT
- | DMA_STAT_ERROR);
- pci->dma_regs[DMA_WMAC] = 0xfffffffd;
-}
-
-static const VMStateDescription vmstate_esp_pci_scsi = {
- .name = "pciespscsi",
- .version_id = 0,
- .minimum_version_id = 0,
- .minimum_version_id_old = 0,
- .fields = (VMStateField[]) {
- VMSTATE_PCI_DEVICE(dev, PCIESPState),
- VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)),
- VMSTATE_STRUCT(esp, PCIESPState, 0, vmstate_esp, ESPState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static void esp_pci_command_complete(SCSIRequest *req, uint32_t status,
- size_t resid)
-{
- ESPState *s = req->hba_private;
- PCIESPState *pci = container_of(s, PCIESPState, esp);
-
- esp_command_complete(req, status, resid);
- pci->dma_regs[DMA_WBC] = 0;
- pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
-}
-
-static const struct SCSIBusInfo esp_pci_scsi_info = {
- .tcq = false,
- .max_target = ESP_MAX_DEVS,
- .max_lun = 7,
-
- .transfer_data = esp_transfer_data,
- .complete = esp_pci_command_complete,
- .cancel = esp_request_cancelled,
-};
-
-static int esp_pci_scsi_init(PCIDevice *dev)
-{
- PCIESPState *pci = DO_UPCAST(PCIESPState, dev, dev);
- ESPState *s = &pci->esp;
- uint8_t *pci_conf;
-
- pci_conf = pci->dev.config;
-
- /* Interrupt pin A */
- pci_conf[PCI_INTERRUPT_PIN] = 0x01;
-
- s->dma_memory_read = esp_pci_dma_memory_read;
- s->dma_memory_write = esp_pci_dma_memory_write;
- s->dma_opaque = pci;
- s->chip_id = TCHI_AM53C974;
- memory_region_init_io(&pci->io, &esp_pci_io_ops, pci, "esp-io", 0x80);
-
- pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io);
- s->irq = pci->dev.irq[0];
-
- scsi_bus_new(&s->bus, &dev->qdev, &esp_pci_scsi_info);
- if (!dev->qdev.hotplugged) {
- return scsi_bus_legacy_handle_cmdline(&s->bus);
- }
- return 0;
-}
-
-static void esp_pci_scsi_uninit(PCIDevice *d)
-{
- PCIESPState *pci = DO_UPCAST(PCIESPState, dev, d);
-
- memory_region_destroy(&pci->io);
-}
-
-static void esp_pci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
- k->init = esp_pci_scsi_init;
- k->exit = esp_pci_scsi_uninit;
- k->vendor_id = PCI_VENDOR_ID_AMD;
- k->device_id = PCI_DEVICE_ID_AMD_SCSI;
- k->revision = 0x10;
- k->class_id = PCI_CLASS_STORAGE_SCSI;
- dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter";
- dc->reset = esp_pci_hard_reset;
- dc->vmsd = &vmstate_esp_pci_scsi;
-}
-
-static const TypeInfo esp_pci_info = {
- .name = "am53c974",
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(PCIESPState),
- .class_init = esp_pci_class_init,
-};
-
static void esp_register_types(void)
{
type_register_static(&sysbus_esp_info);
- type_register_static(&esp_pci_info);
}
type_init(esp_register_types)
diff --git a/hw/esp.h b/hw/esp.h
index 62bfd4d129..fa855e2fdf 100644
--- a/hw/esp.h
+++ b/hw/esp.h
@@ -1,6 +1,8 @@
#ifndef QEMU_HW_ESP_H
#define QEMU_HW_ESP_H
+#include "scsi.h"
+
/* esp.c */
#define ESP_MAX_DEVS 7
typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len);
@@ -10,4 +12,121 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
void *dma_opaque, qemu_irq irq, qemu_irq *reset,
qemu_irq *dma_enable);
+#define ESP_REGS 16
+#define TI_BUFSZ 16
+
+typedef struct ESPState ESPState;
+
+struct ESPState {
+ uint8_t rregs[ESP_REGS];
+ uint8_t wregs[ESP_REGS];
+ qemu_irq irq;
+ uint8_t chip_id;
+ int32_t ti_size;
+ uint32_t ti_rptr, ti_wptr;
+ uint32_t status;
+ uint32_t dma;
+ uint8_t ti_buf[TI_BUFSZ];
+ SCSIBus bus;
+ SCSIDevice *current_dev;
+ SCSIRequest *current_req;
+ uint8_t cmdbuf[TI_BUFSZ];
+ uint32_t cmdlen;
+ uint32_t do_cmd;
+
+ /* The amount of data left in the current DMA transfer. */
+ uint32_t dma_left;
+ /* The size of the current DMA transfer. Zero if no transfer is in
+ progress. */
+ uint32_t dma_counter;
+ int dma_enabled;
+
+ uint32_t async_len;
+ uint8_t *async_buf;
+
+ ESPDMAMemoryReadWriteFunc dma_memory_read;
+ ESPDMAMemoryReadWriteFunc dma_memory_write;
+ void *dma_opaque;
+ void (*dma_cb)(ESPState *s);
+};
+
+#define ESP_TCLO 0x0
+#define ESP_TCMID 0x1
+#define ESP_FIFO 0x2
+#define ESP_CMD 0x3
+#define ESP_RSTAT 0x4
+#define ESP_WBUSID 0x4
+#define ESP_RINTR 0x5
+#define ESP_WSEL 0x5
+#define ESP_RSEQ 0x6
+#define ESP_WSYNTP 0x6
+#define ESP_RFLAGS 0x7
+#define ESP_WSYNO 0x7
+#define ESP_CFG1 0x8
+#define ESP_RRES1 0x9
+#define ESP_WCCF 0x9
+#define ESP_RRES2 0xa
+#define ESP_WTEST 0xa
+#define ESP_CFG2 0xb
+#define ESP_CFG3 0xc
+#define ESP_RES3 0xd
+#define ESP_TCHI 0xe
+#define ESP_RES4 0xf
+
+#define CMD_DMA 0x80
+#define CMD_CMD 0x7f
+
+#define CMD_NOP 0x00
+#define CMD_FLUSH 0x01
+#define CMD_RESET 0x02
+#define CMD_BUSRESET 0x03
+#define CMD_TI 0x10
+#define CMD_ICCS 0x11
+#define CMD_MSGACC 0x12
+#define CMD_PAD 0x18
+#define CMD_SATN 0x1a
+#define CMD_RSTATN 0x1b
+#define CMD_SEL 0x41
+#define CMD_SELATN 0x42
+#define CMD_SELATNS 0x43
+#define CMD_ENSEL 0x44
+#define CMD_DISSEL 0x45
+
+#define STAT_DO 0x00
+#define STAT_DI 0x01
+#define STAT_CD 0x02
+#define STAT_ST 0x03
+#define STAT_MO 0x06
+#define STAT_MI 0x07
+#define STAT_PIO_MASK 0x06
+
+#define STAT_TC 0x10
+#define STAT_PE 0x20
+#define STAT_GE 0x40
+#define STAT_INT 0x80
+
+#define BUSID_DID 0x07
+
+#define INTR_FC 0x08
+#define INTR_BS 0x10
+#define INTR_DC 0x20
+#define INTR_RST 0x80
+
+#define SEQ_0 0x0
+#define SEQ_CD 0x4
+
+#define CFG1_RESREPT 0x40
+
+#define TCHI_FAS100A 0x4
+#define TCHI_AM53C974 0x12
+
+void esp_dma_enable(ESPState *s, int irq, int level);
+void esp_request_cancelled(SCSIRequest *req);
+void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid);
+void esp_transfer_data(SCSIRequest *req, uint32_t len);
+void esp_hard_reset(ESPState *s);
+uint64_t esp_reg_read(ESPState *s, uint32_t saddr);
+void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val);
+extern const VMStateDescription vmstate_esp;
+
#endif
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 22e58dfc8a..5ea9b8f4b2 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -149,7 +149,8 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
}
blkconf_serial(&dev->conf, &dev->serial);
- if (blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) {
+ if (kind != IDE_CD
+ && blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) {
return -1;
}
diff --git a/hw/pci-stub.c b/hw/pci-stub.c
index e083191529..134c4484b6 100644
--- a/hw/pci-stub.c
+++ b/hw/pci-stub.c
@@ -34,21 +34,6 @@ static void pci_error_message(Monitor *mon)
monitor_printf(mon, "PCI devices not supported\n");
}
-void pci_register_bar(PCIDevice *pci_dev, int region_num,
- uint8_t type, MemoryRegion *memory)
-{
-}
-
-const VMStateDescription vmstate_pci_device = {
- .name = "PCIDeviceStub",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_END_OF_LIST()
- }
-};
-
int do_pcie_aer_inject_error(Monitor *mon,
const QDict *qdict, QObject **ret_data)
{
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-bus.c b/hw/scsi-bus.c
index 6120cc83c1..b8a857d145 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -1437,7 +1437,6 @@ static const char *scsi_command_name(uint8_t cmd)
[ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12",
[ MOVE_MEDIUM ] = "MOVE_MEDIUM",
[ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM",
- [ LOAD_UNLOAD ] = "LOAD_UNLOAD",
[ READ_12 ] = "READ_12",
[ WRITE_12 ] = "WRITE_12",
[ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE",
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index e71809e091..409f760ef7 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -2049,7 +2049,8 @@ static int scsi_initfn(SCSIDevice *dev)
}
blkconf_serial(&s->qdev.conf, &s->serial);
- if (blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) {
+ if (dev->type == TYPE_DISK
+ && blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) {
return -1;
}
diff --git a/hw/sun4m.c b/hw/sun4m.c
index a959261209..0f909b5f86 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -832,6 +832,10 @@ static void cpu_devinit(const char *cpu_model, unsigned int id,
env->prom_addr = prom_addr;
}
+static void dummy_fdc_tc(void *opaque, int irq, int level)
+{
+}
+
static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
const char *boot_device,
const char *kernel_filename,
@@ -942,9 +946,6 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
serial_hds[0], serial_hds[1], ESCC_CLOCK, 1);
cpu_halt = qemu_allocate_irqs(cpu_halt_signal, NULL, 1);
- slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base,
- slavio_irq[30], fdc_tc);
-
if (hwdef->apc_base) {
apc_init(hwdef->apc_base, cpu_halt[0]);
}
@@ -955,8 +956,13 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
fd[0] = drive_get(IF_FLOPPY, 0, 0);
sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd,
&fdc_tc);
+ } else {
+ fdc_tc = *qemu_allocate_irqs(dummy_fdc_tc, NULL, 1);
}
+ slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base,
+ slavio_irq[30], fdc_tc);
+
if (drive_get_max_bus(IF_SCSI) > 0) {
fprintf(stderr, "qemu: too many SCSI bus\n");
exit(1);
@@ -1772,16 +1778,18 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size,
slavio_irq[1], serial_hds[0], serial_hds[1],
ESCC_CLOCK, 1);
- slavio_misc_init(0, hwdef->aux1_base, 0, slavio_irq[1], fdc_tc);
-
if (hwdef->fd_base != (target_phys_addr_t)-1) {
/* there is zero or one floppy drive */
memset(fd, 0, sizeof(fd));
fd[0] = drive_get(IF_FLOPPY, 0, 0);
sun4m_fdctrl_init(slavio_irq[1], hwdef->fd_base, fd,
&fdc_tc);
+ } else {
+ fdc_tc = *qemu_allocate_irqs(dummy_fdc_tc, NULL, 1);
}
+ slavio_misc_init(0, hwdef->aux1_base, 0, slavio_irq[1], fdc_tc);
+
if (drive_get_max_bus(IF_SCSI) > 0) {
fprintf(stderr, "qemu: too many SCSI bus\n");
exit(1);
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/usb/dev-storage.c b/hw/usb/dev-storage.c
index 7fa8b83d2e..ff48d91049 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -247,6 +247,9 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
the status read packet. */
usb_msd_send_status(s, p);
s->mode = USB_MSDM_CBW;
+ } else if (s->mode == USB_MSDM_CSW) {
+ usb_msd_send_status(s, p);
+ s->mode = USB_MSDM_CBW;
} else {
if (s->data_len) {
int len = (p->iov.size - p->result);
@@ -383,6 +386,9 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
assert(le32_to_cpu(s->csw.residue) == 0);
s->scsi_len = 0;
s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL);
+#ifdef DEBUG_MSD
+ scsi_req_print(s->req);
+#endif
scsi_req_enqueue(s->req);
if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) {
scsi_req_continue(s->req);
@@ -410,7 +416,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
}
}
if (p->result < p->iov.size) {
- DPRINTF("Deferring packet %p\n", p);
+ DPRINTF("Deferring packet %p [wait data-out]\n", p);
s->packet = p;
ret = USB_RET_ASYNC;
} else {
@@ -445,6 +451,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
if (s->req) {
/* still in flight */
+ DPRINTF("Deferring packet %p [wait status]\n", p);
s->packet = p;
ret = USB_RET_ASYNC;
} else {
@@ -471,7 +478,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
}
}
if (p->result < p->iov.size) {
- DPRINTF("Deferring packet %p\n", p);
+ DPRINTF("Deferring packet %p [wait data-in]\n", p);
s->packet = p;
ret = USB_RET_ASYNC;
} else {
diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c
index adfaf2c50e..9b08c62912 100644
--- a/hw/xilinx_axienet.c
+++ b/hw/xilinx_axienet.c
@@ -648,7 +648,6 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
uint16_t csum16;
int i;
- s = s;
DENET(qemu_log("%s: %zd bytes\n", __func__, size));
unicast = ~buf[0] & 0x1;
diff --git a/hw/xtensa_lx60.c b/hw/xtensa_lx60.c
index c4f616f4fb..3653f65b1e 100644
--- a/hw/xtensa_lx60.c
+++ b/hw/xtensa_lx60.c
@@ -173,7 +173,7 @@ static void lx_init(const LxBoardDesc *board,
int n;
if (!cpu_model) {
- cpu_model = "dc232b";
+ cpu_model = XTENSA_DEFAULT_CPU_MODEL;
}
for (n = 0; n < smp_cpus; n++) {
@@ -300,14 +300,14 @@ static void xtensa_lx200_init(ram_addr_t ram_size,
static QEMUMachine xtensa_lx60_machine = {
.name = "lx60",
- .desc = "lx60 EVB (dc232b)",
+ .desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
.init = xtensa_lx60_init,
.max_cpus = 4,
};
static QEMUMachine xtensa_lx200_machine = {
.name = "lx200",
- .desc = "lx200 EVB (dc232b)",
+ .desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
.init = xtensa_lx200_init,
.max_cpus = 4,
};
diff --git a/hw/xtensa_sim.c b/hw/xtensa_sim.c
index 1ce07fb899..831460b7c4 100644
--- a/hw/xtensa_sim.c
+++ b/hw/xtensa_sim.c
@@ -102,7 +102,7 @@ static void xtensa_sim_init(ram_addr_t ram_size,
const char *initrd_filename, const char *cpu_model)
{
if (!cpu_model) {
- cpu_model = "dc232b";
+ cpu_model = XTENSA_DEFAULT_CPU_MODEL;
}
sim_init(ram_size, boot_device, kernel_filename, kernel_cmdline,
initrd_filename, cpu_model);
@@ -110,7 +110,8 @@ static void xtensa_sim_init(ram_addr_t ram_size,
static QEMUMachine xtensa_sim_machine = {
.name = "sim",
- .desc = "sim machine (dc232b)",
+ .desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")",
+ .is_default = true,
.init = xtensa_sim_init,
.max_cpus = 4,
};
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/linux-user/signal.c b/linux-user/signal.c
index 9be5ac0788..78691473fa 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -1844,7 +1844,7 @@ typedef struct {
} __siginfo_t;
typedef struct {
- unsigned long si_float_regs [32];
+ abi_ulong si_float_regs[32];
unsigned long si_fsr;
unsigned long si_fpqdepth;
struct {
@@ -2056,11 +2056,9 @@ restore_fpu_state(CPUSPARCState *env, qemu_siginfo_fpu_t *fpu)
return -EFAULT;
#endif
-#if 0
/* XXX: incorrect */
- err = __copy_from_user(&env->fpr[0], &fpu->si_float_regs[0],
- (sizeof(unsigned long) * 32));
-#endif
+ err = copy_from_user(&env->fpr[0], fpu->si_float_regs[0],
+ (sizeof(abi_ulong) * 32));
err |= __get_user(env->fsr, &fpu->si_fsr);
#if 0
err |= __get_user(current->thread.fpqdepth, &fpu->si_fpqdepth);
diff --git a/net/slirp.c b/net/slirp.c
index 08adb97da5..8db66ea539 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -718,9 +718,9 @@ int net_init_slirp(const NetClientOptions *opts, const char *name,
net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
net_init_slirp_configs(user->guestfwd, 0);
- ret = net_slirp_init(peer, "user", name, user->restrict, vnet, user->host,
- user->hostname, user->tftp, user->bootfile,
- user->dhcpstart, user->dns, user->smb,
+ ret = net_slirp_init(peer, "user", name, user->q_restrict, vnet,
+ user->host, user->hostname, user->tftp,
+ user->bootfile, user->dhcpstart, user->dns, user->smb,
user->smbserver);
while (slirp_configs) {
diff --git a/qapi-schema.json b/qapi-schema.json
index cddf63a878..bd9c450029 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1660,7 +1660,7 @@
# Returns: Nothing on success
# If the job type does not support throttling, NotSupported
# If the speed value is invalid, InvalidParameter
-# If streaming is not active on this device, DeviceNotActive
+# If no background operation is active on this device, DeviceNotActive
#
# Since: 1.1
##
@@ -1670,9 +1670,9 @@
##
# @block-job-cancel:
#
-# Stop an active block streaming operation.
+# Stop an active background block operation.
#
-# This command returns immediately after marking the active block streaming
+# This command returns immediately after marking the active background block
# operation for cancellation. It is an error to call this command if no
# operation is in progress.
#
@@ -1680,16 +1680,15 @@
# BLOCK_JOB_CANCELLED event. Before that happens the job is still visible when
# enumerated using query-block-jobs.
#
-# The image file retains its backing file unless the streaming operation happens
-# to complete just as it is being cancelled.
-#
-# A new block streaming operation can be started at a later time to finish
-# copying all data from the backing file.
+# For streaming, the image file retains its backing file unless the streaming
+# operation happens to complete just as it is being cancelled. A new streaming
+# operation can be started at a later time to finish copying all data from the
+# backing file.
#
# @device: the device name
#
# Returns: Nothing on success
-# If streaming is not active on this device, DeviceNotActive
+# If no background operation is active on this device, DeviceNotActive
# If cancellation already in progress, DeviceInUse
#
# Since: 1.1
diff --git a/qemu-img.c b/qemu-img.c
index b866f8081e..94a31ad9f0 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1567,14 +1567,19 @@ static int img_resize(int argc, char **argv)
const char *filename, *fmt, *size;
int64_t n, total_size;
BlockDriverState *bs = NULL;
- QEMUOptionParameter *param;
- QEMUOptionParameter resize_options[] = {
- {
- .name = BLOCK_OPT_SIZE,
- .type = OPT_SIZE,
- .help = "Virtual disk size"
+ QemuOpts *param;
+ static QemuOptsList resize_options = {
+ .name = "resize_options",
+ .head = QTAILQ_HEAD_INITIALIZER(resize_options.head),
+ .desc = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = QEMU_OPT_SIZE,
+ .help = "Virtual disk size"
+ }, {
+ /* end of list */
+ }
},
- { NULL }
};
/* Remove size from argv manually so that negative numbers are not treated
@@ -1624,14 +1629,15 @@ static int img_resize(int argc, char **argv)
}
/* Parse size */
- param = parse_option_parameters("", resize_options, NULL);
- if (set_option_parameter(param, BLOCK_OPT_SIZE, size)) {
+ param = qemu_opts_create(&resize_options, NULL, 0, NULL);
+ if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) {
/* Error message already printed when size parsing fails */
ret = -1;
+ qemu_opts_del(param);
goto out;
}
- n = get_option_parameter(param, BLOCK_OPT_SIZE)->value.n;
- free_option_parameters(param);
+ n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0);
+ qemu_opts_del(param);
bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR);
if (!bs) {
diff --git a/qemu-io.c b/qemu-io.c
index 8f3b94b838..d0f4fb70c7 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -1652,6 +1652,17 @@ static const cmdinfo_t map_cmd = {
.oneline = "prints the allocated areas of a file",
};
+static int abort_f(int argc, char **argv)
+{
+ abort();
+}
+
+static const cmdinfo_t abort_cmd = {
+ .name = "abort",
+ .cfunc = abort_f,
+ .flags = CMD_NOFILE_OK,
+ .oneline = "simulate a program crash using abort(3)",
+};
static int close_f(int argc, char **argv)
{
@@ -1905,6 +1916,7 @@ int main(int argc, char **argv)
add_command(&discard_cmd);
add_command(&alloc_cmd);
add_command(&map_cmd);
+ add_command(&abort_cmd);
add_args_command(init_args_command);
add_check_command(init_check_command);
diff --git a/qemu-timer.c b/qemu-timer.c
index 062fdf2cb9..5aea94e8e0 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -112,14 +112,10 @@ static int64_t qemu_next_alarm_deadline(void)
static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
{
- int64_t nearest_delta_ns;
- if (!rt_clock->active_timers &&
- !vm_clock->active_timers &&
- !host_clock->active_timers) {
- return;
+ int64_t nearest_delta_ns = qemu_next_alarm_deadline();
+ if (nearest_delta_ns < INT64_MAX) {
+ t->rearm(t, nearest_delta_ns);
}
- nearest_delta_ns = qemu_next_alarm_deadline();
- t->rearm(t, nearest_delta_ns);
}
/* TODO: MIN_TIMER_REARM_NS should be optimized */
diff --git a/scripts/qapi.py b/scripts/qapi.py
index d3b8b4d851..122b4cb6d1 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -142,6 +142,22 @@ def camel_case(name):
return new_name
def c_var(name):
+ # ANSI X3J11/88-090, 3.1.1
+ c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
+ 'default', 'do', 'double', 'else', 'enum', 'extern', 'float',
+ 'for', 'goto', 'if', 'int', 'long', 'register', 'return',
+ 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
+ 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'])
+ # ISO/IEC 9899:1999, 6.4.1
+ c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
+ # ISO/IEC 9899:2011, 6.4.1
+ c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn',
+ '_Static_assert', '_Thread_local'])
+ # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
+ # excluding _.*
+ gcc_words = set(['asm', 'typeof'])
+ if name in c89_words | c99_words | c11_words | gcc_words:
+ return "q_" + name
return name.replace('-', '_').lstrip("*")
def c_fun(name):
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 857b94ea87..880cfea3f8 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1746,6 +1746,7 @@ static void x86_cpu_initfn(Object *obj)
{
X86CPU *cpu = X86_CPU(obj);
CPUX86State *env = &cpu->env;
+ static int inited;
cpu_exec_init(env);
@@ -1775,6 +1776,15 @@ static void x86_cpu_initfn(Object *obj)
x86_cpuid_set_tsc_freq, NULL, NULL, NULL);
env->cpuid_apic_id = env->cpu_index;
+
+ /* init various static tables used in TCG mode */
+ if (tcg_enabled() && !inited) {
+ inited = 1;
+ optimize_flags_init();
+#ifndef CONFIG_USER_ONLY
+ cpu_set_debug_excp_handler(breakpoint_handler);
+#endif
+ }
}
static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 2a61c810bb..60f9e972bd 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -935,6 +935,7 @@ static inline int hw_breakpoint_len(unsigned long dr7, int index)
void hw_breakpoint_insert(CPUX86State *env, int index);
void hw_breakpoint_remove(CPUX86State *env, int index);
int check_hw_breakpoints(CPUX86State *env, int force_dr6_update);
+void breakpoint_handler(CPUX86State *env);
/* will be suppressed */
void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0);
diff --git a/target-i386/helper.c b/target-i386/helper.c
index b748d90063..8a5da3d7c0 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -941,9 +941,7 @@ int check_hw_breakpoints(CPUX86State *env, int force_dr6_update)
return hit_enabled;
}
-static CPUDebugExcpHandler *prev_debug_excp_handler;
-
-static void breakpoint_handler(CPUX86State *env)
+void breakpoint_handler(CPUX86State *env)
{
CPUBreakpoint *bp;
@@ -965,8 +963,6 @@ static void breakpoint_handler(CPUX86State *env)
break;
}
}
- if (prev_debug_excp_handler)
- prev_debug_excp_handler(env);
}
typedef struct MCEInjectionParams {
@@ -1155,21 +1151,11 @@ X86CPU *cpu_x86_init(const char *cpu_model)
{
X86CPU *cpu;
CPUX86State *env;
- static int inited;
cpu = X86_CPU(object_new(TYPE_X86_CPU));
env = &cpu->env;
env->cpu_model_str = cpu_model;
- /* init various static tables used in TCG mode */
- if (tcg_enabled() && !inited) {
- inited = 1;
- optimize_flags_init();
-#ifndef CONFIG_USER_ONLY
- prev_debug_excp_handler =
- cpu_set_debug_excp_handler(breakpoint_handler);
-#endif
- }
if (cpu_x86_register(cpu, cpu_model) < 0) {
object_delete(OBJECT(cpu));
return NULL;
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 4e15ee36b8..47daf8574f 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -12763,6 +12763,7 @@ void cpu_state_reset(CPUMIPSState *env)
env->CP0_SRSConf3 = env->cpu_model->CP0_SRSConf3;
env->CP0_SRSConf4_rw_bitmask = env->cpu_model->CP0_SRSConf4_rw_bitmask;
env->CP0_SRSConf4 = env->cpu_model->CP0_SRSConf4;
+ env->active_fpu.fcr0 = env->cpu_model->CP1_fcr0;
env->insn_flags = env->cpu_model->insn_flags;
#if defined(CONFIG_USER_ONLY)
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/target-xtensa/cpu.h b/target-xtensa/cpu.h
index f7db116400..177094ae9a 100644
--- a/target-xtensa/cpu.h
+++ b/target-xtensa/cpu.h
@@ -351,6 +351,12 @@ typedef struct CPUXtensaState {
#define cpu_signal_handler cpu_xtensa_signal_handler
#define cpu_list xtensa_cpu_list
+#ifdef TARGET_WORDS_BIGENDIAN
+#define XTENSA_DEFAULT_CPU_MODEL "fsf"
+#else
+#define XTENSA_DEFAULT_CPU_MODEL "dc232b"
+#endif
+
XtensaCPU *cpu_xtensa_init(const char *cpu_model);
static inline CPUXtensaState *cpu_init(const char *cpu_model)
diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c
index 044ce18364..d5bb171fcd 100644
--- a/target-xtensa/helper.c
+++ b/target-xtensa/helper.c
@@ -54,8 +54,6 @@ static uint32_t check_hw_breakpoints(CPUXtensaState *env)
return 0;
}
-static CPUDebugExcpHandler *prev_debug_excp_handler;
-
static void breakpoint_handler(CPUXtensaState *env)
{
if (env->watchpoint_hit) {
@@ -70,9 +68,6 @@ static void breakpoint_handler(CPUXtensaState *env)
cpu_resume_from_signal(env, NULL);
}
}
- if (prev_debug_excp_handler) {
- prev_debug_excp_handler(env);
- }
}
XtensaCPU *cpu_xtensa_init(const char *cpu_model)
@@ -105,8 +100,7 @@ XtensaCPU *cpu_xtensa_init(const char *cpu_model)
if (!debug_handler_inited && tcg_enabled()) {
debug_handler_inited = 1;
- prev_debug_excp_handler =
- cpu_set_debug_excp_handler(breakpoint_handler);
+ cpu_set_debug_excp_handler(breakpoint_handler);
}
xtensa_irq_init(env);
diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out
index d3cab301d4..796c993df2 100644
--- a/tests/qemu-iotests/031.out
+++ b/tests/qemu-iotests/031.out
@@ -54,8 +54,8 @@ header_length 72
Header extension:
magic 0x6803f857
-length 0
-data ''
+length 96
+data <binary>
Header extension:
magic 0x12345678
@@ -68,7 +68,7 @@ No errors were found on the image.
magic 0x514649fb
version 2
-backing_file_offset 0x98
+backing_file_offset 0xf8
backing_file_size 0x17
cluster_bits 16
size 67108864
@@ -92,8 +92,8 @@ data 'host_device'
Header extension:
magic 0x6803f857
-length 0
-data ''
+length 96
+data <binary>
Header extension:
magic 0x12345678
@@ -155,8 +155,8 @@ header_length 104
Header extension:
magic 0x6803f857
-length 0
-data ''
+length 96
+data <binary>
Header extension:
magic 0x12345678
@@ -169,7 +169,7 @@ No errors were found on the image.
magic 0x514649fb
version 3
-backing_file_offset 0xb8
+backing_file_offset 0x118
backing_file_size 0x17
cluster_bits 16
size 67108864
@@ -193,8 +193,8 @@ data 'host_device'
Header extension:
magic 0x6803f857
-length 0
-data ''
+length 96
+data <binary>
Header extension:
magic 0x12345678
diff --git a/tests/qemu-iotests/036.out b/tests/qemu-iotests/036.out
index 6953e37ab6..063ca22d66 100644
--- a/tests/qemu-iotests/036.out
+++ b/tests/qemu-iotests/036.out
@@ -46,7 +46,7 @@ header_length 104
Header extension:
magic 0x6803f857
-length 0
-data ''
+length 96
+data <binary>
*** done
diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039
new file mode 100755
index 0000000000..a749fcf23b
--- /dev/null
+++ b/tests/qemu-iotests/039
@@ -0,0 +1,136 @@
+#!/bin/bash
+#
+# Test qcow2 lazy refcounts
+#
+# Copyright (C) 2012 Red Hat, Inc.
+# Copyright IBM, Corp. 2010
+#
+# Based on test 038.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=stefanha@linux.vnet.ibm.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+size=128M
+
+echo
+echo "== Checking that image is clean on shutdown =="
+
+IMGOPTS="compat=1.1,lazy_refcounts=on"
+_make_test_img $size
+
+$QEMU_IO -c "write -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io
+
+# The dirty bit must not be set
+./qcow2.py $TEST_IMG dump-header | grep incompatible_features
+_check_test_img
+
+echo
+echo "== Creating a dirty image file =="
+
+IMGOPTS="compat=1.1,lazy_refcounts=on"
+_make_test_img $size
+
+old_ulimit=$(ulimit -c)
+ulimit -c 0 # do not produce a core dump on abort(3)
+$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io
+ulimit -c "$old_ulimit"
+
+# The dirty bit must be set
+./qcow2.py $TEST_IMG dump-header | grep incompatible_features
+_check_test_img
+
+echo
+echo "== Read-only access must still work =="
+
+$QEMU_IO -r -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io
+
+# The dirty bit must be set
+./qcow2.py $TEST_IMG dump-header | grep incompatible_features
+
+echo
+echo "== Repairing the image file must succeed =="
+
+$QEMU_IMG check -r all $TEST_IMG
+
+# The dirty bit must not be set
+./qcow2.py $TEST_IMG dump-header | grep incompatible_features
+
+echo
+echo "== Data should still be accessible after repair =="
+
+$QEMU_IO -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io
+
+echo
+echo "== Opening a dirty image read/write should repair it =="
+
+IMGOPTS="compat=1.1,lazy_refcounts=on"
+_make_test_img $size
+
+old_ulimit=$(ulimit -c)
+ulimit -c 0 # do not produce a core dump on abort(3)
+$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io
+ulimit -c "$old_ulimit"
+
+# The dirty bit must be set
+./qcow2.py $TEST_IMG dump-header | grep incompatible_features
+
+$QEMU_IO -c "write 0 512" $TEST_IMG | _filter_qemu_io
+
+# The dirty bit must not be set
+./qcow2.py $TEST_IMG dump-header | grep incompatible_features
+
+echo
+echo "== Creating an image file with lazy_refcounts=off =="
+
+IMGOPTS="compat=1.1,lazy_refcounts=off"
+_make_test_img $size
+
+old_ulimit=$(ulimit -c)
+ulimit -c 0 # do not produce a core dump on abort(3)
+$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io
+ulimit -c "$old_ulimit"
+
+# The dirty bit must not be set since lazy_refcounts=off
+./qcow2.py $TEST_IMG dump-header | grep incompatible_features
+_check_test_img
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
+
diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out
new file mode 100644
index 0000000000..155a05e109
--- /dev/null
+++ b/tests/qemu-iotests/039.out
@@ -0,0 +1,53 @@
+QA output created by 039
+
+== Checking that image is clean on shutdown ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+incompatible_features 0x0
+No errors were found on the image.
+
+== Creating a dirty image file ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+incompatible_features 0x1
+ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0
+ERROR cluster 5 refcount=0 reference=1
+
+2 errors were found on the image.
+Data may be corrupted, or further writes to the image may corrupt it.
+
+== Read-only access must still work ==
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+incompatible_features 0x1
+
+== Repairing the image file must succeed ==
+ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0
+Repairing cluster 5 refcount=0 reference=1
+No errors were found on the image.
+incompatible_features 0x0
+
+== Data should still be accessible after repair ==
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Opening a dirty image read/write should repair it ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+incompatible_features 0x1
+ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0
+Repairing cluster 5 refcount=0 reference=1
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+incompatible_features 0x0
+
+== Creating an image file with lazy_refcounts=off ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+incompatible_features 0x0
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 5e3a524bc8..7782808a26 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -110,10 +110,11 @@ _make_test_img()
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##g" | \
+ sed -e "s# table_size=[0-9]\\+##g" | \
sed -e "s# compat='[^']*'##g" | \
- sed -e "s# compat6=off##g" | \
- sed -e "s# static=off##g"
+ sed -e "s# compat6=\\(on\\|off\\)##g" | \
+ sed -e "s# static=\\(on\\|off\\)##g" | \
+ sed -e "s# lazy_refcounts=\\(on\\|off\\)##g"
}
_cleanup_test_img()
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 7a2c92b6e9..ebb5ca4b41 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -45,3 +45,4 @@
036 rw auto quick
037 rw auto backing
038 rw auto backing
+039 rw auto
diff --git a/tests/qemu-iotests/qed.py b/tests/qemu-iotests/qed.py
new file mode 100755
index 0000000000..52ff845590
--- /dev/null
+++ b/tests/qemu-iotests/qed.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python
+#
+# Tool to manipulate QED image files
+#
+# Copyright (C) 2010 IBM, Corp.
+#
+# Authors:
+# Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+import sys
+import struct
+import random
+import optparse
+
+# This can be used as a module
+__all__ = ['QED_F_NEED_CHECK', 'QED']
+
+QED_F_NEED_CHECK = 0x02
+
+header_fmt = '<IIIIQQQQQII'
+header_size = struct.calcsize(header_fmt)
+field_names = ['magic', 'cluster_size', 'table_size',
+ 'header_size', 'features', 'compat_features',
+ 'autoclear_features', 'l1_table_offset', 'image_size',
+ 'backing_filename_offset', 'backing_filename_size']
+table_elem_fmt = '<Q'
+table_elem_size = struct.calcsize(table_elem_fmt)
+
+def err(msg):
+ sys.stderr.write(msg + '\n')
+ sys.exit(1)
+
+def unpack_header(s):
+ fields = struct.unpack(header_fmt, s)
+ return dict((field_names[idx], val) for idx, val in enumerate(fields))
+
+def pack_header(header):
+ fields = tuple(header[x] for x in field_names)
+ return struct.pack(header_fmt, *fields)
+
+def unpack_table_elem(s):
+ return struct.unpack(table_elem_fmt, s)[0]
+
+def pack_table_elem(elem):
+ return struct.pack(table_elem_fmt, elem)
+
+class QED(object):
+ def __init__(self, f):
+ self.f = f
+
+ self.f.seek(0, 2)
+ self.filesize = f.tell()
+
+ self.load_header()
+ self.load_l1_table()
+
+ def raw_pread(self, offset, size):
+ self.f.seek(offset)
+ return self.f.read(size)
+
+ def raw_pwrite(self, offset, data):
+ self.f.seek(offset)
+ return self.f.write(data)
+
+ def load_header(self):
+ self.header = unpack_header(self.raw_pread(0, header_size))
+
+ def store_header(self):
+ self.raw_pwrite(0, pack_header(self.header))
+
+ def read_table(self, offset):
+ size = self.header['table_size'] * self.header['cluster_size']
+ s = self.raw_pread(offset, size)
+ table = [unpack_table_elem(s[i:i + table_elem_size]) for i in xrange(0, size, table_elem_size)]
+ return table
+
+ def load_l1_table(self):
+ self.l1_table = self.read_table(self.header['l1_table_offset'])
+ self.table_nelems = self.header['table_size'] * self.header['cluster_size'] / table_elem_size
+
+ def write_table(self, offset, table):
+ s = ''.join(pack_table_elem(x) for x in table)
+ self.raw_pwrite(offset, s)
+
+def random_table_item(table):
+ vals = [(index, offset) for index, offset in enumerate(table) if offset != 0]
+ if not vals:
+ err('cannot pick random item because table is empty')
+ return random.choice(vals)
+
+def corrupt_table_duplicate(table):
+ '''Corrupt a table by introducing a duplicate offset'''
+ victim_idx, victim_val = random_table_item(table)
+ unique_vals = set(table)
+ if len(unique_vals) == 1:
+ err('no duplication corruption possible in table')
+ dup_val = random.choice(list(unique_vals.difference([victim_val])))
+ table[victim_idx] = dup_val
+
+def corrupt_table_invalidate(qed, table):
+ '''Corrupt a table by introducing an invalid offset'''
+ index, _ = random_table_item(table)
+ table[index] = qed.filesize + random.randint(0, 100 * 1024 * 1024 * 1024 * 1024)
+
+def cmd_show(qed, *args):
+ '''show [header|l1|l2 <offset>]- Show header or l1/l2 tables'''
+ if not args or args[0] == 'header':
+ print qed.header
+ elif args[0] == 'l1':
+ print qed.l1_table
+ elif len(args) == 2 and args[0] == 'l2':
+ offset = int(args[1])
+ print qed.read_table(offset)
+ else:
+ err('unrecognized sub-command')
+
+def cmd_duplicate(qed, table_level):
+ '''duplicate l1|l2 - Duplicate a random table element'''
+ if table_level == 'l1':
+ offset = qed.header['l1_table_offset']
+ table = qed.l1_table
+ elif table_level == 'l2':
+ _, offset = random_table_item(qed.l1_table)
+ table = qed.read_table(offset)
+ else:
+ err('unrecognized sub-command')
+ corrupt_table_duplicate(table)
+ qed.write_table(offset, table)
+
+def cmd_invalidate(qed, table_level):
+ '''invalidate l1|l2 - Plant an invalid table element at random'''
+ if table_level == 'l1':
+ offset = qed.header['l1_table_offset']
+ table = qed.l1_table
+ elif table_level == 'l2':
+ _, offset = random_table_item(qed.l1_table)
+ table = qed.read_table(offset)
+ else:
+ err('unrecognized sub-command')
+ corrupt_table_invalidate(qed, table)
+ qed.write_table(offset, table)
+
+def cmd_need_check(qed, *args):
+ '''need-check [on|off] - Test, set, or clear the QED_F_NEED_CHECK header bit'''
+ if not args:
+ print bool(qed.header['features'] & QED_F_NEED_CHECK)
+ return
+
+ if args[0] == 'on':
+ qed.header['features'] |= QED_F_NEED_CHECK
+ elif args[0] == 'off':
+ qed.header['features'] &= ~QED_F_NEED_CHECK
+ else:
+ err('unrecognized sub-command')
+ qed.store_header()
+
+def cmd_zero_cluster(qed, pos, *args):
+ '''zero-cluster <pos> [<n>] - Zero data clusters'''
+ pos, n = int(pos), 1
+ if args:
+ if len(args) != 1:
+ err('expected one argument')
+ n = int(args[0])
+
+ for i in xrange(n):
+ l1_index = pos / qed.header['cluster_size'] / len(qed.l1_table)
+ if qed.l1_table[l1_index] == 0:
+ err('no l2 table allocated')
+
+ l2_offset = qed.l1_table[l1_index]
+ l2_table = qed.read_table(l2_offset)
+
+ l2_index = (pos / qed.header['cluster_size']) % len(qed.l1_table)
+ l2_table[l2_index] = 1 # zero the data cluster
+ qed.write_table(l2_offset, l2_table)
+ pos += qed.header['cluster_size']
+
+def cmd_copy_metadata(qed, outfile):
+ '''copy-metadata <outfile> - Copy metadata only (for scrubbing corrupted images)'''
+ out = open(outfile, 'wb')
+
+ # Match file size
+ out.seek(qed.filesize - 1)
+ out.write('\0')
+
+ # Copy header clusters
+ out.seek(0)
+ header_size_bytes = qed.header['header_size'] * qed.header['cluster_size']
+ out.write(qed.raw_pread(0, header_size_bytes))
+
+ # Copy L1 table
+ out.seek(qed.header['l1_table_offset'])
+ s = ''.join(pack_table_elem(x) for x in qed.l1_table)
+ out.write(s)
+
+ # Copy L2 tables
+ for l2_offset in qed.l1_table:
+ if l2_offset == 0:
+ continue
+ l2_table = qed.read_table(l2_offset)
+ out.seek(l2_offset)
+ s = ''.join(pack_table_elem(x) for x in l2_table)
+ out.write(s)
+
+ out.close()
+
+def usage():
+ print 'Usage: %s <file> <cmd> [<arg>, ...]' % sys.argv[0]
+ print
+ print 'Supported commands:'
+ for cmd in sorted(x for x in globals() if x.startswith('cmd_')):
+ print globals()[cmd].__doc__
+ sys.exit(1)
+
+def main():
+ if len(sys.argv) < 3:
+ usage()
+ filename, cmd = sys.argv[1:3]
+
+ cmd = 'cmd_' + cmd.replace('-', '_')
+ if cmd not in globals():
+ usage()
+
+ qed = QED(open(filename, 'r+b'))
+ try:
+ globals()[cmd](qed, *sys.argv[3:])
+ except TypeError, e:
+ sys.stderr.write(globals()[cmd].__doc__ + '\n')
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/user-exec.c b/user-exec.c
index 1a9c276eb3..b9ea9dd32f 100644
--- a/user-exec.c
+++ b/user-exec.c
@@ -18,7 +18,9 @@
*/
#include "config.h"
#include "cpu.h"
+#ifndef CONFIG_TCG_PASS_AREG0
#include "dyngen-exec.h"
+#endif
#include "disas.h"
#include "tcg.h"
@@ -58,9 +60,11 @@ void cpu_resume_from_signal(CPUArchState *env1, void *puc)
struct sigcontext *uc = puc;
#endif
+#ifndef CONFIG_TCG_PASS_AREG0
env = env1;
/* XXX: restore cpu registers saved in host registers */
+#endif
if (puc) {
/* XXX: use siglongjmp ? */
@@ -74,8 +78,8 @@ void cpu_resume_from_signal(CPUArchState *env1, void *puc)
sigprocmask(SIG_SETMASK, &uc->sc_mask, NULL);
#endif
}
- env->exception_index = -1;
- longjmp(env->jmp_env, 1);
+ env1->exception_index = -1;
+ longjmp(env1->jmp_env, 1);
}
/* 'pc' is the host PC at which the exception was raised. 'address' is
@@ -89,9 +93,11 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address,
TranslationBlock *tb;
int ret;
+#ifndef CONFIG_TCG_PASS_AREG0
if (cpu_single_env) {
env = cpu_single_env; /* XXX: find a correct solution for multithread */
}
+#endif
#if defined(DEBUG_SIGNAL)
qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
pc, address, is_write, *(unsigned long *)old_set);
@@ -103,7 +109,8 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address,
}
/* see if it is an MMU fault */
- ret = cpu_handle_mmu_fault(env, address, is_write, MMU_USER_IDX);
+ ret = cpu_handle_mmu_fault(cpu_single_env, address, is_write,
+ MMU_USER_IDX);
if (ret < 0) {
return 0; /* not an MMU fault */
}
@@ -115,13 +122,13 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address,
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_restore_state(tb, cpu_single_env, pc);
}
/* we restore the process signal mask as the sigreturn should
do it (XXX: use sigsetjmp) */
sigprocmask(SIG_SETMASK, old_set, NULL);
- exception_action(env);
+ exception_action(cpu_single_env);
/* never comes here */
return 1;
diff --git a/vl.c b/vl.c
index 065aec290e..ad9b03602b 100644
--- a/vl.c
+++ b/vl.c
@@ -3350,6 +3350,11 @@ int main(int argc, char **argv, char **envp)
ram_size = DEFAULT_RAM_SIZE * 1024 * 1024;
}
+ if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0)
+ != 0) {
+ exit(0);
+ }
+
configure_accelerator();
qemu_init_cpu_loop();
@@ -3505,9 +3510,6 @@ int main(int argc, char **argv, char **envp)
}
select_vgahw(vga_model);
- if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0) != 0)
- exit(0);
-
if (watchdog) {
i = select_watchdog(watchdog);
if (i > 0)