[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1062044: marked as done (bookworm-pu: package qemu/1:7.2+dfsg-7+deb12u4)



Your message dated Sat, 10 Feb 2024 13:11:22 +0000
with message-id <E1rYn8c-002yd4-9L@coccia.debian.org>
and subject line Released with 12.5
has caused the Debian Bug report #1062044,
regarding bookworm-pu: package qemu/1:7.2+dfsg-7+deb12u4
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1062044: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1062044
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: qemu@packages.debian.org, pkg-qemu-devel@lists.alioth.debian.org
Control: affects -1 + src:qemu

[ Reason ]
There were 2 qemu stable/bugfix releases (7.2.8 and 7.2.9) since
the previous debian release, fixing a big share of issues.
It would be nice to have these fixes in debian too, so debian users
will benefit from the qemu stable series.

One of the issues fixed in 7.2.9 is a security issue, CVE-2023-6693,
in virtio_net device.  Additionally, I'm including another security
fix from upstream which is not part of a stable series yet (for
CVE-2023-6683, NULL-pointer deref in VNC clipboard code).

Also this qemu debian release includes a fix for a bug in bookworm
qemu which affects multiple users and which is reported multiple
times - qemu-guest-agent is stopped during upgrades but never re-
started (#1061588, #1061683).

[ Tests ]
Both upstream automatic tests are passed, and my usual share of
quick real-life tests too (a bunch of qemu/kvm guests which I
test for every new qemu release).

[ Risks ]
The risks do exists obviously, however we're trying hard to minimize
possible risks as much as possible by carefully selecting which changes
to pick and how to do that.

[ Checklist ]
  [X] *all* changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in (old)stable
  [X] the issue is verified as fixed in unstable

[ Changes ]

All changes except one comes from the upstream repository,
which is also mirrored on salsa:
https://salsa.debian.org/qemu-team/qemu/-/commits/stable-7.2/
In this case the talk is about v7.2.8 and v7.2.9 tags.

Complete changelog is below (a part of debdiff).

[ Other info ]
Historically, qemu in debian were built with base upstream release
plus stable/bugfix patches (7.2.orig.tar.gz which corresponds to
upstream 7.2.0 plus 7.2.1..7.2.2..7.2.3 etc patches).  I don't
remember why this has been done this way, and changed it to include
complete 3-component upstream version tarball past bookworm, but
continue this scheme in bookworm stable.

[ Debdiff]
diff -Nru qemu-7.2+dfsg/debian/changelog qemu-7.2+dfsg/debian/changelog
--- qemu-7.2+dfsg/debian/changelog	2023-12-03 15:36:08.000000000 +0300
+++ qemu-7.2+dfsg/debian/changelog	2024-01-30 19:15:04.000000000 +0300
@@ -1,3 +1,85 @@
+qemu (1:7.2+dfsg-7+deb12u4) bookworm; urgency=medium
+
+  [ Michael Tokarev ]
+  * update to upstream 7.2.9 stable/bugfix release, v7.2.9.diff,
+    https://gitlab.com/qemu-project/qemu/-/commits/v7.2.9 :
+   - Update version for 7.2.9 release
+   - target/xtensa: fix OOB TLB entry access
+   - qtest: bump aspeed_smc-test timeout to 6 minutes
+   - monitor: only run coroutine commands in qemu_aio_context
+   - iotests: port 141 to Python for reliable QMP testing
+   - iotests: add filter_qmp_generated_node_ids()
+   - block/blklogwrites: Fix a bug when logging "write zeroes" operations.
+   - virtio-net: correctly copy vnet header when flushing TX
+     Closes: CVE-2023-6693
+   - block/io: clear BDRV_BLOCK_RECURSE flag after recursing in
+     bdrv_co_block_status
+   - accel/tcg: Revert mapping of PCREL translation block to multiple
+     virtual addresses
+   - readthodocs: fully specify a build environment
+   - hw/scsi/esp-pci: set DMA_STAT_BCMBLT when BLAST command issued
+   - hw/scsi/esp-pci: synchronise setting of DMA_STAT_DONE with ESP
+     completion interrupt
+   - hw/scsi/esp-pci: generate PCI interrupt from separate ESP and PCI sources
+   - hw/scsi/esp-pci: use correct address register for PCI DMA transfers
+   - hw/pflash: implement update buffer for block writes
+   - hw/pflash: use ldn_{be,le}_p and stn_{be,le}_p
+   - hw/pflash: refactor pflash_data_write()
+   - target/i386: pcrel: store low bits of physical address in data[0]
+   - target/i386: fix incorrect EIP in PC-relative translation blocks
+   - target/i386: Do not re-compute new pc with CF_PCREL
+   - target/i386: Fix 32-bit wrapping of pc/eip computation
+   - load_elf: fix iterator's type for elf file processing
+   - .gitlab-ci.d/buildtest.yml: Work around htags bug when environment
+     is large
+   - target/s390x: Fix LAE setting a wrong access register
+   - hw/intc/arm_gicv3_cpuif: handle LPIs in the list registers
+   - chardev/char.c: fix "abstract device type" error message
+   - target/riscv: Fix mcycle/minstret increment behavior
+   - iotests: Basic tests for internal snapshots
+   - vl: Improve error message for conflicting -incoming and -loadvm
+   - block: Fix crash when loading snapshot on inactive node
+  * update to upstream 7.2.8 stable/bugfix release, v7.2.8.diff,
+    https://gitlab.com/qemu-project/qemu/-/commits/v7.2.8 :
+   - Update version for 7.2.8 release
+   - target/arm/helper: Propagate MDCR_EL2.HPMN into PMCR_EL0.N
+   - system/memory: use ldn_he_p/stn_he_p
+   - target/arm: Disable SME if SVE is disabled
+   - ui/vnc-clipboard: fix inflate_buffer
+   - ui/gtk-egl: move function calls back to regular code path
+   - ui/gtk-egl: Check EGLSurface before doing scanout
+   - msix: unset PCIDevice::msix_vector_poll_notifier in rollback
+   - hw/acpi/erst: Do not ignore Error* in realize handler
+   - pcie_sriov: Remove g_new assertion
+   - hw/audio/hda-codec: fix multiplication overflow
+   - hw/mips/malta: Fix the malta machine on big endian hosts
+   - vmdk: Don't corrupt desc file in vmdk_write_cid
+   - hw/virtio: Add VirtioPCIDeviceTypeInfo::instance_finalize field
+   - hw/nvram/xlnx-efuse-ctrl: Free XlnxVersalEFuseCtrl[] "pg0-lock" array
+   - hw/nvram/xlnx-efuse: Free XlnxEFuse::ro_bits[] array on finalize()
+   - hw/misc/mps2-scc: Free MPS2SCC::oscclk[] array on finalize()
+   - hw/virtio: Free VirtIOIOMMUPCI::vdev.reserved_regions[] on finalize()
+   - target/arm: Set IL bit for pauth, SVE access, BTI trap syndromes
+   - tests/avocado: Replace assertRegexpMatches() for Python 3.12 compatibility
+   - tests/avocado: Replace assertEquals() for Python 3.12 compatibility
+   - linux-user: Fix loaddr computation for some elf files
+   - net: Update MemReentrancyGuard for NIC
+   - net: Provide MemReentrancyGuard * to qemu_new_nic()
+   - hw/ide/ahci: fix legacy software reset
+   - target/arm: Fix SME FMOPA (16-bit), BFMOPA
+  * remove patches included in 7.2.8:
+   - hw_mips_malta-Fix-malta-machine-on-big-endian-hosts.patch
+   - hw-ide-ahci-fix-legacy-software-reset.patch
+  * ui-clipboard-mark-type-as-not-available-when-no-data-CVE-2023-6683.patch
+    Closes: #1060749, CVE-2023-6683 (NULL deref in VNC clipboard code)
+
+  [ Sergio Durigan Junior ]
+  * d/rules: omit --no-start for qemu-guest-agent, this should
+    re-start it on upgrades.  Closes: #1061588, #1061683
+    LP: #2028124
+
+ -- Michael Tokarev <mjt@tls.msk.ru>  Tue, 30 Jan 2024 19:15:04 +0300
+
 qemu (1:7.2+dfsg-7+deb12u3) bookworm; urgency=medium
 
   * +hw-ide-ahci-fix-legacy-software-reset.patch - fix legacy ide regression
diff -Nru qemu-7.2+dfsg/debian/patches/hw-ide-ahci-fix-legacy-software-reset.patch qemu-7.2+dfsg/debian/patches/hw-ide-ahci-fix-legacy-software-reset.patch
--- qemu-7.2+dfsg/debian/patches/hw-ide-ahci-fix-legacy-software-reset.patch	2023-12-03 15:28:24.000000000 +0300
+++ qemu-7.2+dfsg/debian/patches/hw-ide-ahci-fix-legacy-software-reset.patch	1970-01-01 03:00:00.000000000 +0300
@@ -1,116 +0,0 @@
-Origin: upstream, https://gitlab.com/qemu-project/qemu/-/commit/b9fd6d95211fb5190c3aa862b2f26b6735916791
-From: Niklas Cassel <niklas.cassel@wdc.com>
-Date: Wed, 8 Nov 2023 23:26:57 +0100
-Subject: hw/ide/ahci: fix legacy software reset
-
-Legacy software contains a standard mechanism for generating a reset to a
-Serial ATA device - setting the SRST (software reset) bit in the Device
-Control register.
-
-Serial ATA has a more robust mechanism called COMRESET, also referred to
-as port reset. A port reset is the preferred mechanism for error
-recovery and should be used in place of software reset.
-
-Commit e2a5d9b3d9c3 ("hw/ide/ahci: simplify and document PxCI handling")
-(mjt:  1e5ad6b06b1e in stable-7.2 series, v7.2.6)
-improved the handling of PxCI, such that PxCI gets cleared after handling
-a non-NCQ, or NCQ command (instead of incorrectly clearing PxCI after
-receiving anything - even a FIS that failed to parse, which should NOT
-clear PxCI, so that you can see which command slot that caused an error).
-
-However, simply clearing PxCI after a non-NCQ, or NCQ command, is not
-enough, we also need to clear PxCI when receiving a SRST in the Device
-Control register.
-
-A legacy software reset is performed by the host sending two H2D FISes,
-the first H2D FIS asserts SRST, and the second H2D FIS deasserts SRST.
-
-The first H2D FIS will not get a D2H reply, and requires the FIS to have
-the C bit set to one, such that the HBA itself will clear the bit in PxCI.
-
-The second H2D FIS will get a D2H reply once the diagnostic is completed.
-The clearing of the bit in PxCI for this command should ideally be done
-in ahci_init_d2h() (if it was a legacy software reset that caused the
-reset (a COMRESET does not use a command slot)). However, since the reset
-value for PxCI is 0, modify ahci_reset_port() to actually clear PxCI to 0,
-that way we can avoid complex logic in ahci_init_d2h().
-
-This fixes an issue for FreeBSD where the device would fail to reset.
-The problem was not noticed in Linux, because Linux uses a COMRESET
-instead of a legacy software reset by default.
-
-Fixes: e2a5d9b3d9c3 ("hw/ide/ahci: simplify and document PxCI handling")
-Reported-by: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org>
-Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
-Message-ID: <20231108222657.117984-1-nks@flawful.org>
-Reviewed-by: Kevin Wolf <kwolf@redhat.com>
-Tested-by: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org>
-Signed-off-by: Kevin Wolf <kwolf@redhat.com>
-(cherry picked from commit eabb921250666501ae78714b60090200b639fcfe)
-Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
-(Mjt: mention 1e5ad6b06b1e for stable-7.2)
----
- hw/ide/ahci.c | 27 ++++++++++++++++++++++++++-
- 1 file changed, 26 insertions(+), 1 deletion(-)
-
-diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
-index c5e79b6e6d..0167ab3680 100644
---- a/hw/ide/ahci.c
-+++ b/hw/ide/ahci.c
-@@ -622,9 +622,13 @@ static void ahci_init_d2h(AHCIDevice *ad)
-         return;
-     }
- 
-+    /*
-+     * For simplicity, do not call ahci_clear_cmd_issue() for this
-+     * ahci_write_fis_d2h(). (The reset value for PxCI is 0.)
-+     */
-     if (ahci_write_fis_d2h(ad, true)) {
-         ad->init_d2h_sent = true;
--        /* We're emulating receiving the first Reg H2D Fis from the device;
-+        /* We're emulating receiving the first Reg D2H FIS from the device;
-          * Update the SIG register, but otherwise proceed as normal. */
-         pr->sig = ((uint32_t)ide_state->hcyl << 24) |
-             (ide_state->lcyl << 16) |
-@@ -662,6 +666,7 @@ static void ahci_reset_port(AHCIState *s, int port)
-     pr->scr_act = 0;
-     pr->tfdata = 0x7F;
-     pr->sig = 0xFFFFFFFF;
-+    pr->cmd_issue = 0;
-     d->busy_slot = -1;
-     d->init_d2h_sent = false;
- 
-@@ -1242,10 +1247,30 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
-         case STATE_RUN:
-             if (cmd_fis[15] & ATA_SRST) {
-                 s->dev[port].port_state = STATE_RESET;
-+                /*
-+                 * When setting SRST in the first H2D FIS in the reset sequence,
-+                 * the device does not send a D2H FIS. Host software thus has to
-+                 * set the "Clear Busy upon R_OK" bit such that PxCI (and BUSY)
-+                 * gets cleared. See AHCI 1.3.1, section 10.4.1 Software Reset.
-+                 */
-+                if (opts & AHCI_CMD_CLR_BUSY) {
-+                    ahci_clear_cmd_issue(ad, slot);
-+                }
-             }
-             break;
-         case STATE_RESET:
-             if (!(cmd_fis[15] & ATA_SRST)) {
-+                /*
-+                 * When clearing SRST in the second H2D FIS in the reset
-+                 * sequence, the device will execute diagnostics. When this is
-+                 * done, the device will send a D2H FIS with the good status.
-+                 * See SATA 3.5a Gold, section 11.4 Software reset protocol.
-+                 *
-+                 * This D2H FIS is the first D2H FIS received from the device,
-+                 * and is received regardless if the reset was performed by a
-+                 * COMRESET or by setting and clearing the SRST bit. Therefore,
-+                 * the logic for this is found in ahci_init_d2h() and not here.
-+                 */
-                 ahci_reset_port(s, port);
-             }
-             break;
--- 
-2.39.2
-
diff -Nru qemu-7.2+dfsg/debian/patches/hw_mips_malta-Fix-malta-machine-on-big-endian-hosts.patch qemu-7.2+dfsg/debian/patches/hw_mips_malta-Fix-malta-machine-on-big-endian-hosts.patch
--- qemu-7.2+dfsg/debian/patches/hw_mips_malta-Fix-malta-machine-on-big-endian-hosts.patch	2023-12-03 14:06:01.000000000 +0300
+++ qemu-7.2+dfsg/debian/patches/hw_mips_malta-Fix-malta-machine-on-big-endian-hosts.patch	1970-01-01 03:00:00.000000000 +0300
@@ -1,42 +0,0 @@
-From: Thomas Huth <thuth@redhat.com>
-Subject: [PATCH] hw/mips/malta: Fix the malta machine on big endian hosts
-Date: Thu, 30 Mar 2023 17:26:13 +0200
-Message-Id: <20230330152613.232082-1-thuth@redhat.com>
-List-Id: <qemu-stable.nongnu.org>
-
-Booting a Linux kernel with the malta machine is currently broken
-on big endian hosts. The cpu_to_gt32 macro wants to byteswap a value
-for little endian targets only, but uses the wrong way to do this:
-cpu_to_[lb]e32 works the other way round on big endian hosts! Fix
-it by using the same ways on both, big and little endian hosts.
-
-Fixes: 0c8427baf0 ("hw/mips/malta: Use bootloader helper to set BAR registers")
-Signed-off-by: Thomas Huth <thuth@redhat.com>
----
- I've checked that both, the kernel from
- https://landley.net/toybox/downloads/binaries/mkroot/0.8.9/mipsel.tgz
- and the kernel from
- https://landley.net/toybox/downloads/binaries/mkroot/0.8.9/mips.tgz
- now boot fine on both, a little endian (x86) and a big endian (s390x) host.
-
- hw/mips/malta.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/hw/mips/malta.c b/hw/mips/malta.c
-index af9021316d..b26ed1fc9a 100644
---- a/hw/mips/malta.c
-+++ b/hw/mips/malta.c
-@@ -630,7 +630,7 @@ static void bl_setup_gt64120_jump_kernel(void **p, uint64_t run_addr,
-     /* Bus endianess is always reversed */
- #if TARGET_BIG_ENDIAN
--#define cpu_to_gt32 cpu_to_le32
-+#define cpu_to_gt32(x) (x)
- #else
--#define cpu_to_gt32 cpu_to_be32
-+#define cpu_to_gt32(x) bswap32(x)
- #endif
- 
--- 
-2.31.1
-
-
diff -Nru qemu-7.2+dfsg/debian/patches/series qemu-7.2+dfsg/debian/patches/series
--- qemu-7.2+dfsg/debian/patches/series	2023-12-03 15:28:41.000000000 +0300
+++ qemu-7.2+dfsg/debian/patches/series	2024-01-30 19:15:04.000000000 +0300
@@ -5,6 +5,8 @@
 v7.2.5.diff
 v7.2.6.diff
 v7.2.7.diff
+v7.2.8.diff
+v7.2.9.diff
 microvm-default-machine-type.patch
 skip-meson-pc-bios.diff
 linux-user-binfmt-P.diff
@@ -21,5 +23,4 @@
 openbios-spelling-endianess.patch
 slof-spelling-seperator.patch
 ignore-roms-dependency-in-qtest.patch
-hw_mips_malta-Fix-malta-machine-on-big-endian-hosts.patch
-hw-ide-ahci-fix-legacy-software-reset.patch
+ui-clipboard-mark-type-as-not-available-when-no-data-CVE-2023-6683.patch
diff -Nru qemu-7.2+dfsg/debian/patches/ui-clipboard-mark-type-as-not-available-when-no-data-CVE-2023-6683.patch qemu-7.2+dfsg/debian/patches/ui-clipboard-mark-type-as-not-available-when-no-data-CVE-2023-6683.patch
--- qemu-7.2+dfsg/debian/patches/ui-clipboard-mark-type-as-not-available-when-no-data-CVE-2023-6683.patch	1970-01-01 03:00:00.000000000 +0300
+++ qemu-7.2+dfsg/debian/patches/ui-clipboard-mark-type-as-not-available-when-no-data-CVE-2023-6683.patch	2024-01-30 19:15:04.000000000 +0300
@@ -0,0 +1,82 @@
+From: Fiona Ebner <f.ebner@proxmox.com>
+Date: Wed, 24 Jan 2024 11:57:48 +0100
+Subject: ui/clipboard: mark type as not available when there is no data
+Forwarded: yes
+Origin: upstream, https://lists.nongnu.org/archive/html/qemu-devel/2024-01/msg04742.html
+
+With VNC, a client can send a non-extended VNC_MSG_CLIENT_CUT_TEXT
+message with len=0. In qemu_clipboard_set_data(), the clipboard info
+will be updated setting data to NULL (because g_memdup(data, size)
+returns NULL when size is 0). If the client does not set the
+VNC_ENCODING_CLIPBOARD_EXT feature when setting up the encodings, then
+the 'request' callback for the clipboard peer is not initialized.
+Later, because data is NULL, qemu_clipboard_request() can be reached
+via vdagent_chr_write() and vdagent_clipboard_recv_request() and
+there, the clipboard owner's 'request' callback will be attempted to
+be called, but that is a NULL pointer.
+
+In particular, this can happen when using the KRDC (22.12.3) VNC
+client.
+
+Another scenario leading to the same issue is with two clients (say
+noVNC and KRDC):
+
+The noVNC client sets the extension VNC_FEATURE_CLIPBOARD_EXT and
+initializes its cbpeer.
+
+The KRDC client does not, but triggers a vnc_client_cut_text() (note
+it's not the _ext variant)). There, a new clipboard info with it as
+the 'owner' is created and via qemu_clipboard_set_data() is called,
+which in turn calls qemu_clipboard_update() with that info.
+
+In qemu_clipboard_update(), the notifier for the noVNC client will be
+called, i.e. vnc_clipboard_notify() and also set vs->cbinfo for the
+noVNC client. The 'owner' in that clipboard info is the clipboard peer
+for the KRDC client, which did not initialize the 'request' function.
+That sounds correct to me, it is the owner of that clipboard info.
+
+Then when noVNC sends a VNC_MSG_CLIENT_CUT_TEXT message (it did set
+the VNC_FEATURE_CLIPBOARD_EXT feature correctly, so a check for it
+passes), that clipboard info is passed to qemu_clipboard_request() and
+the original segfault still happens.
+
+Fix the issue by handling updates with size 0 differently. In
+particular, mark in the clipboard info that the type is not available.
+
+While at it, switch to g_memdup2(), because g_memdup() is deprecated.
+
+Cc: qemu-stable@nongnu.org
+Fixes: CVE-2023-6683
+Reported-by: Markus Frank <m.frank@proxmox.com>
+Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ ui/clipboard.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/ui/clipboard.c b/ui/clipboard.c
+index 3d14bffaf8..b3f6fa3c9e 100644
+--- a/ui/clipboard.c
++++ b/ui/clipboard.c
+@@ -163,9 +163,15 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer,
+     }
+ 
+     g_free(info->types[type].data);
+-    info->types[type].data = g_memdup(data, size);
+-    info->types[type].size = size;
+-    info->types[type].available = true;
++    if (size) {
++        info->types[type].data = g_memdup2(data, size);
++        info->types[type].size = size;
++        info->types[type].available = true;
++    } else {
++        info->types[type].data = NULL;
++        info->types[type].size = 0;
++        info->types[type].available = false;
++    }
+ 
+     if (update) {
+         qemu_clipboard_update(info);
+-- 
+2.39.2
+
diff -Nru qemu-7.2+dfsg/debian/patches/v7.2.8.diff qemu-7.2+dfsg/debian/patches/v7.2.8.diff
--- qemu-7.2+dfsg/debian/patches/v7.2.8.diff	1970-01-01 03:00:00.000000000 +0300
+++ qemu-7.2+dfsg/debian/patches/v7.2.8.diff	2024-01-30 19:08:55.000000000 +0300
@@ -0,0 +1,1524 @@
+Subject: v7.2.8
+Date: Fri Dec 22 21:59:29 2023 +0300
+From: Michael Tokarev <mjt@tls.msk.ru>
+Forwarded: not-needed
+
+This is a difference between upstream qemu v7.2.7
+and upstream qemu v7.2.8.
+
+ VERSION                                 |  2 +-
+ block/vmdk.c                            | 28 +++++++---
+ docs/devel/testing.rst                  |  4 +-
+ hw/acpi/erst.c                          | 10 ++++
+ hw/audio/hda-codec.c                    | 29 +++++++---
+ hw/ide/ahci.c                           | 27 ++++++++-
+ hw/mips/malta.c                         |  4 +-
+ hw/misc/mps2-scc.c                      |  8 +++
+ hw/net/allwinner-sun8i-emac.c           |  3 +-
+ hw/net/allwinner_emac.c                 |  3 +-
+ hw/net/cadence_gem.c                    |  3 +-
+ hw/net/dp8393x.c                        |  3 +-
+ hw/net/e1000.c                          |  3 +-
+ hw/net/e1000e.c                         |  2 +-
+ hw/net/eepro100.c                       |  4 +-
+ hw/net/etraxfs_eth.c                    |  3 +-
+ hw/net/fsl_etsec/etsec.c                |  3 +-
+ hw/net/ftgmac100.c                      |  3 +-
+ hw/net/i82596.c                         |  2 +-
+ hw/net/imx_fec.c                        |  2 +-
+ hw/net/lan9118.c                        |  3 +-
+ hw/net/mcf_fec.c                        |  3 +-
+ hw/net/mipsnet.c                        |  3 +-
+ hw/net/msf2-emac.c                      |  3 +-
+ hw/net/mv88w8618_eth.c                  |  3 +-
+ hw/net/ne2000-isa.c                     |  3 +-
+ hw/net/ne2000-pci.c                     |  3 +-
+ hw/net/npcm7xx_emc.c                    |  3 +-
+ hw/net/opencores_eth.c                  |  3 +-
+ hw/net/pcnet.c                          |  3 +-
+ hw/net/rocker/rocker_fp.c               |  4 +-
+ hw/net/rtl8139.c                        |  3 +-
+ hw/net/smc91c111.c                      |  3 +-
+ hw/net/spapr_llan.c                     |  3 +-
+ hw/net/stellaris_enet.c                 |  3 +-
+ hw/net/sungem.c                         |  2 +-
+ hw/net/sunhme.c                         |  3 +-
+ hw/net/tulip.c                          |  3 +-
+ hw/net/virtio-net.c                     |  6 +-
+ hw/net/vmxnet3.c                        |  2 +-
+ hw/net/xen_nic.c                        |  3 +-
+ hw/net/xgmac.c                          |  3 +-
+ hw/net/xilinx_axienet.c                 |  3 +-
+ hw/net/xilinx_ethlite.c                 |  3 +-
+ hw/nvram/xlnx-efuse.c                   |  8 +++
+ hw/nvram/xlnx-versal-efuse-ctrl.c       |  8 +++
+ hw/pci/msix.c                           |  1 +
+ hw/pci/pcie_sriov.c                     |  1 -
+ hw/usb/dev-network.c                    |  3 +-
+ hw/virtio/virtio-iommu-pci.c            |  8 +++
+ hw/virtio/virtio-pci.c                  |  1 +
+ include/hw/virtio/virtio-pci.h          |  1 +
+ include/net/net.h                       |  2 +
+ linux-user/elfload.c                    |  2 +-
+ net/net.c                               | 15 +++++
+ softmmu/memory.c                        | 32 +----------
+ target/arm/cpu.c                        | 10 ++++
+ target/arm/helper.c                     | 22 +++++++-
+ target/arm/sme_helper.c                 | 10 ++--
+ target/arm/syndrome.h                   |  6 +-
+ tests/avocado/cpu_queries.py            |  2 +-
+ tests/avocado/empty_cpu_model.py        |  2 +-
+ tests/avocado/pc_cpu_hotplug_props.py   |  2 +-
+ tests/avocado/version.py                |  2 +-
+ tests/avocado/x86_cpu_model_versions.py | 97 +++++++++++++++++----------------
+ tests/qemu-iotests/059                  |  2 +
+ tests/qemu-iotests/059.out              |  4 ++
+ ui/gtk-egl.c                            |  7 +++
+ ui/vnc-clipboard.c                      |  5 ++
+ 69 files changed, 317 insertions(+), 156 deletions(-)
+
+diff --git a/VERSION b/VERSION
+index 4afc54e7b7..31554632ab 100644
+--- a/VERSION
++++ b/VERSION
+@@ -1 +1 @@
+-7.2.7
++7.2.8
+diff --git a/block/vmdk.c b/block/vmdk.c
+index 26376352b9..f8d3a13568 100644
+--- a/block/vmdk.c
++++ b/block/vmdk.c
+@@ -346,29 +346,41 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
+     BDRVVmdkState *s = bs->opaque;
+     int ret = 0;
+ 
+-    desc = g_malloc0(DESC_SIZE);
+-    tmp_desc = g_malloc0(DESC_SIZE);
+-    ret = bdrv_pread(bs->file, s->desc_offset, DESC_SIZE, desc, 0);
++    size_t desc_buf_size;
++
++    if (s->desc_offset == 0) {
++        desc_buf_size = bdrv_getlength(bs->file->bs);
++        if (desc_buf_size > 16ULL << 20) {
++            error_report("VMDK description file too big");
++            return -EFBIG;
++        }
++    } else {
++        desc_buf_size = DESC_SIZE;
++    }
++
++    desc = g_malloc0(desc_buf_size);
++    tmp_desc = g_malloc0(desc_buf_size);
++    ret = bdrv_pread(bs->file, s->desc_offset, desc_buf_size, desc, 0);
+     if (ret < 0) {
+         goto out;
+     }
+ 
+-    desc[DESC_SIZE - 1] = '\0';
++    desc[desc_buf_size - 1] = '\0';
+     tmp_str = strstr(desc, "parentCID");
+     if (tmp_str == NULL) {
+         ret = -EINVAL;
+         goto out;
+     }
+ 
+-    pstrcpy(tmp_desc, DESC_SIZE, tmp_str);
++    pstrcpy(tmp_desc, desc_buf_size, tmp_str);
+     p_name = strstr(desc, "CID");
+     if (p_name != NULL) {
+         p_name += sizeof("CID");
+-        snprintf(p_name, DESC_SIZE - (p_name - desc), "%" PRIx32 "\n", cid);
+-        pstrcat(desc, DESC_SIZE, tmp_desc);
++        snprintf(p_name, desc_buf_size - (p_name - desc), "%" PRIx32 "\n", cid);
++        pstrcat(desc, desc_buf_size, tmp_desc);
+     }
+ 
+-    ret = bdrv_pwrite_sync(bs->file, s->desc_offset, DESC_SIZE, desc, 0);
++    ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc_buf_size, desc, 0);
+ 
+ out:
+     g_free(desc);
+diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst
+index e10c47b5a7..98c26ecf18 100644
+--- a/docs/devel/testing.rst
++++ b/docs/devel/testing.rst
+@@ -990,7 +990,7 @@ class.  Here's a simple usage example:
+           self.vm.launch()
+           res = self.vm.command('human-monitor-command',
+                                 command_line='info version')
+-          self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)')
++          self.assertRegex(res, r'^(\d+\.\d+\.\d)')
+ 
+ To execute your test, run:
+ 
+@@ -1051,7 +1051,7 @@ and hypothetical example follows:
+               'human-monitor-command',
+               command_line='info version')
+ 
+-          self.assertEquals(first_res, second_res, third_res)
++          self.assertEqual(first_res, second_res, third_res)
+ 
+ At test "tear down", ``avocado_qemu.Test`` handles all the QEMUMachines
+ shutdown.
+diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c
+index aefcc03ad6..2e057b1800 100644
+--- a/hw/acpi/erst.c
++++ b/hw/acpi/erst.c
+@@ -947,6 +947,7 @@ static const VMStateDescription erst_vmstate  = {
+ 
+ static void erst_realizefn(PCIDevice *pci_dev, Error **errp)
+ {
++    ERRP_GUARD();
+     ERSTDeviceState *s = ACPIERST(pci_dev);
+ 
+     trace_acpi_erst_realizefn_in();
+@@ -964,9 +965,15 @@ static void erst_realizefn(PCIDevice *pci_dev, Error **errp)
+ 
+     /* HostMemoryBackend size will be multiple of PAGE_SIZE */
+     s->storage_size = object_property_get_int(OBJECT(s->hostmem), "size", errp);
++    if (*errp) {
++        return;
++    }
+ 
+     /* Initialize backend storage and record_count */
+     check_erst_backend_storage(s, errp);
++    if (*errp) {
++        return;
++    }
+ 
+     /* BAR 0: Programming registers */
+     memory_region_init_io(&s->iomem_mr, OBJECT(pci_dev), &erst_reg_ops, s,
+@@ -977,6 +984,9 @@ static void erst_realizefn(PCIDevice *pci_dev, Error **errp)
+     memory_region_init_ram(&s->exchange_mr, OBJECT(pci_dev),
+                             "erst.exchange",
+                             le32_to_cpu(s->header->record_size), errp);
++    if (*errp) {
++        return;
++    }
+     pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY,
+                         &s->exchange_mr);
+ 
+diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
+index feb8f9e2bb..0f66754b6a 100644
+--- a/hw/audio/hda-codec.c
++++ b/hw/audio/hda-codec.c
+@@ -22,6 +22,7 @@
+ #include "hw/qdev-properties.h"
+ #include "intel-hda.h"
+ #include "migration/vmstate.h"
++#include "qemu/host-utils.h"
+ #include "qemu/module.h"
+ #include "intel-hda-defs.h"
+ #include "audio/audio.h"
+@@ -190,9 +191,9 @@ struct HDAAudioState {
+     bool     use_timer;
+ };
+ 
+-static inline int64_t hda_bytes_per_second(HDAAudioStream *st)
++static inline uint32_t hda_bytes_per_second(HDAAudioStream *st)
+ {
+-    return 2LL * st->as.nchannels * st->as.freq;
++    return 2 * (uint32_t)st->as.nchannels * (uint32_t)st->as.freq;
+ }
+ 
+ static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
+@@ -223,12 +224,18 @@ static void hda_audio_input_timer(void *opaque)
+ 
+     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ 
+-    int64_t buft_start = st->buft_start;
++    int64_t uptime = now - st->buft_start;
+     int64_t wpos = st->wpos;
+     int64_t rpos = st->rpos;
++    int64_t wanted_rpos;
+ 
+-    int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start)
+-                          / NANOSECONDS_PER_SECOND;
++    if (uptime <= 0) {
++        /* wanted_rpos <= 0 */
++        goto out_timer;
++    }
++
++    wanted_rpos = muldiv64(uptime, hda_bytes_per_second(st),
++                           NANOSECONDS_PER_SECOND);
+     wanted_rpos &= -4; /* IMPORTANT! clip to frames */
+ 
+     if (wanted_rpos <= rpos) {
+@@ -287,12 +294,18 @@ static void hda_audio_output_timer(void *opaque)
+ 
+     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ 
+-    int64_t buft_start = st->buft_start;
++    int64_t uptime = now - st->buft_start;
+     int64_t wpos = st->wpos;
+     int64_t rpos = st->rpos;
++    int64_t wanted_wpos;
++
++    if (uptime <= 0) {
++        /* wanted_wpos <= 0 */
++        goto out_timer;
++    }
+ 
+-    int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start)
+-                          / NANOSECONDS_PER_SECOND;
++    wanted_wpos = muldiv64(uptime, hda_bytes_per_second(st),
++                           NANOSECONDS_PER_SECOND);
+     wanted_wpos &= -4; /* IMPORTANT! clip to frames */
+ 
+     if (wanted_wpos <= wpos) {
+diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
+index c5e79b6e6d..0167ab3680 100644
+--- a/hw/ide/ahci.c
++++ b/hw/ide/ahci.c
+@@ -622,9 +622,13 @@ static void ahci_init_d2h(AHCIDevice *ad)
+         return;
+     }
+ 
++    /*
++     * For simplicity, do not call ahci_clear_cmd_issue() for this
++     * ahci_write_fis_d2h(). (The reset value for PxCI is 0.)
++     */
+     if (ahci_write_fis_d2h(ad, true)) {
+         ad->init_d2h_sent = true;
+-        /* We're emulating receiving the first Reg H2D Fis from the device;
++        /* We're emulating receiving the first Reg D2H FIS from the device;
+          * Update the SIG register, but otherwise proceed as normal. */
+         pr->sig = ((uint32_t)ide_state->hcyl << 24) |
+             (ide_state->lcyl << 16) |
+@@ -662,6 +666,7 @@ static void ahci_reset_port(AHCIState *s, int port)
+     pr->scr_act = 0;
+     pr->tfdata = 0x7F;
+     pr->sig = 0xFFFFFFFF;
++    pr->cmd_issue = 0;
+     d->busy_slot = -1;
+     d->init_d2h_sent = false;
+ 
+@@ -1242,10 +1247,30 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
+         case STATE_RUN:
+             if (cmd_fis[15] & ATA_SRST) {
+                 s->dev[port].port_state = STATE_RESET;
++                /*
++                 * When setting SRST in the first H2D FIS in the reset sequence,
++                 * the device does not send a D2H FIS. Host software thus has to
++                 * set the "Clear Busy upon R_OK" bit such that PxCI (and BUSY)
++                 * gets cleared. See AHCI 1.3.1, section 10.4.1 Software Reset.
++                 */
++                if (opts & AHCI_CMD_CLR_BUSY) {
++                    ahci_clear_cmd_issue(ad, slot);
++                }
+             }
+             break;
+         case STATE_RESET:
+             if (!(cmd_fis[15] & ATA_SRST)) {
++                /*
++                 * When clearing SRST in the second H2D FIS in the reset
++                 * sequence, the device will execute diagnostics. When this is
++                 * done, the device will send a D2H FIS with the good status.
++                 * See SATA 3.5a Gold, section 11.4 Software reset protocol.
++                 *
++                 * This D2H FIS is the first D2H FIS received from the device,
++                 * and is received regardless if the reset was performed by a
++                 * COMRESET or by setting and clearing the SRST bit. Therefore,
++                 * the logic for this is found in ahci_init_d2h() and not here.
++                 */
+                 ahci_reset_port(s, port);
+             }
+             break;
+diff --git a/hw/mips/malta.c b/hw/mips/malta.c
+index c0a2e0ab04..da7c110b73 100644
+--- a/hw/mips/malta.c
++++ b/hw/mips/malta.c
+@@ -877,9 +877,9 @@ static void write_bootloader(uint8_t *base, uint64_t run_addr,
+ 
+     /* Bus endianess is always reversed */
+ #if TARGET_BIG_ENDIAN
+-#define cpu_to_gt32 cpu_to_le32
++#define cpu_to_gt32(x) (x)
+ #else
+-#define cpu_to_gt32 cpu_to_be32
++#define cpu_to_gt32(x) bswap32(x)
+ #endif
+ 
+     /* move GT64120 registers from 0x14000000 to 0x1be00000 */
+diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c
+index b3b42a792c..fe5034db14 100644
+--- a/hw/misc/mps2-scc.c
++++ b/hw/misc/mps2-scc.c
+@@ -329,6 +329,13 @@ static void mps2_scc_realize(DeviceState *dev, Error **errp)
+     s->oscclk = g_new0(uint32_t, s->num_oscclk);
+ }
+ 
++static void mps2_scc_finalize(Object *obj)
++{
++    MPS2SCC *s = MPS2_SCC(obj);
++
++    g_free(s->oscclk_reset);
++}
++
+ static const VMStateDescription mps2_scc_vmstate = {
+     .name = "mps2-scc",
+     .version_id = 3,
+@@ -385,6 +392,7 @@ static const TypeInfo mps2_scc_info = {
+     .parent = TYPE_SYS_BUS_DEVICE,
+     .instance_size = sizeof(MPS2SCC),
+     .instance_init = mps2_scc_init,
++    .instance_finalize = mps2_scc_finalize,
+     .class_init = mps2_scc_class_init,
+ };
+ 
+diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c
+index c3fed5fcbe..1a6a79f5ae 100644
+--- a/hw/net/allwinner-sun8i-emac.c
++++ b/hw/net/allwinner-sun8i-emac.c
+@@ -824,7 +824,8 @@ static void allwinner_sun8i_emac_realize(DeviceState *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_allwinner_sun8i_emac_info, &s->conf,
+-                           object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
+index ddddf35c45..b3d73143bf 100644
+--- a/hw/net/allwinner_emac.c
++++ b/hw/net/allwinner_emac.c
+@@ -453,7 +453,8 @@ static void aw_emac_realize(DeviceState *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ 
+     fifo8_create(&s->rx_fifo, RX_FIFO_SIZE);
+diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
+index 24b3a0ff66..cb61a76417 100644
+--- a/hw/net/cadence_gem.c
++++ b/hw/net/cadence_gem.c
+@@ -1633,7 +1633,8 @@ static void gem_realize(DeviceState *dev, Error **errp)
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ 
+     s->nic = qemu_new_nic(&net_gem_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+ 
+     if (s->jumbo_max_len > MAX_FRAME_SIZE) {
+         error_setg(errp, "jumbo-max-len is greater than %d",
+diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c
+index 45b954e46c..abfcc6f69f 100644
+--- a/hw/net/dp8393x.c
++++ b/hw/net/dp8393x.c
+@@ -943,7 +943,8 @@ static void dp8393x_realize(DeviceState *dev, Error **errp)
+                           "dp8393x-regs", SONIC_REG_COUNT << s->it_shift);
+ 
+     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ 
+     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
+diff --git a/hw/net/e1000.c b/hw/net/e1000.c
+index 0dfdf47313..0a78ad3a58 100644
+--- a/hw/net/e1000.c
++++ b/hw/net/e1000.c
+@@ -1735,7 +1735,8 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp)
+                                macaddr);
+ 
+     d->nic = qemu_new_nic(&net_e1000_info, &d->conf,
+-                          object_get_typename(OBJECT(d)), dev->id, d);
++                          object_get_typename(OBJECT(d)), dev->id,
++                          &dev->mem_reentrancy_guard, d);
+ 
+     qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr);
+ 
+diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
+index 7523e9f5d2..6573cc3cc3 100644
+--- a/hw/net/e1000e.c
++++ b/hw/net/e1000e.c
+@@ -319,7 +319,7 @@ e1000e_init_net_peer(E1000EState *s, PCIDevice *pci_dev, uint8_t *macaddr)
+     int i;
+ 
+     s->nic = qemu_new_nic(&net_e1000e_info, &s->conf,
+-        object_get_typename(OBJECT(s)), dev->id, s);
++        object_get_typename(OBJECT(s)), dev->id, &dev->mem_reentrancy_guard, s);
+ 
+     s->core.max_queue_num = s->conf.peers.queues ? s->conf.peers.queues - 1 : 0;
+ 
+diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
+index 679f52f80f..871d9a0950 100644
+--- a/hw/net/eepro100.c
++++ b/hw/net/eepro100.c
+@@ -1874,7 +1874,9 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
+     nic_reset(s);
+ 
+     s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
+-                          object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
++                          object_get_typename(OBJECT(pci_dev)),
++                          pci_dev->qdev.id,
++                          &pci_dev->qdev.mem_reentrancy_guard, s);
+ 
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+     TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
+diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c
+index 1b82aec794..ba57a978d1 100644
+--- a/hw/net/etraxfs_eth.c
++++ b/hw/net/etraxfs_eth.c
+@@ -618,7 +618,8 @@ static void etraxfs_eth_realize(DeviceState *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf,
+-                          object_get_typename(OBJECT(s)), dev->id, s);
++                          object_get_typename(OBJECT(s)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ 
+     s->phy.read = tdk_read;
+diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
+index b75d8e3dce..102ba69658 100644
+--- a/hw/net/fsl_etsec/etsec.c
++++ b/hw/net/fsl_etsec/etsec.c
+@@ -390,7 +390,8 @@ static void etsec_realize(DeviceState *dev, Error **errp)
+     eTSEC        *etsec = ETSEC_COMMON(dev);
+ 
+     etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf,
+-                              object_get_typename(OBJECT(dev)), dev->id, etsec);
++                              object_get_typename(OBJECT(dev)), dev->id,
++                              &dev->mem_reentrancy_guard, etsec);
+     qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a);
+ 
+     etsec->ptimer = ptimer_init(etsec_timer_hit, etsec, PTIMER_POLICY_LEGACY);
+diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
+index 83ef0a783e..346485ab49 100644
+--- a/hw/net/ftgmac100.c
++++ b/hw/net/ftgmac100.c
+@@ -1118,7 +1118,8 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp)
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ 
+     s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/net/i82596.c b/hw/net/i82596.c
+index ec21e2699a..dc64246f75 100644
+--- a/hw/net/i82596.c
++++ b/hw/net/i82596.c
+@@ -743,7 +743,7 @@ void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *info)
+         qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     }
+     s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)),
+-                dev->id, s);
++                dev->id, &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ 
+     if (USE_TIMER) {
+diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
+index 8c11b237de..7eb2fef626 100644
+--- a/hw/net/imx_fec.c
++++ b/hw/net/imx_fec.c
+@@ -1318,7 +1318,7 @@ static void imx_eth_realize(DeviceState *dev, Error **errp)
+ 
+     s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf,
+                           object_get_typename(OBJECT(dev)),
+-                          dev->id, s);
++                          dev->id, &dev->mem_reentrancy_guard, s);
+ 
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
+index f1cba55967..00a6d82efb 100644
+--- a/hw/net/lan9118.c
++++ b/hw/net/lan9118.c
+@@ -1362,7 +1362,8 @@ static void lan9118_realize(DeviceState *dev, Error **errp)
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ 
+     s->nic = qemu_new_nic(&net_lan9118_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+     s->eeprom[0] = 0xa5;
+     for (i = 0; i < 6; i++) {
+diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c
+index 8aa27bd322..57dd49abea 100644
+--- a/hw/net/mcf_fec.c
++++ b/hw/net/mcf_fec.c
+@@ -643,7 +643,8 @@ static void mcf_fec_realize(DeviceState *dev, Error **errp)
+     mcf_fec_state *s = MCF_FEC_NET(dev);
+ 
+     s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c
+index 2ade72dea0..8e925de867 100644
+--- a/hw/net/mipsnet.c
++++ b/hw/net/mipsnet.c
+@@ -255,7 +255,8 @@ static void mipsnet_realize(DeviceState *dev, Error **errp)
+     sysbus_init_irq(sbd, &s->irq);
+ 
+     s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c
+index db3a04deb1..145a5e46ab 100644
+--- a/hw/net/msf2-emac.c
++++ b/hw/net/msf2-emac.c
+@@ -530,7 +530,8 @@ static void msf2_emac_realize(DeviceState *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_msf2_emac_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/net/mv88w8618_eth.c b/hw/net/mv88w8618_eth.c
+index ef30b0d4a6..2185f1131a 100644
+--- a/hw/net/mv88w8618_eth.c
++++ b/hw/net/mv88w8618_eth.c
+@@ -350,7 +350,8 @@ static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
+ 
+     address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
+     s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+ }
+ 
+ static const VMStateDescription mv88w8618_eth_vmsd = {
+diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
+index 6ced6775ff..a79f7fad1f 100644
+--- a/hw/net/ne2000-isa.c
++++ b/hw/net/ne2000-isa.c
+@@ -74,7 +74,8 @@ static void isa_ne2000_realizefn(DeviceState *dev, Error **errp)
+     ne2000_reset(s);
+ 
+     s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
+ }
+ 
+diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c
+index 9e5d10859a..4f8a699081 100644
+--- a/hw/net/ne2000-pci.c
++++ b/hw/net/ne2000-pci.c
+@@ -71,7 +71,8 @@ static void pci_ne2000_realize(PCIDevice *pci_dev, Error **errp)
+ 
+     s->nic = qemu_new_nic(&net_ne2000_info, &s->c,
+                           object_get_typename(OBJECT(pci_dev)),
+-                          pci_dev->qdev.id, s);
++                          pci_dev->qdev.id,
++                          &pci_dev->qdev.mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
+ }
+ 
+diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c
+index 7c86bb52e5..4bb4e7147d 100644
+--- a/hw/net/npcm7xx_emc.c
++++ b/hw/net/npcm7xx_emc.c
+@@ -803,7 +803,8 @@ static void npcm7xx_emc_realize(DeviceState *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&emc->conf.macaddr);
+     emc->nic = qemu_new_nic(&net_npcm7xx_emc_info, &emc->conf,
+-                            object_get_typename(OBJECT(dev)), dev->id, emc);
++                            object_get_typename(OBJECT(dev)), dev->id,
++                            &dev->mem_reentrancy_guard, emc);
+     qemu_format_nic_info_str(qemu_get_queue(emc->nic), emc->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c
+index 0b3dc3146e..f96d6ea2cc 100644
+--- a/hw/net/opencores_eth.c
++++ b/hw/net/opencores_eth.c
+@@ -732,7 +732,8 @@ static void sysbus_open_eth_realize(DeviceState *dev, Error **errp)
+     sysbus_init_irq(sbd, &s->irq);
+ 
+     s->nic = qemu_new_nic(&net_open_eth_info, &s->conf,
+-                          object_get_typename(OBJECT(s)), dev->id, s);
++                          object_get_typename(OBJECT(s)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+ }
+ 
+ static void qdev_open_eth_reset(DeviceState *dev)
+diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c
+index e63e524913..56c3d14ad6 100644
+--- a/hw/net/pcnet.c
++++ b/hw/net/pcnet.c
+@@ -1718,7 +1718,8 @@ void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
+     s->poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pcnet_poll_timer, s);
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+-    s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s);
++    s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)),
++                          dev->id, &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ 
+     /* Initialize the PROM */
+diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c
+index cbeed65bd5..0d21948ada 100644
+--- a/hw/net/rocker/rocker_fp.c
++++ b/hw/net/rocker/rocker_fp.c
+@@ -241,8 +241,8 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name,
+     port->conf.bootindex = -1;
+     port->conf.peers = *peers;
+ 
+-    port->nic = qemu_new_nic(&fp_port_info, &port->conf,
+-                             sw_name, NULL, port);
++    port->nic = qemu_new_nic(&fp_port_info, &port->conf, sw_name, NULL,
++                             &DEVICE(r)->mem_reentrancy_guard, port);
+     qemu_format_nic_info_str(qemu_get_queue(port->nic),
+                              port->conf.macaddr.a);
+ 
+diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
+index eb679d7c40..a4462af431 100644
+--- a/hw/net/rtl8139.c
++++ b/hw/net/rtl8139.c
+@@ -3400,7 +3400,8 @@ static void pci_rtl8139_realize(PCIDevice *dev, Error **errp)
+     s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
+ 
+     s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), d->id, s);
++                          object_get_typename(OBJECT(dev)), d->id,
++                          &d->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ 
+     s->cplus_txbuffer = NULL;
+diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c
+index ad778cd8fc..4eda971ef3 100644
+--- a/hw/net/smc91c111.c
++++ b/hw/net/smc91c111.c
+@@ -783,7 +783,8 @@ static void smc91c111_realize(DeviceState *dev, Error **errp)
+     sysbus_init_irq(sbd, &s->irq);
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+     /* ??? Save/restore.  */
+ }
+diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c
+index a6876a936d..475d5f3a34 100644
+--- a/hw/net/spapr_llan.c
++++ b/hw/net/spapr_llan.c
+@@ -325,7 +325,8 @@ static void spapr_vlan_realize(SpaprVioDevice *sdev, Error **errp)
+     memcpy(&dev->perm_mac.a, &dev->nicconf.macaddr.a, sizeof(dev->perm_mac.a));
+ 
+     dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf,
+-                            object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev);
++                            object_get_typename(OBJECT(sdev)), sdev->qdev.id,
++                            &sdev->qdev.mem_reentrancy_guard, dev);
+     qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a);
+ 
+     dev->rxp_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, spapr_vlan_flush_rx_queue,
+diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
+index 8dd60783d8..6768a6912f 100644
+--- a/hw/net/stellaris_enet.c
++++ b/hw/net/stellaris_enet.c
+@@ -492,7 +492,8 @@ static void stellaris_enet_realize(DeviceState *dev, Error **errp)
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ 
+     s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/net/sungem.c b/hw/net/sungem.c
+index 3684a4d733..c12d44e9dc 100644
+--- a/hw/net/sungem.c
++++ b/hw/net/sungem.c
+@@ -1361,7 +1361,7 @@ static void sungem_realize(PCIDevice *pci_dev, Error **errp)
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_sungem_info, &s->conf,
+                           object_get_typename(OBJECT(dev)),
+-                          dev->id, s);
++                          dev->id, &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic),
+                              s->conf.macaddr.a);
+ }
+diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c
+index fc34905f87..fa98528d71 100644
+--- a/hw/net/sunhme.c
++++ b/hw/net/sunhme.c
+@@ -892,7 +892,8 @@ static void sunhme_realize(PCIDevice *pci_dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_sunhme_info, &s->conf,
+-                          object_get_typename(OBJECT(d)), d->id, s);
++                          object_get_typename(OBJECT(d)), d->id,
++                          &d->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/net/tulip.c b/hw/net/tulip.c
+index c2b3b1bdfa..956093abd7 100644
+--- a/hw/net/tulip.c
++++ b/hw/net/tulip.c
+@@ -983,7 +983,8 @@ static void pci_tulip_realize(PCIDevice *pci_dev, Error **errp)
+ 
+     s->nic = qemu_new_nic(&net_tulip_info, &s->c,
+                           object_get_typename(OBJECT(pci_dev)),
+-                          pci_dev->qdev.id, s);
++                          pci_dev->qdev.id,
++                          &pci_dev->qdev.mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
+ }
+ 
+diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
+index 1b10cdc127..06f35ac2d8 100644
+--- a/hw/net/virtio-net.c
++++ b/hw/net/virtio-net.c
+@@ -3633,10 +3633,12 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
+          * Happen when virtio_net_set_netclient_name has been called.
+          */
+         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
+-                              n->netclient_type, n->netclient_name, n);
++                              n->netclient_type, n->netclient_name,
++                              &dev->mem_reentrancy_guard, n);
+     } else {
+         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
+-                              object_get_typename(OBJECT(dev)), dev->id, n);
++                              object_get_typename(OBJECT(dev)), dev->id,
++                              &dev->mem_reentrancy_guard, n);
+     }
+ 
+     for (i = 0; i < n->max_queue_pairs; i++) {
+diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
+index 399fc14129..e49b4a7a6c 100644
+--- a/hw/net/vmxnet3.c
++++ b/hw/net/vmxnet3.c
+@@ -2083,7 +2083,7 @@ static void vmxnet3_net_init(VMXNET3State *s)
+ 
+     s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf,
+                           object_get_typename(OBJECT(s)),
+-                          d->id, s);
++                          d->id, &d->mem_reentrancy_guard, s);
+ 
+     s->peer_has_vhdr = vmxnet3_peer_has_vnet_hdr(s);
+     s->tx_sop = true;
+diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
+index 7d92c2d022..1014e84518 100644
+--- a/hw/net/xen_nic.c
++++ b/hw/net/xen_nic.c
+@@ -294,7 +294,8 @@ static int net_init(struct XenLegacyDevice *xendev)
+     }
+ 
+     netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
+-                               "xen", NULL, netdev);
++                               "xen", NULL,
++                               &xendev->qdev.mem_reentrancy_guard, netdev);
+ 
+     qemu_set_info_str(qemu_get_queue(netdev->nic),
+                       "nic: xenbus vif macaddr=%s", netdev->mac);
+diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c
+index 0ab6ae91aa..1f4f277d84 100644
+--- a/hw/net/xgmac.c
++++ b/hw/net/xgmac.c
+@@ -402,7 +402,8 @@ static void xgmac_enet_realize(DeviceState *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ 
+     s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) |
+diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
+index 990ff3a1c2..8a34243803 100644
+--- a/hw/net/xilinx_axienet.c
++++ b/hw/net/xilinx_axienet.c
+@@ -968,7 +968,8 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ 
+     tdk_init(&s->TEMAC.phy);
+diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
+index 6e09f7e422..80cb869e22 100644
+--- a/hw/net/xilinx_ethlite.c
++++ b/hw/net/xilinx_ethlite.c
+@@ -235,7 +235,8 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
+-                          object_get_typename(OBJECT(dev)), dev->id, s);
++                          object_get_typename(OBJECT(dev)), dev->id,
++                          &dev->mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ }
+ 
+diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c
+index fdfffaab99..aff5254129 100644
+--- a/hw/nvram/xlnx-efuse.c
++++ b/hw/nvram/xlnx-efuse.c
+@@ -217,6 +217,13 @@ static void efuse_realize(DeviceState *dev, Error **errp)
+     }
+ }
+ 
++static void efuse_finalize(Object *obj)
++{
++    XlnxEFuse *s = XLNX_EFUSE(obj);
++
++    g_free(s->ro_bits);
++}
++
+ static void efuse_prop_set_drive(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
+ {
+@@ -273,6 +280,7 @@ static const TypeInfo efuse_info = {
+     .name          = TYPE_XLNX_EFUSE,
+     .parent        = TYPE_DEVICE,
+     .instance_size = sizeof(XlnxEFuse),
++    .instance_finalize = efuse_finalize,
+     .class_init    = efuse_class_init,
+ };
+ 
+diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c
+index b35ba65ab5..2d2dc09526 100644
+--- a/hw/nvram/xlnx-versal-efuse-ctrl.c
++++ b/hw/nvram/xlnx-versal-efuse-ctrl.c
+@@ -725,6 +725,13 @@ static void efuse_ctrl_init(Object *obj)
+     sysbus_init_irq(sbd, &s->irq_efuse_imr);
+ }
+ 
++static void efuse_ctrl_finalize(Object *obj)
++{
++    XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj);
++
++    g_free(s->extra_pg0_lock_spec);
++}
++
+ static const VMStateDescription vmstate_efuse_ctrl = {
+     .name = TYPE_XLNX_VERSAL_EFUSE_CTRL,
+     .version_id = 1,
+@@ -762,6 +769,7 @@ static const TypeInfo efuse_ctrl_info = {
+     .instance_size = sizeof(XlnxVersalEFuseCtrl),
+     .class_init    = efuse_ctrl_class_init,
+     .instance_init = efuse_ctrl_init,
++    .instance_finalize = efuse_ctrl_finalize,
+ };
+ 
+ static void efuse_ctrl_register_types(void)
+diff --git a/hw/pci/msix.c b/hw/pci/msix.c
+index 9e70fcd6fa..4b258566d4 100644
+--- a/hw/pci/msix.c
++++ b/hw/pci/msix.c
+@@ -639,6 +639,7 @@ undo:
+     }
+     dev->msix_vector_use_notifier = NULL;
+     dev->msix_vector_release_notifier = NULL;
++    dev->msix_vector_poll_notifier = NULL;
+     return ret;
+ }
+ 
+diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c
+index 8e3faf1f59..61a4e06768 100644
+--- a/hw/pci/pcie_sriov.c
++++ b/hw/pci/pcie_sriov.c
+@@ -178,7 +178,6 @@ static void register_vfs(PCIDevice *dev)
+     num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF);
+ 
+     dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs);
+-    assert(dev->exp.sriov_pf.vf);
+ 
+     trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn),
+                              PCI_FUNC(dev->devfn), num_vfs);
+diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
+index 5fff487ee5..2c33e36cad 100644
+--- a/hw/usb/dev-network.c
++++ b/hw/usb/dev-network.c
+@@ -1386,7 +1386,8 @@ static void usb_net_realize(USBDevice *dev, Error **errp)
+ 
+     qemu_macaddr_default_if_unset(&s->conf.macaddr);
+     s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
+-                          object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
++                          object_get_typename(OBJECT(s)), s->dev.qdev.id,
++                          &s->dev.qdev.mem_reentrancy_guard, s);
+     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+     snprintf(s->usbstring_mac, sizeof(s->usbstring_mac),
+              "%02x%02x%02x%02x%02x%02x",
+diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c
+index 7ef2f9dcdb..eab6e1c793 100644
+--- a/hw/virtio/virtio-iommu-pci.c
++++ b/hw/virtio/virtio-iommu-pci.c
+@@ -95,10 +95,18 @@ static void virtio_iommu_pci_instance_init(Object *obj)
+                                 TYPE_VIRTIO_IOMMU);
+ }
+ 
++static void virtio_iommu_pci_instance_finalize(Object *obj)
++{
++    VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(obj);
++
++    g_free(dev->vdev.reserved_regions);
++}
++
+ static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = {
+     .generic_name  = TYPE_VIRTIO_IOMMU_PCI,
+     .instance_size = sizeof(VirtIOIOMMUPCI),
+     .instance_init = virtio_iommu_pci_instance_init,
++    .instance_finalize = virtio_iommu_pci_instance_finalize,
+     .class_init    = virtio_iommu_pci_class_init,
+ };
+ 
+diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
+index 67e771c373..e5e74a7160 100644
+--- a/hw/virtio/virtio-pci.c
++++ b/hw/virtio/virtio-pci.c
+@@ -2174,6 +2174,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t)
+         .parent        = t->parent ? t->parent : TYPE_VIRTIO_PCI,
+         .instance_size = t->instance_size,
+         .instance_init = t->instance_init,
++        .instance_finalize = t->instance_finalize,
+         .class_size    = t->class_size,
+         .abstract      = true,
+         .interfaces    = t->interfaces,
+diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h
+index 938799e8f6..c4676ff4d4 100644
+--- a/include/hw/virtio/virtio-pci.h
++++ b/include/hw/virtio/virtio-pci.h
+@@ -241,6 +241,7 @@ typedef struct VirtioPCIDeviceTypeInfo {
+     size_t instance_size;
+     size_t class_size;
+     void (*instance_init)(Object *obj);
++    void (*instance_finalize)(Object *obj);
+     void (*class_init)(ObjectClass *klass, void *data);
+     InterfaceInfo *interfaces;
+ } VirtioPCIDeviceTypeInfo;
+diff --git a/include/net/net.h b/include/net/net.h
+index dc20b31e9f..5a7c0e9ebf 100644
+--- a/include/net/net.h
++++ b/include/net/net.h
+@@ -118,6 +118,7 @@ struct NetClientState {
+ typedef struct NICState {
+     NetClientState *ncs;
+     NICConf *conf;
++    MemReentrancyGuard *reentrancy_guard;
+     void *opaque;
+     bool peer_deleted;
+ } NICState;
+@@ -151,6 +152,7 @@ NICState *qemu_new_nic(NetClientInfo *info,
+                        NICConf *conf,
+                        const char *model,
+                        const char *name,
++                       MemReentrancyGuard *reentrancy_guard,
+                        void *opaque);
+ void qemu_del_nic(NICState *nic);
+ NetClientState *qemu_get_subqueue(NICState *nic, int queue_index);
+diff --git a/linux-user/elfload.c b/linux-user/elfload.c
+index c2c095d383..87895847ec 100644
+--- a/linux-user/elfload.c
++++ b/linux-user/elfload.c
+@@ -3001,7 +3001,7 @@ static void load_elf_image(const char *image_name, int image_fd,
+     for (i = 0; i < ehdr->e_phnum; ++i) {
+         struct elf_phdr *eppnt = phdr + i;
+         if (eppnt->p_type == PT_LOAD) {
+-            abi_ulong a = eppnt->p_vaddr - eppnt->p_offset;
++            abi_ulong a = eppnt->p_vaddr & TARGET_PAGE_MASK;
+             if (a < loaddr) {
+                 loaddr = a;
+             }
+diff --git a/net/net.c b/net/net.c
+index 840ad9dca5..c3391168f6 100644
+--- a/net/net.c
++++ b/net/net.c
+@@ -319,6 +319,7 @@ NICState *qemu_new_nic(NetClientInfo *info,
+                        NICConf *conf,
+                        const char *model,
+                        const char *name,
++                       MemReentrancyGuard *reentrancy_guard,
+                        void *opaque)
+ {
+     NetClientState **peers = conf->peers.ncs;
+@@ -331,6 +332,7 @@ NICState *qemu_new_nic(NetClientInfo *info,
+     nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
+     nic->ncs = (void *)nic + info->size;
+     nic->conf = conf;
++    nic->reentrancy_guard = reentrancy_guard,
+     nic->opaque = opaque;
+ 
+     for (i = 0; i < queues; i++) {
+@@ -786,6 +788,7 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender,
+                                        int iovcnt,
+                                        void *opaque)
+ {
++    MemReentrancyGuard *owned_reentrancy_guard;
+     NetClientState *nc = opaque;
+     int ret;
+ 
+@@ -798,12 +801,24 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender,
+         return 0;
+     }
+ 
++    if (nc->info->type != NET_CLIENT_DRIVER_NIC ||
++        qemu_get_nic(nc)->reentrancy_guard->engaged_in_io) {
++        owned_reentrancy_guard = NULL;
++    } else {
++        owned_reentrancy_guard = qemu_get_nic(nc)->reentrancy_guard;
++        owned_reentrancy_guard->engaged_in_io = true;
++    }
++
+     if (nc->info->receive_iov && !(flags & QEMU_NET_PACKET_FLAG_RAW)) {
+         ret = nc->info->receive_iov(nc, iov, iovcnt);
+     } else {
+         ret = nc_sendv_compat(nc, iov, iovcnt, flags);
+     }
+ 
++    if (owned_reentrancy_guard) {
++        owned_reentrancy_guard->engaged_in_io = false;
++    }
++
+     if (ret == 0) {
+         nc->receive_disabled = 1;
+     }
+diff --git a/softmmu/memory.c b/softmmu/memory.c
+index 61569f8306..2b03596bc7 100644
+--- a/softmmu/memory.c
++++ b/softmmu/memory.c
+@@ -1326,22 +1326,7 @@ static uint64_t memory_region_ram_device_read(void *opaque,
+                                               hwaddr addr, unsigned size)
+ {
+     MemoryRegion *mr = opaque;
+-    uint64_t data = (uint64_t)~0;
+-
+-    switch (size) {
+-    case 1:
+-        data = *(uint8_t *)(mr->ram_block->host + addr);
+-        break;
+-    case 2:
+-        data = *(uint16_t *)(mr->ram_block->host + addr);
+-        break;
+-    case 4:
+-        data = *(uint32_t *)(mr->ram_block->host + addr);
+-        break;
+-    case 8:
+-        data = *(uint64_t *)(mr->ram_block->host + addr);
+-        break;
+-    }
++    uint64_t data = ldn_he_p(mr->ram_block->host + addr, size);
+ 
+     trace_memory_region_ram_device_read(get_cpu_index(), mr, addr, data, size);
+ 
+@@ -1355,20 +1340,7 @@ static void memory_region_ram_device_write(void *opaque, hwaddr addr,
+ 
+     trace_memory_region_ram_device_write(get_cpu_index(), mr, addr, data, size);
+ 
+-    switch (size) {
+-    case 1:
+-        *(uint8_t *)(mr->ram_block->host + addr) = (uint8_t)data;
+-        break;
+-    case 2:
+-        *(uint16_t *)(mr->ram_block->host + addr) = (uint16_t)data;
+-        break;
+-    case 4:
+-        *(uint32_t *)(mr->ram_block->host + addr) = (uint32_t)data;
+-        break;
+-    case 8:
+-        *(uint64_t *)(mr->ram_block->host + addr) = data;
+-        break;
+-    }
++    stn_he_p(mr->ram_block->host + addr, size, data);
+ }
+ 
+ static const MemoryRegionOps ram_device_mem_ops = {
+diff --git a/target/arm/cpu.c b/target/arm/cpu.c
+index 38d066c294..6cf7a33591 100644
+--- a/target/arm/cpu.c
++++ b/target/arm/cpu.c
+@@ -1498,6 +1498,16 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp)
+             return;
+         }
+ 
++        /*
++         * FEAT_SME is not architecturally dependent on FEAT_SVE (unless
++         * FEAT_SME_FA64 is present). However our implementation currently
++         * assumes it, so if the user asked for sve=off then turn off SME also.
++         * (KVM doesn't currently support SME at all.)
++         */
++        if (cpu_isar_feature(aa64_sme, cpu) && !cpu_isar_feature(aa64_sve, cpu)) {
++            object_property_set_bool(OBJECT(cpu), "sme", false, &error_abort);
++        }
++
+         arm_cpu_sme_finalize(cpu, &local_err);
+         if (local_err != NULL) {
+             error_propagate(errp, local_err);
+diff --git a/target/arm/helper.c b/target/arm/helper.c
+index a52ef3dfe4..02cfeece45 100644
+--- a/target/arm/helper.c
++++ b/target/arm/helper.c
+@@ -1431,6 +1431,22 @@ static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+     pmu_op_finish(env);
+ }
+ 
++static uint64_t pmcr_read(CPUARMState *env, const ARMCPRegInfo *ri)
++{
++    uint64_t pmcr = env->cp15.c9_pmcr;
++
++    /*
++     * If EL2 is implemented and enabled for the current security state, reads
++     * of PMCR.N from EL1 or EL0 return the value of MDCR_EL2.HPMN or HDCR.HPMN.
++     */
++    if (arm_current_el(env) <= 1 && arm_is_el2_enabled(env)) {
++        pmcr &= ~PMCRN_MASK;
++        pmcr |= (env->cp15.mdcr_el2 & MDCR_HPMN) << PMCRN_SHIFT;
++    }
++
++    return pmcr;
++}
++
+ static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri,
+                           uint64_t value)
+ {
+@@ -6499,8 +6515,9 @@ static void define_pmu_regs(ARMCPU *cpu)
+         .access = PL0_RW,
+         .type = ARM_CP_IO | ARM_CP_ALIAS,
+         .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr),
+-        .accessfn = pmreg_access, .writefn = pmcr_write,
+-        .raw_writefn = raw_write,
++        .accessfn = pmreg_access,
++        .readfn = pmcr_read, .raw_readfn = raw_read,
++        .writefn = pmcr_write, .raw_writefn = raw_write,
+     };
+     ARMCPRegInfo pmcr64 = {
+         .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64,
+@@ -6509,6 +6526,7 @@ static void define_pmu_regs(ARMCPU *cpu)
+         .type = ARM_CP_IO,
+         .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
+         .resetvalue = cpu->isar.reset_pmcr_el0,
++        .readfn = pmcr_read, .raw_readfn = raw_read,
+         .writefn = pmcr_write, .raw_writefn = raw_write,
+     };
+ 
+diff --git a/target/arm/sme_helper.c b/target/arm/sme_helper.c
+index 73dd838330..8856773635 100644
+--- a/target/arm/sme_helper.c
++++ b/target/arm/sme_helper.c
+@@ -1070,10 +1070,9 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn,
+ 
+                         m = f16mop_adj_pair(m, pcol, 0);
+                         *a = f16_dotadd(*a, n, m, &fpst_std, &fpst_odd);
+-
+-                        col += 4;
+-                        pcol >>= 4;
+                     }
++                    col += 4;
++                    pcol >>= 4;
+                 } while (col & 15);
+             }
+             row += 4;
+@@ -1106,10 +1105,9 @@ void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn,
+ 
+                         m = f16mop_adj_pair(m, pcol, 0);
+                         *a = bfdotadd(*a, n, m);
+-
+-                        col += 4;
+-                        pcol >>= 4;
+                     }
++                    col += 4;
++                    pcol >>= 4;
+                 } while (col & 15);
+             }
+             row += 4;
+diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h
+index 73df5e3793..15334a3d15 100644
+--- a/target/arm/syndrome.h
++++ b/target/arm/syndrome.h
+@@ -212,7 +212,7 @@ static inline uint32_t syn_simd_access_trap(int cv, int cond, bool is_16bit)
+ 
+ static inline uint32_t syn_sve_access_trap(void)
+ {
+-    return EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT;
++    return (EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL;
+ }
+ 
+ static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit)
+@@ -223,12 +223,12 @@ static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit)
+ 
+ static inline uint32_t syn_pactrap(void)
+ {
+-    return EC_PACTRAP << ARM_EL_EC_SHIFT;
++    return (EC_PACTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL;
+ }
+ 
+ static inline uint32_t syn_btitrap(int btype)
+ {
+-    return (EC_BTITRAP << ARM_EL_EC_SHIFT) | btype;
++    return (EC_BTITRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | btype;
+ }
+ 
+ static inline uint32_t syn_bxjtrap(int cv, int cond, int rm)
+diff --git a/tests/avocado/cpu_queries.py b/tests/avocado/cpu_queries.py
+index cf69f69b11..295642772e 100644
+--- a/tests/avocado/cpu_queries.py
++++ b/tests/avocado/cpu_queries.py
+@@ -31,4 +31,4 @@ def test(self):
+         for c in cpus:
+             model = {'name': c['name']}
+             e = self.vm.command('query-cpu-model-expansion', model=model, type='full')
+-            self.assertEquals(e['model']['name'], c['name'])
++            self.assertEqual(e['model']['name'], c['name'])
+diff --git a/tests/avocado/empty_cpu_model.py b/tests/avocado/empty_cpu_model.py
+index 22f504418d..d906ef3d3c 100644
+--- a/tests/avocado/empty_cpu_model.py
++++ b/tests/avocado/empty_cpu_model.py
+@@ -15,5 +15,5 @@ def test(self):
+         self.vm.set_qmp_monitor(enabled=False)
+         self.vm.launch()
+         self.vm.wait()
+-        self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1")
++        self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+         self.assertRegex(self.vm.get_log(), r'-cpu option cannot be empty')
+diff --git a/tests/avocado/pc_cpu_hotplug_props.py b/tests/avocado/pc_cpu_hotplug_props.py
+index 52b878188e..6100fb7760 100644
+--- a/tests/avocado/pc_cpu_hotplug_props.py
++++ b/tests/avocado/pc_cpu_hotplug_props.py
+@@ -32,4 +32,4 @@ def test_no_die_id(self):
+         self.vm.add_args('-smp', '1,sockets=2,cores=2,threads=2,maxcpus=8')
+         self.vm.add_args('-device', 'qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0')
+         self.vm.launch()
+-        self.assertEquals(len(self.vm.command('query-cpus-fast')), 2)
++        self.assertEqual(len(self.vm.command('query-cpus-fast')), 2)
+diff --git a/tests/avocado/version.py b/tests/avocado/version.py
+index ded7f039c1..5f88ff300b 100644
+--- a/tests/avocado/version.py
++++ b/tests/avocado/version.py
+@@ -21,4 +21,4 @@ def test_qmp_human_info_version(self):
+         self.vm.launch()
+         res = self.vm.command('human-monitor-command',
+                               command_line='info version')
+-        self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)')
++        self.assertRegex(res, r'^(\d+\.\d+\.\d)')
+diff --git a/tests/avocado/x86_cpu_model_versions.py b/tests/avocado/x86_cpu_model_versions.py
+index a6edf74c1c..262d6a77fb 100644
+--- a/tests/avocado/x86_cpu_model_versions.py
++++ b/tests/avocado/x86_cpu_model_versions.py
+@@ -119,94 +119,95 @@ def test_4_1_alias(self):
+ 
+         self.assertFalse(cpus['Cascadelake-Server']['static'],
+                          'unversioned Cascadelake-Server CPU model must not be static')
+-        self.assertEquals(cpus['Cascadelake-Server'].get('alias-of'), 'Cascadelake-Server-v1',
+-                          'Cascadelake-Server must be an alias of Cascadelake-Server-v1')
++        self.assertEqual(cpus['Cascadelake-Server'].get('alias-of'),
++                         'Cascadelake-Server-v1',
++                         'Cascadelake-Server must be an alias of Cascadelake-Server-v1')
+         self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'],
+                          'Cascadelake-Server-v1 must not be an alias')
+ 
+         self.assertFalse(cpus['qemu64']['static'],
+                          'unversioned qemu64 CPU model must not be static')
+-        self.assertEquals(cpus['qemu64'].get('alias-of'), 'qemu64-v1',
+-                          'qemu64 must be an alias of qemu64-v1')
++        self.assertEqual(cpus['qemu64'].get('alias-of'), 'qemu64-v1',
++                         'qemu64 must be an alias of qemu64-v1')
+         self.assertNotIn('alias-of', cpus['qemu64-v1'],
+                          'qemu64-v1 must not be an alias')
+ 
+         self.validate_variant_aliases(cpus)
+ 
+         # On pc-*-4.1, -noTSX and -IBRS models should be aliases:
+-        self.assertEquals(cpus["Haswell"].get('alias-of'),
+-                          "Haswell-v1",
++        self.assertEqual(cpus["Haswell"].get('alias-of'),
++                         "Haswell-v1",
+                          "Haswell must be an alias")
+-        self.assertEquals(cpus["Haswell-noTSX"].get('alias-of'),
+-                          "Haswell-v2",
++        self.assertEqual(cpus["Haswell-noTSX"].get('alias-of'),
++                         "Haswell-v2",
+                          "Haswell-noTSX must be an alias")
+-        self.assertEquals(cpus["Haswell-IBRS"].get('alias-of'),
+-                          "Haswell-v3",
++        self.assertEqual(cpus["Haswell-IBRS"].get('alias-of'),
++                         "Haswell-v3",
+                          "Haswell-IBRS must be an alias")
+-        self.assertEquals(cpus["Haswell-noTSX-IBRS"].get('alias-of'),
+-                          "Haswell-v4",
++        self.assertEqual(cpus["Haswell-noTSX-IBRS"].get('alias-of'),
++                         "Haswell-v4",
+                          "Haswell-noTSX-IBRS must be an alias")
+ 
+-        self.assertEquals(cpus["Broadwell"].get('alias-of'),
+-                          "Broadwell-v1",
++        self.assertEqual(cpus["Broadwell"].get('alias-of'),
++                         "Broadwell-v1",
+                          "Broadwell must be an alias")
+-        self.assertEquals(cpus["Broadwell-noTSX"].get('alias-of'),
+-                          "Broadwell-v2",
++        self.assertEqual(cpus["Broadwell-noTSX"].get('alias-of'),
++                         "Broadwell-v2",
+                          "Broadwell-noTSX must be an alias")
+-        self.assertEquals(cpus["Broadwell-IBRS"].get('alias-of'),
+-                          "Broadwell-v3",
++        self.assertEqual(cpus["Broadwell-IBRS"].get('alias-of'),
++                         "Broadwell-v3",
+                          "Broadwell-IBRS must be an alias")
+-        self.assertEquals(cpus["Broadwell-noTSX-IBRS"].get('alias-of'),
+-                          "Broadwell-v4",
++        self.assertEqual(cpus["Broadwell-noTSX-IBRS"].get('alias-of'),
++                         "Broadwell-v4",
+                          "Broadwell-noTSX-IBRS must be an alias")
+ 
+-        self.assertEquals(cpus["Nehalem"].get('alias-of'),
+-                          "Nehalem-v1",
++        self.assertEqual(cpus["Nehalem"].get('alias-of'),
++                         "Nehalem-v1",
+                          "Nehalem must be an alias")
+-        self.assertEquals(cpus["Nehalem-IBRS"].get('alias-of'),
+-                          "Nehalem-v2",
++        self.assertEqual(cpus["Nehalem-IBRS"].get('alias-of'),
++                         "Nehalem-v2",
+                          "Nehalem-IBRS must be an alias")
+ 
+-        self.assertEquals(cpus["Westmere"].get('alias-of'),
+-                          "Westmere-v1",
++        self.assertEqual(cpus["Westmere"].get('alias-of'),
++                         "Westmere-v1",
+                          "Westmere must be an alias")
+-        self.assertEquals(cpus["Westmere-IBRS"].get('alias-of'),
+-                          "Westmere-v2",
++        self.assertEqual(cpus["Westmere-IBRS"].get('alias-of'),
++                         "Westmere-v2",
+                          "Westmere-IBRS must be an alias")
+ 
+-        self.assertEquals(cpus["SandyBridge"].get('alias-of'),
+-                          "SandyBridge-v1",
++        self.assertEqual(cpus["SandyBridge"].get('alias-of'),
++                         "SandyBridge-v1",
+                          "SandyBridge must be an alias")
+-        self.assertEquals(cpus["SandyBridge-IBRS"].get('alias-of'),
+-                          "SandyBridge-v2",
++        self.assertEqual(cpus["SandyBridge-IBRS"].get('alias-of'),
++                         "SandyBridge-v2",
+                          "SandyBridge-IBRS must be an alias")
+ 
+-        self.assertEquals(cpus["IvyBridge"].get('alias-of'),
+-                          "IvyBridge-v1",
++        self.assertEqual(cpus["IvyBridge"].get('alias-of'),
++                         "IvyBridge-v1",
+                          "IvyBridge must be an alias")
+-        self.assertEquals(cpus["IvyBridge-IBRS"].get('alias-of'),
+-                          "IvyBridge-v2",
++        self.assertEqual(cpus["IvyBridge-IBRS"].get('alias-of'),
++                         "IvyBridge-v2",
+                          "IvyBridge-IBRS must be an alias")
+ 
+-        self.assertEquals(cpus["Skylake-Client"].get('alias-of'),
+-                          "Skylake-Client-v1",
++        self.assertEqual(cpus["Skylake-Client"].get('alias-of'),
++                         "Skylake-Client-v1",
+                          "Skylake-Client must be an alias")
+-        self.assertEquals(cpus["Skylake-Client-IBRS"].get('alias-of'),
+-                          "Skylake-Client-v2",
++        self.assertEqual(cpus["Skylake-Client-IBRS"].get('alias-of'),
++                         "Skylake-Client-v2",
+                          "Skylake-Client-IBRS must be an alias")
+ 
+-        self.assertEquals(cpus["Skylake-Server"].get('alias-of'),
+-                          "Skylake-Server-v1",
++        self.assertEqual(cpus["Skylake-Server"].get('alias-of'),
++                         "Skylake-Server-v1",
+                          "Skylake-Server must be an alias")
+-        self.assertEquals(cpus["Skylake-Server-IBRS"].get('alias-of'),
+-                          "Skylake-Server-v2",
++        self.assertEqual(cpus["Skylake-Server-IBRS"].get('alias-of'),
++                         "Skylake-Server-v2",
+                          "Skylake-Server-IBRS must be an alias")
+ 
+-        self.assertEquals(cpus["EPYC"].get('alias-of'),
+-                          "EPYC-v1",
++        self.assertEqual(cpus["EPYC"].get('alias-of'),
++                         "EPYC-v1",
+                          "EPYC must be an alias")
+-        self.assertEquals(cpus["EPYC-IBPB"].get('alias-of'),
+-                          "EPYC-v2",
++        self.assertEqual(cpus["EPYC-IBPB"].get('alias-of'),
++                         "EPYC-v2",
+                          "EPYC-IBPB must be an alias")
+ 
+         self.validate_aliases(cpus)
+diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
+index e8be217e1f..9bcf1e7525 100755
+--- a/tests/qemu-iotests/059
++++ b/tests/qemu-iotests/059
+@@ -84,6 +84,8 @@ echo
+ echo "=== Testing big twoGbMaxExtentFlat ==="
+ _make_test_img -o "subformat=twoGbMaxExtentFlat" 1000G
+ _img_info --format-specific | _filter_img_info --format-specific
++$QEMU_IO -c "write 990G 512 -P 89" "$TEST_IMG" | _filter_qemu_io
++$QEMU_IO -c "read 990G 512 -P 89" "$TEST_IMG" | _filter_qemu_io
+ _cleanup_test_img
+ 
+ echo
+diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
+index 2b83c0c8b6..275ee7c778 100644
+--- a/tests/qemu-iotests/059.out
++++ b/tests/qemu-iotests/059.out
+@@ -2032,6 +2032,10 @@ Format specific information:
+             virtual size: 2147483648
+             filename: TEST_DIR/t-f500.IMGFMT
+             format: FLAT
++wrote 512/512 bytes at offset 1063004405760
++512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++read 512/512 bytes at offset 1063004405760
++512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ 
+ === Testing malformed VMFS extent description line ===
+ qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid extent line: RW 12582912 VMFS "dummy.IMGFMT" 1
+diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
+index 52c6246a33..7ff9f1648c 100644
+--- a/ui/gtk-egl.c
++++ b/ui/gtk-egl.c
+@@ -234,6 +234,13 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
+     vc->gfx.h = h;
+     vc->gfx.y0_top = backing_y_0_top;
+ 
++    if (!vc->gfx.esurface) {
++        gd_egl_init(vc);
++        if (!vc->gfx.esurface) {
++            return;
++        }
++    }
++
+     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+                    vc->gfx.esurface, vc->gfx.ectx);
+ 
+diff --git a/ui/vnc-clipboard.c b/ui/vnc-clipboard.c
+index c759be3438..124b6fbd9c 100644
+--- a/ui/vnc-clipboard.c
++++ b/ui/vnc-clipboard.c
+@@ -69,6 +69,11 @@ static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size)
+         }
+     }
+ 
++    *size = stream.total_out;
++    inflateEnd(&stream);
++
++    return out;
++
+ err_end:
+     inflateEnd(&stream);
+ err:
diff -Nru qemu-7.2+dfsg/debian/patches/v7.2.9.diff qemu-7.2+dfsg/debian/patches/v7.2.9.diff
--- qemu-7.2+dfsg/debian/patches/v7.2.9.diff	1970-01-01 03:00:00.000000000 +0300
+++ qemu-7.2+dfsg/debian/patches/v7.2.9.diff	2024-01-30 19:08:55.000000000 +0300
@@ -0,0 +1,3566 @@
+Subject: v7.2.9
+Date: Mon Jan 29 12:49:21 2024 +0300
+Tagger: Michael Tokarev <mjt@tls.msk.ru>
+Forwarded: not-needed
+
+This is a difference between upstream qemu v7.2.9
+and upstream qemu v7.2.9.
+
+ .gitlab-ci.d/buildtest.yml                         |   5 +-
+ .readthedocs.yml                                   |  19 +-
+ VERSION                                            |   2 +-
+ accel/tcg/cpu-exec.c                               |   4 +-
+ accel/tcg/tb-maint.c                               |   6 +-
+ accel/tcg/translate-all.c                          |   2 -
+ block/blklogwrites.c                               |  35 ++-
+ block/io.c                                         |  10 +
+ block/snapshot.c                                   |   4 +-
+ chardev/char.c                                     |   2 +-
+ docs/requirements.txt                              |   2 +
+ hw/block/pflash_cfi01.c                            | 171 ++++++------
+ hw/block/pflash_cfi02.c                            |   2 +-
+ hw/block/trace-events                              |   7 +-
+ hw/intc/arm_gicv3_cpuif.c                          |  17 +-
+ hw/net/virtio-net.c                                |  13 +-
+ hw/scsi/esp-pci.c                                  |  61 ++--
+ include/exec/exec-all.h                            |   6 -
+ include/hw/elf_ops.h                               |   2 +-
+ monitor/qmp.c                                      |  17 --
+ qapi/qmp-dispatch.c                                |  24 +-
+ softmmu/vl.c                                       |   4 +
+ target/i386/cpu.h                                  |   9 +-
+ target/i386/tcg/tcg-cpu.c                          |  25 +-
+ target/i386/tcg/translate.c                        |  22 +-
+ target/riscv/csr.c                                 |  14 +-
+ target/s390x/tcg/translate.c                       |   3 +-
+ target/xtensa/mmu_helper.c                         |  47 +++-
+ tests/qemu-iotests/060.out                         |   4 +-
+ tests/qemu-iotests/071.out                         |   4 +-
+ tests/qemu-iotests/081.out                         |  16 +-
+ tests/qemu-iotests/087.out                         |  12 +-
+ tests/qemu-iotests/108.out                         |   2 +-
+ tests/qemu-iotests/109                             |   4 +-
+ tests/qemu-iotests/109.out                         |  78 +++---
+ tests/qemu-iotests/117.out                         |   2 +-
+ tests/qemu-iotests/120.out                         |   2 +-
+ tests/qemu-iotests/127.out                         |   2 +-
+ tests/qemu-iotests/140.out                         |   2 +-
+ tests/qemu-iotests/141                             | 307 +++++++++------------
+ tests/qemu-iotests/141.out                         | 200 +++-----------
+ tests/qemu-iotests/143.out                         |   2 +-
+ tests/qemu-iotests/156.out                         |   2 +-
+ tests/qemu-iotests/176.out                         |  16 +-
+ tests/qemu-iotests/182.out                         |   2 +-
+ tests/qemu-iotests/183.out                         |   4 +-
+ tests/qemu-iotests/184.out                         |  32 +--
+ tests/qemu-iotests/185                             |   6 +-
+ tests/qemu-iotests/185.out                         |  45 ++-
+ tests/qemu-iotests/191.out                         |  16 +-
+ tests/qemu-iotests/195.out                         |  16 +-
+ tests/qemu-iotests/223.out                         |  12 +-
+ tests/qemu-iotests/227.out                         |  32 +--
+ tests/qemu-iotests/247.out                         |   2 +-
+ tests/qemu-iotests/273.out                         |   8 +-
+ tests/qemu-iotests/308                             |   4 +-
+ tests/qemu-iotests/308.out                         |   2 +-
+ tests/qemu-iotests/iotests.py                      |   7 +
+ tests/qemu-iotests/tests/qcow2-internal-snapshots  | 170 ++++++++++++
+ .../tests/qcow2-internal-snapshots.out             | 107 +++++++
+ tests/qemu-iotests/tests/qsd-jobs.out              |   4 +-
+ tests/qtest/meson.build                            |   1 +
+ 62 files changed, 979 insertions(+), 681 deletions(-)
+
+diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
+index 10886bb414..7243b8079b 100644
+--- a/.gitlab-ci.d/buildtest.yml
++++ b/.gitlab-ci.d/buildtest.yml
+@@ -637,7 +637,10 @@ pages:
+     - mkdir -p public
+     # HTML-ised source tree
+     - make gtags
+-    - htags -anT --tree-view=filetree -m qemu_init
++    # We unset variables to work around a bug in some htags versions
++    # which causes it to fail when the environment is large
++    - CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags
++        -anT --tree-view=filetree -m qemu_init
+         -t "Welcome to the QEMU sourcecode"
+     - mv HTML public/src
+     # Project documentation
+diff --git a/.readthedocs.yml b/.readthedocs.yml
+index 7fb7b8dd61..0b262469ce 100644
+--- a/.readthedocs.yml
++++ b/.readthedocs.yml
+@@ -5,16 +5,21 @@
+ # Required
+ version: 2
+ 
++# Set the version of Python and other tools you might need
++build:
++  os: ubuntu-22.04
++  tools:
++    python: "3.11"
++
+ # Build documentation in the docs/ directory with Sphinx
+ sphinx:
+   configuration: docs/conf.py
+ 
++# We recommend specifying your dependencies to enable reproducible builds:
++# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
++python:
++  install:
++    - requirements: docs/requirements.txt
++
+ # We want all the document formats
+ formats: all
+-
+-# For consistency, we require that QEMU's Sphinx extensions
+-# run with at least the same minimum version of Python that
+-# we require for other Python in our codebase (our conf.py
+-# enforces this, and some code needs it.)
+-python:
+-  version: 3.6
+diff --git a/VERSION b/VERSION
+index 31554632ab..672f66a613 100644
+--- a/VERSION
++++ b/VERSION
+@@ -1 +1 @@
+-7.2.8
++7.2.9
+diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
+index 356fe348de..68fef3e01f 100644
+--- a/accel/tcg/cpu-exec.c
++++ b/accel/tcg/cpu-exec.c
+@@ -186,7 +186,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
+     const TranslationBlock *tb = p;
+     const struct tb_desc *desc = d;
+ 
+-    if ((TARGET_TB_PCREL || tb_pc(tb) == desc->pc) &&
++    if (tb_pc(tb) == desc->pc &&
+         tb_page_addr0(tb) == desc->page_addr0 &&
+         tb->cs_base == desc->cs_base &&
+         tb->flags == desc->flags &&
+@@ -238,7 +238,7 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
+         return NULL;
+     }
+     desc.page_addr0 = phys_pc;
+-    h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : pc),
++    h = tb_hash_func(phys_pc, pc,
+                      flags, cflags, *cpu->trace_dstate);
+     return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
+ }
+diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
+index 0cdb35548c..9d9f651c78 100644
+--- a/accel/tcg/tb-maint.c
++++ b/accel/tcg/tb-maint.c
+@@ -34,7 +34,7 @@ static bool tb_cmp(const void *ap, const void *bp)
+     const TranslationBlock *a = ap;
+     const TranslationBlock *b = bp;
+ 
+-    return ((TARGET_TB_PCREL || tb_pc(a) == tb_pc(b)) &&
++    return (tb_pc(a) == tb_pc(b) &&
+             a->cs_base == b->cs_base &&
+             a->flags == b->flags &&
+             (tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) &&
+@@ -269,7 +269,7 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
+ 
+     /* remove the TB from the hash list */
+     phys_pc = tb_page_addr0(tb);
+-    h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)),
++    h = tb_hash_func(phys_pc, tb_pc(tb),
+                      tb->flags, orig_cflags, tb->trace_vcpu_dstate);
+     if (!qht_remove(&tb_ctx.htable, tb, h)) {
+         return;
+@@ -459,7 +459,7 @@ TranslationBlock *tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
+     }
+ 
+     /* add in the hash table */
+-    h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)),
++    h = tb_hash_func(phys_pc, tb_pc(tb),
+                      tb->flags, tb->cflags, tb->trace_vcpu_dstate);
+     qht_insert(&tb_ctx.htable, tb, h, &existing_tb);
+ 
+diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
+index ac3ee3740c..ed8ddee6e8 100644
+--- a/accel/tcg/translate-all.c
++++ b/accel/tcg/translate-all.c
+@@ -818,9 +818,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
+ 
+     gen_code_buf = tcg_ctx->code_gen_ptr;
+     tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf);
+-#if !TARGET_TB_PCREL
+     tb->pc = pc;
+-#endif
+     tb->cs_base = cs_base;
+     tb->flags = flags;
+     tb->cflags = cflags;
+diff --git a/block/blklogwrites.c b/block/blklogwrites.c
+index cef9efe55d..ad589c4a2e 100644
+--- a/block/blklogwrites.c
++++ b/block/blklogwrites.c
+@@ -321,22 +321,39 @@ typedef struct {
+ static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
+ {
+     BDRVBlkLogWritesState *s = lr->bs->opaque;
+-    uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
+ 
+-    s->nr_entries++;
+-    s->cur_log_sector +=
+-            ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
++    /*
++     * Determine the offsets and sizes of different parts of the entry, and
++     * update the state of the driver.
++     *
++     * This needs to be done in one go, before any actual I/O is done, as the
++     * log entry may have to be written in two parts, and the state of the
++     * driver may be modified by other driver operations while waiting for the
++     * I/O to complete.
++     */
++    const uint64_t entry_start_sector = s->cur_log_sector;
++    const uint64_t entry_offset = entry_start_sector << s->sectorbits;
++    const uint64_t qiov_aligned_size = ROUND_UP(lr->qiov->size, s->sectorsize);
++    const uint64_t entry_aligned_size = qiov_aligned_size +
++        ROUND_UP(lr->zero_size, s->sectorsize);
++    const uint64_t entry_nr_sectors = entry_aligned_size >> s->sectorbits;
+ 
+-    lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
++    s->nr_entries++;
++    s->cur_log_sector += entry_nr_sectors;
++
++    /*
++     * Write the log entry. Note that if this is a "write zeroes" operation,
++     * only the entry header is written here, with the zeroing being done
++     * separately below.
++     */
++    lr->log_ret = bdrv_co_pwritev(s->log_file, entry_offset, lr->qiov->size,
+                                   lr->qiov, 0);
+ 
+     /* Logging for the "write zeroes" operation */
+     if (lr->log_ret == 0 && lr->zero_size) {
+-        cur_log_offset = s->cur_log_sector << s->sectorbits;
+-        s->cur_log_sector +=
+-                ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
++        const uint64_t zeroes_offset = entry_offset + qiov_aligned_size;
+ 
+-        lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
++        lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, zeroes_offset,
+                                             lr->zero_size, 0);
+     }
+ 
+diff --git a/block/io.c b/block/io.c
+index bbaa0d1b2d..4589a58917 100644
+--- a/block/io.c
++++ b/block/io.c
+@@ -2595,6 +2595,16 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
+                 ret |= (ret2 & BDRV_BLOCK_ZERO);
+             }
+         }
++
++        /*
++         * Now that the recursive search was done, clear the flag. Otherwise,
++         * with more complicated block graphs like snapshot-access ->
++         * copy-before-write -> qcow2, where the return value will be propagated
++         * further up to a parent bdrv_co_do_block_status() call, both the
++         * BDRV_BLOCK_RECURSE and BDRV_BLOCK_ZERO flags would be set, which is
++         * not allowed.
++         */
++        ret &= ~BDRV_BLOCK_RECURSE;
+     }
+ 
+ out:
+diff --git a/block/snapshot.c b/block/snapshot.c
+index e22ac3eac6..86e29ca59f 100644
+--- a/block/snapshot.c
++++ b/block/snapshot.c
+@@ -190,8 +190,10 @@ static BlockDriverState *bdrv_snapshot_fallback(BlockDriverState *bs)
+ int bdrv_can_snapshot(BlockDriverState *bs)
+ {
+     BlockDriver *drv = bs->drv;
++
+     GLOBAL_STATE_CODE();
+-    if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
++
++    if (!drv || !bdrv_is_inserted(bs) || !bdrv_is_writable(bs)) {
+         return 0;
+     }
+ 
+diff --git a/chardev/char.c b/chardev/char.c
+index b005df3ccf..193bbac054 100644
+--- a/chardev/char.c
++++ b/chardev/char.c
+@@ -519,7 +519,7 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp)
+ 
+     if (object_class_is_abstract(oc)) {
+         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+-                   "an abstract device type");
++                   "a non-abstract device type");
+         return NULL;
+     }
+ 
+diff --git a/docs/requirements.txt b/docs/requirements.txt
+new file mode 100644
+index 0000000000..691e5218ec
+--- /dev/null
++++ b/docs/requirements.txt
+@@ -0,0 +1,2 @@
++sphinx==5.3.0
++sphinx_rtd_theme==1.1.1
+diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
+index 0cbc2fb4cb..6bc00254cc 100644
+--- a/hw/block/pflash_cfi01.c
++++ b/hw/block/pflash_cfi01.c
+@@ -81,16 +81,39 @@ struct PFlashCFI01 {
+     uint16_t ident3;
+     uint8_t cfi_table[0x52];
+     uint64_t counter;
+-    unsigned int writeblock_size;
++    uint32_t writeblock_size;
+     MemoryRegion mem;
+     char *name;
+     void *storage;
+     VMChangeStateEntry *vmstate;
+     bool old_multiple_chip_handling;
++
++    /* block update buffer */
++    unsigned char *blk_bytes;
++    uint32_t blk_offset;
+ };
+ 
+ static int pflash_post_load(void *opaque, int version_id);
+ 
++static bool pflash_blk_write_state_needed(void *opaque)
++{
++    PFlashCFI01 *pfl = opaque;
++
++    return (pfl->blk_offset != -1);
++}
++
++static const VMStateDescription vmstate_pflash_blk_write = {
++    .name = "pflash_cfi01_blk_write",
++    .version_id = 1,
++    .minimum_version_id = 1,
++    .needed = pflash_blk_write_state_needed,
++    .fields = (const VMStateField[]) {
++        VMSTATE_VBUFFER_UINT32(blk_bytes, PFlashCFI01, 0, NULL, writeblock_size),
++        VMSTATE_UINT32(blk_offset, PFlashCFI01),
++        VMSTATE_END_OF_LIST()
++    }
++};
++
+ static const VMStateDescription vmstate_pflash = {
+     .name = "pflash_cfi01",
+     .version_id = 1,
+@@ -102,6 +125,10 @@ static const VMStateDescription vmstate_pflash = {
+         VMSTATE_UINT8(status, PFlashCFI01),
+         VMSTATE_UINT64(counter, PFlashCFI01),
+         VMSTATE_END_OF_LIST()
++    },
++    .subsections = (const VMStateDescription * []) {
++        &vmstate_pflash_blk_write,
++        NULL
+     }
+ };
+ 
+@@ -226,34 +253,10 @@ static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset,
+     uint32_t ret;
+ 
+     p = pfl->storage;
+-    switch (width) {
+-    case 1:
+-        ret = p[offset];
+-        break;
+-    case 2:
+-        if (be) {
+-            ret = p[offset] << 8;
+-            ret |= p[offset + 1];
+-        } else {
+-            ret = p[offset];
+-            ret |= p[offset + 1] << 8;
+-        }
+-        break;
+-    case 4:
+-        if (be) {
+-            ret = p[offset] << 24;
+-            ret |= p[offset + 1] << 16;
+-            ret |= p[offset + 2] << 8;
+-            ret |= p[offset + 3];
+-        } else {
+-            ret = p[offset];
+-            ret |= p[offset + 1] << 8;
+-            ret |= p[offset + 2] << 16;
+-            ret |= p[offset + 3] << 24;
+-        }
+-        break;
+-    default:
+-        abort();
++    if (be) {
++        ret = ldn_be_p(p + offset, width);
++    } else {
++        ret = ldn_le_p(p + offset, width);
+     }
+     trace_pflash_data_read(pfl->name, offset, width, ret);
+     return ret;
+@@ -401,40 +404,61 @@ static void pflash_update(PFlashCFI01 *pfl, int offset,
+     }
+ }
+ 
++/* copy current flash content to block update buffer */
++static void pflash_blk_write_start(PFlashCFI01 *pfl, hwaddr offset)
++{
++    hwaddr mask = ~(pfl->writeblock_size - 1);
++
++    trace_pflash_write_block_start(pfl->name, pfl->counter);
++    pfl->blk_offset = offset & mask;
++    memcpy(pfl->blk_bytes, pfl->storage + pfl->blk_offset,
++           pfl->writeblock_size);
++}
++
++/* commit block update buffer changes */
++static void pflash_blk_write_flush(PFlashCFI01 *pfl)
++{
++    g_assert(pfl->blk_offset != -1);
++    trace_pflash_write_block_flush(pfl->name);
++    memcpy(pfl->storage + pfl->blk_offset, pfl->blk_bytes,
++           pfl->writeblock_size);
++    pflash_update(pfl, pfl->blk_offset, pfl->writeblock_size);
++    pfl->blk_offset = -1;
++}
++
++/* discard block update buffer changes */
++static void pflash_blk_write_abort(PFlashCFI01 *pfl)
++{
++    trace_pflash_write_block_abort(pfl->name);
++    pfl->blk_offset = -1;
++}
++
+ static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset,
+                                      uint32_t value, int width, int be)
+ {
+-    uint8_t *p = pfl->storage;
++    uint8_t *p;
+ 
+-    trace_pflash_data_write(pfl->name, offset, width, value, pfl->counter);
+-    switch (width) {
+-    case 1:
+-        p[offset] = value;
+-        break;
+-    case 2:
+-        if (be) {
+-            p[offset] = value >> 8;
+-            p[offset + 1] = value;
+-        } else {
+-            p[offset] = value;
+-            p[offset + 1] = value >> 8;
+-        }
+-        break;
+-    case 4:
+-        if (be) {
+-            p[offset] = value >> 24;
+-            p[offset + 1] = value >> 16;
+-            p[offset + 2] = value >> 8;
+-            p[offset + 3] = value;
+-        } else {
+-            p[offset] = value;
+-            p[offset + 1] = value >> 8;
+-            p[offset + 2] = value >> 16;
+-            p[offset + 3] = value >> 24;
++    if (pfl->blk_offset != -1) {
++        /* block write: redirect writes to block update buffer */
++        if ((offset < pfl->blk_offset) ||
++            (offset + width > pfl->blk_offset + pfl->writeblock_size)) {
++            pfl->status |= 0x10; /* Programming error */
++            return;
+         }
+-        break;
++        trace_pflash_data_write_block(pfl->name, offset, width, value,
++                                      pfl->counter);
++        p = pfl->blk_bytes + (offset - pfl->blk_offset);
++    } else {
++        /* write directly to storage */
++        trace_pflash_data_write(pfl->name, offset, width, value);
++        p = pfl->storage + offset;
+     }
+ 
++    if (be) {
++        stn_be_p(p, width, value);
++    } else {
++        stn_le_p(p, width, value);
++    }
+ }
+ 
+ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+@@ -549,9 +573,9 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+             } else {
+                 value = extract32(value, 0, pfl->bank_width * 8);
+             }
+-            trace_pflash_write_block(pfl->name, value);
+             pfl->counter = value;
+             pfl->wcycle++;
++            pflash_blk_write_start(pfl, offset);
+             break;
+         case 0x60:
+             if (cmd == 0xd0) {
+@@ -582,12 +606,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+         switch (pfl->cmd) {
+         case 0xe8: /* Block write */
+             /* FIXME check @offset, @width */
+-            if (!pfl->ro) {
+-                /*
+-                 * FIXME writing straight to memory is *wrong*.  We
+-                 * should write to a buffer, and flush it to memory
+-                 * only on confirm command (see below).
+-                 */
++            if (!pfl->ro && (pfl->blk_offset != -1)) {
+                 pflash_data_write(pfl, offset, value, width, be);
+             } else {
+                 pfl->status |= 0x10; /* Programming error */
+@@ -596,18 +615,8 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+             pfl->status |= 0x80;
+ 
+             if (!pfl->counter) {
+-                hwaddr mask = pfl->writeblock_size - 1;
+-                mask = ~mask;
+-
+                 trace_pflash_write(pfl->name, "block write finished");
+                 pfl->wcycle++;
+-                if (!pfl->ro) {
+-                    /* Flush the entire write buffer onto backing storage.  */
+-                    /* FIXME premature! */
+-                    pflash_update(pfl, offset & mask, pfl->writeblock_size);
+-                } else {
+-                    pfl->status |= 0x10; /* Programming error */
+-                }
+             }
+ 
+             pfl->counter--;
+@@ -619,20 +628,17 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
+     case 3: /* Confirm mode */
+         switch (pfl->cmd) {
+         case 0xe8: /* Block write */
+-            if (cmd == 0xd0) {
+-                /* FIXME this is where we should write out the buffer */
++            if ((cmd == 0xd0) && !(pfl->status & 0x10)) {
++                pflash_blk_write_flush(pfl);
+                 pfl->wcycle = 0;
+                 pfl->status |= 0x80;
+             } else {
+-                qemu_log_mask(LOG_UNIMP,
+-                    "%s: Aborting write to buffer not implemented,"
+-                    " the data is already written to storage!\n"
+-                    "Flash device reset into READ mode.\n",
+-                    __func__);
++                pflash_blk_write_abort(pfl);
+                 goto mode_read_array;
+             }
+             break;
+         default:
++            pflash_blk_write_abort(pfl);
+             goto error_flash;
+         }
+         break;
+@@ -866,6 +872,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
+     pfl->cmd = 0x00;
+     pfl->status = 0x80; /* WSM ready */
+     pflash_cfi01_fill_cfi_table(pfl);
++
++    pfl->blk_bytes = g_malloc(pfl->writeblock_size);
++    pfl->blk_offset = -1;
+ }
+ 
+ static void pflash_cfi01_system_reset(DeviceState *dev)
+@@ -885,6 +894,8 @@ static void pflash_cfi01_system_reset(DeviceState *dev)
+      * This model deliberately ignores this delay.
+      */
+     pfl->status = 0x80;
++
++    pfl->blk_offset = -1;
+ }
+ 
+ static Property pflash_cfi01_properties[] = {
+diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
+index 2a99b286b0..6fa56f14c0 100644
+--- a/hw/block/pflash_cfi02.c
++++ b/hw/block/pflash_cfi02.c
+@@ -546,7 +546,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
+                 }
+                 goto reset_flash;
+             }
+-            trace_pflash_data_write(pfl->name, offset, width, value, 0);
++            trace_pflash_data_write(pfl->name, offset, width, value);
+             if (!pfl->ro) {
+                 p = (uint8_t *)pfl->storage + offset;
+                 if (pfl->be) {
+diff --git a/hw/block/trace-events b/hw/block/trace-events
+index 2c45a62bd5..196493feae 100644
+--- a/hw/block/trace-events
++++ b/hw/block/trace-events
+@@ -12,7 +12,8 @@ fdctrl_tc_pulse(int level) "TC pulse: %u"
+ pflash_chip_erase_invalid(const char *name, uint64_t offset) "%s: chip erase: invalid address 0x%" PRIx64
+ pflash_chip_erase_start(const char *name) "%s: start chip erase"
+ pflash_data_read(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x"
+-pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64
++pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x"
++pflash_data_write_block(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64
+ pflash_device_id(const char *name, uint16_t id) "%s: read device ID: 0x%04x"
+ pflash_device_info(const char *name, uint64_t offset) "%s: read device information offset:0x%04" PRIx64
+ pflash_erase_complete(const char *name) "%s: sector erase complete"
+@@ -32,7 +33,9 @@ pflash_unlock0_failed(const char *name, uint64_t offset, uint8_t cmd, uint16_t a
+ pflash_unlock1_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: unlock0 failed 0x%" PRIx64 " 0x%02x"
+ pflash_unsupported_device_configuration(const char *name, uint8_t width, uint8_t max) "%s: unsupported device configuration: device_width:%d max_device_width:%d"
+ pflash_write(const char *name, const char *str) "%s: %s"
+-pflash_write_block(const char *name, uint32_t value) "%s: block write: bytes:0x%x"
++pflash_write_block_start(const char *name, uint32_t value) "%s: block write start: bytes:0x%x"
++pflash_write_block_flush(const char *name) "%s: block write flush"
++pflash_write_block_abort(const char *name) "%s: block write abort"
+ pflash_write_block_erase(const char *name, uint64_t offset, uint64_t len) "%s: block erase offset:0x%" PRIx64 " bytes:0x%" PRIx64
+ pflash_write_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: command failed 0x%" PRIx64 " 0x%02x"
+ pflash_write_invalid(const char *name, uint8_t cmd) "%s: invalid write for command 0x%02x"
+diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
+index b17b29288c..f71b3b07d8 100644
+--- a/hw/intc/arm_gicv3_cpuif.c
++++ b/hw/intc/arm_gicv3_cpuif.c
+@@ -1432,16 +1432,25 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
+     idx = icv_find_active(cs, irq);
+ 
+     if (idx < 0) {
+-        /* No valid list register corresponding to EOI ID */
+-        icv_increment_eoicount(cs);
++        /*
++         * No valid list register corresponding to EOI ID; if this is a vLPI
++         * not in the list regs then do nothing; otherwise increment EOI count
++         */
++        if (irq < GICV3_LPI_INTID_START) {
++            icv_increment_eoicount(cs);
++        }
+     } else {
+         uint64_t lr = cs->ich_lr_el2[idx];
+         int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
+         int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp);
+ 
+         if (thisgrp == grp && lr_gprio == dropprio) {
+-            if (!icv_eoi_split(env, cs)) {
+-                /* Priority drop and deactivate not split: deactivate irq now */
++            if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) {
++                /*
++                 * Priority drop and deactivate not split: deactivate irq now.
++                 * LPIs always get their active state cleared immediately
++                 * because no separate deactivate is expected.
++                 */
+                 icv_deactivate_irq(cs, idx);
+             }
+         }
+diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
+index 06f35ac2d8..412cba4927 100644
+--- a/hw/net/virtio-net.c
++++ b/hw/net/virtio-net.c
+@@ -664,6 +664,11 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
+ 
+     n->mergeable_rx_bufs = mergeable_rx_bufs;
+ 
++    /*
++     * Note: when extending the vnet header, please make sure to
++     * change the vnet header copying logic in virtio_net_flush_tx()
++     * as well.
++     */
+     if (version_1) {
+         n->guest_hdr_len = hash_report ?
+             sizeof(struct virtio_net_hdr_v1_hash) :
+@@ -2630,7 +2635,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
+         ssize_t ret;
+         unsigned int out_num;
+         struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
+-        struct virtio_net_hdr_mrg_rxbuf mhdr;
++        struct virtio_net_hdr_v1_hash vhdr;
+ 
+         elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
+         if (!elem) {
+@@ -2647,7 +2652,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
+         }
+ 
+         if (n->has_vnet_hdr) {
+-            if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
++            if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
+                 n->guest_hdr_len) {
+                 virtio_error(vdev, "virtio-net header incorrect");
+                 virtqueue_detach_element(q->tx_vq, elem, 0);
+@@ -2655,8 +2660,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
+                 return -EINVAL;
+             }
+             if (n->needs_vnet_hdr_swap) {
+-                virtio_net_hdr_swap(vdev, (void *) &mhdr);
+-                sg2[0].iov_base = &mhdr;
++                virtio_net_hdr_swap(vdev, (void *) &vhdr);
++                sg2[0].iov_base = &vhdr;
+                 sg2[0].iov_len = n->guest_hdr_len;
+                 out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
+                                    out_sg, out_num,
+diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c
+index 1792f84cea..6b7b963409 100644
+--- a/hw/scsi/esp-pci.c
++++ b/hw/scsi/esp-pci.c
+@@ -77,6 +77,41 @@ struct PCIESPState {
+     ESPState esp;
+ };
+ 
++static void esp_pci_update_irq(PCIESPState *pci)
++{
++    int scsi_level = !!(pci->dma_regs[DMA_STAT] & DMA_STAT_SCSIINT);
++    int dma_level = (pci->dma_regs[DMA_CMD] & DMA_CMD_INTE_D) ?
++                    !!(pci->dma_regs[DMA_STAT] & DMA_STAT_DONE) : 0;
++    int level = scsi_level || dma_level;
++
++    pci_set_irq(PCI_DEVICE(pci), level);
++}
++
++static void esp_irq_handler(void *opaque, int irq_num, int level)
++{
++    PCIESPState *pci = PCI_ESP(opaque);
++
++    if (level) {
++        pci->dma_regs[DMA_STAT] |= DMA_STAT_SCSIINT;
++
++        /*
++         * If raising the ESP IRQ to indicate end of DMA transfer, set
++         * DMA_STAT_DONE at the same time. In theory this should be done in
++         * esp_pci_dma_memory_rw(), however there is a delay between setting
++         * DMA_STAT_DONE and the ESP IRQ arriving which is visible to the
++         * guest that can cause confusion e.g. Linux
++         */
++        if ((pci->dma_regs[DMA_CMD] & DMA_CMD_MASK) == 0x3 &&
++            pci->dma_regs[DMA_WBC] == 0) {
++                pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
++        }
++    } else {
++        pci->dma_regs[DMA_STAT] &= ~DMA_STAT_SCSIINT;
++    }
++
++    esp_pci_update_irq(pci);
++}
++
+ static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val)
+ {
+     ESPState *s = ESP(&pci->esp);
+@@ -89,6 +124,7 @@ static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val)
+ {
+     trace_esp_pci_dma_blast(val);
+     qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n");
++    pci->dma_regs[DMA_STAT] |= DMA_STAT_BCMBLT;
+ }
+ 
+ static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val)
+@@ -151,6 +187,7 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val)
+             /* clear some bits on write */
+             uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE;
+             pci->dma_regs[DMA_STAT] &= ~(val & mask);
++            esp_pci_update_irq(pci);
+         }
+         break;
+     default:
+@@ -161,17 +198,14 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val)
+ 
+ static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr)
+ {
+-    ESPState *s = ESP(&pci->esp);
+     uint32_t val;
+ 
+     val = pci->dma_regs[saddr];
+     if (saddr == DMA_STAT) {
+-        if (s->rregs[ESP_RSTAT] & STAT_INT) {
+-            val |= DMA_STAT_SCSIINT;
+-        }
+         if (!(pci->sbac & SBAC_STATUS)) {
+             pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT |
+                                          DMA_STAT_DONE);
++            esp_pci_update_irq(pci);
+         }
+     }
+ 
+@@ -275,7 +309,7 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
+         qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n");
+     }
+ 
+-    addr = pci->dma_regs[DMA_SPA];
++    addr = pci->dma_regs[DMA_WAC];
+     if (pci->dma_regs[DMA_WBC] < len) {
+         len = pci->dma_regs[DMA_WBC];
+     }
+@@ -285,9 +319,6 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
+     /* update status registers */
+     pci->dma_regs[DMA_WBC] -= len;
+     pci->dma_regs[DMA_WAC] += len;
+-    if (pci->dma_regs[DMA_WBC] == 0) {
+-        pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
+-    }
+ }
+ 
+ static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len)
+@@ -342,23 +373,13 @@ static const VMStateDescription vmstate_esp_pci_scsi = {
+     }
+ };
+ 
+-static void esp_pci_command_complete(SCSIRequest *req, size_t resid)
+-{
+-    ESPState *s = req->hba_private;
+-    PCIESPState *pci = container_of(s, PCIESPState, esp);
+-
+-    esp_command_complete(req, resid);
+-    pci->dma_regs[DMA_WBC] = 0;
+-    pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
+-}
+-
+ static const struct SCSIBusInfo esp_pci_scsi_info = {
+     .tcq = false,
+     .max_target = ESP_MAX_DEVS,
+     .max_lun = 7,
+ 
+     .transfer_data = esp_transfer_data,
+-    .complete = esp_pci_command_complete,
++    .complete = esp_command_complete,
+     .cancel = esp_request_cancelled,
+ };
+ 
+@@ -386,7 +407,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp)
+                           "esp-io", 0x80);
+ 
+     pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io);
+-    s->irq = pci_allocate_irq(dev);
++    s->irq = qemu_allocate_irq(esp_irq_handler, pci, 0);
+ 
+     scsi_bus_init(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info);
+ }
+diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
+index 9b7bfbf09a..db677c856b 100644
+--- a/include/exec/exec-all.h
++++ b/include/exec/exec-all.h
+@@ -503,7 +503,6 @@ struct tb_tc {
+ };
+ 
+ struct TranslationBlock {
+-#if !TARGET_TB_PCREL
+     /*
+      * Guest PC corresponding to this block.  This must be the true
+      * virtual address.  Therefore e.g. x86 stores EIP + CS_BASE, and
+@@ -518,7 +517,6 @@ struct TranslationBlock {
+      * deposited into the "current" PC.
+      */
+     target_ulong pc;
+-#endif
+ 
+     /*
+      * Target-specific data associated with the TranslationBlock, e.g.:
+@@ -604,11 +602,7 @@ struct TranslationBlock {
+ /* Hide the read to avoid ifdefs for TARGET_TB_PCREL. */
+ static inline target_ulong tb_pc(const TranslationBlock *tb)
+ {
+-#if TARGET_TB_PCREL
+-    qemu_build_not_reached();
+-#else
+     return tb->pc;
+-#endif
+ }
+ 
+ /* Hide the qatomic_read to make code a little easier on the eyes */
+diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h
+index fbe0b1e956..78f45c8115 100644
+--- a/include/hw/elf_ops.h
++++ b/include/hw/elf_ops.h
+@@ -499,7 +499,7 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd,
+             }
+ 
+             if (data_swab) {
+-                int j;
++                elf_word j;
+                 for (j = 0; j < file_size; j += (1 << data_swab)) {
+                     uint8_t *dp = data + j;
+                     switch (data_swab) {
+diff --git a/monitor/qmp.c b/monitor/qmp.c
+index 092c527b6f..acd0a350c2 100644
+--- a/monitor/qmp.c
++++ b/monitor/qmp.c
+@@ -296,14 +296,6 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data)
+             qemu_coroutine_yield();
+         }
+ 
+-        /*
+-         * Move the coroutine from iohandler_ctx to qemu_aio_context for
+-         * executing the command handler so that it can make progress if it
+-         * involves an AIO_WAIT_WHILE().
+-         */
+-        aio_co_schedule(qemu_get_aio_context(), qmp_dispatcher_co);
+-        qemu_coroutine_yield();
+-
+         /* Process request */
+         if (req_obj->req) {
+             if (trace_event_get_state(TRACE_MONITOR_QMP_CMD_IN_BAND)) {
+@@ -330,15 +322,6 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data)
+         }
+ 
+         qmp_request_free(req_obj);
+-
+-        /*
+-         * Yield and reschedule so the main loop stays responsive.
+-         *
+-         * Move back to iohandler_ctx so that nested event loops for
+-         * qemu_aio_context don't start new monitor commands.
+-         */
+-        aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co);
+-        qemu_coroutine_yield();
+     }
+ }
+ 
+diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
+index 0990873ec8..5d000fae87 100644
+--- a/qapi/qmp-dispatch.c
++++ b/qapi/qmp-dispatch.c
+@@ -206,9 +206,31 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
+     assert(!(oob && qemu_in_coroutine()));
+     assert(monitor_cur() == NULL);
+     if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) {
++        if (qemu_in_coroutine()) {
++            /*
++             * Move the coroutine from iohandler_ctx to qemu_aio_context for
++             * executing the command handler so that it can make progress if it
++             * involves an AIO_WAIT_WHILE().
++             */
++            aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self());
++            qemu_coroutine_yield();
++        }
++
+         monitor_set_cur(qemu_coroutine_self(), cur_mon);
+         cmd->fn(args, &ret, &err);
+         monitor_set_cur(qemu_coroutine_self(), NULL);
++
++        if (qemu_in_coroutine()) {
++            /*
++             * Yield and reschedule so the main loop stays responsive.
++             *
++             * Move back to iohandler_ctx so that nested event loops for
++             * qemu_aio_context don't start new monitor commands.
++             */
++            aio_co_schedule(iohandler_get_aio_context(),
++                            qemu_coroutine_self());
++            qemu_coroutine_yield();
++        }
+     } else {
+        /*
+         * Actual context doesn't match the one the command needs.
+@@ -232,7 +254,7 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
+             .errp       = &err,
+             .co         = qemu_coroutine_self(),
+         };
+-        aio_bh_schedule_oneshot(qemu_get_aio_context(), do_qmp_dispatch_bh,
++        aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh,
+                                 &data);
+         qemu_coroutine_yield();
+     }
+diff --git a/softmmu/vl.c b/softmmu/vl.c
+index 5115221efe..ce88869618 100644
+--- a/softmmu/vl.c
++++ b/softmmu/vl.c
+@@ -2321,6 +2321,10 @@ static void qemu_validate_options(const QDict *machine_opts)
+           }
+     }
+ 
++    if (loadvm && incoming) {
++        error_report("'incoming' and 'loadvm' options are mutually exclusive");
++        exit(EXIT_FAILURE);
++    }
+     if (loadvm && preconfig_requested) {
+         error_report("'preconfig' and 'loadvm' options are "
+                      "mutually exclusive");
+diff --git a/target/i386/cpu.h b/target/i386/cpu.h
+index d4bc19577a..f67cee477a 100644
+--- a/target/i386/cpu.h
++++ b/target/i386/cpu.h
+@@ -2217,10 +2217,15 @@ static inline int cpu_mmu_index_kernel(CPUX86State *env)
+ static inline void cpu_get_tb_cpu_state(CPUX86State *env, target_ulong *pc,
+                                         target_ulong *cs_base, uint32_t *flags)
+ {
+-    *cs_base = env->segs[R_CS].base;
+-    *pc = *cs_base + env->eip;
+     *flags = env->hflags |
+         (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK));
++    if (env->hflags & HF_CS64_MASK) {
++        *cs_base = 0;
++        *pc = env->eip;
++    } else {
++        *cs_base = env->segs[R_CS].base;
++        *pc = (uint32_t)(*cs_base + env->eip);
++    }
+ }
+ 
+ void do_cpu_init(X86CPU *cpu);
+diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c
+index 79ac5908f7..cd49f86341 100644
+--- a/target/i386/tcg/tcg-cpu.c
++++ b/target/i386/tcg/tcg-cpu.c
+@@ -52,7 +52,12 @@ static void x86_cpu_synchronize_from_tb(CPUState *cs,
+     /* The instruction pointer is always up to date with TARGET_TB_PCREL. */
+     if (!TARGET_TB_PCREL) {
+         CPUX86State *env = cs->env_ptr;
+-        env->eip = tb_pc(tb) - tb->cs_base;
++
++        if (tb->flags & HF_CS64_MASK) {
++            env->eip = tb_pc(tb);
++        } else {
++            env->eip = (uint32_t)(tb_pc(tb) - tb->cs_base);
++        }
+     }
+ }
+ 
+@@ -63,12 +68,26 @@ static void x86_restore_state_to_opc(CPUState *cs,
+     X86CPU *cpu = X86_CPU(cs);
+     CPUX86State *env = &cpu->env;
+     int cc_op = data[1];
++    uint64_t new_pc;
+ 
+     if (TARGET_TB_PCREL) {
+-        env->eip = (env->eip & TARGET_PAGE_MASK) | data[0];
++        /*
++         * data[0] in PC-relative TBs is also a linear address, i.e. an address with
++         * the CS base added, because it is not guaranteed that EIP bits 12 and higher
++         * stay the same across the translation block.  Add the CS base back before
++         * replacing the low bits, and subtract it below just like for !CF_PCREL.
++         */
++        uint64_t pc = env->eip + tb->cs_base;
++        new_pc = (pc & TARGET_PAGE_MASK) | data[0];
++    } else {
++        new_pc = data[0];
++    }
++    if (tb->flags & HF_CS64_MASK) {
++        env->eip = new_pc;
+     } else {
+-        env->eip = data[0] - tb->cs_base;
++        env->eip = (uint32_t)(new_pc - tb->cs_base);
+     }
++
+     if (cc_op != CC_OP_DYNAMIC) {
+         env->cc_op = cc_op;
+     }
+diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
+index 7e0b2a709a..68c42fd9ff 100644
+--- a/target/i386/tcg/translate.c
++++ b/target/i386/tcg/translate.c
+@@ -547,8 +547,10 @@ static void gen_update_eip_cur(DisasContext *s)
+     assert(s->pc_save != -1);
+     if (TARGET_TB_PCREL) {
+         tcg_gen_addi_tl(cpu_eip, cpu_eip, s->base.pc_next - s->pc_save);
++    } else if (CODE64(s)) {
++        tcg_gen_movi_tl(cpu_eip, s->base.pc_next);
+     } else {
+-        tcg_gen_movi_tl(cpu_eip, s->base.pc_next - s->cs_base);
++        tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base));
+     }
+     s->pc_save = s->base.pc_next;
+ }
+@@ -558,8 +560,10 @@ static void gen_update_eip_next(DisasContext *s)
+     assert(s->pc_save != -1);
+     if (TARGET_TB_PCREL) {
+         tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save);
++    } else if (CODE64(s)) {
++        tcg_gen_movi_tl(cpu_eip, s->pc);
+     } else {
+-        tcg_gen_movi_tl(cpu_eip, s->pc - s->cs_base);
++        tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->pc - s->cs_base));
+     }
+     s->pc_save = s->pc;
+ }
+@@ -605,8 +609,10 @@ static TCGv eip_next_tl(DisasContext *s)
+         TCGv ret = tcg_temp_new();
+         tcg_gen_addi_tl(ret, cpu_eip, s->pc - s->pc_save);
+         return ret;
++    } else if (CODE64(s)) {
++        return tcg_constant_tl(s->pc);
+     } else {
+-        return tcg_constant_tl(s->pc - s->cs_base);
++        return tcg_constant_tl((uint32_t)(s->pc - s->cs_base));
+     }
+ }
+ 
+@@ -617,8 +623,10 @@ static TCGv eip_cur_tl(DisasContext *s)
+         TCGv ret = tcg_temp_new();
+         tcg_gen_addi_tl(ret, cpu_eip, s->base.pc_next - s->pc_save);
+         return ret;
++    } else if (CODE64(s)) {
++        return tcg_constant_tl(s->base.pc_next);
+     } else {
+-        return tcg_constant_tl(s->base.pc_next - s->cs_base);
++        return tcg_constant_tl((uint32_t)(s->base.pc_next - s->cs_base));
+     }
+ }
+ 
+@@ -2890,10 +2898,11 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num)
+             tcg_gen_andi_tl(cpu_eip, cpu_eip, mask);
+             use_goto_tb = false;
+         }
++    } else if (!CODE64(s)) {
++        new_pc = (uint32_t)(new_eip + s->cs_base);
+     }
+ 
+-    if (use_goto_tb &&
+-        translator_use_goto_tb(&s->base, new_eip + s->cs_base)) {
++    if (use_goto_tb && translator_use_goto_tb(&s->base, new_pc)) {
+         /* jump to same page: we can use a direct jump */
+         tcg_gen_goto_tb(tb_num);
+         if (!TARGET_TB_PCREL) {
+@@ -6974,7 +6983,6 @@ static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
+ 
+     dc->prev_insn_end = tcg_last_op();
+     if (TARGET_TB_PCREL) {
+-        pc_arg -= dc->cs_base;
+         pc_arg &= ~TARGET_PAGE_MASK;
+     }
+     tcg_gen_insn_start(pc_arg, dc->cc_op);
+diff --git a/target/riscv/csr.c b/target/riscv/csr.c
+index 5c9a7ee287..15dba5f653 100644
+--- a/target/riscv/csr.c
++++ b/target/riscv/csr.c
+@@ -697,11 +697,11 @@ static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val)
+ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
+                                          bool upper_half, uint32_t ctr_idx)
+ {
+-    PMUCTRState counter = env->pmu_ctrs[ctr_idx];
+-    target_ulong ctr_prev = upper_half ? counter.mhpmcounterh_prev :
+-                                         counter.mhpmcounter_prev;
+-    target_ulong ctr_val = upper_half ? counter.mhpmcounterh_val :
+-                                        counter.mhpmcounter_val;
++    PMUCTRState *counter = &env->pmu_ctrs[ctr_idx];
++    target_ulong ctr_prev = upper_half ? counter->mhpmcounterh_prev :
++                                         counter->mhpmcounter_prev;
++    target_ulong ctr_val = upper_half ? counter->mhpmcounterh_val :
++                                        counter->mhpmcounter_val;
+ 
+     if (get_field(env->mcountinhibit, BIT(ctr_idx))) {
+         /**
+@@ -709,12 +709,12 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
+          * stop the icount counting. Just return the counter value written by
+          * the supervisor to indicate that counter was not incremented.
+          */
+-        if (!counter.started) {
++        if (!counter->started) {
+             *val = ctr_val;
+             return RISCV_EXCP_NONE;
+         } else {
+             /* Mark that the counter has been stopped */
+-            counter.started = false;
++            counter->started = false;
+         }
+     }
+ 
+diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
+index b0173e968e..a257c06838 100644
+--- a/target/s390x/tcg/translate.c
++++ b/target/s390x/tcg/translate.c
+@@ -3394,6 +3394,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o)
+ {
+     int b2 = get_field(s, b2);
+     TCGv ar1 = tcg_temp_new_i64();
++    int r1 = get_field(s, r1);
+ 
+     o->out = o->in2;
+     o->g_out = o->g_in2;
+@@ -3419,7 +3420,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o)
+         break;
+     }
+ 
+-    tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1]));
++    tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[r1]));
+     tcg_temp_free_i64(ar1);
+ 
+     return DISAS_NEXT;
+diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c
+index fa66e8e867..c4b4df4d74 100644
+--- a/target/xtensa/mmu_helper.c
++++ b/target/xtensa/mmu_helper.c
+@@ -226,22 +226,31 @@ static void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v,
+  * Split TLB address into TLB way, entry index and VPN (with index).
+  * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format
+  */
+-static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb,
+-        uint32_t *vpn, uint32_t *wi, uint32_t *ei)
++static bool split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb,
++                                 uint32_t *vpn, uint32_t *wi, uint32_t *ei)
+ {
+     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+         *wi = v & (dtlb ? 0xf : 0x7);
+-        split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei);
++        if (*wi < (dtlb ? env->config->dtlb.nways : env->config->itlb.nways)) {
++            split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei);
++            return true;
++        } else {
++            return false;
++        }
+     } else {
+         *vpn = v & REGION_PAGE_MASK;
+         *wi = 0;
+         *ei = (v >> 29) & 0x7;
++        return true;
+     }
+ }
+ 
+ static xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env, bool dtlb,
+                                               unsigned wi, unsigned ei)
+ {
++    const xtensa_tlb *tlb = dtlb ? &env->config->dtlb : &env->config->itlb;
++
++    assert(wi < tlb->nways && ei < tlb->way_size[wi]);
+     return dtlb ?
+         env->dtlb[wi] + ei :
+         env->itlb[wi] + ei;
+@@ -254,11 +263,14 @@ static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env,
+     uint32_t wi;
+     uint32_t ei;
+ 
+-    split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei);
+-    if (pwi) {
+-        *pwi = wi;
++    if (split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei)) {
++        if (pwi) {
++            *pwi = wi;
++        }
++        return xtensa_tlb_get_entry(env, dtlb, wi, ei);
++    } else {
++        return NULL;
+     }
+-    return xtensa_tlb_get_entry(env, dtlb, wi, ei);
+ }
+ 
+ static void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env,
+@@ -484,7 +496,12 @@ uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+         uint32_t wi;
+         const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi);
+-        return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid;
++
++        if (entry) {
++            return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid;
++        } else {
++            return 0;
++        }
+     } else {
+         return v & REGION_PAGE_MASK;
+     }
+@@ -493,7 +510,12 @@ uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+ uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+ {
+     const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL);
+-    return entry->paddr | entry->attr;
++
++    if (entry) {
++        return entry->paddr | entry->attr;
++    } else {
++        return 0;
++    }
+ }
+ 
+ void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+@@ -501,7 +523,7 @@ void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb)
+     if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) {
+         uint32_t wi;
+         xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi);
+-        if (entry->variable && entry->asid) {
++        if (entry && entry->variable && entry->asid) {
+             tlb_flush_page(env_cpu(env), entry->vaddr);
+             entry->asid = 0;
+         }
+@@ -539,8 +561,9 @@ void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb)
+     uint32_t vpn;
+     uint32_t wi;
+     uint32_t ei;
+-    split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei);
+-    xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p);
++    if (split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei)) {
++        xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p);
++    }
+ }
+ 
+ /*!
+diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out
+index 329977d9b9..a37bf446e9 100644
+--- a/tests/qemu-iotests/060.out
++++ b/tests/qemu-iotests/060.out
+@@ -421,8 +421,8 @@ QMP_VERSION
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "none0", "msg": "Preventing invalid write on metadata (overlaps with refcount table)", "offset": 65536, "node-name": "drive", "fatal": true, "size": 65536}}
+ write failed: Input/output error
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ === Testing incoming inactive corrupted image ===
+ 
+@@ -432,8 +432,8 @@ QMP_VERSION
+ qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}}
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+     corrupt: false
+ *** done
+diff --git a/tests/qemu-iotests/071.out b/tests/qemu-iotests/071.out
+index bca0c02f5c..a2923b05c2 100644
+--- a/tests/qemu-iotests/071.out
++++ b/tests/qemu-iotests/071.out
+@@ -45,8 +45,8 @@ QMP_VERSION
+ {"return": {}}
+ read failed: Input/output error
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ === Testing blkverify on existing block device ===
+@@ -84,9 +84,9 @@ wrote 512/512 bytes at offset 0
+ {"return": ""}
+ read failed: Input/output error
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ QEMU_PROG: Failed to flush the L2 table cache: Input/output error
+ QEMU_PROG: Failed to flush the refcount block cache: Input/output error
++{"return": {}}
+ 
+ *** done
+diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out
+index 615c083549..aba85ea564 100644
+--- a/tests/qemu-iotests/081.out
++++ b/tests/qemu-iotests/081.out
+@@ -35,8 +35,8 @@ QMP_VERSION
+ read 10485760/10485760 bytes at offset 0
+ 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ == using quorum rewrite corrupted mode ==
+@@ -67,8 +67,8 @@ QMP_VERSION
+ read 10485760/10485760 bytes at offset 0
+ 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ -- checking that the image has been corrected --
+ read 10485760/10485760 bytes at offset 0
+@@ -106,8 +106,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ Testing:
+ QMP_VERSION
+@@ -115,8 +115,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "Cannot add a child to a quorum in blkverify mode"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ == dynamically removing a child from a quorum ==
+@@ -125,31 +125,31 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "blkverify=on can only be set if there are exactly two files and vote-threshold is 2"}}
+ {"error": {"class": "GenericError", "desc": "Cannot find device='drive0-quorum' nor node-name='drive0-quorum'"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ *** done
+diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
+index e1c23a6983..97b6d8036d 100644
+--- a/tests/qemu-iotests/087.out
++++ b/tests/qemu-iotests/087.out
+@@ -7,8 +7,8 @@ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "'node-name' must be specified for the root node"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ === Duplicate ID ===
+@@ -18,8 +18,8 @@ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "node-name=disk is conflicting with a device id"}}
+ {"error": {"class": "GenericError", "desc": "Duplicate nodes with node-name='test-node'"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ === aio=native without O_DIRECT ===
+@@ -28,8 +28,8 @@ Testing:
+ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "aio=native was specified, but it requires cache.direct=on, which was not specified."}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ === Encrypted image QCow ===
+@@ -40,8 +40,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "Use of AES-CBC encrypted IMGFMT images is no longer supported in system emulators"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ === Encrypted image LUKS ===
+@@ -52,8 +52,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ === Missing driver ===
+@@ -63,7 +63,7 @@ Testing: -S
+ QMP_VERSION
+ {"return": {}}
+ {"error": {"class": "GenericError", "desc": "Parameter 'driver' is missing"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ *** done
+diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out
+index b5401d788d..b9c876b394 100644
+--- a/tests/qemu-iotests/108.out
++++ b/tests/qemu-iotests/108.out
+@@ -173,8 +173,8 @@ OK: Reftable is where we expect it
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "create"}}
+ {"return": {}}
+ { "execute": "quit" }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109
+index e207a555f3..0fb580f9a5 100755
+--- a/tests/qemu-iotests/109
++++ b/tests/qemu-iotests/109
+@@ -57,13 +57,13 @@ run_qemu()
+     _launch_qemu -drive file="${source_img}",format=raw,cache=${CACHEMODE},aio=${AIOMODE},id=src
+     _send_qemu_cmd $QEMU_HANDLE "{ 'execute': 'qmp_capabilities' }" "return"
+ 
+-    _send_qemu_cmd $QEMU_HANDLE \
++    capture_events="$qmp_event" _send_qemu_cmd $QEMU_HANDLE \
+         "{'execute':'drive-mirror', 'arguments':{
+             'device': 'src', 'target': '$raw_img', $qmp_format
+             'mode': 'existing', 'sync': 'full'}}" \
+         "return"
+ 
+-    _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event"
++    capture_events="$qmp_event JOB_STATUS_CHANGE" _wait_event $QEMU_HANDLE "$qmp_event"
+     if test "$qmp_event" = BLOCK_JOB_ERROR; then
+         _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"'
+     fi
+diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
+index e29280015e..255b81fcdc 100644
+--- a/tests/qemu-iotests/109.out
++++ b/tests/qemu-iotests/109.out
+@@ -7,7 +7,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -23,8 +23,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -35,12 +35,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -48,6 +46,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Writing a qcow2 header into raw ===
+@@ -57,7 +56,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -73,8 +72,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -85,12 +84,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -98,6 +95,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Writing a qed header into raw ===
+@@ -107,7 +105,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -123,8 +121,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -135,12 +133,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -148,6 +144,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Writing a vdi header into raw ===
+@@ -157,7 +154,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -173,8 +170,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -185,12 +182,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -198,6 +193,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Writing a vmdk header into raw ===
+@@ -207,7 +203,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -223,8 +219,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -235,12 +231,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -248,6 +242,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Writing a vpc header into raw ===
+@@ -257,7 +252,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -273,8 +268,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -285,12 +280,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -298,6 +291,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Copying sample image empty.bochs into raw ===
+@@ -306,7 +300,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -322,8 +316,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -334,12 +328,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -347,6 +339,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Copying sample image iotest-dirtylog-10G-4M.vhdx into raw ===
+@@ -355,7 +348,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -371,8 +364,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -383,12 +376,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -396,6 +387,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Copying sample image parallels-v1 into raw ===
+@@ -404,7 +396,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -420,8 +412,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -432,12 +424,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -445,6 +435,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Copying sample image simple-pattern.cloop into raw ===
+@@ -453,7 +444,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -469,8 +460,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"execute":"query-block-jobs"}
+ {"return": []}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 512/512 bytes at offset 0
+ 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ { 'execute': 'qmp_capabilities' }
+@@ -481,12 +472,10 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -494,6 +483,7 @@ read 512/512 bytes at offset 0
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ 
+ === Write legitimate MBR into raw ===
+@@ -502,7 +492,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+ {'execute':'drive-mirror', 'arguments':{
+-            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT',
++            'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 
+             'mode': 'existing', 'sync': 'full'}}
+ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
+          Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
+@@ -510,12 +500,10 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -523,6 +511,7 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ { 'execute': 'qmp_capabilities' }
+ {"return": {}}
+@@ -532,12 +521,10 @@ Images are identical.
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
+ {"execute":"query-block-jobs"}
+ {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
+@@ -545,5 +532,6 @@ Images are identical.
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
++{"return": {}}
+ Images are identical.
+ *** done
+diff --git a/tests/qemu-iotests/117.out b/tests/qemu-iotests/117.out
+index 735ffd25c6..1cea9e0217 100644
+--- a/tests/qemu-iotests/117.out
++++ b/tests/qemu-iotests/117.out
+@@ -18,8 +18,8 @@ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ {"return": ""}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ No errors were found on the image.
+ read 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+diff --git a/tests/qemu-iotests/120.out b/tests/qemu-iotests/120.out
+index 0744c1f136..35d84a5bc5 100644
+--- a/tests/qemu-iotests/120.out
++++ b/tests/qemu-iotests/120.out
+@@ -5,8 +5,8 @@ QMP_VERSION
+ wrote 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ {"return": ""}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ read 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ read 65536/65536 bytes at offset 0
+diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out
+index 1685c4850a..dd8c4a8aa9 100644
+--- a/tests/qemu-iotests/127.out
++++ b/tests/qemu-iotests/127.out
+@@ -28,6 +28,6 @@ wrote 42/42 bytes at offset 0
+ { 'execute': 'quit' }
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out
+index 312f76d5da..32866440ae 100644
+--- a/tests/qemu-iotests/140.out
++++ b/tests/qemu-iotests/140.out
+@@ -19,6 +19,6 @@ read 65536/65536 bytes at offset 0
+ qemu-io: can't open device nbd+unix:///drv?socket=SOCK_DIR/nbd: Requested export not available
+ server reported: export 'drv' not present
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141
+index a37030ee17..a7d3985a02 100755
+--- a/tests/qemu-iotests/141
++++ b/tests/qemu-iotests/141
+@@ -1,9 +1,12 @@
+-#!/usr/bin/env bash
++#!/usr/bin/env python3
+ # group: rw auto quick
+ #
+ # Test case for ejecting BDSs with block jobs still running on them
+ #
+-# Copyright (C) 2016 Red Hat, Inc.
++# Originally written in bash by Hanna Czenczek, ported to Python by Stefan
++# Hajnoczi.
++#
++# Copyright Red Hat
+ #
+ # 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
+@@ -19,177 +22,129 @@
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ #
+ 
+-# creator
+-owner=hreitz@redhat.com
+-
+-seq="$(basename $0)"
+-echo "QA output created by $seq"
+-
+-status=1	# failure is the default!
+-
+-_cleanup()
+-{
+-    _cleanup_qemu
+-    _cleanup_test_img
+-    for img in "$TEST_DIR"/{b,m,o}.$IMGFMT; do
+-        _rm_test_img "$img"
+-    done
+-}
+-trap "_cleanup; exit \$status" 0 1 2 3 15
+-
+-# get standard environment, filters and checks
+-. ./common.rc
+-. ./common.filter
+-. ./common.qemu
+-
+-# Needs backing file and backing format support
+-_supported_fmt qcow2 qed
+-_supported_proto file
+-_supported_os Linux
+-
+-
+-test_blockjob()
+-{
+-    _send_qemu_cmd $QEMU_HANDLE \
+-        "{'execute': 'blockdev-add',
+-          'arguments': {
+-              'node-name': 'drv0',
+-              'driver': '$IMGFMT',
+-              'file': {
+-                  'driver': 'file',
+-                  'filename': '$TEST_IMG'
+-              }}}" \
+-        'return'
+-
+-    # If "$2" is an event, we may or may not see it before the
+-    # {"return": {}}.  Therefore, filter the {"return": {}} out both
+-    # here and in the next command.  (Naturally, if we do not see it
+-    # here, we will see it before the next command can be executed,
+-    # so it will appear in the next _send_qemu_cmd's output.)
+-    _send_qemu_cmd $QEMU_HANDLE \
+-        "$1" \
+-        "$2" \
+-        | _filter_img_create | _filter_qmp_empty_return
+-
+-    # We want this to return an error because the block job is still running
+-    _send_qemu_cmd $QEMU_HANDLE \
+-        "{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}" \
+-        'error' | _filter_generated_node_ids | _filter_qmp_empty_return
+-
+-    _send_qemu_cmd $QEMU_HANDLE \
+-        "{'execute': 'block-job-cancel',
+-          'arguments': {'device': 'job0'}}" \
+-        "$3"
+-
+-    _send_qemu_cmd $QEMU_HANDLE \
+-        "{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}" \
+-        'return'
+-}
+-
+-
+-TEST_IMG="$TEST_DIR/b.$IMGFMT" _make_test_img 1M
+-TEST_IMG="$TEST_DIR/m.$IMGFMT" _make_test_img -b "$TEST_DIR/b.$IMGFMT" -F $IMGFMT 1M
+-_make_test_img -b "$TEST_DIR/m.$IMGFMT" 1M -F $IMGFMT
+-
+-_launch_qemu -nodefaults
+-
+-_send_qemu_cmd $QEMU_HANDLE \
+-    "{'execute': 'qmp_capabilities'}" \
+-    'return'
+-
+-echo
+-echo '=== Testing drive-backup ==='
+-echo
+-
+-# drive-backup will not send BLOCK_JOB_READY by itself, and cancelling the job
+-# will consequently result in BLOCK_JOB_CANCELLED being emitted.
+-
+-test_blockjob \
+-    "{'execute': 'drive-backup',
+-      'arguments': {'job-id': 'job0',
+-                    'device': 'drv0',
+-                    'target': '$TEST_DIR/o.$IMGFMT',
+-                    'format': '$IMGFMT',
+-                    'sync': 'none'}}" \
+-    'return' \
+-    '"status": "null"'
+-
+-echo
+-echo '=== Testing drive-mirror ==='
+-echo
+-
+-# drive-mirror will send BLOCK_JOB_READY basically immediately, and cancelling
+-# the job will consequently result in BLOCK_JOB_COMPLETED being emitted.
+-
+-test_blockjob \
+-    "{'execute': 'drive-mirror',
+-      'arguments': {'job-id': 'job0',
+-                    'device': 'drv0',
+-                    'target': '$TEST_DIR/o.$IMGFMT',
+-                    'format': '$IMGFMT',
+-                    'sync': 'none'}}" \
+-    'BLOCK_JOB_READY' \
+-    '"status": "null"'
+-
+-echo
+-echo '=== Testing active block-commit ==='
+-echo
+-
+-# An active block-commit will send BLOCK_JOB_READY basically immediately, and
+-# cancelling the job will consequently result in BLOCK_JOB_COMPLETED being
+-# emitted.
+-
+-test_blockjob \
+-    "{'execute': 'block-commit',
+-      'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \
+-    'BLOCK_JOB_READY' \
+-    '"status": "null"'
+-
+-echo
+-echo '=== Testing non-active block-commit ==='
+-echo
+-
+-# Give block-commit something to work on, otherwise it would be done
+-# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just
+-# fine without the block job still running.
+-
+-$QEMU_IO -c 'write 0 1M' "$TEST_DIR/m.$IMGFMT" | _filter_qemu_io
+-
+-test_blockjob \
+-    "{'execute': 'block-commit',
+-      'arguments': {'job-id': 'job0',
+-                    'device': 'drv0',
+-                    'top':    '$TEST_DIR/m.$IMGFMT',
+-                    'speed':  1}}" \
+-    'return' \
+-    '"status": "null"'
+-
+-echo
+-echo '=== Testing block-stream ==='
+-echo
+-
+-# Give block-stream something to work on, otherwise it would be done
+-# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just
+-# fine without the block job still running.
+-
+-$QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io
+-
+-# With some data to stream (and @speed set to 1), block-stream will not complete
+-# until we send the block-job-cancel command.
+-
+-test_blockjob \
+-    "{'execute': 'block-stream',
+-      'arguments': {'job-id': 'job0',
+-                    'device': 'drv0',
+-                    'speed': 1}}" \
+-    'return' \
+-    '"status": "null"'
+-
+-_cleanup_qemu
+-
+-# success, all done
+-echo "*** done"
+-rm -f $seq.full
+-status=0
++import iotests
++
++# Common filters to mask values that vary in the test output
++QMP_FILTERS = [iotests.filter_qmp_testfiles, \
++               iotests.filter_qmp_imgfmt]
++
++
++class TestCase:
++    def __init__(self, name, vm, image_path, cancel_event):
++        self.name = name
++        self.vm = vm
++        self.image_path = image_path
++        self.cancel_event = cancel_event
++
++    def __enter__(self):
++        iotests.log(f'=== Testing {self.name} ===')
++        self.vm.qmp_log('blockdev-add', \
++                        node_name='drv0', \
++                        driver=iotests.imgfmt, \
++                        file={'driver': 'file', 'filename': self.image_path}, \
++                        filters=QMP_FILTERS)
++
++    def __exit__(self, *exc_details):
++        # This is expected to fail because the job still exists
++        self.vm.qmp_log('blockdev-del', node_name='drv0', \
++                        filters=[iotests.filter_qmp_generated_node_ids])
++
++        self.vm.qmp_log('block-job-cancel', device='job0')
++        event = self.vm.event_wait(self.cancel_event)
++        iotests.log(event, filters=[iotests.filter_qmp_event])
++
++        # This time it succeeds
++        self.vm.qmp_log('blockdev-del', node_name='drv0')
++
++        # Separate test cases in output
++        iotests.log('')
++
++
++def main() -> None:
++    with iotests.FilePath('bottom', 'middle', 'top', 'target') as \
++            (bottom_path, middle_path, top_path, target_path), \
++         iotests.VM() as vm:
++
++        iotests.log('Creating bottom <- middle <- top backing file chain...')
++        IMAGE_SIZE='1M'
++        iotests.qemu_img_create('-f', iotests.imgfmt, bottom_path, IMAGE_SIZE)
++        iotests.qemu_img_create('-f', iotests.imgfmt, \
++                                '-F', iotests.imgfmt, \
++                                '-b', bottom_path, \
++                                middle_path, \
++                                IMAGE_SIZE)
++        iotests.qemu_img_create('-f', iotests.imgfmt, \
++                                '-F', iotests.imgfmt, \
++                                '-b', middle_path, \
++                                top_path, \
++                                IMAGE_SIZE)
++
++        iotests.log('Starting VM...')
++        vm.add_args('-nodefaults')
++        vm.launch()
++
++        # drive-backup will not send BLOCK_JOB_READY by itself, and cancelling
++        # the job will consequently result in BLOCK_JOB_CANCELLED being
++        # emitted.
++        with TestCase('drive-backup', vm, top_path, 'BLOCK_JOB_CANCELLED'):
++            vm.qmp_log('drive-backup', \
++                       job_id='job0', \
++                       device='drv0', \
++                       target=target_path, \
++                       format=iotests.imgfmt, \
++                       sync='none', \
++                       filters=QMP_FILTERS)
++
++        # drive-mirror will send BLOCK_JOB_READY basically immediately, and
++        # cancelling the job will consequently result in BLOCK_JOB_COMPLETED
++        # being emitted.
++        with TestCase('drive-mirror', vm, top_path, 'BLOCK_JOB_COMPLETED'):
++            vm.qmp_log('drive-mirror', \
++                       job_id='job0', \
++                       device='drv0', \
++                       target=target_path, \
++                       format=iotests.imgfmt, \
++                       sync='none', \
++                       filters=QMP_FILTERS)
++            event = vm.event_wait('BLOCK_JOB_READY')
++            assert event is not None # silence mypy
++            iotests.log(event, filters=[iotests.filter_qmp_event])
++
++        # An active block-commit will send BLOCK_JOB_READY basically
++        # immediately, and cancelling the job will consequently result in
++        # BLOCK_JOB_COMPLETED being emitted.
++        with TestCase('active block-commit', vm, top_path, \
++                      'BLOCK_JOB_COMPLETED'):
++            vm.qmp_log('block-commit', \
++                       job_id='job0', \
++                       device='drv0')
++            event = vm.event_wait('BLOCK_JOB_READY')
++            assert event is not None # silence mypy
++            iotests.log(event, filters=[iotests.filter_qmp_event])
++
++        # Give block-commit something to work on, otherwise it would be done
++        # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would
++        # work just fine without the block job still running.
++        iotests.qemu_io(middle_path, '-c', f'write 0 {IMAGE_SIZE}')
++        with TestCase('non-active block-commit', vm, top_path, \
++                      'BLOCK_JOB_CANCELLED'):
++            vm.qmp_log('block-commit', \
++                       job_id='job0', \
++                       device='drv0', \
++                       top=middle_path, \
++                       speed=1, \
++                       filters=[iotests.filter_qmp_testfiles])
++
++        # Give block-stream something to work on, otherwise it would be done
++        # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would
++        # work just fine without the block job still running.
++        iotests.qemu_io(bottom_path, '-c', f'write 0 {IMAGE_SIZE}')
++        with TestCase('block-stream', vm, top_path, 'BLOCK_JOB_CANCELLED'):
++            vm.qmp_log('block-stream', \
++                       job_id='job0', \
++                       device='drv0', \
++                       speed=1)
++
++if __name__ == '__main__':
++    iotests.script_main(main, supported_fmts=['qcow2', 'qed'],
++                        supported_protocols=['file'])
+diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
+index 63203d9944..91b7ba50af 100644
+--- a/tests/qemu-iotests/141.out
++++ b/tests/qemu-iotests/141.out
+@@ -1,179 +1,69 @@
+-QA output created by 141
+-Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=1048576
+-Formatting 'TEST_DIR/m.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/b.IMGFMT backing_fmt=IMGFMT
+-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.IMGFMT backing_fmt=IMGFMT
+-{'execute': 'qmp_capabilities'}
+-{"return": {}}
+-
++Creating bottom <- middle <- top backing file chain...
++Starting VM...
+ === Testing drive-backup ===
+-
+-{'execute': 'blockdev-add',
+-          'arguments': {
+-              'node-name': 'drv0',
+-              'driver': 'IMGFMT',
+-              'file': {
+-                  'driver': 'file',
+-                  'filename': 'TEST_DIR/t.IMGFMT'
+-              }}}
+-{"return": {}}
+-{'execute': 'drive-backup',
+-'arguments': {'job-id': 'job0',
+-'device': 'drv0',
+-'target': 'TEST_DIR/o.IMGFMT',
+-'format': 'IMGFMT',
+-'sync': 'none'}}
+-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"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "drive-backup", "arguments": {"device": "drv0", "format": "IMGFMT", "job-id": "job0", "sync": "none", "target": "TEST_DIR/PID-target"}}
++{"return": {}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
+-{'execute': 'block-job-cancel',
+-          'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"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"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+ 
+ === Testing drive-mirror ===
+-
+-{'execute': 'blockdev-add',
+-          'arguments': {
+-              'node-name': 'drv0',
+-              'driver': 'IMGFMT',
+-              'file': {
+-                  'driver': 'file',
+-                  'filename': 'TEST_DIR/t.IMGFMT'
+-              }}}
+-{"return": {}}
+-{'execute': 'drive-mirror',
+-'arguments': {'job-id': 'job0',
+-'device': 'drv0',
+-'target': 'TEST_DIR/o.IMGFMT',
+-'format': 'IMGFMT',
+-'sync': 'none'}}
+-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"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "drive-mirror", "arguments": {"device": "drv0", "format": "IMGFMT", "job-id": "job0", "sync": "none", "target": "TEST_DIR/PID-target"}}
++{"return": {}}
++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: mirror"}}
+-{'execute': 'block-job-cancel',
+-          'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+ 
+ === Testing active block-commit ===
+-
+-{'execute': 'blockdev-add',
+-          'arguments': {
+-              'node-name': 'drv0',
+-              'driver': 'IMGFMT',
+-              'file': {
+-                  'driver': 'file',
+-                  'filename': 'TEST_DIR/t.IMGFMT'
+-              }}}
+-{"return": {}}
+-{'execute': 'block-commit',
+-'arguments': {'job-id': 'job0', 'device': 'drv0'}}
+-{"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"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "block-commit", "arguments": {"device": "drv0", "job-id": "job0"}}
++{"return": {}}
++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}}
+-{'execute': 'block-job-cancel',
+-          'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+ 
+ === Testing non-active block-commit ===
+-
+-wrote 1048576/1048576 bytes at offset 0
+-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+-{'execute': 'blockdev-add',
+-          'arguments': {
+-              'node-name': 'drv0',
+-              'driver': 'IMGFMT',
+-              'file': {
+-                  'driver': 'file',
+-                  'filename': 'TEST_DIR/t.IMGFMT'
+-              }}}
+-{"return": {}}
+-{'execute': 'block-commit',
+-'arguments': {'job-id': 'job0',
+-'device': 'drv0',
+-'top':    'TEST_DIR/m.IMGFMT',
+-'speed':  1}}
+-{"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"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "block-commit", "arguments": {"device": "drv0", "job-id": "job0", "speed": 1, "top": "TEST_DIR/PID-middle"}}
++{"return": {}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}}
+-{'execute': 'block-job-cancel',
+-          'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"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": 524288, "speed": 1, "type": "commit"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+ 
+ === Testing block-stream ===
+-
+-wrote 1048576/1048576 bytes at offset 0
+-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+-{'execute': 'blockdev-add',
+-          'arguments': {
+-              'node-name': 'drv0',
+-              'driver': 'IMGFMT',
+-              'file': {
+-                  'driver': 'file',
+-                  'filename': 'TEST_DIR/t.IMGFMT'
+-              }}}
+-{"return": {}}
+-{'execute': 'block-stream',
+-'arguments': {'job-id': 'job0',
+-'device': 'drv0',
+-'speed': 1}}
+-{"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"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}}
++{"return": {}}
++{"execute": "block-stream", "arguments": {"device": "drv0", "job-id": "job0", "speed": 1}}
++{"return": {}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: stream"}}
+-{'execute': 'block-job-cancel',
+-          'arguments': {'device': 'job0'}}
++{"execute": "block-job-cancel", "arguments": {"device": "job0"}}
+ {"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": 524288, "speed": 1, "type": "stream"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{'execute': 'blockdev-del',
+-          'arguments': {'node-name': 'drv0'}}
++{"data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}}
+ {"return": {}}
+-*** done
++
+diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out
+index 9ec5888e0e..d6afa32abc 100644
+--- a/tests/qemu-iotests/143.out
++++ b/tests/qemu-iotests/143.out
+@@ -10,6 +10,6 @@ server reported: export 'no_such_export' not present
+ qemu-io: can't open device nbd+unix:///aa--aa1?socket=SOCK_DIR/nbd: Requested export not available
+ server reported: export 'aa--aa...' not present
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out
+index 4a22f0c41a..07e5e83f5d 100644
+--- a/tests/qemu-iotests/156.out
++++ b/tests/qemu-iotests/156.out
+@@ -72,8 +72,8 @@ read 65536/65536 bytes at offset 196608
+ {"return": ""}
+ 
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ read 65536/65536 bytes at offset 0
+ 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+diff --git a/tests/qemu-iotests/176.out b/tests/qemu-iotests/176.out
+index 9d09b60452..45e9153ef3 100644
+--- a/tests/qemu-iotests/176.out
++++ b/tests/qemu-iotests/176.out
+@@ -169,8 +169,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ wrote 196608/196608 bytes at offset 2147287040
+ 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ wrote 131072/131072 bytes at offset 2147352576
+@@ -206,8 +206,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {"sha256": HASH}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ === Test pass bitmap.1 ===
+ 
+@@ -218,8 +218,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ wrote 196608/196608 bytes at offset 2147287040
+ 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ wrote 131072/131072 bytes at offset 2147352576
+@@ -256,8 +256,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {"sha256": HASH}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ === Test pass bitmap.2 ===
+ 
+@@ -268,8 +268,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ wrote 196608/196608 bytes at offset 2147287040
+ 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ wrote 131072/131072 bytes at offset 2147352576
+@@ -306,8 +306,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {"sha256": HASH}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ === Test pass bitmap.3 ===
+ 
+@@ -318,8 +318,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ wrote 196608/196608 bytes at offset 2147287040
+ 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ wrote 131072/131072 bytes at offset 2147352576
+@@ -353,6 +353,6 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {"sha256": HASH}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out
+index 57f7265458..83fc1a4797 100644
+--- a/tests/qemu-iotests/182.out
++++ b/tests/qemu-iotests/182.out
+@@ -53,6 +53,6 @@ Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 cluster_size=65536 extended_l2=
+ {'execute': 'qmp_capabilities'}
+ {"return": {}}
+ {'execute': 'quit'}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/183.out b/tests/qemu-iotests/183.out
+index fd9c2e52a5..51aa41c888 100644
+--- a/tests/qemu-iotests/183.out
++++ b/tests/qemu-iotests/183.out
+@@ -53,11 +53,11 @@ wrote 65536/65536 bytes at offset 1048576
+ === Shut down and check image ===
+ 
+ {"execute":"quit"}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"return": {}}
+ {"execute":"quit"}
+-{"return": {}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ No errors were found on the image.
+ No errors were found on the image.
+ wrote 65536/65536 bytes at offset 1048576
+diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out
+index 77e5489d65..e8f631f853 100644
+--- a/tests/qemu-iotests/184.out
++++ b/tests/qemu-iotests/184.out
+@@ -89,10 +89,6 @@ Testing:
+     "return": [
+     ]
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -104,6 +100,10 @@ Testing:
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ 
+ == property changes in ThrottleGroup ==
+@@ -169,10 +169,6 @@ Testing:
+         "iops-total-max": 0
+     }
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -184,6 +180,10 @@ Testing:
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ 
+ == object creation/set errors  ==
+@@ -211,10 +211,6 @@ Testing:
+         "desc": "bps/iops/max total values and read/write values cannot be used at the same time"
+     }
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -226,6 +222,10 @@ Testing:
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ 
+ == don't specify group ==
+@@ -247,10 +247,6 @@ Testing:
+         "desc": "Parameter 'throttle-group' is missing"
+     }
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -262,6 +258,10 @@ Testing:
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ 
+ *** done
+diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
+index 8b1143dc16..61f13d0460 100755
+--- a/tests/qemu-iotests/185
++++ b/tests/qemu-iotests/185
+@@ -344,14 +344,14 @@ wait_for_job_and_quit() {
+ 
+     sleep 1
+ 
++    # List of expected events
++    capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN'
++
+     _send_qemu_cmd $h \
+         '{"execute": "quit"}' \
+         'return'
+ 
+-    # List of expected events
+-    capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN'
+     _wait_event $h 'SHUTDOWN'
+-    QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before SHUTDOWN
+     _wait_event $h 'JOB_STATUS_CHANGE' # standby
+     _wait_event $h 'JOB_STATUS_CHANGE' # ready
+     _wait_event $h 'JOB_STATUS_CHANGE' # aborting
+diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
+index 70e8dd6c87..1cccccb1b6 100644
+--- a/tests/qemu-iotests/185.out
++++ b/tests/qemu-iotests/185.out
+@@ -40,9 +40,16 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+ 
+ === Start active commit job and exit qemu ===
+ 
+@@ -56,9 +63,16 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+ 
+ === Start mirror job and exit qemu ===
+ 
+@@ -75,9 +89,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+ 
+ === Start backup job and exit qemu ===
+ 
+@@ -97,9 +118,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+ 
+ === Start streaming job and exit qemu ===
+ 
+@@ -112,9 +140,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
+ {"return": {}}
+ { 'execute': 'quit' }
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}}
++{"return": {}}
+ No errors were found on the image.
+ 
+ === Start mirror to throttled QSD and exit qemu ===
+diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out
+index ea88777374..c3309e4bc6 100644
+--- a/tests/qemu-iotests/191.out
++++ b/tests/qemu-iotests/191.out
+@@ -378,10 +378,6 @@ wrote 65536/65536 bytes at offset 1048576
+     ]
+ }
+ { 'execute': 'quit' }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -393,6 +389,10 @@ wrote 65536/65536 bytes at offset 1048576
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ image: TEST_DIR/t.IMGFMT
+ file format: IMGFMT
+ virtual size: 64 MiB (67108864 bytes)
+@@ -796,10 +796,6 @@ wrote 65536/65536 bytes at offset 1048576
+     ]
+ }
+ { 'execute': 'quit' }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -811,6 +807,10 @@ wrote 65536/65536 bytes at offset 1048576
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ image: TEST_DIR/t.IMGFMT
+ file format: IMGFMT
+ virtual size: 64 MiB (67108864 bytes)
+diff --git a/tests/qemu-iotests/195.out b/tests/qemu-iotests/195.out
+index ec84df5012..91717d302e 100644
+--- a/tests/qemu-iotests/195.out
++++ b/tests/qemu-iotests/195.out
+@@ -17,10 +17,6 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid
+     "return": {
+     }
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -32,6 +28,10 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ image: TEST_DIR/t.IMGFMT.mid
+ file format: IMGFMT
+@@ -55,10 +55,6 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top
+     "return": {
+     }
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -70,6 +66,10 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ image: TEST_DIR/t.IMGFMT
+ file format: IMGFMT
+diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out
+index 26fb347c5d..65625c491e 100644
+--- a/tests/qemu-iotests/223.out
++++ b/tests/qemu-iotests/223.out
+@@ -11,8 +11,8 @@ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ 
+ === Write part of the file under active bitmap ===
+@@ -142,14 +142,14 @@ read 2097152/2097152 bytes at offset 2097152
+ 
+ {"execute":"nbd-server-remove",
+   "arguments":{"name":"n"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
+ {"return": {}}
+ {"execute":"nbd-server-remove",
+   "arguments":{"name":"n2"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
+ {"return": {}}
+ {"execute":"nbd-server-remove",
+   "arguments":{"name":"n2"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
+ {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
+ {"execute":"nbd-server-stop"}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}}
+@@ -261,14 +261,14 @@ read 2097152/2097152 bytes at offset 2097152
+ 
+ {"execute":"nbd-server-remove",
+   "arguments":{"name":"n"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
+ {"return": {}}
+ {"execute":"nbd-server-remove",
+   "arguments":{"name":"n2"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
++{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
+ {"return": {}}
+ {"execute":"nbd-server-remove",
+   "arguments":{"name":"n2"}}
+-{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
+ {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
+ {"execute":"nbd-server-stop"}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}}
+@@ -276,8 +276,8 @@ read 2097152/2097152 bytes at offset 2097152
+ {"execute":"nbd-server-stop"}
+ {"error": {"class": "GenericError", "desc": "NBD server not running"}}
+ {"execute":"quit"}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ 
+ === Use qemu-nbd as server ===
+ 
+diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out
+index 378c1b8fb1..b6a56118b7 100644
+--- a/tests/qemu-iotests/227.out
++++ b/tests/qemu-iotests/227.out
+@@ -48,10 +48,6 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio
+         }
+     ]
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -63,6 +59,10 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ 
+ === blockstats with -drive if=none ===
+@@ -112,10 +112,6 @@ Testing: -drive driver=null-co,if=none
+         }
+     ]
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -127,6 +123,10 @@ Testing: -drive driver=null-co,if=none
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ 
+ === blockstats with -blockdev ===
+@@ -143,10 +143,6 @@ Testing: -blockdev driver=null-co,node-name=null
+     "return": [
+     ]
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -158,6 +154,10 @@ Testing: -blockdev driver=null-co,node-name=null
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ 
+ === blockstats with -blockdev and -device ===
+@@ -208,10 +208,6 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b
+         }
+     ]
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -223,5 +219,9 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ *** done
+diff --git a/tests/qemu-iotests/247.out b/tests/qemu-iotests/247.out
+index e909e83994..7d252e7fe4 100644
+--- a/tests/qemu-iotests/247.out
++++ b/tests/qemu-iotests/247.out
+@@ -17,6 +17,6 @@ QMP_VERSION
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qemu-iotests/273.out b/tests/qemu-iotests/273.out
+index 6a74a8138b..71843f02de 100644
+--- a/tests/qemu-iotests/273.out
++++ b/tests/qemu-iotests/273.out
+@@ -282,10 +282,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
+         ]
+     }
+ }
+-{
+-    "return": {
+-    }
+-}
+ {
+     "timestamp": {
+         "seconds":  TIMESTAMP,
+@@ -297,5 +293,9 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
+         "reason": "host-qmp-quit"
+     }
+ }
++{
++    "return": {
++    }
++}
+ 
+ *** done
+diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308
+index bde4aac2fa..d1bb49f1de 100755
+--- a/tests/qemu-iotests/308
++++ b/tests/qemu-iotests/308
+@@ -77,6 +77,7 @@ fuse_export_add()
+ # $1: Export ID
+ fuse_export_del()
+ {
++    capture_events="BLOCK_EXPORT_DELETED" \
+     _send_qemu_cmd $QEMU_HANDLE \
+         "{'execute': 'block-export-del',
+           'arguments': {
+@@ -84,8 +85,7 @@ fuse_export_del()
+           } }" \
+         'return'
+ 
+-    _send_qemu_cmd $QEMU_HANDLE \
+-        '' \
++    _wait_event $QEMU_HANDLE \
+         'BLOCK_EXPORT_DELETED'
+ }
+ 
+diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out
+index e4467a10cf..9fcf8844d4 100644
+--- a/tests/qemu-iotests/308.out
++++ b/tests/qemu-iotests/308.out
+@@ -165,9 +165,9 @@ OK: Post-truncate image size is as expected
+ 
+ === Tear down ===
+ {'execute': 'quit'}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export-mp"}}
++{"return": {}}
+ 
+ === Compare copy with original ===
+ Images are identical.
+diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
+index da7d6637e1..6f7c3c0e44 100644
+--- a/tests/qemu-iotests/iotests.py
++++ b/tests/qemu-iotests/iotests.py
+@@ -642,6 +642,13 @@ def _filter(_key, value):
+ def filter_generated_node_ids(msg):
+     return re.sub("#block[0-9]+", "NODE_NAME", msg)
+ 
++def filter_qmp_generated_node_ids(qmsg):
++    def _filter(_key, value):
++        if is_str(value):
++            return filter_generated_node_ids(value)
++        return value
++    return filter_qmp(qmsg, _filter)
++
+ def filter_img_info(output, filename):
+     lines = []
+     for line in output.split('\n'):
+diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots b/tests/qemu-iotests/tests/qcow2-internal-snapshots
+new file mode 100755
+index 0000000000..36523aba06
+--- /dev/null
++++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots
+@@ -0,0 +1,170 @@
++#!/usr/bin/env bash
++# group: rw quick
++#
++# Test case for internal snapshots in qcow2
++#
++# Copyright (C) 2023 Red Hat, Inc.
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program.  If not, see <http://www.gnu.org/licenses/>.
++#
++
++# creator
++owner=kwolf@redhat.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
++
++# This tests qcow2-specific low-level functionality
++_supported_fmt qcow2
++_supported_proto generic
++# Internal snapshots are (currently) impossible with refcount_bits=1,
++# and generally impossible with external data files
++_unsupported_imgopts 'compat=0.10' 'refcount_bits=1[^0-9]' data_file
++
++IMG_SIZE=64M
++
++_qemu()
++{
++    $QEMU -no-shutdown -nographic -monitor stdio -serial none \
++          -blockdev file,filename="$TEST_IMG",node-name=disk0-file \
++          -blockdev "$IMGFMT",file=disk0-file,node-name=disk0 \
++          -object iothread,id=iothread0 \
++          -device virtio-scsi,iothread=iothread0 \
++          -device scsi-hd,drive=disk0,share-rw=on \
++          "$@" 2>&1 |\
++    _filter_qemu | _filter_hmp | _filter_qemu_io
++}
++
++_make_test_img $IMG_SIZE
++
++echo
++echo "=== Write some data, take a snapshot and overwrite part of it ==="
++echo
++
++{
++    echo 'qemu-io disk0 "write -P0x11 0 1M"'
++    # Give qemu some time to boot before saving the VM state
++    sleep 0.5
++    echo "savevm snap0"
++    echo 'qemu-io disk0 "write -P0x22 0 512k"'
++    echo "quit"
++} | _qemu
++
++echo
++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size
++_check_test_img
++
++echo
++echo "=== Verify that loading the snapshot reverts to the old content ==="
++echo
++
++{
++    # -loadvm reverted the write from the previous QEMU instance
++    echo 'qemu-io disk0 "read -P0x11 0 1M"'
++
++    # Verify that it works without restarting QEMU, too
++    echo 'qemu-io disk0 "write -P0x33 512k 512k"'
++    echo "loadvm snap0"
++    echo 'qemu-io disk0 "read -P0x11 0 1M"'
++
++    # Verify COW by writing a partial cluster
++    echo 'qemu-io disk0 "write -P0x33 63k 2k"'
++    echo 'qemu-io disk0 "read -P0x11 0 63k"'
++    echo 'qemu-io disk0 "read -P0x33 63k 2k"'
++    echo 'qemu-io disk0 "read -P0x11 65k 63k"'
++
++    # Take a second snapshot
++    echo "savevm snap1"
++
++    echo "quit"
++} | _qemu -loadvm snap0
++
++echo
++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size
++_check_test_img
++
++echo
++echo "=== qemu-img snapshot can revert to snapshots ==="
++echo
++
++$QEMU_IMG snapshot -a snap0 "$TEST_IMG"
++$QEMU_IO -c "read -P0x11 0 1M" "$TEST_IMG" | _filter_qemu_io
++$QEMU_IMG snapshot -a snap1 "$TEST_IMG"
++$QEMU_IO \
++    -c "read -P0x11 0 63k" \
++    -c "read -P0x33 63k 2k" \
++    -c "read -P0x11 65k 63k" \
++    "$TEST_IMG" | _filter_qemu_io
++
++echo
++echo "=== Deleting snapshots ==="
++echo
++{
++    # The active layer stays unaffected by deleting the snapshot
++    echo "delvm snap1"
++    echo 'qemu-io disk0 "read -P0x11 0 63k"'
++    echo 'qemu-io disk0 "read -P0x33 63k 2k"'
++    echo 'qemu-io disk0 "read -P0x11 65k 63k"'
++
++    echo "quit"
++} | _qemu
++
++
++echo
++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size
++_check_test_img
++
++echo
++echo "=== Error cases ==="
++echo
++
++# snap1 should not exist any more
++_qemu -loadvm snap1
++
++echo
++{
++    echo "loadvm snap1"
++    echo "quit"
++} | _qemu
++
++# Snapshot operations and inactive images are incompatible
++echo
++_qemu -loadvm snap0 -incoming defer
++{
++    echo "loadvm snap0"
++    echo "delvm snap0"
++    echo "savevm snap1"
++    echo "quit"
++} | _qemu -incoming defer
++
++# -loadvm and -preconfig are incompatible
++echo
++_qemu -loadvm snap0 -preconfig
++
++# success, all done
++echo "*** done"
++rm -f $seq.full
++status=0
+diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots.out b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out
+new file mode 100644
+index 0000000000..438f535e6a
+--- /dev/null
++++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out
+@@ -0,0 +1,107 @@
++QA output created by qcow2-internal-snapshots
++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
++
++=== Write some data, take a snapshot and overwrite part of it ===
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) qemu-io disk0 "write -P0x11 0 1M"
++wrote 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) savevm snap0
++(qemu) qemu-io disk0 "write -P0x22 0 512k"
++wrote 524288/524288 bytes at offset 0
++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) quit
++
++Snapshot list:
++ID        TAG               VM SIZE                DATE     VM CLOCK     ICOUNT
++1         snap0                SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000
++No errors were found on the image.
++
++=== Verify that loading the snapshot reverts to the old content ===
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) qemu-io disk0 "read -P0x11 0 1M"
++read 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "write -P0x33 512k 512k"
++wrote 524288/524288 bytes at offset 524288
++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) loadvm snap0
++(qemu) qemu-io disk0 "read -P0x11 0 1M"
++read 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "write -P0x33 63k 2k"
++wrote 2048/2048 bytes at offset 64512
++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x11 0 63k"
++read 64512/64512 bytes at offset 0
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x33 63k 2k"
++read 2048/2048 bytes at offset 64512
++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x11 65k 63k"
++read 64512/64512 bytes at offset 66560
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) savevm snap1
++(qemu) quit
++
++Snapshot list:
++ID        TAG               VM SIZE                DATE     VM CLOCK     ICOUNT
++1         snap0                SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000
++2         snap1                SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000
++No errors were found on the image.
++
++=== qemu-img snapshot can revert to snapshots ===
++
++read 1048576/1048576 bytes at offset 0
++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++read 64512/64512 bytes at offset 0
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++read 2048/2048 bytes at offset 64512
++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++read 64512/64512 bytes at offset 66560
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++
++=== Deleting snapshots ===
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) delvm snap1
++(qemu) qemu-io disk0 "read -P0x11 0 63k"
++read 64512/64512 bytes at offset 0
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x33 63k 2k"
++read 2048/2048 bytes at offset 64512
++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) qemu-io disk0 "read -P0x11 65k 63k"
++read 64512/64512 bytes at offset 66560
++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
++(qemu) quit
++
++Snapshot list:
++ID        TAG               VM SIZE                DATE     VM CLOCK     ICOUNT
++1         snap0                SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000
++No errors were found on the image.
++
++=== Error cases ===
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) QEMU_PROG: Snapshot 'snap1' does not exist in one or more devices
++
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) loadvm snap1
++Error: Snapshot 'snap1' does not exist in one or more devices
++(qemu) quit
++
++QEMU_PROG: 'incoming' and 'loadvm' options are mutually exclusive
++QEMU X.Y.Z monitor - type 'help' for more information
++(qemu) loadvm snap0
++Error: Device 'disk0' is writable but does not support snapshots
++(qemu) delvm snap0
++Error: Device 'disk0' is writable but does not support snapshots
++(qemu) savevm snap1
++Error: Device 'disk0' is writable but does not support snapshots
++(qemu) quit
++
++QEMU_PROG: 'preconfig' and 'loadvm' options are mutually exclusive
++*** done
+diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out
+index c1bc9b8356..aa6b6d1aef 100644
+--- a/tests/qemu-iotests/tests/qsd-jobs.out
++++ b/tests/qemu-iotests/tests/qsd-jobs.out
+@@ -7,8 +7,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
+ QMP_VERSION
+ {"return": {}}
+ {"return": {}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
++{"return": {}}
+ 
+ === Streaming can't get permission on base node ===
+ 
+@@ -17,6 +17,6 @@ QMP_VERSION
+ {"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": "null", "id": "job0"}}
+ {"error": {"class": "GenericError", "desc": "Permission conflict on node 'fmt_base': permissions 'write' are both required by an unnamed block device (uses node 'fmt_base' as 'root' child) and unshared by stream job 'job0' (uses node 'fmt_base' as 'intermediate node' child)."}}
+-{"return": {}}
+ {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}}
++{"return": {}}
+ *** done
+diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
+index c07a5b1a5f..c00b92b89a 100644
+--- a/tests/qtest/meson.build
++++ b/tests/qtest/meson.build
+@@ -6,6 +6,7 @@ endif
+ 
+ slow_qtests = {
+   'ahci-test' : 60,
++  'aspeed_smc-test': 360,
+   'bios-tables-test' : 120,
+   'boot-serial-test' : 60,
+   'migration-test' : 150,
diff -Nru qemu-7.2+dfsg/debian/rules qemu-7.2+dfsg/debian/rules
--- qemu-7.2+dfsg/debian/rules	2023-12-03 14:06:01.000000000 +0300
+++ qemu-7.2+dfsg/debian/rules	2024-01-30 19:01:44.000000000 +0300
@@ -407,7 +407,7 @@
 override_dh_installinit:
 	dh_installinit -pqemu-guest-agent
 override_dh_installsystemd:
-	dh_installsystemd -pqemu-guest-agent --no-start --no-enable
+	dh_installsystemd -pqemu-guest-agent --no-enable
 # default-enable /run/qemu mount only on ubuntu,
 # on debian let it be manually controlled and off by default
 	dh_installsystemd -pqemu-block-extra --no-restart-on-upgrade --name=run-qemu.mount \

--- End Message ---
--- Begin Message ---
Version: 12.5

The upload requested in this bug has been released as part of 12.5.

--- End Message ---

Reply to: