Merge remote-tracking branch 'remotes/rth/tags/pull-tcg-20191013' into staging
Host vector support for tcg/ppc.
Fix thread=single cpu kicking.
# gpg: Signature made Mon 14 Oct 2019 15:11:55 BST
# gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F
# gpg: issuer "richard.henderson@linaro.org"
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full]
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F
* remotes/rth/tags/pull-tcg-20191013: (23 commits)
cpus: kick all vCPUs when running thread=single
tcg/ppc: Update vector support for v3.00 dup/dupi
tcg/ppc: Update vector support for v3.00 load/store
tcg/ppc: Update vector support for v3.00 Altivec
tcg/ppc: Update vector support for v2.07 FP
tcg/ppc: Update vector support for v2.07 VSX
tcg/ppc: Update vector support for v2.07 Altivec
tcg/ppc: Update vector support for VSX
tcg/ppc: Enable Altivec detection
tcg/ppc: Support vector dup2
tcg/ppc: Support vector multiply
tcg/ppc: Support vector shift by immediate
tcg/ppc: Add support for vector saturated add/subtract
tcg/ppc: Add support for vector add/subtract
tcg/ppc: Add support for vector maximum/minimum
tcg/ppc: Add support for load/store/logic/comparison
tcg/ppc: Enable tcg backend vector compilation
tcg/ppc: Replace HAVE_ISEL macro with a variable
tcg/ppc: Replace HAVE_ISA_2_06
tcg/ppc: Create TCGPowerISA and have_isa
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 3ca8148..fe4dc51 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1517,7 +1517,7 @@
virtio-9p
M: Greg Kurz <groug@kaod.org>
-S: Supported
+S: Odd Fixes
F: hw/9pfs/
X: hw/9pfs/xen-9p*
F: fsdev/
diff --git a/Makefile b/Makefile
index 30f0abf..d20e7ff 100644
--- a/Makefile
+++ b/Makefile
@@ -983,7 +983,10 @@
# Canned command to build a single manual
# Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man')
-build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" sphinx-build $(if $(V),,-q) -W -n -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
+# Note the use of different doctree for each (manual, builder) tuple;
+# this works around Sphinx not handling parallel invocation on
+# a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946
+build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" sphinx-build $(if $(V),,-q) -W -n -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
# We assume all RST files in the manual's directory are used in it
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py
diff --git a/block.c b/block.c
index 5944124..1946fc6 100644
--- a/block.c
+++ b/block.c
@@ -5155,6 +5155,15 @@
return NULL;
}
+BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv || !drv->bdrv_get_specific_stats) {
+ return NULL;
+ }
+ return drv->bdrv_get_specific_stats(bs);
+}
+
void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
{
if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
@@ -5164,14 +5173,35 @@
bs->drv->bdrv_debug_event(bs, event);
}
-int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
- const char *tag)
+static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs)
{
while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) {
- bs = bs->file ? bs->file->bs : NULL;
+ if (bs->file) {
+ bs = bs->file->bs;
+ continue;
+ }
+
+ if (bs->drv->is_filter && bs->backing) {
+ bs = bs->backing->bs;
+ continue;
+ }
+
+ break;
}
if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) {
+ assert(bs->drv->bdrv_debug_remove_breakpoint);
+ return bs;
+ }
+
+ return NULL;
+}
+
+int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
+ const char *tag)
+{
+ bs = bdrv_find_debug_node(bs);
+ if (bs) {
return bs->drv->bdrv_debug_breakpoint(bs, event, tag);
}
@@ -5180,11 +5210,8 @@
int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag)
{
- while (bs && bs->drv && !bs->drv->bdrv_debug_remove_breakpoint) {
- bs = bs->file ? bs->file->bs : NULL;
- }
-
- if (bs && bs->drv && bs->drv->bdrv_debug_remove_breakpoint) {
+ bs = bdrv_find_debug_node(bs);
+ if (bs) {
return bs->drv->bdrv_debug_remove_breakpoint(bs, tag);
}
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 35f3bca..e394fe0 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -37,9 +37,13 @@
block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.o copy-on-read.o
+block-obj-y += block-copy.o
block-obj-y += crypto.o
+block-obj-y += aio_task.o
+block-obj-y += backup-top.o
+
common-obj-y += stream.o
nfs.o-libs := $(LIBNFS_LIBS)
diff --git a/block/accounting.c b/block/accounting.c
index 70a3d9a..8d41c8a 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -195,6 +195,10 @@
assert(cookie->type < BLOCK_MAX_IOTYPE);
+ if (cookie->type == BLOCK_ACCT_NONE) {
+ return;
+ }
+
qemu_mutex_lock(&stats->lock);
if (failed) {
@@ -217,6 +221,8 @@
}
qemu_mutex_unlock(&stats->lock);
+
+ cookie->type = BLOCK_ACCT_NONE;
}
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
diff --git a/block/aio_task.c b/block/aio_task.c
new file mode 100644
index 0000000..88989fa
--- /dev/null
+++ b/block/aio_task.c
@@ -0,0 +1,124 @@
+/*
+ * Aio tasks loops
+ *
+ * Copyright (c) 2019 Virtuozzo International GmbH.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/aio.h"
+#include "block/aio_task.h"
+
+struct AioTaskPool {
+ Coroutine *main_co;
+ int status;
+ int max_busy_tasks;
+ int busy_tasks;
+ bool waiting;
+};
+
+static void coroutine_fn aio_task_co(void *opaque)
+{
+ AioTask *task = opaque;
+ AioTaskPool *pool = task->pool;
+
+ assert(pool->busy_tasks < pool->max_busy_tasks);
+ pool->busy_tasks++;
+
+ task->ret = task->func(task);
+
+ pool->busy_tasks--;
+
+ if (task->ret < 0 && pool->status == 0) {
+ pool->status = task->ret;
+ }
+
+ g_free(task);
+
+ if (pool->waiting) {
+ pool->waiting = false;
+ aio_co_wake(pool->main_co);
+ }
+}
+
+void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool)
+{
+ assert(pool->busy_tasks > 0);
+ assert(qemu_coroutine_self() == pool->main_co);
+
+ pool->waiting = true;
+ qemu_coroutine_yield();
+
+ assert(!pool->waiting);
+ assert(pool->busy_tasks < pool->max_busy_tasks);
+}
+
+void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool)
+{
+ if (pool->busy_tasks < pool->max_busy_tasks) {
+ return;
+ }
+
+ aio_task_pool_wait_one(pool);
+}
+
+void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool)
+{
+ while (pool->busy_tasks > 0) {
+ aio_task_pool_wait_one(pool);
+ }
+}
+
+void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task)
+{
+ aio_task_pool_wait_slot(pool);
+
+ task->pool = pool;
+ qemu_coroutine_enter(qemu_coroutine_create(aio_task_co, task));
+}
+
+AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks)
+{
+ AioTaskPool *pool = g_new0(AioTaskPool, 1);
+
+ pool->main_co = qemu_coroutine_self();
+ pool->max_busy_tasks = max_busy_tasks;
+
+ return pool;
+}
+
+void aio_task_pool_free(AioTaskPool *pool)
+{
+ g_free(pool);
+}
+
+int aio_task_pool_status(AioTaskPool *pool)
+{
+ if (!pool) {
+ return 0; /* Sugar for lazy allocation of aio pool */
+ }
+
+ return pool->status;
+}
+
+bool aio_task_pool_empty(AioTaskPool *pool)
+{
+ return pool->busy_tasks == 0;
+}
diff --git a/block/backup-top.c b/block/backup-top.c
new file mode 100644
index 0000000..7cdb1f8
--- /dev/null
+++ b/block/backup-top.c
@@ -0,0 +1,276 @@
+/*
+ * backup-top filter driver
+ *
+ * The driver performs Copy-Before-Write (CBW) operation: it is injected above
+ * some node, and before each write it copies _old_ data to the target node.
+ *
+ * Copyright (c) 2018-2019 Virtuozzo International GmbH.
+ *
+ * Author:
+ * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "sysemu/block-backend.h"
+#include "qemu/cutils.h"
+#include "qapi/error.h"
+#include "block/block_int.h"
+#include "block/qdict.h"
+#include "block/block-copy.h"
+
+#include "block/backup-top.h"
+
+typedef struct BDRVBackupTopState {
+ BlockCopyState *bcs;
+ BdrvChild *target;
+ bool active;
+} BDRVBackupTopState;
+
+static coroutine_fn int backup_top_co_preadv(
+ BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
+}
+
+static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes)
+{
+ BDRVBackupTopState *s = bs->opaque;
+ uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size);
+ uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size);
+
+ return block_copy(s->bcs, off, end - off, NULL);
+}
+
+static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int bytes)
+{
+ int ret = backup_top_cbw(bs, offset, bytes);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return bdrv_co_pdiscard(bs->backing, offset, bytes);
+}
+
+static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
+ int64_t offset, int bytes, BdrvRequestFlags flags)
+{
+ int ret = backup_top_cbw(bs, offset, bytes);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
+}
+
+static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
+ uint64_t offset,
+ uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) {
+ int ret = backup_top_cbw(bs, offset, bytes);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
+}
+
+static int coroutine_fn backup_top_co_flush(BlockDriverState *bs)
+{
+ if (!bs->backing) {
+ return 0;
+ }
+
+ return bdrv_co_flush(bs->backing->bs);
+}
+
+static void backup_top_refresh_filename(BlockDriverState *bs)
+{
+ if (bs->backing == NULL) {
+ /*
+ * we can be here after failed bdrv_attach_child in
+ * bdrv_set_backing_hd
+ */
+ return;
+ }
+ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
+ bs->backing->bs->filename);
+}
+
+static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
+ const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ BDRVBackupTopState *s = bs->opaque;
+
+ if (!s->active) {
+ /*
+ * The filter node may be in process of bdrv_append(), which firstly do
+ * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
+ * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
+ * let's require nothing during bdrv_append() and refresh permissions
+ * after it (see bdrv_backup_top_append()).
+ */
+ *nperm = 0;
+ *nshared = BLK_PERM_ALL;
+ return;
+ }
+
+ if (role == &child_file) {
+ /*
+ * Target child
+ *
+ * Share write to target (child_file), to not interfere
+ * with guest writes to its disk which may be in target backing chain.
+ */
+ *nshared = BLK_PERM_ALL;
+ *nperm = BLK_PERM_WRITE;
+ } else {
+ /* Source child */
+ bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
+ nperm, nshared);
+
+ if (perm & BLK_PERM_WRITE) {
+ *nperm = *nperm | BLK_PERM_CONSISTENT_READ;
+ }
+ *nshared &= ~BLK_PERM_WRITE;
+ }
+}
+
+BlockDriver bdrv_backup_top_filter = {
+ .format_name = "backup-top",
+ .instance_size = sizeof(BDRVBackupTopState),
+
+ .bdrv_co_preadv = backup_top_co_preadv,
+ .bdrv_co_pwritev = backup_top_co_pwritev,
+ .bdrv_co_pwrite_zeroes = backup_top_co_pwrite_zeroes,
+ .bdrv_co_pdiscard = backup_top_co_pdiscard,
+ .bdrv_co_flush = backup_top_co_flush,
+
+ .bdrv_co_block_status = bdrv_co_block_status_from_backing,
+
+ .bdrv_refresh_filename = backup_top_refresh_filename,
+
+ .bdrv_child_perm = backup_top_child_perm,
+
+ .is_filter = true,
+};
+
+BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
+ BlockDriverState *target,
+ const char *filter_node_name,
+ uint64_t cluster_size,
+ BdrvRequestFlags write_flags,
+ BlockCopyState **bcs,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ BDRVBackupTopState *state;
+ BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter,
+ filter_node_name,
+ BDRV_O_RDWR, errp);
+
+ if (!top) {
+ return NULL;
+ }
+
+ top->total_sectors = source->total_sectors;
+ top->opaque = state = g_new0(BDRVBackupTopState, 1);
+
+ bdrv_ref(target);
+ state->target = bdrv_attach_child(top, target, "target", &child_file, errp);
+ if (!state->target) {
+ bdrv_unref(target);
+ bdrv_unref(top);
+ return NULL;
+ }
+
+ bdrv_drained_begin(source);
+
+ bdrv_ref(top);
+ bdrv_append(top, source, &local_err);
+ if (local_err) {
+ error_prepend(&local_err, "Cannot append backup-top filter: ");
+ goto append_failed;
+ }
+
+ /*
+ * bdrv_append() finished successfully, now we can require permissions
+ * we want.
+ */
+ state->active = true;
+ bdrv_child_refresh_perms(top, top->backing, &local_err);
+ if (local_err) {
+ error_prepend(&local_err,
+ "Cannot set permissions for backup-top filter: ");
+ goto failed_after_append;
+ }
+
+ state->bcs = block_copy_state_new(top->backing, state->target,
+ cluster_size, write_flags, &local_err);
+ if (local_err) {
+ error_prepend(&local_err, "Cannot create block-copy-state: ");
+ goto failed_after_append;
+ }
+ *bcs = state->bcs;
+
+ bdrv_drained_end(source);
+
+ return top;
+
+failed_after_append:
+ state->active = false;
+ bdrv_backup_top_drop(top);
+
+append_failed:
+ bdrv_drained_end(source);
+ bdrv_unref_child(top, state->target);
+ bdrv_unref(top);
+ error_propagate(errp, local_err);
+
+ return NULL;
+}
+
+void bdrv_backup_top_drop(BlockDriverState *bs)
+{
+ BDRVBackupTopState *s = bs->opaque;
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+
+ block_copy_state_free(s->bcs);
+
+ aio_context_acquire(aio_context);
+
+ bdrv_drained_begin(bs);
+
+ s->active = false;
+ bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
+ bdrv_replace_node(bs, backing_bs(bs), &error_abort);
+ bdrv_set_backing_hd(bs, NULL, &error_abort);
+
+ bdrv_drained_end(bs);
+
+ bdrv_unref(bs);
+
+ aio_context_release(aio_context);
+}
diff --git a/block/backup-top.h b/block/backup-top.h
new file mode 100644
index 0000000..e5cabfa
--- /dev/null
+++ b/block/backup-top.h
@@ -0,0 +1,41 @@
+/*
+ * backup-top filter driver
+ *
+ * The driver performs Copy-Before-Write (CBW) operation: it is injected above
+ * some node, and before each write it copies _old_ data to the target node.
+ *
+ * Copyright (c) 2018-2019 Virtuozzo International GmbH.
+ *
+ * Author:
+ * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BACKUP_TOP_H
+#define BACKUP_TOP_H
+
+#include "block/block_int.h"
+#include "block/block-copy.h"
+
+BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
+ BlockDriverState *target,
+ const char *filter_node_name,
+ uint64_t cluster_size,
+ BdrvRequestFlags write_flags,
+ BlockCopyState **bcs,
+ Error **errp);
+void bdrv_backup_top_drop(BlockDriverState *bs);
+
+#endif /* BACKUP_TOP_H */
diff --git a/block/backup.c b/block/backup.c
index 763f0d7..46978c1 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -2,6 +2,7 @@
* QEMU backup
*
* Copyright (C) 2013 Proxmox Server Solutions
+ * Copyright (c) 2019 Virtuozzo International GmbH.
*
* Authors:
* Dietmar Maurer (dietmar@proxmox.com)
@@ -18,6 +19,7 @@
#include "block/block_int.h"
#include "block/blockjob_int.h"
#include "block/block_backup.h"
+#include "block/block-copy.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/ratelimit.h"
@@ -26,333 +28,68 @@
#include "qemu/bitmap.h"
#include "qemu/error-report.h"
-#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
+#include "block/backup-top.h"
-typedef struct CowRequest {
- int64_t start_byte;
- int64_t end_byte;
- QLIST_ENTRY(CowRequest) list;
- CoQueue wait_queue; /* coroutines blocked on this request */
-} CowRequest;
+#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
typedef struct BackupBlockJob {
BlockJob common;
- BlockBackend *target;
+ BlockDriverState *backup_top;
+ BlockDriverState *source_bs;
BdrvDirtyBitmap *sync_bitmap;
- BdrvDirtyBitmap *copy_bitmap;
MirrorSyncMode sync_mode;
BitmapSyncMode bitmap_mode;
BlockdevOnError on_source_error;
BlockdevOnError on_target_error;
- CoRwlock flush_rwlock;
uint64_t len;
uint64_t bytes_read;
int64_t cluster_size;
- NotifierWithReturn before_write;
- QLIST_HEAD(, CowRequest) inflight_reqs;
- bool use_copy_range;
- int64_t copy_range_size;
-
- BdrvRequestFlags write_flags;
- bool initializing_bitmap;
+ BlockCopyState *bcs;
} BackupBlockJob;
static const BlockJobDriver backup_job_driver;
-/* See if in-flight requests overlap and wait for them to complete */
-static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
- int64_t start,
- int64_t end)
+static void backup_progress_bytes_callback(int64_t bytes, void *opaque)
{
- CowRequest *req;
- bool retry;
+ BackupBlockJob *s = opaque;
- do {
- retry = false;
- QLIST_FOREACH(req, &job->inflight_reqs, list) {
- if (end > req->start_byte && start < req->end_byte) {
- qemu_co_queue_wait(&req->wait_queue, NULL);
- retry = true;
- break;
- }
- }
- } while (retry);
+ s->bytes_read += bytes;
+ job_progress_update(&s->common.job, bytes);
}
-/* Keep track of an in-flight request */
-static void cow_request_begin(CowRequest *req, BackupBlockJob *job,
- int64_t start, int64_t end)
+static void backup_progress_reset_callback(void *opaque)
{
- req->start_byte = start;
- req->end_byte = end;
- qemu_co_queue_init(&req->wait_queue);
- QLIST_INSERT_HEAD(&job->inflight_reqs, req, list);
-}
+ BackupBlockJob *s = opaque;
+ uint64_t estimate = bdrv_get_dirty_count(s->bcs->copy_bitmap);
-/* Forget about a completed request */
-static void cow_request_end(CowRequest *req)
-{
- QLIST_REMOVE(req, list);
- qemu_co_queue_restart_all(&req->wait_queue);
-}
-
-/* Copy range to target with a bounce buffer and return the bytes copied. If
- * error occurred, return a negative error number */
-static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
- int64_t start,
- int64_t end,
- bool is_write_notifier,
- bool *error_is_read,
- void **bounce_buffer)
-{
- int ret;
- BlockBackend *blk = job->common.blk;
- int nbytes;
- int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
-
- assert(QEMU_IS_ALIGNED(start, job->cluster_size));
- bdrv_reset_dirty_bitmap(job->copy_bitmap, start, job->cluster_size);
- nbytes = MIN(job->cluster_size, job->len - start);
- if (!*bounce_buffer) {
- *bounce_buffer = blk_blockalign(blk, job->cluster_size);
- }
-
- ret = blk_co_pread(blk, start, nbytes, *bounce_buffer, read_flags);
- if (ret < 0) {
- trace_backup_do_cow_read_fail(job, start, ret);
- if (error_is_read) {
- *error_is_read = true;
- }
- goto fail;
- }
-
- ret = blk_co_pwrite(job->target, start, nbytes, *bounce_buffer,
- job->write_flags);
- if (ret < 0) {
- trace_backup_do_cow_write_fail(job, start, ret);
- if (error_is_read) {
- *error_is_read = false;
- }
- goto fail;
- }
-
- return nbytes;
-fail:
- bdrv_set_dirty_bitmap(job->copy_bitmap, start, job->cluster_size);
- return ret;
-
-}
-
-/* Copy range to target and return the bytes copied. If error occurred, return a
- * negative error number. */
-static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
- int64_t start,
- int64_t end,
- bool is_write_notifier)
-{
- int ret;
- int nr_clusters;
- BlockBackend *blk = job->common.blk;
- int nbytes;
- int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
-
- assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
- assert(QEMU_IS_ALIGNED(start, job->cluster_size));
- nbytes = MIN(job->copy_range_size, end - start);
- nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
- bdrv_reset_dirty_bitmap(job->copy_bitmap, start,
- job->cluster_size * nr_clusters);
- ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
- read_flags, job->write_flags);
- if (ret < 0) {
- trace_backup_do_cow_copy_range_fail(job, start, ret);
- bdrv_set_dirty_bitmap(job->copy_bitmap, start,
- job->cluster_size * nr_clusters);
- return ret;
- }
-
- return nbytes;
-}
-
-/*
- * Check if the cluster starting at offset is allocated or not.
- * return via pnum the number of contiguous clusters sharing this allocation.
- */
-static int backup_is_cluster_allocated(BackupBlockJob *s, int64_t offset,
- int64_t *pnum)
-{
- BlockDriverState *bs = blk_bs(s->common.blk);
- int64_t count, total_count = 0;
- int64_t bytes = s->len - offset;
- int ret;
-
- assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
-
- while (true) {
- ret = bdrv_is_allocated(bs, offset, bytes, &count);
- if (ret < 0) {
- return ret;
- }
-
- total_count += count;
-
- if (ret || count == 0) {
- /*
- * ret: partial segment(s) are considered allocated.
- * otherwise: unallocated tail is treated as an entire segment.
- */
- *pnum = DIV_ROUND_UP(total_count, s->cluster_size);
- return ret;
- }
-
- /* Unallocated segment(s) with uncertain following segment(s) */
- if (total_count >= s->cluster_size) {
- *pnum = total_count / s->cluster_size;
- return 0;
- }
-
- offset += count;
- bytes -= count;
- }
-}
-
-/**
- * Reset bits in copy_bitmap starting at offset if they represent unallocated
- * data in the image. May reset subsequent contiguous bits.
- * @return 0 when the cluster at @offset was unallocated,
- * 1 otherwise, and -ret on error.
- */
-static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s,
- int64_t offset, int64_t *count)
-{
- int ret;
- int64_t clusters, bytes, estimate;
-
- ret = backup_is_cluster_allocated(s, offset, &clusters);
- if (ret < 0) {
- return ret;
- }
-
- bytes = clusters * s->cluster_size;
-
- if (!ret) {
- bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
- estimate = bdrv_get_dirty_count(s->copy_bitmap);
- job_progress_set_remaining(&s->common.job, estimate);
- }
-
- *count = bytes;
- return ret;
+ job_progress_set_remaining(&s->common.job, estimate);
}
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
int64_t offset, uint64_t bytes,
- bool *error_is_read,
- bool is_write_notifier)
+ bool *error_is_read)
{
- CowRequest cow_request;
int ret = 0;
int64_t start, end; /* bytes */
- void *bounce_buffer = NULL;
- int64_t status_bytes;
-
- qemu_co_rwlock_rdlock(&job->flush_rwlock);
start = QEMU_ALIGN_DOWN(offset, job->cluster_size);
end = QEMU_ALIGN_UP(bytes + offset, job->cluster_size);
trace_backup_do_cow_enter(job, start, offset, bytes);
- wait_for_overlapping_requests(job, start, end);
- cow_request_begin(&cow_request, job, start, end);
-
- while (start < end) {
- int64_t dirty_end;
-
- if (!bdrv_dirty_bitmap_get(job->copy_bitmap, start)) {
- trace_backup_do_cow_skip(job, start);
- start += job->cluster_size;
- continue; /* already copied */
- }
-
- dirty_end = bdrv_dirty_bitmap_next_zero(job->copy_bitmap, start,
- (end - start));
- if (dirty_end < 0) {
- dirty_end = end;
- }
-
- if (job->initializing_bitmap) {
- ret = backup_bitmap_reset_unallocated(job, start, &status_bytes);
- if (ret == 0) {
- trace_backup_do_cow_skip_range(job, start, status_bytes);
- start += status_bytes;
- continue;
- }
- /* Clamp to known allocated region */
- dirty_end = MIN(dirty_end, start + status_bytes);
- }
-
- trace_backup_do_cow_process(job, start);
-
- if (job->use_copy_range) {
- ret = backup_cow_with_offload(job, start, dirty_end,
- is_write_notifier);
- if (ret < 0) {
- job->use_copy_range = false;
- }
- }
- if (!job->use_copy_range) {
- ret = backup_cow_with_bounce_buffer(job, start, dirty_end,
- is_write_notifier,
- error_is_read, &bounce_buffer);
- }
- if (ret < 0) {
- break;
- }
-
- /* Publish progress, guest I/O counts as progress too. Note that the
- * offset field is an opaque progress value, it is not a disk offset.
- */
- start += ret;
- job->bytes_read += ret;
- job_progress_update(&job->common.job, ret);
- ret = 0;
- }
-
- if (bounce_buffer) {
- qemu_vfree(bounce_buffer);
- }
-
- cow_request_end(&cow_request);
+ ret = block_copy(job->bcs, start, end - start, error_is_read);
trace_backup_do_cow_return(job, offset, bytes, ret);
- qemu_co_rwlock_unlock(&job->flush_rwlock);
-
return ret;
}
-static int coroutine_fn backup_before_write_notify(
- NotifierWithReturn *notifier,
- void *opaque)
-{
- BackupBlockJob *job = container_of(notifier, BackupBlockJob, before_write);
- BdrvTrackedRequest *req = opaque;
-
- assert(req->bs == blk_bs(job->common.blk));
- assert(QEMU_IS_ALIGNED(req->offset, BDRV_SECTOR_SIZE));
- assert(QEMU_IS_ALIGNED(req->bytes, BDRV_SECTOR_SIZE));
-
- return backup_do_cow(job, req->offset, req->bytes, NULL, true);
-}
-
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
{
BdrvDirtyBitmap *bm;
- BlockDriverState *bs = blk_bs(job->common.blk);
bool sync = (((ret == 0) || (job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS)) \
&& (job->bitmap_mode != BITMAP_SYNC_MODE_NEVER));
@@ -361,20 +98,20 @@
* We succeeded, or we always intended to sync the bitmap.
* Delete this bitmap and install the child.
*/
- bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
+ bm = bdrv_dirty_bitmap_abdicate(job->source_bs, job->sync_bitmap, NULL);
} else {
/*
* We failed, or we never intended to sync the bitmap anyway.
* Merge the successor back into the parent, keeping all data.
*/
- bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
+ bm = bdrv_reclaim_dirty_bitmap(job->source_bs, job->sync_bitmap, NULL);
}
assert(bm);
if (ret < 0 && job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS) {
/* If we failed and synced, merge in the bits we didn't copy: */
- bdrv_dirty_bitmap_merge_internal(bm, job->copy_bitmap,
+ bdrv_dirty_bitmap_merge_internal(bm, job->bcs->copy_bitmap,
NULL, true);
}
}
@@ -398,16 +135,8 @@
static void backup_clean(Job *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
- BlockDriverState *bs = blk_bs(s->common.blk);
- if (s->copy_bitmap) {
- bdrv_release_dirty_bitmap(bs, s->copy_bitmap);
- s->copy_bitmap = NULL;
- }
-
- assert(s->target);
- blk_unref(s->target);
- s->target = NULL;
+ bdrv_backup_top_drop(s->backup_top);
}
void backup_do_checkpoint(BlockJob *job, Error **errp)
@@ -422,7 +151,7 @@
return;
}
- bdrv_set_dirty_bitmap(backup_job->copy_bitmap, 0, backup_job->len);
+ bdrv_set_dirty_bitmap(backup_job->bcs->copy_bitmap, 0, backup_job->len);
}
static BlockErrorAction backup_error_action(BackupBlockJob *job,
@@ -445,8 +174,10 @@
return true;
}
- /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
- * return. Without a yield, the VM would not reboot. */
+ /*
+ * We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
+ * return. Without a yield, the VM would not reboot.
+ */
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
job->bytes_read = 0;
job_sleep_ns(&job->common.job, delay_ns);
@@ -465,14 +196,13 @@
BdrvDirtyBitmapIter *bdbi;
int ret = 0;
- bdbi = bdrv_dirty_iter_new(job->copy_bitmap);
+ bdbi = bdrv_dirty_iter_new(job->bcs->copy_bitmap);
while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) {
do {
if (yield_and_check(job)) {
goto out;
}
- ret = backup_do_cow(job, offset,
- job->cluster_size, &error_is_read, false);
+ ret = backup_do_cow(job, offset, job->cluster_size, &error_is_read);
if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
BLOCK_ERROR_ACTION_REPORT)
{
@@ -492,7 +222,7 @@
uint64_t estimate;
if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
- ret = bdrv_dirty_bitmap_merge_internal(job->copy_bitmap,
+ ret = bdrv_dirty_bitmap_merge_internal(job->bcs->copy_bitmap,
job->sync_bitmap,
NULL, true);
assert(ret);
@@ -502,29 +232,22 @@
* We can't hog the coroutine to initialize this thoroughly.
* Set a flag and resume work when we are able to yield safely.
*/
- job->initializing_bitmap = true;
+ job->bcs->skip_unallocated = true;
}
- bdrv_set_dirty_bitmap(job->copy_bitmap, 0, job->len);
+ bdrv_set_dirty_bitmap(job->bcs->copy_bitmap, 0, job->len);
}
- estimate = bdrv_get_dirty_count(job->copy_bitmap);
+ estimate = bdrv_get_dirty_count(job->bcs->copy_bitmap);
job_progress_set_remaining(&job->common.job, estimate);
}
static int coroutine_fn backup_run(Job *job, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
- BlockDriverState *bs = blk_bs(s->common.blk);
int ret = 0;
- QLIST_INIT(&s->inflight_reqs);
- qemu_co_rwlock_init(&s->flush_rwlock);
-
backup_init_copy_bitmap(s);
- s->before_write.notify = backup_before_write_notify;
- bdrv_add_before_write_notifier(bs, &s->before_write);
-
if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
int64_t offset = 0;
int64_t count;
@@ -535,22 +258,26 @@
goto out;
}
- ret = backup_bitmap_reset_unallocated(s, offset, &count);
+ ret = block_copy_reset_unallocated(s->bcs, offset, &count);
if (ret < 0) {
goto out;
}
offset += count;
}
- s->initializing_bitmap = false;
+ s->bcs->skip_unallocated = false;
}
if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
- /* All bits are set in copy_bitmap to allow any cluster to be copied.
- * This does not actually require them to be copied. */
+ /*
+ * All bits are set in copy_bitmap to allow any cluster to be copied.
+ * This does not actually require them to be copied.
+ */
while (!job_is_cancelled(job)) {
- /* Yield until the job is cancelled. We just let our before_write
- * notify callback service CoW requests. */
+ /*
+ * Yield until the job is cancelled. We just let our before_write
+ * notify callback service CoW requests.
+ */
job_yield(job);
}
} else {
@@ -558,12 +285,6 @@
}
out:
- notifier_with_return_remove(&s->before_write);
-
- /* wait until pending backup_do_cow() calls have completed */
- qemu_co_rwlock_wrlock(&s->flush_rwlock);
- qemu_co_rwlock_unlock(&s->flush_rwlock);
-
return ret;
}
@@ -621,6 +342,7 @@
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
BitmapSyncMode bitmap_mode,
bool compress,
+ const char *filter_node_name,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
int creation_flags,
@@ -629,9 +351,10 @@
{
int64_t len;
BackupBlockJob *job = NULL;
- int ret;
int64_t cluster_size;
- BdrvDirtyBitmap *copy_bitmap = NULL;
+ BdrvRequestFlags write_flags;
+ BlockDriverState *backup_top = NULL;
+ BlockCopyState *bcs = NULL;
assert(bs);
assert(target);
@@ -696,76 +419,66 @@
goto error;
}
- copy_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp);
- if (!copy_bitmap) {
+ /*
+ * If source is in backing chain of target assume that target is going to be
+ * used for "image fleecing", i.e. it should represent a kind of snapshot of
+ * source at backup-start point in time. And target is going to be read by
+ * somebody (for example, used as NBD export) during backup job.
+ *
+ * In this case, we need to add BDRV_REQ_SERIALISING write flag to avoid
+ * intersection of backup writes and third party reads from target,
+ * otherwise reading from target we may occasionally read already updated by
+ * guest data.
+ *
+ * For more information see commit f8d59dfb40bb and test
+ * tests/qemu-iotests/222
+ */
+ write_flags = (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
+ (compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
+
+ backup_top = bdrv_backup_top_append(bs, target, filter_node_name,
+ cluster_size, write_flags, &bcs, errp);
+ if (!backup_top) {
goto error;
}
- bdrv_disable_dirty_bitmap(copy_bitmap);
/* job->len is fixed, so we can't allow resize */
- job = block_job_create(job_id, &backup_job_driver, txn, bs,
- BLK_PERM_CONSISTENT_READ,
- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
- BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
+ job = block_job_create(job_id, &backup_job_driver, txn, backup_top,
+ 0, BLK_PERM_ALL,
speed, creation_flags, cb, opaque, errp);
if (!job) {
goto error;
}
- /* The target must match the source in size, so no resize here either */
- job->target = blk_new(job->common.job.aio_context,
- BLK_PERM_WRITE,
- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
- BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
- ret = blk_insert_bs(job->target, target, errp);
- if (ret < 0) {
- goto error;
- }
- blk_set_disable_request_queuing(job->target, true);
-
+ job->backup_top = backup_top;
+ job->source_bs = bs;
job->on_source_error = on_source_error;
job->on_target_error = on_target_error;
job->sync_mode = sync_mode;
job->sync_bitmap = sync_bitmap;
job->bitmap_mode = bitmap_mode;
-
- /*
- * Set write flags:
- * 1. Detect image-fleecing (and similar) schemes
- * 2. Handle compression
- */
- job->write_flags =
- (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
- (compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
-
+ job->bcs = bcs;
job->cluster_size = cluster_size;
- job->copy_bitmap = copy_bitmap;
- copy_bitmap = NULL;
- job->use_copy_range = !compress; /* compression isn't supported for it */
- job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
- blk_get_max_transfer(job->target));
- job->copy_range_size = MAX(job->cluster_size,
- QEMU_ALIGN_UP(job->copy_range_size,
- job->cluster_size));
+ job->len = len;
- /* Required permissions are already taken with target's blk_new() */
+ block_copy_set_callbacks(bcs, backup_progress_bytes_callback,
+ backup_progress_reset_callback, job);
+
+ /* Required permissions are already taken by backup-top target */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
- job->len = len;
return &job->common;
error:
- if (copy_bitmap) {
- assert(!job || !job->copy_bitmap);
- bdrv_release_dirty_bitmap(bs, copy_bitmap);
- }
if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
}
if (job) {
backup_clean(&job->common.job);
job_early_fail(&job->common.job);
+ } else if (backup_top) {
+ bdrv_backup_top_drop(backup_top);
}
return NULL;
diff --git a/block/blkreplay.c b/block/blkreplay.c
index 2b7931b..c96ac8f 100644
--- a/block/blkreplay.c
+++ b/block/blkreplay.c
@@ -126,6 +126,12 @@
return ret;
}
+static int blkreplay_snapshot_goto(BlockDriverState *bs,
+ const char *snapshot_id)
+{
+ return bdrv_snapshot_goto(bs->file->bs, snapshot_id, NULL);
+}
+
static BlockDriver bdrv_blkreplay = {
.format_name = "blkreplay",
.instance_size = 0,
@@ -140,6 +146,8 @@
.bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes,
.bdrv_co_pdiscard = blkreplay_co_pdiscard,
.bdrv_co_flush = blkreplay_co_flush,
+
+ .bdrv_snapshot_goto = blkreplay_snapshot_goto,
};
static void bdrv_blkreplay_init(void)
diff --git a/block/block-backend.c b/block/block-backend.c
index 1c605d5..eb22ff3 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -18,6 +18,8 @@
#include "hw/qdev-core.h"
#include "sysemu/blockdev.h"
#include "sysemu/runstate.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/replay.h"
#include "qapi/error.h"
#include "qapi/qapi-events-block.h"
#include "qemu/id.h"
@@ -1306,7 +1308,8 @@
acb->blk = blk;
acb->ret = ret;
- aio_bh_schedule_oneshot(blk_get_aio_context(blk), error_callback_bh, acb);
+ replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
+ error_callback_bh, acb);
return &acb->common;
}
@@ -1362,8 +1365,8 @@
acb->has_returned = true;
if (acb->rwco.ret != NOT_DONE) {
- aio_bh_schedule_oneshot(blk_get_aio_context(blk),
- blk_aio_complete_bh, acb);
+ replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
+ blk_aio_complete_bh, acb);
}
return &acb->common;
diff --git a/block/block-copy.c b/block/block-copy.c
new file mode 100644
index 0000000..0f76ea1
--- /dev/null
+++ b/block/block-copy.c
@@ -0,0 +1,345 @@
+/*
+ * block_copy API
+ *
+ * Copyright (C) 2013 Proxmox Server Solutions
+ * Copyright (c) 2019 Virtuozzo International GmbH.
+ *
+ * Authors:
+ * Dietmar Maurer (dietmar@proxmox.com)
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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 "trace.h"
+#include "qapi/error.h"
+#include "block/block-copy.h"
+#include "sysemu/block-backend.h"
+
+static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s,
+ int64_t start,
+ int64_t end)
+{
+ BlockCopyInFlightReq *req;
+ bool waited;
+
+ do {
+ waited = false;
+ QLIST_FOREACH(req, &s->inflight_reqs, list) {
+ if (end > req->start_byte && start < req->end_byte) {
+ qemu_co_queue_wait(&req->wait_queue, NULL);
+ waited = true;
+ break;
+ }
+ }
+ } while (waited);
+}
+
+static void block_copy_inflight_req_begin(BlockCopyState *s,
+ BlockCopyInFlightReq *req,
+ int64_t start, int64_t end)
+{
+ req->start_byte = start;
+ req->end_byte = end;
+ qemu_co_queue_init(&req->wait_queue);
+ QLIST_INSERT_HEAD(&s->inflight_reqs, req, list);
+}
+
+static void coroutine_fn block_copy_inflight_req_end(BlockCopyInFlightReq *req)
+{
+ QLIST_REMOVE(req, list);
+ qemu_co_queue_restart_all(&req->wait_queue);
+}
+
+void block_copy_state_free(BlockCopyState *s)
+{
+ if (!s) {
+ return;
+ }
+
+ bdrv_release_dirty_bitmap(s->source->bs, s->copy_bitmap);
+ g_free(s);
+}
+
+BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
+ int64_t cluster_size,
+ BdrvRequestFlags write_flags, Error **errp)
+{
+ BlockCopyState *s;
+ BdrvDirtyBitmap *copy_bitmap;
+ uint32_t max_transfer =
+ MIN_NON_ZERO(INT_MAX, MIN_NON_ZERO(source->bs->bl.max_transfer,
+ target->bs->bl.max_transfer));
+
+ copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
+ errp);
+ if (!copy_bitmap) {
+ return NULL;
+ }
+ bdrv_disable_dirty_bitmap(copy_bitmap);
+
+ s = g_new(BlockCopyState, 1);
+ *s = (BlockCopyState) {
+ .source = source,
+ .target = target,
+ .copy_bitmap = copy_bitmap,
+ .cluster_size = cluster_size,
+ .len = bdrv_dirty_bitmap_size(copy_bitmap),
+ .write_flags = write_flags,
+ };
+
+ s->copy_range_size = QEMU_ALIGN_DOWN(max_transfer, cluster_size),
+ /*
+ * Set use_copy_range, consider the following:
+ * 1. Compression is not supported for copy_range.
+ * 2. copy_range does not respect max_transfer (it's a TODO), so we factor
+ * that in here. If max_transfer is smaller than the job->cluster_size,
+ * we do not use copy_range (in that case it's zero after aligning down
+ * above).
+ */
+ s->use_copy_range =
+ !(write_flags & BDRV_REQ_WRITE_COMPRESSED) && s->copy_range_size > 0;
+
+ QLIST_INIT(&s->inflight_reqs);
+
+ return s;
+}
+
+void block_copy_set_callbacks(
+ BlockCopyState *s,
+ ProgressBytesCallbackFunc progress_bytes_callback,
+ ProgressResetCallbackFunc progress_reset_callback,
+ void *progress_opaque)
+{
+ s->progress_bytes_callback = progress_bytes_callback;
+ s->progress_reset_callback = progress_reset_callback;
+ s->progress_opaque = progress_opaque;
+}
+
+/*
+ * Copy range to target with a bounce buffer and return the bytes copied. If
+ * error occurred, return a negative error number
+ */
+static int coroutine_fn block_copy_with_bounce_buffer(BlockCopyState *s,
+ int64_t start,
+ int64_t end,
+ bool *error_is_read,
+ void **bounce_buffer)
+{
+ int ret;
+ int nbytes;
+
+ assert(QEMU_IS_ALIGNED(start, s->cluster_size));
+ bdrv_reset_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
+ nbytes = MIN(s->cluster_size, s->len - start);
+ if (!*bounce_buffer) {
+ *bounce_buffer = qemu_blockalign(s->source->bs, s->cluster_size);
+ }
+
+ ret = bdrv_co_pread(s->source, start, nbytes, *bounce_buffer, 0);
+ if (ret < 0) {
+ trace_block_copy_with_bounce_buffer_read_fail(s, start, ret);
+ if (error_is_read) {
+ *error_is_read = true;
+ }
+ goto fail;
+ }
+
+ ret = bdrv_co_pwrite(s->target, start, nbytes, *bounce_buffer,
+ s->write_flags);
+ if (ret < 0) {
+ trace_block_copy_with_bounce_buffer_write_fail(s, start, ret);
+ if (error_is_read) {
+ *error_is_read = false;
+ }
+ goto fail;
+ }
+
+ return nbytes;
+fail:
+ bdrv_set_dirty_bitmap(s->copy_bitmap, start, s->cluster_size);
+ return ret;
+
+}
+
+/*
+ * Copy range to target and return the bytes copied. If error occurred, return a
+ * negative error number.
+ */
+static int coroutine_fn block_copy_with_offload(BlockCopyState *s,
+ int64_t start,
+ int64_t end)
+{
+ int ret;
+ int nr_clusters;
+ int nbytes;
+
+ assert(QEMU_IS_ALIGNED(s->copy_range_size, s->cluster_size));
+ assert(QEMU_IS_ALIGNED(start, s->cluster_size));
+ nbytes = MIN(s->copy_range_size, MIN(end, s->len) - start);
+ nr_clusters = DIV_ROUND_UP(nbytes, s->cluster_size);
+ bdrv_reset_dirty_bitmap(s->copy_bitmap, start,
+ s->cluster_size * nr_clusters);
+ ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes,
+ 0, s->write_flags);
+ if (ret < 0) {
+ trace_block_copy_with_offload_fail(s, start, ret);
+ bdrv_set_dirty_bitmap(s->copy_bitmap, start,
+ s->cluster_size * nr_clusters);
+ return ret;
+ }
+
+ return nbytes;
+}
+
+/*
+ * Check if the cluster starting at offset is allocated or not.
+ * return via pnum the number of contiguous clusters sharing this allocation.
+ */
+static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
+ int64_t *pnum)
+{
+ BlockDriverState *bs = s->source->bs;
+ int64_t count, total_count = 0;
+ int64_t bytes = s->len - offset;
+ int ret;
+
+ assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
+
+ while (true) {
+ ret = bdrv_is_allocated(bs, offset, bytes, &count);
+ if (ret < 0) {
+ return ret;
+ }
+
+ total_count += count;
+
+ if (ret || count == 0) {
+ /*
+ * ret: partial segment(s) are considered allocated.
+ * otherwise: unallocated tail is treated as an entire segment.
+ */
+ *pnum = DIV_ROUND_UP(total_count, s->cluster_size);
+ return ret;
+ }
+
+ /* Unallocated segment(s) with uncertain following segment(s) */
+ if (total_count >= s->cluster_size) {
+ *pnum = total_count / s->cluster_size;
+ return 0;
+ }
+
+ offset += count;
+ bytes -= count;
+ }
+}
+
+/*
+ * Reset bits in copy_bitmap starting at offset if they represent unallocated
+ * data in the image. May reset subsequent contiguous bits.
+ * @return 0 when the cluster at @offset was unallocated,
+ * 1 otherwise, and -ret on error.
+ */
+int64_t block_copy_reset_unallocated(BlockCopyState *s,
+ int64_t offset, int64_t *count)
+{
+ int ret;
+ int64_t clusters, bytes;
+
+ ret = block_copy_is_cluster_allocated(s, offset, &clusters);
+ if (ret < 0) {
+ return ret;
+ }
+
+ bytes = clusters * s->cluster_size;
+
+ if (!ret) {
+ bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
+ s->progress_reset_callback(s->progress_opaque);
+ }
+
+ *count = bytes;
+ return ret;
+}
+
+int coroutine_fn block_copy(BlockCopyState *s,
+ int64_t start, uint64_t bytes,
+ bool *error_is_read)
+{
+ int ret = 0;
+ int64_t end = bytes + start; /* bytes */
+ void *bounce_buffer = NULL;
+ int64_t status_bytes;
+ BlockCopyInFlightReq req;
+
+ /*
+ * block_copy() user is responsible for keeping source and target in same
+ * aio context
+ */
+ assert(bdrv_get_aio_context(s->source->bs) ==
+ bdrv_get_aio_context(s->target->bs));
+
+ assert(QEMU_IS_ALIGNED(start, s->cluster_size));
+ assert(QEMU_IS_ALIGNED(end, s->cluster_size));
+
+ block_copy_wait_inflight_reqs(s, start, bytes);
+ block_copy_inflight_req_begin(s, &req, start, end);
+
+ while (start < end) {
+ int64_t dirty_end;
+
+ if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) {
+ trace_block_copy_skip(s, start);
+ start += s->cluster_size;
+ continue; /* already copied */
+ }
+
+ dirty_end = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start,
+ (end - start));
+ if (dirty_end < 0) {
+ dirty_end = end;
+ }
+
+ if (s->skip_unallocated) {
+ ret = block_copy_reset_unallocated(s, start, &status_bytes);
+ if (ret == 0) {
+ trace_block_copy_skip_range(s, start, status_bytes);
+ start += status_bytes;
+ continue;
+ }
+ /* Clamp to known allocated region */
+ dirty_end = MIN(dirty_end, start + status_bytes);
+ }
+
+ trace_block_copy_process(s, start);
+
+ if (s->use_copy_range) {
+ ret = block_copy_with_offload(s, start, dirty_end);
+ if (ret < 0) {
+ s->use_copy_range = false;
+ }
+ }
+ if (!s->use_copy_range) {
+ ret = block_copy_with_bounce_buffer(s, start, dirty_end,
+ error_is_read, &bounce_buffer);
+ }
+ if (ret < 0) {
+ break;
+ }
+
+ start += ret;
+ s->progress_bytes_callback(ret, s->progress_opaque);
+ ret = 0;
+ }
+
+ if (bounce_buffer) {
+ qemu_vfree(bounce_buffer);
+ }
+
+ block_copy_inflight_req_end(&req);
+
+ return ret;
+}
diff --git a/block/file-posix.c b/block/file-posix.c
index f12c06d..695fcf7 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -161,6 +161,11 @@
bool needs_alignment;
bool drop_cache;
bool check_cache_dropped;
+ struct {
+ uint64_t discard_nb_ok;
+ uint64_t discard_nb_failed;
+ uint64_t discard_bytes_ok;
+ } stats;
PRManager *pr_mgr;
} BDRVRawState;
@@ -2660,11 +2665,22 @@
#endif /* !__linux__ */
}
+static void raw_account_discard(BDRVRawState *s, uint64_t nbytes, int ret)
+{
+ if (ret) {
+ s->stats.discard_nb_failed++;
+ } else {
+ s->stats.discard_nb_ok++;
+ s->stats.discard_bytes_ok += nbytes;
+ }
+}
+
static coroutine_fn int
raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev)
{
BDRVRawState *s = bs->opaque;
RawPosixAIOData acb;
+ int ret;
acb = (RawPosixAIOData) {
.bs = bs,
@@ -2678,7 +2694,9 @@
acb.aio_type |= QEMU_AIO_BLKDEV;
}
- return raw_thread_pool_submit(bs, handle_aiocb_discard, &acb);
+ ret = raw_thread_pool_submit(bs, handle_aiocb_discard, &acb);
+ raw_account_discard(s, bytes, ret);
+ return ret;
}
static coroutine_fn int
@@ -2735,6 +2753,36 @@
return 0;
}
+static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ return (BlockStatsSpecificFile) {
+ .discard_nb_ok = s->stats.discard_nb_ok,
+ .discard_nb_failed = s->stats.discard_nb_failed,
+ .discard_bytes_ok = s->stats.discard_bytes_ok,
+ };
+}
+
+static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs)
+{
+ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
+
+ stats->driver = BLOCKDEV_DRIVER_FILE;
+ stats->u.file = get_blockstats_specific_file(bs);
+
+ return stats;
+}
+
+static BlockStatsSpecific *hdev_get_specific_stats(BlockDriverState *bs)
+{
+ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
+
+ stats->driver = BLOCKDEV_DRIVER_HOST_DEVICE;
+ stats->u.host_device = get_blockstats_specific_file(bs);
+
+ return stats;
+}
+
static QemuOptsList raw_create_opts = {
.name = "raw-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
@@ -2942,6 +2990,7 @@
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
+ .bdrv_get_specific_stats = raw_get_specific_stats,
.bdrv_check_perm = raw_check_perm,
.bdrv_set_perm = raw_set_perm,
.bdrv_abort_perm_update = raw_abort_perm_update,
@@ -3301,10 +3350,12 @@
static coroutine_fn int
hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes)
{
+ BDRVRawState *s = bs->opaque;
int ret;
ret = fd_open(bs);
if (ret < 0) {
+ raw_account_discard(s, bytes, ret);
return ret;
}
return raw_do_pdiscard(bs, offset, bytes, true);
@@ -3418,6 +3469,7 @@
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
+ .bdrv_get_specific_stats = hdev_get_specific_stats,
.bdrv_check_perm = raw_check_perm,
.bdrv_set_perm = raw_set_perm,
.bdrv_abort_perm_update = raw_abort_perm_update,
diff --git a/block/io.c b/block/io.c
index 4f9ee97..f0b86c1 100644
--- a/block/io.c
+++ b/block/io.c
@@ -33,6 +33,7 @@
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
+#include "sysemu/replay.h"
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
@@ -368,8 +369,8 @@
if (bs) {
bdrv_inc_in_flight(bs);
}
- aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
- bdrv_co_drain_bh_cb, &data);
+ replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs),
+ bdrv_co_drain_bh_cb, &data);
qemu_coroutine_yield();
/* If we are resumed from some other event (such as an aio completion or a
@@ -600,6 +601,15 @@
return;
}
+ /*
+ * bdrv queue is managed by record/replay,
+ * waiting for finishing the I/O requests may
+ * be infinite
+ */
+ if (replay_events_enabled()) {
+ return;
+ }
+
/* AIO_WAIT_WHILE() with a NULL context can only be called from the main
* loop AioContext, so make sure we're in the main context. */
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
@@ -629,6 +639,15 @@
BlockDriverState *bs = NULL;
int drained_end_counter = 0;
+ /*
+ * bdrv queue is managed by record/replay,
+ * waiting for finishing the I/O requests may
+ * be endless
+ */
+ if (replay_events_enabled()) {
+ return;
+ }
+
while ((bs = bdrv_next_all_states(bs))) {
AioContext *aio_context = bdrv_get_aio_context(bs);
@@ -2071,6 +2090,13 @@
return ret;
}
+ /* If the request is misaligned then we can't make it efficient */
+ if ((flags & BDRV_REQ_NO_FALLBACK) &&
+ !QEMU_IS_ALIGNED(offset | bytes, align))
+ {
+ return -ENOTSUP;
+ }
+
bdrv_inc_in_flight(bs);
/*
* Align write if necessary by performing a read-modify-write cycle.
@@ -2124,6 +2150,15 @@
BlockDriverState *bs = NULL;
int result = 0;
+ /*
+ * bdrv queue is managed by record/replay,
+ * creating new flush request for stopping
+ * the VM may break the determinism
+ */
+ if (replay_events_enabled()) {
+ return result;
+ }
+
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs);
int ret;
diff --git a/block/iscsi.c b/block/iscsi.c
index 506bf5f..2ced150 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -40,6 +40,7 @@
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/uuid.h"
+#include "sysemu/replay.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-misc.h"
#include "qapi/qmp/qdict.h"
@@ -280,8 +281,8 @@
}
if (iTask->co) {
- aio_bh_schedule_oneshot(iTask->iscsilun->aio_context,
- iscsi_co_generic_bh_cb, iTask);
+ replay_bh_schedule_oneshot_event(iTask->iscsilun->aio_context,
+ iscsi_co_generic_bh_cb, iTask);
} else {
iTask->complete = 1;
}
diff --git a/block/nbd.c b/block/nbd.c
index 813c40d..fd78e5f 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -1158,6 +1158,18 @@
BDRV_BLOCK_OFFSET_VALID;
}
+static int nbd_client_reopen_prepare(BDRVReopenState *state,
+ BlockReopenQueue *queue, Error **errp)
+{
+ BDRVNBDState *s = (BDRVNBDState *)state->bs->opaque;
+
+ if ((state->flags & BDRV_O_RDWR) && (s->info.flags & NBD_FLAG_READ_ONLY)) {
+ error_setg(errp, "Can't reopen read-only NBD mount as read/write");
+ return -EACCES;
+ }
+ return 0;
+}
+
static void nbd_client_close(BlockDriverState *bs)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
@@ -1798,6 +1810,7 @@
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
+ .bdrv_reopen_prepare = nbd_client_reopen_prepare,
.bdrv_co_preadv = nbd_client_co_preadv,
.bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
@@ -1820,6 +1833,7 @@
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
+ .bdrv_reopen_prepare = nbd_client_reopen_prepare,
.bdrv_co_preadv = nbd_client_co_preadv,
.bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
@@ -1842,6 +1856,7 @@
.instance_size = sizeof(BDRVNBDState),
.bdrv_parse_filename = nbd_parse_filename,
.bdrv_file_open = nbd_open,
+ .bdrv_reopen_prepare = nbd_client_reopen_prepare,
.bdrv_co_preadv = nbd_client_co_preadv,
.bdrv_co_pwritev = nbd_client_co_pwritev,
.bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes,
diff --git a/block/nfs.c b/block/nfs.c
index f39acfd..40f2349 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -37,6 +37,8 @@
#include "qemu/option.h"
#include "qemu/uri.h"
#include "qemu/cutils.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/replay.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
@@ -257,8 +259,8 @@
if (task->ret < 0) {
error_report("NFS Error: %s", nfs_get_error(nfs));
}
- aio_bh_schedule_oneshot(task->client->aio_context,
- nfs_co_generic_bh_cb, task);
+ replay_bh_schedule_oneshot_event(task->client->aio_context,
+ nfs_co_generic_bh_cb, task);
}
static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset,
diff --git a/block/null.c b/block/null.c
index 699aa29..15e1d56 100644
--- a/block/null.c
+++ b/block/null.c
@@ -17,6 +17,7 @@
#include "qemu/module.h"
#include "qemu/option.h"
#include "block/block_int.h"
+#include "sysemu/replay.h"
#define NULL_OPT_LATENCY "latency-ns"
#define NULL_OPT_ZEROES "read-zeroes"
@@ -179,7 +180,8 @@
timer_mod_ns(&acb->timer,
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
} else {
- aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), null_bh_cb, acb);
+ replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs),
+ null_bh_cb, acb);
}
return &acb->common;
}
diff --git a/block/nvme.c b/block/nvme.c
index 5be3a39..910872e 100644
--- a/block/nvme.c
+++ b/block/nvme.c
@@ -23,6 +23,7 @@
#include "qemu/option.h"
#include "qemu/vfio-helpers.h"
#include "block/block_int.h"
+#include "sysemu/replay.h"
#include "trace.h"
#include "block/nvme.h"
@@ -351,7 +352,8 @@
smp_mb_release();
*q->cq.doorbell = cpu_to_le32(q->cq.head);
if (!qemu_co_queue_empty(&q->free_req_queue)) {
- aio_bh_schedule_oneshot(s->aio_context, nvme_free_req_queue_cb, q);
+ replay_bh_schedule_oneshot_event(s->aio_context,
+ nvme_free_req_queue_cb, q);
}
}
q->busy = false;
@@ -935,7 +937,7 @@
/* The rw coroutine hasn't yielded, don't try to enter. */
return;
}
- aio_bh_schedule_oneshot(data->ctx, nvme_rw_cb_bh, data);
+ replay_bh_schedule_oneshot_event(data->ctx, nvme_rw_cb_bh, data);
}
static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs,
diff --git a/block/qapi.c b/block/qapi.c
index 7ee2ee0..9a5d0c9 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -440,24 +440,30 @@
ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
+ ds->unmap_bytes = stats->nr_bytes[BLOCK_ACCT_UNMAP];
ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
+ ds->unmap_operations = stats->nr_ops[BLOCK_ACCT_UNMAP];
ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
+ ds->failed_unmap_operations = stats->failed_ops[BLOCK_ACCT_UNMAP];
ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
ds->invalid_flush_operations =
stats->invalid_ops[BLOCK_ACCT_FLUSH];
+ ds->invalid_unmap_operations = stats->invalid_ops[BLOCK_ACCT_UNMAP];
ds->rd_merged = stats->merged[BLOCK_ACCT_READ];
ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
+ ds->unmap_merged = stats->merged[BLOCK_ACCT_UNMAP];
ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
+ ds->unmap_total_time_ns = stats->total_time_ns[BLOCK_ACCT_UNMAP];
ds->has_idle_time_ns = stats->last_access_time_ns > 0;
if (ds->has_idle_time_ns) {
@@ -537,6 +543,11 @@
s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset);
+ s->driver_specific = bdrv_get_specific_stats(bs);
+ if (s->driver_specific) {
+ s->has_driver_specific = true;
+ }
+
if (bs->file) {
s->has_parent = true;
s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level);
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 8d5fa15..8982b7b 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -1330,6 +1330,9 @@
nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index);
assert(nb_clusters <= INT_MAX);
+ /* Limit total allocation byte count to INT_MAX */
+ nb_clusters = MIN(nb_clusters, INT_MAX >> s->cluster_bits);
+
/* Find L2 entry for the first involved cluster */
ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index);
if (ret < 0) {
@@ -1412,7 +1415,7 @@
* request actually writes to (excluding COW at the end)
*/
uint64_t requested_bytes = *bytes + offset_into_cluster(s, guest_offset);
- int avail_bytes = MIN(INT_MAX, nb_clusters << s->cluster_bits);
+ int avail_bytes = nb_clusters << s->cluster_bits;
int nb_bytes = MIN(requested_bytes, avail_bytes);
QCowL2Meta *old_m = *m;
diff --git a/block/qcow2.c b/block/qcow2.c
index 4d16393..7961c05 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -41,6 +41,7 @@
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h"
#include "crypto.h"
+#include "block/aio_task.h"
/*
Differences with QCOW:
@@ -1972,20 +1973,184 @@
return ret;
}
+static coroutine_fn int
+qcow2_co_preadv_encrypted(BlockDriverState *bs,
+ uint64_t file_cluster_offset,
+ uint64_t offset,
+ uint64_t bytes,
+ QEMUIOVector *qiov,
+ uint64_t qiov_offset)
+{
+ int ret;
+ BDRVQcow2State *s = bs->opaque;
+ uint8_t *buf;
+
+ assert(bs->encrypted && s->crypto);
+ assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
+
+ /*
+ * For encrypted images, read everything into a temporary
+ * contiguous buffer on which the AES functions can work.
+ * Also, decryption in a separate buffer is better as it
+ * prevents the guest from learning information about the
+ * encrypted nature of the virtual disk.
+ */
+
+ buf = qemu_try_blockalign(s->data_file->bs, bytes);
+ if (buf == NULL) {
+ return -ENOMEM;
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
+ ret = bdrv_co_pread(s->data_file,
+ file_cluster_offset + offset_into_cluster(s, offset),
+ bytes, buf, 0);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
+ assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
+ if (qcow2_co_decrypt(bs,
+ file_cluster_offset + offset_into_cluster(s, offset),
+ offset, buf, bytes) < 0)
+ {
+ ret = -EIO;
+ goto fail;
+ }
+ qemu_iovec_from_buf(qiov, qiov_offset, buf, bytes);
+
+fail:
+ qemu_vfree(buf);
+
+ return ret;
+}
+
+typedef struct Qcow2AioTask {
+ AioTask task;
+
+ BlockDriverState *bs;
+ QCow2ClusterType cluster_type; /* only for read */
+ uint64_t file_cluster_offset;
+ uint64_t offset;
+ uint64_t bytes;
+ QEMUIOVector *qiov;
+ uint64_t qiov_offset;
+ QCowL2Meta *l2meta; /* only for write */
+} Qcow2AioTask;
+
+static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task);
+static coroutine_fn int qcow2_add_task(BlockDriverState *bs,
+ AioTaskPool *pool,
+ AioTaskFunc func,
+ QCow2ClusterType cluster_type,
+ uint64_t file_cluster_offset,
+ uint64_t offset,
+ uint64_t bytes,
+ QEMUIOVector *qiov,
+ size_t qiov_offset,
+ QCowL2Meta *l2meta)
+{
+ Qcow2AioTask local_task;
+ Qcow2AioTask *task = pool ? g_new(Qcow2AioTask, 1) : &local_task;
+
+ *task = (Qcow2AioTask) {
+ .task.func = func,
+ .bs = bs,
+ .cluster_type = cluster_type,
+ .qiov = qiov,
+ .file_cluster_offset = file_cluster_offset,
+ .offset = offset,
+ .bytes = bytes,
+ .qiov_offset = qiov_offset,
+ .l2meta = l2meta,
+ };
+
+ trace_qcow2_add_task(qemu_coroutine_self(), bs, pool,
+ func == qcow2_co_preadv_task_entry ? "read" : "write",
+ cluster_type, file_cluster_offset, offset, bytes,
+ qiov, qiov_offset);
+
+ if (!pool) {
+ return func(&task->task);
+ }
+
+ aio_task_pool_start_task(pool, &task->task);
+
+ return 0;
+}
+
+static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs,
+ QCow2ClusterType cluster_type,
+ uint64_t file_cluster_offset,
+ uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov,
+ size_t qiov_offset)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int offset_in_cluster = offset_into_cluster(s, offset);
+
+ switch (cluster_type) {
+ case QCOW2_CLUSTER_ZERO_PLAIN:
+ case QCOW2_CLUSTER_ZERO_ALLOC:
+ /* Both zero types are handled in qcow2_co_preadv_part */
+ g_assert_not_reached();
+
+ case QCOW2_CLUSTER_UNALLOCATED:
+ assert(bs->backing); /* otherwise handled in qcow2_co_preadv_part */
+
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
+ return bdrv_co_preadv_part(bs->backing, offset, bytes,
+ qiov, qiov_offset, 0);
+
+ case QCOW2_CLUSTER_COMPRESSED:
+ return qcow2_co_preadv_compressed(bs, file_cluster_offset,
+ offset, bytes, qiov, qiov_offset);
+
+ case QCOW2_CLUSTER_NORMAL:
+ if ((file_cluster_offset & 511) != 0) {
+ return -EIO;
+ }
+
+ if (bs->encrypted) {
+ return qcow2_co_preadv_encrypted(bs, file_cluster_offset,
+ offset, bytes, qiov, qiov_offset);
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
+ return bdrv_co_preadv_part(s->data_file,
+ file_cluster_offset + offset_in_cluster,
+ bytes, qiov, qiov_offset, 0);
+
+ default:
+ g_assert_not_reached();
+ }
+
+ g_assert_not_reached();
+}
+
+static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task)
+{
+ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task);
+
+ assert(!t->l2meta);
+
+ return qcow2_co_preadv_task(t->bs, t->cluster_type, t->file_cluster_offset,
+ t->offset, t->bytes, t->qiov, t->qiov_offset);
+}
+
static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov,
size_t qiov_offset, int flags)
{
BDRVQcow2State *s = bs->opaque;
- int offset_in_cluster;
- int ret;
+ int ret = 0;
unsigned int cur_bytes; /* number of bytes in current iteration */
uint64_t cluster_offset = 0;
- uint8_t *cluster_data = NULL;
+ AioTaskPool *aio = NULL;
- while (bytes != 0) {
-
+ while (bytes != 0 && aio_task_pool_status(aio) == 0) {
/* prepare next request */
cur_bytes = MIN(bytes, INT_MAX);
if (s->crypto) {
@@ -1997,110 +2162,39 @@
ret = qcow2_get_cluster_offset(bs, offset, &cur_bytes, &cluster_offset);
qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
- goto fail;
+ goto out;
}
- offset_in_cluster = offset_into_cluster(s, offset);
-
- switch (ret) {
- case QCOW2_CLUSTER_UNALLOCATED:
-
- if (bs->backing) {
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
- ret = bdrv_co_preadv_part(bs->backing, offset, cur_bytes,
- qiov, qiov_offset, 0);
- if (ret < 0) {
- goto fail;
- }
- } else {
- /* Note: in this case, no need to wait */
- qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes);
- }
- break;
-
- case QCOW2_CLUSTER_ZERO_PLAIN:
- case QCOW2_CLUSTER_ZERO_ALLOC:
+ if (ret == QCOW2_CLUSTER_ZERO_PLAIN ||
+ ret == QCOW2_CLUSTER_ZERO_ALLOC ||
+ (ret == QCOW2_CLUSTER_UNALLOCATED && !bs->backing))
+ {
qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes);
- break;
-
- case QCOW2_CLUSTER_COMPRESSED:
- ret = qcow2_co_preadv_compressed(bs, cluster_offset,
- offset, cur_bytes,
- qiov, qiov_offset);
+ } else {
+ if (!aio && cur_bytes != bytes) {
+ aio = aio_task_pool_new(QCOW2_MAX_WORKERS);
+ }
+ ret = qcow2_add_task(bs, aio, qcow2_co_preadv_task_entry, ret,
+ cluster_offset, offset, cur_bytes,
+ qiov, qiov_offset, NULL);
if (ret < 0) {
- goto fail;
+ goto out;
}
-
- break;
-
- case QCOW2_CLUSTER_NORMAL:
- if ((cluster_offset & 511) != 0) {
- ret = -EIO;
- goto fail;
- }
-
- if (bs->encrypted) {
- assert(s->crypto);
-
- /*
- * For encrypted images, read everything into a temporary
- * contiguous buffer on which the AES functions can work.
- */
- if (!cluster_data) {
- cluster_data =
- qemu_try_blockalign(s->data_file->bs,
- QCOW_MAX_CRYPT_CLUSTERS
- * s->cluster_size);
- if (cluster_data == NULL) {
- ret = -ENOMEM;
- goto fail;
- }
- }
-
- assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
-
- BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
- ret = bdrv_co_pread(s->data_file,
- cluster_offset + offset_in_cluster,
- cur_bytes, cluster_data, 0);
- if (ret < 0) {
- goto fail;
- }
-
- assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
- assert(QEMU_IS_ALIGNED(cur_bytes, BDRV_SECTOR_SIZE));
- if (qcow2_co_decrypt(bs, cluster_offset + offset_in_cluster,
- offset,
- cluster_data, cur_bytes) < 0) {
- ret = -EIO;
- goto fail;
- }
- qemu_iovec_from_buf(qiov, qiov_offset, cluster_data, cur_bytes);
- } else {
- BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
- ret = bdrv_co_preadv_part(s->data_file,
- cluster_offset + offset_in_cluster,
- cur_bytes, qiov, qiov_offset, 0);
- if (ret < 0) {
- goto fail;
- }
- }
- break;
-
- default:
- g_assert_not_reached();
- ret = -EIO;
- goto fail;
}
bytes -= cur_bytes;
offset += cur_bytes;
qiov_offset += cur_bytes;
}
- ret = 0;
-fail:
- qemu_vfree(cluster_data);
+out:
+ if (aio) {
+ aio_task_pool_wait_all(aio);
+ if (ret == 0) {
+ ret = aio_task_pool_status(aio);
+ }
+ g_free(aio);
+ }
return ret;
}
@@ -2225,6 +2319,99 @@
return 0;
}
+/*
+ * qcow2_co_pwritev_task
+ * Called with s->lock unlocked
+ * l2meta - if not NULL, qcow2_co_pwritev_task() will consume it. Caller must
+ * not use it somehow after qcow2_co_pwritev_task() call
+ */
+static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs,
+ uint64_t file_cluster_offset,
+ uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov,
+ uint64_t qiov_offset,
+ QCowL2Meta *l2meta)
+{
+ int ret;
+ BDRVQcow2State *s = bs->opaque;
+ void *crypt_buf = NULL;
+ int offset_in_cluster = offset_into_cluster(s, offset);
+ QEMUIOVector encrypted_qiov;
+
+ if (bs->encrypted) {
+ assert(s->crypto);
+ assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
+ crypt_buf = qemu_try_blockalign(bs->file->bs, bytes);
+ if (crypt_buf == NULL) {
+ ret = -ENOMEM;
+ goto out_unlocked;
+ }
+ qemu_iovec_to_buf(qiov, qiov_offset, crypt_buf, bytes);
+
+ if (qcow2_co_encrypt(bs, file_cluster_offset + offset_in_cluster,
+ offset, crypt_buf, bytes) < 0)
+ {
+ ret = -EIO;
+ goto out_unlocked;
+ }
+
+ qemu_iovec_init_buf(&encrypted_qiov, crypt_buf, bytes);
+ qiov = &encrypted_qiov;
+ qiov_offset = 0;
+ }
+
+ /* Try to efficiently initialize the physical space with zeroes */
+ ret = handle_alloc_space(bs, l2meta);
+ if (ret < 0) {
+ goto out_unlocked;
+ }
+
+ /*
+ * If we need to do COW, check if it's possible to merge the
+ * writing of the guest data together with that of the COW regions.
+ * If it's not possible (or not necessary) then write the
+ * guest data now.
+ */
+ if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) {
+ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
+ trace_qcow2_writev_data(qemu_coroutine_self(),
+ file_cluster_offset + offset_in_cluster);
+ ret = bdrv_co_pwritev_part(s->data_file,
+ file_cluster_offset + offset_in_cluster,
+ bytes, qiov, qiov_offset, 0);
+ if (ret < 0) {
+ goto out_unlocked;
+ }
+ }
+
+ qemu_co_mutex_lock(&s->lock);
+
+ ret = qcow2_handle_l2meta(bs, &l2meta, true);
+ goto out_locked;
+
+out_unlocked:
+ qemu_co_mutex_lock(&s->lock);
+
+out_locked:
+ qcow2_handle_l2meta(bs, &l2meta, false);
+ qemu_co_mutex_unlock(&s->lock);
+
+ qemu_vfree(crypt_buf);
+
+ return ret;
+}
+
+static coroutine_fn int qcow2_co_pwritev_task_entry(AioTask *task)
+{
+ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task);
+
+ assert(!t->cluster_type);
+
+ return qcow2_co_pwritev_task(t->bs, t->file_cluster_offset,
+ t->offset, t->bytes, t->qiov, t->qiov_offset,
+ t->l2meta);
+}
+
static coroutine_fn int qcow2_co_pwritev_part(
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, size_t qiov_offset, int flags)
@@ -2234,16 +2421,12 @@
int ret;
unsigned int cur_bytes; /* number of sectors in current iteration */
uint64_t cluster_offset;
- QEMUIOVector encrypted_qiov;
- uint64_t bytes_done = 0;
- uint8_t *cluster_data = NULL;
QCowL2Meta *l2meta = NULL;
+ AioTaskPool *aio = NULL;
trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes);
- qemu_co_mutex_lock(&s->lock);
-
- while (bytes != 0) {
+ while (bytes != 0 && aio_task_pool_status(aio) == 0) {
l2meta = NULL;
@@ -2256,6 +2439,8 @@
- offset_in_cluster);
}
+ qemu_co_mutex_lock(&s->lock);
+
ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes,
&cluster_offset, &l2meta);
if (ret < 0) {
@@ -2273,73 +2458,24 @@
qemu_co_mutex_unlock(&s->lock);
- if (bs->encrypted) {
- assert(s->crypto);
- if (!cluster_data) {
- cluster_data = qemu_try_blockalign(bs->file->bs,
- QCOW_MAX_CRYPT_CLUSTERS
- * s->cluster_size);
- if (cluster_data == NULL) {
- ret = -ENOMEM;
- goto out_unlocked;
- }
- }
-
- assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
- qemu_iovec_to_buf(qiov, qiov_offset + bytes_done,
- cluster_data, cur_bytes);
-
- if (qcow2_co_encrypt(bs, cluster_offset + offset_in_cluster, offset,
- cluster_data, cur_bytes) < 0) {
- ret = -EIO;
- goto out_unlocked;
- }
-
- qemu_iovec_init_buf(&encrypted_qiov, cluster_data, cur_bytes);
+ if (!aio && cur_bytes != bytes) {
+ aio = aio_task_pool_new(QCOW2_MAX_WORKERS);
}
-
- /* Try to efficiently initialize the physical space with zeroes */
- ret = handle_alloc_space(bs, l2meta);
+ ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_task_entry, 0,
+ cluster_offset, offset, cur_bytes,
+ qiov, qiov_offset, l2meta);
+ l2meta = NULL; /* l2meta is consumed by qcow2_co_pwritev_task() */
if (ret < 0) {
- goto out_unlocked;
- }
-
- /* If we need to do COW, check if it's possible to merge the
- * writing of the guest data together with that of the COW regions.
- * If it's not possible (or not necessary) then write the
- * guest data now. */
- if (!merge_cow(offset, cur_bytes,
- bs->encrypted ? &encrypted_qiov : qiov,
- bs->encrypted ? 0 : qiov_offset + bytes_done, l2meta))
- {
- BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
- trace_qcow2_writev_data(qemu_coroutine_self(),
- cluster_offset + offset_in_cluster);
- ret = bdrv_co_pwritev_part(
- s->data_file, cluster_offset + offset_in_cluster, cur_bytes,
- bs->encrypted ? &encrypted_qiov : qiov,
- bs->encrypted ? 0 : qiov_offset + bytes_done, 0);
- if (ret < 0) {
- goto out_unlocked;
- }
- }
-
- qemu_co_mutex_lock(&s->lock);
-
- ret = qcow2_handle_l2meta(bs, &l2meta, true);
- if (ret) {
- goto out_locked;
+ goto fail_nometa;
}
bytes -= cur_bytes;
offset += cur_bytes;
- bytes_done += cur_bytes;
+ qiov_offset += cur_bytes;
trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes);
}
ret = 0;
- goto out_locked;
-out_unlocked:
qemu_co_mutex_lock(&s->lock);
out_locked:
@@ -2347,7 +2483,15 @@
qemu_co_mutex_unlock(&s->lock);
- qemu_vfree(cluster_data);
+fail_nometa:
+ if (aio) {
+ aio_task_pool_wait_all(aio);
+ if (ret == 0) {
+ ret = aio_task_pool_status(aio);
+ }
+ g_free(aio);
+ }
+
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
return ret;
diff --git a/block/qcow2.h b/block/qcow2.h
index a488d76..f51f478 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -65,6 +65,9 @@
#define QCOW2_MAX_BITMAPS 65535
#define QCOW2_MAX_BITMAP_DIRECTORY_SIZE (1024 * QCOW2_MAX_BITMAPS)
+/* Maximum of parallel sub-request per guest request */
+#define QCOW2_MAX_WORKERS 8
+
/* indicate that the refcount of the referenced cluster is exactly one. */
#define QCOW_OFLAG_COPIED (1ULL << 63)
/* indicate that the cluster is compressed (they never have the copied flag) */
diff --git a/block/rbd.c b/block/rbd.c
index 057af43..c71e45d 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -22,6 +22,7 @@
#include "block/qdict.h"
#include "crypto/secret.h"
#include "qemu/cutils.h"
+#include "sysemu/replay.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
@@ -884,8 +885,8 @@
rcb->ret = rbd_aio_get_return_value(c);
rbd_aio_release(c);
- aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
- rbd_finish_bh, rcb);
+ replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs),
+ rbd_finish_bh, rcb);
}
static int rbd_aio_discard_wrapper(rbd_image_t image,
diff --git a/block/replication.c b/block/replication.c
index 936b2f8..99532ce 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -543,7 +543,7 @@
s->backup_job = backup_job_create(
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
- 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false,
+ 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL,
BLOCKDEV_ON_ERROR_REPORT,
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
backup_job_completed, bs, NULL, &local_err);
diff --git a/block/trace-events b/block/trace-events
index 04209f0..b8d70f5 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -40,12 +40,14 @@
# backup.c
backup_do_cow_enter(void *job, int64_t start, int64_t offset, uint64_t bytes) "job %p start %" PRId64 " offset %" PRId64 " bytes %" PRIu64
backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p offset %" PRId64 " bytes %" PRIu64 " ret %d"
-backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64
-backup_do_cow_skip_range(void *job, int64_t start, uint64_t bytes) "job %p start %"PRId64" bytes %"PRId64
-backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
-backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
-backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
-backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
+
+# block-copy.c
+block_copy_skip(void *bcs, int64_t start) "bcs %p start %"PRId64
+block_copy_skip_range(void *bcs, int64_t start, uint64_t bytes) "bcs %p start %"PRId64" bytes %"PRId64
+block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64
+block_copy_with_bounce_buffer_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
+block_copy_with_bounce_buffer_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
+block_copy_with_offload_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d"
# ../blockdev.c
qmp_block_job_cancel(void *job) "job %p"
@@ -62,6 +64,7 @@
file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64
# qcow2.c
+qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t file_cluster_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu"
qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d"
qcow2_writev_done_req(void *co, int ret) "co %p ret %d"
qcow2_writev_start_part(void *co) "co %p"
diff --git a/block/vhdx.c b/block/vhdx.c
index 6a09d0a..371f226 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -24,6 +24,7 @@
#include "qemu/option.h"
#include "qemu/crc32c.h"
#include "qemu/bswap.h"
+#include "qemu/error-report.h"
#include "vhdx.h"
#include "migration/blocker.h"
#include "qemu/uuid.h"
@@ -235,6 +236,9 @@
end = start + length;
QLIST_FOREACH(r, &s->regions, entries) {
if (!((start >= r->end) || (end <= r->start))) {
+ error_report("VHDX region %" PRIu64 "-%" PRIu64 " overlaps with "
+ "region %" PRIu64 "-%." PRIu64, start, end, r->start,
+ r->end);
ret = -EINVAL;
goto exit;
}
@@ -877,6 +881,95 @@
}
+static int vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt)
+{
+ BDRVVHDXState *s = bs->opaque;
+ int64_t image_file_size = bdrv_getlength(bs->file->bs);
+ uint64_t payblocks = s->chunk_ratio;
+ uint64_t i;
+ int ret = 0;
+
+ if (image_file_size < 0) {
+ error_report("Could not determinate VHDX image file size.");
+ return image_file_size;
+ }
+
+ for (i = 0; i < s->bat_entries; i++) {
+ if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
+ PAYLOAD_BLOCK_FULLY_PRESENT) {
+ uint64_t offset = s->bat[i] & VHDX_BAT_FILE_OFF_MASK;
+ /*
+ * Allow that the last block exists only partially. The VHDX spec
+ * states that the image file can only grow in blocksize increments,
+ * but QEMU created images with partial last blocks in the past.
+ */
+ uint32_t block_length = MIN(s->block_size,
+ bs->total_sectors * BDRV_SECTOR_SIZE - i * s->block_size);
+ /*
+ * Check for BAT entry overflow.
+ */
+ if (offset > INT64_MAX - s->block_size) {
+ error_report("VHDX BAT entry %" PRIu64 " offset overflow.", i);
+ ret = -EINVAL;
+ if (!errcnt) {
+ break;
+ }
+ (*errcnt)++;
+ }
+ /*
+ * Check if fully allocated BAT entries do not reside after
+ * end of the image file.
+ */
+ if (offset >= image_file_size) {
+ error_report("VHDX BAT entry %" PRIu64 " start offset %" PRIu64
+ " points after end of file (%" PRIi64 "). Image"
+ " has probably been truncated.",
+ i, offset, image_file_size);
+ ret = -EINVAL;
+ if (!errcnt) {
+ break;
+ }
+ (*errcnt)++;
+ } else if (offset + block_length > image_file_size) {
+ error_report("VHDX BAT entry %" PRIu64 " end offset %" PRIu64
+ " points after end of file (%" PRIi64 "). Image"
+ " has probably been truncated.",
+ i, offset + block_length - 1, image_file_size);
+ ret = -EINVAL;
+ if (!errcnt) {
+ break;
+ }
+ (*errcnt)++;
+ }
+
+ /*
+ * verify populated BAT field file offsets against
+ * region table and log entries
+ */
+ if (payblocks--) {
+ /* payload bat entries */
+ int ret2;
+ ret2 = vhdx_region_check(s, offset, s->block_size);
+ if (ret2 < 0) {
+ ret = -EINVAL;
+ if (!errcnt) {
+ break;
+ }
+ (*errcnt)++;
+ }
+ } else {
+ payblocks = s->chunk_ratio;
+ /*
+ * Once differencing files are supported, verify sector bitmap
+ * blocks here
+ */
+ }
+ }
+ }
+
+ return ret;
+}
+
static void vhdx_close(BlockDriverState *bs)
{
BDRVVHDXState *s = bs->opaque;
@@ -981,25 +1074,15 @@
goto fail;
}
- uint64_t payblocks = s->chunk_ratio;
- /* endian convert, and verify populated BAT field file offsets against
- * region table and log entries */
+ /* endian convert populated BAT field entires */
for (i = 0; i < s->bat_entries; i++) {
s->bat[i] = le64_to_cpu(s->bat[i]);
- if (payblocks--) {
- /* payload bat entries */
- if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
- PAYLOAD_BLOCK_FULLY_PRESENT) {
- ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK,
- s->block_size);
- if (ret < 0) {
- goto fail;
- }
- }
- } else {
- payblocks = s->chunk_ratio;
- /* Once differencing files are supported, verify sector bitmap
- * blocks here */
+ }
+
+ if (!(flags & BDRV_O_CHECK)) {
+ ret = vhdx_check_bat_entries(bs, NULL);
+ if (ret < 0) {
+ goto fail;
}
}
@@ -2072,6 +2155,9 @@
if (s->log_replayed_on_open) {
result->corruptions_fixed++;
}
+
+ vhdx_check_bat_entries(bs, &result->corruptions);
+
return 0;
}
diff --git a/block/vxhs.c b/block/vxhs.c
index 77fd5eb..d79fc97 100644
--- a/block/vxhs.c
+++ b/block/vxhs.c
@@ -22,6 +22,7 @@
#include "qapi/error.h"
#include "qemu/uuid.h"
#include "crypto/tlscredsx509.h"
+#include "sysemu/replay.h"
#define VXHS_OPT_FILENAME "filename"
#define VXHS_OPT_VDISK_ID "vdisk-id"
@@ -105,8 +106,8 @@
trace_vxhs_iio_callback(error);
}
- aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
- vxhs_complete_aio_bh, acb);
+ replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs),
+ vxhs_complete_aio_bh, acb);
break;
default:
diff --git a/blockdev.c b/blockdev.c
index fbef684..f89e48f 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3601,6 +3601,7 @@
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
backup->sync, bmap, backup->bitmap_mode,
backup->compress,
+ backup->filter_node_name,
backup->on_source_error,
backup->on_target_error,
job_flags, NULL, NULL, txn, errp);
diff --git a/configure b/configure
index 08ca4bc..145fcab 100755
--- a/configure
+++ b/configure
@@ -381,6 +381,7 @@
vhost_scsi=""
vhost_vsock=""
vhost_user=""
+vhost_user_fs=""
kvm="no"
hax="no"
hvf="no"
@@ -1293,6 +1294,10 @@
;;
--enable-vhost-vsock) vhost_vsock="yes"
;;
+ --disable-vhost-user-fs) vhost_user_fs="no"
+ ;;
+ --enable-vhost-user-fs) vhost_user_fs="yes"
+ ;;
--disable-opengl) opengl="no"
;;
--enable-opengl) opengl="yes"
@@ -2236,6 +2241,10 @@
if test "$vhost_crypto" = "yes" && test "$vhost_user" = "no"; then
error_exit "--enable-vhost-crypto requires --enable-vhost-user"
fi
+test "$vhost_user_fs" = "" && vhost_user_fs=$vhost_user
+if test "$vhost_user_fs" = "yes" && test "$vhost_user" = "no"; then
+ error_exit "--enable-vhost-user-fs requires --enable-vhost-user"
+fi
# OR the vhost-kernel and vhost-user values for simplicity
if test "$vhost_net" = ""; then
@@ -6374,6 +6383,7 @@
echo "vhost-scsi support $vhost_scsi"
echo "vhost-vsock support $vhost_vsock"
echo "vhost-user support $vhost_user"
+echo "vhost-user-fs support $vhost_user_fs"
echo "Trace backends $trace_backends"
if have_backend "simple"; then
echo "Trace output file $trace_file-<pid>"
@@ -6870,6 +6880,9 @@
if test "$vhost_user" = "yes" ; then
echo "CONFIG_VHOST_USER=y" >> $config_host_mak
fi
+if test "$vhost_user_fs" = "yes" ; then
+ echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak
+fi
if test "$blobs" = "yes" ; then
echo "INSTALL_BLOBS=yes" >> $config_host_mak
fi
diff --git a/cpus.c b/cpus.c
index bee7209..fabbeca 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1107,7 +1107,6 @@
}
bdrv_drain_all();
- replay_disable_events();
ret = bdrv_flush_all();
return ret;
@@ -2193,7 +2192,6 @@
/* We are sending this now, but the CPUs will be resumed shortly later */
qapi_event_send_resume();
- replay_enable_events();
cpu_enable_ticks();
runstate_set(RUN_STATE_RUNNING);
vm_state_notify(1, RUN_STATE_RUNNING);
diff --git a/docs/devel/rcu.txt b/docs/devel/rcu.txt
index c84e7f4..d83fed2 100644
--- a/docs/devel/rcu.txt
+++ b/docs/devel/rcu.txt
@@ -187,6 +187,22 @@
Note that these APIs are relatively heavyweight, and should _not_ be
nested.
+Convenience macros
+==================
+
+Two macros are provided that automatically release the read lock at the
+end of the scope.
+
+ RCU_READ_LOCK_GUARD()
+
+ Takes the lock and will release it at the end of the block it's
+ used in.
+
+ WITH_RCU_READ_LOCK_GUARD() { code }
+
+ Is used at the head of a block to protect the code within the block.
+
+Note that 'goto'ing out of the guarded block will also drop the lock.
DIFFERENCES WITH LINUX
======================
diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt
index 8231bbf..8c0376f 100644
--- a/docs/devel/tracing.txt
+++ b/docs/devel/tracing.txt
@@ -317,7 +317,8 @@
--binary path/to/qemu-binary \
--target-type system \
--target-name x86_64 \
- <trace-events-all >qemu.stp
+ --group=all \
+ trace-events-all >qemu.stp
To facilitate simple usage of systemtap where there merely needs to be printf
logging of certain probes, a helper script "qemu-trace-stap" is provided.
diff --git a/docs/replay.txt b/docs/replay.txt
index ee6aee9..ce97c3f 100644
--- a/docs/replay.txt
+++ b/docs/replay.txt
@@ -27,7 +27,7 @@
* First, record the execution with the following command line:
qemu-system-i386 \
-icount shift=7,rr=record,rrfile=replay.bin \
- -drive file=disk.qcow2,if=none,id=img-direct \
+ -drive file=disk.qcow2,if=none,snapshot,id=img-direct \
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
-device ide-hd,drive=img-blkreplay \
-netdev user,id=net1 -device rtl8139,netdev=net1 \
@@ -35,7 +35,7 @@
* After recording, you can replay it by using another command line:
qemu-system-i386 \
-icount shift=7,rr=replay,rrfile=replay.bin \
- -drive file=disk.qcow2,if=none,id=img-direct \
+ -drive file=disk.qcow2,if=none,snapshot,id=img-direct \
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
-device ide-hd,drive=img-blkreplay \
-netdev user,id=net1 -device rtl8139,netdev=net1 \
@@ -223,7 +223,7 @@
bdrv coroutine functions at the top of block drivers stack.
To record and replay block operations the drive must be configured
as following:
- -drive file=disk.qcow2,if=none,id=img-direct
+ -drive file=disk.qcow2,if=none,snapshot,id=img-direct
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
-device ide-hd,drive=img-blkreplay
@@ -252,6 +252,12 @@
of replaying. It also can be loaded while replaying to roll back
the execution.
+'snapshot' flag of the disk image must be removed to save the snapshots
+in the overlay (or original image) instead of using the temporary overlay.
+ -drive file=disk.ovl,if=none,id=img-direct
+ -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
+ -device ide-hd,drive=img-blkreplay
+
Use QEMU monitor to create additional snapshots. 'savevm <name>' command
created the snapshot and 'loadvm <name>' restores it. To prevent corruption
of the original disk image, use overlay files linked to the original images.
diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst
new file mode 100644
index 0000000..911a982
--- /dev/null
+++ b/docs/specs/acpi_hw_reduced_hotplug.rst
@@ -0,0 +1,70 @@
+==================================================
+QEMU and ACPI BIOS Generic Event Device interface
+==================================================
+
+The ACPI *Generic Event Device* (GED) is a HW reduced platform
+specific device introduced in ACPI v6.1 that handles all platform
+events, including the hotplug ones. GED is modelled as a device
+in the namespace with a _HID defined to be ACPI0013. This document
+describes the interface between QEMU and the ACPI BIOS.
+
+GED allows HW reduced platforms to handle interrupts in ACPI ASL
+statements. It follows a very similar approach to the _EVT method
+from GPIO events. All interrupts are listed in _CRS and the handler
+is written in _EVT method. However, the QEMU implementation uses a
+single interrupt for the GED device, relying on an IO memory region
+to communicate the type of device affected by the interrupt. This way,
+we can support up to 32 events with a unique interrupt.
+
+**Here is an example,**
+
+::
+
+ Device (\_SB.GED)
+ {
+ Name (_HID, "ACPI0013")
+ Name (_UID, Zero)
+ Name (_CRS, ResourceTemplate ()
+ {
+ Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
+ {
+ 0x00000029,
+ }
+ })
+ OperationRegion (EREG, SystemMemory, 0x09080000, 0x04)
+ Field (EREG, DWordAcc, NoLock, WriteAsZeros)
+ {
+ ESEL, 32
+ }
+ Method (_EVT, 1, Serialized)
+ {
+ Local0 = ESEL // ESEL = IO memory region which specifies the
+ // device type.
+ If (((Local0 & One) == One))
+ {
+ MethodEvent1()
+ }
+ If ((Local0 & 0x2) == 0x2)
+ {
+ MethodEvent2()
+ }
+ ...
+ }
+ }
+
+GED IO interface (4 byte access)
+--------------------------------
+**read access:**
+
+::
+
+ [0x0-0x3] Event selector bit field (32 bit) set by QEMU.
+
+ bits:
+ 0: Memory hotplug event
+ 1: System power down event
+ 2-31: Reserved
+
+**write_access:**
+
+Nothing is expected to be written into GED IO memory
diff --git a/docs/specs/index.rst b/docs/specs/index.rst
index 40adb97..984ba44 100644
--- a/docs/specs/index.rst
+++ b/docs/specs/index.rst
@@ -12,3 +12,4 @@
ppc-xive
ppc-spapr-xive
+ acpi_hw_reduced_hotplug
diff --git a/exec.c b/exec.c
index bdcfcdf..fb0943c 100644
--- a/exec.c
+++ b/exec.c
@@ -1037,16 +1037,14 @@
return;
}
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
mr = address_space_translate(as, addr, &addr, &l, false, attrs);
if (!(memory_region_is_ram(mr)
|| memory_region_is_romd(mr))) {
- rcu_read_unlock();
return;
}
ram_addr = memory_region_get_ram_addr(mr) + addr;
tb_invalidate_phys_page_range(ram_addr, ram_addr + 1);
- rcu_read_unlock();
}
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
@@ -1332,14 +1330,13 @@
end = TARGET_PAGE_ALIGN(start + length);
start &= TARGET_PAGE_MASK;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
block = qemu_get_ram_block(start);
assert(block == qemu_get_ram_block(end - 1));
start1 = (uintptr_t)ramblock_ptr(block, start - block->offset);
CPU_FOREACH(cpu) {
tlb_reset_dirty(cpu, start1, length);
}
- rcu_read_unlock();
}
/* Note: start and end must be within the same ram block. */
@@ -1360,30 +1357,29 @@
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
+ ramblock = qemu_get_ram_block(start);
+ /* Range sanity check on the ramblock */
+ assert(start >= ramblock->offset &&
+ start + length <= ramblock->offset + ramblock->used_length);
- blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
- ramblock = qemu_get_ram_block(start);
- /* Range sanity check on the ramblock */
- assert(start >= ramblock->offset &&
- start + length <= ramblock->offset + ramblock->used_length);
+ while (page < end) {
+ unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
+ unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
+ unsigned long num = MIN(end - page,
+ DIRTY_MEMORY_BLOCK_SIZE - offset);
- while (page < end) {
- unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset);
+ dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx],
+ offset, num);
+ page += num;
+ }
- dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx],
- offset, num);
- page += num;
+ mr_offset = (ram_addr_t)(page << TARGET_PAGE_BITS) - ramblock->offset;
+ mr_size = (end - page) << TARGET_PAGE_BITS;
+ memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size);
}
- mr_offset = (ram_addr_t)(page << TARGET_PAGE_BITS) - ramblock->offset;
- mr_size = (end - page) << TARGET_PAGE_BITS;
- memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size);
-
- rcu_read_unlock();
-
if (dirty && tcg_enabled()) {
tlb_reset_dirty_range_all(start, length);
}
@@ -1411,28 +1407,27 @@
end = last >> TARGET_PAGE_BITS;
dest = 0;
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
- blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
+ while (page < end) {
+ unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
+ unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
+ unsigned long num = MIN(end - page,
+ DIRTY_MEMORY_BLOCK_SIZE - offset);
- while (page < end) {
- unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset);
+ assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL)));
+ assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL)));
+ offset >>= BITS_PER_LEVEL;
- assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL)));
- assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL)));
- offset >>= BITS_PER_LEVEL;
-
- bitmap_copy_and_clear_atomic(snap->dirty + dest,
- blocks->blocks[idx] + offset,
- num);
- page += num;
- dest += num >> BITS_PER_LEVEL;
+ bitmap_copy_and_clear_atomic(snap->dirty + dest,
+ blocks->blocks[idx] + offset,
+ num);
+ page += num;
+ dest += num >> BITS_PER_LEVEL;
+ }
}
- rcu_read_unlock();
-
if (tcg_enabled()) {
tlb_reset_dirty_range_all(start, length);
}
@@ -1643,7 +1638,7 @@
RAMBlock *block;
char *psize;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
monitor_printf(mon, "%24s %8s %18s %18s %18s\n",
"Block Name", "PSize", "Offset", "Used", "Total");
RAMBLOCK_FOREACH(block) {
@@ -1655,7 +1650,6 @@
(uint64_t)block->max_length);
g_free(psize);
}
- rcu_read_unlock();
}
#ifdef __linux__
@@ -2009,11 +2003,10 @@
RAMBlock *block;
ram_addr_t last = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
RAMBLOCK_FOREACH(block) {
last = MAX(last, block->offset + block->max_length);
}
- rcu_read_unlock();
return last >> TARGET_PAGE_BITS;
}
@@ -2100,7 +2093,7 @@
}
pstrcat(new_block->idstr, sizeof(new_block->idstr), name);
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
RAMBLOCK_FOREACH(block) {
if (block != new_block &&
!strcmp(block->idstr, new_block->idstr)) {
@@ -2109,7 +2102,6 @@
abort();
}
}
- rcu_read_unlock();
}
/* Called with iothread lock held. */
@@ -2651,17 +2643,16 @@
if (xen_enabled()) {
ram_addr_t ram_addr;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
ram_addr = xen_ram_addr_from_mapcache(ptr);
block = qemu_get_ram_block(ram_addr);
if (block) {
*offset = ram_addr - block->offset;
}
- rcu_read_unlock();
return block;
}
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
block = atomic_rcu_read(&ram_list.mru_block);
if (block && block->host && host - block->host < block->max_length) {
goto found;
@@ -2677,7 +2668,6 @@
}
}
- rcu_read_unlock();
return NULL;
found:
@@ -2685,7 +2675,6 @@
if (round_offset) {
*offset &= TARGET_PAGE_MASK;
}
- rcu_read_unlock();
return block;
}
@@ -3281,10 +3270,9 @@
FlatView *fv;
if (len > 0) {
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
fv = address_space_to_flatview(as);
result = flatview_read(fv, addr, attrs, buf, len);
- rcu_read_unlock();
}
return result;
@@ -3298,10 +3286,9 @@
FlatView *fv;
if (len > 0) {
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
fv = address_space_to_flatview(as);
result = flatview_write(fv, addr, attrs, buf, len);
- rcu_read_unlock();
}
return result;
@@ -3341,7 +3328,7 @@
hwaddr addr1;
MemoryRegion *mr;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
while (len > 0) {
l = len;
mr = address_space_translate(as, addr, &addr1, &l, true, attrs);
@@ -3366,7 +3353,6 @@
buf += l;
addr += l;
}
- rcu_read_unlock();
return MEMTX_OK;
}
@@ -3511,10 +3497,9 @@
FlatView *fv;
bool result;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
fv = address_space_to_flatview(as);
result = flatview_access_valid(fv, addr, len, is_write, attrs);
- rcu_read_unlock();
return result;
}
@@ -3569,13 +3554,12 @@
}
l = len;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
fv = address_space_to_flatview(as);
mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs);
if (!memory_access_is_direct(mr, is_write)) {
if (atomic_xchg(&bounce.in_use, true)) {
- rcu_read_unlock();
return NULL;
}
/* Avoid unbounded allocations */
@@ -3591,7 +3575,6 @@
bounce.buffer, l);
}
- rcu_read_unlock();
*plen = l;
return bounce.buffer;
}
@@ -3601,7 +3584,6 @@
*plen = flatview_extend_translation(fv, addr, len, mr, xlat,
l, is_write, attrs);
ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true);
- rcu_read_unlock();
return ptr;
}
@@ -3869,13 +3851,12 @@
hwaddr l = 1;
bool res;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
mr = address_space_translate(&address_space_memory,
phys_addr, &phys_addr, &l, false,
MEMTXATTRS_UNSPECIFIED);
res = !(memory_region_is_ram(mr) || memory_region_is_romd(mr));
- rcu_read_unlock();
return res;
}
@@ -3884,14 +3865,13 @@
RAMBlock *block;
int ret = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
RAMBLOCK_FOREACH(block) {
ret = func(block, opaque);
if (ret) {
break;
}
}
- rcu_read_unlock();
return ret;
}
diff --git a/fsdev/9p-marshal.h b/fsdev/9p-marshal.h
index c8823d8..8f3babb6 100644
--- a/fsdev/9p-marshal.h
+++ b/fsdev/9p-marshal.h
@@ -9,9 +9,9 @@
typedef struct V9fsQID
{
- int8_t type;
- int32_t version;
- int64_t path;
+ uint8_t type;
+ uint32_t version;
+ uint64_t path;
} V9fsQID;
typedef struct V9fsStat
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index c757c80..f2f7772 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -59,6 +59,11 @@
#define V9FS_RDONLY 0x00000040
#define V9FS_PROXY_SOCK_FD 0x00000080
#define V9FS_PROXY_SOCK_NAME 0x00000100
+/*
+ * multidevs option (either one of the two applies exclusively)
+ */
+#define V9FS_REMAP_INODES 0x00000200
+#define V9FS_FORBID_MULTIDEVS 0x00000400
#define V9FS_SEC_MASK 0x0000003C
diff --git a/fsdev/qemu-fsdev-opts.c b/fsdev/qemu-fsdev-opts.c
index 7c31fff..07a18c6 100644
--- a/fsdev/qemu-fsdev-opts.c
+++ b/fsdev/qemu-fsdev-opts.c
@@ -31,7 +31,9 @@
}, {
.name = "readonly",
.type = QEMU_OPT_BOOL,
-
+ }, {
+ .name = "multidevs",
+ .type = QEMU_OPT_STRING,
}, {
.name = "socket",
.type = QEMU_OPT_STRING,
@@ -76,6 +78,9 @@
.name = "readonly",
.type = QEMU_OPT_BOOL,
}, {
+ .name = "multidevs",
+ .type = QEMU_OPT_STRING,
+ }, {
.name = "socket",
.type = QEMU_OPT_STRING,
}, {
diff --git a/fsdev/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c
index fe1d76d..5c83a1c 100644
--- a/fsdev/qemu-fsdev-throttle.c
+++ b/fsdev/qemu-fsdev-throttle.c
@@ -31,7 +31,7 @@
qemu_co_enter_next(&fst->throttled_reqs[true], NULL);
}
-void fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp)
+int fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp)
{
throttle_config_init(&fst->cfg);
fst->cfg.buckets[THROTTLE_BPS_TOTAL].avg =
@@ -75,7 +75,7 @@
fst->cfg.op_size =
qemu_opt_get_number(opts, "throttling.iops-size", 0);
- throttle_is_valid(&fst->cfg, errp);
+ return throttle_is_valid(&fst->cfg, errp) ? 0 : -1;
}
void fsdev_throttle_init(FsThrottle *fst)
diff --git a/fsdev/qemu-fsdev-throttle.h b/fsdev/qemu-fsdev-throttle.h
index c98e2fe..a21aecd 100644
--- a/fsdev/qemu-fsdev-throttle.h
+++ b/fsdev/qemu-fsdev-throttle.h
@@ -26,7 +26,7 @@
CoQueue throttled_reqs[2];
} FsThrottle;
-void fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **);
+int fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **);
void fsdev_throttle_init(FsThrottle *);
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
index 077a8c4..a9e069c 100644
--- a/fsdev/qemu-fsdev.c
+++ b/fsdev/qemu-fsdev.c
@@ -58,6 +58,7 @@
"writeout",
"fmode",
"dmode",
+ "multidevs",
"throttling.bps-total",
"throttling.bps-read",
"throttling.bps-write",
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index 08e673a..4708c0b 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -1465,6 +1465,10 @@
{
LocalData *data = ctx->private;
+ if (!data) {
+ return;
+ }
+
close(data->mountfd);
g_free(data);
}
@@ -1479,6 +1483,7 @@
{
const char *sec_model = qemu_opt_get(opts, "security_model");
const char *path = qemu_opt_get(opts, "path");
+ const char *multidevs = qemu_opt_get(opts, "multidevs");
Error *local_err = NULL;
if (!sec_model) {
@@ -1502,13 +1507,32 @@
return -1;
}
+ if (multidevs) {
+ if (!strcmp(multidevs, "remap")) {
+ fse->export_flags &= ~V9FS_FORBID_MULTIDEVS;
+ fse->export_flags |= V9FS_REMAP_INODES;
+ } else if (!strcmp(multidevs, "forbid")) {
+ fse->export_flags &= ~V9FS_REMAP_INODES;
+ fse->export_flags |= V9FS_FORBID_MULTIDEVS;
+ } else if (!strcmp(multidevs, "warn")) {
+ fse->export_flags &= ~V9FS_FORBID_MULTIDEVS;
+ fse->export_flags &= ~V9FS_REMAP_INODES;
+ } else {
+ error_setg(&local_err, "invalid multidevs property '%s'",
+ multidevs);
+ error_append_hint(&local_err, "Valid options are: multidevs="
+ "[remap|forbid|warn]\n");
+ error_propagate(errp, local_err);
+ return -1;
+ }
+ }
+
if (!path) {
error_setg(errp, "path property not set");
return -1;
}
- fsdev_throttle_parse_opts(opts, &fse->fst, &local_err);
- if (local_err) {
+ if (fsdev_throttle_parse_opts(opts, &fse->fst, &local_err)) {
error_propagate_prepend(errp, local_err,
"invalid throttle configuration: ");
return -1;
diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c
index 57a8c1c..97ab9c5 100644
--- a/hw/9pfs/9p-proxy.c
+++ b/hw/9pfs/9p-proxy.c
@@ -1185,6 +1185,10 @@
{
V9fsProxy *proxy = ctx->private;
+ if (!proxy) {
+ return;
+ }
+
g_free(proxy->out_iovec.iov_base);
g_free(proxy->in_iovec.iov_base);
if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index cce2366..37abcdb 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -26,6 +26,8 @@
#include "trace.h"
#include "migration/blocker.h"
#include "sysemu/qtest.h"
+#include "qemu/xxhash.h"
+#include <math.h>
int open_fd_hw;
int total_open_fd;
@@ -572,14 +574,374 @@
P9_STAT_MODE_NAMED_PIPE | \
P9_STAT_MODE_SOCKET)
-/* This is the algorithm from ufs in spfs */
-static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
+/* Mirrors all bits of a byte. So e.g. binary 10100000 would become 00000101. */
+static inline uint8_t mirror8bit(uint8_t byte)
{
+ return (byte * 0x0202020202ULL & 0x010884422010ULL) % 1023;
+}
+
+/* Same as mirror8bit() just for a 64 bit data type instead for a byte. */
+static inline uint64_t mirror64bit(uint64_t value)
+{
+ return ((uint64_t)mirror8bit(value & 0xff) << 56) |
+ ((uint64_t)mirror8bit((value >> 8) & 0xff) << 48) |
+ ((uint64_t)mirror8bit((value >> 16) & 0xff) << 40) |
+ ((uint64_t)mirror8bit((value >> 24) & 0xff) << 32) |
+ ((uint64_t)mirror8bit((value >> 32) & 0xff) << 24) |
+ ((uint64_t)mirror8bit((value >> 40) & 0xff) << 16) |
+ ((uint64_t)mirror8bit((value >> 48) & 0xff) << 8) |
+ ((uint64_t)mirror8bit((value >> 56) & 0xff));
+}
+
+/**
+ * @brief Parameter k for the Exponential Golomb algorihm to be used.
+ *
+ * The smaller this value, the smaller the minimum bit count for the Exp.
+ * Golomb generated affixes will be (at lowest index) however for the
+ * price of having higher maximum bit count of generated affixes (at highest
+ * index). Likewise increasing this parameter yields in smaller maximum bit
+ * count for the price of having higher minimum bit count.
+ *
+ * In practice that means: a good value for k depends on the expected amount
+ * of devices to be exposed by one export. For a small amount of devices k
+ * should be small, for a large amount of devices k might be increased
+ * instead. The default of k=0 should be fine for most users though.
+ *
+ * @b IMPORTANT: In case this ever becomes a runtime parameter; the value of
+ * k should not change as long as guest is still running! Because that would
+ * cause completely different inode numbers to be generated on guest.
+ */
+#define EXP_GOLOMB_K 0
+
+/**
+ * @brief Exponential Golomb algorithm for arbitrary k (including k=0).
+ *
+ * The Exponential Golomb algorithm generates @b prefixes (@b not suffixes!)
+ * with growing length and with the mathematical property of being
+ * "prefix-free". The latter means the generated prefixes can be prepended
+ * in front of arbitrary numbers and the resulting concatenated numbers are
+ * guaranteed to be always unique.
+ *
+ * This is a minor adjustment to the original Exp. Golomb algorithm in the
+ * sense that lowest allowed index (@param n) starts with 1, not with zero.
+ *
+ * @param n - natural number (or index) of the prefix to be generated
+ * (1, 2, 3, ...)
+ * @param k - parameter k of Exp. Golomb algorithm to be used
+ * (see comment on EXP_GOLOMB_K macro for details about k)
+ */
+static VariLenAffix expGolombEncode(uint64_t n, int k)
+{
+ const uint64_t value = n + (1 << k) - 1;
+ const int bits = (int) log2(value) + 1;
+ return (VariLenAffix) {
+ .type = AffixType_Prefix,
+ .value = value,
+ .bits = bits + MAX((bits - 1 - k), 0)
+ };
+}
+
+/**
+ * @brief Converts a suffix into a prefix, or a prefix into a suffix.
+ *
+ * Simply mirror all bits of the affix value, for the purpose to preserve
+ * respectively the mathematical "prefix-free" or "suffix-free" property
+ * after the conversion.
+ *
+ * If a passed prefix is suitable to create unique numbers, then the
+ * returned suffix is suitable to create unique numbers as well (and vice
+ * versa).
+ */
+static VariLenAffix invertAffix(const VariLenAffix *affix)
+{
+ return (VariLenAffix) {
+ .type =
+ (affix->type == AffixType_Suffix) ?
+ AffixType_Prefix : AffixType_Suffix,
+ .value =
+ mirror64bit(affix->value) >>
+ ((sizeof(affix->value) * 8) - affix->bits),
+ .bits = affix->bits
+ };
+}
+
+/**
+ * @brief Generates suffix numbers with "suffix-free" property.
+ *
+ * This is just a wrapper function on top of the Exp. Golomb algorithm.
+ *
+ * Since the Exp. Golomb algorithm generates prefixes, but we need suffixes,
+ * this function converts the Exp. Golomb prefixes into appropriate suffixes
+ * which are still suitable for generating unique numbers.
+ *
+ * @param n - natural number (or index) of the suffix to be generated
+ * (1, 2, 3, ...)
+ */
+static VariLenAffix affixForIndex(uint64_t index)
+{
+ VariLenAffix prefix;
+ prefix = expGolombEncode(index, EXP_GOLOMB_K);
+ return invertAffix(&prefix); /* convert prefix to suffix */
+}
+
+/* creative abuse of tb_hash_func7, which is based on xxhash */
+static uint32_t qpp_hash(QppEntry e)
+{
+ return qemu_xxhash7(e.ino_prefix, e.dev, 0, 0, 0);
+}
+
+static uint32_t qpf_hash(QpfEntry e)
+{
+ return qemu_xxhash7(e.ino, e.dev, 0, 0, 0);
+}
+
+static bool qpd_cmp_func(const void *obj, const void *userp)
+{
+ const QpdEntry *e1 = obj, *e2 = userp;
+ return e1->dev == e2->dev;
+}
+
+static bool qpp_cmp_func(const void *obj, const void *userp)
+{
+ const QppEntry *e1 = obj, *e2 = userp;
+ return e1->dev == e2->dev && e1->ino_prefix == e2->ino_prefix;
+}
+
+static bool qpf_cmp_func(const void *obj, const void *userp)
+{
+ const QpfEntry *e1 = obj, *e2 = userp;
+ return e1->dev == e2->dev && e1->ino == e2->ino;
+}
+
+static void qp_table_remove(void *p, uint32_t h, void *up)
+{
+ g_free(p);
+}
+
+static void qp_table_destroy(struct qht *ht)
+{
+ if (!ht || !ht->map) {
+ return;
+ }
+ qht_iter(ht, qp_table_remove, NULL);
+ qht_destroy(ht);
+}
+
+static void qpd_table_init(struct qht *ht)
+{
+ qht_init(ht, qpd_cmp_func, 1, QHT_MODE_AUTO_RESIZE);
+}
+
+static void qpp_table_init(struct qht *ht)
+{
+ qht_init(ht, qpp_cmp_func, 1, QHT_MODE_AUTO_RESIZE);
+}
+
+static void qpf_table_init(struct qht *ht)
+{
+ qht_init(ht, qpf_cmp_func, 1 << 16, QHT_MODE_AUTO_RESIZE);
+}
+
+/*
+ * Returns how many (high end) bits of inode numbers of the passed fs
+ * device shall be used (in combination with the device number) to
+ * generate hash values for qpp_table entries.
+ *
+ * This function is required if variable length suffixes are used for inode
+ * number mapping on guest level. Since a device may end up having multiple
+ * entries in qpp_table, each entry most probably with a different suffix
+ * length, we thus need this function in conjunction with qpd_table to
+ * "agree" about a fix amount of bits (per device) to be always used for
+ * generating hash values for the purpose of accessing qpp_table in order
+ * get consistent behaviour when accessing qpp_table.
+ */
+static int qid_inode_prefix_hash_bits(V9fsPDU *pdu, dev_t dev)
+{
+ QpdEntry lookup = {
+ .dev = dev
+ }, *val;
+ uint32_t hash = dev;
+ VariLenAffix affix;
+
+ val = qht_lookup(&pdu->s->qpd_table, &lookup, hash);
+ if (!val) {
+ val = g_malloc0(sizeof(QpdEntry));
+ *val = lookup;
+ affix = affixForIndex(pdu->s->qp_affix_next);
+ val->prefix_bits = affix.bits;
+ qht_insert(&pdu->s->qpd_table, val, hash, NULL);
+ pdu->s->qp_ndevices++;
+ }
+ return val->prefix_bits;
+}
+
+/**
+ * @brief Slow / full mapping host inode nr -> guest inode nr.
+ *
+ * This function performs a slower and much more costly remapping of an
+ * original file inode number on host to an appropriate different inode
+ * number on guest. For every (dev, inode) combination on host a new
+ * sequential number is generated, cached and exposed as inode number on
+ * guest.
+ *
+ * This is just a "last resort" fallback solution if the much faster/cheaper
+ * qid_path_suffixmap() failed. In practice this slow / full mapping is not
+ * expected ever to be used at all though.
+ *
+ * @see qid_path_suffixmap() for details
+ *
+ */
+static int qid_path_fullmap(V9fsPDU *pdu, const struct stat *stbuf,
+ uint64_t *path)
+{
+ QpfEntry lookup = {
+ .dev = stbuf->st_dev,
+ .ino = stbuf->st_ino
+ }, *val;
+ uint32_t hash = qpf_hash(lookup);
+ VariLenAffix affix;
+
+ val = qht_lookup(&pdu->s->qpf_table, &lookup, hash);
+
+ if (!val) {
+ if (pdu->s->qp_fullpath_next == 0) {
+ /* no more files can be mapped :'( */
+ error_report_once(
+ "9p: No more prefixes available for remapping inodes from "
+ "host to guest."
+ );
+ return -ENFILE;
+ }
+
+ val = g_malloc0(sizeof(QppEntry));
+ *val = lookup;
+
+ /* new unique inode and device combo */
+ affix = affixForIndex(
+ 1ULL << (sizeof(pdu->s->qp_affix_next) * 8)
+ );
+ val->path = (pdu->s->qp_fullpath_next++ << affix.bits) | affix.value;
+ pdu->s->qp_fullpath_next &= ((1ULL << (64 - affix.bits)) - 1);
+ qht_insert(&pdu->s->qpf_table, val, hash, NULL);
+ }
+
+ *path = val->path;
+ return 0;
+}
+
+/**
+ * @brief Quick mapping host inode nr -> guest inode nr.
+ *
+ * This function performs quick remapping of an original file inode number
+ * on host to an appropriate different inode number on guest. This remapping
+ * of inodes is required to avoid inode nr collisions on guest which would
+ * happen if the 9p export contains more than 1 exported file system (or
+ * more than 1 file system data set), because unlike on host level where the
+ * files would have different device nrs, all files exported by 9p would
+ * share the same device nr on guest (the device nr of the virtual 9p device
+ * that is).
+ *
+ * Inode remapping is performed by chopping off high end bits of the original
+ * inode number from host, shifting the result upwards and then assigning a
+ * generated suffix number for the low end bits, where the same suffix number
+ * will be shared by all inodes with the same device id AND the same high end
+ * bits that have been chopped off. That approach utilizes the fact that inode
+ * numbers very likely share the same high end bits (i.e. due to their common
+ * sequential generation by file systems) and hence we only have to generate
+ * and track a very limited amount of suffixes in practice due to that.
+ *
+ * We generate variable size suffixes for that purpose. The 1st generated
+ * suffix will only have 1 bit and hence we only need to chop off 1 bit from
+ * the original inode number. The subsequent suffixes being generated will
+ * grow in (bit) size subsequently, i.e. the 2nd and 3rd suffix being
+ * generated will have 3 bits and hence we have to chop off 3 bits from their
+ * original inodes, and so on. That approach of using variable length suffixes
+ * (i.e. over fixed size ones) utilizes the fact that in practice only a very
+ * limited amount of devices are shared by the same export (e.g. typically
+ * less than 2 dozen devices per 9p export), so in practice we need to chop
+ * off less bits than with fixed size prefixes and yet are flexible to add
+ * new devices at runtime below host's export directory at any time without
+ * having to reboot guest nor requiring to reconfigure guest for that. And due
+ * to the very limited amount of original high end bits that we chop off that
+ * way, the total amount of suffixes we need to generate is less than by using
+ * fixed size prefixes and hence it also improves performance of the inode
+ * remapping algorithm, and finally has the nice side effect that the inode
+ * numbers on guest will be much smaller & human friendly. ;-)
+ */
+static int qid_path_suffixmap(V9fsPDU *pdu, const struct stat *stbuf,
+ uint64_t *path)
+{
+ const int ino_hash_bits = qid_inode_prefix_hash_bits(pdu, stbuf->st_dev);
+ QppEntry lookup = {
+ .dev = stbuf->st_dev,
+ .ino_prefix = (uint16_t) (stbuf->st_ino >> (64 - ino_hash_bits))
+ }, *val;
+ uint32_t hash = qpp_hash(lookup);
+
+ val = qht_lookup(&pdu->s->qpp_table, &lookup, hash);
+
+ if (!val) {
+ if (pdu->s->qp_affix_next == 0) {
+ /* we ran out of affixes */
+ warn_report_once(
+ "9p: Potential degraded performance of inode remapping"
+ );
+ return -ENFILE;
+ }
+
+ val = g_malloc0(sizeof(QppEntry));
+ *val = lookup;
+
+ /* new unique inode affix and device combo */
+ val->qp_affix_index = pdu->s->qp_affix_next++;
+ val->qp_affix = affixForIndex(val->qp_affix_index);
+ qht_insert(&pdu->s->qpp_table, val, hash, NULL);
+ }
+ /* assuming generated affix to be suffix type, not prefix */
+ *path = (stbuf->st_ino << val->qp_affix.bits) | val->qp_affix.value;
+ return 0;
+}
+
+static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, V9fsQID *qidp)
+{
+ int err;
size_t size;
- memset(&qidp->path, 0, sizeof(qidp->path));
- size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
- memcpy(&qidp->path, &stbuf->st_ino, size);
+ if (pdu->s->ctx.export_flags & V9FS_REMAP_INODES) {
+ /* map inode+device to qid path (fast path) */
+ err = qid_path_suffixmap(pdu, stbuf, &qidp->path);
+ if (err == -ENFILE) {
+ /* fast path didn't work, fall back to full map */
+ err = qid_path_fullmap(pdu, stbuf, &qidp->path);
+ }
+ if (err) {
+ return err;
+ }
+ } else {
+ if (pdu->s->dev_id != stbuf->st_dev) {
+ if (pdu->s->ctx.export_flags & V9FS_FORBID_MULTIDEVS) {
+ error_report_once(
+ "9p: Multiple devices detected in same VirtFS export. "
+ "Access of guest to additional devices is (partly) "
+ "denied due to virtfs option 'multidevs=forbid' being "
+ "effective."
+ );
+ return -ENODEV;
+ } else {
+ warn_report_once(
+ "9p: Multiple devices detected in same VirtFS export, "
+ "which might lead to file ID collisions and severe "
+ "misbehaviours on guest! You should either use a "
+ "separate export for each device shared from host or "
+ "use virtfs option 'multidevs=remap'!"
+ );
+ }
+ }
+ memset(&qidp->path, 0, sizeof(qidp->path));
+ size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
+ memcpy(&qidp->path, &stbuf->st_ino, size);
+ }
+
qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
qidp->type = 0;
if (S_ISDIR(stbuf->st_mode)) {
@@ -588,6 +950,8 @@
if (S_ISLNK(stbuf->st_mode)) {
qidp->type |= P9_QID_TYPE_SYMLINK;
}
+
+ return 0;
}
static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
@@ -600,10 +964,37 @@
if (err < 0) {
return err;
}
- stat_to_qid(&stbuf, qidp);
+ err = stat_to_qid(pdu, &stbuf, qidp);
+ if (err < 0) {
+ return err;
+ }
return 0;
}
+static int coroutine_fn dirent_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
+ struct dirent *dent, V9fsQID *qidp)
+{
+ struct stat stbuf;
+ V9fsPath path;
+ int err;
+
+ v9fs_path_init(&path);
+
+ err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path);
+ if (err < 0) {
+ goto out;
+ }
+ err = v9fs_co_lstat(pdu, &path, &stbuf);
+ if (err < 0) {
+ goto out;
+ }
+ err = stat_to_qid(pdu, &stbuf, qidp);
+
+out:
+ v9fs_path_free(&path);
+ return err;
+}
+
V9fsPDU *pdu_alloc(V9fsState *s)
{
V9fsPDU *pdu = NULL;
@@ -744,9 +1135,9 @@
{
if (stat->type == -1 &&
stat->dev == -1 &&
- stat->qid.type == -1 &&
- stat->qid.version == -1 &&
- stat->qid.path == -1 &&
+ stat->qid.type == 0xff &&
+ stat->qid.version == (uint32_t) -1 &&
+ stat->qid.path == (uint64_t) -1 &&
stat->mode == -1 &&
stat->atime == -1 &&
stat->mtime == -1 &&
@@ -831,7 +1222,10 @@
memset(v9stat, 0, sizeof(*v9stat));
- stat_to_qid(stbuf, &v9stat->qid);
+ err = stat_to_qid(pdu, stbuf, &v9stat->qid);
+ if (err < 0) {
+ return err;
+ }
v9stat->mode = stat_to_v9mode(stbuf);
v9stat->atime = stbuf->st_atime;
v9stat->mtime = stbuf->st_mtime;
@@ -892,7 +1286,7 @@
#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */
-static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
+static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
V9fsStatDotl *v9lstat)
{
memset(v9lstat, 0, sizeof(*v9lstat));
@@ -914,7 +1308,7 @@
/* Currently we only support BASIC fields in stat */
v9lstat->st_result_mask = P9_STATS_BASIC;
- stat_to_qid(stbuf, &v9lstat->qid);
+ return stat_to_qid(pdu, stbuf, &v9lstat->qid);
}
static void print_sg(struct iovec *sg, int cnt)
@@ -1116,7 +1510,6 @@
uint64_t request_mask;
V9fsStatDotl v9stat_dotl;
V9fsPDU *pdu = opaque;
- V9fsState *s = pdu->s;
retval = pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask);
if (retval < 0) {
@@ -1137,7 +1530,10 @@
if (retval < 0) {
goto out;
}
- stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl);
+ retval = stat_to_v9stat_dotl(pdu, &stbuf, &v9stat_dotl);
+ if (retval < 0) {
+ goto out;
+ }
/* fill st_gen if requested and supported by underlying fs */
if (request_mask & P9_STATS_GEN) {
@@ -1382,7 +1778,10 @@
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
v9fs_path_copy(&dpath, &path);
}
memcpy(&qids[name_idx], &qid, sizeof(qid));
@@ -1484,7 +1883,10 @@
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
if (S_ISDIR(stbuf.st_mode)) {
err = v9fs_co_opendir(pdu, fidp);
if (err < 0) {
@@ -1594,7 +1996,10 @@
fidp->flags |= FID_NON_RECLAIMABLE;
}
iounit = get_iounit(pdu, &fidp->path);
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Qd", &qid, iounit);
if (err < 0) {
goto out;
@@ -1938,16 +2343,39 @@
v9fs_string_free(&name);
return count;
}
- /*
- * Fill up just the path field of qid because the client uses
- * only that. To fill the entire qid structure we will have
- * to stat each dirent found, which is expensive
- */
- size = MIN(sizeof(dent->d_ino), sizeof(qid.path));
- memcpy(&qid.path, &dent->d_ino, size);
- /* Fill the other fields with dummy values */
- qid.type = 0;
- qid.version = 0;
+
+ if (pdu->s->ctx.export_flags & V9FS_REMAP_INODES) {
+ /*
+ * dirent_to_qid() implies expensive stat call for each entry,
+ * we must do that here though since inode remapping requires
+ * the device id, which in turn might be different for
+ * different entries; we cannot make any assumption to avoid
+ * that here.
+ */
+ err = dirent_to_qid(pdu, fidp, dent, &qid);
+ if (err < 0) {
+ v9fs_readdir_unlock(&fidp->fs.dir);
+ v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
+ v9fs_string_free(&name);
+ return err;
+ }
+ } else {
+ /*
+ * Fill up just the path field of qid because the client uses
+ * only that. To fill the entire qid structure we will have
+ * to stat each dirent found, which is expensive. For the
+ * latter reason we don't call dirent_to_qid() here. Only drawback
+ * is that no multi-device export detection of stat_to_qid()
+ * would be done and provided as error to the user here. But
+ * user would get that error anyway when accessing those
+ * files/dirs through other ways.
+ */
+ size = MIN(sizeof(dent->d_ino), sizeof(qid.path));
+ memcpy(&qid.path, &dent->d_ino, size);
+ /* Fill the other fields with dummy values */
+ qid.type = 0;
+ qid.version = 0;
+ }
/* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
len = pdu_marshal(pdu, 11 + count, "Qqbs",
@@ -2328,7 +2756,10 @@
}
}
iounit = get_iounit(pdu, &fidp->path);
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Qd", &qid, iounit);
if (err < 0) {
goto out;
@@ -2385,7 +2816,10 @@
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Q", &qid);
if (err < 0) {
goto out;
@@ -3065,7 +3499,10 @@
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Q", &qid);
if (err < 0) {
goto out;
@@ -3223,7 +3660,10 @@
if (err < 0) {
goto out;
}
- stat_to_qid(&stbuf, &qid);
+ err = stat_to_qid(pdu, &stbuf, &qid);
+ if (err < 0) {
+ goto out;
+ }
err = pdu_marshal(pdu, offset, "Q", &qid);
if (err < 0) {
goto out;
@@ -3634,31 +4074,43 @@
goto out;
}
+ s->dev_id = stat.st_dev;
+
+ /* init inode remapping : */
+ /* hash table for variable length inode suffixes */
+ qpd_table_init(&s->qpd_table);
+ /* hash table for slow/full inode remapping (most users won't need it) */
+ qpf_table_init(&s->qpf_table);
+ /* hash table for quick inode remapping */
+ qpp_table_init(&s->qpp_table);
+ s->qp_ndevices = 0;
+ s->qp_affix_next = 1; /* reserve 0 to detect overflow */
+ s->qp_fullpath_next = 1;
+
s->ctx.fst = &fse->fst;
fsdev_throttle_init(s->ctx.fst);
- v9fs_path_free(&path);
-
rc = 0;
out:
if (rc) {
- if (s->ops && s->ops->cleanup && s->ctx.private) {
- s->ops->cleanup(&s->ctx);
- }
- g_free(s->tag);
- g_free(s->ctx.fs_root);
- v9fs_path_free(&path);
+ v9fs_device_unrealize_common(s, NULL);
}
+ v9fs_path_free(&path);
return rc;
}
void v9fs_device_unrealize_common(V9fsState *s, Error **errp)
{
- if (s->ops->cleanup) {
+ if (s->ops && s->ops->cleanup) {
s->ops->cleanup(&s->ctx);
}
- fsdev_throttle_cleanup(s->ctx.fst);
+ if (s->ctx.fst) {
+ fsdev_throttle_cleanup(s->ctx.fst);
+ }
g_free(s->tag);
+ qp_table_destroy(&s->qpd_table);
+ qp_table_destroy(&s->qpp_table);
+ qp_table_destroy(&s->qpf_table);
g_free(s->ctx.fs_root);
}
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index 8883761..3904f82 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -8,6 +8,7 @@
#include "fsdev/9p-iov-marshal.h"
#include "qemu/thread.h"
#include "qemu/coroutine.h"
+#include "qemu/qht.h"
enum {
P9_TLERROR = 6,
@@ -235,6 +236,58 @@
V9fsFidState *rclm_lst;
};
+typedef enum AffixType_t {
+ AffixType_Prefix,
+ AffixType_Suffix, /* A.k.a. postfix. */
+} AffixType_t;
+
+/**
+ * @brief Unique affix of variable length.
+ *
+ * An affix is (currently) either a suffix or a prefix, which is either
+ * going to be prepended (prefix) or appended (suffix) with some other
+ * number for the goal to generate unique numbers. Accordingly the
+ * suffixes (or prefixes) we generate @b must all have the mathematical
+ * property of being suffix-free (or prefix-free in case of prefixes)
+ * so that no matter what number we concatenate the affix with, that we
+ * always reliably get unique numbers as result after concatenation.
+ */
+typedef struct VariLenAffix {
+ AffixType_t type; /* Whether this affix is a suffix or a prefix. */
+ uint64_t value; /* Actual numerical value of this affix. */
+ /*
+ * Lenght of the affix, that is how many (of the lowest) bits of @c value
+ * must be used for appending/prepending this affix to its final resulting,
+ * unique number.
+ */
+ int bits;
+} VariLenAffix;
+
+/* See qid_inode_prefix_hash_bits(). */
+typedef struct {
+ dev_t dev; /* FS device on host. */
+ /*
+ * How many (high) bits of the original inode number shall be used for
+ * hashing.
+ */
+ int prefix_bits;
+} QpdEntry;
+
+/* QID path prefix entry, see stat_to_qid */
+typedef struct {
+ dev_t dev;
+ uint16_t ino_prefix;
+ uint32_t qp_affix_index;
+ VariLenAffix qp_affix;
+} QppEntry;
+
+/* QID path full entry, as above */
+typedef struct {
+ dev_t dev;
+ ino_t ino;
+ uint64_t path;
+} QpfEntry;
+
struct V9fsState
{
QLIST_HEAD(, V9fsPDU) free_list;
@@ -256,6 +309,13 @@
Error *migration_blocker;
V9fsConf fsconf;
V9fsQID root_qid;
+ dev_t dev_id;
+ struct qht qpd_table;
+ struct qht qpp_table;
+ struct qht qpf_table;
+ uint64_t qp_ndevices; /* Amount of entries in qpd_table. */
+ uint16_t qp_affix_next;
+ uint64_t qp_fullpath_next;
};
/* 9p2000.L open flags */
diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events
index c0a0a4a..10188da 100644
--- a/hw/9pfs/trace-events
+++ b/hw/9pfs/trace-events
@@ -6,7 +6,7 @@
v9fs_version(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s"
v9fs_version_return(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s"
v9fs_attach(uint16_t tag, uint8_t id, int32_t fid, int32_t afid, char* uname, char* aname) "tag %u id %u fid %d afid %d uname %s aname %s"
-v9fs_attach_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d type %d version %d path %"PRId64
+v9fs_attach_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u type %u version %u path %"PRIu64
v9fs_stat(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d"
v9fs_stat_return(uint16_t tag, uint8_t id, int32_t mode, int32_t atime, int32_t mtime, int64_t length) "tag %d id %d stat={mode %d atime %d mtime %d length %"PRId64"}"
v9fs_getattr(uint16_t tag, uint8_t id, int32_t fid, uint64_t request_mask) "tag %d id %d fid %d request_mask %"PRIu64
@@ -14,9 +14,9 @@
v9fs_walk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, uint16_t nwnames) "tag %d id %d fid %d newfid %d nwnames %d"
v9fs_walk_return(uint16_t tag, uint8_t id, uint16_t nwnames, void* qids) "tag %d id %d nwnames %d qids %p"
v9fs_open(uint16_t tag, uint8_t id, int32_t fid, int32_t mode) "tag %d id %d fid %d mode %d"
-v9fs_open_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d"
+v9fs_open_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d"
v9fs_lcreate(uint16_t tag, uint8_t id, int32_t dfid, int32_t flags, int32_t mode, uint32_t gid) "tag %d id %d dfid %d flags %d mode %d gid %u"
-v9fs_lcreate_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int32_t iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d"
+v9fs_lcreate_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int32_t iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d"
v9fs_fsync(uint16_t tag, uint8_t id, int32_t fid, int datasync) "tag %d id %d fid %d datasync %d"
v9fs_clunk(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d"
v9fs_read(uint16_t tag, uint8_t id, int32_t fid, uint64_t off, uint32_t max_count) "tag %d id %d fid %d off %"PRIu64" max_count %u"
@@ -26,21 +26,21 @@
v9fs_write(uint16_t tag, uint8_t id, int32_t fid, uint64_t off, uint32_t count, int cnt) "tag %d id %d fid %d off %"PRIu64" count %u cnt %d"
v9fs_write_return(uint16_t tag, uint8_t id, int32_t total, ssize_t err) "tag %d id %d total %d err %zd"
v9fs_create(uint16_t tag, uint8_t id, int32_t fid, char* name, int32_t perm, int8_t mode) "tag %d id %d fid %d name %s perm %d mode %d"
-v9fs_create_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d"
+v9fs_create_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d"
v9fs_symlink(uint16_t tag, uint8_t id, int32_t fid, char* name, char* symname, uint32_t gid) "tag %d id %d fid %d name %s symname %s gid %u"
-v9fs_symlink_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}"
+v9fs_symlink_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u qid={type %u version %u path %"PRIu64"}"
v9fs_flush(uint16_t tag, uint8_t id, int16_t flush_tag) "tag %d id %d flush_tag %d"
v9fs_link(uint16_t tag, uint8_t id, int32_t dfid, int32_t oldfid, char* name) "tag %d id %d dfid %d oldfid %d name %s"
v9fs_remove(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d"
v9fs_wstat(uint16_t tag, uint8_t id, int32_t fid, int32_t mode, int32_t atime, int32_t mtime) "tag %u id %u fid %d stat={mode %d atime %d mtime %d}"
v9fs_mknod(uint16_t tag, uint8_t id, int32_t fid, int mode, int major, int minor) "tag %d id %d fid %d mode %d major %d minor %d"
-v9fs_mknod_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}"
+v9fs_mknod_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path) "tag %u id %u qid={type %u version %u path %"PRIu64"}"
v9fs_lock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length) "tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64
v9fs_lock_return(uint16_t tag, uint8_t id, int8_t status) "tag %d id %d status %d"
v9fs_getlock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length)"tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64
v9fs_getlock_return(uint16_t tag, uint8_t id, uint8_t type, uint64_t start, uint64_t length, uint32_t proc_id) "tag %d id %d type %d start %"PRIu64" length %"PRIu64" proc_id %u"
v9fs_mkdir(uint16_t tag, uint8_t id, int32_t fid, char* name, int mode, uint32_t gid) "tag %u id %u fid %d name %s mode %d gid %u"
-v9fs_mkdir_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int err) "tag %u id %u qid={type %d version %d path %"PRId64"} err %d"
+v9fs_mkdir_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int err) "tag %u id %u qid={type %u version %u path %"PRIu64"} err %d"
v9fs_xattrwalk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, char* name) "tag %d id %d fid %d newfid %d name %s"
v9fs_xattrwalk_return(uint16_t tag, uint8_t id, int64_t size) "tag %d id %d size %"PRId64
v9fs_xattrcreate(uint16_t tag, uint8_t id, int32_t fid, char* name, uint64_t size, int flags) "tag %d id %d fid %d name %s size %"PRIu64" flags %d"
diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
index 7c59cf9..12e3f1e 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -31,3 +31,7 @@
bool
default y
depends on PC
+
+config ACPI_HW_REDUCED
+ bool
+ depends on ACPI
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 9bb2101..655a9c1 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -6,6 +6,7 @@
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
+common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
common-obj-y += acpi_interface.o
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
new file mode 100644
index 0000000..9cee90c
--- /dev/null
+++ b/hw/acpi/generic_event_device.c
@@ -0,0 +1,311 @@
+/*
+ *
+ * Copyright (c) 2018 Intel Corporation
+ * Copyright (c) 2019 Huawei Technologies R & D (UK) Ltd
+ * Written by Samuel Ortiz, Shameer Kolothum
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/generic_event_device.h"
+#include "hw/irq.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/error-report.h"
+
+static const uint32_t ged_supported_events[] = {
+ ACPI_GED_MEM_HOTPLUG_EVT,
+ ACPI_GED_PWR_DOWN_EVT,
+};
+
+/*
+ * The ACPI Generic Event Device (GED) is a hardware-reduced specific
+ * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
+ * including the hotplug ones. Platforms need to specify their own
+ * GED Event bitmap to describe what kind of events they want to support
+ * through GED. This routine uses a single interrupt for the GED device,
+ * relying on IO memory region to communicate the type of device
+ * affected by the interrupt. This way, we can support up to 32 events
+ * with a unique interrupt.
+ */
+void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
+ uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base)
+{
+ AcpiGedState *s = ACPI_GED(hotplug_dev);
+ Aml *crs = aml_resource_template();
+ Aml *evt, *field;
+ Aml *dev = aml_device("%s", name);
+ Aml *evt_sel = aml_local(0);
+ Aml *esel = aml_name(AML_GED_EVT_SEL);
+
+ /* _CRS interrupt */
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, &ged_irq, 1));
+
+ aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
+ aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ /* Append IO region */
+ aml_append(dev, aml_operation_region(AML_GED_EVT_REG, rs,
+ aml_int(ged_base + ACPI_GED_EVT_SEL_OFFSET),
+ ACPI_GED_EVT_SEL_LEN));
+ field = aml_field(AML_GED_EVT_REG, AML_DWORD_ACC, AML_NOLOCK,
+ AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_named_field(AML_GED_EVT_SEL,
+ ACPI_GED_EVT_SEL_LEN * BITS_PER_BYTE));
+ aml_append(dev, field);
+
+ /*
+ * For each GED event we:
+ * - Add a conditional block for each event, inside a loop.
+ * - Call a method for each supported GED event type.
+ *
+ * The resulting ASL code looks like:
+ *
+ * Local0 = ESEL
+ * If ((Local0 & One) == One)
+ * {
+ * MethodEvent0()
+ * }
+ *
+ * If ((Local0 & 0x2) == 0x2)
+ * {
+ * MethodEvent1()
+ * }
+ * ...
+ */
+ evt = aml_method("_EVT", 1, AML_SERIALIZED);
+ {
+ Aml *if_ctx;
+ uint32_t i;
+ uint32_t ged_events = ctpop32(s->ged_event_bitmap);
+
+ /* Local0 = ESEL */
+ aml_append(evt, aml_store(esel, evt_sel));
+
+ for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) {
+ uint32_t event = s->ged_event_bitmap & ged_supported_events[i];
+
+ if (!event) {
+ continue;
+ }
+
+ if_ctx = aml_if(aml_equal(aml_and(evt_sel, aml_int(event), NULL),
+ aml_int(event)));
+ switch (event) {
+ case ACPI_GED_MEM_HOTPLUG_EVT:
+ aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "."
+ MEMORY_SLOT_SCAN_METHOD));
+ break;
+ case ACPI_GED_PWR_DOWN_EVT:
+ aml_append(if_ctx,
+ aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
+ aml_int(0x80)));
+ break;
+ default:
+ /*
+ * Please make sure all the events in ged_supported_events[]
+ * are handled above.
+ */
+ g_assert_not_reached();
+ }
+
+ aml_append(evt, if_ctx);
+ ged_events--;
+ }
+
+ if (ged_events) {
+ error_report("Unsupported events specified");
+ abort();
+ }
+ }
+
+ /* Append _EVT method */
+ aml_append(dev, evt);
+
+ aml_append(table, dev);
+}
+
+/* Memory read by the GED _EVT AML dynamic method */
+static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint64_t val = 0;
+ GEDState *ged_st = opaque;
+
+ switch (addr) {
+ case ACPI_GED_EVT_SEL_OFFSET:
+ /* Read the selector value and reset it */
+ val = ged_st->sel;
+ ged_st->sel = 0;
+ break;
+ default:
+ break;
+ }
+
+ return val;
+}
+
+/* Nothing is expected to be written to the GED memory region */
+static void ged_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
+{
+}
+
+static const MemoryRegionOps ged_ops = {
+ .read = ged_read,
+ .write = ged_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(hotplug_dev);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp);
+ } else {
+ error_setg(errp, "virt: device plug request for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
+static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
+{
+ AcpiGedState *s = ACPI_GED(adev);
+ GEDState *ged_st = &s->ged_state;
+ uint32_t sel;
+
+ if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
+ sel = ACPI_GED_MEM_HOTPLUG_EVT;
+ } else if (ev & ACPI_POWER_DOWN_STATUS) {
+ sel = ACPI_GED_PWR_DOWN_EVT;
+ } else {
+ /* Unknown event. Return without generating interrupt. */
+ warn_report("GED: Unsupported event %d. No irq injected", ev);
+ return;
+ }
+
+ /*
+ * Set the GED selector field to communicate the event type.
+ * This will be read by GED aml code to select the appropriate
+ * event method.
+ */
+ ged_st->sel |= sel;
+
+ /* Trigger the event by sending an interrupt to the guest. */
+ qemu_irq_pulse(s->irq);
+}
+
+static Property acpi_ged_properties[] = {
+ DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_memhp_state = {
+ .name = "acpi-ged/memhp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_MEMORY_HOTPLUG(memhp_state, AcpiGedState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_ged_state = {
+ .name = "acpi-ged-state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(sel, GEDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_acpi_ged = {
+ .name = "acpi-ged",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(ged_state, AcpiGedState, 1, vmstate_ged_state, GEDState),
+ VMSTATE_END_OF_LIST(),
+ },
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_memhp_state,
+ NULL
+ }
+};
+
+static void acpi_ged_initfn(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ AcpiGedState *s = ACPI_GED(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ GEDState *ged_st = &s->ged_state;
+
+ memory_region_init_io(&ged_st->io, obj, &ged_ops, ged_st,
+ TYPE_ACPI_GED, ACPI_GED_EVT_SEL_LEN);
+ sysbus_init_mmio(sbd, &ged_st->io);
+
+ sysbus_init_irq(sbd, &s->irq);
+
+ s->memhp_state.is_enabled = true;
+ /*
+ * GED handles memory hotplug event and acpi-mem-hotplug
+ * memory region gets initialized here. Create an exclusive
+ * container for memory hotplug IO and expose it as GED sysbus
+ * MMIO so that boards can map it separately.
+ */
+ memory_region_init(&s->container_memhp, OBJECT(dev), "memhp container",
+ MEMORY_HOTPLUG_IO_LEN);
+ sysbus_init_mmio(sbd, &s->container_memhp);
+ acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev),
+ &s->memhp_state, 0);
+}
+
+static void acpi_ged_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
+ AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
+
+ dc->desc = "ACPI Generic Event Device";
+ dc->props = acpi_ged_properties;
+ dc->vmsd = &vmstate_acpi_ged;
+
+ hc->plug = acpi_ged_device_plug_cb;
+
+ adevc->send_event = acpi_ged_send_event;
+}
+
+static const TypeInfo acpi_ged_info = {
+ .name = TYPE_ACPI_GED,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AcpiGedState),
+ .instance_init = acpi_ged_initfn,
+ .class_init = acpi_ged_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { TYPE_ACPI_DEVICE_IF },
+ { }
+ }
+};
+
+static void acpi_ged_register_types(void)
+{
+ type_register_static(&acpi_ged_info);
+}
+
+type_init(acpi_ged_register_types)
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index 9483d66..8d2e822 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -30,12 +30,7 @@
#define MEMORY_SLOT_PROXIMITY_METHOD "MPXM"
#define MEMORY_SLOT_EJECT_METHOD "MEJ0"
#define MEMORY_SLOT_NOTIFY_METHOD "MTFY"
-#define MEMORY_SLOT_SCAN_METHOD "MSCN"
#define MEMORY_HOTPLUG_DEVICE "MHPD"
-#define MEMORY_HOTPLUG_IO_LEN 24
-#define MEMORY_DEVICES_CONTAINER "\\_SB.MHPC"
-
-static uint16_t memhp_io_base;
static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev)
{
@@ -210,7 +205,7 @@
};
void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
- MemHotplugState *state, uint16_t io_base)
+ MemHotplugState *state, hwaddr io_base)
{
MachineState *machine = MACHINE(qdev_get_machine());
@@ -219,12 +214,10 @@
return;
}
- assert(!memhp_io_base);
- memhp_io_base = io_base;
state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count);
memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state,
"acpi-mem-hotplug", MEMORY_HOTPLUG_IO_LEN);
- memory_region_add_subregion(as, memhp_io_base, &state->io);
+ memory_region_add_subregion(as, io_base, &state->io);
}
/**
@@ -343,7 +336,8 @@
void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
const char *res_root,
- const char *event_handler_method)
+ const char *event_handler_method,
+ AmlRegionSpace rs, hwaddr memhp_io_base)
{
int i;
Aml *ifctx;
@@ -352,10 +346,6 @@
Aml *mem_ctrl_dev;
char *mhp_res_path;
- if (!memhp_io_base) {
- return;
- }
-
mhp_res_path = g_strdup_printf("%s." MEMORY_HOTPLUG_DEVICE, res_root);
mem_ctrl_dev = aml_device("%s", mhp_res_path);
{
@@ -366,14 +356,19 @@
aml_name_decl("_UID", aml_string("Memory hotplug resources")));
crs = aml_resource_template();
- aml_append(crs,
- aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0,
- MEMORY_HOTPLUG_IO_LEN)
- );
+ if (rs == AML_SYSTEM_IO) {
+ aml_append(crs,
+ aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0,
+ MEMORY_HOTPLUG_IO_LEN)
+ );
+ } else {
+ aml_append(crs, aml_memory32_fixed(memhp_io_base,
+ MEMORY_HOTPLUG_IO_LEN, AML_READ_WRITE));
+ }
aml_append(mem_ctrl_dev, aml_name_decl("_CRS", crs));
aml_append(mem_ctrl_dev, aml_operation_region(
- MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO,
+ MEMORY_HOTPLUG_IO_REGION, rs,
aml_int(memhp_io_base), MEMORY_HOTPLUG_IO_LEN)
);
@@ -717,10 +712,12 @@
}
aml_append(table, dev_container);
- method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
- aml_append(method,
- aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD));
- aml_append(table, method);
+ if (event_handler_method) {
+ method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_call0(MEMORY_DEVICES_CONTAINER "."
+ MEMORY_SLOT_SCAN_METHOD));
+ aml_append(table, method);
+ }
g_free(mhp_res_path);
}
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 76a2a6b..c6e7782 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -20,6 +20,10 @@
select SMBIOS
select VIRTIO_MMIO
select ACPI_PCI
+ select MEM_DEVICE
+ select DIMM
+ select ACPI_MEMORY_HOTPLUG
+ select ACPI_HW_REDUCED
config CHEETAH
bool
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 43ce8d5..fe749f6 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -41,7 +41,7 @@
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
-obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o aspeed_ast2600.o
obj-$(CONFIG_MPS2) += mps2.o
obj-$(CONFIG_MPS2) += mps2-tz.o
obj-$(CONFIG_MSF2) += msf2-soc.o
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index aa72be3..52993f8 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -170,6 +170,7 @@
AspeedSoCClass *sc;
DriveInfo *drive0 = drive_get(IF_MTD, 0, 0);
ram_addr_t max_ram_size;
+ int i;
bmc = g_new0(AspeedBoardState, 1);
@@ -214,7 +215,7 @@
memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
memory_region_add_subregion(&bmc->ram_container, 0, &bmc->ram);
memory_region_add_subregion(get_system_memory(),
- sc->info->memmap[ASPEED_SDRAM],
+ sc->memmap[ASPEED_SDRAM],
&bmc->ram_container);
max_ram_size = object_property_get_uint(OBJECT(&bmc->soc), "max-ram-size",
@@ -245,13 +246,26 @@
}
aspeed_board_binfo.ram_size = ram_size;
- aspeed_board_binfo.loader_start = sc->info->memmap[ASPEED_SDRAM];
+ aspeed_board_binfo.loader_start = sc->memmap[ASPEED_SDRAM];
aspeed_board_binfo.nb_cpus = bmc->soc.num_cpus;
if (cfg->i2c_init) {
cfg->i2c_init(bmc);
}
+ for (i = 0; i < ARRAY_SIZE(bmc->soc.sdhci.slots); i++) {
+ SDHCIState *sdhci = &bmc->soc.sdhci.slots[i];
+ DriveInfo *dinfo = drive_get_next(IF_SD);
+ BlockBackend *blk;
+ DeviceState *card;
+
+ blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
+ card = qdev_create(qdev_get_child_bus(DEVICE(sdhci), "sd-bus"),
+ TYPE_SD_CARD);
+ qdev_prop_set_drive(card, "drive", blk, &error_fatal);
+ object_property_set_bool(OBJECT(card), true, "realized", &error_fatal);
+ }
+
arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo);
}
@@ -373,7 +387,6 @@
mc->desc = board->desc;
mc->init = aspeed_machine_init;
mc->max_cpus = ASPEED_CPUS_NUM;
- mc->no_sdcard = 1;
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->no_parallel = 1;
diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c
new file mode 100644
index 0000000..931887a
--- /dev/null
+++ b/hw/arm/aspeed_ast2600.c
@@ -0,0 +1,523 @@
+/*
+ * ASPEED SoC 2600 family
+ *
+ * Copyright (c) 2016-2019, IBM Corporation.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "exec/address-spaces.h"
+#include "hw/misc/unimp.h"
+#include "hw/arm/aspeed_soc.h"
+#include "hw/char/serial.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/error-report.h"
+#include "hw/i2c/aspeed_i2c.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+
+#define ASPEED_SOC_IOMEM_SIZE 0x00200000
+
+static const hwaddr aspeed_soc_ast2600_memmap[] = {
+ [ASPEED_SRAM] = 0x10000000,
+ /* 0x16000000 0x17FFFFFF : AHB BUS do LPC Bus bridge */
+ [ASPEED_IOMEM] = 0x1E600000,
+ [ASPEED_PWM] = 0x1E610000,
+ [ASPEED_FMC] = 0x1E620000,
+ [ASPEED_SPI1] = 0x1E630000,
+ [ASPEED_SPI2] = 0x1E641000,
+ [ASPEED_MII1] = 0x1E650000,
+ [ASPEED_MII2] = 0x1E650008,
+ [ASPEED_MII3] = 0x1E650010,
+ [ASPEED_MII4] = 0x1E650018,
+ [ASPEED_ETH1] = 0x1E660000,
+ [ASPEED_ETH3] = 0x1E670000,
+ [ASPEED_ETH2] = 0x1E680000,
+ [ASPEED_ETH4] = 0x1E690000,
+ [ASPEED_VIC] = 0x1E6C0000,
+ [ASPEED_SDMC] = 0x1E6E0000,
+ [ASPEED_SCU] = 0x1E6E2000,
+ [ASPEED_XDMA] = 0x1E6E7000,
+ [ASPEED_ADC] = 0x1E6E9000,
+ [ASPEED_VIDEO] = 0x1E700000,
+ [ASPEED_SDHCI] = 0x1E740000,
+ [ASPEED_GPIO] = 0x1E780000,
+ [ASPEED_GPIO_1_8V] = 0x1E780800,
+ [ASPEED_RTC] = 0x1E781000,
+ [ASPEED_TIMER1] = 0x1E782000,
+ [ASPEED_WDT] = 0x1E785000,
+ [ASPEED_LPC] = 0x1E789000,
+ [ASPEED_IBT] = 0x1E789140,
+ [ASPEED_I2C] = 0x1E78A000,
+ [ASPEED_UART1] = 0x1E783000,
+ [ASPEED_UART5] = 0x1E784000,
+ [ASPEED_VUART] = 0x1E787000,
+ [ASPEED_SDRAM] = 0x80000000,
+};
+
+#define ASPEED_A7MPCORE_ADDR 0x40460000
+
+#define ASPEED_SOC_AST2600_MAX_IRQ 128
+
+static const int aspeed_soc_ast2600_irqmap[] = {
+ [ASPEED_UART1] = 47,
+ [ASPEED_UART2] = 48,
+ [ASPEED_UART3] = 49,
+ [ASPEED_UART4] = 50,
+ [ASPEED_UART5] = 8,
+ [ASPEED_VUART] = 8,
+ [ASPEED_FMC] = 39,
+ [ASPEED_SDMC] = 0,
+ [ASPEED_SCU] = 12,
+ [ASPEED_ADC] = 78,
+ [ASPEED_XDMA] = 6,
+ [ASPEED_SDHCI] = 43,
+ [ASPEED_GPIO] = 40,
+ [ASPEED_GPIO_1_8V] = 11,
+ [ASPEED_RTC] = 13,
+ [ASPEED_TIMER1] = 16,
+ [ASPEED_TIMER2] = 17,
+ [ASPEED_TIMER3] = 18,
+ [ASPEED_TIMER4] = 19,
+ [ASPEED_TIMER5] = 20,
+ [ASPEED_TIMER6] = 21,
+ [ASPEED_TIMER7] = 22,
+ [ASPEED_TIMER8] = 23,
+ [ASPEED_WDT] = 24,
+ [ASPEED_PWM] = 44,
+ [ASPEED_LPC] = 35,
+ [ASPEED_IBT] = 35, /* LPC */
+ [ASPEED_I2C] = 110, /* 110 -> 125 */
+ [ASPEED_ETH1] = 2,
+ [ASPEED_ETH2] = 3,
+ [ASPEED_ETH3] = 32,
+ [ASPEED_ETH4] = 33,
+
+};
+
+static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl)
+{
+ AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
+
+ return qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[ctrl]);
+}
+
+static void aspeed_soc_ast2600_init(Object *obj)
+{
+ AspeedSoCState *s = ASPEED_SOC(obj);
+ AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
+ int i;
+ char socname[8];
+ char typename[64];
+
+ if (sscanf(sc->name, "%7s", socname) != 1) {
+ g_assert_not_reached();
+ }
+
+ for (i = 0; i < sc->num_cpus; i++) {
+ object_initialize_child(obj, "cpu[*]", OBJECT(&s->cpu[i]),
+ sizeof(s->cpu[i]), sc->cpu_type,
+ &error_abort, NULL);
+ }
+
+ snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname);
+ sysbus_init_child_obj(obj, "scu", OBJECT(&s->scu), sizeof(s->scu),
+ typename);
+ qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev",
+ sc->silicon_rev);
+ object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu),
+ "hw-strap1", &error_abort);
+ object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu),
+ "hw-strap2", &error_abort);
+ object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu),
+ "hw-prot-key", &error_abort);
+
+ sysbus_init_child_obj(obj, "a7mpcore", &s->a7mpcore,
+ sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV);
+
+ sysbus_init_child_obj(obj, "rtc", OBJECT(&s->rtc), sizeof(s->rtc),
+ TYPE_ASPEED_RTC);
+
+ snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname);
+ sysbus_init_child_obj(obj, "timerctrl", OBJECT(&s->timerctrl),
+ sizeof(s->timerctrl), typename);
+ object_property_add_const_link(OBJECT(&s->timerctrl), "scu",
+ OBJECT(&s->scu), &error_abort);
+
+ snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname);
+ sysbus_init_child_obj(obj, "i2c", OBJECT(&s->i2c), sizeof(s->i2c),
+ typename);
+
+ snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname);
+ sysbus_init_child_obj(obj, "fmc", OBJECT(&s->fmc), sizeof(s->fmc),
+ typename);
+ object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs",
+ &error_abort);
+ object_property_add_alias(obj, "dram", OBJECT(&s->fmc), "dram",
+ &error_abort);
+
+ for (i = 0; i < sc->spis_num; i++) {
+ snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname);
+ sysbus_init_child_obj(obj, "spi[*]", OBJECT(&s->spi[i]),
+ sizeof(s->spi[i]), typename);
+ }
+
+ snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname);
+ sysbus_init_child_obj(obj, "sdmc", OBJECT(&s->sdmc), sizeof(s->sdmc),
+ typename);
+ object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc),
+ "ram-size", &error_abort);
+ object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc),
+ "max-ram-size", &error_abort);
+
+ for (i = 0; i < sc->wdts_num; i++) {
+ snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname);
+ sysbus_init_child_obj(obj, "wdt[*]", OBJECT(&s->wdt[i]),
+ sizeof(s->wdt[i]), typename);
+ object_property_add_const_link(OBJECT(&s->wdt[i]), "scu",
+ OBJECT(&s->scu), &error_abort);
+ }
+
+ for (i = 0; i < sc->macs_num; i++) {
+ sysbus_init_child_obj(obj, "ftgmac100[*]", OBJECT(&s->ftgmac100[i]),
+ sizeof(s->ftgmac100[i]), TYPE_FTGMAC100);
+
+ sysbus_init_child_obj(obj, "mii[*]", &s->mii[i], sizeof(s->mii[i]),
+ TYPE_ASPEED_MII);
+ object_property_add_const_link(OBJECT(&s->mii[i]), "nic",
+ OBJECT(&s->ftgmac100[i]),
+ &error_abort);
+ }
+
+ sysbus_init_child_obj(obj, "xdma", OBJECT(&s->xdma), sizeof(s->xdma),
+ TYPE_ASPEED_XDMA);
+
+ snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname);
+ sysbus_init_child_obj(obj, "gpio", OBJECT(&s->gpio), sizeof(s->gpio),
+ typename);
+
+ snprintf(typename, sizeof(typename), "aspeed.gpio-%s-1_8v", socname);
+ sysbus_init_child_obj(obj, "gpio_1_8v", OBJECT(&s->gpio_1_8v),
+ sizeof(s->gpio_1_8v), typename);
+
+ sysbus_init_child_obj(obj, "sdc", OBJECT(&s->sdhci), sizeof(s->sdhci),
+ TYPE_ASPEED_SDHCI);
+
+ /* Init sd card slot class here so that they're under the correct parent */
+ for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) {
+ sysbus_init_child_obj(obj, "sdhci[*]", OBJECT(&s->sdhci.slots[i]),
+ sizeof(s->sdhci.slots[i]), TYPE_SYSBUS_SDHCI);
+ }
+}
+
+/*
+ * ASPEED ast2600 has 0xf as cluster ID
+ *
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0388e/CIHEBGFG.html
+ */
+static uint64_t aspeed_calc_affinity(int cpu)
+{
+ return (0xf << ARM_AFF1_SHIFT) | cpu;
+}
+
+static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
+{
+ int i;
+ AspeedSoCState *s = ASPEED_SOC(dev);
+ AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
+ Error *err = NULL, *local_err = NULL;
+ qemu_irq irq;
+
+ /* IO space */
+ create_unimplemented_device("aspeed_soc.io", sc->memmap[ASPEED_IOMEM],
+ ASPEED_SOC_IOMEM_SIZE);
+
+ /* Video engine stub */
+ create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_VIDEO],
+ 0x1000);
+
+ if (s->num_cpus > sc->num_cpus) {
+ warn_report("%s: invalid number of CPUs %d, using default %d",
+ sc->name, s->num_cpus, sc->num_cpus);
+ s->num_cpus = sc->num_cpus;
+ }
+
+ /* CPU */
+ for (i = 0; i < s->num_cpus; i++) {
+ object_property_set_int(OBJECT(&s->cpu[i]), QEMU_PSCI_CONDUIT_SMC,
+ "psci-conduit", &error_abort);
+ if (s->num_cpus > 1) {
+ object_property_set_int(OBJECT(&s->cpu[i]),
+ ASPEED_A7MPCORE_ADDR,
+ "reset-cbar", &error_abort);
+ }
+ object_property_set_int(OBJECT(&s->cpu[i]), aspeed_calc_affinity(i),
+ "mp-affinity", &error_abort);
+
+ /*
+ * TODO: the secondary CPUs are started and a boot helper
+ * is needed when using -kernel
+ */
+
+ object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+
+ /* A7MPCORE */
+ object_property_set_int(OBJECT(&s->a7mpcore), s->num_cpus, "num-cpu",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->a7mpcore),
+ ASPEED_SOC_AST2600_MAX_IRQ + GIC_INTERNAL,
+ "num-irq", &error_abort);
+
+ object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, ASPEED_A7MPCORE_ADDR);
+
+ for (i = 0; i < s->num_cpus; i++) {
+ SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore);
+ DeviceState *d = DEVICE(qemu_get_cpu(i));
+
+ irq = qdev_get_gpio_in(d, ARM_CPU_IRQ);
+ sysbus_connect_irq(sbd, i, irq);
+ irq = qdev_get_gpio_in(d, ARM_CPU_FIQ);
+ sysbus_connect_irq(sbd, i + s->num_cpus, irq);
+ irq = qdev_get_gpio_in(d, ARM_CPU_VIRQ);
+ sysbus_connect_irq(sbd, i + 2 * s->num_cpus, irq);
+ irq = qdev_get_gpio_in(d, ARM_CPU_VFIQ);
+ sysbus_connect_irq(sbd, i + 3 * s->num_cpus, irq);
+ }
+
+ /* SRAM */
+ memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram",
+ sc->sram_size, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(),
+ sc->memmap[ASPEED_SRAM], &s->sram);
+
+ /* SCU */
+ object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_SCU]);
+
+ /* RTC */
+ object_property_set_bool(OBJECT(&s->rtc), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_RTC]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0,
+ aspeed_soc_get_irq(s, ASPEED_RTC));
+
+ /* Timer */
+ object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0,
+ sc->memmap[ASPEED_TIMER1]);
+ for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
+ qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_TIMER1 + i);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
+ }
+
+ /* UART - attach an 8250 to the IO space as our UART5 */
+ if (serial_hd(0)) {
+ qemu_irq uart5 = aspeed_soc_get_irq(s, ASPEED_UART5);
+ serial_mm_init(get_system_memory(), sc->memmap[ASPEED_UART5], 2,
+ uart5, 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
+ }
+
+ /* I2C */
+ object_property_set_bool(OBJECT(&s->i2c), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_I2C]);
+ for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) {
+ qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore),
+ sc->irqmap[ASPEED_I2C] + i);
+ /*
+ * The AST2600 SoC has one IRQ per I2C bus. Skip the common
+ * IRQ (AST2400 and AST2500) and connect all bussses.
+ */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), i + 1, irq);
+ }
+
+ /* FMC, The number of CS is set at the board level */
+ object_property_set_int(OBJECT(&s->fmc), sc->memmap[ASPEED_SDRAM],
+ "sdram-base", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ object_property_set_bool(OBJECT(&s->fmc), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_FMC]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1,
+ s->fmc.ctrl->flash_window_base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0,
+ aspeed_soc_get_irq(s, ASPEED_FMC));
+
+ /* SPI */
+ for (i = 0; i < sc->spis_num; i++) {
+ object_property_set_int(OBJECT(&s->spi[i]), 1, "num-cs", &err);
+ object_property_set_bool(OBJECT(&s->spi[i]), true, "realized",
+ &local_err);
+ error_propagate(&err, local_err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
+ sc->memmap[ASPEED_SPI1 + i]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1,
+ s->spi[i].ctrl->flash_window_base);
+ }
+
+ /* SDMC - SDRAM Memory Controller */
+ object_property_set_bool(OBJECT(&s->sdmc), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_SDMC]);
+
+ /* Watch dog */
+ for (i = 0; i < sc->wdts_num; i++) {
+ AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]);
+
+ object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0,
+ sc->memmap[ASPEED_WDT] + i * awc->offset);
+ }
+
+ /* Net */
+ for (i = 0; i < nb_nics && i < sc->macs_num; i++) {
+ qdev_set_nic_properties(DEVICE(&s->ftgmac100[i]), &nd_table[i]);
+ object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "aspeed",
+ &err);
+ object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "realized",
+ &local_err);
+ error_propagate(&err, local_err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0,
+ sc->memmap[ASPEED_ETH1 + i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0,
+ aspeed_soc_get_irq(s, ASPEED_ETH1 + i));
+
+ object_property_set_bool(OBJECT(&s->mii[i]), true, "realized",
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->mii[i]), 0,
+ sc->memmap[ASPEED_MII1 + i]);
+ }
+
+ /* XDMA */
+ object_property_set_bool(OBJECT(&s->xdma), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0,
+ sc->memmap[ASPEED_XDMA]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0,
+ aspeed_soc_get_irq(s, ASPEED_XDMA));
+
+ /* GPIO */
+ object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_GPIO]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0,
+ aspeed_soc_get_irq(s, ASPEED_GPIO));
+
+ object_property_set_bool(OBJECT(&s->gpio_1_8v), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio_1_8v), 0,
+ sc->memmap[ASPEED_GPIO_1_8V]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio_1_8v), 0,
+ aspeed_soc_get_irq(s, ASPEED_GPIO_1_8V));
+
+ /* SDHCI */
+ object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0,
+ sc->memmap[ASPEED_SDHCI]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
+ aspeed_soc_get_irq(s, ASPEED_SDHCI));
+}
+
+static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc);
+
+ dc->realize = aspeed_soc_ast2600_realize;
+
+ sc->name = "ast2600-a0";
+ sc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a7");
+ sc->silicon_rev = AST2600_A0_SILICON_REV;
+ sc->sram_size = 0x10000;
+ sc->spis_num = 2;
+ sc->wdts_num = 4;
+ sc->macs_num = 4;
+ sc->irqmap = aspeed_soc_ast2600_irqmap;
+ sc->memmap = aspeed_soc_ast2600_memmap;
+ sc->num_cpus = 2;
+}
+
+static const TypeInfo aspeed_soc_ast2600_type_info = {
+ .name = "ast2600-a0",
+ .parent = TYPE_ASPEED_SOC,
+ .instance_size = sizeof(AspeedSoCState),
+ .instance_init = aspeed_soc_ast2600_init,
+ .class_init = aspeed_soc_ast2600_class_init,
+ .class_size = sizeof(AspeedSoCClass),
+};
+
+static void aspeed_soc_register_types(void)
+{
+ type_register_static(&aspeed_soc_ast2600_type_info);
+};
+
+type_init(aspeed_soc_register_types)
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index cf1d0cf..f4fe243 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -34,8 +34,10 @@
[ASPEED_SDMC] = 0x1E6E0000,
[ASPEED_SCU] = 0x1E6E2000,
[ASPEED_XDMA] = 0x1E6E7000,
+ [ASPEED_VIDEO] = 0x1E700000,
[ASPEED_ADC] = 0x1E6E9000,
[ASPEED_SRAM] = 0x1E720000,
+ [ASPEED_SDHCI] = 0x1E740000,
[ASPEED_GPIO] = 0x1E780000,
[ASPEED_RTC] = 0x1E781000,
[ASPEED_TIMER1] = 0x1E782000,
@@ -62,7 +64,9 @@
[ASPEED_SCU] = 0x1E6E2000,
[ASPEED_XDMA] = 0x1E6E7000,
[ASPEED_ADC] = 0x1E6E9000,
+ [ASPEED_VIDEO] = 0x1E700000,
[ASPEED_SRAM] = 0x1E720000,
+ [ASPEED_SDHCI] = 0x1E740000,
[ASPEED_GPIO] = 0x1E780000,
[ASPEED_RTC] = 0x1E781000,
[ASPEED_TIMER1] = 0x1E782000,
@@ -108,39 +112,16 @@
[ASPEED_ETH1] = 2,
[ASPEED_ETH2] = 3,
[ASPEED_XDMA] = 6,
+ [ASPEED_SDHCI] = 26,
};
#define aspeed_soc_ast2500_irqmap aspeed_soc_ast2400_irqmap
-static const AspeedSoCInfo aspeed_socs[] = {
- {
- .name = "ast2400-a1",
- .cpu_type = ARM_CPU_TYPE_NAME("arm926"),
- .silicon_rev = AST2400_A1_SILICON_REV,
- .sram_size = 0x8000,
- .spis_num = 1,
- .wdts_num = 2,
- .irqmap = aspeed_soc_ast2400_irqmap,
- .memmap = aspeed_soc_ast2400_memmap,
- .num_cpus = 1,
- }, {
- .name = "ast2500-a1",
- .cpu_type = ARM_CPU_TYPE_NAME("arm1176"),
- .silicon_rev = AST2500_A1_SILICON_REV,
- .sram_size = 0x9000,
- .spis_num = 2,
- .wdts_num = 3,
- .irqmap = aspeed_soc_ast2500_irqmap,
- .memmap = aspeed_soc_ast2500_memmap,
- .num_cpus = 1,
- },
-};
-
static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl)
{
AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
- return qdev_get_gpio_in(DEVICE(&s->vic), sc->info->irqmap[ctrl]);
+ return qdev_get_gpio_in(DEVICE(&s->vic), sc->irqmap[ctrl]);
}
static void aspeed_soc_init(Object *obj)
@@ -151,13 +132,13 @@
char socname[8];
char typename[64];
- if (sscanf(sc->info->name, "%7s", socname) != 1) {
+ if (sscanf(sc->name, "%7s", socname) != 1) {
g_assert_not_reached();
}
- for (i = 0; i < sc->info->num_cpus; i++) {
+ for (i = 0; i < sc->num_cpus; i++) {
object_initialize_child(obj, "cpu[*]", OBJECT(&s->cpu[i]),
- sizeof(s->cpu[i]), sc->info->cpu_type,
+ sizeof(s->cpu[i]), sc->cpu_type,
&error_abort, NULL);
}
@@ -165,7 +146,7 @@
sysbus_init_child_obj(obj, "scu", OBJECT(&s->scu), sizeof(s->scu),
typename);
qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev",
- sc->info->silicon_rev);
+ sc->silicon_rev);
object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu),
"hw-strap1", &error_abort);
object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu),
@@ -179,13 +160,15 @@
sysbus_init_child_obj(obj, "rtc", OBJECT(&s->rtc), sizeof(s->rtc),
TYPE_ASPEED_RTC);
+ snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname);
sysbus_init_child_obj(obj, "timerctrl", OBJECT(&s->timerctrl),
- sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
+ sizeof(s->timerctrl), typename);
object_property_add_const_link(OBJECT(&s->timerctrl), "scu",
OBJECT(&s->scu), &error_abort);
+ snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname);
sysbus_init_child_obj(obj, "i2c", OBJECT(&s->i2c), sizeof(s->i2c),
- TYPE_ASPEED_I2C);
+ typename);
snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname);
sysbus_init_child_obj(obj, "fmc", OBJECT(&s->fmc), sizeof(s->fmc),
@@ -195,31 +178,29 @@
object_property_add_alias(obj, "dram", OBJECT(&s->fmc), "dram",
&error_abort);
- for (i = 0; i < sc->info->spis_num; i++) {
+ for (i = 0; i < sc->spis_num; i++) {
snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname);
sysbus_init_child_obj(obj, "spi[*]", OBJECT(&s->spi[i]),
sizeof(s->spi[i]), typename);
}
+ snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname);
sysbus_init_child_obj(obj, "sdmc", OBJECT(&s->sdmc), sizeof(s->sdmc),
- TYPE_ASPEED_SDMC);
- qdev_prop_set_uint32(DEVICE(&s->sdmc), "silicon-rev",
- sc->info->silicon_rev);
+ typename);
object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc),
"ram-size", &error_abort);
object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc),
"max-ram-size", &error_abort);
- for (i = 0; i < sc->info->wdts_num; i++) {
+ for (i = 0; i < sc->wdts_num; i++) {
+ snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname);
sysbus_init_child_obj(obj, "wdt[*]", OBJECT(&s->wdt[i]),
- sizeof(s->wdt[i]), TYPE_ASPEED_WDT);
- qdev_prop_set_uint32(DEVICE(&s->wdt[i]), "silicon-rev",
- sc->info->silicon_rev);
+ sizeof(s->wdt[i]), typename);
object_property_add_const_link(OBJECT(&s->wdt[i]), "scu",
OBJECT(&s->scu), &error_abort);
}
- for (i = 0; i < ASPEED_MACS_NUM; i++) {
+ for (i = 0; i < sc->macs_num; i++) {
sysbus_init_child_obj(obj, "ftgmac100[*]", OBJECT(&s->ftgmac100[i]),
sizeof(s->ftgmac100[i]), TYPE_FTGMAC100);
}
@@ -230,6 +211,15 @@
snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname);
sysbus_init_child_obj(obj, "gpio", OBJECT(&s->gpio), sizeof(s->gpio),
typename);
+
+ sysbus_init_child_obj(obj, "sdc", OBJECT(&s->sdhci), sizeof(s->sdhci),
+ TYPE_ASPEED_SDHCI);
+
+ /* Init sd card slot class here so that they're under the correct parent */
+ for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) {
+ sysbus_init_child_obj(obj, "sdhci[*]", OBJECT(&s->sdhci.slots[i]),
+ sizeof(s->sdhci.slots[i]), TYPE_SYSBUS_SDHCI);
+ }
}
static void aspeed_soc_realize(DeviceState *dev, Error **errp)
@@ -240,13 +230,17 @@
Error *err = NULL, *local_err = NULL;
/* IO space */
- create_unimplemented_device("aspeed_soc.io", sc->info->memmap[ASPEED_IOMEM],
+ create_unimplemented_device("aspeed_soc.io", sc->memmap[ASPEED_IOMEM],
ASPEED_SOC_IOMEM_SIZE);
- if (s->num_cpus > sc->info->num_cpus) {
+ /* Video engine stub */
+ create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_VIDEO],
+ 0x1000);
+
+ if (s->num_cpus > sc->num_cpus) {
warn_report("%s: invalid number of CPUs %d, using default %d",
- sc->info->name, s->num_cpus, sc->info->num_cpus);
- s->num_cpus = sc->info->num_cpus;
+ sc->name, s->num_cpus, sc->num_cpus);
+ s->num_cpus = sc->num_cpus;
}
/* CPU */
@@ -260,13 +254,13 @@
/* SRAM */
memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram",
- sc->info->sram_size, &err);
+ sc->sram_size, &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(get_system_memory(),
- sc->info->memmap[ASPEED_SRAM], &s->sram);
+ sc->memmap[ASPEED_SRAM], &s->sram);
/* SCU */
object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
@@ -274,7 +268,7 @@
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->info->memmap[ASPEED_SCU]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_SCU]);
/* VIC */
object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
@@ -282,7 +276,7 @@
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, sc->info->memmap[ASPEED_VIC]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, sc->memmap[ASPEED_VIC]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
@@ -294,7 +288,7 @@
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->info->memmap[ASPEED_RTC]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_RTC]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0,
aspeed_soc_get_irq(s, ASPEED_RTC));
@@ -305,7 +299,7 @@
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0,
- sc->info->memmap[ASPEED_TIMER1]);
+ sc->memmap[ASPEED_TIMER1]);
for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_TIMER1 + i);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
@@ -314,7 +308,7 @@
/* UART - attach an 8250 to the IO space as our UART5 */
if (serial_hd(0)) {
qemu_irq uart5 = aspeed_soc_get_irq(s, ASPEED_UART5);
- serial_mm_init(get_system_memory(), sc->info->memmap[ASPEED_UART5], 2,
+ serial_mm_init(get_system_memory(), sc->memmap[ASPEED_UART5], 2,
uart5, 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
}
@@ -324,12 +318,12 @@
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->info->memmap[ASPEED_I2C]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_I2C]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0,
aspeed_soc_get_irq(s, ASPEED_I2C));
/* FMC, The number of CS is set at the board level */
- object_property_set_int(OBJECT(&s->fmc), sc->info->memmap[ASPEED_SDRAM],
+ object_property_set_int(OBJECT(&s->fmc), sc->memmap[ASPEED_SDRAM],
"sdram-base", &err);
if (err) {
error_propagate(errp, err);
@@ -340,14 +334,14 @@
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->info->memmap[ASPEED_FMC]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_FMC]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1,
s->fmc.ctrl->flash_window_base);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0,
aspeed_soc_get_irq(s, ASPEED_FMC));
/* SPI */
- for (i = 0; i < sc->info->spis_num; i++) {
+ for (i = 0; i < sc->spis_num; i++) {
object_property_set_int(OBJECT(&s->spi[i]), 1, "num-cs", &err);
object_property_set_bool(OBJECT(&s->spi[i]), true, "realized",
&local_err);
@@ -357,7 +351,7 @@
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
- sc->info->memmap[ASPEED_SPI1 + i]);
+ sc->memmap[ASPEED_SPI1 + i]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1,
s->spi[i].ctrl->flash_window_base);
}
@@ -368,21 +362,23 @@
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->info->memmap[ASPEED_SDMC]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_SDMC]);
/* Watch dog */
- for (i = 0; i < sc->info->wdts_num; i++) {
+ for (i = 0; i < sc->wdts_num; i++) {
+ AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]);
+
object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0,
- sc->info->memmap[ASPEED_WDT] + i * 0x20);
+ sc->memmap[ASPEED_WDT] + i * awc->offset);
}
/* Net */
- for (i = 0; i < nb_nics; i++) {
+ for (i = 0; i < nb_nics && i < sc->macs_num; i++) {
qdev_set_nic_properties(DEVICE(&s->ftgmac100[i]), &nd_table[i]);
object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "aspeed",
&err);
@@ -394,7 +390,7 @@
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0,
- sc->info->memmap[ASPEED_ETH1 + i]);
+ sc->memmap[ASPEED_ETH1 + i]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0,
aspeed_soc_get_irq(s, ASPEED_ETH1 + i));
}
@@ -406,7 +402,7 @@
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0,
- sc->info->memmap[ASPEED_XDMA]);
+ sc->memmap[ASPEED_XDMA]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0,
aspeed_soc_get_irq(s, ASPEED_XDMA));
@@ -416,9 +412,20 @@
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->info->memmap[ASPEED_GPIO]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_GPIO]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0,
aspeed_soc_get_irq(s, ASPEED_GPIO));
+
+ /* SDHCI */
+ object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0,
+ sc->memmap[ASPEED_SDHCI]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
+ aspeed_soc_get_irq(s, ASPEED_SDHCI));
}
static Property aspeed_soc_properties[] = {
DEFINE_PROP_UINT32("num-cpus", AspeedSoCState, num_cpus, 0),
@@ -428,9 +435,7 @@
static void aspeed_soc_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc);
- sc->info = (AspeedSoCInfo *) data;
dc->realize = aspeed_soc_realize;
/* Reason: Uses serial_hds and nd_table in realize() directly */
dc->user_creatable = false;
@@ -440,26 +445,64 @@
static const TypeInfo aspeed_soc_type_info = {
.name = TYPE_ASPEED_SOC,
.parent = TYPE_DEVICE,
- .instance_init = aspeed_soc_init,
.instance_size = sizeof(AspeedSoCState),
.class_size = sizeof(AspeedSoCClass),
+ .class_init = aspeed_soc_class_init,
.abstract = true,
};
+static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data)
+{
+ AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc);
+
+ sc->name = "ast2400-a1";
+ sc->cpu_type = ARM_CPU_TYPE_NAME("arm926");
+ sc->silicon_rev = AST2400_A1_SILICON_REV;
+ sc->sram_size = 0x8000;
+ sc->spis_num = 1;
+ sc->wdts_num = 2;
+ sc->macs_num = 2;
+ sc->irqmap = aspeed_soc_ast2400_irqmap;
+ sc->memmap = aspeed_soc_ast2400_memmap;
+ sc->num_cpus = 1;
+}
+
+static const TypeInfo aspeed_soc_ast2400_type_info = {
+ .name = "ast2400-a1",
+ .parent = TYPE_ASPEED_SOC,
+ .instance_init = aspeed_soc_init,
+ .instance_size = sizeof(AspeedSoCState),
+ .class_init = aspeed_soc_ast2400_class_init,
+};
+
+static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data)
+{
+ AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc);
+
+ sc->name = "ast2500-a1";
+ sc->cpu_type = ARM_CPU_TYPE_NAME("arm1176");
+ sc->silicon_rev = AST2500_A1_SILICON_REV;
+ sc->sram_size = 0x9000;
+ sc->spis_num = 2;
+ sc->wdts_num = 3;
+ sc->macs_num = 2;
+ sc->irqmap = aspeed_soc_ast2500_irqmap;
+ sc->memmap = aspeed_soc_ast2500_memmap;
+ sc->num_cpus = 1;
+}
+
+static const TypeInfo aspeed_soc_ast2500_type_info = {
+ .name = "ast2500-a1",
+ .parent = TYPE_ASPEED_SOC,
+ .instance_init = aspeed_soc_init,
+ .instance_size = sizeof(AspeedSoCState),
+ .class_init = aspeed_soc_ast2500_class_init,
+};
static void aspeed_soc_register_types(void)
{
- int i;
-
type_register_static(&aspeed_soc_type_info);
- for (i = 0; i < ARRAY_SIZE(aspeed_socs); ++i) {
- TypeInfo ti = {
- .name = aspeed_socs[i].name,
- .parent = TYPE_ASPEED_SOC,
- .class_init = aspeed_soc_class_init,
- .class_data = (void *) &aspeed_socs[i],
- };
- type_register(&ti);
- }
-}
+ type_register_static(&aspeed_soc_ast2400_type_info);
+ type_register_static(&aspeed_soc_ast2500_type_info);
+};
type_init(aspeed_soc_register_types)
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 8984e2e..fdcf616 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -22,6 +22,20 @@
/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
#define BCM2835_SDHC_CAPAREG 0x52134b4
+static void create_unimp(BCM2835PeripheralState *ps,
+ UnimplementedDeviceState *uds,
+ const char *name, hwaddr ofs, hwaddr size)
+{
+ sysbus_init_child_obj(OBJECT(ps), name, uds,
+ sizeof(UnimplementedDeviceState),
+ TYPE_UNIMPLEMENTED_DEVICE);
+ qdev_prop_set_string(DEVICE(uds), "name", name);
+ qdev_prop_set_uint64(DEVICE(uds), "size", size);
+ object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal);
+ memory_region_add_subregion_overlap(&ps->peri_mr, ofs,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0), -1000);
+}
+
static void bcm2835_peripherals_init(Object *obj)
{
BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj);
@@ -165,7 +179,8 @@
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart0), 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart0), 0,
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
- INTERRUPT_UART));
+ INTERRUPT_UART0));
+
/* AUX / UART1 */
qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hd(1));
@@ -175,7 +190,7 @@
return;
}
- memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
+ memory_region_add_subregion(&s->peri_mr, AUX_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
@@ -268,7 +283,7 @@
return;
}
- memory_region_add_subregion(&s->peri_mr, EMMC_OFFSET,
+ memory_region_add_subregion(&s->peri_mr, EMMC1_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
@@ -322,6 +337,23 @@
error_propagate(errp, err);
return;
}
+
+ create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
+ create_unimp(s, &s->systmr, "bcm2835-systimer", ST_OFFSET, 0x20);
+ create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x1000);
+ create_unimp(s, &s->a2w, "bcm2835-a2w", A2W_OFFSET, 0x1000);
+ create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
+ create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
+ create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20);
+ create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
+ create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20);
+ create_unimp(s, &s->i2c[1], "bcm2835-i2c1", BSC1_OFFSET, 0x20);
+ create_unimp(s, &s->i2c[2], "bcm2835-i2c2", BSC2_OFFSET, 0x20);
+ create_unimp(s, &s->otp, "bcm2835-otp", OTP_OFFSET, 0x80);
+ create_unimp(s, &s->dbus, "bcm2835-dbus", DBUS_OFFSET, 0x8000);
+ create_unimp(s, &s->ave0, "bcm2835-ave0", AVE0_OFFSET, 0x8000);
+ create_unimp(s, &s->dwc2, "dwc-usb2", USB_OTG_OFFSET, 0x1000);
+ create_unimp(s, &s->sdramc, "bcm2835-sdramc", SDRAMC_OFFSET, 0x100);
}
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c
index 493a913..723aef6 100644
--- a/hw/arm/bcm2836.c
+++ b/hw/arm/bcm2836.c
@@ -126,7 +126,7 @@
/* set periphbase/CBAR value for CPU-local registers */
object_property_set_int(OBJECT(&s->cpus[n]),
- BCM2836_PERI_BASE + MCORE_OFFSET,
+ BCM2836_PERI_BASE + MSYNC_OFFSET,
"reset-cbar", &err);
if (err) {
error_propagate(errp, err);
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 246cbb1..f68a399 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -843,13 +843,10 @@
static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s,
uint32_t freq)
{
- QEMUBH *bh;
-
sysbus_init_irq(dev, &s->irq);
s->freq = freq;
- bh = qemu_bh_new(mv88w8618_timer_tick, s);
- s->ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->ptimer = ptimer_init(mv88w8618_timer_tick, s, PTIMER_POLICY_DEFAULT);
}
static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset,
@@ -879,16 +876,19 @@
case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH:
t = &s->timer[offset >> 2];
t->limit = value;
+ ptimer_transaction_begin(t->ptimer);
if (t->limit > 0) {
ptimer_set_limit(t->ptimer, t->limit, 1);
} else {
ptimer_stop(t->ptimer);
}
+ ptimer_transaction_commit(t->ptimer);
break;
case MP_PIT_CONTROL:
for (i = 0; i < 4; i++) {
t = &s->timer[i];
+ ptimer_transaction_begin(t->ptimer);
if (value & 0xf && t->limit > 0) {
ptimer_set_limit(t->ptimer, t->limit, 0);
ptimer_set_freq(t->ptimer, t->freq);
@@ -896,6 +896,7 @@
} else {
ptimer_stop(t->ptimer);
}
+ ptimer_transaction_commit(t->ptimer);
value >>= 4;
}
break;
@@ -914,8 +915,11 @@
int i;
for (i = 0; i < 4; i++) {
- ptimer_stop(s->timer[i].ptimer);
- s->timer[i].limit = 0;
+ mv88w8618_timer_state *t = &s->timer[i];
+ ptimer_transaction_begin(t->ptimer);
+ ptimer_stop(t->ptimer);
+ ptimer_transaction_commit(t->ptimer);
+ t->limit = 0;
}
}
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
index 74c062d..615d755 100644
--- a/hw/arm/raspi.c
+++ b/hw/arm/raspi.c
@@ -230,7 +230,7 @@
mc->max_cpus = BCM283X_NCPUS;
mc->min_cpus = BCM283X_NCPUS;
mc->default_cpus = BCM283X_NCPUS;
- mc->default_ram_size = 1024 * 1024 * 1024;
+ mc->default_ram_size = 1 * GiB;
mc->ignore_memory_transaction_failures = true;
};
DEFINE_MACHINE("raspi2", raspi2_machine_init)
@@ -252,7 +252,7 @@
mc->max_cpus = BCM283X_NCPUS;
mc->min_cpus = BCM283X_NCPUS;
mc->default_cpus = BCM283X_NCPUS;
- mc->default_ram_size = 1024 * 1024 * 1024;
+ mc->default_ram_size = 1 * GiB;
}
DEFINE_MACHINE("raspi3", raspi3_machine_init)
#endif
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 6cdf156..4cd5017 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -39,6 +39,8 @@
#include "hw/acpi/aml-build.h"
#include "hw/acpi/utils.h"
#include "hw/acpi/pci.h"
+#include "hw/acpi/memory_hotplug.h"
+#include "hw/acpi/generic_event_device.h"
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
#include "hw/arm/virt.h"
@@ -48,7 +50,6 @@
#include "migration/vmstate.h"
#define ARM_SPI_BASE 32
-#define ACPI_POWER_BUTTON_DEVICE "PWRB"
static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
{
@@ -544,6 +545,14 @@
}
}
+ if (ms->device_memory) {
+ numamem = acpi_data_push(table_data, sizeof *numamem);
+ build_srat_memory(numamem, ms->device_memory->base,
+ memory_region_size(&ms->device_memory->mr),
+ ms->numa_state->num_nodes - 1,
+ MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
+ }
+
build_header(linker, table_data, (void *)(table_data->data + srat_start),
"SRAT", table_data->len - srat_start, 3, NULL, NULL);
}
@@ -708,6 +717,7 @@
build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
{
Aml *scope, *dsdt;
+ MachineState *ms = MACHINE(vms);
const MemMapEntry *memmap = vms->memmap;
const int *irqmap = vms->irqmap;
@@ -730,8 +740,27 @@
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
vms->highmem, vms->highmem_ecam);
- acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
- (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
+ if (vms->acpi_dev) {
+ build_ged_aml(scope, "\\_SB."GED_DEVICE,
+ HOTPLUG_HANDLER(vms->acpi_dev),
+ irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY,
+ memmap[VIRT_ACPI_GED].base);
+ } else {
+ acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
+ (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
+ }
+
+ if (vms->acpi_dev) {
+ uint32_t event = object_property_get_uint(OBJECT(vms->acpi_dev),
+ "ged-event", &error_abort);
+
+ if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
+ build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
+ AML_SYSTEM_MEMORY,
+ memmap[VIRT_PCDIMM_ACPI].base);
+ }
+ }
+
acpi_dsdt_add_power_button(scope);
aml_append(dsdt, scope);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index d74538b..d4bedc2 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -68,6 +68,9 @@
#include "hw/arm/smmuv3.h"
#include "hw/acpi/acpi.h"
#include "target/arm/internals.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/mem/nvdimm.h"
+#include "hw/acpi/generic_event_device.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -138,6 +141,8 @@
[VIRT_GPIO] = { 0x09030000, 0x00001000 },
[VIRT_SECURE_UART] = { 0x09040000, 0x00001000 },
[VIRT_SMMU] = { 0x09050000, 0x00020000 },
+ [VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN },
+ [VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
@@ -173,6 +178,7 @@
[VIRT_PCIE] = 3, /* ... to 6 */
[VIRT_GPIO] = 7,
[VIRT_SECURE_UART] = 8,
+ [VIRT_ACPI_GED] = 9,
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
[VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */
@@ -525,6 +531,29 @@
}
}
+static inline DeviceState *create_acpi_ged(VirtMachineState *vms, qemu_irq *pic)
+{
+ DeviceState *dev;
+ MachineState *ms = MACHINE(vms);
+ int irq = vms->irqmap[VIRT_ACPI_GED];
+ uint32_t event = ACPI_GED_PWR_DOWN_EVT;
+
+ if (ms->ram_slots) {
+ event |= ACPI_GED_MEM_HOTPLUG_EVT;
+ }
+
+ dev = qdev_create(NULL, TYPE_ACPI_GED);
+ qdev_prop_set_uint32(dev, "ged-event", event);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irq]);
+
+ qdev_init_nofail(dev);
+
+ return dev;
+}
+
static void create_its(VirtMachineState *vms, DeviceState *gicdev)
{
const char *itsclass = its_class_name();
@@ -764,13 +793,15 @@
static DeviceState *gpio_key_dev;
static void virt_powerdown_req(Notifier *n, void *opaque)
{
- /* use gpio Pin 3 for power button event */
- qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
-}
+ VirtMachineState *s = container_of(n, VirtMachineState, powerdown_notifier);
-static Notifier virt_system_powerdown_notifier = {
- .notify = virt_powerdown_req
-};
+ if (s->acpi_dev) {
+ acpi_send_event(s->acpi_dev, ACPI_POWER_DOWN_STATUS);
+ } else {
+ /* use gpio Pin 3 for power button event */
+ qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
+ }
+}
static void create_gpio(const VirtMachineState *vms, qemu_irq *pic)
{
@@ -812,10 +843,6 @@
KEY_POWER);
qemu_fdt_setprop_cells(vms->fdt, "/gpio-keys/poweroff",
"gpios", phandle, 3, 0);
-
- /* connect powerdown request */
- qemu_register_powerdown_notifier(&virt_system_powerdown_notifier);
-
g_free(nodename);
}
@@ -1489,6 +1516,7 @@
MemoryRegion *ram = g_new(MemoryRegion, 1);
bool firmware_loaded;
bool aarch64 = true;
+ bool has_ged = !vmc->no_ged;
unsigned int smp_cpus = machine->smp.cpus;
unsigned int max_cpus = machine->smp.max_cpus;
@@ -1701,7 +1729,15 @@
create_pcie(vms, pic);
- create_gpio(vms, pic);
+ if (has_ged && aarch64 && firmware_loaded && acpi_enabled) {
+ vms->acpi_dev = create_acpi_ged(vms, pic);
+ } else {
+ create_gpio(vms, pic);
+ }
+
+ /* connect powerdown request */
+ vms->powerdown_notifier.notify = virt_powerdown_req;
+ qemu_register_powerdown_notifier(&vms->powerdown_notifier);
/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
@@ -1876,6 +1912,52 @@
return ms->possible_cpus;
}
+static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
+
+ if (is_nvdimm) {
+ error_setg(errp, "nvdimm is not yet supported");
+ return;
+ }
+
+ if (!vms->acpi_dev) {
+ error_setg(errp,
+ "memory hotplug is not enabled: missing acpi-ged device");
+ return;
+ }
+
+ pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
+}
+
+static void virt_memory_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ HotplugHandlerClass *hhc;
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ Error *local_err = NULL;
+
+ pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
+ hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &error_abort);
+out:
+ error_propagate(errp, local_err);
+}
+
+static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ virt_memory_pre_plug(hotplug_dev, dev, errp);
+ }
+}
+
static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -1887,12 +1969,23 @@
SYS_BUS_DEVICE(dev));
}
}
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ virt_memory_plug(hotplug_dev, dev, errp);
+ }
+}
+
+static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ error_setg(errp, "device unplug request for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
}
static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
- if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+ if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) ||
+ (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
return HOTPLUG_HANDLER(machine);
}
@@ -1956,8 +2049,11 @@
mc->kvm_type = virt_kvm_type;
assert(!mc->get_hotplug_handler);
mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
+ hc->pre_plug = virt_machine_device_pre_plug_cb;
hc->plug = virt_machine_device_plug_cb;
+ hc->unplug_request = virt_machine_device_unplug_request_cb;
mc->numa_mem_supported = true;
+ mc->auto_enable_numa_with_memhp = true;
}
static void virt_instance_init(Object *obj)
@@ -2058,8 +2154,12 @@
static void virt_machine_4_1_options(MachineClass *mc)
{
+ VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
+
virt_machine_4_2_options(mc);
compat_props_add(mc->compat_props, hw_compat_4_1, hw_compat_4_1_len);
+ vmc->no_ged = true;
+ mc->auto_enable_numa_with_memhp = false;
}
DEFINE_VIRT_MACHINE(4, 1)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 47159de..11ff5b9 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -327,6 +327,7 @@
{ INFO("w25q80", 0xef5014, 0, 64 << 10, 16, ER_4K) },
{ INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) },
{ INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) },
+ { INFO("w25q512jv", 0xef4020, 0, 64 << 10, 1024, ER_4K) },
};
typedef enum {
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index 3f85519..a6fc1bf 100644
--- a/hw/char/bcm2835_aux.c
+++ b/hw/char/bcm2835_aux.c
@@ -162,8 +162,9 @@
switch (offset) {
case AUX_ENABLES:
if (value != 1) {
- qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
- "or disable UART\n", __func__);
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI"
+ " or disable UART: 0x%"PRIx64"\n",
+ __func__, value);
}
break;
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index d58e2df..7239b82 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -31,6 +31,16 @@
uint8_t policy_mask;
QEMUBH *bh;
QEMUTimer *timer;
+ ptimer_cb callback;
+ void *callback_opaque;
+ /*
+ * These track whether we're in a transaction block, and if we
+ * need to do a timer reload when the block finishes. They don't
+ * need to be migrated because migration can never happen in the
+ * middle of a transaction block.
+ */
+ bool in_transaction;
+ bool need_reload;
};
/* Use a bottom-half routine to avoid reentrancy issues. */
@@ -39,13 +49,16 @@
if (s->bh) {
replay_bh_schedule_event(s->bh);
}
+ if (s->callback) {
+ s->callback(s->callback_opaque);
+ }
}
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;
+ uint32_t period_frac;
+ uint64_t period;
+ uint64_t delta;
bool suppress_trigger = false;
/*
@@ -58,11 +71,20 @@
(s->policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT)) {
suppress_trigger = true;
}
- if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)
+ if (s->delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)
&& !suppress_trigger) {
ptimer_trigger(s);
}
+ /*
+ * Note that ptimer_trigger() might call the device callback function,
+ * which can then modify timer state, so we must not cache any fields
+ * from ptimer_state until after we have called it.
+ */
+ delta = s->delta;
+ period = s->period;
+ period_frac = s->period_frac;
+
if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
delta = s->delta = s->limit;
}
@@ -136,6 +158,15 @@
ptimer_state *s = (ptimer_state *)opaque;
bool trigger = true;
+ /*
+ * We perform all the tick actions within a begin/commit block
+ * because the callback function that ptimer_trigger() calls
+ * might make calls into the ptimer APIs that provoke another
+ * trigger, and we want that to cause the callback function
+ * to be called iteratively, not recursively.
+ */
+ ptimer_transaction_begin(s);
+
if (s->enabled == 2) {
s->delta = 0;
s->enabled = 0;
@@ -164,6 +195,8 @@
if (trigger) {
ptimer_trigger(s);
}
+
+ ptimer_transaction_commit(s);
}
uint64_t ptimer_get_count(ptimer_state *s)
@@ -263,10 +296,15 @@
void ptimer_set_count(ptimer_state *s, uint64_t count)
{
+ assert(s->in_transaction || !s->callback);
s->delta = count;
if (s->enabled) {
- s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s, 0);
+ if (!s->callback) {
+ s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ptimer_reload(s, 0);
+ } else {
+ s->need_reload = true;
+ }
}
}
@@ -274,6 +312,8 @@
{
bool was_disabled = !s->enabled;
+ assert(s->in_transaction || !s->callback);
+
if (was_disabled && s->period == 0) {
if (!qtest_enabled()) {
fprintf(stderr, "Timer with period zero, disabling\n");
@@ -282,8 +322,12 @@
}
s->enabled = oneshot ? 2 : 1;
if (was_disabled) {
- s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s, 0);
+ if (!s->callback) {
+ s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ptimer_reload(s, 0);
+ } else {
+ s->need_reload = true;
+ }
}
}
@@ -291,35 +335,50 @@
is immediately restarted. */
void ptimer_stop(ptimer_state *s)
{
+ assert(s->in_transaction || !s->callback);
+
if (!s->enabled)
return;
s->delta = ptimer_get_count(s);
timer_del(s->timer);
s->enabled = 0;
+ if (s->callback) {
+ s->need_reload = false;
+ }
}
/* Set counter increment interval in nanoseconds. */
void ptimer_set_period(ptimer_state *s, int64_t period)
{
+ assert(s->in_transaction || !s->callback);
s->delta = ptimer_get_count(s);
s->period = period;
s->period_frac = 0;
if (s->enabled) {
- s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s, 0);
+ if (!s->callback) {
+ s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ptimer_reload(s, 0);
+ } else {
+ s->need_reload = true;
+ }
}
}
/* Set counter frequency in Hz. */
void ptimer_set_freq(ptimer_state *s, uint32_t freq)
{
+ assert(s->in_transaction || !s->callback);
s->delta = ptimer_get_count(s);
s->period = 1000000000ll / freq;
s->period_frac = (1000000000ll << 32) / freq;
if (s->enabled) {
- s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s, 0);
+ if (!s->callback) {
+ s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ptimer_reload(s, 0);
+ } else {
+ s->need_reload = true;
+ }
}
}
@@ -327,12 +386,17 @@
count = limit. */
void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
{
+ assert(s->in_transaction || !s->callback);
s->limit = limit;
if (reload)
s->delta = limit;
if (s->enabled && reload) {
- s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s, 0);
+ if (!s->callback) {
+ s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ptimer_reload(s, 0);
+ } else {
+ s->need_reload = true;
+ }
}
}
@@ -341,6 +405,32 @@
return s->limit;
}
+void ptimer_transaction_begin(ptimer_state *s)
+{
+ assert(!s->in_transaction || !s->callback);
+ s->in_transaction = true;
+ s->need_reload = false;
+}
+
+void ptimer_transaction_commit(ptimer_state *s)
+{
+ assert(s->in_transaction);
+ /*
+ * We must loop here because ptimer_reload() can call the callback
+ * function, which might then update ptimer state in a way that
+ * means we need to do another reload and possibly another callback.
+ * A disabled timer never needs reloading (and if we don't check
+ * this then we loop forever if ptimer_reload() disables the timer).
+ */
+ while (s->need_reload && s->enabled) {
+ s->need_reload = false;
+ s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ptimer_reload(s, 0);
+ }
+ /* Now we've finished reload we can leave the transaction block. */
+ s->in_transaction = false;
+}
+
const VMStateDescription vmstate_ptimer = {
.name = "ptimer",
.version_id = 1,
@@ -358,7 +448,7 @@
}
};
-ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask)
+ptimer_state *ptimer_init_with_bh(QEMUBH *bh, uint8_t policy_mask)
{
ptimer_state *s;
@@ -377,9 +467,41 @@
return s;
}
+ptimer_state *ptimer_init(ptimer_cb callback, void *callback_opaque,
+ uint8_t policy_mask)
+{
+ ptimer_state *s;
+
+ /*
+ * The callback function is mandatory; so we use it to distinguish
+ * old-style QEMUBH ptimers from new transaction API ptimers.
+ * (ptimer_init_with_bh() allows a NULL bh pointer and at least
+ * one device (digic-timer) passes NULL, so it's not the case
+ * that either s->bh != NULL or s->callback != NULL.)
+ */
+ assert(callback);
+
+ s = g_new0(ptimer_state, 1);
+ s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s);
+ s->policy_mask = policy_mask;
+ s->callback = callback;
+ s->callback_opaque = callback_opaque;
+
+ /*
+ * These two policies are incompatible -- trigger-on-decrement implies
+ * a timer trigger when the count becomes 0, but no-immediate-trigger
+ * implies a trigger when the count stops being 0.
+ */
+ assert(!((policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) &&
+ (policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)));
+ return s;
+}
+
void ptimer_free(ptimer_state *s)
{
- qemu_bh_delete(s->bh);
+ if (s->bh) {
+ qemu_bh_delete(s->bh);
+ }
timer_free(s->timer);
g_free(s);
}
diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c
index 8f85687..85aaa54 100644
--- a/hw/display/bcm2835_fb.c
+++ b/hw/display/bcm2835_fb.c
@@ -425,7 +425,7 @@
s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET;
s->dma_mr = MEMORY_REGION(obj);
- address_space_init(&s->dma_as, s->dma_mr, NULL);
+ address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_FB "-memory");
bcm2835_fb_reset(dev);
diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c
index 192bd37..1e458d7 100644
--- a/hw/dma/bcm2835_dma.c
+++ b/hw/dma/bcm2835_dma.c
@@ -180,7 +180,7 @@
res = ch->debug;
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
__func__, offset);
break;
}
@@ -228,7 +228,7 @@
ch->debug = value;
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
__func__, offset);
break;
}
@@ -247,7 +247,7 @@
case BCM2708_DMA_ENABLE:
return s->enable;
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
__func__, offset);
return 0;
}
@@ -274,7 +274,7 @@
s->enable = (value & 0xffff);
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
__func__, offset);
}
}
@@ -383,7 +383,7 @@
}
s->dma_mr = MEMORY_REGION(obj);
- address_space_init(&s->dma_as, s->dma_mr, NULL);
+ address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_DMA "-memory");
bcm2835_dma_reset(dev);
}
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index a254275..e035d1f 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -552,7 +552,7 @@
st->nr = i;
st->bh = qemu_bh_new(timer_hit, st);
- st->ptimer = ptimer_init(st->bh, PTIMER_POLICY_DEFAULT);
+ st->ptimer = ptimer_init_with_bh(st->bh, PTIMER_POLICY_DEFAULT);
ptimer_set_freq(st->ptimer, s->freqhz);
}
return;
diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c
index 25fbfec..196e47c 100644
--- a/hw/gpio/aspeed_gpio.c
+++ b/hw/gpio/aspeed_gpio.c
@@ -169,6 +169,48 @@
#define GPIO_3_6V_MEM_SIZE 0x1F0
#define GPIO_3_6V_REG_ARRAY_SIZE (GPIO_3_6V_MEM_SIZE >> 2)
+/* AST2600 only - 1.8V gpios */
+/*
+ * The AST2600 has same 3.6V gpios as the AST2400 (memory offsets 0x0-0x198)
+ * and addtional 1.8V gpios (memory offsets 0x800-0x9D4).
+ */
+#define GPIO_1_8V_REG_OFFSET 0x800
+#define GPIO_1_8V_ABCD_DATA_VALUE ((0x800 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_DIRECTION ((0x804 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_INT_ENABLE ((0x808 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_INT_SENS_0 ((0x80C - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_INT_SENS_1 ((0x810 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_INT_SENS_2 ((0x814 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_INT_STATUS ((0x818 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_RESET_TOLERANT ((0x81C - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_DATA_VALUE ((0x820 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_DIRECTION ((0x824 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_INT_ENABLE ((0x828 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_INT_SENS_0 ((0x82C - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_INT_SENS_1 ((0x830 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_INT_SENS_2 ((0x834 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_INT_STATUS ((0x838 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_RESET_TOLERANT ((0x83C - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_DEBOUNCE_1 ((0x840 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_DEBOUNCE_2 ((0x844 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_DEBOUNCE_1 ((0x848 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_DEBOUNCE_2 ((0x84C - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_DEBOUNCE_TIME_1 ((0x850 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_DEBOUNCE_TIME_2 ((0x854 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_DEBOUNCE_TIME_3 ((0x858 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_COMMAND_SRC_0 ((0x860 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_COMMAND_SRC_1 ((0x864 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_COMMAND_SRC_0 ((0x868 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_COMMAND_SRC_1 ((0x86C - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_DATA_READ ((0x8C0 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_DATA_READ ((0x8C4 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_ABCD_INPUT_MASK ((0x9D0 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_E_INPUT_MASK ((0x9D4 - GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_1_8V_MEM_SIZE 0x9D8
+#define GPIO_1_8V_REG_ARRAY_SIZE ((GPIO_1_8V_MEM_SIZE - \
+ GPIO_1_8V_REG_OFFSET) >> 2)
+#define GPIO_MAX_MEM_SIZE MAX(GPIO_3_6V_MEM_SIZE, GPIO_1_8V_MEM_SIZE)
+
static int aspeed_evaluate_irq(GPIOSets *regs, int gpio_prev_high, int gpio)
{
uint32_t falling_edge = 0, rising_edge = 0;
@@ -465,6 +507,39 @@
[GPIO_AC_INPUT_MASK] = { 7, gpio_reg_input_mask },
};
+static const AspeedGPIOReg aspeed_1_8v_gpios[GPIO_1_8V_REG_ARRAY_SIZE] = {
+ /* 1.8V Set ABCD */
+ [GPIO_1_8V_ABCD_DATA_VALUE] = {0, gpio_reg_data_value},
+ [GPIO_1_8V_ABCD_DIRECTION] = {0, gpio_reg_direction},
+ [GPIO_1_8V_ABCD_INT_ENABLE] = {0, gpio_reg_int_enable},
+ [GPIO_1_8V_ABCD_INT_SENS_0] = {0, gpio_reg_int_sens_0},
+ [GPIO_1_8V_ABCD_INT_SENS_1] = {0, gpio_reg_int_sens_1},
+ [GPIO_1_8V_ABCD_INT_SENS_2] = {0, gpio_reg_int_sens_2},
+ [GPIO_1_8V_ABCD_INT_STATUS] = {0, gpio_reg_int_status},
+ [GPIO_1_8V_ABCD_RESET_TOLERANT] = {0, gpio_reg_reset_tolerant},
+ [GPIO_1_8V_ABCD_DEBOUNCE_1] = {0, gpio_reg_debounce_1},
+ [GPIO_1_8V_ABCD_DEBOUNCE_2] = {0, gpio_reg_debounce_2},
+ [GPIO_1_8V_ABCD_COMMAND_SRC_0] = {0, gpio_reg_cmd_source_0},
+ [GPIO_1_8V_ABCD_COMMAND_SRC_1] = {0, gpio_reg_cmd_source_1},
+ [GPIO_1_8V_ABCD_DATA_READ] = {0, gpio_reg_data_read},
+ [GPIO_1_8V_ABCD_INPUT_MASK] = {0, gpio_reg_input_mask},
+ /* 1.8V Set E */
+ [GPIO_1_8V_E_DATA_VALUE] = {1, gpio_reg_data_value},
+ [GPIO_1_8V_E_DIRECTION] = {1, gpio_reg_direction},
+ [GPIO_1_8V_E_INT_ENABLE] = {1, gpio_reg_int_enable},
+ [GPIO_1_8V_E_INT_SENS_0] = {1, gpio_reg_int_sens_0},
+ [GPIO_1_8V_E_INT_SENS_1] = {1, gpio_reg_int_sens_1},
+ [GPIO_1_8V_E_INT_SENS_2] = {1, gpio_reg_int_sens_2},
+ [GPIO_1_8V_E_INT_STATUS] = {1, gpio_reg_int_status},
+ [GPIO_1_8V_E_RESET_TOLERANT] = {1, gpio_reg_reset_tolerant},
+ [GPIO_1_8V_E_DEBOUNCE_1] = {1, gpio_reg_debounce_1},
+ [GPIO_1_8V_E_DEBOUNCE_2] = {1, gpio_reg_debounce_2},
+ [GPIO_1_8V_E_COMMAND_SRC_0] = {1, gpio_reg_cmd_source_0},
+ [GPIO_1_8V_E_COMMAND_SRC_1] = {1, gpio_reg_cmd_source_1},
+ [GPIO_1_8V_E_DATA_READ] = {1, gpio_reg_data_read},
+ [GPIO_1_8V_E_INPUT_MASK] = {1, gpio_reg_input_mask},
+};
+
static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
{
AspeedGPIOState *s = ASPEED_GPIO(opaque);
@@ -663,8 +738,11 @@
int set_idx, group_idx = 0;
if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) {
- error_setg(errp, "%s: error reading %s", __func__, name);
- return;
+ /* 1.8V gpio */
+ if (sscanf(name, "gpio%3s%1d", group, &pin) != 2) {
+ error_setg(errp, "%s: error reading %s", __func__, name);
+ return;
+ }
}
set_idx = get_set_idx(s, group, &group_idx);
if (set_idx == -1) {
@@ -692,8 +770,11 @@
return;
}
if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) {
- error_setg(errp, "%s: error reading %s", __func__, name);
- return;
+ /* 1.8V gpio */
+ if (sscanf(name, "gpio%3s%1d", group, &pin) != 2) {
+ error_setg(errp, "%s: error reading %s", __func__, name);
+ return;
+ }
}
set_idx = get_set_idx(s, group, &group_idx);
if (set_idx == -1) {
@@ -726,6 +807,21 @@
[7] = {0x000000ff, 0x000000ff, {"AC"} },
};
+static GPIOSetProperties ast2600_3_6v_set_props[] = {
+ [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} },
+ [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} },
+ [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} },
+ [3] = {0xffffffff, 0xffffffff, {"M", "N", "O", "P"} },
+ [4] = {0xffffffff, 0xffffffff, {"Q", "R", "S", "T"} },
+ [5] = {0xffffffff, 0x0000ffff, {"U", "V", "W", "X"} },
+ [6] = {0xffff0000, 0x0fff0000, {"Y", "Z", "", ""} },
+};
+
+static GPIOSetProperties ast2600_1_8v_set_props[] = {
+ [0] = {0xffffffff, 0xffffffff, {"18A", "18B", "18C", "18D"} },
+ [1] = {0x0000000f, 0x0000000f, {"18E"} },
+};
+
static const MemoryRegionOps aspeed_gpio_ops = {
.read = aspeed_gpio_read,
.write = aspeed_gpio_write,
@@ -758,7 +854,7 @@
}
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_gpio_ops, s,
- TYPE_ASPEED_GPIO, GPIO_3_6V_MEM_SIZE);
+ TYPE_ASPEED_GPIO, GPIO_MAX_MEM_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
@@ -851,6 +947,26 @@
agc->reg_table = aspeed_3_6v_gpios;
}
+static void aspeed_gpio_ast2600_3_6v_class_init(ObjectClass *klass, void *data)
+{
+ AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass);
+
+ agc->props = ast2600_3_6v_set_props;
+ agc->nr_gpio_pins = 208;
+ agc->nr_gpio_sets = 7;
+ agc->reg_table = aspeed_3_6v_gpios;
+}
+
+static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data)
+{
+ AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass);
+
+ agc->props = ast2600_1_8v_set_props;
+ agc->nr_gpio_pins = 36;
+ agc->nr_gpio_sets = 2;
+ agc->reg_table = aspeed_1_8v_gpios;
+}
+
static const TypeInfo aspeed_gpio_info = {
.name = TYPE_ASPEED_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
@@ -874,11 +990,27 @@
.instance_init = aspeed_gpio_init,
};
+static const TypeInfo aspeed_gpio_ast2600_3_6v_info = {
+ .name = TYPE_ASPEED_GPIO "-ast2600",
+ .parent = TYPE_ASPEED_GPIO,
+ .class_init = aspeed_gpio_ast2600_3_6v_class_init,
+ .instance_init = aspeed_gpio_init,
+};
+
+static const TypeInfo aspeed_gpio_ast2600_1_8v_info = {
+ .name = TYPE_ASPEED_GPIO "-ast2600-1_8v",
+ .parent = TYPE_ASPEED_GPIO,
+ .class_init = aspeed_gpio_ast2600_1_8v_class_init,
+ .instance_init = aspeed_gpio_init,
+};
+
static void aspeed_gpio_register_types(void)
{
type_register_static(&aspeed_gpio_info);
type_register_static(&aspeed_gpio_ast2400_info);
type_register_static(&aspeed_gpio_ast2500_info);
+ type_register_static(&aspeed_gpio_ast2600_3_6v_info);
+ type_register_static(&aspeed_gpio_ast2600_1_8v_info);
}
type_init(aspeed_gpio_register_types);
diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
index a956eb3..06c119f 100644
--- a/hw/i2c/aspeed_i2c.c
+++ b/hw/i2c/aspeed_i2c.c
@@ -145,10 +145,12 @@
static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus)
{
+ AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
+
bus->intr_status &= bus->intr_ctrl;
if (bus->intr_status) {
bus->controller->intr_status |= 1 << bus->id;
- qemu_irq_raise(bus->controller->irq);
+ qemu_irq_raise(aic->bus_get_irq(bus));
}
}
@@ -273,6 +275,7 @@
uint64_t value, unsigned size)
{
AspeedI2CBus *bus = opaque;
+ AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
bool handle_rx;
switch (offset) {
@@ -299,7 +302,7 @@
bus->intr_status &= ~(value & 0x7FFF);
if (!bus->intr_status) {
bus->controller->intr_status &= ~(1 << bus->id);
- qemu_irq_lower(bus->controller->irq);
+ qemu_irq_lower(aic->bus_get_irq(bus));
}
if (handle_rx && (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST))) {
aspeed_i2c_handle_rx_cmd(bus);
@@ -408,10 +411,11 @@
{
int i;
AspeedI2CState *s = ASPEED_I2C(dev);
+ AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s);
s->intr_status = 0;
- for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) {
+ for (i = 0; i < aic->num_busses; i++) {
s->busses[i].intr_ctrl = 0;
s->busses[i].intr_status = 0;
s->busses[i].cmd = 0;
@@ -421,7 +425,7 @@
}
/*
- * Address Definitions
+ * Address Definitions (AST2400 and AST2500)
*
* 0x000 ... 0x03F: Global Register
* 0x040 ... 0x07F: Device 1
@@ -446,22 +450,26 @@
int i;
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedI2CState *s = ASPEED_I2C(dev);
+ AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s);
sysbus_init_irq(sbd, &s->irq);
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s,
"aspeed.i2c", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
- for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) {
- char name[16];
- int offset = i < 7 ? 1 : 5;
+ for (i = 0; i < aic->num_busses; i++) {
+ char name[32];
+ int offset = i < aic->gap ? 1 : 5;
+
+ sysbus_init_irq(sbd, &s->busses[i].irq);
snprintf(name, sizeof(name), "aspeed.i2c.%d", i);
s->busses[i].controller = s;
s->busses[i].id = i;
s->busses[i].bus = i2c_init_bus(dev, name);
memory_region_init_io(&s->busses[i].mr, OBJECT(dev),
- &aspeed_i2c_bus_ops, &s->busses[i], name, 0x40);
- memory_region_add_subregion(&s->iomem, 0x40 * (i + offset),
+ &aspeed_i2c_bus_ops, &s->busses[i], name,
+ aic->reg_size);
+ memory_region_add_subregion(&s->iomem, aic->reg_size * (i + offset),
&s->busses[i].mr);
}
}
@@ -481,11 +489,88 @@
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedI2CState),
.class_init = aspeed_i2c_class_init,
+ .class_size = sizeof(AspeedI2CClass),
+ .abstract = true,
+};
+
+static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus)
+{
+ return bus->controller->irq;
+}
+
+static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass);
+
+ dc->desc = "ASPEED 2400 I2C Controller";
+
+ aic->num_busses = 14;
+ aic->reg_size = 0x40;
+ aic->gap = 7;
+ aic->bus_get_irq = aspeed_2400_i2c_bus_get_irq;
+}
+
+static const TypeInfo aspeed_2400_i2c_info = {
+ .name = TYPE_ASPEED_2400_I2C,
+ .parent = TYPE_ASPEED_I2C,
+ .class_init = aspeed_2400_i2c_class_init,
+};
+
+static qemu_irq aspeed_2500_i2c_bus_get_irq(AspeedI2CBus *bus)
+{
+ return bus->controller->irq;
+}
+
+static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass);
+
+ dc->desc = "ASPEED 2500 I2C Controller";
+
+ aic->num_busses = 14;
+ aic->reg_size = 0x40;
+ aic->gap = 7;
+ aic->bus_get_irq = aspeed_2500_i2c_bus_get_irq;
+}
+
+static const TypeInfo aspeed_2500_i2c_info = {
+ .name = TYPE_ASPEED_2500_I2C,
+ .parent = TYPE_ASPEED_I2C,
+ .class_init = aspeed_2500_i2c_class_init,
+};
+
+static qemu_irq aspeed_2600_i2c_bus_get_irq(AspeedI2CBus *bus)
+{
+ return bus->irq;
+}
+
+static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass);
+
+ dc->desc = "ASPEED 2600 I2C Controller";
+
+ aic->num_busses = 16;
+ aic->reg_size = 0x80;
+ aic->gap = -1; /* no gap */
+ aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq;
+}
+
+static const TypeInfo aspeed_2600_i2c_info = {
+ .name = TYPE_ASPEED_2600_I2C,
+ .parent = TYPE_ASPEED_I2C,
+ .class_init = aspeed_2600_i2c_class_init,
};
static void aspeed_i2c_register_types(void)
{
type_register_static(&aspeed_i2c_info);
+ type_register_static(&aspeed_2400_i2c_info);
+ type_register_static(&aspeed_2500_i2c_info);
+ type_register_static(&aspeed_2600_i2c_info);
}
type_init(aspeed_i2c_register_types)
@@ -494,9 +579,10 @@
I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr)
{
AspeedI2CState *s = ASPEED_I2C(dev);
+ AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s);
I2CBus *bus = NULL;
- if (busnr >= 0 && busnr < ASPEED_I2C_NR_BUSSES) {
+ if (busnr >= 0 && busnr < aic->num_busses) {
bus = s->busses[busnr].bus;
}
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 4e0f9f4..1d077a7 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1888,7 +1888,12 @@
build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base,
"\\_SB.PCI0", "\\_GPE._E02");
}
- build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", "\\_GPE._E03");
+
+ if (pcms->memhp_io_base && nr_mem) {
+ build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0",
+ "\\_GPE._E03", AML_SYSTEM_IO,
+ pcms->memhp_io_base);
+ }
scope = aml_scope("_GPE");
{
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index bcda50e..4b19042 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1766,6 +1766,9 @@
/* Init default IOAPIC address space */
pcms->ioapic_as = &address_space_memory;
+
+ /* Init ACPI memory hotplug IO base address */
+ pcms->memhp_io_base = ACPI_MEMORY_HOTPLUG_BASE;
}
/*
diff --git a/hw/ide/core.c b/hw/ide/core.c
index e6e54c6..754ff4d 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -442,6 +442,14 @@
TrimAIOCB *iocb = opaque;
IDEState *s = iocb->s;
+ if (iocb->i >= 0) {
+ if (ret >= 0) {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ }
+ }
+
if (ret >= 0) {
while (iocb->j < iocb->qiov->niov) {
int j = iocb->j;
@@ -459,10 +467,14 @@
}
if (!ide_sect_range_ok(s, sector, count)) {
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_UNMAP);
iocb->ret = -EINVAL;
goto done;
}
+ block_acct_start(blk_get_stats(s->blk), &s->acct,
+ count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP);
+
/* Got an entry! Submit and exit. */
iocb->aiocb = blk_aio_pdiscard(s->blk,
sector << BDRV_SECTOR_BITS,
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index b56fda1..9deb15e 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -55,7 +55,7 @@
* has separate fields in the irq number for type,
* CPU number and interrupt number.
*/
- int kvm_irq, irqtype, cpu;
+ int irqtype, cpu;
if (irq < (num_irq - GIC_INTERNAL)) {
/* External interrupt. The kernel numbers these like the GIC
@@ -72,10 +72,7 @@
cpu = irq / GIC_INTERNAL;
irq %= GIC_INTERNAL;
}
- kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT)
- | (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq;
-
- kvm_set_irq(kvm_state, kvm_irq, !!level);
+ kvm_arm_set_irq(cpu, irqtype, irq, !!level);
}
static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level)
diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
index 04229b8..61f884f 100644
--- a/hw/intc/bcm2836_control.c
+++ b/hw/intc/bcm2836_control.c
@@ -264,7 +264,7 @@
} else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
} else {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx"\n",
__func__, offset);
return 0;
}
@@ -293,8 +293,9 @@
} else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
} else {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
- __func__, offset);
+ qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx
+ " value 0x%"PRIx64"\n",
+ __func__, offset, val);
return;
}
diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c
index a9c2c95..a490963 100644
--- a/hw/m68k/mcf5206.c
+++ b/hw/m68k/mcf5206.c
@@ -141,7 +141,7 @@
s = g_new0(m5206_timer_state, 1);
bh = qemu_bh_new(m5206_timer_trigger, s);
- s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT);
s->irq = irq;
m5206_timer_reset(s);
return s;
diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c
index 60c5802..34d34eb 100644
--- a/hw/m68k/mcf5208.c
+++ b/hw/m68k/mcf5208.c
@@ -192,7 +192,7 @@
for (i = 0; i < 2; i++) {
s = g_new0(m5208_timer_state, 1);
bh = qemu_bh_new(m5208_timer_trigger, s);
- s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT);
memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s,
"m5208-timer", 0x00004000);
memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i,
diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c
index 620b25c..717509b 100644
--- a/hw/misc/aspeed_scu.c
+++ b/hw/misc/aspeed_scu.c
@@ -88,6 +88,36 @@
#define BMC_REV TO_REG(0x19C)
#define BMC_DEV_ID TO_REG(0x1A4)
+#define AST2600_PROT_KEY TO_REG(0x00)
+#define AST2600_SILICON_REV TO_REG(0x04)
+#define AST2600_SILICON_REV2 TO_REG(0x14)
+#define AST2600_SYS_RST_CTRL TO_REG(0x40)
+#define AST2600_SYS_RST_CTRL_CLR TO_REG(0x44)
+#define AST2600_SYS_RST_CTRL2 TO_REG(0x50)
+#define AST2600_SYS_RST_CTRL2_CLR TO_REG(0x54)
+#define AST2600_CLK_STOP_CTRL TO_REG(0x80)
+#define AST2600_CLK_STOP_CTRL_CLR TO_REG(0x84)
+#define AST2600_CLK_STOP_CTRL2 TO_REG(0x90)
+#define AST2600_CLK_STOP_CTR2L_CLR TO_REG(0x94)
+#define AST2600_SDRAM_HANDSHAKE TO_REG(0x100)
+#define AST2600_HPLL_PARAM TO_REG(0x200)
+#define AST2600_HPLL_EXT TO_REG(0x204)
+#define AST2600_MPLL_EXT TO_REG(0x224)
+#define AST2600_EPLL_EXT TO_REG(0x244)
+#define AST2600_CLK_SEL TO_REG(0x300)
+#define AST2600_CLK_SEL2 TO_REG(0x304)
+#define AST2600_CLK_SEL3 TO_REG(0x310)
+#define AST2600_HW_STRAP1 TO_REG(0x500)
+#define AST2600_HW_STRAP1_CLR TO_REG(0x504)
+#define AST2600_HW_STRAP1_PROT TO_REG(0x508)
+#define AST2600_HW_STRAP2 TO_REG(0x510)
+#define AST2600_HW_STRAP2_CLR TO_REG(0x514)
+#define AST2600_HW_STRAP2_PROT TO_REG(0x518)
+#define AST2600_RNG_CTRL TO_REG(0x524)
+#define AST2600_RNG_DATA TO_REG(0x540)
+
+#define AST2600_CLK TO_REG(0x40)
+
#define SCU_IO_REGION_SIZE 0x1000
static const uint32_t ast2400_a0_resets[ASPEED_SCU_NR_REGS] = {
@@ -178,7 +208,7 @@
AspeedSCUState *s = ASPEED_SCU(opaque);
int reg = TO_REG(offset);
- if (reg >= ARRAY_SIZE(s->regs)) {
+ if (reg >= ASPEED_SCU_NR_REGS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
@@ -208,7 +238,7 @@
AspeedSCUState *s = ASPEED_SCU(opaque);
int reg = TO_REG(offset);
- if (reg >= ARRAY_SIZE(s->regs)) {
+ if (reg >= ASPEED_SCU_NR_REGS) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
@@ -346,7 +376,7 @@
AspeedSCUState *s = ASPEED_SCU(dev);
AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev);
- memcpy(s->regs, asc->resets, sizeof(s->regs));
+ memcpy(s->regs, asc->resets, asc->nr_regs * 4);
s->regs[SILICON_REV] = s->silicon_rev;
s->regs[HW_STRAP1] = s->hw_strap1;
s->regs[HW_STRAP2] = s->hw_strap2;
@@ -358,6 +388,7 @@
AST2400_A1_SILICON_REV,
AST2500_A0_SILICON_REV,
AST2500_A1_SILICON_REV,
+ AST2600_A0_SILICON_REV,
};
bool is_supported_silicon_rev(uint32_t silicon_rev)
@@ -377,6 +408,7 @@
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedSCUState *s = ASPEED_SCU(dev);
+ AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev);
if (!is_supported_silicon_rev(s->silicon_rev)) {
error_setg(errp, "Unknown silicon revision: 0x%" PRIx32,
@@ -384,7 +416,7 @@
return;
}
- memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_scu_ops, s,
+ memory_region_init_io(&s->iomem, OBJECT(s), asc->ops, s,
TYPE_ASPEED_SCU, SCU_IO_REGION_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
@@ -392,10 +424,10 @@
static const VMStateDescription vmstate_aspeed_scu = {
.name = "aspeed.scu",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, AspeedSCUState, ASPEED_SCU_NR_REGS),
+ VMSTATE_UINT32_ARRAY(regs, AspeedSCUState, ASPEED_AST2600_SCU_NR_REGS),
VMSTATE_END_OF_LIST()
}
};
@@ -436,6 +468,8 @@
asc->resets = ast2400_a0_resets;
asc->calc_hpll = aspeed_2400_scu_calc_hpll;
asc->apb_divider = 2;
+ asc->nr_regs = ASPEED_SCU_NR_REGS;
+ asc->ops = &aspeed_scu_ops;
}
static const TypeInfo aspeed_2400_scu_info = {
@@ -454,6 +488,8 @@
asc->resets = ast2500_a1_resets;
asc->calc_hpll = aspeed_2500_scu_calc_hpll;
asc->apb_divider = 4;
+ asc->nr_regs = ASPEED_SCU_NR_REGS;
+ asc->ops = &aspeed_scu_ops;
}
static const TypeInfo aspeed_2500_scu_info = {
@@ -463,11 +499,155 @@
.class_init = aspeed_2500_scu_class_init,
};
+static uint64_t aspeed_ast2600_scu_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ AspeedSCUState *s = ASPEED_SCU(opaque);
+ int reg = TO_REG(offset);
+
+ if (reg >= ASPEED_AST2600_SCU_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return 0;
+ }
+
+ switch (reg) {
+ case AST2600_HPLL_EXT:
+ case AST2600_EPLL_EXT:
+ case AST2600_MPLL_EXT:
+ /* PLLs are always "locked" */
+ return s->regs[reg] | BIT(31);
+ case AST2600_RNG_DATA:
+ /*
+ * On hardware, RNG_DATA works regardless of the state of the
+ * enable bit in RNG_CTRL
+ *
+ * TODO: Check this is true for ast2600
+ */
+ s->regs[AST2600_RNG_DATA] = aspeed_scu_get_random();
+ break;
+ }
+
+ return s->regs[reg];
+}
+
+static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset, uint64_t data,
+ unsigned size)
+{
+ AspeedSCUState *s = ASPEED_SCU(opaque);
+ int reg = TO_REG(offset);
+
+ if (reg >= ASPEED_AST2600_SCU_NR_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return;
+ }
+
+ if (reg > PROT_KEY && !s->regs[PROT_KEY]) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: SCU is locked!\n", __func__);
+ }
+
+ trace_aspeed_scu_write(offset, size, data);
+
+ switch (reg) {
+ case AST2600_PROT_KEY:
+ s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0;
+ return;
+ case AST2600_HW_STRAP1:
+ case AST2600_HW_STRAP2:
+ if (s->regs[reg + 2]) {
+ return;
+ }
+ /* fall through */
+ case AST2600_SYS_RST_CTRL:
+ case AST2600_SYS_RST_CTRL2:
+ /* W1S (Write 1 to set) registers */
+ s->regs[reg] |= data;
+ return;
+ case AST2600_SYS_RST_CTRL_CLR:
+ case AST2600_SYS_RST_CTRL2_CLR:
+ case AST2600_HW_STRAP1_CLR:
+ case AST2600_HW_STRAP2_CLR:
+ /* W1C (Write 1 to clear) registers */
+ s->regs[reg] &= ~data;
+ return;
+
+ case AST2600_RNG_DATA:
+ case AST2600_SILICON_REV:
+ case AST2600_SILICON_REV2:
+ /* Add read only registers here */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return;
+ }
+
+ s->regs[reg] = data;
+}
+
+static const MemoryRegionOps aspeed_ast2600_scu_ops = {
+ .read = aspeed_ast2600_scu_read,
+ .write = aspeed_ast2600_scu_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static const uint32_t ast2600_a0_resets[ASPEED_AST2600_SCU_NR_REGS] = {
+ [AST2600_SILICON_REV] = AST2600_SILICON_REV,
+ [AST2600_SILICON_REV2] = AST2600_SILICON_REV,
+ [AST2600_SYS_RST_CTRL] = 0xF7CFFEDC | 0x100,
+ [AST2600_SYS_RST_CTRL2] = 0xFFFFFFFC,
+ [AST2600_CLK_STOP_CTRL] = 0xEFF43E8B,
+ [AST2600_CLK_STOP_CTRL2] = 0xFFF0FFF0,
+ [AST2600_SDRAM_HANDSHAKE] = 0x00000040, /* SoC completed DRAM init */
+ [AST2600_HPLL_PARAM] = 0x1000405F,
+};
+
+static void aspeed_ast2600_scu_reset(DeviceState *dev)
+{
+ AspeedSCUState *s = ASPEED_SCU(dev);
+ AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev);
+
+ memcpy(s->regs, asc->resets, asc->nr_regs * 4);
+
+ s->regs[AST2600_SILICON_REV] = s->silicon_rev;
+ s->regs[AST2600_SILICON_REV2] = s->silicon_rev;
+ s->regs[AST2600_HW_STRAP1] = s->hw_strap1;
+ s->regs[AST2600_HW_STRAP2] = s->hw_strap2;
+ s->regs[PROT_KEY] = s->hw_prot_key;
+}
+
+static void aspeed_2600_scu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass);
+
+ dc->desc = "ASPEED 2600 System Control Unit";
+ dc->reset = aspeed_ast2600_scu_reset;
+ asc->resets = ast2600_a0_resets;
+ asc->calc_hpll = aspeed_2500_scu_calc_hpll; /* No change since AST2500 */
+ asc->apb_divider = 4;
+ asc->nr_regs = ASPEED_AST2600_SCU_NR_REGS;
+ asc->ops = &aspeed_ast2600_scu_ops;
+}
+
+static const TypeInfo aspeed_2600_scu_info = {
+ .name = TYPE_ASPEED_2600_SCU,
+ .parent = TYPE_ASPEED_SCU,
+ .instance_size = sizeof(AspeedSCUState),
+ .class_init = aspeed_2600_scu_class_init,
+};
+
static void aspeed_scu_register_types(void)
{
type_register_static(&aspeed_scu_info);
type_register_static(&aspeed_2400_scu_info);
type_register_static(&aspeed_2500_scu_info);
+ type_register_static(&aspeed_2600_scu_info);
}
type_init(aspeed_scu_register_types);
diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c
index cb13c63..f3a63a2 100644
--- a/hw/misc/aspeed_sdmc.c
+++ b/hw/misc/aspeed_sdmc.c
@@ -28,6 +28,7 @@
/* Control/Status Register #1 (ast2500) */
#define R_STATUS1 (0x60 / 4)
#define PHY_BUSY_STATE BIT(0)
+#define PHY_PLL_LOCK_STATUS BIT(4)
#define R_ECC_TEST_CTRL (0x70 / 4)
#define ECC_TEST_FINISHED BIT(12)
@@ -85,6 +86,11 @@
#define ASPEED_SDMC_AST2500_512MB 0x2
#define ASPEED_SDMC_AST2500_1024MB 0x3
+#define ASPEED_SDMC_AST2600_256MB 0x0
+#define ASPEED_SDMC_AST2600_512MB 0x1
+#define ASPEED_SDMC_AST2600_1024MB 0x2
+#define ASPEED_SDMC_AST2600_2048MB 0x3
+
#define ASPEED_SDMC_AST2500_READONLY_MASK \
(ASPEED_SDMC_HW_VERSION(0xf) | ASPEED_SDMC_CACHE_INITIAL_DONE | \
ASPEED_SDMC_AST2500_RESERVED | ASPEED_SDMC_VGA_COMPAT | \
@@ -110,6 +116,7 @@
unsigned int size)
{
AspeedSDMCState *s = ASPEED_SDMC(opaque);
+ AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s);
addr >>= 2;
@@ -130,41 +137,7 @@
return;
}
- if (addr == R_CONF) {
- /* Make sure readonly bits are kept */
- switch (s->silicon_rev) {
- case AST2400_A0_SILICON_REV:
- case AST2400_A1_SILICON_REV:
- data &= ~ASPEED_SDMC_READONLY_MASK;
- data |= s->fixed_conf;
- break;
- case AST2500_A0_SILICON_REV:
- case AST2500_A1_SILICON_REV:
- data &= ~ASPEED_SDMC_AST2500_READONLY_MASK;
- data |= s->fixed_conf;
- break;
- default:
- g_assert_not_reached();
- }
- }
- if (s->silicon_rev == AST2500_A0_SILICON_REV ||
- s->silicon_rev == AST2500_A1_SILICON_REV) {
- switch (addr) {
- case R_STATUS1:
- /* Will never return 'busy' */
- data &= ~PHY_BUSY_STATE;
- break;
- case R_ECC_TEST_CTRL:
- /* Always done, always happy */
- data |= ECC_TEST_FINISHED;
- data &= ~ECC_TEST_FAIL;
- break;
- default:
- break;
- }
- }
-
- s->regs[addr] = data;
+ asc->write(s, addr, data);
}
static const MemoryRegionOps aspeed_sdmc_ops = {
@@ -219,47 +192,46 @@
return ASPEED_SDMC_AST2500_512MB;
}
+static int ast2600_rambits(AspeedSDMCState *s)
+{
+ switch (s->ram_size >> 20) {
+ case 256:
+ return ASPEED_SDMC_AST2600_256MB;
+ case 512:
+ return ASPEED_SDMC_AST2600_512MB;
+ case 1024:
+ return ASPEED_SDMC_AST2600_1024MB;
+ case 2048:
+ return ASPEED_SDMC_AST2600_2048MB;
+ default:
+ break;
+ }
+
+ /* use a common default */
+ warn_report("Invalid RAM size 0x%" PRIx64 ". Using default 512M",
+ s->ram_size);
+ s->ram_size = 512 << 20;
+ return ASPEED_SDMC_AST2600_512MB;
+}
+
static void aspeed_sdmc_reset(DeviceState *dev)
{
AspeedSDMCState *s = ASPEED_SDMC(dev);
+ AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s);
memset(s->regs, 0, sizeof(s->regs));
/* Set ram size bit and defaults values */
- s->regs[R_CONF] = s->fixed_conf;
+ s->regs[R_CONF] = asc->compute_conf(s, 0);
}
static void aspeed_sdmc_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedSDMCState *s = ASPEED_SDMC(dev);
+ AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s);
- if (!is_supported_silicon_rev(s->silicon_rev)) {
- error_setg(errp, "Unknown silicon revision: 0x%" PRIx32,
- s->silicon_rev);
- return;
- }
-
- switch (s->silicon_rev) {
- case AST2400_A0_SILICON_REV:
- case AST2400_A1_SILICON_REV:
- s->ram_bits = ast2400_rambits(s);
- s->max_ram_size = 512 << 20;
- s->fixed_conf = ASPEED_SDMC_VGA_COMPAT |
- ASPEED_SDMC_DRAM_SIZE(s->ram_bits);
- break;
- case AST2500_A0_SILICON_REV:
- case AST2500_A1_SILICON_REV:
- s->ram_bits = ast2500_rambits(s);
- s->max_ram_size = 1024 << 20;
- s->fixed_conf = ASPEED_SDMC_HW_VERSION(1) |
- ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) |
- ASPEED_SDMC_CACHE_INITIAL_DONE |
- ASPEED_SDMC_DRAM_SIZE(s->ram_bits);
- break;
- default:
- g_assert_not_reached();
- }
+ s->max_ram_size = asc->max_ram_size;
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sdmc_ops, s,
TYPE_ASPEED_SDMC, 0x1000);
@@ -277,7 +249,6 @@
};
static Property aspeed_sdmc_properties[] = {
- DEFINE_PROP_UINT32("silicon-rev", AspeedSDMCState, silicon_rev, 0),
DEFINE_PROP_UINT64("ram-size", AspeedSDMCState, ram_size, 0),
DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0),
DEFINE_PROP_END_OF_LIST(),
@@ -298,11 +269,164 @@
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedSDMCState),
.class_init = aspeed_sdmc_class_init,
+ .class_size = sizeof(AspeedSDMCClass),
+ .abstract = true,
+};
+
+static uint32_t aspeed_2400_sdmc_compute_conf(AspeedSDMCState *s, uint32_t data)
+{
+ uint32_t fixed_conf = ASPEED_SDMC_VGA_COMPAT |
+ ASPEED_SDMC_DRAM_SIZE(ast2400_rambits(s));
+
+ /* Make sure readonly bits are kept */
+ data &= ~ASPEED_SDMC_READONLY_MASK;
+
+ return data | fixed_conf;
+}
+
+static void aspeed_2400_sdmc_write(AspeedSDMCState *s, uint32_t reg,
+ uint32_t data)
+{
+ switch (reg) {
+ case R_CONF:
+ data = aspeed_2400_sdmc_compute_conf(s, data);
+ break;
+ default:
+ break;
+ }
+
+ s->regs[reg] = data;
+}
+
+static void aspeed_2400_sdmc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass);
+
+ dc->desc = "ASPEED 2400 SDRAM Memory Controller";
+ asc->max_ram_size = 512 << 20;
+ asc->compute_conf = aspeed_2400_sdmc_compute_conf;
+ asc->write = aspeed_2400_sdmc_write;
+}
+
+static const TypeInfo aspeed_2400_sdmc_info = {
+ .name = TYPE_ASPEED_2400_SDMC,
+ .parent = TYPE_ASPEED_SDMC,
+ .class_init = aspeed_2400_sdmc_class_init,
+};
+
+static uint32_t aspeed_2500_sdmc_compute_conf(AspeedSDMCState *s, uint32_t data)
+{
+ uint32_t fixed_conf = ASPEED_SDMC_HW_VERSION(1) |
+ ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) |
+ ASPEED_SDMC_CACHE_INITIAL_DONE |
+ ASPEED_SDMC_DRAM_SIZE(ast2500_rambits(s));
+
+ /* Make sure readonly bits are kept */
+ data &= ~ASPEED_SDMC_AST2500_READONLY_MASK;
+
+ return data | fixed_conf;
+}
+
+static void aspeed_2500_sdmc_write(AspeedSDMCState *s, uint32_t reg,
+ uint32_t data)
+{
+ switch (reg) {
+ case R_CONF:
+ data = aspeed_2500_sdmc_compute_conf(s, data);
+ break;
+ case R_STATUS1:
+ /* Will never return 'busy' */
+ data &= ~PHY_BUSY_STATE;
+ break;
+ case R_ECC_TEST_CTRL:
+ /* Always done, always happy */
+ data |= ECC_TEST_FINISHED;
+ data &= ~ECC_TEST_FAIL;
+ break;
+ default:
+ break;
+ }
+
+ s->regs[reg] = data;
+}
+
+static void aspeed_2500_sdmc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass);
+
+ dc->desc = "ASPEED 2500 SDRAM Memory Controller";
+ asc->max_ram_size = 1024 << 20;
+ asc->compute_conf = aspeed_2500_sdmc_compute_conf;
+ asc->write = aspeed_2500_sdmc_write;
+}
+
+static const TypeInfo aspeed_2500_sdmc_info = {
+ .name = TYPE_ASPEED_2500_SDMC,
+ .parent = TYPE_ASPEED_SDMC,
+ .class_init = aspeed_2500_sdmc_class_init,
+};
+
+static uint32_t aspeed_2600_sdmc_compute_conf(AspeedSDMCState *s, uint32_t data)
+{
+ uint32_t fixed_conf = ASPEED_SDMC_HW_VERSION(3) |
+ ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) |
+ ASPEED_SDMC_DRAM_SIZE(ast2600_rambits(s));
+
+ /* Make sure readonly bits are kept (use ast2500 mask) */
+ data &= ~ASPEED_SDMC_AST2500_READONLY_MASK;
+
+ return data | fixed_conf;
+}
+
+static void aspeed_2600_sdmc_write(AspeedSDMCState *s, uint32_t reg,
+ uint32_t data)
+{
+ switch (reg) {
+ case R_CONF:
+ data = aspeed_2600_sdmc_compute_conf(s, data);
+ break;
+ case R_STATUS1:
+ /* Will never return 'busy'. 'lock status' is always set */
+ data &= ~PHY_BUSY_STATE;
+ data |= PHY_PLL_LOCK_STATUS;
+ break;
+ case R_ECC_TEST_CTRL:
+ /* Always done, always happy */
+ data |= ECC_TEST_FINISHED;
+ data &= ~ECC_TEST_FAIL;
+ break;
+ default:
+ break;
+ }
+
+ s->regs[reg] = data;
+}
+
+static void aspeed_2600_sdmc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass);
+
+ dc->desc = "ASPEED 2600 SDRAM Memory Controller";
+ asc->max_ram_size = 2048 << 20;
+ asc->compute_conf = aspeed_2600_sdmc_compute_conf;
+ asc->write = aspeed_2600_sdmc_write;
+}
+
+static const TypeInfo aspeed_2600_sdmc_info = {
+ .name = TYPE_ASPEED_2600_SDMC,
+ .parent = TYPE_ASPEED_SDMC,
+ .class_init = aspeed_2600_sdmc_class_init,
};
static void aspeed_sdmc_register_types(void)
{
type_register_static(&aspeed_sdmc_info);
+ type_register_static(&aspeed_2400_sdmc_info);
+ type_register_static(&aspeed_2500_sdmc_info);
+ type_register_static(&aspeed_2600_sdmc_info);
}
type_init(aspeed_sdmc_register_types);
diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c
index 79bad11..77d2d80 100644
--- a/hw/misc/bcm2835_mbox.c
+++ b/hw/misc/bcm2835_mbox.c
@@ -15,6 +15,7 @@
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
+#include "trace.h"
#define MAIL0_PEEK 0x90
#define MAIL0_SENDER 0x94
@@ -123,6 +124,7 @@
set = true;
}
}
+ trace_bcm2835_mbox_irq(set);
qemu_set_irq(s->arm_irq, set);
}
@@ -176,10 +178,12 @@
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx"\n",
__func__, offset);
+ trace_bcm2835_mbox_read(size, offset, res);
return 0;
}
+ trace_bcm2835_mbox_read(size, offset, res);
bcm2835_mbox_update(s);
@@ -195,6 +199,7 @@
offset &= 0xff;
+ trace_bcm2835_mbox_write(size, offset, value);
switch (offset) {
case MAIL0_SENDER:
break;
@@ -228,8 +233,9 @@
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
- __func__, offset);
+ qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx
+ " value 0x%"PRIx64"\n",
+ __func__, offset, value);
return;
}
@@ -310,7 +316,7 @@
}
s->mbox_mr = MEMORY_REGION(obj);
- address_space_init(&s->mbox_as, s->mbox_mr, NULL);
+ address_space_init(&s->mbox_as, s->mbox_mr, TYPE_BCM2835_MBOX "-memory");
bcm2835_mbox_reset(dev);
}
diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c
index d86d510..0eea2e2 100644
--- a/hw/misc/bcm2835_property.c
+++ b/hw/misc/bcm2835_property.c
@@ -13,6 +13,7 @@
#include "sysemu/dma.h"
#include "qemu/log.h"
#include "qemu/module.h"
+#include "trace.h"
/* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
@@ -56,7 +57,8 @@
break;
case 0x00010001: /* Get board model */
qemu_log_mask(LOG_UNIMP,
- "bcm2835_property: %x get board model NYI\n", tag);
+ "bcm2835_property: 0x%08x get board model NYI\n",
+ tag);
resplen = 4;
break;
case 0x00010002: /* Get board revision */
@@ -69,7 +71,8 @@
break;
case 0x00010004: /* Get board serial */
qemu_log_mask(LOG_UNIMP,
- "bcm2835_property: %x get board serial NYI\n", tag);
+ "bcm2835_property: 0x%08x get board serial NYI\n",
+ tag);
resplen = 8;
break;
case 0x00010005: /* Get ARM memory */
@@ -104,7 +107,8 @@
case 0x00038001: /* Set clock state */
qemu_log_mask(LOG_UNIMP,
- "bcm2835_property: %x set clock state NYI\n", tag);
+ "bcm2835_property: 0x%08x set clock state NYI\n",
+ tag);
resplen = 8;
break;
@@ -129,7 +133,8 @@
case 0x00038004: /* Set max clock rate */
case 0x00038007: /* Set min clock rate */
qemu_log_mask(LOG_UNIMP,
- "bcm2835_property: %x set clock rates NYI\n", tag);
+ "bcm2835_property: 0x%08x set clock rate NYI\n",
+ tag);
resplen = 8;
break;
@@ -274,11 +279,12 @@
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "bcm2835_property: unhandled tag %08x\n", tag);
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_property: unhandled tag 0x%08x\n", tag);
break;
}
+ trace_bcm2835_mbox_property(tag, bufsize, resplen);
if (tag == 0) {
break;
}
@@ -403,7 +409,7 @@
}
s->dma_mr = MEMORY_REGION(obj);
- address_space_init(&s->dma_as, s->dma_mr, NULL);
+ address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory");
/* TODO: connect to MAC address of USB NIC device, once we emulate it */
qemu_macaddr_default_if_unset(&s->macaddr);
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 7427622..1deb1d0 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -143,3 +143,9 @@
# aspeed_xdma.c
aspeed_xdma_write(uint64_t offset, uint64_t data) "XDMA write: offset 0x%" PRIx64 " data 0x%" PRIx64
+
+# bcm2835_mbox.c
+bcm2835_mbox_write(unsigned int size, uint64_t addr, uint64_t value) "mbox write sz:%u addr:0x%"PRIx64" data:0x%"PRIx64
+bcm2835_mbox_read(unsigned int size, uint64_t addr, uint64_t value) "mbox read sz:%u addr:0x%"PRIx64" data:0x%"PRIx64
+bcm2835_mbox_irq(unsigned level) "mbox irq:ARM level:%u"
+bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu"
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index 8451c17..d9b3e8c 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -393,7 +393,7 @@
etsec->bh = qemu_bh_new(etsec_timer_hit, etsec);
- etsec->ptimer = ptimer_init(etsec->bh, PTIMER_POLICY_DEFAULT);
+ etsec->ptimer = ptimer_init_with_bh(etsec->bh, PTIMER_POLICY_DEFAULT);
ptimer_set_freq(etsec->ptimer, 100);
}
diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
index 04c78e8..eb8b441 100644
--- a/hw/net/ftgmac100.c
+++ b/hw/net/ftgmac100.c
@@ -15,6 +15,7 @@
#include "hw/irq.h"
#include "hw/net/ftgmac100.h"
#include "sysemu/dma.h"
+#include "qapi/error.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "net/checksum.h"
@@ -1087,9 +1088,170 @@
.class_init = ftgmac100_class_init,
};
+/*
+ * AST2600 MII controller
+ */
+#define ASPEED_MII_PHYCR_FIRE BIT(31)
+#define ASPEED_MII_PHYCR_ST_22 BIT(28)
+#define ASPEED_MII_PHYCR_OP(x) ((x) & (ASPEED_MII_PHYCR_OP_WRITE | \
+ ASPEED_MII_PHYCR_OP_READ))
+#define ASPEED_MII_PHYCR_OP_WRITE BIT(26)
+#define ASPEED_MII_PHYCR_OP_READ BIT(27)
+#define ASPEED_MII_PHYCR_DATA(x) (x & 0xffff)
+#define ASPEED_MII_PHYCR_PHY(x) (((x) >> 21) & 0x1f)
+#define ASPEED_MII_PHYCR_REG(x) (((x) >> 16) & 0x1f)
+
+#define ASPEED_MII_PHYDATA_IDLE BIT(16)
+
+static void aspeed_mii_transition(AspeedMiiState *s, bool fire)
+{
+ if (fire) {
+ s->phycr |= ASPEED_MII_PHYCR_FIRE;
+ s->phydata &= ~ASPEED_MII_PHYDATA_IDLE;
+ } else {
+ s->phycr &= ~ASPEED_MII_PHYCR_FIRE;
+ s->phydata |= ASPEED_MII_PHYDATA_IDLE;
+ }
+}
+
+static void aspeed_mii_do_phy_ctl(AspeedMiiState *s)
+{
+ uint8_t reg;
+ uint16_t data;
+
+ if (!(s->phycr & ASPEED_MII_PHYCR_ST_22)) {
+ aspeed_mii_transition(s, !ASPEED_MII_PHYCR_FIRE);
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported ST code\n", __func__);
+ return;
+ }
+
+ /* Nothing to do */
+ if (!(s->phycr & ASPEED_MII_PHYCR_FIRE)) {
+ return;
+ }
+
+ reg = ASPEED_MII_PHYCR_REG(s->phycr);
+ data = ASPEED_MII_PHYCR_DATA(s->phycr);
+
+ switch (ASPEED_MII_PHYCR_OP(s->phycr)) {
+ case ASPEED_MII_PHYCR_OP_WRITE:
+ do_phy_write(s->nic, reg, data);
+ break;
+ case ASPEED_MII_PHYCR_OP_READ:
+ s->phydata = (s->phydata & ~0xffff) | do_phy_read(s->nic, reg);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid OP code %08x\n",
+ __func__, s->phycr);
+ }
+
+ aspeed_mii_transition(s, !ASPEED_MII_PHYCR_FIRE);
+}
+
+static uint64_t aspeed_mii_read(void *opaque, hwaddr addr, unsigned size)
+{
+ AspeedMiiState *s = ASPEED_MII(opaque);
+
+ switch (addr) {
+ case 0x0:
+ return s->phycr;
+ case 0x4:
+ return s->phydata;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void aspeed_mii_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ AspeedMiiState *s = ASPEED_MII(opaque);
+
+ switch (addr) {
+ case 0x0:
+ s->phycr = value & ~(s->phycr & ASPEED_MII_PHYCR_FIRE);
+ break;
+ case 0x4:
+ s->phydata = value & ~(0xffff | ASPEED_MII_PHYDATA_IDLE);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ aspeed_mii_transition(s, !!(s->phycr & ASPEED_MII_PHYCR_FIRE));
+ aspeed_mii_do_phy_ctl(s);
+}
+
+static const MemoryRegionOps aspeed_mii_ops = {
+ .read = aspeed_mii_read,
+ .write = aspeed_mii_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void aspeed_mii_reset(DeviceState *dev)
+{
+ AspeedMiiState *s = ASPEED_MII(dev);
+
+ s->phycr = 0;
+ s->phydata = 0;
+
+ aspeed_mii_transition(s, !!(s->phycr & ASPEED_MII_PHYCR_FIRE));
+};
+
+static void aspeed_mii_realize(DeviceState *dev, Error **errp)
+{
+ AspeedMiiState *s = ASPEED_MII(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ Object *obj;
+ Error *local_err = NULL;
+
+ obj = object_property_get_link(OBJECT(dev), "nic", &local_err);
+ if (!obj) {
+ error_propagate(errp, local_err);
+ error_prepend(errp, "required link 'nic' not found: ");
+ return;
+ }
+
+ s->nic = FTGMAC100(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &aspeed_mii_ops, s,
+ TYPE_ASPEED_MII, 0x8);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription vmstate_aspeed_mii = {
+ .name = TYPE_ASPEED_MII,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(phycr, FTGMAC100State),
+ VMSTATE_UINT32(phydata, FTGMAC100State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+static void aspeed_mii_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_aspeed_mii;
+ dc->reset = aspeed_mii_reset;
+ dc->realize = aspeed_mii_realize;
+ dc->desc = "Aspeed MII controller";
+}
+
+static const TypeInfo aspeed_mii_info = {
+ .name = TYPE_ASPEED_MII,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedMiiState),
+ .class_init = aspeed_mii_class_init,
+};
+
static void ftgmac100_register_types(void)
{
type_register_static(&ftgmac100_info);
+ type_register_static(&aspeed_mii_info);
}
type_init(ftgmac100_register_types)
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index 8bba2a8..ed551f2 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -21,7 +21,6 @@
#include "hw/ptimer.h"
#include "hw/qdev-properties.h"
#include "qemu/log.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
/* For crc32 */
#include <zlib.h>
@@ -450,8 +449,10 @@
s->e2p_data = 0;
s->free_timer_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 40;
+ ptimer_transaction_begin(s->timer);
ptimer_stop(s->timer);
ptimer_set_count(s->timer, 0xffff);
+ ptimer_transaction_commit(s->timer);
s->gpt_cfg = 0xffff;
s->mac_cr = MAC_CR_PRMS;
@@ -1100,6 +1101,7 @@
break;
case CSR_GPT_CFG:
if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) {
+ ptimer_transaction_begin(s->timer);
if (val & GPT_TIMER_EN) {
ptimer_set_count(s->timer, val & 0xffff);
ptimer_run(s->timer, 0);
@@ -1107,6 +1109,7 @@
ptimer_stop(s->timer);
ptimer_set_count(s->timer, 0xffff);
}
+ ptimer_transaction_commit(s->timer);
}
s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff);
break;
@@ -1328,7 +1331,6 @@
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
lan9118_state *s = LAN9118(dev);
- QEMUBH *bh;
int i;
const MemoryRegionOps *mem_ops =
s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
@@ -1349,10 +1351,11 @@
s->pmt_ctrl = 1;
s->txp = &s->tx_packet;
- bh = qemu_bh_new(lan9118_tick, s);
- s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->timer = ptimer_init(lan9118_tick, s, PTIMER_POLICY_DEFAULT);
+ ptimer_transaction_begin(s->timer);
ptimer_set_freq(s->timer, 10000);
ptimer_set_limit(s->timer, 0xffff, 1);
+ ptimer_transaction_commit(s->timer);
}
static Property lan9118_properties[] = {
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 4b3bd4a..92c7e45 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -157,7 +157,7 @@
int i;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
@@ -168,7 +168,7 @@
reqh = (ClpReqHdr *)buffer;
req_len = lduw_p(&reqh->len);
if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
@@ -180,11 +180,11 @@
resh = (ClpRspHdr *)(buffer + req_len);
res_len = lduw_p(&resh->len);
if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
if ((req_len + res_len) > 8192) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
@@ -390,12 +390,12 @@
uint8_t pcias;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -429,25 +429,25 @@
switch (pcias) {
case ZPCI_IO_BAR_MIN...ZPCI_IO_BAR_MAX:
if (!len || (len > (8 - (offset & 0x7)))) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
result = zpci_read_bar(pbdev, pcias, offset, &data, len);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
case ZPCI_CONFIG_BAR:
if (!len || (len > (4 - (offset & 0x3))) || len == 3) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
data = pci_host_config_read_common(
pbdev->pdev, offset, pci_config_size(pbdev->pdev), len);
if (zpci_endian_swap(&data, len)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
@@ -489,12 +489,12 @@
uint8_t pcias;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -536,13 +536,13 @@
* A length of 0 is invalid and length should not cross a double word
*/
if (!len || (len > (8 - (offset & 0x7)))) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
result = zpci_write_bar(pbdev, pcias, offset, data, len);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
@@ -550,7 +550,7 @@
/* ZPCI uses the pseudo BAR number 15 as configuration space */
/* possible access lengths are 1,2,4 and must not cross a word */
if (!len || (len > (4 - (offset & 0x3))) || len == 3) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
/* len = 1,2,4 so we do not need to test */
@@ -622,12 +622,12 @@
hwaddr start, end;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -709,7 +709,7 @@
uint8_t buffer[128];
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
@@ -772,7 +772,7 @@
if (!memory_region_access_valid(mr, offset, len, true,
MEMTXATTRS_UNSPECIFIED)) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
@@ -786,7 +786,7 @@
ldq_p(buffer + i * 8),
MO_64, MEMTXATTRS_UNSPECIFIED);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
}
@@ -797,7 +797,7 @@
return 0;
specification_error:
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -871,14 +871,14 @@
pba &= ~0xfff;
pal |= 0xfff;
if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return -EINVAL;
}
/* currently we only support designation type 1 with translation */
if (!(dt == ZPCI_IOTA_RTTO && t)) {
error_report("unsupported ioat dt %d t %d", dt, t);
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return -EINVAL;
}
@@ -1003,7 +1003,7 @@
uint64_t cc = ZPCI_PCI_LS_OK;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
@@ -1012,7 +1012,7 @@
fh = env->regs[r1] >> 32;
if (fiba & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
@@ -1040,7 +1040,7 @@
}
if (fib.fmt != 0) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
@@ -1151,7 +1151,7 @@
break;
}
default:
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
cc = ZPCI_PCI_LS_ERR;
}
@@ -1171,7 +1171,7 @@
uint64_t cc = ZPCI_PCI_LS_OK;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
@@ -1185,7 +1185,7 @@
}
if (fiba & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 18ad279..d3edeef 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -650,7 +650,9 @@
static void ccw_machine_4_1_instance_options(MachineState *machine)
{
+ static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V4_1 };
ccw_machine_4_2_instance_options(machine);
+ s390_set_qemu_cpu_model(0x2964, 13, 2, qemu_cpu_feat);
}
static void ccw_machine_4_1_class_options(MachineClass *mc)
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 915641a..68b1675 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1608,25 +1608,28 @@
{
SCSIDiskReq *r = data->r;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
- uint64_t sector_num;
- uint32_t nb_sectors;
assert(r->req.aiocb == NULL);
- if (scsi_disk_req_check_error(r, ret, false)) {
- goto done;
- }
if (data->count > 0) {
- sector_num = ldq_be_p(&data->inbuf[0]);
- nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
- if (!check_lba_range(s, sector_num, nb_sectors)) {
+ r->sector = ldq_be_p(&data->inbuf[0])
+ * (s->qdev.blocksize / BDRV_SECTOR_SIZE);
+ r->sector_count = (ldl_be_p(&data->inbuf[8]) & 0xffffffffULL)
+ * (s->qdev.blocksize / BDRV_SECTOR_SIZE);
+ if (!check_lba_range(s, r->sector, r->sector_count)) {
+ block_acct_invalid(blk_get_stats(s->qdev.conf.blk),
+ BLOCK_ACCT_UNMAP);
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
goto done;
}
+ block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
+ r->sector_count * BDRV_SECTOR_SIZE,
+ BLOCK_ACCT_UNMAP);
+
r->req.aiocb = blk_aio_pdiscard(s->qdev.conf.blk,
- sector_num * s->qdev.blocksize,
- nb_sectors * s->qdev.blocksize,
+ r->sector * BDRV_SECTOR_SIZE,
+ r->sector_count * BDRV_SECTOR_SIZE,
scsi_unmap_complete, data);
data->count--;
data->inbuf += 16;
@@ -1650,7 +1653,13 @@
r->req.aiocb = NULL;
aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
- scsi_unmap_complete_noio(data, ret);
+ if (scsi_disk_req_check_error(r, ret, true)) {
+ scsi_req_unref(&r->req);
+ g_free(data);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ scsi_unmap_complete_noio(data, ret);
+ }
aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
}
@@ -1680,6 +1689,7 @@
}
if (blk_is_read_only(s->qdev.conf.blk)) {
+ block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
return;
}
@@ -1695,10 +1705,12 @@
return;
invalid_param_len:
+ block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
return;
invalid_field:
+ block_acct_invalid(blk_get_stats(s->qdev.conf.blk), BLOCK_ACCT_UNMAP);
scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
}
diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs
index 0665727..a884c23 100644
--- a/hw/sd/Makefile.objs
+++ b/hw/sd/Makefile.objs
@@ -8,3 +8,4 @@
obj-$(CONFIG_OMAP) += omap_mmc.o
obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o
obj-$(CONFIG_RASPI) += bcm2835_sdhost.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_sdhci.o
diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c
new file mode 100644
index 0000000..cff3eb7
--- /dev/null
+++ b/hw/sd/aspeed_sdhci.c
@@ -0,0 +1,198 @@
+/*
+ * Aspeed SD Host Controller
+ * Eddie James <eajames@linux.ibm.com>
+ *
+ * Copyright (C) 2019 IBM Corp
+ * SPDX-License-Identifer: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "hw/sd/aspeed_sdhci.h"
+#include "qapi/error.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+
+#define ASPEED_SDHCI_INFO 0x00
+#define ASPEED_SDHCI_INFO_RESET 0x00030000
+#define ASPEED_SDHCI_DEBOUNCE 0x04
+#define ASPEED_SDHCI_DEBOUNCE_RESET 0x00000005
+#define ASPEED_SDHCI_BUS 0x08
+#define ASPEED_SDHCI_SDIO_140 0x10
+#define ASPEED_SDHCI_SDIO_148 0x18
+#define ASPEED_SDHCI_SDIO_240 0x20
+#define ASPEED_SDHCI_SDIO_248 0x28
+#define ASPEED_SDHCI_WP_POL 0xec
+#define ASPEED_SDHCI_CARD_DET 0xf0
+#define ASPEED_SDHCI_IRQ_STAT 0xfc
+
+#define TO_REG(addr) ((addr) / sizeof(uint32_t))
+
+static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ uint32_t val = 0;
+ AspeedSDHCIState *sdhci = opaque;
+
+ switch (addr) {
+ case ASPEED_SDHCI_SDIO_140:
+ val = (uint32_t)sdhci->slots[0].capareg;
+ break;
+ case ASPEED_SDHCI_SDIO_148:
+ val = (uint32_t)sdhci->slots[0].maxcurr;
+ break;
+ case ASPEED_SDHCI_SDIO_240:
+ val = (uint32_t)sdhci->slots[1].capareg;
+ break;
+ case ASPEED_SDHCI_SDIO_248:
+ val = (uint32_t)sdhci->slots[1].maxcurr;
+ break;
+ default:
+ if (addr < ASPEED_SDHCI_REG_SIZE) {
+ val = sdhci->regs[TO_REG(addr)];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ }
+ }
+
+ return (uint64_t)val;
+}
+
+static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned int size)
+{
+ AspeedSDHCIState *sdhci = opaque;
+
+ switch (addr) {
+ case ASPEED_SDHCI_SDIO_140:
+ sdhci->slots[0].capareg = (uint64_t)(uint32_t)val;
+ break;
+ case ASPEED_SDHCI_SDIO_148:
+ sdhci->slots[0].maxcurr = (uint64_t)(uint32_t)val;
+ break;
+ case ASPEED_SDHCI_SDIO_240:
+ sdhci->slots[1].capareg = (uint64_t)(uint32_t)val;
+ break;
+ case ASPEED_SDHCI_SDIO_248:
+ sdhci->slots[1].maxcurr = (uint64_t)(uint32_t)val;
+ break;
+ default:
+ if (addr < ASPEED_SDHCI_REG_SIZE) {
+ sdhci->regs[TO_REG(addr)] = (uint32_t)val;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ }
+ }
+}
+
+static const MemoryRegionOps aspeed_sdhci_ops = {
+ .read = aspeed_sdhci_read,
+ .write = aspeed_sdhci_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void aspeed_sdhci_set_irq(void *opaque, int n, int level)
+{
+ AspeedSDHCIState *sdhci = opaque;
+
+ if (level) {
+ sdhci->regs[TO_REG(ASPEED_SDHCI_IRQ_STAT)] |= BIT(n);
+
+ qemu_irq_raise(sdhci->irq);
+ } else {
+ sdhci->regs[TO_REG(ASPEED_SDHCI_IRQ_STAT)] &= ~BIT(n);
+
+ qemu_irq_lower(sdhci->irq);
+ }
+}
+
+static void aspeed_sdhci_realize(DeviceState *dev, Error **errp)
+{
+ Error *err = NULL;
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev);
+
+ /* Create input irqs for the slots */
+ qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq,
+ sdhci, NULL, ASPEED_SDHCI_NUM_SLOTS);
+
+ sysbus_init_irq(sbd, &sdhci->irq);
+ memory_region_init_io(&sdhci->iomem, OBJECT(sdhci), &aspeed_sdhci_ops,
+ sdhci, TYPE_ASPEED_SDHCI, 0x1000);
+ sysbus_init_mmio(sbd, &sdhci->iomem);
+
+ for (int i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) {
+ Object *sdhci_slot = OBJECT(&sdhci->slots[i]);
+ SysBusDevice *sbd_slot = SYS_BUS_DEVICE(&sdhci->slots[i]);
+
+ object_property_set_int(sdhci_slot, 2, "sd-spec-version", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_uint(sdhci_slot, ASPEED_SDHCI_CAPABILITIES,
+ "capareg", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(sdhci_slot, true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_connect_irq(sbd_slot, 0, qdev_get_gpio_in(DEVICE(sbd), i));
+ memory_region_add_subregion(&sdhci->iomem, (i + 1) * 0x100,
+ &sdhci->slots[i].iomem);
+ }
+}
+
+static void aspeed_sdhci_reset(DeviceState *dev)
+{
+ AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev);
+
+ memset(sdhci->regs, 0, ASPEED_SDHCI_REG_SIZE);
+ sdhci->regs[TO_REG(ASPEED_SDHCI_INFO)] = ASPEED_SDHCI_INFO_RESET;
+ sdhci->regs[TO_REG(ASPEED_SDHCI_DEBOUNCE)] = ASPEED_SDHCI_DEBOUNCE_RESET;
+}
+
+static const VMStateDescription vmstate_aspeed_sdhci = {
+ .name = TYPE_ASPEED_SDHCI,
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AspeedSDHCIState, ASPEED_SDHCI_NUM_REGS),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static void aspeed_sdhci_class_init(ObjectClass *classp, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(classp);
+
+ dc->realize = aspeed_sdhci_realize;
+ dc->reset = aspeed_sdhci_reset;
+ dc->vmsd = &vmstate_aspeed_sdhci;
+}
+
+static TypeInfo aspeed_sdhci_info = {
+ .name = TYPE_ASPEED_SDHCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedSDHCIState),
+ .class_init = aspeed_sdhci_class_init,
+};
+
+static void aspeed_sdhci_register_types(void)
+{
+ type_register_static(&aspeed_sdhci_info);
+}
+
+type_init(aspeed_sdhci_register_types)
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 9ffc7e0..f0c7bbb 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -30,6 +30,7 @@
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "exec/address-spaces.h"
+#include "qemu/units.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
@@ -50,7 +51,7 @@
#define CONF_FLASH_TYPE0 0
#define CONF_FLASH_TYPE_NOR 0x0
#define CONF_FLASH_TYPE_NAND 0x1
-#define CONF_FLASH_TYPE_SPI 0x2
+#define CONF_FLASH_TYPE_SPI 0x2 /* AST2600 is SPI only */
/* CE Control Register */
#define R_CE_CTRL (0x04 / 4)
@@ -71,8 +72,11 @@
/* CEx Control Register */
#define R_CTRL0 (0x10 / 4)
+#define CTRL_IO_QPI (1 << 31)
+#define CTRL_IO_QUAD_DATA (1 << 30)
#define CTRL_IO_DUAL_DATA (1 << 29)
#define CTRL_IO_DUAL_ADDR_DATA (1 << 28) /* Includes dummies */
+#define CTRL_IO_QUAD_ADDR_DATA (1 << 28) /* Includes dummies */
#define CTRL_CMD_SHIFT 16
#define CTRL_CMD_MASK 0xff
#define CTRL_DUMMY_HIGH_SHIFT 14
@@ -136,7 +140,7 @@
/* Misc Control Register #2 */
#define R_TIMINGS (0x94 / 4)
-/* SPI controller registers and bits */
+/* SPI controller registers and bits (AST2400) */
#define R_SPI_CONF (0x00 / 4)
#define SPI_CONF_ENABLE_W0 0
#define R_SPI_CTRL0 (0x4 / 4)
@@ -211,6 +215,39 @@
{ 0x38000000, 32 * 1024 * 1024 }, /* start address is readonly */
{ 0x3A000000, 96 * 1024 * 1024 }, /* end address is readonly */
};
+static uint32_t aspeed_smc_segment_to_reg(const AspeedSMCState *s,
+ const AspeedSegments *seg);
+static void aspeed_smc_reg_to_segment(const AspeedSMCState *s, uint32_t reg,
+ AspeedSegments *seg);
+
+/*
+ * AST2600 definitions
+ */
+#define ASPEED26_SOC_FMC_FLASH_BASE 0x20000000
+#define ASPEED26_SOC_SPI_FLASH_BASE 0x30000000
+#define ASPEED26_SOC_SPI2_FLASH_BASE 0x50000000
+
+static const AspeedSegments aspeed_segments_ast2600_fmc[] = {
+ { 0x0, 128 * MiB }, /* start address is readonly */
+ { 0x0, 0 }, /* disabled */
+ { 0x0, 0 }, /* disabled */
+};
+
+static const AspeedSegments aspeed_segments_ast2600_spi1[] = {
+ { 0x0, 128 * MiB }, /* start address is readonly */
+ { 0x0, 0 }, /* disabled */
+};
+
+static const AspeedSegments aspeed_segments_ast2600_spi2[] = {
+ { 0x0, 128 * MiB }, /* start address is readonly */
+ { 0x0, 0 }, /* disabled */
+ { 0x0, 0 }, /* disabled */
+};
+
+static uint32_t aspeed_2600_smc_segment_to_reg(const AspeedSMCState *s,
+ const AspeedSegments *seg);
+static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s,
+ uint32_t reg, AspeedSegments *seg);
static const AspeedSMCController controllers[] = {
{
@@ -226,6 +263,8 @@
.flash_window_size = 0x6000000,
.has_dma = false,
.nregs = ASPEED_SMC_R_SMC_MAX,
+ .segment_to_reg = aspeed_smc_segment_to_reg,
+ .reg_to_segment = aspeed_smc_reg_to_segment,
}, {
.name = "aspeed.fmc-ast2400",
.r_conf = R_CONF,
@@ -241,6 +280,8 @@
.dma_flash_mask = 0x0FFFFFFC,
.dma_dram_mask = 0x1FFFFFFC,
.nregs = ASPEED_SMC_R_MAX,
+ .segment_to_reg = aspeed_smc_segment_to_reg,
+ .reg_to_segment = aspeed_smc_reg_to_segment,
}, {
.name = "aspeed.spi1-ast2400",
.r_conf = R_SPI_CONF,
@@ -254,6 +295,8 @@
.flash_window_size = 0x10000000,
.has_dma = false,
.nregs = ASPEED_SMC_R_SPI_MAX,
+ .segment_to_reg = aspeed_smc_segment_to_reg,
+ .reg_to_segment = aspeed_smc_reg_to_segment,
}, {
.name = "aspeed.fmc-ast2500",
.r_conf = R_CONF,
@@ -269,6 +312,8 @@
.dma_flash_mask = 0x0FFFFFFC,
.dma_dram_mask = 0x3FFFFFFC,
.nregs = ASPEED_SMC_R_MAX,
+ .segment_to_reg = aspeed_smc_segment_to_reg,
+ .reg_to_segment = aspeed_smc_reg_to_segment,
}, {
.name = "aspeed.spi1-ast2500",
.r_conf = R_CONF,
@@ -282,6 +327,8 @@
.flash_window_size = 0x8000000,
.has_dma = false,
.nregs = ASPEED_SMC_R_MAX,
+ .segment_to_reg = aspeed_smc_segment_to_reg,
+ .reg_to_segment = aspeed_smc_reg_to_segment,
}, {
.name = "aspeed.spi2-ast2500",
.r_conf = R_CONF,
@@ -295,19 +342,64 @@
.flash_window_size = 0x8000000,
.has_dma = false,
.nregs = ASPEED_SMC_R_MAX,
+ .segment_to_reg = aspeed_smc_segment_to_reg,
+ .reg_to_segment = aspeed_smc_reg_to_segment,
+ }, {
+ .name = "aspeed.fmc-ast2600",
+ .r_conf = R_CONF,
+ .r_ce_ctrl = R_CE_CTRL,
+ .r_ctrl0 = R_CTRL0,
+ .r_timings = R_TIMINGS,
+ .conf_enable_w0 = CONF_ENABLE_W0,
+ .max_slaves = 3,
+ .segments = aspeed_segments_ast2600_fmc,
+ .flash_window_base = ASPEED26_SOC_FMC_FLASH_BASE,
+ .flash_window_size = 0x10000000,
+ .has_dma = true,
+ .nregs = ASPEED_SMC_R_MAX,
+ .segment_to_reg = aspeed_2600_smc_segment_to_reg,
+ .reg_to_segment = aspeed_2600_smc_reg_to_segment,
+ }, {
+ .name = "aspeed.spi1-ast2600",
+ .r_conf = R_CONF,
+ .r_ce_ctrl = R_CE_CTRL,
+ .r_ctrl0 = R_CTRL0,
+ .r_timings = R_TIMINGS,
+ .conf_enable_w0 = CONF_ENABLE_W0,
+ .max_slaves = 2,
+ .segments = aspeed_segments_ast2600_spi1,
+ .flash_window_base = ASPEED26_SOC_SPI_FLASH_BASE,
+ .flash_window_size = 0x10000000,
+ .has_dma = false,
+ .nregs = ASPEED_SMC_R_MAX,
+ .segment_to_reg = aspeed_2600_smc_segment_to_reg,
+ .reg_to_segment = aspeed_2600_smc_reg_to_segment,
+ }, {
+ .name = "aspeed.spi2-ast2600",
+ .r_conf = R_CONF,
+ .r_ce_ctrl = R_CE_CTRL,
+ .r_ctrl0 = R_CTRL0,
+ .r_timings = R_TIMINGS,
+ .conf_enable_w0 = CONF_ENABLE_W0,
+ .max_slaves = 3,
+ .segments = aspeed_segments_ast2600_spi2,
+ .flash_window_base = ASPEED26_SOC_SPI2_FLASH_BASE,
+ .flash_window_size = 0x10000000,
+ .has_dma = false,
+ .nregs = ASPEED_SMC_R_MAX,
+ .segment_to_reg = aspeed_2600_smc_segment_to_reg,
+ .reg_to_segment = aspeed_2600_smc_reg_to_segment,
},
};
/*
- * The Segment Register uses a 8MB unit to encode the start address
- * and the end address of the mapping window of a flash SPI slave :
- *
- * | byte 1 | byte 2 | byte 3 | byte 4 |
- * +--------+--------+--------+--------+
- * | end | start | 0 | 0 |
- *
+ * The Segment Registers of the AST2400 and AST2500 have a 8MB
+ * unit. The address range of a flash SPI slave is encoded with
+ * absolute addresses which should be part of the overall controller
+ * window.
*/
-static inline uint32_t aspeed_smc_segment_to_reg(const AspeedSegments *seg)
+static uint32_t aspeed_smc_segment_to_reg(const AspeedSMCState *s,
+ const AspeedSegments *seg)
{
uint32_t reg = 0;
reg |= ((seg->addr >> 23) & SEG_START_MASK) << SEG_START_SHIFT;
@@ -315,12 +407,47 @@
return reg;
}
-static inline void aspeed_smc_reg_to_segment(uint32_t reg, AspeedSegments *seg)
+static void aspeed_smc_reg_to_segment(const AspeedSMCState *s,
+ uint32_t reg, AspeedSegments *seg)
{
seg->addr = ((reg >> SEG_START_SHIFT) & SEG_START_MASK) << 23;
seg->size = (((reg >> SEG_END_SHIFT) & SEG_END_MASK) << 23) - seg->addr;
}
+/*
+ * The Segment Registers of the AST2600 have a 1MB unit. The address
+ * range of a flash SPI slave is encoded with offsets in the overall
+ * controller window. The previous SoC AST2400 and AST2500 used
+ * absolute addresses. Only bits [27:20] are relevant and the end
+ * address is an upper bound limit.
+ */
+#define AST2600_SEG_ADDR_MASK 0x0ff00000
+
+static uint32_t aspeed_2600_smc_segment_to_reg(const AspeedSMCState *s,
+ const AspeedSegments *seg)
+{
+ uint32_t reg = 0;
+
+ /* Disabled segments have a nil register */
+ if (!seg->size) {
+ return 0;
+ }
+
+ reg |= (seg->addr & AST2600_SEG_ADDR_MASK) >> 16; /* start offset */
+ reg |= (seg->addr + seg->size - 1) & AST2600_SEG_ADDR_MASK; /* end offset */
+ return reg;
+}
+
+static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s,
+ uint32_t reg, AspeedSegments *seg)
+{
+ uint32_t start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK;
+ uint32_t end_offset = reg & AST2600_SEG_ADDR_MASK;
+
+ seg->addr = s->ctrl->flash_window_base + start_offset;
+ seg->size = end_offset + MiB - start_offset;
+}
+
static bool aspeed_smc_flash_overlap(const AspeedSMCState *s,
const AspeedSegments *new,
int cs)
@@ -333,7 +460,7 @@
continue;
}
- aspeed_smc_reg_to_segment(s->regs[R_SEG_ADDR0 + i], &seg);
+ s->ctrl->reg_to_segment(s, s->regs[R_SEG_ADDR0 + i], &seg);
if (new->addr + new->size > seg.addr &&
new->addr < seg.addr + seg.size) {
@@ -354,7 +481,7 @@
AspeedSMCFlash *fl = &s->flashes[cs];
AspeedSegments seg;
- aspeed_smc_reg_to_segment(new, &seg);
+ s->ctrl->reg_to_segment(s, new, &seg);
/* The start address of CS0 is read-only */
if (cs == 0 && seg.addr != s->ctrl->flash_window_base) {
@@ -362,7 +489,7 @@
"%s: Tried to change CS0 start address to 0x%"
HWADDR_PRIx "\n", s->ctrl->name, seg.addr);
seg.addr = s->ctrl->flash_window_base;
- new = aspeed_smc_segment_to_reg(&seg);
+ new = s->ctrl->segment_to_reg(s, &seg);
}
/*
@@ -379,7 +506,7 @@
HWADDR_PRIx "\n", s->ctrl->name, cs, seg.addr + seg.size);
seg.size = s->ctrl->segments[cs].addr + s->ctrl->segments[cs].size -
seg.addr;
- new = aspeed_smc_segment_to_reg(&seg);
+ new = s->ctrl->segment_to_reg(s, &seg);
}
/* Keep the segment in the overall flash window */
@@ -455,8 +582,12 @@
const AspeedSMCState *s = fl->controller;
int cmd = (s->regs[s->r_ctrl0 + fl->id] >> CTRL_CMD_SHIFT) & CTRL_CMD_MASK;
- /* In read mode, the default SPI command is READ (0x3). In other
- * modes, the command should necessarily be defined */
+ /*
+ * In read mode, the default SPI command is READ (0x3). In other
+ * modes, the command should necessarily be defined
+ *
+ * TODO: add support for READ4 (0x13) on AST2600
+ */
if (aspeed_smc_flash_mode(fl) == CTRL_READMODE) {
cmd = SPI_OP_READ;
}
@@ -509,7 +640,7 @@
const AspeedSMCState *s = fl->controller;
AspeedSegments seg;
- aspeed_smc_reg_to_segment(s->regs[R_SEG_ADDR0 + fl->id], &seg);
+ s->ctrl->reg_to_segment(s, s->regs[R_SEG_ADDR0 + fl->id], &seg);
if ((addr % seg.size) != addr) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid address 0x%08x for CS%d segment : "
@@ -769,7 +900,15 @@
/* setup default segment register values for all */
for (i = 0; i < s->ctrl->max_slaves; ++i) {
s->regs[R_SEG_ADDR0 + i] =
- aspeed_smc_segment_to_reg(&s->ctrl->segments[i]);
+ s->ctrl->segment_to_reg(s, &s->ctrl->segments[i]);
+ }
+
+ /* HW strapping flash type for the AST2600 controllers */
+ if (s->ctrl->segments == aspeed_segments_ast2600_fmc) {
+ /* flash type is fixed to SPI for all */
+ s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0);
+ s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1);
+ s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE2);
}
/* HW strapping flash type for FMC controllers */
diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c
index ca5a905..aae880f 100644
--- a/hw/timer/allwinner-a10-pit.c
+++ b/hw/timer/allwinner-a10-pit.c
@@ -22,7 +22,6 @@
#include "hw/timer/allwinner-a10-pit.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
static void a10_pit_update_irq(AwA10PITState *s)
@@ -80,6 +79,7 @@
return 0;
}
+/* Must be called inside a ptimer transaction block for s->timer[index] */
static void a10_pit_set_freq(AwA10PITState *s, int index)
{
uint32_t prescaler, source, source_freq;
@@ -118,6 +118,7 @@
switch (offset & 0x0f) {
case AW_A10_PIT_TIMER_CONTROL:
s->control[index] = value;
+ ptimer_transaction_begin(s->timer[index]);
a10_pit_set_freq(s, index);
if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
ptimer_set_count(s->timer[index], s->interval[index]);
@@ -131,10 +132,13 @@
} else {
ptimer_stop(s->timer[index]);
}
+ ptimer_transaction_commit(s->timer[index]);
break;
case AW_A10_PIT_TIMER_INTERVAL:
s->interval[index] = value;
+ ptimer_transaction_begin(s->timer[index]);
ptimer_set_limit(s->timer[index], s->interval[index], 1);
+ ptimer_transaction_commit(s->timer[index]);
break;
case AW_A10_PIT_TIMER_COUNT:
s->count[index] = value;
@@ -225,8 +229,10 @@
s->control[i] = AW_A10_PIT_DEFAULT_CLOCK;
s->interval[i] = 0;
s->count[i] = 0;
+ ptimer_transaction_begin(s->timer[i]);
ptimer_stop(s->timer[i]);
a10_pit_set_freq(s, i);
+ ptimer_transaction_commit(s->timer[i]);
}
s->watch_dog_mode = 0;
s->watch_dog_control = 0;
@@ -255,7 +261,6 @@
{
AwA10PITState *s = AW_A10_PIT(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- QEMUBH * bh[AW_A10_PIT_TIMER_NR];
uint8_t i;
for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
@@ -270,8 +275,7 @@
tc->container = s;
tc->index = i;
- bh[i] = qemu_bh_new(a10_pit_timer_cb, tc);
- s->timer[i] = ptimer_init(bh[i], PTIMER_POLICY_DEFAULT);
+ s->timer[i] = ptimer_init(a10_pit_timer_cb, tc, PTIMER_POLICY_DEFAULT);
}
}
diff --git a/hw/timer/altera_timer.c b/hw/timer/altera_timer.c
index 936b313..ee32e0e 100644
--- a/hw/timer/altera_timer.c
+++ b/hw/timer/altera_timer.c
@@ -184,7 +184,7 @@
}
t->bh = qemu_bh_new(timer_hit, t);
- t->ptimer = ptimer_init(t->bh, PTIMER_POLICY_DEFAULT);
+ t->ptimer = ptimer_init_with_bh(t->bh, PTIMER_POLICY_DEFAULT);
ptimer_set_freq(t->ptimer, t->freq_hz);
memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t,
diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c
index 9f63abe..fdf97d1 100644
--- a/hw/timer/arm_mptimer.c
+++ b/hw/timer/arm_mptimer.c
@@ -27,7 +27,6 @@
#include "hw/timer/arm_mptimer.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "hw/core/cpu.h"
@@ -65,6 +64,7 @@
return (((control >> 8) & 0xff) + 1) * 10;
}
+/* Must be called within a ptimer transaction block */
static inline void timerblock_set_count(struct ptimer_state *timer,
uint32_t control, uint64_t *count)
{
@@ -77,6 +77,7 @@
ptimer_set_count(timer, *count);
}
+/* Must be called within a ptimer transaction block */
static inline void timerblock_run(struct ptimer_state *timer,
uint32_t control, uint32_t load)
{
@@ -124,6 +125,7 @@
uint32_t control = tb->control;
switch (addr) {
case 0: /* Load */
+ ptimer_transaction_begin(tb->timer);
/* Setting load to 0 stops the timer without doing the tick if
* prescaler = 0.
*/
@@ -132,8 +134,10 @@
}
ptimer_set_limit(tb->timer, value, 1);
timerblock_run(tb->timer, control, value);
+ ptimer_transaction_commit(tb->timer);
break;
case 4: /* Counter. */
+ ptimer_transaction_begin(tb->timer);
/* Setting counter to 0 stops the one-shot timer, or periodic with
* load = 0, without doing the tick if prescaler = 0.
*/
@@ -143,8 +147,10 @@
}
timerblock_set_count(tb->timer, control, &value);
timerblock_run(tb->timer, control, value);
+ ptimer_transaction_commit(tb->timer);
break;
case 8: /* Control. */
+ ptimer_transaction_begin(tb->timer);
if ((control & 3) != (value & 3)) {
ptimer_stop(tb->timer);
}
@@ -160,6 +166,7 @@
timerblock_run(tb->timer, value, count);
}
tb->control = value;
+ ptimer_transaction_commit(tb->timer);
break;
case 12: /* Interrupt status. */
tb->status &= ~value;
@@ -212,9 +219,11 @@
tb->control = 0;
tb->status = 0;
if (tb->timer) {
+ ptimer_transaction_begin(tb->timer);
ptimer_stop(tb->timer);
ptimer_set_limit(tb->timer, 0, 1);
ptimer_set_period(tb->timer, timerblock_scale(0));
+ ptimer_transaction_commit(tb->timer);
}
}
@@ -228,7 +237,7 @@
}
}
-static void arm_mptimer_init(Object *obj)
+static void arm_mptimer_init_with_bh(Object *obj)
{
ARMMPTimerState *s = ARM_MPTIMER(obj);
@@ -260,8 +269,7 @@
*/
for (i = 0; i < s->num_cpu; i++) {
TimerBlock *tb = &s->timerblock[i];
- QEMUBH *bh = qemu_bh_new(timerblock_tick, tb);
- tb->timer = ptimer_init(bh, PTIMER_POLICY);
+ tb->timer = ptimer_init(timerblock_tick, tb, PTIMER_POLICY);
sysbus_init_irq(sbd, &tb->irq);
memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb,
"arm_mptimer_timerblock", 0x20);
@@ -311,7 +319,7 @@
.name = TYPE_ARM_MPTIMER,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ARMMPTimerState),
- .instance_init = arm_mptimer_init,
+ .instance_init = arm_mptimer_init_with_bh,
.class_init = arm_mptimer_class_init,
};
diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c
index c2e6211..af524fa 100644
--- a/hw/timer/arm_timer.c
+++ b/hw/timer/arm_timer.c
@@ -14,7 +14,6 @@
#include "hw/irq.h"
#include "hw/ptimer.h"
#include "hw/qdev-properties.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/log.h"
@@ -75,7 +74,10 @@
}
}
-/* Reset the timer limit after settings have changed. */
+/*
+ * Reset the timer limit after settings have changed.
+ * May only be called from inside a ptimer transaction block.
+ */
static void arm_timer_recalibrate(arm_timer_state *s, int reload)
{
uint32_t limit;
@@ -102,13 +104,16 @@
switch (offset >> 2) {
case 0: /* TimerLoad */
s->limit = value;
+ ptimer_transaction_begin(s->timer);
arm_timer_recalibrate(s, 1);
+ ptimer_transaction_commit(s->timer);
break;
case 1: /* TimerValue */
/* ??? Linux seems to want to write to this readonly register.
Ignore it. */
break;
case 2: /* TimerControl */
+ ptimer_transaction_begin(s->timer);
if (s->control & TIMER_CTRL_ENABLE) {
/* Pause the timer if it is running. This may cause some
inaccuracy dure to rounding, but avoids a whole lot of other
@@ -128,13 +133,16 @@
/* Restart the timer if still enabled. */
ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
}
+ ptimer_transaction_commit(s->timer);
break;
case 3: /* TimerIntClr */
s->int_level = 0;
break;
case 6: /* TimerBGLoad */
s->limit = value;
+ ptimer_transaction_begin(s->timer);
arm_timer_recalibrate(s, 0);
+ ptimer_transaction_commit(s->timer);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -166,14 +174,12 @@
static arm_timer_state *arm_timer_init(uint32_t freq)
{
arm_timer_state *s;
- QEMUBH *bh;
s = (arm_timer_state *)g_malloc0(sizeof(arm_timer_state));
s->freq = freq;
s->control = TIMER_CTRL_IE;
- bh = qemu_bh_new(arm_timer_tick, s);
- s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->timer = ptimer_init(arm_timer_tick, s, PTIMER_POLICY_DEFAULT);
vmstate_register(NULL, -1, &vmstate_arm_timer, s);
return s;
}
diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
index 2bda826..bcce219 100644
--- a/hw/timer/aspeed_timer.c
+++ b/hw/timer/aspeed_timer.c
@@ -160,7 +160,9 @@
timer_del(&t->timer);
if (timer_overflow_interrupt(t)) {
+ AspeedTimerCtrlState *s = timer_to_ctrl(t);
t->level = !t->level;
+ s->irq_sts |= BIT(t->id);
qemu_set_irq(t->irq, t->level);
}
@@ -199,7 +201,9 @@
}
if (interrupt) {
+ AspeedTimerCtrlState *s = timer_to_ctrl(t);
t->level = !t->level;
+ s->irq_sts |= BIT(t->id);
qemu_set_irq(t->irq, t->level);
}
@@ -244,22 +248,14 @@
case 0x30: /* Control Register */
value = s->ctrl;
break;
- case 0x34: /* Control Register 2 */
- value = s->ctrl2;
- break;
case 0x00 ... 0x2c: /* Timers 1 - 4 */
value = aspeed_timer_get_value(&s->timers[(offset >> 4)], reg);
break;
case 0x40 ... 0x8c: /* Timers 5 - 8 */
value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg);
break;
- /* Illegal */
- case 0x38:
- case 0x3C:
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
- __func__, offset);
- value = 0;
+ value = ASPEED_TIMER_GET_CLASS(s)->read(s, offset);
break;
}
trace_aspeed_timer_read(offset, size, value);
@@ -443,9 +439,6 @@
case 0x30:
aspeed_timer_set_ctrl(s, tv);
break;
- case 0x34:
- aspeed_timer_set_ctrl2(s, tv);
- break;
/* Timer Registers */
case 0x00 ... 0x2c:
aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS), reg, tv);
@@ -453,12 +446,8 @@
case 0x40 ... 0x8c:
aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv);
break;
- /* Illegal */
- case 0x38:
- case 0x3C:
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
- __func__, offset);
+ ASPEED_TIMER_GET_CLASS(s)->write(s, offset, value);
break;
}
}
@@ -472,6 +461,135 @@
.valid.unaligned = false,
};
+static uint64_t aspeed_2400_timer_read(AspeedTimerCtrlState *s, hwaddr offset)
+{
+ uint64_t value;
+
+ switch (offset) {
+ case 0x34:
+ value = s->ctrl2;
+ break;
+ case 0x38:
+ case 0x3C:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static void aspeed_2400_timer_write(AspeedTimerCtrlState *s, hwaddr offset,
+ uint64_t value)
+{
+ const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF);
+
+ switch (offset) {
+ case 0x34:
+ aspeed_timer_set_ctrl2(s, tv);
+ break;
+ case 0x38:
+ case 0x3C:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+}
+
+static uint64_t aspeed_2500_timer_read(AspeedTimerCtrlState *s, hwaddr offset)
+{
+ uint64_t value;
+
+ switch (offset) {
+ case 0x34:
+ value = s->ctrl2;
+ break;
+ case 0x38:
+ value = s->ctrl3 & BIT(0);
+ break;
+ case 0x3C:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static void aspeed_2500_timer_write(AspeedTimerCtrlState *s, hwaddr offset,
+ uint64_t value)
+{
+ const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF);
+ uint8_t command;
+
+ switch (offset) {
+ case 0x34:
+ aspeed_timer_set_ctrl2(s, tv);
+ break;
+ case 0x38:
+ command = (value >> 1) & 0xFF;
+ if (command == 0xAE) {
+ s->ctrl3 = 0x1;
+ } else if (command == 0xEA) {
+ s->ctrl3 = 0x0;
+ }
+ break;
+ case 0x3C:
+ if (s->ctrl3 & BIT(0)) {
+ aspeed_timer_set_ctrl(s, s->ctrl & ~tv);
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+}
+
+static uint64_t aspeed_2600_timer_read(AspeedTimerCtrlState *s, hwaddr offset)
+{
+ uint64_t value;
+
+ switch (offset) {
+ case 0x34:
+ value = s->irq_sts;
+ break;
+ case 0x38:
+ case 0x3C:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static void aspeed_2600_timer_write(AspeedTimerCtrlState *s, hwaddr offset,
+ uint64_t value)
+{
+ const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF);
+
+ switch (offset) {
+ case 0x34:
+ s->irq_sts &= tv;
+ break;
+ case 0x3C:
+ aspeed_timer_set_ctrl(s, s->ctrl & ~tv);
+ break;
+
+ case 0x38:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+}
+
static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id)
{
AspeedTimer *t = &s->timers[id];
@@ -525,6 +643,8 @@
}
s->ctrl = 0;
s->ctrl2 = 0;
+ s->ctrl3 = 0;
+ s->irq_sts = 0;
}
static const VMStateDescription vmstate_aspeed_timer = {
@@ -543,11 +663,13 @@
static const VMStateDescription vmstate_aspeed_timer_state = {
.name = "aspeed.timerctrl",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_UINT32(ctrl, AspeedTimerCtrlState),
VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState),
+ VMSTATE_UINT32(ctrl3, AspeedTimerCtrlState),
+ VMSTATE_UINT32(irq_sts, AspeedTimerCtrlState),
VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState,
ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer,
AspeedTimer),
@@ -570,11 +692,64 @@
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedTimerCtrlState),
.class_init = timer_class_init,
+ .class_size = sizeof(AspeedTimerClass),
+ .abstract = true,
+};
+
+static void aspeed_2400_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
+
+ dc->desc = "ASPEED 2400 Timer";
+ awc->read = aspeed_2400_timer_read;
+ awc->write = aspeed_2400_timer_write;
+}
+
+static const TypeInfo aspeed_2400_timer_info = {
+ .name = TYPE_ASPEED_2400_TIMER,
+ .parent = TYPE_ASPEED_TIMER,
+ .class_init = aspeed_2400_timer_class_init,
+};
+
+static void aspeed_2500_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
+
+ dc->desc = "ASPEED 2500 Timer";
+ awc->read = aspeed_2500_timer_read;
+ awc->write = aspeed_2500_timer_write;
+}
+
+static const TypeInfo aspeed_2500_timer_info = {
+ .name = TYPE_ASPEED_2500_TIMER,
+ .parent = TYPE_ASPEED_TIMER,
+ .class_init = aspeed_2500_timer_class_init,
+};
+
+static void aspeed_2600_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
+
+ dc->desc = "ASPEED 2600 Timer";
+ awc->read = aspeed_2600_timer_read;
+ awc->write = aspeed_2600_timer_write;
+}
+
+static const TypeInfo aspeed_2600_timer_info = {
+ .name = TYPE_ASPEED_2600_TIMER,
+ .parent = TYPE_ASPEED_TIMER,
+ .class_init = aspeed_2600_timer_class_init,
};
static void aspeed_timer_register_types(void)
{
type_register_static(&aspeed_timer_info);
+ type_register_static(&aspeed_2400_timer_info);
+ type_register_static(&aspeed_2500_timer_info);
+ type_register_static(&aspeed_2600_timer_info);
}
type_init(aspeed_timer_register_types)
diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c
index 5e2352d..e28ba9c 100644
--- a/hw/timer/cmsdk-apb-dualtimer.c
+++ b/hw/timer/cmsdk-apb-dualtimer.c
@@ -20,7 +20,6 @@
#include "qemu/log.h"
#include "trace.h"
#include "qapi/error.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
@@ -112,6 +111,8 @@
/* Handle a write to the CONTROL register */
uint32_t changed;
+ ptimer_transaction_begin(m->timer);
+
newctrl &= R_CONTROL_VALID_MASK;
changed = m->control ^ newctrl;
@@ -213,6 +214,8 @@
}
m->control = newctrl;
+
+ ptimer_transaction_commit(m->timer);
}
static uint64_t cmsdk_apb_dualtimer_read(void *opaque, hwaddr offset,
@@ -330,6 +333,7 @@
if (!(m->control & R_CONTROL_SIZE_MASK)) {
value &= 0xffff;
}
+ ptimer_transaction_begin(m->timer);
if (!(m->control & R_CONTROL_MODE_MASK)) {
/*
* In free-running mode this won't set the limit but will
@@ -346,6 +350,7 @@
ptimer_run(m->timer, 1);
}
}
+ ptimer_transaction_commit(m->timer);
break;
case A_TIMER1BGLOAD:
/* Set the limit, but not the current count */
@@ -357,7 +362,9 @@
if (!(m->control & R_CONTROL_SIZE_MASK)) {
value &= 0xffff;
}
+ ptimer_transaction_begin(m->timer);
ptimer_set_limit(m->timer, value, 0);
+ ptimer_transaction_commit(m->timer);
break;
case A_TIMER1CONTROL:
cmsdk_dualtimermod_write_control(m, value);
@@ -398,6 +405,7 @@
m->intstatus = 0;
m->load = 0;
m->value = 0xffffffff;
+ ptimer_transaction_begin(m->timer);
ptimer_stop(m->timer);
/*
* We start in free-running mode, with VALUE at 0xffffffff, and
@@ -406,6 +414,7 @@
*/
ptimer_set_limit(m->timer, 0xffff, 1);
ptimer_set_freq(m->timer, m->parent->pclk_frq);
+ ptimer_transaction_commit(m->timer);
}
static void cmsdk_apb_dualtimer_reset(DeviceState *dev)
@@ -450,10 +459,9 @@
for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
CMSDKAPBDualTimerModule *m = &s->timermod[i];
- QEMUBH *bh = qemu_bh_new(cmsdk_dualtimermod_tick, m);
m->parent = s;
- m->timer = ptimer_init(bh,
+ m->timer = ptimer_init(cmsdk_dualtimermod_tick, m,
PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c
index c83e265..40728e8 100644
--- a/hw/timer/cmsdk-apb-timer.c
+++ b/hw/timer/cmsdk-apb-timer.c
@@ -29,7 +29,6 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "trace.h"
@@ -121,14 +120,17 @@
"CMSDK APB timer: EXTIN input not supported\n");
}
s->ctrl = value & 0xf;
+ ptimer_transaction_begin(s->timer);
if (s->ctrl & R_CTRL_EN_MASK) {
ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
} else {
ptimer_stop(s->timer);
}
+ ptimer_transaction_commit(s->timer);
break;
case A_RELOAD:
/* Writing to reload also sets the current timer value */
+ ptimer_transaction_begin(s->timer);
if (!value) {
ptimer_stop(s->timer);
}
@@ -140,8 +142,10 @@
*/
ptimer_run(s->timer, 0);
}
+ ptimer_transaction_commit(s->timer);
break;
case A_VALUE:
+ ptimer_transaction_begin(s->timer);
if (!value && !ptimer_get_limit(s->timer)) {
ptimer_stop(s->timer);
}
@@ -149,6 +153,7 @@
if (value && (s->ctrl & R_CTRL_EN_MASK)) {
ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
}
+ ptimer_transaction_commit(s->timer);
break;
case A_INTSTATUS:
/* Just one bit, which is W1C. */
@@ -191,9 +196,11 @@
trace_cmsdk_apb_timer_reset();
s->ctrl = 0;
s->intstatus = 0;
+ ptimer_transaction_begin(s->timer);
ptimer_stop(s->timer);
/* Set the limit and the count */
ptimer_set_limit(s->timer, 0, 1);
+ ptimer_transaction_commit(s->timer);
}
static void cmsdk_apb_timer_init(Object *obj)
@@ -210,21 +217,21 @@
static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
{
CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
- QEMUBH *bh;
if (s->pclk_frq == 0) {
error_setg(errp, "CMSDK APB timer: pclk-frq property must be set");
return;
}
- bh = qemu_bh_new(cmsdk_apb_timer_tick, s);
- s->timer = ptimer_init(bh,
+ s->timer = ptimer_init(cmsdk_apb_timer_tick, s,
PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
+ ptimer_transaction_begin(s->timer);
ptimer_set_freq(s->timer, s->pclk_frq);
+ ptimer_transaction_commit(s->timer);
}
static const VMStateDescription cmsdk_apb_timer_vmstate = {
diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c
index 021c4ef..3261222 100644
--- a/hw/timer/digic-timer.c
+++ b/hw/timer/digic-timer.c
@@ -29,7 +29,6 @@
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/ptimer.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/log.h"
@@ -52,7 +51,9 @@
{
DigicTimerState *s = DIGIC_TIMER(dev);
+ ptimer_transaction_begin(s->ptimer);
ptimer_stop(s->ptimer);
+ ptimer_transaction_commit(s->ptimer);
s->control = 0;
s->relvalue = 0;
}
@@ -93,16 +94,20 @@
break;
}
+ ptimer_transaction_begin(s->ptimer);
if (value & DIGIC_TIMER_CONTROL_EN) {
ptimer_run(s->ptimer, 0);
}
s->control = (uint32_t)value;
+ ptimer_transaction_commit(s->ptimer);
break;
case DIGIC_TIMER_RELVALUE:
s->relvalue = extract32(value, 0, 16);
+ ptimer_transaction_begin(s->ptimer);
ptimer_set_limit(s->ptimer, s->relvalue, 1);
+ ptimer_transaction_commit(s->ptimer);
break;
case DIGIC_TIMER_VALUE:
@@ -125,17 +130,24 @@
.endianness = DEVICE_NATIVE_ENDIAN,
};
+static void digic_timer_tick(void *opaque)
+{
+ /* Nothing to do on timer rollover */
+}
+
static void digic_timer_init(Object *obj)
{
DigicTimerState *s = DIGIC_TIMER(obj);
- s->ptimer = ptimer_init(NULL, PTIMER_POLICY_DEFAULT);
+ s->ptimer = ptimer_init(digic_timer_tick, NULL, PTIMER_POLICY_DEFAULT);
/*
* FIXME: there is no documentation on Digic timer
* frequency setup so let it always run at 1 MHz
*/
+ ptimer_transaction_begin(s->ptimer);
ptimer_set_freq(s->ptimer, 1 * 1000 * 1000);
+ ptimer_transaction_commit(s->ptimer);
memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s,
TYPE_DIGIC_TIMER, 0x100);
diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c
index d62025b..ab27fe1 100644
--- a/hw/timer/etraxfs_timer.c
+++ b/hw/timer/etraxfs_timer.c
@@ -328,9 +328,9 @@
t->bh_t0 = qemu_bh_new(timer0_hit, t);
t->bh_t1 = qemu_bh_new(timer1_hit, t);
t->bh_wd = qemu_bh_new(watchdog_hit, t);
- t->ptimer_t0 = ptimer_init(t->bh_t0, PTIMER_POLICY_DEFAULT);
- t->ptimer_t1 = ptimer_init(t->bh_t1, PTIMER_POLICY_DEFAULT);
- t->ptimer_wd = ptimer_init(t->bh_wd, PTIMER_POLICY_DEFAULT);
+ t->ptimer_t0 = ptimer_init_with_bh(t->bh_t0, PTIMER_POLICY_DEFAULT);
+ t->ptimer_t1 = ptimer_init_with_bh(t->bh_t1, PTIMER_POLICY_DEFAULT);
+ t->ptimer_wd = ptimer_init_with_bh(t->bh_wd, PTIMER_POLICY_DEFAULT);
sysbus_init_irq(sbd, &t->irq);
sysbus_init_irq(sbd, &t->nmi);
diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c
index 77b9af0..7225758 100644
--- a/hw/timer/exynos4210_mct.c
+++ b/hw/timer/exynos4210_mct.c
@@ -58,7 +58,6 @@
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/timer.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "hw/ptimer.h"
@@ -364,6 +363,7 @@
/*
* Set counter of FRC global timer.
+ * Must be called within exynos4210_gfrc_tx_begin/commit block.
*/
static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count)
{
@@ -385,6 +385,7 @@
/*
* Stop global FRC timer
+ * Must be called within exynos4210_gfrc_tx_begin/commit block.
*/
static void exynos4210_gfrc_stop(Exynos4210MCTGT *s)
{
@@ -395,6 +396,7 @@
/*
* Start global FRC timer
+ * Must be called within exynos4210_gfrc_tx_begin/commit block.
*/
static void exynos4210_gfrc_start(Exynos4210MCTGT *s)
{
@@ -404,6 +406,21 @@
}
/*
+ * Start ptimer transaction for global FRC timer; this is just for
+ * consistency with the way we wrap operations like stop and run.
+ */
+static void exynos4210_gfrc_tx_begin(Exynos4210MCTGT *s)
+{
+ ptimer_transaction_begin(s->ptimer_frc);
+}
+
+/* Commit ptimer transaction for global FRC timer. */
+static void exynos4210_gfrc_tx_commit(Exynos4210MCTGT *s)
+{
+ ptimer_transaction_commit(s->ptimer_frc);
+}
+
+/*
* Find next nearest Comparator. If current Comparator value equals to other
* Comparator value, skip them both
*/
@@ -492,6 +509,7 @@
/*
* Restart global FRC timer
+ * Must be called within exynos4210_gfrc_tx_begin/commit block.
*/
static void exynos4210_gfrc_restart(Exynos4210MCTState *s)
{
@@ -589,6 +607,7 @@
/*
* Set counter of FRC local timer.
+ * Must be called from within exynos4210_lfrc_tx_begin/commit block.
*/
static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s)
{
@@ -601,6 +620,7 @@
/*
* Start local FRC timer
+ * Must be called from within exynos4210_lfrc_tx_begin/commit block.
*/
static void exynos4210_lfrc_start(Exynos4210MCTLT *s)
{
@@ -609,12 +629,25 @@
/*
* Stop local FRC timer
+ * Must be called from within exynos4210_lfrc_tx_begin/commit block.
*/
static void exynos4210_lfrc_stop(Exynos4210MCTLT *s)
{
ptimer_stop(s->ptimer_frc);
}
+/* Start ptimer transaction for local FRC timer */
+static void exynos4210_lfrc_tx_begin(Exynos4210MCTLT *s)
+{
+ ptimer_transaction_begin(s->ptimer_frc);
+}
+
+/* Commit ptimer transaction for local FRC timer */
+static void exynos4210_lfrc_tx_commit(Exynos4210MCTLT *s)
+{
+ ptimer_transaction_commit(s->ptimer_frc);
+}
+
/*
* Local timer free running counter tick handler
*/
@@ -701,6 +734,7 @@
/*
* Start local tick cnt timer.
+ * Must be called within exynos4210_ltick_tx_begin/commit block.
*/
static void exynos4210_ltick_cnt_start(struct tick_timer *s)
{
@@ -716,6 +750,7 @@
/*
* Stop local tick cnt timer.
+ * Must be called within exynos4210_ltick_tx_begin/commit block.
*/
static void exynos4210_ltick_cnt_stop(struct tick_timer *s)
{
@@ -733,6 +768,18 @@
}
}
+/* Start ptimer transaction for local tick timer */
+static void exynos4210_ltick_tx_begin(struct tick_timer *s)
+{
+ ptimer_transaction_begin(s->ptimer_tick);
+}
+
+/* Commit ptimer transaction for local tick timer */
+static void exynos4210_ltick_tx_commit(struct tick_timer *s)
+{
+ ptimer_transaction_commit(s->ptimer_tick);
+}
+
/*
* Get counter for CNT timer
*/
@@ -778,6 +825,7 @@
/*
* Set new values of counters for CNT and INT timers
+ * Must be called within exynos4210_ltick_tx_begin/commit block.
*/
static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt,
uint32_t new_int)
@@ -851,7 +899,9 @@
static void exynos4210_ltick_timer_init(struct tick_timer *s)
{
exynos4210_ltick_int_stop(s);
+ exynos4210_ltick_tx_begin(s);
exynos4210_ltick_cnt_stop(s);
+ exynos4210_ltick_tx_commit(s);
s->count = 0;
s->distance = 0;
@@ -933,6 +983,19 @@
exynos4210_ltick_int_start(&s->tick_timer);
}
+static void tx_ptimer_set_freq(ptimer_state *s, uint32_t freq)
+{
+ /*
+ * callers of exynos4210_mct_update_freq() never do anything
+ * else that needs to be in the same ptimer transaction, so
+ * to avoid a lot of repetition we have a convenience function
+ * for begin/set_freq/commit.
+ */
+ ptimer_transaction_begin(s);
+ ptimer_set_freq(s, freq);
+ ptimer_transaction_commit(s);
+}
+
/* update timer frequency */
static void exynos4210_mct_update_freq(Exynos4210MCTState *s)
{
@@ -945,13 +1008,13 @@
DPRINTF("freq=%dHz\n", s->freq);
/* global timer */
- ptimer_set_freq(s->g_timer.ptimer_frc, s->freq);
+ tx_ptimer_set_freq(s->g_timer.ptimer_frc, s->freq);
/* local timer */
- ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq);
- ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq);
- ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq);
- ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq);
+ tx_ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq);
+ tx_ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq);
+ tx_ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq);
+ tx_ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq);
}
}
@@ -965,7 +1028,9 @@
/* global timer */
memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg));
+ exynos4210_gfrc_tx_begin(&s->g_timer);
exynos4210_gfrc_stop(&s->g_timer);
+ exynos4210_gfrc_tx_commit(&s->g_timer);
/* local timer */
memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt));
@@ -978,7 +1043,9 @@
s->l_timer[i].tick_timer.count = 0;
s->l_timer[i].tick_timer.distance = 0;
s->l_timer[i].tick_timer.progress = 0;
+ exynos4210_lfrc_tx_begin(&s->l_timer[i]);
ptimer_stop(s->l_timer[i].ptimer_frc);
+ exynos4210_lfrc_tx_commit(&s->l_timer[i]);
exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer);
}
@@ -1144,7 +1211,9 @@
}
s->g_timer.reg.cnt = new_frc;
+ exynos4210_gfrc_tx_begin(&s->g_timer);
exynos4210_gfrc_restart(s);
+ exynos4210_gfrc_tx_commit(&s->g_timer);
break;
case G_CNT_WSTAT:
@@ -1168,7 +1237,9 @@
s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
}
+ exynos4210_gfrc_tx_begin(&s->g_timer);
exynos4210_gfrc_restart(s);
+ exynos4210_gfrc_tx_commit(&s->g_timer);
break;
case G_TCON:
@@ -1178,6 +1249,8 @@
DPRINTF("global timer write to reg.g_tcon %llx\n", value);
+ exynos4210_gfrc_tx_begin(&s->g_timer);
+
/* Start FRC if transition from disabled to enabled */
if ((value & G_TCON_TIMER_ENABLE) > (old_val &
G_TCON_TIMER_ENABLE)) {
@@ -1195,6 +1268,8 @@
exynos4210_gfrc_restart(s);
}
}
+
+ exynos4210_gfrc_tx_commit(&s->g_timer);
break;
case G_INT_CSTAT:
@@ -1245,6 +1320,7 @@
s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE;
s->l_timer[lt_i].reg.tcon = value;
+ exynos4210_ltick_tx_begin(&s->l_timer[lt_i].tick_timer);
/* Stop local CNT */
if ((value & L_TCON_TICK_START) <
(old_val & L_TCON_TICK_START)) {
@@ -1272,8 +1348,10 @@
DPRINTF("local timer[%d] start int\n", lt_i);
exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer);
}
+ exynos4210_ltick_tx_commit(&s->l_timer[lt_i].tick_timer);
/* Start or Stop local FRC if TCON changed */
+ exynos4210_lfrc_tx_begin(&s->l_timer[lt_i]);
if ((value & L_TCON_FRC_START) >
(s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
DPRINTF("local timer[%d] start frc\n", lt_i);
@@ -1284,6 +1362,7 @@
DPRINTF("local timer[%d] stop frc\n", lt_i);
exynos4210_lfrc_stop(&s->l_timer[lt_i]);
}
+ exynos4210_lfrc_tx_commit(&s->l_timer[lt_i]);
break;
case L0_TCNTB: case L1_TCNTB:
@@ -1295,8 +1374,10 @@
* Due to this we should reload timer to nearest moment when CNT is
* expired and then in event handler update tcntb to new TCNTB value.
*/
+ exynos4210_ltick_tx_begin(&s->l_timer[lt_i].tick_timer);
exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value,
s->l_timer[lt_i].tick_timer.icntb);
+ exynos4210_ltick_tx_commit(&s->l_timer[lt_i].tick_timer);
s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE;
s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value;
@@ -1425,20 +1506,20 @@
int i;
Exynos4210MCTState *s = EXYNOS4210_MCT(obj);
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
- QEMUBH *bh[2];
/* Global timer */
- bh[0] = qemu_bh_new(exynos4210_gfrc_event, s);
- s->g_timer.ptimer_frc = ptimer_init(bh[0], PTIMER_POLICY_DEFAULT);
+ s->g_timer.ptimer_frc = ptimer_init(exynos4210_gfrc_event, s,
+ PTIMER_POLICY_DEFAULT);
memset(&s->g_timer.reg, 0, sizeof(struct gregs));
/* Local timers */
for (i = 0; i < 2; i++) {
- bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]);
- bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]);
s->l_timer[i].tick_timer.ptimer_tick =
- ptimer_init(bh[0], PTIMER_POLICY_DEFAULT);
- s->l_timer[i].ptimer_frc = ptimer_init(bh[1], PTIMER_POLICY_DEFAULT);
+ ptimer_init(exynos4210_ltick_event, &s->l_timer[i],
+ PTIMER_POLICY_DEFAULT);
+ s->l_timer[i].ptimer_frc =
+ ptimer_init(exynos4210_lfrc_event, &s->l_timer[i],
+ PTIMER_POLICY_DEFAULT);
s->l_timer[i].id = i;
}
diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c
index b7fad2a..59a8c08 100644
--- a/hw/timer/exynos4210_pwm.c
+++ b/hw/timer/exynos4210_pwm.c
@@ -25,7 +25,6 @@
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/timer.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "hw/ptimer.h"
@@ -150,7 +149,9 @@
};
/*
- * PWM update frequency
+ * PWM update frequency.
+ * Must be called within a ptimer_transaction_begin/commit block
+ * for s->timer[id].ptimer.
*/
static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
{
@@ -281,12 +282,15 @@
/* update timers frequencies */
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ ptimer_transaction_begin(s->timer[i].ptimer);
exynos4210_pwm_update_freq(s, s->timer[i].id);
+ ptimer_transaction_commit(s->timer[i].ptimer);
}
break;
case TCON:
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ ptimer_transaction_begin(s->timer[i].ptimer);
if ((value & TCON_TIMER_MANUAL_UPD(i)) >
(s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
/*
@@ -315,6 +319,7 @@
ptimer_stop(s->timer[i].ptimer);
DPRINTF("stop timer %d\n", i);
}
+ ptimer_transaction_commit(s->timer[i].ptimer);
}
s->reg_tcon = value;
break;
@@ -369,8 +374,10 @@
s->timer[i].reg_tcmpb = 0;
s->timer[i].reg_tcntb = 0;
+ ptimer_transaction_begin(s->timer[i].ptimer);
exynos4210_pwm_update_freq(s, s->timer[i].id);
ptimer_stop(s->timer[i].ptimer);
+ ptimer_transaction_commit(s->timer[i].ptimer);
}
}
@@ -388,12 +395,12 @@
Exynos4210PWMState *s = EXYNOS4210_PWM(obj);
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
int i;
- QEMUBH *bh;
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
- bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]);
sysbus_init_irq(dev, &s->timer[i].irq);
- s->timer[i].ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->timer[i].ptimer = ptimer_init(exynos4210_pwm_tick,
+ &s->timer[i],
+ PTIMER_POLICY_DEFAULT);
s->timer[i].id = i;
s->timer[i].parent = s;
}
diff --git a/hw/timer/exynos4210_rtc.c b/hw/timer/exynos4210_rtc.c
index ea68904..f85483a 100644
--- a/hw/timer/exynos4210_rtc.c
+++ b/hw/timer/exynos4210_rtc.c
@@ -28,7 +28,6 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/log.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
@@ -195,6 +194,7 @@
* RTC update frequency
* Parameters:
* reg_value - current RTCCON register or his new value
+ * Must be called within a ptimer_transaction_begin/commit block for s->ptimer.
*/
static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
uint32_t reg_value)
@@ -401,6 +401,8 @@
}
break;
case RTCCON:
+ ptimer_transaction_begin(s->ptimer_1Hz);
+ ptimer_transaction_begin(s->ptimer);
if (value & RTC_ENABLE) {
exynos4210_rtc_update_freq(s, value);
}
@@ -430,6 +432,8 @@
ptimer_stop(s->ptimer);
}
}
+ ptimer_transaction_commit(s->ptimer_1Hz);
+ ptimer_transaction_commit(s->ptimer);
s->reg_rtccon = value;
break;
case TICCNT:
@@ -537,9 +541,13 @@
s->reg_curticcnt = 0;
+ ptimer_transaction_begin(s->ptimer);
exynos4210_rtc_update_freq(s, s->reg_rtccon);
ptimer_stop(s->ptimer);
+ ptimer_transaction_commit(s->ptimer);
+ ptimer_transaction_begin(s->ptimer_1Hz);
ptimer_stop(s->ptimer_1Hz);
+ ptimer_transaction_commit(s->ptimer_1Hz);
}
static const MemoryRegionOps exynos4210_rtc_ops = {
@@ -555,16 +563,18 @@
{
Exynos4210RTCState *s = EXYNOS4210_RTC(obj);
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
- QEMUBH *bh;
- bh = qemu_bh_new(exynos4210_rtc_tick, s);
- s->ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->ptimer = ptimer_init(exynos4210_rtc_tick, s, PTIMER_POLICY_DEFAULT);
+ ptimer_transaction_begin(s->ptimer);
ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
exynos4210_rtc_update_freq(s, 0);
+ ptimer_transaction_commit(s->ptimer);
- bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
- s->ptimer_1Hz = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->ptimer_1Hz = ptimer_init(exynos4210_rtc_1Hz_tick,
+ s, PTIMER_POLICY_DEFAULT);
+ ptimer_transaction_begin(s->ptimer_1Hz);
ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
+ ptimer_transaction_commit(s->ptimer_1Hz);
sysbus_init_irq(dev, &s->alm_irq);
sysbus_init_irq(dev, &s->tick_irq);
diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c
index 32dbf87..bb09268 100644
--- a/hw/timer/grlib_gptimer.c
+++ b/hw/timer/grlib_gptimer.c
@@ -366,7 +366,7 @@
timer->unit = unit;
timer->bh = qemu_bh_new(grlib_gptimer_hit, timer);
- timer->ptimer = ptimer_init(timer->bh, PTIMER_POLICY_DEFAULT);
+ timer->ptimer = ptimer_init_with_bh(timer->bh, PTIMER_POLICY_DEFAULT);
timer->id = i;
/* One IRQ line for each timer */
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index f54e059..baf6338 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -17,7 +17,6 @@
#include "migration/vmstate.h"
#include "hw/irq.h"
#include "hw/misc/imx_ccm.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/log.h"
@@ -74,6 +73,10 @@
}
}
+/*
+ * Must be called from within a ptimer_transaction_begin/commit block
+ * for both s->timer_cmp and s->timer_reload.
+ */
static void imx_epit_set_freq(IMXEPITState *s)
{
uint32_t clksrc;
@@ -105,6 +108,8 @@
s->lr = EPIT_TIMER_MAX;
s->cmp = 0;
s->cnt = 0;
+ ptimer_transaction_begin(s->timer_cmp);
+ ptimer_transaction_begin(s->timer_reload);
/* stop both timers */
ptimer_stop(s->timer_cmp);
ptimer_stop(s->timer_reload);
@@ -117,6 +122,8 @@
/* if the timer is still enabled, restart it */
ptimer_run(s->timer_reload, 0);
}
+ ptimer_transaction_commit(s->timer_cmp);
+ ptimer_transaction_commit(s->timer_reload);
}
static uint32_t imx_epit_update_count(IMXEPITState *s)
@@ -164,6 +171,7 @@
return reg_value;
}
+/* Must be called from ptimer_transaction_begin/commit block for s->timer_cmp */
static void imx_epit_reload_compare_timer(IMXEPITState *s)
{
if ((s->cr & (CR_EN | CR_OCIEN)) == (CR_EN | CR_OCIEN)) {
@@ -191,6 +199,8 @@
switch (offset >> 2) {
case 0: /* CR */
+ ptimer_transaction_begin(s->timer_cmp);
+ ptimer_transaction_begin(s->timer_reload);
oldcr = s->cr;
s->cr = value & 0x03ffffff;
@@ -231,6 +241,9 @@
} else {
ptimer_stop(s->timer_cmp);
}
+
+ ptimer_transaction_commit(s->timer_cmp);
+ ptimer_transaction_commit(s->timer_reload);
break;
case 1: /* SR - ACK*/
@@ -244,6 +257,8 @@
case 2: /* LR - set ticks */
s->lr = value;
+ ptimer_transaction_begin(s->timer_cmp);
+ ptimer_transaction_begin(s->timer_reload);
if (s->cr & CR_RLD) {
/* Also set the limit if the LRD bit is set */
/* If IOVW bit is set then set the timer value */
@@ -255,12 +270,16 @@
}
imx_epit_reload_compare_timer(s);
+ ptimer_transaction_commit(s->timer_cmp);
+ ptimer_transaction_commit(s->timer_reload);
break;
case 3: /* CMP */
s->cmp = value;
+ ptimer_transaction_begin(s->timer_cmp);
imx_epit_reload_compare_timer(s);
+ ptimer_transaction_commit(s->timer_cmp);
break;
@@ -281,6 +300,11 @@
imx_epit_update_int(s);
}
+static void imx_epit_reload(void *opaque)
+{
+ /* No action required on rollover of timer_reload */
+}
+
static const MemoryRegionOps imx_epit_ops = {
.read = imx_epit_read,
.write = imx_epit_write,
@@ -308,7 +332,6 @@
{
IMXEPITState *s = IMX_EPIT(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- QEMUBH *bh;
DPRINTF("\n");
@@ -317,10 +340,9 @@
0x00001000);
sysbus_init_mmio(sbd, &s->iomem);
- s->timer_reload = ptimer_init(NULL, PTIMER_POLICY_DEFAULT);
+ s->timer_reload = ptimer_init(imx_epit_reload, s, PTIMER_POLICY_DEFAULT);
- bh = qemu_bh_new(imx_epit_cmp, s);
- s->timer_cmp = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->timer_cmp = ptimer_init(imx_epit_cmp, s, PTIMER_POLICY_DEFAULT);
}
static void imx_epit_class_init(ObjectClass *klass, void *data)
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 49a441f..5c0d9a2 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -16,7 +16,6 @@
#include "hw/irq.h"
#include "hw/timer/imx_gpt.h"
#include "migration/vmstate.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/log.h"
@@ -127,6 +126,7 @@
CLK_NONE, /* 111 not defined */
};
+/* Must be called from within ptimer_transaction_begin/commit block */
static void imx_gpt_set_freq(IMXGPTState *s)
{
uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
@@ -167,6 +167,7 @@
return timeout;
}
+/* Must be called from within ptimer_transaction_begin/commit block */
static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
{
uint32_t timeout = GPT_TIMER_MAX;
@@ -313,6 +314,7 @@
static void imx_gpt_reset_common(IMXGPTState *s, bool is_soft_reset)
{
+ ptimer_transaction_begin(s->timer);
/* stop timer */
ptimer_stop(s->timer);
@@ -350,6 +352,7 @@
if (s->freq && (s->cr & GPT_CR_EN)) {
ptimer_run(s->timer, 1);
}
+ ptimer_transaction_commit(s->timer);
}
static void imx_gpt_soft_reset(DeviceState *dev)
@@ -382,6 +385,7 @@
imx_gpt_soft_reset(DEVICE(s));
} else {
/* set our freq, as the source might have changed */
+ ptimer_transaction_begin(s->timer);
imx_gpt_set_freq(s);
if ((oldreg ^ s->cr) & GPT_CR_EN) {
@@ -397,12 +401,15 @@
ptimer_stop(s->timer);
}
}
+ ptimer_transaction_commit(s->timer);
}
break;
case 1: /* Prescaler */
s->pr = value & 0xfff;
+ ptimer_transaction_begin(s->timer);
imx_gpt_set_freq(s);
+ ptimer_transaction_commit(s->timer);
break;
case 2: /* SR */
@@ -414,13 +421,16 @@
s->ir = value & 0x3f;
imx_gpt_update_int(s);
+ ptimer_transaction_begin(s->timer);
imx_gpt_compute_next_timeout(s, false);
+ ptimer_transaction_commit(s->timer);
break;
case 4: /* OCR1 -- output compare register */
s->ocr1 = value;
+ ptimer_transaction_begin(s->timer);
/* In non-freerun mode, reset count when this register is written */
if (!(s->cr & GPT_CR_FRR)) {
s->next_timeout = GPT_TIMER_MAX;
@@ -429,6 +439,7 @@
/* compute the new timeout */
imx_gpt_compute_next_timeout(s, false);
+ ptimer_transaction_commit(s->timer);
break;
@@ -436,7 +447,9 @@
s->ocr2 = value;
/* compute the new timeout */
+ ptimer_transaction_begin(s->timer);
imx_gpt_compute_next_timeout(s, false);
+ ptimer_transaction_commit(s->timer);
break;
@@ -444,7 +457,9 @@
s->ocr3 = value;
/* compute the new timeout */
+ ptimer_transaction_begin(s->timer);
imx_gpt_compute_next_timeout(s, false);
+ ptimer_transaction_commit(s->timer);
break;
@@ -484,15 +499,13 @@
{
IMXGPTState *s = IMX_GPT(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- QEMUBH *bh;
sysbus_init_irq(sbd, &s->irq);
memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpt_ops, s, TYPE_IMX_GPT,
0x00001000);
sysbus_init_mmio(sbd, &s->iomem);
- bh = qemu_bh_new(imx_gpt_timeout, s);
- s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->timer = ptimer_init(imx_gpt_timeout, s, PTIMER_POLICY_DEFAULT);
}
static void imx_gpt_class_init(ObjectClass *klass, void *data)
diff --git a/hw/timer/lm32_timer.c b/hw/timer/lm32_timer.c
index cf316ed..fabde76 100644
--- a/hw/timer/lm32_timer.c
+++ b/hw/timer/lm32_timer.c
@@ -196,7 +196,7 @@
LM32TimerState *s = LM32_TIMER(dev);
s->bh = qemu_bh_new(timer_hit, s);
- s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT);
+ s->ptimer = ptimer_init_with_bh(s->bh, PTIMER_POLICY_DEFAULT);
ptimer_set_freq(s->ptimer, s->freq_hz);
}
diff --git a/hw/timer/milkymist-sysctl.c b/hw/timer/milkymist-sysctl.c
index 6aedc11..5193c03 100644
--- a/hw/timer/milkymist-sysctl.c
+++ b/hw/timer/milkymist-sysctl.c
@@ -294,8 +294,8 @@
s->bh0 = qemu_bh_new(timer0_hit, s);
s->bh1 = qemu_bh_new(timer1_hit, s);
- s->ptimer0 = ptimer_init(s->bh0, PTIMER_POLICY_DEFAULT);
- s->ptimer1 = ptimer_init(s->bh1, PTIMER_POLICY_DEFAULT);
+ s->ptimer0 = ptimer_init_with_bh(s->bh0, PTIMER_POLICY_DEFAULT);
+ s->ptimer1 = ptimer_init_with_bh(s->bh1, PTIMER_POLICY_DEFAULT);
ptimer_set_freq(s->ptimer0, s->freq_hz);
ptimer_set_freq(s->ptimer1, s->freq_hz);
diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c
index 45f1cf4..b1c9a80 100644
--- a/hw/timer/mss-timer.c
+++ b/hw/timer/mss-timer.c
@@ -24,7 +24,6 @@
*/
#include "qemu/osdep.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/log.h"
#include "hw/irq.h"
@@ -67,6 +66,7 @@
qemu_set_irq(st->irq, (ier && isr));
}
+/* Must be called from within a ptimer_transaction_begin/commit block */
static void timer_update(struct Msf2Timer *st)
{
uint64_t count;
@@ -159,7 +159,9 @@
switch (addr) {
case R_TIM_CTRL:
st->regs[R_TIM_CTRL] = value;
+ ptimer_transaction_begin(st->ptimer);
timer_update(st);
+ ptimer_transaction_commit(st->ptimer);
break;
case R_TIM_RIS:
@@ -171,7 +173,9 @@
case R_TIM_LOADVAL:
st->regs[R_TIM_LOADVAL] = value;
if (st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL) {
+ ptimer_transaction_begin(st->ptimer);
timer_update(st);
+ ptimer_transaction_commit(st->ptimer);
}
break;
@@ -228,9 +232,10 @@
for (i = 0; i < NUM_TIMERS; i++) {
struct Msf2Timer *st = &t->timers[i];
- st->bh = qemu_bh_new(timer_hit, st);
- st->ptimer = ptimer_init(st->bh, PTIMER_POLICY_DEFAULT);
+ st->ptimer = ptimer_init(timer_hit, st, PTIMER_POLICY_DEFAULT);
+ ptimer_transaction_begin(st->ptimer);
ptimer_set_freq(st->ptimer, t->freq_hz);
+ ptimer_transaction_commit(st->ptimer);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &st->irq);
}
diff --git a/hw/timer/puv3_ost.c b/hw/timer/puv3_ost.c
index 6fe3700..0898da5 100644
--- a/hw/timer/puv3_ost.c
+++ b/hw/timer/puv3_ost.c
@@ -129,7 +129,7 @@
sysbus_init_irq(sbd, &s->irq);
s->bh = qemu_bh_new(puv3_ost_tick, s);
- s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT);
+ s->ptimer = ptimer_init_with_bh(s->bh, PTIMER_POLICY_DEFAULT);
ptimer_set_freq(s->ptimer, 50 * 1000 * 1000);
memory_region_init_io(&s->iomem, OBJECT(s), &puv3_ost_ops, s, "puv3_ost",
diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c
index adcc0c1..48a81b4 100644
--- a/hw/timer/sh_timer.c
+++ b/hw/timer/sh_timer.c
@@ -204,7 +204,7 @@
s->irq = irq;
bh = qemu_bh_new(sh_timer_tick, s);
- s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT);
sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c
index 38fd32b..692d213 100644
--- a/hw/timer/slavio_timer.c
+++ b/hw/timer/slavio_timer.c
@@ -393,7 +393,7 @@
tc->timer_index = i;
bh = qemu_bh_new(slavio_timer_irq, tc);
- s->cputimer[i].timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
+ s->cputimer[i].timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT);
ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD);
size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE;
diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c
index 3555182..92dbff3 100644
--- a/hw/timer/xilinx_timer.c
+++ b/hw/timer/xilinx_timer.c
@@ -221,7 +221,7 @@
xt->parent = t;
xt->nr = i;
xt->bh = qemu_bh_new(timer_hit, xt);
- xt->ptimer = ptimer_init(xt->bh, PTIMER_POLICY_DEFAULT);
+ xt->ptimer = ptimer_init_with_bh(xt->bh, PTIMER_POLICY_DEFAULT);
ptimer_set_freq(xt->ptimer, t->freq_hz);
}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index c5e6fe6..12fac39 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -651,6 +651,7 @@
}
g_free(vdev->msi_vectors);
+ vdev->msi_vectors = NULL;
if (ret > 0 && ret != vdev->nr_vectors) {
vdev->nr_vectors = ret;
diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs
index 964ce78..e2f70fb 100644
--- a/hw/virtio/Makefile.objs
+++ b/hw/virtio/Makefile.objs
@@ -11,9 +11,11 @@
common-obj-$(CONFIG_VIRTIO_MMIO) += virtio-mmio.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon.o
obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-crypto.o
+obj-$(CONFIG_VHOST_USER_FS) += vhost-user-fs.o
obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-pci.o
obj-$(CONFIG_VIRTIO_PMEM) += virtio-pmem.o
common-obj-$(call land,$(CONFIG_VIRTIO_PMEM),$(CONFIG_VIRTIO_PCI)) += virtio-pmem-pci.o
+obj-$(call land,$(CONFIG_VHOST_USER_FS),$(CONFIG_VIRTIO_PCI)) += vhost-user-fs-pci.o
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o
ifeq ($(CONFIG_VIRTIO_PCI),y)
diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c
new file mode 100644
index 0000000..933a3f2
--- /dev/null
+++ b/hw/virtio/vhost-user-fs-pci.c
@@ -0,0 +1,85 @@
+/*
+ * Vhost-user filesystem virtio device PCI glue
+ *
+ * Copyright 2018-2019 Red Hat, Inc.
+ *
+ * Authors:
+ * Dr. David Alan Gilbert <dgilbert@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/vhost-user-fs.h"
+#include "virtio-pci.h"
+
+struct VHostUserFSPCI {
+ VirtIOPCIProxy parent_obj;
+ VHostUserFS vdev;
+};
+
+typedef struct VHostUserFSPCI VHostUserFSPCI;
+
+#define TYPE_VHOST_USER_FS_PCI "vhost-user-fs-pci-base"
+
+#define VHOST_USER_FS_PCI(obj) \
+ OBJECT_CHECK(VHostUserFSPCI, (obj), TYPE_VHOST_USER_FS_PCI)
+
+static Property vhost_user_fs_pci_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VHostUserFSPCI *dev = VHOST_USER_FS_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 1;
+ }
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void vhost_user_fs_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = vhost_user_fs_pci_realize;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = vhost_user_fs_pci_properties;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */
+ pcidev_k->revision = 0x00;
+ pcidev_k->class_id = PCI_CLASS_STORAGE_OTHER;
+}
+
+static void vhost_user_fs_pci_instance_init(Object *obj)
+{
+ VHostUserFSPCI *dev = VHOST_USER_FS_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_USER_FS);
+}
+
+static const VirtioPCIDeviceTypeInfo vhost_user_fs_pci_info = {
+ .base_name = TYPE_VHOST_USER_FS_PCI,
+ .non_transitional_name = "vhost-user-fs-pci",
+ .instance_size = sizeof(VHostUserFSPCI),
+ .instance_init = vhost_user_fs_pci_instance_init,
+ .class_init = vhost_user_fs_pci_class_init,
+};
+
+static void vhost_user_fs_pci_register(void)
+{
+ virtio_pci_types_register(&vhost_user_fs_pci_info);
+}
+
+type_init(vhost_user_fs_pci_register);
diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c
new file mode 100644
index 0000000..f0df7f4
--- /dev/null
+++ b/hw/virtio/vhost-user-fs.c
@@ -0,0 +1,299 @@
+/*
+ * Vhost-user filesystem virtio device
+ *
+ * Copyright 2018-2019 Red Hat, Inc.
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <sys/ioctl.h>
+#include "standard-headers/linux/virtio_fs.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-access.h"
+#include "qemu/error-report.h"
+#include "hw/virtio/vhost-user-fs.h"
+#include "monitor/monitor.h"
+
+static void vuf_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+ struct virtio_fs_config fscfg = {};
+
+ memcpy((char *)fscfg.tag, fs->conf.tag,
+ MIN(strlen(fs->conf.tag) + 1, sizeof(fscfg.tag)));
+
+ virtio_stl_p(vdev, &fscfg.num_request_queues, fs->conf.num_request_queues);
+
+ memcpy(config, &fscfg, sizeof(fscfg));
+}
+
+static void vuf_start(VirtIODevice *vdev)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int ret;
+ int i;
+
+ if (!k->set_guest_notifiers) {
+ error_report("binding does not support guest notifiers");
+ return;
+ }
+
+ ret = vhost_dev_enable_notifiers(&fs->vhost_dev, vdev);
+ if (ret < 0) {
+ error_report("Error enabling host notifiers: %d", -ret);
+ return;
+ }
+
+ ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, true);
+ if (ret < 0) {
+ error_report("Error binding guest notifier: %d", -ret);
+ goto err_host_notifiers;
+ }
+
+ fs->vhost_dev.acked_features = vdev->guest_features;
+ ret = vhost_dev_start(&fs->vhost_dev, vdev);
+ if (ret < 0) {
+ error_report("Error starting vhost: %d", -ret);
+ goto err_guest_notifiers;
+ }
+
+ /*
+ * guest_notifier_mask/pending not used yet, so just unmask
+ * everything here. virtio-pci will do the right thing by
+ * enabling/disabling irqfd.
+ */
+ for (i = 0; i < fs->vhost_dev.nvqs; i++) {
+ vhost_virtqueue_mask(&fs->vhost_dev, vdev, i, false);
+ }
+
+ return;
+
+err_guest_notifiers:
+ k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false);
+err_host_notifiers:
+ vhost_dev_disable_notifiers(&fs->vhost_dev, vdev);
+}
+
+static void vuf_stop(VirtIODevice *vdev)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int ret;
+
+ if (!k->set_guest_notifiers) {
+ return;
+ }
+
+ vhost_dev_stop(&fs->vhost_dev, vdev);
+
+ ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false);
+ if (ret < 0) {
+ error_report("vhost guest notifier cleanup failed: %d", ret);
+ return;
+ }
+
+ vhost_dev_disable_notifiers(&fs->vhost_dev, vdev);
+}
+
+static void vuf_set_status(VirtIODevice *vdev, uint8_t status)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+ bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
+
+ if (!vdev->vm_running) {
+ should_start = false;
+ }
+
+ if (fs->vhost_dev.started == should_start) {
+ return;
+ }
+
+ if (should_start) {
+ vuf_start(vdev);
+ } else {
+ vuf_stop(vdev);
+ }
+}
+
+static uint64_t vuf_get_features(VirtIODevice *vdev,
+ uint64_t requested_features,
+ Error **errp)
+{
+ /* No feature bits used yet */
+ return requested_features;
+}
+
+static void vuf_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /*
+ * Not normally called; it's the daemon that handles the queue;
+ * however virtio's cleanup path can call this.
+ */
+}
+
+static void vuf_guest_notifier_mask(VirtIODevice *vdev, int idx,
+ bool mask)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+
+ vhost_virtqueue_mask(&fs->vhost_dev, vdev, idx, mask);
+}
+
+static bool vuf_guest_notifier_pending(VirtIODevice *vdev, int idx)
+{
+ VHostUserFS *fs = VHOST_USER_FS(vdev);
+
+ return vhost_virtqueue_pending(&fs->vhost_dev, idx);
+}
+
+static void vuf_device_realize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserFS *fs = VHOST_USER_FS(dev);
+ unsigned int i;
+ size_t len;
+ int ret;
+
+ if (!fs->conf.chardev.chr) {
+ error_setg(errp, "missing chardev");
+ return;
+ }
+
+ if (!fs->conf.tag) {
+ error_setg(errp, "missing tag property");
+ return;
+ }
+ len = strlen(fs->conf.tag);
+ if (len == 0) {
+ error_setg(errp, "tag property cannot be empty");
+ return;
+ }
+ if (len > sizeof_field(struct virtio_fs_config, tag)) {
+ error_setg(errp, "tag property must be %zu bytes or less",
+ sizeof_field(struct virtio_fs_config, tag));
+ return;
+ }
+
+ if (fs->conf.num_request_queues == 0) {
+ error_setg(errp, "num-request-queues property must be larger than 0");
+ return;
+ }
+
+ if (!is_power_of_2(fs->conf.queue_size)) {
+ error_setg(errp, "queue-size property must be a power of 2");
+ return;
+ }
+
+ if (fs->conf.queue_size > VIRTQUEUE_MAX_SIZE) {
+ error_setg(errp, "queue-size property must be %u or smaller",
+ VIRTQUEUE_MAX_SIZE);
+ return;
+ }
+
+ if (!vhost_user_init(&fs->vhost_user, &fs->conf.chardev, errp)) {
+ return;
+ }
+
+ virtio_init(vdev, "vhost-user-fs", VIRTIO_ID_FS,
+ sizeof(struct virtio_fs_config));
+
+ /* Hiprio queue */
+ virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
+
+ /* Request queues */
+ for (i = 0; i < fs->conf.num_request_queues; i++) {
+ virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
+ }
+
+ /* 1 high prio queue, plus the number configured */
+ fs->vhost_dev.nvqs = 1 + fs->conf.num_request_queues;
+ fs->vhost_dev.vqs = g_new0(struct vhost_virtqueue, fs->vhost_dev.nvqs);
+ ret = vhost_dev_init(&fs->vhost_dev, &fs->vhost_user,
+ VHOST_BACKEND_TYPE_USER, 0);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "vhost_dev_init failed");
+ goto err_virtio;
+ }
+
+ return;
+
+err_virtio:
+ vhost_user_cleanup(&fs->vhost_user);
+ virtio_cleanup(vdev);
+ g_free(fs->vhost_dev.vqs);
+ return;
+}
+
+static void vuf_device_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserFS *fs = VHOST_USER_FS(dev);
+
+ /* This will stop vhost backend if appropriate. */
+ vuf_set_status(vdev, 0);
+
+ vhost_dev_cleanup(&fs->vhost_dev);
+
+ vhost_user_cleanup(&fs->vhost_user);
+
+ virtio_cleanup(vdev);
+ g_free(fs->vhost_dev.vqs);
+ fs->vhost_dev.vqs = NULL;
+}
+
+static const VMStateDescription vuf_vmstate = {
+ .name = "vhost-user-fs",
+ .unmigratable = 1,
+};
+
+static Property vuf_properties[] = {
+ DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev),
+ DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag),
+ DEFINE_PROP_UINT16("num-request-queues", VHostUserFS,
+ conf.num_request_queues, 1),
+ DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128),
+ DEFINE_PROP_STRING("vhostfd", VHostUserFS, conf.vhostfd),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vuf_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ dc->props = vuf_properties;
+ dc->vmsd = &vuf_vmstate;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ vdc->realize = vuf_device_realize;
+ vdc->unrealize = vuf_device_unrealize;
+ vdc->get_features = vuf_get_features;
+ vdc->get_config = vuf_get_config;
+ vdc->set_status = vuf_set_status;
+ vdc->guest_notifier_mask = vuf_guest_notifier_mask;
+ vdc->guest_notifier_pending = vuf_guest_notifier_pending;
+}
+
+static const TypeInfo vuf_info = {
+ .name = TYPE_VHOST_USER_FS,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VHostUserFS),
+ .class_init = vuf_class_init,
+};
+
+static void vuf_register_types(void)
+{
+ type_register_static(&vuf_info);
+}
+
+type_init(vuf_register_types)
diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c
index 6bf43f9..e6f3b93 100644
--- a/hw/watchdog/cmsdk-apb-watchdog.c
+++ b/hw/watchdog/cmsdk-apb-watchdog.c
@@ -24,7 +24,6 @@
#include "qemu/log.h"
#include "trace.h"
#include "qapi/error.h"
-#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "sysemu/watchdog.h"
#include "hw/sysbus.h"
@@ -200,8 +199,10 @@
* Reset the load value and the current count, and make sure
* we're counting.
*/
+ ptimer_transaction_begin(s->timer);
ptimer_set_limit(s->timer, value, 1);
ptimer_run(s->timer, 0);
+ ptimer_transaction_commit(s->timer);
break;
case A_WDOGCONTROL:
if (s->is_luminary && 0 != (R_WDOGCONTROL_INTEN_MASK & s->control)) {
@@ -217,7 +218,9 @@
break;
case A_WDOGINTCLR:
s->intstatus = 0;
+ ptimer_transaction_begin(s->timer);
ptimer_set_count(s->timer, ptimer_get_limit(s->timer));
+ ptimer_transaction_commit(s->timer);
cmsdk_apb_watchdog_update(s);
break;
case A_WDOGLOCK:
@@ -299,8 +302,10 @@
s->itop = 0;
s->resetstatus = 0;
/* Set the limit and the count */
+ ptimer_transaction_begin(s->timer);
ptimer_set_limit(s->timer, 0xffffffff, 1);
ptimer_run(s->timer, 0);
+ ptimer_transaction_commit(s->timer);
}
static void cmsdk_apb_watchdog_init(Object *obj)
@@ -320,7 +325,6 @@
static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp)
{
CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev);
- QEMUBH *bh;
if (s->wdogclk_frq == 0) {
error_setg(errp,
@@ -328,14 +332,15 @@
return;
}
- bh = qemu_bh_new(cmsdk_apb_watchdog_tick, s);
- s->timer = ptimer_init(bh,
+ s->timer = ptimer_init(cmsdk_apb_watchdog_tick, s,
PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
+ ptimer_transaction_begin(s->timer);
ptimer_set_freq(s->timer, s->wdogclk_frq);
+ ptimer_transaction_commit(s->timer);
}
static const VMStateDescription cmsdk_apb_watchdog_vmstate = {
diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c
index 9b93213..145be6f 100644
--- a/hw/watchdog/wdt_aspeed.c
+++ b/hw/watchdog/wdt_aspeed.c
@@ -40,12 +40,14 @@
#define WDT_DRIVE_TYPE_MASK (0xFF << 24)
#define WDT_PUSH_PULL_MAGIC (0xA8 << 24)
#define WDT_OPEN_DRAIN_MAGIC (0x8A << 24)
+#define WDT_RESET_MASK1 (0x1c / 4)
#define WDT_TIMEOUT_STATUS (0x10 / 4)
#define WDT_TIMEOUT_CLEAR (0x14 / 4)
#define WDT_RESTART_MAGIC 0x4755
+#define AST2600_SCU_RESET_CONTROL1 (0x40 / 4)
#define SCU_RESET_CONTROL1 (0x04 / 4)
#define SCU_RESET_SDRAM BIT(0)
@@ -54,21 +56,6 @@
return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE;
}
-static bool is_ast2500(const AspeedWDTState *s)
-{
- switch (s->silicon_rev) {
- case AST2500_A0_SILICON_REV:
- case AST2500_A1_SILICON_REV:
- return true;
- case AST2400_A0_SILICON_REV:
- case AST2400_A1_SILICON_REV:
- default:
- break;
- }
-
- return false;
-}
-
static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size)
{
AspeedWDTState *s = ASPEED_WDT(opaque);
@@ -89,6 +76,8 @@
return s->regs[WDT_CTRL];
case WDT_RESET_WIDTH:
return s->regs[WDT_RESET_WIDTH];
+ case WDT_RESET_MASK1:
+ return s->regs[WDT_RESET_MASK1];
case WDT_TIMEOUT_STATUS:
case WDT_TIMEOUT_CLEAR:
qemu_log_mask(LOG_UNIMP,
@@ -124,6 +113,7 @@
unsigned size)
{
AspeedWDTState *s = ASPEED_WDT(opaque);
+ AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(s);
bool enable = data & WDT_CTRL_ENABLE;
offset >>= 2;
@@ -140,7 +130,7 @@
case WDT_RESTART:
if ((data & 0xFFFF) == WDT_RESTART_MAGIC) {
s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE];
- aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK));
+ aspeed_wdt_reload(s, !(s->regs[WDT_CTRL] & WDT_CTRL_1MHZ_CLK));
}
break;
case WDT_CTRL:
@@ -153,24 +143,18 @@
}
break;
case WDT_RESET_WIDTH:
- {
- uint32_t property = data & WDT_POLARITY_MASK;
-
- if (property && is_ast2500(s)) {
- if (property == WDT_ACTIVE_HIGH_MAGIC) {
- s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_ACTIVE_HIGH;
- } else if (property == WDT_ACTIVE_LOW_MAGIC) {
- s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_ACTIVE_HIGH;
- } else if (property == WDT_PUSH_PULL_MAGIC) {
- s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_PUSH_PULL;
- } else if (property == WDT_OPEN_DRAIN_MAGIC) {
- s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_PUSH_PULL;
- }
+ if (awc->reset_pulse) {
+ awc->reset_pulse(s, data & WDT_POLARITY_MASK);
}
- s->regs[WDT_RESET_WIDTH] &= ~s->ext_pulse_width_mask;
- s->regs[WDT_RESET_WIDTH] |= data & s->ext_pulse_width_mask;
+ s->regs[WDT_RESET_WIDTH] &= ~awc->ext_pulse_width_mask;
+ s->regs[WDT_RESET_WIDTH] |= data & awc->ext_pulse_width_mask;
break;
- }
+
+ case WDT_RESET_MASK1:
+ /* TODO: implement */
+ s->regs[WDT_RESET_MASK1] = data;
+ break;
+
case WDT_TIMEOUT_STATUS:
case WDT_TIMEOUT_CLEAR:
qemu_log_mask(LOG_UNIMP,
@@ -226,9 +210,10 @@
static void aspeed_wdt_timer_expired(void *dev)
{
AspeedWDTState *s = ASPEED_WDT(dev);
+ uint32_t reset_ctrl_reg = ASPEED_WDT_GET_CLASS(s)->reset_ctrl_reg;
/* Do not reset on SDRAM controller reset */
- if (s->scu->regs[SCU_RESET_CONTROL1] & SCU_RESET_SDRAM) {
+ if (s->scu->regs[reset_ctrl_reg] & SCU_RESET_SDRAM) {
timer_del(s->timer);
s->regs[WDT_CTRL] = 0;
return;
@@ -256,25 +241,6 @@
}
s->scu = ASPEED_SCU(obj);
- if (!is_supported_silicon_rev(s->silicon_rev)) {
- error_setg(errp, "Unknown silicon revision: 0x%" PRIx32,
- s->silicon_rev);
- return;
- }
-
- switch (s->silicon_rev) {
- case AST2400_A0_SILICON_REV:
- case AST2400_A1_SILICON_REV:
- s->ext_pulse_width_mask = 0xff;
- break;
- case AST2500_A0_SILICON_REV:
- case AST2500_A1_SILICON_REV:
- s->ext_pulse_width_mask = 0xfffff;
- break;
- default:
- g_assert_not_reached();
- }
-
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev);
/* FIXME: This setting should be derived from the SCU hw strapping
@@ -287,20 +253,15 @@
sysbus_init_mmio(sbd, &s->iomem);
}
-static Property aspeed_wdt_properties[] = {
- DEFINE_PROP_UINT32("silicon-rev", AspeedWDTState, silicon_rev, 0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void aspeed_wdt_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->desc = "ASPEED Watchdog Controller";
dc->realize = aspeed_wdt_realize;
dc->reset = aspeed_wdt_reset;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->vmsd = &vmstate_aspeed_wdt;
- dc->props = aspeed_wdt_properties;
}
static const TypeInfo aspeed_wdt_info = {
@@ -308,12 +269,88 @@
.name = TYPE_ASPEED_WDT,
.instance_size = sizeof(AspeedWDTState),
.class_init = aspeed_wdt_class_init,
+ .class_size = sizeof(AspeedWDTClass),
+ .abstract = true,
+};
+
+static void aspeed_2400_wdt_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass);
+
+ dc->desc = "ASPEED 2400 Watchdog Controller";
+ awc->offset = 0x20;
+ awc->ext_pulse_width_mask = 0xff;
+ awc->reset_ctrl_reg = SCU_RESET_CONTROL1;
+}
+
+static const TypeInfo aspeed_2400_wdt_info = {
+ .name = TYPE_ASPEED_2400_WDT,
+ .parent = TYPE_ASPEED_WDT,
+ .instance_size = sizeof(AspeedWDTState),
+ .class_init = aspeed_2400_wdt_class_init,
+};
+
+static void aspeed_2500_wdt_reset_pulse(AspeedWDTState *s, uint32_t property)
+{
+ if (property) {
+ if (property == WDT_ACTIVE_HIGH_MAGIC) {
+ s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_ACTIVE_HIGH;
+ } else if (property == WDT_ACTIVE_LOW_MAGIC) {
+ s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_ACTIVE_HIGH;
+ } else if (property == WDT_PUSH_PULL_MAGIC) {
+ s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_PUSH_PULL;
+ } else if (property == WDT_OPEN_DRAIN_MAGIC) {
+ s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_PUSH_PULL;
+ }
+ }
+}
+
+static void aspeed_2500_wdt_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass);
+
+ dc->desc = "ASPEED 2500 Watchdog Controller";
+ awc->offset = 0x20;
+ awc->ext_pulse_width_mask = 0xfffff;
+ awc->reset_ctrl_reg = SCU_RESET_CONTROL1;
+ awc->reset_pulse = aspeed_2500_wdt_reset_pulse;
+}
+
+static const TypeInfo aspeed_2500_wdt_info = {
+ .name = TYPE_ASPEED_2500_WDT,
+ .parent = TYPE_ASPEED_WDT,
+ .instance_size = sizeof(AspeedWDTState),
+ .class_init = aspeed_2500_wdt_class_init,
+};
+
+static void aspeed_2600_wdt_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass);
+
+ dc->desc = "ASPEED 2600 Watchdog Controller";
+ awc->offset = 0x40;
+ awc->ext_pulse_width_mask = 0xfffff; /* TODO */
+ awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1;
+ awc->reset_pulse = aspeed_2500_wdt_reset_pulse;
+}
+
+static const TypeInfo aspeed_2600_wdt_info = {
+ .name = TYPE_ASPEED_2600_WDT,
+ .parent = TYPE_ASPEED_WDT,
+ .instance_size = sizeof(AspeedWDTState),
+ .class_init = aspeed_2600_wdt_class_init,
};
static void wdt_aspeed_register_types(void)
{
watchdog_add_model(&model);
type_register_static(&aspeed_wdt_info);
+ type_register_static(&aspeed_2400_wdt_info);
+ type_register_static(&aspeed_2500_wdt_info);
+ type_register_static(&aspeed_2600_wdt_info);
}
type_init(wdt_aspeed_register_types)
diff --git a/include/block/accounting.h b/include/block/accounting.h
index d1f67b1..878b4c3 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -33,9 +33,11 @@
typedef struct BlockAcctStats BlockAcctStats;
enum BlockAcctType {
+ BLOCK_ACCT_NONE = 0,
BLOCK_ACCT_READ,
BLOCK_ACCT_WRITE,
BLOCK_ACCT_FLUSH,
+ BLOCK_ACCT_UNMAP,
BLOCK_MAX_IOTYPE,
};
diff --git a/include/block/aio_task.h b/include/block/aio_task.h
new file mode 100644
index 0000000..50bc1e1
--- /dev/null
+++ b/include/block/aio_task.h
@@ -0,0 +1,54 @@
+/*
+ * Aio tasks loops
+ *
+ * Copyright (c) 2019 Virtuozzo International GmbH.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLOCK_AIO_TASK_H
+#define BLOCK_AIO_TASK_H
+
+#include "qemu/coroutine.h"
+
+typedef struct AioTaskPool AioTaskPool;
+typedef struct AioTask AioTask;
+typedef int coroutine_fn (*AioTaskFunc)(AioTask *task);
+struct AioTask {
+ AioTaskPool *pool;
+ AioTaskFunc func;
+ int ret;
+};
+
+AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks);
+void aio_task_pool_free(AioTaskPool *);
+
+/* error code of failed task or 0 if all is OK */
+int aio_task_pool_status(AioTaskPool *pool);
+
+bool aio_task_pool_empty(AioTaskPool *pool);
+
+/* User provides filled @task, however task->pool will be set automatically */
+void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task);
+
+void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool);
+void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool);
+void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool);
+
+#endif /* BLOCK_AIO_TASK_H */
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
new file mode 100644
index 0000000..e2e135ff
--- /dev/null
+++ b/include/block/block-copy.h
@@ -0,0 +1,93 @@
+/*
+ * block_copy API
+ *
+ * Copyright (C) 2013 Proxmox Server Solutions
+ * Copyright (c) 2019 Virtuozzo International GmbH.
+ *
+ * Authors:
+ * Dietmar Maurer (dietmar@proxmox.com)
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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.
+ */
+
+#ifndef BLOCK_COPY_H
+#define BLOCK_COPY_H
+
+#include "block/block.h"
+
+typedef struct BlockCopyInFlightReq {
+ int64_t start_byte;
+ int64_t end_byte;
+ QLIST_ENTRY(BlockCopyInFlightReq) list;
+ CoQueue wait_queue; /* coroutines blocked on this request */
+} BlockCopyInFlightReq;
+
+typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque);
+typedef void (*ProgressResetCallbackFunc)(void *opaque);
+typedef struct BlockCopyState {
+ /*
+ * BdrvChild objects are not owned or managed by block-copy. They are
+ * provided by block-copy user and user is responsible for appropriate
+ * permissions on these children.
+ */
+ BdrvChild *source;
+ BdrvChild *target;
+ BdrvDirtyBitmap *copy_bitmap;
+ int64_t cluster_size;
+ bool use_copy_range;
+ int64_t copy_range_size;
+ uint64_t len;
+ QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs;
+
+ BdrvRequestFlags write_flags;
+
+ /*
+ * skip_unallocated:
+ *
+ * Used by sync=top jobs, which first scan the source node for unallocated
+ * areas and clear them in the copy_bitmap. During this process, the bitmap
+ * is thus not fully initialized: It may still have bits set for areas that
+ * are unallocated and should actually not be copied.
+ *
+ * This is indicated by skip_unallocated.
+ *
+ * In this case, block_copy() will query the source’s allocation status,
+ * skip unallocated regions, clear them in the copy_bitmap, and invoke
+ * block_copy_reset_unallocated() every time it does.
+ */
+ bool skip_unallocated;
+
+ /* progress_bytes_callback: called when some copying progress is done. */
+ ProgressBytesCallbackFunc progress_bytes_callback;
+
+ /*
+ * progress_reset_callback: called when some bytes reset from copy_bitmap
+ * (see @skip_unallocated above). The callee is assumed to recalculate how
+ * many bytes remain based on the dirty bit count of copy_bitmap.
+ */
+ ProgressResetCallbackFunc progress_reset_callback;
+ void *progress_opaque;
+} BlockCopyState;
+
+BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
+ int64_t cluster_size,
+ BdrvRequestFlags write_flags,
+ Error **errp);
+
+void block_copy_set_callbacks(
+ BlockCopyState *s,
+ ProgressBytesCallbackFunc progress_bytes_callback,
+ ProgressResetCallbackFunc progress_reset_callback,
+ void *progress_opaque);
+
+void block_copy_state_free(BlockCopyState *s);
+
+int64_t block_copy_reset_unallocated(BlockCopyState *s,
+ int64_t offset, int64_t *count);
+
+int coroutine_fn block_copy(BlockCopyState *s, int64_t start, uint64_t bytes,
+ bool *error_is_read);
+
+#endif /* BLOCK_COPY_H */
diff --git a/include/block/block.h b/include/block/block.h
index 37c9de7..792bb82 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -501,6 +501,7 @@
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs,
Error **errp);
+BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs);
void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t offset, int64_t bytes,
int64_t *cluster_offset,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 0422acd..05056b3 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -366,6 +366,7 @@
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs,
Error **errp);
+ BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs);
int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
QEMUIOVector *qiov,
@@ -1196,6 +1197,7 @@
BdrvDirtyBitmap *sync_bitmap,
BitmapSyncMode bitmap_mode,
bool compress,
+ const char *filter_node_name,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
int creation_flags,
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 49db07b..04795c4 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -72,6 +72,23 @@
void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc);
void QEMU_NORETURN cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc);
+/**
+ * cpu_loop_exit_requested:
+ * @cpu: The CPU state to be tested
+ *
+ * Indicate if somebody asked for a return of the CPU to the main loop
+ * (e.g., via cpu_exit() or cpu_interrupt()).
+ *
+ * This is helpful for architectures that support interruptible
+ * instructions. After writing back all state to registers/memory, this
+ * call can be used to check if it makes sense to return to the main loop
+ * or to continue executing the interruptible instruction.
+ */
+static inline bool cpu_loop_exit_requested(CPUState *cpu)
+{
+ return (int32_t)atomic_read(&cpu_neg(cpu)->icount_decr.u32) < 0;
+}
+
#if !defined(CONFIG_USER_ONLY)
void cpu_reloading_memory_map(void);
/**
diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h
index e96e621..ad158bb 100644
--- a/include/exec/ram_addr.h
+++ b/include/exec/ram_addr.h
@@ -193,30 +193,29 @@
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
- blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
+ idx = page / DIRTY_MEMORY_BLOCK_SIZE;
+ offset = page % DIRTY_MEMORY_BLOCK_SIZE;
+ base = page - offset;
+ while (page < end) {
+ unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE);
+ unsigned long num = next - base;
+ unsigned long found = find_next_bit(blocks->blocks[idx],
+ num, offset);
+ if (found < num) {
+ dirty = true;
+ break;
+ }
- idx = page / DIRTY_MEMORY_BLOCK_SIZE;
- offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- base = page - offset;
- while (page < end) {
- unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE);
- unsigned long num = next - base;
- unsigned long found = find_next_bit(blocks->blocks[idx], num, offset);
- if (found < num) {
- dirty = true;
- break;
+ page = next;
+ idx++;
+ offset = 0;
+ base += DIRTY_MEMORY_BLOCK_SIZE;
}
-
- page = next;
- idx++;
- offset = 0;
- base += DIRTY_MEMORY_BLOCK_SIZE;
}
- rcu_read_unlock();
-
return dirty;
}
@@ -234,7 +233,7 @@
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
@@ -256,8 +255,6 @@
base += DIRTY_MEMORY_BLOCK_SIZE;
}
- rcu_read_unlock();
-
return dirty;
}
@@ -309,13 +306,11 @@
idx = page / DIRTY_MEMORY_BLOCK_SIZE;
offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
set_bit_atomic(offset, blocks->blocks[idx]);
-
- rcu_read_unlock();
}
static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start,
@@ -334,39 +329,37 @@
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
+ blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i]);
+ }
- for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
- blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i]);
+ idx = page / DIRTY_MEMORY_BLOCK_SIZE;
+ offset = page % DIRTY_MEMORY_BLOCK_SIZE;
+ base = page - offset;
+ while (page < end) {
+ unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE);
+
+ if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) {
+ bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx],
+ offset, next - page);
+ }
+ if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) {
+ bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx],
+ offset, next - page);
+ }
+ if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) {
+ bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx],
+ offset, next - page);
+ }
+
+ page = next;
+ idx++;
+ offset = 0;
+ base += DIRTY_MEMORY_BLOCK_SIZE;
+ }
}
- idx = page / DIRTY_MEMORY_BLOCK_SIZE;
- offset = page % DIRTY_MEMORY_BLOCK_SIZE;
- base = page - offset;
- while (page < end) {
- unsigned long next = MIN(end, base + DIRTY_MEMORY_BLOCK_SIZE);
-
- if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) {
- bitmap_set_atomic(blocks[DIRTY_MEMORY_MIGRATION]->blocks[idx],
- offset, next - page);
- }
- if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) {
- bitmap_set_atomic(blocks[DIRTY_MEMORY_VGA]->blocks[idx],
- offset, next - page);
- }
- if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) {
- bitmap_set_atomic(blocks[DIRTY_MEMORY_CODE]->blocks[idx],
- offset, next - page);
- }
-
- page = next;
- idx++;
- offset = 0;
- base += DIRTY_MEMORY_BLOCK_SIZE;
- }
-
- rcu_read_unlock();
-
xen_hvm_modified_memory(start, length);
}
@@ -396,36 +389,35 @@
offset = BIT_WORD((start >> TARGET_PAGE_BITS) %
DIRTY_MEMORY_BLOCK_SIZE);
- rcu_read_lock();
-
- for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
- blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i])->blocks;
- }
-
- for (k = 0; k < nr; k++) {
- if (bitmap[k]) {
- unsigned long temp = leul_to_cpu(bitmap[k]);
-
- atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp);
-
- if (global_dirty_log) {
- atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset],
- temp);
- }
-
- if (tcg_enabled()) {
- atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], temp);
- }
+ WITH_RCU_READ_LOCK_GUARD() {
+ for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
+ blocks[i] = atomic_rcu_read(&ram_list.dirty_memory[i])->blocks;
}
- if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) {
- offset = 0;
- idx++;
+ for (k = 0; k < nr; k++) {
+ if (bitmap[k]) {
+ unsigned long temp = leul_to_cpu(bitmap[k]);
+
+ atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp);
+
+ if (global_dirty_log) {
+ atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset],
+ temp);
+ }
+
+ if (tcg_enabled()) {
+ atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset],
+ temp);
+ }
+ }
+
+ if (++offset >= BITS_TO_LONGS(DIRTY_MEMORY_BLOCK_SIZE)) {
+ offset = 0;
+ idx++;
+ }
}
}
- rcu_read_unlock();
-
xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS);
} else {
uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE;
diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h
index 0ba90ef..a2a12af 100644
--- a/include/hw/acpi/acpi_dev_interface.h
+++ b/include/hw/acpi/acpi_dev_interface.h
@@ -13,6 +13,7 @@
ACPI_MEMORY_HOTPLUG_STATUS = 8,
ACPI_NVDIMM_HOTPLUG_STATUS = 16,
ACPI_VMGENID_CHANGE_STATUS = 32,
+ ACPI_POWER_DOWN_STATUS = 64,
} AcpiEventStatusBits;
#define TYPE_ACPI_DEVICE_IF "acpi-device-interface"
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
new file mode 100644
index 0000000..d157eac
--- /dev/null
+++ b/include/hw/acpi/generic_event_device.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright (c) 2018 Intel Corporation
+ * Copyright (c) 2019 Huawei Technologies R & D (UK) Ltd
+ * Written by Samuel Ortiz, Shameer Kolothum
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * The ACPI Generic Event Device (GED) is a hardware-reduced specific
+ * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
+ * including the hotplug ones. Generic Event Device allows platforms
+ * to handle interrupts in ACPI ASL statements. It follows a very
+ * similar approach like the _EVT method from GPIO events. All
+ * interrupts are listed in _CRS and the handler is written in _EVT
+ * method. Here, we use a single interrupt for the GED device, relying
+ * on IO memory region to communicate the type of device affected by
+ * the interrupt. This way, we can support up to 32 events with a
+ * unique interrupt.
+ *
+ * Here is an example.
+ *
+ * Device (\_SB.GED)
+ * {
+ * Name (_HID, "ACPI0013")
+ * Name (_UID, Zero)
+ * Name (_CRS, ResourceTemplate ()
+ * {
+ * Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
+ * {
+ * 0x00000029,
+ * }
+ * })
+ * OperationRegion (EREG, SystemMemory, 0x09080000, 0x04)
+ * Field (EREG, DWordAcc, NoLock, WriteAsZeros)
+ * {
+ * ESEL, 32
+ * }
+ *
+ * Method (_EVT, 1, Serialized) // _EVT: Event
+ * {
+ * Local0 = ESEL // ESEL = IO memory region which specifies the
+ * // device type.
+ * If (((Local0 & One) == One))
+ * {
+ * MethodEvent1()
+ * }
+ * If ((Local0 & 0x2) == 0x2)
+ * {
+ * MethodEvent2()
+ * }
+ * ...
+ * }
+ * }
+ *
+ */
+
+#ifndef HW_ACPI_GED_H
+#define HW_ACPI_GED_H
+
+#include "hw/sysbus.h"
+#include "hw/acpi/memory_hotplug.h"
+
+#define ACPI_POWER_BUTTON_DEVICE "PWRB"
+
+#define TYPE_ACPI_GED "acpi-ged"
+#define ACPI_GED(obj) \
+ OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
+
+#define ACPI_GED_EVT_SEL_OFFSET 0x0
+#define ACPI_GED_EVT_SEL_LEN 0x4
+
+#define GED_DEVICE "GED"
+#define AML_GED_EVT_REG "EREG"
+#define AML_GED_EVT_SEL "ESEL"
+
+/*
+ * Platforms need to specify the GED event bitmap
+ * to describe what kind of events they want to support
+ * through GED.
+ */
+#define ACPI_GED_MEM_HOTPLUG_EVT 0x1
+#define ACPI_GED_PWR_DOWN_EVT 0x2
+
+typedef struct GEDState {
+ MemoryRegion io;
+ uint32_t sel;
+} GEDState;
+
+typedef struct AcpiGedState {
+ SysBusDevice parent_obj;
+ MemHotplugState memhp_state;
+ MemoryRegion container_memhp;
+ GEDState ged_state;
+ uint32_t ged_event_bitmap;
+ qemu_irq irq;
+} AcpiGedState;
+
+void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
+ uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base);
+
+#endif
diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h
index 77c6576..dfe9cf3 100644
--- a/include/hw/acpi/memory_hotplug.h
+++ b/include/hw/acpi/memory_hotplug.h
@@ -5,6 +5,10 @@
#include "hw/acpi/acpi.h"
#include "hw/acpi/aml-build.h"
+#define MEMORY_SLOT_SCAN_METHOD "MSCN"
+#define MEMORY_DEVICES_CONTAINER "\\_SB.MHPC"
+#define MEMORY_HOTPLUG_IO_LEN 24
+
/**
* MemStatus:
* @is_removing: the memory device in slot has been requested to be ejected.
@@ -29,7 +33,7 @@
} MemHotplugState;
void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
- MemHotplugState *state, uint16_t io_base);
+ MemHotplugState *state, hwaddr io_base);
void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st,
DeviceState *dev, Error **errp);
@@ -48,5 +52,6 @@
void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
const char *res_root,
- const char *event_handler_method);
+ const char *event_handler_method,
+ AmlRegionSpace rs, hwaddr memhp_io_base);
#endif
diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
index ab5052b..cccb684 100644
--- a/include/hw/arm/aspeed_soc.h
+++ b/include/hw/arm/aspeed_soc.h
@@ -12,6 +12,7 @@
#ifndef ASPEED_SOC_H
#define ASPEED_SOC_H
+#include "hw/cpu/a15mpcore.h"
#include "hw/intc/aspeed_vic.h"
#include "hw/misc/aspeed_scu.h"
#include "hw/misc/aspeed_sdmc.h"
@@ -24,11 +25,12 @@
#include "hw/net/ftgmac100.h"
#include "target/arm/cpu.h"
#include "hw/gpio/aspeed_gpio.h"
+#include "hw/sd/aspeed_sdhci.h"
#define ASPEED_SPIS_NUM 2
-#define ASPEED_WDTS_NUM 3
+#define ASPEED_WDTS_NUM 4
#define ASPEED_CPUS_NUM 2
-#define ASPEED_MACS_NUM 2
+#define ASPEED_MACS_NUM 4
typedef struct AspeedSoCState {
/*< private >*/
@@ -37,6 +39,7 @@
/*< public >*/
ARMCPU cpu[ASPEED_CPUS_NUM];
uint32_t num_cpus;
+ A15MPPrivState a7mpcore;
MemoryRegion sram;
AspeedVICState vic;
AspeedRtcState rtc;
@@ -49,27 +52,28 @@
AspeedSDMCState sdmc;
AspeedWDTState wdt[ASPEED_WDTS_NUM];
FTGMAC100State ftgmac100[ASPEED_MACS_NUM];
+ AspeedMiiState mii[ASPEED_MACS_NUM];
AspeedGPIOState gpio;
+ AspeedGPIOState gpio_1_8v;
+ AspeedSDHCIState sdhci;
} AspeedSoCState;
#define TYPE_ASPEED_SOC "aspeed-soc"
#define ASPEED_SOC(obj) OBJECT_CHECK(AspeedSoCState, (obj), TYPE_ASPEED_SOC)
-typedef struct AspeedSoCInfo {
+typedef struct AspeedSoCClass {
+ DeviceClass parent_class;
+
const char *name;
const char *cpu_type;
uint32_t silicon_rev;
uint64_t sram_size;
int spis_num;
int wdts_num;
+ int macs_num;
const int *irqmap;
const hwaddr *memmap;
uint32_t num_cpus;
-} AspeedSoCInfo;
-
-typedef struct AspeedSoCClass {
- DeviceClass parent_class;
- AspeedSoCInfo *info;
} AspeedSoCClass;
#define ASPEED_SOC_CLASS(klass) \
@@ -92,8 +96,11 @@
ASPEED_SDMC,
ASPEED_SCU,
ASPEED_ADC,
+ ASPEED_VIDEO,
ASPEED_SRAM,
+ ASPEED_SDHCI,
ASPEED_GPIO,
+ ASPEED_GPIO_1_8V,
ASPEED_RTC,
ASPEED_TIMER1,
ASPEED_TIMER2,
@@ -110,6 +117,12 @@
ASPEED_I2C,
ASPEED_ETH1,
ASPEED_ETH2,
+ ASPEED_ETH3,
+ ASPEED_ETH4,
+ ASPEED_MII1,
+ ASPEED_MII2,
+ ASPEED_MII3,
+ ASPEED_MII4,
ASPEED_SDRAM,
ASPEED_XDMA,
};
diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index 6b17f6a..62a4c7b 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -23,6 +23,7 @@
#include "hw/sd/sdhci.h"
#include "hw/sd/bcm2835_sdhost.h"
#include "hw/gpio/bcm2835_gpio.h"
+#include "hw/misc/unimp.h"
#define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals"
#define BCM2835_PERIPHERALS(obj) \
@@ -37,6 +38,10 @@
MemoryRegion ram_alias[4];
qemu_irq irq, fiq;
+ UnimplementedDeviceState systmr;
+ UnimplementedDeviceState armtmr;
+ UnimplementedDeviceState cprman;
+ UnimplementedDeviceState a2w;
PL011State uart0;
BCM2835AuxState aux;
BCM2835FBState fb;
@@ -48,6 +53,16 @@
SDHCIState sdhci;
BCM2835SDHostState sdhost;
BCM2835GpioState gpio;
+ UnimplementedDeviceState i2s;
+ UnimplementedDeviceState spi[1];
+ UnimplementedDeviceState i2c[3];
+ UnimplementedDeviceState otp;
+ UnimplementedDeviceState dbus;
+ UnimplementedDeviceState ave0;
+ UnimplementedDeviceState bscsl;
+ UnimplementedDeviceState smi;
+ UnimplementedDeviceState dwc2;
+ UnimplementedDeviceState sdramc;
} BCM2835PeripheralState;
#endif /* BCM2835_PERIPHERALS_H */
diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h
index 10083d3..cdcbca9 100644
--- a/include/hw/arm/raspi_platform.h
+++ b/include/hw/arm/raspi_platform.h
@@ -25,8 +25,7 @@
#ifndef HW_ARM_RASPI_PLATFORM_H
#define HW_ARM_RASPI_PLATFORM_H
-#define MCORE_OFFSET 0x0000 /* Fake frame buffer device
- * (the multicore sync block) */
+#define MSYNC_OFFSET 0x0000 /* Multicore Sync Block */
#define IC0_OFFSET 0x2000
#define ST_OFFSET 0x3000 /* System Timer */
#define MPHI_OFFSET 0x6000 /* Message-based Parallel Host Intf. */
@@ -37,9 +36,10 @@
#define ARMCTRL_TIMER0_1_OFFSET (ARM_OFFSET + 0x400) /* Timer 0 and 1 */
#define ARMCTRL_0_SBM_OFFSET (ARM_OFFSET + 0x800) /* User 0 (ARM) Semaphores
* Doorbells & Mailboxes */
-#define PM_OFFSET 0x100000 /* Power Management, Reset controller
- * and Watchdog registers */
-#define PCM_CLOCK_OFFSET 0x101098
+#define CPRMAN_OFFSET 0x100000 /* Power Management, Watchdog */
+#define CM_OFFSET 0x101000 /* Clock Management */
+#define A2W_OFFSET 0x102000 /* Reset controller */
+#define AVS_OFFSET 0x103000 /* Audio Video Standard */
#define RNG_OFFSET 0x104000
#define GPIO_OFFSET 0x200000
#define UART0_OFFSET 0x201000
@@ -47,11 +47,17 @@
#define I2S_OFFSET 0x203000
#define SPI0_OFFSET 0x204000
#define BSC0_OFFSET 0x205000 /* BSC0 I2C/TWI */
-#define UART1_OFFSET 0x215000
-#define EMMC_OFFSET 0x300000
+#define OTP_OFFSET 0x20f000
+#define BSC_SL_OFFSET 0x214000 /* SPI slave */
+#define AUX_OFFSET 0x215000 /* AUX: UART1/SPI1/SPI2 */
+#define EMMC1_OFFSET 0x300000
#define SMI_OFFSET 0x600000
#define BSC1_OFFSET 0x804000 /* BSC1 I2C/TWI */
-#define USB_OFFSET 0x980000 /* DTC_OTG USB controller */
+#define BSC2_OFFSET 0x805000 /* BSC2 I2C/TWI */
+#define DBUS_OFFSET 0x900000
+#define AVE0_OFFSET 0x910000
+#define USB_OTG_OFFSET 0x980000 /* DTC_OTG USB controller */
+#define SDRAMC_OFFSET 0xe00000
#define DMA15_OFFSET 0xE05000 /* DMA controller, channel 15 */
/* GPU interrupts */
@@ -112,7 +118,7 @@
#define INTERRUPT_SPI 54
#define INTERRUPT_I2SPCM 55
#define INTERRUPT_SDIO 56
-#define INTERRUPT_UART 57
+#define INTERRUPT_UART0 57
#define INTERRUPT_SLIMBUS 58
#define INTERRUPT_VEC 59
#define INTERRUPT_CPG 60
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index a720942..0b41083 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -77,6 +77,8 @@
VIRT_GPIO,
VIRT_SECURE_UART,
VIRT_SECURE_MEM,
+ VIRT_PCDIMM_ACPI,
+ VIRT_ACPI_GED,
VIRT_LOWMEMMAP_LAST,
};
@@ -106,6 +108,7 @@
bool claim_edge_triggered_timers;
bool smbios_old_sys_ver;
bool no_highmem_ecam;
+ bool no_ged; /* Machines < 4.2 has no support for ACPI GED device */
} VirtMachineClass;
typedef struct {
@@ -133,6 +136,8 @@
uint32_t iommu_phandle;
int psci_conduit;
hwaddr highest_gpa;
+ DeviceState *acpi_dev;
+ Notifier powerdown_notifier;
} VirtMachineState;
#define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h
index a2753f0..13e0105 100644
--- a/include/hw/i2c/aspeed_i2c.h
+++ b/include/hw/i2c/aspeed_i2c.h
@@ -25,10 +25,13 @@
#include "hw/sysbus.h"
#define TYPE_ASPEED_I2C "aspeed.i2c"
+#define TYPE_ASPEED_2400_I2C TYPE_ASPEED_I2C "-ast2400"
+#define TYPE_ASPEED_2500_I2C TYPE_ASPEED_I2C "-ast2500"
+#define TYPE_ASPEED_2600_I2C TYPE_ASPEED_I2C "-ast2600"
#define ASPEED_I2C(obj) \
OBJECT_CHECK(AspeedI2CState, (obj), TYPE_ASPEED_I2C)
-#define ASPEED_I2C_NR_BUSSES 14
+#define ASPEED_I2C_NR_BUSSES 16
struct AspeedI2CState;
@@ -39,6 +42,7 @@
I2CBus *bus;
uint8_t id;
+ qemu_irq irq;
uint32_t ctrl;
uint32_t timing[2];
@@ -59,6 +63,20 @@
AspeedI2CBus busses[ASPEED_I2C_NR_BUSSES];
} AspeedI2CState;
+#define ASPEED_I2C_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AspeedI2CClass, (klass), TYPE_ASPEED_I2C)
+#define ASPEED_I2C_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AspeedI2CClass, (obj), TYPE_ASPEED_I2C)
+
+typedef struct AspeedI2CClass {
+ SysBusDeviceClass parent_class;
+
+ uint8_t num_busses;
+ uint8_t reg_size;
+ uint8_t gap;
+ qemu_irq (*bus_get_irq)(AspeedI2CBus *);
+} AspeedI2CClass;
+
I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr);
#endif /* ASPEED_I2C_H */
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 6df4f4b..37bfd95 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -70,6 +70,9 @@
/* Address space used by IOAPIC device. All IOAPIC interrupts
* will be translated to MSI messages in the address space. */
AddressSpace *ioapic_as;
+
+ /* ACPI Memory hotplug IO base address */
+ hwaddr memhp_io_base;
};
#define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device"
diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h
index 239e94f..1d7f7ff 100644
--- a/include/hw/misc/aspeed_scu.h
+++ b/include/hw/misc/aspeed_scu.h
@@ -17,8 +17,10 @@
#define ASPEED_SCU(obj) OBJECT_CHECK(AspeedSCUState, (obj), TYPE_ASPEED_SCU)
#define TYPE_ASPEED_2400_SCU TYPE_ASPEED_SCU "-ast2400"
#define TYPE_ASPEED_2500_SCU TYPE_ASPEED_SCU "-ast2500"
+#define TYPE_ASPEED_2600_SCU TYPE_ASPEED_SCU "-ast2600"
#define ASPEED_SCU_NR_REGS (0x1A8 >> 2)
+#define ASPEED_AST2600_SCU_NR_REGS (0xE20 >> 2)
typedef struct AspeedSCUState {
/*< private >*/
@@ -27,7 +29,7 @@
/*< public >*/
MemoryRegion iomem;
- uint32_t regs[ASPEED_SCU_NR_REGS];
+ uint32_t regs[ASPEED_AST2600_SCU_NR_REGS];
uint32_t silicon_rev;
uint32_t hw_strap1;
uint32_t hw_strap2;
@@ -38,6 +40,7 @@
#define AST2400_A1_SILICON_REV 0x02010303U
#define AST2500_A0_SILICON_REV 0x04000303U
#define AST2500_A1_SILICON_REV 0x04010303U
+#define AST2600_A0_SILICON_REV 0x05000303U
#define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04)
@@ -54,6 +57,8 @@
const uint32_t *resets;
uint32_t (*calc_hpll)(AspeedSCUState *s, uint32_t hpll_reg);
uint32_t apb_divider;
+ uint32_t nr_regs;
+ const MemoryRegionOps *ops;
} AspeedSCUClass;
#define ASPEED_SCU_PROT_KEY 0x1688A8A8
diff --git a/include/hw/misc/aspeed_sdmc.h b/include/hw/misc/aspeed_sdmc.h
index b3c926a..5dbde59 100644
--- a/include/hw/misc/aspeed_sdmc.h
+++ b/include/hw/misc/aspeed_sdmc.h
@@ -13,6 +13,9 @@
#define TYPE_ASPEED_SDMC "aspeed.sdmc"
#define ASPEED_SDMC(obj) OBJECT_CHECK(AspeedSDMCState, (obj), TYPE_ASPEED_SDMC)
+#define TYPE_ASPEED_2400_SDMC TYPE_ASPEED_SDMC "-ast2400"
+#define TYPE_ASPEED_2500_SDMC TYPE_ASPEED_SDMC "-ast2500"
+#define TYPE_ASPEED_2600_SDMC TYPE_ASPEED_SDMC "-ast2600"
#define ASPEED_SDMC_NR_REGS (0x174 >> 2)
@@ -24,12 +27,21 @@
MemoryRegion iomem;
uint32_t regs[ASPEED_SDMC_NR_REGS];
- uint32_t silicon_rev;
- uint32_t ram_bits;
uint64_t ram_size;
uint64_t max_ram_size;
- uint32_t fixed_conf;
-
} AspeedSDMCState;
+#define ASPEED_SDMC_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AspeedSDMCClass, (klass), TYPE_ASPEED_SDMC)
+#define ASPEED_SDMC_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AspeedSDMCClass, (obj), TYPE_ASPEED_SDMC)
+
+typedef struct AspeedSDMCClass {
+ SysBusDeviceClass parent_class;
+
+ uint64_t max_ram_size;
+ uint32_t (*compute_conf)(AspeedSDMCState *s, uint32_t data);
+ void (*write)(AspeedSDMCState *s, uint32_t reg, uint32_t data);
+} AspeedSDMCClass;
+
#endif /* ASPEED_SDMC_H */
diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h
index 94cfe05..ab37e7b 100644
--- a/include/hw/net/ftgmac100.h
+++ b/include/hw/net/ftgmac100.h
@@ -66,4 +66,21 @@
uint32_t rxdes0_edorr;
} FTGMAC100State;
+#define TYPE_ASPEED_MII "aspeed-mmi"
+#define ASPEED_MII(obj) OBJECT_CHECK(AspeedMiiState, (obj), TYPE_ASPEED_MII)
+
+/*
+ * AST2600 MII controller
+ */
+typedef struct AspeedMiiState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ FTGMAC100State *nic;
+
+ MemoryRegion iomem;
+ uint32_t phycr;
+ uint32_t phydata;
+} AspeedMiiState;
+
#endif
diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h
index 9c77055..4c321f6 100644
--- a/include/hw/ptimer.h
+++ b/include/hw/ptimer.h
@@ -72,7 +72,7 @@
* ptimer_set_count() or ptimer_set_limit() will not trigger the timer
* (though it will cause a reload). Only a counter decrement to "0"
* will cause a trigger. Not compatible with NO_IMMEDIATE_TRIGGER;
- * ptimer_init() will assert() that you don't set both.
+ * ptimer_init_with_bh() will assert() that you don't set both.
*/
#define PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT (1 << 5)
@@ -81,7 +81,7 @@
typedef void (*ptimer_cb)(void *opaque);
/**
- * ptimer_init - Allocate and return a new ptimer
+ * ptimer_init_with_bh - Allocate and return a new ptimer
* @bh: QEMU bottom half which is run on timer expiry
* @policy: PTIMER_POLICY_* bits specifying behaviour
*
@@ -89,18 +89,72 @@
* The ptimer takes ownership of @bh and will delete it
* when the ptimer is eventually freed.
*/
-ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask);
+ptimer_state *ptimer_init_with_bh(QEMUBH *bh, uint8_t policy_mask);
+
+/**
+ * ptimer_init - Allocate and return a new ptimer
+ * @callback: function to call on ptimer expiry
+ * @callback_opaque: opaque pointer passed to @callback
+ * @policy: PTIMER_POLICY_* bits specifying behaviour
+ *
+ * The ptimer returned must be freed using ptimer_free().
+ *
+ * If a ptimer is created using this API then will use the
+ * transaction-based API for modifying ptimer state: all calls
+ * to functions which modify ptimer state:
+ * - ptimer_set_period()
+ * - ptimer_set_freq()
+ * - ptimer_set_limit()
+ * - ptimer_set_count()
+ * - ptimer_run()
+ * - ptimer_stop()
+ * must be between matched calls to ptimer_transaction_begin()
+ * and ptimer_transaction_commit(). When ptimer_transaction_commit()
+ * is called it will evaluate the state of the timer after all the
+ * changes in the transaction, and call the callback if necessary.
+ *
+ * The callback function is always called from within a transaction
+ * begin/commit block, so the callback should not call the
+ * ptimer_transaction_begin() function itself. If the callback changes
+ * the ptimer state such that another ptimer expiry is triggered, then
+ * the callback will be called a second time after the first call returns.
+ */
+ptimer_state *ptimer_init(ptimer_cb callback,
+ void *callback_opaque,
+ uint8_t policy_mask);
/**
* ptimer_free - Free a ptimer
* @s: timer to free
*
- * Free a ptimer created using ptimer_init() (including
+ * Free a ptimer created using ptimer_init_with_bh() (including
* deleting the bottom half which it is using).
*/
void ptimer_free(ptimer_state *s);
/**
+ * ptimer_transaction_begin() - Start a ptimer modification transaction
+ *
+ * This function must be called before making any calls to functions
+ * which modify the ptimer's state (see the ptimer_init() documentation
+ * for a list of these), and must always have a matched call to
+ * ptimer_transaction_commit().
+ * It is an error to call this function for a BH-based ptimer;
+ * attempting to do this will trigger an assert.
+ */
+void ptimer_transaction_begin(ptimer_state *s);
+
+/**
+ * ptimer_transaction_commit() - Commit a ptimer modification transaction
+ *
+ * This function must be called after calls to functions which modify
+ * the ptimer's state, and completes the update of the ptimer. If the
+ * ptimer state now means that we should trigger the timer expiry
+ * callback, it will be called directly.
+ */
+void ptimer_transaction_commit(ptimer_state *s);
+
+/**
* ptimer_set_period - Set counter increment interval in nanoseconds
* @s: ptimer to configure
* @period: period of the counter in nanoseconds
@@ -108,6 +162,9 @@
* Note that if your counter behaviour is specified as having a
* particular frequency rather than a period then ptimer_set_freq()
* may be more appropriate.
+ *
+ * This function will assert if it is called outside a
+ * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer.
*/
void ptimer_set_period(ptimer_state *s, int64_t period);
@@ -121,6 +178,9 @@
* as setting the frequency then this function is more appropriate,
* because it allows specifying an effective period which is
* precise to fractions of a nanosecond, avoiding rounding errors.
+ *
+ * This function will assert if it is called outside a
+ * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer.
*/
void ptimer_set_freq(ptimer_state *s, uint32_t freq);
@@ -148,6 +208,9 @@
* Set the limit value of the down-counter. The @reload flag can
* be used to emulate the behaviour of timers which immediately
* reload the counter when their reload register is written to.
+ *
+ * This function will assert if it is called outside a
+ * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer.
*/
void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload);
@@ -169,6 +232,9 @@
* Set the value of the down-counter. If the counter is currently
* enabled this will arrange for a timer callback at the appropriate
* point in the future.
+ *
+ * This function will assert if it is called outside a
+ * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer.
*/
void ptimer_set_count(ptimer_state *s, uint64_t count);
@@ -178,10 +244,14 @@
* @oneshot: non-zero if this timer should only count down once
*
* Start a ptimer counting down; when it reaches zero the bottom half
- * passed to ptimer_init() will be invoked. If the @oneshot argument is zero,
+ * passed to ptimer_init_with_bh() will be invoked.
+ * If the @oneshot argument is zero,
* the counter value will then be reloaded from the limit and it will
* start counting down again. If @oneshot is non-zero, then the counter
* will disable itself when it reaches zero.
+ *
+ * This function will assert if it is called outside a
+ * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer.
*/
void ptimer_run(ptimer_state *s, int oneshot);
@@ -194,6 +264,9 @@
*
* Note that this can cause it to "lose" time, even if it is immediately
* restarted.
+ *
+ * This function will assert if it is called outside a
+ * ptimer_transaction_begin/commit block, unless this is a bottom-half ptimer.
*/
void ptimer_stop(ptimer_state *s);
diff --git a/include/hw/sd/aspeed_sdhci.h b/include/hw/sd/aspeed_sdhci.h
new file mode 100644
index 0000000..dfdab43
--- /dev/null
+++ b/include/hw/sd/aspeed_sdhci.h
@@ -0,0 +1,34 @@
+/*
+ * Aspeed SD Host Controller
+ * Eddie James <eajames@linux.ibm.com>
+ *
+ * Copyright (C) 2019 IBM Corp
+ * SPDX-License-Identifer: GPL-2.0-or-later
+ */
+
+#ifndef ASPEED_SDHCI_H
+#define ASPEED_SDHCI_H
+
+#include "hw/sd/sdhci.h"
+
+#define TYPE_ASPEED_SDHCI "aspeed.sdhci"
+#define ASPEED_SDHCI(obj) OBJECT_CHECK(AspeedSDHCIState, (obj), \
+ TYPE_ASPEED_SDHCI)
+
+#define ASPEED_SDHCI_CAPABILITIES 0x01E80080
+#define ASPEED_SDHCI_NUM_SLOTS 2
+#define ASPEED_SDHCI_NUM_REGS (ASPEED_SDHCI_REG_SIZE / sizeof(uint32_t))
+#define ASPEED_SDHCI_REG_SIZE 0x100
+
+typedef struct AspeedSDHCIState {
+ SysBusDevice parent;
+
+ SDHCIState slots[ASPEED_SDHCI_NUM_SLOTS];
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+
+ uint32_t regs[ASPEED_SDHCI_NUM_REGS];
+} AspeedSDHCIState;
+
+#endif /* ASPEED_SDHCI_H */
diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index 5176ff6..684d16e 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -49,6 +49,10 @@
hwaddr dma_flash_mask;
hwaddr dma_dram_mask;
uint32_t nregs;
+ uint32_t (*segment_to_reg)(const struct AspeedSMCState *s,
+ const AspeedSegments *seg);
+ void (*reg_to_segment)(const struct AspeedSMCState *s, uint32_t reg,
+ AspeedSegments *seg);
} AspeedSMCController;
typedef struct AspeedSMCFlash {
diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h
index 1fb949e..9483298 100644
--- a/include/hw/timer/aspeed_timer.h
+++ b/include/hw/timer/aspeed_timer.h
@@ -28,6 +28,10 @@
#define ASPEED_TIMER(obj) \
OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER);
#define TYPE_ASPEED_TIMER "aspeed.timer"
+#define TYPE_ASPEED_2400_TIMER TYPE_ASPEED_TIMER "-ast2400"
+#define TYPE_ASPEED_2500_TIMER TYPE_ASPEED_TIMER "-ast2500"
+#define TYPE_ASPEED_2600_TIMER TYPE_ASPEED_TIMER "-ast2600"
+
#define ASPEED_TIMER_NR_TIMERS 8
typedef struct AspeedTimer {
@@ -55,9 +59,23 @@
uint32_t ctrl;
uint32_t ctrl2;
+ uint32_t ctrl3;
+ uint32_t irq_sts;
AspeedTimer timers[ASPEED_TIMER_NR_TIMERS];
AspeedSCUState *scu;
} AspeedTimerCtrlState;
+#define ASPEED_TIMER_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AspeedTimerClass, (klass), TYPE_ASPEED_TIMER)
+#define ASPEED_TIMER_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AspeedTimerClass, (obj), TYPE_ASPEED_TIMER)
+
+typedef struct AspeedTimerClass {
+ SysBusDeviceClass parent_class;
+
+ uint64_t (*read)(AspeedTimerCtrlState *s, hwaddr offset);
+ void (*write)(AspeedTimerCtrlState *s, hwaddr offset, uint64_t value);
+} AspeedTimerClass;
+
#endif /* ASPEED_TIMER_H */
diff --git a/include/hw/timer/mss-timer.h b/include/hw/timer/mss-timer.h
index d15d173..e5a784b 100644
--- a/include/hw/timer/mss-timer.h
+++ b/include/hw/timer/mss-timer.h
@@ -46,7 +46,6 @@
#define R_TIM1_MAX 6
struct Msf2Timer {
- QEMUBH *bh;
ptimer_state *ptimer;
uint32_t regs[R_TIM1_MAX];
diff --git a/include/hw/virtio/vhost-user-fs.h b/include/hw/virtio/vhost-user-fs.h
new file mode 100644
index 0000000..539885b
--- /dev/null
+++ b/include/hw/virtio/vhost-user-fs.h
@@ -0,0 +1,45 @@
+/*
+ * Vhost-user filesystem virtio device
+ *
+ * Copyright 2018-2019 Red Hat, Inc.
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#ifndef _QEMU_VHOST_USER_FS_H
+#define _QEMU_VHOST_USER_FS_H
+
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/vhost.h"
+#include "hw/virtio/vhost-user.h"
+#include "chardev/char-fe.h"
+
+#define TYPE_VHOST_USER_FS "vhost-user-fs-device"
+#define VHOST_USER_FS(obj) \
+ OBJECT_CHECK(VHostUserFS, (obj), TYPE_VHOST_USER_FS)
+
+typedef struct {
+ CharBackend chardev;
+ char *tag;
+ uint16_t num_request_queues;
+ uint16_t queue_size;
+ char *vhostfd;
+} VHostUserFSConf;
+
+typedef struct {
+ /*< private >*/
+ VirtIODevice parent;
+ VHostUserFSConf conf;
+ struct vhost_virtqueue *vhost_vqs;
+ struct vhost_dev vhost_dev;
+ VhostUserState vhost_user;
+
+ /*< public >*/
+} VHostUserFS;
+
+#endif /* _QEMU_VHOST_USER_FS_H */
diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h
index 8c5691c..dfedd76 100644
--- a/include/hw/watchdog/wdt_aspeed.h
+++ b/include/hw/watchdog/wdt_aspeed.h
@@ -16,6 +16,9 @@
#define TYPE_ASPEED_WDT "aspeed.wdt"
#define ASPEED_WDT(obj) \
OBJECT_CHECK(AspeedWDTState, (obj), TYPE_ASPEED_WDT)
+#define TYPE_ASPEED_2400_WDT TYPE_ASPEED_WDT "-ast2400"
+#define TYPE_ASPEED_2500_WDT TYPE_ASPEED_WDT "-ast2500"
+#define TYPE_ASPEED_2600_WDT TYPE_ASPEED_WDT "-ast2600"
#define ASPEED_WDT_REGS_MAX (0x20 / 4)
@@ -30,8 +33,20 @@
AspeedSCUState *scu;
uint32_t pclk_freq;
- uint32_t silicon_rev;
- uint32_t ext_pulse_width_mask;
} AspeedWDTState;
+#define ASPEED_WDT_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AspeedWDTClass, (klass), TYPE_ASPEED_WDT)
+#define ASPEED_WDT_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AspeedWDTClass, (obj), TYPE_ASPEED_WDT)
+
+typedef struct AspeedWDTClass {
+ SysBusDeviceClass parent_class;
+
+ uint32_t offset;
+ uint32_t ext_pulse_width_mask;
+ uint32_t reset_ctrl_reg;
+ void (*reset_pulse)(AspeedWDTState *s, uint32_t property);
+} AspeedWDTClass;
+
#endif /* WDT_ASPEED_H */
diff --git a/include/migration/misc.h b/include/migration/misc.h
index b9d8e78..d276225 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -60,6 +60,7 @@
void migration_shutdown(void);
void qemu_start_incoming_migration(const char *uri, Error **errp);
bool migration_is_idle(void);
+bool migration_is_active(MigrationState *);
void add_migration_state_change_notifier(Notifier *notify);
void remove_migration_state_change_notifier(Notifier *notify);
bool migration_in_setup(MigrationState *);
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 1fbfd09..b9ee563 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -224,6 +224,7 @@
extern const VMStateInfo vmstate_info_tmp;
extern const VMStateInfo vmstate_info_bitmap;
extern const VMStateInfo vmstate_info_qtailq;
+extern const VMStateInfo vmstate_info_gtree;
#define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
/*
@@ -754,6 +755,45 @@
.start = offsetof(_type, _next), \
}
+/*
+ * For migrating a GTree whose key is a pointer to _key_type and the
+ * value, a pointer to _val_type
+ * The target tree must have been properly initialized
+ * _vmsd: Start address of the 2 element array containing the data vmsd
+ * and the key vmsd, in that order
+ * _key_type: type of the key
+ * _val_type: type of the value
+ */
+#define VMSTATE_GTREE_V(_field, _state, _version, _vmsd, \
+ _key_type, _val_type) \
+{ \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .vmsd = (_vmsd), \
+ .info = &vmstate_info_gtree, \
+ .start = sizeof(_key_type), \
+ .size = sizeof(_val_type), \
+ .offset = offsetof(_state, _field), \
+}
+
+/*
+ * For migrating a GTree with direct key and the value a pointer
+ * to _val_type
+ * The target tree must have been properly initialized
+ * _vmsd: data vmsd
+ * _val_type: type of the value
+ */
+#define VMSTATE_GTREE_DIRECT_KEY_V(_field, _state, _version, _vmsd, _val_type) \
+{ \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .vmsd = (_vmsd), \
+ .info = &vmstate_info_gtree, \
+ .start = 0, \
+ .size = sizeof(_val_type), \
+ .offset = offsetof(_state, _field), \
+}
+
/* _f : field name
_f_n : num of elements field_name
_n : num of elements
diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h
index 22876d1..9c82683 100644
--- a/include/qemu/rcu.h
+++ b/include/qemu/rcu.h
@@ -154,6 +154,31 @@
}), \
(RCUCBFunc *)g_free);
+typedef void RCUReadAuto;
+static inline RCUReadAuto *rcu_read_auto_lock(void)
+{
+ rcu_read_lock();
+ /* Anything non-NULL causes the cleanup function to be called */
+ return (void *)(uintptr_t)0x1;
+}
+
+static inline void rcu_read_auto_unlock(RCUReadAuto *r)
+{
+ rcu_read_unlock();
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(RCUReadAuto, rcu_read_auto_unlock)
+
+#define WITH_RCU_READ_LOCK_GUARD() \
+ WITH_RCU_READ_LOCK_GUARD_(_rcu_read_auto##__COUNTER__)
+
+#define WITH_RCU_READ_LOCK_GUARD_(var) \
+ for (g_autoptr(RCUReadAuto) var = rcu_read_auto_lock(); \
+ (var); rcu_read_auto_unlock(var), (var) = NULL)
+
+#define RCU_READ_LOCK_GUARD() \
+ g_autoptr(RCUReadAuto) _rcu_read_auto __attribute__((unused)) = rcu_read_auto_lock()
+
#ifdef __cplusplus
}
#endif
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
index 682ba1d..3e4e1d9 100644
--- a/include/qom/object_interfaces.h
+++ b/include/qom/object_interfaces.h
@@ -133,6 +133,18 @@
QemuOpts *opts, Error **errp);
/**
+ * user_creatable_print_help:
+ * @type: the QOM type to be added
+ * @opts: options to create
+ *
+ * Prints help if requested in @opts.
+ *
+ * Returns: true if @opts contained a help option and help was printed, false
+ * if no help option was found.
+ */
+bool user_creatable_print_help(const char *type, QemuOpts *opts);
+
+/**
* user_creatable_del:
* @id: the unique ID for the object
* @errp: if an error occurs, a pointer to an area to store the error
diff --git a/include/standard-headers/asm-x86/bootparam.h b/include/standard-headers/asm-x86/bootparam.h
index 67d4f01..a6f7cf5 100644
--- a/include/standard-headers/asm-x86/bootparam.h
+++ b/include/standard-headers/asm-x86/bootparam.h
@@ -29,6 +29,8 @@
#define XLF_EFI_HANDOVER_32 (1<<2)
#define XLF_EFI_HANDOVER_64 (1<<3)
#define XLF_EFI_KEXEC (1<<4)
+#define XLF_5LEVEL (1<<5)
+#define XLF_5LEVEL_ENABLED (1<<6)
#endif /* _ASM_X86_BOOTPARAM_H */
diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h
index e171514..90604a8 100644
--- a/include/standard-headers/asm-x86/kvm_para.h
+++ b/include/standard-headers/asm-x86/kvm_para.h
@@ -30,6 +30,7 @@
#define KVM_FEATURE_ASYNC_PF_VMEXIT 10
#define KVM_FEATURE_PV_SEND_IPI 11
#define KVM_FEATURE_POLL_CONTROL 12
+#define KVM_FEATURE_PV_SCHED_YIELD 13
#define KVM_HINTS_REALTIME 0
diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h
index 9b9919a..4ff422b 100644
--- a/include/standard-headers/linux/ethtool.h
+++ b/include/standard-headers/linux/ethtool.h
@@ -259,10 +259,32 @@
#define ETHTOOL_PHY_FAST_LINK_DOWN_ON 0
#define ETHTOOL_PHY_FAST_LINK_DOWN_OFF 0xff
+/* Energy Detect Power Down (EDPD) is a feature supported by some PHYs, where
+ * the PHY's RX & TX blocks are put into a low-power mode when there is no
+ * link detected (typically cable is un-plugged). For RX, only a minimal
+ * link-detection is available, and for TX the PHY wakes up to send link pulses
+ * to avoid any lock-ups in case the peer PHY may also be running in EDPD mode.
+ *
+ * Some PHYs may support configuration of the wake-up interval for TX pulses,
+ * and some PHYs may support only disabling TX pulses entirely. For the latter
+ * a special value is required (ETHTOOL_PHY_EDPD_NO_TX) so that this can be
+ * configured from userspace (should the user want it).
+ *
+ * The interval units for TX wake-up are in milliseconds, since this should
+ * cover a reasonable range of intervals:
+ * - from 1 millisecond, which does not sound like much of a power-saver
+ * - to ~65 seconds which is quite a lot to wait for a link to come up when
+ * plugging a cable
+ */
+#define ETHTOOL_PHY_EDPD_DFLT_TX_MSECS 0xffff
+#define ETHTOOL_PHY_EDPD_NO_TX 0xfffe
+#define ETHTOOL_PHY_EDPD_DISABLE 0
+
enum phy_tunable_id {
ETHTOOL_PHY_ID_UNSPEC,
ETHTOOL_PHY_DOWNSHIFT,
ETHTOOL_PHY_FAST_LINK_DOWN,
+ ETHTOOL_PHY_EDPD,
/*
* Add your fresh new phy tunable attribute above and remember to update
* phy_tunable_strings[] in net/core/ethtool.c
@@ -1483,6 +1505,8 @@
ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT = 64,
ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT = 65,
ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66,
+ ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67,
+ ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68,
/* must be last entry */
__ETHTOOL_LINK_MODE_MASK_NBITS
diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h
index 2716476..29d6e93 100644
--- a/include/standard-headers/linux/pci_regs.h
+++ b/include/standard-headers/linux/pci_regs.h
@@ -528,6 +528,7 @@
#define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */
#define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */
#define PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */
+#define PCI_EXP_LNKCAP_SLS_32_0GB 0x00000005 /* LNKCAP2 SLS Vector bit 4 */
#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */
#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */
#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */
@@ -556,6 +557,7 @@
#define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */
#define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */
#define PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */
+#define PCI_EXP_LNKSTA_CLS_32_0GB 0x0005 /* Current Link Speed 32.0GT/s */
#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */
#define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */
#define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */
@@ -589,6 +591,7 @@
#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */
#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */
#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */
+#define PCI_EXP_SLTCTL_ATTN_IND_SHIFT 6 /* Attention Indicator shift */
#define PCI_EXP_SLTCTL_ATTN_IND_ON 0x0040 /* Attention Indicator on */
#define PCI_EXP_SLTCTL_ATTN_IND_BLINK 0x0080 /* Attention Indicator blinking */
#define PCI_EXP_SLTCTL_ATTN_IND_OFF 0x00c0 /* Attention Indicator off */
@@ -661,6 +664,7 @@
#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */
#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */
#define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */
+#define PCI_EXP_LNKCAP2_SLS_32_0GB 0x00000020 /* Supported Speed 32GT/s */
#define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */
#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
#define PCI_EXP_LNKCTL2_TLS 0x000f
@@ -668,6 +672,7 @@
#define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Supported Speed 5GT/s */
#define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */
#define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */
+#define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed 32GT/s */
#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */
#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */
@@ -709,7 +714,9 @@
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
#define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */
#define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */
-#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
+#define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */
+#define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */
+#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_16GT
#define PCI_EXT_CAP_DSN_SIZEOF 12
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
@@ -1049,4 +1056,14 @@
#define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */
#define PCI_L1SS_CTL2 0x0c /* Control 2 Register */
+/* Data Link Feature */
+#define PCI_DLF_CAP 0x04 /* Capabilities Register */
+#define PCI_DLF_EXCHANGE_ENABLE 0x80000000 /* Data Link Feature Exchange Enable */
+
+/* Physical Layer 16.0 GT/s */
+#define PCI_PL_16GT_LE_CTRL 0x20 /* Lane Equalization Control Register */
+#define PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK 0x0000000F
+#define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0
+#define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4
+
#endif /* LINUX_PCI_REGS_H */
diff --git a/include/standard-headers/linux/virtio_fs.h b/include/standard-headers/linux/virtio_fs.h
new file mode 100644
index 0000000..9d88817
--- /dev/null
+++ b/include/standard-headers/linux/virtio_fs.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+
+#ifndef _LINUX_VIRTIO_FS_H
+#define _LINUX_VIRTIO_FS_H
+
+#include "standard-headers/linux/types.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "standard-headers/linux/virtio_config.h"
+#include "standard-headers/linux/virtio_types.h"
+
+struct virtio_fs_config {
+ /* Filesystem name (UTF-8, not NUL-terminated, padded with NULs) */
+ uint8_t tag[36];
+
+ /* Number of request queues */
+ uint32_t num_request_queues;
+} QEMU_PACKED;
+
+#endif /* _LINUX_VIRTIO_FS_H */
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h
index 32b2f94..585e07b 100644
--- a/include/standard-headers/linux/virtio_ids.h
+++ b/include/standard-headers/linux/virtio_ids.h
@@ -43,6 +43,8 @@
#define VIRTIO_ID_INPUT 18 /* virtio input */
#define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
#define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
+#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
+#define VIRTIO_ID_FS 26 /* virtio filesystem */
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
#endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/standard-headers/linux/virtio_iommu.h b/include/standard-headers/linux/virtio_iommu.h
new file mode 100644
index 0000000..b9443b8
--- /dev/null
+++ b/include/standard-headers/linux/virtio_iommu.h
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Virtio-iommu definition v0.12
+ *
+ * Copyright (C) 2019 Arm Ltd.
+ */
+#ifndef _LINUX_VIRTIO_IOMMU_H
+#define _LINUX_VIRTIO_IOMMU_H
+
+#include "standard-headers/linux/types.h"
+
+/* Feature bits */
+#define VIRTIO_IOMMU_F_INPUT_RANGE 0
+#define VIRTIO_IOMMU_F_DOMAIN_RANGE 1
+#define VIRTIO_IOMMU_F_MAP_UNMAP 2
+#define VIRTIO_IOMMU_F_BYPASS 3
+#define VIRTIO_IOMMU_F_PROBE 4
+#define VIRTIO_IOMMU_F_MMIO 5
+
+struct virtio_iommu_range_64 {
+ uint64_t start;
+ uint64_t end;
+};
+
+struct virtio_iommu_range_32 {
+ uint32_t start;
+ uint32_t end;
+};
+
+struct virtio_iommu_config {
+ /* Supported page sizes */
+ uint64_t page_size_mask;
+ /* Supported IOVA range */
+ struct virtio_iommu_range_64 input_range;
+ /* Max domain ID size */
+ struct virtio_iommu_range_32 domain_range;
+ /* Probe buffer size */
+ uint32_t probe_size;
+};
+
+/* Request types */
+#define VIRTIO_IOMMU_T_ATTACH 0x01
+#define VIRTIO_IOMMU_T_DETACH 0x02
+#define VIRTIO_IOMMU_T_MAP 0x03
+#define VIRTIO_IOMMU_T_UNMAP 0x04
+#define VIRTIO_IOMMU_T_PROBE 0x05
+
+/* Status types */
+#define VIRTIO_IOMMU_S_OK 0x00
+#define VIRTIO_IOMMU_S_IOERR 0x01
+#define VIRTIO_IOMMU_S_UNSUPP 0x02
+#define VIRTIO_IOMMU_S_DEVERR 0x03
+#define VIRTIO_IOMMU_S_INVAL 0x04
+#define VIRTIO_IOMMU_S_RANGE 0x05
+#define VIRTIO_IOMMU_S_NOENT 0x06
+#define VIRTIO_IOMMU_S_FAULT 0x07
+#define VIRTIO_IOMMU_S_NOMEM 0x08
+
+struct virtio_iommu_req_head {
+ uint8_t type;
+ uint8_t reserved[3];
+};
+
+struct virtio_iommu_req_tail {
+ uint8_t status;
+ uint8_t reserved[3];
+};
+
+struct virtio_iommu_req_attach {
+ struct virtio_iommu_req_head head;
+ uint32_t domain;
+ uint32_t endpoint;
+ uint8_t reserved[8];
+ struct virtio_iommu_req_tail tail;
+};
+
+struct virtio_iommu_req_detach {
+ struct virtio_iommu_req_head head;
+ uint32_t domain;
+ uint32_t endpoint;
+ uint8_t reserved[8];
+ struct virtio_iommu_req_tail tail;
+};
+
+#define VIRTIO_IOMMU_MAP_F_READ (1 << 0)
+#define VIRTIO_IOMMU_MAP_F_WRITE (1 << 1)
+#define VIRTIO_IOMMU_MAP_F_MMIO (1 << 2)
+
+#define VIRTIO_IOMMU_MAP_F_MASK (VIRTIO_IOMMU_MAP_F_READ | \
+ VIRTIO_IOMMU_MAP_F_WRITE | \
+ VIRTIO_IOMMU_MAP_F_MMIO)
+
+struct virtio_iommu_req_map {
+ struct virtio_iommu_req_head head;
+ uint32_t domain;
+ uint64_t virt_start;
+ uint64_t virt_end;
+ uint64_t phys_start;
+ uint32_t flags;
+ struct virtio_iommu_req_tail tail;
+};
+
+struct virtio_iommu_req_unmap {
+ struct virtio_iommu_req_head head;
+ uint32_t domain;
+ uint64_t virt_start;
+ uint64_t virt_end;
+ uint8_t reserved[4];
+ struct virtio_iommu_req_tail tail;
+};
+
+#define VIRTIO_IOMMU_PROBE_T_NONE 0
+#define VIRTIO_IOMMU_PROBE_T_RESV_MEM 1
+
+#define VIRTIO_IOMMU_PROBE_T_MASK 0xfff
+
+struct virtio_iommu_probe_property {
+ uint16_t type;
+ uint16_t length;
+};
+
+#define VIRTIO_IOMMU_RESV_MEM_T_RESERVED 0
+#define VIRTIO_IOMMU_RESV_MEM_T_MSI 1
+
+struct virtio_iommu_probe_resv_mem {
+ struct virtio_iommu_probe_property head;
+ uint8_t subtype;
+ uint8_t reserved[3];
+ uint64_t start;
+ uint64_t end;
+};
+
+struct virtio_iommu_req_probe {
+ struct virtio_iommu_req_head head;
+ uint32_t endpoint;
+ uint8_t reserved[64];
+
+ uint8_t properties[];
+
+ /*
+ * Tail follows the variable-length properties array. No padding,
+ * property lengths are all aligned on 8 bytes.
+ */
+};
+
+/* Fault types */
+#define VIRTIO_IOMMU_FAULT_R_UNKNOWN 0
+#define VIRTIO_IOMMU_FAULT_R_DOMAIN 1
+#define VIRTIO_IOMMU_FAULT_R_MAPPING 2
+
+#define VIRTIO_IOMMU_FAULT_F_READ (1 << 0)
+#define VIRTIO_IOMMU_FAULT_F_WRITE (1 << 1)
+#define VIRTIO_IOMMU_FAULT_F_EXEC (1 << 2)
+#define VIRTIO_IOMMU_FAULT_F_ADDRESS (1 << 8)
+
+struct virtio_iommu_fault {
+ uint8_t reason;
+ uint8_t reserved[3];
+ uint32_t flags;
+ uint32_t endpoint;
+ uint8_t reserved2[4];
+ uint64_t address;
+};
+
+#endif
diff --git a/include/standard-headers/linux/virtio_pmem.h b/include/standard-headers/linux/virtio_pmem.h
index 7e3d43b..fc029de 100644
--- a/include/standard-headers/linux/virtio_pmem.h
+++ b/include/standard-headers/linux/virtio_pmem.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause */
/*
* Definitions for virtio-pmem devices.
*
@@ -7,8 +7,8 @@
* Author(s): Pankaj Gupta <pagupta@redhat.com>
*/
-#ifndef _UAPI_LINUX_VIRTIO_PMEM_H
-#define _UAPI_LINUX_VIRTIO_PMEM_H
+#ifndef _LINUX_VIRTIO_PMEM_H
+#define _LINUX_VIRTIO_PMEM_H
#include "standard-headers/linux/types.h"
#include "standard-headers/linux/virtio_ids.h"
diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h
index dfc7a31..8df5172 100644
--- a/include/sysemu/replay.h
+++ b/include/sysemu/replay.h
@@ -15,6 +15,7 @@
#include "qapi/qapi-types-misc.h"
#include "qapi/qapi-types-run-state.h"
#include "qapi/qapi-types-ui.h"
+#include "block/aio.h"
/* replay clock kinds */
enum ReplayClockKind {
@@ -140,6 +141,9 @@
bool replay_events_enabled(void);
/*! Adds bottom half event to the queue */
void replay_bh_schedule_event(QEMUBH *bh);
+/* Adds oneshot bottom half event to the queue */
+void replay_bh_schedule_oneshot_event(AioContext *ctx,
+ QEMUBHFunc *cb, void *opaque);
/*! Adds input event to the queue */
void replay_input_event(QemuConsole *src, InputEvent *evt);
/*! Adds input sync event to the queue */
diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h
index e1f8b74..9d379d3 100644
--- a/linux-headers/asm-arm/kvm.h
+++ b/linux-headers/asm-arm/kvm.h
@@ -214,6 +214,18 @@
#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \
KVM_REG_ARM_FW | ((r) & 0xffff))
#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1)
+ /* Higher values mean better protection. */
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL 1
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED 2
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 KVM_REG_ARM_FW_REG(2)
+ /* Higher values mean better protection. */
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL 0
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN 1
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL 2
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4)
/* Device Control API: ARM VGIC */
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
@@ -254,8 +266,10 @@
#define KVM_DEV_ARM_ITS_CTRL_RESET 4
/* KVM_IRQ_LINE irq field index values */
+#define KVM_ARM_IRQ_VCPU2_SHIFT 28
+#define KVM_ARM_IRQ_VCPU2_MASK 0xf
#define KVM_ARM_IRQ_TYPE_SHIFT 24
-#define KVM_ARM_IRQ_TYPE_MASK 0xff
+#define KVM_ARM_IRQ_TYPE_MASK 0xf
#define KVM_ARM_IRQ_VCPU_SHIFT 16
#define KVM_ARM_IRQ_VCPU_MASK 0xff
#define KVM_ARM_IRQ_NUM_SHIFT 0
diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h
index 27a9b6d..eb5d361 100644
--- a/linux-headers/asm-arm/unistd-common.h
+++ b/linux-headers/asm-arm/unistd-common.h
@@ -388,5 +388,7 @@
#define __NR_fsconfig (__NR_SYSCALL_BASE + 431)
#define __NR_fsmount (__NR_SYSCALL_BASE + 432)
#define __NR_fspick (__NR_SYSCALL_BASE + 433)
+#define __NR_pidfd_open (__NR_SYSCALL_BASE + 434)
+#define __NR_clone3 (__NR_SYSCALL_BASE + 435)
#endif /* _ASM_ARM_UNISTD_COMMON_H */
diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h
index 2431ec3..0ce6e49 100644
--- a/linux-headers/asm-arm64/kvm.h
+++ b/linux-headers/asm-arm64/kvm.h
@@ -229,6 +229,16 @@
#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
KVM_REG_ARM_FW | ((r) & 0xffff))
#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1)
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL 1
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED 2
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 KVM_REG_ARM_FW_REG(2)
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL 0
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN 1
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL 2
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4)
/* SVE registers */
#define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)
@@ -260,6 +270,13 @@
KVM_REG_SIZE_U256 | \
((i) & (KVM_ARM64_SVE_MAX_SLICES - 1)))
+/*
+ * Register values for KVM_REG_ARM64_SVE_ZREG(), KVM_REG_ARM64_SVE_PREG() and
+ * KVM_REG_ARM64_SVE_FFR() are represented in memory in an endianness-
+ * invariant layout which differs from the layout used for the FPSIMD
+ * V-registers on big-endian systems: see sigcontext.h for more explanation.
+ */
+
#define KVM_ARM64_SVE_VQ_MIN __SVE_VQ_MIN
#define KVM_ARM64_SVE_VQ_MAX __SVE_VQ_MAX
@@ -308,8 +325,10 @@
#define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1
/* KVM_IRQ_LINE irq field index values */
+#define KVM_ARM_IRQ_VCPU2_SHIFT 28
+#define KVM_ARM_IRQ_VCPU2_MASK 0xf
#define KVM_ARM_IRQ_TYPE_SHIFT 24
-#define KVM_ARM_IRQ_TYPE_MASK 0xff
+#define KVM_ARM_IRQ_TYPE_MASK 0xf
#define KVM_ARM_IRQ_VCPU_SHIFT 16
#define KVM_ARM_IRQ_VCPU_MASK 0xff
#define KVM_ARM_IRQ_NUM_SHIFT 0
diff --git a/linux-headers/asm-generic/mman-common.h b/linux-headers/asm-generic/mman-common.h
index abd238d..c160a53 100644
--- a/linux-headers/asm-generic/mman-common.h
+++ b/linux-headers/asm-generic/mman-common.h
@@ -19,15 +19,18 @@
#define MAP_TYPE 0x0f /* Mask for type of mapping */
#define MAP_FIXED 0x10 /* Interpret addr exactly */
#define MAP_ANONYMOUS 0x20 /* don't use a file */
-#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED
-# define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be uninitialized */
-#else
-# define MAP_UNINITIALIZED 0x0 /* Don't support this flag */
-#endif
-/* 0x0100 - 0x80000 flags are defined in asm-generic/mman.h */
+/* 0x0100 - 0x4000 flags are defined in asm-generic/mman.h */
+#define MAP_POPULATE 0x008000 /* populate (prefault) pagetables */
+#define MAP_NONBLOCK 0x010000 /* do not block on IO */
+#define MAP_STACK 0x020000 /* give out an address that is best suited for process/thread stacks */
+#define MAP_HUGETLB 0x040000 /* create a huge page mapping */
+#define MAP_SYNC 0x080000 /* perform synchronous page faults for the mapping */
#define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED which doesn't unmap underlying mapping */
+#define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be
+ * uninitialized */
+
/*
* Flags for mlock
*/
@@ -64,6 +67,9 @@
#define MADV_WIPEONFORK 18 /* Zero memory on fork, child only */
#define MADV_KEEPONFORK 19 /* Undo MADV_WIPEONFORK */
+#define MADV_COLD 20 /* deactivate these pages */
+#define MADV_PAGEOUT 21 /* reclaim these pages */
+
/* compatibility flags */
#define MAP_FILE 0
diff --git a/linux-headers/asm-generic/mman.h b/linux-headers/asm-generic/mman.h
index 653687d..57e8195 100644
--- a/linux-headers/asm-generic/mman.h
+++ b/linux-headers/asm-generic/mman.h
@@ -9,13 +9,11 @@
#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */
#define MAP_LOCKED 0x2000 /* pages are locked */
#define MAP_NORESERVE 0x4000 /* don't check for reservations */
-#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
-#define MAP_NONBLOCK 0x10000 /* do not block on IO */
-#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
-#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
-#define MAP_SYNC 0x80000 /* perform synchronous page faults for the mapping */
-/* Bits [26:31] are reserved, see mman-common.h for MAP_HUGETLB usage */
+/*
+ * Bits [26:31] are reserved, see asm-generic/hugetlb_encode.h
+ * for MAP_HUGETLB usage
+ */
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h
index a87904d..1fc8faa 100644
--- a/linux-headers/asm-generic/unistd.h
+++ b/linux-headers/asm-generic/unistd.h
@@ -569,7 +569,7 @@
__SC_COMP(__NR_semctl, sys_semctl, compat_sys_semctl)
#if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32
#define __NR_semtimedop 192
-__SC_COMP(__NR_semtimedop, sys_semtimedop, sys_semtimedop_time32)
+__SC_3264(__NR_semtimedop, sys_semtimedop_time32, sys_semtimedop)
#endif
#define __NR_semop 193
__SYSCALL(__NR_semop, sys_semop)
@@ -844,9 +844,15 @@
__SYSCALL(__NR_fsmount, sys_fsmount)
#define __NR_fspick 433
__SYSCALL(__NR_fspick, sys_fspick)
+#define __NR_pidfd_open 434
+__SYSCALL(__NR_pidfd_open, sys_pidfd_open)
+#ifdef __ARCH_WANT_SYS_CLONE3
+#define __NR_clone3 435
+__SYSCALL(__NR_clone3, sys_clone3)
+#endif
#undef __NR_syscalls
-#define __NR_syscalls 434
+#define __NR_syscalls 436
/*
* 32 bit systems traditionally used different
diff --git a/linux-headers/asm-mips/mman.h b/linux-headers/asm-mips/mman.h
index c2b4096..57dc2ac 100644
--- a/linux-headers/asm-mips/mman.h
+++ b/linux-headers/asm-mips/mman.h
@@ -95,6 +95,9 @@
#define MADV_WIPEONFORK 18 /* Zero memory on fork, child only */
#define MADV_KEEPONFORK 19 /* Undo MADV_WIPEONFORK */
+#define MADV_COLD 20 /* deactivate these pages */
+#define MADV_PAGEOUT 21 /* reclaim these pages */
+
/* compatibility flags */
#define MAP_FILE 0
diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h
index fb988de..7dffe8e 100644
--- a/linux-headers/asm-mips/unistd_n32.h
+++ b/linux-headers/asm-mips/unistd_n32.h
@@ -363,6 +363,7 @@
#define __NR_fsconfig (__NR_Linux + 431)
#define __NR_fsmount (__NR_Linux + 432)
#define __NR_fspick (__NR_Linux + 433)
+#define __NR_pidfd_open (__NR_Linux + 434)
#endif /* _ASM_MIPS_UNISTD_N32_H */
diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h
index 1735916..f4592d6 100644
--- a/linux-headers/asm-mips/unistd_n64.h
+++ b/linux-headers/asm-mips/unistd_n64.h
@@ -339,6 +339,7 @@
#define __NR_fsconfig (__NR_Linux + 431)
#define __NR_fsmount (__NR_Linux + 432)
#define __NR_fspick (__NR_Linux + 433)
+#define __NR_pidfd_open (__NR_Linux + 434)
#endif /* _ASM_MIPS_UNISTD_N64_H */
diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h
index 83c8d8f..04c6728 100644
--- a/linux-headers/asm-mips/unistd_o32.h
+++ b/linux-headers/asm-mips/unistd_o32.h
@@ -409,6 +409,7 @@
#define __NR_fsconfig (__NR_Linux + 431)
#define __NR_fsmount (__NR_Linux + 432)
#define __NR_fspick (__NR_Linux + 433)
+#define __NR_pidfd_open (__NR_Linux + 434)
#endif /* _ASM_MIPS_UNISTD_O32_H */
diff --git a/linux-headers/asm-powerpc/mman.h b/linux-headers/asm-powerpc/mman.h
index 1c2b3fc..8db7c2a 100644
--- a/linux-headers/asm-powerpc/mman.h
+++ b/linux-headers/asm-powerpc/mman.h
@@ -21,15 +21,11 @@
#define MAP_DENYWRITE 0x0800 /* ETXTBSY */
#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */
+
#define MCL_CURRENT 0x2000 /* lock all currently mapped pages */
#define MCL_FUTURE 0x4000 /* lock all additions to address space */
#define MCL_ONFAULT 0x8000 /* lock all pages that are faulted in */
-#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
-#define MAP_NONBLOCK 0x10000 /* do not block on IO */
-#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
-#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
-
/* Override any generic PKEY permission defines */
#define PKEY_DISABLE_EXECUTE 0x4
#undef PKEY_ACCESS_MASK
diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h
index 04cb2d3..5584cc1 100644
--- a/linux-headers/asm-powerpc/unistd_32.h
+++ b/linux-headers/asm-powerpc/unistd_32.h
@@ -416,6 +416,8 @@
#define __NR_fsconfig 431
#define __NR_fsmount 432
#define __NR_fspick 433
+#define __NR_pidfd_open 434
+#define __NR_clone3 435
#endif /* _ASM_POWERPC_UNISTD_32_H */
diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h
index b1e6921..251bcff 100644
--- a/linux-headers/asm-powerpc/unistd_64.h
+++ b/linux-headers/asm-powerpc/unistd_64.h
@@ -388,6 +388,8 @@
#define __NR_fsconfig 431
#define __NR_fsmount 432
#define __NR_fspick 433
+#define __NR_pidfd_open 434
+#define __NR_clone3 435
#endif /* _ASM_POWERPC_UNISTD_64_H */
diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h
index 03ab596..0138ccb 100644
--- a/linux-headers/asm-s390/kvm.h
+++ b/linux-headers/asm-s390/kvm.h
@@ -231,6 +231,12 @@
#define KVM_SYNC_GSCB (1UL << 9)
#define KVM_SYNC_BPBC (1UL << 10)
#define KVM_SYNC_ETOKEN (1UL << 11)
+
+#define KVM_SYNC_S390_VALID_FIELDS \
+ (KVM_SYNC_PREFIX | KVM_SYNC_GPRS | KVM_SYNC_ACRS | KVM_SYNC_CRS | \
+ KVM_SYNC_ARCH0 | KVM_SYNC_PFAULT | KVM_SYNC_VRS | KVM_SYNC_RICCB | \
+ KVM_SYNC_FPRS | KVM_SYNC_GSCB | KVM_SYNC_BPBC | KVM_SYNC_ETOKEN)
+
/* length and alignment of the sdnx as a power of two */
#define SDNXC 8
#define SDNXL (1UL << SDNXC)
diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h
index 941853f..7cce3ee 100644
--- a/linux-headers/asm-s390/unistd_32.h
+++ b/linux-headers/asm-s390/unistd_32.h
@@ -406,5 +406,7 @@
#define __NR_fsconfig 431
#define __NR_fsmount 432
#define __NR_fspick 433
+#define __NR_pidfd_open 434
+#define __NR_clone3 435
#endif /* _ASM_S390_UNISTD_32_H */
diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h
index 90271d7..2371ff1 100644
--- a/linux-headers/asm-s390/unistd_64.h
+++ b/linux-headers/asm-s390/unistd_64.h
@@ -354,5 +354,7 @@
#define __NR_fsconfig 431
#define __NR_fsmount 432
#define __NR_fspick 433
+#define __NR_pidfd_open 434
+#define __NR_clone3 435
#endif /* _ASM_S390_UNISTD_64_H */
diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h
index 6e7dd79..503d3f4 100644
--- a/linux-headers/asm-x86/kvm.h
+++ b/linux-headers/asm-x86/kvm.h
@@ -378,23 +378,24 @@
struct kvm_vcpu_events events;
};
-#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0)
-#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1)
-#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2)
-#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3)
+#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0)
+#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1)
+#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2)
+#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3)
+#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4)
#define KVM_STATE_NESTED_FORMAT_VMX 0
-#define KVM_STATE_NESTED_FORMAT_SVM 1
+#define KVM_STATE_NESTED_FORMAT_SVM 1 /* unused */
#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002
#define KVM_STATE_NESTED_EVMCS 0x00000004
-#define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000
-
#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_SMM_VMXON 0x00000002
+#define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000
+
struct kvm_vmx_nested_state_data {
__u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
__u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
@@ -432,4 +433,17 @@
} data;
};
+/* for KVM_CAP_PMU_EVENT_FILTER */
+struct kvm_pmu_event_filter {
+ __u32 action;
+ __u32 nevents;
+ __u32 fixed_counter_bitmap;
+ __u32 flags;
+ __u32 pad[4];
+ __u64 events[0];
+};
+
+#define KVM_PMU_EVENT_ALLOW 0
+#define KVM_PMU_EVENT_DENY 1
+
#endif /* _ASM_X86_KVM_H */
diff --git a/linux-headers/asm-x86/unistd.h b/linux-headers/asm-x86/unistd.h
index c04f638..498d151 100644
--- a/linux-headers/asm-x86/unistd.h
+++ b/linux-headers/asm-x86/unistd.h
@@ -3,7 +3,7 @@
#define _ASM_X86_UNISTD_H
/* x32 syscall flag bit */
-#define __X32_SYSCALL_BIT 0x40000000
+#define __X32_SYSCALL_BIT 0x40000000UL
# ifdef __i386__
# include <asm/unistd_32.h>
diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h
index 57bb488..e8ebec1 100644
--- a/linux-headers/asm-x86/unistd_32.h
+++ b/linux-headers/asm-x86/unistd_32.h
@@ -424,5 +424,7 @@
#define __NR_fsconfig 431
#define __NR_fsmount 432
#define __NR_fspick 433
+#define __NR_pidfd_open 434
+#define __NR_clone3 435
#endif /* _ASM_X86_UNISTD_32_H */
diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h
index fe6aa06..a2f863d 100644
--- a/linux-headers/asm-x86/unistd_64.h
+++ b/linux-headers/asm-x86/unistd_64.h
@@ -346,5 +346,7 @@
#define __NR_fsconfig 431
#define __NR_fsmount 432
#define __NR_fspick 433
+#define __NR_pidfd_open 434
+#define __NR_clone3 435
#endif /* _ASM_X86_UNISTD_64_H */
diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h
index 09cca49..4cdc67d 100644
--- a/linux-headers/asm-x86/unistd_x32.h
+++ b/linux-headers/asm-x86/unistd_x32.h
@@ -299,6 +299,8 @@
#define __NR_fsconfig (__X32_SYSCALL_BIT + 431)
#define __NR_fsmount (__X32_SYSCALL_BIT + 432)
#define __NR_fspick (__X32_SYSCALL_BIT + 433)
+#define __NR_pidfd_open (__X32_SYSCALL_BIT + 434)
+#define __NR_clone3 (__X32_SYSCALL_BIT + 435)
#define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512)
#define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513)
#define __NR_ioctl (__X32_SYSCALL_BIT + 514)
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 18892d6..3d9b18f 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -116,7 +116,7 @@
* ACPI gsi notion of irq.
* For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47..
* For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23..
- * For ARM: See Documentation/virtual/kvm/api.txt
+ * For ARM: See Documentation/virt/kvm/api.txt
*/
union {
__u32 irq;
@@ -243,6 +243,8 @@
#define KVM_INTERNAL_ERROR_SIMUL_EX 2
/* Encounter unexpected vm-exit due to delivery event. */
#define KVM_INTERNAL_ERROR_DELIVERY_EV 3
+/* Encounter unexpected vm-exit reason */
+#define KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON 4
/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */
struct kvm_run {
@@ -995,6 +997,9 @@
#define KVM_CAP_ARM_SVE 170
#define KVM_CAP_ARM_PTRAUTH_ADDRESS 171
#define KVM_CAP_ARM_PTRAUTH_GENERIC 172
+#define KVM_CAP_PMU_EVENT_FILTER 173
+#define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174
+#define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175
#ifdef KVM_CAP_IRQ_ROUTING
@@ -1085,7 +1090,7 @@
*
* KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies
* the irqfd to operate in resampling mode for level triggered interrupt
- * emulation. See Documentation/virtual/kvm/api.txt.
+ * emulation. See Documentation/virt/kvm/api.txt.
*/
#define KVM_IRQFD_FLAG_RESAMPLE (1 << 1)
@@ -1141,6 +1146,7 @@
#define KVM_REG_S390 0x5000000000000000ULL
#define KVM_REG_ARM64 0x6000000000000000ULL
#define KVM_REG_MIPS 0x7000000000000000ULL
+#define KVM_REG_RISCV 0x8000000000000000ULL
#define KVM_REG_SIZE_SHIFT 52
#define KVM_REG_SIZE_MASK 0x00f0000000000000ULL
@@ -1329,6 +1335,8 @@
#define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info)
/* Available with KVM_CAP_PPC_GET_CPU_CHAR */
#define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char)
+/* Available with KVM_CAP_PMU_EVENT_FILTER */
+#define KVM_SET_PMU_EVENT_FILTER _IOW(KVMIO, 0xb2, struct kvm_pmu_event_filter)
/* ioctl for vm fd */
#define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device)
diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h
index 36bbe17..34c3969 100644
--- a/linux-headers/linux/psp-sev.h
+++ b/linux-headers/linux/psp-sev.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Userspace interface for AMD Secure Encrypted Virtualization (SEV)
* platform management commands.
@@ -7,10 +8,6 @@
* Author: Brijesh Singh <brijesh.singh@amd.com>
*
* SEV API specification is available at: https://developer.amd.com/sev/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef __PSP_SEV_USER_H__
diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h
index 24f5051..fb10370 100644
--- a/linux-headers/linux/vfio.h
+++ b/linux-headers/linux/vfio.h
@@ -295,15 +295,38 @@
__u32 subtype; /* type specific */
};
+/*
+ * List of region types, global per bus driver.
+ * If you introduce a new type, please add it here.
+ */
+
+/* PCI region type containing a PCI vendor part */
#define VFIO_REGION_TYPE_PCI_VENDOR_TYPE (1 << 31)
#define VFIO_REGION_TYPE_PCI_VENDOR_MASK (0xffff)
+#define VFIO_REGION_TYPE_GFX (1)
+#define VFIO_REGION_TYPE_CCW (2)
-/* 8086 Vendor sub-types */
+/* sub-types for VFIO_REGION_TYPE_PCI_* */
+
+/* 8086 vendor PCI sub-types */
#define VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION (1)
#define VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG (2)
#define VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG (3)
-#define VFIO_REGION_TYPE_GFX (1)
+/* 10de vendor PCI sub-types */
+/*
+ * NVIDIA GPU NVlink2 RAM is coherent RAM mapped onto the host address space.
+ */
+#define VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM (1)
+
+/* 1014 vendor PCI sub-types */
+/*
+ * IBM NPU NVlink2 ATSD (Address Translation Shootdown) register of NPU
+ * to do TLB invalidation on a GPU.
+ */
+#define VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD (1)
+
+/* sub-types for VFIO_REGION_TYPE_GFX */
#define VFIO_REGION_SUBTYPE_GFX_EDID (1)
/**
@@ -353,26 +376,10 @@
#define VFIO_DEVICE_GFX_LINK_STATE_DOWN 2
};
-#define VFIO_REGION_TYPE_CCW (2)
-/* ccw sub-types */
+/* sub-types for VFIO_REGION_TYPE_CCW */
#define VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD (1)
/*
- * 10de vendor sub-type
- *
- * NVIDIA GPU NVlink2 RAM is coherent RAM mapped onto the host address space.
- */
-#define VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM (1)
-
-/*
- * 1014 vendor sub-type
- *
- * IBM NPU NVlink2 ATSD (Address Translation Shootdown) register of NPU
- * to do TLB invalidation on a GPU.
- */
-#define VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD (1)
-
-/*
* The MSIX mappable capability informs that MSIX data of a BAR can be mmapped
* which allows direct access to non-MSIX registers which happened to be within
* the same system page.
@@ -714,7 +721,31 @@
__u32 argsz;
__u32 flags;
#define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */
- __u64 iova_pgsizes; /* Bitmap of supported page sizes */
+#define VFIO_IOMMU_INFO_CAPS (1 << 1) /* Info supports caps */
+ __u64 iova_pgsizes; /* Bitmap of supported page sizes */
+ __u32 cap_offset; /* Offset within info struct of first cap */
+};
+
+/*
+ * The IOVA capability allows to report the valid IOVA range(s)
+ * excluding any non-relaxable reserved regions exposed by
+ * devices attached to the container. Any DMA map attempt
+ * outside the valid iova range will return error.
+ *
+ * The structures below define version 1 of this capability.
+ */
+#define VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE 1
+
+struct vfio_iova_range {
+ __u64 start;
+ __u64 end;
+};
+
+struct vfio_iommu_type1_info_cap_iova_range {
+ struct vfio_info_cap_header header;
+ __u32 nr_iovas;
+ __u32 reserved;
+ struct vfio_iova_range iova_ranges[];
};
#define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12)
diff --git a/memory.c b/memory.c
index 978da3d..c952eab 100644
--- a/memory.c
+++ b/memory.c
@@ -779,14 +779,13 @@
{
FlatView *view;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
do {
view = address_space_to_flatview(as);
/* If somebody has replaced as->current_map concurrently,
* flatview_ref returns false.
*/
} while (!flatview_ref(view));
- rcu_read_unlock();
return view;
}
@@ -2166,12 +2165,11 @@
{
int fd;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
while (mr->alias) {
mr = mr->alias;
}
fd = mr->ram_block->fd;
- rcu_read_unlock();
return fd;
}
@@ -2181,14 +2179,13 @@
void *ptr;
uint64_t offset = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
while (mr->alias) {
offset += mr->alias_offset;
mr = mr->alias;
}
assert(mr->ram_block);
ptr = qemu_map_ram_ptr(mr->ram_block, offset);
- rcu_read_unlock();
return ptr;
}
@@ -2578,12 +2575,11 @@
hwaddr addr, uint64_t size)
{
MemoryRegionSection ret;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
ret = memory_region_find_rcu(mr, addr, size);
if (ret.mr) {
memory_region_ref(ret.mr);
}
- rcu_read_unlock();
return ret;
}
@@ -2591,9 +2587,8 @@
{
MemoryRegion *mr;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
mr = memory_region_find_rcu(container, addr, 1).mr;
- rcu_read_unlock();
return mr && mr != container;
}
diff --git a/migration/migration.c b/migration/migration.c
index 5f7e4d1..3febd0f 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1533,8 +1533,7 @@
qemu_fclose(tmp);
}
- assert((s->state != MIGRATION_STATUS_ACTIVE) &&
- (s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE));
+ assert(!migration_is_active(s));
if (s->state == MIGRATION_STATUS_CANCELLING) {
migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING,
@@ -1703,6 +1702,12 @@
return false;
}
+bool migration_is_active(MigrationState *s)
+{
+ return (s->state == MIGRATION_STATUS_ACTIVE ||
+ s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
+}
+
void migrate_init(MigrationState *s)
{
/*
@@ -2481,7 +2486,7 @@
out:
res = qemu_file_get_error(rp);
if (res) {
- if (res == -EIO) {
+ if (res == -EIO && migration_in_postcopy()) {
/*
* Maybe there is something we can do: it looks like a
* network down issue, and we pause for a recovery.
@@ -3144,8 +3149,7 @@
return MIG_ITERATE_SKIP;
}
/* Just another iteration step */
- qemu_savevm_state_iterate(s->to_dst_file,
- s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ qemu_savevm_state_iterate(s->to_dst_file, in_postcopy);
} else {
trace_migration_thread_low_pending(pending_size);
migration_completion(s);
@@ -3266,8 +3270,7 @@
trace_migration_thread_setup_complete();
- while (s->state == MIGRATION_STATUS_ACTIVE ||
- s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
+ while (migration_is_active(s)) {
int64_t current_time;
if (urgent || !qemu_file_rate_limit(s->to_dst_file)) {
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index 1f63e65..abccafc 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -577,8 +577,6 @@
}
}
- postcopy_state_set(POSTCOPY_INCOMING_END);
-
if (mis->postcopy_tmp_page) {
munmap(mis->postcopy_tmp_page, mis->largest_page_size);
mis->postcopy_tmp_page = NULL;
@@ -768,9 +766,11 @@
atomic_xchg(&dc->page_fault_vcpu_time[cpu], low_time_offset);
atomic_xchg(&dc->vcpu_addr[cpu], addr);
- /* check it here, not at the begining of the function,
- * due to, check could accur early than bitmap_set in
- * qemu_ufd_copy_ioctl */
+ /*
+ * check it here, not at the beginning of the function,
+ * due to, check could occur early than bitmap_set in
+ * qemu_ufd_copy_ioctl
+ */
already_received = ramblock_recv_bitmap_test(rb, (void *)addr);
if (already_received) {
atomic_xchg(&dc->vcpu_addr[cpu], 0);
@@ -1094,7 +1094,7 @@
return NULL;
}
-int postcopy_ram_enable_notify(MigrationIncomingState *mis)
+int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
{
/* Open the fd for the kernel to give us userfaults */
mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
@@ -1134,6 +1134,32 @@
return -1;
}
+ mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE |
+ MAP_ANONYMOUS, -1, 0);
+ if (mis->postcopy_tmp_page == MAP_FAILED) {
+ mis->postcopy_tmp_page = NULL;
+ error_report("%s: Failed to map postcopy_tmp_page %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Map large zero page when kernel can't use UFFDIO_ZEROPAGE for hugepages
+ */
+ mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (mis->postcopy_tmp_zero_page == MAP_FAILED) {
+ int e = errno;
+ mis->postcopy_tmp_zero_page = NULL;
+ error_report("%s: Failed to map large zero page %s",
+ __func__, strerror(e));
+ return -e;
+ }
+ memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size);
+
/*
* Ballooning can mark pages as absent while we're postcopying
* that would cause false userfaults.
@@ -1240,50 +1266,10 @@
qemu_ram_block_host_offset(rb,
host));
} else {
- /* The kernel can't use UFFDIO_ZEROPAGE for hugepages */
- if (!mis->postcopy_tmp_zero_page) {
- mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1, 0);
- if (mis->postcopy_tmp_zero_page == MAP_FAILED) {
- int e = errno;
- mis->postcopy_tmp_zero_page = NULL;
- error_report("%s: %s mapping large zero page",
- __func__, strerror(e));
- return -e;
- }
- memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size);
- }
- return postcopy_place_page(mis, host, mis->postcopy_tmp_zero_page,
- rb);
+ return postcopy_place_page(mis, host, mis->postcopy_tmp_zero_page, rb);
}
}
-/*
- * Returns a target page of memory that can be mapped at a later point in time
- * using postcopy_place_page
- * The same address is used repeatedly, postcopy_place_page just takes the
- * backing page away.
- * Returns: Pointer to allocated page
- *
- */
-void *postcopy_get_tmp_page(MigrationIncomingState *mis)
-{
- if (!mis->postcopy_tmp_page) {
- mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size,
- PROT_READ | PROT_WRITE, MAP_PRIVATE |
- MAP_ANONYMOUS, -1, 0);
- if (mis->postcopy_tmp_page == MAP_FAILED) {
- mis->postcopy_tmp_page = NULL;
- error_report("%s: %s", __func__, strerror(errno));
- return NULL;
- }
- }
-
- return mis->postcopy_tmp_page;
-}
-
#else
/* No target OS support, stubs just fail */
void fill_destination_postcopy_migration_info(MigrationInfo *info)
@@ -1321,7 +1307,7 @@
return -1;
}
-int postcopy_ram_enable_notify(MigrationIncomingState *mis)
+int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
{
assert(0);
return -1;
@@ -1341,12 +1327,6 @@
return -1;
}
-void *postcopy_get_tmp_page(MigrationIncomingState *mis)
-{
- assert(0);
- return NULL;
-}
-
int postcopy_wake_shared(struct PostCopyFD *pcfd,
uint64_t client_addr,
RAMBlock *rb)
diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h
index 9c8bd2b..9941feb 100644
--- a/migration/postcopy-ram.h
+++ b/migration/postcopy-ram.h
@@ -20,7 +20,7 @@
* Make all of RAM sensitive to accesses to areas that haven't yet been written
* and wire up anything necessary to deal with it.
*/
-int postcopy_ram_enable_notify(MigrationIncomingState *mis);
+int postcopy_ram_incoming_setup(MigrationIncomingState *mis);
/*
* Initialise postcopy-ram, setting the RAM to a state where we can go into
@@ -100,13 +100,6 @@
POSTCOPY_INCOMING_END
} PostcopyState;
-/*
- * Allocate a page of memory that can be mapped at a later point in time
- * using postcopy_place_page
- * Returns: Pointer to allocated page
- */
-void *postcopy_get_tmp_page(MigrationIncomingState *mis);
-
PostcopyState postcopy_state_get(void);
/* Set the state and return the old state */
PostcopyState postcopy_state_set(PostcopyState new_state);
diff --git a/migration/ram.c b/migration/ram.c
index 22423f0..5078f94 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -181,14 +181,14 @@
RAMBlock *block;
int ret = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
+
RAMBLOCK_FOREACH_NOT_IGNORED(block) {
ret = func(block, opaque);
if (ret) {
break;
}
}
- rcu_read_unlock();
return ret;
}
@@ -791,13 +791,10 @@
static void multifd_send_fill_packet(MultiFDSendParams *p)
{
MultiFDPacket_t *packet = p->packet;
- uint32_t page_max = MULTIFD_PACKET_SIZE / qemu_target_page_size();
int i;
- packet->magic = cpu_to_be32(MULTIFD_MAGIC);
- packet->version = cpu_to_be32(MULTIFD_VERSION);
packet->flags = cpu_to_be32(p->flags);
- packet->pages_alloc = cpu_to_be32(page_max);
+ packet->pages_alloc = cpu_to_be32(p->pages->allocated);
packet->pages_used = cpu_to_be32(p->pages->used);
packet->next_packet_size = cpu_to_be32(p->next_packet_size);
packet->packet_num = cpu_to_be64(p->packet_num);
@@ -838,7 +835,7 @@
packet->pages_alloc = be32_to_cpu(packet->pages_alloc);
/*
- * If we recevied a packet that is 100 times bigger than expected
+ * If we received a packet that is 100 times bigger than expected
* just stop migration. It is a magic number.
*/
if (packet->pages_alloc > pages_max * 100) {
@@ -1132,7 +1129,6 @@
p->flags = 0;
p->num_packets++;
p->num_pages += used;
- p->pages->used = 0;
qemu_mutex_unlock(&p->mutex);
trace_multifd_send(p->id, packet_num, used, flags,
@@ -1241,6 +1237,8 @@
p->packet_len = sizeof(MultiFDPacket_t)
+ sizeof(ram_addr_t) * page_count;
p->packet = g_malloc0(p->packet_len);
+ p->packet->magic = cpu_to_be32(MULTIFD_MAGIC);
+ p->packet->version = cpu_to_be32(MULTIFD_VERSION);
p->name = g_strdup_printf("multifdsend_%d", i);
socket_send_channel_create(multifd_new_send_channel_async, p);
}
@@ -1848,12 +1846,12 @@
memory_global_dirty_log_sync();
qemu_mutex_lock(&rs->bitmap_mutex);
- rcu_read_lock();
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- ramblock_sync_dirty_bitmap(rs, block);
+ WITH_RCU_READ_LOCK_GUARD() {
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ ramblock_sync_dirty_bitmap(rs, block);
+ }
+ ram_counters.remaining = ram_bytes_remaining();
}
- ram_counters.remaining = ram_bytes_remaining();
- rcu_read_unlock();
qemu_mutex_unlock(&rs->bitmap_mutex);
memory_global_after_dirty_log_sync();
@@ -2397,13 +2395,12 @@
/* This queue generally should be empty - but in the case of a failed
* migration might have some droppings in.
*/
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
QSIMPLEQ_FOREACH_SAFE(mspr, &rs->src_page_requests, next_req, next_mspr) {
memory_region_unref(mspr->rb->mr);
QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req);
g_free(mspr);
}
- rcu_read_unlock();
}
/**
@@ -2424,7 +2421,8 @@
RAMState *rs = ram_state;
ram_counters.postcopy_requests++;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
+
if (!rbname) {
/* Reuse last RAMBlock */
ramblock = rs->last_req_rb;
@@ -2466,12 +2464,10 @@
QSIMPLEQ_INSERT_TAIL(&rs->src_page_requests, new_entry, next_req);
migration_make_urgent_request();
qemu_mutex_unlock(&rs->src_page_req_mutex);
- rcu_read_unlock();
return 0;
err:
- rcu_read_unlock();
return -1;
}
@@ -2700,7 +2696,8 @@
RAMBlock *block;
uint64_t total = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
+
if (count_ignored) {
RAMBLOCK_FOREACH_MIGRATABLE(block) {
total += block->used_length;
@@ -2710,7 +2707,6 @@
total += block->used_length;
}
}
- rcu_read_unlock();
return total;
}
@@ -3034,7 +3030,7 @@
RAMBlock *block;
int ret;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
/* This should be our last sync, the src is now paused */
migration_bitmap_sync(rs);
@@ -3048,7 +3044,6 @@
/* Deal with TPS != HPS and huge pages */
ret = postcopy_chunk_hostpages(ms, block);
if (ret) {
- rcu_read_unlock();
return ret;
}
@@ -3060,7 +3055,6 @@
trace_ram_postcopy_send_discard_bitmap();
ret = postcopy_each_ram_send_discard(ms);
- rcu_read_unlock();
return ret;
}
@@ -3081,7 +3075,7 @@
trace_ram_discard_range(rbname, start, length);
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
RAMBlock *rb = qemu_ram_block_by_name(rbname);
if (!rb) {
@@ -3101,8 +3095,6 @@
ret = ram_block_discard_range(rb, start, length);
err:
- rcu_read_unlock();
-
return ret;
}
@@ -3231,13 +3223,12 @@
/* For memory_global_dirty_log_start below. */
qemu_mutex_lock_iothread();
qemu_mutex_lock_ramlist();
- rcu_read_lock();
- ram_list_init_bitmaps();
- memory_global_dirty_log_start();
- migration_bitmap_sync_precopy(rs);
-
- rcu_read_unlock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ ram_list_init_bitmaps();
+ memory_global_dirty_log_start();
+ migration_bitmap_sync_precopy(rs);
+ }
qemu_mutex_unlock_ramlist();
qemu_mutex_unlock_iothread();
}
@@ -3373,24 +3364,23 @@
}
(*rsp)->f = f;
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ qemu_put_be64(f, ram_bytes_total_common(true) | RAM_SAVE_FLAG_MEM_SIZE);
- qemu_put_be64(f, ram_bytes_total_common(true) | RAM_SAVE_FLAG_MEM_SIZE);
-
- RAMBLOCK_FOREACH_MIGRATABLE(block) {
- qemu_put_byte(f, strlen(block->idstr));
- qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
- qemu_put_be64(f, block->used_length);
- if (migrate_postcopy_ram() && block->page_size != qemu_host_page_size) {
- qemu_put_be64(f, block->page_size);
- }
- if (migrate_ignore_shared()) {
- qemu_put_be64(f, block->mr->addr);
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
+ qemu_put_byte(f, strlen(block->idstr));
+ qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
+ qemu_put_be64(f, block->used_length);
+ if (migrate_postcopy_ram() && block->page_size !=
+ qemu_host_page_size) {
+ qemu_put_be64(f, block->page_size);
+ }
+ if (migrate_ignore_shared()) {
+ qemu_put_be64(f, block->mr->addr);
+ }
}
}
- rcu_read_unlock();
-
ram_control_before_iterate(f, RAM_CONTROL_SETUP);
ram_control_after_iterate(f, RAM_CONTROL_SETUP);
@@ -3425,55 +3415,57 @@
goto out;
}
- rcu_read_lock();
- if (ram_list.version != rs->last_version) {
- ram_state_reset(rs);
- }
-
- /* Read version before ram_list.blocks */
- smp_rmb();
-
- ram_control_before_iterate(f, RAM_CONTROL_ROUND);
-
- t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
- i = 0;
- while ((ret = qemu_file_rate_limit(f)) == 0 ||
- !QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
- int pages;
-
- if (qemu_file_get_error(f)) {
- break;
+ WITH_RCU_READ_LOCK_GUARD() {
+ if (ram_list.version != rs->last_version) {
+ ram_state_reset(rs);
}
- pages = ram_find_and_save_block(rs, false);
- /* no more pages to sent */
- if (pages == 0) {
- done = 1;
- break;
- }
+ /* Read version before ram_list.blocks */
+ smp_rmb();
- if (pages < 0) {
- qemu_file_set_error(f, pages);
- break;
- }
+ ram_control_before_iterate(f, RAM_CONTROL_ROUND);
- rs->target_page_count += pages;
+ t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ i = 0;
+ while ((ret = qemu_file_rate_limit(f)) == 0 ||
+ !QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
+ int pages;
- /* we want to check in the 1st loop, just in case it was the 1st time
- and we had to sync the dirty bitmap.
- qemu_clock_get_ns() is a bit expensive, so we only check each some
- iterations
- */
- if ((i & 63) == 0) {
- uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / 1000000;
- if (t1 > MAX_WAIT) {
- trace_ram_save_iterate_big_wait(t1, i);
+ if (qemu_file_get_error(f)) {
break;
}
+
+ pages = ram_find_and_save_block(rs, false);
+ /* no more pages to sent */
+ if (pages == 0) {
+ done = 1;
+ break;
+ }
+
+ if (pages < 0) {
+ qemu_file_set_error(f, pages);
+ break;
+ }
+
+ rs->target_page_count += pages;
+
+ /*
+ * we want to check in the 1st loop, just in case it was the 1st
+ * time and we had to sync the dirty bitmap.
+ * qemu_clock_get_ns() is a bit expensive, so we only check each
+ * some iterations
+ */
+ if ((i & 63) == 0) {
+ uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) /
+ 1000000;
+ if (t1 > MAX_WAIT) {
+ trace_ram_save_iterate_big_wait(t1, i);
+ break;
+ }
+ }
+ i++;
}
- i++;
}
- rcu_read_unlock();
/*
* Must occur before EOS (or any QEMUFile operation)
@@ -3511,36 +3503,34 @@
RAMState *rs = *temp;
int ret = 0;
- rcu_read_lock();
-
- if (!migration_in_postcopy()) {
- migration_bitmap_sync_precopy(rs);
- }
-
- ram_control_before_iterate(f, RAM_CONTROL_FINISH);
-
- /* try transferring iterative blocks of memory */
-
- /* flush all remaining blocks regardless of rate limiting */
- while (true) {
- int pages;
-
- pages = ram_find_and_save_block(rs, !migration_in_colo_state());
- /* no more blocks to sent */
- if (pages == 0) {
- break;
+ WITH_RCU_READ_LOCK_GUARD() {
+ if (!migration_in_postcopy()) {
+ migration_bitmap_sync_precopy(rs);
}
- if (pages < 0) {
- ret = pages;
- break;
+
+ ram_control_before_iterate(f, RAM_CONTROL_FINISH);
+
+ /* try transferring iterative blocks of memory */
+
+ /* flush all remaining blocks regardless of rate limiting */
+ while (true) {
+ int pages;
+
+ pages = ram_find_and_save_block(rs, !migration_in_colo_state());
+ /* no more blocks to sent */
+ if (pages == 0) {
+ break;
+ }
+ if (pages < 0) {
+ ret = pages;
+ break;
+ }
}
+
+ flush_compressed_data(rs);
+ ram_control_after_iterate(f, RAM_CONTROL_FINISH);
}
- flush_compressed_data(rs);
- ram_control_after_iterate(f, RAM_CONTROL_FINISH);
-
- rcu_read_unlock();
-
multifd_send_sync_main(rs);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f);
@@ -3562,9 +3552,9 @@
if (!migration_in_postcopy() &&
remaining_size < max_size) {
qemu_mutex_lock_iothread();
- rcu_read_lock();
- migration_bitmap_sync_precopy(rs);
- rcu_read_unlock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ migration_bitmap_sync_precopy(rs);
+ }
qemu_mutex_unlock_iothread();
remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE;
}
@@ -3908,7 +3898,13 @@
error_report("%s: Can't alloc memory for COLO cache of block %s,"
"size 0x" RAM_ADDR_FMT, __func__, block->idstr,
block->used_length);
- goto out_locked;
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ if (block->colo_cache) {
+ qemu_anon_ram_free(block->colo_cache, block->used_length);
+ block->colo_cache = NULL;
+ }
+ }
+ return -errno;
}
memcpy(block->colo_cache, block->host, block->used_length);
}
@@ -3934,18 +3930,6 @@
memory_global_dirty_log_start();
return 0;
-
-out_locked:
-
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- if (block->colo_cache) {
- qemu_anon_ram_free(block->colo_cache, block->used_length);
- block->colo_cache = NULL;
- }
- }
-
- rcu_read_unlock();
- return -errno;
}
/* It is need to hold the global lock to call this helper */
@@ -3959,16 +3943,14 @@
block->bmap = NULL;
}
- rcu_read_lock();
-
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- if (block->colo_cache) {
- qemu_anon_ram_free(block->colo_cache, block->used_length);
- block->colo_cache = NULL;
+ WITH_RCU_READ_LOCK_GUARD() {
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ if (block->colo_cache) {
+ qemu_anon_ram_free(block->colo_cache, block->used_length);
+ block->colo_cache = NULL;
+ }
}
}
-
- rcu_read_unlock();
qemu_mutex_destroy(&ram_state->bitmap_mutex);
g_free(ram_state);
ram_state = NULL;
@@ -4048,7 +4030,7 @@
bool matches_target_page_size = false;
MigrationIncomingState *mis = migration_incoming_get_current();
/* Temporary page that is later 'placed' */
- void *postcopy_host_page = postcopy_get_tmp_page(mis);
+ void *postcopy_host_page = mis->postcopy_tmp_page;
void *last_host = NULL;
bool all_zero = false;
@@ -4206,31 +4188,30 @@
unsigned long offset = 0;
memory_global_dirty_log_sync();
- rcu_read_lock();
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- ramblock_sync_dirty_bitmap(ram_state, block);
- }
- rcu_read_unlock();
-
- trace_colo_flush_ram_cache_begin(ram_state->migration_dirty_pages);
- rcu_read_lock();
- block = QLIST_FIRST_RCU(&ram_list.blocks);
-
- while (block) {
- offset = migration_bitmap_find_dirty(ram_state, block, offset);
-
- if (offset << TARGET_PAGE_BITS >= block->used_length) {
- offset = 0;
- block = QLIST_NEXT_RCU(block, next);
- } else {
- migration_bitmap_clear_dirty(ram_state, block, offset);
- dst_host = block->host + (offset << TARGET_PAGE_BITS);
- src_host = block->colo_cache + (offset << TARGET_PAGE_BITS);
- memcpy(dst_host, src_host, TARGET_PAGE_SIZE);
+ WITH_RCU_READ_LOCK_GUARD() {
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ ramblock_sync_dirty_bitmap(ram_state, block);
}
}
- rcu_read_unlock();
+ trace_colo_flush_ram_cache_begin(ram_state->migration_dirty_pages);
+ WITH_RCU_READ_LOCK_GUARD() {
+ block = QLIST_FIRST_RCU(&ram_list.blocks);
+
+ while (block) {
+ offset = migration_bitmap_find_dirty(ram_state, block, offset);
+
+ if (offset << TARGET_PAGE_BITS >= block->used_length) {
+ offset = 0;
+ block = QLIST_NEXT_RCU(block, next);
+ } else {
+ migration_bitmap_clear_dirty(ram_state, block, offset);
+ dst_host = block->host + (offset << TARGET_PAGE_BITS);
+ src_host = block->colo_cache + (offset << TARGET_PAGE_BITS);
+ memcpy(dst_host, src_host, TARGET_PAGE_SIZE);
+ }
+ }
+ }
trace_colo_flush_ram_cache_end();
}
@@ -4429,16 +4410,15 @@
* it will be necessary to reduce the granularity of this
* critical section.
*/
- rcu_read_lock();
+ WITH_RCU_READ_LOCK_GUARD() {
+ if (postcopy_running) {
+ ret = ram_load_postcopy(f);
+ } else {
+ ret = ram_load_precopy(f);
+ }
- if (postcopy_running) {
- ret = ram_load_postcopy(f);
- } else {
- ret = ram_load_precopy(f);
+ ret |= wait_for_decompress_done();
}
-
- ret |= wait_for_decompress_done();
- rcu_read_unlock();
trace_ram_load_complete(ret, seq_iter);
if (!ret && migration_incoming_in_colo_state()) {
diff --git a/migration/rdma.c b/migration/rdma.c
index 4c74e88..e241dcb 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -88,7 +88,6 @@
" to abort!"); \
rdma->error_reported = 1; \
} \
- rcu_read_unlock(); \
return rdma->error_state; \
} \
} while (0)
@@ -2678,11 +2677,10 @@
size_t i;
size_t len = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmaout);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
@@ -2695,7 +2693,6 @@
ret = qemu_rdma_write_flush(f, rdma);
if (ret < 0) {
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
@@ -2715,7 +2712,6 @@
if (ret < 0) {
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
@@ -2724,7 +2720,6 @@
}
}
- rcu_read_unlock();
return done;
}
@@ -2764,11 +2759,10 @@
ssize_t i;
size_t done = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmain);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
@@ -2805,7 +2799,6 @@
if (ret < 0) {
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
@@ -2819,14 +2812,12 @@
/* Still didn't get enough, so lets just return */
if (want) {
if (done == 0) {
- rcu_read_unlock();
return QIO_CHANNEL_ERR_BLOCK;
} else {
break;
}
}
}
- rcu_read_unlock();
return done;
}
@@ -2882,7 +2873,7 @@
GIOCondition cond = 0;
*timeout = -1;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
if (rsource->condition == G_IO_IN) {
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
} else {
@@ -2891,7 +2882,6 @@
if (!rdma) {
error_report("RDMAContext is NULL when prepare Gsource");
- rcu_read_unlock();
return FALSE;
}
@@ -2900,7 +2890,6 @@
}
cond |= G_IO_OUT;
- rcu_read_unlock();
return cond & rsource->condition;
}
@@ -2911,7 +2900,7 @@
RDMAContext *rdma;
GIOCondition cond = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
if (rsource->condition == G_IO_IN) {
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
} else {
@@ -2920,7 +2909,6 @@
if (!rdma) {
error_report("RDMAContext is NULL when check Gsource");
- rcu_read_unlock();
return FALSE;
}
@@ -2929,7 +2917,6 @@
}
cond |= G_IO_OUT;
- rcu_read_unlock();
return cond & rsource->condition;
}
@@ -2943,7 +2930,7 @@
RDMAContext *rdma;
GIOCondition cond = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
if (rsource->condition == G_IO_IN) {
rdma = atomic_rcu_read(&rsource->rioc->rdmain);
} else {
@@ -2952,7 +2939,6 @@
if (!rdma) {
error_report("RDMAContext is NULL when dispatch Gsource");
- rcu_read_unlock();
return FALSE;
}
@@ -2961,7 +2947,6 @@
}
cond |= G_IO_OUT;
- rcu_read_unlock();
return (*func)(QIO_CHANNEL(rsource->rioc),
(cond & rsource->condition),
user_data);
@@ -3073,7 +3058,7 @@
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
RDMAContext *rdmain, *rdmaout;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdmain = atomic_rcu_read(&rioc->rdmain);
rdmaout = atomic_rcu_read(&rioc->rdmain);
@@ -3100,7 +3085,6 @@
break;
}
- rcu_read_unlock();
return 0;
}
@@ -3146,18 +3130,16 @@
RDMAContext *rdma;
int ret;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmaout);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
CHECK_ERROR_STATE();
if (migration_in_postcopy()) {
- rcu_read_unlock();
return RAM_SAVE_CONTROL_NOT_SUPP;
}
@@ -3242,11 +3224,9 @@
}
}
- rcu_read_unlock();
return RAM_SAVE_CONTROL_DELAYED;
err:
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
@@ -3470,11 +3450,10 @@
int count = 0;
int i = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmain);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
@@ -3717,7 +3696,6 @@
if (ret < 0) {
rdma->error_state = ret;
}
- rcu_read_unlock();
return ret;
}
@@ -3735,11 +3713,10 @@
int curr;
int found = -1;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmain);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
@@ -3753,7 +3730,6 @@
if (found == -1) {
error_report("RAMBlock '%s' not found on destination", name);
- rcu_read_unlock();
return -ENOENT;
}
@@ -3761,7 +3737,6 @@
trace_rdma_block_notification_handle(name, rdma->next_src_index);
rdma->next_src_index++;
- rcu_read_unlock();
return 0;
}
@@ -3786,17 +3761,15 @@
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
RDMAContext *rdma;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmaout);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
CHECK_ERROR_STATE();
if (migration_in_postcopy()) {
- rcu_read_unlock();
return 0;
}
@@ -3804,7 +3777,6 @@
qemu_put_be64(f, RAM_SAVE_FLAG_HOOK);
qemu_fflush(f);
- rcu_read_unlock();
return 0;
}
@@ -3821,17 +3793,15 @@
RDMAControlHeader head = { .len = 0, .repeat = 1 };
int ret = 0;
- rcu_read_lock();
+ RCU_READ_LOCK_GUARD();
rdma = atomic_rcu_read(&rioc->rdmaout);
if (!rdma) {
- rcu_read_unlock();
return -EIO;
}
CHECK_ERROR_STATE();
if (migration_in_postcopy()) {
- rcu_read_unlock();
return 0;
}
@@ -3863,7 +3833,6 @@
qemu_rdma_reg_whole_ram_blocks : NULL);
if (ret < 0) {
ERROR(errp, "receiving remote info!");
- rcu_read_unlock();
return ret;
}
@@ -3887,7 +3856,6 @@
"not identical on both the source and destination.",
local->nb_blocks, nb_dest_blocks);
rdma->error_state = -EINVAL;
- rcu_read_unlock();
return -EINVAL;
}
@@ -3904,7 +3872,6 @@
local->block[i].length,
rdma->dest_blocks[i].length);
rdma->error_state = -EINVAL;
- rcu_read_unlock();
return -EINVAL;
}
local->block[i].remote_host_addr =
@@ -3922,11 +3889,9 @@
goto err;
}
- rcu_read_unlock();
return 0;
err:
rdma->error_state = ret;
- rcu_read_unlock();
return ret;
}
diff --git a/migration/savevm.c b/migration/savevm.c
index bb9462a..8d95e26 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1215,6 +1215,8 @@
save_section_footer(f, se);
if (ret < 0) {
+ error_report("failed to save SaveStateEntry with id(name): %d(%s)",
+ se->section_id, se->idstr);
qemu_file_set_error(f, ret);
}
if (ret <= 0) {
@@ -1835,6 +1837,8 @@
rcu_unregister_thread();
mis->have_listen_thread = false;
+ postcopy_state_set(POSTCOPY_INCOMING_END);
+
return NULL;
}
@@ -1865,7 +1869,7 @@
* shouldn't be doing anything yet so don't actually expect requests
*/
if (migrate_postcopy_ram()) {
- if (postcopy_ram_enable_notify(mis)) {
+ if (postcopy_ram_incoming_setup(mis)) {
postcopy_ram_incoming_cleanup(mis);
return -1;
}
@@ -1876,11 +1880,6 @@
return -1;
}
- if (mis->have_listen_thread) {
- error_report("CMD_POSTCOPY_RAM_LISTEN already has a listen thread");
- return -1;
- }
-
mis->have_listen_thread = true;
/* Start up the listening thread and wait for it to signal ready */
qemu_sem_init(&mis->listen_thread_sem, 0);
@@ -1934,7 +1933,7 @@
/* After all discards we can start running and asking for pages */
static int loadvm_postcopy_handle_run(MigrationIncomingState *mis)
{
- PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_RUNNING);
+ PostcopyState ps = postcopy_state_get();
trace_loadvm_postcopy_handle_run();
if (ps != POSTCOPY_INCOMING_LISTENING) {
@@ -1942,6 +1941,7 @@
return -1;
}
+ postcopy_state_set(POSTCOPY_INCOMING_RUNNING);
mis->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, mis);
qemu_bh_schedule(mis->bh);
diff --git a/migration/trace-events b/migration/trace-events
index 858d415..6dee7b5 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -71,6 +71,11 @@
put_qtailq(const char *name, int version_id) "%s v%d"
put_qtailq_end(const char *name, const char *reason) "%s %s"
+get_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d"
+get_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d"
+put_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d"
+put_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d"
+
# qemu-file.c
qemu_file_fclose(void) ""
diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
index bee658a..7236cf9 100644
--- a/migration/vmstate-types.c
+++ b/migration/vmstate-types.c
@@ -691,3 +691,155 @@
.get = get_qtailq,
.put = put_qtailq,
};
+
+struct put_gtree_data {
+ QEMUFile *f;
+ const VMStateDescription *key_vmsd;
+ const VMStateDescription *val_vmsd;
+ QJSON *vmdesc;
+ int ret;
+};
+
+static gboolean put_gtree_elem(gpointer key, gpointer value, gpointer data)
+{
+ struct put_gtree_data *capsule = (struct put_gtree_data *)data;
+ QEMUFile *f = capsule->f;
+ int ret;
+
+ qemu_put_byte(f, true);
+
+ /* put the key */
+ if (!capsule->key_vmsd) {
+ qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */
+ } else {
+ ret = vmstate_save_state(f, capsule->key_vmsd, key, capsule->vmdesc);
+ if (ret) {
+ capsule->ret = ret;
+ return true;
+ }
+ }
+
+ /* put the data */
+ ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc);
+ if (ret) {
+ capsule->ret = ret;
+ return true;
+ }
+ return false;
+}
+
+static int put_gtree(QEMUFile *f, void *pv, size_t unused_size,
+ const VMStateField *field, QJSON *vmdesc)
+{
+ bool direct_key = (!field->start);
+ const VMStateDescription *key_vmsd = direct_key ? NULL : &field->vmsd[1];
+ const VMStateDescription *val_vmsd = &field->vmsd[0];
+ const char *key_vmsd_name = direct_key ? "direct" : key_vmsd->name;
+ struct put_gtree_data capsule = {
+ .f = f,
+ .key_vmsd = key_vmsd,
+ .val_vmsd = val_vmsd,
+ .vmdesc = vmdesc,
+ .ret = 0};
+ GTree **pval = pv;
+ GTree *tree = *pval;
+ uint32_t nnodes = g_tree_nnodes(tree);
+ int ret;
+
+ trace_put_gtree(field->name, key_vmsd_name, val_vmsd->name, nnodes);
+ qemu_put_be32(f, nnodes);
+ g_tree_foreach(tree, put_gtree_elem, (gpointer)&capsule);
+ qemu_put_byte(f, false);
+ ret = capsule.ret;
+ if (ret) {
+ error_report("%s : failed to save gtree (%d)", field->name, ret);
+ }
+ trace_put_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret);
+ return ret;
+}
+
+static int get_gtree(QEMUFile *f, void *pv, size_t unused_size,
+ const VMStateField *field)
+{
+ bool direct_key = (!field->start);
+ const VMStateDescription *key_vmsd = direct_key ? NULL : &field->vmsd[1];
+ const VMStateDescription *val_vmsd = &field->vmsd[0];
+ const char *key_vmsd_name = direct_key ? "direct" : key_vmsd->name;
+ int version_id = field->version_id;
+ size_t key_size = field->start;
+ size_t val_size = field->size;
+ int nnodes, count = 0;
+ GTree **pval = pv;
+ GTree *tree = *pval;
+ void *key, *val;
+ int ret = 0;
+
+ /* in case of direct key, the key vmsd can be {}, ie. check fields */
+ if (!direct_key && version_id > key_vmsd->version_id) {
+ error_report("%s %s", key_vmsd->name, "too new");
+ return -EINVAL;
+ }
+ if (!direct_key && version_id < key_vmsd->minimum_version_id) {
+ error_report("%s %s", key_vmsd->name, "too old");
+ return -EINVAL;
+ }
+ if (version_id > val_vmsd->version_id) {
+ error_report("%s %s", val_vmsd->name, "too new");
+ return -EINVAL;
+ }
+ if (version_id < val_vmsd->minimum_version_id) {
+ error_report("%s %s", val_vmsd->name, "too old");
+ return -EINVAL;
+ }
+
+ nnodes = qemu_get_be32(f);
+ trace_get_gtree(field->name, key_vmsd_name, val_vmsd->name, nnodes);
+
+ while (qemu_get_byte(f)) {
+ if ((++count) > nnodes) {
+ ret = -EINVAL;
+ break;
+ }
+ if (direct_key) {
+ key = (void *)(uintptr_t)qemu_get_be64(f);
+ } else {
+ key = g_malloc0(key_size);
+ ret = vmstate_load_state(f, key_vmsd, key, version_id);
+ if (ret) {
+ error_report("%s : failed to load %s (%d)",
+ field->name, key_vmsd->name, ret);
+ goto key_error;
+ }
+ }
+ val = g_malloc0(val_size);
+ ret = vmstate_load_state(f, val_vmsd, val, version_id);
+ if (ret) {
+ error_report("%s : failed to load %s (%d)",
+ field->name, val_vmsd->name, ret);
+ goto val_error;
+ }
+ g_tree_insert(tree, key, val);
+ }
+ if (count != nnodes) {
+ error_report("%s inconsistent stream when loading the gtree",
+ field->name);
+ return -EINVAL;
+ }
+ trace_get_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret);
+ return ret;
+val_error:
+ g_free(val);
+key_error:
+ if (!direct_key) {
+ g_free(key);
+ }
+ trace_get_gtree_end(field->name, key_vmsd_name, val_vmsd->name, ret);
+ return ret;
+}
+
+
+const VMStateInfo vmstate_info_gtree = {
+ .name = "gtree",
+ .get = get_gtree,
+ .put = put_gtree,
+};
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 51921de..014199d 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -235,6 +235,10 @@
s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
+ if (s->vhost_net) {
+ s->acked_features = vhost_net_get_acked_features(s->vhost_net);
+ }
+
qmp_set_link(name, false, &err);
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc
index aaeb2c6..28f6d4b 100644
--- a/pc-bios/openbios-ppc
+++ b/pc-bios/openbios-ppc
Binary files differ
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index b471b87..964893f 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index 8c77a06..0e19138 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/qapi/block-core.json b/qapi/block-core.json
index e6edd64..f66553a 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -860,6 +860,8 @@
#
# @wr_bytes: The number of bytes written by the device.
#
+# @unmap_bytes: The number of bytes unmapped by the device (Since 4.2)
+#
# @rd_operations: The number of read operations performed by the device.
#
# @wr_operations: The number of write operations performed by the device.
@@ -867,12 +869,18 @@
# @flush_operations: The number of cache flush operations performed by the
# device (since 0.15.0)
#
-# @flush_total_time_ns: Total time spend on cache flushes in nano-seconds
+# @unmap_operations: The number of unmap operations performed by the device
+# (Since 4.2)
+#
+# @rd_total_time_ns: Total time spent on reads in nanoseconds (since 0.15.0).
+#
+# @wr_total_time_ns: Total time spent on writes in nanoseconds (since 0.15.0).
+#
+# @flush_total_time_ns: Total time spent on cache flushes in nanoseconds
# (since 0.15.0).
#
-# @wr_total_time_ns: Total time spend on writes in nano-seconds (since 0.15.0).
-#
-# @rd_total_time_ns: Total_time_spend on reads in nano-seconds (since 0.15.0).
+# @unmap_total_time_ns: Total time spent on unmap operations in nanoseconds
+# (Since 4.2)
#
# @wr_highest_offset: The offset after the greatest byte written to the
# device. The intended use of this information is for
@@ -885,6 +893,9 @@
# @wr_merged: Number of write requests that have been merged into another
# request (Since 2.3).
#
+# @unmap_merged: Number of unmap requests that have been merged into another
+# request (Since 4.2)
+#
# @idle_time_ns: Time since the last I/O operation, in
# nanoseconds. If the field is absent it means that
# there haven't been any operations yet (Since 2.5).
@@ -898,6 +909,9 @@
# @failed_flush_operations: The number of failed flush operations
# performed by the device (Since 2.5)
#
+# @failed_unmap_operations: The number of failed unmap operations performed
+# by the device (Since 4.2)
+#
# @invalid_rd_operations: The number of invalid read operations
# performed by the device (Since 2.5)
#
@@ -907,6 +921,9 @@
# @invalid_flush_operations: The number of invalid flush operations
# performed by the device (Since 2.5)
#
+# @invalid_unmap_operations: The number of invalid unmap operations performed
+# by the device (Since 4.2)
+#
# @account_invalid: Whether invalid operations are included in the
# last access statistics (Since 2.5)
#
@@ -925,14 +942,18 @@
# Since: 0.14.0
##
{ 'struct': 'BlockDeviceStats',
- 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int',
- 'wr_operations': 'int', 'flush_operations': 'int',
- 'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
- 'rd_total_time_ns': 'int', 'wr_highest_offset': 'int',
- 'rd_merged': 'int', 'wr_merged': 'int', '*idle_time_ns': 'int',
+ 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'unmap_bytes' : 'int',
+ 'rd_operations': 'int', 'wr_operations': 'int',
+ 'flush_operations': 'int', 'unmap_operations': 'int',
+ 'rd_total_time_ns': 'int', 'wr_total_time_ns': 'int',
+ 'flush_total_time_ns': 'int', 'unmap_total_time_ns': 'int',
+ 'wr_highest_offset': 'int',
+ 'rd_merged': 'int', 'wr_merged': 'int', 'unmap_merged': 'int',
+ '*idle_time_ns': 'int',
'failed_rd_operations': 'int', 'failed_wr_operations': 'int',
- 'failed_flush_operations': 'int', 'invalid_rd_operations': 'int',
- 'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
+ 'failed_flush_operations': 'int', 'failed_unmap_operations': 'int',
+ 'invalid_rd_operations': 'int', 'invalid_wr_operations': 'int',
+ 'invalid_flush_operations': 'int', 'invalid_unmap_operations': 'int',
'account_invalid': 'bool', 'account_failed': 'bool',
'timed_stats': ['BlockDeviceTimedStats'],
'*rd_latency_histogram': 'BlockLatencyHistogramInfo',
@@ -940,6 +961,41 @@
'*flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
##
+# @BlockStatsSpecificFile:
+#
+# File driver statistics
+#
+# @discard-nb-ok: The number of successful discard operations performed by
+# the driver.
+#
+# @discard-nb-failed: The number of failed discard operations performed by
+# the driver.
+#
+# @discard-bytes-ok: The number of bytes discarded by the driver.
+#
+# Since: 4.2
+##
+{ 'struct': 'BlockStatsSpecificFile',
+ 'data': {
+ 'discard-nb-ok': 'uint64',
+ 'discard-nb-failed': 'uint64',
+ 'discard-bytes-ok': 'uint64' } }
+
+##
+# @BlockStatsSpecific:
+#
+# Block driver specific statistics
+#
+# Since: 4.2
+##
+{ 'union': 'BlockStatsSpecific',
+ 'base': { 'driver': 'BlockdevDriver' },
+ 'discriminator': 'driver',
+ 'data': {
+ 'file': 'BlockStatsSpecificFile',
+ 'host_device': 'BlockStatsSpecificFile' } }
+
+##
# @BlockStats:
#
# Statistics of a virtual block device or a block backing device.
@@ -954,6 +1010,8 @@
#
# @stats: A @BlockDeviceStats for the device.
#
+# @driver-specific: Optional driver-specific stats. (Since 4.2)
+#
# @parent: This describes the file block device if it has one.
# Contains recursively the statistics of the underlying
# protocol (e.g. the host file for a qcow2 image). If there is
@@ -967,6 +1025,7 @@
{ 'struct': 'BlockStats',
'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str',
'stats': 'BlockDeviceStats',
+ '*driver-specific': 'BlockStatsSpecific',
'*parent': 'BlockStats',
'*backing': 'BlockStats'} }
@@ -1391,6 +1450,11 @@
# list without user intervention.
# Defaults to true. (Since 2.12)
#
+# @filter-node-name: the node name that should be assigned to the
+# filter driver that the backup job inserts into the graph
+# above node specified by @drive. If this option is not given,
+# a node name is autogenerated. (Since: 4.2)
+#
# Note: @on-source-error and @on-target-error only affect background
# I/O. If an error occurs during a guest write request, the device's
# rerror/werror actions will be used.
@@ -1404,7 +1468,8 @@
'*compress': 'bool',
'*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError',
- '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
+ '*auto-finalize': 'bool', '*auto-dismiss': 'bool',
+ '*filter-node-name': 'str' } }
##
# @DriveBackup:
diff --git a/qemu-img.c b/qemu-img.c
index 384c6f3..8b03ef8 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -214,6 +214,14 @@
},
};
+static bool qemu_img_object_print_help(const char *type, QemuOpts *opts)
+{
+ if (user_creatable_print_help(type, opts)) {
+ exit(0);
+ }
+ return true;
+}
+
static QemuOptsList qemu_source_opts = {
.name = "source",
.implied_opt_name = "file",
@@ -516,7 +524,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
goto fail;
}
@@ -766,7 +774,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
return 1;
}
@@ -979,7 +987,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
return 1;
}
@@ -1362,7 +1370,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
ret = 2;
goto out4;
}
@@ -2210,7 +2218,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
goto fail_getopt;
}
@@ -2776,7 +2784,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
return 1;
}
@@ -3002,7 +3010,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
return 1;
}
@@ -3154,7 +3162,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
return 1;
}
@@ -3321,7 +3329,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
return 1;
}
@@ -3742,7 +3750,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
return 1;
}
@@ -3986,7 +3994,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
ret = -1;
goto out_no_progress;
}
@@ -4630,7 +4638,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
ret = -1;
goto out;
}
@@ -4907,7 +4915,7 @@
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal)) {
+ qemu_img_object_print_help, &error_fatal)) {
goto out;
}
diff --git a/qemu-io.c b/qemu-io.c
index f64eca6..91e3276 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -475,6 +475,13 @@
},
};
+static bool qemu_io_object_print_help(const char *type, QemuOpts *opts)
+{
+ if (user_creatable_print_help(type, opts)) {
+ exit(0);
+ }
+ return true;
+}
static QemuOptsList file_opts = {
.name = "file",
@@ -622,7 +629,7 @@
qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal);
+ qemu_io_object_print_help, &error_fatal);
if (!trace_init_backends()) {
exit(1);
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 9032b6d..caacf0e 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -507,6 +507,13 @@
},
};
+static bool qemu_nbd_object_print_help(const char *type, QemuOpts *opts)
+{
+ if (user_creatable_print_help(type, opts)) {
+ exit(0);
+ }
+ return true;
+}
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list,
@@ -902,7 +909,7 @@
qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, &error_fatal);
+ qemu_nbd_object_print_help, &error_fatal);
if (!trace_init_backends()) {
exit(1);
diff --git a/qemu-options.hx b/qemu-options.hx
index 2a04ca6..793d70f 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1339,7 +1339,7 @@
DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
"-virtfs local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passthrough|none\n"
- " [,id=id][,writeout=immediate][,readonly][,fmode=fmode][,dmode=dmode]\n"
+ " [,id=id][,writeout=immediate][,readonly][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn]\n"
"-virtfs proxy,mount_tag=tag,socket=socket[,id=id][,writeout=immediate][,readonly]\n"
"-virtfs proxy,mount_tag=tag,sock_fd=sock_fd[,id=id][,writeout=immediate][,readonly]\n"
"-virtfs synth,mount_tag=tag[,id=id][,readonly]\n",
@@ -1347,7 +1347,7 @@
STEXI
-@item -virtfs local,path=@var{path},mount_tag=@var{mount_tag} ,security_model=@var{security_model}[,writeout=@var{writeout}][,readonly] [,fmode=@var{fmode}][,dmode=@var{dmode}]
+@item -virtfs local,path=@var{path},mount_tag=@var{mount_tag} ,security_model=@var{security_model}[,writeout=@var{writeout}][,readonly] [,fmode=@var{fmode}][,dmode=@var{dmode}][,multidevs=@var{multidevs}]
@itemx -virtfs proxy,socket=@var{socket},mount_tag=@var{mount_tag} [,writeout=@var{writeout}][,readonly]
@itemx -virtfs proxy,sock_fd=@var{sock_fd},mount_tag=@var{mount_tag} [,writeout=@var{writeout}][,readonly]
@itemx -virtfs synth,mount_tag=@var{mount_tag}
@@ -1403,6 +1403,28 @@
only with security models "mapped-xattr" and "mapped-file".
@item mount_tag=@var{mount_tag}
Specifies the tag name to be used by the guest to mount this export point.
+@item multidevs=@var{multidevs}
+Specifies how to deal with multiple devices being shared with a 9p export.
+Supported behaviours are either "remap", "forbid" or "warn". The latter is
+the default behaviour on which virtfs 9p expects only one device to be
+shared with the same export, and if more than one device is shared and
+accessed via the same 9p export then only a warning message is logged
+(once) by qemu on host side. In order to avoid file ID collisions on guest
+you should either create a separate virtfs export for each device to be
+shared with guests (recommended way) or you might use "remap" instead which
+allows you to share multiple devices with only one export instead, which is
+achieved by remapping the original inode numbers from host to guest in a
+way that would prevent such collisions. Remapping inodes in such use cases
+is required because the original device IDs from host are never passed and
+exposed on guest. Instead all files of an export shared with virtfs always
+share the same device id on guest. So two files with identical inode
+numbers but from actually different devices on host would otherwise cause a
+file ID collision and hence potential misbehaviours on guest. "forbid" on
+the other hand assumes like "warn" that only one device is shared by the
+same export, however it will not only log a warning message but also
+deny access to additional devices on guest. Note though that "forbid" does
+currently not block all possible file access operations (e.g. readdir()
+would still return entries from other devices).
@end table
ETEXI
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index cb58099..46cd6ea 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -1,8 +1,11 @@
#include "qemu/osdep.h"
+
+#include "qemu/cutils.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qom/object_interfaces.h"
+#include "qemu/help_option.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qapi/opts-visitor.h"
@@ -155,6 +158,64 @@
return 0;
}
+bool user_creatable_print_help(const char *type, QemuOpts *opts)
+{
+ ObjectClass *klass;
+
+ if (is_help_option(type)) {
+ GSList *l, *list;
+
+ printf("List of user creatable objects:\n");
+ list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
+ for (l = list; l != NULL; l = l->next) {
+ ObjectClass *oc = OBJECT_CLASS(l->data);
+ printf(" %s\n", object_class_get_name(oc));
+ }
+ g_slist_free(list);
+ return true;
+ }
+
+ klass = object_class_by_name(type);
+ if (klass && qemu_opt_has_help_opt(opts)) {
+ ObjectPropertyIterator iter;
+ ObjectProperty *prop;
+ GPtrArray *array = g_ptr_array_new();
+ int i;
+
+ object_class_property_iter_init(&iter, klass);
+ while ((prop = object_property_iter_next(&iter))) {
+ GString *str;
+
+ if (!prop->set) {
+ continue;
+ }
+
+ str = g_string_new(NULL);
+ g_string_append_printf(str, " %s=<%s>", prop->name, prop->type);
+ if (prop->description) {
+ if (str->len < 24) {
+ g_string_append_printf(str, "%*s", 24 - (int)str->len, "");
+ }
+ g_string_append_printf(str, " - %s", prop->description);
+ }
+ g_ptr_array_add(array, g_string_free(str, false));
+ }
+ g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
+ if (array->len > 0) {
+ printf("%s options:\n", type);
+ } else {
+ printf("There are no options for %s.\n", type);
+ }
+ for (i = 0; i < array->len; i++) {
+ printf("%s\n", (char *)array->pdata[i]);
+ }
+ g_ptr_array_set_free_func(array, g_free);
+ g_ptr_array_free(array, true);
+ return true;
+ }
+
+ return false;
+}
void user_creatable_del(const char *id, Error **errp)
{
diff --git a/replay/replay-events.c b/replay/replay-events.c
index 008e80f..302b840 100644
--- a/replay/replay-events.c
+++ b/replay/replay-events.c
@@ -36,6 +36,9 @@
case REPLAY_ASYNC_EVENT_BH:
aio_bh_call(event->opaque);
break;
+ case REPLAY_ASYNC_EVENT_BH_ONESHOT:
+ ((QEMUBHFunc *)event->opaque)(event->opaque2);
+ break;
case REPLAY_ASYNC_EVENT_INPUT:
qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque);
qapi_free_InputEvent((InputEvent *)event->opaque);
@@ -131,6 +134,17 @@
}
}
+void replay_bh_schedule_oneshot_event(AioContext *ctx,
+ QEMUBHFunc *cb, void *opaque)
+{
+ if (events_enabled) {
+ uint64_t id = replay_get_current_icount();
+ replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id);
+ } else {
+ aio_bh_schedule_oneshot(ctx, cb, opaque);
+ }
+}
+
void replay_add_input_event(struct InputEvent *event)
{
replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
@@ -161,6 +175,7 @@
/* save event-specific data */
switch (event->event_kind) {
case REPLAY_ASYNC_EVENT_BH:
+ case REPLAY_ASYNC_EVENT_BH_ONESHOT:
replay_put_qword(event->id);
break;
case REPLAY_ASYNC_EVENT_INPUT:
@@ -216,6 +231,7 @@
/* Events that has not to be in the queue */
switch (replay_state.read_event_kind) {
case REPLAY_ASYNC_EVENT_BH:
+ case REPLAY_ASYNC_EVENT_BH_ONESHOT:
if (replay_state.read_event_id == -1) {
replay_state.read_event_id = replay_get_qword();
}
diff --git a/replay/replay-internal.h b/replay/replay-internal.h
index afba9a3..55fca1a 100644
--- a/replay/replay-internal.h
+++ b/replay/replay-internal.h
@@ -51,6 +51,7 @@
enum ReplayAsyncEventKind {
REPLAY_ASYNC_EVENT_BH,
+ REPLAY_ASYNC_EVENT_BH_ONESHOT,
REPLAY_ASYNC_EVENT_INPUT,
REPLAY_ASYNC_EVENT_INPUT_SYNC,
REPLAY_ASYNC_EVENT_CHAR_READ,
diff --git a/replay/replay.c b/replay/replay.c
index 713395b..5cc25bd2 100644
--- a/replay/replay.c
+++ b/replay/replay.c
@@ -385,6 +385,8 @@
g_free(replay_snapshot);
replay_snapshot = NULL;
+ replay_mode = REPLAY_MODE_NONE;
+
replay_finish_events();
}
diff --git a/roms/openbios b/roms/openbios
index c79e0ec..f28e16f 160000
--- a/roms/openbios
+++ b/roms/openbios
@@ -1 +1 @@
-Subproject commit c79e0ecb84f4f1ee3f73f521622e264edd1bf174
+Subproject commit f28e16f9aab36f723df525e8a2a1a798b18e19bd
diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py
index 04279fa..44c118b 100644
--- a/scripts/tracetool/__init__.py
+++ b/scripts/tracetool/__init__.py
@@ -456,12 +456,12 @@
import tracetool
format = str(format)
- if len(format) is 0:
+ if len(format) == 0:
raise TracetoolError("format not set")
if not tracetool.format.exists(format):
raise TracetoolError("unknown format: %s" % format)
- if len(backends) is 0:
+ if len(backends) == 0:
raise TracetoolError("no backends specified")
for backend in backends:
if not tracetool.backend.exists(backend):
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index 9c7393b..4a50e95 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -20,6 +20,7 @@
stub-obj-y += notify-event.o
stub-obj-y += qtest.o
stub-obj-y += replay.o
+stub-obj-y += replay-user.o
stub-obj-y += runstate-check.o
stub-obj-y += set-fd-handler.o
stub-obj-y += sysbus.o
diff --git a/stubs/replay-user.c b/stubs/replay-user.c
new file mode 100644
index 0000000..2ad9e27
--- /dev/null
+++ b/stubs/replay-user.c
@@ -0,0 +1,9 @@
+#include "qemu/osdep.h"
+#include "sysemu/replay.h"
+#include "sysemu/sysemu.h"
+
+void replay_bh_schedule_oneshot_event(AioContext *ctx,
+ QEMUBHFunc *cb, void *opaque)
+{
+ aio_bh_schedule_oneshot(ctx, cb, opaque);
+}
diff --git a/target/arm/arm-semi.c b/target/arm/arm-semi.c
index 90423a3..6f7b6d8 100644
--- a/target/arm/arm-semi.c
+++ b/target/arm/arm-semi.c
@@ -59,6 +59,7 @@
#define TARGET_SYS_HEAPINFO 0x16
#define TARGET_SYS_EXIT 0x18
#define TARGET_SYS_SYNCCACHE 0x19
+#define TARGET_SYS_EXIT_EXTENDED 0x20
/* ADP_Stopped_ApplicationExit is used for exit(0),
* anything else is implemented as exit(1) */
@@ -106,43 +107,169 @@
O_RDWR | O_CREAT | O_APPEND | O_BINARY
};
-#ifdef CONFIG_USER_ONLY
-static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
+typedef enum GuestFDType {
+ GuestFDUnused = 0,
+ GuestFDHost = 1,
+ GuestFDGDB = 2,
+ GuestFDFeatureFile = 3,
+} GuestFDType;
+
+/*
+ * Guest file descriptors are integer indexes into an array of
+ * these structures (we will dynamically resize as necessary).
+ */
+typedef struct GuestFD {
+ GuestFDType type;
+ union {
+ int hostfd;
+ target_ulong featurefile_offset;
+ };
+} GuestFD;
+
+static GArray *guestfd_array;
+
+/*
+ * Allocate a new guest file descriptor and return it; if we
+ * couldn't allocate a new fd then return -1.
+ * This is a fairly simplistic implementation because we don't
+ * expect that most semihosting guest programs will make very
+ * heavy use of opening and closing fds.
+ */
+static int alloc_guestfd(void)
{
- if (code == (uint32_t)-1)
- ts->swi_errno = errno;
- return code;
+ guint i;
+
+ if (!guestfd_array) {
+ /* New entries zero-initialized, i.e. type GuestFDUnused */
+ guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD));
+ }
+
+ for (i = 0; i < guestfd_array->len; i++) {
+ GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i);
+
+ if (gf->type == GuestFDUnused) {
+ return i;
+ }
+ }
+
+ /* All elements already in use: expand the array */
+ g_array_set_size(guestfd_array, i + 1);
+ return i;
}
-#else
-static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
+
+/*
+ * Look up the guestfd in the data structure; return NULL
+ * for out of bounds, but don't check whether the slot is unused.
+ * This is used internally by the other guestfd functions.
+ */
+static GuestFD *do_get_guestfd(int guestfd)
{
- return code;
+ if (!guestfd_array) {
+ return NULL;
+ }
+
+ if (guestfd < 0 || guestfd >= guestfd_array->len) {
+ return NULL;
+ }
+
+ return &g_array_index(guestfd_array, GuestFD, guestfd);
}
+/*
+ * Associate the specified guest fd (which must have been
+ * allocated via alloc_fd() and not previously used) with
+ * the specified host/gdb fd.
+ */
+static void associate_guestfd(int guestfd, int hostfd)
+{
+ GuestFD *gf = do_get_guestfd(guestfd);
+
+ assert(gf);
+ gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost;
+ gf->hostfd = hostfd;
+}
+
+/*
+ * Deallocate the specified guest file descriptor. This doesn't
+ * close the host fd, it merely undoes the work of alloc_fd().
+ */
+static void dealloc_guestfd(int guestfd)
+{
+ GuestFD *gf = do_get_guestfd(guestfd);
+
+ assert(gf);
+ gf->type = GuestFDUnused;
+}
+
+/*
+ * Given a guest file descriptor, get the associated struct.
+ * If the fd is not valid, return NULL. This is the function
+ * used by the various semihosting calls to validate a handle
+ * from the guest.
+ * Note: calling alloc_guestfd() or dealloc_guestfd() will
+ * invalidate any GuestFD* obtained by calling this function.
+ */
+static GuestFD *get_guestfd(int guestfd)
+{
+ GuestFD *gf = do_get_guestfd(guestfd);
+
+ if (!gf || gf->type == GuestFDUnused) {
+ return NULL;
+ }
+ return gf;
+}
+
+/*
+ * The semihosting API has no concept of its errno being thread-safe,
+ * as the API design predates SMP CPUs and was intended as a simple
+ * real-hardware set of debug functionality. For QEMU, we make the
+ * errno be per-thread in linux-user mode; in softmmu it is a simple
+ * global, and we assume that the guest takes care of avoiding any races.
+ */
+#ifndef CONFIG_USER_ONLY
+static target_ulong syscall_err;
+
#include "exec/softmmu-semi.h"
#endif
-static target_ulong arm_semi_syscall_len;
+static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
+{
+ if (code == (uint32_t)-1) {
+#ifdef CONFIG_USER_ONLY
+ CPUState *cs = env_cpu(env);
+ TaskState *ts = cs->opaque;
-#if !defined(CONFIG_USER_ONLY)
-static target_ulong syscall_err;
+ ts->swi_errno = errno;
+#else
+ syscall_err = errno;
#endif
+ }
+ return code;
+}
+
+static inline uint32_t get_swi_errno(CPUARMState *env)
+{
+#ifdef CONFIG_USER_ONLY
+ CPUState *cs = env_cpu(env);
+ TaskState *ts = cs->opaque;
+
+ return ts->swi_errno;
+#else
+ return syscall_err;
+#endif
+}
+
+static target_ulong arm_semi_syscall_len;
static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
-#ifdef CONFIG_USER_ONLY
- TaskState *ts = cs->opaque;
-#endif
target_ulong reg0 = is_a64(env) ? env->xregs[0] : env->regs[0];
if (ret == (target_ulong)-1) {
-#ifdef CONFIG_USER_ONLY
- ts->swi_errno = err;
-#else
- syscall_err = err;
-#endif
+ errno = err;
+ set_swi_errno(env, -1);
reg0 = ret;
} else {
/* Fixup syscalls that use nonstardard return conventions. */
@@ -199,11 +326,30 @@
} else {
env->regs[0] = size;
}
-#ifdef CONFIG_USER_ONLY
- ((TaskState *)cs->opaque)->swi_errno = err;
-#else
- syscall_err = err;
-#endif
+ errno = err;
+ set_swi_errno(env, -1);
+}
+
+static int arm_semi_open_guestfd;
+
+static void arm_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+ if (ret == (target_ulong)-1) {
+ errno = err;
+ set_swi_errno(env, -1);
+ dealloc_guestfd(arm_semi_open_guestfd);
+ } else {
+ associate_guestfd(arm_semi_open_guestfd, ret);
+ ret = arm_semi_open_guestfd;
+ }
+
+ if (is_a64(env)) {
+ env->xregs[0] = ret;
+ } else {
+ env->regs[0] = ret;
+ }
}
static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb,
@@ -216,26 +362,289 @@
gdb_do_syscallv(cb, fmt, va);
va_end(va);
- /* FIXME: we are implicitly relying on the syscall completing
- * before this point, which is not guaranteed. We should
- * put in an explicit synchronization between this and
- * the callback function.
+ /*
+ * FIXME: in softmmu mode, the gdbstub will schedule our callback
+ * to occur, but will not actually call it to complete the syscall
+ * until after this function has returned and we are back in the
+ * CPU main loop. Therefore callers to this function must not
+ * do anything with its return value, because it is not necessarily
+ * the result of the syscall, but could just be the old value of X0.
+ * The only thing safe to do with this is that the callers of
+ * do_arm_semihosting() will write it straight back into X0.
+ * (In linux-user mode, the callback will have happened before
+ * gdb_do_syscallv() returns.)
+ *
+ * We should tidy this up so neither this function nor
+ * do_arm_semihosting() return a value, so the mistake of
+ * doing something with the return value is not possible to make.
*/
return is_a64(env) ? env->xregs[0] : env->regs[0];
}
+/*
+ * Types for functions implementing various semihosting calls
+ * for specific types of guest file descriptor. These must all
+ * do the work and return the required return value for the guest,
+ * setting the guest errno if appropriate.
+ */
+typedef uint32_t sys_closefn(ARMCPU *cpu, GuestFD *gf);
+typedef uint32_t sys_writefn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong buf, uint32_t len);
+typedef uint32_t sys_readfn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong buf, uint32_t len);
+typedef uint32_t sys_isattyfn(ARMCPU *cpu, GuestFD *gf);
+typedef uint32_t sys_seekfn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong offset);
+typedef uint32_t sys_flenfn(ARMCPU *cpu, GuestFD *gf);
+
+static uint32_t host_closefn(ARMCPU *cpu, GuestFD *gf)
+{
+ CPUARMState *env = &cpu->env;
+
+ return set_swi_errno(env, close(gf->hostfd));
+}
+
+static uint32_t host_writefn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong buf, uint32_t len)
+{
+ uint32_t ret;
+ CPUARMState *env = &cpu->env;
+ char *s = lock_user(VERIFY_READ, buf, len, 1);
+ if (!s) {
+ /* Return bytes not written on error */
+ return len;
+ }
+ ret = set_swi_errno(env, write(gf->hostfd, s, len));
+ unlock_user(s, buf, 0);
+ if (ret == (uint32_t)-1) {
+ ret = 0;
+ }
+ /* Return bytes not written */
+ return len - ret;
+}
+
+static uint32_t host_readfn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong buf, uint32_t len)
+{
+ uint32_t ret;
+ CPUARMState *env = &cpu->env;
+ char *s = lock_user(VERIFY_WRITE, buf, len, 0);
+ if (!s) {
+ /* return bytes not read */
+ return len;
+ }
+ do {
+ ret = set_swi_errno(env, read(gf->hostfd, s, len));
+ } while (ret == -1 && errno == EINTR);
+ unlock_user(s, buf, len);
+ if (ret == (uint32_t)-1) {
+ ret = 0;
+ }
+ /* Return bytes not read */
+ return len - ret;
+}
+
+static uint32_t host_isattyfn(ARMCPU *cpu, GuestFD *gf)
+{
+ return isatty(gf->hostfd);
+}
+
+static uint32_t host_seekfn(ARMCPU *cpu, GuestFD *gf, target_ulong offset)
+{
+ CPUARMState *env = &cpu->env;
+ uint32_t ret = set_swi_errno(env, lseek(gf->hostfd, offset, SEEK_SET));
+ if (ret == (uint32_t)-1) {
+ return -1;
+ }
+ return 0;
+}
+
+static uint32_t host_flenfn(ARMCPU *cpu, GuestFD *gf)
+{
+ CPUARMState *env = &cpu->env;
+ struct stat buf;
+ uint32_t ret = set_swi_errno(env, fstat(gf->hostfd, &buf));
+ if (ret == (uint32_t)-1) {
+ return -1;
+ }
+ return buf.st_size;
+}
+
+static uint32_t gdb_closefn(ARMCPU *cpu, GuestFD *gf)
+{
+ return arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", gf->hostfd);
+}
+
+static uint32_t gdb_writefn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong buf, uint32_t len)
+{
+ arm_semi_syscall_len = len;
+ return arm_gdb_syscall(cpu, arm_semi_cb, "write,%x,%x,%x",
+ gf->hostfd, buf, len);
+}
+
+static uint32_t gdb_readfn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong buf, uint32_t len)
+{
+ arm_semi_syscall_len = len;
+ return arm_gdb_syscall(cpu, arm_semi_cb, "read,%x,%x,%x",
+ gf->hostfd, buf, len);
+}
+
+static uint32_t gdb_isattyfn(ARMCPU *cpu, GuestFD *gf)
+{
+ return arm_gdb_syscall(cpu, arm_semi_cb, "isatty,%x", gf->hostfd);
+}
+
+static uint32_t gdb_seekfn(ARMCPU *cpu, GuestFD *gf, target_ulong offset)
+{
+ return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0",
+ gf->hostfd, offset);
+}
+
+static uint32_t gdb_flenfn(ARMCPU *cpu, GuestFD *gf)
+{
+ return arm_gdb_syscall(cpu, arm_semi_flen_cb, "fstat,%x,%x",
+ gf->hostfd, arm_flen_buf(cpu));
+}
+
+#define SHFB_MAGIC_0 0x53
+#define SHFB_MAGIC_1 0x48
+#define SHFB_MAGIC_2 0x46
+#define SHFB_MAGIC_3 0x42
+
+/* Feature bits reportable in feature byte 0 */
+#define SH_EXT_EXIT_EXTENDED (1 << 0)
+#define SH_EXT_STDOUT_STDERR (1 << 1)
+
+static const uint8_t featurefile_data[] = {
+ SHFB_MAGIC_0,
+ SHFB_MAGIC_1,
+ SHFB_MAGIC_2,
+ SHFB_MAGIC_3,
+ SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */
+};
+
+static void init_featurefile_guestfd(int guestfd)
+{
+ GuestFD *gf = do_get_guestfd(guestfd);
+
+ assert(gf);
+ gf->type = GuestFDFeatureFile;
+ gf->featurefile_offset = 0;
+}
+
+static uint32_t featurefile_closefn(ARMCPU *cpu, GuestFD *gf)
+{
+ /* Nothing to do */
+ return 0;
+}
+
+static uint32_t featurefile_writefn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong buf, uint32_t len)
+{
+ /* This fd can never be open for writing */
+ CPUARMState *env = &cpu->env;
+
+ errno = EBADF;
+ return set_swi_errno(env, -1);
+}
+
+static uint32_t featurefile_readfn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong buf, uint32_t len)
+{
+ uint32_t i;
+#ifndef CONFIG_USER_ONLY
+ CPUARMState *env = &cpu->env;
+#endif
+ char *s;
+
+ s = lock_user(VERIFY_WRITE, buf, len, 0);
+ if (!s) {
+ return len;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (gf->featurefile_offset >= sizeof(featurefile_data)) {
+ break;
+ }
+ s[i] = featurefile_data[gf->featurefile_offset];
+ gf->featurefile_offset++;
+ }
+
+ unlock_user(s, buf, len);
+
+ /* Return number of bytes not read */
+ return len - i;
+}
+
+static uint32_t featurefile_isattyfn(ARMCPU *cpu, GuestFD *gf)
+{
+ return 0;
+}
+
+static uint32_t featurefile_seekfn(ARMCPU *cpu, GuestFD *gf,
+ target_ulong offset)
+{
+ gf->featurefile_offset = offset;
+ return 0;
+}
+
+static uint32_t featurefile_flenfn(ARMCPU *cpu, GuestFD *gf)
+{
+ return sizeof(featurefile_data);
+}
+
+typedef struct GuestFDFunctions {
+ sys_closefn *closefn;
+ sys_writefn *writefn;
+ sys_readfn *readfn;
+ sys_isattyfn *isattyfn;
+ sys_seekfn *seekfn;
+ sys_flenfn *flenfn;
+} GuestFDFunctions;
+
+static const GuestFDFunctions guestfd_fns[] = {
+ [GuestFDHost] = {
+ .closefn = host_closefn,
+ .writefn = host_writefn,
+ .readfn = host_readfn,
+ .isattyfn = host_isattyfn,
+ .seekfn = host_seekfn,
+ .flenfn = host_flenfn,
+ },
+ [GuestFDGDB] = {
+ .closefn = gdb_closefn,
+ .writefn = gdb_writefn,
+ .readfn = gdb_readfn,
+ .isattyfn = gdb_isattyfn,
+ .seekfn = gdb_seekfn,
+ .flenfn = gdb_flenfn,
+ },
+ [GuestFDFeatureFile] = {
+ .closefn = featurefile_closefn,
+ .writefn = featurefile_writefn,
+ .readfn = featurefile_readfn,
+ .isattyfn = featurefile_isattyfn,
+ .seekfn = featurefile_seekfn,
+ .flenfn = featurefile_flenfn,
+ },
+};
+
/* Read the input value from the argument block; fail the semihosting
* call if the memory read fails.
*/
#define GET_ARG(n) do { \
if (is_a64(env)) { \
if (get_user_u64(arg ## n, args + (n) * 8)) { \
- return -1; \
+ errno = EFAULT; \
+ return set_swi_errno(env, -1); \
} \
} else { \
if (get_user_u32(arg ## n, args + (n) * 4)) { \
- return -1; \
+ errno = EFAULT; \
+ return set_swi_errno(env, -1); \
} \
} \
} while (0)
@@ -264,11 +673,7 @@
int nr;
uint32_t ret;
uint32_t len;
-#ifdef CONFIG_USER_ONLY
- TaskState *ts = cs->opaque;
-#else
- CPUARMState *ts = env;
-#endif
+ GuestFD *gf;
if (is_a64(env)) {
/* Note that the syscall number is in W0, not X0 */
@@ -281,38 +686,90 @@
switch (nr) {
case TARGET_SYS_OPEN:
+ {
+ int guestfd;
+
GET_ARG(0);
GET_ARG(1);
GET_ARG(2);
s = lock_user_string(arg0);
if (!s) {
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- return (uint32_t)-1;
+ errno = EFAULT;
+ return set_swi_errno(env, -1);
}
if (arg1 >= 12) {
unlock_user(s, arg0, 0);
- return (uint32_t)-1;
+ errno = EINVAL;
+ return set_swi_errno(env, -1);
}
- if (strcmp(s, ":tt") == 0) {
- int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
+
+ guestfd = alloc_guestfd();
+ if (guestfd < 0) {
unlock_user(s, arg0, 0);
- return result_fileno;
+ errno = EMFILE;
+ return set_swi_errno(env, -1);
}
+
+ if (strcmp(s, ":tt") == 0) {
+ int result_fileno;
+
+ /*
+ * We implement SH_EXT_STDOUT_STDERR, so:
+ * open for read == stdin
+ * open for write == stdout
+ * open for append == stderr
+ */
+ if (arg1 < 4) {
+ result_fileno = STDIN_FILENO;
+ } else if (arg1 < 8) {
+ result_fileno = STDOUT_FILENO;
+ } else {
+ result_fileno = STDERR_FILENO;
+ }
+ associate_guestfd(guestfd, result_fileno);
+ unlock_user(s, arg0, 0);
+ return guestfd;
+ }
+ if (strcmp(s, ":semihosting-features") == 0) {
+ unlock_user(s, arg0, 0);
+ /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */
+ if (arg1 != 0 && arg1 != 1) {
+ dealloc_guestfd(guestfd);
+ errno = EACCES;
+ return set_swi_errno(env, -1);
+ }
+ init_featurefile_guestfd(guestfd);
+ return guestfd;
+ }
+
if (use_gdb_syscalls()) {
- ret = arm_gdb_syscall(cpu, arm_semi_cb, "open,%s,%x,1a4", arg0,
+ arm_semi_open_guestfd = guestfd;
+ ret = arm_gdb_syscall(cpu, arm_semi_open_cb, "open,%s,%x,1a4", arg0,
(int)arg2+1, gdb_open_modeflags[arg1]);
} else {
- ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
+ ret = set_swi_errno(env, open(s, open_modeflags[arg1], 0644));
+ if (ret == (uint32_t)-1) {
+ dealloc_guestfd(guestfd);
+ } else {
+ associate_guestfd(guestfd, ret);
+ ret = guestfd;
+ }
}
unlock_user(s, arg0, 0);
return ret;
+ }
case TARGET_SYS_CLOSE:
GET_ARG(0);
- if (use_gdb_syscalls()) {
- return arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", arg0);
- } else {
- return set_swi_errno(ts, close(arg0));
+
+ gf = get_guestfd(arg0);
+ if (!gf) {
+ errno = EBADF;
+ return set_swi_errno(env, -1);
}
+
+ ret = guestfd_fns[gf->type].closefn(cpu, gf);
+ dealloc_guestfd(arg0);
+ return ret;
case TARGET_SYS_WRITEC:
qemu_semihosting_console_outc(env, args);
return 0xdeadbeef;
@@ -323,83 +780,61 @@
GET_ARG(1);
GET_ARG(2);
len = arg2;
- if (use_gdb_syscalls()) {
- arm_semi_syscall_len = len;
- return arm_gdb_syscall(cpu, arm_semi_cb, "write,%x,%x,%x",
- arg0, arg1, len);
- } else {
- s = lock_user(VERIFY_READ, arg1, len, 1);
- if (!s) {
- /* Return bytes not written on error */
- return len;
- }
- ret = set_swi_errno(ts, write(arg0, s, len));
- unlock_user(s, arg1, 0);
- if (ret == (uint32_t)-1) {
- ret = 0;
- }
- /* Return bytes not written */
- return len - ret;
+
+ gf = get_guestfd(arg0);
+ if (!gf) {
+ errno = EBADF;
+ return set_swi_errno(env, -1);
}
+
+ return guestfd_fns[gf->type].writefn(cpu, gf, arg1, len);
case TARGET_SYS_READ:
GET_ARG(0);
GET_ARG(1);
GET_ARG(2);
len = arg2;
- if (use_gdb_syscalls()) {
- arm_semi_syscall_len = len;
- return arm_gdb_syscall(cpu, arm_semi_cb, "read,%x,%x,%x",
- arg0, arg1, len);
- } else {
- s = lock_user(VERIFY_WRITE, arg1, len, 0);
- if (!s) {
- /* return bytes not read */
- return len;
- }
- do {
- ret = set_swi_errno(ts, read(arg0, s, len));
- } while (ret == -1 && errno == EINTR);
- unlock_user(s, arg1, len);
- if (ret == (uint32_t)-1) {
- ret = 0;
- }
- /* Return bytes not read */
- return len - ret;
+
+ gf = get_guestfd(arg0);
+ if (!gf) {
+ errno = EBADF;
+ return set_swi_errno(env, -1);
}
+
+ return guestfd_fns[gf->type].readfn(cpu, gf, arg1, len);
case TARGET_SYS_READC:
qemu_log_mask(LOG_UNIMP, "%s: SYS_READC not implemented", __func__);
return 0;
case TARGET_SYS_ISTTY:
GET_ARG(0);
- if (use_gdb_syscalls()) {
- return arm_gdb_syscall(cpu, arm_semi_cb, "isatty,%x", arg0);
- } else {
- return isatty(arg0);
+
+ gf = get_guestfd(arg0);
+ if (!gf) {
+ errno = EBADF;
+ return set_swi_errno(env, -1);
}
+
+ return guestfd_fns[gf->type].isattyfn(cpu, gf);
case TARGET_SYS_SEEK:
GET_ARG(0);
GET_ARG(1);
- if (use_gdb_syscalls()) {
- return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0",
- arg0, arg1);
- } else {
- ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
- if (ret == (uint32_t)-1)
- return -1;
- return 0;
+
+ gf = get_guestfd(arg0);
+ if (!gf) {
+ errno = EBADF;
+ return set_swi_errno(env, -1);
}
+
+ return guestfd_fns[gf->type].seekfn(cpu, gf, arg1);
case TARGET_SYS_FLEN:
GET_ARG(0);
- if (use_gdb_syscalls()) {
- return arm_gdb_syscall(cpu, arm_semi_flen_cb, "fstat,%x,%x",
- arg0, arm_flen_buf(cpu));
- } else {
- struct stat buf;
- ret = set_swi_errno(ts, fstat(arg0, &buf));
- if (ret == (uint32_t)-1)
- return -1;
- return buf.st_size;
+
+ gf = get_guestfd(arg0);
+ if (!gf) {
+ errno = EBADF;
+ return set_swi_errno(env, -1);
}
+
+ return guestfd_fns[gf->type].flenfn(cpu, gf);
case TARGET_SYS_TMPNAM:
qemu_log_mask(LOG_UNIMP, "%s: SYS_TMPNAM not implemented", __func__);
return -1;
@@ -412,10 +847,10 @@
} else {
s = lock_user_string(arg0);
if (!s) {
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- return (uint32_t)-1;
+ errno = EFAULT;
+ return set_swi_errno(env, -1);
}
- ret = set_swi_errno(ts, remove(s));
+ ret = set_swi_errno(env, remove(s));
unlock_user(s, arg0, 0);
}
return ret;
@@ -431,11 +866,12 @@
char *s2;
s = lock_user_string(arg0);
s2 = lock_user_string(arg2);
- if (!s || !s2)
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- ret = (uint32_t)-1;
- else
- ret = set_swi_errno(ts, rename(s, s2));
+ if (!s || !s2) {
+ errno = EFAULT;
+ ret = set_swi_errno(env, -1);
+ } else {
+ ret = set_swi_errno(env, rename(s, s2));
+ }
if (s2)
unlock_user(s2, arg2, 0);
if (s)
@@ -445,7 +881,7 @@
case TARGET_SYS_CLOCK:
return clock() / (CLOCKS_PER_SEC / 100);
case TARGET_SYS_TIME:
- return set_swi_errno(ts, time(NULL));
+ return set_swi_errno(env, time(NULL));
case TARGET_SYS_SYSTEM:
GET_ARG(0);
GET_ARG(1);
@@ -455,19 +891,15 @@
} else {
s = lock_user_string(arg0);
if (!s) {
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- return (uint32_t)-1;
+ errno = EFAULT;
+ return set_swi_errno(env, -1);
}
- ret = set_swi_errno(ts, system(s));
+ ret = set_swi_errno(env, system(s));
unlock_user(s, arg0, 0);
return ret;
}
case TARGET_SYS_ERRNO:
-#ifdef CONFIG_USER_ONLY
- return ts->swi_errno;
-#else
- return syscall_err;
-#endif
+ return get_swi_errno(env);
case TARGET_SYS_GET_CMDLINE:
{
/* Build a command-line from the original argv.
@@ -490,6 +922,8 @@
int status = 0;
#if !defined(CONFIG_USER_ONLY)
const char *cmdline;
+#else
+ TaskState *ts = cs->opaque;
#endif
GET_ARG(0);
GET_ARG(1);
@@ -516,19 +950,22 @@
if (output_size > input_size) {
/* Not enough space to store command-line arguments. */
- return -1;
+ errno = E2BIG;
+ return set_swi_errno(env, -1);
}
/* Adjust the command-line length. */
if (SET_ARG(1, output_size - 1)) {
/* Couldn't write back to argument block */
- return -1;
+ errno = EFAULT;
+ return set_swi_errno(env, -1);
}
/* Lock the buffer on the ARM side. */
output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
if (!output_buffer) {
- return -1;
+ errno = EFAULT;
+ return set_swi_errno(env, -1);
}
/* Copy the command-line arguments. */
@@ -543,7 +980,8 @@
if (copy_from_user(output_buffer, ts->info->arg_start,
output_size)) {
- status = -1;
+ errno = EFAULT;
+ status = set_swi_errno(env, -1);
goto out;
}
@@ -565,6 +1003,9 @@
target_ulong retvals[4];
target_ulong limit;
int i;
+#ifdef CONFIG_USER_ONLY
+ TaskState *ts = cs->opaque;
+#endif
GET_ARG(0);
@@ -613,17 +1054,21 @@
if (fail) {
/* Couldn't write back to argument block */
- return -1;
+ errno = EFAULT;
+ return set_swi_errno(env, -1);
}
}
return 0;
}
case TARGET_SYS_EXIT:
- if (is_a64(env)) {
+ case TARGET_SYS_EXIT_EXTENDED:
+ if (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(env)) {
/*
- * The A64 version of this call takes a parameter block,
+ * The A64 version of SYS_EXIT takes a parameter block,
* so the application-exit type can return a subcode which
* is the exit status code from the application.
+ * SYS_EXIT_EXTENDED is an a new-in-v2.0 optional function
+ * which allows A32/T32 guests to also provide a status code.
*/
GET_ARG(0);
GET_ARG(1);
@@ -635,8 +1080,10 @@
}
} else {
/*
- * ARM specifies only Stopped_ApplicationExit as normal
- * exit, everything else is considered an error
+ * The A32/T32 version of SYS_EXIT specifies only
+ * Stopped_ApplicationExit as normal exit, but does not
+ * allow the guest to specify the exit status code.
+ * Everything else is considered an error.
*/
ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
}
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 2399c14..13813fb 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -576,16 +576,16 @@
ARMCPU *cpu = opaque;
CPUARMState *env = &cpu->env;
CPUState *cs = CPU(cpu);
- int kvm_irq = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT;
uint32_t linestate_bit;
+ int irq_id;
switch (irq) {
case ARM_CPU_IRQ:
- kvm_irq |= KVM_ARM_IRQ_CPU_IRQ;
+ irq_id = KVM_ARM_IRQ_CPU_IRQ;
linestate_bit = CPU_INTERRUPT_HARD;
break;
case ARM_CPU_FIQ:
- kvm_irq |= KVM_ARM_IRQ_CPU_FIQ;
+ irq_id = KVM_ARM_IRQ_CPU_FIQ;
linestate_bit = CPU_INTERRUPT_FIQ;
break;
default:
@@ -597,9 +597,7 @@
} else {
env->irq_line_state &= ~linestate_bit;
}
-
- kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT;
- kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0);
+ kvm_arm_set_irq(cs->cpu_index, KVM_ARM_IRQ_TYPE_CPU, irq_id, !!level);
#endif
}
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index b2eaa50..b473c63 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -182,6 +182,7 @@
int kvm_arch_init(MachineState *ms, KVMState *s)
{
+ int ret = 0;
/* For ARM interrupt delivery is always asynchronous,
* whether we are using an in-kernel VGIC or not.
*/
@@ -195,7 +196,14 @@
cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
- return 0;
+ if (ms->smp.cpus > 256 &&
+ !kvm_check_extension(s, KVM_CAP_ARM_IRQ_LINE_LAYOUT_2)) {
+ error_report("Using more than 256 vcpus requires a host kernel "
+ "with KVM_CAP_ARM_IRQ_LINE_LAYOUT_2");
+ ret = -EINVAL;
+ }
+
+ return ret;
}
unsigned long kvm_arch_vcpu_id(CPUState *cpu)
@@ -744,6 +752,18 @@
}
}
+int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level)
+{
+ int kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT) | irq;
+ int cpu_idx1 = cpu % 256;
+ int cpu_idx2 = cpu / 256;
+
+ kvm_irq |= (cpu_idx1 << KVM_ARM_IRQ_VCPU_SHIFT) |
+ (cpu_idx2 << KVM_ARM_IRQ_VCPU2_SHIFT);
+
+ return kvm_set_irq(kvm_state, kvm_irq, !!level);
+}
+
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
uint64_t address, uint32_t data, PCIDevice *dev)
{
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index b3106c8..b4e1945 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -253,6 +253,7 @@
void kvm_arm_pmu_set_irq(CPUState *cs, int irq);
void kvm_arm_pmu_init(CPUState *cs);
+int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level);
#else
diff --git a/target/s390x/cc_helper.c b/target/s390x/cc_helper.c
index cf68792..44731e4 100644
--- a/target/s390x/cc_helper.c
+++ b/target/s390x/cc_helper.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
+#include "tcg_s390x.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "qemu/host-utils.h"
@@ -588,8 +589,7 @@
break;
default:
HELPER_LOG("unknown sacf mode: %" PRIx64 "\n", a1);
- s390_program_interrupt(env, PGM_SPECIFICATION, 2, GETPC());
- break;
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
}
#endif
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index 163dae1..17460ed7 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -1,6 +1,10 @@
/*
* S/390 virtual CPU header
*
+ * For details on the s390x architecture and used definitions (e.g.,
+ * PSW, PER and DAT (Dynamic Address Translation)), please refer to
+ * the "z/Architecture Principles of Operations" - a.k.a. PoP.
+ *
* Copyright (c) 2009 Ulrich Hecht
* Copyright IBM Corp. 2012, 2018
*
@@ -30,7 +34,7 @@
/* The z/Architecture has a strong memory model with some store-after-load re-ordering */
#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD)
-#define TARGET_INSN_START_EXTRA_WORDS 1
+#define TARGET_INSN_START_EXTRA_WORDS 2
#define MMU_MODE0_SUFFIX _primary
#define MMU_MODE1_SUFFIX _secondary
@@ -311,6 +315,7 @@
#define CR0_EDAT 0x0000000000800000ULL
#define CR0_AFP 0x0000000000040000ULL
#define CR0_VECTOR 0x0000000000020000ULL
+#define CR0_IEP 0x0000000000100000ULL
#define CR0_EMERGENCY_SIGNAL_SC 0x0000000000004000ULL
#define CR0_EXTERNAL_CALL_SC 0x0000000000002000ULL
#define CR0_CKC_SC 0x0000000000000800ULL
@@ -558,26 +563,60 @@
#define ASCE_TYPE_SEGMENT 0x00 /* segment table type */
#define ASCE_TABLE_LENGTH 0x03 /* region table length */
-#define REGION_ENTRY_ORIGIN (~0xfffULL) /* region/segment table origin */
-#define REGION_ENTRY_RO 0x200 /* region/segment protection bit */
-#define REGION_ENTRY_TF 0xc0 /* region/segment table offset */
-#define REGION_ENTRY_INV 0x20 /* invalid region table entry */
-#define REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */
-#define REGION_ENTRY_TYPE_R1 0x0c /* region first table type */
-#define REGION_ENTRY_TYPE_R2 0x08 /* region second table type */
-#define REGION_ENTRY_TYPE_R3 0x04 /* region third table type */
-#define REGION_ENTRY_LENGTH 0x03 /* region third length */
+#define REGION_ENTRY_ORIGIN 0xfffffffffffff000ULL
+#define REGION_ENTRY_P 0x0000000000000200ULL
+#define REGION_ENTRY_TF 0x00000000000000c0ULL
+#define REGION_ENTRY_I 0x0000000000000020ULL
+#define REGION_ENTRY_TT 0x000000000000000cULL
+#define REGION_ENTRY_TL 0x0000000000000003ULL
-#define SEGMENT_ENTRY_ORIGIN (~0x7ffULL) /* segment table origin */
-#define SEGMENT_ENTRY_FC 0x400 /* format control */
-#define SEGMENT_ENTRY_RO 0x200 /* page protection bit */
-#define SEGMENT_ENTRY_INV 0x20 /* invalid segment table entry */
+#define REGION_ENTRY_TT_REGION1 0x000000000000000cULL
+#define REGION_ENTRY_TT_REGION2 0x0000000000000008ULL
+#define REGION_ENTRY_TT_REGION3 0x0000000000000004ULL
-#define VADDR_PX 0xff000 /* page index bits */
+#define REGION3_ENTRY_RFAA 0xffffffff80000000ULL
+#define REGION3_ENTRY_AV 0x0000000000010000ULL
+#define REGION3_ENTRY_ACC 0x000000000000f000ULL
+#define REGION3_ENTRY_F 0x0000000000000800ULL
+#define REGION3_ENTRY_FC 0x0000000000000400ULL
+#define REGION3_ENTRY_IEP 0x0000000000000100ULL
+#define REGION3_ENTRY_CR 0x0000000000000010ULL
-#define PAGE_RO 0x200 /* HW read-only bit */
-#define PAGE_INVALID 0x400 /* HW invalid bit */
-#define PAGE_RES0 0x800 /* bit must be zero */
+#define SEGMENT_ENTRY_ORIGIN 0xfffffffffffff800ULL
+#define SEGMENT_ENTRY_SFAA 0xfffffffffff00000ULL
+#define SEGMENT_ENTRY_AV 0x0000000000010000ULL
+#define SEGMENT_ENTRY_ACC 0x000000000000f000ULL
+#define SEGMENT_ENTRY_F 0x0000000000000800ULL
+#define SEGMENT_ENTRY_FC 0x0000000000000400ULL
+#define SEGMENT_ENTRY_P 0x0000000000000200ULL
+#define SEGMENT_ENTRY_IEP 0x0000000000000100ULL
+#define SEGMENT_ENTRY_I 0x0000000000000020ULL
+#define SEGMENT_ENTRY_CS 0x0000000000000010ULL
+#define SEGMENT_ENTRY_TT 0x000000000000000cULL
+
+#define SEGMENT_ENTRY_TT_SEGMENT 0x0000000000000000ULL
+
+#define PAGE_ENTRY_0 0x0000000000000800ULL
+#define PAGE_ENTRY_I 0x0000000000000400ULL
+#define PAGE_ENTRY_P 0x0000000000000200ULL
+#define PAGE_ENTRY_IEP 0x0000000000000100ULL
+
+#define VADDR_REGION1_TX_MASK 0xffe0000000000000ULL
+#define VADDR_REGION2_TX_MASK 0x001ffc0000000000ULL
+#define VADDR_REGION3_TX_MASK 0x000003ff80000000ULL
+#define VADDR_SEGMENT_TX_MASK 0x000000007ff00000ULL
+#define VADDR_PAGE_TX_MASK 0x00000000000ff000ULL
+
+#define VADDR_REGION1_TX(vaddr) (((vaddr) & VADDR_REGION1_TX_MASK) >> 53)
+#define VADDR_REGION2_TX(vaddr) (((vaddr) & VADDR_REGION2_TX_MASK) >> 42)
+#define VADDR_REGION3_TX(vaddr) (((vaddr) & VADDR_REGION3_TX_MASK) >> 31)
+#define VADDR_SEGMENT_TX(vaddr) (((vaddr) & VADDR_SEGMENT_TX_MASK) >> 20)
+#define VADDR_PAGE_TX(vaddr) (((vaddr) & VADDR_PAGE_TX_MASK) >> 12)
+
+#define VADDR_REGION1_TL(vaddr) (((vaddr) & 0xc000000000000000ULL) >> 62)
+#define VADDR_REGION2_TL(vaddr) (((vaddr) & 0x0018000000000000ULL) >> 51)
+#define VADDR_REGION3_TL(vaddr) (((vaddr) & 0x0000030000000000ULL) >> 40)
+#define VADDR_SEGMENT_TL(vaddr) (((vaddr) & 0x0000000060000000ULL) >> 29)
#define SK_C (0x1 << 1)
#define SK_R (0x1 << 2)
@@ -765,11 +804,8 @@
void s390_crw_mchk(void);
void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
uint32_t io_int_parm, uint32_t io_int_word);
-/* automatically detect the instruction length */
-#define ILEN_AUTO 0xff
#define RA_IGNORED 0
-void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
- uintptr_t ra);
+void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra);
/* service interrupts are floating therefore we must not pass an cpustate */
void s390_sclp_extint(uint32_t parm);
diff --git a/target/s390x/crypto_helper.c b/target/s390x/crypto_helper.c
index 5c79790..ff3fbc3 100644
--- a/target/s390x/crypto_helper.c
+++ b/target/s390x/crypto_helper.c
@@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "internal.h"
+#include "tcg_s390x.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
@@ -34,16 +35,14 @@
case S390_FEAT_TYPE_PCKMO:
case S390_FEAT_TYPE_PCC:
if (mod) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
- return 0;
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
break;
}
s390_get_feat_block(type, subfunc);
if (!test_be_bit(fc, subfunc)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
- return 0;
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
switch (fc) {
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index 65eabf0..53c2f81 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -61,12 +61,12 @@
IplParameterBlock *iplb;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return;
}
if ((subcode & ~0x0ffffULL) || (subcode > 6)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
@@ -82,13 +82,13 @@
break;
case 5:
if ((r1 & 1) || (addr & 0x0fffULL)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (!address_space_access_valid(&address_space_memory, addr,
sizeof(IplParameterBlock), false,
MEMTXATTRS_UNSPECIFIED)) {
- s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_ADDRESSING, ra);
return;
}
iplb = g_new0(IplParameterBlock, 1);
@@ -112,13 +112,13 @@
return;
case 6:
if ((r1 & 1) || (addr & 0x0fffULL)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (!address_space_access_valid(&address_space_memory, addr,
sizeof(IplParameterBlock), true,
MEMTXATTRS_UNSPECIFIED)) {
- s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_ADDRESSING, ra);
return;
}
iplb = s390_ipl_get_iplb();
@@ -130,7 +130,7 @@
}
return;
default:
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
break;
}
}
diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c
index 892f659..e70c20d 100644
--- a/target/s390x/excp_helper.c
+++ b/target/s390x/excp_helper.c
@@ -34,15 +34,15 @@
#include "hw/boards.h"
#endif
-void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
- int ilen, uintptr_t ra)
+void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env,
+ uint32_t code, uintptr_t ra)
{
CPUState *cs = env_cpu(env);
cpu_restore_state(cs, ra, true);
qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
env->psw.addr);
- trigger_pgm_exception(env, code, ilen);
+ trigger_pgm_exception(env, code);
cpu_loop_exit(cs);
}
@@ -60,7 +60,7 @@
if (env->cregs[0] & CR0_AFP) {
env->fpc = deposit32(env->fpc, 8, 8, dxc);
}
- tcg_s390_program_interrupt(env, PGM_DATA, ILEN_AUTO, ra);
+ tcg_s390_program_interrupt(env, PGM_DATA, ra);
}
void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc,
@@ -75,7 +75,7 @@
/* Always store the VXC into the FPC, without AFP it is undefined */
env->fpc = deposit32(env->fpc, 8, 8, vxc);
- tcg_s390_program_interrupt(env, PGM_VECTOR_PROCESSING, ILEN_AUTO, ra);
+ tcg_s390_program_interrupt(env, PGM_VECTOR_PROCESSING, ra);
}
void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc)
@@ -96,7 +96,7 @@
{
S390CPU *cpu = S390_CPU(cs);
- trigger_pgm_exception(&cpu->env, PGM_ADDRESSING, ILEN_AUTO);
+ trigger_pgm_exception(&cpu->env, PGM_ADDRESSING);
/* On real machines this value is dropped into LowMem. Since this
is userland, simply put this someplace that cpu_loop can find it. */
cpu->env.__excp_addr = address;
@@ -126,8 +126,8 @@
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
target_ulong vaddr, raddr;
- uint64_t asc;
- int prot, fail;
+ uint64_t asc, tec;
+ int prot, excp;
qemu_log_mask(CPU_LOG_MMU, "%s: addr 0x%" VADDR_PRIx " rw %d mmu_idx %d\n",
__func__, address, access_type, mmu_idx);
@@ -140,30 +140,30 @@
if (!(env->psw.mask & PSW_MASK_64)) {
vaddr &= 0x7fffffff;
}
- fail = mmu_translate(env, vaddr, access_type, asc, &raddr, &prot, true);
+ excp = mmu_translate(env, vaddr, access_type, asc, &raddr, &prot, &tec);
} else if (mmu_idx == MMU_REAL_IDX) {
/* 31-Bit mode */
if (!(env->psw.mask & PSW_MASK_64)) {
vaddr &= 0x7fffffff;
}
- fail = mmu_translate_real(env, vaddr, access_type, &raddr, &prot);
+ excp = mmu_translate_real(env, vaddr, access_type, &raddr, &prot, &tec);
} else {
g_assert_not_reached();
}
/* check out of RAM access */
- if (!fail &&
+ if (!excp &&
!address_space_access_valid(&address_space_memory, raddr,
TARGET_PAGE_SIZE, access_type,
MEMTXATTRS_UNSPECIFIED)) {
qemu_log_mask(CPU_LOG_MMU,
"%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n",
__func__, (uint64_t)raddr, (uint64_t)ram_size);
- trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO);
- fail = 1;
+ excp = PGM_ADDRESSING;
+ tec = 0; /* unused */
}
- if (!fail) {
+ if (!excp) {
qemu_log_mask(CPU_LOG_MMU,
"%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n",
__func__, (uint64_t)vaddr, (uint64_t)raddr, prot);
@@ -175,23 +175,20 @@
return false;
}
- cpu_restore_state(cs, retaddr, true);
-
- /*
- * The ILC value for code accesses is undefined. The important
- * thing here is to *not* leave env->int_pgm_ilen set to ILEN_AUTO,
- * which would cause do_program_interrupt to attempt to read from
- * env->psw.addr again. C.f. the condition in trigger_page_fault,
- * but is not universally applied.
- *
- * ??? If we remove ILEN_AUTO, by moving the computation of ILEN
- * into cpu_restore_state, then we may remove this entirely.
- */
- if (access_type == MMU_INST_FETCH) {
- env->int_pgm_ilen = 2;
+ if (excp != PGM_ADDRESSING) {
+ stq_phys(env_cpu(env)->as,
+ env->psa + offsetof(LowCore, trans_exc_code), tec);
}
- cpu_loop_exit(cs);
+ /*
+ * For data accesses, ILEN will be filled in from the unwind info,
+ * within cpu_loop_exit_restore. For code accesses, retaddr == 0,
+ * and so unwinding will not occur. However, ILEN is also undefined
+ * for that case -- we choose to set ILEN = 2.
+ */
+ env->int_pgm_ilen = 2;
+ trigger_pgm_exception(env, excp);
+ cpu_loop_exit_restore(cs, retaddr);
}
static void do_program_interrupt(CPUS390XState *env)
@@ -200,9 +197,6 @@
LowCore *lowcore;
int ilen = env->int_pgm_ilen;
- if (ilen == ILEN_AUTO) {
- ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
- }
assert(ilen == 2 || ilen == 4 || ilen == 6);
switch (env->int_pgm_code) {
@@ -614,7 +608,7 @@
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, retaddr);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, retaddr);
}
#endif /* CONFIG_USER_ONLY */
diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c
index 5faf973..8bb9f54 100644
--- a/target/s390x/fpu_helper.c
+++ b/target/s390x/fpu_helper.c
@@ -825,7 +825,7 @@
{
if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
(!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
/* Install everything in the main FPC. */
@@ -843,7 +843,7 @@
if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
(!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
/*
@@ -880,7 +880,7 @@
void HELPER(srnm)(CPUS390XState *env, uint64_t rnd)
{
if (rnd > 0x7 || fpc_to_rnd[rnd & 0x7] == -1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
env->fpc = deposit32(env->fpc, 0, 3, rnd);
diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
index 49a650a..6278845 100644
--- a/target/s390x/gen-features.c
+++ b/target/s390x/gen-features.c
@@ -698,15 +698,23 @@
S390_FEAT_ZPCI,
};
-static uint16_t qemu_LATEST[] = {
+static uint16_t qemu_V4_1[] = {
S390_FEAT_STFLE_53,
S390_FEAT_VECTOR,
};
+static uint16_t qemu_LATEST[] = {
+ S390_FEAT_ACCESS_EXCEPTION_FS_INDICATION,
+ S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2,
+ S390_FEAT_ESOP,
+};
+
/* add all new definitions before this point */
static uint16_t qemu_MAX[] = {
/* generates a dependency warning, leave it out for now */
S390_FEAT_MSA_EXT_5,
+ /* features introduced after the z13 */
+ S390_FEAT_INSTRUCTION_EXEC_PROT,
};
/****** END FEATURE DEFS ******/
@@ -824,6 +832,7 @@
QEMU_FEAT_INITIALIZER(V2_11),
QEMU_FEAT_INITIALIZER(V3_1),
QEMU_FEAT_INITIALIZER(V4_0),
+ QEMU_FEAT_INITIALIZER(V4_1),
QEMU_FEAT_INITIALIZER(LATEST),
QEMU_FEAT_INITIALIZER(MAX),
};
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index 948c039..a3a4916 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -52,6 +52,7 @@
target_ulong raddr;
int prot;
uint64_t asc = env->psw.mask & PSW_MASK_ASC;
+ uint64_t tec;
/* 31-Bit mode */
if (!(env->psw.mask & PSW_MASK_64)) {
@@ -63,7 +64,11 @@
asc = PSW_ASC_PRIMARY;
}
- if (mmu_translate(env, vaddr, MMU_INST_FETCH, asc, &raddr, &prot, false)) {
+ /*
+ * We want to read code even if IEP is active. Use MMU_DATA_LOAD instead
+ * of MMU_INST_FETCH.
+ */
+ if (mmu_translate(env, vaddr, MMU_DATA_LOAD, asc, &raddr, &prot, &tec)) {
return -1;
}
return raddr;
diff --git a/target/s390x/int_helper.c b/target/s390x/int_helper.c
index d13cc49..658507d 100644
--- a/target/s390x/int_helper.c
+++ b/target/s390x/int_helper.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
+#include "tcg_s390x.h"
#include "exec/exec-all.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
@@ -39,7 +40,7 @@
int64_t q;
if (b == 0) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
ret = q = a / b;
@@ -47,7 +48,7 @@
/* Catch non-representable quotient. */
if (ret != q) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
return ret;
@@ -60,7 +61,7 @@
uint64_t q;
if (b == 0) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
ret = q = a / b;
@@ -68,7 +69,7 @@
/* Catch non-representable quotient. */
if (ret != q) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
return ret;
@@ -79,7 +80,7 @@
{
/* Catch divide by zero, and non-representable quotient (MIN / -1). */
if (b == 0 || (b == -1 && a == (1ll << 63))) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
env->retxl = a % b;
return a / b;
@@ -92,7 +93,7 @@
uint64_t ret;
/* Signal divide by zero. */
if (b == 0) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
if (ah == 0) {
/* 64 -> 64/64 case */
@@ -106,7 +107,7 @@
env->retxl = a % b;
ret = q;
if (ret != q) {
- s390_program_interrupt(env, PGM_FIXPT_DIVIDE, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
}
#else
/* 32-bit hosts would need special wrapper functionality - just abort if
diff --git a/target/s390x/internal.h b/target/s390x/internal.h
index c243fa7..d378161 100644
--- a/target/s390x/internal.h
+++ b/target/s390x/internal.h
@@ -317,7 +317,7 @@
/* interrupt.c */
-void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen);
+void trigger_pgm_exception(CPUS390XState *env, uint32_t code);
void cpu_inject_clock_comparator(S390CPU *cpu);
void cpu_inject_cpu_timer(S390CPU *cpu);
void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr);
@@ -360,9 +360,9 @@
/* mmu_helper.c */
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
- target_ulong *raddr, int *flags, bool exc);
+ target_ulong *raddr, int *flags, uint64_t *tec);
int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
- target_ulong *addr, int *flags);
+ target_ulong *addr, int *flags, uint64_t *tec);
/* misc_helper.c */
diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c
index a841f71..4cdbbc8 100644
--- a/target/s390x/interrupt.c
+++ b/target/s390x/interrupt.c
@@ -22,22 +22,21 @@
#endif
/* Ensure to exit the TB after this call! */
-void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
+void trigger_pgm_exception(CPUS390XState *env, uint32_t code)
{
CPUState *cs = env_cpu(env);
cs->exception_index = EXCP_PGM;
env->int_pgm_code = code;
- env->int_pgm_ilen = ilen;
+ /* env->int_pgm_ilen is already set, or will be set during unwinding */
}
-void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
- uintptr_t ra)
+void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra)
{
if (kvm_enabled()) {
kvm_s390_program_interrupt(env_archcpu(env), code);
} else if (tcg_enabled()) {
- tcg_s390_program_interrupt(env, code, ilen, ra);
+ tcg_s390_program_interrupt(env, code, ra);
} else {
g_assert_not_reached();
}
diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c
index 83c164a..c437a1d 100644
--- a/target/s390x/ioinst.c
+++ b/target/s390x/ioinst.c
@@ -44,7 +44,7 @@
SubchDev *sch;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("xsch", cssid, ssid, schid);
@@ -62,7 +62,7 @@
SubchDev *sch;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("csch", cssid, ssid, schid);
@@ -80,7 +80,7 @@
SubchDev *sch;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("hsch", cssid, ssid, schid);
@@ -116,7 +116,7 @@
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (s390_cpu_virt_mem_read(cpu, addr, ar, &schib, sizeof(schib))) {
@@ -125,7 +125,7 @@
}
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
!ioinst_schib_valid(&schib)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("msch", cssid, ssid, schid);
@@ -173,7 +173,7 @@
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (s390_cpu_virt_mem_read(cpu, addr, ar, &orig_orb, sizeof(orb))) {
@@ -183,7 +183,7 @@
copy_orb_from_guest(&orb, &orig_orb);
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
!ioinst_orb_valid(&orb)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("ssch", cssid, ssid, schid);
@@ -205,7 +205,7 @@
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
@@ -236,7 +236,7 @@
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
@@ -247,7 +247,7 @@
* access execption if it is not) first.
*/
if (!s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib))) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
} else {
s390_cpu_virt_mem_handle_exc(cpu, ra);
}
@@ -299,13 +299,13 @@
uint8_t ar;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return -EIO;
}
trace_ioinst_sch_id("tsch", cssid, ssid, schid);
addr = decode_basedisp_s(env, ipb, &ar);
if (addr & 3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return -EIO;
}
@@ -613,7 +613,7 @@
addr = env->regs[reg];
/* Page boundary? */
if (addr & 0xfff) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
/*
@@ -629,7 +629,7 @@
len = be16_to_cpu(req->len);
/* Length field valid? */
if ((len < 16) || (len > 4088) || (len & 7)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
@@ -678,7 +678,7 @@
trace_ioinst("schm");
if (SCHM_REG1_RES(reg1)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
@@ -687,7 +687,7 @@
dct = SCHM_REG1_DCT(reg1);
if (update && (reg2 & 0x000000000000001f)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
@@ -700,7 +700,7 @@
SubchDev *sch;
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
return;
}
trace_ioinst_sch_id("rsch", cssid, ssid, schid);
@@ -724,7 +724,7 @@
CPUS390XState *env = &cpu->env;
if (RCHP_REG1_RES(reg1)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
@@ -747,7 +747,7 @@
break;
default:
/* Invalid channel subsystem. */
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return;
}
setcc(cpu, cc);
@@ -758,6 +758,6 @@
{
/* We do not provide address limit checking, so let's suppress it. */
if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) {
- s390_program_interrupt(&cpu->env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
}
}
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index 44e5358..2325767 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
+#include "tcg_s390x.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
@@ -71,7 +72,7 @@
int wordsize, uintptr_t ra)
{
if (v % wordsize) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
}
@@ -730,7 +731,7 @@
/* Bits 32-55 must contain all 0. */
if (env->regs[0] & 0xffffff00u) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
str = get_address(env, r2);
@@ -767,7 +768,7 @@
/* Bits 32-47 of R0 must be zero. */
if (env->regs[0] & 0xffff0000u) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
str = get_address(env, r2);
@@ -846,7 +847,7 @@
S390Access srca, desta;
if ((f && s) || extract64(r0, 12, 4)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
r1 = wrap_address(env, r1 & TARGET_PAGE_MASK);
@@ -879,7 +880,7 @@
int i;
if (env->regs[0] & 0xffffff00ull) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
/*
@@ -911,8 +912,7 @@
int i;
if (a2 & 0x3) {
- /* we either came here by lam or lamy, which have different lengths */
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -932,7 +932,7 @@
int i;
if (a2 & 0x3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -1015,6 +1015,7 @@
uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
uint64_t src = get_address(env, r2);
uint8_t pad = env->regs[r2 + 1] >> 24;
+ CPUState *cs = env_cpu(env);
S390Access srca, desta;
uint32_t cc, cur_len;
@@ -1065,7 +1066,15 @@
env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen);
set_address_zero(env, r1, dest);
- /* TODO: Deliver interrupts. */
+ /*
+ * MVCL is interruptible. Return to the main loop if requested after
+ * writing back all state to registers. If no interrupt will get
+ * injected, we'll end up back in this handler and continue processing
+ * the remaining parts.
+ */
+ if (destlen && unlikely(cpu_loop_exit_requested(cs))) {
+ cpu_loop_exit_restore(cs, ra);
+ }
}
return cc;
}
@@ -1888,8 +1897,7 @@
return cc;
spec_exception:
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
- g_assert_not_reached();
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2)
@@ -1912,7 +1920,7 @@
uint32_t i;
if (src & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -1945,7 +1953,7 @@
uint32_t i;
if (src & 0x3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -1976,7 +1984,7 @@
uint32_t i;
if (dest & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -1996,7 +2004,7 @@
uint32_t i;
if (dest & 0x3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
for (i = r1;; i = (i + 1) % 16) {
@@ -2168,7 +2176,7 @@
if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) ||
psw_as == AS_HOME || psw_as == AS_ACCREG) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
l = wrap_length32(env, l);
@@ -2199,7 +2207,7 @@
if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) ||
psw_as == AS_HOME || psw_as == AS_ACCREG) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, ILEN_AUTO, ra);
+ s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
l = wrap_length32(env, l);
@@ -2226,7 +2234,7 @@
uint16_t entries, i, index = 0;
if (r2 & 0xff000) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
if (!(r2 & 0x800)) {
@@ -2252,9 +2260,9 @@
/* addresses are not wrapped in 24/31bit mode but table index is */
raddr = table + ((index + i) & 0x7ff) * sizeof(entry);
entry = cpu_ldq_real_ra(env, raddr, ra);
- if (!(entry & REGION_ENTRY_INV)) {
+ if (!(entry & REGION_ENTRY_I)) {
/* we are allowed to not store if already invalid */
- entry |= REGION_ENTRY_INV;
+ entry |= REGION_ENTRY_I;
cpu_stq_real_ra(env, raddr, entry, ra);
}
}
@@ -2279,17 +2287,17 @@
/* Compute the page table entry address */
pte_addr = (pto & SEGMENT_ENTRY_ORIGIN);
- pte_addr += (vaddr & VADDR_PX) >> 9;
+ pte_addr += VADDR_PAGE_TX(vaddr) * 8;
/* Mark the page table entry as invalid */
pte = cpu_ldq_real_ra(env, pte_addr, ra);
- pte |= PAGE_INVALID;
+ pte |= PAGE_ENTRY_I;
cpu_stq_real_ra(env, pte_addr, pte, ra);
/* XXX we exploit the fact that Linux passes the exact virtual
address here - it's not obliged to! */
if (m4 & 1) {
- if (vaddr & ~VADDR_PX) {
+ if (vaddr & ~VADDR_PAGE_TX_MASK) {
tlb_flush_page(cs, page);
/* XXX 31-bit hack */
tlb_flush_page(cs, page ^ 0x80000000);
@@ -2298,7 +2306,7 @@
tlb_flush(cs);
}
} else {
- if (vaddr & ~VADDR_PX) {
+ if (vaddr & ~VADDR_PAGE_TX_MASK) {
tlb_flush_page_all_cpus_synced(cs, page);
/* XXX 31-bit hack */
tlb_flush_page_all_cpus_synced(cs, page ^ 0x80000000);
@@ -2362,27 +2370,23 @@
/* load real address */
uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
{
- CPUState *cs = env_cpu(env);
- uint32_t cc = 0;
uint64_t asc = env->psw.mask & PSW_MASK_ASC;
- uint64_t ret;
- int old_exc, flags;
+ uint64_t ret, tec;
+ int flags, exc, cc;
/* XXX incomplete - has more corner cases */
if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, 2, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, GETPC());
}
- old_exc = cs->exception_index;
- if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
+ exc = mmu_translate(env, addr, 0, asc, &ret, &flags, &tec);
+ if (exc) {
cc = 3;
- }
- if (cs->exception_index == EXCP_PGM) {
- ret = env->int_pgm_code | 0x80000000;
+ ret = exc | 0x80000000;
} else {
+ cc = 0;
ret |= addr & ~TARGET_PAGE_MASK;
}
- cs->exception_index = old_exc;
env->cc_op = cc;
return ret;
@@ -2539,7 +2543,7 @@
__func__, dest, src, len);
if (!(env->psw.mask & PSW_MASK_DAT)) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
/* OAC (operand access control) for the first operand -> dest */
@@ -2570,14 +2574,14 @@
}
if (dest_a && dest_as == AS_HOME && (env->psw.mask & PSW_MASK_PSTATE)) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
if (!(env->cregs[0] & CR0_SECONDARY) &&
(dest_as == AS_SECONDARY || src_as == AS_SECONDARY)) {
- s390_program_interrupt(env, PGM_SPECIAL_OP, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
}
if (!psw_key_valid(env, dest_key) || !psw_key_valid(env, src_key)) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_PRIVILEGED, ra);
}
len = wrap_length32(env, len);
@@ -2591,7 +2595,7 @@
(env->psw.mask & PSW_MASK_PSTATE)) {
qemu_log_mask(LOG_UNIMP, "%s: AR-mode and PSTATE support missing\n",
__func__);
- s390_program_interrupt(env, PGM_ADDRESSING, 6, ra);
+ tcg_s390_program_interrupt(env, PGM_ADDRESSING, ra);
}
/* FIXME: Access using correct keys and AR-mode */
diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c
index 7530dcb..bfb457f 100644
--- a/target/s390x/misc_helper.c
+++ b/target/s390x/misc_helper.c
@@ -106,7 +106,7 @@
int r = sclp_service_call(env, r1, r2);
qemu_mutex_unlock_iothread();
if (r < 0) {
- s390_program_interrupt(env, -r, 4, GETPC());
+ tcg_s390_program_interrupt(env, -r, GETPC());
}
return r;
}
@@ -143,7 +143,7 @@
}
if (r) {
- s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
}
@@ -222,7 +222,7 @@
uint32_t val = r0;
if (val & 0xffff0000) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 2, GETPC());
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
env->todpr = val;
}
@@ -266,7 +266,7 @@
}
if ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK)) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
if ((r0 & STSI_R0_FC_MASK) == STSI_R0_FC_CURRENT) {
@@ -276,7 +276,7 @@
}
if (a0 & ~TARGET_PAGE_MASK) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
/* count the cpus and split them into configured and reserved ones */
@@ -509,7 +509,7 @@
LowCore *lowcore;
if (addr & 0x3) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
qemu_mutex_lock_iothread();
@@ -573,17 +573,8 @@
#ifndef CONFIG_USER_ONLY
void HELPER(per_check_exception)(CPUS390XState *env)
{
- uint32_t ilen;
-
if (env->per_perc_atmid) {
- /*
- * FIXME: ILEN_AUTO is most probably the right thing to use. ilen
- * always has to match the instruction referenced in the PSW. E.g.
- * if a PER interrupt is triggered via EXECUTE, we have to use ilen
- * of EXECUTE, while per_address contains the target of EXECUTE.
- */
- ilen = get_ilen(cpu_ldub_code(env, env->per_address));
- s390_program_interrupt(env, PGM_PER, ilen, GETPC());
+ tcg_s390_program_interrupt(env, PGM_PER, GETPC());
}
}
@@ -673,7 +664,7 @@
int i;
if (addr & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
prepare_stfl();
@@ -746,7 +737,7 @@
qemu_mutex_unlock_iothread();
/* css_do_sic() may actually return a PGM_xxx value to inject */
if (r) {
- s390_program_interrupt(env, -r, 4, GETPC());
+ tcg_s390_program_interrupt(env, -r, GETPC());
}
}
diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c
index 7e6b0d0..90b8133 100644
--- a/target/s390x/mmu_helper.c
+++ b/target/s390x/mmu_helper.c
@@ -28,37 +28,12 @@
#include "hw/hw.h"
#include "hw/s390x/storage-keys.h"
-/* #define DEBUG_S390 */
-/* #define DEBUG_S390_PTE */
-/* #define DEBUG_S390_STDOUT */
-
-#ifdef DEBUG_S390
-#ifdef DEBUG_S390_STDOUT
-#define DPRINTF(fmt, ...) \
- do { fprintf(stderr, fmt, ## __VA_ARGS__); \
- if (qemu_log_separate()) qemu_log(fmt, ##__VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
- do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
-#endif
-#else
-#define DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
-
-#ifdef DEBUG_S390_PTE
-#define PTE_DPRINTF DPRINTF
-#else
-#define PTE_DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
-
/* Fetch/store bits in the translation exception code: */
#define FS_READ 0x800
#define FS_WRITE 0x400
static void trigger_access_exception(CPUS390XState *env, uint32_t type,
- uint32_t ilen, uint64_t tec)
+ uint64_t tec)
{
S390CPU *cpu = env_archcpu(env);
@@ -69,48 +44,10 @@
if (type != PGM_ADDRESSING) {
stq_phys(cs->as, env->psa + offsetof(LowCore, trans_exc_code), tec);
}
- trigger_pgm_exception(env, type, ilen);
+ trigger_pgm_exception(env, type);
}
}
-static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
- uint64_t asc, int rw, bool exc)
-{
- uint64_t tec;
-
- tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | 4 | asc >> 46;
-
- DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec);
-
- if (!exc) {
- return;
- }
-
- trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, tec);
-}
-
-static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr,
- uint32_t type, uint64_t asc, int rw, bool exc)
-{
- int ilen = ILEN_AUTO;
- uint64_t tec;
-
- tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | asc >> 46;
-
- DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec);
-
- if (!exc) {
- return;
- }
-
- /* Code accesses have an undefined ilc. */
- if (rw == MMU_INST_FETCH) {
- ilen = 2;
- }
-
- trigger_access_exception(env, type, ilen, tec);
-}
-
/* check whether the address would be proteted by Low-Address Protection */
static bool is_low_address(uint64_t addr)
{
@@ -156,122 +93,40 @@
return raddr;
}
-/* Decode page table entry (normal 4KB page) */
-static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr,
- uint64_t asc, uint64_t pt_entry,
- target_ulong *raddr, int *flags, int rw, bool exc)
-{
- if (pt_entry & PAGE_INVALID) {
- DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, pt_entry);
- trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw, exc);
- return -1;
- }
- if (pt_entry & PAGE_RES0) {
- trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
- return -1;
- }
- if (pt_entry & PAGE_RO) {
- *flags &= ~PAGE_WRITE;
- }
-
- *raddr = pt_entry & ASCE_ORIGIN;
-
- PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, pt_entry);
-
- return 0;
-}
-
-/* Decode segment table entry */
-static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr,
- uint64_t asc, uint64_t st_entry,
- target_ulong *raddr, int *flags, int rw,
- bool exc)
+static inline bool read_table_entry(CPUS390XState *env, hwaddr gaddr,
+ uint64_t *entry)
{
CPUState *cs = env_cpu(env);
- uint64_t origin, offs, pt_entry;
- if (st_entry & SEGMENT_ENTRY_RO) {
- *flags &= ~PAGE_WRITE;
+ /*
+ * According to the PoP, these table addresses are "unpredictably real
+ * or absolute". Also, "it is unpredictable whether the address wraps
+ * or an addressing exception is recognized".
+ *
+ * We treat them as absolute addresses and don't wrap them.
+ */
+ if (unlikely(address_space_read(cs->as, gaddr, MEMTXATTRS_UNSPECIFIED,
+ (uint8_t *)entry, sizeof(*entry)) !=
+ MEMTX_OK)) {
+ return false;
}
-
- if ((st_entry & SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) {
- /* Decode EDAT1 segment frame absolute address (1MB page) */
- *raddr = (st_entry & 0xfffffffffff00000ULL) | (vaddr & 0xfffff);
- PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, st_entry);
- return 0;
- }
-
- /* Look up 4KB page entry */
- origin = st_entry & SEGMENT_ENTRY_ORIGIN;
- offs = (vaddr & VADDR_PX) >> 9;
- pt_entry = ldq_phys(cs->as, origin + offs);
- PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
- __func__, origin, offs, pt_entry);
- return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw, exc);
-}
-
-/* Decode region table entries */
-static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
- uint64_t asc, uint64_t entry, int level,
- target_ulong *raddr, int *flags, int rw,
- bool exc)
-{
- CPUState *cs = env_cpu(env);
- uint64_t origin, offs, new_entry;
- const int pchks[4] = {
- PGM_SEGMENT_TRANS, PGM_REG_THIRD_TRANS,
- PGM_REG_SEC_TRANS, PGM_REG_FIRST_TRANS
- };
-
- PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, entry);
-
- origin = entry & REGION_ENTRY_ORIGIN;
- offs = (vaddr >> (17 + 11 * level / 4)) & 0x3ff8;
-
- new_entry = ldq_phys(cs->as, origin + offs);
- PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
- __func__, origin, offs, new_entry);
-
- if ((new_entry & REGION_ENTRY_INV) != 0) {
- DPRINTF("%s: invalid region\n", __func__);
- trigger_page_fault(env, vaddr, pchks[level / 4], asc, rw, exc);
- return -1;
- }
-
- if ((new_entry & REGION_ENTRY_TYPE_MASK) != level) {
- trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
- return -1;
- }
-
- if (level == ASCE_TYPE_SEGMENT) {
- return mmu_translate_segment(env, vaddr, asc, new_entry, raddr, flags,
- rw, exc);
- }
-
- /* Check region table offset and length */
- offs = (vaddr >> (28 + 11 * (level - 4) / 4)) & 3;
- if (offs < ((new_entry & REGION_ENTRY_TF) >> 6)
- || offs > (new_entry & REGION_ENTRY_LENGTH)) {
- DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry);
- trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw, exc);
- return -1;
- }
-
- if ((env->cregs[0] & CR0_EDAT) && (new_entry & REGION_ENTRY_RO)) {
- *flags &= ~PAGE_WRITE;
- }
-
- /* yet another region */
- return mmu_translate_region(env, vaddr, asc, new_entry, level - 4,
- raddr, flags, rw, exc);
+ *entry = be64_to_cpu(*entry);
+ return true;
}
static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
uint64_t asc, uint64_t asce, target_ulong *raddr,
- int *flags, int rw, bool exc)
+ int *flags, int rw)
{
- int level;
- int r;
+ const bool edat1 = (env->cregs[0] & CR0_EDAT) &&
+ s390_has_feat(S390_FEAT_EDAT);
+ const bool edat2 = edat1 && s390_has_feat(S390_FEAT_EDAT_2);
+ const bool iep = (env->cregs[0] & CR0_IEP) &&
+ s390_has_feat(S390_FEAT_INSTRUCTION_EXEC_PROT);
+ const int asce_tl = asce & ASCE_TABLE_LENGTH;
+ const int asce_p = asce & ASCE_PRIVATE_SPACE;
+ hwaddr gaddr = asce & ASCE_ORIGIN;
+ uint64_t entry;
if (asce & ASCE_REAL_SPACE) {
/* direct mapping */
@@ -279,60 +134,158 @@
return 0;
}
- level = asce & ASCE_TYPE_MASK;
- switch (level) {
+ switch (asce & ASCE_TYPE_MASK) {
case ASCE_TYPE_REGION1:
- if ((vaddr >> 62) > (asce & ASCE_TABLE_LENGTH)) {
- trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw, exc);
- return -1;
+ if (VADDR_REGION1_TL(vaddr) > asce_tl) {
+ return PGM_REG_FIRST_TRANS;
}
+ gaddr += VADDR_REGION1_TX(vaddr) * 8;
break;
case ASCE_TYPE_REGION2:
- if (vaddr & 0xffe0000000000000ULL) {
- DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
- " 0xffe0000000000000ULL\n", __func__, vaddr);
- trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
- return -1;
+ if (VADDR_REGION1_TX(vaddr)) {
+ return PGM_ASCE_TYPE;
}
- if ((vaddr >> 51 & 3) > (asce & ASCE_TABLE_LENGTH)) {
- trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw, exc);
- return -1;
+ if (VADDR_REGION2_TL(vaddr) > asce_tl) {
+ return PGM_REG_SEC_TRANS;
}
+ gaddr += VADDR_REGION2_TX(vaddr) * 8;
break;
case ASCE_TYPE_REGION3:
- if (vaddr & 0xfffffc0000000000ULL) {
- DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
- " 0xfffffc0000000000ULL\n", __func__, vaddr);
- trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
- return -1;
+ if (VADDR_REGION1_TX(vaddr) || VADDR_REGION2_TX(vaddr)) {
+ return PGM_ASCE_TYPE;
}
- if ((vaddr >> 40 & 3) > (asce & ASCE_TABLE_LENGTH)) {
- trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw, exc);
- return -1;
+ if (VADDR_REGION3_TL(vaddr) > asce_tl) {
+ return PGM_REG_THIRD_TRANS;
}
+ gaddr += VADDR_REGION3_TX(vaddr) * 8;
break;
case ASCE_TYPE_SEGMENT:
- if (vaddr & 0xffffffff80000000ULL) {
- DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
- " 0xffffffff80000000ULL\n", __func__, vaddr);
- trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
- return -1;
+ if (VADDR_REGION1_TX(vaddr) || VADDR_REGION2_TX(vaddr) ||
+ VADDR_REGION3_TX(vaddr)) {
+ return PGM_ASCE_TYPE;
}
- if ((vaddr >> 29 & 3) > (asce & ASCE_TABLE_LENGTH)) {
- trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc);
- return -1;
+ if (VADDR_SEGMENT_TL(vaddr) > asce_tl) {
+ return PGM_SEGMENT_TRANS;
}
+ gaddr += VADDR_SEGMENT_TX(vaddr) * 8;
break;
}
- r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw,
- exc);
- if (!r && rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE)) {
- trigger_prot_fault(env, vaddr, asc, rw, exc);
- return -1;
+ switch (asce & ASCE_TYPE_MASK) {
+ case ASCE_TYPE_REGION1:
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & REGION_ENTRY_I) {
+ return PGM_REG_FIRST_TRANS;
+ }
+ if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION1) {
+ return PGM_TRANS_SPEC;
+ }
+ if (VADDR_REGION2_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 ||
+ VADDR_REGION2_TL(vaddr) > (entry & REGION_ENTRY_TL)) {
+ return PGM_REG_SEC_TRANS;
+ }
+ if (edat1 && (entry & REGION_ENTRY_P)) {
+ *flags &= ~PAGE_WRITE;
+ }
+ gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_REGION2_TX(vaddr) * 8;
+ /* fall through */
+ case ASCE_TYPE_REGION2:
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & REGION_ENTRY_I) {
+ return PGM_REG_SEC_TRANS;
+ }
+ if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION2) {
+ return PGM_TRANS_SPEC;
+ }
+ if (VADDR_REGION3_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 ||
+ VADDR_REGION3_TL(vaddr) > (entry & REGION_ENTRY_TL)) {
+ return PGM_REG_THIRD_TRANS;
+ }
+ if (edat1 && (entry & REGION_ENTRY_P)) {
+ *flags &= ~PAGE_WRITE;
+ }
+ gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_REGION3_TX(vaddr) * 8;
+ /* fall through */
+ case ASCE_TYPE_REGION3:
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & REGION_ENTRY_I) {
+ return PGM_REG_THIRD_TRANS;
+ }
+ if ((entry & REGION_ENTRY_TT) != REGION_ENTRY_TT_REGION3) {
+ return PGM_TRANS_SPEC;
+ }
+ if (edat2 && (entry & REGION3_ENTRY_CR) && asce_p) {
+ return PGM_TRANS_SPEC;
+ }
+ if (edat1 && (entry & REGION_ENTRY_P)) {
+ *flags &= ~PAGE_WRITE;
+ }
+ if (edat2 && (entry & REGION3_ENTRY_FC)) {
+ if (iep && (entry & REGION3_ENTRY_IEP)) {
+ *flags &= ~PAGE_EXEC;
+ }
+ *raddr = (entry & REGION3_ENTRY_RFAA) |
+ (vaddr & ~REGION3_ENTRY_RFAA);
+ return 0;
+ }
+ if (VADDR_SEGMENT_TL(vaddr) < (entry & REGION_ENTRY_TF) >> 6 ||
+ VADDR_SEGMENT_TL(vaddr) > (entry & REGION_ENTRY_TL)) {
+ return PGM_SEGMENT_TRANS;
+ }
+ gaddr = (entry & REGION_ENTRY_ORIGIN) + VADDR_SEGMENT_TX(vaddr) * 8;
+ /* fall through */
+ case ASCE_TYPE_SEGMENT:
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & SEGMENT_ENTRY_I) {
+ return PGM_SEGMENT_TRANS;
+ }
+ if ((entry & SEGMENT_ENTRY_TT) != SEGMENT_ENTRY_TT_SEGMENT) {
+ return PGM_TRANS_SPEC;
+ }
+ if ((entry & SEGMENT_ENTRY_CS) && asce_p) {
+ return PGM_TRANS_SPEC;
+ }
+ if (entry & SEGMENT_ENTRY_P) {
+ *flags &= ~PAGE_WRITE;
+ }
+ if (edat1 && (entry & SEGMENT_ENTRY_FC)) {
+ if (iep && (entry & SEGMENT_ENTRY_IEP)) {
+ *flags &= ~PAGE_EXEC;
+ }
+ *raddr = (entry & SEGMENT_ENTRY_SFAA) |
+ (vaddr & ~SEGMENT_ENTRY_SFAA);
+ return 0;
+ }
+ gaddr = (entry & SEGMENT_ENTRY_ORIGIN) + VADDR_PAGE_TX(vaddr) * 8;
+ break;
}
- return r;
+ if (!read_table_entry(env, gaddr, &entry)) {
+ return PGM_ADDRESSING;
+ }
+ if (entry & PAGE_ENTRY_I) {
+ return PGM_PAGE_TRANS;
+ }
+ if (entry & PAGE_ENTRY_0) {
+ return PGM_TRANS_SPEC;
+ }
+ if (entry & PAGE_ENTRY_P) {
+ *flags &= ~PAGE_WRITE;
+ }
+ if (iep && (entry & PAGE_ENTRY_IEP)) {
+ *flags &= ~PAGE_EXEC;
+ }
+
+ *raddr = entry & TARGET_PAGE_MASK;
+ return 0;
}
static void mmu_handle_skey(target_ulong addr, int rw, int *flags)
@@ -412,16 +365,18 @@
* @param raddr the translated address is stored to this pointer
* @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer
* @param exc true = inject a program check if a fault occurred
- * @return 0 if the translation was successful, -1 if a fault occurred
+ * @return 0 = success, != 0, the exception to raise
*/
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
- target_ulong *raddr, int *flags, bool exc)
+ target_ulong *raddr, int *flags, uint64_t *tec)
{
uint64_t asce;
int r;
-
+ *tec = (vaddr & TARGET_PAGE_MASK) | (asc >> 46) |
+ (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ);
*flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+
if (is_low_address(vaddr & TARGET_PAGE_MASK) && lowprot_enabled(env, asc)) {
/*
* If any part of this page is currently protected, make sure the
@@ -433,10 +388,9 @@
*/
*flags |= PAGE_WRITE_INV;
if (is_low_address(vaddr) && rw == MMU_DATA_STORE) {
- if (exc) {
- trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, 0);
- }
- return -EACCES;
+ /* LAP sets bit 56 */
+ *tec |= 0x80;
+ return PGM_PROTECTION;
}
}
@@ -449,15 +403,12 @@
switch (asc) {
case PSW_ASC_PRIMARY:
- PTE_DPRINTF("%s: asc=primary\n", __func__);
asce = env->cregs[1];
break;
case PSW_ASC_HOME:
- PTE_DPRINTF("%s: asc=home\n", __func__);
asce = env->cregs[13];
break;
case PSW_ASC_SECONDARY:
- PTE_DPRINTF("%s: asc=secondary\n", __func__);
asce = env->cregs[7];
break;
case PSW_ASC_ACCREG:
@@ -467,11 +418,25 @@
}
/* perform the DAT translation */
- r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags, rw, exc);
- if (r) {
+ r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags, rw);
+ if (unlikely(r)) {
return r;
}
+ /* check for DAT protection */
+ if (unlikely(rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE))) {
+ /* DAT sets bit 61 only */
+ *tec |= 0x4;
+ return PGM_PROTECTION;
+ }
+
+ /* check for Instruction-Execution-Protection */
+ if (unlikely(rw == MMU_INST_FETCH && !(*flags & PAGE_EXEC))) {
+ /* IEP sets bit 56 and 61 */
+ *tec |= 0x84;
+ return PGM_PROTECTION;
+ }
+
nodat:
/* Convert real address -> absolute address */
*raddr = mmu_real2abs(env, *raddr);
@@ -486,22 +451,22 @@
* the MEMOP interface.
*/
static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages,
- target_ulong *pages, bool is_write)
+ target_ulong *pages, bool is_write, uint64_t *tec)
{
uint64_t asc = cpu->env.psw.mask & PSW_MASK_ASC;
CPUS390XState *env = &cpu->env;
int ret, i, pflags;
for (i = 0; i < nr_pages; i++) {
- ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, true);
+ ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, tec);
if (ret) {
return ret;
}
if (!address_space_access_valid(&address_space_memory, pages[i],
TARGET_PAGE_SIZE, is_write,
MEMTXATTRS_UNSPECIFIED)) {
- trigger_access_exception(env, PGM_ADDRESSING, ILEN_AUTO, 0);
- return -EFAULT;
+ *tec = 0; /* unused */
+ return PGM_ADDRESSING;
}
addr += TARGET_PAGE_SIZE;
}
@@ -529,6 +494,7 @@
{
int currlen, nr_pages, i;
target_ulong *pages;
+ uint64_t tec;
int ret;
if (kvm_enabled()) {
@@ -542,8 +508,10 @@
+ 1;
pages = g_malloc(nr_pages * sizeof(*pages));
- ret = translate_pages(cpu, laddr, nr_pages, pages, is_write);
- if (ret == 0 && hostbuf != NULL) {
+ ret = translate_pages(cpu, laddr, nr_pages, pages, is_write, &tec);
+ if (ret) {
+ trigger_access_exception(&cpu->env, ret, tec);
+ } else if (hostbuf != NULL) {
/* Copy data by stepping through the area page by page */
for (i = 0; i < nr_pages; i++) {
currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE));
@@ -575,10 +543,10 @@
* @param rw 0 = read, 1 = write, 2 = code fetch
* @param addr the translated address is stored to this pointer
* @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer
- * @return 0 if the translation was successful, < 0 if a fault occurred
+ * @return 0 = success, != 0, the exception to raise
*/
int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
- target_ulong *addr, int *flags)
+ target_ulong *addr, int *flags, uint64_t *tec)
{
const bool lowprot_enabled = env->cregs[0] & CR0_LOWPROT;
@@ -587,8 +555,11 @@
/* see comment in mmu_translate() how this works */
*flags |= PAGE_WRITE_INV;
if (is_low_address(raddr) && rw == MMU_DATA_STORE) {
- trigger_access_exception(env, PGM_PROTECTION, ILEN_AUTO, 0);
- return -EACCES;
+ /* LAP sets bit 56 */
+ *tec = (raddr & TARGET_PAGE_MASK)
+ | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ)
+ | 0x80;
+ return PGM_PROTECTION;
}
}
diff --git a/target/s390x/tcg-stub.c b/target/s390x/tcg-stub.c
index 32adb72..d22c898 100644
--- a/target/s390x/tcg-stub.c
+++ b/target/s390x/tcg-stub.c
@@ -18,8 +18,8 @@
void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque)
{
}
-void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
- int ilen, uintptr_t ra)
+void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env,
+ uint32_t code, uintptr_t ra)
{
g_assert_not_reached();
}
diff --git a/target/s390x/tcg_s390x.h b/target/s390x/tcg_s390x.h
index 2813f9d..2f54ccb 100644
--- a/target/s390x/tcg_s390x.h
+++ b/target/s390x/tcg_s390x.h
@@ -14,8 +14,8 @@
#define TCG_S390X_H
void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque);
-void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
- int ilen, uintptr_t ra);
+void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env,
+ uint32_t code, uintptr_t ra);
void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc,
uintptr_t ra);
void QEMU_NORETURN tcg_s390_vector_exception(CPUS390XState *env, uint32_t vxc,
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index a3e43ff..151dfa9 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -6309,6 +6309,9 @@
/* Search for the insn in the table. */
insn = extract_insn(env, s, &f);
+ /* Emit insn_start now that we know the ILEN. */
+ tcg_gen_insn_start(s->base.pc_next, s->cc_op, s->ilen);
+
/* Not found means unimplemented/illegal opcode. */
if (insn == NULL) {
qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%02x%02x\n",
@@ -6463,9 +6466,6 @@
static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
{
- DisasContext *dc = container_of(dcbase, DisasContext, base);
-
- tcg_gen_insn_start(dc->base.pc_next, dc->cc_op);
}
static bool s390x_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
@@ -6473,6 +6473,14 @@
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
+ /*
+ * Emit an insn_start to accompany the breakpoint exception.
+ * The ILEN value is a dummy, since this does not result in
+ * an s390x exception, but an internal qemu exception which
+ * brings us back to interact with the gdbstub.
+ */
+ tcg_gen_insn_start(dc->base.pc_next, dc->cc_op, 2);
+
dc->base.is_jmp = DISAS_PC_STALE;
dc->do_debug = true;
/* The address covered by the breakpoint must be included in
@@ -6567,8 +6575,14 @@
target_ulong *data)
{
int cc_op = data[1];
+
env->psw.addr = data[0];
+
+ /* Update the CC opcode if it is not already up-to-date. */
if ((cc_op != CC_OP_DYNAMIC) && (cc_op != CC_OP_STATIC)) {
env->cc_op = cc_op;
}
+
+ /* Record ILEN. */
+ env->int_pgm_ilen = data[2];
}
diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index 9b3d8b0..0b33fb2 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -10,6 +10,33 @@
* See the COPYING file in the top-level directory.
*/
+/*
+ * How to add or update the tests:
+ * Contributor:
+ * 1. add empty files for new tables, if any, under tests/data/acpi
+ * 2. list any changed files in tests/bios-tables-test-allowed-diff.h
+ * 3. commit the above *before* making changes that affect the tables
+ * Maintainer:
+ * After 1-3 above tests will pass but ignore differences with the expected files.
+ * You will also notice that tests/bios-tables-test-allowed-diff.h lists
+ * a bunch of files. This is your hint that you need to do the below:
+ * 4. Run
+ * make check V=1
+ * this will produce a bunch of warnings about differences
+ * beween actual and expected ACPI tables. If you have IASL installed,
+ * they will also be disassembled so you can look at the disassembled
+ * output. If not - disassemble them yourself in any way you like.
+ * Look at the differences - make sure they make sense and match what the
+ * changes you are merging are supposed to do.
+ *
+ * 5. From build directory, run:
+ * $(SRC_PATH)/tests/data/acpi/rebuild-expected-aml.sh
+ * 6. Now commit any changes.
+ * 7. Before doing a pull request, make sure tests/bios-tables-test-allowed-diff.h
+ * is empty - this will ensure following changes to ACPI tables will
+ * be noticed.
+ */
+
#include "qemu/osdep.h"
#include <glib/gstdio.h>
#include "qemu-common.h"
@@ -334,7 +361,10 @@
g_assert(ret);
g_assert_no_error(error);
g_assert(exp_sdt.aml);
- g_assert(exp_sdt.aml_len);
+ if (!exp_sdt.aml_len) {
+ fprintf(stderr, "Warning! zero length expected file '%s'\n",
+ aml_file);
+ }
g_array_append_val(exp_tables, exp_sdt);
}
@@ -870,6 +900,53 @@
test_acpi_tcg_dimm_pxm(MACHINE_PC);
}
+static void test_acpi_virt_tcg_memhp(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .accel = "tcg",
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 256ULL * 1024 * 1024,
+ };
+
+ data.variant = ".memhp";
+ test_acpi_one(" -cpu cortex-a57"
+ " -m 256M,slots=3,maxmem=1G"
+ " -object memory-backend-ram,id=ram0,size=128M"
+ " -object memory-backend-ram,id=ram1,size=128M"
+ " -numa node,memdev=ram0 -numa node,memdev=ram1"
+ " -numa dist,src=0,dst=1,val=21",
+ &data);
+
+ free_test_data(&data);
+
+}
+
+static void test_acpi_virt_tcg_numamem(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .accel = "tcg",
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 128ULL * 1024 * 1024,
+ };
+
+ data.variant = ".numamem";
+ test_acpi_one(" -cpu cortex-a57"
+ " -object memory-backend-ram,id=ram0,size=128M"
+ " -numa node,memdev=ram0",
+ &data);
+
+ free_test_data(&data);
+
+}
+
static void test_acpi_virt_tcg(void)
{
test_data data = {
@@ -916,6 +993,8 @@
qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm);
} else if (strcmp(arch, "aarch64") == 0) {
qtest_add_func("acpi/virt", test_acpi_virt_tcg);
+ qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem);
+ qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp);
}
ret = g_test_run();
boot_sector_cleanup(disk);
diff --git a/tests/data/acpi/virt/APIC.memhp b/tests/data/acpi/virt/APIC.memhp
new file mode 100644
index 0000000..797dfde
--- /dev/null
+++ b/tests/data/acpi/virt/APIC.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/APIC.numamem b/tests/data/acpi/virt/APIC.numamem
new file mode 100644
index 0000000..797dfde
--- /dev/null
+++ b/tests/data/acpi/virt/APIC.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT
index 20e85c7..bce76e3 100644
--- a/tests/data/acpi/virt/DSDT
+++ b/tests/data/acpi/virt/DSDT
Binary files differ
diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp
new file mode 100644
index 0000000..b4b153f
--- /dev/null
+++ b/tests/data/acpi/virt/DSDT.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem
new file mode 100644
index 0000000..bce76e3
--- /dev/null
+++ b/tests/data/acpi/virt/DSDT.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/FACP.memhp b/tests/data/acpi/virt/FACP.memhp
new file mode 100644
index 0000000..27de99f
--- /dev/null
+++ b/tests/data/acpi/virt/FACP.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/FACP.numamem b/tests/data/acpi/virt/FACP.numamem
new file mode 100644
index 0000000..27de99f
--- /dev/null
+++ b/tests/data/acpi/virt/FACP.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/GTDT.memhp b/tests/data/acpi/virt/GTDT.memhp
new file mode 100644
index 0000000..10107a6
--- /dev/null
+++ b/tests/data/acpi/virt/GTDT.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/GTDT.numamem b/tests/data/acpi/virt/GTDT.numamem
new file mode 100644
index 0000000..10107a6
--- /dev/null
+++ b/tests/data/acpi/virt/GTDT.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/MCFG.memhp b/tests/data/acpi/virt/MCFG.memhp
new file mode 100644
index 0000000..e8987e1
--- /dev/null
+++ b/tests/data/acpi/virt/MCFG.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/MCFG.numamem b/tests/data/acpi/virt/MCFG.numamem
new file mode 100644
index 0000000..e8987e1
--- /dev/null
+++ b/tests/data/acpi/virt/MCFG.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/SLIT.memhp b/tests/data/acpi/virt/SLIT.memhp
new file mode 100644
index 0000000..74ec3b4
--- /dev/null
+++ b/tests/data/acpi/virt/SLIT.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/SPCR.memhp b/tests/data/acpi/virt/SPCR.memhp
new file mode 100644
index 0000000..377271a
--- /dev/null
+++ b/tests/data/acpi/virt/SPCR.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/SPCR.numamem b/tests/data/acpi/virt/SPCR.numamem
new file mode 100644
index 0000000..377271a
--- /dev/null
+++ b/tests/data/acpi/virt/SPCR.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/SRAT.memhp b/tests/data/acpi/virt/SRAT.memhp
new file mode 100644
index 0000000..1b57db2
--- /dev/null
+++ b/tests/data/acpi/virt/SRAT.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/SRAT.numamem b/tests/data/acpi/virt/SRAT.numamem
new file mode 100644
index 0000000..9526e5a
--- /dev/null
+++ b/tests/data/acpi/virt/SRAT.numamem
Binary files differ
diff --git a/tests/iothread.c b/tests/iothread.c
index 777d9ee..13c9fdc 100644
--- a/tests/iothread.c
+++ b/tests/iothread.c
@@ -55,10 +55,16 @@
return NULL;
}
+static void iothread_stop_bh(void *opaque)
+{
+ IOThread *iothread = opaque;
+
+ iothread->stopping = true;
+}
+
void iothread_join(IOThread *iothread)
{
- iothread->stopping = true;
- aio_notify(iothread->ctx);
+ aio_bh_schedule_oneshot(iothread->ctx, iothread_stop_bh, iothread);
qemu_thread_join(&iothread->thread);
qemu_cond_destroy(&iothread->init_done_cond);
qemu_mutex_destroy(&iothread->init_done_lock);
diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c
index 5b20e91..e16c30c 100644
--- a/tests/ptimer-test.c
+++ b/tests/ptimer-test.c
@@ -67,12 +67,13 @@
static void check_set_count(gconstpointer arg)
{
const uint8_t *policy = arg;
- QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
- ptimer_state *ptimer = ptimer_init(bh, *policy);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 1000);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 1000);
g_assert_false(triggered);
ptimer_free(ptimer);
@@ -81,17 +82,20 @@
static void check_set_limit(gconstpointer arg)
{
const uint8_t *policy = arg;
- QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
- ptimer_state *ptimer = ptimer_init(bh, *policy);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_limit(ptimer, 1000, 0);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
g_assert_cmpuint(ptimer_get_limit(ptimer), ==, 1000);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_limit(ptimer, 2000, 1);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 2000);
g_assert_cmpuint(ptimer_get_limit(ptimer), ==, 2000);
g_assert_false(triggered);
@@ -101,22 +105,25 @@
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);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 2000000);
ptimer_set_count(ptimer, 10);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 * 2 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_stop(ptimer);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
g_assert_false(triggered);
@@ -126,7 +133,9 @@
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 * 7 + 1);
@@ -157,28 +166,36 @@
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 10);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(20000000 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_limit(ptimer, 9, 1);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(20000000 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 20);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 * 19 + 1);
@@ -190,7 +207,9 @@
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
g_assert_true(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_stop(ptimer);
+ ptimer_transaction_commit(ptimer);
triggered = false;
@@ -204,8 +223,7 @@
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);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *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);
@@ -214,9 +232,11 @@
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 2000000);
ptimer_set_limit(ptimer, 10, 1);
ptimer_run(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10);
g_assert_false(triggered);
@@ -245,7 +265,9 @@
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 20);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 20);
g_assert_false(triggered);
@@ -268,7 +290,9 @@
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 3);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
g_assert_false(triggered);
@@ -284,7 +308,9 @@
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
g_assert_true(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_stop(ptimer);
+ ptimer_transaction_commit(ptimer);
triggered = false;
qemu_clock_step(2000000);
@@ -293,8 +319,10 @@
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 3);
ptimer_run(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 * 3 + 1);
@@ -310,7 +338,9 @@
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
no_immediate_reload ? 0 : 10);
@@ -348,7 +378,9 @@
(no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0));
g_assert_true(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_stop(ptimer);
+ ptimer_transaction_commit(ptimer);
triggered = false;
@@ -358,8 +390,13 @@
(no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0));
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_run(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
+
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 + 1);
@@ -372,23 +409,26 @@
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);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD);
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 2000000);
ptimer_set_limit(ptimer, 10, 1);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 * 9 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_run(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
g_assert_false(triggered);
@@ -403,7 +443,9 @@
qemu_clock_step(2000000 * 9);
+ ptimer_transaction_begin(ptimer);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
(no_round_down ? 1 : 0) + (wrap_policy ? 1 : 0));
@@ -419,22 +461,25 @@
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);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 2000000);
ptimer_set_limit(ptimer, 8, 1);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 * 4 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 4000000);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
qemu_clock_step(4000000 * 2 + 1);
@@ -452,22 +497,25 @@
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);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_freq(ptimer, 500);
ptimer_set_limit(ptimer, 8, 1);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 * 4 + 1);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
g_assert_false(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_set_freq(ptimer, 250);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
qemu_clock_step(2000000 * 4 + 1);
@@ -485,13 +533,14 @@
static void check_run_with_period_0(gconstpointer arg)
{
const uint8_t *policy = arg;
- QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
- ptimer_state *ptimer = ptimer_init(bh, *policy);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 99);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(10 * NANOSECONDS_PER_SECOND);
@@ -503,8 +552,7 @@
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);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *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);
@@ -513,9 +561,11 @@
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 2000000);
ptimer_set_limit(ptimer, 99, 0);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
no_immediate_reload ? 0 : 99);
@@ -541,8 +591,10 @@
g_assert_false(triggered);
}
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 99);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
}
qemu_clock_step(2000000 + 1);
@@ -562,8 +614,10 @@
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 0);
ptimer_run(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
no_immediate_reload ? 0 : 99);
@@ -602,23 +656,26 @@
wrap_policy ? 0 : (no_round_down ? 99 : 98));
g_assert_true(triggered);
+ ptimer_transaction_begin(ptimer);
ptimer_stop(ptimer);
+ ptimer_transaction_commit(ptimer);
ptimer_free(ptimer);
}
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);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER);
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 2000000);
ptimer_run(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
@@ -642,8 +699,10 @@
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_count(ptimer, 10);
ptimer_run(ptimer, 0);
+ ptimer_transaction_commit(ptimer);
qemu_clock_step(2000000 * 10 + 1);
@@ -662,22 +721,25 @@
g_assert_false(triggered);
}
+ ptimer_transaction_begin(ptimer);
ptimer_stop(ptimer);
+ ptimer_transaction_commit(ptimer);
ptimer_free(ptimer);
}
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);
+ ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy);
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
triggered = false;
+ ptimer_transaction_begin(ptimer);
ptimer_set_period(ptimer, 2000000);
ptimer_run(ptimer, 1);
+ ptimer_transaction_commit(ptimer);
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026
index ffb18ab..3430029 100755
--- a/tests/qemu-iotests/026
+++ b/tests/qemu-iotests/026
@@ -107,7 +107,7 @@
$QEMU_IO -c "read $vmstate 0 128k " "$BLKDBG_TEST_IMG" | _filter_qemu_io
fi
-_check_test_img 2>&1 | grep -v "refcount=1 reference=0"
+_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
done
done
@@ -152,7 +152,7 @@
echo "Event: $event; errno: $errno; imm: $imm; once: $once; write $vmstate"
$QEMU_IO -c "write $vmstate 0 64M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
-_check_test_img 2>&1 | grep -v "refcount=1 reference=0"
+_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
done
done
@@ -191,7 +191,7 @@
echo "Event: $event; errno: $errno; imm: $imm; once: $once"
$QEMU_IO -c "write -b 0 64k" "$BLKDBG_TEST_IMG" | _filter_qemu_io
-_check_test_img 2>&1 | grep -v "refcount=1 reference=0"
+_check_test_img_ignore_leaks 2>&1 | grep -v "refcount=1 reference=0"
done
done
diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out
index fb89b84..ff0817b 100644
--- a/tests/qemu-iotests/026.out
+++ b/tests/qemu-iotests/026.out
@@ -17,18 +17,14 @@
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: on; write
@@ -45,18 +41,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_load; errno: 5; imm: off; once: on; write
@@ -137,18 +129,14 @@
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 5; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: on; write
@@ -165,18 +153,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc_write; errno: 5; imm: off; once: on; write
@@ -200,9 +184,7 @@
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc_write; errno: 28; imm: off; once: on; write
@@ -226,9 +208,7 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: write_aio; errno: 5; imm: off; once: on; write
@@ -480,18 +460,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-55 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-251 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write; errno: 28; imm: off; once: on; write
@@ -532,18 +508,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write
@@ -560,18 +532,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write
@@ -588,18 +556,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
=== L1 growth tests ===
@@ -658,9 +622,7 @@
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-96 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_grow_activate_table; errno: 28; imm: off; once: on
@@ -672,9 +634,7 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-96 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
=== Avoid cluster leaks after temporary failure ===
diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache
index 6dda95d..495d013 100644
--- a/tests/qemu-iotests/026.out.nocache
+++ b/tests/qemu-iotests/026.out.nocache
@@ -17,18 +17,14 @@
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 5; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: on; write
@@ -45,18 +41,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_update; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_load; errno: 5; imm: off; once: on; write
@@ -140,9 +132,7 @@
qemu-io: Failed to flush the refcount block cache: Input/output error
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 5; imm: off; once: off; write -b
@@ -150,9 +140,7 @@
qemu-io: Failed to flush the refcount block cache: Input/output error
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: on; write
@@ -172,9 +160,7 @@
qemu-io: Failed to flush the refcount block cache: No space left on device
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_update; errno: 28; imm: off; once: off; write -b
@@ -182,9 +168,7 @@
qemu-io: Failed to flush the refcount block cache: No space left on device
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-
-127 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc_write; errno: 5; imm: off; once: on; write
@@ -208,9 +192,7 @@
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l2_alloc_write; errno: 28; imm: off; once: on; write
@@ -234,9 +216,7 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-1 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: write_aio; errno: 5; imm: off; once: on; write
@@ -488,18 +468,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-55 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_hookup; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-251 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write; errno: 28; imm: off; once: on; write
@@ -540,18 +516,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_blocks; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_table; errno: 28; imm: off; once: on; write
@@ -568,18 +540,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_write_table; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_switch_table; errno: 28; imm: off; once: on; write
@@ -596,18 +564,14 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-10 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: refblock_alloc_switch_table; errno: 28; imm: off; once: off; write -b
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-23 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
=== L1 growth tests ===
@@ -666,9 +630,7 @@
qemu-io: Failed to flush the L2 table cache: Input/output error
qemu-io: Failed to flush the refcount block cache: Input/output error
write failed: Input/output error
-
-96 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
Event: l1_grow_activate_table; errno: 28; imm: off; once: on
@@ -680,9 +642,7 @@
qemu-io: Failed to flush the L2 table cache: No space left on device
qemu-io: Failed to flush the refcount block cache: No space left on device
write failed: No space left on device
-
-96 leaked clusters were found on the image.
-This means waste of disk space, but no harm to data.
+No errors were found on the image.
=== Avoid cluster leaks after temporary failure ===
diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028
index 71301ec..bba1ee5 100755
--- a/tests/qemu-iotests/028
+++ b/tests/qemu-iotests/028
@@ -119,9 +119,14 @@
# Silence output since it contains the disk image path and QEMU's readline
# character echoing makes it very hard to filter the output. Plus, there
# is no telling how many times the command will repeat before succeeding.
-_send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)" >/dev/null
-_send_qemu_cmd $h "" "Formatting" | _filter_img_create
-qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs" >/dev/null
+# (Note that creating the image results in a "Formatting..." message over
+# stdout, which is the same channel the monitor uses. We cannot reliably
+# wait for it because the monitor output may interact with it in such a
+# way that _timed_wait_for cannot read it. However, once the block job is
+# done, we know that the "Formatting..." message must have appeared
+# already, so the output is still deterministic.)
+silent=y _send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)"
+silent=y qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs"
_send_qemu_cmd $h "info block-jobs" "No active jobs"
_send_qemu_cmd $h 'quit' ""
diff --git a/tests/qemu-iotests/028.out b/tests/qemu-iotests/028.out
index 7d54aeb..37aed84 100644
--- a/tests/qemu-iotests/028.out
+++ b/tests/qemu-iotests/028.out
@@ -468,7 +468,6 @@
block-backup
-Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
(qemu) info block-jobs
No active jobs
=== IO: pattern 195
diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056
index 98c55d8..f39287c 100755
--- a/tests/qemu-iotests/056
+++ b/tests/qemu-iotests/056
@@ -133,6 +133,7 @@
self.vm = iotests.VM()
self.test_img = img_create('test')
self.dest_img = img_create('dest')
+ self.dest_img2 = img_create('dest2')
self.ref_img = img_create('ref')
self.vm.add_drive(self.test_img)
self.vm.launch()
@@ -141,6 +142,7 @@
self.vm.shutdown()
try_remove(self.test_img)
try_remove(self.dest_img)
+ try_remove(self.dest_img2)
try_remove(self.ref_img)
def hmp_io_writes(self, drive, patterns):
@@ -253,9 +255,9 @@
res = self.vm.qmp('query-block-jobs')
self.assert_qmp(res, 'return[0]/status', 'concluded')
# Leave zombie job un-dismissed, observe a failure:
- res = self.qmp_backup_and_wait(serror="Node 'drive0' is busy: block device is in use by block job: backup",
+ res = self.qmp_backup_and_wait(serror="Job ID 'drive0' already in use",
device='drive0', format=iotests.imgfmt,
- sync='full', target=self.dest_img,
+ sync='full', target=self.dest_img2,
auto_dismiss=False)
self.assertEqual(res, False)
# OK, dismiss the zombie.
@@ -265,7 +267,7 @@
self.assert_qmp(res, 'return', [])
# Ensure it's really gone.
self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
- sync='full', target=self.dest_img,
+ sync='full', target=self.dest_img2,
auto_dismiss=False)
def dismissal_failure(self, dismissal_opt):
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index ca40ba3..d3e851e 100755
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -105,7 +105,7 @@
# Create a base image with a distinctive patterning
drive0 = self.add_node('drive0')
self.img_create(drive0['file'], drive0['fmt'])
- self.vm.add_drive(drive0['file'])
+ self.vm.add_drive(drive0['file'], opts='node-name=node0')
self.write_default_pattern(drive0['file'])
self.vm.launch()
@@ -348,12 +348,14 @@
('0xfe', '16M', '256k'),
('0x64', '32736k', '64k')))
# Check the dirty bitmap stats
- result = self.vm.qmp('query-block')
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/name', 'bitmap0')
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/count', 458752)
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/granularity', 65536)
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/status', 'active')
- self.assert_qmp(result, 'return[0]/dirty-bitmaps[0]/persistent', False)
+ self.assertTrue(self.vm.check_bitmap_status(
+ 'node0', bitmap0.name, {
+ 'name': 'bitmap0',
+ 'count': 458752,
+ 'granularity': 65536,
+ 'status': 'active',
+ 'persistent': False
+ }))
# Prepare a cluster_size=128k backup target without a backing file.
(target, _) = bitmap0.new_target()
@@ -670,9 +672,8 @@
"""
drive0 = self.drives[0]
- # NB: The blkdebug script here looks for a "flush, read, read" pattern.
- # The flush occurs in hmp_io_writes, the first read in device_add, and
- # the last read during the block job.
+ # NB: The blkdebug script here looks for a "flush, read" pattern.
+ # The flush occurs in hmp_io_writes, and the read during the block job.
result = self.vm.qmp('blockdev-add',
node_name=drive0['id'],
driver=drive0['fmt'],
@@ -686,15 +687,11 @@
'event': 'flush_to_disk',
'state': 1,
'new_state': 2
- },{
- 'event': 'read_aio',
- 'state': 2,
- 'new_state': 3
}],
'inject-error': [{
'event': 'read_aio',
'errno': 5,
- 'state': 3,
+ 'state': 2,
'immediately': False,
'once': True
}],
@@ -708,23 +705,15 @@
('0xfe', '16M', '256k'),
('0x64', '32736k', '64k')))
- # For the purposes of query-block visibility of bitmaps, add a drive
- # frontend after we've written data; otherwise we can't use hmp-io
- result = self.vm.qmp("device_add",
- id="device0",
- drive=drive0['id'],
- driver="virtio-blk")
- self.assert_qmp(result, 'return', {})
-
# Bitmap Status Check
- query = self.vm.qmp('query-block')
- ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
- if bmap.get('name') == bitmap.name][0]
- self.assert_qmp(ret, 'count', 458752)
- self.assert_qmp(ret, 'granularity', 65536)
- self.assert_qmp(ret, 'status', 'active')
- self.assert_qmp(ret, 'busy', False)
- self.assert_qmp(ret, 'recording', True)
+ self.assertTrue(self.vm.check_bitmap_status(
+ drive0['id'], bitmap.name, {
+ 'count': 458752,
+ 'granularity': 65536,
+ 'status': 'active',
+ 'busy': False,
+ 'recording': True
+ }))
# Start backup
parent, _ = bitmap.last_target()
@@ -748,14 +737,14 @@
'operation': 'read'})
# Bitmap Status Check
- query = self.vm.qmp('query-block')
- ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
- if bmap.get('name') == bitmap.name][0]
- self.assert_qmp(ret, 'count', 458752)
- self.assert_qmp(ret, 'granularity', 65536)
- self.assert_qmp(ret, 'status', 'frozen')
- self.assert_qmp(ret, 'busy', True)
- self.assert_qmp(ret, 'recording', True)
+ self.assertTrue(self.vm.check_bitmap_status(
+ drive0['id'], bitmap.name, {
+ 'count': 458752,
+ 'granularity': 65536,
+ 'status': 'frozen',
+ 'busy': True,
+ 'recording': True
+ }))
# Resume and check incremental backup for consistency
res = self.vm.qmp('block-job-resume', device=bitmap.drive['id'])
@@ -763,14 +752,14 @@
self.wait_qmp_backup(bitmap.drive['id'])
# Bitmap Status Check
- query = self.vm.qmp('query-block')
- ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
- if bmap.get('name') == bitmap.name][0]
- self.assert_qmp(ret, 'count', 0)
- self.assert_qmp(ret, 'granularity', 65536)
- self.assert_qmp(ret, 'status', 'active')
- self.assert_qmp(ret, 'busy', False)
- self.assert_qmp(ret, 'recording', True)
+ self.assertTrue(self.vm.check_bitmap_status(
+ drive0['id'], bitmap.name, {
+ 'count': 0,
+ 'granularity': 65536,
+ 'status': 'active',
+ 'busy': False,
+ 'recording': True
+ }))
# Finalize / Cleanup
self.make_reference_backup(bitmap)
diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125
index dc4b8f5..4e31aa4 100755
--- a/tests/qemu-iotests/125
+++ b/tests/qemu-iotests/125
@@ -34,8 +34,7 @@
get_image_size_on_host()
{
- $QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "disk size" \
- | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/'
+ echo $(($(stat -c '%b * %B' "$TEST_IMG_FILE")))
}
# get standard environment and filters
@@ -49,6 +48,46 @@
TEST_IMG_FILE=$TEST_IMG
fi
+# Test whether we are running on a broken XFS version. There is this
+# bug:
+
+# $ rm -f foo
+# $ touch foo
+# $ block_size=4096 # Your FS's block size
+# $ fallocate -o $((block_size / 2)) -l $block_size foo
+# $ LANG=C xfs_bmap foo | grep hole
+# 1: [8..15]: hole
+#
+# The problem is that the XFS driver rounds down the offset and
+# rounds up the length to the block size, but independently. As
+# such, it only allocates the first block in the example above,
+# even though it should allocate the first two blocks (because our
+# request is to fallocate something that touches both the first
+# two blocks).
+#
+# This means that when you then write to the beginning of the
+# second block, the disk usage of the first two blocks grows.
+#
+# That is precisely what fallocate() promises, though: That when you
+# write to an area that you have fallocated, no new blocks will have
+# to be allocated.
+
+touch "$TEST_IMG_FILE"
+# Assuming there is no FS with a block size greater than 64k
+fallocate -o 65535 -l 2 "$TEST_IMG_FILE"
+len0=$(get_image_size_on_host)
+
+# Write to something that in theory we have just fallocated
+# (Thus, the on-disk size should not increase)
+poke_file "$TEST_IMG_FILE" 65536 42
+len1=$(get_image_size_on_host)
+
+if [ $len1 -gt $len0 ]; then
+ _notrun "the test filesystem's fallocate() is broken"
+fi
+
+rm -f "$TEST_IMG_FILE"
+
# Generally, we create some image with or without existing preallocation and
# then resize it. Then we write some data into the image and verify that its
# size does not change if we have used preallocation.
@@ -111,7 +150,7 @@
if [ $file_length_2 -gt $file_length_1 ]; then
echo "ERROR (grow): Image length has grown from $file_length_1 to $file_length_2"
fi
- if [ $create_mode != metadata ]; then
+ if [ $growth_mode != metadata ]; then
# The host size should not have grown either
if [ $host_size_2 -gt $host_size_1 ]; then
echo "ERROR (grow): Host size has grown from $host_size_1 to $host_size_2"
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
index dbd3bde..e3b5782 100644
--- a/tests/qemu-iotests/141.out
+++ b/tests/qemu-iotests/141.out
@@ -10,7 +10,7 @@
Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
-{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}}
diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149
index 4f363f2..8ab42e9 100755
--- a/tests/qemu-iotests/149
+++ b/tests/qemu-iotests/149
@@ -153,7 +153,7 @@
(password, slot) = config.first_password()
- args = ["luksFormat"]
+ args = ["luksFormat", "--type", "luks1"]
cipher = config.cipher + "-" + config.mode + "-" + config.ivgen
if config.ivgen_hash is not None:
cipher = cipher + ":" + config.ivgen_hash
diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out
index 1407ce6..6877ab6 100644
--- a/tests/qemu-iotests/149.out
+++ b/tests/qemu-iotests/149.out
@@ -2,7 +2,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1
# Write test pattern 0xa7
@@ -122,7 +122,7 @@
# Create image
truncate TEST_DIR/luks-twofish-256-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher twofish-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1
# Write test pattern 0xa7
@@ -242,7 +242,7 @@
# Create image
truncate TEST_DIR/luks-serpent-256-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1
# Write test pattern 0xa7
@@ -362,7 +362,7 @@
# Create image
truncate TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher cast5-cbc-plain64 --key-size 128 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher cast5-cbc-plain64 --key-size 128 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1
# Write test pattern 0xa7
@@ -483,7 +483,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-cbc-plain-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1
# Write test pattern 0xa7
@@ -603,7 +603,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-cbc-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1
# Write test pattern 0xa7
@@ -723,7 +723,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1
# Write test pattern 0xa7
@@ -843,7 +843,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-essiv:sha256 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-essiv:sha256 --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1
# Write test pattern 0xa7
@@ -963,7 +963,7 @@
# Create image
truncate TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1
# Write test pattern 0xa7
@@ -1083,7 +1083,7 @@
# Create image
truncate TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1
# Write test pattern 0xa7
@@ -1203,7 +1203,7 @@
# Create image
truncate TEST_DIR/luks-twofish-128-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher twofish-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1
# Write test pattern 0xa7
@@ -1324,7 +1324,7 @@
# Create image
truncate TEST_DIR/luks-serpent-128-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1
# Write test pattern 0xa7
@@ -1444,7 +1444,7 @@
# Create image
truncate TEST_DIR/luks-serpent-192-xts-plain64-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher serpent-xts-plain64 --key-size 384 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1
# Write test pattern 0xa7
@@ -1566,7 +1566,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha224.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha224 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha224.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha224 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha224.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224
# Write test pattern 0xa7
@@ -1686,7 +1686,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha256.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha256 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha256.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha256 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha256.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256
# Write test pattern 0xa7
@@ -1806,7 +1806,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha384.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha384 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha384.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha384 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha384.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384
# Write test pattern 0xa7
@@ -1926,7 +1926,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-sha512.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sha512 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha512.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha512 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-sha512.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512
# Write test pattern 0xa7
@@ -2046,7 +2046,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash ripemd160 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash ripemd160 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160
# Write test pattern 0xa7
@@ -2166,7 +2166,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 3 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 3 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3
# Write test pattern 0xa7
@@ -2226,7 +2226,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-xts-plain --key-size 512 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img
# Add password slot 1
sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img --key-slot 1 --key-file - --iter-time 10 TEST_DIR/passwd.txt
# Add password slot 2
@@ -2360,7 +2360,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1
# Write test pattern 0xa7
@@ -2480,7 +2480,7 @@
# Create image
truncate TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img --size 4194304MB
# Format image
-sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img
+sudo cryptsetup -q -v luksFormat --type luks1 --cipher aes-cbc-plain64:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - --iter-time 10 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1
# Write test pattern 0xa7
diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162
index 2d719af..c0053ed9 100755
--- a/tests/qemu-iotests/162
+++ b/tests/qemu-iotests/162
@@ -46,7 +46,7 @@
# NBD expects all of its arguments to be strings
# So this should not crash
-$QEMU_IMG info 'json:{"driver": "nbd", "host": 42}'
+$QEMU_IMG info 'json:{"driver": "nbd", "host": -1}'
# And this should not treat @port as if it had not been specified
# (We need to set up a server here, because the error message for "Connection
diff --git a/tests/qemu-iotests/162.out b/tests/qemu-iotests/162.out
index 3c5be2c..5a00d36 100644
--- a/tests/qemu-iotests/162.out
+++ b/tests/qemu-iotests/162.out
@@ -1,7 +1,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": -1}': address resolution failed for -1:10809: Name or service not known
image: nbd://localhost:PORT
image: nbd+unix://?socket=42
diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out
index 3dd3ca5..9c09ee3 100644
--- a/tests/qemu-iotests/227.out
+++ b/tests/qemu-iotests/227.out
@@ -15,6 +15,8 @@
{
"device": "virtio0",
"stats": {
+ "unmap_operations": 0,
+ "unmap_merged": 0,
"flush_total_time_ns": 0,
"wr_highest_offset": 0,
"wr_total_time_ns": 0,
@@ -24,13 +26,17 @@
"wr_bytes": 0,
"timed_stats": [
],
+ "failed_unmap_operations": 0,
"failed_flush_operations": 0,
"account_invalid": true,
"rd_total_time_ns": 0,
+ "invalid_unmap_operations": 0,
"flush_operations": 0,
"wr_operations": 0,
+ "unmap_bytes": 0,
"rd_merged": 0,
"rd_bytes": 0,
+ "unmap_total_time_ns": 0,
"invalid_flush_operations": 0,
"account_failed": true,
"rd_operations": 0,
@@ -74,6 +80,8 @@
{
"device": "none0",
"stats": {
+ "unmap_operations": 0,
+ "unmap_merged": 0,
"flush_total_time_ns": 0,
"wr_highest_offset": 0,
"wr_total_time_ns": 0,
@@ -83,13 +91,17 @@
"wr_bytes": 0,
"timed_stats": [
],
+ "failed_unmap_operations": 0,
"failed_flush_operations": 0,
"account_invalid": true,
"rd_total_time_ns": 0,
+ "invalid_unmap_operations": 0,
"flush_operations": 0,
"wr_operations": 0,
+ "unmap_bytes": 0,
"rd_merged": 0,
"rd_bytes": 0,
+ "unmap_total_time_ns": 0,
"invalid_flush_operations": 0,
"account_failed": true,
"rd_operations": 0,
@@ -163,6 +175,8 @@
{
"device": "",
"stats": {
+ "unmap_operations": 0,
+ "unmap_merged": 0,
"flush_total_time_ns": 0,
"wr_highest_offset": 0,
"wr_total_time_ns": 0,
@@ -172,13 +186,17 @@
"wr_bytes": 0,
"timed_stats": [
],
+ "failed_unmap_operations": 0,
"failed_flush_operations": 0,
"account_invalid": false,
"rd_total_time_ns": 0,
+ "invalid_unmap_operations": 0,
"flush_operations": 0,
"wr_operations": 0,
+ "unmap_bytes": 0,
"rd_merged": 0,
"rd_bytes": 0,
+ "unmap_total_time_ns": 0,
"invalid_flush_operations": 0,
"account_failed": false,
"rd_operations": 0,
diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
index 4a636d8..a982825 100755
--- a/tests/qemu-iotests/257
+++ b/tests/qemu-iotests/257
@@ -148,11 +148,6 @@
self.fmt = None
self.size = None
self.node = None
- self.device = None
-
- @property
- def name(self):
- return self.node or self.device
def img_create(self, fmt, size):
self.fmt = fmt
@@ -188,25 +183,6 @@
self.size = size
self.node = name
-def query_bitmaps(vm):
- res = vm.qmp("query-block")
- return {"bitmaps": {device['device'] or device['qdev']:
- device.get('dirty-bitmaps', []) for
- device in res['return']}}
-
-def get_bitmap(bitmaps, drivename, name, recording=None):
- """
- get a specific bitmap from the object returned by query_bitmaps.
- :param recording: If specified, filter results by the specified value.
- """
- for bitmap in bitmaps['bitmaps'][drivename]:
- if bitmap.get('name', '') == name:
- if recording is None:
- return bitmap
- elif bitmap.get('recording') == recording:
- return bitmap
- return None
-
def blockdev_backup(vm, device, target, sync, **kwargs):
# Strip any arguments explicitly nulled by the caller:
kwargs = {key: val for key, val in kwargs.items() if val is not None}
@@ -214,13 +190,14 @@
device=device,
target=target,
sync=sync,
+ filter_node_name='backup-top',
**kwargs)
return result
def blockdev_backup_mktarget(drive, target_id, filepath, sync, **kwargs):
target_drive = Drive(filepath, vm=drive.vm)
target_drive.create_target(target_id, drive.fmt, drive.size)
- blockdev_backup(drive.vm, drive.name, target_id, sync, **kwargs)
+ blockdev_backup(drive.vm, drive.node, target_id, sync, **kwargs)
def reference_backup(drive, n, filepath):
log("--- Reference Backup #{:d} ---\n".format(n))
@@ -240,7 +217,7 @@
job_id=job_id, **kwargs)
return job_id
-def perform_writes(drive, n):
+def perform_writes(drive, n, filter_node_name=None):
log("--- Write #{:d} ---\n".format(n))
for pattern in GROUPS[n].patterns:
cmd = "write -P{:s} 0x{:07x} 0x{:x}".format(
@@ -248,9 +225,9 @@
pattern.offset,
pattern.size)
log(cmd)
- log(drive.vm.hmp_qemu_io(drive.name, cmd))
- bitmaps = query_bitmaps(drive.vm)
- log(bitmaps, indent=2)
+ log(drive.vm.hmp_qemu_io(filter_node_name or drive.node, cmd))
+ bitmaps = drive.vm.query_bitmaps()
+ log({'bitmaps': bitmaps}, indent=2)
log('')
return bitmaps
@@ -343,26 +320,19 @@
}]
}
+ drive0.node = 'drive0'
vm.qmp_log('blockdev-add',
filters=[iotests.filter_qmp_testfiles],
- node_name="drive0",
+ node_name=drive0.node,
driver=drive0.fmt,
file=file_config)
- drive0.node = 'drive0'
- drive0.device = 'device0'
- # Use share-rw to allow writes directly to the node;
- # The anonymous block-backend for this configuration prevents us
- # from using HMP's qemu-io commands to address the device.
- vm.qmp_log("device_add", id=drive0.device,
- drive=drive0.name, driver="scsi-hd",
- share_rw=True)
log('')
# 0 - Writes and Reference Backup
perform_writes(drive0, 0)
reference_backup(drive0, 0, fbackup0)
log('--- Add Bitmap ---\n')
- vm.qmp_log("block-dirty-bitmap-add", node=drive0.name,
+ vm.qmp_log("block-dirty-bitmap-add", node=drive0.node,
name="bitmap0", granularity=GRANULARITY)
log('')
ebitmap = EmulatedBitmap()
@@ -370,14 +340,14 @@
# 1 - Writes and Reference Backup
bitmaps = perform_writes(drive0, 1)
ebitmap.dirty_group(1)
- bitmap = get_bitmap(bitmaps, drive0.device, 'bitmap0')
+ bitmap = vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps)
ebitmap.compare(bitmap)
reference_backup(drive0, 1, fbackup1)
# 1 - Test Backup (w/ Optional induced failure)
if failure == 'intermediate':
# Activate blkdebug induced failure for second-to-next read
- log(vm.hmp_qemu_io(drive0.name, 'flush'))
+ log(vm.hmp_qemu_io(drive0.node, 'flush'))
log('')
job = backup(drive0, 1, bsync1, msync_mode,
bitmap="bitmap0", bitmap_mode=bsync_mode)
@@ -386,14 +356,15 @@
"""Issue writes while the job is open to test bitmap divergence."""
# Note: when `failure` is 'intermediate', this isn't called.
log('')
- bitmaps = perform_writes(drive0, 2)
+ bitmaps = perform_writes(drive0, 2, filter_node_name='backup-top')
# Named bitmap (static, should be unchanged)
- ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0',
+ bitmaps=bitmaps))
# Anonymous bitmap (dynamic, shows new writes)
anonymous = EmulatedBitmap()
anonymous.dirty_group(2)
- anonymous.compare(get_bitmap(bitmaps, drive0.device, '',
- recording=True))
+ anonymous.compare(vm.get_bitmap(drive0.node, '', recording=True,
+ bitmaps=bitmaps))
# Simulate the order in which this will happen:
# group 1 gets cleared first, then group two gets written.
@@ -405,8 +376,8 @@
vm.run_job(job, auto_dismiss=True, auto_finalize=False,
pre_finalize=_callback,
cancel=(failure == 'simulated'))
- bitmaps = query_bitmaps(vm)
- log(bitmaps, indent=2)
+ bitmaps = vm.query_bitmaps()
+ log({'bitmaps': bitmaps}, indent=2)
log('')
if bsync_mode == 'always' and failure == 'intermediate':
@@ -423,29 +394,30 @@
ebitmap.clear()
ebitmap.dirty_bits(range(fail_bit, SIZE // GRANULARITY))
- ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
# 2 - Writes and Reference Backup
bitmaps = perform_writes(drive0, 3)
ebitmap.dirty_group(3)
- ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
reference_backup(drive0, 2, fbackup2)
# 2 - Bitmap Backup (In failure modes, this is a recovery.)
job = backup(drive0, 2, bsync2, "bitmap",
bitmap="bitmap0", bitmap_mode=bsync_mode)
vm.run_job(job, auto_dismiss=True, auto_finalize=False)
- bitmaps = query_bitmaps(vm)
- log(bitmaps, indent=2)
+ bitmaps = vm.query_bitmaps()
+ log({'bitmaps': bitmaps}, indent=2)
log('')
if bsync_mode != 'never':
ebitmap.clear()
- ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0'))
+ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
log('--- Cleanup ---\n')
vm.qmp_log("block-dirty-bitmap-remove",
- node=drive0.name, name="bitmap0")
- log(query_bitmaps(vm), indent=2)
+ node=drive0.node, name="bitmap0")
+ bitmaps = vm.query_bitmaps()
+ log({'bitmaps': bitmaps}, indent=2)
vm.shutdown()
log('')
@@ -484,22 +456,19 @@
'filename': drive0.path
}
+ drive0.node = 'drive0'
vm.qmp_log('blockdev-add',
filters=[iotests.filter_qmp_testfiles],
- node_name="drive0",
+ node_name=drive0.node,
driver=drive0.fmt,
file=file_config)
- drive0.node = 'drive0'
- drive0.device = 'device0'
- vm.qmp_log("device_add", id=drive0.device,
- drive=drive0.name, driver="scsi-hd")
log('')
target0 = Drive(backup_path, vm=vm)
target0.create_target("backup_target", drive0.fmt, drive0.size)
log('')
- vm.qmp_log("block-dirty-bitmap-add", node=drive0.name,
+ vm.qmp_log("block-dirty-bitmap-add", node=drive0.node,
name="bitmap0", granularity=GRANULARITY)
log('')
@@ -538,7 +507,7 @@
log("-- Sync mode {:s} tests --\n".format(sync_mode))
for bitmap in (None, 'bitmap404', 'bitmap0'):
for policy in error_cases[sync_mode][bitmap]:
- blockdev_backup(drive0.vm, drive0.name, "backup_target",
+ blockdev_backup(drive0.vm, drive0.node, "backup_target",
sync_mode, job_id='api_job',
bitmap=bitmap, bitmap_mode=policy)
log('')
diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out
index 84b79d7..64dd460 100644
--- a/tests/qemu-iotests/257.out
+++ b/tests/qemu-iotests/257.out
@@ -5,8 +5,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -19,9 +17,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -34,7 +30,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -55,7 +51,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -82,7 +78,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -96,7 +92,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -111,7 +107,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -153,7 +149,7 @@
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -182,7 +178,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -209,7 +205,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -223,7 +219,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -231,7 +227,7 @@
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -253,9 +249,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -271,8 +265,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -285,9 +277,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -300,7 +290,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -321,7 +311,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -348,7 +338,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -364,13 +354,13 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -399,7 +389,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -426,7 +416,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -440,7 +430,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -448,7 +438,7 @@
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -470,9 +460,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -488,8 +476,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -502,9 +488,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -517,7 +501,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -538,7 +522,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -565,7 +549,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -579,7 +563,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -594,7 +578,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -636,7 +620,7 @@
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -665,7 +649,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -692,7 +676,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -706,7 +690,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -714,7 +698,7 @@
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -736,9 +720,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -754,8 +736,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -768,9 +748,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -783,7 +761,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -804,7 +782,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -831,7 +809,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -845,7 +823,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -860,7 +838,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -902,7 +880,7 @@
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -931,7 +909,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -958,7 +936,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -972,7 +950,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -980,7 +958,7 @@
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1002,9 +980,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1020,8 +996,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -1034,9 +1008,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -1049,7 +1021,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1070,7 +1042,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1097,7 +1069,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1113,13 +1085,13 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1148,7 +1120,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -1175,7 +1147,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1189,7 +1161,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1197,7 +1169,7 @@
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1219,9 +1191,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1237,8 +1207,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -1251,9 +1219,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -1266,7 +1232,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1287,7 +1253,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1314,7 +1280,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1328,7 +1294,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -1343,7 +1309,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1385,7 +1351,7 @@
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -1414,7 +1380,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -1441,7 +1407,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1455,7 +1421,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1463,7 +1429,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1485,9 +1451,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1503,8 +1467,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -1517,9 +1479,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -1532,7 +1492,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1553,7 +1513,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1580,7 +1540,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1594,7 +1554,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -1609,7 +1569,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1651,7 +1611,7 @@
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -1680,7 +1640,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -1707,7 +1667,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1721,7 +1681,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1729,7 +1689,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1751,9 +1711,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1769,8 +1727,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -1783,9 +1739,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -1798,7 +1752,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1819,7 +1773,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -1846,7 +1800,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1862,13 +1816,13 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 327680,
@@ -1897,7 +1851,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 851968,
@@ -1924,7 +1878,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -1938,7 +1892,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -1946,7 +1900,7 @@
{"data": {"device": "backup_2", "len": 851968, "offset": 851968, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -1968,9 +1922,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -1986,8 +1938,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -2000,9 +1950,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -2015,7 +1963,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2036,7 +1984,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2063,7 +2011,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2077,7 +2025,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -2092,7 +2040,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2134,7 +2082,7 @@
{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -2163,7 +2111,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -2190,7 +2138,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2204,7 +2152,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2212,7 +2160,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2234,9 +2182,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -2252,8 +2198,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -2266,9 +2210,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -2281,7 +2223,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2302,7 +2244,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2329,7 +2271,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2343,7 +2285,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -2358,7 +2300,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2400,7 +2342,7 @@
{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -2429,7 +2371,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -2456,7 +2398,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2470,7 +2412,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2478,7 +2420,7 @@
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2500,9 +2442,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -2518,8 +2458,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -2532,9 +2470,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -2547,7 +2483,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2568,7 +2504,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2595,7 +2531,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2611,13 +2547,13 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 67108864, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2646,7 +2582,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -2673,7 +2609,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2687,7 +2623,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2695,7 +2631,7 @@
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2717,9 +2653,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -2735,8 +2669,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -2749,9 +2681,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -2764,7 +2694,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2785,7 +2715,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -2812,7 +2742,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2826,7 +2756,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -2841,7 +2771,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2883,7 +2813,7 @@
{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -2912,7 +2842,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -2939,7 +2869,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -2953,7 +2883,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -2961,7 +2891,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -2983,9 +2913,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -3001,8 +2929,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -3015,9 +2941,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -3030,7 +2954,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3051,7 +2975,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -3078,7 +3002,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3092,7 +3016,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -3107,7 +3031,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3149,7 +3073,7 @@
{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -3178,7 +3102,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -3205,7 +3129,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3219,7 +3143,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3227,7 +3151,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3249,9 +3173,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -3267,8 +3189,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -3281,9 +3201,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -3296,7 +3214,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3317,7 +3235,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -3344,7 +3262,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3360,13 +3278,13 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 67108864, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 66125824,
@@ -3395,7 +3313,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 66453504,
@@ -3422,7 +3340,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3436,7 +3354,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3444,7 +3362,7 @@
{"data": {"device": "backup_2", "len": 66453504, "offset": 66453504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3466,9 +3384,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -3484,8 +3400,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -3498,9 +3412,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -3513,7 +3425,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3534,7 +3446,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -3561,7 +3473,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3575,7 +3487,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "full", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -3590,7 +3502,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3632,7 +3544,7 @@
{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -3661,7 +3573,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -3688,7 +3600,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3702,7 +3614,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3710,7 +3622,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3732,9 +3644,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -3750,8 +3660,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -3764,9 +3672,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -3779,7 +3685,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3800,7 +3706,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -3827,7 +3733,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3841,7 +3747,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -3856,7 +3762,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3898,7 +3804,7 @@
{"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 655360,
@@ -3927,7 +3833,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 983040,
@@ -3954,7 +3860,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -3968,7 +3874,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -3976,7 +3882,7 @@
{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -3998,9 +3904,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4016,8 +3920,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4030,9 +3932,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -4045,7 +3945,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4066,7 +3966,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4093,7 +3993,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4109,13 +4009,13 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 458752, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4144,7 +4044,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -4171,7 +4071,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4185,7 +4085,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4193,7 +4093,7 @@
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4215,9 +4115,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4233,8 +4131,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4247,9 +4143,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -4262,7 +4156,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4283,7 +4177,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4310,7 +4204,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4324,7 +4218,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -4339,7 +4233,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4381,7 +4275,7 @@
{"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -4410,7 +4304,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -4437,7 +4331,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4451,7 +4345,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4459,7 +4353,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4481,9 +4375,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4499,8 +4391,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4513,9 +4403,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -4528,7 +4416,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4549,7 +4437,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4576,7 +4464,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4590,7 +4478,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -4605,7 +4493,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4647,7 +4535,7 @@
{"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -4676,7 +4564,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -4703,7 +4591,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4717,7 +4605,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4725,7 +4613,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4747,9 +4635,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4765,8 +4651,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}]}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4779,9 +4663,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -4794,7 +4676,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4815,7 +4697,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4842,7 +4724,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4858,13 +4740,13 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
{"return": {}}
{"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "backup_1", "error": "Input/output error", "len": 458752, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -4893,7 +4775,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 917504,
@@ -4920,7 +4802,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -4934,7 +4816,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -4942,7 +4824,7 @@
{"data": {"device": "backup_2", "len": 917504, "offset": 917504, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -4964,9 +4846,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -4982,8 +4862,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}}
-{"return": {}}
--- Write #0 ---
@@ -4996,9 +4874,7 @@
write -P0x76 0x3ff0000 0x10000
{"return": ""}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Reference Backup #0 ---
@@ -5011,7 +4887,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_0", "sync": "full", "target": "ref_target_0"}}
{"return": {}}
{"data": {"device": "ref_backup_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -5032,7 +4908,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 393216,
@@ -5059,7 +4935,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_1", "sync": "full", "target": "ref_target_1"}}
{"return": {}}
{"data": {"device": "ref_backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -5073,7 +4949,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_1", "sync": "top", "target": "backup_target_1"}}
{"return": {}}
--- Write #2 ---
@@ -5088,7 +4964,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -5130,7 +5006,7 @@
{"data": {"device": "backup_1", "len": 458752, "offset": 458752, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 458752,
@@ -5159,7 +5035,7 @@
{"return": ""}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 786432,
@@ -5186,7 +5062,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "ref_backup_2", "sync": "full", "target": "ref_target_2"}}
{"return": {}}
{"data": {"device": "ref_backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
@@ -5200,7 +5076,7 @@
{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
{"return": {}}
{}
-{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
+{"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "backup_2", "sync": "bitmap", "target": "backup_target_2"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "backup_2"}}
{"return": {}}
@@ -5208,7 +5084,7 @@
{"data": {"device": "backup_2", "len": 786432, "offset": 786432, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{
"bitmaps": {
- "device0": [
+ "drive0": [
{
"busy": false,
"count": 0,
@@ -5230,9 +5106,7 @@
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
{"return": {}}
{
- "bitmaps": {
- "device0": []
- }
+ "bitmaps": {}
}
--- Verification ---
@@ -5248,8 +5122,6 @@
{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
{"return": {}}
-{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0"}}
-{"return": {}}
{}
{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
@@ -5267,155 +5139,155 @@
-- Sync mode incremental tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
-- Sync mode bitmap tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'bitmap' sync mode"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "bitmap", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
-- Sync mode full tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode 'never' has no meaningful effect when combined with sync mode 'full'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "full", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
-- Sync mode top tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode 'never' has no meaningful effect when combined with sync mode 'top'"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "top", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
-- Sync mode none tests --
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap404' could not be found"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "sync mode 'none' does not produce meaningful bitmap outputs"}}
-{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
+{"execute": "blockdev-backup", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "backup-top", "job-id": "api_job", "sync": "none", "target": "backup_target"}}
{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be given when providing a bitmap"}}
diff --git a/tests/qemu-iotests/268 b/tests/qemu-iotests/268
new file mode 100755
index 0000000..78c3f4d
--- /dev/null
+++ b/tests/qemu-iotests/268
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+#
+# Test write request with required alignment larger than the cluster size
+#
+# Copyright (C) 2019 Igalia, S.L.
+# Author: Alberto Garcia <berto@igalia.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=berto@igalia.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+
+echo
+echo "== Required alignment larger than cluster size =="
+
+CLUSTER_SIZE=2k _make_test_img 1M
+# Since commit c8bb23cbdb writing to an unallocated cluster fills the
+# empty COW areas with bdrv_write_zeroes(flags=BDRV_REQ_NO_FALLBACK)
+$QEMU_IO -c "open -o driver=$IMGFMT,file.align=4k blkdebug::$TEST_IMG" \
+ -c "write 0 512" | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/268.out b/tests/qemu-iotests/268.out
new file mode 100644
index 0000000..2ed6c68
--- /dev/null
+++ b/tests/qemu-iotests/268.out
@@ -0,0 +1,7 @@
+QA output created by 268
+
+== Required alignment larger than cluster size ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/270 b/tests/qemu-iotests/270
new file mode 100755
index 0000000..b9a12b9
--- /dev/null
+++ b/tests/qemu-iotests/270
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+#
+# Test large write to a qcow2 image
+#
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+seq=$(basename "$0")
+echo "QA output created by $seq"
+
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+# This is a qcow2 regression test
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+# We use our own external data file and our own cluster size, and we
+# require v3 images
+_unsupported_imgopts data_file cluster_size 'compat=0.10'
+
+
+# We need a backing file so that handle_alloc_space() will not do
+# anything. (If it were to do anything, it would simply fail its
+# write-zeroes request because the request range is too large.)
+TEST_IMG="$TEST_IMG.base" _make_test_img 4G
+$QEMU_IO -c 'write 0 512' "$TEST_IMG.base" | _filter_qemu_io
+
+# (Use .orig because _cleanup_test_img will remove that file)
+# We need a large cluster size, see below for why (above the $QEMU_IO
+# invocation)
+_make_test_img -o cluster_size=2M,data_file="$TEST_IMG.orig" \
+ -b "$TEST_IMG.base" 4G
+
+# We want a null-co as the data file, because it allows us to quickly
+# "write" 2G of data without using any space.
+# (qemu-img create does not like it, though, because null-co does not
+# support image creation.)
+$QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \
+ "$TEST_IMG"
+
+# This gives us a range of:
+# 2^31 - 512 + 768 - 1 = 2^31 + 255 > 2^31
+# until the beginning of the end COW block. (The total allocation
+# size depends on the cluster size, but all that is important is that
+# it exceeds INT_MAX.)
+#
+# 2^31 - 512 is the maximum request size. We want this to result in a
+# single allocation, and because the qcow2 driver splits allocations
+# on L2 boundaries, we need large L2 tables; hence the cluster size of
+# 2 MB. (Anything from 256 kB should work, though, because then one L2
+# table covers 8 GB.)
+$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$TEST_IMG" | _filter_qemu_io
+
+_check_test_img
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/270.out b/tests/qemu-iotests/270.out
new file mode 100644
index 0000000..c7be111
--- /dev/null
+++ b/tests/qemu-iotests/270.out
@@ -0,0 +1,9 @@
+QA output created by 270
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base data_file=TEST_DIR/t.IMGFMT.orig
+wrote 2147483136/2147483136 bytes at offset 768
+2 GiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index e45cdfa..12b4751 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -405,6 +405,23 @@
$QEMU_IMG check "$@" -f $IMGFMT "$TEST_IMG" 2>&1
fi
) | _filter_testdir | _filter_qemu_img_check
+
+ # return real qemu_img check status, to analyze in
+ # _check_test_img_ignore_leaks
+ return ${PIPESTATUS[0]}
+}
+
+_check_test_img_ignore_leaks()
+{
+ out=$(_check_test_img "$@")
+ status=$?
+ if [ $status = 3 ]; then
+ # This must correspond to success output in dump_human_image_check()
+ echo "No errors were found on the image."
+ return 0
+ fi
+ echo "$out"
+ return $status
}
_img_info()
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 5805a79..7dac79a 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -278,3 +278,5 @@
265 rw auto quick
266 rw quick
267 rw auto quick snapshot
+268 rw auto quick
+270 rw backing quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 9fb5181..3a8f378 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -641,6 +641,33 @@
return x
return None
+ def query_bitmaps(self):
+ res = self.qmp("query-named-block-nodes")
+ return {device['node-name']: device['dirty-bitmaps']
+ for device in res['return'] if 'dirty-bitmaps' in device}
+
+ def get_bitmap(self, node_name, bitmap_name, recording=None, bitmaps=None):
+ """
+ get a specific bitmap from the object returned by query_bitmaps.
+ :param recording: If specified, filter results by the specified value.
+ :param bitmaps: If specified, use it instead of call query_bitmaps()
+ """
+ if bitmaps is None:
+ bitmaps = self.query_bitmaps()
+
+ for bitmap in bitmaps[node_name]:
+ if bitmap.get('name', '') == bitmap_name:
+ if recording is None:
+ return bitmap
+ elif bitmap.get('recording') == recording:
+ return bitmap
+ return None
+
+ def check_bitmap_status(self, node_name, bitmap_name, fields):
+ ret = self.get_bitmap(node_name, bitmap_name)
+
+ return fields.items() <= ret.items()
+
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
index e80c4c61..1e5be1d 100644
--- a/tests/test-vmstate.c
+++ b/tests/test-vmstate.c
@@ -812,6 +812,423 @@
qemu_fclose(fload);
}
+/* interval (key) */
+typedef struct TestGTreeInterval {
+ uint64_t low;
+ uint64_t high;
+} TestGTreeInterval;
+
+#define VMSTATE_INTERVAL \
+{ \
+ .name = "interval", \
+ .version_id = 1, \
+ .minimum_version_id = 1, \
+ .fields = (VMStateField[]) { \
+ VMSTATE_UINT64(low, TestGTreeInterval), \
+ VMSTATE_UINT64(high, TestGTreeInterval), \
+ VMSTATE_END_OF_LIST() \
+ } \
+}
+
+/* mapping (value) */
+typedef struct TestGTreeMapping {
+ uint64_t phys_addr;
+ uint32_t flags;
+} TestGTreeMapping;
+
+#define VMSTATE_MAPPING \
+{ \
+ .name = "mapping", \
+ .version_id = 1, \
+ .minimum_version_id = 1, \
+ .fields = (VMStateField[]) { \
+ VMSTATE_UINT64(phys_addr, TestGTreeMapping), \
+ VMSTATE_UINT32(flags, TestGTreeMapping), \
+ VMSTATE_END_OF_LIST() \
+ }, \
+}
+
+static const VMStateDescription vmstate_interval_mapping[2] = {
+ VMSTATE_MAPPING, /* value */
+ VMSTATE_INTERVAL /* key */
+};
+
+typedef struct TestGTreeDomain {
+ int32_t id;
+ GTree *mappings;
+} TestGTreeDomain;
+
+typedef struct TestGTreeIOMMU {
+ int32_t id;
+ GTree *domains;
+} TestGTreeIOMMU;
+
+/* Interval comparison function */
+static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ TestGTreeInterval *inta = (TestGTreeInterval *)a;
+ TestGTreeInterval *intb = (TestGTreeInterval *)b;
+
+ if (inta->high < intb->low) {
+ return -1;
+ } else if (intb->high < inta->low) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ID comparison function */
+static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ uint ua = GPOINTER_TO_UINT(a);
+ uint ub = GPOINTER_TO_UINT(b);
+ return (ua > ub) - (ua < ub);
+}
+
+static void destroy_domain(gpointer data)
+{
+ TestGTreeDomain *domain = (TestGTreeDomain *)data;
+
+ g_tree_destroy(domain->mappings);
+ g_free(domain);
+}
+
+static int domain_preload(void *opaque)
+{
+ TestGTreeDomain *domain = opaque;
+
+ domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
+ NULL, g_free, g_free);
+ return 0;
+}
+
+static int iommu_preload(void *opaque)
+{
+ TestGTreeIOMMU *iommu = opaque;
+
+ iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
+ NULL, NULL, destroy_domain);
+ return 0;
+}
+
+static const VMStateDescription vmstate_domain = {
+ .name = "domain",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_load = domain_preload,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(id, TestGTreeDomain),
+ VMSTATE_GTREE_V(mappings, TestGTreeDomain, 1,
+ vmstate_interval_mapping,
+ TestGTreeInterval, TestGTreeMapping),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_iommu = {
+ .name = "iommu",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_load = iommu_preload,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(id, TestGTreeIOMMU),
+ VMSTATE_GTREE_DIRECT_KEY_V(domains, TestGTreeIOMMU, 1,
+ &vmstate_domain, TestGTreeDomain),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+uint8_t first_domain_dump[] = {
+ /* id */
+ 0x00, 0x0, 0x0, 0x6,
+ 0x00, 0x0, 0x0, 0x2, /* 2 mappings */
+ 0x1, /* start of a */
+ /* a */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
+ /* map_a */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x1, /* start of b */
+ /* b */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF,
+ /* map_b */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x0, /* end of gtree */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+};
+
+static TestGTreeDomain *create_first_domain(void)
+{
+ TestGTreeDomain *domain;
+ TestGTreeMapping *map_a, *map_b;
+ TestGTreeInterval *a, *b;
+
+ domain = g_malloc0(sizeof(TestGTreeDomain));
+ domain->id = 6;
+
+ a = g_malloc0(sizeof(TestGTreeInterval));
+ a->low = 0x1000;
+ a->high = 0x1FFF;
+
+ b = g_malloc0(sizeof(TestGTreeInterval));
+ b->low = 0x4000;
+ b->high = 0x4FFF;
+
+ map_a = g_malloc0(sizeof(TestGTreeMapping));
+ map_a->phys_addr = 0xa000;
+ map_a->flags = 1;
+
+ map_b = g_malloc0(sizeof(TestGTreeMapping));
+ map_b->phys_addr = 0xe0000;
+ map_b->flags = 2;
+
+ domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, NULL,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+ g_tree_insert(domain->mappings, a, map_a);
+ g_tree_insert(domain->mappings, b, map_b);
+ return domain;
+}
+
+static void test_gtree_save_domain(void)
+{
+ TestGTreeDomain *first_domain = create_first_domain();
+
+ save_vmstate(&vmstate_domain, first_domain);
+ compare_vmstate(first_domain_dump, sizeof(first_domain_dump));
+ destroy_domain(first_domain);
+}
+
+struct match_node_data {
+ GTree *tree;
+ gpointer key;
+ gpointer value;
+};
+
+struct tree_cmp_data {
+ GTree *tree1;
+ GTree *tree2;
+ GTraverseFunc match_node;
+};
+
+static gboolean match_interval_mapping_node(gpointer key,
+ gpointer value, gpointer data)
+{
+ TestGTreeMapping *map_a, *map_b;
+ TestGTreeInterval *a, *b;
+ struct match_node_data *d = (struct match_node_data *)data;
+ char *str = g_strdup_printf("dest");
+
+ g_free(str);
+ a = (TestGTreeInterval *)key;
+ b = (TestGTreeInterval *)d->key;
+
+ map_a = (TestGTreeMapping *)value;
+ map_b = (TestGTreeMapping *)d->value;
+
+ assert(a->low == b->low);
+ assert(a->high == b->high);
+ assert(map_a->phys_addr == map_b->phys_addr);
+ assert(map_a->flags == map_b->flags);
+ g_tree_remove(d->tree, key);
+ return true;
+}
+
+static gboolean diff_tree(gpointer key, gpointer value, gpointer data)
+{
+ struct tree_cmp_data *tp = (struct tree_cmp_data *)data;
+ struct match_node_data d = {tp->tree2, key, value};
+
+ g_tree_foreach(tp->tree2, tp->match_node, &d);
+ g_tree_remove(tp->tree1, key);
+ return false;
+}
+
+static void compare_trees(GTree *tree1, GTree *tree2,
+ GTraverseFunc function)
+{
+ struct tree_cmp_data tp = {tree1, tree2, function};
+
+ g_tree_foreach(tree1, diff_tree, &tp);
+ assert(g_tree_nnodes(tree1) == 0);
+ assert(g_tree_nnodes(tree2) == 0);
+}
+
+static void diff_domain(TestGTreeDomain *d1, TestGTreeDomain *d2)
+{
+ assert(d1->id == d2->id);
+ compare_trees(d1->mappings, d2->mappings, match_interval_mapping_node);
+}
+
+static gboolean match_domain_node(gpointer key, gpointer value, gpointer data)
+{
+ uint64_t id1, id2;
+ TestGTreeDomain *d1, *d2;
+ struct match_node_data *d = (struct match_node_data *)data;
+
+ id1 = (uint64_t)(uintptr_t)key;
+ id2 = (uint64_t)(uintptr_t)d->key;
+ d1 = (TestGTreeDomain *)value;
+ d2 = (TestGTreeDomain *)d->value;
+ assert(id1 == id2);
+ diff_domain(d1, d2);
+ g_tree_remove(d->tree, key);
+ return true;
+}
+
+static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2)
+{
+ assert(iommu1->id == iommu2->id);
+ compare_trees(iommu1->domains, iommu2->domains, match_domain_node);
+}
+
+static void test_gtree_load_domain(void)
+{
+ TestGTreeDomain *dest_domain = g_malloc0(sizeof(TestGTreeDomain));
+ TestGTreeDomain *orig_domain = create_first_domain();
+ QEMUFile *fload, *fsave;
+ char eof;
+
+ fsave = open_test_file(true);
+ qemu_put_buffer(fsave, first_domain_dump, sizeof(first_domain_dump));
+ g_assert(!qemu_file_get_error(fsave));
+ qemu_fclose(fsave);
+
+ fload = open_test_file(false);
+
+ vmstate_load_state(fload, &vmstate_domain, dest_domain, 1);
+ eof = qemu_get_byte(fload);
+ g_assert(!qemu_file_get_error(fload));
+ g_assert_cmpint(orig_domain->id, ==, dest_domain->id);
+ g_assert_cmpint(eof, ==, QEMU_VM_EOF);
+
+ diff_domain(orig_domain, dest_domain);
+ destroy_domain(orig_domain);
+ destroy_domain(dest_domain);
+ qemu_fclose(fload);
+}
+
+uint8_t iommu_dump[] = {
+ /* iommu id */
+ 0x00, 0x0, 0x0, 0x7,
+ 0x00, 0x0, 0x0, 0x2, /* 2 domains */
+ 0x1,/* start of domain 5 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x5, /* key = 5 */
+ 0x00, 0x0, 0x0, 0x5, /* domain1 id */
+ 0x00, 0x0, 0x0, 0x1, /* 1 mapping */
+ 0x1, /* start of mappings */
+ /* c */
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF,
+ /* map_c */
+ 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
+ 0x00, 0x0, 0x0, 0x3,
+ 0x0, /* end of domain1 mappings*/
+ 0x1,/* start of domain 6 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x6, /* key = 6 */
+ 0x00, 0x0, 0x0, 0x6, /* domain6 id */
+ 0x00, 0x0, 0x0, 0x2, /* 2 mappings */
+ 0x1, /* start of a */
+ /* a */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
+ /* map_a */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x1, /* start of b */
+ /* b */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF,
+ /* map_b */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x0, /* end of domain6 mappings*/
+ 0x0, /* end of domains */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+};
+
+static TestGTreeIOMMU *create_iommu(void)
+{
+ TestGTreeIOMMU *iommu = g_malloc0(sizeof(TestGTreeIOMMU));
+ TestGTreeDomain *first_domain = create_first_domain();
+ TestGTreeDomain *second_domain;
+ TestGTreeMapping *map_c;
+ TestGTreeInterval *c;
+
+ iommu->id = 7;
+ iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, NULL,
+ NULL,
+ destroy_domain);
+
+ second_domain = g_malloc0(sizeof(TestGTreeDomain));
+ second_domain->id = 5;
+ second_domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
+ NULL,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+
+ g_tree_insert(iommu->domains, GUINT_TO_POINTER(6), first_domain);
+ g_tree_insert(iommu->domains, (gpointer)0x0000000000000005, second_domain);
+
+ c = g_malloc0(sizeof(TestGTreeInterval));
+ c->low = 0x1000000;
+ c->high = 0x1FFFFFF;
+
+ map_c = g_malloc0(sizeof(TestGTreeMapping));
+ map_c->phys_addr = 0xF000000;
+ map_c->flags = 0x3;
+
+ g_tree_insert(second_domain->mappings, c, map_c);
+ return iommu;
+}
+
+static void destroy_iommu(TestGTreeIOMMU *iommu)
+{
+ g_tree_destroy(iommu->domains);
+ g_free(iommu);
+}
+
+static void test_gtree_save_iommu(void)
+{
+ TestGTreeIOMMU *iommu = create_iommu();
+
+ save_vmstate(&vmstate_iommu, iommu);
+ compare_vmstate(iommu_dump, sizeof(iommu_dump));
+ destroy_iommu(iommu);
+}
+
+static void test_gtree_load_iommu(void)
+{
+ TestGTreeIOMMU *dest_iommu = g_malloc0(sizeof(TestGTreeIOMMU));
+ TestGTreeIOMMU *orig_iommu = create_iommu();
+ QEMUFile *fsave, *fload;
+ char eof;
+ int ret;
+
+ fsave = open_test_file(true);
+ qemu_put_buffer(fsave, iommu_dump, sizeof(iommu_dump));
+ g_assert(!qemu_file_get_error(fsave));
+ qemu_fclose(fsave);
+
+ fload = open_test_file(false);
+ vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1);
+ ret = qemu_file_get_error(fload);
+ eof = qemu_get_byte(fload);
+ ret = qemu_file_get_error(fload);
+ g_assert(!ret);
+ g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id);
+ g_assert_cmpint(eof, ==, QEMU_VM_EOF);
+
+ diff_iommu(orig_iommu, dest_iommu);
+ destroy_iommu(orig_iommu);
+ destroy_iommu(dest_iommu);
+ qemu_fclose(fload);
+}
+
typedef struct TmpTestStruct {
TestStruct *parent;
int64_t diff;
@@ -932,6 +1349,10 @@
test_arr_ptr_prim_0_load);
g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q);
g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q);
+ g_test_add_func("/vmstate/gtree/save/savedomain", test_gtree_save_domain);
+ g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain);
+ g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu);
+ g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu);
g_test_add_func("/vmstate/tmp_struct", test_tmp_struct);
g_test_run();
diff --git a/vl.c b/vl.c
index 002bf49..4489cfb 100644
--- a/vl.c
+++ b/vl.c
@@ -1203,7 +1203,7 @@
qapi_free_BlockdevOptions(bdo->bdo);
g_free(bdo);
}
- if (snapshot || replay_mode != REPLAY_MODE_NONE) {
+ if (snapshot) {
qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot,
NULL, NULL);
}
@@ -2649,57 +2649,7 @@
*/
static bool object_create_initial(const char *type, QemuOpts *opts)
{
- ObjectClass *klass;
-
- if (is_help_option(type)) {
- GSList *l, *list;
-
- printf("List of user creatable objects:\n");
- list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
- for (l = list; l != NULL; l = l->next) {
- ObjectClass *oc = OBJECT_CLASS(l->data);
- printf(" %s\n", object_class_get_name(oc));
- }
- g_slist_free(list);
- exit(0);
- }
-
- klass = object_class_by_name(type);
- if (klass && qemu_opt_has_help_opt(opts)) {
- ObjectPropertyIterator iter;
- ObjectProperty *prop;
- GPtrArray *array = g_ptr_array_new();
- int i;
-
- object_class_property_iter_init(&iter, klass);
- while ((prop = object_property_iter_next(&iter))) {
- GString *str;
-
- if (!prop->set) {
- continue;
- }
-
- str = g_string_new(NULL);
- g_string_append_printf(str, " %s=<%s>", prop->name, prop->type);
- if (prop->description) {
- if (str->len < 24) {
- g_string_append_printf(str, "%*s", 24 - (int)str->len, "");
- }
- g_string_append_printf(str, " - %s", prop->description);
- }
- g_ptr_array_add(array, g_string_free(str, false));
- }
- g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
- if (array->len > 0) {
- printf("%s options:\n", type);
- } else {
- printf("There are no options for %s.\n", type);
- }
- for (i = 0; i < array->len; i++) {
- printf("%s\n", (char *)array->pdata[i]);
- }
- g_ptr_array_set_free_func(array, g_free);
- g_ptr_array_free(array, true);
+ if (user_creatable_print_help(type, opts)) {
exit(0);
}
@@ -3066,7 +3016,13 @@
drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS);
break;
case QEMU_OPTION_snapshot:
- snapshot = 1;
+ {
+ Error *blocker = NULL;
+ snapshot = 1;
+ error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED,
+ "-snapshot");
+ replay_add_blocker(blocker);
+ }
break;
case QEMU_OPTION_numa:
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
@@ -3335,7 +3291,8 @@
case QEMU_OPTION_virtfs: {
QemuOpts *fsdev;
QemuOpts *device;
- const char *writeout, *sock_fd, *socket, *path, *security_model;
+ const char *writeout, *sock_fd, *socket, *path, *security_model,
+ *multidevs;
olist = qemu_find_opts("virtfs");
if (!olist) {
@@ -3395,6 +3352,10 @@
qemu_opt_set_bool(fsdev, "readonly",
qemu_opt_get_bool(opts, "readonly", 0),
&error_abort);
+ multidevs = qemu_opt_get(opts, "multidevs");
+ if (multidevs) {
+ qemu_opt_set(fsdev, "multidevs", multidevs, &error_abort);
+ }
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
&error_abort);
qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort);
@@ -4513,6 +4474,7 @@
/* No more vcpu or device emulation activity beyond this point */
vm_shutdown();
+ replay_finish();
job_cancel_sync_all();
bdrv_close_all();