diff options
197 files changed, 5830 insertions, 2050 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 50343e9..d7e9ba2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -157,6 +157,7 @@ F: hw/sh4/ SPARC M: Blue Swirl <blauwirbel@gmail.com> +M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> S: Maintained F: target-sparc/ F: hw/sparc/ @@ -516,11 +517,13 @@ SPARC Machines -------------- Sun4m M: Blue Swirl <blauwirbel@gmail.com> +M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> S: Maintained F: hw/sparc/sun4m.c Sun4u M: Blue Swirl <blauwirbel@gmail.com> +M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> S: Maintained F: hw/sparc64/sun4u.c @@ -602,11 +605,25 @@ S: Maintained F: hw/misc/edu.c IDE -M: Kevin Wolf <kwolf@redhat.com> -M: Stefan Hajnoczi <stefanha@redhat.com> -S: Odd Fixes +M: John Snow <jsnow@redhat.com> +L: qemu-block@nongnu.org +S: Supported F: include/hw/ide.h F: hw/ide/ +F: hw/block/block.c +F: hw/block/cdrom.c +F: hw/block/hd-geometry.c +F: tests/ide-test.c +F: tests/ahci-test.c +T: git git://github.com/jnsnow/qemu.git ide + +Floppy +M: John Snow <jsnow@redhat.com> +L: qemu-block@nongnu.org +S: Supported +F: hw/block/fdc.c +F: include/hw/block/fdc.h +T: git git://github.com/jnsnow/qemu.git ide OMAP M: Peter Maydell <peter.maydell@linaro.org> @@ -766,6 +783,18 @@ F: tests/qemu-iotests/ T: git git://repo.or.cz/qemu/kevin.git block T: git git://github.com/stefanha/qemu.git block +Block Jobs +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org +S: Supported +F: blockjob.c +F: include/block/blockjob.h +F: block/backup.c +F: block/commit.c +F: block/stream.h +F: block/mirror.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + Character Devices M: Paolo Bonzini <pbonzini@redhat.com> S: Maintained @@ -1070,20 +1099,28 @@ F: block/vmdk.c RBD M: Josh Durgin <josh.durgin@inktank.com> +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org S: Supported F: block/rbd.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block Sheepdog M: Hitoshi Mitake <mitake.hitoshi@lab.ntt.co.jp> M: Liu Yuan <namei.unix@gmail.com> +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org L: sheepdog@lists.wpkg.org S: Supported F: block/sheepdog.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block VHDX M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org S: Supported F: block/vhdx* +T: git git://github.com/codyprime/qemu-kvm-jtc.git block VDI M: Stefan Weil <sw@weilnetz.de> @@ -1098,19 +1135,42 @@ S: Supported F: block/iscsi.c NFS +M: Jeff Cody <jcody@redhat.com> M: Peter Lieven <pl@kamp.de> +L: qemu-block@nongnu.org S: Maintained F: block/nfs.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block SSH M: Richard W.M. Jones <rjones@redhat.com> +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org S: Supported F: block/ssh.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block ARCHIPELAGO M: Chrysostomos Nanakos <chris@include.gr> +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org S: Maintained F: block/archipelago.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +CURL +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org +S: Supported +F: block/curl.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block + +GLUSTER +M: Jeff Cody <jcody@redhat.com> +L: qemu-block@nongnu.org +S: Supported +F: block/gluster.c +T: git git://github.com/codyprime/qemu-kvm-jtc.git block Bootdevice M: Gonglei <arei.gonglei@huawei.com> @@ -84,6 +84,9 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) ifdef BUILD_DOCS DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt +ifdef CONFIG_LINUX +DOCS+=kvm_stat.1 +endif ifdef CONFIG_VIRTFS DOCS+=fsdev/virtfs-proxy-helper.1 endif @@ -490,6 +493,12 @@ qemu-nbd.8: qemu-nbd.texi $(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ " GEN $@") +kvm_stat.1: scripts/kvm/kvm_stat.texi + $(call quiet-command, \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \ + $(POD2MAN) --section=1 --center=" " --release=" " kvm_stat.pod > $@, \ + " GEN $@") + dvi: qemu-doc.dvi qemu-tech.dvi html: qemu-doc.html qemu-tech.html info: qemu-doc.info qemu-tech.info @@ -568,6 +568,40 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) } } +/** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz struct and return 0. + * On failure return -errno. + * @bs must not be empty. + */ +int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_probe_blocksizes) { + return drv->bdrv_probe_blocksizes(bs, bsz); + } + + return -ENOTSUP; +} + +/** + * Try to get @bs's geometry (cyls, heads, sectors). + * On success, store them in @geo struct and return 0. + * On failure return -errno. + * @bs must not be empty. + */ +int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + BlockDriver *drv = bs->drv; + + if (drv && drv->bdrv_probe_geometry) { + return drv->bdrv_probe_geometry(bs, geo); + } + + return -ENOTSUP; +} + /* * Create a uniquely-named empty temporary file. * Return 0 upon success, otherwise a negative errno value. diff --git a/block/blkdebug.c b/block/blkdebug.c index 9ce35cd..63611e0 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -472,12 +472,14 @@ static BlockAIOCB *inject_error(BlockDriverState *bs, int error = rule->options.inject.error; struct BlkdebugAIOCB *acb; QEMUBH *bh; + bool immediately = rule->options.inject.immediately; if (rule->options.inject.once) { - QSIMPLEQ_INIT(&s->active_rules); + QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next); + remove_rule(rule); } - if (rule->options.inject.immediately) { + if (immediately) { return NULL; } diff --git a/block/block-backend.c b/block/block-backend.c index bfb0418..48b6e4c 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -892,3 +892,13 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) { return bdrv_load_vmstate(blk->bs, buf, pos, size); } + +int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) +{ + return bdrv_probe_blocksizes(blk->bs, bsz); +} + +int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) +{ + return bdrv_probe_geometry(blk->bs, geo); +} diff --git a/block/iscsi.c b/block/iscsi.c index 1fa855a..3e34b1f 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1326,7 +1326,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, goto out; } - if (iscsi_url->user != NULL) { + if (iscsi_url->user[0] != '\0') { ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd); if (ret != 0) { diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 183177d..ed2b44d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1640,7 +1640,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, for (i = 0; i < l1_size; i++) { uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; bool l2_dirty = false; - int l2_refcount; + uint64_t l2_refcount; if (!l2_offset) { /* unallocated */ @@ -1672,9 +1672,9 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - l2_refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); - if (l2_refcount < 0) { - ret = l2_refcount; + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &l2_refcount); + if (ret < 0) { goto fail; } @@ -1707,7 +1707,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, /* For shared L2 tables, set the refcount accordingly (it is * already 1 and needs to be l2_refcount) */ ret = qcow2_update_cluster_refcount(bs, - offset >> s->cluster_bits, l2_refcount - 1, + offset >> s->cluster_bits, + refcount_diff(1, l2_refcount), false, QCOW2_DISCARD_OTHER); if (ret < 0) { qcow2_free_clusters(bs, offset, s->cluster_size, diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 9b80ca7..dc8d186 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -29,8 +29,52 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, - int64_t offset, int64_t length, - int addend, enum qcow2_discard_type type); + int64_t offset, int64_t length, uint64_t addend, + bool decrease, enum qcow2_discard_type type); + +static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index); +static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index); + +static void set_refcount_ro0(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro1(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro2(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro3(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro4(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro5(void *refcount_array, uint64_t index, + uint64_t value); +static void set_refcount_ro6(void *refcount_array, uint64_t index, + uint64_t value); + + +static Qcow2GetRefcountFunc *const get_refcount_funcs[] = { + &get_refcount_ro0, + &get_refcount_ro1, + &get_refcount_ro2, + &get_refcount_ro3, + &get_refcount_ro4, + &get_refcount_ro5, + &get_refcount_ro6 +}; + +static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { + &set_refcount_ro0, + &set_refcount_ro1, + &set_refcount_ro2, + &set_refcount_ro3, + &set_refcount_ro4, + &set_refcount_ro5, + &set_refcount_ro6 +}; /*********************************************************/ @@ -42,6 +86,11 @@ int qcow2_refcount_init(BlockDriverState *bs) unsigned int refcount_table_size2, i; int ret; + assert(s->refcount_order >= 0 && s->refcount_order <= 6); + + s->get_refcount = get_refcount_funcs[s->refcount_order]; + s->set_refcount = set_refcount_funcs[s->refcount_order]; + assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t)); refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = g_try_malloc(refcount_table_size2); @@ -72,6 +121,95 @@ void qcow2_refcount_close(BlockDriverState *bs) } +static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 8] >> (index % 8)) & 0x1; +} + +static void set_refcount_ro0(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 1)); + ((uint8_t *)refcount_array)[index / 8] &= ~(0x1 << (index % 8)); + ((uint8_t *)refcount_array)[index / 8] |= value << (index % 8); +} + +static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 4] >> (2 * (index % 4))) + & 0x3; +} + +static void set_refcount_ro1(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 2)); + ((uint8_t *)refcount_array)[index / 4] &= ~(0x3 << (2 * (index % 4))); + ((uint8_t *)refcount_array)[index / 4] |= value << (2 * (index % 4)); +} + +static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index) +{ + return (((const uint8_t *)refcount_array)[index / 2] >> (4 * (index % 2))) + & 0xf; +} + +static void set_refcount_ro2(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 4)); + ((uint8_t *)refcount_array)[index / 2] &= ~(0xf << (4 * (index % 2))); + ((uint8_t *)refcount_array)[index / 2] |= value << (4 * (index % 2)); +} + +static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index) +{ + return ((const uint8_t *)refcount_array)[index]; +} + +static void set_refcount_ro3(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 8)); + ((uint8_t *)refcount_array)[index] = value; +} + +static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index) +{ + return be16_to_cpu(((const uint16_t *)refcount_array)[index]); +} + +static void set_refcount_ro4(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 16)); + ((uint16_t *)refcount_array)[index] = cpu_to_be16(value); +} + +static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index) +{ + return be32_to_cpu(((const uint32_t *)refcount_array)[index]); +} + +static void set_refcount_ro5(void *refcount_array, uint64_t index, + uint64_t value) +{ + assert(!(value >> 32)); + ((uint32_t *)refcount_array)[index] = cpu_to_be32(value); +} + +static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index) +{ + return be64_to_cpu(((const uint64_t *)refcount_array)[index]); +} + +static void set_refcount_ro6(void *refcount_array, uint64_t index, + uint64_t value) +{ + ((uint64_t *)refcount_array)[index] = cpu_to_be64(value); +} + + static int load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, void **refcount_block) @@ -87,26 +225,29 @@ static int load_refcount_block(BlockDriverState *bs, } /* - * Returns the refcount of the cluster given by its index. Any non-negative - * return value is the refcount of the cluster, negative values are -errno - * and indicate an error. + * Retrieves the refcount of the cluster given by its index and stores it in + * *refcount. Returns 0 on success and -errno on failure. */ -int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index) +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount) { BDRVQcowState *s = bs->opaque; uint64_t refcount_table_index, block_index; int64_t refcount_block_offset; int ret; - uint16_t *refcount_block; - uint16_t refcount; + void *refcount_block; refcount_table_index = cluster_index >> s->refcount_block_bits; - if (refcount_table_index >= s->refcount_table_size) + if (refcount_table_index >= s->refcount_table_size) { + *refcount = 0; return 0; + } refcount_block_offset = s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; - if (!refcount_block_offset) + if (!refcount_block_offset) { + *refcount = 0; return 0; + } if (offset_into_cluster(s, refcount_block_offset)) { qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64 @@ -116,21 +257,20 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index) } ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, - (void**) &refcount_block); + &refcount_block); if (ret < 0) { return ret; } block_index = cluster_index & (s->refcount_block_size - 1); - refcount = be16_to_cpu(refcount_block[block_index]); + *refcount = s->get_refcount(refcount_block, block_index); - ret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + ret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); if (ret < 0) { return ret; } - return refcount; + return 0; } /* @@ -169,7 +309,7 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, * Returns 0 on success or -errno in error case */ static int alloc_refcount_block(BlockDriverState *bs, - int64_t cluster_index, uint16_t **refcount_block) + int64_t cluster_index, void **refcount_block) { BDRVQcowState *s = bs->opaque; unsigned int refcount_table_index; @@ -196,7 +336,7 @@ static int alloc_refcount_block(BlockDriverState *bs, } return load_refcount_block(bs, refcount_block_offset, - (void**) refcount_block); + refcount_block); } } @@ -246,7 +386,7 @@ static int alloc_refcount_block(BlockDriverState *bs, if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { /* Zero the new refcount block before updating it */ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, - (void**) refcount_block); + refcount_block); if (ret < 0) { goto fail_block; } @@ -256,11 +396,11 @@ static int alloc_refcount_block(BlockDriverState *bs, /* The block describes itself, need to update the cache */ int block_index = (new_block >> s->cluster_bits) & (s->refcount_block_size - 1); - (*refcount_block)[block_index] = cpu_to_be16(1); + s->set_refcount(*refcount_block, block_index, 1); } else { /* Described somewhere else. This can recurse at most twice before we * arrive at a block that describes itself. */ - ret = update_refcount(bs, new_block, s->cluster_size, 1, + ret = update_refcount(bs, new_block, s->cluster_size, 1, false, QCOW2_DISCARD_NEVER); if (ret < 0) { goto fail_block; @@ -274,7 +414,7 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Initialize the new refcount block only after updating its refcount, * update_refcount uses the refcount cache itself */ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, - (void**) refcount_block); + refcount_block); if (ret < 0) { goto fail_block; } @@ -308,7 +448,7 @@ static int alloc_refcount_block(BlockDriverState *bs, return -EAGAIN; } - ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + ret = qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); if (ret < 0) { goto fail_block; } @@ -362,7 +502,7 @@ static int alloc_refcount_block(BlockDriverState *bs, s->cluster_size; uint64_t table_offset = meta_offset + blocks_clusters * s->cluster_size; uint64_t *new_table = g_try_new0(uint64_t, table_size); - uint16_t *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size); + void *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size); assert(table_size > 0 && blocks_clusters > 0); if (new_table == NULL || new_blocks == NULL) { @@ -384,7 +524,7 @@ static int alloc_refcount_block(BlockDriverState *bs, uint64_t table_clusters = size_to_clusters(s, table_size * sizeof(uint64_t)); int block = 0; for (i = 0; i < table_clusters + blocks_clusters; i++) { - new_blocks[block++] = cpu_to_be16(1); + s->set_refcount(new_blocks, block++, 1); } /* Write refcount blocks to disk */ @@ -437,7 +577,7 @@ static int alloc_refcount_block(BlockDriverState *bs, qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t), QCOW2_DISCARD_OTHER); - ret = load_refcount_block(bs, new_block, (void**) refcount_block); + ret = load_refcount_block(bs, new_block, refcount_block); if (ret < 0) { return ret; } @@ -452,7 +592,7 @@ fail_table: g_free(new_table); fail_block: if (*refcount_block != NULL) { - qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); } return ret; } @@ -527,18 +667,25 @@ found: } /* XXX: cache several refcount block clusters ? */ +/* @addend is the absolute value of the addend; if @decrease is set, @addend + * will be subtracted from the current refcount, otherwise it will be added */ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, - int64_t offset, int64_t length, int addend, enum qcow2_discard_type type) + int64_t offset, + int64_t length, + uint64_t addend, + bool decrease, + enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; - uint16_t *refcount_block = NULL; + void *refcount_block = NULL; int64_t old_table_index = -1; int ret; #ifdef DEBUG_ALLOC2 - fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 " addend=%d\n", - offset, length, addend); + fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 + " addend=%s%" PRIu64 "\n", offset, length, decrease ? "-" : "", + addend); #endif if (length < 0) { return -EINVAL; @@ -546,7 +693,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, return 0; } - if (addend < 0) { + if (decrease) { qcow2_cache_set_dependency(bs, s->refcount_block_cache, s->l2_table_cache); } @@ -556,7 +703,8 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, for(cluster_offset = start; cluster_offset <= last; cluster_offset += s->cluster_size) { - int block_index, refcount; + int block_index; + uint64_t refcount; int64_t cluster_index = cluster_offset >> s->cluster_bits; int64_t table_index = cluster_index >> s->refcount_block_bits; @@ -564,7 +712,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, if (table_index != old_table_index) { if (refcount_block) { ret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + &refcount_block); if (ret < 0) { goto fail; } @@ -582,16 +730,23 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, /* we can update the count and save it */ block_index = cluster_index & (s->refcount_block_size - 1); - refcount = be16_to_cpu(refcount_block[block_index]); - refcount += addend; - if (refcount < 0 || refcount > 0xffff) { + refcount = s->get_refcount(refcount_block, block_index); + if (decrease ? (refcount - addend > refcount) + : (refcount + addend < refcount || + refcount + addend > s->refcount_max)) + { ret = -EINVAL; goto fail; } + if (decrease) { + refcount -= addend; + } else { + refcount += addend; + } if (refcount == 0 && cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } - refcount_block[block_index] = cpu_to_be16(refcount); + s->set_refcount(refcount_block, block_index, refcount); if (refcount == 0 && s->discard_passthrough[type]) { update_refcount_discard(bs, cluster_offset, s->cluster_size); @@ -607,8 +762,7 @@ fail: /* Write last changed block to disk */ if (refcount_block) { int wret; - wret = qcow2_cache_put(bs, s->refcount_block_cache, - (void**) &refcount_block); + wret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); if (wret < 0) { return ret < 0 ? ret : wret; } @@ -620,8 +774,8 @@ fail: */ if (ret < 0) { int dummy; - dummy = update_refcount(bs, offset, cluster_offset - offset, -addend, - QCOW2_DISCARD_NEVER); + dummy = update_refcount(bs, offset, cluster_offset - offset, addend, + !decrease, QCOW2_DISCARD_NEVER); (void)dummy; } @@ -631,24 +785,26 @@ fail: /* * Increases or decreases the refcount of a given cluster. * - * If the return value is non-negative, it is the new refcount of the cluster. - * If it is negative, it is -errno and indicates an error. + * @addend is the absolute value of the addend; if @decrease is set, @addend + * will be subtracted from the current refcount, otherwise it will be added. + * + * On success 0 is returned; on failure -errno is returned. */ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, - int addend, + uint64_t addend, bool decrease, enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; int ret; ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, - type); + decrease, type); if (ret < 0) { return ret; } - return qcow2_get_refcount(bs, cluster_index); + return 0; } @@ -662,17 +818,17 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) { BDRVQcowState *s = bs->opaque; - uint64_t i, nb_clusters; - int refcount; + uint64_t i, nb_clusters, refcount; + int ret; nb_clusters = size_to_clusters(s, size); retry: for(i = 0; i < nb_clusters; i++) { uint64_t next_cluster_index = s->free_cluster_index++; - refcount = qcow2_get_refcount(bs, next_cluster_index); + ret = qcow2_get_refcount(bs, next_cluster_index, &refcount); - if (refcount < 0) { - return refcount; + if (ret < 0) { + return ret; } else if (refcount != 0) { goto retry; } @@ -706,7 +862,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) return offset; } - ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER); + ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); } while (ret == -EAGAIN); if (ret < 0) { @@ -720,9 +876,9 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, int nb_clusters) { BDRVQcowState *s = bs->opaque; - uint64_t cluster_index; + uint64_t cluster_index, refcount; uint64_t i; - int refcount, ret; + int ret; assert(nb_clusters >= 0); if (nb_clusters == 0) { @@ -733,17 +889,16 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, /* Check how many clusters there are free */ cluster_index = offset >> s->cluster_bits; for(i = 0; i < nb_clusters; i++) { - refcount = qcow2_get_refcount(bs, cluster_index++); - - if (refcount < 0) { - return refcount; + ret = qcow2_get_refcount(bs, cluster_index++, &refcount); + if (ret < 0) { + return ret; } else if (refcount != 0) { break; } } /* And then allocate them */ - ret = update_refcount(bs, offset, i << s->cluster_bits, 1, + ret = update_refcount(bs, offset, i << s->cluster_bits, 1, false, QCOW2_DISCARD_NEVER); } while (ret == -EAGAIN); @@ -770,12 +925,13 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) offset = s->free_byte_offset; if (offset) { - int refcount = qcow2_get_refcount(bs, offset >> s->cluster_bits); - if (refcount < 0) { - return refcount; + uint64_t refcount; + ret = qcow2_get_refcount(bs, offset >> s->cluster_bits, &refcount); + if (ret < 0) { + return ret; } - if (refcount == 0xffff) { + if (refcount == s->refcount_max) { offset = 0; } } @@ -793,7 +949,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) } assert(offset); - ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER); + ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); if (ret < 0) { return ret; } @@ -817,7 +973,7 @@ void qcow2_free_clusters(BlockDriverState *bs, int ret; BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE); - ret = update_refcount(bs, offset, size, -1, type); + ret = update_refcount(bs, offset, size, 1, true, type); if (ret < 0) { fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret)); /* TODO Remember the clusters to free them later and avoid leaking */ @@ -876,12 +1032,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend) { BDRVQcowState *s = bs->opaque; - uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2; + uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount; bool l1_allocated = false; int64_t old_offset, old_l2_offset; - int i, j, l1_modified = 0, nb_csectors, refcount; + int i, j, l1_modified = 0, nb_csectors; int ret; + assert(addend >= -1 && addend <= 1); + l2_table = NULL; l1_table = NULL; l1_size2 = l1_size * sizeof(uint64_t); @@ -946,7 +1104,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (addend != 0) { ret = update_refcount(bs, (offset & s->cluster_offset_mask) & ~511, - nb_csectors * 512, addend, + nb_csectors * 512, abs(addend), addend < 0, QCOW2_DISCARD_SNAPSHOT); if (ret < 0) { goto fail; @@ -976,15 +1134,16 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, break; } if (addend != 0) { - refcount = qcow2_update_cluster_refcount(bs, - cluster_index, addend, + ret = qcow2_update_cluster_refcount(bs, + cluster_index, abs(addend), addend < 0, QCOW2_DISCARD_SNAPSHOT); - } else { - refcount = qcow2_get_refcount(bs, cluster_index); + if (ret < 0) { + goto fail; + } } - if (refcount < 0) { - ret = refcount; + ret = qcow2_get_refcount(bs, cluster_index, &refcount); + if (ret < 0) { goto fail; } break; @@ -1017,13 +1176,17 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (addend != 0) { - refcount = qcow2_update_cluster_refcount(bs, l2_offset >> - s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT); - } else { - refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); + ret = qcow2_update_cluster_refcount(bs, l2_offset >> + s->cluster_bits, + abs(addend), addend < 0, + QCOW2_DISCARD_SNAPSHOT); + if (ret < 0) { + goto fail; + } } - if (refcount < 0) { - ret = refcount; + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { goto fail; } else if (refcount == 1) { l2_offset |= QCOW_OFLAG_COPIED; @@ -1068,6 +1231,63 @@ fail: /* refcount checking functions */ +static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) +{ + /* This assertion holds because there is no way we can address more than + * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because + * offsets have to be representable in bytes); due to every cluster + * corresponding to one refcount entry, we are well below that limit */ + assert(entries < (UINT64_C(1) << (64 - 9))); + + /* Thanks to the assertion this will not overflow, because + * s->refcount_order < 7. + * (note: x << s->refcount_order == x * s->refcount_bits) */ + return DIV_ROUND_UP(entries << s->refcount_order, 8); +} + +/** + * Reallocates *array so that it can hold new_size entries. *size must contain + * the current number of entries in *array. If the reallocation fails, *array + * and *size will not be modified and -errno will be returned. If the + * reallocation is successful, *array will be set to the new buffer, *size + * will be set to new_size and 0 will be returned. The size of the reallocated + * refcount array buffer will be aligned to a cluster boundary, and the newly + * allocated area will be zeroed. + */ +static int realloc_refcount_array(BDRVQcowState *s, void **array, + int64_t *size, int64_t new_size) +{ + size_t old_byte_size, new_byte_size; + void *new_ptr; + + /* Round to clusters so the array can be directly written to disk */ + old_byte_size = size_to_clusters(s, refcount_array_byte_size(s, *size)) + * s->cluster_size; + new_byte_size = size_to_clusters(s, refcount_array_byte_size(s, new_size)) + * s->cluster_size; + + if (new_byte_size == old_byte_size) { + *size = new_size; + return 0; + } + + assert(new_byte_size > 0); + + new_ptr = g_try_realloc(*array, new_byte_size); + if (!new_ptr) { + return -ENOMEM; + } + + if (new_byte_size > old_byte_size) { + memset((void *)((uintptr_t)new_ptr + old_byte_size), 0, + new_byte_size - old_byte_size); + } + + *array = new_ptr; + *size = new_size; + + return 0; +} /* * Increases the refcount for a range of clusters in a given refcount table. @@ -1078,12 +1298,13 @@ fail: */ static int inc_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *refcount_table_size, int64_t offset, int64_t size) { BDRVQcowState *s = bs->opaque; - uint64_t start, last, cluster_offset, k; + uint64_t start, last, cluster_offset, k, refcount; + int ret; if (size <= 0) { return 0; @@ -1095,30 +1316,22 @@ static int inc_refcounts(BlockDriverState *bs, cluster_offset += s->cluster_size) { k = cluster_offset >> s->cluster_bits; if (k >= *refcount_table_size) { - int64_t old_refcount_table_size = *refcount_table_size; - uint16_t *new_refcount_table; - - *refcount_table_size = k + 1; - new_refcount_table = g_try_realloc(*refcount_table, - *refcount_table_size * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *refcount_table_size = old_refcount_table_size; + ret = realloc_refcount_array(s, refcount_table, + refcount_table_size, k + 1); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_refcount_table_size, 0, - (*refcount_table_size - old_refcount_table_size) * - sizeof(**refcount_table)); } - if (++(*refcount_table)[k] == 0) { + refcount = s->get_refcount(*refcount_table, k); + if (refcount == s->refcount_max) { fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 "\n", cluster_offset); res->corruptions++; + continue; } + s->set_refcount(*refcount_table, k, refcount + 1); } return 0; @@ -1138,8 +1351,9 @@ enum { * error occurred. */ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, int64_t *refcount_table_size, int64_t l2_offset, - int flags) + void **refcount_table, + int64_t *refcount_table_size, int64_t l2_offset, + int flags) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table, l2_entry; @@ -1256,7 +1470,7 @@ fail: */ static int check_refcounts_l1(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *refcount_table_size, int64_t l1_table_offset, int l1_size, int flags) @@ -1341,7 +1555,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, BDRVQcowState *s = bs->opaque; uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); int ret; - int refcount; + uint64_t refcount; int i, j; for (i = 0; i < s->l1_size; i++) { @@ -1353,14 +1567,15 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, continue; } - refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); - if (refcount < 0) { + ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { /* don't print message nor increment check_errors */ continue; } if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " - "l1_entry=%" PRIx64 " refcount=%d\n", + "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i, l1_entry, refcount); @@ -1395,15 +1610,16 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((cluster_type == QCOW2_CLUSTER_NORMAL) || ((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) { - refcount = qcow2_get_refcount(bs, - data_offset >> s->cluster_bits); - if (refcount < 0) { + ret = qcow2_get_refcount(bs, + data_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { /* don't print message nor increment check_errors */ continue; } if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED data cluster: " - "l2_entry=%" PRIx64 " refcount=%d\n", + "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", l2_entry, refcount); @@ -1453,7 +1669,7 @@ fail: */ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, - uint16_t **refcount_table, int64_t *nb_clusters) + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i, size; @@ -1478,8 +1694,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); if (fix & BDRV_FIX_ERRORS) { - int64_t old_nb_clusters = *nb_clusters; - uint16_t *new_refcount_table; + int64_t new_nb_clusters; if (offset > INT64_MAX - s->cluster_size) { ret = -EINVAL; @@ -1496,22 +1711,15 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, goto resize_fail; } - *nb_clusters = size_to_clusters(s, size); - assert(*nb_clusters >= old_nb_clusters); + new_nb_clusters = size_to_clusters(s, size); + assert(new_nb_clusters >= *nb_clusters); - new_refcount_table = g_try_realloc(*refcount_table, - *nb_clusters * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *nb_clusters = old_nb_clusters; + ret = realloc_refcount_array(s, refcount_table, + nb_clusters, new_nb_clusters); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_nb_clusters, 0, - (*nb_clusters - old_nb_clusters) * - sizeof(**refcount_table)); if (cluster >= *nb_clusters) { ret = -EINVAL; @@ -1546,9 +1754,10 @@ resize_fail: if (ret < 0) { return ret; } - if ((*refcount_table)[cluster] != 1) { + if (s->get_refcount(*refcount_table, cluster) != 1) { fprintf(stderr, "ERROR refcount block %" PRId64 - " refcount=%d\n", i, (*refcount_table)[cluster]); + " refcount=%" PRIu64 "\n", i, + s->get_refcount(*refcount_table, cluster)); res->corruptions++; *rebuild = true; } @@ -1563,7 +1772,7 @@ resize_fail: */ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, - uint16_t **refcount_table, int64_t *nb_clusters) + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i; @@ -1571,10 +1780,12 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, int ret; if (!*refcount_table) { - *refcount_table = g_try_new0(uint16_t, *nb_clusters); - if (*nb_clusters && *refcount_table == NULL) { + int64_t old_size = 0; + ret = realloc_refcount_array(s, refcount_table, + &old_size, *nb_clusters); + if (ret < 0) { res->check_errors++; - return -ENOMEM; + return ret; } } @@ -1625,22 +1836,23 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, int64_t *highest_cluster, - uint16_t *refcount_table, int64_t nb_clusters) + void *refcount_table, int64_t nb_clusters) { BDRVQcowState *s = bs->opaque; int64_t i; - int refcount1, refcount2, ret; + uint64_t refcount1, refcount2; + int ret; for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) { - refcount1 = qcow2_get_refcount(bs, i); - if (refcount1 < 0) { + ret = qcow2_get_refcount(bs, i, &refcount1); + if (ret < 0) { fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", - i, strerror(-refcount1)); + i, strerror(-ret)); res->check_errors++; continue; } - refcount2 = refcount_table[i]; + refcount2 = s->get_refcount(refcount_table, i); if (refcount1 > 0 || refcount2 > 0) { *highest_cluster = i; @@ -1657,7 +1869,8 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, num_fixed = &res->corruptions_fixed; } - fprintf(stderr, "%s cluster %" PRId64 " refcount=%d reference=%d\n", + fprintf(stderr, "%s cluster %" PRId64 " refcount=%" PRIu64 + " reference=%" PRIu64 "\n", num_fixed != NULL ? "Repairing" : refcount1 < refcount2 ? "ERROR" : "Leaked", @@ -1665,7 +1878,8 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, if (num_fixed) { ret = update_refcount(bs, i << s->cluster_bits, 1, - refcount2 - refcount1, + refcount_diff(refcount1, refcount2), + refcount1 > refcount2, QCOW2_DISCARD_ALWAYS); if (ret >= 0) { (*num_fixed)++; @@ -1697,7 +1911,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, */ static int64_t alloc_clusters_imrt(BlockDriverState *bs, int cluster_count, - uint16_t **refcount_table, + void **refcount_table, int64_t *imrt_nb_clusters, int64_t *first_free_cluster) { @@ -1705,6 +1919,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, int64_t cluster = *first_free_cluster, i; bool first_gap = true; int contiguous_free_clusters; + int ret; /* Starting at *first_free_cluster, find a range of at least cluster_count * continuously free clusters */ @@ -1713,7 +1928,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, contiguous_free_clusters < cluster_count; cluster++) { - if (!(*refcount_table)[cluster]) { + if (!s->get_refcount(*refcount_table, cluster)) { contiguous_free_clusters++; if (first_gap) { /* If this is the first free cluster found, update @@ -1734,34 +1949,24 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, /* If no such range could be found, grow the in-memory refcount table * accordingly to append free clusters at the end of the image */ if (contiguous_free_clusters < cluster_count) { - int64_t old_imrt_nb_clusters = *imrt_nb_clusters; - uint16_t *new_refcount_table; - /* contiguous_free_clusters clusters are already empty at the image end; * we need cluster_count clusters; therefore, we have to allocate * cluster_count - contiguous_free_clusters new clusters at the end of * the image (which is the current value of cluster; note that cluster * may exceed old_imrt_nb_clusters if *first_free_cluster pointed beyond * the image end) */ - *imrt_nb_clusters = cluster + cluster_count - contiguous_free_clusters; - new_refcount_table = g_try_realloc(*refcount_table, - *imrt_nb_clusters * - sizeof(**refcount_table)); - if (!new_refcount_table) { - *imrt_nb_clusters = old_imrt_nb_clusters; - return -ENOMEM; + ret = realloc_refcount_array(s, refcount_table, imrt_nb_clusters, + cluster + cluster_count + - contiguous_free_clusters); + if (ret < 0) { + return ret; } - *refcount_table = new_refcount_table; - - memset(*refcount_table + old_imrt_nb_clusters, 0, - (*imrt_nb_clusters - old_imrt_nb_clusters) * - sizeof(**refcount_table)); } /* Go back to the first free cluster */ cluster -= contiguous_free_clusters; for (i = 0; i < cluster_count; i++) { - (*refcount_table)[cluster + i] = 1; + s->set_refcount(*refcount_table, cluster + i, 1); } return cluster << s->cluster_bits; @@ -1777,7 +1982,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, */ static int rebuild_refcount_structure(BlockDriverState *bs, BdrvCheckResult *res, - uint16_t **refcount_table, + void **refcount_table, int64_t *nb_clusters) { BDRVQcowState *s = bs->opaque; @@ -1785,8 +1990,8 @@ static int rebuild_refcount_structure(BlockDriverState *bs, int64_t refblock_offset, refblock_start, refblock_index; uint32_t reftable_size = 0; uint64_t *on_disk_reftable = NULL; - uint16_t *on_disk_refblock; - int i, ret = 0; + void *on_disk_refblock; + int ret = 0; struct { uint64_t reftable_offset; uint32_t reftable_clusters; @@ -1796,7 +2001,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs, write_refblocks: for (; cluster < *nb_clusters; cluster++) { - if (!(*refcount_table)[cluster]) { + if (!s->get_refcount(*refcount_table, cluster)) { continue; } @@ -1869,17 +2074,13 @@ write_refblocks: goto fail; } - on_disk_refblock = qemu_blockalign0(bs->file, s->cluster_size); - for (i = 0; i < s->refcount_block_size && - refblock_start + i < *nb_clusters; i++) - { - on_disk_refblock[i] = - cpu_to_be16((*refcount_table)[refblock_start + i]); - } + /* The size of *refcount_table is always cluster-aligned, therefore the + * write operation will not overflow */ + on_disk_refblock = (void *)((char *) *refcount_table + + refblock_index * s->cluster_size); ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, - (void *)on_disk_refblock, s->cluster_sectors); - qemu_vfree(on_disk_refblock); + on_disk_refblock, s->cluster_sectors); if (ret < 0) { fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); goto fail; @@ -1974,7 +2175,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BDRVQcowState *s = bs->opaque; BdrvCheckResult pre_compare_res; int64_t size, highest_cluster, nb_clusters; - uint16_t *refcount_table = NULL; + void *refcount_table = NULL; bool rebuild = false; int ret; @@ -2023,7 +2224,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* Because the old reftable has been exchanged for a new one the * references have to be recalculated */ rebuild = false; - memset(refcount_table, 0, nb_clusters * sizeof(uint16_t)); + memset(refcount_table, 0, refcount_array_byte_size(s, nb_clusters)); ret = calculate_refcounts(bs, res, 0, &rebuild, &refcount_table, &nb_clusters); if (ret < 0) { diff --git a/block/qcow2.c b/block/qcow2.c index 50e0a94..8bfb094 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -677,13 +677,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } /* Check support for various header values */ - if (header.refcount_order != 4) { - report_unsupported(bs, errp, "%d bit reference counts", - 1 << header.refcount_order); - ret = -ENOTSUP; + if (header.refcount_order > 6) { + error_setg(errp, "Reference count entry width too large; may not " + "exceed 64 bits"); + ret = -EINVAL; goto fail; } s->refcount_order = header.refcount_order; + s->refcount_bits = 1 << s->refcount_order; + s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); + s->refcount_max += s->refcount_max - 1; if (header.crypt_method > QCOW_CRYPT_AES) { error_setg(errp, "Unsupported encryption method: %" PRIu32, @@ -1780,7 +1783,7 @@ static int preallocate(BlockDriverState *bs) static int qcow2_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, int flags, size_t cluster_size, PreallocMode prealloc, - QemuOpts *opts, int version, + QemuOpts *opts, int version, int refcount_order, Error **errp) { /* Calculate cluster_bits */ @@ -1813,9 +1816,21 @@ static int qcow2_create2(const char *filename, int64_t total_size, int ret; if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { + /* Note: The following calculation does not need to be exact; if it is a + * bit off, either some bytes will be "leaked" (which is fine) or we + * will need to increase the file size by some bytes (which is fine, + * too, as long as the bulk is allocated here). Therefore, using + * floating point arithmetic is fine. */ int64_t meta_size = 0; uint64_t nreftablee, nrefblocke, nl1e, nl2e; int64_t aligned_total_size = align_offset(total_size, cluster_size); + int refblock_bits, refblock_size; + /* refcount entry size in bytes */ + double rces = (1 << refcount_order) / 8.; + + /* see qcow2_open() */ + refblock_bits = cluster_bits - (refcount_order - 3); + refblock_size = 1 << refblock_bits; /* header: 1 cluster */ meta_size += cluster_size; @@ -1840,20 +1855,20 @@ static int qcow2_create2(const char *filename, int64_t total_size, * c = cluster size * y1 = number of refcount blocks entries * y2 = meta size including everything + * rces = refcount entry size in bytes * then, * y1 = (y2 + a)/c - * y2 = y1 * sizeof(u16) + y1 * sizeof(u16) * sizeof(u64) / c + m + * y2 = y1 * rces + y1 * rces * sizeof(u64) / c + m * we can get y1: - * y1 = (a + m) / (c - sizeof(u16) - sizeof(u16) * sizeof(u64) / c) + * y1 = (a + m) / (c - rces - rces * sizeof(u64) / c) */ - nrefblocke = (aligned_total_size + meta_size + cluster_size) / - (cluster_size - sizeof(uint16_t) - - 1.0 * sizeof(uint16_t) * sizeof(uint64_t) / cluster_size); - nrefblocke = align_offset(nrefblocke, cluster_size / sizeof(uint16_t)); - meta_size += nrefblocke * sizeof(uint16_t); + nrefblocke = (aligned_total_size + meta_size + cluster_size) + / (cluster_size - rces - rces * sizeof(uint64_t) + / cluster_size); + meta_size += DIV_ROUND_UP(nrefblocke, refblock_size) * cluster_size; /* total size of refcount tables */ - nreftablee = nrefblocke * sizeof(uint16_t) / cluster_size; + nreftablee = nrefblocke / refblock_size; nreftablee = align_offset(nreftablee, cluster_size / sizeof(uint64_t)); meta_size += nreftablee * sizeof(uint64_t); @@ -1889,7 +1904,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, .l1_size = cpu_to_be32(0), .refcount_table_offset = cpu_to_be64(cluster_size), .refcount_table_clusters = cpu_to_be32(1), - .refcount_order = cpu_to_be32(4), + .refcount_order = cpu_to_be32(refcount_order), .header_length = cpu_to_be32(sizeof(*header)), }; @@ -2008,6 +2023,8 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) size_t cluster_size = DEFAULT_CLUSTER_SIZE; PreallocMode prealloc; int version = 3; + uint64_t refcount_bits = 16; + int refcount_order; Error *local_err = NULL; int ret; @@ -2062,8 +2079,28 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) goto finish; } + refcount_bits = qemu_opt_get_number_del(opts, BLOCK_OPT_REFCOUNT_BITS, + refcount_bits); + if (refcount_bits > 64 || !is_power_of_2(refcount_bits)) { + error_setg(errp, "Refcount width must be a power of two and may not " + "exceed 64 bits"); + ret = -EINVAL; + goto finish; + } + + if (version < 3 && refcount_bits != 16) { + error_setg(errp, "Different refcount widths than 16 bits require " + "compatibility level 1.1 or above (use compat=1.1 or " + "greater)"); + ret = -EINVAL; + goto finish; + } + + refcount_order = ffs(refcount_bits) - 1; + ret = qcow2_create2(filename, size, backing_file, backing_fmt, flags, - cluster_size, prealloc, opts, version, &local_err); + cluster_size, prealloc, opts, version, refcount_order, + &local_err); if (local_err) { error_propagate(errp, local_err); } @@ -2479,7 +2516,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) }; if (s->qcow_version == 2) { *spec_info->qcow2 = (ImageInfoSpecificQCow2){ - .compat = g_strdup("0.10"), + .compat = g_strdup("0.10"), + .refcount_bits = s->refcount_bits, }; } else if (s->qcow_version == 3) { *spec_info->qcow2 = (ImageInfoSpecificQCow2){ @@ -2490,6 +2528,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) .corrupt = s->incompatible_features & QCOW2_INCOMPAT_CORRUPT, .has_corrupt = true, + .refcount_bits = s->refcount_bits, }; } @@ -2642,8 +2681,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, continue; } - if (!strcmp(desc->name, "compat")) { - compat = qemu_opt_get(opts, "compat"); + if (!strcmp(desc->name, BLOCK_OPT_COMPAT_LEVEL)) { + compat = qemu_opt_get(opts, BLOCK_OPT_COMPAT_LEVEL); if (!compat) { /* preserve default */ } else if (!strcmp(compat, "0.10")) { @@ -2654,33 +2693,37 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, fprintf(stderr, "Unknown compatibility level %s.\n", compat); return -EINVAL; } - } else if (!strcmp(desc->name, "preallocation")) { + } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { fprintf(stderr, "Cannot change preallocation mode.\n"); return -ENOTSUP; - } else if (!strcmp(desc->name, "size")) { - new_size = qemu_opt_get_size(opts, "size", 0); - } else if (!strcmp(desc->name, "backing_file")) { - backing_file = qemu_opt_get(opts, "backing_file"); - } else if (!strcmp(desc->name, "backing_fmt")) { - backing_format = qemu_opt_get(opts, "backing_fmt"); - } else if (!strcmp(desc->name, "encryption")) { - encrypt = qemu_opt_get_bool(opts, "encryption", s->crypt_method); + } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { + new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) { + backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { + backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); + } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) { + encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, + s->crypt_method); if (encrypt != !!s->crypt_method) { fprintf(stderr, "Changing the encryption flag is not " "supported.\n"); return -ENOTSUP; } - } else if (!strcmp(desc->name, "cluster_size")) { - cluster_size = qemu_opt_get_size(opts, "cluster_size", + } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { + cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size); if (cluster_size != s->cluster_size) { fprintf(stderr, "Changing the cluster size is not " "supported.\n"); return -ENOTSUP; } - } else if (!strcmp(desc->name, "lazy_refcounts")) { - lazy_refcounts = qemu_opt_get_bool(opts, "lazy_refcounts", + } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { + lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, lazy_refcounts); + } else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) { + error_report("Cannot change refcount entry width"); + return -ENOTSUP; } else { /* if this assertion fails, this probably means a new option was * added without having it covered here */ @@ -2850,6 +2893,12 @@ static QemuOptsList qcow2_create_opts = { .help = "Postpone refcount updates", .def_value_str = "off" }, + { + .name = BLOCK_OPT_REFCOUNT_BITS, + .type = QEMU_OPT_NUMBER, + .help = "Width of a reference count entry in bits", + .def_value_str = "16" + }, { /* end of list */ } } }; diff --git a/block/qcow2.h b/block/qcow2.h index 6e39a1b..aa6d367 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -213,6 +213,11 @@ typedef struct Qcow2DiscardRegion { QTAILQ_ENTRY(Qcow2DiscardRegion) next; } Qcow2DiscardRegion; +typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, + uint64_t index); +typedef void Qcow2SetRefcountFunc(void *refcount_array, + uint64_t index, uint64_t value); + typedef struct BDRVQcowState { int cluster_bits; int cluster_size; @@ -258,6 +263,11 @@ typedef struct BDRVQcowState { int qcow_version; bool use_lazy_refcounts; int refcount_order; + int refcount_bits; + uint64_t refcount_max; + + Qcow2GetRefcountFunc *get_refcount; + Qcow2SetRefcountFunc *set_refcount; bool discard_passthrough[QCOW2_DISCARD_MAX]; @@ -275,17 +285,6 @@ typedef struct BDRVQcowState { bool cache_discards; } BDRVQcowState; -/* XXX: use std qcow open function ? */ -typedef struct QCowCreateState { - int cluster_size; - int cluster_bits; - uint16_t *refcount_block; - uint64_t *refcount_table; - int64_t l1_table_offset; - int64_t refcount_table_offset; - int64_t refcount_block_offset; -} QCowCreateState; - struct QCowAIOCB; typedef struct Qcow2COWRegion { @@ -468,6 +467,11 @@ static inline uint64_t l2meta_cow_end(QCowL2Meta *m) + (m->cow_end.nb_sectors << BDRV_SECTOR_BITS); } +static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2) +{ + return r1 > r2 ? r1 - r2 : r2 - r1; +} + // FIXME Need qcow2_ prefix to global functions /* qcow2.c functions */ @@ -487,10 +491,12 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, int qcow2_refcount_init(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs); -int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index); +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount); int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, - int addend, enum qcow2_discard_type type); + uint64_t addend, bool decrease, + enum qcow2_discard_type type); int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, diff --git a/block/raw-posix.c b/block/raw-posix.c index b5f077a..f0b4488 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -56,6 +56,10 @@ #include <linux/cdrom.h> #include <linux/fd.h> #include <linux/fs.h> +#include <linux/hdreg.h> +#ifdef __s390__ +#include <asm/dasd.h> +#endif #ifndef FS_NOCOW_FL #define FS_NOCOW_FL 0x00800000 /* Do not cow file */ #endif @@ -218,39 +222,100 @@ static int raw_normalize_devicepath(const char **filename) } #endif -static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) +/* + * Get logical block size via ioctl. On success store it in @sector_size_p. + */ +static int probe_logical_blocksize(int fd, unsigned int *sector_size_p) { - BDRVRawState *s = bs->opaque; - char *buf; unsigned int sector_size; + bool success = false; - /* For /dev/sg devices the alignment is not really used. - With buffered I/O, we don't have any restrictions. */ - if (bs->sg || !s->needs_alignment) { - bs->request_alignment = 1; - s->buf_align = 1; - return; - } + errno = ENOTSUP; /* Try a few ioctls to get the right size */ - bs->request_alignment = 0; - s->buf_align = 0; - #ifdef BLKSSZGET if (ioctl(fd, BLKSSZGET, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; } #endif #ifdef DKIOCGETBLOCKSIZE if (ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; } #endif #ifdef DIOCGSECTORSIZE if (ioctl(fd, DIOCGSECTORSIZE, §or_size) >= 0) { - bs->request_alignment = sector_size; + *sector_size_p = sector_size; + success = true; + } +#endif + + return success ? 0 : -errno; +} + +/** + * Get physical block size of @fd. + * On success, store it in @blk_size and return 0. + * On failure, return -errno. + */ +static int probe_physical_blocksize(int fd, unsigned int *blk_size) +{ +#ifdef BLKPBSZGET + if (ioctl(fd, BLKPBSZGET, blk_size) < 0) { + return -errno; + } + return 0; +#else + return -ENOTSUP; +#endif +} + +/* Check if read is allowed with given memory buffer and length. + * + * This function is used to check O_DIRECT memory buffer and request alignment. + */ +static bool raw_is_io_aligned(int fd, void *buf, size_t len) +{ + ssize_t ret = pread(fd, buf, len, 0); + + if (ret >= 0) { + return true; + } + +#ifdef __linux__ + /* The Linux kernel returns EINVAL for misaligned O_DIRECT reads. Ignore + * other errors (e.g. real I/O error), which could happen on a failed + * drive, since we only care about probing alignment. + */ + if (errno != EINVAL) { + return true; } #endif + + return false; +} + +static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) +{ + BDRVRawState *s = bs->opaque; + char *buf; + + /* For /dev/sg devices the alignment is not really used. + With buffered I/O, we don't have any restrictions. */ + if (bs->sg || !s->needs_alignment) { + bs->request_alignment = 1; + s->buf_align = 1; + return; + } + + bs->request_alignment = 0; + s->buf_align = 0; + /* Let's try to use the logical blocksize for the alignment. */ + if (probe_logical_blocksize(fd, &bs->request_alignment) < 0) { + bs->request_alignment = 0; + } #ifdef CONFIG_XFS if (s->is_xfs) { struct dioattr da; @@ -267,7 +332,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) size_t align; buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE); for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { - if (pread(fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) { + if (raw_is_io_aligned(fd, buf + align, MAX_BLOCKSIZE)) { s->buf_align = align; break; } @@ -279,7 +344,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) size_t align; buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE); for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { - if (pread(fd, buf, align, 0) >= 0) { + if (raw_is_io_aligned(fd, buf, align)) { bs->request_alignment = align; break; } @@ -655,6 +720,86 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.opt_mem_alignment = s->buf_align; } +static int check_for_dasd(int fd) +{ +#ifdef BIODASDINFO2 + struct dasd_information2_t info = {0}; + + return ioctl(fd, BIODASDINFO2, &info); +#else + return -1; +#endif +} + +/** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz and return zero. + * On failure, return negative errno. + */ +static int hdev_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + BDRVRawState *s = bs->opaque; + int ret; + + /* If DASD, get blocksizes */ + if (check_for_dasd(s->fd) < 0) { + return -ENOTSUP; + } + ret = probe_logical_blocksize(s->fd, &bsz->log); + if (ret < 0) { + return ret; + } + return probe_physical_blocksize(s->fd, &bsz->phys); +} + +/** + * Try to get @bs's geometry: cyls, heads, sectors. + * On success, store them in @geo and return 0. + * On failure return -errno. + * (Allows block driver to assign default geometry values that guest sees) + */ +#ifdef __linux__ +static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + BDRVRawState *s = bs->opaque; + struct hd_geometry ioctl_geo = {0}; + uint32_t blksize; + + /* If DASD, get its geometry */ + if (check_for_dasd(s->fd) < 0) { + return -ENOTSUP; + } + if (ioctl(s->fd, HDIO_GETGEO, &ioctl_geo) < 0) { + return -errno; + } + /* HDIO_GETGEO may return success even though geo contains zeros + (e.g. certain multipath setups) */ + if (!ioctl_geo.heads || !ioctl_geo.sectors || !ioctl_geo.cylinders) { + return -ENOTSUP; + } + /* Do not return a geometry for partition */ + if (ioctl_geo.start != 0) { + return -ENOTSUP; + } + geo->heads = ioctl_geo.heads; + geo->sectors = ioctl_geo.sectors; + if (!probe_physical_blocksize(s->fd, &blksize)) { + /* overwrite cyls: HDIO_GETGEO result is incorrect for big drives */ + geo->cylinders = bdrv_nb_sectors(bs) / (blksize / BDRV_SECTOR_SIZE) + / (geo->heads * geo->sectors); + return 0; + } + geo->cylinders = ioctl_geo.cylinders; + + return 0; +} +#else /* __linux__ */ +static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + return -ENOTSUP; +} +#endif + static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb) { int ret; @@ -944,7 +1089,9 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) { +#if defined(CONFIG_FALLOCATE) || defined(CONFIG_XFS) BDRVRawState *s = aiocb->bs->opaque; +#endif if (aiocb->aio_type & QEMU_AIO_BLKDEV) { return handle_aiocb_write_zeroes_block(aiocb); @@ -2194,6 +2341,8 @@ static BlockDriver bdrv_host_device = { .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, + .bdrv_probe_blocksizes = hdev_probe_blocksizes, + .bdrv_probe_geometry = hdev_probe_geometry, .bdrv_detach_aio_context = raw_detach_aio_context, .bdrv_attach_aio_context = raw_attach_aio_context, diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 05b02c7..e3d2d04 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -235,6 +235,16 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) return 1; } +static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +{ + return bdrv_probe_blocksizes(bs->file, bsz); +} + +static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +{ + return bdrv_probe_geometry(bs->file, geo); +} + BlockDriver bdrv_raw = { .format_name = "raw", .bdrv_probe = &raw_probe, @@ -252,6 +262,8 @@ BlockDriver bdrv_raw = { .has_variable_length = true, .bdrv_get_info = &raw_get_info, .bdrv_refresh_limits = &raw_refresh_limits, + .bdrv_probe_blocksizes = &raw_probe_blocksizes, + .bdrv_probe_geometry = &raw_probe_geometry, .bdrv_is_inserted = &raw_is_inserted, .bdrv_media_changed = &raw_media_changed, .bdrv_eject = &raw_eject, diff --git a/block/sheepdog.c b/block/sheepdog.c index d17ee36..c14172c 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -37,6 +37,7 @@ #define SD_OP_READ_VDIS 0x15 #define SD_OP_FLUSH_VDI 0x16 #define SD_OP_DEL_VDI 0x17 +#define SD_OP_GET_CLUSTER_DEFAULT 0x18 #define SD_FLAG_CMD_WRITE 0x01 #define SD_FLAG_CMD_COW 0x02 @@ -91,6 +92,7 @@ #define SD_NR_VDIS (1U << 24) #define SD_DATA_OBJ_SIZE (UINT64_C(1) << 22) #define SD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * MAX_DATA_OBJS) +#define SD_DEFAULT_BLOCK_SIZE_SHIFT 22 /* * For erasure coding, we use at most SD_EC_MAX_STRIP for data strips and * (SD_EC_MAX_STRIP - 1) for parity strips @@ -167,7 +169,8 @@ typedef struct SheepdogVdiReq { uint32_t base_vdi_id; uint8_t copies; uint8_t copy_policy; - uint8_t reserved[2]; + uint8_t store_policy; + uint8_t block_size_shift; uint32_t snapid; uint32_t type; uint32_t pad[2]; @@ -186,6 +189,21 @@ typedef struct SheepdogVdiRsp { uint32_t pad[5]; } SheepdogVdiRsp; +typedef struct SheepdogClusterRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint8_t nr_copies; + uint8_t copy_policy; + uint8_t block_size_shift; + uint8_t __pad1; + uint32_t __pad2[6]; +} SheepdogClusterRsp; + typedef struct SheepdogInode { char name[SD_MAX_VDI_LEN]; char tag[SD_MAX_VDI_TAG_LEN]; @@ -527,6 +545,7 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, return acb; } +/* Return -EIO in case of error, file descriptor on success */ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) { int fd; @@ -546,11 +565,14 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) if (fd >= 0) { qemu_set_nonblock(fd); + } else { + fd = -EIO; } return fd; } +/* Return 0 on success and -errno in case of error */ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, unsigned int *wlen) { @@ -559,11 +581,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, ret = qemu_co_send(sockfd, hdr, sizeof(*hdr)); if (ret != sizeof(*hdr)) { error_report("failed to send a req, %s", strerror(errno)); + ret = -socket_error(); return ret; } ret = qemu_co_send(sockfd, data, *wlen); if (ret != *wlen) { + ret = -socket_error(); error_report("failed to send a req, %s", strerror(errno)); } @@ -638,6 +662,11 @@ out: srco->finished = true; } +/* + * Send the request to the sheep in a synchronous manner. + * + * Return 0 on success, -errno in case of error. + */ static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr, void *data, unsigned int *wlen, unsigned int *rlen) { @@ -1541,6 +1570,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, hdr.vdi_size = s->inode.vdi_size; hdr.copy_policy = s->inode.copy_policy; hdr.copies = s->inode.nr_copies; + hdr.block_size_shift = s->inode.block_size_shift; ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen); @@ -1566,9 +1596,12 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, static int sd_prealloc(const char *filename, Error **errp) { BlockDriverState *bs = NULL; + BDRVSheepdogState *base = NULL; + unsigned long buf_size; uint32_t idx, max_idx; + uint32_t object_size; int64_t vdi_size; - void *buf = g_malloc0(SD_DATA_OBJ_SIZE); + void *buf = NULL; int ret; ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, @@ -1582,18 +1615,24 @@ static int sd_prealloc(const char *filename, Error **errp) ret = vdi_size; goto out; } - max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE); + + base = bs->opaque; + object_size = (UINT32_C(1) << base->inode.block_size_shift); + buf_size = MIN(object_size, SD_DATA_OBJ_SIZE); + buf = g_malloc0(buf_size); + + max_idx = DIV_ROUND_UP(vdi_size, buf_size); for (idx = 0; idx < max_idx; idx++) { /* * The created image can be a cloned image, so we need to read * a data from the source image. */ - ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + ret = bdrv_pread(bs, idx * buf_size, buf, buf_size); if (ret < 0) { goto out; } - ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size); if (ret < 0) { goto out; } @@ -1666,6 +1705,27 @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt) return 0; } +static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt) +{ + struct SheepdogInode *inode = &s->inode; + uint64_t object_size; + int obj_order; + + object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0); + if (object_size) { + if ((object_size - 1) & object_size) { /* not a power of 2? */ + return -EINVAL; + } + obj_order = ffs(object_size) - 1; + if (obj_order < 20 || obj_order > 31) { + return -EINVAL; + } + inode->block_size_shift = (uint8_t)obj_order; + } + + return 0; +} + static int sd_create(const char *filename, QemuOpts *opts, Error **errp) { @@ -1676,6 +1736,7 @@ static int sd_create(const char *filename, QemuOpts *opts, BDRVSheepdogState *s; char tag[SD_MAX_VDI_TAG_LEN]; uint32_t snapid; + uint64_t max_vdi_size; bool prealloc = false; s = g_new0(BDRVSheepdogState, 1); @@ -1714,10 +1775,11 @@ static int sd_create(const char *filename, QemuOpts *opts, goto out; } } - - if (s->inode.vdi_size > SD_MAX_VDI_SIZE) { - error_setg(errp, "too big image size"); - ret = -EINVAL; + ret = parse_block_size_shift(s, opts); + if (ret < 0) { + error_setg(errp, "Invalid object_size." + " obect_size needs to be power of 2" + " and be limited from 2^20 to 2^31"); goto out; } @@ -1754,6 +1816,51 @@ static int sd_create(const char *filename, QemuOpts *opts, } s->aio_context = qemu_get_aio_context(); + + /* if block_size_shift is not specified, get cluster default value */ + if (s->inode.block_size_shift == 0) { + SheepdogVdiReq hdr; + SheepdogClusterRsp *rsp = (SheepdogClusterRsp *)&hdr; + Error *local_err = NULL; + int fd; + unsigned int wlen = 0, rlen = 0; + + fd = connect_to_sdog(s, &local_err); + if (fd < 0) { + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + ret = -EIO; + goto out; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = SD_OP_GET_CLUSTER_DEFAULT; + hdr.proto_ver = SD_PROTO_VER; + + ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, + NULL, &wlen, &rlen); + closesocket(fd); + if (ret) { + error_setg_errno(errp, -ret, "failed to get cluster default"); + goto out; + } + if (rsp->result == SD_RES_SUCCESS) { + s->inode.block_size_shift = rsp->block_size_shift; + } else { + s->inode.block_size_shift = SD_DEFAULT_BLOCK_SIZE_SHIFT; + } + } + + max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; + + if (s->inode.vdi_size > max_vdi_size) { + error_setg(errp, "An image is too large." + " The maximum image size is %"PRIu64 "GB", + max_vdi_size / 1024 / 1024 / 1024); + ret = -EINVAL; + goto out; + } + ret = do_sd_create(s, &vid, 0, errp); if (ret) { goto out; @@ -1823,11 +1930,13 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) BDRVSheepdogState *s = bs->opaque; int ret, fd; unsigned int datalen; + uint64_t max_vdi_size; + max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; if (offset < s->inode.vdi_size) { error_report("shrinking is not supported"); return -EINVAL; - } else if (offset > SD_MAX_VDI_SIZE) { + } else if (offset > max_vdi_size) { error_report("too big image size"); return -EINVAL; } @@ -2005,9 +2114,10 @@ static int coroutine_fn sd_co_rw_vector(void *p) SheepdogAIOCB *acb = p; int ret = 0; unsigned long len, done = 0, total = acb->nb_sectors * BDRV_SECTOR_SIZE; - unsigned long idx = acb->sector_num * BDRV_SECTOR_SIZE / SD_DATA_OBJ_SIZE; + unsigned long idx; + uint32_t object_size; uint64_t oid; - uint64_t offset = (acb->sector_num * BDRV_SECTOR_SIZE) % SD_DATA_OBJ_SIZE; + uint64_t offset; BDRVSheepdogState *s = acb->common.bs->opaque; SheepdogInode *inode = &s->inode; AIOReq *aio_req; @@ -2024,6 +2134,10 @@ static int coroutine_fn sd_co_rw_vector(void *p) } } + object_size = (UINT32_C(1) << inode->block_size_shift); + idx = acb->sector_num * BDRV_SECTOR_SIZE / object_size; + offset = (acb->sector_num * BDRV_SECTOR_SIZE) % object_size; + /* * Make sure we don't free the aiocb before we are done with all requests. * This additional reference is dropped at the end of this function. @@ -2037,7 +2151,7 @@ static int coroutine_fn sd_co_rw_vector(void *p) oid = vid_to_data_oid(inode->data_vdi_id[idx], idx); - len = MIN(total - done, SD_DATA_OBJ_SIZE - offset); + len = MIN(total - done, object_size - offset); switch (acb->aiocb_type) { case AIOCB_READ_UDATA: @@ -2061,7 +2175,7 @@ static int coroutine_fn sd_co_rw_vector(void *p) * We discard the object only when the whole object is * 1) allocated 2) trimmed. Otherwise, simply skip it. */ - if (len != SD_DATA_OBJ_SIZE || inode->data_vdi_id[idx] == 0) { + if (len != object_size || inode->data_vdi_id[idx] == 0) { goto done; } break; @@ -2225,9 +2339,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) ret = do_sd_create(s, &new_vid, 1, &local_err); if (ret < 0) { - error_report_err(local_err); - error_report("failed to create inode for snapshot. %s", - strerror(errno)); + error_report("failed to create inode for snapshot: %s", + error_get_pretty(local_err)); goto cleanup; } @@ -2414,6 +2527,7 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, uint64_t offset; uint32_t vdi_index; uint32_t vdi_id = load ? s->inode.parent_vdi_id : s->inode.vdi_id; + uint32_t object_size = (UINT32_C(1) << s->inode.block_size_shift); fd = connect_to_sdog(s, &local_err); if (fd < 0) { @@ -2422,10 +2536,10 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, } while (remaining) { - vdi_index = pos / SD_DATA_OBJ_SIZE; - offset = pos % SD_DATA_OBJ_SIZE; + vdi_index = pos / object_size; + offset = pos % object_size; - data_len = MIN(remaining, SD_DATA_OBJ_SIZE - offset); + data_len = MIN(remaining, object_size - offset); vmstate_oid = vid_to_vmstate_oid(vdi_id, vdi_index); @@ -2512,10 +2626,11 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, { BDRVSheepdogState *s = bs->opaque; SheepdogInode *inode = &s->inode; + uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); uint64_t offset = sector_num * BDRV_SECTOR_SIZE; - unsigned long start = offset / SD_DATA_OBJ_SIZE, + unsigned long start = offset / object_size, end = DIV_ROUND_UP((sector_num + nb_sectors) * - BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE); + BDRV_SECTOR_SIZE, object_size); unsigned long idx; int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; @@ -2534,7 +2649,7 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, } } - *pnum = (idx - start) * SD_DATA_OBJ_SIZE / BDRV_SECTOR_SIZE; + *pnum = (idx - start) * object_size / BDRV_SECTOR_SIZE; if (*pnum > nb_sectors) { *pnum = nb_sectors; } @@ -2545,14 +2660,15 @@ static int64_t sd_get_allocated_file_size(BlockDriverState *bs) { BDRVSheepdogState *s = bs->opaque; SheepdogInode *inode = &s->inode; - unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, SD_DATA_OBJ_SIZE); + uint32_t object_size = (UINT32_C(1) << inode->block_size_shift); + unsigned long i, last = DIV_ROUND_UP(inode->vdi_size, object_size); uint64_t size = 0; for (i = 0; i < last; i++) { if (inode->data_vdi_id[i] == 0) { continue; } - size += SD_DATA_OBJ_SIZE; + size += object_size; } return size; } @@ -2581,6 +2697,11 @@ static QemuOptsList sd_create_opts = { .type = QEMU_OPT_STRING, .help = "Redundancy of the image" }, + { + .name = BLOCK_OPT_OBJECT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Object size of the image" + }, { /* end of list */ } } }; diff --git a/block/vdi.c b/block/vdi.c index 74030c6..53bd02f 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -53,6 +53,7 @@ #include "block/block_int.h" #include "qemu/module.h" #include "migration/migration.h" +#include "block/coroutine.h" #if defined(CONFIG_UUID) #include <uuid/uuid.h> @@ -196,6 +197,8 @@ typedef struct { /* VDI header (converted to host endianness). */ VdiHeader header; + CoMutex write_lock; + Error *migration_blocker; } BDRVVdiState; @@ -504,6 +507,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, "vdi", bdrv_get_device_name(bs), "live migration"); migrate_add_blocker(s->migration_blocker); + qemu_co_mutex_init(&s->write_lock); + return 0; fail_free_bmap: @@ -639,11 +644,31 @@ static int vdi_co_write(BlockDriverState *bs, buf, n_sectors * SECTOR_SIZE); memset(block + (sector_in_block + n_sectors) * SECTOR_SIZE, 0, (s->block_sectors - n_sectors - sector_in_block) * SECTOR_SIZE); + + /* Note that this coroutine does not yield anywhere from reading the + * bmap entry until here, so in regards to all the coroutines trying + * to write to this cluster, the one doing the allocation will + * always be the first to try to acquire the lock. + * Therefore, it is also the first that will actually be able to + * acquire the lock and thus the padded cluster is written before + * the other coroutines can write to the affected area. */ + qemu_co_mutex_lock(&s->write_lock); ret = bdrv_write(bs->file, offset, block, s->block_sectors); + qemu_co_mutex_unlock(&s->write_lock); } else { uint64_t offset = s->header.offset_data / SECTOR_SIZE + (uint64_t)bmap_entry * s->block_sectors + sector_in_block; + qemu_co_mutex_lock(&s->write_lock); + /* This lock is only used to make sure the following write operation + * is executed after the write issued by the coroutine allocating + * this cluster, therefore we do not need to keep it locked. + * As stated above, the allocating coroutine will always try to lock + * the mutex before all the other concurrent accesses to that + * cluster, therefore at this point we can be absolutely certain + * that that write operation has returned (there may be other writes + * in flight, but they do not concern this very operation). */ + qemu_co_mutex_unlock(&s->write_lock); ret = bdrv_write(bs->file, offset, buf, n_sectors); } diff --git a/block/vpc.c b/block/vpc.c index 46803b1..1533b6a 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -597,6 +597,51 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num, return ret; } +static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) +{ + BDRVVPCState *s = bs->opaque; + VHDFooter *footer = (VHDFooter*) s->footer_buf; + int64_t start, offset, next; + bool allocated; + int n; + + if (be32_to_cpu(footer->type) == VHD_FIXED) { + *pnum = nb_sectors; + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | + (sector_num << BDRV_SECTOR_BITS); + } + + offset = get_sector_offset(bs, sector_num, 0); + start = offset; + allocated = (offset != -1); + *pnum = 0; + + do { + /* All sectors in a block are contiguous (without using the bitmap) */ + n = ROUND_UP(sector_num + 1, s->block_size / BDRV_SECTOR_SIZE) + - sector_num; + n = MIN(n, nb_sectors); + + *pnum += n; + sector_num += n; + nb_sectors -= n; + next = start + (*pnum * BDRV_SECTOR_SIZE); + + if (nb_sectors == 0) { + break; + } + + offset = get_sector_offset(bs, sector_num, 0); + } while ((allocated && offset == next) || (!allocated && offset == -1)); + + if (allocated) { + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + } else { + return 0; + } +} + /* * Calculates the number of cylinders, heads and sectors per cylinder * based on a given number of sectors. This is the algorithm described @@ -801,6 +846,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) } total_sectors = (int64_t) cyls * heads * secs_per_cyl; + total_size = total_sectors * BDRV_SECTOR_SIZE; /* Prepare the Hard Disk Footer */ memset(buf, 0, 1024); @@ -822,13 +868,8 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) /* Version of Virtual PC 2007 */ footer->major = cpu_to_be16(0x0005); footer->minor = cpu_to_be16(0x0003); - if (disk_type == VHD_DYNAMIC) { - footer->orig_size = cpu_to_be64(total_sectors * 512); - footer->size = cpu_to_be64(total_sectors * 512); - } else { - footer->orig_size = cpu_to_be64(total_size); - footer->size = cpu_to_be64(total_size); - } + footer->orig_size = cpu_to_be64(total_size); + footer->size = cpu_to_be64(total_size); footer->cyls = cpu_to_be16(cyls); footer->heads = heads; footer->secs_per_cyl = secs_per_cyl; @@ -907,8 +948,9 @@ static BlockDriver bdrv_vpc = { .bdrv_reopen_prepare = vpc_reopen_prepare, .bdrv_create = vpc_create, - .bdrv_read = vpc_co_read, - .bdrv_write = vpc_co_write, + .bdrv_read = vpc_co_read, + .bdrv_write = vpc_co_write, + .bdrv_co_get_block_status = vpc_co_get_block_status, .bdrv_get_info = vpc_get_info, diff --git a/bsd-user/main.c b/bsd-user/main.c index 0e8c26c..1bb2754 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -908,12 +908,12 @@ int main(int argc, char **argv) cpu_exec_init_all(); /* NOTE: we need to init the CPU at this stage to get qemu_host_page_size */ - env = cpu_init(cpu_model); - if (!env) { + cpu = cpu_init(cpu_model); + if (!cpu) { fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - cpu = ENV_GET_CPU(env); + env = cpu->env_ptr; #if defined(TARGET_SPARC) || defined(TARGET_PPC) cpu_reset(cpu); #endif diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 149ae1b..87d4e34 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -80,6 +80,10 @@ CONFIG_NSERIES=y CONFIG_REALVIEW=y CONFIG_ZAURUS=y CONFIG_ZYNQ=y +CONFIG_STM32F2XX_TIMER=y +CONFIG_STM32F2XX_USART=y +CONFIG_STM32F2XX_SYSCFG=y +CONFIG_STM32F205_SOC=y CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y @@ -551,6 +551,7 @@ void cpu_exec_init(CPUArchState *env) #ifndef CONFIG_USER_ONLY cpu->as = &address_space_memory; cpu->thread_id = qemu_get_thread_id(); + cpu_reload_memory_map(cpu); #endif QTAILQ_INSERT_TAIL(&cpus, cpu, node); #if defined(CONFIG_USER_ONLY) diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 6088e53..2577f68 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -3,8 +3,10 @@ obj-$(CONFIG_DIGIC) += digic_boards.o obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o +obj-y += netduino2.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o obj-y += omap1.o omap2.o strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o +obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 949ae1e..cb609cd 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -406,16 +406,39 @@ static int icp_pic_init(SysBusDevice *sbd) /* CP control registers. */ +#define TYPE_ICP_CONTROL_REGS "icp-ctrl-regs" +#define ICP_CONTROL_REGS(obj) \ + OBJECT_CHECK(ICPCtrlRegsState, (obj), TYPE_ICP_CONTROL_REGS) + +typedef struct ICPCtrlRegsState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + + qemu_irq mmc_irq; + uint32_t intreg_state; +} ICPCtrlRegsState; + +#define ICP_GPIO_MMC_WPROT "mmc-wprot" +#define ICP_GPIO_MMC_CARDIN "mmc-cardin" + +#define ICP_INTREG_WPROT (1 << 0) +#define ICP_INTREG_CARDIN (1 << 3) + static uint64_t icp_control_read(void *opaque, hwaddr offset, unsigned size) { + ICPCtrlRegsState *s = opaque; + switch (offset >> 2) { case 0: /* CP_IDFIELD */ return 0x41034003; case 1: /* CP_FLASHPROG */ return 0; case 2: /* CP_INTREG */ - return 0; + return s->intreg_state; case 3: /* CP_DECODE */ return 0x11; default: @@ -427,9 +450,14 @@ static uint64_t icp_control_read(void *opaque, hwaddr offset, static void icp_control_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { + ICPCtrlRegsState *s = opaque; + switch (offset >> 2) { - case 1: /* CP_FLASHPROG */ case 2: /* CP_INTREG */ + s->intreg_state &= ~(value & ICP_INTREG_CARDIN); + qemu_set_irq(s->mmc_irq, !!(s->intreg_state & ICP_INTREG_CARDIN)); + break; + case 1: /* CP_FLASHPROG */ case 3: /* CP_DECODE */ /* Nothing interesting implemented yet. */ break; @@ -444,15 +472,41 @@ static const MemoryRegionOps icp_control_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void icp_control_init(hwaddr base) +static void icp_control_mmc_wprot(void *opaque, int line, int level) { - MemoryRegion *io; + ICPCtrlRegsState *s = opaque; - io = (MemoryRegion *)g_malloc0(sizeof(MemoryRegion)); - memory_region_init_io(io, NULL, &icp_control_ops, NULL, - "control", 0x00800000); - memory_region_add_subregion(get_system_memory(), base, io); - /* ??? Save/restore. */ + s->intreg_state &= ~ICP_INTREG_WPROT; + if (level) { + s->intreg_state |= ICP_INTREG_WPROT; + } +} + +static void icp_control_mmc_cardin(void *opaque, int line, int level) +{ + ICPCtrlRegsState *s = opaque; + + /* line is released by writing to CP_INTREG */ + if (level) { + s->intreg_state |= ICP_INTREG_CARDIN; + qemu_set_irq(s->mmc_irq, 1); + } +} + +static void icp_control_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ICPCtrlRegsState *s = ICP_CONTROL_REGS(obj); + DeviceState *dev = DEVICE(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &icp_control_ops, s, + "icp_ctrl_regs", 0x00800000); + sysbus_init_mmio(sbd, &s->iomem); + + qdev_init_gpio_in_named(dev, icp_control_mmc_wprot, ICP_GPIO_MMC_WPROT, 1); + qdev_init_gpio_in_named(dev, icp_control_mmc_cardin, + ICP_GPIO_MMC_CARDIN, 1); + sysbus_init_irq(sbd, &s->mmc_irq); } @@ -477,7 +531,7 @@ static void integratorcp_init(MachineState *machine) MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *ram_alias = g_new(MemoryRegion, 1); qemu_irq pic[32]; - DeviceState *dev; + DeviceState *dev, *sic, *icp; int i; Error *err = NULL; @@ -535,17 +589,24 @@ static void integratorcp_init(MachineState *machine) for (i = 0; i < 32; i++) { pic[i] = qdev_get_gpio_in(dev, i); } - sysbus_create_simple(TYPE_INTEGRATOR_PIC, 0xca000000, pic[26]); + sic = sysbus_create_simple(TYPE_INTEGRATOR_PIC, 0xca000000, pic[26]); sysbus_create_varargs("integrator_pit", 0x13000000, pic[5], pic[6], pic[7], NULL); sysbus_create_simple("pl031", 0x15000000, pic[8]); sysbus_create_simple("pl011", 0x16000000, pic[1]); sysbus_create_simple("pl011", 0x17000000, pic[2]); - icp_control_init(0xcb000000); + icp = sysbus_create_simple(TYPE_ICP_CONTROL_REGS, 0xcb000000, + qdev_get_gpio_in(sic, 3)); sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]); sysbus_create_simple("pl050_mouse", 0x19000000, pic[4]); sysbus_create_simple(TYPE_INTEGRATOR_DEBUG, 0x1a000000, 0); - sysbus_create_varargs("pl181", 0x1c000000, pic[23], pic[24], NULL); + + dev = sysbus_create_varargs("pl181", 0x1c000000, pic[23], pic[24], NULL); + qdev_connect_gpio_out(dev, 0, + qdev_get_gpio_in_named(icp, ICP_GPIO_MMC_WPROT, 0)); + qdev_connect_gpio_out(dev, 1, + qdev_get_gpio_in_named(icp, ICP_GPIO_MMC_CARDIN, 0)); + if (nd_table[0].used) smc91c111_init(&nd_table[0], 0xc8000000, pic[27]); @@ -606,10 +667,18 @@ static const TypeInfo icp_pic_info = { .class_init = icp_pic_class_init, }; +static const TypeInfo icp_ctrl_regs_info = { + .name = TYPE_ICP_CONTROL_REGS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ICPCtrlRegsState), + .instance_init = icp_control_init, +}; + static void integratorcp_register_types(void) { type_register_static(&icp_pic_info); type_register_static(&core_info); + type_register_static(&icp_ctrl_regs_info); } type_init(integratorcp_register_types) diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c new file mode 100644 index 0000000..8f26780 --- /dev/null +++ b/hw/arm/netduino2.c @@ -0,0 +1,57 @@ +/* + * Netduino 2 Machine Model + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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 "hw/boards.h" +#include "qemu/error-report.h" +#include "hw/arm/stm32f205_soc.h" + +static void netduino2_init(MachineState *machine) +{ + DeviceState *dev; + Error *err = NULL; + + dev = qdev_create(NULL, TYPE_STM32F205_SOC); + if (machine->kernel_filename) { + qdev_prop_set_string(dev, "kernel-filename", machine->kernel_filename); + } + qdev_prop_set_string(dev, "cpu-model", "cortex-m3"); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err != NULL) { + error_report("%s", error_get_pretty(err)); + exit(1); + } +} + +static QEMUMachine netduino2_machine = { + .name = "netduino2", + .desc = "Netduino 2 Machine", + .init = netduino2_init, +}; + +static void netduino2_machine_init(void) +{ + qemu_register_machine(&netduino2_machine); +} + +machine_init(netduino2_machine_init); diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c new file mode 100644 index 0000000..0f3bdc7 --- /dev/null +++ b/hw/arm/stm32f205_soc.c @@ -0,0 +1,160 @@ +/* + * STM32F205 SoC + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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 "hw/arm/arm.h" +#include "exec/address-spaces.h" +#include "hw/arm/stm32f205_soc.h" + +/* At the moment only Timer 2 to 5 are modelled */ +static const uint32_t timer_addr[STM_NUM_TIMERS] = { 0x40000000, 0x40000400, + 0x40000800, 0x40000C00 }; +static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40011000, 0x40004400, + 0x40004800, 0x40004C00, 0x40005000, 0x40011400 }; + +static const int timer_irq[STM_NUM_TIMERS] = {28, 29, 30, 50}; +static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39, 52, 53, 71}; + +static void stm32f205_soc_initfn(Object *obj) +{ + STM32F205State *s = STM32F205_SOC(obj); + int i; + + object_initialize(&s->syscfg, sizeof(s->syscfg), TYPE_STM32F2XX_SYSCFG); + qdev_set_parent_bus(DEVICE(&s->syscfg), sysbus_get_default()); + + for (i = 0; i < STM_NUM_USARTS; i++) { + object_initialize(&s->usart[i], sizeof(s->usart[i]), + TYPE_STM32F2XX_USART); + qdev_set_parent_bus(DEVICE(&s->usart[i]), sysbus_get_default()); + } + + for (i = 0; i < STM_NUM_TIMERS; i++) { + object_initialize(&s->timer[i], sizeof(s->timer[i]), + TYPE_STM32F2XX_TIMER); + qdev_set_parent_bus(DEVICE(&s->timer[i]), sysbus_get_default()); + } +} + +static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) +{ + STM32F205State *s = STM32F205_SOC(dev_soc); + DeviceState *syscfgdev, *usartdev, *timerdev; + SysBusDevice *syscfgbusdev, *usartbusdev, *timerbusdev; + qemu_irq *pic; + Error *err = NULL; + int i; + + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *sram = g_new(MemoryRegion, 1); + MemoryRegion *flash = g_new(MemoryRegion, 1); + MemoryRegion *flash_alias = g_new(MemoryRegion, 1); + + memory_region_init_ram(flash, NULL, "STM32F205.flash", FLASH_SIZE, + &error_abort); + memory_region_init_alias(flash_alias, NULL, "STM32F205.flash.alias", + flash, 0, FLASH_SIZE); + + vmstate_register_ram_global(flash); + + memory_region_set_readonly(flash, true); + memory_region_set_readonly(flash_alias, true); + + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash); + memory_region_add_subregion(system_memory, 0, flash_alias); + + memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE, + &error_abort); + vmstate_register_ram_global(sram); + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); + + pic = armv7m_init(get_system_memory(), FLASH_SIZE, 96, + s->kernel_filename, s->cpu_model); + + /* System configuration controller */ + syscfgdev = DEVICE(&s->syscfg); + object_property_set_bool(OBJECT(&s->syscfg), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + syscfgbusdev = SYS_BUS_DEVICE(syscfgdev); + sysbus_mmio_map(syscfgbusdev, 0, 0x40013800); + sysbus_connect_irq(syscfgbusdev, 0, pic[71]); + + /* Attach UART (uses USART registers) and USART controllers */ + for (i = 0; i < STM_NUM_USARTS; i++) { + usartdev = DEVICE(&(s->usart[i])); + object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + usartbusdev = SYS_BUS_DEVICE(usartdev); + sysbus_mmio_map(usartbusdev, 0, usart_addr[i]); + sysbus_connect_irq(usartbusdev, 0, pic[usart_irq[i]]); + } + + /* Timer 2 to 5 */ + for (i = 0; i < STM_NUM_TIMERS; i++) { + timerdev = DEVICE(&(s->timer[i])); + qdev_prop_set_uint64(timerdev, "clock-frequency", 1000000000); + object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + timerbusdev = SYS_BUS_DEVICE(timerdev); + sysbus_mmio_map(timerbusdev, 0, timer_addr[i]); + sysbus_connect_irq(timerbusdev, 0, pic[timer_irq[i]]); + } +} + +static Property stm32f205_soc_properties[] = { + DEFINE_PROP_STRING("kernel-filename", STM32F205State, kernel_filename), + DEFINE_PROP_STRING("cpu-model", STM32F205State, cpu_model), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32f205_soc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = stm32f205_soc_realize; + dc->props = stm32f205_soc_properties; +} + +static const TypeInfo stm32f205_soc_info = { + .name = TYPE_STM32F205_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F205State), + .instance_init = stm32f205_soc_initfn, + .class_init = stm32f205_soc_class_init, +}; + +static void stm32f205_soc_types(void) +{ + type_register_static(&stm32f205_soc_info); +} + +type_init(stm32f205_soc_types) diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 5933454..8496c16 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -515,9 +515,9 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, { DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); - if (di && qdev_prop_set_drive(dev, "drive", - blk_by_legacy_dinfo(di))) { - abort(); + if (di) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di), + &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 69f51ac..9072bc2 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -522,9 +522,9 @@ static void create_one_flash(const char *name, hwaddr flashbase, DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); const uint64_t sectorlength = 256 * 1024; - if (dinfo && qdev_prop_set_drive(dev, "drive", - blk_by_legacy_dinfo(dinfo))) { - abort(); + if (dinfo) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", flashsize / sectorlength); @@ -758,6 +758,7 @@ static void machvirt_init(MachineState *machine) CPUClass *cc = CPU_CLASS(oc); Object *cpuobj; Error *err = NULL; + char *cpuopts = g_strdup(cpustr[1]); if (!oc) { fprintf(stderr, "Unable to find CPU definition\n"); @@ -766,7 +767,8 @@ static void machvirt_init(MachineState *machine) cpuobj = object_new(object_class_get_name(oc)); /* Handle any CPU options specified by the user */ - cc->parse_features(CPU(cpuobj), cpustr[1], &err); + cc->parse_features(CPU(cpuobj), cpuopts, &err); + g_free(cpuopts); if (err) { error_report("%s", error_get_pretty(err)); exit(1); diff --git a/hw/block/block.c b/hw/block/block.c index a625773..f7243e5 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -25,6 +25,30 @@ void blkconf_serial(BlockConf *conf, char **serial) } } +void blkconf_blocksizes(BlockConf *conf) +{ + BlockBackend *blk = conf->blk; + BlockSizes blocksizes; + int backend_ret; + + backend_ret = blk_probe_blocksizes(blk, &blocksizes); + /* fill in detected values if they are not defined via qemu command line */ + if (!conf->physical_block_size) { + if (!backend_ret) { + conf->physical_block_size = blocksizes.phys; + } else { + conf->physical_block_size = BDRV_SECTOR_SIZE; + } + } + if (!conf->logical_block_size) { + if (!backend_ret) { + conf->logical_block_size = blocksizes.log; + } else { + conf->logical_block_size = BDRV_SECTOR_SIZE; + } + } +} + void blkconf_geometry(BlockConf *conf, int *ptrans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp) diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index 6fcf74d..b187878 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -121,8 +121,16 @@ void hd_geometry_guess(BlockBackend *blk, int *ptrans) { int cylinders, heads, secs, translation; + HDGeometry geo; - if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { + /* Try to probe the backing device geometry, otherwise fallback + to the old logic. (as of 12/2014 probing only succeeds on DASDs) */ + if (blk_probe_geometry(blk, &geo) == 0) { + *pcyls = geo.cylinders; + *psecs = geo.sectors; + *pheads = geo.heads; + translation = BIOS_ATA_TRANSLATION_NONE; + } else if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { /* no LCHS guess: use a standard physical disk geometry */ guess_chs_for_size(blk, pcyls, pheads, psecs); translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs); diff --git a/hw/block/nvme.c b/hw/block/nvme.c index ce079ae..0f3dfb9 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -765,6 +765,7 @@ static int nvme_init(PCIDevice *pci_dev) if (!n->serial) { return -1; } + blkconf_blocksizes(&n->conf); pci_conf = pci_dev->config; pci_conf[PCI_INTERRUPT_PIN] = 1; diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 89d380e..d282695 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -969,8 +969,8 @@ pflash_t *pflash_cfi01_register(hwaddr base, { DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); - if (blk && qdev_prop_set_drive(dev, "drive", blk)) { - abort(); + if (blk) { + qdev_prop_set_drive(dev, "drive", blk, &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); qdev_prop_set_uint64(dev, "sector-length", sector_len); diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 389b4aa..074a005 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -773,8 +773,8 @@ pflash_t *pflash_cfi02_register(hwaddr base, { DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02); - if (blk && qdev_prop_set_drive(dev, "drive", blk)) { - abort(); + if (blk) { + qdev_prop_set_drive(dev, "drive", blk, &error_abort); } qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); qdev_prop_set_uint32(dev, "sector-length", sector_len); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 1e5b918..000c38d 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -201,6 +201,7 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) #ifdef __linux__ int i; VirtIOBlockIoctlReq *ioctl_req; + BlockAIOCB *acb; #endif /* @@ -278,8 +279,13 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) ioctl_req->hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; ioctl_req->hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; - blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, - virtio_blk_ioctl_complete, ioctl_req); + acb = blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, + virtio_blk_ioctl_complete, ioctl_req); + if (!acb) { + g_free(ioctl_req); + status = VIRTIO_BLK_S_UNSUPP; + goto fail; + } return -EINPROGRESS; #else abort(); @@ -591,12 +597,6 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) if (mrb.num_reqs) { virtio_blk_submit_multireq(s->blk, &mrb); } - - /* - * FIXME: Want to check for completions before returning to guest mode, - * so cached reads and writes are reported as quickly as possible. But - * that should be done in the generic block layer. - */ } static void virtio_blk_dma_restart_bh(void *opaque) @@ -884,6 +884,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } + blkconf_blocksizes(&conf->conf); virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config)); diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 317385d..5931cc8 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -15,6 +15,7 @@ obj-$(CONFIG_OMAP) += omap_uart.o obj-$(CONFIG_SH4) += sh_serial.o obj-$(CONFIG_PSERIES) += spapr_vty.o obj-$(CONFIG_DIGIC) += digic-uart.o +obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c new file mode 100644 index 0000000..260b053 --- /dev/null +++ b/hw/char/stm32f2xx_usart.c @@ -0,0 +1,229 @@ +/* + * STM32F2XX USART + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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 "hw/char/stm32f2xx_usart.h" + +#ifndef STM_USART_ERR_DEBUG +#define STM_USART_ERR_DEBUG 0 +#endif + +#define DB_PRINT_L(lvl, fmt, args...) do { \ + if (STM_USART_ERR_DEBUG >= lvl) { \ + qemu_log("%s: " fmt, __func__, ## args); \ + } \ +} while (0); + +#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) + +static int stm32f2xx_usart_can_receive(void *opaque) +{ + STM32F2XXUsartState *s = opaque; + + if (!(s->usart_sr & USART_SR_RXNE)) { + return 1; + } + + return 0; +} + +static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) +{ + STM32F2XXUsartState *s = opaque; + + s->usart_dr = *buf; + + if (!(s->usart_cr1 & USART_CR1_UE && s->usart_cr1 & USART_CR1_RE)) { + /* USART not enabled - drop the chars */ + DB_PRINT("Dropping the chars\n"); + return; + } + + s->usart_sr |= USART_SR_RXNE; + + if (s->usart_cr1 & USART_CR1_RXNEIE) { + qemu_set_irq(s->irq, 1); + } + + DB_PRINT("Receiving: %c\n", s->usart_dr); +} + +static void stm32f2xx_usart_reset(DeviceState *dev) +{ + STM32F2XXUsartState *s = STM32F2XX_USART(dev); + + s->usart_sr = USART_SR_RESET; + s->usart_dr = 0x00000000; + s->usart_brr = 0x00000000; + s->usart_cr1 = 0x00000000; + s->usart_cr2 = 0x00000000; + s->usart_cr3 = 0x00000000; + s->usart_gtpr = 0x00000000; + + qemu_set_irq(s->irq, 0); +} + +static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, + unsigned int size) +{ + STM32F2XXUsartState *s = opaque; + uint64_t retvalue; + + DB_PRINT("Read 0x%"HWADDR_PRIx"\n", addr); + + switch (addr) { + case USART_SR: + retvalue = s->usart_sr; + s->usart_sr &= ~USART_SR_TC; + if (s->chr) { + qemu_chr_accept_input(s->chr); + } + return retvalue; + case USART_DR: + DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr); + s->usart_sr |= USART_SR_TXE; + s->usart_sr &= ~USART_SR_RXNE; + if (s->chr) { + qemu_chr_accept_input(s->chr); + } + qemu_set_irq(s->irq, 0); + return s->usart_dr & 0x3FF; + case USART_BRR: + return s->usart_brr; + case USART_CR1: + return s->usart_cr1; + case USART_CR2: + return s->usart_cr2; + case USART_CR3: + return s->usart_cr3; + case USART_GTPR: + return s->usart_gtpr; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + return 0; + } + + return 0; +} + +static void stm32f2xx_usart_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + STM32F2XXUsartState *s = opaque; + uint32_t value = val64; + unsigned char ch; + + DB_PRINT("Write 0x%" PRIx32 ", 0x%"HWADDR_PRIx"\n", value, addr); + + switch (addr) { + case USART_SR: + if (value <= 0x3FF) { + s->usart_sr = value; + } else { + s->usart_sr &= value; + } + if (!(s->usart_sr & USART_SR_RXNE)) { + qemu_set_irq(s->irq, 0); + } + return; + case USART_DR: + if (value < 0xF000) { + ch = value; + if (s->chr) { + qemu_chr_fe_write_all(s->chr, &ch, 1); + } + s->usart_sr |= USART_SR_TC; + s->usart_sr &= ~USART_SR_TXE; + } + return; + case USART_BRR: + s->usart_brr = value; + return; + case USART_CR1: + s->usart_cr1 = value; + if (s->usart_cr1 & USART_CR1_RXNEIE && + s->usart_sr & USART_SR_RXNE) { + qemu_set_irq(s->irq, 1); + } + return; + case USART_CR2: + s->usart_cr2 = value; + return; + case USART_CR3: + s->usart_cr3 = value; + return; + case USART_GTPR: + s->usart_gtpr = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32f2xx_usart_ops = { + .read = stm32f2xx_usart_read, + .write = stm32f2xx_usart_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32f2xx_usart_init(Object *obj) +{ + STM32F2XXUsartState *s = STM32F2XX_USART(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32f2xx_usart_ops, s, + TYPE_STM32F2XX_USART, 0x2000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + s->chr = qemu_char_get_next_serial(); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, stm32f2xx_usart_can_receive, + stm32f2xx_usart_receive, NULL, s); + } +} + +static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32f2xx_usart_reset; +} + +static const TypeInfo stm32f2xx_usart_info = { + .name = TYPE_STM32F2XX_USART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F2XXUsartState), + .instance_init = stm32f2xx_usart_init, + .class_init = stm32f2xx_usart_class_init, +}; + +static void stm32f2xx_usart_register_types(void) +{ + type_register_static(&stm32f2xx_usart_info); +} + +type_init(stm32f2xx_usart_register_types) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 9a029d2..c86814f 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -64,7 +64,7 @@ static VirtIOSerialPort *find_port_by_name(char *name) VirtIOSerialPort *port; QTAILQ_FOREACH(port, &vser->ports, next) { - if (!strcmp(port->name, name)) { + if (port->name && !strcmp(port->name, name)) { return port; } } diff --git a/hw/core/loader.c b/hw/core/loader.c index e45dc0b..76d8aca 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -297,6 +297,7 @@ static void *load_at(int fd, int offset, int size) #undef elf_phdr #undef elf_shdr #undef elf_sym +#undef elf_rela #undef elf_note #undef elf_word #undef elf_sword @@ -307,6 +308,7 @@ static void *load_at(int fd, int offset, int size) #define elf_note elf64_note #define elf_shdr elf64_shdr #define elf_sym elf64_sym +#define elf_rela elf64_rela #define elf_word uint64_t #define elf_sword int64_t #define bswapSZs bswap64s diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index a2e44bd..c413226 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -341,27 +341,25 @@ PropertyInfo qdev_prop_vlan = { .set = set_vlan, }; -int qdev_prop_set_drive(DeviceState *dev, const char *name, - BlockBackend *value) +void qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockBackend *value, Error **errp) { - Error *err = NULL; - object_property_set_str(OBJECT(dev), - value ? blk_name(value) : "", name, &err); - if (err) { - qerror_report_err(err); - error_free(err); - return -1; - } - return 0; + object_property_set_str(OBJECT(dev), value ? blk_name(value) : "", + name, errp); } void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockBackend *value) { - if (qdev_prop_set_drive(dev, name, value) < 0) { + Error *err = NULL; + + qdev_prop_set_drive(dev, name, value, &err); + if (err) { + error_report_err(err); exit(1); } } + void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value) { diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 5a4e4d5..570d5f0 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -580,7 +580,8 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque, error_propagate(errp, local_err); return; } - if (value < min || value > max) { + /* value of 0 means "unset" */ + if (value && (value < min || value > max)) { error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, dev->id?:"", name, (int64_t)value, min, max); return; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 79eaad5..b5b2aad 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -25,6 +25,8 @@ #include "hw/i386/pc.h" #include "hw/char/serial.h" #include "hw/i386/apic.h" +#include "hw/i386/topology.h" +#include "sysemu/cpus.h" #include "hw/block/fdc.h" #include "hw/ide.h" #include "hw/pci/pci.h" @@ -629,6 +631,39 @@ bool e820_get_entry(int idx, uint32_t type, uint64_t *address, uint64_t *length) return false; } +/* Enables contiguous-apic-ID mode, for compatibility */ +static bool compat_apic_id_mode; + +void enable_compat_apic_id_mode(void) +{ + compat_apic_id_mode = true; +} + +/* Calculates initial APIC ID for a specific CPU index + * + * Currently we need to be able to calculate the APIC ID from the CPU index + * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have + * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of + * all CPUs up to max_cpus. + */ +static uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index) +{ + uint32_t correct_id; + static bool warned; + + correct_id = x86_apicid_from_cpu_idx(smp_cores, smp_threads, cpu_index); + if (compat_apic_id_mode) { + if (cpu_index != correct_id && !warned) { + error_report("APIC IDs set in compatibility mode, " + "CPU topology won't match the configuration"); + warned = true; + } + return cpu_index; + } else { + return correct_id; + } +} + /* Calculates the limit to CPU APIC ID values * * This function returns the limit for the APIC ID value, so that all diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 5651372..e1ae36f 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1160,6 +1160,11 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s, dma_cb(s, 0); } +static void ahci_restart_dma(IDEDMA *dma) +{ + /* Nothing to do, ahci_start_dma already resets s->io_buffer_offset. */ +} + /** * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist. * Not currently invoked by PIO R/W chains, @@ -1226,12 +1231,6 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) return 1; } -static int ahci_dma_set_unit(IDEDMA *dma, int unit) -{ - /* only a single unit per link */ - return 0; -} - static void ahci_cmd_done(IDEDMA *dma) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1252,19 +1251,14 @@ static void ahci_irq_set(void *opaque, int n, int level) { } -static void ahci_dma_restart_cb(void *opaque, int running, RunState state) -{ -} - static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, + .restart_dma = ahci_restart_dma, .start_transfer = ahci_start_transfer, .prepare_buf = ahci_dma_prepare_buf, .commit_buf = ahci_commit_buf, .rw_buf = ahci_dma_rw_buf, - .set_unit = ahci_dma_set_unit, .cmd_done = ahci_cmd_done, - .restart_cb = ahci_dma_restart_cb, }; void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) @@ -1294,6 +1288,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) ad->port_no = i; ad->port.dma = &ad->dma; ad->port.dma->ops = &ahci_dma_ops; + ide_register_restart_cb(&ad->port); } } @@ -1333,6 +1328,7 @@ static const VMStateDescription vmstate_ahci_device = { .version_id = 1, .fields = (VMStateField[]) { VMSTATE_IDE_BUS(port, AHCIDevice), + VMSTATE_IDE_DRIVE(port.ifs[0], AHCIDevice), VMSTATE_UINT32(port_state, AHCIDevice), VMSTATE_UINT32(finished, AHCIDevice), VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice), @@ -1371,16 +1367,23 @@ static int ahci_state_post_load(void *opaque, int version_id) map_page(s->as, &ad->res_fis, ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); /* - * All pending i/o should be flushed out on a migrate. However, - * we might not have cleared the busy_slot since this is done - * in a bh. Also, issue i/o against any slots that are pending. + * If an error is present, ad->busy_slot will be valid and not -1. + * In this case, an operation is waiting to resume and will re-check + * for additional AHCI commands to execute upon completion. + * + * In the case where no error was present, busy_slot will be -1, + * and we should check to see if there are additional commands waiting. */ - if ((ad->busy_slot != -1) && - !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { - pr->cmd_issue &= ~(1 << ad->busy_slot); - ad->busy_slot = -1; + if (ad->busy_slot == -1) { + check_cmd(s, i); + } else { + /* We are in the middle of a command, and may need to access + * the command header in guest memory again. */ + if (ad->busy_slot < 0 || ad->busy_slot >= AHCI_MAX_CMDS) { + return -1; + } + ad->cur_cmd = &((AHCICmdHdr *)ad->lst)[ad->busy_slot]; } - check_cmd(s, i); } return 0; diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 1bf8b34..950e311 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -252,7 +252,6 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) s->packet_transfer_size = size; s->io_buffer_size = size; /* dma: send the reply data as one chunk */ s->elementary_transfer_size = 0; - s->io_buffer_index = 0; if (s->atapi_dma) { block_acct_start(blk_get_stats(s->blk), &s->acct, size, @@ -261,6 +260,7 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) ide_start_dma(s, ide_atapi_cmd_read_dma_cb); } else { s->status = READY_STAT | SEEK_STAT; + s->io_buffer_index = 0; ide_atapi_cmd_reply_end(s); } } @@ -368,7 +368,6 @@ static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, { s->lba = lba; s->packet_transfer_size = nb_sectors * sector_size; - s->io_buffer_index = 0; s->io_buffer_size = 0; s->cd_sector_size = sector_size; diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index dafa9ec..66fb9d9 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -368,8 +368,7 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); diff --git a/hw/ide/core.c b/hw/ide/core.c index ac3f015..ef52f35 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -561,6 +561,8 @@ static bool ide_sect_range_ok(IDEState *s, return true; } +static void ide_sector_read(IDEState *s); + static void ide_sector_read_cb(void *opaque, int ret) { IDEState *s = opaque; @@ -595,7 +597,7 @@ static void ide_sector_read_cb(void *opaque, int ret) s->io_buffer_offset += 512 * n; } -void ide_sector_read(IDEState *s) +static void ide_sector_read(IDEState *s) { int64_t sector_num; int n; @@ -646,6 +648,9 @@ static void dma_buf_commit(IDEState *s, uint32_t tx_bytes) void ide_set_inactive(IDEState *s, bool more) { s->bus->dma->aiocb = NULL; + s->bus->retry_unit = -1; + s->bus->retry_sector_num = 0; + s->bus->retry_nsector = 0; if (s->bus->dma->ops->set_inactive) { s->bus->dma->ops->set_inactive(s->bus->dma, more); } @@ -666,7 +671,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) BlockErrorAction action = blk_get_error_action(s->blk, is_read, error); if (action == BLOCK_ERROR_ACTION_STOP) { - s->bus->dma->ops->set_unit(s->bus->dma, s->unit); + assert(s->bus->retry_unit == s->unit); s->bus->error_status = op; } else if (action == BLOCK_ERROR_ACTION_REPORT) { if (op & IDE_RETRY_DMA) { @@ -679,7 +684,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) return action != BLOCK_ERROR_ACTION_IGNORE; } -void ide_dma_cb(void *opaque, int ret) +static void ide_dma_cb(void *opaque, int ret) { IDEState *s = opaque; int n; @@ -777,7 +782,6 @@ eot: static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) { s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; - s->io_buffer_index = 0; s->io_buffer_size = 0; s->dma_cmd = dma_cmd; @@ -799,11 +803,17 @@ static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) void ide_start_dma(IDEState *s, BlockCompletionFunc *cb) { + s->io_buffer_index = 0; + s->bus->retry_unit = s->unit; + s->bus->retry_sector_num = ide_get_sector(s); + s->bus->retry_nsector = s->nsector; if (s->bus->dma->ops->start_dma) { s->bus->dma->ops->start_dma(s->bus->dma, s, cb); } } +static void ide_sector_write(IDEState *s); + static void ide_sector_write_timer_cb(void *opaque) { IDEState *s = opaque; @@ -863,7 +873,7 @@ static void ide_sector_write_cb(void *opaque, int ret) } } -void ide_sector_write(IDEState *s) +static void ide_sector_write(IDEState *s) { int64_t sector_num; int n; @@ -917,7 +927,7 @@ static void ide_flush_cb(void *opaque, int ret) ide_set_irq(s->bus); } -void ide_flush_cache(IDEState *s) +static void ide_flush_cache(IDEState *s) { if (s->blk == NULL) { ide_flush_cb(s, 0); @@ -2314,22 +2324,101 @@ static int ide_nop_int(IDEDMA *dma, int x) return 0; } -static int32_t ide_nop_int32(IDEDMA *dma, int x) +static void ide_nop(IDEDMA *dma) { - return 0; } -static void ide_nop_restart(void *opaque, int x, RunState y) +static int32_t ide_nop_int32(IDEDMA *dma, int x) { + return 0; } static const IDEDMAOps ide_dma_nop_ops = { .prepare_buf = ide_nop_int32, + .restart_dma = ide_nop, .rw_buf = ide_nop_int, - .set_unit = ide_nop_int, - .restart_cb = ide_nop_restart, }; +static void ide_restart_dma(IDEState *s, enum ide_dma_cmd dma_cmd) +{ + s->unit = s->bus->retry_unit; + ide_set_sector(s, s->bus->retry_sector_num); + s->nsector = s->bus->retry_nsector; + s->bus->dma->ops->restart_dma(s->bus->dma); + s->io_buffer_size = 0; + s->dma_cmd = dma_cmd; + ide_start_dma(s, ide_dma_cb); +} + +static void ide_restart_bh(void *opaque) +{ + IDEBus *bus = opaque; + IDEState *s; + bool is_read; + int error_status; + + qemu_bh_delete(bus->bh); + bus->bh = NULL; + + error_status = bus->error_status; + if (bus->error_status == 0) { + return; + } + + s = idebus_active_if(bus); + is_read = (bus->error_status & IDE_RETRY_READ) != 0; + + /* The error status must be cleared before resubmitting the request: The + * request may fail again, and this case can only be distinguished if the + * called function can set a new error status. */ + bus->error_status = 0; + + if (error_status & IDE_RETRY_DMA) { + if (error_status & IDE_RETRY_TRIM) { + ide_restart_dma(s, IDE_DMA_TRIM); + } else { + ide_restart_dma(s, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); + } + } else if (error_status & IDE_RETRY_PIO) { + if (is_read) { + ide_sector_read(s); + } else { + ide_sector_write(s); + } + } else if (error_status & IDE_RETRY_FLUSH) { + ide_flush_cache(s); + } else { + /* + * We've not got any bits to tell us about ATAPI - but + * we do have the end_transfer_func that tells us what + * we're trying to do. + */ + if (s->end_transfer_func == ide_atapi_cmd) { + ide_atapi_dma_restart(s); + } + } +} + +static void ide_restart_cb(void *opaque, int running, RunState state) +{ + IDEBus *bus = opaque; + + if (!running) + return; + + if (!bus->bh) { + bus->bh = qemu_bh_new(ide_restart_bh, bus); + qemu_bh_schedule(bus->bh); + } +} + +void ide_register_restart_cb(IDEBus *bus) +{ + if (bus->dma->ops->restart_dma) { + qemu_add_vm_change_state_handler(ide_restart_cb, bus); + } +} + static IDEDMA ide_dma_nop = { .ops = &ide_dma_nop_ops, .aiocb = NULL, @@ -2557,10 +2646,13 @@ const VMStateDescription vmstate_ide_drive = { static const VMStateDescription vmstate_ide_error_status = { .name ="ide_bus/error", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT32(error_status, IDEBus), + VMSTATE_INT64_V(retry_sector_num, IDEBus, 2), + VMSTATE_UINT32_V(retry_nsector, IDEBus, 2), + VMSTATE_UINT8_V(retry_unit, IDEBus, 2), VMSTATE_END_OF_LIST() } }; diff --git a/hw/ide/internal.h b/hw/ide/internal.h index ee9a57f..965cc55 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -436,10 +436,9 @@ struct IDEDMAOps { DMAInt32Func *prepare_buf; DMAu32Func *commit_buf; DMAIntFunc *rw_buf; - DMAIntFunc *set_unit; + DMAVoidFunc *restart_dma; DMAStopFunc *set_inactive; DMAVoidFunc *cmd_done; - DMARestartFunc *restart_cb; DMAVoidFunc *reset; }; @@ -455,6 +454,8 @@ struct IDEBus { IDEDevice *master; IDEDevice *slave; IDEState ifs[2]; + QEMUBH *bh; + int bus_id; int max_units; IDEDMA *dma; @@ -463,6 +464,9 @@ struct IDEBus { qemu_irq irq; int error_status; + uint8_t retry_unit; + int64_t retry_sector_num; + uint32_t retry_nsector; }; #define TYPE_IDE_DEVICE "ide-device" @@ -522,6 +526,9 @@ extern const VMStateDescription vmstate_ide_drive; #define VMSTATE_IDE_DRIVES(_field, _state) \ VMSTATE_STRUCT_ARRAY(_field, _state, 2, 3, vmstate_ide_drive, IDEState) +#define VMSTATE_IDE_DRIVE(_field, _state) \ + VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState) + void ide_bus_reset(IDEBus *bus); int64_t ide_get_sector(IDEState *s); void ide_set_sector(IDEState *s, int64_t sector_num); @@ -550,12 +557,9 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, int chs_trans); void ide_init2(IDEBus *bus, qemu_irq irq); void ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); +void ide_register_restart_cb(IDEBus *bus); void ide_exec_cmd(IDEBus *bus, uint32_t val); -void ide_dma_cb(void *opaque, int ret); -void ide_sector_write(IDEState *s); -void ide_sector_read(IDEState *s); -void ide_flush_cache(IDEState *s); void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func); diff --git a/hw/ide/isa.c b/hw/ide/isa.c index c0c4e1b..9f80503 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -74,7 +74,8 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) isa_init_irq(isadev, &s->irq, s->isairq); ide_init2(&s->bus, s->irq); vmstate_register(dev, 0, &vmstate_ide_isa, s); -}; + ide_register_restart_cb(&s->bus); +} ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, DriveInfo *hd0, DriveInfo *hd1) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index f6074f2..a009674 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -558,10 +558,6 @@ static int32_t ide_nop_int32(IDEDMA *dma, int x) return 0; } -static void ide_nop_restart(void *opaque, int x, RunState y) -{ -} - static void ide_dbdma_start(IDEDMA *dma, IDEState *s, BlockCompletionFunc *cb) { @@ -576,8 +572,6 @@ static const IDEDMAOps dbdma_ops = { .start_dma = ide_dbdma_start, .prepare_buf = ide_nop_int32, .rw_buf = ide_nop_int, - .set_unit = ide_nop_int, - .restart_cb = ide_nop_restart, }; static void macio_ide_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 913a976..1b3d1c1 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -42,13 +42,10 @@ static void bmdma_start_dma(IDEDMA *dma, IDEState *s, { BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - bm->unit = s->unit; bm->dma_cb = dma_cb; bm->cur_prd_last = 0; bm->cur_prd_addr = 0; bm->cur_prd_len = 0; - bm->sector_num = ide_get_sector(s); - bm->nsector = s->nsector; if (bm->status & BM_STATUS_DMAING) { bm->dma_cb(bmdma_active_if(bm), 0); @@ -163,20 +160,11 @@ static int bmdma_rw_buf(IDEDMA *dma, int is_write) return 1; } -static int bmdma_set_unit(IDEDMA *dma, int unit) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - bm->unit = unit; - - return 0; -} - static void bmdma_set_inactive(IDEDMA *dma, bool more) { BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); bm->dma_cb = NULL; - bm->unit = -1; if (more) { bm->status |= BM_STATUS_DMAING; } else { @@ -184,83 +172,11 @@ static void bmdma_set_inactive(IDEDMA *dma, bool more) } } -static void bmdma_restart_dma(BMDMAState *bm, enum ide_dma_cmd dma_cmd) -{ - IDEState *s = bmdma_active_if(bm); - - ide_set_sector(s, bm->sector_num); - s->io_buffer_index = 0; - s->io_buffer_size = 0; - s->nsector = bm->nsector; - s->dma_cmd = dma_cmd; - bm->cur_addr = bm->addr; - bm->dma_cb = ide_dma_cb; - bmdma_start_dma(&bm->dma, s, bm->dma_cb); -} - -/* TODO This should be common IDE code */ -static void bmdma_restart_bh(void *opaque) -{ - BMDMAState *bm = opaque; - IDEBus *bus = bm->bus; - bool is_read; - int error_status; - - qemu_bh_delete(bm->bh); - bm->bh = NULL; - - if (bm->unit == (uint8_t) -1) { - return; - } - - is_read = (bus->error_status & IDE_RETRY_READ) != 0; - - /* The error status must be cleared before resubmitting the request: The - * request may fail again, and this case can only be distinguished if the - * called function can set a new error status. */ - error_status = bus->error_status; - bus->error_status = 0; - - if (error_status & IDE_RETRY_DMA) { - if (error_status & IDE_RETRY_TRIM) { - bmdma_restart_dma(bm, IDE_DMA_TRIM); - } else { - bmdma_restart_dma(bm, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); - } - } else if (error_status & IDE_RETRY_PIO) { - if (is_read) { - ide_sector_read(bmdma_active_if(bm)); - } else { - ide_sector_write(bmdma_active_if(bm)); - } - } else if (error_status & IDE_RETRY_FLUSH) { - ide_flush_cache(bmdma_active_if(bm)); - } else { - IDEState *s = bmdma_active_if(bm); - - /* - * We've not got any bits to tell us about ATAPI - but - * we do have the end_transfer_func that tells us what - * we're trying to do. - */ - if (s->end_transfer_func == ide_atapi_cmd) { - ide_atapi_dma_restart(s); - } - } -} - -static void bmdma_restart_cb(void *opaque, int running, RunState state) +static void bmdma_restart_dma(IDEDMA *dma) { - IDEDMA *dma = opaque; BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - if (!running) - return; - - if (!bm->bh) { - bm->bh = qemu_bh_new(bmdma_restart_bh, &bm->dma); - qemu_bh_schedule(bm->bh); - } + bm->cur_addr = bm->addr; } static void bmdma_cancel(BMDMAState *bm) @@ -286,8 +202,6 @@ static void bmdma_reset(IDEDMA *dma) bm->cur_prd_last = 0; bm->cur_prd_addr = 0; bm->cur_prd_len = 0; - bm->sector_num = 0; - bm->nsector = 0; } static void bmdma_irq(void *opaque, int n, int level) @@ -404,6 +318,9 @@ static void ide_bmdma_pre_save(void *opaque) BMDMAState *bm = opaque; uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; + bm->migration_retry_unit = bm->bus->retry_unit; + bm->migration_retry_sector_num = bm->bus->retry_sector_num; + bm->migration_retry_nsector = bm->bus->retry_nsector; bm->migration_compat_status = (bm->status & ~abused_bits) | (bm->bus->error_status & abused_bits); } @@ -420,6 +337,11 @@ static int ide_bmdma_post_load(void *opaque, int version_id) bm->status = bm->migration_compat_status & ~abused_bits; bm->bus->error_status |= bm->migration_compat_status & abused_bits; } + if (bm->bus->error_status) { + bm->bus->retry_sector_num = bm->migration_retry_sector_num; + bm->bus->retry_nsector = bm->migration_retry_nsector; + bm->bus->retry_unit = bm->migration_retry_unit; + } return 0; } @@ -456,9 +378,9 @@ static const VMStateDescription vmstate_bmdma = { VMSTATE_UINT8(cmd, BMDMAState), VMSTATE_UINT8(migration_compat_status, BMDMAState), VMSTATE_UINT32(addr, BMDMAState), - VMSTATE_INT64(sector_num, BMDMAState), - VMSTATE_UINT32(nsector, BMDMAState), - VMSTATE_UINT8(unit, BMDMAState), + VMSTATE_INT64(migration_retry_sector_num, BMDMAState), + VMSTATE_UINT32(migration_retry_nsector, BMDMAState), + VMSTATE_UINT8(migration_retry_unit, BMDMAState), VMSTATE_END_OF_LIST() }, .subsections = (VMStateSubsection []) { @@ -482,7 +404,7 @@ static int ide_pci_post_load(void *opaque, int version_id) for(i = 0; i < 2; i++) { /* current versions always store 0/1, but older version stored bigger values. We only need last bit */ - d->bmdma[i].unit &= 1; + d->bmdma[i].migration_retry_unit &= 1; ide_bmdma_post_load(&d->bmdma[i], -1); } @@ -523,9 +445,8 @@ static const struct IDEDMAOps bmdma_ops = { .start_dma = bmdma_start_dma, .prepare_buf = bmdma_prepare_buf, .rw_buf = bmdma_rw_buf, - .set_unit = bmdma_set_unit, + .restart_dma = bmdma_restart_dma, .set_inactive = bmdma_set_inactive, - .restart_cb = bmdma_restart_cb, .reset = bmdma_reset, }; diff --git a/hw/ide/pci.h b/hw/ide/pci.h index 2e9314a..0f2d4b9 100644 --- a/hw/ide/pci.h +++ b/hw/ide/pci.h @@ -22,18 +22,18 @@ typedef struct BMDMAState { uint32_t cur_prd_last; uint32_t cur_prd_addr; uint32_t cur_prd_len; - uint8_t unit; BlockCompletionFunc *dma_cb; - int64_t sector_num; - uint32_t nsector; MemoryRegion addr_ioport; MemoryRegion extra_io; - QEMUBH *bh; qemu_irq irq; /* Bit 0-2 and 7: BM status register * Bit 3-6: bus->error_status */ uint8_t migration_compat_status; + uint8_t migration_retry_unit; + int64_t migration_retry_sector_num; + uint32_t migration_retry_nsector; + struct PCIIDEState *pci_dev; } BMDMAState; @@ -62,8 +62,8 @@ typedef struct PCIIDEState { static inline IDEState *bmdma_active_if(BMDMAState *bmdma) { - assert(bmdma->unit != (uint8_t)-1); - return bmdma->bus->ifs + bmdma->unit; + assert(bmdma->bus->retry_unit != (uint8_t)-1); + return bmdma->bus->ifs + bmdma->bus->retry_unit; } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index ab93084..adb6649 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -143,8 +143,7 @@ static void pci_piix_init_ports(PCIIDEState *d) { bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } } diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index b4103fa..788b361 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -163,6 +163,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) return -1; } + blkconf_blocksizes(&dev->conf); if (dev->conf.logical_block_size != 512) { error_report("logical_block_size must be 512 for IDE"); return -1; diff --git a/hw/ide/via.c b/hw/ide/via.c index 7a5151c..e2da9ef 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -166,8 +166,7 @@ static void vt82c686b_init_ports(PCIIDEState *d) { bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; - qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, - &d->bmdma[i].dma); + ide_register_restart_cb(&d->bus[i]); } } diff --git a/hw/m68k/dummy_m68k.c b/hw/m68k/dummy_m68k.c index facd561..278f4c0 100644 --- a/hw/m68k/dummy_m68k.c +++ b/hw/m68k/dummy_m68k.c @@ -21,6 +21,7 @@ static void dummy_m68k_init(MachineState *machine) ram_addr_t ram_size = machine->ram_size; const char *cpu_model = machine->cpu_model; const char *kernel_filename = machine->kernel_filename; + M68kCPU *cpu; CPUM68KState *env; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); @@ -30,11 +31,12 @@ static void dummy_m68k_init(MachineState *machine) if (!cpu_model) cpu_model = "cfv4e"; - env = cpu_init(cpu_model); - if (!env) { + cpu = cpu_m68k_init(cpu_model); + if (!cpu) { fprintf(stderr, "Unable to find m68k CPU definition\n"); exit(1); } + env = &cpu->env; /* Initialize CPU registers. */ env->vbr = 0; diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 6c6e296..4aa76ff 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -36,6 +36,7 @@ obj-$(CONFIG_OMAP) += omap_sdrc.o obj-$(CONFIG_OMAP) += omap_tap.o obj-$(CONFIG_SLAVIO) += slavio_misc.o obj-$(CONFIG_ZYNQ) += zynq_slcr.o +obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_EDU) += edu.o diff --git a/hw/misc/stm32f2xx_syscfg.c b/hw/misc/stm32f2xx_syscfg.c new file mode 100644 index 0000000..4ae4042 --- /dev/null +++ b/hw/misc/stm32f2xx_syscfg.c @@ -0,0 +1,160 @@ +/* + * STM32F2XX SYSCFG + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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 "hw/misc/stm32f2xx_syscfg.h" + +#ifndef STM_SYSCFG_ERR_DEBUG +#define STM_SYSCFG_ERR_DEBUG 0 +#endif + +#define DB_PRINT_L(lvl, fmt, args...) do { \ + if (STM_SYSCFG_ERR_DEBUG >= lvl) { \ + qemu_log("%s: " fmt, __func__, ## args); \ + } \ +} while (0); + +#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) + +static void stm32f2xx_syscfg_reset(DeviceState *dev) +{ + STM32F2XXSyscfgState *s = STM32F2XX_SYSCFG(dev); + + s->syscfg_memrmp = 0x00000000; + s->syscfg_pmc = 0x00000000; + s->syscfg_exticr1 = 0x00000000; + s->syscfg_exticr2 = 0x00000000; + s->syscfg_exticr3 = 0x00000000; + s->syscfg_exticr4 = 0x00000000; + s->syscfg_cmpcr = 0x00000000; +} + +static uint64_t stm32f2xx_syscfg_read(void *opaque, hwaddr addr, + unsigned int size) +{ + STM32F2XXSyscfgState *s = opaque; + + DB_PRINT("0x%"HWADDR_PRIx"\n", addr); + + switch (addr) { + case SYSCFG_MEMRMP: + return s->syscfg_memrmp; + case SYSCFG_PMC: + return s->syscfg_pmc; + case SYSCFG_EXTICR1: + return s->syscfg_exticr1; + case SYSCFG_EXTICR2: + return s->syscfg_exticr2; + case SYSCFG_EXTICR3: + return s->syscfg_exticr3; + case SYSCFG_EXTICR4: + return s->syscfg_exticr4; + case SYSCFG_CMPCR: + return s->syscfg_cmpcr; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + return 0; + } + + return 0; +} + +static void stm32f2xx_syscfg_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + STM32F2XXSyscfgState *s = opaque; + uint32_t value = val64; + + DB_PRINT("0x%x, 0x%"HWADDR_PRIx"\n", value, addr); + + switch (addr) { + case SYSCFG_MEMRMP: + qemu_log_mask(LOG_UNIMP, + "%s: Changeing the memory mapping isn't supported " \ + "in QEMU\n", __func__); + return; + case SYSCFG_PMC: + qemu_log_mask(LOG_UNIMP, + "%s: Changeing the memory mapping isn't supported " \ + "in QEMU\n", __func__); + return; + case SYSCFG_EXTICR1: + s->syscfg_exticr1 = (value & 0xFFFF); + return; + case SYSCFG_EXTICR2: + s->syscfg_exticr2 = (value & 0xFFFF); + return; + case SYSCFG_EXTICR3: + s->syscfg_exticr3 = (value & 0xFFFF); + return; + case SYSCFG_EXTICR4: + s->syscfg_exticr4 = (value & 0xFFFF); + return; + case SYSCFG_CMPCR: + s->syscfg_cmpcr = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32f2xx_syscfg_ops = { + .read = stm32f2xx_syscfg_read, + .write = stm32f2xx_syscfg_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32f2xx_syscfg_init(Object *obj) +{ + STM32F2XXSyscfgState *s = STM32F2XX_SYSCFG(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32f2xx_syscfg_ops, s, + TYPE_STM32F2XX_SYSCFG, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void stm32f2xx_syscfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32f2xx_syscfg_reset; +} + +static const TypeInfo stm32f2xx_syscfg_info = { + .name = TYPE_STM32F2XX_SYSCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F2XXSyscfgState), + .instance_init = stm32f2xx_syscfg_init, + .class_init = stm32f2xx_syscfg_class_init, +}; + +static void stm32f2xx_syscfg_register_types(void) +{ + type_register_static(&stm32f2xx_syscfg_info); +} + +type_init(stm32f2xx_syscfg_register_types) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 5ce565d..99db56c 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -1318,167 +1318,6 @@ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) } } -/*****************************************************************************/ -/* NVRAM helpers */ -static inline uint32_t nvram_read (nvram_t *nvram, uint32_t addr) -{ - return (*nvram->read_fn)(nvram->opaque, addr); -} - -static inline void nvram_write (nvram_t *nvram, uint32_t addr, uint32_t val) -{ - (*nvram->write_fn)(nvram->opaque, addr, val); -} - -static void NVRAM_set_byte(nvram_t *nvram, uint32_t addr, uint8_t value) -{ - nvram_write(nvram, addr, value); -} - -static uint8_t NVRAM_get_byte(nvram_t *nvram, uint32_t addr) -{ - return nvram_read(nvram, addr); -} - -static void NVRAM_set_word(nvram_t *nvram, uint32_t addr, uint16_t value) -{ - nvram_write(nvram, addr, value >> 8); - nvram_write(nvram, addr + 1, value & 0xFF); -} - -static uint16_t NVRAM_get_word(nvram_t *nvram, uint32_t addr) -{ - uint16_t tmp; - - tmp = nvram_read(nvram, addr) << 8; - tmp |= nvram_read(nvram, addr + 1); - - return tmp; -} - -static void NVRAM_set_lword(nvram_t *nvram, uint32_t addr, uint32_t value) -{ - nvram_write(nvram, addr, value >> 24); - nvram_write(nvram, addr + 1, (value >> 16) & 0xFF); - nvram_write(nvram, addr + 2, (value >> 8) & 0xFF); - nvram_write(nvram, addr + 3, value & 0xFF); -} - -uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr) -{ - uint32_t tmp; - - tmp = nvram_read(nvram, addr) << 24; - tmp |= nvram_read(nvram, addr + 1) << 16; - tmp |= nvram_read(nvram, addr + 2) << 8; - tmp |= nvram_read(nvram, addr + 3); - - return tmp; -} - -static void NVRAM_set_string(nvram_t *nvram, uint32_t addr, const char *str, - uint32_t max) -{ - int i; - - for (i = 0; i < max && str[i] != '\0'; i++) { - nvram_write(nvram, addr + i, str[i]); - } - nvram_write(nvram, addr + i, str[i]); - nvram_write(nvram, addr + max - 1, '\0'); -} - -int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max) -{ - int i; - - memset(dst, 0, max); - for (i = 0; i < max; i++) { - dst[i] = NVRAM_get_byte(nvram, addr + i); - if (dst[i] == '\0') - break; - } - - return i; -} - -static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) -{ - uint16_t tmp; - uint16_t pd, pd1, pd2; - - tmp = prev >> 8; - pd = prev ^ value; - pd1 = pd & 0x000F; - pd2 = ((pd >> 4) & 0x000F) ^ pd1; - tmp ^= (pd1 << 3) | (pd1 << 8); - tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); - - return tmp; -} - -static uint16_t NVRAM_compute_crc (nvram_t *nvram, uint32_t start, uint32_t count) -{ - uint32_t i; - uint16_t crc = 0xFFFF; - int odd; - - odd = count & 1; - count &= ~1; - for (i = 0; i != count; i++) { - crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); - } - if (odd) { - crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); - } - - return crc; -} - -#define CMDLINE_ADDR 0x017ff000 - -int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size, - const char *arch, - uint32_t RAM_size, int boot_device, - uint32_t kernel_image, uint32_t kernel_size, - const char *cmdline, - uint32_t initrd_image, uint32_t initrd_size, - uint32_t NVRAM_image, - int width, int height, int depth) -{ - uint16_t crc; - - /* Set parameters for Open Hack'Ware BIOS */ - NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16); - NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */ - NVRAM_set_word(nvram, 0x14, NVRAM_size); - NVRAM_set_string(nvram, 0x20, arch, 16); - NVRAM_set_lword(nvram, 0x30, RAM_size); - NVRAM_set_byte(nvram, 0x34, boot_device); - NVRAM_set_lword(nvram, 0x38, kernel_image); - NVRAM_set_lword(nvram, 0x3C, kernel_size); - if (cmdline) { - /* XXX: put the cmdline in NVRAM too ? */ - pstrcpy_targphys("cmdline", CMDLINE_ADDR, RAM_size - CMDLINE_ADDR, cmdline); - NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR); - NVRAM_set_lword(nvram, 0x44, strlen(cmdline)); - } else { - NVRAM_set_lword(nvram, 0x40, 0); - NVRAM_set_lword(nvram, 0x44, 0); - } - NVRAM_set_lword(nvram, 0x48, initrd_image); - NVRAM_set_lword(nvram, 0x4C, initrd_size); - NVRAM_set_lword(nvram, 0x50, NVRAM_image); - - NVRAM_set_word(nvram, 0x54, width); - NVRAM_set_word(nvram, 0x56, height); - NVRAM_set_word(nvram, 0x58, depth); - crc = NVRAM_compute_crc(nvram, 0x00, 0xF8); - NVRAM_set_word(nvram, 0xFC, crc); - - return 0; -} - /* CPU device-tree ID helpers */ int ppc_get_vcpu_dt_id(PowerPCCPU *cpu) { diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 1dcea77..ec6c4cb 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -283,7 +283,7 @@ static void ref405ep_init(MachineState *machine) #ifdef DEBUG_BOARD_INIT printf("%s: register NVRAM\n", __func__); #endif - m48t59_init(NULL, 0xF0000000, 0, 8192, 8); + m48t59_init(NULL, 0xF0000000, 0, 8192, 1968, 8); /* Load kernel */ linux_boot = (kernel_filename != NULL); if (linux_boot) { diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 15df7f3..7f52662 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -181,7 +181,7 @@ static const MemoryRegionOps PPC_XCSR_ops = { /* Fake super-io ports for PREP platform (Intel 82378ZB) */ typedef struct sysctrl_t { qemu_irq reset_irq; - M48t59State *nvram; + Nvram *nvram; uint8_t state; uint8_t syscontrol; int contiguous_map; @@ -235,13 +235,17 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) break; case 0x0810: /* Password protect 1 register */ - if (sysctrl->nvram != NULL) - m48t59_toggle_lock(sysctrl->nvram, 1); + if (sysctrl->nvram != NULL) { + NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + (k->toggle_lock)(sysctrl->nvram, 1); + } break; case 0x0812: /* Password protect 2 register */ - if (sysctrl->nvram != NULL) - m48t59_toggle_lock(sysctrl->nvram, 2); + if (sysctrl->nvram != NULL) { + NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + (k->toggle_lock)(sysctrl->nvram, 2); + } break; case 0x0814: /* L2 invalidate register */ @@ -360,6 +364,144 @@ static const MemoryRegionPortio prep_portio_list[] = { static PortioList prep_port_list; +/*****************************************************************************/ +/* NVRAM helpers */ +static inline uint32_t nvram_read(Nvram *nvram, uint32_t addr) +{ + NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + return (k->read)(nvram, addr); +} + +static inline void nvram_write(Nvram *nvram, uint32_t addr, uint32_t val) +{ + NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + (k->write)(nvram, addr, val); +} + +static void NVRAM_set_byte(Nvram *nvram, uint32_t addr, uint8_t value) +{ + nvram_write(nvram, addr, value); +} + +static uint8_t NVRAM_get_byte(Nvram *nvram, uint32_t addr) +{ + return nvram_read(nvram, addr); +} + +static void NVRAM_set_word(Nvram *nvram, uint32_t addr, uint16_t value) +{ + nvram_write(nvram, addr, value >> 8); + nvram_write(nvram, addr + 1, value & 0xFF); +} + +static uint16_t NVRAM_get_word(Nvram *nvram, uint32_t addr) +{ + uint16_t tmp; + + tmp = nvram_read(nvram, addr) << 8; + tmp |= nvram_read(nvram, addr + 1); + + return tmp; +} + +static void NVRAM_set_lword(Nvram *nvram, uint32_t addr, uint32_t value) +{ + nvram_write(nvram, addr, value >> 24); + nvram_write(nvram, addr + 1, (value >> 16) & 0xFF); + nvram_write(nvram, addr + 2, (value >> 8) & 0xFF); + nvram_write(nvram, addr + 3, value & 0xFF); +} + +static void NVRAM_set_string(Nvram *nvram, uint32_t addr, const char *str, + uint32_t max) +{ + int i; + + for (i = 0; i < max && str[i] != '\0'; i++) { + nvram_write(nvram, addr + i, str[i]); + } + nvram_write(nvram, addr + i, str[i]); + nvram_write(nvram, addr + max - 1, '\0'); +} + +static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) +{ + uint16_t tmp; + uint16_t pd, pd1, pd2; + + tmp = prev >> 8; + pd = prev ^ value; + pd1 = pd & 0x000F; + pd2 = ((pd >> 4) & 0x000F) ^ pd1; + tmp ^= (pd1 << 3) | (pd1 << 8); + tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); + + return tmp; +} + +static uint16_t NVRAM_compute_crc (Nvram *nvram, uint32_t start, uint32_t count) +{ + uint32_t i; + uint16_t crc = 0xFFFF; + int odd; + + odd = count & 1; + count &= ~1; + for (i = 0; i != count; i++) { + crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); + } + if (odd) { + crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); + } + + return crc; +} + +#define CMDLINE_ADDR 0x017ff000 + +static int PPC_NVRAM_set_params (Nvram *nvram, uint16_t NVRAM_size, + const char *arch, + uint32_t RAM_size, int boot_device, + uint32_t kernel_image, uint32_t kernel_size, + const char *cmdline, + uint32_t initrd_image, uint32_t initrd_size, + uint32_t NVRAM_image, + int width, int height, int depth) +{ + uint16_t crc; + + /* Set parameters for Open Hack'Ware BIOS */ + NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16); + NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */ + NVRAM_set_word(nvram, 0x14, NVRAM_size); + NVRAM_set_string(nvram, 0x20, arch, 16); + NVRAM_set_lword(nvram, 0x30, RAM_size); + NVRAM_set_byte(nvram, 0x34, boot_device); + NVRAM_set_lword(nvram, 0x38, kernel_image); + NVRAM_set_lword(nvram, 0x3C, kernel_size); + if (cmdline) { + /* XXX: put the cmdline in NVRAM too ? */ + pstrcpy_targphys("cmdline", CMDLINE_ADDR, RAM_size - CMDLINE_ADDR, + cmdline); + NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR); + NVRAM_set_lword(nvram, 0x44, strlen(cmdline)); + } else { + NVRAM_set_lword(nvram, 0x40, 0); + NVRAM_set_lword(nvram, 0x44, 0); + } + NVRAM_set_lword(nvram, 0x48, initrd_image); + NVRAM_set_lword(nvram, 0x4C, initrd_size); + NVRAM_set_lword(nvram, 0x50, NVRAM_image); + + NVRAM_set_word(nvram, 0x54, width); + NVRAM_set_word(nvram, 0x56, height); + NVRAM_set_word(nvram, 0x58, depth); + crc = NVRAM_compute_crc(nvram, 0x00, 0xF8); + NVRAM_set_word(nvram, 0xFC, crc); + + return 0; +} + /* PowerPC PREP hardware initialisation */ static void ppc_prep_init(MachineState *machine) { @@ -372,8 +514,7 @@ static void ppc_prep_init(MachineState *machine) MemoryRegion *sysmem = get_system_memory(); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; - nvram_t nvram; - M48t59State *m48t59; + Nvram *m48t59; #if 0 MemoryRegion *xcsr = g_new(MemoryRegion, 1); #endif @@ -543,16 +684,14 @@ static void ppc_prep_init(MachineState *machine) pci_create_simple(pci_bus, -1, "pci-ohci"); } - m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 59); + m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 2000, 59); if (m48t59 == NULL) return; sysctrl->nvram = m48t59; /* Initialise NVRAM */ - nvram.opaque = m48t59; - nvram.read_fn = &m48t59_read; - nvram.write_fn = &m48t59_write; - PPC_NVRAM_set_params(&nvram, NVRAM_SIZE, "PREP", ram_size, ppc_boot_device, + PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size, + ppc_boot_device, kernel_base, kernel_size, kernel_cmdline, initrd_base, initrd_size, diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index b57adbd..d6c0a49 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -14,6 +14,7 @@ #include "sysemu/sysemu.h" #include "cpu.h" #include "elf.h" +#include "exec/ram_addr.h" #include "hw/loader.h" #include "hw/sysbus.h" #include "hw/s390x/virtio-ccw.h" @@ -95,6 +96,16 @@ static const VMStateDescription vmstate_ipl = { } }; +static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr) +{ + uint64_t dstaddr = *(uint64_t *) opaque; + /* + * Assuming that our s390-ccw.img was linked for starting at address 0, + * we can simply add the destination address for the final location + */ + return srcaddr + dstaddr; +} + static int s390_ipl_init(SysBusDevice *dev) { S390IPLState *ipl = S390_IPL(dev); @@ -109,6 +120,8 @@ static int s390_ipl_init(SysBusDevice *dev) * even if an external kernel has been defined. */ if (!ipl->kernel || ipl->enforce_bios) { + uint64_t fwbase = (MIN(ram_size, 0x80000000U) - 0x200000) & ~0xffffUL; + if (bios_name == NULL) { bios_name = ipl->firmware; } @@ -118,9 +131,14 @@ static int s390_ipl_init(SysBusDevice *dev) hw_error("could not find stage1 bootloader\n"); } - bios_size = load_elf(bios_filename, NULL, NULL, &ipl->bios_start_addr, - NULL, NULL, 1, ELF_MACHINE, 0); - if (bios_size < 0) { + bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase, + &ipl->bios_start_addr, NULL, NULL, 1, + ELF_MACHINE, 0); + if (bios_size > 0) { + /* Adjust ELF start address to final location */ + ipl->bios_start_addr += fwbase; + } else { + /* Try to load non-ELF file (e.g. s390-zipl.rom) */ bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START, 4096); ipl->bios_start_addr = ZIPL_IMAGE_START; diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c index 39dc201..55a5581 100644 --- a/hw/s390x/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -111,7 +111,8 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) return bus; } -static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev) +static void s390_virtio_device_init(VirtIOS390Device *dev, + VirtIODevice *vdev) { VirtIOS390Bus *bus; int dev_len; @@ -135,25 +136,26 @@ static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev) if (dev->qdev.hotplugged) { s390_virtio_irq(VIRTIO_PARAM_DEV_ADD, dev->dev_offs); } - - return 0; } -static int s390_virtio_net_init(VirtIOS390Device *s390_dev) +static void s390_virtio_net_realize(VirtIOS390Device *s390_dev, Error **errp) { DeviceState *qdev = DEVICE(s390_dev); VirtIONetS390 *dev = VIRTIO_NET_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; virtio_net_set_config_size(&dev->vdev, s390_dev->host_features); virtio_net_set_netclient_name(&dev->vdev, qdev->id, object_get_typename(OBJECT(qdev))); qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_virtio_net_instance_init(Object *obj) @@ -166,15 +168,19 @@ static void s390_virtio_net_instance_init(Object *obj) "bootindex", &error_abort); } -static int s390_virtio_blk_init(VirtIOS390Device *s390_dev) +static void s390_virtio_blk_realize(VirtIOS390Device *s390_dev, Error **errp) { VirtIOBlkS390 *dev = VIRTIO_BLK_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; + qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_virtio_blk_instance_init(Object *obj) @@ -189,13 +195,13 @@ static void s390_virtio_blk_instance_init(Object *obj) "bootindex", &error_abort); } -static int s390_virtio_serial_init(VirtIOS390Device *s390_dev) +static void s390_virtio_serial_realize(VirtIOS390Device *s390_dev, Error **errp) { VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); DeviceState *qdev = DEVICE(s390_dev); + Error *err = NULL; VirtIOS390Bus *bus; - int r; char *bus_name; bus = DO_UPCAST(VirtIOS390Bus, bus, qdev->parent_bus); @@ -211,16 +217,14 @@ static int s390_virtio_serial_init(VirtIOS390Device *s390_dev) } qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - r = s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); - if (!r) { - bus->console = s390_dev; - } - - return r; + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + bus->console = s390_dev; } static void s390_virtio_serial_instance_init(Object *obj) @@ -231,11 +235,12 @@ static void s390_virtio_serial_instance_init(Object *obj) TYPE_VIRTIO_SERIAL); } -static int s390_virtio_scsi_init(VirtIOS390Device *s390_dev) +static void s390_virtio_scsi_realize(VirtIOS390Device *s390_dev, Error **errp) { VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); DeviceState *qdev = DEVICE(s390_dev); + Error *err = NULL; char *bus_name; /* @@ -249,11 +254,13 @@ static int s390_virtio_scsi_init(VirtIOS390Device *s390_dev) } qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_virtio_scsi_instance_init(Object *obj) @@ -265,17 +272,20 @@ static void s390_virtio_scsi_instance_init(Object *obj) } #ifdef CONFIG_VHOST_SCSI -static int s390_vhost_scsi_init(VirtIOS390Device *s390_dev) +static void s390_vhost_scsi_realize(VirtIOS390Device *s390_dev, Error **errp) { VHostSCSIS390 *dev = VHOST_SCSI_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_vhost_scsi_instance_init(Object *obj) @@ -288,21 +298,24 @@ static void s390_vhost_scsi_instance_init(Object *obj) #endif -static int s390_virtio_rng_init(VirtIOS390Device *s390_dev) +static void s390_virtio_rng_realize(VirtIOS390Device *s390_dev, Error **errp) { VirtIORNGS390 *dev = VIRTIO_RNG_S390(s390_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } object_property_set_link(OBJECT(dev), OBJECT(dev->vdev.conf.rng), "rng", NULL); - return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); + s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); } static void s390_virtio_rng_instance_init(Object *obj) @@ -509,7 +522,7 @@ static void s390_virtio_net_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_net_init; + k->realize = s390_virtio_net_realize; dc->props = s390_virtio_net_properties; } @@ -525,7 +538,7 @@ static void s390_virtio_blk_class_init(ObjectClass *klass, void *data) { VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_blk_init; + k->realize = s390_virtio_blk_realize; } static const TypeInfo s390_virtio_blk = { @@ -545,7 +558,7 @@ static void s390_virtio_serial_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_serial_init; + k->realize = s390_virtio_serial_realize; dc->props = s390_virtio_serial_properties; } @@ -567,7 +580,7 @@ static void s390_virtio_rng_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_rng_init; + k->realize = s390_virtio_rng_realize; dc->props = s390_virtio_rng_properties; } @@ -579,14 +592,14 @@ static const TypeInfo s390_virtio_rng = { .class_init = s390_virtio_rng_class_init, }; -static int s390_virtio_busdev_init(DeviceState *dev) +static void s390_virtio_busdev_realize(DeviceState *dev, Error **errp) { VirtIOS390Device *_dev = (VirtIOS390Device *)dev; VirtIOS390DeviceClass *_info = VIRTIO_S390_DEVICE_GET_CLASS(dev); virtio_s390_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); - return _info->init(_dev); + _info->realize(_dev, errp); } static void s390_virtio_busdev_reset(DeviceState *dev) @@ -600,7 +613,7 @@ static void virtio_s390_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = s390_virtio_busdev_init; + dc->realize = s390_virtio_busdev_realize; dc->bus_type = TYPE_S390_VIRTIO_BUS; dc->reset = s390_virtio_busdev_reset; } @@ -625,7 +638,7 @@ static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_virtio_scsi_init; + k->realize = s390_virtio_scsi_realize; dc->props = s390_virtio_scsi_properties; } @@ -648,7 +661,7 @@ static void s390_vhost_scsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); - k->init = s390_vhost_scsi_init; + k->realize = s390_vhost_scsi_realize; dc->props = s390_vhost_scsi_properties; } diff --git a/hw/s390x/s390-virtio-bus.h b/hw/s390x/s390-virtio-bus.h index 92aa9d0..810a6ef 100644 --- a/hw/s390x/s390-virtio-bus.h +++ b/hw/s390x/s390-virtio-bus.h @@ -83,7 +83,7 @@ typedef struct VirtIOS390Device VirtIOS390Device; typedef struct VirtIOS390DeviceClass { DeviceClass qdev; - int (*init)(VirtIOS390Device *dev); + void (*realize)(VirtIOS390Device *dev, Error **errp); } VirtIOS390DeviceClass; struct VirtIOS390Device { diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 8f0ae59..dac00ce 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -97,6 +97,7 @@ static void ccw_init(MachineState *machine) ram_addr_t pad_size = 0; ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size); ram_addr_t standby_mem_size = maxmem - my_ram_size; + uint64_t kvm_limit; /* The storage increment size is a multiple of 1M and is a power of 2. * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. @@ -121,6 +122,15 @@ static void ccw_init(MachineState *machine) /* let's propagate the changed ram size into the global variable. */ ram_size = my_ram_size; + machine->maxram_size = my_ram_size + standby_mem_size; + + ret = s390_set_memory_limit(machine->maxram_size, &kvm_limit); + if (ret == -E2BIG) { + hw_error("qemu: host supports a maximum of %" PRIu64 " GB", + kvm_limit >> 30); + } else if (ret) { + hw_error("qemu: setting the guest size failed"); + } /* get a BUS */ css_bus = virtual_css_bus_init(); diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index ffbb9c2..fce52a9 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -607,7 +607,8 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) return ret; } -static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) +static void virtio_ccw_device_realize(VirtioCcwDevice *dev, + VirtIODevice *vdev, Error **errp) { unsigned int cssid = 0; unsigned int ssid = 0; @@ -616,7 +617,6 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) bool have_devno = false; bool found = false; SubchDev *sch; - int ret; int num; DeviceState *parent = DEVICE(dev); @@ -639,21 +639,19 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno); if (num == 3) { if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) { - ret = -EINVAL; - error_report("Invalid cssid or ssid: cssid %x, ssid %x", - cssid, ssid); + error_setg(errp, "Invalid cssid or ssid: cssid %x, ssid %x", + cssid, ssid); goto out_err; } /* Enforce use of virtual cssid. */ if (cssid != VIRTUAL_CSSID) { - ret = -EINVAL; - error_report("cssid %x not valid for virtio devices", cssid); + error_setg(errp, "cssid %x not valid for virtio devices", + cssid); goto out_err; } if (css_devno_used(cssid, ssid, devno)) { - ret = -EEXIST; - error_report("Device %x.%x.%04x already exists", cssid, ssid, - devno); + error_setg(errp, "Device %x.%x.%04x already exists", + cssid, ssid, devno); goto out_err; } sch->cssid = cssid; @@ -661,8 +659,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) sch->devno = devno; have_devno = true; } else { - ret = -EINVAL; - error_report("Malformed devno parameter '%s'", dev->bus_id); + error_setg(errp, "Malformed devno parameter '%s'", dev->bus_id); goto out_err; } } @@ -678,9 +675,8 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) } } if (!found) { - ret = -ENODEV; - error_report("No free subchannel found for %x.%x.%04x", cssid, ssid, - devno); + error_setg(errp, "No free subchannel found for %x.%x.%04x", + cssid, ssid, devno); goto out_err; } trace_virtio_ccw_new_device(cssid, ssid, schid, devno, @@ -702,8 +698,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) if (devno == MAX_SCHID) { devno = 0; } else if (devno == schid - 1) { - ret = -ENODEV; - error_report("No free devno found"); + error_setg(errp, "No free devno found"); goto out_err; } else { devno++; @@ -720,8 +715,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) } } if (!found) { - ret = -ENODEV; - error_report("Virtual channel subsystem is full!"); + error_setg(errp, "Virtual channel subsystem is full!"); goto out_err; } trace_virtio_ccw_new_device(cssid, ssid, schid, devno, @@ -748,12 +742,11 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, parent->hotplugged, 1); - return 0; + return; out_err: dev->sch = NULL; g_free(sch); - return ret; } static int virtio_ccw_exit(VirtioCcwDevice *dev) @@ -771,21 +764,24 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev) return 0; } -static int virtio_ccw_net_init(VirtioCcwDevice *ccw_dev) +static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp) { DeviceState *qdev = DEVICE(ccw_dev); VirtIONetCcw *dev = VIRTIO_NET_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; virtio_net_set_config_size(&dev->vdev, ccw_dev->host_features[0]); virtio_net_set_netclient_name(&dev->vdev, qdev->id, object_get_typename(OBJECT(qdev))); qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void virtio_ccw_net_instance_init(Object *obj) @@ -798,16 +794,20 @@ static void virtio_ccw_net_instance_init(Object *obj) "bootindex", &error_abort); } -static int virtio_ccw_blk_init(VirtioCcwDevice *ccw_dev) +static void virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void virtio_ccw_blk_instance_init(Object *obj) @@ -822,11 +822,12 @@ static void virtio_ccw_blk_instance_init(Object *obj) "bootindex", &error_abort); } -static int virtio_ccw_serial_init(VirtioCcwDevice *ccw_dev) +static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); DeviceState *proxy = DEVICE(ccw_dev); + Error *err = NULL; char *bus_name; /* @@ -840,11 +841,13 @@ static int virtio_ccw_serial_init(VirtioCcwDevice *ccw_dev) } qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } @@ -856,17 +859,20 @@ static void virtio_ccw_serial_instance_init(Object *obj) TYPE_VIRTIO_SERIAL); } -static int virtio_ccw_balloon_init(VirtioCcwDevice *ccw_dev) +static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void balloon_ccw_stats_get_all(Object *obj, struct Visitor *v, @@ -909,11 +915,12 @@ static void virtio_ccw_balloon_instance_init(Object *obj) NULL, dev, NULL); } -static int virtio_ccw_scsi_init(VirtioCcwDevice *ccw_dev) +static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); DeviceState *qdev = DEVICE(ccw_dev); + Error *err = NULL; char *bus_name; /* @@ -927,11 +934,13 @@ static int virtio_ccw_scsi_init(VirtioCcwDevice *ccw_dev) } qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void virtio_ccw_scsi_instance_init(Object *obj) @@ -945,17 +954,20 @@ static void virtio_ccw_scsi_instance_init(Object *obj) } #ifdef CONFIG_VHOST_SCSI -static int vhost_ccw_scsi_init(VirtioCcwDevice *ccw_dev) +static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } static void vhost_ccw_scsi_instance_init(Object *obj) @@ -967,21 +979,24 @@ static void vhost_ccw_scsi_instance_init(Object *obj) } #endif -static int virtio_ccw_rng_init(VirtioCcwDevice *ccw_dev) +static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VirtIORNGCcw *dev = VIRTIO_RNG_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - if (qdev_init(vdev) < 0) { - return -1; + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } object_property_set_link(OBJECT(dev), OBJECT(dev->vdev.conf.rng), "rng", NULL); - return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); + virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp); } /* DeviceState to VirtioCcwDevice. Note: used on datapath, @@ -1391,7 +1406,7 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_net_init; + k->realize = virtio_ccw_net_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_net_properties; @@ -1417,7 +1432,7 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_blk_init; + k->realize = virtio_ccw_blk_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_blk_properties; @@ -1443,7 +1458,7 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_serial_init; + k->realize = virtio_ccw_serial_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_serial_properties; @@ -1469,7 +1484,7 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_balloon_init; + k->realize = virtio_ccw_balloon_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_balloon_properties; @@ -1496,7 +1511,7 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_scsi_init; + k->realize = virtio_ccw_scsi_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_scsi_properties; @@ -1521,7 +1536,7 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = vhost_ccw_scsi_init; + k->realize = vhost_ccw_scsi_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = vhost_ccw_scsi_properties; @@ -1558,7 +1573,7 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - k->init = virtio_ccw_rng_init; + k->realize = virtio_ccw_rng_realize; k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_rng_properties; @@ -1572,14 +1587,13 @@ static const TypeInfo virtio_ccw_rng = { .class_init = virtio_ccw_rng_class_init, }; -static int virtio_ccw_busdev_init(DeviceState *dev) +static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) { VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); - - return _info->init(_dev); + _info->realize(_dev, errp); } static int virtio_ccw_busdev_exit(DeviceState *dev) @@ -1622,7 +1636,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->props = virtio_ccw_properties; - dc->init = virtio_ccw_busdev_init; + dc->realize = virtio_ccw_busdev_realize; dc->exit = virtio_ccw_busdev_exit; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; } diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 5a1f16e..4fceda7 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -64,7 +64,7 @@ typedef struct VirtioCcwDevice VirtioCcwDevice; typedef struct VirtIOCCWDeviceClass { DeviceClass parent_class; - int (*init)(VirtioCcwDevice *dev); + void (*realize)(VirtioCcwDevice *dev, Error **errp); int (*exit)(VirtioCcwDevice *dev); } VirtIOCCWDeviceClass; diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 00b7297..8d2242d 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -342,13 +342,12 @@ static const struct SCSIBusInfo esp_pci_scsi_info = { .cancel = esp_request_cancelled, }; -static int esp_pci_scsi_init(PCIDevice *dev) +static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) { PCIESPState *pci = PCI_ESP(dev); DeviceState *d = DEVICE(dev); ESPState *s = &pci->esp; uint8_t *pci_conf; - Error *err = NULL; pci_conf = dev->config; @@ -367,13 +366,8 @@ static int esp_pci_scsi_init(PCIDevice *dev) scsi_bus_new(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info, NULL); if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void esp_pci_scsi_uninit(PCIDevice *d) @@ -388,7 +382,7 @@ 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->realize = esp_pci_scsi_realize; k->exit = esp_pci_scsi_uninit; k->vendor_id = PCI_VENDOR_ID_AMD; k->device_id = PCI_DEVICE_ID_AMD_SCSI; @@ -466,17 +460,19 @@ static void dc390_write_config(PCIDevice *dev, } } -static int dc390_scsi_init(PCIDevice *dev) +static void dc390_scsi_realize(PCIDevice *dev, Error **errp) { DC390State *pci = DC390(dev); + Error *err = NULL; uint8_t *contents; uint16_t chksum = 0; - int i, ret; + int i; /* init base class */ - ret = esp_pci_scsi_init(dev); - if (ret < 0) { - return ret; + esp_pci_scsi_realize(dev, &err); + if (err) { + error_propagate(errp, err); + return; } /* EEPROM */ @@ -503,8 +499,6 @@ static int dc390_scsi_init(PCIDevice *dev) chksum = 0x1234 - chksum; contents[EE_CHKSUM1] = chksum & 0xff; contents[EE_CHKSUM2] = chksum >> 8; - - return 0; } static void dc390_class_init(ObjectClass *klass, void *data) @@ -512,7 +506,7 @@ 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->realize = dc390_scsi_realize; k->config_read = dc390_read_config; k->config_write = dc390_write_config; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index db7d4b8..c5b0cc5 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -19,7 +19,6 @@ #include "hw/pci/pci.h" #include "hw/scsi/scsi.h" #include "sysemu/dma.h" -#include "qemu/error-report.h" //#define DEBUG_LSI //#define DEBUG_LSI_REG @@ -2089,12 +2088,11 @@ static const struct SCSIBusInfo lsi_scsi_info = { .cancel = lsi_request_cancelled }; -static int lsi_scsi_init(PCIDevice *dev) +static void lsi_scsi_realize(PCIDevice *dev, Error **errp) { LSIState *s = LSI53C895A(dev); DeviceState *d = DEVICE(dev); uint8_t *pci_conf; - Error *err = NULL; pci_conf = dev->config; @@ -2117,13 +2115,8 @@ static int lsi_scsi_init(PCIDevice *dev) scsi_bus_new(&s->bus, sizeof(s->bus), d, &lsi_scsi_info, NULL); if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void lsi_class_init(ObjectClass *klass, void *data) @@ -2131,7 +2124,7 @@ static void lsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = lsi_scsi_init; + k->realize = lsi_scsi_realize; k->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; k->device_id = PCI_DEVICE_ID_LSI_53C895A; k->class_id = PCI_CLASS_STORAGE_SCSI; diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 4852237..bf83b65 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2320,14 +2320,13 @@ static const struct SCSIBusInfo megasas_scsi_info = { .cancel = megasas_command_cancel, }; -static int megasas_scsi_init(PCIDevice *dev) +static void megasas_scsi_realize(PCIDevice *dev, Error **errp) { DeviceState *d = DEVICE(dev); MegasasState *s = MEGASAS(dev); MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s); uint8_t *pci_conf; int i, bar_type; - Error *err = NULL; pci_conf = dev->config; @@ -2407,13 +2406,8 @@ static int megasas_scsi_init(PCIDevice *dev) scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), &megasas_scsi_info, NULL); if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - return 0; } static void @@ -2507,7 +2501,7 @@ static void megasas_class_init(ObjectClass *oc, void *data) MegasasBaseClass *e = MEGASAS_DEVICE_CLASS(oc); const MegasasInfo *info = data; - pc->init = megasas_scsi_init; + pc->realize = megasas_scsi_realize; pc->exit = megasas_scsi_uninit; pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; pc->device_id = info->device_id; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index dca9576..bd2c0e4 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -242,8 +242,9 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, if (serial && object_property_find(OBJECT(dev), "serial", NULL)) { qdev_prop_set_string(dev, "serial", serial); } - if (qdev_prop_set_drive(dev, "drive", blk) < 0) { - error_setg(errp, "Setting drive property failed"); + qdev_prop_set_drive(dev, "drive", blk, &err); + if (err) { + error_propagate(errp, err); object_unparent(OBJECT(dev)); return NULL; } @@ -273,7 +274,6 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp) scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), unit, false, -1, NULL, &err); if (err != NULL) { - error_report("%s", error_get_pretty(err)); error_propagate(errp, err); break; } diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index f65618d..54d71f4 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2251,6 +2251,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) } blkconf_serial(&s->qdev.conf, &s->serial); + blkconf_blocksizes(&s->qdev.conf); if (dev->type == TYPE_DISK) { blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err); if (err) { @@ -2290,6 +2291,12 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) static void scsi_hd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + /* can happen for devices without drive. The error message for missing + * backend will be issued in scsi_realize + */ + if (s->qdev.conf.blk) { + blkconf_blocksizes(&s->qdev.conf); + } s->qdev.blocksize = s->qdev.conf.logical_block_size; s->qdev.type = TYPE_DISK; if (!s->product) { diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 09afccf..b879aa9 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -127,7 +127,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } -static void nvram_init(M48t59State *nvram, uint8_t *macaddr, +static void nvram_init(Nvram *nvram, uint8_t *macaddr, const char *cmdline, const char *boot_devices, ram_addr_t RAM_size, uint32_t kernel_size, int width, int height, int depth, @@ -137,6 +137,7 @@ static void nvram_init(M48t59State *nvram, uint8_t *macaddr, uint32_t start, end; uint8_t image[0x1ff0]; struct OpenBIOS_nvpart_v1 *part_header; + NvramClass *k = NVRAM_GET_CLASS(nvram); memset(image, '\0', sizeof(image)); @@ -170,8 +171,9 @@ static void nvram_init(M48t59State *nvram, uint8_t *macaddr, Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, nvram_machine_id); - for (i = 0; i < sizeof(image); i++) - m48t59_write(nvram, i, image[i]); + for (i = 0; i < sizeof(image); i++) { + (k->write)(nvram, i, image[i]); + } } static DeviceState *slavio_intctl; @@ -1012,7 +1014,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq); - nvram = m48t59_init(slavio_irq[0], hwdef->nvram_base, 0, 0x2000, 8); + nvram = m48t59_init(slavio_irq[0], hwdef->nvram_base, 0, 0x2000, 1968, 8); slavio_timer_init_all(hwdef->counter_base, slavio_irq[19], slavio_cpu_irq, smp_cpus); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index b310588..f027caf 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -130,7 +130,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } -static int sun4u_NVRAM_set_params(M48t59State *nvram, uint16_t NVRAM_size, +static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size, const char *arch, ram_addr_t RAM_size, const char *boot_devices, uint32_t kernel_image, uint32_t kernel_size, @@ -144,6 +144,7 @@ static int sun4u_NVRAM_set_params(M48t59State *nvram, uint16_t NVRAM_size, uint32_t start, end; uint8_t image[0x1ff0]; struct OpenBIOS_nvpart_v1 *part_header; + NvramClass *k = NVRAM_GET_CLASS(nvram); memset(image, '\0', sizeof(image)); @@ -176,8 +177,9 @@ static int sun4u_NVRAM_set_params(M48t59State *nvram, uint16_t NVRAM_size, Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, 0x80); - for (i = 0; i < sizeof(image); i++) - m48t59_write(nvram, i, image[i]); + for (i = 0; i < sizeof(image); i++) { + (k->write)(nvram, i, image[i]); + } return 0; } @@ -610,7 +612,7 @@ pci_ebus_init1(PCIDevice *pci_dev) 0, 0x1000000); pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), - 0, 0x1000); + 0, 0x4000); pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); return 0; } @@ -818,11 +820,12 @@ static void sun4uv_init(MemoryRegion *address_space_mem, const struct hwdef *hwdef) { SPARCCPU *cpu; - M48t59State *nvram; + Nvram *nvram; unsigned int i; uint64_t initrd_addr, initrd_size, kernel_addr, kernel_size, kernel_entry; PCIBus *pci_bus, *pci_bus2, *pci_bus3; ISABus *isa_bus; + SysBusDevice *s; qemu_irq *ivec_irqs, *pbm_irqs; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DriveInfo *fd[MAX_FD]; @@ -866,8 +869,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem, fd[i] = drive_get(IF_FLOPPY, 0, i); } fdctrl_init_isa(isa_bus, fd); - nvram = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 59); + /* Map NVRAM into I/O (ebus) space */ + nvram = m48t59_init(NULL, 0, 0, NVRAM_SIZE, 1968, 59); + s = SYS_BUS_DEVICE(nvram); + memory_region_add_subregion(get_system_io(), 0x2000, + sysbus_mmio_get_region(s, 0)); + initrd_size = 0; initrd_addr = 0; kernel_size = sun4u_load_kernel(machine->kernel_filename, diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 2c86c3d..133bd0d 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -31,3 +31,5 @@ obj-$(CONFIG_DIGIC) += digic-timer.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o + +common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index 31509d5..8ab683d 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -2,6 +2,7 @@ * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms * * Copyright (c) 2003-2005, 2007 Jocelyn Mayer + * Copyright (c) 2013 Hervé 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 @@ -37,12 +38,35 @@ #define NVRAM_PRINTF(fmt, ...) do { } while (0) #endif +#define TYPE_M48TXX_SYS_BUS "sysbus-m48txx" +#define M48TXX_SYS_BUS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(M48txxSysBusDeviceClass, (obj), TYPE_M48TXX_SYS_BUS) +#define M48TXX_SYS_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(M48txxSysBusDeviceClass, (klass), TYPE_M48TXX_SYS_BUS) +#define M48TXX_SYS_BUS(obj) \ + OBJECT_CHECK(M48txxSysBusState, (obj), TYPE_M48TXX_SYS_BUS) + +#define TYPE_M48TXX_ISA "isa-m48txx" +#define M48TXX_ISA_GET_CLASS(obj) \ + OBJECT_GET_CLASS(M48txxISADeviceClass, (obj), TYPE_M48TXX_ISA) +#define M48TXX_ISA_CLASS(klass) \ + OBJECT_CLASS_CHECK(M48txxISADeviceClass, (klass), TYPE_M48TXX_ISA) +#define M48TXX_ISA(obj) \ + OBJECT_CHECK(M48txxISAState, (obj), TYPE_M48TXX_ISA) + /* * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has * alarm and a watchdog timer and related control registers. In the * PPC platform there is also a nvram lock function. */ +typedef struct M48txxInfo { + const char *isa_name; + const char *sysbus_name; + uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */ + uint32_t size; +} M48txxInfo; + /* * Chipset docs: * http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf @@ -50,12 +74,12 @@ * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf */ -struct M48t59State { +typedef struct M48t59State { /* Hardware parameters */ qemu_irq IRQ; MemoryRegion iomem; - uint32_t io_base; uint32_t size; + int32_t base_year; /* RTC management */ time_t time_offset; time_t stop_time; @@ -70,28 +94,51 @@ struct M48t59State { /* NVRAM storage */ uint16_t addr; uint8_t lock; -}; - -#define TYPE_ISA_M48T59 "m48t59_isa" -#define ISA_M48T59(obj) \ - OBJECT_CHECK(M48t59ISAState, (obj), TYPE_ISA_M48T59) +} M48t59State; -typedef struct M48t59ISAState { +typedef struct M48txxISAState { ISADevice parent_obj; - M48t59State state; + uint32_t io_base; MemoryRegion io; -} M48t59ISAState; +} M48txxISAState; -#define SYSBUS_M48T59(obj) \ - OBJECT_CHECK(M48t59SysBusState, (obj), TYPE_SYSBUS_M48T59) +typedef struct M48txxISADeviceClass { + ISADeviceClass parent_class; + M48txxInfo info; +} M48txxISADeviceClass; -typedef struct M48t59SysBusState { +typedef struct M48txxSysBusState { SysBusDevice parent_obj; - M48t59State state; MemoryRegion io; -} M48t59SysBusState; +} M48txxSysBusState; + +typedef struct M48txxSysBusDeviceClass { + SysBusDeviceClass parent_class; + M48txxInfo info; +} M48txxSysBusDeviceClass; + +static M48txxInfo m48txx_info[] = { + { + .sysbus_name = "sysbus-m48t02", + .model = 2, + .size = 0x800, + },{ + .sysbus_name = "sysbus-m48t08", + .model = 8, + .size = 0x2000, + },{ + .sysbus_name = "sysbus-m48t59", + .model = 59, + .size = 0x2000, + },{ + .isa_name = "isa-m48t59", + .model = 59, + .size = 0x2000, + } +}; + /* Fake timer functions */ @@ -198,9 +245,8 @@ static void set_up_watchdog(M48t59State *NVRAM, uint8_t value) } /* Direct access to NVRAM */ -void m48t59_write (void *opaque, uint32_t addr, uint32_t val) +static void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) { - M48t59State *NVRAM = opaque; struct tm tm; int tmp; @@ -346,11 +392,7 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val) tmp = from_bcd(val); if (tmp >= 0 && tmp <= 99) { get_time(NVRAM, &tm); - if (NVRAM->model == 8) { - tm.tm_year = from_bcd(val) + 68; // Base year is 1968 - } else { - tm.tm_year = from_bcd(val); - } + tm.tm_year = from_bcd(val) + NVRAM->base_year - 1900; set_time(NVRAM, &tm); } break; @@ -368,9 +410,8 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val) } } -uint32_t m48t59_read (void *opaque, uint32_t addr) +static uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) { - M48t59State *NVRAM = opaque; struct tm tm; uint32_t retval = 0xFF; @@ -453,11 +494,7 @@ uint32_t m48t59_read (void *opaque, uint32_t addr) case 0x07FF: /* year */ get_time(NVRAM, &tm); - if (NVRAM->model == 8) { - retval = to_bcd(tm.tm_year - 68); // Base year is 1968 - } else { - retval = to_bcd(tm.tm_year); - } + retval = to_bcd((tm.tm_year + 1900 - NVRAM->base_year) % 100); break; default: /* Check lock registers state */ @@ -477,10 +514,8 @@ uint32_t m48t59_read (void *opaque, uint32_t addr) return retval; } -void m48t59_toggle_lock (void *opaque, int lock) +static void m48t59_toggle_lock(M48t59State *NVRAM, int lock) { - M48t59State *NVRAM = opaque; - NVRAM->lock ^= 1 << lock; } @@ -616,7 +651,7 @@ static void m48t59_reset_common(M48t59State *NVRAM) static void m48t59_reset_isa(DeviceState *d) { - M48t59ISAState *isa = ISA_M48T59(d); + M48txxISAState *isa = M48TXX_ISA(d); M48t59State *NVRAM = &isa->state; m48t59_reset_common(NVRAM); @@ -624,7 +659,7 @@ static void m48t59_reset_isa(DeviceState *d) static void m48t59_reset_sysbus(DeviceState *d) { - M48t59SysBusState *sys = SYSBUS_M48T59(d); + M48txxSysBusState *sys = M48TXX_SYS_BUS(d); M48t59State *NVRAM = &sys->state; m48t59_reset_common(NVRAM); @@ -641,58 +676,63 @@ static const MemoryRegionOps m48t59_io_ops = { }; /* Initialisation routine */ -M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base, - uint32_t io_base, uint16_t size, int model) +Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, + uint32_t io_base, uint16_t size, int base_year, + int model) { DeviceState *dev; SysBusDevice *s; - M48t59SysBusState *d; - M48t59State *state; - - dev = qdev_create(NULL, TYPE_SYSBUS_M48T59); - qdev_prop_set_uint32(dev, "model", model); - qdev_prop_set_uint32(dev, "size", size); - qdev_prop_set_uint32(dev, "io_base", io_base); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - d = SYSBUS_M48T59(dev); - state = &d->state; - sysbus_connect_irq(s, 0, IRQ); - memory_region_init_io(&d->io, OBJECT(d), &m48t59_io_ops, state, - "m48t59", 4); - if (io_base != 0) { - memory_region_add_subregion(get_system_io(), io_base, &d->io); - } - if (mem_base != 0) { - sysbus_mmio_map(s, 0, mem_base); + int i; + + for (i = 0; i < ARRAY_SIZE(m48txx_info); i++) { + if (!m48txx_info[i].sysbus_name || + m48txx_info[i].size != size || + m48txx_info[i].model != model) { + continue; + } + + dev = qdev_create(NULL, m48txx_info[i].sysbus_name); + qdev_prop_set_int32(dev, "base-year", base_year); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(s, 0, IRQ); + if (io_base != 0) { + memory_region_add_subregion(get_system_io(), io_base, + sysbus_mmio_get_region(s, 1)); + } + if (mem_base != 0) { + sysbus_mmio_map(s, 0, mem_base); + } + + return NVRAM(s); } - return state; + assert(false); + return NULL; } -M48t59State *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, - int model) +Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, + int base_year, int model) { - M48t59ISAState *d; - ISADevice *isadev; DeviceState *dev; - M48t59State *s; - - isadev = isa_create(bus, TYPE_ISA_M48T59); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "model", model); - qdev_prop_set_uint32(dev, "size", size); - qdev_prop_set_uint32(dev, "io_base", io_base); - qdev_init_nofail(dev); - d = ISA_M48T59(isadev); - s = &d->state; - - memory_region_init_io(&d->io, OBJECT(d), &m48t59_io_ops, s, "m48t59", 4); - if (io_base != 0) { - isa_register_ioport(isadev, &d->io, io_base); + int i; + + for (i = 0; i < ARRAY_SIZE(m48txx_info); i++) { + if (!m48txx_info[i].isa_name || + m48txx_info[i].size != size || + m48txx_info[i].model != model) { + continue; + } + + dev = DEVICE(isa_create(bus, m48txx_info[i].isa_name)); + qdev_prop_set_uint32(dev, "iobase", io_base); + qdev_prop_set_int32(dev, "base-year", base_year); + qdev_init_nofail(dev); + return NVRAM(dev); } - return s; + assert(false); + return NULL; } static void m48t59_realize_common(M48t59State *s, Error **errp) @@ -709,25 +749,38 @@ static void m48t59_realize_common(M48t59State *s, Error **errp) static void m48t59_isa_realize(DeviceState *dev, Error **errp) { + M48txxISADeviceClass *u = M48TXX_ISA_GET_CLASS(dev); ISADevice *isadev = ISA_DEVICE(dev); - M48t59ISAState *d = ISA_M48T59(dev); + M48txxISAState *d = M48TXX_ISA(dev); M48t59State *s = &d->state; + s->model = u->info.model; + s->size = u->info.size; isa_init_irq(isadev, &s->IRQ, 8); m48t59_realize_common(s, errp); + memory_region_init_io(&d->io, OBJECT(dev), &m48t59_io_ops, s, "m48t59", 4); + if (d->io_base != 0) { + isa_register_ioport(isadev, &d->io, d->io_base); + } } static int m48t59_init1(SysBusDevice *dev) { - M48t59SysBusState *d = SYSBUS_M48T59(dev); + M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(dev); + M48txxSysBusState *d = M48TXX_SYS_BUS(dev); + Object *o = OBJECT(dev); M48t59State *s = &d->state; Error *err = NULL; + s->model = u->info.model; + s->size = u->info.size; sysbus_init_irq(dev, &s->IRQ); - memory_region_init_io(&s->iomem, OBJECT(d), &nvram_ops, s, - "m48t59.nvram", s->size); + memory_region_init_io(&s->iomem, o, &nvram_ops, s, "m48t59.nvram", + s->size); + memory_region_init_io(&d->io, o, &m48t59_io_ops, s, "m48t59", 4); sysbus_init_mmio(dev, &s->iomem); + sysbus_init_mmio(dev, &d->io); m48t59_realize_common(s, &err); if (err != NULL) { error_free(err); @@ -737,59 +790,157 @@ static int m48t59_init1(SysBusDevice *dev) return 0; } +static uint32_t m48txx_isa_read(Nvram *obj, uint32_t addr) +{ + M48txxISAState *d = M48TXX_ISA(obj); + return m48t59_read(&d->state, addr); +} + +static void m48txx_isa_write(Nvram *obj, uint32_t addr, uint32_t val) +{ + M48txxISAState *d = M48TXX_ISA(obj); + m48t59_write(&d->state, addr, val); +} + +static void m48txx_isa_toggle_lock(Nvram *obj, int lock) +{ + M48txxISAState *d = M48TXX_ISA(obj); + m48t59_toggle_lock(&d->state, lock); +} + static Property m48t59_isa_properties[] = { - DEFINE_PROP_UINT32("size", M48t59ISAState, state.size, -1), - DEFINE_PROP_UINT32("model", M48t59ISAState, state.model, -1), - DEFINE_PROP_UINT32("io_base", M48t59ISAState, state.io_base, 0), + DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0), + DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74), DEFINE_PROP_END_OF_LIST(), }; -static void m48t59_isa_class_init(ObjectClass *klass, void *data) +static void m48txx_isa_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + NvramClass *nc = NVRAM_CLASS(klass); dc->realize = m48t59_isa_realize; dc->reset = m48t59_reset_isa; dc->props = m48t59_isa_properties; - /* Reason: needs to be wired up by m48t59_init_isa() */ - dc->cannot_instantiate_with_device_add_yet = true; + nc->read = m48txx_isa_read; + nc->write = m48txx_isa_write; + nc->toggle_lock = m48txx_isa_toggle_lock; } -static const TypeInfo m48t59_isa_info = { - .name = TYPE_ISA_M48T59, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(M48t59ISAState), - .class_init = m48t59_isa_class_init, -}; +static void m48txx_isa_concrete_class_init(ObjectClass *klass, void *data) +{ + M48txxISADeviceClass *u = M48TXX_ISA_CLASS(klass); + M48txxInfo *info = data; + + u->info = *info; +} + +static uint32_t m48txx_sysbus_read(Nvram *obj, uint32_t addr) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + return m48t59_read(&d->state, addr); +} + +static void m48txx_sysbus_write(Nvram *obj, uint32_t addr, uint32_t val) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + m48t59_write(&d->state, addr, val); +} + +static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + m48t59_toggle_lock(&d->state, lock); +} -static Property m48t59_properties[] = { - DEFINE_PROP_UINT32("size", M48t59SysBusState, state.size, -1), - DEFINE_PROP_UINT32("model", M48t59SysBusState, state.model, -1), - DEFINE_PROP_UINT32("io_base", M48t59SysBusState, state.io_base, 0), +static Property m48t59_sysbus_properties[] = { + DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), DEFINE_PROP_END_OF_LIST(), }; -static void m48t59_class_init(ObjectClass *klass, void *data) +static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + NvramClass *nc = NVRAM_CLASS(klass); k->init = m48t59_init1; dc->reset = m48t59_reset_sysbus; - dc->props = m48t59_properties; + dc->props = m48t59_sysbus_properties; + nc->read = m48txx_sysbus_read; + nc->write = m48txx_sysbus_write; + nc->toggle_lock = m48txx_sysbus_toggle_lock; +} + +static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data) +{ + M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass); + M48txxInfo *info = data; + + u->info = *info; } -static const TypeInfo m48t59_info = { - .name = TYPE_SYSBUS_M48T59, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(M48t59SysBusState), - .class_init = m48t59_class_init, +static const TypeInfo nvram_info = { + .name = TYPE_NVRAM, + .parent = TYPE_INTERFACE, + .class_size = sizeof(NvramClass), +}; + +static const TypeInfo m48txx_sysbus_type_info = { + .name = TYPE_M48TXX_SYS_BUS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(M48txxSysBusState), + .abstract = true, + .class_init = m48txx_sysbus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NVRAM }, + { } + } +}; + +static const TypeInfo m48txx_isa_type_info = { + .name = TYPE_M48TXX_ISA, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(M48txxISAState), + .abstract = true, + .class_init = m48txx_isa_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NVRAM }, + { } + } }; static void m48t59_register_types(void) { - type_register_static(&m48t59_info); - type_register_static(&m48t59_isa_info); + TypeInfo sysbus_type_info = { + .parent = TYPE_M48TXX_SYS_BUS, + .class_size = sizeof(M48txxSysBusDeviceClass), + .class_init = m48txx_sysbus_concrete_class_init, + }; + TypeInfo isa_type_info = { + .parent = TYPE_M48TXX_ISA, + .class_size = sizeof(M48txxISADeviceClass), + .class_init = m48txx_isa_concrete_class_init, + }; + int i; + + type_register_static(&nvram_info); + type_register_static(&m48txx_sysbus_type_info); + type_register_static(&m48txx_isa_type_info); + + for (i = 0; i < ARRAY_SIZE(m48txx_info); i++) { + if (m48txx_info[i].sysbus_name) { + sysbus_type_info.name = m48txx_info[i].sysbus_name; + sysbus_type_info.class_data = &m48txx_info[i]; + type_register(&sysbus_type_info); + } + + if (m48txx_info[i].isa_name) { + isa_type_info.name = m48txx_info[i].isa_name; + isa_type_info.class_data = &m48txx_info[i]; + type_register(&isa_type_info); + } + } } type_init(m48t59_register_types) diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c new file mode 100644 index 0000000..ecadf9d --- /dev/null +++ b/hw/timer/stm32f2xx_timer.c @@ -0,0 +1,328 @@ +/* + * STM32F2XX Timer + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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 "hw/timer/stm32f2xx_timer.h" + +#ifndef STM_TIMER_ERR_DEBUG +#define STM_TIMER_ERR_DEBUG 0 +#endif + +#define DB_PRINT_L(lvl, fmt, args...) do { \ + if (STM_TIMER_ERR_DEBUG >= lvl) { \ + qemu_log("%s: " fmt, __func__, ## args); \ + } \ +} while (0); + +#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) + +static void stm32f2xx_timer_set_alarm(STM32F2XXTimerState *s, int64_t now); + +static void stm32f2xx_timer_interrupt(void *opaque) +{ + STM32F2XXTimerState *s = opaque; + + DB_PRINT("Interrupt\n"); + + if (s->tim_dier & TIM_DIER_UIE && s->tim_cr1 & TIM_CR1_CEN) { + s->tim_sr |= 1; + qemu_irq_pulse(s->irq); + stm32f2xx_timer_set_alarm(s, s->hit_time); + } +} + +static inline int64_t stm32f2xx_ns_to_ticks(STM32F2XXTimerState *s, int64_t t) +{ + return muldiv64(t, s->freq_hz, 1000000000ULL) / (s->tim_psc + 1); +} + +static void stm32f2xx_timer_set_alarm(STM32F2XXTimerState *s, int64_t now) +{ + uint64_t ticks; + int64_t now_ticks; + + if (s->tim_arr == 0) { + return; + } + + DB_PRINT("Alarm set at: 0x%x\n", s->tim_cr1); + + now_ticks = stm32f2xx_ns_to_ticks(s, now); + ticks = s->tim_arr - (now_ticks - s->tick_offset); + + DB_PRINT("Alarm set in %d ticks\n", (int) ticks); + + s->hit_time = muldiv64((ticks + (uint64_t) now_ticks) * (s->tim_psc + 1), + 1000000000ULL, s->freq_hz); + + timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hit_time); + DB_PRINT("Wait Time: %" PRId64 " ticks\n", s->hit_time); +} + +static void stm32f2xx_timer_reset(DeviceState *dev) +{ + STM32F2XXTimerState *s = STM32F2XXTIMER(dev); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + s->tim_cr1 = 0; + s->tim_cr2 = 0; + s->tim_smcr = 0; + s->tim_dier = 0; + s->tim_sr = 0; + s->tim_egr = 0; + s->tim_ccmr1 = 0; + s->tim_ccmr2 = 0; + s->tim_ccer = 0; + s->tim_psc = 0; + s->tim_arr = 0; + s->tim_ccr1 = 0; + s->tim_ccr2 = 0; + s->tim_ccr3 = 0; + s->tim_ccr4 = 0; + s->tim_dcr = 0; + s->tim_dmar = 0; + s->tim_or = 0; + + s->tick_offset = stm32f2xx_ns_to_ticks(s, now); +} + +static uint64_t stm32f2xx_timer_read(void *opaque, hwaddr offset, + unsigned size) +{ + STM32F2XXTimerState *s = opaque; + + DB_PRINT("Read 0x%"HWADDR_PRIx"\n", offset); + + switch (offset) { + case TIM_CR1: + return s->tim_cr1; + case TIM_CR2: + return s->tim_cr2; + case TIM_SMCR: + return s->tim_smcr; + case TIM_DIER: + return s->tim_dier; + case TIM_SR: + return s->tim_sr; + case TIM_EGR: + return s->tim_egr; + case TIM_CCMR1: + return s->tim_ccmr1; + case TIM_CCMR2: + return s->tim_ccmr2; + case TIM_CCER: + return s->tim_ccer; + case TIM_CNT: + return stm32f2xx_ns_to_ticks(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) - + s->tick_offset; + case TIM_PSC: + return s->tim_psc; + case TIM_ARR: + return s->tim_arr; + case TIM_CCR1: + return s->tim_ccr1; + case TIM_CCR2: + return s->tim_ccr2; + case TIM_CCR3: + return s->tim_ccr3; + case TIM_CCR4: + return s->tim_ccr4; + case TIM_DCR: + return s->tim_dcr; + case TIM_DMAR: + return s->tim_dmar; + case TIM_OR: + return s->tim_or; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset); + } + + return 0; +} + +static void stm32f2xx_timer_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + STM32F2XXTimerState *s = opaque; + uint32_t value = val64; + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint32_t timer_val = 0; + + DB_PRINT("Write 0x%x, 0x%"HWADDR_PRIx"\n", value, offset); + + switch (offset) { + case TIM_CR1: + s->tim_cr1 = value; + return; + case TIM_CR2: + s->tim_cr2 = value; + return; + case TIM_SMCR: + s->tim_smcr = value; + return; + case TIM_DIER: + s->tim_dier = value; + return; + case TIM_SR: + /* This is set by hardware and cleared by software */ + s->tim_sr &= value; + return; + case TIM_EGR: + s->tim_egr = value; + if (s->tim_egr & TIM_EGR_UG) { + timer_val = 0; + break; + } + return; + case TIM_CCMR1: + s->tim_ccmr1 = value; + return; + case TIM_CCMR2: + s->tim_ccmr2 = value; + return; + case TIM_CCER: + s->tim_ccer = value; + return; + case TIM_PSC: + timer_val = stm32f2xx_ns_to_ticks(s, now) - s->tick_offset; + s->tim_psc = value; + value = timer_val; + break; + case TIM_CNT: + timer_val = value; + break; + case TIM_ARR: + s->tim_arr = value; + stm32f2xx_timer_set_alarm(s, now); + return; + case TIM_CCR1: + s->tim_ccr1 = value; + return; + case TIM_CCR2: + s->tim_ccr2 = value; + return; + case TIM_CCR3: + s->tim_ccr3 = value; + return; + case TIM_CCR4: + s->tim_ccr4 = value; + return; + case TIM_DCR: + s->tim_dcr = value; + return; + case TIM_DMAR: + s->tim_dmar = value; + return; + case TIM_OR: + s->tim_or = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset); + return; + } + + /* This means that a register write has affected the timer in a way that + * requires a refresh of both tick_offset and the alarm. + */ + s->tick_offset = stm32f2xx_ns_to_ticks(s, now) - timer_val; + stm32f2xx_timer_set_alarm(s, now); +} + +static const MemoryRegionOps stm32f2xx_timer_ops = { + .read = stm32f2xx_timer_read, + .write = stm32f2xx_timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_stm32f2xx_timer = { + .name = TYPE_STM32F2XX_TIMER, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(tick_offset, STM32F2XXTimerState), + VMSTATE_UINT32(tim_cr1, STM32F2XXTimerState), + VMSTATE_UINT32(tim_cr2, STM32F2XXTimerState), + VMSTATE_UINT32(tim_smcr, STM32F2XXTimerState), + VMSTATE_UINT32(tim_dier, STM32F2XXTimerState), + VMSTATE_UINT32(tim_sr, STM32F2XXTimerState), + VMSTATE_UINT32(tim_egr, STM32F2XXTimerState), + VMSTATE_UINT32(tim_ccmr1, STM32F2XXTimerState), + VMSTATE_UINT32(tim_ccmr2, STM32F2XXTimerState), + VMSTATE_UINT32(tim_ccer, STM32F2XXTimerState), + VMSTATE_UINT32(tim_psc, STM32F2XXTimerState), + VMSTATE_UINT32(tim_arr, STM32F2XXTimerState), + VMSTATE_UINT32(tim_ccr1, STM32F2XXTimerState), + VMSTATE_UINT32(tim_ccr2, STM32F2XXTimerState), + VMSTATE_UINT32(tim_ccr3, STM32F2XXTimerState), + VMSTATE_UINT32(tim_ccr4, STM32F2XXTimerState), + VMSTATE_UINT32(tim_dcr, STM32F2XXTimerState), + VMSTATE_UINT32(tim_dmar, STM32F2XXTimerState), + VMSTATE_UINT32(tim_or, STM32F2XXTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static Property stm32f2xx_timer_properties[] = { + DEFINE_PROP_UINT64("clock-frequency", struct STM32F2XXTimerState, + freq_hz, 1000000000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32f2xx_timer_init(Object *obj) +{ + STM32F2XXTimerState *s = STM32F2XXTIMER(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->iomem, obj, &stm32f2xx_timer_ops, s, + "stm32f2xx_timer", 0x4000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); + + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32f2xx_timer_interrupt, s); +} + +static void stm32f2xx_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32f2xx_timer_reset; + dc->props = stm32f2xx_timer_properties; + dc->vmsd = &vmstate_stm32f2xx_timer; +} + +static const TypeInfo stm32f2xx_timer_info = { + .name = TYPE_STM32F2XX_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F2XXTimerState), + .instance_init = stm32f2xx_timer_init, + .class_init = stm32f2xx_timer_class_init, +}; + +static void stm32f2xx_timer_register_types(void) +{ + type_register_static(&stm32f2xx_timer_info); +} + +type_init(stm32f2xx_timer_register_types) diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index c41499e..cc9a21a 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -109,6 +109,7 @@ static void puv3_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; CPUUniCore32State *env; + UniCore32CPU *cpu; if (initrd_filename) { hw_error("Please use kernel built-in initramdisk.\n"); @@ -118,10 +119,11 @@ static void puv3_init(MachineState *machine) cpu_model = "UniCore-II"; } - env = cpu_init(cpu_model); - if (!env) { + cpu = uc32_cpu_init(cpu_model); + if (!cpu) { hw_error("Unable to find CPU definition\n"); } + env = &cpu->env; puv3_soc_init(env); puv3_board_init(env, ram_size); diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 65d9aa6..dacefd7 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -611,6 +611,7 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) } blkconf_serial(&s->conf, &dev->serial); + blkconf_blocksizes(&s->conf); /* * Hack alert: this pretends to be a block device, but it's really @@ -663,6 +664,7 @@ static void usb_msd_realize_bot(USBDevice *dev, Error **errp) static USBDevice *usb_msd_init(USBBus *bus, const char *filename) { static int nr=0; + Error *err = NULL; char id[8]; QemuOpts *opts; DriveInfo *dinfo; @@ -706,8 +708,10 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) /* create guest device */ dev = usb_create(bus, "usb-storage"); - if (qdev_prop_set_drive(&dev->qdev, "drive", - blk_by_legacy_dinfo(dinfo)) < 0) { + qdev_prop_set_drive(&dev->qdev, "drive", blk_by_legacy_dinfo(dinfo), + &err); + if (err) { + error_report_err(err); object_unparent(OBJECT(dev)); return NULL; } diff --git a/include/block/block.h b/include/block/block.h index 649c269..473c746 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -60,6 +60,17 @@ typedef enum { BDRV_REQ_MAY_UNMAP = 0x4, } BdrvRequestFlags; +typedef struct BlockSizes { + uint32_t phys; + uint32_t log; +} BlockSizes; + +typedef struct HDGeometry { + uint32_t heads; + uint32_t sectors; + uint32_t cylinders; +} HDGeometry; + #define BDRV_O_RDWR 0x0002 #define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */ #define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ @@ -550,6 +561,8 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs); * This function must be called with iothread lock held. */ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context); +int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); +int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); void bdrv_io_plug(BlockDriverState *bs); void bdrv_io_unplug(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index b340e7e..dccb092 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -56,6 +56,8 @@ #define BLOCK_OPT_ADAPTER_TYPE "adapter_type" #define BLOCK_OPT_REDUNDANCY "redundancy" #define BLOCK_OPT_NOCOW "nocow" +#define BLOCK_OPT_OBJECT_SIZE "object_size" +#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" #define BLOCK_PROBE_BUF_SIZE 512 @@ -273,6 +275,21 @@ struct BlockDriver { void (*bdrv_io_unplug)(BlockDriverState *bs); void (*bdrv_flush_io_queue)(BlockDriverState *bs); + /** + * Try to get @bs's logical and physical block size. + * On success, store them in @bsz and return zero. + * On failure, return negative errno. + */ + int (*bdrv_probe_blocksizes)(BlockDriverState *bs, BlockSizes *bsz); + /** + * Try to get @bs's geometry (cyls, heads, sectors) + * On success, store them in @geo and return 0. + * On failure return -errno. + * Only drivers that want to override guest geometry implement this + * callback; see hd_geometry_guess(). + */ + int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); + QLIST_ENTRY(BlockDriver) list; }; diff --git a/include/block/coroutine_int.h b/include/block/coroutine_int.h index f133d65..9aa1aae 100644 --- a/include/block/coroutine_int.h +++ b/include/block/coroutine_int.h @@ -31,6 +31,7 @@ typedef enum { COROUTINE_YIELD = 1, COROUTINE_TERMINATE = 2, + COROUTINE_ENTER = 3, } CoroutineAction; struct Coroutine { diff --git a/include/elf.h b/include/elf.h index a516584..3e75f05 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1508,6 +1508,7 @@ struct elf32_fdpic_loadmap { #define elf_shdr elf32_shdr #define elf_sym elf32_sym #define elf_addr_t Elf32_Off +#define elf_rela elf32_rela #ifdef ELF_USES_RELOCA # define ELF_RELOC Elf32_Rela @@ -1523,6 +1524,7 @@ struct elf32_fdpic_loadmap { #define elf_shdr elf64_shdr #define elf_sym elf64_sym #define elf_addr_t Elf64_Off +#define elf_rela elf64_rela #ifdef ELF_USES_RELOCA # define ELF_RELOC Elf64_Rela diff --git a/include/hw/arm/stm32f205_soc.h b/include/hw/arm/stm32f205_soc.h new file mode 100644 index 0000000..3cda170 --- /dev/null +++ b/include/hw/arm/stm32f205_soc.h @@ -0,0 +1,57 @@ +/* + * STM32F205 SoC + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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. + */ + +#ifndef HW_ARM_STM32F205SOC_H +#define HW_ARM_STM32F205SOC_H + +#include "hw/misc/stm32f2xx_syscfg.h" +#include "hw/timer/stm32f2xx_timer.h" +#include "hw/char/stm32f2xx_usart.h" + +#define TYPE_STM32F205_SOC "stm32f205_soc" +#define STM32F205_SOC(obj) \ + OBJECT_CHECK(STM32F205State, (obj), TYPE_STM32F205_SOC) + +#define STM_NUM_USARTS 6 +#define STM_NUM_TIMERS 4 + +#define FLASH_BASE_ADDRESS 0x08000000 +#define FLASH_SIZE (1024 * 1024) +#define SRAM_BASE_ADDRESS 0x20000000 +#define SRAM_SIZE (128 * 1024) + +typedef struct STM32F205State { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + char *kernel_filename; + char *cpu_model; + + STM32F2XXSyscfgState syscfg; + STM32F2XXUsartState usart[STM_NUM_USARTS]; + STM32F2XXTimerState timer[STM_NUM_TIMERS]; +} STM32F205State; + +#endif diff --git a/include/hw/block/block.h b/include/hw/block/block.h index 0d0ce9a..8d7c4b4 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -44,9 +44,9 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) #define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ DEFINE_PROP_DRIVE("drive", _state, _conf.blk), \ DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ - _conf.logical_block_size, 512), \ + _conf.logical_block_size), \ DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ - _conf.physical_block_size, 512), \ + _conf.physical_block_size), \ DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \ DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ DEFINE_PROP_UINT32("discard_granularity", _state, \ @@ -63,6 +63,7 @@ void blkconf_serial(BlockConf *conf, char **serial); void blkconf_geometry(BlockConf *conf, int *trans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp); +void blkconf_blocksizes(BlockConf *conf); /* Hard disk geometry */ diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h new file mode 100644 index 0000000..b97f192 --- /dev/null +++ b/include/hw/char/stm32f2xx_usart.h @@ -0,0 +1,73 @@ +/* + * STM32F2XX USART + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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. + */ + +#ifndef HW_STM32F2XX_USART_H +#define HW_STM32F2XX_USART_H + +#include "hw/sysbus.h" +#include "sysemu/char.h" +#include "hw/hw.h" + +#define USART_SR 0x00 +#define USART_DR 0x04 +#define USART_BRR 0x08 +#define USART_CR1 0x0C +#define USART_CR2 0x10 +#define USART_CR3 0x14 +#define USART_GTPR 0x18 + +#define USART_SR_RESET 0x00C00000 + +#define USART_SR_TXE (1 << 7) +#define USART_SR_TC (1 << 6) +#define USART_SR_RXNE (1 << 5) + +#define USART_CR1_UE (1 << 13) +#define USART_CR1_RXNEIE (1 << 5) +#define USART_CR1_TE (1 << 3) +#define USART_CR1_RE (1 << 2) + +#define TYPE_STM32F2XX_USART "stm32f2xx-usart" +#define STM32F2XX_USART(obj) \ + OBJECT_CHECK(STM32F2XXUsartState, (obj), TYPE_STM32F2XX_USART) + +typedef struct { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion mmio; + + uint32_t usart_sr; + uint32_t usart_dr; + uint32_t usart_brr; + uint32_t usart_cr1; + uint32_t usart_cr2; + uint32_t usart_cr3; + uint32_t usart_gtpr; + + CharDriverState *chr; + qemu_irq irq; +} STM32F2XXUsartState; +#endif /* HW_STM32F2XX_USART_H */ diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index a517753..16a627b 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -49,6 +49,13 @@ static void glue(bswap_sym, SZ)(struct elf_sym *sym) bswap16s(&sym->st_shndx); } +static void glue(bswap_rela, SZ)(struct elf_rela *rela) +{ + bswapSZs(&rela->r_offset); + bswapSZs(&rela->r_info); + bswapSZs((elf_word *)&rela->r_addend); +} + static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, int n, int type) { @@ -182,6 +189,75 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, return -1; } +static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint8_t *data, + struct elf_phdr *ph, int elf_machine) +{ + struct elf_shdr *reltab, *shdr_table = NULL; + struct elf_rela *rels = NULL; + int nrels, i, ret = -1; + elf_word wordval; + void *addr; + + shdr_table = load_at(fd, ehdr->e_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) { + return -1; + } + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(&shdr_table[i]); + } + } + + reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); + if (!reltab) { + goto fail; + } + rels = load_at(fd, reltab->sh_offset, reltab->sh_size); + if (!rels) { + goto fail; + } + nrels = reltab->sh_size / sizeof(struct elf_rela); + + for (i = 0; i < nrels; i++) { + if (must_swab) { + glue(bswap_rela, SZ)(&rels[i]); + } + if (rels[i].r_offset < ph->p_vaddr || + rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { + continue; + } + addr = &data[rels[i].r_offset - ph->p_vaddr]; + switch (elf_machine) { + case EM_S390: + switch (rels[i].r_info) { + case R_390_RELATIVE: + wordval = *(elf_word *)addr; + if (must_swab) { + bswapSZs(&wordval); + } + wordval = translate_fn(translate_opaque, wordval); + if (must_swab) { + bswapSZs(&wordval); + } + *(elf_word *)addr = wordval; + break; + default: + fprintf(stderr, "Unsupported relocation type %i!\n", + (int)rels[i].r_info); + } + } + } + + ret = 0; +fail: + g_free(rels); + g_free(shdr_table); + return ret; +} + static int glue(load_elf, SZ)(const char *name, int fd, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, @@ -271,6 +347,8 @@ static int glue(load_elf, SZ)(const char *name, int fd, linked at the wrong physical address. */ if (translate_fn) { addr = translate_fn(translate_opaque, ph->p_paddr); + glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, + translate_opaque, data, ph, elf_machine); } else { addr = ph->p_paddr; } diff --git a/target-i386/topology.h b/include/hw/i386/topology.h index 07a6c5f..9c6f3a9 100644 --- a/target-i386/topology.h +++ b/include/hw/i386/topology.h @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef TARGET_I386_TOPOLOGY_H -#define TARGET_I386_TOPOLOGY_H +#ifndef HW_I386_TOPOLOGY_H +#define HW_I386_TOPOLOGY_H /* This file implements the APIC-ID-based CPU topology enumeration logic, * documented at the following document: @@ -131,4 +131,4 @@ static inline apic_id_t x86_apicid_from_cpu_idx(unsigned nr_cores, return apicid_from_topo_ids(nr_cores, nr_threads, pkg_id, core_id, smt_id); } -#endif /* TARGET_I386_TOPOLOGY_H */ +#endif /* HW_I386_TOPOLOGY_H */ diff --git a/include/hw/misc/stm32f2xx_syscfg.h b/include/hw/misc/stm32f2xx_syscfg.h new file mode 100644 index 0000000..69e6a30 --- /dev/null +++ b/include/hw/misc/stm32f2xx_syscfg.h @@ -0,0 +1,61 @@ +/* + * STM32F2XX SYSCFG + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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. + */ + +#ifndef HW_STM32F2XX_SYSCFG_H +#define HW_STM32F2XX_SYSCFG_H + +#include "hw/sysbus.h" +#include "hw/hw.h" + +#define SYSCFG_MEMRMP 0x00 +#define SYSCFG_PMC 0x04 +#define SYSCFG_EXTICR1 0x08 +#define SYSCFG_EXTICR2 0x0C +#define SYSCFG_EXTICR3 0x10 +#define SYSCFG_EXTICR4 0x14 +#define SYSCFG_CMPCR 0x20 + +#define TYPE_STM32F2XX_SYSCFG "stm32f2xx-syscfg" +#define STM32F2XX_SYSCFG(obj) \ + OBJECT_CHECK(STM32F2XXSyscfgState, (obj), TYPE_STM32F2XX_SYSCFG) + +typedef struct { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion mmio; + + uint32_t syscfg_memrmp; + uint32_t syscfg_pmc; + uint32_t syscfg_exticr1; + uint32_t syscfg_exticr2; + uint32_t syscfg_exticr3; + uint32_t syscfg_exticr4; + uint32_t syscfg_cmpcr; + + qemu_irq irq; +} STM32F2XXSyscfgState; + +#endif /* HW_STM32F2XX_SYSCFG_H */ diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 57ee363..d67dad5 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -149,8 +149,8 @@ extern PropertyInfo qdev_prop_arraylen; LostTickPolicy) #define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int) -#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f, _d) \ - DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blocksize, uint16_t) +#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, 0, qdev_prop_blocksize, uint16_t) #define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress) @@ -168,8 +168,8 @@ void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value); void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value); void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value); void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value); -int qdev_prop_set_drive(DeviceState *dev, const char *name, - BlockBackend *value) QEMU_WARN_UNUSED_RESULT; +void qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockBackend *value, Error **errp); void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockBackend *value); void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value); diff --git a/include/hw/timer/m48t59.h b/include/hw/timer/m48t59.h index 8217522..3367923 100644 --- a/include/hw/timer/m48t59.h +++ b/include/hw/timer/m48t59.h @@ -1,37 +1,34 @@ #ifndef NVRAM_H #define NVRAM_H -/* NVRAM helpers */ -typedef uint32_t (*nvram_read_t)(void *private, uint32_t addr); -typedef void (*nvram_write_t)(void *private, uint32_t addr, uint32_t val); -typedef struct nvram_t { - void *opaque; - nvram_read_t read_fn; - nvram_write_t write_fn; -} nvram_t; - -uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr); -int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max); - -int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size, - const char *arch, - uint32_t RAM_size, int boot_device, - uint32_t kernel_image, uint32_t kernel_size, - const char *cmdline, - uint32_t initrd_image, uint32_t initrd_size, - uint32_t NVRAM_image, - int width, int height, int depth); - -#define TYPE_SYSBUS_M48T59 "m48t59" - -typedef struct M48t59State M48t59State; - -void m48t59_write (void *private, uint32_t addr, uint32_t val); -uint32_t m48t59_read (void *private, uint32_t addr); -void m48t59_toggle_lock (void *private, int lock); -M48t59State *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, - int type); -M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base, - uint32_t io_base, uint16_t size, int type); +#include "qemu-common.h" +#include "qom/object.h" + +#define TYPE_NVRAM "nvram" + +#define NVRAM_CLASS(klass) \ + OBJECT_CLASS_CHECK(NvramClass, (klass), TYPE_NVRAM) +#define NVRAM_GET_CLASS(obj) \ + OBJECT_GET_CLASS(NvramClass, (obj), TYPE_NVRAM) +#define NVRAM(obj) \ + INTERFACE_CHECK(Nvram, (obj), TYPE_NVRAM) + +typedef struct Nvram { + Object parent; +} Nvram; + +typedef struct NvramClass { + InterfaceClass parent; + + uint32_t (*read)(Nvram *obj, uint32_t addr); + void (*write)(Nvram *obj, uint32_t addr, uint32_t val); + void (*toggle_lock)(Nvram *obj, int lock); +} NvramClass; + +Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, + int base_year, int type); +Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, + uint32_t io_base, uint16_t size, int base_year, + int type); #endif /* !NVRAM_H */ diff --git a/include/hw/timer/stm32f2xx_timer.h b/include/hw/timer/stm32f2xx_timer.h new file mode 100644 index 0000000..e6a8323 --- /dev/null +++ b/include/hw/timer/stm32f2xx_timer.h @@ -0,0 +1,101 @@ +/* + * STM32F2XX Timer + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * 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. + */ + +#ifndef HW_STM32F2XX_TIMER_H +#define HW_STM32F2XX_TIMER_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" + +#define TIM_CR1 0x00 +#define TIM_CR2 0x04 +#define TIM_SMCR 0x08 +#define TIM_DIER 0x0C +#define TIM_SR 0x10 +#define TIM_EGR 0x14 +#define TIM_CCMR1 0x18 +#define TIM_CCMR2 0x1C +#define TIM_CCER 0x20 +#define TIM_CNT 0x24 +#define TIM_PSC 0x28 +#define TIM_ARR 0x2C +#define TIM_CCR1 0x34 +#define TIM_CCR2 0x38 +#define TIM_CCR3 0x3C +#define TIM_CCR4 0x40 +#define TIM_DCR 0x48 +#define TIM_DMAR 0x4C +#define TIM_OR 0x50 + +#define TIM_CR1_CEN 1 + +#define TIM_EGR_UG 1 + +#define TIM_CCER_CC2E (1 << 4) +#define TIM_CCMR1_OC2M2 (1 << 14) +#define TIM_CCMR1_OC2M1 (1 << 13) +#define TIM_CCMR1_OC2M0 (1 << 12) +#define TIM_CCMR1_OC2PE (1 << 11) + +#define TIM_DIER_UIE 1 + +#define TYPE_STM32F2XX_TIMER "stm32f2xx-timer" +#define STM32F2XXTIMER(obj) OBJECT_CHECK(STM32F2XXTimerState, \ + (obj), TYPE_STM32F2XX_TIMER) + +typedef struct STM32F2XXTimerState { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + QEMUTimer *timer; + qemu_irq irq; + + int64_t tick_offset; + uint64_t hit_time; + uint64_t freq_hz; + + uint32_t tim_cr1; + uint32_t tim_cr2; + uint32_t tim_smcr; + uint32_t tim_dier; + uint32_t tim_sr; + uint32_t tim_egr; + uint32_t tim_ccmr1; + uint32_t tim_ccmr2; + uint32_t tim_ccer; + uint32_t tim_psc; + uint32_t tim_arr; + uint32_t tim_ccr1; + uint32_t tim_ccr2; + uint32_t tim_ccr3; + uint32_t tim_ccr4; + uint32_t tim_dcr; + uint32_t tim_dmar; + uint32_t tim_or; +} STM32F2XXTimerState; + +#endif /* HW_STM32F2XX_TIMER_H */ diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 181bd46..90ca8df 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -354,7 +354,7 @@ static inline int32_t sextract32(uint32_t value, int start, int length) * Returns: the sign extended value of the bit field extracted from the * input value. */ -static inline uint64_t sextract64(uint64_t value, int start, int length) +static inline int64_t sextract64(uint64_t value, int start, int length) { assert(start >= 0 && length > 0 && length <= 64 - start); /* Note that this implementation relies on right shift of signed diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 48fd6fb..d6279c0 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -82,6 +82,10 @@ struct TranslationBlock; * @do_unassigned_access: Callback for unassigned access handling. * @do_unaligned_access: Callback for unaligned access handling, if * the target defines #ALIGNED_ONLY. + * @virtio_is_big_endian: Callback to return %true if a CPU which supports + * runtime configurable endianness is currently big-endian. Non-configurable + * CPUs can use the default implementation of this method. This method should + * not be used by any callers other than the pre-1.0 virtio devices. * @memory_rw_debug: Callback for GDB memory access. * @dump_state: Callback for dumping state. * @dump_statistics: Callback for dumping statistics. @@ -96,6 +100,14 @@ struct TranslationBlock; * @gdb_read_register: Callback for letting GDB read a register. * @gdb_write_register: Callback for letting GDB write a register. * @debug_excp_handler: Callback for handling debug exceptions. + * @write_elf64_note: Callback for writing a CPU-specific ELF note to a + * 64-bit VM coredump. + * @write_elf32_qemunote: Callback for writing a CPU- and QEMU-specific ELF + * note to a 32-bit VM coredump. + * @write_elf32_note: Callback for writing a CPU-specific ELF note to a + * 32-bit VM coredump. + * @write_elf32_qemunote: Callback for writing a CPU- and QEMU-specific ELF + * note to a 32-bit VM coredump. * @vmsd: State description for migration. * @gdb_num_core_regs: Number of core registers accessible to GDB. * @gdb_core_xml_file: File name for core registers GDB XML description. diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index 95faf67..3209c90 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -74,39 +74,12 @@ struct virtio_net_config { uint16_t max_virtqueue_pairs; } QEMU_PACKED; -#ifndef VIRTIO_NET_NO_LEGACY -/* This header comes first in the scatter-gather list. - * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must - * be the first element of the scatter-gather list. If you don't - * specify GSO or CSUM features, you can simply ignore the header. */ -struct virtio_net_hdr { -#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset -#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid - uint8_t flags; -#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame -#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) -#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) -#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP -#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set - uint8_t gso_type; - __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ - __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ - __virtio16 csum_start; /* Position to start checksumming from */ - __virtio16 csum_offset; /* Offset after that to place checksum */ -}; - -/* This is the version of the header to use when the MRG_RXBUF - * feature has been negotiated. */ -struct virtio_net_hdr_mrg_rxbuf { - struct virtio_net_hdr hdr; - __virtio16 num_buffers; /* Number of merged rx buffers */ -}; -#else /* ... VIRTIO_NET_NO_LEGACY */ /* * This header comes first in the scatter-gather list. If you don't * specify GSO or CSUM features, you can simply ignore the header. * - * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf. + * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf, + * only flattened. */ struct virtio_net_hdr_v1 { #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */ @@ -124,6 +97,29 @@ struct virtio_net_hdr_v1 { __virtio16 csum_offset; /* Offset after that to place checksum */ __virtio16 num_buffers; /* Number of merged rx buffers */ }; + +#ifndef VIRTIO_NET_NO_LEGACY +/* This header comes first in the scatter-gather list. + * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must + * be the first element of the scatter-gather list. If you don't + * specify GSO or CSUM features, you can simply ignore the header. */ +struct virtio_net_hdr { + /* See VIRTIO_NET_HDR_F_* */ + uint8_t flags; + /* See VIRTIO_NET_HDR_GSO_* */ + uint8_t gso_type; + __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ + __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ + __virtio16 csum_start; /* Position to start checksumming from */ + __virtio16 csum_offset; /* Offset after that to place checksum */ +}; + +/* This is the version of the header to use when the MRG_RXBUF + * feature has been negotiated. */ +struct virtio_net_hdr_mrg_rxbuf { + struct virtio_net_hdr hdr; + __virtio16 num_buffers; /* Number of merged rx buffers */ +}; #endif /* ...VIRTIO_NET_NO_LEGACY */ /* diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 3ff9aee..77e9b9c 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -164,5 +164,7 @@ int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors); int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int64_t pos, int size); int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); +int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); +int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); #endif diff --git a/include/ui/console.h b/include/ui/console.h index 6e5a867..2f5b9f0 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -332,7 +332,6 @@ void vnc_display_init(const char *id); void vnc_display_open(const char *id, Error **errp); void vnc_display_add_client(const char *id, int csock, bool skipauth); char *vnc_display_local_addr(const char *id); -void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts); #ifdef CONFIG_VNC int vnc_display_password(const char *id, const char *password); int vnc_display_pw_expire(const char *id, time_t expires); diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 09ee408..0db25bc 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -175,6 +175,8 @@ struct kvm_arch_memory_slot { #define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0 #define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) #define KVM_DEV_ARM_VGIC_GRP_NR_IRQS 3 +#define KVM_DEV_ARM_VGIC_GRP_CTRL 4 +#define KVM_DEV_ARM_VGIC_CTRL_INIT 0 /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 8e38878..3ef77a4 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -78,6 +78,13 @@ struct kvm_regs { #define KVM_VGIC_V2_DIST_SIZE 0x1000 #define KVM_VGIC_V2_CPU_SIZE 0x2000 +/* Supported VGICv3 address types */ +#define KVM_VGIC_V3_ADDR_TYPE_DIST 2 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 + +#define KVM_VGIC_V3_DIST_SIZE SZ_64K +#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) + #define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ #define KVM_ARM_VCPU_EL1_32BIT 1 /* CPU running a 32bit VM */ #define KVM_ARM_VCPU_PSCI_0_2 2 /* CPU uses PSCI v0.2 */ @@ -161,6 +168,8 @@ struct kvm_arch_memory_slot { #define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0 #define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) #define KVM_DEV_ARM_VGIC_GRP_NR_IRQS 3 +#define KVM_DEV_ARM_VGIC_GRP_CTRL 4 +#define KVM_DEV_ARM_VGIC_CTRL_INIT 0 /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index d36b2fa..c5a93eb 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -57,10 +57,44 @@ struct kvm_s390_io_adapter_req { /* kvm attr_group on vm fd */ #define KVM_S390_VM_MEM_CTRL 0 +#define KVM_S390_VM_TOD 1 +#define KVM_S390_VM_CRYPTO 2 +#define KVM_S390_VM_CPU_MODEL 3 /* kvm attributes for mem_ctrl */ #define KVM_S390_VM_MEM_ENABLE_CMMA 0 #define KVM_S390_VM_MEM_CLR_CMMA 1 +#define KVM_S390_VM_MEM_LIMIT_SIZE 2 + +/* kvm attributes for KVM_S390_VM_TOD */ +#define KVM_S390_VM_TOD_LOW 0 +#define KVM_S390_VM_TOD_HIGH 1 + +/* kvm attributes for KVM_S390_VM_CPU_MODEL */ +/* processor related attributes are r/w */ +#define KVM_S390_VM_CPU_PROCESSOR 0 +struct kvm_s390_vm_cpu_processor { + __u64 cpuid; + __u16 ibc; + __u8 pad[6]; + __u64 fac_list[256]; +}; + +/* machine related attributes are r/o */ +#define KVM_S390_VM_CPU_MACHINE 1 +struct kvm_s390_vm_cpu_machine { + __u64 cpuid; + __u32 ibc; + __u8 pad[4]; + __u64 fac_mask[256]; + __u64 fac_list[256]; +}; + +/* kvm attributes for crypto */ +#define KVM_S390_VM_CRYPTO_ENABLE_AES_KW 0 +#define KVM_S390_VM_CRYPTO_ENABLE_DEA_KW 1 +#define KVM_S390_VM_CRYPTO_DISABLE_AES_KW 2 +#define KVM_S390_VM_CRYPTO_DISABLE_DEA_KW 3 /* for KVM_GET_REGS and KVM_SET_REGS */ struct kvm_regs { @@ -107,6 +141,9 @@ struct kvm_guest_debug_arch { struct kvm_hw_breakpoint *hw_bp; }; +/* for KVM_SYNC_PFAULT and KVM_REG_S390_PFTOKEN */ +#define KVM_S390_PFAULT_TOKEN_INVALID 0xffffffffffffffffULL + #define KVM_SYNC_PREFIX (1UL << 0) #define KVM_SYNC_GPRS (1UL << 1) #define KVM_SYNC_ACRS (1UL << 2) diff --git a/linux-headers/asm-x86/hyperv.h b/linux-headers/asm-x86/hyperv.h index 462efe7..90c458e 100644 --- a/linux-headers/asm-x86/hyperv.h +++ b/linux-headers/asm-x86/hyperv.h @@ -187,6 +187,17 @@ #define HV_X64_MSR_SINT14 0x4000009E #define HV_X64_MSR_SINT15 0x4000009F +/* + * Synthetic Timer MSRs. Four timers per vcpu. + */ +#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0 +#define HV_X64_MSR_STIMER0_COUNT 0x400000B1 +#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2 +#define HV_X64_MSR_STIMER1_COUNT 0x400000B3 +#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4 +#define HV_X64_MSR_STIMER2_COUNT 0x400000B5 +#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 +#define HV_X64_MSR_STIMER3_COUNT 0x400000B7 #define HV_X64_MSR_HYPERCALL_ENABLE 0x00000001 #define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12 diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 12045a1..60a54c8 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -491,6 +491,11 @@ struct kvm_s390_emerg_info { __u16 code; }; +#define KVM_S390_STOP_FLAG_STORE_STATUS 0x01 +struct kvm_s390_stop_info { + __u32 flags; +}; + struct kvm_s390_mchk_info { __u64 cr14; __u64 mcic; @@ -509,6 +514,7 @@ struct kvm_s390_irq { struct kvm_s390_emerg_info emerg; struct kvm_s390_extcall_info extcall; struct kvm_s390_prefix_info prefix; + struct kvm_s390_stop_info stop; struct kvm_s390_mchk_info mchk; char reserved[64]; } u; @@ -647,11 +653,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_MP_STATE 14 #define KVM_CAP_COALESCED_MMIO 15 #define KVM_CAP_SYNC_MMU 16 /* Changes to host mmap are reflected in guest */ -#define KVM_CAP_DEVICE_ASSIGNMENT 17 #define KVM_CAP_IOMMU 18 -#ifdef __KVM_HAVE_MSI -#define KVM_CAP_DEVICE_MSI 20 -#endif /* Bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21 #define KVM_CAP_USER_NMI 22 @@ -663,10 +665,6 @@ struct kvm_ppc_smmu_info { #endif #define KVM_CAP_IRQ_ROUTING 25 #define KVM_CAP_IRQ_INJECT_STATUS 26 -#define KVM_CAP_DEVICE_DEASSIGNMENT 27 -#ifdef __KVM_HAVE_MSIX -#define KVM_CAP_DEVICE_MSIX 28 -#endif #define KVM_CAP_ASSIGN_DEV_IRQ 29 /* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 @@ -761,6 +759,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_PPC_FIXUP_HCALL 103 #define KVM_CAP_PPC_ENABLE_HCALL 104 #define KVM_CAP_CHECK_EXTENSION_VM 105 +#define KVM_CAP_S390_USER_SIGP 106 #ifdef KVM_CAP_IRQ_ROUTING @@ -960,6 +959,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_ARM_VGIC_V2 KVM_DEV_TYPE_ARM_VGIC_V2 KVM_DEV_TYPE_FLIC, #define KVM_DEV_TYPE_FLIC KVM_DEV_TYPE_FLIC + KVM_DEV_TYPE_ARM_VGIC_V3, +#define KVM_DEV_TYPE_ARM_VGIC_V3 KVM_DEV_TYPE_ARM_VGIC_V3 KVM_DEV_TYPE_MAX, }; @@ -1107,9 +1108,6 @@ struct kvm_s390_ucas_mapping { #define KVM_X86_SETUP_MCE _IOW(KVMIO, 0x9c, __u64) #define KVM_X86_GET_MCE_CAP_SUPPORTED _IOR(KVMIO, 0x9d, __u64) #define KVM_X86_SET_MCE _IOW(KVMIO, 0x9e, struct kvm_x86_mce) -/* IA64 stack access */ -#define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *) -#define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *) /* Available with KVM_CAP_VCPU_EVENTS */ #define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) #define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) diff --git a/linux-user/main.c b/linux-user/main.c index d92702a..6bd23af 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3452,8 +3452,8 @@ void init_task_state(TaskState *ts) CPUArchState *cpu_copy(CPUArchState *env) { CPUState *cpu = ENV_GET_CPU(env); - CPUArchState *new_env = cpu_init(cpu_model); - CPUState *new_cpu = ENV_GET_CPU(new_env); + CPUState *new_cpu = cpu_init(cpu_model); + CPUArchState *new_env = cpu->env_ptr; CPUBreakpoint *bp; CPUWatchpoint *wp; @@ -3939,12 +3939,12 @@ int main(int argc, char **argv, char **envp) cpu_exec_init_all(); /* NOTE: we need to init the CPU at this stage to get qemu_host_page_size */ - env = cpu_init(cpu_model); - if (!env) { + cpu = cpu_init(cpu_model); + if (!cpu) { fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - cpu = ENV_GET_CPU(env); + env = cpu->env_ptr; cpu_reset(cpu); thread_cpu = cpu; diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc Binary files differindex 994052f..91e4193 100644 --- a/pc-bios/openbios-ppc +++ b/pc-bios/openbios-ppc diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 Binary files differindex 6d5a381..d693865 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 Binary files differindex 61bd46b..e5d9e5a 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex dbe5a38..3c6b01f 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index ad55a14..009bb8d 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -9,10 +9,9 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS=main.o bootmap.o sclp-ascii.o virtio.o start.o -CFLAGS += -fno-stack-protector -# XXX find a more clever to locate the bootloader -LDFLAGS += -Wl,-Ttext,0x7e00000,-Tbss,0x7f00000 -nostdlib +OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o +CFLAGS += -fPIE -fno-stack-protector -ffreestanding +LDFLAGS += -Wl,-pie -nostdlib build-all: s390-ccw.img @@ -20,7 +19,9 @@ s390-ccw.elf: $(OBJECTS) $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS)," Building $(TARGET_DIR)$@") s390-ccw.img: s390-ccw.elf - $(call quiet-command,strip $< -o $@," Stripping $(TARGET_DIR)$@") + $(call quiet-command,strip --strip-unneeded $< -o $@," Stripping $(TARGET_DIR)$@") + +$(OBJECTS): Makefile clean: rm -f *.o *.d *.img *.elf *~ diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 6f707bb..584d4a2 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -12,6 +12,7 @@ #include "virtio.h" char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); +char ring_area[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); uint64_t boot_value; static struct subchannel_id blk_schid = { .one = 1 }; diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index ceb7418..9b3868b 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -52,6 +52,7 @@ void disabled_wait(void); void virtio_panic(const char *string); void write_subsystem_identification(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); +extern char ring_area[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); extern uint64_t boot_value; /* sclp-ascii.c */ diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 4dc91a7..57ff1b0 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -362,6 +362,7 @@ void virtio_setup_block(struct subchannel_id schid) struct vq_config_block config = {}; blk_cfg.blk_size = 0; /* mark "illegal" - setup started... */ + guessed_disk_nature = false; virtio_reset(schid); @@ -378,10 +379,10 @@ void virtio_setup_block(struct subchannel_id schid) if (run_ccw(schid, CCW_CMD_READ_CONF, &blk_cfg, sizeof(blk_cfg))) { virtio_panic("Could not get block device configuration\n"); } - vring_init(&block, config.num, (void *)(100 * 1024 * 1024), + vring_init(&block, config.num, ring_area, KVM_S390_VIRTIO_RING_ALIGN); - info.queue = (100ULL * 1024ULL* 1024ULL); + info.queue = (unsigned long long) ring_area; info.align = KVM_S390_VIRTIO_RING_ALIGN; info.index = 0; info.num = config.num; diff --git a/qapi/block-core.json b/qapi/block-core.json index a3fdaf0..90586a5 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -41,13 +41,16 @@ # @corrupt: #optional true if the image has been marked corrupt; only valid for # compat >= 1.1 (since 2.2) # +# @refcount-bits: width of a refcount entry in bits (since 2.3) +# # Since: 1.7 ## { 'type': 'ImageInfoSpecificQCow2', 'data': { 'compat': 'str', '*lazy-refcounts': 'bool', - '*corrupt': 'bool' + '*corrupt': 'bool', + 'refcount-bits': 'int' } } ## diff --git a/qemu-coroutine.c b/qemu-coroutine.c index 525247b..c17a92b 100644 --- a/qemu-coroutine.c +++ b/qemu-coroutine.c @@ -99,29 +99,10 @@ static void coroutine_delete(Coroutine *co) qemu_coroutine_delete(co); } -static void coroutine_swap(Coroutine *from, Coroutine *to) -{ - CoroutineAction ret; - - ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD); - - qemu_co_queue_run_restart(to); - - switch (ret) { - case COROUTINE_YIELD: - return; - case COROUTINE_TERMINATE: - trace_qemu_coroutine_terminate(to); - coroutine_delete(to); - return; - default: - abort(); - } -} - void qemu_coroutine_enter(Coroutine *co, void *opaque) { Coroutine *self = qemu_coroutine_self(); + CoroutineAction ret; trace_qemu_coroutine_enter(self, co, opaque); @@ -132,7 +113,20 @@ void qemu_coroutine_enter(Coroutine *co, void *opaque) co->caller = self; co->entry_arg = opaque; - coroutine_swap(self, co); + ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER); + + qemu_co_queue_run_restart(co); + + switch (ret) { + case COROUTINE_YIELD: + return; + case COROUTINE_TERMINATE: + trace_qemu_coroutine_terminate(co); + coroutine_delete(co); + return; + default: + abort(); + } } void coroutine_fn qemu_coroutine_yield(void) @@ -148,5 +142,5 @@ void coroutine_fn qemu_coroutine_yield(void) } self->caller = NULL; - coroutine_swap(self, to); + qemu_coroutine_switch(self, to, COROUTINE_YIELD); } diff --git a/qemu-doc.texi b/qemu-doc.texi index aabe8df..f5b0dc4 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2052,7 +2052,7 @@ firmware implementation. The goal is to implement a 100% IEEE A sample Linux 2.6 series kernel and ram disk image are available on the QEMU web site. There are still issues with NetBSD and OpenBSD, but -some kernel versions work. Please note that currently older Solaris kernels +most kernel versions work. Please note that currently older Solaris kernels don't work probably due to interface issues between OpenBIOS and Solaris. @@ -2091,8 +2091,9 @@ Set the emulated machine type. Default is SS-5. Use the executable @file{qemu-system-sparc64} to simulate a Sun4u (UltraSPARC PC-like machine), Sun4v (T1 PC-like machine), or generic -Niagara (T1) machine. The emulator is not usable for anything yet, but -it can launch some kernels. +Niagara (T1) machine. The Sun4u emulator is mostly complete, being +able to run Linux, NetBSD and OpenBSD in headless (-nographic) mode. The +Sun4v and Niagara emulators are still a work in progress. QEMU emulates the following peripherals: @@ -391,7 +391,6 @@ static void qmp_change_vnc_listen(const char *target, Error **errp) return; } - vnc_auto_assign_id(olist, opts); vnc_display_open("default", errp); } diff --git a/roms/openbios b/roms/openbios -Subproject 038aa78d3c331731733378a73e778ee620a5b9d +Subproject b8dea39718916f4d7d391cd1664314d52a45b87 diff --git a/scripts/kvm/kvm_stat b/scripts/kvm/kvm_stat index c65cabd..7e5d256 100755 --- a/scripts/kvm/kvm_stat +++ b/scripts/kvm/kvm_stat @@ -519,7 +519,10 @@ def tui(screen, stats): def refresh(sleeptime): screen.erase() screen.addstr(0, 0, 'kvm statistics') - row = 2 + screen.addstr(2, 1, 'Event') + screen.addstr(2, 1 + label_width + number_width - len('Total'), 'Total') + screen.addstr(2, 1 + label_width + number_width + 8 - len('Current'), 'Current') + row = 3 s = stats.get() def sortkey(x): if s[x][1]: diff --git a/scripts/kvm/kvm_stat.texi b/scripts/kvm/kvm_stat.texi new file mode 100644 index 0000000..6ce00d8 --- /dev/null +++ b/scripts/kvm/kvm_stat.texi @@ -0,0 +1,55 @@ +@example +@c man begin SYNOPSIS +usage: kvm_stat [OPTION]... +@c man end +@end example + +@c man begin DESCRIPTION + +kvm_stat prints counts of KVM kernel module trace events. These events signify +state transitions such as guest mode entry and exit. + +This tool is useful for observing guest behavior from the host perspective. +Often conclusions about performance or buggy behavior can be drawn from the +output. + +The set of KVM kernel module trace events may be specific to the kernel version +or architecture. It is best to check the KVM kernel module source code for the +meaning of events. + +Note that trace events are counted globally across all running guests. + +@c man end + +@c man begin OPTIONS +@table @option +@item -1, --once, --batch + run in batch mode for one second +@item -l, --log + run in logging mode (like vmstat) +@item -t, --tracepoints + retrieve statistics from tracepoints +@item -d, --debugfs + retrieve statistics from debugfs +@item -f, --fields=@var{fields} + fields to display (regex) +@item -h, --help + show help message +@end table + +@c man end + +@ignore + +@setfilename kvm_stat +@settitle Report KVM kernel module event counters. + +@c man begin AUTHOR +Stefan Hajnoczi <stefanha@redhat.com> +@c man end + +@c man begin SEEALSO +perf(1), trace-cmd(1) +@c man end + +@end ignore diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index e276dbf..9538f19 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -429,14 +429,7 @@ void alpha_translate_init(void); AlphaCPU *cpu_alpha_init(const char *cpu_model); -static inline CPUAlphaState *cpu_init(const char *cpu_model) -{ - AlphaCPU *cpu = cpu_alpha_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_alpha_init(cpu_model)) void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf); int cpu_alpha_exec(CPUAlphaState *s); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 11845a6..083211c 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -1569,14 +1569,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx) return unmasked || pstate_unmasked; } -static inline CPUARMState *cpu_init(const char *cpu_model) -{ - ARMCPU *cpu = cpu_arm_init(cpu_model); - if (cpu) { - return &cpu->env; - } - return NULL; -} +#define cpu_init(cpu_model) CPU(cpu_arm_init(cpu_model)) #define cpu_exec cpu_arm_exec #define cpu_gen_code cpu_arm_gen_code diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index 823c739..270bc2f 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -96,6 +96,7 @@ static void aarch64_a57_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + cpu->dtb_compatible = "arm,cortex-a57"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); diff --git a/target-cris/cpu.h b/target-cris/cpu.h index eea14b6..677b38c 100644 --- a/target-cris/cpu.h +++ b/target-cris/cpu.h @@ -221,14 +221,7 @@ enum { #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -static inline CPUCRISState *cpu_init(const char *cpu_model) -{ - CRISCPU *cpu = cpu_cris_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_cris_init(cpu_model)) #define cpu_exec cpu_cris_exec #define cpu_gen_code cpu_cris_gen_code diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index b557b61..31a0c1e 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -93,6 +93,7 @@ typedef struct X86CPU { bool expose_kvm; bool migratable; bool host_features; + int64_t apic_id; /* if true the CPUID code directly forward host cache leaves to the guest */ bool cache_info_passthrough; diff --git a/target-i386/cpu.c b/target-i386/cpu.c index d543e2b..ed7e5d5 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -25,7 +25,6 @@ #include "sysemu/kvm.h" #include "sysemu/cpus.h" #include "kvm_i386.h" -#include "topology.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -1690,7 +1689,7 @@ static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { X86CPU *cpu = X86_CPU(obj); - int64_t value = cpu->env.cpuid_apic_id; + int64_t value = cpu->apic_id; visit_type_int(v, &value, name, errp); } @@ -1723,11 +1722,11 @@ static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque, return; } - if ((value != cpu->env.cpuid_apic_id) && cpu_exists(value)) { + if ((value != cpu->apic_id) && cpu_exists(value)) { error_setg(errp, "CPU with APIC ID %" PRIi64 " exists", value); return; } - cpu->env.cpuid_apic_id = value; + cpu->apic_id = value; } /* Generic getter for "feature-words" and "filtered-features" properties */ @@ -1911,34 +1910,19 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, } } -/* generate a composite string into buf of all cpuid names in featureset - * selected by fbits. indicate truncation at bufsize in the event of overflow. - * if flags, suppress names undefined in featureset. +/* Print all cpuid feature names in featureset */ -static void listflags(char *buf, int bufsize, uint32_t fbits, - const char **featureset, uint32_t flags) -{ - const char **p = &featureset[31]; - char *q, *b, bit; - int nc; - - b = 4 <= bufsize ? buf + (bufsize -= 3) - 1 : NULL; - *buf = '\0'; - for (q = buf, bit = 31; fbits && bufsize; --p, fbits &= ~(1 << bit), --bit) - if (fbits & 1 << bit && (*p || !flags)) { - if (*p) - nc = snprintf(q, bufsize, "%s%s", q == buf ? "" : " ", *p); - else - nc = snprintf(q, bufsize, "%s[%d]", q == buf ? "" : " ", bit); - if (bufsize <= nc) { - if (b) { - memcpy(b, "...", sizeof("...")); - } - return; - } - q += nc; - bufsize -= nc; +static void listflags(FILE *f, fprintf_function print, const char **featureset) +{ + int bit; + bool first = true; + + for (bit = 0; bit < 32; bit++) { + if (featureset[bit]) { + print(f, "%s%s", first ? "" : " ", featureset[bit]); + first = false; } + } } /* generate CPU information. */ @@ -1963,8 +1947,9 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) { FeatureWordInfo *fw = &feature_word_info[i]; - listflags(buf, sizeof(buf), (uint32_t)~0, fw->feat_names, 1); - (*cpu_fprintf)(f, " %s\n", buf); + (*cpu_fprintf)(f, " "); + listflags(f, cpu_fprintf, fw->feat_names); + (*cpu_fprintf)(f, "\n"); } } @@ -2227,14 +2212,6 @@ void x86_cpudef_setup(void) } } -static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) -{ - *ebx = env->cpuid_vendor1; - *edx = env->cpuid_vendor2; - *ecx = env->cpuid_vendor3; -} - void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) @@ -2268,11 +2245,14 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch(index) { case 0: *eax = env->cpuid_level; - get_cpuid_vendor(env, ebx, ecx, edx); + *ebx = env->cpuid_vendor1; + *edx = env->cpuid_vendor2; + *ecx = env->cpuid_vendor3; break; case 1: *eax = env->cpuid_version; - *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ + *ebx = (cpu->apic_id << 24) | + 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ *ecx = env->features[FEAT_1_ECX]; *edx = env->features[FEAT_1_EDX]; if (cs->nr_cores * cs->nr_threads > 1) { @@ -2461,11 +2441,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * So dont set it here for Intel to make Linux guests happy. */ if (cs->nr_cores * cs->nr_threads > 1) { - uint32_t tebx, tecx, tedx; - get_cpuid_vendor(env, &tebx, &tecx, &tedx); - if (tebx != CPUID_VENDOR_INTEL_1 || - tedx != CPUID_VENDOR_INTEL_2 || - tecx != CPUID_VENDOR_INTEL_3) { + if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 || + env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 || + env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) { *ecx |= 1 << 1; /* CmpLegacy bit */ } } @@ -2721,7 +2699,6 @@ static void mce_init(X86CPU *cpu) #ifndef CONFIG_USER_ONLY static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) { - CPUX86State *env = &cpu->env; DeviceState *dev = DEVICE(cpu); APICCommonState *apic; const char *apic_type = "apic"; @@ -2740,7 +2717,7 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) object_property_add_child(OBJECT(cpu), "apic", OBJECT(cpu->apic_state), NULL); - qdev_prop_set_uint8(cpu->apic_state, "id", env->cpuid_apic_id); + qdev_prop_set_uint8(cpu->apic_state, "id", cpu->apic_id); /* TODO: convert to link<> */ apic = APIC_COMMON(cpu->apic_state); apic->cpu = cpu; @@ -2751,12 +2728,8 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) if (cpu->apic_state == NULL) { return; } - - if (qdev_init(cpu->apic_state)) { - error_setg(errp, "APIC device '%s' could not be initialized", - object_get_typename(OBJECT(cpu->apic_state))); - return; - } + object_property_set_bool(OBJECT(cpu->apic_state), true, "realized", + errp); } #else static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) @@ -2780,6 +2753,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) Error *local_err = NULL; static bool ht_warned; + if (cpu->apic_id < 0) { + error_setg(errp, "apic-id property was not initialized properly"); + return; + } + if (env->features[FEAT_7_0_EBX] && env->cpuid_level < 7) { env->cpuid_level = 7; } @@ -2844,39 +2822,6 @@ out: } } -/* Enables contiguous-apic-ID mode, for compatibility */ -static bool compat_apic_id_mode; - -void enable_compat_apic_id_mode(void) -{ - compat_apic_id_mode = true; -} - -/* Calculates initial APIC ID for a specific CPU index - * - * Currently we need to be able to calculate the APIC ID from the CPU index - * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have - * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of - * all CPUs up to max_cpus. - */ -uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index) -{ - uint32_t correct_id; - static bool warned; - - correct_id = x86_apicid_from_cpu_idx(smp_cores, smp_threads, cpu_index); - if (compat_apic_id_mode) { - if (cpu_index != correct_id && !warned) { - error_report("APIC IDs set in compatibility mode, " - "CPU topology won't match the configuration"); - warned = true; - } - return cpu_index; - } else { - return correct_id; - } -} - static void x86_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -2923,7 +2868,11 @@ static void x86_cpu_initfn(Object *obj) NULL, NULL, (void *)cpu->filtered_features, NULL); cpu->hyperv_spinlock_attempts = HYPERV_SPINLOCK_NEVER_RETRY; - env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index); + +#ifndef CONFIG_USER_ONLY + /* Any code creating new X86CPU objects have to set apic-id explicitly */ + cpu->apic_id = -1; +#endif x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort); @@ -2937,9 +2886,8 @@ static void x86_cpu_initfn(Object *obj) static int64_t x86_cpu_get_arch_id(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - return env->cpuid_apic_id; + return cpu->apic_id; } static bool x86_cpu_get_paging_enabled(const CPUState *cs) diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 478450c..e4c27b1 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -944,7 +944,6 @@ typedef struct CPUX86State { uint32_t cpuid_version; FeatureWordArray features; uint32_t cpuid_model[12]; - uint32_t cpuid_apic_id; /* MTRRs */ uint64_t mtrr_fixed[11]; @@ -1171,14 +1170,7 @@ uint64_t cpu_get_tsc(CPUX86State *env); # define PHYS_ADDR_MASK 0xfffffffffLL # endif -static inline CPUX86State *cpu_init(const char *cpu_model) -{ - X86CPU *cpu = cpu_x86_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_x86_init(cpu_model)) #define cpu_exec cpu_x86_exec #define cpu_gen_code cpu_x86_gen_code @@ -1329,7 +1321,6 @@ void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features); /* Return name of 32-bit register, from a R_* constant */ const char *get_register_name_32(unsigned int reg); -uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index); void enable_compat_apic_id_mode(void); #define APIC_DEFAULT_ADDRESS 0xfee00000 diff --git a/target-i386/kvm.c b/target-i386/kvm.c index acb6831..41d09e5 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -430,7 +430,7 @@ static void cpu_update_state(void *opaque, int running, RunState state) unsigned long kvm_arch_vcpu_id(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); - return cpu->env.cpuid_apic_id; + return cpu->apic_id; } #ifndef KVM_CPUID_SIGNATURE_NEXT diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c index fa374d0..2bc757a 100644 --- a/target-i386/seg_helper.c +++ b/target-i386/seg_helper.c @@ -1043,7 +1043,7 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); env->eip = (uint32_t)env->regs[R_ECX]; } - cpu_x86_load_seg_cache(env, R_SS, selector + 8, + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | @@ -1056,7 +1056,7 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); env->eip = (uint32_t)env->regs[R_ECX]; - cpu_x86_load_seg_cache(env, R_SS, selector + 8, + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h index e558c59..11ae68d 100644 --- a/target-lm32/cpu.h +++ b/target-lm32/cpu.h @@ -217,14 +217,7 @@ void lm32_watchpoint_insert(CPULM32State *env, int index, target_ulong address, void lm32_watchpoint_remove(CPULM32State *env, int index); bool lm32_cpu_do_semihosting(CPUState *cs); -static inline CPULM32State *cpu_init(const char *cpu_model) -{ - LM32CPU *cpu = cpu_lm32_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_lm32_init(cpu_model)) #define cpu_list lm32_cpu_list #define cpu_exec cpu_lm32_exec diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index 3a1b9ab..5f165da 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -212,14 +212,7 @@ void register_m68k_insns (CPUM68KState *env); #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -static inline CPUM68KState *cpu_init(const char *cpu_model) -{ - M68kCPU *cpu = cpu_m68k_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_m68k_init(cpu_model)) #define cpu_exec cpu_m68k_exec #define cpu_gen_code cpu_m68k_gen_code diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h index 5794f89..7d06227 100644 --- a/target-microblaze/cpu.h +++ b/target-microblaze/cpu.h @@ -297,14 +297,7 @@ enum { #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -static inline CPUMBState *cpu_init(const char *cpu_model) -{ - MicroBlazeCPU *cpu = cpu_mb_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_mb_init(cpu_model)) #define cpu_exec cpu_mb_exec #define cpu_gen_code cpu_mb_gen_code diff --git a/target-mips/cpu-qom.h b/target-mips/cpu-qom.h index 2ffc1bf..4d6f9de 100644 --- a/target-mips/cpu-qom.h +++ b/target-mips/cpu-qom.h @@ -74,6 +74,10 @@ static inline MIPSCPU *mips_env_get_cpu(CPUMIPSState *env) #define ENV_OFFSET offsetof(MIPSCPU, env) +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vmstate_mips_cpu; +#endif + void mips_cpu_do_interrupt(CPUState *cpu); bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); void mips_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, diff --git a/target-mips/cpu.c b/target-mips/cpu.c index 98dc94e..958c999 100644 --- a/target-mips/cpu.c +++ b/target-mips/cpu.c @@ -148,6 +148,7 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) cc->do_unassigned_access = mips_cpu_unassigned_access; cc->do_unaligned_access = mips_cpu_do_unaligned_access; cc->get_phys_page_debug = mips_cpu_get_phys_page_debug; + cc->vmsd = &vmstate_mips_cpu; #endif cc->gdb_num_core_regs = 73; diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 5ea61bc..f9d2b4c 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -614,8 +614,6 @@ void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); -#define CPU_SAVE_VERSION 5 - /* MMU modes definitions. We carefully match the indices with our hflags layout. */ #define MMU_MODE0_SUFFIX _kernel @@ -739,14 +737,7 @@ void mips_tcg_init(void); MIPSCPU *cpu_mips_init(const char *cpu_model); int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc); -static inline CPUMIPSState *cpu_init(const char *cpu_model) -{ - MIPSCPU *cpu = cpu_mips_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model)) /* TODO QOM'ify CPU reset and remove */ void cpu_state_reset(CPUMIPSState *s); @@ -788,6 +779,23 @@ static inline void restore_flush_mode(CPUMIPSState *env) &env->active_fpu.fp_status); } +static inline void restore_fp_status(CPUMIPSState *env) +{ + restore_rounding_mode(env); + restore_flush_mode(env); +} + +static inline void restore_msa_fp_status(CPUMIPSState *env) +{ + float_status *status = &env->active_tc.msa_fp_status; + int rounding_mode = (env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM; + bool flush_to_zero = (env->active_tc.msacsr & MSACSR_FS_MASK) != 0; + + set_float_rounding_mode(ieee_rm[rounding_mode], status); + set_flush_to_zero(flush_to_zero, status); + set_flush_inputs_to_zero(flush_to_zero, status); +} + static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { diff --git a/target-mips/machine.c b/target-mips/machine.c index 6c76dfb..7d1fa32 100644 --- a/target-mips/machine.c +++ b/target-mips/machine.c @@ -1,341 +1,296 @@ #include "hw/hw.h" -#include "hw/boards.h" #include "cpu.h" -static void save_tc(QEMUFile *f, TCState *tc) +static int cpu_post_load(void *opaque, int version_id) { - int i; + MIPSCPU *cpu = opaque; + CPUMIPSState *env = &cpu->env; + + restore_fp_status(env); + restore_msa_fp_status(env); + compute_hflags(env); - /* Save active TC */ - for(i = 0; i < 32; i++) - qemu_put_betls(f, &tc->gpr[i]); - qemu_put_betls(f, &tc->PC); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_put_betls(f, &tc->HI[i]); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_put_betls(f, &tc->LO[i]); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_put_betls(f, &tc->ACX[i]); - qemu_put_betls(f, &tc->DSPControl); - qemu_put_sbe32s(f, &tc->CP0_TCStatus); - qemu_put_sbe32s(f, &tc->CP0_TCBind); - qemu_put_betls(f, &tc->CP0_TCHalt); - qemu_put_betls(f, &tc->CP0_TCContext); - qemu_put_betls(f, &tc->CP0_TCSchedule); - qemu_put_betls(f, &tc->CP0_TCScheFBack); - qemu_put_sbe32s(f, &tc->CP0_Debug_tcstatus); - qemu_put_betls(f, &tc->CP0_UserLocal); + return 0; } -static void save_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu) +/* FPU state */ + +static int get_fpr(QEMUFile *f, void *pv, size_t size) { int i; - - for(i = 0; i < 32; i++) - qemu_put_be64s(f, &fpu->fpr[i].d); - qemu_put_s8s(f, &fpu->fp_status.float_detect_tininess); - qemu_put_s8s(f, &fpu->fp_status.float_rounding_mode); - qemu_put_s8s(f, &fpu->fp_status.float_exception_flags); - qemu_put_be32s(f, &fpu->fcr0); - qemu_put_be32s(f, &fpu->fcr31); + fpr_t *v = pv; + /* Restore entire MSA vector register */ + for (i = 0; i < MSA_WRLEN/64; i++) { + qemu_get_sbe64s(f, &v->wr.d[i]); + } + return 0; } -void cpu_save(QEMUFile *f, void *opaque) +static void put_fpr(QEMUFile *f, void *pv, size_t size) { - CPUMIPSState *env = opaque; int i; - - /* Save active TC */ - save_tc(f, &env->active_tc); - - /* Save active FPU */ - save_fpu(f, &env->active_fpu); - - /* Save MVP */ - qemu_put_sbe32s(f, &env->mvp->CP0_MVPControl); - qemu_put_sbe32s(f, &env->mvp->CP0_MVPConf0); - qemu_put_sbe32s(f, &env->mvp->CP0_MVPConf1); - - /* Save TLB */ - qemu_put_be32s(f, &env->tlb->nb_tlb); - qemu_put_be32s(f, &env->tlb->tlb_in_use); - for(i = 0; i < MIPS_TLB_MAX; i++) { - uint16_t flags = ((env->tlb->mmu.r4k.tlb[i].EHINV << 15) | - (env->tlb->mmu.r4k.tlb[i].RI1 << 14) | - (env->tlb->mmu.r4k.tlb[i].RI0 << 13) | - (env->tlb->mmu.r4k.tlb[i].XI1 << 12) | - (env->tlb->mmu.r4k.tlb[i].XI0 << 11) | - (env->tlb->mmu.r4k.tlb[i].G << 10) | - (env->tlb->mmu.r4k.tlb[i].C0 << 7) | - (env->tlb->mmu.r4k.tlb[i].C1 << 4) | - (env->tlb->mmu.r4k.tlb[i].V0 << 3) | - (env->tlb->mmu.r4k.tlb[i].V1 << 2) | - (env->tlb->mmu.r4k.tlb[i].D0 << 1) | - (env->tlb->mmu.r4k.tlb[i].D1 << 0)); - uint8_t asid; - - qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].VPN); - qemu_put_be32s(f, &env->tlb->mmu.r4k.tlb[i].PageMask); - asid = env->tlb->mmu.r4k.tlb[i].ASID; - qemu_put_8s(f, &asid); - qemu_put_be16s(f, &flags); - qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[0]); - qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[1]); + fpr_t *v = pv; + /* Save entire MSA vector register */ + for (i = 0; i < MSA_WRLEN/64; i++) { + qemu_put_sbe64s(f, &v->wr.d[i]); } +} + +const VMStateInfo vmstate_info_fpr = { + .name = "fpr", + .get = get_fpr, + .put = put_fpr, +}; + +#define VMSTATE_FPR_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_fpr, fpr_t) - /* Save CPU metastate */ - qemu_put_be32s(f, &env->current_tc); - qemu_put_be32s(f, &env->current_fpu); - qemu_put_sbe32s(f, &env->error_code); - qemu_put_be32s(f, &env->hflags); - qemu_put_betls(f, &env->btarget); - i = env->bcond; - qemu_put_sbe32s(f, &i); - - /* Save remaining CP1 registers */ - qemu_put_sbe32s(f, &env->CP0_Index); - qemu_put_sbe32s(f, &env->CP0_Random); - qemu_put_sbe32s(f, &env->CP0_VPEControl); - qemu_put_sbe32s(f, &env->CP0_VPEConf0); - qemu_put_sbe32s(f, &env->CP0_VPEConf1); - qemu_put_betls(f, &env->CP0_YQMask); - qemu_put_betls(f, &env->CP0_VPESchedule); - qemu_put_betls(f, &env->CP0_VPEScheFBack); - qemu_put_sbe32s(f, &env->CP0_VPEOpt); - qemu_put_betls(f, &env->CP0_EntryLo0); - qemu_put_betls(f, &env->CP0_EntryLo1); - qemu_put_betls(f, &env->CP0_Context); - qemu_put_sbe32s(f, &env->CP0_PageMask); - qemu_put_sbe32s(f, &env->CP0_PageGrain); - qemu_put_sbe32s(f, &env->CP0_Wired); - qemu_put_sbe32s(f, &env->CP0_SRSConf0); - qemu_put_sbe32s(f, &env->CP0_SRSConf1); - qemu_put_sbe32s(f, &env->CP0_SRSConf2); - qemu_put_sbe32s(f, &env->CP0_SRSConf3); - qemu_put_sbe32s(f, &env->CP0_SRSConf4); - qemu_put_sbe32s(f, &env->CP0_HWREna); - qemu_put_betls(f, &env->CP0_BadVAddr); - qemu_put_be32s(f, &env->CP0_BadInstr); - qemu_put_be32s(f, &env->CP0_BadInstrP); - qemu_put_sbe32s(f, &env->CP0_Count); - qemu_put_betls(f, &env->CP0_EntryHi); - qemu_put_sbe32s(f, &env->CP0_Compare); - qemu_put_sbe32s(f, &env->CP0_Status); - qemu_put_sbe32s(f, &env->CP0_IntCtl); - qemu_put_sbe32s(f, &env->CP0_SRSCtl); - qemu_put_sbe32s(f, &env->CP0_SRSMap); - qemu_put_sbe32s(f, &env->CP0_Cause); - qemu_put_betls(f, &env->CP0_EPC); - qemu_put_sbe32s(f, &env->CP0_PRid); - qemu_put_sbe32s(f, &env->CP0_EBase); - qemu_put_sbe32s(f, &env->CP0_Config0); - qemu_put_sbe32s(f, &env->CP0_Config1); - qemu_put_sbe32s(f, &env->CP0_Config2); - qemu_put_sbe32s(f, &env->CP0_Config3); - qemu_put_sbe32s(f, &env->CP0_Config6); - qemu_put_sbe32s(f, &env->CP0_Config7); - qemu_put_betls(f, &env->lladdr); - for(i = 0; i < 8; i++) - qemu_put_betls(f, &env->CP0_WatchLo[i]); - for(i = 0; i < 8; i++) - qemu_put_sbe32s(f, &env->CP0_WatchHi[i]); - qemu_put_betls(f, &env->CP0_XContext); - qemu_put_sbe32s(f, &env->CP0_Framemask); - qemu_put_sbe32s(f, &env->CP0_Debug); - qemu_put_betls(f, &env->CP0_DEPC); - qemu_put_sbe32s(f, &env->CP0_Performance0); - qemu_put_sbe32s(f, &env->CP0_TagLo); - qemu_put_sbe32s(f, &env->CP0_DataLo); - qemu_put_sbe32s(f, &env->CP0_TagHi); - qemu_put_sbe32s(f, &env->CP0_DataHi); - qemu_put_betls(f, &env->CP0_ErrorEPC); - qemu_put_sbe32s(f, &env->CP0_DESAVE); - for (i = 0; i < MIPS_KSCRATCH_NUM; i++) { - qemu_put_betls(f, &env->CP0_KScratch[i]); +#define VMSTATE_FPR_ARRAY(_f, _s, _n) \ + VMSTATE_FPR_ARRAY_V(_f, _s, _n, 0) + +static VMStateField vmstate_fpu_fields[] = { + VMSTATE_FPR_ARRAY(fpr, CPUMIPSFPUContext, 32), + VMSTATE_UINT32(fcr0, CPUMIPSFPUContext), + VMSTATE_UINT32(fcr31, CPUMIPSFPUContext), + VMSTATE_END_OF_LIST() +}; + +const VMStateDescription vmstate_fpu = { + .name = "cpu/fpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_fpu_fields +}; + +const VMStateDescription vmstate_inactive_fpu = { + .name = "cpu/inactive_fpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_fpu_fields +}; + +/* TC state */ + +static VMStateField vmstate_tc_fields[] = { + VMSTATE_UINTTL_ARRAY(gpr, TCState, 32), + VMSTATE_UINTTL(PC, TCState), + VMSTATE_UINTTL_ARRAY(HI, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL_ARRAY(LO, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL_ARRAY(ACX, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL(DSPControl, TCState), + VMSTATE_INT32(CP0_TCStatus, TCState), + VMSTATE_INT32(CP0_TCBind, TCState), + VMSTATE_UINTTL(CP0_TCHalt, TCState), + VMSTATE_UINTTL(CP0_TCContext, TCState), + VMSTATE_UINTTL(CP0_TCSchedule, TCState), + VMSTATE_UINTTL(CP0_TCScheFBack, TCState), + VMSTATE_INT32(CP0_Debug_tcstatus, TCState), + VMSTATE_UINTTL(CP0_UserLocal, TCState), + VMSTATE_INT32(msacsr, TCState), + VMSTATE_END_OF_LIST() +}; + +const VMStateDescription vmstate_tc = { + .name = "cpu/tc", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_tc_fields +}; + +const VMStateDescription vmstate_inactive_tc = { + .name = "cpu/inactive_tc", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_tc_fields +}; + +/* MVP state */ + +const VMStateDescription vmstate_mvp = { + .name = "cpu/mvp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(CP0_MVPControl, CPUMIPSMVPContext), + VMSTATE_INT32(CP0_MVPConf0, CPUMIPSMVPContext), + VMSTATE_INT32(CP0_MVPConf1, CPUMIPSMVPContext), + VMSTATE_END_OF_LIST() } +}; - /* Save inactive TC state */ - for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) - save_tc(f, &env->tcs[i]); - for (i = 0; i < MIPS_FPU_MAX; i++) - save_fpu(f, &env->fpus[i]); -} +/* TLB state */ -static void load_tc(QEMUFile *f, TCState *tc, int version_id) +static int get_tlb(QEMUFile *f, void *pv, size_t size) { - int i; + r4k_tlb_t *v = pv; + uint16_t flags; - /* Save active TC */ - for(i = 0; i < 32; i++) - qemu_get_betls(f, &tc->gpr[i]); - qemu_get_betls(f, &tc->PC); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_get_betls(f, &tc->HI[i]); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_get_betls(f, &tc->LO[i]); - for(i = 0; i < MIPS_DSP_ACC; i++) - qemu_get_betls(f, &tc->ACX[i]); - qemu_get_betls(f, &tc->DSPControl); - qemu_get_sbe32s(f, &tc->CP0_TCStatus); - qemu_get_sbe32s(f, &tc->CP0_TCBind); - qemu_get_betls(f, &tc->CP0_TCHalt); - qemu_get_betls(f, &tc->CP0_TCContext); - qemu_get_betls(f, &tc->CP0_TCSchedule); - qemu_get_betls(f, &tc->CP0_TCScheFBack); - qemu_get_sbe32s(f, &tc->CP0_Debug_tcstatus); - if (version_id >= 4) { - qemu_get_betls(f, &tc->CP0_UserLocal); - } + qemu_get_betls(f, &v->VPN); + qemu_get_be32s(f, &v->PageMask); + qemu_get_8s(f, &v->ASID); + qemu_get_be16s(f, &flags); + v->G = (flags >> 10) & 1; + v->C0 = (flags >> 7) & 3; + v->C1 = (flags >> 4) & 3; + v->V0 = (flags >> 3) & 1; + v->V1 = (flags >> 2) & 1; + v->D0 = (flags >> 1) & 1; + v->D1 = (flags >> 0) & 1; + v->EHINV = (flags >> 15) & 1; + v->RI1 = (flags >> 14) & 1; + v->RI0 = (flags >> 13) & 1; + v->XI1 = (flags >> 12) & 1; + v->XI0 = (flags >> 11) & 1; + qemu_get_betls(f, &v->PFN[0]); + qemu_get_betls(f, &v->PFN[1]); + + return 0; } -static void load_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu) +static void put_tlb(QEMUFile *f, void *pv, size_t size) { - int i; + r4k_tlb_t *v = pv; + + uint16_t flags = ((v->EHINV << 15) | + (v->RI1 << 14) | + (v->RI0 << 13) | + (v->XI1 << 12) | + (v->XI0 << 11) | + (v->G << 10) | + (v->C0 << 7) | + (v->C1 << 4) | + (v->V0 << 3) | + (v->V1 << 2) | + (v->D0 << 1) | + (v->D1 << 0)); - for(i = 0; i < 32; i++) - qemu_get_be64s(f, &fpu->fpr[i].d); - qemu_get_s8s(f, &fpu->fp_status.float_detect_tininess); - qemu_get_s8s(f, &fpu->fp_status.float_rounding_mode); - qemu_get_s8s(f, &fpu->fp_status.float_exception_flags); - qemu_get_be32s(f, &fpu->fcr0); - qemu_get_be32s(f, &fpu->fcr31); + qemu_put_betls(f, &v->VPN); + qemu_put_be32s(f, &v->PageMask); + qemu_put_8s(f, &v->ASID); + qemu_put_be16s(f, &flags); + qemu_put_betls(f, &v->PFN[0]); + qemu_put_betls(f, &v->PFN[1]); } -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - CPUMIPSState *env = opaque; - MIPSCPU *cpu = mips_env_get_cpu(env); - int i; +const VMStateInfo vmstate_info_tlb = { + .name = "tlb_entry", + .get = get_tlb, + .put = put_tlb, +}; - if (version_id < 3) { - return -EINVAL; - } +#define VMSTATE_TLB_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_tlb, r4k_tlb_t) - /* Load active TC */ - load_tc(f, &env->active_tc, version_id); - - /* Load active FPU */ - load_fpu(f, &env->active_fpu); - - /* Load MVP */ - qemu_get_sbe32s(f, &env->mvp->CP0_MVPControl); - qemu_get_sbe32s(f, &env->mvp->CP0_MVPConf0); - qemu_get_sbe32s(f, &env->mvp->CP0_MVPConf1); - - /* Load TLB */ - qemu_get_be32s(f, &env->tlb->nb_tlb); - qemu_get_be32s(f, &env->tlb->tlb_in_use); - for(i = 0; i < MIPS_TLB_MAX; i++) { - uint16_t flags; - uint8_t asid; - - qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].VPN); - qemu_get_be32s(f, &env->tlb->mmu.r4k.tlb[i].PageMask); - qemu_get_8s(f, &asid); - env->tlb->mmu.r4k.tlb[i].ASID = asid; - qemu_get_be16s(f, &flags); - env->tlb->mmu.r4k.tlb[i].G = (flags >> 10) & 1; - env->tlb->mmu.r4k.tlb[i].C0 = (flags >> 7) & 3; - env->tlb->mmu.r4k.tlb[i].C1 = (flags >> 4) & 3; - env->tlb->mmu.r4k.tlb[i].V0 = (flags >> 3) & 1; - env->tlb->mmu.r4k.tlb[i].V1 = (flags >> 2) & 1; - env->tlb->mmu.r4k.tlb[i].D0 = (flags >> 1) & 1; - env->tlb->mmu.r4k.tlb[i].D1 = (flags >> 0) & 1; - if (version_id >= 5) { - env->tlb->mmu.r4k.tlb[i].EHINV = (flags >> 15) & 1; - env->tlb->mmu.r4k.tlb[i].RI1 = (flags >> 14) & 1; - env->tlb->mmu.r4k.tlb[i].RI0 = (flags >> 13) & 1; - env->tlb->mmu.r4k.tlb[i].XI1 = (flags >> 12) & 1; - env->tlb->mmu.r4k.tlb[i].XI0 = (flags >> 11) & 1; - } - qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[0]); - qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[1]); - } +#define VMSTATE_TLB_ARRAY(_f, _s, _n) \ + VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0) - /* Load CPU metastate */ - qemu_get_be32s(f, &env->current_tc); - qemu_get_be32s(f, &env->current_fpu); - qemu_get_sbe32s(f, &env->error_code); - qemu_get_be32s(f, &env->hflags); - qemu_get_betls(f, &env->btarget); - qemu_get_sbe32s(f, &i); - env->bcond = i; - - /* Load remaining CP1 registers */ - qemu_get_sbe32s(f, &env->CP0_Index); - qemu_get_sbe32s(f, &env->CP0_Random); - qemu_get_sbe32s(f, &env->CP0_VPEControl); - qemu_get_sbe32s(f, &env->CP0_VPEConf0); - qemu_get_sbe32s(f, &env->CP0_VPEConf1); - qemu_get_betls(f, &env->CP0_YQMask); - qemu_get_betls(f, &env->CP0_VPESchedule); - qemu_get_betls(f, &env->CP0_VPEScheFBack); - qemu_get_sbe32s(f, &env->CP0_VPEOpt); - qemu_get_betls(f, &env->CP0_EntryLo0); - qemu_get_betls(f, &env->CP0_EntryLo1); - qemu_get_betls(f, &env->CP0_Context); - qemu_get_sbe32s(f, &env->CP0_PageMask); - qemu_get_sbe32s(f, &env->CP0_PageGrain); - qemu_get_sbe32s(f, &env->CP0_Wired); - qemu_get_sbe32s(f, &env->CP0_SRSConf0); - qemu_get_sbe32s(f, &env->CP0_SRSConf1); - qemu_get_sbe32s(f, &env->CP0_SRSConf2); - qemu_get_sbe32s(f, &env->CP0_SRSConf3); - qemu_get_sbe32s(f, &env->CP0_SRSConf4); - qemu_get_sbe32s(f, &env->CP0_HWREna); - qemu_get_betls(f, &env->CP0_BadVAddr); - if (version_id >= 5) { - qemu_get_be32s(f, &env->CP0_BadInstr); - qemu_get_be32s(f, &env->CP0_BadInstrP); - } - qemu_get_sbe32s(f, &env->CP0_Count); - qemu_get_betls(f, &env->CP0_EntryHi); - qemu_get_sbe32s(f, &env->CP0_Compare); - qemu_get_sbe32s(f, &env->CP0_Status); - qemu_get_sbe32s(f, &env->CP0_IntCtl); - qemu_get_sbe32s(f, &env->CP0_SRSCtl); - qemu_get_sbe32s(f, &env->CP0_SRSMap); - qemu_get_sbe32s(f, &env->CP0_Cause); - qemu_get_betls(f, &env->CP0_EPC); - qemu_get_sbe32s(f, &env->CP0_PRid); - qemu_get_sbe32s(f, &env->CP0_EBase); - qemu_get_sbe32s(f, &env->CP0_Config0); - qemu_get_sbe32s(f, &env->CP0_Config1); - qemu_get_sbe32s(f, &env->CP0_Config2); - qemu_get_sbe32s(f, &env->CP0_Config3); - qemu_get_sbe32s(f, &env->CP0_Config6); - qemu_get_sbe32s(f, &env->CP0_Config7); - qemu_get_betls(f, &env->lladdr); - for(i = 0; i < 8; i++) - qemu_get_betls(f, &env->CP0_WatchLo[i]); - for(i = 0; i < 8; i++) - qemu_get_sbe32s(f, &env->CP0_WatchHi[i]); - qemu_get_betls(f, &env->CP0_XContext); - qemu_get_sbe32s(f, &env->CP0_Framemask); - qemu_get_sbe32s(f, &env->CP0_Debug); - qemu_get_betls(f, &env->CP0_DEPC); - qemu_get_sbe32s(f, &env->CP0_Performance0); - qemu_get_sbe32s(f, &env->CP0_TagLo); - qemu_get_sbe32s(f, &env->CP0_DataLo); - qemu_get_sbe32s(f, &env->CP0_TagHi); - qemu_get_sbe32s(f, &env->CP0_DataHi); - qemu_get_betls(f, &env->CP0_ErrorEPC); - qemu_get_sbe32s(f, &env->CP0_DESAVE); - if (version_id >= 5) { - for (i = 0; i < MIPS_KSCRATCH_NUM; i++) { - qemu_get_betls(f, &env->CP0_KScratch[i]); - } +const VMStateDescription vmstate_tlb = { + .name = "cpu/tlb", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(nb_tlb, CPUMIPSTLBContext), + VMSTATE_UINT32(tlb_in_use, CPUMIPSTLBContext), + VMSTATE_TLB_ARRAY(mmu.r4k.tlb, CPUMIPSTLBContext, MIPS_TLB_MAX), + VMSTATE_END_OF_LIST() } +}; - /* Load inactive TC state */ - for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) { - load_tc(f, &env->tcs[i], version_id); - } - for (i = 0; i < MIPS_FPU_MAX; i++) - load_fpu(f, &env->fpus[i]); +/* MIPS CPU state */ - /* XXX: ensure compatibility for halted bit ? */ - tlb_flush(CPU(cpu), 1); - return 0; -} +const VMStateDescription vmstate_mips_cpu = { + .name = "cpu", + .version_id = 6, + .minimum_version_id = 6, + .post_load = cpu_post_load, + .fields = (VMStateField[]) { + /* Active TC */ + VMSTATE_STRUCT(env.active_tc, MIPSCPU, 1, vmstate_tc, TCState), + + /* Active FPU */ + VMSTATE_STRUCT(env.active_fpu, MIPSCPU, 1, vmstate_fpu, + CPUMIPSFPUContext), + + /* MVP */ + VMSTATE_STRUCT_POINTER(env.mvp, MIPSCPU, vmstate_mvp, + CPUMIPSMVPContext), + + /* TLB */ + VMSTATE_STRUCT_POINTER(env.tlb, MIPSCPU, vmstate_tlb, + CPUMIPSTLBContext), + + /* CPU metastate */ + VMSTATE_UINT32(env.current_tc, MIPSCPU), + VMSTATE_UINT32(env.current_fpu, MIPSCPU), + VMSTATE_INT32(env.error_code, MIPSCPU), + VMSTATE_UINTTL(env.btarget, MIPSCPU), + VMSTATE_UINTTL(env.bcond, MIPSCPU), + + /* Remaining CP0 registers */ + VMSTATE_INT32(env.CP0_Index, MIPSCPU), + VMSTATE_INT32(env.CP0_Random, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEControl, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEConf0, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEConf1, MIPSCPU), + VMSTATE_UINTTL(env.CP0_YQMask, MIPSCPU), + VMSTATE_UINTTL(env.CP0_VPESchedule, MIPSCPU), + VMSTATE_UINTTL(env.CP0_VPEScheFBack, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEOpt, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EntryLo0, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EntryLo1, MIPSCPU), + VMSTATE_UINTTL(env.CP0_Context, MIPSCPU), + VMSTATE_INT32(env.CP0_PageMask, MIPSCPU), + VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU), + VMSTATE_INT32(env.CP0_Wired, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf0, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf1, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf2, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf3, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf4, MIPSCPU), + VMSTATE_INT32(env.CP0_HWREna, MIPSCPU), + VMSTATE_UINTTL(env.CP0_BadVAddr, MIPSCPU), + VMSTATE_UINT32(env.CP0_BadInstr, MIPSCPU), + VMSTATE_UINT32(env.CP0_BadInstrP, MIPSCPU), + VMSTATE_INT32(env.CP0_Count, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EntryHi, MIPSCPU), + VMSTATE_INT32(env.CP0_Compare, MIPSCPU), + VMSTATE_INT32(env.CP0_Status, MIPSCPU), + VMSTATE_INT32(env.CP0_IntCtl, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSCtl, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSMap, MIPSCPU), + VMSTATE_INT32(env.CP0_Cause, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EPC, MIPSCPU), + VMSTATE_INT32(env.CP0_PRid, MIPSCPU), + VMSTATE_INT32(env.CP0_EBase, MIPSCPU), + VMSTATE_INT32(env.CP0_Config0, MIPSCPU), + VMSTATE_INT32(env.CP0_Config1, MIPSCPU), + VMSTATE_INT32(env.CP0_Config2, MIPSCPU), + VMSTATE_INT32(env.CP0_Config3, MIPSCPU), + VMSTATE_INT32(env.CP0_Config6, MIPSCPU), + VMSTATE_INT32(env.CP0_Config7, MIPSCPU), + VMSTATE_UINTTL(env.lladdr, MIPSCPU), + VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), + VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), + VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU), + VMSTATE_INT32(env.CP0_Framemask, MIPSCPU), + VMSTATE_INT32(env.CP0_Debug, MIPSCPU), + VMSTATE_UINTTL(env.CP0_DEPC, MIPSCPU), + VMSTATE_INT32(env.CP0_Performance0, MIPSCPU), + VMSTATE_INT32(env.CP0_TagLo, MIPSCPU), + VMSTATE_INT32(env.CP0_DataLo, MIPSCPU), + VMSTATE_INT32(env.CP0_TagHi, MIPSCPU), + VMSTATE_INT32(env.CP0_DataHi, MIPSCPU), + VMSTATE_UINTTL(env.CP0_ErrorEPC, MIPSCPU), + VMSTATE_INT32(env.CP0_DESAVE, MIPSCPU), + VMSTATE_UINTTL_ARRAY(env.CP0_KScratch, MIPSCPU, MIPS_KSCRATCH_NUM), + + /* Inactive TC */ + VMSTATE_STRUCT_ARRAY(env.tcs, MIPSCPU, MIPS_SHADOW_SET_MAX, 1, + vmstate_inactive_tc, TCState), + VMSTATE_STRUCT_ARRAY(env.fpus, MIPSCPU, MIPS_FPU_MAX, 1, + vmstate_inactive_fpu, CPUMIPSFPUContext), + + VMSTATE_END_OF_LIST() + }, +}; diff --git a/target-mips/msa_helper.c b/target-mips/msa_helper.c index c2160a6..26ffdc7 100644 --- a/target-mips/msa_helper.c +++ b/target-mips/msa_helper.c @@ -1348,17 +1348,7 @@ void helper_msa_ctcmsa(CPUMIPSState *env, target_ulong elm, uint32_t cd) break; case 1: env->active_tc.msacsr = (int32_t)elm & MSACSR_MASK; - /* set float_status rounding mode */ - set_float_rounding_mode( - ieee_rm[(env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM], - &env->active_tc.msa_fp_status); - /* set float_status flush modes */ - set_flush_to_zero( - (env->active_tc.msacsr & MSACSR_FS_MASK) != 0 ? 1 : 0, - &env->active_tc.msa_fp_status); - set_flush_inputs_to_zero( - (env->active_tc.msacsr & MSACSR_FS_MASK) != 0 ? 1 : 0, - &env->active_tc.msa_fp_status); + restore_msa_fp_status(env); /* check exception */ if ((GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED) & GET_FP_CAUSE(env->active_tc.msacsr)) { diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index 9e8433a..85a65e7 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -835,6 +835,8 @@ static void msa_reset(CPUMIPSState *env) - round to nearest / ties to even (RM bits are 0) */ env->active_tc.msacsr = 0; + restore_msa_fp_status(env); + /* tininess detected after rounding.*/ set_float_detect_tininess(float_tininess_after_rounding, &env->active_tc.msa_fp_status); @@ -842,14 +844,6 @@ static void msa_reset(CPUMIPSState *env) /* clear float_status exception flags */ set_float_exception_flags(0, &env->active_tc.msa_fp_status); - /* set float_status rounding mode */ - set_float_rounding_mode(float_round_nearest_even, - &env->active_tc.msa_fp_status); - - /* set float_status flush modes */ - set_flush_to_zero(0, &env->active_tc.msa_fp_status); - set_flush_inputs_to_zero(0, &env->active_tc.msa_fp_status); - /* clear float_status nan mode */ set_default_nan_mode(0, &env->active_tc.msa_fp_status); } diff --git a/target-moxie/cpu.h b/target-moxie/cpu.h index d809393..c2733a2 100644 --- a/target-moxie/cpu.h +++ b/target-moxie/cpu.h @@ -121,14 +121,7 @@ void moxie_translate_init(void); int cpu_moxie_signal_handler(int host_signum, void *pinfo, void *puc); -static inline CPUMoxieState *cpu_init(const char *cpu_model) -{ - MoxieCPU *cpu = cpu_moxie_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_moxie_init(cpu_model)) #define cpu_exec cpu_moxie_exec #define cpu_gen_code cpu_moxie_gen_code diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h index 69b96c6..b25324b 100644 --- a/target-openrisc/cpu.h +++ b/target-openrisc/cpu.h @@ -389,14 +389,7 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, int *prot, target_ulong address, int rw); #endif -static inline CPUOpenRISCState *cpu_init(const char *cpu_model) -{ - OpenRISCCPU *cpu = cpu_openrisc_init(cpu_model); - if (cpu) { - return &cpu->env; - } - return NULL; -} +#define cpu_init(cpu_model) CPU(cpu_openrisc_init(cpu_model)) #include "exec/cpu-all.h" diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index abc3545..f15815f 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1238,14 +1238,7 @@ static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn) int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp); int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val); -static inline CPUPPCState *cpu_init(const char *cpu_model) -{ - PowerPCCPU *cpu = cpu_ppc_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_ppc_init(cpu_model)) #define cpu_exec cpu_ppc_exec #define cpu_gen_code cpu_ppc_gen_code diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index d2f6312..e0537fa 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -96,6 +96,7 @@ static void s390_cpu_reset(CPUState *s) env->pfault_token = -1UL; scc->parent_reset(s); + cpu->env.sigp_order = 0; s390_cpu_set_state(CPU_STATE_STOPPED, cpu); tlb_flush(s, 1); } @@ -131,6 +132,7 @@ static void s390_cpu_full_reset(CPUState *s) CPUS390XState *env = &cpu->env; scc->parent_reset(s); + cpu->env.sigp_order = 0; s390_cpu_set_state(CPU_STATE_STOPPED, cpu); memset(env, 0, offsetof(CPUS390XState, cpu_num)); diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index b6b4632..0171de0 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -157,6 +157,9 @@ typedef struct CPUS390XState { #define CPU_STATE_LOAD 0x04 uint8_t cpu_state; + /* currently processed sigp order */ + uint8_t sigp_order; + } CPUS390XState; #include "cpu-qom.h" @@ -349,7 +352,10 @@ int s390_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, #include "ioinst.h" + #ifndef CONFIG_USER_ONLY +void do_restart_interrupt(CPUS390XState *env); + static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb) { hwaddr addr = 0; @@ -411,6 +417,10 @@ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr); unsigned int s390_cpu_halt(S390CPU *cpu); void s390_cpu_unhalt(S390CPU *cpu); unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu); +static inline uint8_t s390_cpu_get_state(S390CPU *cpu) +{ + return cpu->env.cpu_state; +} /* service interrupts are floating therefore we must not pass an cpustate */ void s390_sclp_extint(uint32_t parm); @@ -466,7 +476,7 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid); bool css_present(uint8_t cssid); #endif -#define cpu_init(model) (&cpu_s390x_init(model)->env) +#define cpu_init(model) CPU(cpu_s390x_init(model)) #define cpu_exec cpu_s390x_exec #define cpu_gen_code cpu_s390x_gen_code #define cpu_signal_handler cpu_s390x_signal_handler @@ -664,7 +674,7 @@ typedef struct LowCore PSW mcck_old_psw; /* 0x160 */ PSW io_old_psw; /* 0x170 */ uint8_t pad7[0x1a0-0x180]; /* 0x180 */ - PSW restart_psw; /* 0x1a0 */ + PSW restart_new_psw; /* 0x1a0 */ PSW external_new_psw; /* 0x1b0 */ PSW svc_new_psw; /* 0x1c0 */ PSW program_new_psw; /* 0x1d0 */ @@ -864,6 +874,7 @@ struct sysib_322 { #define SK_F (0x1 << 3) #define SK_ACC_MASK (0xf << 4) +/* SIGP order codes */ #define SIGP_SENSE 0x01 #define SIGP_EXTERNAL_CALL 0x02 #define SIGP_EMERGENCY 0x03 @@ -877,7 +888,13 @@ struct sysib_322 { #define SIGP_STORE_STATUS_ADDR 0x0e #define SIGP_SET_ARCH 0x12 -/* cpu status bits */ +/* SIGP condition codes */ +#define SIGP_CC_ORDER_CODE_ACCEPTED 0 +#define SIGP_CC_STATUS_STORED 1 +#define SIGP_CC_BUSY 2 +#define SIGP_CC_NOT_OPERATIONAL 3 + +/* SIGP status bits */ #define SIGP_STAT_EQUIPMENT_CHECK 0x80000000UL #define SIGP_STAT_INCORRECT_STATE 0x00000200UL #define SIGP_STAT_INVALID_PARAMETER 0x00000100UL @@ -889,6 +906,11 @@ struct sysib_322 { #define SIGP_STAT_INVALID_ORDER 0x00000002UL #define SIGP_STAT_RECEIVER_CHECK 0x00000001UL +/* SIGP SET ARCHITECTURE modes */ +#define SIGP_MODE_ESA_S390 0 +#define SIGP_MODE_Z_ARCH_TRANS_ALL_PSW 1 +#define SIGP_MODE_Z_ARCH_TRANS_CUR_PSW 2 + void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr); int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, target_ulong *raddr, int *flags, bool exc); @@ -1007,6 +1029,7 @@ int kvm_s390_get_memslot_count(KVMState *s); void kvm_s390_clear_cmma_callback(void *opaque); int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state); void kvm_s390_reset_vcpu(S390CPU *cpu); +int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit); #else static inline void kvm_s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, @@ -1044,8 +1067,21 @@ static inline int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) static inline void kvm_s390_reset_vcpu(S390CPU *cpu) { } +static inline int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, + uint64_t *hw_limit) +{ + return 0; +} #endif +static inline int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) +{ + if (kvm_enabled()) { + return kvm_s390_set_mem_limit(kvm_state, new_limit, hw_limit); + } + return 0; +} + static inline void cmma_reset(S390CPU *cpu) { if (kvm_enabled()) { diff --git a/target-s390x/helper.c b/target-s390x/helper.c index e0fd8fc..f1060c2 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -183,7 +183,9 @@ void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) { env->psw.addr = addr; env->psw.mask = mask; - env->cc_op = (mask >> 44) & 3; + if (tcg_enabled()) { + env->cc_op = (mask >> 44) & 3; + } if (mask & PSW_MASK_WAIT) { S390CPU *cpu = s390_env_get_cpu(env); @@ -197,14 +199,16 @@ void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) static uint64_t get_psw_mask(CPUS390XState *env) { - uint64_t r; + uint64_t r = env->psw.mask; - env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr); + if (tcg_enabled()) { + env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, + env->cc_vr); - r = env->psw.mask; - r &= ~PSW_MASK_CC; - assert(!(env->cc_op & ~3)); - r |= (uint64_t)env->cc_op << 44; + r &= ~PSW_MASK_CC; + assert(!(env->cc_op & ~3)); + r |= (uint64_t)env->cc_op << 44; + } return r; } @@ -229,6 +233,23 @@ static void cpu_unmap_lowcore(LowCore *lowcore) cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); } +void do_restart_interrupt(CPUS390XState *env) +{ + uint64_t mask, addr; + LowCore *lowcore; + + lowcore = cpu_map_lowcore(env); + + lowcore->restart_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->restart_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->restart_new_psw.mask); + addr = be64_to_cpu(lowcore->restart_new_psw.addr); + + cpu_unmap_lowcore(lowcore); + + load_psw(env, mask, addr); +} + static void do_svc_interrupt(CPUS390XState *env) { uint64_t mask, addr; diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index b1369d5..896534b 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -38,6 +38,7 @@ #include "qapi/qmp/qjson.h" #include "monitor/monitor.h" #include "exec/gdbstub.h" +#include "exec/address-spaces.h" #include "trace.h" #include "qapi-event.h" #include "hw/s390x/s390-pci-inst.h" @@ -121,6 +122,51 @@ static int cap_async_pf; static void *legacy_s390_alloc(size_t size, uint64_t *align); +static int kvm_s390_supports_mem_limit(KVMState *s) +{ + struct kvm_device_attr attr = { + .group = KVM_S390_VM_MEM_CTRL, + .attr = KVM_S390_VM_MEM_LIMIT_SIZE, + }; + + return (kvm_vm_ioctl(s, KVM_HAS_DEVICE_ATTR, &attr) == 0); +} + +static int kvm_s390_query_mem_limit(KVMState *s, uint64_t *memory_limit) +{ + struct kvm_device_attr attr = { + .group = KVM_S390_VM_MEM_CTRL, + .attr = KVM_S390_VM_MEM_LIMIT_SIZE, + .addr = (uint64_t) memory_limit, + }; + + return kvm_vm_ioctl(s, KVM_GET_DEVICE_ATTR, &attr); +} + +int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit) +{ + int rc; + + struct kvm_device_attr attr = { + .group = KVM_S390_VM_MEM_CTRL, + .attr = KVM_S390_VM_MEM_LIMIT_SIZE, + .addr = (uint64_t) &new_limit, + }; + + if (!kvm_s390_supports_mem_limit(s)) { + return 0; + } + + rc = kvm_s390_query_mem_limit(s, hw_limit); + if (rc) { + return rc; + } else if (*hw_limit < new_limit) { + return -E2BIG; + } + + return kvm_vm_ioctl(s, KVM_SET_DEVICE_ATTR, &attr); +} + static int kvm_s390_check_clear_cmma(KVMState *s) { struct kvm_device_attr attr = { @@ -186,6 +232,9 @@ int kvm_arch_init(MachineState *ms, KVMState *s) || !kvm_check_extension(s, KVM_CAP_S390_COW)) { phys_mem_set_alloc(legacy_s390_alloc); } + + kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0); + return 0; } @@ -1111,110 +1160,356 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) return r; } -static void sigp_cpu_start(void *arg) +typedef struct SigpInfo { + S390CPU *cpu; + uint64_t param; + int cc; + uint64_t *status_reg; +} SigpInfo; + +static void set_sigp_status(SigpInfo *si, uint64_t status) { - CPUState *cs = arg; - S390CPU *cpu = S390_CPU(cs); + *si->status_reg &= 0xffffffff00000000ULL; + *si->status_reg |= status; + si->cc = SIGP_CC_STATUS_STORED; +} + +static void sigp_start(void *arg) +{ + SigpInfo *si = arg; + + if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; + return; + } - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); - DPRINTF("DONE: KVM cpu start: %p\n", &cpu->env); + s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu); + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -static void sigp_cpu_restart(void *arg) +static void sigp_stop(void *arg) { - CPUState *cs = arg; - S390CPU *cpu = S390_CPU(cs); + SigpInfo *si = arg; + struct kvm_s390_irq irq = { + .type = KVM_S390_SIGP_STOP, + }; + + if (s390_cpu_get_state(si->cpu) != CPU_STATE_OPERATING) { + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; + return; + } + + /* disabled wait - sleeping in user space */ + if (CPU(si->cpu)->halted) { + s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu); + } else { + /* execute the stop function */ + si->cpu->env.sigp_order = SIGP_STOP; + kvm_s390_vcpu_interrupt(si->cpu, &irq); + } + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; +} + +#define KVM_S390_STORE_STATUS_DEF_ADDR offsetof(LowCore, floating_pt_save_area) +#define SAVE_AREA_SIZE 512 +static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) +{ + static const uint8_t ar_id = 1; + uint64_t ckc = cpu->env.ckc >> 8; + void *mem; + hwaddr len = SAVE_AREA_SIZE; + + mem = cpu_physical_memory_map(addr, &len, 1); + if (!mem) { + return -EFAULT; + } + if (len != SAVE_AREA_SIZE) { + cpu_physical_memory_unmap(mem, len, 1, 0); + return -EFAULT; + } + + if (store_arch) { + cpu_physical_memory_write(offsetof(LowCore, ar_access_id), &ar_id, 1); + } + memcpy(mem, &cpu->env.fregs, 128); + memcpy(mem + 128, &cpu->env.regs, 128); + memcpy(mem + 256, &cpu->env.psw, 16); + memcpy(mem + 280, &cpu->env.psa, 4); + memcpy(mem + 284, &cpu->env.fpc, 4); + memcpy(mem + 292, &cpu->env.todpr, 4); + memcpy(mem + 296, &cpu->env.cputm, 8); + memcpy(mem + 304, &ckc, 8); + memcpy(mem + 320, &cpu->env.aregs, 64); + memcpy(mem + 384, &cpu->env.cregs, 128); + + cpu_physical_memory_unmap(mem, len, 1, len); + + return 0; +} + +static void sigp_stop_and_store_status(void *arg) +{ + SigpInfo *si = arg; + struct kvm_s390_irq irq = { + .type = KVM_S390_SIGP_STOP, + }; + + /* disabled wait - sleeping in user space */ + if (s390_cpu_get_state(si->cpu) == CPU_STATE_OPERATING && + CPU(si->cpu)->halted) { + s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu); + } + + switch (s390_cpu_get_state(si->cpu)) { + case CPU_STATE_OPERATING: + si->cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; + kvm_s390_vcpu_interrupt(si->cpu, &irq); + /* store will be performed when handling the stop intercept */ + break; + case CPU_STATE_STOPPED: + /* already stopped, just store the status */ + cpu_synchronize_state(CPU(si->cpu)); + kvm_s390_store_status(si->cpu, KVM_S390_STORE_STATUS_DEF_ADDR, true); + break; + } + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; +} + +static void sigp_store_status_at_address(void *arg) +{ + SigpInfo *si = arg; + uint32_t address = si->param & 0x7ffffe00u; + + /* cpu has to be stopped */ + if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); + return; + } + + cpu_synchronize_state(CPU(si->cpu)); + + if (kvm_s390_store_status(si->cpu, address, false)) { + set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); + return; + } + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; +} + +static void sigp_restart(void *arg) +{ + SigpInfo *si = arg; struct kvm_s390_irq irq = { .type = KVM_S390_RESTART, }; - kvm_s390_vcpu_interrupt(cpu, &irq); - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); + switch (s390_cpu_get_state(si->cpu)) { + case CPU_STATE_STOPPED: + /* the restart irq has to be delivered prior to any other pending irq */ + cpu_synchronize_state(CPU(si->cpu)); + do_restart_interrupt(&si->cpu->env); + s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu); + break; + case CPU_STATE_OPERATING: + kvm_s390_vcpu_interrupt(si->cpu, &irq); + break; + } + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } int kvm_s390_cpu_restart(S390CPU *cpu) { - run_on_cpu(CPU(cpu), sigp_cpu_restart, CPU(cpu)); + SigpInfo si = { + .cpu = cpu, + }; + + run_on_cpu(CPU(cpu), sigp_restart, &si); DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env); return 0; } static void sigp_initial_cpu_reset(void *arg) { - CPUState *cpu = arg; - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); + SigpInfo *si = arg; + CPUState *cs = CPU(si->cpu); + S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu); - cpu_synchronize_state(cpu); - scc->initial_cpu_reset(cpu); - cpu_synchronize_post_reset(cpu); + cpu_synchronize_state(cs); + scc->initial_cpu_reset(cs); + cpu_synchronize_post_reset(cs); + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } static void sigp_cpu_reset(void *arg) { - CPUState *cpu = arg; - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); + SigpInfo *si = arg; + CPUState *cs = CPU(si->cpu); + S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu); - cpu_synchronize_state(cpu); - scc->cpu_reset(cpu); - cpu_synchronize_post_reset(cpu); + cpu_synchronize_state(cs); + scc->cpu_reset(cs); + cpu_synchronize_post_reset(cs); + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } -#define SIGP_ORDER_MASK 0x000000ff - -static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) +static void sigp_set_prefix(void *arg) { - CPUS390XState *env = &cpu->env; - uint8_t order_code; - uint16_t cpu_addr; - S390CPU *target_cpu; - uint64_t *statusreg = &env->regs[ipa1 >> 4]; - int cc; + SigpInfo *si = arg; + uint32_t addr = si->param & 0x7fffe000u; - cpu_synchronize_state(CPU(cpu)); + cpu_synchronize_state(CPU(si->cpu)); - /* get order code */ - order_code = decode_basedisp_rs(env, run->s390_sieic.ipb) & SIGP_ORDER_MASK; + if (!address_space_access_valid(&address_space_memory, addr, + sizeof(struct LowCore), false)) { + set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); + return; + } - cpu_addr = env->regs[ipa1 & 0x0f]; - target_cpu = s390_cpu_addr2state(cpu_addr); - if (target_cpu == NULL) { - cc = 3; /* not operational */ - goto out; + /* cpu has to be stopped */ + if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) { + set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); + return; } - switch (order_code) { + si->cpu->env.psa = addr; + cpu_synchronize_post_init(CPU(si->cpu)); + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; +} + +static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order, + uint64_t param, uint64_t *status_reg) +{ + SigpInfo si = { + .cpu = dst_cpu, + .param = param, + .status_reg = status_reg, + }; + + /* cpu available? */ + if (dst_cpu == NULL) { + return SIGP_CC_NOT_OPERATIONAL; + } + + /* only resets can break pending orders */ + if (dst_cpu->env.sigp_order != 0 && + order != SIGP_CPU_RESET && + order != SIGP_INITIAL_CPU_RESET) { + return SIGP_CC_BUSY; + } + + switch (order) { case SIGP_START: - run_on_cpu(CPU(target_cpu), sigp_cpu_start, CPU(target_cpu)); - cc = 0; + run_on_cpu(CPU(dst_cpu), sigp_start, &si); + break; + case SIGP_STOP: + run_on_cpu(CPU(dst_cpu), sigp_stop, &si); break; case SIGP_RESTART: - run_on_cpu(CPU(target_cpu), sigp_cpu_restart, CPU(target_cpu)); - cc = 0; + run_on_cpu(CPU(dst_cpu), sigp_restart, &si); break; - case SIGP_SET_ARCH: - *statusreg &= 0xffffffff00000000UL; - *statusreg |= SIGP_STAT_INVALID_PARAMETER; - cc = 1; /* status stored */ + case SIGP_STOP_STORE_STATUS: + run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, &si); + break; + case SIGP_STORE_STATUS_ADDR: + run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, &si); + break; + case SIGP_SET_PREFIX: + run_on_cpu(CPU(dst_cpu), sigp_set_prefix, &si); break; case SIGP_INITIAL_CPU_RESET: - run_on_cpu(CPU(target_cpu), sigp_initial_cpu_reset, CPU(target_cpu)); - cc = 0; + run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, &si); break; case SIGP_CPU_RESET: - run_on_cpu(CPU(target_cpu), sigp_cpu_reset, CPU(target_cpu)); - cc = 0; + run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, &si); break; default: - DPRINTF("KVM: unknown SIGP: 0x%x\n", order_code); - *statusreg &= 0xffffffff00000000UL; - *statusreg |= SIGP_STAT_INVALID_ORDER; - cc = 1; /* status stored */ + DPRINTF("KVM: unknown SIGP: 0x%x\n", order); + set_sigp_status(&si, SIGP_STAT_INVALID_ORDER); + } + + return si.cc; +} + +static int sigp_set_architecture(S390CPU *cpu, uint32_t param, + uint64_t *status_reg) +{ + CPUState *cur_cs; + S390CPU *cur_cpu; + + /* due to the BQL, we are the only active cpu */ + CPU_FOREACH(cur_cs) { + cur_cpu = S390_CPU(cur_cs); + if (cur_cpu->env.sigp_order != 0) { + return SIGP_CC_BUSY; + } + cpu_synchronize_state(cur_cs); + /* all but the current one have to be stopped */ + if (cur_cpu != cpu && + s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) { + *status_reg &= 0xffffffff00000000ULL; + *status_reg |= SIGP_STAT_INCORRECT_STATE; + return SIGP_CC_STATUS_STORED; + } + } + + switch (param & 0xff) { + case SIGP_MODE_ESA_S390: + /* not supported */ + return SIGP_CC_NOT_OPERATIONAL; + case SIGP_MODE_Z_ARCH_TRANS_ALL_PSW: + case SIGP_MODE_Z_ARCH_TRANS_CUR_PSW: + CPU_FOREACH(cur_cs) { + cur_cpu = S390_CPU(cur_cs); + cur_cpu->env.pfault_token = -1UL; + } break; + default: + *status_reg &= 0xffffffff00000000ULL; + *status_reg |= SIGP_STAT_INVALID_PARAMETER; + return SIGP_CC_STATUS_STORED; } -out: - setcc(cpu, cc); - return 0; + return SIGP_CC_ORDER_CODE_ACCEPTED; +} + +#define SIGP_ORDER_MASK 0x000000ff + +static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) +{ + CPUS390XState *env = &cpu->env; + const uint8_t r1 = ipa1 >> 4; + const uint8_t r3 = ipa1 & 0x0f; + int ret; + uint8_t order; + uint64_t *status_reg; + uint64_t param; + S390CPU *dst_cpu = NULL; + + cpu_synchronize_state(CPU(cpu)); + + /* get order code */ + order = decode_basedisp_rs(env, run->s390_sieic.ipb) & SIGP_ORDER_MASK; + status_reg = &env->regs[r1]; + param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1]; + + switch (order) { + case SIGP_SET_ARCH: + ret = sigp_set_architecture(cpu, param, status_reg); + break; + default: + /* all other sigp orders target a single vcpu */ + dst_cpu = s390_cpu_addr2state(env->regs[r3]); + ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg); + } + + trace_kvm_sigp_finished(order, CPU(cpu)->cpu_index, + dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret); + + if (ret >= 0) { + setcc(cpu, ret); + return 0; + } + + return ret; } static int handle_instruction(S390CPU *cpu, struct kvm_run *run) @@ -1317,6 +1612,11 @@ static int handle_intercept(S390CPU *cpu) if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) { qemu_system_shutdown_request(); } + if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) { + kvm_s390_store_status(cpu, KVM_S390_STORE_STATUS_DEF_ADDR, + true); + } + cpu->env.sigp_order = 0; r = EXCP_HALTED; break; case ICPT_SOFT_INTERCEPT: diff --git a/target-s390x/machine.c b/target-s390x/machine.c index fbcb0d0..bd4cea7 100644 --- a/target-s390x/machine.c +++ b/target-s390x/machine.c @@ -36,8 +36,8 @@ static int cpu_post_load(void *opaque, int version_id) const VMStateDescription vmstate_s390_cpu = { .name = "cpu", .post_load = cpu_post_load, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT64(env.fregs[0].ll, S390CPU), VMSTATE_UINT64(env.fregs[1].ll, S390CPU), @@ -71,6 +71,7 @@ const VMStateDescription vmstate_s390_cpu = { VMSTATE_UINT32_ARRAY(env.aregs, S390CPU, 16), VMSTATE_UINT64_ARRAY(env.cregs, S390CPU, 16), VMSTATE_UINT8(env.cpu_state, S390CPU), + VMSTATE_UINT8(env.sigp_order, S390CPU), VMSTATE_END_OF_LIST() }, }; diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index 1c3df8e..e1007fa 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -456,7 +456,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, uint64_t cpu_addr) { - int cc = 0; + int cc = SIGP_CC_ORDER_CODE_ACCEPTED; HELPER_LOG("%s: %016" PRIx64 " %08x %016" PRIx64 "\n", __func__, order_code, r1, cpu_addr); @@ -490,7 +490,7 @@ uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, default: /* unknown sigp */ fprintf(stderr, "XXX unknown sigp: 0x%" PRIx64 "\n", order_code); - cc = 3; + cc = SIGP_CC_NOT_OPERATIONAL; } return cc; diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index b2fb199..c8dea6c 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -221,14 +221,7 @@ int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr); void cpu_load_tlb(CPUSH4State * env); -static inline CPUSH4State *cpu_init(const char *cpu_model) -{ - SuperHCPU *cpu = cpu_sh4_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_sh4_init(cpu_model)) #define cpu_exec cpu_sh4_exec #define cpu_gen_code cpu_sh4_gen_code diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 0a50e5d..f5c9006 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -594,14 +594,7 @@ hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr, int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); #ifndef NO_CPU_IO_DEFS -static inline CPUSPARCState *cpu_init(const char *cpu_model) -{ - SPARCCPU *cpu = cpu_sparc_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_sparc_init(cpu_model)) #endif #define cpu_exec cpu_sparc_exec diff --git a/target-tricore/cpu.h b/target-tricore/cpu.h index e5409e4..b473426 100644 --- a/target-tricore/cpu.h +++ b/target-tricore/cpu.h @@ -378,15 +378,7 @@ static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, target_ulong *pc, TriCoreCPU *cpu_tricore_init(const char *cpu_model); -static inline CPUTriCoreState *cpu_init(const char *cpu_model) -{ - TriCoreCPU *cpu = cpu_tricore_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; - -} +#define cpu_init(cpu_model) CPU(cpu_tricore_init(cpu_model)) /* helpers.c */ diff --git a/target-unicore32/cpu.h b/target-unicore32/cpu.h index 50972f9..14dc862 100644 --- a/target-unicore32/cpu.h +++ b/target-unicore32/cpu.h @@ -122,11 +122,9 @@ 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 cpu_init uc32_cpu_init #define cpu_exec uc32_cpu_exec #define cpu_signal_handler uc32_cpu_signal_handler -CPUUniCore32State *uc32_cpu_init(const char *cpu_model); int uc32_cpu_exec(CPUUniCore32State *s); int uc32_cpu_signal_handler(int host_signum, void *pinfo, void *puc); @@ -143,6 +141,10 @@ static inline int cpu_mmu_index(CPUUniCore32State *env) #include "cpu-qom.h" #include "exec/exec-all.h" +UniCore32CPU *uc32_cpu_init(const char *cpu_model); + +#define cpu_init(cpu_model) CPU(uc32_cpu_init(cpu_model)) + static inline void cpu_get_tb_cpu_state(CPUUniCore32State *env, target_ulong *pc, target_ulong *cs_base, int *flags) { diff --git a/target-unicore32/helper.c b/target-unicore32/helper.c index b4654fa..ae63277 100644 --- a/target-unicore32/helper.c +++ b/target-unicore32/helper.c @@ -25,15 +25,9 @@ #define DPRINTF(fmt, ...) do {} while (0) #endif -CPUUniCore32State *uc32_cpu_init(const char *cpu_model) +UniCore32CPU *uc32_cpu_init(const char *cpu_model) { - UniCore32CPU *cpu; - - cpu = UNICORE32_CPU(cpu_generic_init(TYPE_UNICORE32_CPU, cpu_model)); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; + return UNICORE32_CPU(cpu_generic_init(TYPE_UNICORE32_CPU, cpu_model)); } uint32_t HELPER(clo)(uint32_t x) diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index 60ee563..dfd0d1c 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -379,14 +379,7 @@ typedef struct CPUXtensaState { XtensaCPU *cpu_xtensa_init(const char *cpu_model); -static inline CPUXtensaState *cpu_init(const char *cpu_model) -{ - XtensaCPU *cpu = cpu_xtensa_init(cpu_model); - if (cpu == NULL) { - return NULL; - } - return &cpu->env; -} +#define cpu_init(cpu_model) CPU(cpu_xtensa_init(cpu_model)) void xtensa_translate_init(void); void xtensa_breakpoint_handler(CPUState *cs); diff --git a/tests/Makefile b/tests/Makefile index 307035c..588f438 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -191,6 +191,8 @@ gcov-files-sparc-y += hw/timer/m48t59.c gcov-files-sparc64-y += hw/timer/m48t59.c check-qtest-arm-y = tests/tmp105-test$(EXESUF) gcov-files-arm-y += hw/misc/tmp105.c +check-qtest-arm-y += tests/virtio-blk-test$(EXESUF) +gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c check-qtest-ppc-y += tests/boot-order-test$(EXESUF) check-qtest-ppc64-y += tests/boot-order-test$(EXESUF) check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF) @@ -239,8 +241,6 @@ $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o -tests/test-x86-cpuid.o: QEMU_INCLUDES += -I$(SRC_PATH)/target-i386 - tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a @@ -315,8 +315,8 @@ libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o libqos-pc-obj-y += tests/libqos/ahci.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o -libqos-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o +libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o tests/rtc-test$(EXESUF): tests/rtc-test.o tests/m48t59-test$(EXESUF): tests/m48t59-test.o diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 53fd068..cf0b98b 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -107,6 +107,21 @@ static void ahci_shutdown(AHCIQState *ahci) qtest_shutdown(qs); } +/** + * Boot and fully enable the HBA device. + * @see ahci_boot, ahci_pci_enable and ahci_hba_enable. + */ +static AHCIQState *ahci_boot_and_enable(void) +{ + AHCIQState *ahci; + ahci = ahci_boot(); + + ahci_pci_enable(ahci); + ahci_hba_enable(ahci); + + return ahci; +} + /*** Specification Adherence Tests ***/ /** @@ -716,12 +731,12 @@ static void ahci_test_identify(AHCIQState *ahci) g_assert_cmphex(sect_size, ==, 0x200); } -static void ahci_test_dma_rw_simple(AHCIQState *ahci) +static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, + uint8_t read_cmd, uint8_t write_cmd) { uint64_t ptr; uint8_t port; unsigned i; - const unsigned bufsize = 4096; unsigned char *tx = g_malloc(bufsize); unsigned char *rx = g_malloc0(bufsize); @@ -736,16 +751,16 @@ static void ahci_test_dma_rw_simple(AHCIQState *ahci) ptr = ahci_alloc(ahci, bufsize); g_assert(ptr); - /* Write some indicative pattern to our 4K buffer. */ + /* Write some indicative pattern to our buffer. */ for (i = 0; i < bufsize; i++) { tx[i] = (bufsize - i); } memwrite(ptr, tx, bufsize); /* Write this buffer to disk, then read it back to the DMA buffer. */ - ahci_guest_io(ahci, port, CMD_WRITE_DMA, ptr, bufsize); + ahci_guest_io(ahci, port, write_cmd, ptr, bufsize); qmemset(ptr, 0x00, bufsize); - ahci_guest_io(ahci, port, CMD_READ_DMA, ptr, bufsize); + ahci_guest_io(ahci, port, read_cmd, ptr, bufsize); /*** Read back the Data ***/ memread(ptr, rx, bufsize); @@ -831,25 +846,204 @@ static void test_identify(void) { AHCIQState *ahci; - ahci = ahci_boot(); - ahci_pci_enable(ahci); - ahci_hba_enable(ahci); + ahci = ahci_boot_and_enable(); ahci_test_identify(ahci); ahci_shutdown(ahci); } /** - * Perform a simple DMA R/W test, using a single PRD and non-NCQ commands. + * Fragmented DMA test: Perform a standard 4K DMA read/write + * test, but make sure the physical regions are fragmented to + * be very small, each just 32 bytes, to see how AHCI performs + * with chunks defined to be much less than a sector. */ -static void test_dma_rw_simple(void) +static void test_dma_fragmented(void) { AHCIQState *ahci; + AHCICommand *cmd; + uint8_t px; + size_t bufsize = 4096; + unsigned char *tx = g_malloc(bufsize); + unsigned char *rx = g_malloc0(bufsize); + unsigned i; + uint64_t ptr; + + ahci = ahci_boot_and_enable(); + px = ahci_port_select(ahci); + ahci_port_clear(ahci, px); + + /* create pattern */ + for (i = 0; i < bufsize; i++) { + tx[i] = (bufsize - i); + } + + /* Create a DMA buffer in guest memory, and write our pattern to it. */ + ptr = guest_alloc(ahci->parent->alloc, bufsize); + g_assert(ptr); + memwrite(ptr, tx, bufsize); + + cmd = ahci_command_create(CMD_WRITE_DMA); + ahci_command_adjust(cmd, 0, ptr, bufsize, 32); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + g_free(cmd); + + cmd = ahci_command_create(CMD_READ_DMA); + ahci_command_adjust(cmd, 0, ptr, bufsize, 32); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + g_free(cmd); + + /* Read back the guest's receive buffer into local memory */ + memread(ptr, rx, bufsize); + guest_free(ahci->parent->alloc, ptr); + + g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); - ahci = ahci_boot(); - ahci_pci_enable(ahci); - ahci_hba_enable(ahci); - ahci_test_dma_rw_simple(ahci); ahci_shutdown(ahci); + + g_free(rx); + g_free(tx); +} + +/******************************************************************************/ +/* AHCI I/O Test Matrix Definitions */ + +enum BuffLen { + LEN_BEGIN = 0, + LEN_SIMPLE = LEN_BEGIN, + LEN_DOUBLE, + LEN_LONG, + LEN_SHORT, + NUM_LENGTHS +}; + +static const char *buff_len_str[NUM_LENGTHS] = { "simple", "double", + "long", "short" }; + +enum AddrMode { + ADDR_MODE_BEGIN = 0, + ADDR_MODE_LBA28 = ADDR_MODE_BEGIN, + ADDR_MODE_LBA48, + NUM_ADDR_MODES +}; + +static const char *addr_mode_str[NUM_ADDR_MODES] = { "lba28", "lba48" }; + +enum IOMode { + MODE_BEGIN = 0, + MODE_PIO = MODE_BEGIN, + MODE_DMA, + NUM_MODES +}; + +static const char *io_mode_str[NUM_MODES] = { "pio", "dma" }; + +enum IOOps { + IO_BEGIN = 0, + IO_READ = IO_BEGIN, + IO_WRITE, + NUM_IO_OPS +}; + +typedef struct AHCIIOTestOptions { + enum BuffLen length; + enum AddrMode address_type; + enum IOMode io_type; +} AHCIIOTestOptions; + +/** + * Table of possible I/O ATA commands given a set of enumerations. + */ +static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = { + [MODE_PIO] = { + [ADDR_MODE_LBA28] = { + [IO_READ] = CMD_READ_PIO, + [IO_WRITE] = CMD_WRITE_PIO }, + [ADDR_MODE_LBA48] = { + [IO_READ] = CMD_READ_PIO_EXT, + [IO_WRITE] = CMD_WRITE_PIO_EXT } + }, + [MODE_DMA] = { + [ADDR_MODE_LBA28] = { + [IO_READ] = CMD_READ_DMA, + [IO_WRITE] = CMD_WRITE_DMA }, + [ADDR_MODE_LBA48] = { + [IO_READ] = CMD_READ_DMA_EXT, + [IO_WRITE] = CMD_WRITE_DMA_EXT } + } +}; + +/** + * Test a Read/Write pattern using various commands, addressing modes, + * transfer modes, and buffer sizes. + */ +static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma, + unsigned bufsize) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(); + ahci_test_io_rw_simple(ahci, bufsize, + io_cmds[dma][lba48][IO_READ], + io_cmds[dma][lba48][IO_WRITE]); + ahci_shutdown(ahci); +} + +/** + * Demultiplex the test data and invoke the actual test routine. + */ +static void test_io_interface(gconstpointer opaque) +{ + AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque; + unsigned bufsize; + + switch (opts->length) { + case LEN_SIMPLE: + bufsize = 4096; + break; + case LEN_DOUBLE: + bufsize = 8192; + break; + case LEN_LONG: + bufsize = 4096 * 64; + break; + case LEN_SHORT: + bufsize = 512; + break; + default: + g_assert_not_reached(); + } + + test_io_rw_interface(opts->address_type, opts->io_type, bufsize); + g_free(opts); + return; +} + +static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, + enum BuffLen len) +{ + static const char *arch; + char *name; + AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions)); + + opts->length = len; + opts->address_type = addr; + opts->io_type = type; + + if (!arch) { + arch = qtest_get_arch(); + } + + name = g_strdup_printf("/%s/ahci/io/%s/%s/%s", arch, + io_mode_str[type], + addr_mode_str[addr], + buff_len_str[len]); + + g_test_add_data_func(name, opts, test_io_interface); + g_free(name); } /******************************************************************************/ @@ -860,6 +1054,7 @@ int main(int argc, char **argv) int fd; int ret; int c; + int i, j, k; static struct option long_options[] = { {"pedantic", no_argument, 0, 'p' }, @@ -907,7 +1102,16 @@ int main(int argc, char **argv) qtest_add_func("/ahci/hba_spec", test_hba_spec); qtest_add_func("/ahci/hba_enable", test_hba_enable); qtest_add_func("/ahci/identify", test_identify); - qtest_add_func("/ahci/dma/simple", test_dma_rw_simple); + + for (i = MODE_BEGIN; i < NUM_MODES; i++) { + for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) { + for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) { + create_ahci_io_test(i, j, k); + } + } + } + + qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented); ret = g_test_run(); diff --git a/tests/ide-test.c b/tests/ide-test.c index 29f4039..b28a302 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -118,7 +118,6 @@ static void ide_test_start(const char *cmdline_fmt, ...) va_end(ap); qtest_start(cmdline); - qtest_irq_intercept_in(global_qtest, "ioapic"); guest_malloc = pc_alloc_init(); g_free(cmdline); @@ -388,6 +387,7 @@ static void test_bmdma_setup(void) "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " "-global ide-hd.ver=%s", tmp_path, "testdisk", "version"); + qtest_irq_intercept_in(global_qtest, "ioapic"); } static void test_bmdma_teardown(void) @@ -516,7 +516,7 @@ static void prepare_blkdebug_script(const char *debug_fn, const char *event) g_assert(ret == 0); } -static void test_retry_flush(void) +static void test_retry_flush(const char *machine) { uint8_t data; const char *s; @@ -580,6 +580,16 @@ static void test_flush_nodev(void) ide_test_quit(); } +static void test_pci_retry_flush(const char *machine) +{ + test_retry_flush("pc"); +} + +static void test_isa_retry_flush(const char *machine) +{ + test_retry_flush("isapc"); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -617,9 +627,9 @@ int main(int argc, char **argv) qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown); qtest_add_func("/ide/flush", test_flush); - qtest_add_func("/ide/flush_nodev", test_flush_nodev); - - qtest_add_func("/ide/retry/flush", test_retry_flush); + qtest_add_func("/ide/flush/nodev", test_flush_nodev); + qtest_add_func("/ide/flush/retry_pci", test_pci_retry_flush); + qtest_add_func("/ide/flush/retry_isa", test_isa_retry_flush); ret = g_test_run(); diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index a6105c7..b0f39a5 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -487,7 +487,7 @@ void ahci_get_command_header(AHCIQState *ahci, uint8_t port, void ahci_set_command_header(AHCIQState *ahci, uint8_t port, uint8_t slot, AHCICommandHeader *cmd) { - AHCICommandHeader tmp; + AHCICommandHeader tmp = { .flags = 0 }; uint64_t ba = ahci->port[port].clb; ba += slot * sizeof(AHCICommandHeader); @@ -713,6 +713,40 @@ void ahci_command_free(AHCICommand *cmd) g_free(cmd); } +void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags) +{ + cmd->header.flags |= cmdh_flags; +} + +void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags) +{ + cmd->header.flags &= ~cmdh_flags; +} + +void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect) +{ + RegH2DFIS *fis = &(cmd->fis); + if (cmd->props->lba28) { + g_assert_cmphex(lba_sect, <=, 0xFFFFFFF); + } else if (cmd->props->lba48) { + g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF); + } else { + /* Can't set offset if we don't know the format. */ + g_assert_not_reached(); + } + + /* LBA28 uses the low nibble of the device/control register for LBA24:27 */ + fis->lba_lo[0] = (lba_sect & 0xFF); + fis->lba_lo[1] = (lba_sect >> 8) & 0xFF; + fis->lba_lo[2] = (lba_sect >> 16) & 0xFF; + if (cmd->props->lba28) { + fis->device = (fis->device & 0xF0) || (lba_sect >> 24) & 0x0F; + } + fis->lba_hi[0] = (lba_sect >> 24) & 0xFF; + fis->lba_hi[1] = (lba_sect >> 32) & 0xFF; + fis->lba_hi[2] = (lba_sect >> 40) & 0xFF; +} + void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer) { cmd->buffer = buffer; @@ -740,6 +774,14 @@ void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size) ahci_command_set_sizes(cmd, cmd->xbytes, prd_size); } +void ahci_command_adjust(AHCICommand *cmd, uint64_t offset, uint64_t buffer, + uint64_t xbytes, unsigned prd_size) +{ + ahci_command_set_sizes(cmd, xbytes, prd_size); + ahci_command_set_buffer(cmd, buffer); + ahci_command_set_offset(cmd, offset); +} + void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port) { uint16_t i, prdtl; diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 39b99d3..888545d 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -537,11 +537,16 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd); void ahci_command_free(AHCICommand *cmd); /* Command adjustments */ +void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags); +void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags); +void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect); void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer); void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes); void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size); void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, unsigned prd_size); +void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer, + uint64_t xbytes, unsigned prd_size); /* Command Misc */ uint8_t ahci_command_slot(AHCICommand *cmd); diff --git a/tests/libqos/malloc-generic.c b/tests/libqos/malloc-generic.c new file mode 100644 index 0000000..d30a2f4 --- /dev/null +++ b/tests/libqos/malloc-generic.c @@ -0,0 +1,39 @@ +/* + * Basic libqos generic malloc support + * + * Copyright (c) 2014 Marc Marà + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include "libqos/malloc-generic.h" +#include "libqos/malloc.h" + +/* + * Mostly for valgrind happiness, but it does offer + * a chokepoint for debugging guest memory leaks, too. + */ +void generic_alloc_uninit(QGuestAllocator *allocator) +{ + alloc_uninit(allocator); +} + +QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, + uint32_t page_size, QAllocOpts flags) +{ + QGuestAllocator *s; + uint64_t start = base_addr + (1 << 20); /* Start at 1MB */ + + s = alloc_init_flags(flags, start, start + size); + alloc_set_page_size(s, page_size); + + return s; +} + +inline QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, + uint32_t page_size) +{ + return generic_alloc_init_flags(base_addr, size, page_size, ALLOC_NO_FLAGS); +} diff --git a/tests/libqos/malloc-generic.h b/tests/libqos/malloc-generic.h new file mode 100644 index 0000000..90104ec --- /dev/null +++ b/tests/libqos/malloc-generic.h @@ -0,0 +1,21 @@ +/* + * Basic libqos generic malloc support + * + * Copyright (c) 2014 Marc Marà + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_MALLOC_GENERIC_H +#define LIBQOS_MALLOC_GENERIC_H + +#include "libqos/malloc.h" + +QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, + uint32_t page_size); +QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, + uint32_t page_size, QAllocOpts flags); +void generic_alloc_uninit(QGuestAllocator *allocator); + +#endif diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c new file mode 100644 index 0000000..b3e62e7 --- /dev/null +++ b/tests/libqos/virtio-mmio.c @@ -0,0 +1,198 @@ +/* + * libqos virtio MMIO driver + * + * Copyright (c) 2014 Marc Marà + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <stdio.h> +#include "libqtest.h" +#include "libqos/virtio.h" +#include "libqos/virtio-mmio.h" +#include "libqos/malloc.h" +#include "libqos/malloc-generic.h" + +static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readb(dev->addr + addr); +} + +static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readw(dev->addr + addr); +} + +static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readl(dev->addr + addr); +} + +static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return readq(dev->addr + addr); +} + +static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); + return readl(dev->addr + QVIRTIO_MMIO_HOST_FEATURES); +} + +static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + dev->features = features; + writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); + writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); +} + +static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return dev->features; +} + +static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return (uint8_t)readl(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS); +} + +static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status); +} + +static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + uint32_t isr; + + isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1; + if (isr != 0) { + writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1); + return true; + } + + return false; +} + +static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + uint32_t isr; + + isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2; + if (isr != 0) { + writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2); + return true; + } + + return false; +} + +static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index); + + g_assert_cmphex(readl(dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0); +} + +static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + return (uint16_t)readl(dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); +} + +static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); +} + +static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtQueue *vq; + uint64_t addr; + + vq = g_malloc0(sizeof(*vq)); + qvirtio_mmio_queue_select(d, index); + writel(dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); + + vq->index = index; + vq->size = qvirtio_mmio_get_queue_size(d); + vq->free_head = 0; + vq->num_free = vq->size; + vq->align = dev->page_size; + vq->indirect = (dev->features & QVIRTIO_F_RING_INDIRECT_DESC) != 0; + vq->event = (dev->features & QVIRTIO_F_RING_EVENT_IDX) != 0; + + writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); + + /* Check different than 0 */ + g_assert_cmpint(vq->size, !=, 0); + + /* Check power of 2 */ + g_assert_cmpint(vq->size & (vq->size - 1), ==, 0); + + addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size)); + qvring_init(alloc, vq, addr); + qvirtio_mmio_set_queue_address(d, vq->desc / dev->page_size); + + return vq; +} + +static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + writel(dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index); +} + +const QVirtioBus qvirtio_mmio = { + .config_readb = qvirtio_mmio_config_readb, + .config_readw = qvirtio_mmio_config_readw, + .config_readl = qvirtio_mmio_config_readl, + .config_readq = qvirtio_mmio_config_readq, + .get_features = qvirtio_mmio_get_features, + .set_features = qvirtio_mmio_set_features, + .get_guest_features = qvirtio_mmio_get_guest_features, + .get_status = qvirtio_mmio_get_status, + .set_status = qvirtio_mmio_set_status, + .get_queue_isr_status = qvirtio_mmio_get_queue_isr_status, + .get_config_isr_status = qvirtio_mmio_get_config_isr_status, + .queue_select = qvirtio_mmio_queue_select, + .get_queue_size = qvirtio_mmio_get_queue_size, + .set_queue_address = qvirtio_mmio_set_queue_address, + .virtqueue_setup = qvirtio_mmio_virtqueue_setup, + .virtqueue_kick = qvirtio_mmio_virtqueue_kick, +}; + +QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) +{ + QVirtioMMIODevice *dev; + uint32_t magic; + dev = g_malloc0(sizeof(*dev)); + + magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE); + g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)); + + dev->addr = addr; + dev->page_size = page_size; + dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); + + writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); + + return dev; +} diff --git a/tests/libqos/virtio-mmio.h b/tests/libqos/virtio-mmio.h new file mode 100644 index 0000000..e3e52b9 --- /dev/null +++ b/tests/libqos/virtio-mmio.h @@ -0,0 +1,46 @@ +/* + * libqos virtio MMIO definitions + * + * Copyright (c) 2014 Marc Marà + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_VIRTIO_MMIO_H +#define LIBQOS_VIRTIO_MMIO_H + +#include "libqos/virtio.h" + +#define QVIRTIO_MMIO_MAGIC_VALUE 0x000 +#define QVIRTIO_MMIO_VERSION 0x004 +#define QVIRTIO_MMIO_DEVICE_ID 0x008 +#define QVIRTIO_MMIO_VENDOR_ID 0x00C +#define QVIRTIO_MMIO_HOST_FEATURES 0x010 +#define QVIRTIO_MMIO_HOST_FEATURES_SEL 0x014 +#define QVIRTIO_MMIO_GUEST_FEATURES 0x020 +#define QVIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 +#define QVIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 +#define QVIRTIO_MMIO_QUEUE_SEL 0x030 +#define QVIRTIO_MMIO_QUEUE_NUM_MAX 0x034 +#define QVIRTIO_MMIO_QUEUE_NUM 0x038 +#define QVIRTIO_MMIO_QUEUE_ALIGN 0x03C +#define QVIRTIO_MMIO_QUEUE_PFN 0x040 +#define QVIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define QVIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define QVIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define QVIRTIO_MMIO_DEVICE_STATUS 0x070 +#define QVIRTIO_MMIO_DEVICE_SPECIFIC 0x100 + +typedef struct QVirtioMMIODevice { + QVirtioDevice vdev; + uint64_t addr; + uint32_t page_size; + uint32_t features; /* As it cannot be read later, save it */ +} QVirtioMMIODevice; + +extern const QVirtioBus qvirtio_mmio; + +QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size); + +#endif diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 788ebaf..f9fb924 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -60,25 +60,25 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) *vpcidev = (QVirtioPCIDevice *)d; } -static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, void *addr) +static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, addr); + return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr); } -static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, void *addr) +static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, addr); + return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr); } -static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, void *addr) +static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, addr); + return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr); } -static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) +static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; int i; @@ -86,11 +86,13 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) if (qtest_big_endian()) { for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << (7 - i) * 8; + u64 |= (uint64_t)qpci_io_readb(dev->pdev, + (void *)(uintptr_t)addr + i) << (7 - i) * 8; } } else { for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, addr + i) << i * 8; + u64 |= (uint64_t)qpci_io_readb(dev->pdev, + (void *)(uintptr_t)addr + i) << i * 8; } } @@ -100,31 +102,31 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, void *addr) static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_DEVICE_FEATURES); + return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_FEATURES); } static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES, features); + qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES, features); } static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_GUEST_FEATURES); + return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES); } static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS); + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS); } static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_DEVICE_STATUS, status); + qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS, status); } static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) @@ -140,11 +142,15 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) return qpci_msix_pending(dev->pdev, vqpci->msix_entry); } else { data = readl(vqpci->msix_addr); - writel(vqpci->msix_addr, 0); - return data == vqpci->msix_data; + if (data == vqpci->msix_data) { + writel(vqpci->msix_addr, 0); + return true; + } else { + return false; + } } } else { - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 1; + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 1; } } @@ -160,30 +166,34 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) return qpci_msix_pending(dev->pdev, dev->config_msix_entry); } else { data = readl(dev->config_msix_addr); - writel(dev->config_msix_addr, 0); - return data == dev->config_msix_data; + if (data == dev->config_msix_data) { + writel(dev->config_msix_addr, 0); + return true; + } else { + return false; + } } } else { - return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_ISR_STATUS) & 2; + return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 2; } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_QUEUE_SELECT, index); + qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SELECT, index); } static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_QUEUE_SIZE); + return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SIZE); } static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_QUEUE_ADDRESS, pfn); + qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_ADDRESS, pfn); } static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, @@ -225,7 +235,7 @@ static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_QUEUE_NOTIFY, vq->index); + qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_NOTIFY, vq->index); } const QVirtioBus qvirtio_pci = { @@ -305,8 +315,8 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); - qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_QUEUE_VECTOR); + qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR); g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); } @@ -337,7 +347,7 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); - qpci_io_writew(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_MSIX_CONF_VECTOR); + qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR); g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); } diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index 883f7ff..8f0e52a 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -13,18 +13,18 @@ #include "libqos/virtio.h" #include "libqos/pci.h" -#define QVIRTIO_DEVICE_FEATURES 0x00 -#define QVIRTIO_GUEST_FEATURES 0x04 -#define QVIRTIO_QUEUE_ADDRESS 0x08 -#define QVIRTIO_QUEUE_SIZE 0x0C -#define QVIRTIO_QUEUE_SELECT 0x0E -#define QVIRTIO_QUEUE_NOTIFY 0x10 -#define QVIRTIO_DEVICE_STATUS 0x12 -#define QVIRTIO_ISR_STATUS 0x13 -#define QVIRTIO_MSIX_CONF_VECTOR 0x14 -#define QVIRTIO_MSIX_QUEUE_VECTOR 0x16 -#define QVIRTIO_DEVICE_SPECIFIC_MSIX 0x18 -#define QVIRTIO_DEVICE_SPECIFIC_NO_MSIX 0x14 +#define QVIRTIO_PCI_DEVICE_FEATURES 0x00 +#define QVIRTIO_PCI_GUEST_FEATURES 0x04 +#define QVIRTIO_PCI_QUEUE_ADDRESS 0x08 +#define QVIRTIO_PCI_QUEUE_SIZE 0x0C +#define QVIRTIO_PCI_QUEUE_SELECT 0x0E +#define QVIRTIO_PCI_QUEUE_NOTIFY 0x10 +#define QVIRTIO_PCI_DEVICE_STATUS 0x12 +#define QVIRTIO_PCI_ISR_STATUS 0x13 +#define QVIRTIO_PCI_MSIX_CONF_VECTOR 0x14 +#define QVIRTIO_PCI_MSIX_QUEUE_VECTOR 0x16 +#define QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX 0x18 +#define QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX 0x14 #define QVIRTIO_PCI_ALIGN 4096 diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index a061289..3205b88 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -12,25 +12,25 @@ #include "libqos/virtio.h" uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readb(d, addr); } uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readw(d, addr); } uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readl(d, addr); } uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - void *addr) + uint64_t addr) { return bus->config_readq(d, addr); } diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 29fbacb..2449fee 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -93,10 +93,10 @@ typedef struct QVRingIndirectDesc { } QVRingIndirectDesc; typedef struct QVirtioBus { - uint8_t (*config_readb)(QVirtioDevice *d, void *addr); - uint16_t (*config_readw)(QVirtioDevice *d, void *addr); - uint32_t (*config_readl)(QVirtioDevice *d, void *addr); - uint64_t (*config_readq)(QVirtioDevice *d, void *addr); + uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr); + uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr); + uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr); + uint64_t (*config_readq)(QVirtioDevice *d, uint64_t addr); /* Get features of the device */ uint32_t (*get_features)(QVirtioDevice *d); @@ -144,13 +144,13 @@ static inline uint32_t qvring_size(uint32_t num, uint32_t align) } uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - void *addr); + uint64_t addr); uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d); void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, uint32_t features); diff --git a/tests/qemu-iotests/004 b/tests/qemu-iotests/004 index 651072e..2ad77ed 100755 --- a/tests/qemu-iotests/004 +++ b/tests/qemu-iotests/004 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw qcow qcow2 qed vdi vmdk vhdx _supported_proto generic _supported_os Linux diff --git a/tests/qemu-iotests/006.out b/tests/qemu-iotests/006.out deleted file mode 100644 index d135841..0000000 --- a/tests/qemu-iotests/006.out +++ /dev/null @@ -1,6 +0,0 @@ -QA output created by 006 - -creating 128GB image -qemu-img: The image size is too large for file format 'vpc' -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=137438953472 -*** done diff --git a/tests/qemu-iotests/007 b/tests/qemu-iotests/007 index fe1a743..7b5aff5 100755 --- a/tests/qemu-iotests/007 +++ b/tests/qemu-iotests/007 @@ -43,6 +43,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# refcount_bits must be at least 4 so we can create ten internal snapshots +# (1 bit supports none, 2 bits support two, 4 bits support 14) +_unsupported_imgopts 'refcount_bits=\(1\|2\)[^0-9]' echo echo "creating image" diff --git a/tests/qemu-iotests/015 b/tests/qemu-iotests/015 index 099d757..6f26095 100755 --- a/tests/qemu-iotests/015 +++ b/tests/qemu-iotests/015 @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' echo echo "creating image" diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index df2884b..0fc3244 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -46,6 +46,13 @@ _supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" "none" +# The refcount table tests expect a certain minimum width for refcount entries +# (so that the refcount table actually needs to grow); that minimum is 16 bits, +# being the default refcount entry width. +# 32 and 64 bits do not work either, however, due to different leaked cluster +# count on error. +# Thus, the only remaining option is refcount_bits=16. +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' echo "Errors while writing 128 kB" echo diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index 524f7ee..5e964fb 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -140,19 +140,13 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -Failed to flush the L2 table cache: Input/output error write failed: Input/output error - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -b -Failed to flush the L2 table cache: Input/output error write failed: Input/output error - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write @@ -174,19 +168,13 @@ This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -Failed to flush the L2 table cache: No space left on device write failed: No space left on device - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -b -Failed to flush the L2 table cache: No space left on device write failed: No space left on device - -127 leaked clusters were found on the image. -This means waste of disk space, but no harm to data. +No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write @@ -356,13 +344,11 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write -Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write -b -Failed to flush the refcount block cache: Input/output error write failed: Input/output error No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -382,13 +368,11 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write -Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write -b -Failed to flush the refcount block cache: No space left on device write failed: No space left on device No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index fa46ace..b9cd826 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -44,6 +44,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' offset_size=24 offset_l1_size=36 diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index b6311b0..765afdd 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -4,90 +4,90 @@ QA output created by 049 == 1. Traditional size parameter == qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == 2. Specifying size via -o == qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == 3. Invalid sizes == @@ -97,7 +97,7 @@ qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 qemu-img: qcow2 doesn't support shrinking images yet qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k qemu-img: Image size must be less than 8 EiB! @@ -105,14 +105,14 @@ qemu-img: Image size must be less than 8 EiB! qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 qemu-img: qcow2 doesn't support shrinking images yet qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for @@ -125,84 +125,84 @@ qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' == Check correct interpretation of suffixes for cluster size == qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=1024.0b TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5k TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5K TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o cluster_size=0.5M TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off refcount_bits=16 == Check compat level option == qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar' -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check preallocation option == qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234 -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off refcount_bits=16 == Check encryption option == qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o encryption=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check lazy_refcounts option (only with v3) == qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on refcount_bits=16 *** done diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 27138a2..0360f37 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# A compat=0.10 image is created in this test which does not support anything +# other than refcount_bits=16 +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' function do_run_qemu() { @@ -96,6 +99,12 @@ run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2 run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2 echo +echo === Device without drive === +echo + +run_qemu -device virtio-scsi-pci -device scsi-hd + +echo echo === Overriding backing file === echo diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index bf52bf0..09895e6 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -47,6 +47,14 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' +=== Device without drive === + +Testing: -device virtio-scsi-pci -device scsi-hd +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: -device scsi-hd: drive property not set +QEMU_PROG: -device scsi-hd: Device 'scsi-hd' could not be initialized + + === Overriding backing file === Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig -nodefaults @@ -115,8 +123,7 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive if=ide QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: Device needs media, but drive is empty -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device ide-hd failed +QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. Testing: -drive if=virtio QEMU X.Y.Z monitor - type 'help' for more information @@ -127,8 +134,7 @@ QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized Testing: -drive if=scsi QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device lsi53c895a failed +QEMU_PROG: Initialization of device lsi53c895a failed: Device initialization failed. Testing: -drive if=none,id=disk -device ide-cd,drive=disk QEMU X.Y.Z monitor - type 'help' for more information @@ -178,8 +184,7 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on QEMU X.Y.Z monitor - type 'help' for more information (qemu) QEMU_PROG: Can't use a read-only drive -QEMU_PROG: Device initialization failed. -QEMU_PROG: Initialization of device ide-hd failed +QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed. Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on QEMU X.Y.Z monitor - type 'help' for more information diff --git a/tests/qemu-iotests/058 b/tests/qemu-iotests/058 index a60b34b..f2bdd0b 100755 --- a/tests/qemu-iotests/058 +++ b/tests/qemu-iotests/058 @@ -89,6 +89,8 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux _require_command QEMU_NBD +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' # Use -f raw instead of -f $IMGFMT for the NBD connection QEMU_IO_NBD="$QEMU_IO -f raw --cache=$CACHEMODE" diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index dc9f6b7..7511189 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -18,6 +18,7 @@ cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: true qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write read 512/512 bytes at offset 0 diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index 8d3a9c9..72aa970 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -88,34 +88,41 @@ class TestQMP(TestImageInfoSpecific): class TestQCow2(TestQemuImgInfo): '''Testing a qcow2 version 2 image''' img_options = 'compat=0.10' - json_compare = { 'compat': '0.10' } - human_compare = [ 'compat: 0.10' ] + json_compare = { 'compat': '0.10', 'refcount-bits': 16 } + human_compare = [ 'compat: 0.10', 'refcount bits: 16' ] class TestQCow3NotLazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts disabled''' img_options = 'compat=1.1,lazy_refcounts=off' - json_compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False } - human_compare = [ 'compat: 1.1', 'lazy refcounts: false', 'corrupt: false' ] + json_compare = { 'compat': '1.1', 'lazy-refcounts': False, + 'refcount-bits': 16, 'corrupt': False } + human_compare = [ 'compat: 1.1', 'lazy refcounts: false', + 'refcount bits: 16', 'corrupt: false' ] class TestQCow3Lazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts enabled''' img_options = 'compat=1.1,lazy_refcounts=on' - json_compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False } - human_compare = [ 'compat: 1.1', 'lazy refcounts: true', 'corrupt: false' ] + json_compare = { 'compat': '1.1', 'lazy-refcounts': True, + 'refcount-bits': 16, 'corrupt': False } + human_compare = [ 'compat: 1.1', 'lazy refcounts: true', + 'refcount bits: 16', 'corrupt: false' ] class TestQCow3NotLazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts disabled, opening with lazy refcounts enabled''' img_options = 'compat=1.1,lazy_refcounts=off' qemu_options = 'lazy-refcounts=on' - compare = { 'compat': '1.1', 'lazy-refcounts': False, 'corrupt': False } + compare = { 'compat': '1.1', 'lazy-refcounts': False, + 'refcount-bits': 16, 'corrupt': False } + class TestQCow3LazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts enabled, opening with lazy refcounts disabled''' img_options = 'compat=1.1,lazy_refcounts=on' qemu_options = 'lazy-refcounts=off' - compare = { 'compat': '1.1', 'lazy-refcounts': True, 'corrupt': False } + compare = { 'compat': '1.1', 'lazy-refcounts': True, + 'refcount-bits': 16, 'corrupt': False } TestImageInfoSpecific = None TestQemuImgInfo = None diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 index 0508c69..83eefa3 100755 --- a/tests/qemu-iotests/067 +++ b/tests/qemu-iotests/067 @@ -35,6 +35,8 @@ status=1 # failure is the default! _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Because anything other than 16 would change the output of query-block +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' function do_run_qemu() { diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out index 00b3eae..6ff41bc 100644 --- a/tests/qemu-iotests/067.out +++ b/tests/qemu-iotests/067.out @@ -32,6 +32,7 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virti "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, @@ -208,6 +209,7 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, @@ -414,6 +416,7 @@ Testing: "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, @@ -599,6 +602,7 @@ Testing: "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, @@ -710,6 +714,7 @@ Testing: "data": { "compat": "1.1", "lazy-refcounts": false, + "refcount-bits": 16, "corrupt": false } }, diff --git a/tests/qemu-iotests/079 b/tests/qemu-iotests/079 index 6613cfb..ade6efa 100755 --- a/tests/qemu-iotests/079 +++ b/tests/qemu-iotests/079 @@ -42,19 +42,13 @@ _supported_fmt qcow2 _supported_proto file nfs _supported_os Linux -function test_qemu_img() -{ - echo qemu-img "$@" | _filter_testdir - $QEMU_IMG "$@" 2>&1 | _filter_testdir - echo -} - echo "=== Check option preallocation and cluster_size ===" echo cluster_sizes="16384 32768 65536 131072 262144 524288 1048576 2097152 4194304" for s in $cluster_sizes; do - test_qemu_img create -f $IMGFMT -o preallocation=metadata,cluster_size=$s "$TEST_IMG" 4G + IMGOPTS=$(_optstr_add "$IMGOPTS" "preallocation=metadata,cluster_size=$s") \ + _make_test_img 4G done # success, all done diff --git a/tests/qemu-iotests/079.out b/tests/qemu-iotests/079.out index ef4b8c9..6dc5d57 100644 --- a/tests/qemu-iotests/079.out +++ b/tests/qemu-iotests/079.out @@ -1,32 +1,14 @@ QA output created by 079 === Check option preallocation and cluster_size === -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=16384 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=16384 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=32768 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=32768 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=65536 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=131072 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=131072 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=262144 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=262144 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=524288 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=524288 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=1048576 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=1048576 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=2097152 TEST_DIR/t.qcow2 4G -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=2097152 preallocation='metadata' lazy_refcounts=off - -qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=4194304 TEST_DIR/t.qcow2 4G -qemu-img: TEST_DIR/t.qcow2: Cluster size must be a power of two between 512 and 2048k -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=4194304 preallocation='metadata' lazy_refcounts=off - +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' +qemu-img: TEST_DIR/t.IMGFMT: Cluster size must be a power of two between 512 and 2048k +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation='metadata' *** done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index 73795f1..a2c58ae 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -42,6 +42,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' header_size=104 diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index d0234e6..3a749b8 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -3,14 +3,14 @@ QA output created by 082 === create: Options specified more than once === Testing: create -f foo -f qcow2 TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) cluster_size: 65536 Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=4096 lazy_refcounts=on refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) @@ -18,10 +18,11 @@ cluster_size: 4096 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: create -f qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=on refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) @@ -29,10 +30,11 @@ cluster_size: 8192 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: create -f qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=8192 lazy_refcounts=off refcount_bits=16 image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128M (134217728 bytes) @@ -50,6 +52,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M @@ -62,6 +65,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M @@ -74,6 +78,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M @@ -86,6 +91,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M @@ -98,6 +104,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M @@ -110,6 +117,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M @@ -122,6 +130,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M @@ -134,13 +143,14 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,help' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2,?' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2, @@ -161,6 +171,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: create -o help Supported options: @@ -169,7 +180,7 @@ size Virtual disk size === convert: Options specified more than once === Testing: create -f qcow2 TEST_DIR/t.qcow2 128M -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 Testing: convert -f foo -f qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base image: TEST_DIR/t.IMGFMT.base @@ -190,6 +201,7 @@ cluster_size: 4096 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: convert -O qcow2 -o cluster_size=4k -o lazy_refcounts=on -o cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -200,6 +212,7 @@ cluster_size: 8192 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: convert -O qcow2 -o cluster_size=4k,cluster_size=8k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -220,6 +233,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -232,6 +246,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -244,6 +259,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -256,6 +272,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -268,6 +285,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -280,6 +298,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -292,6 +311,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -304,6 +324,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base @@ -331,6 +352,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: convert -o help Supported options: @@ -346,6 +368,7 @@ cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=130M -o lazy_refcounts=off TEST_DIR/t.qcow2 @@ -356,6 +379,7 @@ cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=8M -o lazy_refcounts=on -o size=132M TEST_DIR/t.qcow2 @@ -366,6 +390,7 @@ cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: true + refcount bits: 16 corrupt: false Testing: amend -f qcow2 -o size=4M,size=148M TEST_DIR/t.qcow2 @@ -386,6 +411,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 @@ -398,6 +424,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 @@ -410,6 +437,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 @@ -422,6 +450,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 @@ -434,6 +463,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 @@ -446,6 +476,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 @@ -458,6 +489,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 @@ -470,6 +502,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits nocow Turn off copy-on-write (valid only on btrfs) Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 @@ -499,6 +532,7 @@ encryption Encrypt the image cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates +refcount_bits Width of a reference count entry in bits Testing: convert -o help Supported options: diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out index 0f2b17f..5eb8b94 100644 --- a/tests/qemu-iotests/085.out +++ b/tests/qemu-iotests/085.out @@ -11,7 +11,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 === Create a single snapshot on virtio0 === -Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} === Invalid command - missing device and nodename === @@ -25,31 +25,31 @@ Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file === Create several transactional group snapshots === -Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} -Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off -Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"return": {}} *** done diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 index 6f74cec..3e0038d 100755 --- a/tests/qemu-iotests/089 +++ b/tests/qemu-iotests/089 @@ -41,6 +41,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Because anything other than 16 would change the output of qemu_io -c info +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' # Using an image filename containing quotation marks will render the JSON data # below invalid. In that case, we have little choice but simply not to run this diff --git a/tests/qemu-iotests/089.out b/tests/qemu-iotests/089.out index a6d3711..5b541a3 100644 --- a/tests/qemu-iotests/089.out +++ b/tests/qemu-iotests/089.out @@ -43,6 +43,7 @@ vm state offset: 512 MiB Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false format name: IMGFMT cluster size: 64 KiB @@ -50,5 +51,6 @@ vm state offset: 512 MiB Format specific information: compat: 1.1 lazy refcounts: false + refcount bits: 16 corrupt: false *** done diff --git a/tests/qemu-iotests/104 b/tests/qemu-iotests/104 index f32752b..2e35ea8 100755 --- a/tests/qemu-iotests/104 +++ b/tests/qemu-iotests/104 @@ -34,7 +34,7 @@ trap "exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw qcow qcow2 qed vdi vmdk vhdx _supported_proto generic _supported_os Linux diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 12fc92a..ce44749 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# This test directly modifies a refblock so it relies on refcount_bits being 16 +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' echo echo '=== Repairing an image without any refcount table ===' diff --git a/tests/qemu-iotests/112 b/tests/qemu-iotests/112 new file mode 100755 index 0000000..3f054a3 --- /dev/null +++ b/tests/qemu-iotests/112 @@ -0,0 +1,187 @@ +#!/bin/bash +# +# Test cases for different refcount_bits values +# +# Copyright (C) 2015 Red Hat, Inc. +# +# 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=mreitz@redhat.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 + +# This tests qcow2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux +# This test will set refcount_bits on its own which would conflict with the +# manual setting; compat will be overridden as well +_unsupported_imgopts refcount_bits 'compat=0.10' + +function print_refcount_bits() +{ + $QEMU_IMG info "$TEST_IMG" | sed -n '/refcount bits:/ s/^ *//p' +} + +echo +echo '=== refcount_bits limits ===' +echo + +# Must be positive (non-zero) +IMGOPTS="$IMGOPTS,refcount_bits=0" _make_test_img 64M +# Must be positive (non-negative) +IMGOPTS="$IMGOPTS,refcount_bits=-1" _make_test_img 64M +# May not exceed 64 +IMGOPTS="$IMGOPTS,refcount_bits=128" _make_test_img 64M +# Must be a power of two +IMGOPTS="$IMGOPTS,refcount_bits=42" _make_test_img 64M + +# 1 is the minimum +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +# 64 is the maximum +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +# 16 is the default +_make_test_img 64M +print_refcount_bits + +echo +echo '=== refcount_bits and compat=0.10 ===' +echo + +# Should work +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=16" _make_test_img 64M +print_refcount_bits + +# Should not work +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=1" _make_test_img 64M +IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=64" _make_test_img 64M + + +echo +echo '=== Snapshot limit on refcount_bits=1 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Should fail for now; in the future, this might be supported by automatically +# copying all clusters with overflowing refcount +$QEMU_IMG snapshot -c foo "$TEST_IMG" + +# The new L1 table could/should be leaked +_check_test_img + +echo +echo '=== Snapshot limit on refcount_bits=2 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=2" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Should succeed +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IMG snapshot -c bar "$TEST_IMG" +# Should fail (4th reference) +$QEMU_IMG snapshot -c baz "$TEST_IMG" + +# The new L1 table could/should be leaked +_check_test_img + +echo +echo '=== Compressed clusters with refcount_bits=1 ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +print_refcount_bits + +# Both should fit into a single host cluster; instead of failing to increase the +# refcount of that cluster, qemu should just allocate a new cluster and make +# this operation succeed +$QEMU_IO -c 'write -P 0 -c 0 64k' \ + -c 'write -P 1 -c 64k 64k' \ + "$TEST_IMG" | _filter_qemu_io + +_check_test_img + +echo +echo '=== MSb set in 64 bit refcount ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Set the MSb in the refblock entry of the data cluster +poke_file "$TEST_IMG" $((0x20028)) "\x80\x00\x00\x00\x00\x00\x00\x00" + +# Clear OFLAG_COPIED in the L2 entry of the data cluster +poke_file "$TEST_IMG" $((0x40000)) "\x00\x00\x00\x00\x00\x05\x00\x00" + +# Try to write to that cluster (should work, even though the MSb is set) +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +echo +echo '=== Snapshot on maximum 64 bit refcount value ===' +echo + +IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +print_refcount_bits + +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Set the refblock entry to the maximum value possible +poke_file "$TEST_IMG" $((0x20028)) "\xff\xff\xff\xff\xff\xff\xff\xff" + +# Clear OFLAG_COPIED in the L2 entry of the data cluster +poke_file "$TEST_IMG" $((0x40000)) "\x00\x00\x00\x00\x00\x05\x00\x00" + +# Try a snapshot (should correctly identify the overflow; may work in the future +# by falling back to COW) +$QEMU_IMG snapshot -c foo "$TEST_IMG" + +# The new L1 table could/should be leaked; and obviously the data cluster is +# leaked (refcount=UINT64_MAX reference=1) +_check_test_img + + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out new file mode 100644 index 0000000..9a98633 --- /dev/null +++ b/tests/qemu-iotests/112.out @@ -0,0 +1,84 @@ +QA output created by 112 + +=== refcount_bits limits === + +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount_bits=-1 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 16 + +=== refcount_bits and compat=0.10 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 16 +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + +=== Snapshot limit on refcount_bits=1 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'foo': -22 (Invalid argument) +Leaked cluster 6 refcount=1 reference=0 + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +=== Snapshot limit on refcount_bits=2 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 2 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'baz': -22 (Invalid argument) +Leaked cluster 7 refcount=1 reference=0 + +1 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +=== Compressed clusters with refcount_bits=1 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 1 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +=== MSb set in 64 bit refcount === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Snapshot on maximum 64 bit refcount value === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +refcount bits: 64 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'foo': -22 (Invalid argument) +Leaked cluster 5 refcount=18446744073709551615 reference=1 +Leaked cluster 6 refcount=1 reference=0 + +2 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +*** done diff --git a/tests/qemu-iotests/006 b/tests/qemu-iotests/128 index 0c0cf5d..249a865 100755 --- a/tests/qemu-iotests/006 +++ b/tests/qemu-iotests/128 @@ -1,9 +1,8 @@ #!/bin/bash # -# Make sure qemu-img rejects > 127GB images for the vpc format as the format -# doesn't support this. +# Test that opening O_DIRECT succeeds when image file I/O produces EIO # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright (C) 2015 Red Hat, Inc. # # 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 @@ -20,7 +19,7 @@ # # creator -owner=hch@lst.de +owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" @@ -29,9 +28,34 @@ here=`pwd` tmp=/tmp/$$ status=1 # failure is the default! +devname="eiodev$$" + +_setup_eiodev() +{ + # This test should either be run as root or with passwordless sudo + for cmd in "" "sudo -n"; do + echo "0 $((1024 * 1024 * 1024 / 512)) error" | \ + $cmd dmsetup create "$devname" 2>/dev/null + if [ "$?" -eq 0 ]; then + return + fi + done + _notrun "root privileges required to run dmsetup" +} + +_cleanup_eiodev() +{ + for cmd in "" "sudo -n"; do + $cmd dmsetup remove "$devname" 2>/dev/null + if [ "$?" -eq 0 ]; then + return + fi + done +} + _cleanup() { - _cleanup_test_img + _cleanup_eiodev } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -39,14 +63,18 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt vpc -_supported_proto generic +_supported_fmt raw +_supported_proto file _supported_os Linux +_setup_eiodev + +TEST_IMG="/dev/mapper/$devname" echo -echo "creating 128GB image" -_make_test_img 128G +echo "== reading from error device ==" +# Opening image should succeed but the read operation should fail +$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/128.out b/tests/qemu-iotests/128.out new file mode 100644 index 0000000..4e43f5f --- /dev/null +++ b/tests/qemu-iotests/128.out @@ -0,0 +1,5 @@ +QA output created by 128 + +== reading from error device == +read failed: Input/output error +*** done diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 06e1bb0..012a812 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -192,7 +192,8 @@ _filter_img_create() -e "s# block_size=[0-9]\\+##g" \ -e "s# block_state_zero=\\(on\\|off\\)##g" \ -e "s# log_size=[0-9]\\+##g" \ - -e "s/archipelago:a/TEST_DIR\//g" + -e "s/archipelago:a/TEST_DIR\//g" \ + -e "s# refcount_bits=[0-9]\\+##g" } _filter_img_info() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 0d3b95c..71f19d4 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -12,7 +12,7 @@ 003 rw auto 004 rw auto quick 005 img auto quick -006 img auto +# 006 was removed, do not reuse 007 snapshot auto 008 rw auto quick 009 rw auto quick @@ -116,7 +116,9 @@ 109 rw auto 110 rw auto backing quick 111 rw auto quick +112 rw auto 113 rw auto quick 114 rw auto quick 116 rw auto quick 123 rw auto quick +128 rw auto quick diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c index 27d1b6f..b552d9f 100644 --- a/tests/test-coroutine.c +++ b/tests/test-coroutine.c @@ -13,6 +13,7 @@ #include <glib.h> #include "block/coroutine.h" +#include "block/coroutine_int.h" /* * Check that qemu_in_coroutine() works @@ -122,6 +123,30 @@ static void test_yield(void) g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ } +static void coroutine_fn c2_fn(void *opaque) +{ + qemu_coroutine_yield(); +} + +static void coroutine_fn c1_fn(void *opaque) +{ + Coroutine *c2 = opaque; + qemu_coroutine_enter(c2, NULL); +} + +static void test_co_queue(void) +{ + Coroutine *c1; + Coroutine *c2; + + c1 = qemu_coroutine_create(c1_fn); + c2 = qemu_coroutine_create(c2_fn); + + qemu_coroutine_enter(c1, c2); + memset(c1, 0xff, sizeof(Coroutine)); + qemu_coroutine_enter(c2, NULL); +} + /* * Check that creation, enter, and return work */ @@ -343,6 +368,7 @@ static void perf_cost(void) int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + g_test_add_func("/basic/co_queue", test_co_queue); g_test_add_func("/basic/lifecycle", test_lifecycle); g_test_add_func("/basic/yield", test_yield); g_test_add_func("/basic/nesting", test_nesting); diff --git a/tests/test-x86-cpuid.c b/tests/test-x86-cpuid.c index 8d9f96a..6cd20d4 100644 --- a/tests/test-x86-cpuid.c +++ b/tests/test-x86-cpuid.c @@ -24,7 +24,7 @@ #include <glib.h> -#include "topology.h" +#include "hw/i386/topology.h" static void test_topo_bits(void) { diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 89d7cbf..4078321 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -16,9 +16,11 @@ #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" +#include "libqos/virtio-mmio.h" #include "libqos/pci-pc.h" #include "libqos/malloc.h" #include "libqos/malloc-pc.h" +#include "libqos/malloc-generic.h" #include "qemu/bswap.h" #define QVIRTIO_BLK_F_BARRIER 0x00000001 @@ -42,10 +44,14 @@ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) +#define PCI_SLOT_HP 0x06 #define PCI_SLOT 0x04 #define PCI_FN 0x00 -#define PCI_SLOT_HP 0x06 +#define MMIO_PAGE_SIZE 4096 +#define MMIO_DEV_BASE_ADDR 0x0A003E00 +#define MMIO_RAM_ADDR 0x40000000 +#define MMIO_RAM_SIZE 0x20000000 typedef struct QVirtioBlkReq { uint32_t type; @@ -55,11 +61,10 @@ typedef struct QVirtioBlkReq { uint8_t status; } QVirtioBlkReq; -static QPCIBus *test_start(void) +static char *drive_create(void) { - char *cmdline; - char tmp_path[] = "/tmp/qtest.XXXXXX"; int fd, ret; + char *tmp_path = g_strdup("/tmp/qtest.XXXXXX"); /* Create a temporary raw image */ fd = mkstemp(tmp_path); @@ -68,24 +73,52 @@ static QPCIBus *test_start(void) g_assert_cmpint(ret, ==, 0); close(fd); + return tmp_path; +} + +static QPCIBus *pci_test_start(void) +{ + char *cmdline; + char *tmp_path; + + tmp_path = drive_create(); + cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s,format=raw " - "-drive if=none,id=drive1,file=/dev/null,format=raw " - "-device virtio-blk-pci,id=drv0,drive=drive0," - "addr=%x.%x", - tmp_path, PCI_SLOT, PCI_FN); + "-drive if=none,id=drive1,file=/dev/null,format=raw " + "-device virtio-blk-pci,id=drv0,drive=drive0," + "addr=%x.%x", + tmp_path, PCI_SLOT, PCI_FN); qtest_start(cmdline); unlink(tmp_path); + g_free(tmp_path); g_free(cmdline); return qpci_init_pc(); } +static void arm_test_start(void) +{ + char *cmdline; + char *tmp_path; + + tmp_path = drive_create(); + + cmdline = g_strdup_printf("-machine virt " + "-drive if=none,id=drive0,file=%s,format=raw " + "-device virtio-blk-device,drive=drive0", + tmp_path); + qtest_start(cmdline); + unlink(tmp_path); + g_free(tmp_path); + g_free(cmdline); +} + static void test_end(void) { qtest_end(); } -static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus, int slot) +static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) { QVirtioPCIDevice *dev; @@ -135,14 +168,10 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, return addr; } -static void pci_basic(void) +static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, + QGuestAllocator *alloc, QVirtQueue *vq, uint64_t device_specific) { - QVirtioPCIDevice *dev; - QPCIBus *bus; - QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; QVirtioBlkReq req; - void *addr; uint64_t req_addr; uint64_t capacity; uint32_t features; @@ -150,29 +179,19 @@ static void pci_basic(void) uint8_t status; char *data; - bus = test_start(); + capacity = qvirtio_config_readq(bus, dev, device_specific); - dev = virtio_blk_init(bus, PCI_SLOT); - - /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(bus, dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX | QVIRTIO_BLK_F_SCSI); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); - - alloc = pc_alloc_init(); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); + qvirtio_set_features(bus, dev, features); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(bus, dev); - /* Write and read with 2 descriptor layout */ + /* Write and read with 3 descriptor layout */ /* Write request */ req.type = QVIRTIO_BLK_T_OUT; req.ioprio = 1; @@ -184,12 +203,13 @@ static void pci_basic(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtqueue_kick(bus, dev, vq, free_head); + + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -205,13 +225,13 @@ static void pci_basic(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, true, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -222,61 +242,84 @@ static void pci_basic(void) guest_free(alloc, req_addr); - /* Write and read with 3 descriptor layout */ - /* Write request */ - req.type = QVIRTIO_BLK_T_OUT; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); - strcpy(req.data, "TEST"); + if (features & QVIRTIO_F_ANY_LAYOUT) { + /* Write and read with 2 descriptor layout */ + /* Write request */ + req.type = QVIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, &req, 512); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + g_free(req.data); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 528, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); - guest_free(alloc, req_addr); + guest_free(alloc, req_addr); - /* Read request */ - req.type = QVIRTIO_BLK_T_IN; - req.ioprio = 1; - req.sector = 1; - req.data = g_malloc0(512); + /* Read request */ + req.type = QVIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, &req, 512); - g_free(req.data); + g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 513, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(bus, dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); - status = readb(req_addr + 528); - g_assert_cmpint(status, ==, 0); + qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); - data = g_malloc0(512); - memread(req_addr + 16, data, 512); - g_assert_cmpstr(data, ==, "TEST"); - g_free(data); + data = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); - guest_free(alloc, req_addr); + guest_free(alloc, req_addr); + } +} + +static void pci_basic(void) +{ + QVirtioPCIDevice *dev; + QPCIBus *bus; + QVirtQueuePCI *vqpci; + QGuestAllocator *alloc; + void *addr; + + bus = pci_test_start(); + dev = virtio_blk_pci_init(bus, PCI_SLOT); + + alloc = pc_alloc_init(); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, + alloc, 0); + + /* MSI-X is not enabled */ + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; + + test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq, + (uint64_t)(uintptr_t)addr); /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -296,14 +339,15 @@ static void pci_indirect(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -374,8 +418,10 @@ static void pci_indirect(void) /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -387,14 +433,15 @@ static void pci_config(void) void *addr; uint64_t capacity; - bus = test_start(); + bus = pci_test_start(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); /* MSI-X is not enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); @@ -403,11 +450,13 @@ static void pci_config(void) " 'size': %d } }", n_size); qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, n_size / 512); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -427,18 +476,19 @@ static void pci_msix(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); alloc = pc_alloc_init(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); qpci_msix_enable(dev->pdev); qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); /* MSI-X is enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -458,7 +508,8 @@ static void pci_msix(void) qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, n_size / 512); /* Write request */ @@ -472,7 +523,8 @@ static void pci_msix(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -495,7 +547,8 @@ static void pci_msix(void) g_free(req.data); free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -514,10 +567,12 @@ static void pci_msix(void) guest_free(alloc, req_addr); /* End test */ - guest_free(alloc, (uint64_t)vqpci->vq.desc); + guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } @@ -536,18 +591,19 @@ static void pci_idx(void) uint8_t status; char *data; - bus = test_start(); + bus = pci_test_start(); alloc = pc_alloc_init(); - dev = virtio_blk_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(bus, PCI_SLOT); qpci_msix_enable(dev->pdev); qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); /* MSI-X is enabled */ - addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX; + addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_MSIX; - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr); + capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, + (uint64_t)(uintptr_t)addr); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); @@ -573,7 +629,8 @@ static void pci_idx(void) g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -593,7 +650,8 @@ static void pci_idx(void) /* Notify after processing the third request */ qvirtqueue_set_used_event(&vqpci->vq, 2); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true); + free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); @@ -616,11 +674,11 @@ static void pci_idx(void) g_free(req.data); free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false); + qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); + qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); @@ -636,45 +694,94 @@ static void pci_idx(void) /* End test */ guest_free(alloc, vqpci->vq.desc); + pc_alloc_uninit(alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); + qpci_free_pc(bus); test_end(); } -static void hotplug(void) +static void pci_hotplug(void) { QPCIBus *bus; QVirtioPCIDevice *dev; - bus = test_start(); + bus = pci_test_start(); /* plug secondary disk */ qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, "'drive': 'drive1'"); - dev = virtio_blk_init(bus, PCI_SLOT_HP); + dev = virtio_blk_pci_init(bus, PCI_SLOT_HP); g_assert(dev); qvirtio_pci_device_disable(dev); g_free(dev); /* unplug secondary disk */ qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); + qpci_free_pc(bus); + test_end(); +} + +static void mmio_basic(void) +{ + QVirtioMMIODevice *dev; + QVirtQueue *vq; + QGuestAllocator *alloc; + int n_size = TEST_IMAGE_SIZE / 2; + uint64_t capacity; + + arm_test_start(); + + dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); + g_assert(dev != NULL); + g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID); + + qvirtio_reset(&qvirtio_mmio, &dev->vdev); + qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev); + qvirtio_set_driver(&qvirtio_mmio, &dev->vdev); + + alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); + vq = qvirtqueue_setup(&qvirtio_mmio, &dev->vdev, alloc, 0); + + test_basic(&qvirtio_mmio, &dev->vdev, alloc, vq, + QVIRTIO_MMIO_DEVICE_SPECIFIC); + + qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); + + qvirtio_wait_queue_isr(&qvirtio_mmio, &dev->vdev, vq, + QVIRTIO_BLK_TIMEOUT_US); + + capacity = qvirtio_config_readq(&qvirtio_mmio, &dev->vdev, + QVIRTIO_MMIO_DEVICE_SPECIFIC); + g_assert_cmpint(capacity, ==, n_size / 512); + + /* End test */ + guest_free(alloc, vq->desc); + generic_alloc_uninit(alloc); + g_free(dev); test_end(); } int main(int argc, char **argv) { int ret; + const char *arch = qtest_get_arch(); g_test_init(&argc, &argv, NULL); - g_test_add_func("/virtio/blk/pci/basic", pci_basic); - g_test_add_func("/virtio/blk/pci/indirect", pci_indirect); - g_test_add_func("/virtio/blk/pci/config", pci_config); - g_test_add_func("/virtio/blk/pci/msix", pci_msix); - g_test_add_func("/virtio/blk/pci/idx", pci_idx); - g_test_add_func("/virtio/blk/pci/hotplug", hotplug); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("/virtio/blk/pci/basic", pci_basic); + qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); + qtest_add_func("/virtio/blk/pci/config", pci_config); + qtest_add_func("/virtio/blk/pci/msix", pci_msix); + qtest_add_func("/virtio/blk/pci/idx", pci_idx); + qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); + } else if (strcmp(arch, "arm") == 0) { + qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); + } ret = g_test_run(); diff --git a/trace-events b/trace-events index 4ac588c..30eba92 100644 --- a/trace-events +++ b/trace-events @@ -1581,6 +1581,7 @@ mhp_pc_dimm_assigned_address(uint64_t addr) "0x%"PRIx64 kvm_enable_cmma(int rc) "CMMA: enabling with result code %d" kvm_clear_cmma(int rc) "CMMA: clearing with result code %d" kvm_failed_cpu_state_set(int cpu_index, uint8_t state, const char *msg) "Warning: Unable to set cpu %d state %" PRIu8 " to KVM: %s" +kvm_sigp_finished(uint8_t order, int cpu_index, int dst_index, int cc) "SIGP: Finished order %u on cpu %d -> cpu %d with cc=%d" # hw/dma/i8257.c i8257_unregistered_dma(int nchan, int dma_pos, int dma_len) "unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d" diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index e304baf..d75950d 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -207,8 +207,7 @@ static void vncws_send_handshake_response(VncState *vs, const char* key) } response = g_strdup_printf(WS_HANDSHAKE, accept); - vnc_write(vs, response, strlen(response)); - vnc_flush(vs); + vnc_client_write_buf(vs, (const uint8_t *)response, strlen(response)); g_free(accept); g_free(response); @@ -3687,12 +3687,7 @@ void vnc_display_add_client(const char *id, int csock, bool skipauth) vnc_connect(vs, csock, skipauth, false); } -QemuOpts *vnc_parse_func(const char *str) -{ - return qemu_opts_parse(qemu_find_opts("vnc"), str, 1); -} - -void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) +static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) { int i = 2; char *id; @@ -3705,18 +3700,25 @@ void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) qemu_opts_set_id(opts, id); } -int vnc_init_func(QemuOpts *opts, void *opaque) +QemuOpts *vnc_parse_func(const char *str) { - Error *local_err = NULL; QemuOptsList *olist = qemu_find_opts("vnc"); - char *id = (char *)qemu_opts_id(opts); + QemuOpts *opts = qemu_opts_parse(olist, str, 1); + const char *id = qemu_opts_id(opts); if (!id) { /* auto-assign id if not present */ vnc_auto_assign_id(olist, opts); - id = (char *)qemu_opts_id(opts); } + return opts; +} + +int vnc_init_func(QemuOpts *opts, void *opaque) +{ + Error *local_err = NULL; + char *id = (char *)qemu_opts_id(opts); + assert(id); vnc_display_init(id); vnc_display_open(id, &local_err); if (local_err != NULL) { diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 50a29d8..ba67cec 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -51,12 +51,8 @@ static void error_exit(int err, const char *msg) void qemu_mutex_init(QemuMutex *mutex) { int err; - pthread_mutexattr_t mutexattr; - pthread_mutexattr_init(&mutexattr); - pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK); - err = pthread_mutex_init(&mutex->lock, &mutexattr); - pthread_mutexattr_destroy(&mutexattr); + err = pthread_mutex_init(&mutex->lock, NULL); if (err) error_exit(err, __func__); } @@ -283,7 +283,7 @@ void rcu_unregister_thread(void) qemu_mutex_unlock(&rcu_gp_lock); } -static void __attribute__((__constructor__)) rcu_init(void) +static void rcu_init_complete(void) { QemuThread thread; @@ -291,8 +291,39 @@ static void __attribute__((__constructor__)) rcu_init(void) qemu_event_init(&rcu_gp_event, true); qemu_event_init(&rcu_call_ready_event, false); + + /* The caller is assumed to have iothread lock, so the call_rcu thread + * must have been quiescent even after forking, just recreate it. + */ qemu_thread_create(&thread, "call_rcu", call_rcu_thread, NULL, QEMU_THREAD_DETACHED); rcu_register_thread(); } + +#ifdef CONFIG_POSIX +static void rcu_init_lock(void) +{ + qemu_mutex_lock(&rcu_gp_lock); +} + +static void rcu_init_unlock(void) +{ + qemu_mutex_unlock(&rcu_gp_lock); +} + +static void rcu_init_child(void) +{ + qemu_mutex_unlock(&rcu_gp_lock); + memset(®istry, 0, sizeof(registry)); + rcu_init_complete(); +} +#endif + +static void __attribute__((__constructor__)) rcu_init(void) +{ +#ifdef CONFIG_POSIX + pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child); +#endif + rcu_init_complete(); +} @@ -2001,7 +2001,6 @@ static DisplayType select_display(const char *p) } else if (strstart(p, "vnc", &opts)) { #ifdef CONFIG_VNC if (*opts == '=') { - display_remote++; if (vnc_parse_func(opts+1) == NULL) { exit(1); } @@ -3477,7 +3476,6 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_vnc: #ifdef CONFIG_VNC - display_remote++; if (vnc_parse_func(optarg) == NULL) { exit(1); } @@ -3970,6 +3968,11 @@ int main(int argc, char **argv, char **envp) } } +#if defined(CONFIG_VNC) + if (!QTAILQ_EMPTY(&(qemu_find_opts("vnc")->head))) { + display_remote++; + } +#endif if (display_type == DT_DEFAULT && !display_remote) { #if defined(CONFIG_GTK) display_type = DT_GTK; |