summaryrefslogtreecommitdiffstats
path: root/debian/patches/v7.2.5.diff
diff options
context:
space:
mode:
authorDaniel Baumann <mail@daniel-baumann.ch>2025-06-06 10:05:27 +0000
committerDaniel Baumann <mail@daniel-baumann.ch>2025-06-06 10:05:27 +0000
commit43904a02caeb311a505bbb5ffa431ea9859db5f4 (patch)
treecd841d75f639d9092243b0d02a3bb93cbdea5804 /debian/patches/v7.2.5.diff
parentAdding upstream version 1:7.2+dfsg. (diff)
downloadqemu-debian.tar.xz
qemu-debian.zip
Adding debian version 1:7.2+dfsg-7+deb12u13.debian/1%7.2+dfsg-7+deb12u13debian
Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
Diffstat (limited to '')
-rw-r--r--debian/patches/v7.2.5.diff1575
1 files changed, 1575 insertions, 0 deletions
diff --git a/debian/patches/v7.2.5.diff b/debian/patches/v7.2.5.diff
new file mode 100644
index 00000000..56749cbe
--- /dev/null
+++ b/debian/patches/v7.2.5.diff
@@ -0,0 +1,1575 @@
+diff --git a/VERSION b/VERSION
+index 2bbaead448..8aea167e72 100644
+--- a/VERSION
++++ b/VERSION
+@@ -1 +1 @@
+-7.2.4
++7.2.5
+diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
+index e09b9c13b7..bbca3a8db3 100644
+--- a/hw/arm/smmu-common.c
++++ b/hw/arm/smmu-common.c
+@@ -193,8 +193,7 @@ static int get_pte(dma_addr_t baseaddr, uint32_t index, uint64_t *pte,
+ dma_addr_t addr = baseaddr + index * sizeof(*pte);
+
+ /* TODO: guarantee 64-bit single-copy atomicity */
+- ret = dma_memory_read(&address_space_memory, addr, pte, sizeof(*pte),
+- MEMTXATTRS_UNSPECIFIED);
++ ret = ldq_le_dma(&address_space_memory, addr, pte, MEMTXATTRS_UNSPECIFIED);
+
+ if (ret != MEMTX_OK) {
+ info->type = SMMU_PTW_ERR_WALK_EABT;
+diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
+index daa80e9c7b..ce7091ff8e 100644
+--- a/hw/arm/smmuv3.c
++++ b/hw/arm/smmuv3.c
+@@ -98,20 +98,34 @@ static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn)
+ trace_smmuv3_write_gerrorn(toggled & pending, s->gerrorn);
+ }
+
+-static inline MemTxResult queue_read(SMMUQueue *q, void *data)
++static inline MemTxResult queue_read(SMMUQueue *q, Cmd *cmd)
+ {
+ dma_addr_t addr = Q_CONS_ENTRY(q);
++ MemTxResult ret;
++ int i;
+
+- return dma_memory_read(&address_space_memory, addr, data, q->entry_size,
+- MEMTXATTRS_UNSPECIFIED);
++ ret = dma_memory_read(&address_space_memory, addr, cmd, sizeof(Cmd),
++ MEMTXATTRS_UNSPECIFIED);
++ if (ret != MEMTX_OK) {
++ return ret;
++ }
++ for (i = 0; i < ARRAY_SIZE(cmd->word); i++) {
++ le32_to_cpus(&cmd->word[i]);
++ }
++ return ret;
+ }
+
+-static MemTxResult queue_write(SMMUQueue *q, void *data)
++static MemTxResult queue_write(SMMUQueue *q, Evt *evt_in)
+ {
+ dma_addr_t addr = Q_PROD_ENTRY(q);
+ MemTxResult ret;
++ Evt evt = *evt_in;
++ int i;
+
+- ret = dma_memory_write(&address_space_memory, addr, data, q->entry_size,
++ for (i = 0; i < ARRAY_SIZE(evt.word); i++) {
++ cpu_to_le32s(&evt.word[i]);
++ }
++ ret = dma_memory_write(&address_space_memory, addr, &evt, sizeof(Evt),
+ MEMTXATTRS_UNSPECIFIED);
+ if (ret != MEMTX_OK) {
+ return ret;
+@@ -290,7 +304,7 @@ static void smmuv3_init_regs(SMMUv3State *s)
+ static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf,
+ SMMUEventInfo *event)
+ {
+- int ret;
++ int ret, i;
+
+ trace_smmuv3_get_ste(addr);
+ /* TODO: guarantee 64-bit single-copy atomicity */
+@@ -303,6 +317,9 @@ static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf,
+ event->u.f_ste_fetch.addr = addr;
+ return -EINVAL;
+ }
++ for (i = 0; i < ARRAY_SIZE(buf->word); i++) {
++ le32_to_cpus(&buf->word[i]);
++ }
+ return 0;
+
+ }
+@@ -312,7 +329,7 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid,
+ CD *buf, SMMUEventInfo *event)
+ {
+ dma_addr_t addr = STE_CTXPTR(ste);
+- int ret;
++ int ret, i;
+
+ trace_smmuv3_get_cd(addr);
+ /* TODO: guarantee 64-bit single-copy atomicity */
+@@ -325,6 +342,9 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid,
+ event->u.f_ste_fetch.addr = addr;
+ return -EINVAL;
+ }
++ for (i = 0; i < ARRAY_SIZE(buf->word); i++) {
++ le32_to_cpus(&buf->word[i]);
++ }
+ return 0;
+ }
+
+@@ -406,7 +426,7 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste,
+ return -EINVAL;
+ }
+ if (s->features & SMMU_FEATURE_2LVL_STE) {
+- int l1_ste_offset, l2_ste_offset, max_l2_ste, span;
++ int l1_ste_offset, l2_ste_offset, max_l2_ste, span, i;
+ dma_addr_t l1ptr, l2ptr;
+ STEDesc l1std;
+
+@@ -430,6 +450,9 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste,
+ event->u.f_ste_fetch.addr = l1ptr;
+ return -EINVAL;
+ }
++ for (i = 0; i < ARRAY_SIZE(l1std.word); i++) {
++ le32_to_cpus(&l1std.word[i]);
++ }
+
+ span = L1STD_SPAN(&l1std);
+
+diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
+index 345b284d70..5e45a8b729 100644
+--- a/hw/block/xen-block.c
++++ b/hw/block/xen-block.c
+@@ -759,14 +759,15 @@ static XenBlockDrive *xen_block_drive_create(const char *id,
+ drive = g_new0(XenBlockDrive, 1);
+ drive->id = g_strdup(id);
+
+- file_layer = qdict_new();
+- driver_layer = qdict_new();
+-
+ rc = stat(filename, &st);
+ if (rc) {
+ error_setg_errno(errp, errno, "Could not stat file '%s'", filename);
+ goto done;
+ }
++
++ file_layer = qdict_new();
++ driver_layer = qdict_new();
++
+ if (S_ISBLK(st.st_mode)) {
+ qdict_put_str(file_layer, "driver", "host_device");
+ } else {
+@@ -774,7 +775,6 @@ static XenBlockDrive *xen_block_drive_create(const char *id,
+ }
+
+ qdict_put_str(file_layer, "filename", filename);
+- g_free(filename);
+
+ if (mode && *mode != 'w') {
+ qdict_put_bool(file_layer, "read-only", true);
+@@ -809,7 +809,6 @@ static XenBlockDrive *xen_block_drive_create(const char *id,
+ qdict_put_str(file_layer, "locking", "off");
+
+ qdict_put_str(driver_layer, "driver", driver);
+- g_free(driver);
+
+ qdict_put(driver_layer, "file", file_layer);
+
+@@ -820,6 +819,8 @@ static XenBlockDrive *xen_block_drive_create(const char *id,
+ qobject_unref(driver_layer);
+
+ done:
++ g_free(filename);
++ g_free(driver);
+ if (*errp) {
+ xen_block_drive_destroy(drive, NULL);
+ return NULL;
+diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
+index d025ef2873..6640b669e2 100644
+--- a/hw/i386/intel_iommu.c
++++ b/hw/i386/intel_iommu.c
+@@ -755,6 +755,8 @@ static int vtd_get_pdire_from_pdir_table(dma_addr_t pasid_dir_base,
+ return -VTD_FR_PASID_TABLE_INV;
+ }
+
++ pdire->val = le64_to_cpu(pdire->val);
++
+ return 0;
+ }
+
+@@ -779,6 +781,9 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s,
+ pe, entry_size, MEMTXATTRS_UNSPECIFIED)) {
+ return -VTD_FR_PASID_TABLE_INV;
+ }
++ for (size_t i = 0; i < ARRAY_SIZE(pe->val); i++) {
++ pe->val[i] = le64_to_cpu(pe->val[i]);
++ }
+
+ /* Do translation type check */
+ if (!vtd_pe_type_check(x86_iommu, pe)) {
+@@ -3318,14 +3323,15 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index,
+ return -VTD_FR_IR_ROOT_INVAL;
+ }
+
+- trace_vtd_ir_irte_get(index, le64_to_cpu(entry->data[1]),
+- le64_to_cpu(entry->data[0]));
++ entry->data[0] = le64_to_cpu(entry->data[0]);
++ entry->data[1] = le64_to_cpu(entry->data[1]);
++
++ trace_vtd_ir_irte_get(index, entry->data[1], entry->data[0]);
+
+ if (!entry->irte.present) {
+ error_report_once("%s: detected non-present IRTE "
+ "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")",
+- __func__, index, le64_to_cpu(entry->data[1]),
+- le64_to_cpu(entry->data[0]));
++ __func__, index, entry->data[1], entry->data[0]);
+ return -VTD_FR_IR_ENTRY_P;
+ }
+
+@@ -3333,14 +3339,13 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index,
+ entry->irte.__reserved_2) {
+ error_report_once("%s: detected non-zero reserved IRTE "
+ "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")",
+- __func__, index, le64_to_cpu(entry->data[1]),
+- le64_to_cpu(entry->data[0]));
++ __func__, index, entry->data[1], entry->data[0]);
+ return -VTD_FR_IR_IRTE_RSVD;
+ }
+
+ if (sid != X86_IOMMU_SID_INVALID) {
+ /* Validate IRTE SID */
+- source_id = le32_to_cpu(entry->irte.source_id);
++ source_id = entry->irte.source_id;
+ switch (entry->irte.sid_vtype) {
+ case VTD_SVT_NONE:
+ break;
+@@ -3394,7 +3399,7 @@ static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index,
+ irq->trigger_mode = irte.irte.trigger_mode;
+ irq->vector = irte.irte.vector;
+ irq->delivery_mode = irte.irte.delivery_mode;
+- irq->dest = le32_to_cpu(irte.irte.dest_id);
++ irq->dest = irte.irte.dest_id;
+ if (!iommu->intr_eime) {
+ #define VTD_IR_APIC_DEST_MASK (0xff00ULL)
+ #define VTD_IR_APIC_DEST_SHIFT (8)
+@@ -3449,7 +3454,7 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu,
+ goto out;
+ }
+
+- index = addr.addr.index_h << 15 | le16_to_cpu(addr.addr.index_l);
++ index = addr.addr.index_h << 15 | addr.addr.index_l;
+
+ #define VTD_IR_MSI_DATA_SUBHANDLE (0x0000ffff)
+ #define VTD_IR_MSI_DATA_RESERVED (0xffff0000)
+diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
+index f090e61e11..e4d43ce48c 100644
+--- a/hw/i386/intel_iommu_internal.h
++++ b/hw/i386/intel_iommu_internal.h
+@@ -321,12 +321,21 @@ typedef enum VTDFaultReason {
+
+ /* Interrupt Entry Cache Invalidation Descriptor: VT-d 6.5.2.7. */
+ struct VTDInvDescIEC {
++#if HOST_BIG_ENDIAN
++ uint64_t reserved_2:16;
++ uint64_t index:16; /* Start index to invalidate */
++ uint64_t index_mask:5; /* 2^N for continuous int invalidation */
++ uint64_t resved_1:22;
++ uint64_t granularity:1; /* If set, it's global IR invalidation */
++ uint64_t type:4; /* Should always be 0x4 */
++#else
+ uint32_t type:4; /* Should always be 0x4 */
+ uint32_t granularity:1; /* If set, it's global IR invalidation */
+ uint32_t resved_1:22;
+ uint32_t index_mask:5; /* 2^N for continuous int invalidation */
+ uint32_t index:16; /* Start index to invalidate */
+ uint32_t reserved_2:16;
++#endif
+ };
+ typedef struct VTDInvDescIEC VTDInvDescIEC;
+
+diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c
+index 01d11325a6..726e9e1d16 100644
+--- a/hw/i386/x86-iommu.c
++++ b/hw/i386/x86-iommu.c
+@@ -63,7 +63,7 @@ void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out)
+ msg.redir_hint = irq->redir_hint;
+ msg.dest = irq->dest;
+ msg.__addr_hi = irq->dest & 0xffffff00;
+- msg.__addr_head = cpu_to_le32(0xfee);
++ msg.__addr_head = 0xfee;
+ /* Keep this from original MSI address bits */
+ msg.__not_used = irq->msi_addr_last_bits;
+
+diff --git a/hw/ide/piix.c b/hw/ide/piix.c
+index 267dbf37db..066be77c8e 100644
+--- a/hw/ide/piix.c
++++ b/hw/ide/piix.c
+@@ -123,7 +123,7 @@ static void piix_ide_reset(DeviceState *dev)
+ pci_set_word(pci_conf + PCI_COMMAND, 0x0000);
+ pci_set_word(pci_conf + PCI_STATUS,
+ PCI_STATUS_DEVSEL_MEDIUM | PCI_STATUS_FAST_BACK);
+- pci_set_byte(pci_conf + 0x20, 0x01); /* BMIBA: 20-23h */
++ pci_set_long(pci_conf + 0x20, 0x1); /* BMIBA: 20-23h */
+ }
+
+ static int pci_piix_init_ports(PCIIDEState *d)
+diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
+index 4abd49e298..8cd7a400a0 100644
+--- a/hw/net/virtio-net.c
++++ b/hw/net/virtio-net.c
+@@ -3888,6 +3888,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data)
+ vdc->vmsd = &vmstate_virtio_net_device;
+ vdc->primary_unplug_pending = primary_unplug_pending;
+ vdc->get_vhost = virtio_net_get_vhost;
++ vdc->toggle_device_iotlb = vhost_toggle_device_iotlb;
+ }
+
+ static const TypeInfo virtio_net_info = {
+diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c
+index eaf217ff55..d2552cff1b 100644
+--- a/hw/pci/pci_host.c
++++ b/hw/pci/pci_host.c
+@@ -62,6 +62,17 @@ static void pci_adjust_config_limit(PCIBus *bus, uint32_t *limit)
+ }
+ }
+
++static bool is_pci_dev_ejected(PCIDevice *pci_dev)
++{
++ /*
++ * device unplug was requested and the guest acked it,
++ * so we stop responding config accesses even if the
++ * device is not deleted (failover flow)
++ */
++ return pci_dev && pci_dev->partially_hotplugged &&
++ !pci_dev->qdev.pending_deleted_event;
++}
++
+ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
+ uint32_t limit, uint32_t val, uint32_t len)
+ {
+@@ -75,7 +86,7 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
+ * allowing direct removal of unexposed functions.
+ */
+ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) ||
+- !pci_dev->has_power) {
++ !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) {
+ return;
+ }
+
+@@ -100,7 +111,7 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr,
+ * allowing direct removal of unexposed functions.
+ */
+ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) ||
+- !pci_dev->has_power) {
++ !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) {
+ return ~0x0;
+ }
+
+diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
+index 92a45de4c3..71509f9c7e 100644
+--- a/hw/vfio/pci.c
++++ b/hw/vfio/pci.c
+@@ -3158,6 +3158,9 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
+ return;
+
+ out_deregister:
++ if (vdev->interrupt == VFIO_INT_INTx) {
++ vfio_intx_disable(vdev);
++ }
+ pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
+ if (vdev->irqchip_change_notifier.notify) {
+ kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier);
+diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c
+index c175148fce..aa858ef3fb 100644
+--- a/hw/virtio/vhost-stub.c
++++ b/hw/virtio/vhost-stub.c
+@@ -15,3 +15,7 @@ bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp)
+ void vhost_user_cleanup(VhostUserState *user)
+ {
+ }
++
++void vhost_toggle_device_iotlb(VirtIODevice *vdev)
++{
++}
+diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
+index f38997b3f6..35274393e2 100644
+--- a/hw/virtio/vhost.c
++++ b/hw/virtio/vhost.c
+@@ -781,7 +781,6 @@ static void vhost_iommu_region_add(MemoryListener *listener,
+ Int128 end;
+ int iommu_idx;
+ IOMMUMemoryRegion *iommu_mr;
+- int ret;
+
+ if (!memory_region_is_iommu(section->mr)) {
+ return;
+@@ -796,7 +795,9 @@ static void vhost_iommu_region_add(MemoryListener *listener,
+ iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr,
+ MEMTXATTRS_UNSPECIFIED);
+ iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify,
+- IOMMU_NOTIFIER_DEVIOTLB_UNMAP,
++ dev->vdev->device_iotlb_enabled ?
++ IOMMU_NOTIFIER_DEVIOTLB_UNMAP :
++ IOMMU_NOTIFIER_UNMAP,
+ section->offset_within_region,
+ int128_get64(end),
+ iommu_idx);
+@@ -804,16 +805,8 @@ static void vhost_iommu_region_add(MemoryListener *listener,
+ iommu->iommu_offset = section->offset_within_address_space -
+ section->offset_within_region;
+ iommu->hdev = dev;
+- ret = memory_region_register_iommu_notifier(section->mr, &iommu->n, NULL);
+- if (ret) {
+- /*
+- * Some vIOMMUs do not support dev-iotlb yet. If so, try to use the
+- * UNMAP legacy message
+- */
+- iommu->n.notifier_flags = IOMMU_NOTIFIER_UNMAP;
+- memory_region_register_iommu_notifier(section->mr, &iommu->n,
+- &error_fatal);
+- }
++ memory_region_register_iommu_notifier(section->mr, &iommu->n,
++ &error_fatal);
+ QLIST_INSERT_HEAD(&dev->iommu_list, iommu, iommu_next);
+ /* TODO: can replay help performance here? */
+ }
+@@ -841,6 +834,27 @@ static void vhost_iommu_region_del(MemoryListener *listener,
+ }
+ }
+
++void vhost_toggle_device_iotlb(VirtIODevice *vdev)
++{
++ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
++ struct vhost_dev *dev;
++ struct vhost_iommu *iommu;
++
++ if (vdev->vhost_started) {
++ dev = vdc->get_vhost(vdev);
++ } else {
++ return;
++ }
++
++ QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) {
++ memory_region_unregister_iommu_notifier(iommu->mr, &iommu->n);
++ iommu->n.notifier_flags = vdev->device_iotlb_enabled ?
++ IOMMU_NOTIFIER_DEVIOTLB_UNMAP : IOMMU_NOTIFIER_UNMAP;
++ memory_region_register_iommu_notifier(iommu->mr, &iommu->n,
++ &error_fatal);
++ }
++}
++
+ static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
+ struct vhost_virtqueue *vq,
+ unsigned idx, bool enable_log)
+diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
+index a6dbdd32da..406b4e5fd0 100644
+--- a/hw/virtio/virtio-crypto.c
++++ b/hw/virtio/virtio-crypto.c
+@@ -635,6 +635,11 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev,
+ return NULL;
+ }
+
++ if (unlikely(src_len != dst_len)) {
++ virtio_error(vdev, "sym request src len is different from dst len");
++ return NULL;
++ }
++
+ max_len = (uint64_t)iv_len + aad_len + src_len + dst_len + hash_result_len;
+ if (unlikely(max_len > vcrypto->conf.max_size)) {
+ virtio_error(vdev, "virtio-crypto too big length");
+diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
+index 62e07ec2e4..eb82462c95 100644
+--- a/hw/virtio/virtio-iommu.c
++++ b/hw/virtio/virtio-iommu.c
+@@ -727,13 +727,15 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq)
+ VirtIOIOMMU *s = VIRTIO_IOMMU(vdev);
+ struct virtio_iommu_req_head head;
+ struct virtio_iommu_req_tail tail = {};
+- size_t output_size = sizeof(tail), sz;
+ VirtQueueElement *elem;
+ unsigned int iov_cnt;
+ struct iovec *iov;
+ void *buf = NULL;
++ size_t sz;
+
+ for (;;) {
++ size_t output_size = sizeof(tail);
++
+ elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+ if (!elem) {
+ return;
+diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
+index a1c9dfa7bb..67e771c373 100644
+--- a/hw/virtio/virtio-pci.c
++++ b/hw/virtio/virtio-pci.c
+@@ -631,6 +631,38 @@ virtio_address_space_read(VirtIOPCIProxy *proxy, hwaddr addr,
+ }
+ }
+
++static void virtio_pci_ats_ctrl_trigger(PCIDevice *pci_dev, bool enable)
++{
++ VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
++ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
++ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
++
++ vdev->device_iotlb_enabled = enable;
++
++ if (k->toggle_device_iotlb) {
++ k->toggle_device_iotlb(vdev);
++ }
++}
++
++static void pcie_ats_config_write(PCIDevice *dev, uint32_t address,
++ uint32_t val, int len)
++{
++ uint32_t off;
++ uint16_t ats_cap = dev->exp.ats_cap;
++
++ if (!ats_cap || address < ats_cap) {
++ return;
++ }
++ off = address - ats_cap;
++ if (off >= PCI_EXT_CAP_ATS_SIZEOF) {
++ return;
++ }
++
++ if (range_covers_byte(off, len, PCI_ATS_CTRL + 1)) {
++ virtio_pci_ats_ctrl_trigger(dev, !!(val & PCI_ATS_CTRL_ENABLE));
++ }
++}
++
+ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
+ uint32_t val, int len)
+ {
+@@ -644,6 +676,10 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
+ pcie_cap_flr_write_config(pci_dev, address, val, len);
+ }
+
++ if (proxy->flags & VIRTIO_PCI_FLAG_ATS) {
++ pcie_ats_config_write(pci_dev, address, val, len);
++ }
++
+ if (range_covers_byte(address, len, PCI_COMMAND)) {
+ if (!(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
+ virtio_set_disabled(vdev, true);
+diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
+index 46d973e629..7660dda768 100644
+--- a/include/hw/i386/intel_iommu.h
++++ b/include/hw/i386/intel_iommu.h
+@@ -142,37 +142,39 @@ enum {
+ union VTD_IR_TableEntry {
+ struct {
+ #if HOST_BIG_ENDIAN
+- uint32_t __reserved_1:8; /* Reserved 1 */
+- uint32_t vector:8; /* Interrupt Vector */
+- uint32_t irte_mode:1; /* IRTE Mode */
+- uint32_t __reserved_0:3; /* Reserved 0 */
+- uint32_t __avail:4; /* Available spaces for software */
+- uint32_t delivery_mode:3; /* Delivery Mode */
+- uint32_t trigger_mode:1; /* Trigger Mode */
+- uint32_t redir_hint:1; /* Redirection Hint */
+- uint32_t dest_mode:1; /* Destination Mode */
+- uint32_t fault_disable:1; /* Fault Processing Disable */
+- uint32_t present:1; /* Whether entry present/available */
++ uint64_t dest_id:32; /* Destination ID */
++ uint64_t __reserved_1:8; /* Reserved 1 */
++ uint64_t vector:8; /* Interrupt Vector */
++ uint64_t irte_mode:1; /* IRTE Mode */
++ uint64_t __reserved_0:3; /* Reserved 0 */
++ uint64_t __avail:4; /* Available spaces for software */
++ uint64_t delivery_mode:3; /* Delivery Mode */
++ uint64_t trigger_mode:1; /* Trigger Mode */
++ uint64_t redir_hint:1; /* Redirection Hint */
++ uint64_t dest_mode:1; /* Destination Mode */
++ uint64_t fault_disable:1; /* Fault Processing Disable */
++ uint64_t present:1; /* Whether entry present/available */
+ #else
+- uint32_t present:1; /* Whether entry present/available */
+- uint32_t fault_disable:1; /* Fault Processing Disable */
+- uint32_t dest_mode:1; /* Destination Mode */
+- uint32_t redir_hint:1; /* Redirection Hint */
+- uint32_t trigger_mode:1; /* Trigger Mode */
+- uint32_t delivery_mode:3; /* Delivery Mode */
+- uint32_t __avail:4; /* Available spaces for software */
+- uint32_t __reserved_0:3; /* Reserved 0 */
+- uint32_t irte_mode:1; /* IRTE Mode */
+- uint32_t vector:8; /* Interrupt Vector */
+- uint32_t __reserved_1:8; /* Reserved 1 */
++ uint64_t present:1; /* Whether entry present/available */
++ uint64_t fault_disable:1; /* Fault Processing Disable */
++ uint64_t dest_mode:1; /* Destination Mode */
++ uint64_t redir_hint:1; /* Redirection Hint */
++ uint64_t trigger_mode:1; /* Trigger Mode */
++ uint64_t delivery_mode:3; /* Delivery Mode */
++ uint64_t __avail:4; /* Available spaces for software */
++ uint64_t __reserved_0:3; /* Reserved 0 */
++ uint64_t irte_mode:1; /* IRTE Mode */
++ uint64_t vector:8; /* Interrupt Vector */
++ uint64_t __reserved_1:8; /* Reserved 1 */
++ uint64_t dest_id:32; /* Destination ID */
+ #endif
+- uint32_t dest_id; /* Destination ID */
+- uint16_t source_id; /* Source-ID */
+ #if HOST_BIG_ENDIAN
+ uint64_t __reserved_2:44; /* Reserved 2 */
+ uint64_t sid_vtype:2; /* Source-ID Validation Type */
+ uint64_t sid_q:2; /* Source-ID Qualifier */
++ uint64_t source_id:16; /* Source-ID */
+ #else
++ uint64_t source_id:16; /* Source-ID */
+ uint64_t sid_q:2; /* Source-ID Qualifier */
+ uint64_t sid_vtype:2; /* Source-ID Validation Type */
+ uint64_t __reserved_2:44; /* Reserved 2 */
+diff --git a/include/hw/i386/x86-iommu.h b/include/hw/i386/x86-iommu.h
+index 7637edb430..02dc2fe9ee 100644
+--- a/include/hw/i386/x86-iommu.h
++++ b/include/hw/i386/x86-iommu.h
+@@ -88,40 +88,42 @@ struct X86IOMMU_MSIMessage {
+ union {
+ struct {
+ #if HOST_BIG_ENDIAN
+- uint32_t __addr_head:12; /* 0xfee */
+- uint32_t dest:8;
+- uint32_t __reserved:8;
+- uint32_t redir_hint:1;
+- uint32_t dest_mode:1;
+- uint32_t __not_used:2;
++ uint64_t __addr_hi:32;
++ uint64_t __addr_head:12; /* 0xfee */
++ uint64_t dest:8;
++ uint64_t __reserved:8;
++ uint64_t redir_hint:1;
++ uint64_t dest_mode:1;
++ uint64_t __not_used:2;
+ #else
+- uint32_t __not_used:2;
+- uint32_t dest_mode:1;
+- uint32_t redir_hint:1;
+- uint32_t __reserved:8;
+- uint32_t dest:8;
+- uint32_t __addr_head:12; /* 0xfee */
++ uint64_t __not_used:2;
++ uint64_t dest_mode:1;
++ uint64_t redir_hint:1;
++ uint64_t __reserved:8;
++ uint64_t dest:8;
++ uint64_t __addr_head:12; /* 0xfee */
++ uint64_t __addr_hi:32;
+ #endif
+- uint32_t __addr_hi;
+ } QEMU_PACKED;
+ uint64_t msi_addr;
+ };
+ union {
+ struct {
+ #if HOST_BIG_ENDIAN
+- uint16_t trigger_mode:1;
+- uint16_t level:1;
+- uint16_t __resved:3;
+- uint16_t delivery_mode:3;
+- uint16_t vector:8;
++ uint32_t __resved1:16;
++ uint32_t trigger_mode:1;
++ uint32_t level:1;
++ uint32_t __resved:3;
++ uint32_t delivery_mode:3;
++ uint32_t vector:8;
+ #else
+- uint16_t vector:8;
+- uint16_t delivery_mode:3;
+- uint16_t __resved:3;
+- uint16_t level:1;
+- uint16_t trigger_mode:1;
++ uint32_t vector:8;
++ uint32_t delivery_mode:3;
++ uint32_t __resved:3;
++ uint32_t level:1;
++ uint32_t trigger_mode:1;
++ uint32_t __resved1:16;
+ #endif
+- uint16_t __resved1;
+ } QEMU_PACKED;
+ uint32_t msi_data;
+ };
+diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
+index 67a6807fac..c82dbb2c32 100644
+--- a/include/hw/virtio/vhost.h
++++ b/include/hw/virtio/vhost.h
+@@ -297,6 +297,7 @@ bool vhost_has_free_slot(void);
+ int vhost_net_set_backend(struct vhost_dev *hdev,
+ struct vhost_vring_file *file);
+
++void vhost_toggle_device_iotlb(VirtIODevice *vdev);
+ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write);
+
+ int vhost_virtqueue_start(struct vhost_dev *dev, struct VirtIODevice *vdev,
+diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
+index acfd4df125..96a56430a6 100644
+--- a/include/hw/virtio/virtio.h
++++ b/include/hw/virtio/virtio.h
+@@ -135,6 +135,7 @@ struct VirtIODevice
+ AddressSpace *dma_as;
+ QLIST_HEAD(, VirtQueue) *vector_queues;
+ QTAILQ_ENTRY(VirtIODevice) next;
++ bool device_iotlb_enabled;
+ };
+
+ struct VirtioDeviceClass {
+@@ -192,6 +193,7 @@ struct VirtioDeviceClass {
+ const VMStateDescription *vmsd;
+ bool (*primary_unplug_pending)(void *opaque);
+ struct vhost_dev *(*get_vhost)(VirtIODevice *vdev);
++ void (*toggle_device_iotlb)(VirtIODevice *vdev);
+ };
+
+ void virtio_instance_init_common(Object *proxy_obj, void *data,
+diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h
+index 5672479e9e..26c67f17e2 100644
+--- a/include/io/channel-tls.h
++++ b/include/io/channel-tls.h
+@@ -48,6 +48,7 @@ struct QIOChannelTLS {
+ QIOChannel *master;
+ QCryptoTLSSession *session;
+ QIOChannelShutdown shutdown;
++ guint hs_ioc_tag;
+ };
+
+ /**
+diff --git a/io/channel-tls.c b/io/channel-tls.c
+index 4ce08ccc28..a91efb57f3 100644
+--- a/io/channel-tls.c
++++ b/io/channel-tls.c
+@@ -198,12 +198,13 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
+ }
+
+ trace_qio_channel_tls_handshake_pending(ioc, status);
+- qio_channel_add_watch_full(ioc->master,
+- condition,
+- qio_channel_tls_handshake_io,
+- data,
+- NULL,
+- context);
++ ioc->hs_ioc_tag =
++ qio_channel_add_watch_full(ioc->master,
++ condition,
++ qio_channel_tls_handshake_io,
++ data,
++ NULL,
++ context);
+ }
+ }
+
+@@ -218,6 +219,7 @@ static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(
+ qio_task_get_source(task));
+
++ tioc->hs_ioc_tag = 0;
+ g_free(data);
+ qio_channel_tls_handshake_task(tioc, task, context);
+
+@@ -377,6 +379,10 @@ static int qio_channel_tls_close(QIOChannel *ioc,
+ {
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+
++ if (tioc->hs_ioc_tag) {
++ g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove);
++ }
++
+ return qio_channel_close(tioc->master, errp);
+ }
+
+diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c
+index c0790f3246..85804c367a 100644
+--- a/linux-user/arm/cpu_loop.c
++++ b/linux-user/arm/cpu_loop.c
+@@ -117,8 +117,9 @@ static void arm_kernel_cmpxchg32_helper(CPUARMState *env)
+ {
+ uint32_t oldval, newval, val, addr, cpsr, *host_addr;
+
+- oldval = env->regs[0];
+- newval = env->regs[1];
++ /* Swap if host != guest endianness, for the host cmpxchg below */
++ oldval = tswap32(env->regs[0]);
++ newval = tswap32(env->regs[1]);
+ addr = env->regs[2];
+
+ mmap_lock();
+@@ -174,6 +175,10 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env)
+ return;
+ }
+
++ /* Swap if host != guest endianness, for the host cmpxchg below */
++ oldval = tswap64(oldval);
++ newval = tswap64(newval);
++
+ #ifdef CONFIG_ATOMIC64
+ val = qatomic_cmpxchg__nocheck(host_addr, oldval, newval);
+ cpsr = (val == oldval) * CPSR_C;
+diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
+index e533f8a348..1b1a27de02 100644
+--- a/net/vhost-vdpa.c
++++ b/net/vhost-vdpa.c
+@@ -403,8 +403,9 @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
+ if (unlikely(dev_written < 0)) {
+ return dev_written;
+ }
+-
+- return *s->status != VIRTIO_NET_OK;
++ if (*s->status != VIRTIO_NET_OK) {
++ return -EIO;
++ }
+ }
+
+ return 0;
+@@ -428,8 +429,11 @@ static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
+ if (unlikely(dev_written < 0)) {
+ return dev_written;
+ }
++ if (*s->status != VIRTIO_NET_OK) {
++ return -EIO;
++ }
+
+- return *s->status != VIRTIO_NET_OK;
++ return 0;
+ }
+
+ static int vhost_vdpa_net_load(NetClientState *nc)
+@@ -525,7 +529,16 @@ out:
+ error_report("Bad device CVQ written length");
+ }
+ vhost_svq_push_elem(svq, elem, MIN(in_len, sizeof(status)));
+- g_free(elem);
++ /*
++ * `elem` belongs to vhost_vdpa_net_handle_ctrl_avail() only when
++ * the function successfully forwards the CVQ command, indicated
++ * by a non-negative value of `dev_written`. Otherwise, it still
++ * belongs to SVQ.
++ * This function should only free the `elem` when it owns.
++ */
++ if (dev_written >= 0) {
++ g_free(elem);
++ }
+ return dev_written < 0 ? dev_written : 0;
+ }
+
+diff --git a/qemu-nbd.c b/qemu-nbd.c
+index 0cd5aa6f02..f71f5125d8 100644
+--- a/qemu-nbd.c
++++ b/qemu-nbd.c
+@@ -272,9 +272,14 @@ static void *show_parts(void *arg)
+ return NULL;
+ }
+
++struct NbdClientOpts {
++ char *device;
++ bool fork_process;
++};
++
+ static void *nbd_client_thread(void *arg)
+ {
+- char *device = arg;
++ struct NbdClientOpts *opts = arg;
+ NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") };
+ QIOChannelSocket *sioc;
+ int fd = -1;
+@@ -298,10 +303,10 @@ static void *nbd_client_thread(void *arg)
+ goto out;
+ }
+
+- fd = open(device, O_RDWR);
++ fd = open(opts->device, O_RDWR);
+ if (fd < 0) {
+ /* Linux-only, we can use %m in printf. */
+- error_report("Failed to open %s: %m", device);
++ error_report("Failed to open %s: %m", opts->device);
+ goto out;
+ }
+
+@@ -311,11 +316,11 @@ static void *nbd_client_thread(void *arg)
+ }
+
+ /* update partition table */
+- pthread_create(&show_parts_thread, NULL, show_parts, device);
++ pthread_create(&show_parts_thread, NULL, show_parts, opts->device);
+
+- if (verbose) {
++ if (verbose && !opts->fork_process) {
+ fprintf(stderr, "NBD device %s is now connected to %s\n",
+- device, srcpath);
++ opts->device, srcpath);
+ } else {
+ /* Close stderr so that the qemu-nbd process exits. */
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+@@ -575,11 +580,13 @@ int main(int argc, char **argv)
+ bool writethrough = false; /* Client will flush as needed. */
+ bool fork_process = false;
+ bool list = false;
+- int old_stderr = -1;
+ unsigned socket_activation;
+ const char *pid_file_name = NULL;
+ const char *selinux_label = NULL;
+ BlockExportOptions *export_opts;
++#if HAVE_NBD_DEVICE
++ struct NbdClientOpts opts;
++#endif
+
+ #ifdef CONFIG_POSIX
+ os_setup_early_signal_handling();
+@@ -930,11 +937,6 @@ int main(int argc, char **argv)
+ } else if (pid == 0) {
+ close(stderr_fd[0]);
+
+- /* Remember parent's stderr if we will be restoring it. */
+- if (fork_process) {
+- old_stderr = dup(STDERR_FILENO);
+- }
+-
+ ret = qemu_daemon(1, 0);
+
+ /* Temporarily redirect stderr to the parent's pipe... */
+@@ -1123,8 +1125,12 @@ int main(int argc, char **argv)
+ if (device) {
+ #if HAVE_NBD_DEVICE
+ int ret;
++ opts = (struct NbdClientOpts) {
++ .device = device,
++ .fork_process = fork_process,
++ };
+
+- ret = pthread_create(&client_thread, NULL, nbd_client_thread, device);
++ ret = pthread_create(&client_thread, NULL, nbd_client_thread, &opts);
+ if (ret != 0) {
+ error_report("Failed to create client thread: %s", strerror(ret));
+ exit(EXIT_FAILURE);
+@@ -1150,8 +1156,7 @@ int main(int argc, char **argv)
+ }
+
+ if (fork_process) {
+- dup2(old_stderr, STDERR_FILENO);
+- close(old_stderr);
++ dup2(STDOUT_FILENO, STDERR_FILENO);
+ }
+
+ state = RUNNING;
+diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs
+index 3442383627..949ba07fd2 100644
+--- a/qga/installer/qemu-ga.wxs
++++ b/qga/installer/qemu-ga.wxs
+@@ -116,22 +116,22 @@
+ </Directory>
+ </Directory>
+
+- <Property Id="cmd" Value="cmd.exe"/>
++ <Property Id="rundll" Value="rundll32.exe"/>
+ <Property Id="REINSTALLMODE" Value="amus"/>
+
+ <?ifdef var.InstallVss?>
+ <CustomAction Id="RegisterCom"
+- ExeCommand='/c "[qemu_ga_directory]qemu-ga.exe" -s vss-install'
++ ExeCommand='"[qemu_ga_directory]qga-vss.dll",DLLCOMRegister'
+ Execute="deferred"
+- Property="cmd"
++ Property="rundll"
+ Impersonate="no"
+ Return="check"
+ >
+ </CustomAction>
+ <CustomAction Id="UnRegisterCom"
+- ExeCommand='/c "[qemu_ga_directory]qemu-ga.exe" -s vss-uninstall'
++ ExeCommand='"[qemu_ga_directory]qga-vss.dll",DLLCOMUnregister'
+ Execute="deferred"
+- Property="cmd"
++ Property="rundll"
+ Impersonate="no"
+ Return="check"
+ >
+diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp
+index b8087e5baa..ff93b08a9e 100644
+--- a/qga/vss-win32/install.cpp
++++ b/qga/vss-win32/install.cpp
+@@ -357,6 +357,15 @@ out:
+ return hr;
+ }
+
++STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int)
++{
++ COMRegister();
++}
++
++STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int)
++{
++ COMUnregister();
++}
+
+ static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data)
+ {
+diff --git a/qga/vss-win32/qga-vss.def b/qga/vss-win32/qga-vss.def
+index 927782c31b..ee97a81427 100644
+--- a/qga/vss-win32/qga-vss.def
++++ b/qga/vss-win32/qga-vss.def
+@@ -1,6 +1,8 @@
+ LIBRARY "QGA-PROVIDER.DLL"
+
+ EXPORTS
++ DLLCOMRegister
++ DLLCOMUnregister
+ COMRegister PRIVATE
+ COMUnregister PRIVATE
+ DllCanUnloadNow PRIVATE
+diff --git a/target/arm/translate.c b/target/arm/translate.c
+index a06da05640..9cf4a6819e 100644
+--- a/target/arm/translate.c
++++ b/target/arm/translate.c
+@@ -9030,7 +9030,7 @@ static bool trans_IT(DisasContext *s, arg_IT *a)
+ /* v8.1M CSEL/CSINC/CSNEG/CSINV */
+ static bool trans_CSEL(DisasContext *s, arg_CSEL *a)
+ {
+- TCGv_i32 rn, rm, zero;
++ TCGv_i32 rn, rm;
+ DisasCompare c;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
+@@ -9048,16 +9048,17 @@ static bool trans_CSEL(DisasContext *s, arg_CSEL *a)
+ }
+
+ /* In this insn input reg fields of 0b1111 mean "zero", not "PC" */
+- zero = tcg_constant_i32(0);
++ rn = tcg_temp_new_i32();
++ rm = tcg_temp_new_i32();
+ if (a->rn == 15) {
+- rn = zero;
++ tcg_gen_movi_i32(rn, 0);
+ } else {
+- rn = load_reg(s, a->rn);
++ load_reg_var(s, rn, a->rn);
+ }
+ if (a->rm == 15) {
+- rm = zero;
++ tcg_gen_movi_i32(rm, 0);
+ } else {
+- rm = load_reg(s, a->rm);
++ load_reg_var(s, rm, a->rm);
+ }
+
+ switch (a->op) {
+@@ -9077,7 +9078,7 @@ static bool trans_CSEL(DisasContext *s, arg_CSEL *a)
+ }
+
+ arm_test_cc(&c, a->fcond);
+- tcg_gen_movcond_i32(c.cond, rn, c.value, zero, rn, rm);
++ tcg_gen_movcond_i32(c.cond, rn, c.value, tcg_constant_i32(0), rn, rm);
+ arm_free_cc(&c);
+
+ store_reg(s, a->rd, rn);
+diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
+index 6f3b6beecf..6f441f159b 100644
+--- a/target/hppa/cpu.h
++++ b/target/hppa/cpu.h
+@@ -168,6 +168,9 @@ typedef struct {
+ } hppa_tlb_entry;
+
+ typedef struct CPUArchState {
++ target_ureg iaoq_f; /* front */
++ target_ureg iaoq_b; /* back, aka next instruction */
++
+ target_ureg gr[32];
+ uint64_t fr[32];
+ uint64_t sr[8]; /* stored shifted into place for gva */
+@@ -186,8 +189,6 @@ typedef struct CPUArchState {
+ target_ureg psw_cb; /* in least significant bit of next nibble */
+ target_ureg psw_cb_msb; /* boolean */
+
+- target_ureg iaoq_f; /* front */
+- target_ureg iaoq_b; /* back, aka next instruction */
+ uint64_t iasq_f;
+ uint64_t iasq_b;
+
+diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc
+index c2ee712561..ee4f4a899f 100644
+--- a/target/i386/tcg/decode-new.c.inc
++++ b/target/i386/tcg/decode-new.c.inc
+@@ -1815,16 +1815,18 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
+ }
+ break;
+
+- case X86_SPECIAL_MMX:
+- if (!(s->prefix & (PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA))) {
+- gen_helper_enter_mmx(cpu_env);
+- }
++ default:
+ break;
+ }
+
+ if (!validate_vex(s, &decode)) {
+ return;
+ }
++ if (decode.e.special == X86_SPECIAL_MMX &&
++ !(s->prefix & (PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA))) {
++ gen_helper_enter_mmx(cpu_env);
++ }
++
+ if (decode.op[0].has_ea || decode.op[1].has_ea || decode.op[2].has_ea) {
+ gen_load_ea(s, &decode.mem, decode.e.vex_class == 12);
+ }
+diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
+index e15c633b0b..6fc583f3e8 100644
+--- a/target/loongarch/cpu.h
++++ b/target/loongarch/cpu.h
+@@ -317,6 +317,7 @@ typedef struct CPUArchState {
+ uint64_t CSR_DBG;
+ uint64_t CSR_DERA;
+ uint64_t CSR_DSAVE;
++ uint64_t CSR_CPUID;
+
+ #ifndef CONFIG_USER_ONLY
+ LoongArchTLB tlb[LOONGARCH_TLB_MAX];
+diff --git a/target/loongarch/csr_helper.c b/target/loongarch/csr_helper.c
+index 7e02787895..b778e6952d 100644
+--- a/target/loongarch/csr_helper.c
++++ b/target/loongarch/csr_helper.c
+@@ -36,6 +36,15 @@ target_ulong helper_csrrd_pgd(CPULoongArchState *env)
+ return v;
+ }
+
++target_ulong helper_csrrd_cpuid(CPULoongArchState *env)
++{
++ LoongArchCPU *lac = env_archcpu(env);
++
++ env->CSR_CPUID = CPU(lac)->cpu_index;
++
++ return env->CSR_CPUID;
++}
++
+ target_ulong helper_csrrd_tval(CPULoongArchState *env)
+ {
+ LoongArchCPU *cpu = env_archcpu(env);
+diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
+index 9c01823a26..f47b0f2d05 100644
+--- a/target/loongarch/helper.h
++++ b/target/loongarch/helper.h
+@@ -98,6 +98,7 @@ DEF_HELPER_1(rdtime_d, i64, env)
+ #ifndef CONFIG_USER_ONLY
+ /* CSRs helper */
+ DEF_HELPER_1(csrrd_pgd, i64, env)
++DEF_HELPER_1(csrrd_cpuid, i64, env)
+ DEF_HELPER_1(csrrd_tval, i64, env)
+ DEF_HELPER_2(csrwr_estat, i64, env, tl)
+ DEF_HELPER_2(csrwr_asid, i64, env, tl)
+diff --git a/target/loongarch/insn_trans/trans_privileged.c.inc b/target/loongarch/insn_trans/trans_privileged.c.inc
+index 40f82becb0..e3d92c7a22 100644
+--- a/target/loongarch/insn_trans/trans_privileged.c.inc
++++ b/target/loongarch/insn_trans/trans_privileged.c.inc
+@@ -99,13 +99,7 @@ static const CSRInfo csr_info[] = {
+ CSR_OFF(PWCH),
+ CSR_OFF(STLBPS),
+ CSR_OFF(RVACFG),
+- [LOONGARCH_CSR_CPUID] = {
+- .offset = (int)offsetof(CPUState, cpu_index)
+- - (int)offsetof(LoongArchCPU, env),
+- .flags = CSRFL_READONLY,
+- .readfn = NULL,
+- .writefn = NULL
+- },
++ CSR_OFF_FUNCS(CPUID, CSRFL_READONLY, gen_helper_csrrd_cpuid, NULL),
+ CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY),
+ CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY),
+ CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY),
+diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c
+index 87b1314925..7a88205ce7 100644
+--- a/target/m68k/m68k-semi.c
++++ b/target/m68k/m68k-semi.c
+@@ -165,7 +165,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr)
+ GET_ARG64(2);
+ GET_ARG64(3);
+ semihost_sys_lseek(cs, m68k_semi_u64_cb, arg0,
+- deposit64(arg2, arg1, 32, 32), arg3);
++ deposit64(arg2, 32, 32, arg1), arg3);
+ break;
+
+ case HOSTED_RENAME:
+diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c
+index f76e8588c5..19a7d0e763 100644
+--- a/target/nios2/nios2-semi.c
++++ b/target/nios2/nios2-semi.c
+@@ -132,8 +132,8 @@ void do_nios2_semihosting(CPUNios2State *env)
+ args = env->regs[R_ARG1];
+ switch (nr) {
+ case HOSTED_EXIT:
+- gdb_exit(env->regs[R_ARG0]);
+- exit(env->regs[R_ARG0]);
++ gdb_exit(env->regs[R_ARG1]);
++ exit(env->regs[R_ARG1]);
+
+ case HOSTED_OPEN:
+ GET_ARG(0);
+@@ -168,7 +168,7 @@ void do_nios2_semihosting(CPUNios2State *env)
+ GET_ARG64(2);
+ GET_ARG64(3);
+ semihost_sys_lseek(cs, nios2_semi_u64_cb, arg0,
+- deposit64(arg2, arg1, 32, 32), arg3);
++ deposit64(arg2, 32, 32, arg1), arg3);
+ break;
+
+ case HOSTED_RENAME:
+diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
+index 6cf88f635a..839d95c1eb 100644
+--- a/target/ppc/excp_helper.c
++++ b/target/ppc/excp_helper.c
+@@ -2645,6 +2645,12 @@ void helper_pminsn(CPUPPCState *env, uint32_t insn)
+ env->resume_as_sreset = (insn != PPC_PM_STOP) ||
+ (env->spr[SPR_PSSCR] & PSSCR_EC);
+
++ /* HDECR is not to wake from PM state, it may have already fired */
++ if (env->resume_as_sreset) {
++ PowerPCCPU *cpu = env_archcpu(env);
++ ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0);
++ }
++
+ ppc_maybe_interrupt(env);
+ }
+ #endif /* defined(TARGET_PPC64) */
+diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c
+index b9b31fd276..64c2a9cab3 100644
+--- a/target/ppc/mmu-hash64.c
++++ b/target/ppc/mmu-hash64.c
+@@ -770,7 +770,8 @@ static bool ppc_hash64_use_vrma(CPUPPCState *env)
+ }
+ }
+
+-static void ppc_hash64_set_isi(CPUState *cs, int mmu_idx, uint64_t error_code)
++static void ppc_hash64_set_isi(CPUState *cs, int mmu_idx, uint64_t slb_vsid,
++ uint64_t error_code)
+ {
+ CPUPPCState *env = &POWERPC_CPU(cs)->env;
+ bool vpm;
+@@ -782,13 +783,15 @@ static void ppc_hash64_set_isi(CPUState *cs, int mmu_idx, uint64_t error_code)
+ }
+ if (vpm && !mmuidx_hv(mmu_idx)) {
+ cs->exception_index = POWERPC_EXCP_HISI;
++ env->spr[SPR_ASDR] = slb_vsid;
+ } else {
+ cs->exception_index = POWERPC_EXCP_ISI;
+ }
+ env->error_code = error_code;
+ }
+
+-static void ppc_hash64_set_dsi(CPUState *cs, int mmu_idx, uint64_t dar, uint64_t dsisr)
++static void ppc_hash64_set_dsi(CPUState *cs, int mmu_idx, uint64_t slb_vsid,
++ uint64_t dar, uint64_t dsisr)
+ {
+ CPUPPCState *env = &POWERPC_CPU(cs)->env;
+ bool vpm;
+@@ -802,6 +805,7 @@ static void ppc_hash64_set_dsi(CPUState *cs, int mmu_idx, uint64_t dar, uint64_t
+ cs->exception_index = POWERPC_EXCP_HDSI;
+ env->spr[SPR_HDAR] = dar;
+ env->spr[SPR_HDSISR] = dsisr;
++ env->spr[SPR_ASDR] = slb_vsid;
+ } else {
+ cs->exception_index = POWERPC_EXCP_DSI;
+ env->spr[SPR_DAR] = dar;
+@@ -870,12 +874,46 @@ static target_ulong rmls_limit(PowerPCCPU *cpu)
+ return rma_sizes[rmls];
+ }
+
+-static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb)
++/* Return the LLP in SLB_VSID format */
++static uint64_t get_vrma_llp(PowerPCCPU *cpu)
+ {
+ CPUPPCState *env = &cpu->env;
+- target_ulong lpcr = env->spr[SPR_LPCR];
+- uint32_t vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT;
+- target_ulong vsid = SLB_VSID_VRMA | ((vrmasd << 4) & SLB_VSID_LLP_MASK);
++ uint64_t llp;
++
++ if (env->mmu_model == POWERPC_MMU_3_00) {
++ ppc_v3_pate_t pate;
++ uint64_t ps, l, lp;
++
++ /*
++ * ISA v3.0 removes the LPCR[VRMASD] field and puts the VRMA base
++ * page size (L||LP equivalent) in the PS field in the HPT partition
++ * table entry.
++ */
++ if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) {
++ error_report("Bad VRMA with no partition table entry");
++ return 0;
++ }
++ ps = PATE0_GET_PS(pate.dw0);
++ /* PS has L||LP in 3 consecutive bits, put them into SLB LLP format */
++ l = (ps >> 2) & 0x1;
++ lp = ps & 0x3;
++ llp = (l << SLB_VSID_L_SHIFT) | (lp << SLB_VSID_LP_SHIFT);
++
++ } else {
++ uint64_t lpcr = env->spr[SPR_LPCR];
++ target_ulong vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT;
++
++ /* VRMASD LLP matches SLB format, just shift and mask it */
++ llp = (vrmasd << SLB_VSID_LP_SHIFT) & SLB_VSID_LLP_MASK;
++ }
++
++ return llp;
++}
++
++static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb)
++{
++ uint64_t llp = get_vrma_llp(cpu);
++ target_ulong vsid = SLB_VSID_VRMA | llp;
+ int i;
+
+ for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) {
+@@ -893,8 +931,7 @@ static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb)
+ }
+ }
+
+- error_report("Bad page size encoding in LPCR[VRMASD]; LPCR=0x"
+- TARGET_FMT_lx, lpcr);
++ error_report("Bad VRMA page size encoding 0x" TARGET_FMT_lx, llp);
+
+ return -1;
+ }
+@@ -963,13 +1000,13 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
+ }
+ switch (access_type) {
+ case MMU_INST_FETCH:
+- ppc_hash64_set_isi(cs, mmu_idx, SRR1_PROTFAULT);
++ ppc_hash64_set_isi(cs, mmu_idx, 0, SRR1_PROTFAULT);
+ break;
+ case MMU_DATA_LOAD:
+- ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_PROTFAULT);
++ ppc_hash64_set_dsi(cs, mmu_idx, 0, eaddr, DSISR_PROTFAULT);
+ break;
+ case MMU_DATA_STORE:
+- ppc_hash64_set_dsi(cs, mmu_idx, eaddr,
++ ppc_hash64_set_dsi(cs, mmu_idx, 0, eaddr,
+ DSISR_PROTFAULT | DSISR_ISSTORE);
+ break;
+ default:
+@@ -1022,7 +1059,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
+ /* 3. Check for segment level no-execute violation */
+ if (access_type == MMU_INST_FETCH && (slb->vsid & SLB_VSID_N)) {
+ if (guest_visible) {
+- ppc_hash64_set_isi(cs, mmu_idx, SRR1_NOEXEC_GUARD);
++ ppc_hash64_set_isi(cs, mmu_idx, slb->vsid, SRR1_NOEXEC_GUARD);
+ }
+ return false;
+ }
+@@ -1035,13 +1072,14 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
+ }
+ switch (access_type) {
+ case MMU_INST_FETCH:
+- ppc_hash64_set_isi(cs, mmu_idx, SRR1_NOPTE);
++ ppc_hash64_set_isi(cs, mmu_idx, slb->vsid, SRR1_NOPTE);
+ break;
+ case MMU_DATA_LOAD:
+- ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_NOPTE);
++ ppc_hash64_set_dsi(cs, mmu_idx, slb->vsid, eaddr, DSISR_NOPTE);
+ break;
+ case MMU_DATA_STORE:
+- ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_NOPTE | DSISR_ISSTORE);
++ ppc_hash64_set_dsi(cs, mmu_idx, slb->vsid, eaddr,
++ DSISR_NOPTE | DSISR_ISSTORE);
+ break;
+ default:
+ g_assert_not_reached();
+@@ -1075,7 +1113,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
+ if (PAGE_EXEC & ~amr_prot) {
+ srr1 |= SRR1_IAMR; /* Access violates virt pg class key prot */
+ }
+- ppc_hash64_set_isi(cs, mmu_idx, srr1);
++ ppc_hash64_set_isi(cs, mmu_idx, slb->vsid, srr1);
+ } else {
+ int dsisr = 0;
+ if (need_prot & ~pp_prot) {
+@@ -1087,7 +1125,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
+ if (need_prot & ~amr_prot) {
+ dsisr |= DSISR_AMR;
+ }
+- ppc_hash64_set_dsi(cs, mmu_idx, eaddr, dsisr);
++ ppc_hash64_set_dsi(cs, mmu_idx, slb->vsid, eaddr, dsisr);
+ }
+ return false;
+ }
+diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h
+index 1496955d38..de653fcae5 100644
+--- a/target/ppc/mmu-hash64.h
++++ b/target/ppc/mmu-hash64.h
+@@ -41,8 +41,10 @@ void ppc_hash64_finalize(PowerPCCPU *cpu);
+ #define SLB_VSID_KP 0x0000000000000400ULL
+ #define SLB_VSID_N 0x0000000000000200ULL /* no-execute */
+ #define SLB_VSID_L 0x0000000000000100ULL
++#define SLB_VSID_L_SHIFT PPC_BIT_NR(55)
+ #define SLB_VSID_C 0x0000000000000080ULL /* class */
+ #define SLB_VSID_LP 0x0000000000000030ULL
++#define SLB_VSID_LP_SHIFT PPC_BIT_NR(59)
+ #define SLB_VSID_ATTR 0x0000000000000FFFULL
+ #define SLB_VSID_LLP_MASK (SLB_VSID_L | SLB_VSID_LP)
+ #define SLB_VSID_4K 0x0000000000000000ULL
+@@ -58,6 +60,9 @@ void ppc_hash64_finalize(PowerPCCPU *cpu);
+ #define SDR_64_HTABSIZE 0x000000000000001FULL
+
+ #define PATE0_HTABORG 0x0FFFFFFFFFFC0000ULL
++#define PATE0_PS PPC_BITMASK(56, 58)
++#define PATE0_GET_PS(dw0) (((dw0) & PATE0_PS) >> PPC_BIT_NR(58))
++
+ #define HPTES_PER_GROUP 8
+ #define HASH_PTE_SIZE_64 16
+ #define HASH_PTEG_SIZE_64 (HASH_PTE_SIZE_64 * HPTES_PER_GROUP)
+diff --git a/target/ppc/translate.c b/target/ppc/translate.c
+index 1de7eca9c4..90f749a728 100644
+--- a/target/ppc/translate.c
++++ b/target/ppc/translate.c
+@@ -4327,6 +4327,9 @@ static void pmu_count_insns(DisasContext *ctx)
+
+ static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
+ {
++ if (unlikely(ctx->singlestep_enabled)) {
++ return false;
++ }
+ return translator_use_goto_tb(&ctx->base, dest);
+ }
+
+diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c
+index fe02d82201..7094020dcd 100644
+--- a/target/s390x/tcg/excp_helper.c
++++ b/target/s390x/tcg/excp_helper.c
+@@ -638,7 +638,7 @@ void monitor_event(CPUS390XState *env,
+ void HELPER(monitor_call)(CPUS390XState *env, uint64_t monitor_code,
+ uint32_t monitor_class)
+ {
+- g_assert(monitor_class <= 0xff);
++ g_assert(monitor_class <= 0xf);
+
+ if (env->cregs[8] & (0x8000 >> monitor_class)) {
+ monitor_event(env, monitor_code, monitor_class, GETPC());
+diff --git a/target/s390x/tcg/fpu_helper.c b/target/s390x/tcg/fpu_helper.c
+index be80b2373c..0bde369768 100644
+--- a/target/s390x/tcg/fpu_helper.c
++++ b/target/s390x/tcg/fpu_helper.c
+@@ -44,7 +44,8 @@ uint8_t s390_softfloat_exc_to_ieee(unsigned int exc)
+ s390_exc |= (exc & float_flag_divbyzero) ? S390_IEEE_MASK_DIVBYZERO : 0;
+ s390_exc |= (exc & float_flag_overflow) ? S390_IEEE_MASK_OVERFLOW : 0;
+ s390_exc |= (exc & float_flag_underflow) ? S390_IEEE_MASK_UNDERFLOW : 0;
+- s390_exc |= (exc & float_flag_inexact) ? S390_IEEE_MASK_INEXACT : 0;
++ s390_exc |= (exc & (float_flag_inexact | float_flag_invalid_cvti)) ?
++ S390_IEEE_MASK_INEXACT : 0;
+
+ return s390_exc;
+ }
+diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc
+index 4249632af3..0e328ea0fd 100644
+--- a/target/s390x/tcg/insn-data.h.inc
++++ b/target/s390x/tcg/insn-data.h.inc
+@@ -157,7 +157,7 @@
+ C(0xb2fa, NIAI, E, EH, 0, 0, 0, 0, 0, 0)
+
+ /* CHECKSUM */
+- C(0xb241, CKSM, RRE, Z, r1_o, ra2, new, r1_32, cksm, 0)
++ C(0xb241, CKSM, RRE, Z, r1_o, ra2_E, new, r1_32, cksm, 0)
+
+ /* COPY SIGN */
+ F(0xb372, CPSDR, RRF_b, FPSSH, f3, f2, new, f1, cps, 0, IF_AFP1 | IF_AFP2 | IF_AFP3)
+diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c
+index 7e7de5e2f1..791a412d95 100644
+--- a/target/s390x/tcg/mem_helper.c
++++ b/target/s390x/tcg/mem_helper.c
+@@ -704,6 +704,11 @@ uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
+ HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
+ mask, addr);
+
++ if (!mask) {
++ /* Recognize access exceptions for the first byte */
++ probe_read(env, addr, 1, cpu_mmu_index(env, false), ra);
++ }
++
+ while (mask) {
+ if (mask & 8) {
+ uint8_t d = cpu_ldub_data_ra(env, addr, ra);
+diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
+index 0885bf2641..ff64d6c28f 100644
+--- a/target/s390x/tcg/translate.c
++++ b/target/s390x/tcg/translate.c
+@@ -2641,6 +2641,12 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o)
+ ccm = ((1ull << len) - 1) << pos;
+ break;
+
++ case 0:
++ /* Recognize access exceptions for the first byte. */
++ tcg_gen_qemu_ld_i64(tmp, o->in2, get_mem_index(s), MO_UB);
++ gen_op_movi_cc(s, 0);
++ return DISAS_NEXT;
++
+ default:
+ /* This is going to be a sequence of loads and inserts. */
+ pos = base + 32 - 8;
+@@ -3344,9 +3350,9 @@ static DisasJumpType op_mc(DisasContext *s, DisasOps *o)
+ #if !defined(CONFIG_USER_ONLY)
+ TCGv_i32 i2;
+ #endif
+- const uint16_t monitor_class = get_field(s, i2);
++ const uint8_t monitor_class = get_field(s, i2);
+
+- if (monitor_class & 0xff00) {
++ if (monitor_class & 0xf0) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return DISAS_NORETURN;
+ }
+@@ -5992,6 +5998,12 @@ static void in2_ra2(DisasContext *s, DisasOps *o)
+ }
+ #define SPEC_in2_ra2 0
+
++static void in2_ra2_E(DisasContext *s, DisasOps *o)
++{
++ return in2_ra2(s, o);
++}
++#define SPEC_in2_ra2_E SPEC_r2_even
++
+ static void in2_a2(DisasContext *s, DisasOps *o)
+ {
+ int x2 = have_field(s, x2) ? get_field(s, x2) : 0;
+diff --git a/target/s390x/tcg/translate_vx.c.inc b/target/s390x/tcg/translate_vx.c.inc
+index d39ee81cd6..79e2bbe0a7 100644
+--- a/target/s390x/tcg/translate_vx.c.inc
++++ b/target/s390x/tcg/translate_vx.c.inc
+@@ -3192,7 +3192,7 @@ static DisasJumpType op_vfmax(DisasContext *s, DisasOps *o)
+ const uint8_t m5 = get_field(s, m5);
+ gen_helper_gvec_3_ptr *fn;
+
+- if (m6 == 5 || m6 == 6 || m6 == 7 || m6 > 13) {
++ if (m6 == 5 || m6 == 6 || m6 == 7 || m6 >= 13) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return DISAS_NORETURN;
+ }
+diff --git a/ui/vnc-clipboard.c b/ui/vnc-clipboard.c
+index 8aeadfaa21..c759be3438 100644
+--- a/ui/vnc-clipboard.c
++++ b/ui/vnc-clipboard.c
+@@ -50,8 +50,11 @@ static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size)
+ ret = inflate(&stream, Z_FINISH);
+ switch (ret) {
+ case Z_OK:
+- case Z_STREAM_END:
+ break;
++ case Z_STREAM_END:
++ *size = stream.total_out;
++ inflateEnd(&stream);
++ return out;
+ case Z_BUF_ERROR:
+ out_len <<= 1;
+ if (out_len > (1 << 20)) {
+@@ -66,11 +69,6 @@ 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 --git a/util/thread-pool.c b/util/thread-pool.c
+index 31113b5860..39accc9ebe 100644
+--- a/util/thread-pool.c
++++ b/util/thread-pool.c
+@@ -120,13 +120,13 @@ static void *worker_thread(void *opaque)
+
+ pool->cur_threads--;
+ qemu_cond_signal(&pool->worker_stopped);
+- qemu_mutex_unlock(&pool->lock);
+
+ /*
+ * Wake up another thread, in case we got a wakeup but decided
+ * to exit due to pool->cur_threads > pool->max_threads.
+ */
+ qemu_cond_signal(&pool->request_cond);
++ qemu_mutex_unlock(&pool->lock);
+ return NULL;
+ }
+