aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/backup.c14
-rw-r--r--block/dirty-bitmap.c160
-rw-r--r--block/mirror.c24
-rw-r--r--block/qapi.c1
-rw-r--r--block/qcow2-cluster.c9
-rw-r--r--block/qcow2.c3
-rw-r--r--block/qcow2.h3
-rw-r--r--block/quorum.c93
-rw-r--r--block/raw-posix.c1
-rw-r--r--block/raw-win32.c1
-rw-r--r--block/replication.c5
-rw-r--r--block/throttle-groups.c27
-rw-r--r--docs/qmp-commands.txt84
-rw-r--r--exec.c47
-rw-r--r--hw/arm/musicpal.c88
-rw-r--r--hw/arm/pxa2xx_gpio.c25
-rw-r--r--hw/arm/strongarm.c15
-rw-r--r--hw/arm/virt-acpi-build.c71
-rw-r--r--hw/arm/virt.c4
-rw-r--r--hw/core/ptimer.c130
-rw-r--r--hw/display/pl110.c8
-rw-r--r--hw/i2c/core.c39
-rw-r--r--hw/i2c/smbus.c12
-rw-r--r--hw/timer/a9gtimer.c14
-rw-r--r--hw/timer/arm_mptimer.c149
-rw-r--r--hw/timer/stm32f2xx_timer.c2
-rw-r--r--include/block/dirty-bitmap.h35
-rw-r--r--include/exec/cpu-all.h9
-rw-r--r--include/hw/acpi/acpi-defs.h68
-rw-r--r--include/hw/boards.h7
-rw-r--r--include/hw/ptimer.h20
-rw-r--r--include/hw/timer/arm_mptimer.h5
-rw-r--r--include/qemu-common.h12
-rw-r--r--include/qemu/hbitmap.h100
-rw-r--r--include/qemu/typedefs.h1
-rw-r--r--linux-user/main.c3
-rw-r--r--migration/ram.c4
-rw-r--r--migration/savevm.c49
-rw-r--r--qapi/block-core.json7
-rw-r--r--qemu-img.c2
-rw-r--r--qemu-nbd.c17
-rw-r--r--qemu-nbd.texi2
-rw-r--r--target-arm/cpu.c24
-rw-r--r--target-arm/cpu.h11
-rw-r--r--target-arm/helper.c11
-rw-r--r--target-arm/translate.c54
-rw-r--r--tests/Makefile.include3
-rw-r--r--tests/ptimer-test-stubs.c2
-rw-r--r--tests/ptimer-test.c362
-rw-r--r--tests/ptimer-test.h2
-rwxr-xr-xtests/qemu-iotests/04111
-rwxr-xr-xtests/qemu-iotests/06712
-rwxr-xr-xtests/qemu-iotests/071118
-rwxr-xr-xtests/qemu-iotests/08152
-rwxr-xr-xtests/qemu-iotests/0859
-rwxr-xr-xtests/qemu-iotests/08776
-rwxr-xr-xtests/qemu-iotests/09333
-rw-r--r--tests/qemu-iotests/093.out4
-rwxr-xr-xtests/qemu-iotests/11712
-rwxr-xr-xtests/qemu-iotests/11842
-rw-r--r--tests/qemu-iotests/12420
-rw-r--r--tests/qemu-iotests/13910
-rwxr-xr-xtests/qemu-iotests/14113
-rwxr-xr-xtests/qemu-iotests/15510
-rwxr-xr-xtests/qemu-iotests/16222
-rw-r--r--tests/qemu-iotests/162.out2
-rw-r--r--tests/test-arm-mptimer.c1105
-rw-r--r--tests/test-hbitmap.c272
-rw-r--r--translate-all.c71
-rw-r--r--util/hbitmap.c206
-rw-r--r--vl.c10
71 files changed, 3275 insertions, 674 deletions
diff --git a/block/backup.c b/block/backup.c
index 582bd0f7ee..02dbe48035 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -372,14 +372,14 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
int64_t end;
int64_t last_cluster = -1;
int64_t sectors_per_cluster = cluster_size_sectors(job);
- HBitmapIter hbi;
+ BdrvDirtyBitmapIter *dbi;
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
clusters_per_iter = MAX((granularity / job->cluster_size), 1);
- bdrv_dirty_iter_init(job->sync_bitmap, &hbi);
+ dbi = bdrv_dirty_iter_new(job->sync_bitmap, 0);
/* Find the next dirty sector(s) */
- while ((sector = hbitmap_iter_next(&hbi)) != -1) {
+ while ((sector = bdrv_dirty_iter_next(dbi)) != -1) {
cluster = sector / sectors_per_cluster;
/* Fake progress updates for any clusters we skipped */
@@ -391,7 +391,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
do {
if (yield_and_check(job)) {
- return ret;
+ goto out;
}
ret = backup_do_cow(job, cluster * sectors_per_cluster,
sectors_per_cluster, &error_is_read,
@@ -399,7 +399,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
if ((ret < 0) &&
backup_error_action(job, error_is_read, -ret) ==
BLOCK_ERROR_ACTION_REPORT) {
- return ret;
+ goto out;
}
} while (ret < 0);
}
@@ -407,7 +407,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
/* If the bitmap granularity is smaller than the backup granularity,
* we need to advance the iterator pointer to the next cluster. */
if (granularity < job->cluster_size) {
- bdrv_set_dirty_iter(&hbi, cluster * sectors_per_cluster);
+ bdrv_set_dirty_iter(dbi, cluster * sectors_per_cluster);
}
last_cluster = cluster - 1;
@@ -419,6 +419,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
}
+out:
+ bdrv_dirty_iter_free(dbi);
return ret;
}
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
index f2bfdcfdea..519737c8d3 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -38,13 +38,20 @@
*/
struct BdrvDirtyBitmap {
HBitmap *bitmap; /* Dirty sector bitmap implementation */
+ HBitmap *meta; /* Meta dirty bitmap */
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
char *name; /* Optional non-empty unique ID */
int64_t size; /* Size of the bitmap (Number of sectors) */
bool disabled; /* Bitmap is read-only */
+ int active_iterators; /* How many iterators are active */
QLIST_ENTRY(BdrvDirtyBitmap) list;
};
+struct BdrvDirtyBitmapIter {
+ HBitmapIter hbi;
+ BdrvDirtyBitmap *bitmap;
+};
+
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
{
BdrvDirtyBitmap *bm;
@@ -97,6 +104,66 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
return bitmap;
}
+/* bdrv_create_meta_dirty_bitmap
+ *
+ * Create a meta dirty bitmap that tracks the changes of bits in @bitmap. I.e.
+ * when a dirty status bit in @bitmap is changed (either from reset to set or
+ * the other way around), its respective meta dirty bitmap bit will be marked
+ * dirty as well.
+ *
+ * @bitmap: the block dirty bitmap for which to create a meta dirty bitmap.
+ * @chunk_size: how many bytes of bitmap data does each bit in the meta bitmap
+ * track.
+ */
+void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap,
+ int chunk_size)
+{
+ assert(!bitmap->meta);
+ bitmap->meta = hbitmap_create_meta(bitmap->bitmap,
+ chunk_size * BITS_PER_BYTE);
+}
+
+void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap)
+{
+ assert(bitmap->meta);
+ hbitmap_free_meta(bitmap->bitmap);
+ bitmap->meta = NULL;
+}
+
+int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs,
+ BdrvDirtyBitmap *bitmap, int64_t sector,
+ int nb_sectors)
+{
+ uint64_t i;
+ int sectors_per_bit = 1 << hbitmap_granularity(bitmap->meta);
+
+ /* To optimize: we can make hbitmap to internally check the range in a
+ * coarse level, or at least do it word by word. */
+ for (i = sector; i < sector + nb_sectors; i += sectors_per_bit) {
+ if (hbitmap_get(bitmap->meta, i)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs,
+ BdrvDirtyBitmap *bitmap, int64_t sector,
+ int nb_sectors)
+{
+ hbitmap_reset(bitmap->meta, sector, nb_sectors);
+}
+
+int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap)
+{
+ return bitmap->size;
+}
+
+const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap)
+{
+ return bitmap->name;
+}
+
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
{
return bitmap->successor;
@@ -212,6 +279,7 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
assert(!bdrv_dirty_bitmap_frozen(bitmap));
+ assert(!bitmap->active_iterators);
hbitmap_truncate(bitmap->bitmap, size);
bitmap->size = size;
}
@@ -224,7 +292,9 @@ static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
BdrvDirtyBitmap *bm, *next;
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
+ assert(!bm->active_iterators);
assert(!bdrv_dirty_bitmap_frozen(bm));
+ assert(!bm->meta);
QLIST_REMOVE(bm, list);
hbitmap_free(bm->bitmap);
g_free(bm->name);
@@ -235,6 +305,9 @@ static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
}
}
}
+ if (bitmap) {
+ abort();
+ }
}
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
@@ -320,9 +393,43 @@ uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap)
return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap);
}
-void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi)
+uint32_t bdrv_dirty_bitmap_meta_granularity(BdrvDirtyBitmap *bitmap)
+{
+ return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->meta);
+}
+
+BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap,
+ uint64_t first_sector)
{
- hbitmap_iter_init(hbi, bitmap->bitmap, 0);
+ BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1);
+ hbitmap_iter_init(&iter->hbi, bitmap->bitmap, first_sector);
+ iter->bitmap = bitmap;
+ bitmap->active_iterators++;
+ return iter;
+}
+
+BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap)
+{
+ BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1);
+ hbitmap_iter_init(&iter->hbi, bitmap->meta, 0);
+ iter->bitmap = bitmap;
+ bitmap->active_iterators++;
+ return iter;
+}
+
+void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)
+{
+ if (!iter) {
+ return;
+ }
+ assert(iter->bitmap->active_iterators > 0);
+ iter->bitmap->active_iterators--;
+ g_free(iter);
+}
+
+int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
+{
+ return hbitmap_iter_next(&iter->hbi);
}
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
@@ -360,6 +467,43 @@ void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
hbitmap_free(tmp);
}
+uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap,
+ uint64_t start, uint64_t count)
+{
+ return hbitmap_serialization_size(bitmap->bitmap, start, count);
+}
+
+uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap)
+{
+ return hbitmap_serialization_granularity(bitmap->bitmap);
+}
+
+void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
+ uint8_t *buf, uint64_t start,
+ uint64_t count)
+{
+ hbitmap_serialize_part(bitmap->bitmap, buf, start, count);
+}
+
+void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap,
+ uint8_t *buf, uint64_t start,
+ uint64_t count, bool finish)
+{
+ hbitmap_deserialize_part(bitmap->bitmap, buf, start, count, finish);
+}
+
+void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap,
+ uint64_t start, uint64_t count,
+ bool finish)
+{
+ hbitmap_deserialize_zeroes(bitmap->bitmap, start, count, finish);
+}
+
+void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap)
+{
+ hbitmap_deserialize_finish(bitmap->bitmap);
+}
+
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
int64_t nr_sectors)
{
@@ -373,15 +517,19 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
}
/**
- * Advance an HBitmapIter to an arbitrary offset.
+ * Advance a BdrvDirtyBitmapIter to an arbitrary offset.
*/
-void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset)
+void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *iter, int64_t sector_num)
{
- assert(hbi->hb);
- hbitmap_iter_init(hbi, hbi->hb, offset);
+ hbitmap_iter_init(&iter->hbi, iter->hbi.hb, sector_num);
}
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap)
{
return hbitmap_count(bitmap->bitmap);
}
+
+int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap)
+{
+ return hbitmap_count(bitmap->meta);
+}
diff --git a/block/mirror.c b/block/mirror.c
index f9d1fecaa0..a433e6848c 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -55,7 +55,7 @@ typedef struct MirrorBlockJob {
int64_t bdev_length;
unsigned long *cow_bitmap;
BdrvDirtyBitmap *dirty_bitmap;
- HBitmapIter hbi;
+ BdrvDirtyBitmapIter *dbi;
uint8_t *buf;
QSIMPLEQ_HEAD(, MirrorBuffer) buf_free;
int buf_free_count;
@@ -330,10 +330,10 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
int max_io_sectors = MAX((s->buf_size >> BDRV_SECTOR_BITS) / MAX_IN_FLIGHT,
MAX_IO_SECTORS);
- sector_num = hbitmap_iter_next(&s->hbi);
+ sector_num = bdrv_dirty_iter_next(s->dbi);
if (sector_num < 0) {
- bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
- sector_num = hbitmap_iter_next(&s->hbi);
+ bdrv_set_dirty_iter(s->dbi, 0);
+ sector_num = bdrv_dirty_iter_next(s->dbi);
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
assert(sector_num >= 0);
}
@@ -349,7 +349,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
/* Find the number of consective dirty chunks following the first dirty
* one, and wait for in flight requests in them. */
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
- int64_t hbitmap_next;
+ int64_t next_dirty;
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
int64_t next_chunk = next_sector / sectors_per_chunk;
if (next_sector >= end ||
@@ -360,13 +360,13 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
break;
}
- hbitmap_next = hbitmap_iter_next(&s->hbi);
- if (hbitmap_next > next_sector || hbitmap_next < 0) {
+ next_dirty = bdrv_dirty_iter_next(s->dbi);
+ if (next_dirty > next_sector || next_dirty < 0) {
/* The bitmap iterator's cache is stale, refresh it */
- bdrv_set_dirty_iter(&s->hbi, next_sector);
- hbitmap_next = hbitmap_iter_next(&s->hbi);
+ bdrv_set_dirty_iter(s->dbi, next_sector);
+ next_dirty = bdrv_dirty_iter_next(s->dbi);
}
- assert(hbitmap_next == next_sector);
+ assert(next_dirty == next_sector);
nb_chunks++;
}
@@ -679,7 +679,8 @@ static void coroutine_fn mirror_run(void *opaque)
}
}
- bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
+ assert(!s->dbi);
+ s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap, 0);
for (;;) {
uint64_t delay_ns = 0;
int64_t cnt, delta;
@@ -793,6 +794,7 @@ immediate_exit:
qemu_vfree(s->buf);
g_free(s->cow_bitmap);
g_free(s->in_flight_bitmap);
+ bdrv_dirty_iter_free(s->dbi);
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
data = g_malloc(sizeof(*data));
diff --git a/block/qapi.c b/block/qapi.c
index 6f947e3e66..50d30907a2 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -698,6 +698,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
assert(qobject_type(obj) == QTYPE_QDICT);
data = qdict_get(qobject_to_qdict(obj), "data");
dump_qobject(func_fprintf, f, 1, data);
+ qobject_decref(obj);
visit_free(v);
}
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 61d1ffd223..928c1e298d 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -1558,7 +1558,7 @@ fail:
* clusters.
*/
static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
- uint64_t nb_clusters)
+ uint64_t nb_clusters, int flags)
{
BDRVQcow2State *s = bs->opaque;
uint64_t *l2_table;
@@ -1582,7 +1582,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
/* Update L2 entries */
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
- if (old_offset & QCOW_OFLAG_COMPRESSED) {
+ if (old_offset & QCOW_OFLAG_COMPRESSED || flags & BDRV_REQ_MAY_UNMAP) {
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
} else {
@@ -1595,7 +1595,8 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
return nb_clusters;
}
-int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
+int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors,
+ int flags)
{
BDRVQcow2State *s = bs->opaque;
uint64_t nb_clusters;
@@ -1612,7 +1613,7 @@ int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
s->cache_discards = true;
while (nb_clusters > 0) {
- ret = zero_single_l2(bs, offset, nb_clusters);
+ ret = zero_single_l2(bs, offset, nb_clusters, flags);
if (ret < 0) {
goto fail;
}
diff --git a/block/qcow2.c b/block/qcow2.c
index e11c7c9d16..6d5689a23c 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1155,6 +1155,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
/* Initialise locks */
qemu_co_mutex_init(&s->lock);
+ bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP;
/* Repair image if dirty */
if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
@@ -2477,7 +2478,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, count);
/* Whatever is left can use real zero clusters */
- ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS);
+ ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS, flags);
qemu_co_mutex_unlock(&s->lock);
return ret;
diff --git a/block/qcow2.h b/block/qcow2.h
index 9ce5a37d3a..92203a8b8c 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -547,7 +547,8 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
int nb_sectors, enum qcow2_discard_type type, bool full_discard);
-int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
+int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors,
+ int flags);
int qcow2_expand_zero_clusters(BlockDriverState *bs,
BlockDriverAmendStatusCB *status_cb,
diff --git a/block/quorum.c b/block/quorum.c
index 9cf876fb34..d122299352 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -130,7 +130,7 @@ struct QuorumAIOCB {
bool is_read;
int vote_ret;
- int child_iter; /* which child to read in fifo pattern */
+ int children_read; /* how many children have been read from */
};
static bool quorum_vote(QuorumAIOCB *acb);
@@ -156,22 +156,7 @@ static AIOCBInfo quorum_aiocb_info = {
static void quorum_aio_finalize(QuorumAIOCB *acb)
{
- int i, ret = 0;
-
- if (acb->vote_ret) {
- ret = acb->vote_ret;
- }
-
- acb->common.cb(acb->common.opaque, ret);
-
- if (acb->is_read) {
- /* on the quorum case acb->child_iter == s->num_children - 1 */
- for (i = 0; i <= acb->child_iter; i++) {
- qemu_vfree(acb->qcrs[i].buf);
- qemu_iovec_destroy(&acb->qcrs[i].qiov);
- }
- }
-
+ acb->common.cb(acb->common.opaque, acb->vote_ret);
g_free(acb->qcrs);
qemu_aio_unref(acb);
}
@@ -283,39 +268,52 @@ static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
}
}
-static void quorum_aio_cb(void *opaque, int ret)
+static void quorum_report_bad_acb(QuorumChildRequest *sacb, int ret)
+{
+ QuorumAIOCB *acb = sacb->parent;
+ QuorumOpType type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE;
+ quorum_report_bad(type, acb->sector_num, acb->nb_sectors,
+ sacb->aiocb->bs->node_name, ret);
+}
+
+static void quorum_fifo_aio_cb(void *opaque, int ret)
{
QuorumChildRequest *sacb = opaque;
QuorumAIOCB *acb = sacb->parent;
BDRVQuorumState *s = acb->common.bs->opaque;
- bool rewrite = false;
- if (ret == 0) {
- acb->success_count++;
- } else {
- QuorumOpType type;
- type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE;
- quorum_report_bad(type, acb->sector_num, acb->nb_sectors,
- sacb->aiocb->bs->node_name, ret);
- }
+ assert(acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO);
+
+ if (ret < 0) {
+ quorum_report_bad_acb(sacb, ret);
- if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) {
/* We try to read next child in FIFO order if we fail to read */
- if (ret < 0 && (acb->child_iter + 1) < s->num_children) {
- acb->child_iter++;
+ if (acb->children_read < s->num_children) {
read_fifo_child(acb);
return;
}
-
- if (ret == 0) {
- quorum_copy_qiov(acb->qiov, &acb->qcrs[acb->child_iter].qiov);
- }
- acb->vote_ret = ret;
- quorum_aio_finalize(acb);
- return;
}
+ acb->vote_ret = ret;
+
+ /* FIXME: rewrite failed children if acb->children_read > 1? */
+ quorum_aio_finalize(acb);
+}
+
+static void quorum_aio_cb(void *opaque, int ret)
+{
+ QuorumChildRequest *sacb = opaque;
+ QuorumAIOCB *acb = sacb->parent;
+ BDRVQuorumState *s = acb->common.bs->opaque;
+ bool rewrite = false;
+ int i;
+
sacb->ret = ret;
+ if (ret == 0) {
+ acb->success_count++;
+ } else {
+ quorum_report_bad_acb(sacb, ret);
+ }
acb->count++;
assert(acb->count <= s->num_children);
assert(acb->success_count <= s->num_children);
@@ -326,6 +324,10 @@ static void quorum_aio_cb(void *opaque, int ret)
/* Do the vote on read */
if (acb->is_read) {
rewrite = quorum_vote(acb);
+ for (i = 0; i < s->num_children; i++) {
+ qemu_vfree(acb->qcrs[i].buf);
+ qemu_iovec_destroy(&acb->qcrs[i].qiov);
+ }
} else {
quorum_has_too_much_io_failed(acb);
}
@@ -653,6 +655,7 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb)
BDRVQuorumState *s = acb->common.bs->opaque;
int i;
+ acb->children_read = s->num_children;
for (i = 0; i < s->num_children; i++) {
acb->qcrs[i].buf = qemu_blockalign(s->children[i]->bs, acb->qiov->size);
qemu_iovec_init(&acb->qcrs[i].qiov, acb->qiov->niov);
@@ -671,16 +674,11 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb)
static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb)
{
BDRVQuorumState *s = acb->common.bs->opaque;
+ int n = acb->children_read++;
- acb->qcrs[acb->child_iter].buf =
- qemu_blockalign(s->children[acb->child_iter]->bs, acb->qiov->size);
- qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov);
- qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov,
- acb->qcrs[acb->child_iter].buf);
- acb->qcrs[acb->child_iter].aiocb =
- bdrv_aio_readv(s->children[acb->child_iter], acb->sector_num,
- &acb->qcrs[acb->child_iter].qiov, acb->nb_sectors,
- quorum_aio_cb, &acb->qcrs[acb->child_iter]);
+ acb->qcrs[n].aiocb = bdrv_aio_readv(s->children[n], acb->sector_num,
+ acb->qiov, acb->nb_sectors,
+ quorum_fifo_aio_cb, &acb->qcrs[n]);
return &acb->common;
}
@@ -696,13 +694,12 @@ static BlockAIOCB *quorum_aio_readv(BlockDriverState *bs,
QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num,
nb_sectors, cb, opaque);
acb->is_read = true;
+ acb->children_read = 0;
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
- acb->child_iter = s->num_children - 1;
return read_quorum_children(acb);
}
- acb->child_iter = 0;
return read_fifo_child(acb);
}
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 166e9d1ad5..f481e57f78 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -443,6 +443,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
fd = qemu_open(filename, s->open_flags, 0644);
if (fd < 0) {
ret = -errno;
+ error_setg_errno(errp, errno, "Could not open '%s'", filename);
if (ret == -EROFS) {
ret = -EACCES;
}
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 734bb105bd..800fabdd72 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -373,6 +373,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
if (s->hfile == INVALID_HANDLE_VALUE) {
int err = GetLastError();
+ error_setg_win32(errp, err, "Could not open '%s'", filename);
if (err == ERROR_ACCESS_DENIED) {
ret = -EACCES;
} else {
diff --git a/block/replication.c b/block/replication.c
index 3bd1cf1809..8bbfc8f870 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -101,6 +101,11 @@ static int replication_open(BlockDriverState *bs, QDict *options,
if (!strcmp(mode, "primary")) {
s->mode = REPLICATION_MODE_PRIMARY;
+ top_id = qemu_opt_get(opts, REPLICATION_TOP_ID);
+ if (top_id) {
+ error_setg(&local_err, "The primary side does not support option top-id");
+ goto fail;
+ }
} else if (!strcmp(mode, "secondary")) {
s->mode = REPLICATION_MODE_SECONDARY;
top_id = qemu_opt_get(opts, REPLICATION_TOP_ID);
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 59545e287e..17b2efb7c7 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -168,6 +168,22 @@ static BlockBackend *throttle_group_next_blk(BlockBackend *blk)
return blk_by_public(next);
}
+/*
+ * Return whether a BlockBackend has pending requests.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @blk: the BlockBackend
+ * @is_write: the type of operation (read/write)
+ * @ret: whether the BlockBackend has pending requests.
+ */
+static inline bool blk_has_pending_reqs(BlockBackend *blk,
+ bool is_write)
+{
+ const BlockBackendPublic *blkp = blk_get_public(blk);
+ return blkp->pending_reqs[is_write];
+}
+
/* Return the next BlockBackend in the round-robin sequence with pending I/O
* requests.
*
@@ -188,7 +204,7 @@ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write)
/* get next bs round in round robin style */
token = throttle_group_next_blk(token);
- while (token != start && !blkp->pending_reqs[is_write]) {
+ while (token != start && !blk_has_pending_reqs(token, is_write)) {
token = throttle_group_next_blk(token);
}
@@ -196,10 +212,13 @@ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write)
* then decide the token is the current bs because chances are
* the current bs get the current request queued.
*/
- if (token == start && !blkp->pending_reqs[is_write]) {
+ if (token == start && !blk_has_pending_reqs(token, is_write)) {
token = blk;
}
+ /* Either we return the original BB, or one with pending requests */
+ assert(token == blk || blk_has_pending_reqs(token, is_write));
+
return token;
}
@@ -257,7 +276,7 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
/* Check if there's any pending request to schedule next */
token = next_throttle_token(blk, is_write);
- if (!blkp->pending_reqs[is_write]) {
+ if (!blk_has_pending_reqs(token, is_write)) {
return;
}
@@ -271,7 +290,7 @@ static void schedule_next_request(BlockBackend *blk, bool is_write)
qemu_co_queue_next(&blkp->throttled_reqs[is_write])) {
token = blk;
} else {
- ThrottleTimers *tt = &blkp->throttle_timers;
+ ThrottleTimers *tt = &blk_get_public(token)->throttle_timers;
int64_t now = qemu_clock_get_ns(tt->clock_type);
timer_mod(tt->timers[is_write], now + 1);
tg->any_timer_armed[is_write] = true;
diff --git a/docs/qmp-commands.txt b/docs/qmp-commands.txt
index 3220fb1075..284576d795 100644
--- a/docs/qmp-commands.txt
+++ b/docs/qmp-commands.txt
@@ -1090,11 +1090,11 @@ Arguments:
Example:
-> { "execute": "blockdev-add",
- "arguments": { "options": { "driver": "qcow2",
- "node-name": "node1534",
- "file": { "driver": "file",
- "filename": "hd1.qcow2" },
- "backing": "" } } }
+ "arguments": { "driver": "qcow2",
+ "node-name": "node1534",
+ "file": { "driver": "file",
+ "filename": "hd1.qcow2" },
+ "backing": "" } }
<- { "return": {} }
@@ -3130,41 +3130,37 @@ This command is still a work in progress. It doesn't support all
block drivers among other things. Stay away from it unless you want
to help with its development.
-Arguments:
-
-- "options": block driver options
+For the arguments, see the QAPI schema documentation of BlockdevOptions.
Example (1):
-> { "execute": "blockdev-add",
- "arguments": { "options" : { "driver": "qcow2",
- "file": { "driver": "file",
- "filename": "test.qcow2" } } } }
+ "arguments": { "driver": "qcow2",
+ "file": { "driver": "file",
+ "filename": "test.qcow2" } } }
<- { "return": {} }
Example (2):
-> { "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "qcow2",
- "node-name": "my_disk",
- "discard": "unmap",
- "cache": {
- "direct": true,
- "writeback": true
- },
- "file": {
- "driver": "file",
- "filename": "/tmp/test.qcow2"
- },
- "backing": {
- "driver": "raw",
- "file": {
- "driver": "file",
- "filename": "/dev/fdset/4"
- }
- }
+ "driver": "qcow2",
+ "node-name": "my_disk",
+ "discard": "unmap",
+ "cache": {
+ "direct": true,
+ "writeback": true
+ },
+ "file": {
+ "driver": "file",
+ "filename": "/tmp/test.qcow2"
+ },
+ "backing": {
+ "driver": "raw",
+ "file": {
+ "driver": "file",
+ "filename": "/dev/fdset/4"
+ }
}
}
}
@@ -3191,13 +3187,11 @@ Example:
-> { "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "qcow2",
- "node-name": "node0",
- "file": {
- "driver": "file",
- "filename": "test.qcow2"
- }
+ "driver": "qcow2",
+ "node-name": "node0",
+ "file": {
+ "driver": "file",
+ "filename": "test.qcow2"
}
}
}
@@ -3342,10 +3336,10 @@ Arguments:
Example:
-> { "execute": "blockdev-add",
- "arguments": { "options": { "node-name": "node0",
- "driver": "raw",
- "file": { "driver": "file",
- "filename": "fedora.iso" } } } }
+ "arguments": { { "node-name": "node0",
+ "driver": "raw",
+ "file": { "driver": "file",
+ "filename": "fedora.iso" } } }
<- { "return": {} }
@@ -3383,10 +3377,10 @@ Example:
Add a new node to a quorum
-> { "execute": "blockdev-add",
- "arguments": { "options": { "driver": "raw",
- "node-name": "new_node",
- "file": { "driver": "file",
- "filename": "test.raw" } } } }
+ "arguments": { "driver": "raw",
+ "node-name": "new_node",
+ "file": { "driver": "file",
+ "filename": "test.raw" } } }
<- { "return": {} }
-> { "execute": "x-blockdev-change",
"arguments": { "parent": "disk1",
diff --git a/exec.c b/exec.c
index 9a736da955..587b489eef 100644
--- a/exec.c
+++ b/exec.c
@@ -93,6 +93,11 @@ static MemoryRegion io_mem_unassigned;
#endif
+#ifdef TARGET_PAGE_BITS_VARY
+int target_page_bits;
+bool target_page_bits_decided;
+#endif
+
struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
/* current CPU in the current thread. It is only valid inside
cpu_exec() */
@@ -102,8 +107,37 @@ __thread CPUState *current_cpu;
2 = Adaptive rate instruction counting. */
int use_icount;
+bool set_preferred_target_page_bits(int bits)
+{
+ /* The target page size is the lowest common denominator for all
+ * the CPUs in the system, so we can only make it smaller, never
+ * larger. And we can't make it smaller once we've committed to
+ * a particular size.
+ */
+#ifdef TARGET_PAGE_BITS_VARY
+ assert(bits >= TARGET_PAGE_BITS_MIN);
+ if (target_page_bits == 0 || target_page_bits > bits) {
+ if (target_page_bits_decided) {
+ return false;
+ }
+ target_page_bits = bits;
+ }
+#endif
+ return true;
+}
+
#if !defined(CONFIG_USER_ONLY)
+static void finalize_target_page_bits(void)
+{
+#ifdef TARGET_PAGE_BITS_VARY
+ if (target_page_bits == 0) {
+ target_page_bits = TARGET_PAGE_BITS_MIN;
+ }
+ target_page_bits_decided = true;
+#endif
+}
+
typedef struct PhysPageEntry PhysPageEntry;
struct PhysPageEntry {
@@ -153,7 +187,7 @@ typedef struct subpage_t {
MemoryRegion iomem;
AddressSpace *as;
hwaddr base;
- uint16_t sub_section[TARGET_PAGE_SIZE];
+ uint16_t sub_section[];
} subpage_t;
#define PHYS_SECTION_UNASSIGNED 0
@@ -2217,8 +2251,7 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base)
{
subpage_t *mmio;
- mmio = g_malloc0(sizeof(subpage_t));
-
+ mmio = g_malloc0(sizeof(subpage_t) + TARGET_PAGE_SIZE * sizeof(uint16_t));
mmio->as = as;
mmio->base = base;
memory_region_init_io(&mmio->iomem, NULL, &subpage_ops, mmio,
@@ -2810,6 +2843,14 @@ void cpu_register_map_client(QEMUBH *bh)
void cpu_exec_init_all(void)
{
qemu_mutex_init(&ram_list.mutex);
+ /* The data structures we set up here depend on knowing the page size,
+ * so no more changes can be made after this point.
+ * In an ideal world, nothing we did before we had finished the
+ * machine setup would care about the target page size, and we could
+ * do this much later, rather than requiring board models to state
+ * up front what their requirements are.
+ */
+ finalize_target_page_bits();
io_mem_init();
memory_map_init();
qemu_mutex_init(&map_client_list_lock);
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 7527037c23..cbbca4e17a 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -384,18 +384,24 @@ static NetClientInfo net_mv88w8618_info = {
.cleanup = eth_cleanup,
};
-static int mv88w8618_eth_init(SysBusDevice *sbd)
+static void mv88w8618_eth_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
sysbus_init_irq(sbd, &s->irq);
- s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
- object_get_typename(OBJECT(dev)), dev->id, s);
- memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
"mv88w8618-eth", MP_ETH_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
- return 0;
+}
+
+static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
+{
+ mv88w8618_eth_state *s = MV88W8618_ETH(dev);
+
+ s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->id, s);
}
static const VMStateDescription mv88w8618_eth_vmsd = {
@@ -423,17 +429,17 @@ static Property mv88w8618_eth_properties[] = {
static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mv88w8618_eth_init;
dc->vmsd = &mv88w8618_eth_vmsd;
dc->props = mv88w8618_eth_properties;
+ dc->realize = mv88w8618_eth_realize;
}
static const TypeInfo mv88w8618_eth_info = {
.name = TYPE_MV88W8618_ETH,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_eth_state),
+ .instance_init = mv88w8618_eth_init,
.class_init = mv88w8618_eth_class_init,
};
@@ -615,23 +621,26 @@ static const GraphicHwOps musicpal_gfx_ops = {
.gfx_update = lcd_refresh,
};
-static int musicpal_lcd_init(SysBusDevice *sbd)
+static void musicpal_lcd_realize(DeviceState *dev, Error **errp)
+{
+ musicpal_lcd_state *s = MUSICPAL_LCD(dev);
+ s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
+ qemu_console_resize(s->con, 128 * 3, 64 * 3);
+}
+
+static void musicpal_lcd_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
musicpal_lcd_state *s = MUSICPAL_LCD(dev);
s->brightness = 7;
- memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s,
+ memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s,
"musicpal-lcd", MP_LCD_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
- s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
- qemu_console_resize(s->con, 128*3, 64*3);
-
qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
-
- return 0;
}
static const VMStateDescription musicpal_lcd_vmsd = {
@@ -652,16 +661,16 @@ static const VMStateDescription musicpal_lcd_vmsd = {
static void musicpal_lcd_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = musicpal_lcd_init;
dc->vmsd = &musicpal_lcd_vmsd;
+ dc->realize = musicpal_lcd_realize;
}
static const TypeInfo musicpal_lcd_info = {
.name = TYPE_MUSICPAL_LCD,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(musicpal_lcd_state),
+ .instance_init = musicpal_lcd_init,
.class_init = musicpal_lcd_class_init,
};
@@ -748,16 +757,16 @@ static const MemoryRegionOps mv88w8618_pic_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int mv88w8618_pic_init(SysBusDevice *dev)
+static void mv88w8618_pic_init(Object *obj)
{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
mv88w8618_pic_state *s = MV88W8618_PIC(dev);
qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32);
sysbus_init_irq(dev, &s->parent_irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s,
"musicpal-pic", MP_PIC_SIZE);
sysbus_init_mmio(dev, &s->iomem);
- return 0;
}
static const VMStateDescription mv88w8618_pic_vmsd = {
@@ -774,9 +783,7 @@ static const VMStateDescription mv88w8618_pic_vmsd = {
static void mv88w8618_pic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mv88w8618_pic_init;
dc->reset = mv88w8618_pic_reset;
dc->vmsd = &mv88w8618_pic_vmsd;
}
@@ -785,6 +792,7 @@ static const TypeInfo mv88w8618_pic_info = {
.name = TYPE_MV88W8618_PIC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_pic_state),
+ .instance_init = mv88w8618_pic_init,
.class_init = mv88w8618_pic_class_init,
};
@@ -913,8 +921,9 @@ static const MemoryRegionOps mv88w8618_pit_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int mv88w8618_pit_init(SysBusDevice *dev)
+static void mv88w8618_pit_init(Object *obj)
{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
mv88w8618_pit_state *s = MV88W8618_PIT(dev);
int i;
@@ -924,10 +933,9 @@ static int mv88w8618_pit_init(SysBusDevice *dev)
mv88w8618_timer_init(dev, &s->timer[i], 1000000);
}
- memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s,
"musicpal-pit", MP_PIT_SIZE);
sysbus_init_mmio(dev, &s->iomem);
- return 0;
}
static const VMStateDescription mv88w8618_timer_vmsd = {
@@ -955,9 +963,7 @@ static const VMStateDescription mv88w8618_pit_vmsd = {
static void mv88w8618_pit_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mv88w8618_pit_init;
dc->reset = mv88w8618_pit_reset;
dc->vmsd = &mv88w8618_pit_vmsd;
}
@@ -966,6 +972,7 @@ static const TypeInfo mv88w8618_pit_info = {
.name = TYPE_MV88W8618_PIT,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_pit_state),
+ .instance_init = mv88w8618_pit_init,
.class_init = mv88w8618_pit_class_init,
};
@@ -1018,15 +1025,15 @@ static const MemoryRegionOps mv88w8618_flashcfg_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int mv88w8618_flashcfg_init(SysBusDevice *dev)
+static void mv88w8618_flashcfg_init(Object *obj)
{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev);
s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
- memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s,
"musicpal-flashcfg", MP_FLASHCFG_SIZE);
sysbus_init_mmio(dev, &s->iomem);
- return 0;
}
static const VMStateDescription mv88w8618_flashcfg_vmsd = {
@@ -1042,9 +1049,7 @@ static const VMStateDescription mv88w8618_flashcfg_vmsd = {
static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mv88w8618_flashcfg_init;
dc->vmsd = &mv88w8618_flashcfg_vmsd;
}
@@ -1052,6 +1057,7 @@ static const TypeInfo mv88w8618_flashcfg_info = {
.name = TYPE_MV88W8618_FLASHCFG,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_flashcfg_state),
+ .instance_init = mv88w8618_flashcfg_init,
.class_init = mv88w8618_flashcfg_class_init,
};
@@ -1350,22 +1356,21 @@ static void musicpal_gpio_reset(DeviceState *d)
s->isr = 0;
}
-static int musicpal_gpio_init(SysBusDevice *sbd)
+static void musicpal_gpio_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
musicpal_gpio_state *s = MUSICPAL_GPIO(dev);
sysbus_init_irq(sbd, &s->irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s,
+ memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s,
"musicpal-gpio", MP_GPIO_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32);
-
- return 0;
}
static const VMStateDescription musicpal_gpio_vmsd = {
@@ -1386,9 +1391,7 @@ static const VMStateDescription musicpal_gpio_vmsd = {
static void musicpal_gpio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = musicpal_gpio_init;
dc->reset = musicpal_gpio_reset;
dc->vmsd = &musicpal_gpio_vmsd;
}
@@ -1397,6 +1400,7 @@ static const TypeInfo musicpal_gpio_info = {
.name = TYPE_MUSICPAL_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(musicpal_gpio_state),
+ .instance_init = musicpal_gpio_init,
.class_init = musicpal_gpio_class_init,
};
@@ -1516,12 +1520,13 @@ static void musicpal_key_event(void *opaque, int keycode)
s->kbd_extended = 0;
}
-static int musicpal_key_init(SysBusDevice *sbd)
+static void musicpal_key_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
musicpal_key_state *s = MUSICPAL_KEY(dev);
- memory_region_init(&s->iomem, OBJECT(s), "dummy", 0);
+ memory_region_init(&s->iomem, obj, "dummy", 0);
sysbus_init_mmio(sbd, &s->iomem);
s->kbd_extended = 0;
@@ -1530,8 +1535,6 @@ static int musicpal_key_init(SysBusDevice *sbd)
qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
qemu_add_kbd_event_handler(musicpal_key_event, s);
-
- return 0;
}
static const VMStateDescription musicpal_key_vmsd = {
@@ -1548,9 +1551,7 @@ static const VMStateDescription musicpal_key_vmsd = {
static void musicpal_key_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = musicpal_key_init;
dc->vmsd = &musicpal_key_vmsd;
}
@@ -1558,6 +1559,7 @@ static const TypeInfo musicpal_key_info = {
.name = TYPE_MUSICPAL_KEY,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(musicpal_key_state),
+ .instance_init = musicpal_key_init,
.class_init = musicpal_key_class_init,
};
diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c
index 576a8eb91f..521dbad039 100644
--- a/hw/arm/pxa2xx_gpio.c
+++ b/hw/arm/pxa2xx_gpio.c
@@ -280,23 +280,28 @@ DeviceState *pxa2xx_gpio_init(hwaddr base,
return dev;
}
-static int pxa2xx_gpio_initfn(SysBusDevice *sbd)
+static void pxa2xx_gpio_initfn(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev);
- s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu));
-
- qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines);
- qdev_init_gpio_out(dev, s->handler, s->lines);
-
- memory_region_init_io(&s->iomem, OBJECT(s), &pxa_gpio_ops, s, "pxa2xx-gpio", 0x1000);
+ memory_region_init_io(&s->iomem, obj, &pxa_gpio_ops,
+ s, "pxa2xx-gpio", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq0);
sysbus_init_irq(sbd, &s->irq1);
sysbus_init_irq(sbd, &s->irqX);
+}
- return 0;
+static void pxa2xx_gpio_realize(DeviceState *dev, Error **errp)
+{
+ PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev);
+
+ s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu));
+
+ qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines);
+ qdev_init_gpio_out(dev, s->handler, s->lines);
}
/*
@@ -336,18 +341,18 @@ static Property pxa2xx_gpio_properties[] = {
static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = pxa2xx_gpio_initfn;
dc->desc = "PXA2xx GPIO controller";
dc->props = pxa2xx_gpio_properties;
dc->vmsd = &vmstate_pxa2xx_gpio_regs;
+ dc->realize = pxa2xx_gpio_realize;
}
static const TypeInfo pxa2xx_gpio_info = {
.name = TYPE_PXA2XX_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PXA2xxGPIOInfo),
+ .instance_init = pxa2xx_gpio_initfn,
.class_init = pxa2xx_gpio_class_init,
};
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index 85db1e2813..3311cc38a4 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -1236,6 +1236,11 @@ static void strongarm_uart_init(Object *obj)
s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s);
s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s);
+}
+
+static void strongarm_uart_realize(DeviceState *dev, Error **errp)
+{
+ StrongARMUARTState *s = STRONGARM_UART(dev);
qemu_chr_fe_set_handlers(&s->chr,
strongarm_uart_can_receive,
@@ -1316,6 +1321,7 @@ static void strongarm_uart_class_init(ObjectClass *klass, void *data)
dc->reset = strongarm_uart_reset;
dc->vmsd = &vmstate_strongarm_uart_regs;
dc->props = strongarm_uart_properties;
+ dc->realize = strongarm_uart_realize;
}
static const TypeInfo strongarm_uart_info = {
@@ -1516,19 +1522,19 @@ static int strongarm_ssp_post_load(void *opaque, int version_id)
return 0;
}
-static int strongarm_ssp_init(SysBusDevice *sbd)
+static void strongarm_ssp_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
StrongARMSSPState *s = STRONGARM_SSP(dev);
sysbus_init_irq(sbd, &s->irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ssp_ops, s,
+ memory_region_init_io(&s->iomem, obj, &strongarm_ssp_ops, s,
"ssp", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
s->bus = ssi_create_bus(dev, "ssi");
- return 0;
}
static void strongarm_ssp_reset(DeviceState *dev)
@@ -1558,9 +1564,7 @@ static const VMStateDescription vmstate_strongarm_ssp_regs = {
static void strongarm_ssp_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = strongarm_ssp_init;
dc->desc = "StrongARM SSP controller";
dc->reset = strongarm_ssp_reset;
dc->vmsd = &vmstate_strongarm_ssp_regs;
@@ -1570,6 +1574,7 @@ static const TypeInfo strongarm_ssp_info = {
.name = TYPE_STRONGARM_SSP,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(StrongARMSSPState),
+ .instance_init = strongarm_ssp_init,
.class_init = strongarm_ssp_class_init,
};
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index fa0655a775..5fc10df08e 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -384,6 +384,61 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
}
static void
+build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
+{
+ int iort_start = table_data->len;
+ AcpiIortIdMapping *idmap;
+ AcpiIortItsGroup *its;
+ AcpiIortTable *iort;
+ size_t node_size, iort_length;
+ AcpiIortRC *rc;
+
+ iort = acpi_data_push(table_data, sizeof(*iort));
+
+ iort_length = sizeof(*iort);
+ iort->node_count = cpu_to_le32(2); /* RC and ITS nodes */
+ iort->node_offset = cpu_to_le32(sizeof(*iort));
+
+ /* ITS group node */
+ node_size = sizeof(*its) + sizeof(uint32_t);
+ iort_length += node_size;
+ its = acpi_data_push(table_data, node_size);
+
+ its->type = ACPI_IORT_NODE_ITS_GROUP;
+ its->length = cpu_to_le16(node_size);
+ its->its_count = cpu_to_le32(1);
+ its->identifiers[0] = 0; /* MADT translation_id */
+
+ /* Root Complex Node */
+ node_size = sizeof(*rc) + sizeof(*idmap);
+ iort_length += node_size;
+ rc = acpi_data_push(table_data, node_size);
+
+ rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
+ rc->length = cpu_to_le16(node_size);
+ rc->mapping_count = cpu_to_le32(1);
+ rc->mapping_offset = cpu_to_le32(sizeof(*rc));
+
+ /* fully coherent device */
+ rc->memory_properties.cache_coherency = cpu_to_le32(1);
+ rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
+ rc->pci_segment_number = 0; /* MCFG pci_segment */
+
+ /* Identity RID mapping covering the whole input RID range */
+ idmap = &rc->id_mapping_array[0];
+ idmap->input_base = 0;
+ idmap->id_count = cpu_to_le32(0xFFFF);
+ idmap->output_base = 0;
+ /* output IORT node is the ITS group node (the first node) */
+ idmap->output_reference = cpu_to_le32(iort->node_offset);
+
+ iort->length = cpu_to_le32(iort_length);
+
+ build_header(linker, table_data, (void *)(table_data->data + iort_start),
+ "IORT", table_data->len - iort_start, 0, NULL, NULL);
+}
+
+static void
build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
{
AcpiSerialPortConsoleRedirection *spcr;
@@ -667,17 +722,6 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
ACPI_BUILD_TABLE_FILE, tables_blob,
64, false /* high memory */);
- /*
- * The ACPI v5.1 tables for Hardware-reduced ACPI platform are:
- * RSDP
- * RSDT
- * FADT
- * GTDT
- * MADT
- * MCFG
- * DSDT
- */
-
/* DSDT is pointed to by FADT */
dsdt = tables_blob->len;
build_dsdt(tables_blob, tables->linker, guest_info);
@@ -703,6 +747,11 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
build_srat(tables_blob, tables->linker, guest_info);
}
+ if (its_class_name() && !guest_info->no_its) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_iort(tables_blob, tables->linker, guest_info);
+ }
+
/* RSDT is pointed to by RSDP */
rsdt = tables_blob->len;
build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c3a1e92e51..070bbf89d4 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1499,6 +1499,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->pci_allow_0_address = true;
+ /* We know we will never create a pre-ARMv7 CPU which needs 1K pages */
+ mc->minimum_page_bits = 12;
}
static const TypeInfo virt_machine_info = {
@@ -1570,6 +1572,8 @@ static void virt_machine_2_7_options(MachineClass *mc)
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_7);
/* ITS was introduced with 2.8 */
vmc->no_its = true;
+ /* Stick with 1K pages for migration compatibility */
+ mc->minimum_page_bits = 0;
}
DEFINE_VIRT_MACHINE(2, 7)
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index c45c835a17..3af82afe78 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -13,6 +13,9 @@
#include "sysemu/replay.h"
#include "sysemu/qtest.h"
+#define DELTA_ADJUST 1
+#define DELTA_NO_ADJUST -1
+
struct ptimer_state
{
uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */
@@ -35,16 +38,21 @@ static void ptimer_trigger(ptimer_state *s)
}
}
-static void ptimer_reload(ptimer_state *s)
+static void ptimer_reload(ptimer_state *s, int delta_adjust)
{
uint32_t period_frac = s->period_frac;
uint64_t period = s->period;
+ uint64_t delta = s->delta;
- if (s->delta == 0) {
+ if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
ptimer_trigger(s);
- s->delta = s->limit;
}
- if (s->delta == 0 || s->period == 0) {
+
+ if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
+ delta = s->delta = s->limit;
+ }
+
+ if (s->period == 0) {
if (!qtest_enabled()) {
fprintf(stderr, "Timer with period zero, disabling\n");
}
@@ -53,6 +61,39 @@ static void ptimer_reload(ptimer_state *s)
return;
}
+ if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
+ if (delta_adjust != DELTA_NO_ADJUST) {
+ delta += delta_adjust;
+ }
+ }
+
+ if (delta == 0 && (s->policy_mask & PTIMER_POLICY_CONTINUOUS_TRIGGER)) {
+ if (s->enabled == 1 && s->limit == 0) {
+ delta = 1;
+ }
+ }
+
+ if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
+ if (delta_adjust != DELTA_NO_ADJUST) {
+ delta = 1;
+ }
+ }
+
+ if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
+ if (s->enabled == 1 && s->limit != 0) {
+ delta = 1;
+ }
+ }
+
+ if (delta == 0) {
+ if (!qtest_enabled()) {
+ fprintf(stderr, "Timer with delta zero, disabling\n");
+ }
+ timer_del(s->timer);
+ s->enabled = 0;
+ return;
+ }
+
/*
* Artificially limit timeout rate to something
* achievable under QEMU. Otherwise, QEMU spends all
@@ -62,15 +103,15 @@ static void ptimer_reload(ptimer_state *s)
* on the current generation of host machines.
*/
- if (s->enabled == 1 && (s->delta * period < 10000) && !use_icount) {
- period = 10000 / s->delta;
+ if (s->enabled == 1 && (delta * period < 10000) && !use_icount) {
+ period = 10000 / delta;
period_frac = 0;
}
s->last_event = s->next_event;
- s->next_event = s->last_event + s->delta * period;
+ s->next_event = s->last_event + delta * period;
if (period_frac) {
- s->next_event += ((int64_t)period_frac * s->delta) >> 32;
+ s->next_event += ((int64_t)period_frac * delta) >> 32;
}
timer_mod(s->timer, s->next_event);
}
@@ -78,12 +119,35 @@ static void ptimer_reload(ptimer_state *s)
static void ptimer_tick(void *opaque)
{
ptimer_state *s = (ptimer_state *)opaque;
- ptimer_trigger(s);
- s->delta = 0;
+ bool trigger = true;
+
if (s->enabled == 2) {
+ s->delta = 0;
s->enabled = 0;
} else {
- ptimer_reload(s);
+ int delta_adjust = DELTA_ADJUST;
+
+ if (s->delta == 0 || s->limit == 0) {
+ /* If a "continuous trigger" policy is not used and limit == 0,
+ we should error out. delta == 0 means that this tick is
+ caused by a "no immediate reload" policy, so it shouldn't
+ be adjusted. */
+ delta_adjust = DELTA_NO_ADJUST;
+ }
+
+ if (!(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
+ /* Avoid re-trigger on deferred reload if "no immediate trigger"
+ policy isn't used. */
+ trigger = (delta_adjust == DELTA_ADJUST);
+ }
+
+ s->delta = s->limit;
+
+ ptimer_reload(s, delta_adjust);
+ }
+
+ if (trigger) {
+ ptimer_trigger(s);
}
}
@@ -91,9 +155,10 @@ uint64_t ptimer_get_count(ptimer_state *s)
{
uint64_t counter;
- if (s->enabled) {
+ if (s->enabled && s->delta != 0) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t next = s->next_event;
+ int64_t last = s->last_event;
bool expired = (now - next >= 0);
bool oneshot = (s->enabled == 2);
@@ -118,7 +183,7 @@ uint64_t ptimer_get_count(ptimer_state *s)
/* We need to divide time by period, where time is stored in
rem (64-bit integer) and period is stored in period/period_frac
(64.32 fixed point).
-
+
Doing full precision division is hard, so scale values and
do a 64-bit division. The result should be rounded down,
so that the rounding error never causes the timer to go
@@ -145,6 +210,35 @@ uint64_t ptimer_get_count(ptimer_state *s)
div += 1;
}
counter = rem / div;
+
+ if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
+ /* Before wrapping around, timer should stay with counter = 0
+ for a one period. */
+ if (!oneshot && s->delta == s->limit) {
+ if (now == last) {
+ /* Counter == delta here, check whether it was
+ adjusted and if it was, then right now it is
+ that "one period". */
+ if (counter == s->limit + DELTA_ADJUST) {
+ return 0;
+ }
+ } else if (counter == s->limit) {
+ /* Since the counter is rounded down and now != last,
+ the counter == limit means that delta was adjusted
+ by +1 and right now it is that adjusted period. */
+ return 0;
+ }
+ }
+ }
+ }
+
+ if (s->policy_mask & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) {
+ /* If now == last then delta == limit, i.e. the counter already
+ represents the correct value. It would be rounded down a 1ns
+ later. */
+ if (now != last) {
+ counter += 1;
+ }
}
} else {
counter = s->delta;
@@ -157,7 +251,7 @@ void ptimer_set_count(ptimer_state *s, uint64_t count)
s->delta = count;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -174,7 +268,7 @@ void ptimer_run(ptimer_state *s, int oneshot)
s->enabled = oneshot ? 2 : 1;
if (was_disabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -198,7 +292,7 @@ void ptimer_set_period(ptimer_state *s, int64_t period)
s->period_frac = 0;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -210,7 +304,7 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq)
s->period_frac = (1000000000ll << 32) / freq;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -223,7 +317,7 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
s->delta = limit;
if (s->enabled && reload) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index c069c0b7fd..8c7dcc6f0a 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -466,17 +466,16 @@ static const GraphicHwOps pl110_gfx_ops = {
.gfx_update = pl110_update_display,
};
-static int pl110_initfn(SysBusDevice *sbd)
+static void pl110_realize(DeviceState *dev, Error **errp)
{
- DeviceState *dev = DEVICE(sbd);
PL110State *s = PL110(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
- return 0;
}
static void pl110_init(Object *obj)
@@ -503,11 +502,10 @@ static void pl111_init(Object *obj)
static void pl110_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = pl110_initfn;
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
dc->vmsd = &vmstate_pl110;
+ dc->realize = pl110_realize;
}
static const TypeInfo pl110_info = {
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index 4afbe0bde5..abd4c4cddb 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -88,7 +88,12 @@ int i2c_bus_busy(I2CBus *bus)
return !QLIST_EMPTY(&bus->current_devs);
}
-/* Returns non-zero if the address is not valid. */
+/*
+ * Returns non-zero if the address is not valid. If this is called
+ * again without an intervening i2c_end_transfer(), like in the SMBus
+ * case where the operation is switched from write to read, this
+ * function will not rescan the bus and thus cannot fail.
+ */
/* TODO: Make this handle multiple masters. */
int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
{
@@ -104,15 +109,25 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
bus->broadcast = true;
}
- QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
- DeviceState *qdev = kid->child;
- I2CSlave *candidate = I2C_SLAVE(qdev);
- if ((candidate->address == address) || (bus->broadcast)) {
- node = g_malloc(sizeof(struct I2CNode));
- node->elt = candidate;
- QLIST_INSERT_HEAD(&bus->current_devs, node, next);
- if (!bus->broadcast) {
- break;
+ /*
+ * If there are already devices in the list, that means we are in
+ * the middle of a transaction and we shouldn't rescan the bus.
+ *
+ * This happens with any SMBus transaction, even on a pure I2C
+ * device. The interface does a transaction start without
+ * terminating the previous transaction.
+ */
+ if (QLIST_EMPTY(&bus->current_devs)) {
+ QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+ DeviceState *qdev = kid->child;
+ I2CSlave *candidate = I2C_SLAVE(qdev);
+ if ((candidate->address == address) || (bus->broadcast)) {
+ node = g_malloc(sizeof(struct I2CNode));
+ node->elt = candidate;
+ QLIST_INSERT_HEAD(&bus->current_devs, node, next);
+ if (!bus->broadcast) {
+ break;
+ }
}
}
}
@@ -137,10 +152,6 @@ void i2c_end_transfer(I2CBus *bus)
I2CSlaveClass *sc;
I2CNode *node, *next;
- if (QLIST_EMPTY(&bus->current_devs)) {
- return;
- }
-
QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {
sc = I2C_SLAVE_GET_CLASS(node->elt);
if (sc->event) {
diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c
index 3979b3dad7..5b4dd3eba4 100644
--- a/hw/i2c/smbus.c
+++ b/hw/i2c/smbus.c
@@ -248,7 +248,9 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
return -1;
}
i2c_send(bus, command);
- i2c_start_transfer(bus, addr, 1);
+ if (i2c_start_transfer(bus, addr, 1)) {
+ assert(0);
+ }
data = i2c_recv(bus);
i2c_nack(bus);
i2c_end_transfer(bus);
@@ -273,7 +275,9 @@ int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
return -1;
}
i2c_send(bus, command);
- i2c_start_transfer(bus, addr, 1);
+ if (i2c_start_transfer(bus, addr, 1)) {
+ assert(0);
+ }
data = i2c_recv(bus);
data |= i2c_recv(bus) << 8;
i2c_nack(bus);
@@ -302,7 +306,9 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data)
return -1;
}
i2c_send(bus, command);
- i2c_start_transfer(bus, addr, 1);
+ if (i2c_start_transfer(bus, addr, 1)) {
+ assert(0);
+ }
len = i2c_recv(bus);
if (len > 32) {
len = 0;
diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c
index 772f85f5fd..ce1dc63911 100644
--- a/hw/timer/a9gtimer.c
+++ b/hw/timer/a9gtimer.c
@@ -82,15 +82,15 @@ static void a9_gtimer_update(A9GTimerState *s, bool sync)
if ((s->control & R_CONTROL_TIMER_ENABLE) &&
(gtb->control & R_CONTROL_COMP_ENABLE)) {
/* R2p0+, where the compare function is >= */
- while (gtb->compare < update.new) {
+ if (gtb->compare < update.new) {
DB_PRINT("Compare event happened for CPU %d\n", i);
gtb->status = 1;
- if (gtb->control & R_CONTROL_AUTO_INCREMENT) {
- DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n",
- gtb->inc);
- gtb->compare += gtb->inc;
- } else {
- break;
+ if (gtb->control & R_CONTROL_AUTO_INCREMENT && gtb->inc) {
+ uint64_t inc =
+ QEMU_ALIGN_UP(update.new - gtb->compare, gtb->inc);
+ DB_PRINT("Auto incrementing timer compare by %"
+ PRId64 "\n", inc);
+ gtb->compare += inc;
}
}
cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1;
diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c
index d66bbf01b4..daf6c48797 100644
--- a/hw/timer/arm_mptimer.c
+++ b/hw/timer/arm_mptimer.c
@@ -20,22 +20,33 @@
*/
#include "qemu/osdep.h"
+#include "hw/ptimer.h"
#include "hw/timer/arm_mptimer.h"
#include "qapi/error.h"
-#include "qemu/timer.h"
+#include "qemu/main-loop.h"
#include "qom/cpu.h"
+#define PTIMER_POLICY \
+ (PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | \
+ PTIMER_POLICY_CONTINUOUS_TRIGGER | \
+ PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | \
+ PTIMER_POLICY_NO_IMMEDIATE_RELOAD | \
+ PTIMER_POLICY_NO_COUNTER_ROUND_DOWN)
+
/* This device implements the per-cpu private timer and watchdog block
* which is used in both the ARM11MPCore and Cortex-A9MP.
*/
static inline int get_current_cpu(ARMMPTimerState *s)
{
- if (current_cpu->cpu_index >= s->num_cpu) {
+ int cpu_id = current_cpu ? current_cpu->cpu_index : 0;
+
+ if (cpu_id >= s->num_cpu) {
hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
- s->num_cpu, current_cpu->cpu_index);
+ s->num_cpu, cpu_id);
}
- return current_cpu->cpu_index;
+
+ return cpu_id;
}
static inline void timerblock_update_irq(TimerBlock *tb)
@@ -44,33 +55,42 @@ static inline void timerblock_update_irq(TimerBlock *tb)
}
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
-static inline uint32_t timerblock_scale(TimerBlock *tb)
+static inline uint32_t timerblock_scale(uint32_t control)
{
- return (((tb->control >> 8) & 0xff) + 1) * 10;
+ return (((control >> 8) & 0xff) + 1) * 10;
}
-static void timerblock_reload(TimerBlock *tb, int restart)
+static inline void timerblock_set_count(struct ptimer_state *timer,
+ uint32_t control, uint64_t *count)
{
- if (tb->count == 0) {
- return;
+ /* PTimer would trigger interrupt for periodic timer when counter set
+ * to 0, MPtimer under certain condition only.
+ */
+ if ((control & 3) == 3 && (control & 0xff00) == 0 && *count == 0) {
+ *count = ptimer_get_limit(timer);
}
- if (restart) {
- tb->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ptimer_set_count(timer, *count);
+}
+
+static inline void timerblock_run(struct ptimer_state *timer,
+ uint32_t control, uint32_t load)
+{
+ if ((control & 1) && ((control & 0xff00) || load != 0)) {
+ ptimer_run(timer, !(control & 2));
}
- tb->tick += (int64_t)tb->count * timerblock_scale(tb);
- timer_mod(tb->timer, tb->tick);
}
static void timerblock_tick(void *opaque)
{
TimerBlock *tb = (TimerBlock *)opaque;
- tb->status = 1;
- if (tb->control & 2) {
- tb->count = tb->load;
- timerblock_reload(tb, 0);
- } else {
- tb->count = 0;
+ /* Periodic timer with load = 0 and prescaler != 0 would re-trigger
+ * IRQ after one period, otherwise it either stops or wraps around.
+ */
+ if ((tb->control & 2) && (tb->control & 0xff00) == 0 &&
+ ptimer_get_limit(tb->timer) == 0) {
+ ptimer_stop(tb->timer);
}
+ tb->status = 1;
timerblock_update_irq(tb);
}
@@ -78,21 +98,11 @@ static uint64_t timerblock_read(void *opaque, hwaddr addr,
unsigned size)
{
TimerBlock *tb = (TimerBlock *)opaque;
- int64_t val;
switch (addr) {
case 0: /* Load */
- return tb->load;
+ return ptimer_get_limit(tb->timer);
case 4: /* Counter. */
- if (((tb->control & 1) == 0) || (tb->count == 0)) {
- return 0;
- }
- /* Slow and ugly, but hopefully won't happen too often. */
- val = tb->tick - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- val /= timerblock_scale(tb);
- if (val < 0) {
- val = 0;
- }
- return val;
+ return ptimer_get_count(tb->timer);
case 8: /* Control. */
return tb->control;
case 12: /* Interrupt status. */
@@ -106,37 +116,45 @@ static void timerblock_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
TimerBlock *tb = (TimerBlock *)opaque;
- int64_t old;
+ uint32_t control = tb->control;
switch (addr) {
case 0: /* Load */
- tb->load = value;
- /* Fall through. */
- case 4: /* Counter. */
- if ((tb->control & 1) && tb->count) {
- /* Cancel the previous timer. */
- timer_del(tb->timer);
+ /* Setting load to 0 stops the timer without doing the tick if
+ * prescaler = 0.
+ */
+ if ((control & 1) && (control & 0xff00) == 0 && value == 0) {
+ ptimer_stop(tb->timer);
}
- tb->count = value;
- if (tb->control & 1) {
- timerblock_reload(tb, 1);
+ ptimer_set_limit(tb->timer, value, 1);
+ timerblock_run(tb->timer, control, value);
+ break;
+ case 4: /* Counter. */
+ /* Setting counter to 0 stops the one-shot timer, or periodic with
+ * load = 0, without doing the tick if prescaler = 0.
+ */
+ if ((control & 1) && (control & 0xff00) == 0 && value == 0 &&
+ (!(control & 2) || ptimer_get_limit(tb->timer) == 0)) {
+ ptimer_stop(tb->timer);
}
+ timerblock_set_count(tb->timer, control, &value);
+ timerblock_run(tb->timer, control, value);
break;
case 8: /* Control. */
- old = tb->control;
- tb->control = value;
+ if ((control & 3) != (value & 3)) {
+ ptimer_stop(tb->timer);
+ }
+ if ((control & 0xff00) != (value & 0xff00)) {
+ ptimer_set_period(tb->timer, timerblock_scale(value));
+ }
if (value & 1) {
- if ((old & 1) && (tb->count != 0)) {
- /* Do nothing if timer is ticking right now. */
- break;
+ uint64_t count = ptimer_get_count(tb->timer);
+ /* Re-load periodic timer counter if needed. */
+ if ((value & 2) && count == 0) {
+ timerblock_set_count(tb->timer, value, &count);
}
- if (tb->control & 2) {
- tb->count = tb->load;
- }
- timerblock_reload(tb, 1);
- } else if (old & 1) {
- /* Shutdown the timer. */
- timer_del(tb->timer);
+ timerblock_run(tb->timer, value, count);
}
+ tb->control = value;
break;
case 12: /* Interrupt status. */
tb->status &= ~value;
@@ -186,13 +204,12 @@ static const MemoryRegionOps timerblock_ops = {
static void timerblock_reset(TimerBlock *tb)
{
- tb->count = 0;
- tb->load = 0;
tb->control = 0;
tb->status = 0;
- tb->tick = 0;
if (tb->timer) {
- timer_del(tb->timer);
+ ptimer_stop(tb->timer);
+ ptimer_set_limit(tb->timer, 0, 1);
+ ptimer_set_period(tb->timer, timerblock_scale(0));
}
}
@@ -238,7 +255,8 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp)
*/
for (i = 0; i < s->num_cpu; i++) {
TimerBlock *tb = &s->timerblock[i];
- tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb);
+ QEMUBH *bh = qemu_bh_new(timerblock_tick, tb);
+ tb->timer = ptimer_init(bh, PTIMER_POLICY);
sysbus_init_irq(sbd, &tb->irq);
memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb,
"arm_mptimer_timerblock", 0x20);
@@ -248,26 +266,23 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp)
static const VMStateDescription vmstate_timerblock = {
.name = "arm_mptimer_timerblock",
- .version_id = 2,
- .minimum_version_id = 2,
+ .version_id = 3,
+ .minimum_version_id = 3,
.fields = (VMStateField[]) {
- VMSTATE_UINT32(count, TimerBlock),
- VMSTATE_UINT32(load, TimerBlock),
VMSTATE_UINT32(control, TimerBlock),
VMSTATE_UINT32(status, TimerBlock),
- VMSTATE_INT64(tick, TimerBlock),
- VMSTATE_TIMER_PTR(timer, TimerBlock),
+ VMSTATE_PTIMER(timer, TimerBlock),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_arm_mptimer = {
.name = "arm_mptimer",
- .version_id = 2,
- .minimum_version_id = 2,
+ .version_id = 3,
+ .minimum_version_id = 3,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu,
- 2, vmstate_timerblock, TimerBlock),
+ 3, vmstate_timerblock, TimerBlock),
VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c
index 8c4c1f9f05..e5f5e14a90 100644
--- a/hw/timer/stm32f2xx_timer.c
+++ b/hw/timer/stm32f2xx_timer.c
@@ -217,7 +217,7 @@ static void stm32f2xx_timer_write(void *opaque, hwaddr offset,
return;
case TIM_PSC:
timer_val = stm32f2xx_ns_to_ticks(s, now) - s->tick_offset;
- s->tim_psc = value;
+ s->tim_psc = value & 0xFFFF;
value = timer_val;
break;
case TIM_CNT:
diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
index ee3388f90d..9dea14ba03 100644
--- a/include/block/dirty-bitmap.h
+++ b/include/block/dirty-bitmap.h
@@ -8,6 +8,9 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
uint32_t granularity,
const char *name,
Error **errp);
+void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap,
+ int chunk_size);
+void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap);
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
BdrvDirtyBitmap *bitmap,
Error **errp);
@@ -27,8 +30,11 @@ void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs);
uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs);
uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap);
+uint32_t bdrv_dirty_bitmap_meta_granularity(BdrvDirtyBitmap *bitmap);
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap);
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap);
+const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap);
+int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap);
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap);
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
int64_t sector);
@@ -36,9 +42,34 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
int64_t cur_sector, int64_t nr_sectors);
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
int64_t cur_sector, int64_t nr_sectors);
-void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi);
-void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
+int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs,
+ BdrvDirtyBitmap *bitmap, int64_t sector,
+ int nb_sectors);
+void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs,
+ BdrvDirtyBitmap *bitmap, int64_t sector,
+ int nb_sectors);
+BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap);
+BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap,
+ uint64_t first_sector);
+void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter);
+int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter);
+void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t sector_num);
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap);
+int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap);
void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
+uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap,
+ uint64_t start, uint64_t count);
+uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap);
+void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
+ uint8_t *buf, uint64_t start,
+ uint64_t count);
+void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap,
+ uint8_t *buf, uint64_t start,
+ uint64_t count, bool finish);
+void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap,
+ uint64_t start, uint64_t count,
+ bool finish);
+void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap);
+
#endif
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
index b6a705982f..861260d3db 100644
--- a/include/exec/cpu-all.h
+++ b/include/exec/cpu-all.h
@@ -189,6 +189,15 @@ void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val,
/* page related stuff */
+#ifdef TARGET_PAGE_BITS_VARY
+extern bool target_page_bits_decided;
+extern int target_page_bits;
+#define TARGET_PAGE_BITS ({ assert(target_page_bits_decided); \
+ target_page_bits; })
+#else
+#define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS
+#endif
+
#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
index fa89abc44d..d1d1d61fcb 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -638,4 +638,72 @@ typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit;
/* Masks for Flags field above */
#define ACPI_DMAR_INCLUDE_PCI_ALL 1
+/*
+ * Input Output Remapping Table (IORT)
+ * Conforms to "IO Remapping Table System Software on ARM Platforms",
+ * Document number: ARM DEN 0049B, October 2015
+ */
+
+struct AcpiIortTable {
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ uint32_t node_count;
+ uint32_t node_offset;
+ uint32_t reserved;
+} QEMU_PACKED;
+typedef struct AcpiIortTable AcpiIortTable;
+
+/*
+ * IORT node types
+ */
+
+#define ACPI_IORT_NODE_HEADER_DEF /* Node format common fields */ \
+ uint8_t type; \
+ uint16_t length; \
+ uint8_t revision; \
+ uint32_t reserved; \
+ uint32_t mapping_count; \
+ uint32_t mapping_offset;
+
+/* Values for node Type above */
+enum {
+ ACPI_IORT_NODE_ITS_GROUP = 0x00,
+ ACPI_IORT_NODE_NAMED_COMPONENT = 0x01,
+ ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02,
+ ACPI_IORT_NODE_SMMU = 0x03,
+ ACPI_IORT_NODE_SMMU_V3 = 0x04
+};
+
+struct AcpiIortIdMapping {
+ uint32_t input_base;
+ uint32_t id_count;
+ uint32_t output_base;
+ uint32_t output_reference;
+ uint32_t flags;
+} QEMU_PACKED;
+typedef struct AcpiIortIdMapping AcpiIortIdMapping;
+
+struct AcpiIortMemoryAccess {
+ uint32_t cache_coherency;
+ uint8_t hints;
+ uint16_t reserved;
+ uint8_t memory_flags;
+} QEMU_PACKED;
+typedef struct AcpiIortMemoryAccess AcpiIortMemoryAccess;
+
+struct AcpiIortItsGroup {
+ ACPI_IORT_NODE_HEADER_DEF
+ uint32_t its_count;
+ uint32_t identifiers[0];
+} QEMU_PACKED;
+typedef struct AcpiIortItsGroup AcpiIortItsGroup;
+
+struct AcpiIortRC {
+ ACPI_IORT_NODE_HEADER_DEF
+ AcpiIortMemoryAccess memory_properties;
+ uint32_t ats_attribute;
+ uint32_t pci_segment_number;
+ AcpiIortIdMapping id_mapping_array[0];
+} QEMU_PACKED;
+typedef struct AcpiIortRC AcpiIortRC;
+
#endif
diff --git a/include/hw/boards.h b/include/hw/boards.h
index e46a744bcd..a51da9c440 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -86,6 +86,12 @@ typedef struct {
* Returns a @HotpluggableCPUList, which describes CPUs objects which
* could be added with -device/device_add.
* Caller is responsible for freeing returned list.
+ * @minimum_page_bits:
+ * If non-zero, the board promises never to create a CPU with a page size
+ * smaller than this, so QEMU can use a more efficient larger page
+ * size than the target architecture's minimum. (Attempting to create
+ * such a CPU will fail.) Note that changing this is a migration
+ * compatibility break for the machine.
*/
struct MachineClass {
/*< private >*/
@@ -124,6 +130,7 @@ struct MachineClass {
ram_addr_t default_ram_size;
bool option_rom_has_mr;
bool rom_file_has_mr;
+ int minimum_page_bits;
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
DeviceState *dev);
diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h
index 26c7fdcd75..48cccbdb51 100644
--- a/include/hw/ptimer.h
+++ b/include/hw/ptimer.h
@@ -35,6 +35,26 @@
*/
#define PTIMER_POLICY_DEFAULT 0
+/* Periodic timer counter stays with "0" for a one period before wrapping
+ * around. */
+#define PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD (1 << 0)
+
+/* Running periodic timer that has counter = limit = 0 would continuously
+ * re-trigger every period. */
+#define PTIMER_POLICY_CONTINUOUS_TRIGGER (1 << 1)
+
+/* Starting to run with/setting counter to "0" won't trigger immediately,
+ * but after a one period for both oneshot and periodic modes. */
+#define PTIMER_POLICY_NO_IMMEDIATE_TRIGGER (1 << 2)
+
+/* Starting to run with/setting counter to "0" won't re-load counter
+ * immediately, but after a one period. */
+#define PTIMER_POLICY_NO_IMMEDIATE_RELOAD (1 << 3)
+
+/* Make counter value of the running timer represent the actual value and
+ * not the one less. */
+#define PTIMER_POLICY_NO_COUNTER_ROUND_DOWN (1 << 4)
+
/* ptimer.c */
typedef struct ptimer_state ptimer_state;
typedef void (*ptimer_cb)(void *opaque);
diff --git a/include/hw/timer/arm_mptimer.h b/include/hw/timer/arm_mptimer.h
index b34cba00ce..c46d8d2309 100644
--- a/include/hw/timer/arm_mptimer.h
+++ b/include/hw/timer/arm_mptimer.h
@@ -27,12 +27,9 @@
/* State of a single timer or watchdog block */
typedef struct {
- uint32_t count;
- uint32_t load;
uint32_t control;
uint32_t status;
- int64_t tick;
- QEMUTimer *timer;
+ struct ptimer_state *timer;
qemu_irq irq;
MemoryRegion iomem;
} TimerBlock;
diff --git a/include/qemu-common.h b/include/qemu-common.h
index 9e8b0bd991..7e6e4feb4b 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -82,6 +82,18 @@ bool tcg_enabled(void);
void cpu_exec_init_all(void);
/**
+ * set_preferred_target_page_bits:
+ * @bits: number of bits needed to represent an address within the page
+ *
+ * Set the preferred target page size (the actual target page
+ * size may be smaller than any given CPU's preference).
+ * Returns true on success, false on failure (which can only happen
+ * if this is called after the system has already finalized its
+ * choice of page size and the requested page size is smaller than that).
+ */
+bool set_preferred_target_page_bits(int bits);
+
+/**
* Sends a (part of) iovec down a socket, yielding when the socket is full, or
* Receives data into a (part of) iovec from a socket,
* yielding when there is no data in the socket.
diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h
index 8ab721e5aa..eb464759d5 100644
--- a/include/qemu/hbitmap.h
+++ b/include/qemu/hbitmap.h
@@ -146,6 +146,85 @@ void hbitmap_reset_all(HBitmap *hb);
bool hbitmap_get(const HBitmap *hb, uint64_t item);
/**
+ * hbitmap_serialization_granularity:
+ * @hb: HBitmap to operate on.
+ *
+ * Granularity of serialization chunks, used by other serialization functions.
+ * For every chunk:
+ * 1. Chunk start should be aligned to this granularity.
+ * 2. Chunk size should be aligned too, except for last chunk (for which
+ * start + count == hb->size)
+ */
+uint64_t hbitmap_serialization_granularity(const HBitmap *hb);
+
+/**
+ * hbitmap_serialization_size:
+ * @hb: HBitmap to operate on.
+ * @start: Starting bit
+ * @count: Number of bits
+ *
+ * Return number of bytes hbitmap_(de)serialize_part needs
+ */
+uint64_t hbitmap_serialization_size(const HBitmap *hb,
+ uint64_t start, uint64_t count);
+
+/**
+ * hbitmap_serialize_part
+ * @hb: HBitmap to operate on.
+ * @buf: Buffer to store serialized bitmap.
+ * @start: First bit to store.
+ * @count: Number of bits to store.
+ *
+ * Stores HBitmap data corresponding to given region. The format of saved data
+ * is linear sequence of bits, so it can be used by hbitmap_deserialize_part
+ * independently of endianness and size of HBitmap level array elements
+ */
+void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf,
+ uint64_t start, uint64_t count);
+
+/**
+ * hbitmap_deserialize_part
+ * @hb: HBitmap to operate on.
+ * @buf: Buffer to restore bitmap data from.
+ * @start: First bit to restore.
+ * @count: Number of bits to restore.
+ * @finish: Whether to call hbitmap_deserialize_finish automatically.
+ *
+ * Restores HBitmap data corresponding to given region. The format is the same
+ * as for hbitmap_serialize_part.
+ *
+ * If @finish is false, caller must call hbitmap_serialize_finish before using
+ * the bitmap.
+ */
+void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf,
+ uint64_t start, uint64_t count,
+ bool finish);
+
+/**
+ * hbitmap_deserialize_zeroes
+ * @hb: HBitmap to operate on.
+ * @start: First bit to restore.
+ * @count: Number of bits to restore.
+ * @finish: Whether to call hbitmap_deserialize_finish automatically.
+ *
+ * Fills the bitmap with zeroes.
+ *
+ * If @finish is false, caller must call hbitmap_serialize_finish before using
+ * the bitmap.
+ */
+void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count,
+ bool finish);
+
+/**
+ * hbitmap_deserialize_finish
+ * @hb: HBitmap to operate on.
+ *
+ * Repair HBitmap after calling hbitmap_deserialize_data. Actually, all HBitmap
+ * layers are restored here.
+ */
+void hbitmap_deserialize_finish(HBitmap *hb);
+
+/**
* hbitmap_free:
* @hb: HBitmap to operate on.
*
@@ -178,6 +257,27 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first);
*/
unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi);
+/* hbitmap_create_meta:
+ * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap.
+ * The caller owns the created bitmap and must call hbitmap_free_meta(hb) to
+ * free it.
+ *
+ * Currently, we only guarantee that if a bit in the hbitmap is changed it
+ * will be reflected in the meta bitmap, but we do not yet guarantee the
+ * opposite.
+ *
+ * @hb: The HBitmap to operate on.
+ * @chunk_size: How many bits in @hb does one bit in the meta track.
+ */
+HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size);
+
+/* hbitmap_free_meta:
+ * Free the meta bitmap of @hb.
+ *
+ * @hb: The HBitmap whose meta bitmap should be freed.
+ */
+void hbitmap_free_meta(HBitmap *hb);
+
/**
* hbitmap_iter_next:
* @hbi: HBitmapIter to operate on.
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index b113fcf156..1b8c30a7a0 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -11,6 +11,7 @@ typedef struct AioContext AioContext;
typedef struct AllwinnerAHCIState AllwinnerAHCIState;
typedef struct AudioState AudioState;
typedef struct BdrvDirtyBitmap BdrvDirtyBitmap;
+typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter;
typedef struct BlockBackend BlockBackend;
typedef struct BlockBackendRootState BlockBackendRootState;
typedef struct BlockDriverState BlockDriverState;
diff --git a/linux-user/main.c b/linux-user/main.c
index c6f2e20c09..54970bc4d9 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -806,6 +806,9 @@ void cpu_loop(CPUARMState *env)
}
}
break;
+ case EXCP_SEMIHOST:
+ env->regs[0] = do_arm_semihosting(env);
+ break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
diff --git a/migration/ram.c b/migration/ram.c
index bc6154fe34..d032d389c4 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -69,7 +69,7 @@ static uint64_t bitmap_sync_count;
/* 0x80 is reserved in migration.h start with 0x100 next */
#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100
-static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE];
+static uint8_t *ZERO_TARGET_PAGE;
static inline bool is_zero_range(uint8_t *p, uint64_t size)
{
@@ -1431,6 +1431,7 @@ static void ram_migration_cleanup(void *opaque)
cache_fini(XBZRLE.cache);
g_free(XBZRLE.encoded_buf);
g_free(XBZRLE.current_buf);
+ g_free(ZERO_TARGET_PAGE);
XBZRLE.cache = NULL;
XBZRLE.encoded_buf = NULL;
XBZRLE.current_buf = NULL;
@@ -1889,6 +1890,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
if (migrate_use_xbzrle()) {
XBZRLE_cache_lock();
+ ZERO_TARGET_PAGE = g_malloc0(TARGET_PAGE_SIZE);
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
TARGET_PAGE_SIZE,
TARGET_PAGE_SIZE);
diff --git a/migration/savevm.c b/migration/savevm.c
index a831ec2d67..cfcbbd00d3 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -265,6 +265,7 @@ typedef struct SaveState {
bool skip_configuration;
uint32_t len;
const char *name;
+ uint32_t target_page_bits;
} SaveState;
static SaveState savevm_state = {
@@ -286,6 +287,19 @@ static void configuration_pre_save(void *opaque)
state->len = strlen(current_name);
state->name = current_name;
+ state->target_page_bits = TARGET_PAGE_BITS;
+}
+
+static int configuration_pre_load(void *opaque)
+{
+ SaveState *state = opaque;
+
+ /* If there is no target-page-bits subsection it means the source
+ * predates the variable-target-page-bits support and is using the
+ * minimum possible value for this CPU.
+ */
+ state->target_page_bits = TARGET_PAGE_BITS_MIN;
+ return 0;
}
static int configuration_post_load(void *opaque, int version_id)
@@ -298,12 +312,43 @@ static int configuration_post_load(void *opaque, int version_id)
(int) state->len, state->name, current_name);
return -EINVAL;
}
+
+ if (state->target_page_bits != TARGET_PAGE_BITS) {
+ error_report("Received TARGET_PAGE_BITS is %d but local is %d",
+ state->target_page_bits, TARGET_PAGE_BITS);
+ return -EINVAL;
+ }
+
return 0;
}
+/* The target-page-bits subsection is present only if the
+ * target page size is not the same as the default (ie the
+ * minimum page size for a variable-page-size guest CPU).
+ * If it is present then it contains the actual target page
+ * bits for the machine, and migration will fail if the
+ * two ends don't agree about it.
+ */
+static bool vmstate_target_page_bits_needed(void *opaque)
+{
+ return TARGET_PAGE_BITS > TARGET_PAGE_BITS_MIN;
+}
+
+static const VMStateDescription vmstate_target_page_bits = {
+ .name = "configuration/target-page-bits",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = vmstate_target_page_bits_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(target_page_bits, SaveState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_configuration = {
.name = "configuration",
.version_id = 1,
+ .pre_load = configuration_pre_load,
.post_load = configuration_post_load,
.pre_save = configuration_pre_save,
.fields = (VMStateField[]) {
@@ -311,6 +356,10 @@ static const VMStateDescription vmstate_configuration = {
VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, 0, len),
VMSTATE_END_OF_LIST()
},
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_target_page_bits,
+ NULL
+ }
};
static void dump_vmstate_vmsd(FILE *out_file,
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 1b7aa1befd..97b120532a 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2197,7 +2197,8 @@
# @mode: the replication mode
#
# @top-id: #optional In secondary mode, node name or device ID of the root
-# node who owns the replication node chain. Ignored in primary mode.
+# node who owns the replication node chain. Must not be given in
+# primary mode.
#
# Since: 2.8
##
@@ -2312,11 +2313,11 @@
# block drivers among other things. Stay away from it unless you want
# to help with its development.
#
-# @options: block device options for the new device
+# For the arguments, see the documentation of BlockdevOptions.
#
# Since: 1.7
##
-{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
+{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true }
##
# @x-blockdev-del:
diff --git a/qemu-img.c b/qemu-img.c
index 67e851248a..ab395a9b1a 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -2956,6 +2956,7 @@ static int img_rebase(int argc, char **argv)
error_reportf_err(local_err,
"Could not open old backing file '%s': ",
backing_name);
+ ret = -1;
goto out;
}
@@ -2973,6 +2974,7 @@ static int img_rebase(int argc, char **argv)
error_reportf_err(local_err,
"Could not open new backing file '%s': ",
out_baseimg);
+ ret = -1;
goto out;
}
}
diff --git a/qemu-nbd.c b/qemu-nbd.c
index cca4a983b7..b757dc7621 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -48,6 +48,7 @@
#define QEMU_NBD_OPT_OBJECT 260
#define QEMU_NBD_OPT_TLSCREDS 261
#define QEMU_NBD_OPT_IMAGE_OPTS 262
+#define QEMU_NBD_OPT_FORK 263
#define MBR_SIZE 512
@@ -92,6 +93,8 @@ static void usage(const char *name)
" passwords and/or encryption keys\n"
" -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
" specify tracing options\n"
+" --fork fork off the server process and exit the parent\n"
+" once the server is running\n"
#ifdef __linux__
"Kernel NBD client support:\n"
" -c, --connect=DEV connect FILE to the local NBD device DEV\n"
@@ -503,6 +506,7 @@ int main(int argc, char **argv)
{ "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS },
{ "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS },
{ "trace", required_argument, NULL, 'T' },
+ { "fork", no_argument, NULL, QEMU_NBD_OPT_FORK },
{ NULL, 0, NULL, 0 }
};
int ch;
@@ -524,6 +528,8 @@ int main(int argc, char **argv)
bool imageOpts = false;
bool writethrough = true;
char *trace_file = NULL;
+ bool fork_process = false;
+ int old_stderr = -1;
/* The client thread uses SIGTERM to interrupt the server. A signal
* handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@@ -715,6 +721,9 @@ int main(int argc, char **argv)
g_free(trace_file);
trace_file = trace_opt_parse(optarg);
break;
+ case QEMU_NBD_OPT_FORK:
+ fork_process = true;
+ break;
}
}
@@ -774,7 +783,7 @@ int main(int argc, char **argv)
return 0;
}
- if (device && !verbose) {
+ if ((device && !verbose) || fork_process) {
int stderr_fd[2];
pid_t pid;
int ret;
@@ -797,6 +806,7 @@ int main(int argc, char **argv)
ret = qemu_daemon(1, 0);
/* Temporarily redirect stderr to the parent's pipe... */
+ old_stderr = dup(STDERR_FILENO);
dup2(stderr_fd[1], STDERR_FILENO);
if (ret < 0) {
error_report("Failed to daemonize: %s", strerror(errno));
@@ -960,6 +970,11 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
+ if (fork_process) {
+ dup2(old_stderr, STDERR_FILENO);
+ close(old_stderr);
+ }
+
state = RUNNING;
do {
main_loop_wait(false);
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
index 91ebf04b5b..b7a9c6d02f 100644
--- a/qemu-nbd.texi
+++ b/qemu-nbd.texi
@@ -86,6 +86,8 @@ the new style NBD protocol negotiation
Enable mandatory TLS encryption for the server by setting the ID
of the TLS credentials object previously created with the --object
option.
+@item --fork
+Fork off the server process and exit the parent once the server is running.
@item -v, --verbose
Display extra debugging information
@item -h, --help
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index fb272a86c1..2439ca57d0 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -563,6 +563,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
ARMCPU *cpu = ARM_CPU(dev);
ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev);
CPUARMState *env = &cpu->env;
+ int pagebits;
Error *local_err = NULL;
cpu_exec_realizefn(cs, &local_err);
@@ -625,6 +626,29 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
set_feature(env, ARM_FEATURE_THUMB_DSP);
}
+ if (arm_feature(env, ARM_FEATURE_V7) &&
+ !arm_feature(env, ARM_FEATURE_M) &&
+ !arm_feature(env, ARM_FEATURE_MPU)) {
+ /* v7VMSA drops support for the old ARMv5 tiny pages, so we
+ * can use 4K pages.
+ */
+ pagebits = 12;
+ } else {
+ /* For CPUs which might have tiny 1K pages, or which have an
+ * MPU and might have small region sizes, stick with 1K pages.
+ */
+ pagebits = 10;
+ }
+ if (!set_preferred_target_page_bits(pagebits)) {
+ /* This can only ever happen for hotplugging a CPU, or if
+ * the board code incorrectly creates a CPU which it has
+ * promised via minimum_page_size that it will not.
+ */
+ error_setg(errp, "This CPU requires a smaller page size than the "
+ "system is using");
+ return;
+ }
+
/* This cpu-id-to-MPIDR affinity is used only for TCG; KVM will override it.
* We don't support setting cluster ID ([16..23]) (known as Aff2
* in later ARM ARM versions), or any of the higher affinity level fields,
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 2218c00dad..9d75227e04 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -52,7 +52,7 @@
#define EXCP_SMC 13 /* Secure Monitor Call */
#define EXCP_VIRQ 14
#define EXCP_VFIQ 15
-#define EXCP_SEMIHOST 16 /* semihosting call (A64 only) */
+#define EXCP_SEMIHOST 16 /* semihosting call */
#define ARMV7M_EXCP_RESET 1
#define ARMV7M_EXCP_NMI 2
@@ -1766,10 +1766,11 @@ bool write_cpustate_to_list(ARMCPU *cpu);
#if defined(CONFIG_USER_ONLY)
#define TARGET_PAGE_BITS 12
#else
-/* The ARM MMU allows 1k pages. */
-/* ??? Linux doesn't actually use these, and they're deprecated in recent
- architecture revisions. Maybe a configure option to disable them. */
-#define TARGET_PAGE_BITS 10
+/* ARMv7 and later CPUs have 4K pages minimum, but ARMv5 and v6
+ * have to support 1K tiny pages.
+ */
+#define TARGET_PAGE_BITS_VARY
+#define TARGET_PAGE_BITS_MIN 10
#endif
#if defined(TARGET_AARCH64)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index cb83ee2008..25b15dc100 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -6573,12 +6573,19 @@ static inline bool check_for_semihosting(CPUState *cs)
/* Only intercept calls from privileged modes, to provide some
* semblance of security.
*/
- if (!semihosting_enabled() ||
- ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)) {
+ if (cs->exception_index != EXCP_SEMIHOST &&
+ (!semihosting_enabled() ||
+ ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR))) {
return false;
}
switch (cs->exception_index) {
+ case EXCP_SEMIHOST:
+ /* This is always a semihosting call; the "is this usermode"
+ * and "is semihosting enabled" checks have been done at
+ * translate time.
+ */
+ break;
case EXCP_SWI:
/* Check for semihosting interrupt. */
if (env->thumb) {
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 164b52a0d0..ef62f8b0c4 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -28,6 +28,7 @@
#include "qemu/log.h"
#include "qemu/bitops.h"
#include "arm_ldst.h"
+#include "exec/semihost.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
@@ -1144,6 +1145,33 @@ static inline void gen_lookup_tb(DisasContext *s)
s->is_jmp = DISAS_JUMP;
}
+static inline void gen_hlt(DisasContext *s, int imm)
+{
+ /* HLT. This has two purposes.
+ * Architecturally, it is an external halting debug instruction.
+ * Since QEMU doesn't implement external debug, we treat this as
+ * it is required for halting debug disabled: it will UNDEF.
+ * Secondly, "HLT 0x3C" is a T32 semihosting trap instruction,
+ * and "HLT 0xF000" is an A32 semihosting syscall. These traps
+ * must trigger semihosting even for ARMv7 and earlier, where
+ * HLT was an undefined encoding.
+ * In system mode, we don't allow userspace access to
+ * semihosting, to provide some semblance of security
+ * (and for consistency with our 32-bit semihosting).
+ */
+ if (semihosting_enabled() &&
+#ifndef CONFIG_USER_ONLY
+ s->current_el != 0 &&
+#endif
+ (imm == (s->thumb ? 0x3c : 0xf000))) {
+ gen_exception_internal_insn(s, 0, EXCP_SEMIHOST);
+ return;
+ }
+
+ gen_exception_insn(s, s->thumb ? 2 : 4, EXCP_UDEF, syn_uncategorized(),
+ default_exception_el(s));
+}
+
static inline void gen_add_data_offset(DisasContext *s, unsigned int insn,
TCGv_i32 var)
{
@@ -8395,6 +8423,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
{
int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4);
switch (op1) {
+ case 0:
+ /* HLT */
+ gen_hlt(s, imm16);
+ break;
case 1:
/* bkpt */
ARCH(5);
@@ -8419,7 +8451,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
gen_smc(s);
break;
default:
- goto illegal_op;
+ g_assert_not_reached();
}
break;
}
@@ -11451,19 +11483,33 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
break;
}
- case 0xa: /* rev */
+ case 0xa: /* rev, and hlt */
+ {
+ int op1 = extract32(insn, 6, 2);
+
+ if (op1 == 2) {
+ /* HLT */
+ int imm6 = extract32(insn, 0, 6);
+
+ gen_hlt(s, imm6);
+ break;
+ }
+
+ /* Otherwise this is rev */
ARCH(6);
rn = (insn >> 3) & 0x7;
rd = insn & 0x7;
tmp = load_reg(s, rn);
- switch ((insn >> 6) & 3) {
+ switch (op1) {
case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
case 1: gen_rev16(tmp); break;
case 3: gen_revsh(tmp); break;
- default: goto illegal_op;
+ default:
+ g_assert_not_reached();
}
store_reg(s, rd, tmp);
break;
+ }
case 6:
switch ((insn >> 5) & 7) {
diff --git a/tests/Makefile.include b/tests/Makefile.include
index e65e9f7819..91cc308eb6 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -303,6 +303,8 @@ check-qtest-arm-y += tests/m25p80-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-arm-y += tests/test-arm-mptimer$(EXESUF)
+gcov-files-arm-y += hw/timer/arm_mptimer.c
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
@@ -684,6 +686,7 @@ tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-
tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y)
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
+tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
tests/migration/stress$(EXESUF): tests/migration/stress.o
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
diff --git a/tests/ptimer-test-stubs.c b/tests/ptimer-test-stubs.c
index e028a81a29..21d4ebb0fe 100644
--- a/tests/ptimer-test-stubs.c
+++ b/tests/ptimer-test-stubs.c
@@ -1,7 +1,7 @@
/*
* Stubs for the ptimer-test
*
- * Author: Dmitry Osipenko <digetx@gmail.com>
+ * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c
index 7b0ddf64e0..b36a476483 100644
--- a/tests/ptimer-test.c
+++ b/tests/ptimer-test.c
@@ -1,7 +1,7 @@
/*
* QTest testcase for the ptimer
*
- * Author: Dmitry Osipenko <digetx@gmail.com>
+ * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -99,6 +99,7 @@ static void check_oneshot(gconstpointer arg)
const uint8_t *policy = arg;
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
ptimer_state *ptimer = ptimer_init(bh, *policy);
+ bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
@@ -106,34 +107,46 @@ static void check_oneshot(gconstpointer arg)
ptimer_set_count(ptimer, 10);
ptimer_run(ptimer, 1);
- qemu_clock_step(2000000 * 2 + 100000);
+ qemu_clock_step(2000000 * 2 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
g_assert_false(triggered);
ptimer_stop(ptimer);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
g_assert_false(triggered);
qemu_clock_step(2000000 * 11);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
g_assert_false(triggered);
ptimer_run(ptimer, 1);
- qemu_clock_step(2000000 * 7 + 100000);
+ qemu_clock_step(2000000 * 7 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
- g_assert_true(triggered);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
- triggered = false;
+ if (no_round_down) {
+ g_assert_false(triggered);
+ } else {
+ g_assert_true(triggered);
+
+ triggered = false;
+ }
qemu_clock_step(2000000);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
- g_assert_false(triggered);
+
+ if (no_round_down) {
+ g_assert_true(triggered);
+
+ triggered = false;
+ } else {
+ g_assert_false(triggered);
+ }
qemu_clock_step(4000000);
@@ -142,30 +155,30 @@ static void check_oneshot(gconstpointer arg)
ptimer_set_count(ptimer, 10);
- qemu_clock_step(20000000 + 100000);
+ qemu_clock_step(20000000 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10);
g_assert_false(triggered);
ptimer_set_limit(ptimer, 9, 1);
- qemu_clock_step(20000000 + 100000);
+ qemu_clock_step(20000000 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
g_assert_false(triggered);
ptimer_run(ptimer, 1);
- qemu_clock_step(2000000 + 100000);
+ qemu_clock_step(2000000 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
g_assert_false(triggered);
ptimer_set_count(ptimer, 20);
- qemu_clock_step(2000000 * 19 + 100000);
+ qemu_clock_step(2000000 * 19 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
g_assert_false(triggered);
qemu_clock_step(2000000);
@@ -177,7 +190,7 @@ static void check_oneshot(gconstpointer arg)
triggered = false;
- qemu_clock_step(2000000 * 12 + 100000);
+ qemu_clock_step(2000000 * 12 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
g_assert_false(triggered);
@@ -188,6 +201,10 @@ static void check_periodic(gconstpointer arg)
const uint8_t *policy = arg;
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
ptimer_state *ptimer = ptimer_init(bh, *policy);
+ bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD);
+ bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
+ bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD);
+ bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
@@ -195,28 +212,70 @@ static void check_periodic(gconstpointer arg)
ptimer_set_limit(ptimer, 10, 1);
ptimer_run(ptimer, 0);
- qemu_clock_step(2000000 * 10 + 100000);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10);
+ g_assert_false(triggered);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
+ qemu_clock_step(1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9);
+ g_assert_false(triggered);
+
+ qemu_clock_step(2000000 * 10 - 1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, wrap_policy ? 0 : 10);
+ g_assert_true(triggered);
+
+ qemu_clock_step(1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ wrap_policy ? 0 : (no_round_down ? 10 : 9));
g_assert_true(triggered);
triggered = false;
qemu_clock_step(2000000);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
ptimer_set_count(ptimer, 20);
- qemu_clock_step(2000000 * 11 + 100000);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, 20);
+ g_assert_false(triggered);
+
+ qemu_clock_step(1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 20 : 19);
+ g_assert_false(triggered);
+
+ qemu_clock_step(2000000 * 11 + 1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 9 : 8);
g_assert_false(triggered);
qemu_clock_step(2000000 * 10);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
+ g_assert_true(triggered);
+
+ triggered = false;
+
+ ptimer_set_count(ptimer, 3);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
+ g_assert_false(triggered);
+
+ qemu_clock_step(1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2);
+ g_assert_false(triggered);
+
+ qemu_clock_step(2000000 * 4);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
g_assert_true(triggered);
ptimer_stop(ptimer);
@@ -224,50 +283,82 @@ static void check_periodic(gconstpointer arg)
qemu_clock_step(2000000);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
ptimer_set_count(ptimer, 3);
ptimer_run(ptimer, 0);
- qemu_clock_step(2000000 * 3 + 100000);
+ qemu_clock_step(2000000 * 3 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ wrap_policy ? 0 : (no_round_down ? 10 : 9));
g_assert_true(triggered);
triggered = false;
qemu_clock_step(2000000);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
ptimer_set_count(ptimer, 0);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10);
- g_assert_true(triggered);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ no_immediate_reload ? 0 : 10);
+
+ if (no_immediate_trigger) {
+ g_assert_false(triggered);
+ } else {
+ g_assert_true(triggered);
+ }
triggered = false;
- qemu_clock_step(2000000 * 12 + 100000);
+ qemu_clock_step(1);
+
+ if (no_immediate_reload) {
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+ g_assert_false(triggered);
+
+ qemu_clock_step(2000000);
+
+ if (no_immediate_trigger) {
+ g_assert_true(triggered);
+ } else {
+ g_assert_false(triggered);
+ }
+
+ triggered = false;
+ }
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9);
+ g_assert_false(triggered);
+
+ qemu_clock_step(2000000 * 12);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0));
g_assert_true(triggered);
ptimer_stop(ptimer);
triggered = false;
- qemu_clock_step(2000000 * 12 + 100000);
+ qemu_clock_step(2000000 * 10);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
ptimer_run(ptimer, 0);
ptimer_set_period(ptimer, 0);
- qemu_clock_step(2000000 + 100000);
+ qemu_clock_step(2000000 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
}
@@ -276,6 +367,8 @@ static void check_on_the_fly_mode_change(gconstpointer arg)
const uint8_t *policy = arg;
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
ptimer_state *ptimer = ptimer_init(bh, *policy);
+ bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD);
+ bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
@@ -283,16 +376,20 @@ static void check_on_the_fly_mode_change(gconstpointer arg)
ptimer_set_limit(ptimer, 10, 1);
ptimer_run(ptimer, 1);
- qemu_clock_step(2000000 * 9 + 100000);
+ qemu_clock_step(2000000 * 9 + 1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
+ g_assert_false(triggered);
ptimer_run(ptimer, 0);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
g_assert_false(triggered);
qemu_clock_step(2000000);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ wrap_policy ? 0 : (no_round_down ? 10 : 9));
g_assert_true(triggered);
triggered = false;
@@ -301,7 +398,8 @@ static void check_on_the_fly_mode_change(gconstpointer arg)
ptimer_run(ptimer, 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ (no_round_down ? 1 : 0) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
qemu_clock_step(2000000 * 3);
@@ -315,6 +413,7 @@ static void check_on_the_fly_period_change(gconstpointer arg)
const uint8_t *policy = arg;
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
ptimer_state *ptimer = ptimer_init(bh, *policy);
+ bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
@@ -322,17 +421,17 @@ static void check_on_the_fly_period_change(gconstpointer arg)
ptimer_set_limit(ptimer, 8, 1);
ptimer_run(ptimer, 1);
- qemu_clock_step(2000000 * 4 + 100000);
+ qemu_clock_step(2000000 * 4 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
g_assert_false(triggered);
ptimer_set_period(ptimer, 4000000);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
- qemu_clock_step(4000000 * 2 + 100000);
+ qemu_clock_step(4000000 * 2 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0);
g_assert_false(triggered);
qemu_clock_step(4000000 * 2);
@@ -346,6 +445,7 @@ static void check_on_the_fly_freq_change(gconstpointer arg)
const uint8_t *policy = arg;
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
ptimer_state *ptimer = ptimer_init(bh, *policy);
+ bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
@@ -353,17 +453,17 @@ static void check_on_the_fly_freq_change(gconstpointer arg)
ptimer_set_limit(ptimer, 8, 1);
ptimer_run(ptimer, 1);
- qemu_clock_step(2000000 * 4 + 100000);
+ qemu_clock_step(2000000 * 4 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
g_assert_false(triggered);
ptimer_set_freq(ptimer, 250);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
- qemu_clock_step(2000000 * 4 + 100000);
+ qemu_clock_step(2000000 * 4 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0);
g_assert_false(triggered);
qemu_clock_step(2000000 * 4);
@@ -394,25 +494,53 @@ static void check_run_with_delta_0(gconstpointer arg)
const uint8_t *policy = arg;
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
ptimer_state *ptimer = ptimer_init(bh, *policy);
+ bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD);
+ bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
+ bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD);
+ bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
ptimer_set_period(ptimer, 2000000);
ptimer_set_limit(ptimer, 99, 0);
ptimer_run(ptimer, 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99);
- g_assert_true(triggered);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ no_immediate_reload ? 0 : 99);
+
+ if (no_immediate_trigger) {
+ g_assert_false(triggered);
+ } else {
+ g_assert_true(triggered);
+ }
triggered = false;
- qemu_clock_step(2000000 + 100000);
+ if (no_immediate_trigger || no_immediate_reload) {
+ qemu_clock_step(2000000 + 1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ no_immediate_reload ? 0 : (no_round_down ? 98 : 97));
+
+ if (no_immediate_trigger && no_immediate_reload) {
+ g_assert_true(triggered);
+
+ triggered = false;
+ } else {
+ g_assert_false(triggered);
+ }
+
+ ptimer_set_count(ptimer, 99);
+ ptimer_run(ptimer, 1);
+ }
+
+ qemu_clock_step(2000000 + 1);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 97);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97);
g_assert_false(triggered);
qemu_clock_step(2000000 * 97);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
g_assert_false(triggered);
qemu_clock_step(2000000 * 2);
@@ -424,19 +552,42 @@ static void check_run_with_delta_0(gconstpointer arg)
ptimer_set_count(ptimer, 0);
ptimer_run(ptimer, 0);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99);
- g_assert_true(triggered);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ no_immediate_reload ? 0 : 99);
+
+ if (no_immediate_trigger) {
+ g_assert_false(triggered);
+ } else {
+ g_assert_true(triggered);
+ }
+
+ triggered = false;
+
+ qemu_clock_step(1);
+
+ if (no_immediate_reload) {
+ qemu_clock_step(2000000);
+ }
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 99 : 98);
+
+ if (no_immediate_reload && no_immediate_trigger) {
+ g_assert_true(triggered);
+ } else {
+ g_assert_false(triggered);
+ }
triggered = false;
- qemu_clock_step(2000000 + 100000);
+ qemu_clock_step(2000000);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 97);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97);
g_assert_false(triggered);
qemu_clock_step(2000000 * 98);
- g_assert_cmpuint(ptimer_get_count(ptimer), ==, 98);
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==,
+ wrap_policy ? 0 : (no_round_down ? 99 : 98));
g_assert_true(triggered);
ptimer_stop(ptimer);
@@ -447,6 +598,8 @@ static void check_periodic_with_load_0(gconstpointer arg)
const uint8_t *policy = arg;
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
ptimer_state *ptimer = ptimer_init(bh, *policy);
+ bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER);
+ bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
triggered = false;
@@ -454,14 +607,46 @@ static void check_periodic_with_load_0(gconstpointer arg)
ptimer_run(ptimer, 0);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+
+ if (no_immediate_trigger) {
+ g_assert_false(triggered);
+ } else {
+ g_assert_true(triggered);
+ }
+
+ triggered = false;
+
+ qemu_clock_step(2000000 + 1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
+
+ if (continuous_trigger || no_immediate_trigger) {
+ g_assert_true(triggered);
+ } else {
+ g_assert_false(triggered);
+ }
+
+ triggered = false;
+
+ ptimer_set_count(ptimer, 10);
+ ptimer_run(ptimer, 0);
+
+ qemu_clock_step(2000000 * 10 + 1);
+
+ g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
g_assert_true(triggered);
triggered = false;
- qemu_clock_step(2000000 + 100000);
+ qemu_clock_step(2000000 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
- g_assert_false(triggered);
+
+ if (continuous_trigger) {
+ g_assert_true(triggered);
+ } else {
+ g_assert_false(triggered);
+ }
ptimer_stop(ptimer);
}
@@ -471,6 +656,7 @@ static void check_oneshot_with_load_0(gconstpointer arg)
const uint8_t *policy = arg;
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
ptimer_state *ptimer = ptimer_init(bh, *policy);
+ bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
triggered = false;
@@ -478,26 +664,30 @@ static void check_oneshot_with_load_0(gconstpointer arg)
ptimer_run(ptimer, 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
- g_assert_true(triggered);
+
+ if (no_immediate_trigger) {
+ g_assert_false(triggered);
+ } else {
+ g_assert_true(triggered);
+ }
triggered = false;
- qemu_clock_step(2000000 + 100000);
+ qemu_clock_step(2000000 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
- g_assert_false(triggered);
- triggered = false;
-
- qemu_clock_step(2000000 + 100000);
-
- g_assert_false(triggered);
+ if (no_immediate_trigger) {
+ g_assert_true(triggered);
+ } else {
+ g_assert_false(triggered);
+ }
}
static void add_ptimer_tests(uint8_t policy)
{
uint8_t *ppolicy = g_malloc(1);
- char *policy_name = g_malloc(64);
+ char *policy_name = g_malloc0(256);
*ppolicy = policy;
@@ -505,6 +695,26 @@ static void add_ptimer_tests(uint8_t policy)
g_sprintf(policy_name, "default");
}
+ if (policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
+ g_strlcat(policy_name, "wrap_after_one_period,", 256);
+ }
+
+ if (policy & PTIMER_POLICY_CONTINUOUS_TRIGGER) {
+ g_strlcat(policy_name, "continuous_trigger,", 256);
+ }
+
+ if (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) {
+ g_strlcat(policy_name, "no_immediate_trigger,", 256);
+ }
+
+ if (policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD) {
+ g_strlcat(policy_name, "no_immediate_reload,", 256);
+ }
+
+ if (policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) {
+ g_strlcat(policy_name, "no_counter_rounddown,", 256);
+ }
+
g_test_add_data_func(
g_strdup_printf("/ptimer/set_count policy=%s", policy_name),
ppolicy, check_set_count);
@@ -550,6 +760,16 @@ static void add_ptimer_tests(uint8_t policy)
ppolicy, check_oneshot_with_load_0);
}
+static void add_all_ptimer_policies_comb_tests(void)
+{
+ int last_policy = PTIMER_POLICY_NO_COUNTER_ROUND_DOWN;
+ int policy = PTIMER_POLICY_DEFAULT;
+
+ for (; policy < (last_policy << 1); policy++) {
+ add_ptimer_tests(policy);
+ }
+}
+
int main(int argc, char **argv)
{
int i;
@@ -560,7 +780,7 @@ int main(int argc, char **argv)
main_loop_tlg.tl[i] = g_new0(QEMUTimerList, 1);
}
- add_ptimer_tests(PTIMER_POLICY_DEFAULT);
+ add_all_ptimer_policies_comb_tests();
qtest_allowed = true;
diff --git a/tests/ptimer-test.h b/tests/ptimer-test.h
index 98d9b8f347..09ac56da9e 100644
--- a/tests/ptimer-test.h
+++ b/tests/ptimer-test.h
@@ -1,7 +1,7 @@
/*
* QTest testcase for the ptimer
*
- * Author: Dmitry Osipenko <digetx@gmail.com>
+ * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index d1e1ad8bd2..30e628f0f7 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -194,10 +194,9 @@ class TestSingleBlockdev(TestSingleDrive):
def setUp(self):
TestSingleDrive.setUp(self)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
- args = {'options':
- {'driver': iotests.imgfmt,
- 'node-name': self.qmp_target,
- 'file': { 'filename': target_img, 'driver': 'file' } } }
+ args = {'driver': iotests.imgfmt,
+ 'node-name': self.qmp_target,
+ 'file': { 'filename': target_img, 'driver': 'file' } }
result = self.vm.qmp("blockdev-add", **args)
self.assert_qmp(result, 'return', {})
@@ -782,8 +781,8 @@ class TestRepairQuorum(iotests.QMPTestCase):
self.vm.launch()
#assemble the quorum block device from the individual files
- args = { "options" : { "driver": "quorum", "node-name": "quorum0",
- "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } }
+ args = { "driver": "quorum", "node-name": "quorum0",
+ "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] }
if self.has_quorum():
result = self.vm.qmp("blockdev-add", **args)
self.assert_qmp(result, 'return', {})
diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067
index a12125bd46..38d23fce6b 100755
--- a/tests/qemu-iotests/067
+++ b/tests/qemu-iotests/067
@@ -119,13 +119,11 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "node-name": "disk",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "driver": "$IMGFMT",
+ "node-name": "disk",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
}
diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071
index 6d0864cff6..48b495513f 100755
--- a/tests/qemu-iotests/071
+++ b/tests/qemu-iotests/071
@@ -107,25 +107,21 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "node-name": "drive0",
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "node-name": "drive0",
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "node-name": "drive0-debug",
- "file": {
- "driver": "blkdebug",
- "image": "drive0",
- "inject-error": [{
- "event": "l2_load"
- }]
- }
+ "driver": "$IMGFMT",
+ "node-name": "drive0-debug",
+ "file": {
+ "driver": "blkdebug",
+ "image": "drive0",
+ "inject-error": [{
+ "event": "l2_load"
+ }]
}
}
}
@@ -145,26 +141,22 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "node-name": "drive0",
- "driver": "$IMGFMT",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "node-name": "drive0",
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
}
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "blkverify",
- "node-name": "drive0-verify",
- "test": "drive0",
- "raw": {
- "driver": "file",
- "filename": "$TEST_IMG.base"
- }
+ "driver": "blkverify",
+ "node-name": "drive0-verify",
+ "test": "drive0",
+ "raw": {
+ "driver": "file",
+ "filename": "$TEST_IMG.base"
}
}
}
@@ -184,27 +176,23 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "node-name": "drive0",
- "driver": "file",
- "filename": "$TEST_IMG.base"
- }
+ "node-name": "drive0",
+ "driver": "file",
+ "filename": "$TEST_IMG.base"
}
}
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "blkverify",
- "node-name": "drive0-verify",
- "test": {
- "driver": "$IMGFMT",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG"
- }
- },
- "raw": "drive0"
- }
+ "driver": "blkverify",
+ "node-name": "drive0-verify",
+ "test": {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
+ }
+ },
+ "raw": "drive0"
}
}
{ "execute": "human-monitor-command",
@@ -223,30 +211,26 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "node-name": "drive0",
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "node-name": "drive0",
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "node-name": "drive0-debug",
- "file": {
- "driver": "blkdebug",
- "image": "drive0",
- "inject-error": [{
- "event": "read_aio",
- "state": 42
- }],
- "set-state": [{
- "event": "write_aio",
- "new_state": 42
- }]
- }
+ "driver": "$IMGFMT",
+ "node-name": "drive0-debug",
+ "file": {
+ "driver": "blkdebug",
+ "image": "drive0",
+ "inject-error": [{
+ "event": "read_aio",
+ "state": 42
+ }],
+ "set-state": [{
+ "event": "write_aio",
+ "new_state": 42
+ }]
}
}
}
diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081
index 0a809f3499..da3fb0984b 100755
--- a/tests/qemu-iotests/081
+++ b/tests/qemu-iotests/081
@@ -105,40 +105,36 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "node-name": "drive2",
- "driver": "$IMGFMT",
- "file": {
- "driver": "file",
- "filename": "$TEST_DIR/2.raw"
- }
+ "node-name": "drive2",
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_DIR/2.raw"
}
}
}
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "quorum",
- "node-name": "drive0-quorum",
- "vote-threshold": 2,
- "children": [
- {
- "driver": "$IMGFMT",
- "file": {
- "driver": "file",
- "filename": "$TEST_DIR/1.raw"
- }
- },
- "drive2",
- {
- "driver": "$IMGFMT",
- "file": {
- "driver": "file",
- "filename": "$TEST_DIR/3.raw"
- }
+ "driver": "quorum",
+ "node-name": "drive0-quorum",
+ "vote-threshold": 2,
+ "children": [
+ {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_DIR/1.raw"
}
- ]
- }
+ },
+ "drive2",
+ {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_DIR/3.raw"
+ }
+ }
+ ]
}
}
{ "execute": "human-monitor-command",
diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085
index aa77eca77d..c53e97f067 100755
--- a/tests/qemu-iotests/085
+++ b/tests/qemu-iotests/085
@@ -100,11 +100,10 @@ function add_snapshot_image()
_make_test_img -b "${base_image}" "$size"
mv "${TEST_IMG}" "${snapshot_file}"
cmd="{ 'execute': 'blockdev-add', 'arguments':
- { 'options':
- { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params}
- 'file':
- { 'driver': 'file', 'filename': '${snapshot_file}',
- 'node-name': 'file_${1}' } } } }"
+ { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params}
+ 'file':
+ { 'driver': 'file', 'filename': '${snapshot_file}',
+ 'node-name': 'file_${1}' } } }"
_send_qemu_cmd $h "${cmd}" "return"
}
diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087
index b1ac71f2b8..9de57ddf6d 100755
--- a/tests/qemu-iotests/087
+++ b/tests/qemu-iotests/087
@@ -61,12 +61,10 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
}
@@ -81,25 +79,21 @@ run_qemu -drive driver=$IMGFMT,id=disk,node-name=test-node,file="$TEST_IMG" <<EO
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "node-name": "disk",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "driver": "$IMGFMT",
+ "node-name": "disk",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
}
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "node-name": "test-node",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "driver": "$IMGFMT",
+ "node-name": "test-node",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
}
@@ -114,14 +108,12 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "node-name": "disk",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG",
- "aio": "native"
- }
+ "driver": "$IMGFMT",
+ "node-name": "disk",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG",
+ "aio": "native"
}
}
}
@@ -137,13 +129,11 @@ run_qemu -S <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "node-name": "disk",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "driver": "$IMGFMT",
+ "node-name": "disk",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
}
@@ -154,13 +144,11 @@ run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "driver": "$IMGFMT",
- "node-name": "disk",
- "file": {
- "driver": "file",
- "filename": "$TEST_IMG"
- }
+ "driver": "$IMGFMT",
+ "node-name": "disk",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
}
}
}
@@ -176,9 +164,7 @@ run_qemu -S <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": {
- "options": {
- "node-name": "disk"
- }
+ "node-name": "disk"
}
}
{ "execute": "quit" }
diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
index ffcb271b36..2ed393a548 100755
--- a/tests/qemu-iotests/093
+++ b/tests/qemu-iotests/093
@@ -53,7 +53,7 @@ class ThrottleTestCase(iotests.QMPTestCase):
result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
self.assert_qmp(result, 'return', {})
- def do_test_throttle(self, ndrives, seconds, params):
+ def do_test_throttle(self, ndrives, seconds, params, first_drive = 0):
def check_limit(limit, num):
# IO throttling algorithm is discrete, allow 10% error so the test
# is more robust
@@ -85,12 +85,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
# Send I/O requests to all drives
for i in range(rd_nr):
for drive in range(0, ndrives):
- self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" %
+ idx = first_drive + drive
+ self.vm.hmp_qemu_io("drive%d" % idx, "aio_read %d %d" %
(i * rq_size, rq_size))
for i in range(wr_nr):
for drive in range(0, ndrives):
- self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" %
+ idx = first_drive + drive
+ self.vm.hmp_qemu_io("drive%d" % idx, "aio_write %d %d" %
(i * rq_size, rq_size))
# We'll store the I/O stats for each drive in these arrays
@@ -105,15 +107,17 @@ class ThrottleTestCase(iotests.QMPTestCase):
# Read the stats before advancing the clock
for i in range(0, ndrives):
+ idx = first_drive + i
start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \
- start_wr_iops[i] = self.blockstats('drive%d' % i)
+ start_wr_iops[i] = self.blockstats('drive%d' % idx)
self.vm.qtest("clock_step %d" % ns)
# Read the stats after advancing the clock
for i in range(0, ndrives):
+ idx = first_drive + i
end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \
- end_wr_iops[i] = self.blockstats('drive%d' % i)
+ end_wr_iops[i] = self.blockstats('drive%d' % idx)
# Check that the I/O is within the limits and evenly distributed
for i in range(0, ndrives):
@@ -129,6 +133,7 @@ class ThrottleTestCase(iotests.QMPTestCase):
self.assertTrue(check_limit(params['iops_rd'], rd_iops))
self.assertTrue(check_limit(params['iops_wr'], wr_iops))
+ # Connect N drives to a VM and test I/O in all of them
def test_all(self):
params = {"bps": 4096,
"bps_rd": 4096,
@@ -146,6 +151,24 @@ class ThrottleTestCase(iotests.QMPTestCase):
self.configure_throttle(ndrives, limits)
self.do_test_throttle(ndrives, 5, limits)
+ # Connect N drives to a VM and test I/O in just one of them a time
+ def test_one(self):
+ params = {"bps": 4096,
+ "bps_rd": 4096,
+ "bps_wr": 4096,
+ "iops": 10,
+ "iops_rd": 10,
+ "iops_wr": 10,
+ }
+ # Repeat the test for each one of the drives
+ for drive in range(0, self.max_drives):
+ # Pick each out of all possible params and test
+ for tk in params:
+ limits = dict([(k, 0) for k in params])
+ limits[tk] = params[tk] * self.max_drives
+ self.configure_throttle(self.max_drives, limits)
+ self.do_test_throttle(1, 5, limits, drive)
+
def test_burst(self):
params = {"bps": 4096,
"bps_rd": 4096,
diff --git a/tests/qemu-iotests/093.out b/tests/qemu-iotests/093.out
index 914e3737bd..2f7d3902f2 100644
--- a/tests/qemu-iotests/093.out
+++ b/tests/qemu-iotests/093.out
@@ -1,5 +1,5 @@
-.....
+.......
----------------------------------------------------------------------
-Ran 5 tests
+Ran 7 tests
OK
diff --git a/tests/qemu-iotests/117 b/tests/qemu-iotests/117
index 5b28039e17..e955d52de3 100755
--- a/tests/qemu-iotests/117
+++ b/tests/qemu-iotests/117
@@ -52,16 +52,16 @@ _send_qemu_cmd $QEMU_HANDLE \
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'blockdev-add',
- 'arguments': { 'options': { 'node-name': 'protocol',
- 'driver': 'file',
- 'filename': '$TEST_IMG' } } }" \
+ 'arguments': { 'node-name': 'protocol',
+ 'driver': 'file',
+ 'filename': '$TEST_IMG' } }" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'blockdev-add',
- 'arguments': { 'options': { 'node-name': 'format',
- 'driver': '$IMGFMT',
- 'file': 'protocol' } } }" \
+ 'arguments': { 'node-name': 'format',
+ 'driver': '$IMGFMT',
+ 'file': 'protocol' } }" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118
index e63a40fa94..8a9e838c90 100755
--- a/tests/qemu-iotests/118
+++ b/tests/qemu-iotests/118
@@ -229,10 +229,10 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
def test_cycle(self):
result = self.vm.qmp('blockdev-add',
- options={'node-name': 'new',
- 'driver': iotests.imgfmt,
- 'file': {'filename': new_img,
- 'driver': 'file'}})
+ node_name='new',
+ driver=iotests.imgfmt,
+ file={'filename': new_img,
+ 'driver': 'file'})
self.assert_qmp(result, 'return', {})
if self.device_name is not None:
@@ -309,10 +309,10 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass):
return
result = self.vm.qmp('blockdev-add',
- options={'node-name': 'new',
- 'driver': iotests.imgfmt,
- 'file': {'filename': new_img,
- 'driver': 'file'}})
+ node_name='new',
+ driver=iotests.imgfmt,
+ file={'filename': new_img,
+ 'driver': 'file'})
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('x-blockdev-insert-medium', device='drive0',
@@ -341,10 +341,10 @@ class TestInitiallyFilled(GeneralChangeTestsBaseClass):
def test_insert_on_filled(self):
result = self.vm.qmp('blockdev-add',
- options={'node-name': 'new',
- 'driver': iotests.imgfmt,
- 'file': {'filename': new_img,
- 'driver': 'file'}})
+ node_name='new',
+ driver=iotests.imgfmt,
+ file={'filename': new_img,
+ 'driver': 'file'})
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('blockdev-open-tray', device='drive0')
@@ -609,11 +609,11 @@ class TestChangeReadOnly(ChangeBaseClass):
self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
result = self.vm.qmp('blockdev-add',
- options={'node-name': 'new',
- 'driver': iotests.imgfmt,
- 'read-only': True,
- 'file': {'filename': new_img,
- 'driver': 'file'}})
+ node_name='new',
+ driver=iotests.imgfmt,
+ read_only=True,
+ file={'filename': new_img,
+ 'driver': 'file'})
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block')
@@ -663,10 +663,10 @@ class TestBlockJobsAfterCycle(ChangeBaseClass):
self.assert_qmp_absent(result, 'return[0]/inserted')
result = self.vm.qmp('blockdev-add',
- options={'node-name': 'node0',
- 'driver': iotests.imgfmt,
- 'file': {'filename': old_img,
- 'driver': 'file'}})
+ node_name='node0',
+ driver=iotests.imgfmt,
+ file={'filename': old_img,
+ 'driver': 'file'})
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('x-blockdev-insert-medium', device='drive0',
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index 2f0bc24cd0..f06938eeb7 100644
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -416,10 +416,10 @@ class TestIncrementalBackup(TestIncrementalBackupBase):
('0xcd', '32M', '124k')))
# Create a blkdebug interface to this img as 'drive1'
- result = self.vm.qmp('blockdev-add', options={
- 'node-name': drive1['id'],
- 'driver': drive1['fmt'],
- 'file': {
+ result = self.vm.qmp('blockdev-add',
+ node_name=drive1['id'],
+ driver=drive1['fmt'],
+ file={
'driver': 'blkdebug',
'image': {
'driver': 'file',
@@ -438,7 +438,7 @@ class TestIncrementalBackup(TestIncrementalBackupBase):
'once': True
}],
}
- })
+ )
self.assert_qmp(result, 'return', {})
# Create bitmaps and full backups for both drives
@@ -560,10 +560,10 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
'''
drive0 = self.drives[0]
- result = self.vm.qmp('blockdev-add', options={
- 'node-name': drive0['id'],
- 'driver': drive0['fmt'],
- 'file': {
+ result = self.vm.qmp('blockdev-add',
+ node_name=drive0['id'],
+ driver=drive0['fmt'],
+ file={
'driver': 'blkdebug',
'image': {
'driver': 'file',
@@ -582,7 +582,7 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
'once': True
}],
}
- })
+ )
self.assert_qmp(result, 'return', {})
self.create_anchor_backup(drive0)
diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139
index 47a4c26e29..6a0f6ca569 100644
--- a/tests/qemu-iotests/139
+++ b/tests/qemu-iotests/139
@@ -57,7 +57,7 @@ class TestBlockdevDel(iotests.QMPTestCase):
'file': {'driver': 'file',
'node-name': file_node,
'filename': base_img}}
- result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node)
self.checkBlockDriverState(file_node)
@@ -72,7 +72,7 @@ class TestBlockdevDel(iotests.QMPTestCase):
'backing': '',
'file': {'driver': 'file',
'filename': new_img}}
- result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node)
@@ -185,7 +185,7 @@ class TestBlockdevDel(iotests.QMPTestCase):
opts = {'driver': 'blkdebug',
'node-name': debug,
'image': image}
- result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node)
self.checkBlockDriverState(debug)
@@ -210,7 +210,7 @@ class TestBlockdevDel(iotests.QMPTestCase):
'node-name': blkverify,
'test': node_0,
'raw': node_1}
- result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(test)
self.checkBlockDriverState(raw)
@@ -234,7 +234,7 @@ class TestBlockdevDel(iotests.QMPTestCase):
'node-name': quorum,
'vote-threshold': 1,
'children': [ child_0, child_1 ]}
- result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(child0)
self.checkBlockDriverState(child1)
diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141
index c092d872dc..3ba79f027a 100755
--- a/tests/qemu-iotests/141
+++ b/tests/qemu-iotests/141
@@ -50,13 +50,12 @@ test_blockjob()
_send_qemu_cmd $QEMU_HANDLE \
"{'execute': 'blockdev-add',
'arguments': {
- 'options': {
- 'node-name': 'drv0',
- 'driver': '$IMGFMT',
- 'file': {
- 'driver': 'file',
- 'filename': '$TEST_IMG'
- }}}}" \
+ 'node-name': 'drv0',
+ 'driver': '$IMGFMT',
+ 'file': {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG'
+ }}}" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
index 4057b5e2aa..0b86ea4e5c 100755
--- a/tests/qemu-iotests/155
+++ b/tests/qemu-iotests/155
@@ -63,10 +63,10 @@ class BaseClass(iotests.QMPTestCase):
# Add the BDS via blockdev-add so it stays around after the mirror block
# job has been completed
result = self.vm.qmp('blockdev-add',
- options={'node-name': 'source',
- 'driver': iotests.imgfmt,
- 'file': {'driver': 'file',
- 'filename': source_img}})
+ node_name='source',
+ driver=iotests.imgfmt,
+ file={'driver': 'file',
+ 'filename': source_img})
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('x-blockdev-insert-medium',
@@ -90,7 +90,7 @@ class BaseClass(iotests.QMPTestCase):
if self.target_blockdev_backing:
options['backing'] = self.target_blockdev_backing
- result = self.vm.qmp('blockdev-add', options=options)
+ result = self.vm.qmp('blockdev-add', **options)
self.assert_qmp(result, 'return', {})
def tearDown(self):
diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162
index 0b43ea3395..f8eecb325b 100755
--- a/tests/qemu-iotests/162
+++ b/tests/qemu-iotests/162
@@ -43,16 +43,26 @@ echo '=== NBD ==='
$QEMU_IMG info 'json:{"driver": "nbd", "host": 42}'
# And this should not treat @port as if it had not been specified
-# (We cannot use localhost with an invalid port here, but we need to use a
-# non-existing domain, because otherwise the error message will not contain
-# the port)
-$QEMU_IMG info 'json:{"driver": "nbd", "host": "does.not.exist.example.com", "port": 42}'
+# (We need to set up a server here, because the error message for "Connection
+# refused" does not contain the destination port)
+
+# Launching qemu-nbd is done in a loop: We try to set up an NBD server on some
+# random port and continue until success, i.e. until we have found a port that
+# is not in use yet.
+while true; do
+ port=$((RANDOM + 32768))
+ if $QEMU_NBD -p $port -f raw --fork null-co:// 2> /dev/null; then
+ break
+ fi
+done
+
+$QEMU_IMG info "json:{'driver': 'nbd', 'host': 'localhost', 'port': $port}" \
+ | grep '^image' | sed -e "s/$port/PORT/"
# This is a test for NBD's bdrv_refresh_filename() implementation: It expects
# either host or path to be set, but it must not assume that they are set to
# strings in the options QDict
-$QEMU_NBD -k "$PWD/42" -f raw null-co:// &
-sleep 0.5
+$QEMU_NBD -k "$PWD/42" -f raw --fork null-co://
$QEMU_IMG info 'json:{"driver": "nbd", "path": 42}' | grep '^image'
rm -f 42
diff --git a/tests/qemu-iotests/162.out b/tests/qemu-iotests/162.out
index 9bba72353a..3c5be2c569 100644
--- a/tests/qemu-iotests/162.out
+++ b/tests/qemu-iotests/162.out
@@ -2,7 +2,7 @@ QA output created by 162
=== NBD ===
qemu-img: Could not open 'json:{"driver": "nbd", "host": 42}': Failed to connect socket: Invalid argument
-qemu-img: Could not open 'json:{"driver": "nbd", "host": "does.not.exist.example.com", "port": 42}': address resolution failed for does.not.exist.example.com:42: Name or service not known
+image: nbd://localhost:PORT
image: nbd+unix://?socket=42
=== SSH ===
diff --git a/tests/test-arm-mptimer.c b/tests/test-arm-mptimer.c
new file mode 100644
index 0000000000..cb8f2df914
--- /dev/null
+++ b/tests/test-arm-mptimer.c
@@ -0,0 +1,1105 @@
+/*
+ * QTest testcase for the ARM MPTimer
+ *
+ * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/timer.h"
+#include "libqtest.h"
+
+#define TIMER_BLOCK_SCALE(s) ((((s) & 0xff) + 1) * 10)
+
+#define TIMER_BLOCK_STEP(scaler, steps_nb) \
+ clock_step(TIMER_BLOCK_SCALE(scaler) * (int64_t)(steps_nb) + 1)
+
+#define TIMER_BASE_PHYS 0x1e000600
+
+#define TIMER_LOAD 0x00
+#define TIMER_COUNTER 0x04
+#define TIMER_CONTROL 0x08
+#define TIMER_INTSTAT 0x0C
+
+#define TIMER_CONTROL_ENABLE (1 << 0)
+#define TIMER_CONTROL_PERIODIC (1 << 1)
+#define TIMER_CONTROL_IT_ENABLE (1 << 2)
+#define TIMER_CONTROL_PRESCALER(p) (((p) & 0xff) << 8)
+
+#define PERIODIC 1
+#define ONESHOT 0
+#define NOSCALE 0
+
+static int nonscaled = NOSCALE;
+static int scaled = 122;
+
+static void timer_load(uint32_t load)
+{
+ writel(TIMER_BASE_PHYS + TIMER_LOAD, load);
+}
+
+static void timer_start(int periodic, uint32_t scale)
+{
+ uint32_t ctl = TIMER_CONTROL_ENABLE | TIMER_CONTROL_PRESCALER(scale);
+
+ if (periodic) {
+ ctl |= TIMER_CONTROL_PERIODIC;
+ }
+
+ writel(TIMER_BASE_PHYS + TIMER_CONTROL, ctl);
+}
+
+static void timer_stop(void)
+{
+ writel(TIMER_BASE_PHYS + TIMER_CONTROL, 0);
+}
+
+static void timer_int_clr(void)
+{
+ writel(TIMER_BASE_PHYS + TIMER_INTSTAT, 1);
+}
+
+static void timer_reset(void)
+{
+ timer_stop();
+ timer_load(0);
+ timer_int_clr();
+}
+
+static uint32_t timer_get_and_clr_int_sts(void)
+{
+ uint32_t int_sts = readl(TIMER_BASE_PHYS + TIMER_INTSTAT);
+
+ if (int_sts) {
+ timer_int_clr();
+ }
+
+ return int_sts;
+}
+
+static uint32_t timer_counter(void)
+{
+ return readl(TIMER_BASE_PHYS + TIMER_COUNTER);
+}
+
+static void timer_set_counter(uint32_t value)
+{
+ writel(TIMER_BASE_PHYS + TIMER_COUNTER, value);
+}
+
+static void test_timer_oneshot(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(9999999);
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 9999);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+ g_assert_cmpuint(timer_counter(), ==, 9990000);
+
+ TIMER_BLOCK_STEP(scaler, 9990000);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+
+ TIMER_BLOCK_STEP(scaler, 9990000);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_pause(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(999999999);
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 999);
+
+ g_assert_cmpuint(timer_counter(), ==, 999999000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(scaler, 9000);
+
+ g_assert_cmpuint(timer_counter(), ==, 999990000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_stop();
+
+ g_assert_cmpuint(timer_counter(), ==, 999990000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(scaler, 90000);
+
+ g_assert_cmpuint(timer_counter(), ==, 999990000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 999990000);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+
+ TIMER_BLOCK_STEP(scaler, 999990000);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+}
+
+static void test_timer_reload(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(UINT32_MAX);
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 90000);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_load(UINT32_MAX);
+
+ TIMER_BLOCK_STEP(scaler, 90000);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_periodic(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+ int repeat = 10;
+
+ timer_reset();
+ timer_load(100);
+ timer_start(PERIODIC, scaler);
+
+ while (repeat--) {
+ clock_step(TIMER_BLOCK_SCALE(scaler) * (101 + repeat) + 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 100 - repeat);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+
+ clock_step(TIMER_BLOCK_SCALE(scaler) * (101 - repeat) - 1);
+ }
+}
+
+static void test_timer_oneshot_to_periodic(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(10000);
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1000);
+
+ g_assert_cmpuint(timer_counter(), ==, 9000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 14001);
+
+ g_assert_cmpuint(timer_counter(), ==, 5000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+}
+
+static void test_timer_periodic_to_oneshot(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(99999999);
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 999);
+
+ g_assert_cmpuint(timer_counter(), ==, 99999000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 99999009);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+}
+
+static void test_timer_prescaler(void)
+{
+ timer_reset();
+ timer_load(9999999);
+ timer_start(ONESHOT, NOSCALE);
+
+ TIMER_BLOCK_STEP(NOSCALE, 9999998);
+
+ g_assert_cmpuint(timer_counter(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(NOSCALE, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+
+ timer_reset();
+ timer_load(9999999);
+ timer_start(ONESHOT, 0xAB);
+
+ TIMER_BLOCK_STEP(0xAB, 9999998);
+
+ g_assert_cmpuint(timer_counter(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(0xAB, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+}
+
+static void test_timer_prescaler_on_the_fly(void)
+{
+ timer_reset();
+ timer_load(9999999);
+ timer_start(ONESHOT, NOSCALE);
+
+ TIMER_BLOCK_STEP(NOSCALE, 999);
+
+ g_assert_cmpuint(timer_counter(), ==, 9999000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_start(ONESHOT, 0xAB);
+
+ TIMER_BLOCK_STEP(0xAB, 9000);
+
+ g_assert_cmpuint(timer_counter(), ==, 9990000);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_set_oneshot_counter_to_0(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(UINT32_MAX);
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_set_counter(0);
+
+ TIMER_BLOCK_STEP(scaler, 10);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+}
+
+static void test_timer_set_periodic_counter_to_0(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(UINT32_MAX);
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_set_counter(0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - (scaler ? 0 : 1));
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ timer_reset();
+ timer_set_counter(UINT32_MAX);
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_set_counter(0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+}
+
+static void test_timer_noload_oneshot(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_noload_periodic(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+}
+
+static void test_timer_zero_load_oneshot(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_zero_load_periodic(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+}
+
+static void test_timer_zero_load_oneshot_to_nonzero(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+
+ timer_load(999);
+
+ TIMER_BLOCK_STEP(scaler, 1001);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+}
+
+static void test_timer_zero_load_periodic_to_nonzero(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+ int i;
+
+ timer_reset();
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ timer_load(1999999);
+
+ for (i = 1; i < 10; i++) {
+ TIMER_BLOCK_STEP(scaler, 2000001);
+
+ g_assert_cmpuint(timer_counter(), ==, 1999999 - i);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+ }
+}
+
+static void test_timer_nonzero_load_oneshot_to_zero(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+
+ timer_load(UINT32_MAX);
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+}
+
+static void test_timer_nonzero_load_periodic_to_zero(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ timer_load(UINT32_MAX);
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+}
+
+static void test_timer_set_periodic_counter_on_the_fly(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(UINT32_MAX / 2);
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX / 2 - 100);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_set_counter(UINT32_MAX);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_enable_and_set_counter(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ timer_set_counter(UINT32_MAX);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_set_counter_and_enable(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_set_counter(UINT32_MAX);
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_set_counter_disabled(void)
+{
+ timer_reset();
+ timer_set_counter(999999999);
+
+ TIMER_BLOCK_STEP(NOSCALE, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 999999999);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_load_disabled(void)
+{
+ timer_reset();
+ timer_load(999999999);
+
+ TIMER_BLOCK_STEP(NOSCALE, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 999999999);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_oneshot_with_counter_0_on_start(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(999);
+ timer_set_counter(0);
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_periodic_with_counter_0_on_start(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+ int i;
+
+ timer_reset();
+ timer_load(UINT32_MAX);
+ timer_set_counter(0);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 100);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 200);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_reset();
+ timer_load(1999999);
+ timer_set_counter(0);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ for (i = 2 - (!!scaler ? 1 : 0); i < 10; i++) {
+ TIMER_BLOCK_STEP(scaler, 2000001);
+
+ g_assert_cmpuint(timer_counter(), ==, 1999999 - i);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+ }
+}
+
+static void test_periodic_counter(gconstpointer arg)
+{
+ const int test_load = 10;
+ int scaler = *((int *) arg);
+ int test_val;
+
+ timer_reset();
+ timer_load(test_load);
+ timer_start(PERIODIC, scaler);
+
+ clock_step(1);
+
+ for (test_val = 0; test_val <= test_load; test_val++) {
+ clock_step(TIMER_BLOCK_SCALE(scaler) * test_load);
+ g_assert_cmpint(timer_counter(), ==, test_val);
+ }
+}
+
+static void test_timer_set_counter_periodic_with_zero_load(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_start(PERIODIC, scaler);
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ timer_set_counter(999);
+
+ TIMER_BLOCK_STEP(scaler, 999);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+}
+
+static void test_timer_set_oneshot_load_to_0(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(UINT32_MAX);
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_set_periodic_load_to_0(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(UINT32_MAX);
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_load(0);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 100);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+ g_assert_cmpuint(timer_counter(), ==, 0);
+}
+
+static void test_deferred_trigger(void)
+{
+ int mode = ONESHOT;
+
+again:
+ timer_reset();
+ timer_start(mode, 255);
+
+ clock_step(100);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+
+ timer_reset();
+ timer_load(2);
+ timer_start(mode, 255);
+
+ clock_step(100);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+
+ timer_reset();
+ timer_load(UINT32_MAX);
+ timer_start(mode, 255);
+
+ clock_step(100);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_set_counter(0);
+
+ clock_step(100);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+
+ timer_reset();
+ timer_load(UINT32_MAX);
+ timer_start(mode, 255);
+
+ clock_step(100);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_load(0);
+
+ clock_step(100);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+
+ if (mode == ONESHOT) {
+ mode = PERIODIC;
+ goto again;
+ }
+}
+
+static void test_timer_zero_load_mode_switch(gconstpointer arg)
+{
+ int scaler = *((int *) arg);
+
+ timer_reset();
+ timer_load(0);
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ timer_start(ONESHOT, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ timer_start(PERIODIC, scaler);
+
+ TIMER_BLOCK_STEP(scaler, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler);
+}
+
+static void test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot(void)
+{
+ timer_reset();
+ timer_load(0);
+ timer_start(PERIODIC, 255);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ timer_start(ONESHOT, NOSCALE);
+
+ TIMER_BLOCK_STEP(NOSCALE, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(NOSCALE, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic(void)
+{
+ timer_reset();
+ timer_load(0);
+ timer_start(ONESHOT, 255);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_start(PERIODIC, NOSCALE);
+
+ TIMER_BLOCK_STEP(NOSCALE, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic(void)
+{
+ timer_reset();
+ timer_load(0);
+ timer_start(ONESHOT, NOSCALE);
+
+ TIMER_BLOCK_STEP(NOSCALE, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_start(PERIODIC, 255);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+static void test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot(void)
+{
+ timer_reset();
+ timer_load(0);
+ timer_start(PERIODIC, NOSCALE);
+
+ TIMER_BLOCK_STEP(NOSCALE, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ timer_start(ONESHOT, 255);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+
+ TIMER_BLOCK_STEP(255, 1);
+
+ g_assert_cmpuint(timer_counter(), ==, 0);
+ g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0);
+}
+
+int main(int argc, char **argv)
+{
+ int *scaler = &nonscaled;
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("mptimer/deferred_trigger", test_deferred_trigger);
+ qtest_add_func("mptimer/load_disabled", test_timer_load_disabled);
+ qtest_add_func("mptimer/set_counter_disabled", test_timer_set_counter_disabled);
+ qtest_add_func("mptimer/zero_load_prescaled_periodic_to_nonscaled_oneshot",
+ test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot);
+ qtest_add_func("mptimer/zero_load_prescaled_oneshot_to_nonscaled_periodic",
+ test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic);
+ qtest_add_func("mptimer/zero_load_nonscaled_oneshot_to_prescaled_periodic",
+ test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic);
+ qtest_add_func("mptimer/zero_load_nonscaled_periodic_to_prescaled_oneshot",
+ test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot);
+ qtest_add_func("mptimer/prescaler", test_timer_prescaler);
+ qtest_add_func("mptimer/prescaler_on_the_fly", test_timer_prescaler_on_the_fly);
+
+tests_with_prescaler_arg:
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/oneshot scaler=%d", *scaler),
+ scaler, test_timer_oneshot);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/pause scaler=%d", *scaler),
+ scaler, test_timer_pause);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/reload scaler=%d", *scaler),
+ scaler, test_timer_reload);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/periodic scaler=%d", *scaler),
+ scaler, test_timer_periodic);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/oneshot_to_periodic scaler=%d", *scaler),
+ scaler, test_timer_oneshot_to_periodic);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/periodic_to_oneshot scaler=%d", *scaler),
+ scaler, test_timer_periodic_to_oneshot);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/set_oneshot_counter_to_0 scaler=%d", *scaler),
+ scaler, test_timer_set_oneshot_counter_to_0);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/set_periodic_counter_to_0 scaler=%d", *scaler),
+ scaler, test_timer_set_periodic_counter_to_0);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/noload_oneshot scaler=%d", *scaler),
+ scaler, test_timer_noload_oneshot);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/noload_periodic scaler=%d", *scaler),
+ scaler, test_timer_noload_periodic);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/zero_load_oneshot scaler=%d", *scaler),
+ scaler, test_timer_zero_load_oneshot);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/zero_load_periodic scaler=%d", *scaler),
+ scaler, test_timer_zero_load_periodic);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/zero_load_oneshot_to_nonzero scaler=%d", *scaler),
+ scaler, test_timer_zero_load_oneshot_to_nonzero);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/zero_load_periodic_to_nonzero scaler=%d", *scaler),
+ scaler, test_timer_zero_load_periodic_to_nonzero);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/nonzero_load_oneshot_to_zero scaler=%d", *scaler),
+ scaler, test_timer_nonzero_load_oneshot_to_zero);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/nonzero_load_periodic_to_zero scaler=%d", *scaler),
+ scaler, test_timer_nonzero_load_periodic_to_zero);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/set_periodic_counter_on_the_fly scaler=%d", *scaler),
+ scaler, test_timer_set_periodic_counter_on_the_fly);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/enable_and_set_counter scaler=%d", *scaler),
+ scaler, test_timer_enable_and_set_counter);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/set_counter_and_enable scaler=%d", *scaler),
+ scaler, test_timer_set_counter_and_enable);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/oneshot_with_counter_0_on_start scaler=%d", *scaler),
+ scaler, test_timer_oneshot_with_counter_0_on_start);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/periodic_with_counter_0_on_start scaler=%d", *scaler),
+ scaler, test_timer_periodic_with_counter_0_on_start);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/periodic_counter scaler=%d", *scaler),
+ scaler, test_periodic_counter);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/set_counter_periodic_with_zero_load scaler=%d", *scaler),
+ scaler, test_timer_set_counter_periodic_with_zero_load);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/set_oneshot_load_to_0 scaler=%d", *scaler),
+ scaler, test_timer_set_oneshot_load_to_0);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/set_periodic_load_to_0 scaler=%d", *scaler),
+ scaler, test_timer_set_periodic_load_to_0);
+ qtest_add_data_func(
+ g_strdup_printf("mptimer/zero_load_mode_switch scaler=%d", *scaler),
+ scaler, test_timer_zero_load_mode_switch);
+
+ if (scaler == &nonscaled) {
+ scaler = &scaled;
+ goto tests_with_prescaler_arg;
+ }
+
+ qtest_start("-machine vexpress-a9");
+ ret = g_test_run();
+ qtest_end();
+
+ return ret;
+}
diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c
index c0e9895fb9..9b7495cc32 100644
--- a/tests/test-hbitmap.c
+++ b/tests/test-hbitmap.c
@@ -11,6 +11,8 @@
#include "qemu/osdep.h"
#include "qemu/hbitmap.h"
+#include "qemu/bitmap.h"
+#include "block/block.h"
#define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6)
@@ -20,6 +22,7 @@
typedef struct TestHBitmapData {
HBitmap *hb;
+ HBitmap *meta;
unsigned long *bits;
size_t size;
size_t old_size;
@@ -91,6 +94,14 @@ static void hbitmap_test_init(TestHBitmapData *data,
}
}
+static void hbitmap_test_init_meta(TestHBitmapData *data,
+ uint64_t size, int granularity,
+ int meta_chunk)
+{
+ hbitmap_test_init(data, size, granularity);
+ data->meta = hbitmap_create_meta(data->hb, meta_chunk);
+}
+
static inline size_t hbitmap_test_array_size(size_t bits)
{
size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG);
@@ -133,6 +144,9 @@ static void hbitmap_test_teardown(TestHBitmapData *data,
const void *unused)
{
if (data->hb) {
+ if (data->meta) {
+ hbitmap_free_meta(data->hb);
+ }
hbitmap_free(data->hb);
data->hb = NULL;
}
@@ -634,6 +648,249 @@ static void test_hbitmap_truncate_shrink_large(TestHBitmapData *data,
hbitmap_test_truncate(data, size, -diff, 0);
}
+static void hbitmap_check_meta(TestHBitmapData *data,
+ int64_t start, int count)
+{
+ int64_t i;
+
+ for (i = 0; i < data->size; i++) {
+ if (i >= start && i < start + count) {
+ g_assert(hbitmap_get(data->meta, i));
+ } else {
+ g_assert(!hbitmap_get(data->meta, i));
+ }
+ }
+}
+
+static void hbitmap_test_meta(TestHBitmapData *data,
+ int64_t start, int count,
+ int64_t check_start, int check_count)
+{
+ hbitmap_reset_all(data->hb);
+ hbitmap_reset_all(data->meta);
+
+ /* Test "unset" -> "unset" will not update meta. */
+ hbitmap_reset(data->hb, start, count);
+ hbitmap_check_meta(data, 0, 0);
+
+ /* Test "unset" -> "set" will update meta */
+ hbitmap_set(data->hb, start, count);
+ hbitmap_check_meta(data, check_start, check_count);
+
+ /* Test "set" -> "set" will not update meta */
+ hbitmap_reset_all(data->meta);
+ hbitmap_set(data->hb, start, count);
+ hbitmap_check_meta(data, 0, 0);
+
+ /* Test "set" -> "unset" will update meta */
+ hbitmap_reset_all(data->meta);
+ hbitmap_reset(data->hb, start, count);
+ hbitmap_check_meta(data, check_start, check_count);
+}
+
+static void hbitmap_test_meta_do(TestHBitmapData *data, int chunk_size)
+{
+ uint64_t size = chunk_size * 100;
+ hbitmap_test_init_meta(data, size, 0, chunk_size);
+
+ hbitmap_test_meta(data, 0, 1, 0, chunk_size);
+ hbitmap_test_meta(data, 0, chunk_size, 0, chunk_size);
+ hbitmap_test_meta(data, chunk_size - 1, 1, 0, chunk_size);
+ hbitmap_test_meta(data, chunk_size - 1, 2, 0, chunk_size * 2);
+ hbitmap_test_meta(data, chunk_size - 1, chunk_size + 1, 0, chunk_size * 2);
+ hbitmap_test_meta(data, chunk_size - 1, chunk_size + 2, 0, chunk_size * 3);
+ hbitmap_test_meta(data, 7 * chunk_size - 1, chunk_size + 2,
+ 6 * chunk_size, chunk_size * 3);
+ hbitmap_test_meta(data, size - 1, 1, size - chunk_size, chunk_size);
+ hbitmap_test_meta(data, 0, size, 0, size);
+}
+
+static void test_hbitmap_meta_byte(TestHBitmapData *data, const void *unused)
+{
+ hbitmap_test_meta_do(data, BITS_PER_BYTE);
+}
+
+static void test_hbitmap_meta_word(TestHBitmapData *data, const void *unused)
+{
+ hbitmap_test_meta_do(data, BITS_PER_LONG);
+}
+
+static void test_hbitmap_meta_sector(TestHBitmapData *data, const void *unused)
+{
+ hbitmap_test_meta_do(data, BDRV_SECTOR_SIZE * BITS_PER_BYTE);
+}
+
+/**
+ * Create an HBitmap and test set/unset.
+ */
+static void test_hbitmap_meta_one(TestHBitmapData *data, const void *unused)
+{
+ int i;
+ int64_t offsets[] = {
+ 0, 1, L1 - 1, L1, L1 + 1, L2 - 1, L2, L2 + 1, L3 - 1, L3, L3 + 1
+ };
+
+ hbitmap_test_init_meta(data, L3 * 2, 0, 1);
+ for (i = 0; i < ARRAY_SIZE(offsets); i++) {
+ hbitmap_test_meta(data, offsets[i], 1, offsets[i], 1);
+ hbitmap_test_meta(data, offsets[i], L1, offsets[i], L1);
+ hbitmap_test_meta(data, offsets[i], L2, offsets[i], L2);
+ }
+}
+
+static void test_hbitmap_serialize_granularity(TestHBitmapData *data,
+ const void *unused)
+{
+ int r;
+
+ hbitmap_test_init(data, L3 * 2, 3);
+ r = hbitmap_serialization_granularity(data->hb);
+ g_assert_cmpint(r, ==, 64 << 3);
+}
+
+static void test_hbitmap_meta_zero(TestHBitmapData *data, const void *unused)
+{
+ hbitmap_test_init_meta(data, 0, 0, 1);
+
+ hbitmap_check_meta(data, 0, 0);
+}
+
+static void hbitmap_test_serialize_range(TestHBitmapData *data,
+ uint8_t *buf, size_t buf_size,
+ uint64_t pos, uint64_t count)
+{
+ size_t i;
+ unsigned long *el = (unsigned long *)buf;
+
+ assert(hbitmap_granularity(data->hb) == 0);
+ hbitmap_reset_all(data->hb);
+ memset(buf, 0, buf_size);
+ if (count) {
+ hbitmap_set(data->hb, pos, count);
+ }
+ hbitmap_serialize_part(data->hb, buf, 0, data->size);
+
+ /* Serialized buffer is inherently LE, convert it back manually to test */
+ for (i = 0; i < buf_size / sizeof(unsigned long); i++) {
+ el[i] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[i]) : le64_to_cpu(el[i]));
+ }
+
+ for (i = 0; i < data->size; i++) {
+ int is_set = test_bit(i, (unsigned long *)buf);
+ if (i >= pos && i < pos + count) {
+ g_assert(is_set);
+ } else {
+ g_assert(!is_set);
+ }
+ }
+
+ /* Re-serialize for deserialization testing */
+ memset(buf, 0, buf_size);
+ hbitmap_serialize_part(data->hb, buf, 0, data->size);
+ hbitmap_reset_all(data->hb);
+ hbitmap_deserialize_part(data->hb, buf, 0, data->size, true);
+
+ for (i = 0; i < data->size; i++) {
+ int is_set = hbitmap_get(data->hb, i);
+ if (i >= pos && i < pos + count) {
+ g_assert(is_set);
+ } else {
+ g_assert(!is_set);
+ }
+ }
+}
+
+static void test_hbitmap_serialize_basic(TestHBitmapData *data,
+ const void *unused)
+{
+ int i, j;
+ size_t buf_size;
+ uint8_t *buf;
+ uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 };
+ int num_positions = sizeof(positions) / sizeof(positions[0]);
+
+ hbitmap_test_init(data, L3, 0);
+ buf_size = hbitmap_serialization_size(data->hb, 0, data->size);
+ buf = g_malloc0(buf_size);
+
+ for (i = 0; i < num_positions; i++) {
+ for (j = 0; j < num_positions; j++) {
+ hbitmap_test_serialize_range(data, buf, buf_size,
+ positions[i],
+ MIN(positions[j], L3 - positions[i]));
+ }
+ }
+
+ g_free(buf);
+}
+
+static void test_hbitmap_serialize_part(TestHBitmapData *data,
+ const void *unused)
+{
+ int i, j, k;
+ size_t buf_size;
+ uint8_t *buf;
+ uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 };
+ int num_positions = sizeof(positions) / sizeof(positions[0]);
+
+ hbitmap_test_init(data, L3, 0);
+ buf_size = L2;
+ buf = g_malloc0(buf_size);
+
+ for (i = 0; i < num_positions; i++) {
+ hbitmap_set(data->hb, positions[i], 1);
+ }
+
+ for (i = 0; i < data->size; i += buf_size) {
+ unsigned long *el = (unsigned long *)buf;
+ hbitmap_serialize_part(data->hb, buf, i, buf_size);
+ for (j = 0; j < buf_size / sizeof(unsigned long); j++) {
+ el[j] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[j]) : le64_to_cpu(el[j]));
+ }
+
+ for (j = 0; j < buf_size; j++) {
+ bool should_set = false;
+ for (k = 0; k < num_positions; k++) {
+ if (positions[k] == j + i) {
+ should_set = true;
+ break;
+ }
+ }
+ g_assert_cmpint(should_set, ==, test_bit(j, (unsigned long *)buf));
+ }
+ }
+
+ g_free(buf);
+}
+
+static void test_hbitmap_serialize_zeroes(TestHBitmapData *data,
+ const void *unused)
+{
+ int i;
+ HBitmapIter iter;
+ int64_t next;
+ uint64_t min_l1 = MAX(L1, 64);
+ uint64_t positions[] = { 0, min_l1, L2, L3 - min_l1};
+ int num_positions = sizeof(positions) / sizeof(positions[0]);
+
+ hbitmap_test_init(data, L3, 0);
+
+ for (i = 0; i < num_positions; i++) {
+ hbitmap_set(data->hb, positions[i], L1);
+ }
+
+ for (i = 0; i < num_positions; i++) {
+ hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true);
+ hbitmap_iter_init(&iter, data->hb, 0);
+ next = hbitmap_iter_next(&iter);
+ if (i == num_positions - 1) {
+ g_assert_cmpint(next, ==, -1);
+ } else {
+ g_assert_cmpint(next, ==, positions[i + 1]);
+ }
+ }
+}
+
static void hbitmap_test_add(const char *testpath,
void (*test_func)(TestHBitmapData *data, const void *user_data))
{
@@ -683,6 +940,21 @@ int main(int argc, char **argv)
test_hbitmap_truncate_grow_large);
hbitmap_test_add("/hbitmap/truncate/shrink/large",
test_hbitmap_truncate_shrink_large);
+
+ hbitmap_test_add("/hbitmap/meta/zero", test_hbitmap_meta_zero);
+ hbitmap_test_add("/hbitmap/meta/one", test_hbitmap_meta_one);
+ hbitmap_test_add("/hbitmap/meta/byte", test_hbitmap_meta_byte);
+ hbitmap_test_add("/hbitmap/meta/word", test_hbitmap_meta_word);
+ hbitmap_test_add("/hbitmap/meta/sector", test_hbitmap_meta_sector);
+
+ hbitmap_test_add("/hbitmap/serialize/granularity",
+ test_hbitmap_serialize_granularity);
+ hbitmap_test_add("/hbitmap/serialize/basic",
+ test_hbitmap_serialize_basic);
+ hbitmap_test_add("/hbitmap/serialize/part",
+ test_hbitmap_serialize_part);
+ hbitmap_test_add("/hbitmap/serialize/zeroes",
+ test_hbitmap_serialize_zeroes);
g_test_run();
return 0;
diff --git a/translate-all.c b/translate-all.c
index 8ca393c9d0..86b45a19c5 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -97,25 +97,24 @@ typedef struct PageDesc {
#define V_L2_BITS 10
#define V_L2_SIZE (1 << V_L2_BITS)
-/* The bits remaining after N lower levels of page tables. */
-#define V_L1_BITS_REM \
- ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS)
-
-#if V_L1_BITS_REM < 4
-#define V_L1_BITS (V_L1_BITS_REM + V_L2_BITS)
-#else
-#define V_L1_BITS V_L1_BITS_REM
-#endif
-
-#define V_L1_SIZE ((target_ulong)1 << V_L1_BITS)
-
-#define V_L1_SHIFT (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - V_L1_BITS)
-
uintptr_t qemu_host_page_size;
intptr_t qemu_host_page_mask;
-/* The bottom level has pointers to PageDesc */
-static void *l1_map[V_L1_SIZE];
+/*
+ * L1 Mapping properties
+ */
+static int v_l1_size;
+static int v_l1_shift;
+static int v_l2_levels;
+
+/* The bottom level has pointers to PageDesc, and is indexed by
+ * anything from 4 to (V_L2_BITS + 3) bits, depending on target page size.
+ */
+#define V_L1_MIN_BITS 4
+#define V_L1_MAX_BITS (V_L2_BITS + 3)
+#define V_L1_MAX_SIZE (1 << V_L1_MAX_BITS)
+
+static void *l1_map[V_L1_MAX_SIZE];
/* code generation context */
TCGContext tcg_ctx;
@@ -125,6 +124,26 @@ TCGContext tcg_ctx;
__thread int have_tb_lock;
#endif
+static void page_table_config_init(void)
+{
+ uint32_t v_l1_bits;
+
+ assert(TARGET_PAGE_BITS);
+ /* The bits remaining after N lower levels of page tables. */
+ v_l1_bits = (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS;
+ if (v_l1_bits < V_L1_MIN_BITS) {
+ v_l1_bits += V_L2_BITS;
+ }
+
+ v_l1_size = 1 << v_l1_bits;
+ v_l1_shift = L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - v_l1_bits;
+ v_l2_levels = v_l1_shift / V_L2_BITS - 1;
+
+ assert(v_l1_bits <= V_L1_MAX_BITS);
+ assert(v_l1_shift % V_L2_BITS == 0);
+ assert(v_l2_levels >= 0);
+}
+
void tb_lock(void)
{
#ifdef CONFIG_USER_ONLY
@@ -332,6 +351,8 @@ void page_size_init(void)
static void page_init(void)
{
page_size_init();
+ page_table_config_init();
+
#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY)
{
#ifdef HAVE_KINFO_GETVMMAP
@@ -408,10 +429,10 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
int i;
/* Level 1. Always allocated. */
- lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1));
+ lp = l1_map + ((index >> v_l1_shift) & (v_l1_size - 1));
/* Level 2..N-1. */
- for (i = V_L1_SHIFT / V_L2_BITS - 1; i > 0; i--) {
+ for (i = v_l2_levels; i > 0; i--) {
void **p = atomic_rcu_read(lp);
if (p == NULL) {
@@ -826,10 +847,10 @@ static void page_flush_tb_1(int level, void **lp)
static void page_flush_tb(void)
{
- int i;
+ int i, l1_sz = v_l1_size;
- for (i = 0; i < V_L1_SIZE; i++) {
- page_flush_tb_1(V_L1_SHIFT / V_L2_BITS - 1, l1_map + i);
+ for (i = 0; i < l1_sz; i++) {
+ page_flush_tb_1(v_l2_levels, l1_map + i);
}
}
@@ -1883,16 +1904,16 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data,
int walk_memory_regions(void *priv, walk_memory_regions_fn fn)
{
struct walk_memory_regions_data data;
- uintptr_t i;
+ uintptr_t i, l1_sz = v_l1_size;
data.fn = fn;
data.priv = priv;
data.start = -1u;
data.prot = 0;
- for (i = 0; i < V_L1_SIZE; i++) {
- int rc = walk_memory_regions_1(&data, (target_ulong)i << (V_L1_SHIFT + TARGET_PAGE_BITS),
- V_L1_SHIFT / V_L2_BITS - 1, l1_map + i);
+ for (i = 0; i < l1_sz; i++) {
+ target_ulong base = i << (v_l1_shift + TARGET_PAGE_BITS);
+ int rc = walk_memory_regions_1(&data, base, v_l2_levels, l1_map + i);
if (rc != 0) {
return rc;
}
diff --git a/util/hbitmap.c b/util/hbitmap.c
index 99fd2ba37b..5d1a21ce91 100644
--- a/util/hbitmap.c
+++ b/util/hbitmap.c
@@ -78,6 +78,9 @@ struct HBitmap {
*/
int granularity;
+ /* A meta dirty bitmap to track the dirtiness of bits in this HBitmap. */
+ HBitmap *meta;
+
/* A number of progressively less coarse bitmaps (i.e. level 0 is the
* coarsest). Each bit in level N represents a word in level N+1 that
* has a set bit, except the last level where each bit represents the
@@ -209,25 +212,27 @@ static uint64_t hb_count_between(HBitmap *hb, uint64_t start, uint64_t last)
}
/* Setting starts at the last layer and propagates up if an element
- * changes from zero to non-zero.
+ * changes.
*/
static inline bool hb_set_elem(unsigned long *elem, uint64_t start, uint64_t last)
{
unsigned long mask;
- bool changed;
+ unsigned long old;
assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL));
assert(start <= last);
mask = 2UL << (last & (BITS_PER_LONG - 1));
mask -= 1UL << (start & (BITS_PER_LONG - 1));
- changed = (*elem == 0);
+ old = *elem;
*elem |= mask;
- return changed;
+ return old != *elem;
}
-/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */
-static void hb_set_between(HBitmap *hb, int level, uint64_t start, uint64_t last)
+/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)...
+ * Returns true if at least one bit is changed. */
+static bool hb_set_between(HBitmap *hb, int level, uint64_t start,
+ uint64_t last)
{
size_t pos = start >> BITS_PER_LEVEL;
size_t lastpos = last >> BITS_PER_LEVEL;
@@ -256,23 +261,28 @@ static void hb_set_between(HBitmap *hb, int level, uint64_t start, uint64_t last
if (level > 0 && changed) {
hb_set_between(hb, level - 1, pos, lastpos);
}
+ return changed;
}
void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count)
{
/* Compute range in the last layer. */
+ uint64_t first, n;
uint64_t last = start + count - 1;
trace_hbitmap_set(hb, start, count,
start >> hb->granularity, last >> hb->granularity);
- start >>= hb->granularity;
+ first = start >> hb->granularity;
last >>= hb->granularity;
- count = last - start + 1;
assert(last < hb->size);
+ n = last - first + 1;
- hb->count += count - hb_count_between(hb, start, last);
- hb_set_between(hb, HBITMAP_LEVELS - 1, start, last);
+ hb->count += n - hb_count_between(hb, first, last);
+ if (hb_set_between(hb, HBITMAP_LEVELS - 1, first, last) &&
+ hb->meta) {
+ hbitmap_set(hb->meta, start, count);
+ }
}
/* Resetting works the other way round: propagate up if the new
@@ -293,8 +303,10 @@ static inline bool hb_reset_elem(unsigned long *elem, uint64_t start, uint64_t l
return blanked;
}
-/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */
-static void hb_reset_between(HBitmap *hb, int level, uint64_t start, uint64_t last)
+/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)...
+ * Returns true if at least one bit is changed. */
+static bool hb_reset_between(HBitmap *hb, int level, uint64_t start,
+ uint64_t last)
{
size_t pos = start >> BITS_PER_LEVEL;
size_t lastpos = last >> BITS_PER_LEVEL;
@@ -337,22 +349,29 @@ static void hb_reset_between(HBitmap *hb, int level, uint64_t start, uint64_t la
if (level > 0 && changed) {
hb_reset_between(hb, level - 1, pos, lastpos);
}
+
+ return changed;
+
}
void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count)
{
/* Compute range in the last layer. */
+ uint64_t first;
uint64_t last = start + count - 1;
trace_hbitmap_reset(hb, start, count,
start >> hb->granularity, last >> hb->granularity);
- start >>= hb->granularity;
+ first = start >> hb->granularity;
last >>= hb->granularity;
assert(last < hb->size);
- hb->count -= hb_count_between(hb, start, last);
- hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last);
+ hb->count -= hb_count_between(hb, first, last);
+ if (hb_reset_between(hb, HBITMAP_LEVELS - 1, first, last) &&
+ hb->meta) {
+ hbitmap_set(hb->meta, start, count);
+ }
}
void hbitmap_reset_all(HBitmap *hb)
@@ -378,9 +397,147 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item)
return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0;
}
+uint64_t hbitmap_serialization_granularity(const HBitmap *hb)
+{
+ /* Require at least 64 bit granularity to be safe on both 64 bit and 32 bit
+ * hosts. */
+ return 64 << hb->granularity;
+}
+
+/* Start should be aligned to serialization granularity, chunk size should be
+ * aligned to serialization granularity too, except for last chunk.
+ */
+static void serialization_chunk(const HBitmap *hb,
+ uint64_t start, uint64_t count,
+ unsigned long **first_el, uint64_t *el_count)
+{
+ uint64_t last = start + count - 1;
+ uint64_t gran = hbitmap_serialization_granularity(hb);
+
+ assert((start & (gran - 1)) == 0);
+ assert((last >> hb->granularity) < hb->size);
+ if ((last >> hb->granularity) != hb->size - 1) {
+ assert((count & (gran - 1)) == 0);
+ }
+
+ start = (start >> hb->granularity) >> BITS_PER_LEVEL;
+ last = (last >> hb->granularity) >> BITS_PER_LEVEL;
+
+ *first_el = &hb->levels[HBITMAP_LEVELS - 1][start];
+ *el_count = last - start + 1;
+}
+
+uint64_t hbitmap_serialization_size(const HBitmap *hb,
+ uint64_t start, uint64_t count)
+{
+ uint64_t el_count;
+ unsigned long *cur;
+
+ if (!count) {
+ return 0;
+ }
+ serialization_chunk(hb, start, count, &cur, &el_count);
+
+ return el_count * sizeof(unsigned long);
+}
+
+void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf,
+ uint64_t start, uint64_t count)
+{
+ uint64_t el_count;
+ unsigned long *cur, *end;
+
+ if (!count) {
+ return;
+ }
+ serialization_chunk(hb, start, count, &cur, &el_count);
+ end = cur + el_count;
+
+ while (cur != end) {
+ unsigned long el =
+ (BITS_PER_LONG == 32 ? cpu_to_le32(*cur) : cpu_to_le64(*cur));
+
+ memcpy(buf, &el, sizeof(el));
+ buf += sizeof(el);
+ cur++;
+ }
+}
+
+void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf,
+ uint64_t start, uint64_t count,
+ bool finish)
+{
+ uint64_t el_count;
+ unsigned long *cur, *end;
+
+ if (!count) {
+ return;
+ }
+ serialization_chunk(hb, start, count, &cur, &el_count);
+ end = cur + el_count;
+
+ while (cur != end) {
+ memcpy(cur, buf, sizeof(*cur));
+
+ if (BITS_PER_LONG == 32) {
+ le32_to_cpus((uint32_t *)cur);
+ } else {
+ le64_to_cpus((uint64_t *)cur);
+ }
+
+ buf += sizeof(unsigned long);
+ cur++;
+ }
+ if (finish) {
+ hbitmap_deserialize_finish(hb);
+ }
+}
+
+void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count,
+ bool finish)
+{
+ uint64_t el_count;
+ unsigned long *first;
+
+ if (!count) {
+ return;
+ }
+ serialization_chunk(hb, start, count, &first, &el_count);
+
+ memset(first, 0, el_count * sizeof(unsigned long));
+ if (finish) {
+ hbitmap_deserialize_finish(hb);
+ }
+}
+
+void hbitmap_deserialize_finish(HBitmap *bitmap)
+{
+ int64_t i, size, prev_size;
+ int lev;
+
+ /* restore levels starting from penultimate to zero level, assuming
+ * that the last level is ok */
+ size = MAX((bitmap->size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1);
+ for (lev = HBITMAP_LEVELS - 1; lev-- > 0; ) {
+ prev_size = size;
+ size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1);
+ memset(bitmap->levels[lev], 0, size * sizeof(unsigned long));
+
+ for (i = 0; i < prev_size; ++i) {
+ if (bitmap->levels[lev + 1][i]) {
+ bitmap->levels[lev][i >> BITS_PER_LEVEL] |=
+ 1UL << (i & (BITS_PER_LONG - 1));
+ }
+ }
+ }
+
+ bitmap->levels[0][0] |= 1UL << (BITS_PER_LONG - 1);
+}
+
void hbitmap_free(HBitmap *hb)
{
unsigned i;
+ assert(!hb->meta);
for (i = HBITMAP_LEVELS; i-- > 0; ) {
g_free(hb->levels[i]);
}
@@ -458,6 +615,9 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size)
(size - old) * sizeof(*hb->levels[i]));
}
}
+ if (hb->meta) {
+ hbitmap_truncate(hb->meta, hb->size << hb->granularity);
+ }
}
@@ -493,3 +653,19 @@ bool hbitmap_merge(HBitmap *a, const HBitmap *b)
return true;
}
+
+HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size)
+{
+ assert(!(chunk_size & (chunk_size - 1)));
+ assert(!hb->meta);
+ hb->meta = hbitmap_alloc(hb->size << hb->granularity,
+ hb->granularity + ctz32(chunk_size));
+ return hb->meta;
+}
+
+void hbitmap_free_meta(HBitmap *hb)
+{
+ assert(hb->meta);
+ hbitmap_free(hb->meta);
+ hb->meta = NULL;
+}
diff --git a/vl.c b/vl.c
index 44e08b4fc0..4ec8120834 100644
--- a/vl.c
+++ b/vl.c
@@ -4088,6 +4088,16 @@ int main(int argc, char **argv, char **envp)
}
object_property_add_child(object_get_root(), "machine",
OBJECT(current_machine), &error_abort);
+
+ if (machine_class->minimum_page_bits) {
+ if (!set_preferred_target_page_bits(machine_class->minimum_page_bits)) {
+ /* This would be a board error: specifying a minimum smaller than
+ * a target's compile-time fixed setting.
+ */
+ g_assert_not_reached();
+ }
+ }
+
cpu_exec_init_all();
if (machine_class->hw_version) {