From ac8fc4ccacd8a77d8d56dc3990bfb221c1f48fcd Mon Sep 17 00:00:00 2001 From: Yuri Benditovich Date: Thu, 15 May 2025 09:32:37 +0300 Subject: [PATCH 01/26] virtio: check for validity of indirect descriptors virtio processes indirect descriptors even if the respected feature VIRTIO_RING_F_INDIRECT_DESC was not negotiated. If qemu is used with reduced set of features to emulate the hardware device that does not support indirect descriptors, the will probably trigger problematic flows on the hardware setup but do not reveal the mistake on qemu. Add LOG_GUEST_ERROR for such case. This will issue logs with '-d guest_errors' in the command line Signed-off-by: Yuri Benditovich Message-Id: <20250515063237.808293-1-yuri.benditovich@daynix.com> Signed-off-by: Yuri Benditovich --- hw/virtio/virtio.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2e98cecf64..5534251e01 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -205,6 +205,15 @@ static const char *virtio_id_to_name(uint16_t device_id) return name; } +static void virtio_check_indirect_feature(VirtIODevice *vdev) +{ + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Device %s: indirect_desc was not negotiated!\n", + vdev->name); + } +} + /* Called within call_rcu(). */ static void virtio_free_region_cache(VRingMemoryRegionCaches *caches) { @@ -1733,6 +1742,7 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } + virtio_check_indirect_feature(vdev); /* loop over the indirect descriptor table */ len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as, @@ -1870,6 +1880,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } + virtio_check_indirect_feature(vdev); /* loop over the indirect descriptor table */ len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as, From 31753d5a336fbb4e9246397f4b90b6f611f27f22 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 16 May 2025 15:35:34 +0530 Subject: [PATCH 02/26] hw/i386/amd_iommu: Fix device setup failure when PT is on. Commit c1f46999ef506 ("amd_iommu: Add support for pass though mode") introduces the support for "pt" flag by enabling nodma memory when "pt=off". This allowed VFIO devices to successfully register notifiers by using nodma region. But, This also broke things when guest is booted with the iommu=nopt because, devices bypass the IOMMU and use untranslated addresses (IOVA) to perform DMA reads/writes to the nodma memory region, ultimately resulting in a failure to setup the devices in the guest. Fix the above issue by always enabling the amdvi_dev_as->iommu memory region. But this will once again cause VFIO devices to fail while registering the notifiers with AMD IOMMU memory region. Fixes: c1f46999ef506 ("amd_iommu: Add support for pass though mode") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250516100535.4980-2-sarunkod@amd.com> Fixes: c1f46999ef506 ("amd_iommu: Add support for pass though mode") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde --- hw/i386/amd_iommu.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 0775c8f3bb..17379db52a 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1426,7 +1426,6 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) AMDVIState *s = opaque; AMDVIAddressSpace **iommu_as, *amdvi_dev_as; int bus_num = pci_bus_num(bus); - X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); iommu_as = s->address_spaces[bus_num]; @@ -1486,15 +1485,8 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) AMDVI_INT_ADDR_FIRST, &amdvi_dev_as->iommu_ir, 1); - if (!x86_iommu->pt_supported) { - memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false); - memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), - true); - } else { - memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), - false); - memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, true); - } + memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false); + memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), true); } return &iommu_as[devfn]->as; } From 0f178860df3489a9d3c19a5f7f024e6aa6c26515 Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Fri, 16 May 2025 15:35:35 +0530 Subject: [PATCH 03/26] hw/i386/amd_iommu: Fix xtsup when vcpus < 255 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If vCPUs > 255 then x86 common code (x86_cpus_init()) call kvm_enable_x2apic(). But if vCPUs <= 255 then the common code won't calls kvm_enable_x2apic(). This is because commit 8c6619f3e692 ("hw/i386/amd_iommu: Simplify non-KVM checks on XTSup feature") removed the call to kvm_enable_x2apic when xtsup is "on", which break things when guest is booted with x2apic mode and there are <= 255 vCPUs. Fix this by adding back kvm_enable_x2apic() call when xtsup=on. Fixes: 8c6619f3e692 ("hw/i386/amd_iommu: Simplify non-KVM checks on XTSup feature") Reported-by: Alejandro Jimenez Tested-by: Tested-by: Alejandro Jimenez Cc: Philippe Mathieu-Daudé Cc: Joao Martins Signed-off-by: Vasant Hegde Signed-off-by: Sairaj Kodilkar Message-Id: <20250516100535.4980-3-sarunkod@amd.com> Fixes: 8c6619f3e692 ("hw/i386/amd_iommu: Simplify non-KVM checks on XTSup feature") Reported-by: Alejandro Jimenez Tested-by: Tested-by: Alejandro Jimenez Cc: Philippe Mathieu-Daudé Cc: Joao Martins Signed-off-by: Vasant Hegde Signed-off-by: Sairaj Kodilkar --- hw/i386/amd_iommu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 17379db52a..963aa2450c 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1715,6 +1715,14 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) exit(EXIT_FAILURE); } + if (s->xtsup) { + if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) { + error_report("AMD IOMMU xtsup=on requires x2APIC support on " + "the KVM side"); + exit(EXIT_FAILURE); + } + } + pci_setup_iommu(bus, &amdvi_iommu_ops, s); amdvi_init(s); } From a8d178e1492a1a803898501a84829ac517ae2fb0 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:51 +0000 Subject: [PATCH 04/26] pcie: Add helper to declare PASID capability for a pcie device Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-2-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 25 +++++++++++++++++++++++++ include/hw/pci/pcie.h | 6 +++++- include/hw/pci/pcie_regs.h | 5 +++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 1b12db6fa2..4f935ff420 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1214,3 +1214,28 @@ void pcie_acs_reset(PCIDevice *dev) pci_set_word(dev->config + dev->exp.acs_cap + PCI_ACS_CTRL, 0); } } + +/* PASID */ +void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, + bool exec_perm, bool priv_mod) +{ + static const uint16_t control_reg_rw_mask = 0x07; + uint16_t capability_reg; + + assert(pasid_width <= PCI_EXT_CAP_PASID_MAX_WIDTH); + + pcie_add_capability(dev, PCI_EXT_CAP_ID_PASID, PCI_PASID_VER, offset, + PCI_EXT_CAP_PASID_SIZEOF); + + capability_reg = ((uint16_t)pasid_width) << PCI_PASID_CAP_WIDTH_SHIFT; + capability_reg |= exec_perm ? PCI_PASID_CAP_EXEC : 0; + capability_reg |= priv_mod ? PCI_PASID_CAP_PRIV : 0; + pci_set_word(dev->config + offset + PCI_PASID_CAP, capability_reg); + + /* Everything is disabled by default */ + pci_set_word(dev->config + offset + PCI_PASID_CTRL, 0); + + pci_set_word(dev->wmask + offset + PCI_PASID_CTRL, control_reg_rw_mask); + + dev->exp.pasid_cap = offset; +} diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 70a5de09de..fe82e0a915 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -70,8 +70,9 @@ struct PCIExpressDevice { uint16_t aer_cap; PCIEAERLog aer_log; - /* Offset of ATS capability in config space */ + /* Offset of ATS and PASID capabilities in config space */ uint16_t ats_cap; + uint16_t pasid_cap; /* ACS */ uint16_t acs_cap; @@ -150,4 +151,7 @@ void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); + +void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, + bool exec_perm, bool priv_mod); #endif /* QEMU_PCIE_H */ diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 9d3b6868dc..4d9cf4a29c 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -86,6 +86,11 @@ typedef enum PCIExpLinkWidth { #define PCI_ARI_VER 1 #define PCI_ARI_SIZEOF 8 +/* PASID */ +#define PCI_PASID_VER 1 +#define PCI_EXT_CAP_PASID_MAX_WIDTH 20 +#define PCI_PASID_CAP_WIDTH_SHIFT 8 + /* AER */ #define PCI_ERR_VER 2 #define PCI_ERR_SIZEOF 0x48 From 1e82e8a828cf18a8abfeca7295322db91879de04 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:51 +0000 Subject: [PATCH 05/26] pcie: Helper functions to check if PASID is enabled pasid_enabled checks whether the capability is present or not. If so, we read the configuration space to get the status of the feature (enabled or not). Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-3-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 9 +++++++++ include/hw/pci/pcie.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 4f935ff420..db9756d861 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1239,3 +1239,12 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, dev->exp.pasid_cap = offset; } + +bool pcie_pasid_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.pasid_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.pasid_cap + PCI_PASID_CTRL) & + PCI_PASID_CTRL_ENABLE) != 0; +} diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index fe82e0a915..dff98ff2c6 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -154,4 +154,6 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, bool exec_perm, bool priv_mod); + +bool pcie_pasid_enabled(const PCIDevice *dev); #endif /* QEMU_PCIE_H */ From 6a3ae6a2440dead9dd8e3f84152dc53a5214c48d Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:52 +0000 Subject: [PATCH 06/26] pcie: Helper function to check if ATS is enabled ats_enabled checks whether the capability is present or not. If so, we read the configuration space to get the status of the feature (enabled or not). Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-4-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 9 +++++++++ include/hw/pci/pcie.h | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index db9756d861..36de709801 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1248,3 +1248,12 @@ bool pcie_pasid_enabled(const PCIDevice *dev) return (pci_get_word(dev->config + dev->exp.pasid_cap + PCI_PASID_CTRL) & PCI_PASID_CTRL_ENABLE) != 0; } + +bool pcie_ats_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.ats_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.ats_cap + PCI_ATS_CTRL) & + PCI_ATS_CTRL_ENABLE) != 0; +} diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index dff98ff2c6..497d0bc2d2 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -156,4 +156,5 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, bool exec_perm, bool priv_mod); bool pcie_pasid_enabled(const PCIDevice *dev); +bool pcie_ats_enabled(const PCIDevice *dev); #endif /* QEMU_PCIE_H */ From dcad6cb2abf4ffc4f911041d0547c4b54c2f92e2 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:54 +0000 Subject: [PATCH 07/26] pcie: Add a helper to declare the PRI capability for a pcie device Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-5-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 26 ++++++++++++++++++++++++++ include/hw/pci/pcie.h | 5 ++++- include/hw/pci/pcie_regs.h | 3 +++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 36de709801..542172b3fa 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1240,6 +1240,32 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, dev->exp.pasid_cap = offset; } +/* PRI */ +void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, + bool prg_response_pasid_req) +{ + static const uint16_t control_reg_rw_mask = 0x3; + static const uint16_t status_reg_rw1_mask = 0x3; + static const uint32_t pr_alloc_reg_rw_mask = 0xffffffff; + uint16_t status_reg; + + status_reg = prg_response_pasid_req ? PCI_PRI_STATUS_PASID : 0; + status_reg |= PCI_PRI_STATUS_STOPPED; /* Stopped by default */ + + pcie_add_capability(dev, PCI_EXT_CAP_ID_PRI, PCI_PRI_VER, offset, + PCI_EXT_CAP_PRI_SIZEOF); + /* Disabled by default */ + + pci_set_word(dev->config + offset + PCI_PRI_STATUS, status_reg); + pci_set_long(dev->config + offset + PCI_PRI_MAX_REQ, outstanding_pr_cap); + + pci_set_word(dev->wmask + offset + PCI_PRI_CTRL, control_reg_rw_mask); + pci_set_word(dev->w1cmask + offset + PCI_PRI_STATUS, status_reg_rw1_mask); + pci_set_long(dev->wmask + offset + PCI_PRI_ALLOC_REQ, pr_alloc_reg_rw_mask); + + dev->exp.pri_cap = offset; +} + bool pcie_pasid_enabled(const PCIDevice *dev) { if (!pci_is_express(dev) || !dev->exp.pasid_cap) { diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 497d0bc2d2..17f06cd5d6 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -70,9 +70,10 @@ struct PCIExpressDevice { uint16_t aer_cap; PCIEAERLog aer_log; - /* Offset of ATS and PASID capabilities in config space */ + /* Offset of ATS, PRI and PASID capabilities in config space */ uint16_t ats_cap; uint16_t pasid_cap; + uint16_t pri_cap; /* ACS */ uint16_t acs_cap; @@ -154,6 +155,8 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, bool exec_perm, bool priv_mod); +void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, + bool prg_response_pasid_req); bool pcie_pasid_enabled(const PCIDevice *dev); bool pcie_ats_enabled(const PCIDevice *dev); diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 4d9cf4a29c..33a22229fe 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -91,6 +91,9 @@ typedef enum PCIExpLinkWidth { #define PCI_EXT_CAP_PASID_MAX_WIDTH 20 #define PCI_PASID_CAP_WIDTH_SHIFT 8 +/* PRI */ +#define PCI_PRI_VER 1 + /* AER */ #define PCI_ERR_VER 2 #define PCI_ERR_SIZEOF 0x48 From 5be8cf79188a2a1c73d16f3a8b458d909ac976f4 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:57 +0000 Subject: [PATCH 08/26] pcie: Helper functions to check to check if PRI is enabled pri_enabled can be used to check whether the capability is present and enabled on a PCIe device Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-6-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 9 +++++++++ include/hw/pci/pcie.h | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 542172b3fa..eaeb68894e 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1266,6 +1266,15 @@ void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, dev->exp.pri_cap = offset; } +bool pcie_pri_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.pri_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.pri_cap + PCI_PRI_CTRL) & + PCI_PRI_CTRL_ENABLE) != 0; +} + bool pcie_pasid_enabled(const PCIDevice *dev) { if (!pci_is_express(dev) || !dev->exp.pasid_cap) { diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 17f06cd5d6..ff6ce08e13 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -158,6 +158,7 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, bool prg_response_pasid_req); +bool pcie_pri_enabled(const PCIDevice *dev); bool pcie_pasid_enabled(const PCIDevice *dev); bool pcie_ats_enabled(const PCIDevice *dev); #endif /* QEMU_PCIE_H */ From 8ff9e1def0ef3388333b6cc639c9f958f97ebe05 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:58 +0000 Subject: [PATCH 09/26] pci: Cache the bus mastering status in the device The cached is_master value is necessary to know if a device is allowed to issue ATS/PRI requests or not as these operations do not go through the master_enable memory region. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-7-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 23 +++++++++++++---------- include/hw/pci/pci_device.h | 1 + 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index f5ab510697..1114ba8529 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -128,6 +128,12 @@ static GSequence *pci_acpi_index_list(void) return used_acpi_index_list; } +static void pci_set_master(PCIDevice *d, bool enable) +{ + memory_region_set_enabled(&d->bus_master_enable_region, enable); + d->is_master = enable; /* cache the status */ +} + static void pci_init_bus_master(PCIDevice *pci_dev) { AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev); @@ -135,7 +141,7 @@ static void pci_init_bus_master(PCIDevice *pci_dev) memory_region_init_alias(&pci_dev->bus_master_enable_region, OBJECT(pci_dev), "bus master", dma_as->root, 0, memory_region_size(dma_as->root)); - memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); + pci_set_master(pci_dev, false); memory_region_add_subregion(&pci_dev->bus_master_container_region, 0, &pci_dev->bus_master_enable_region); } @@ -804,9 +810,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size, pci_bridge_update_mappings(PCI_BRIDGE(s)); } - memory_region_set_enabled(&s->bus_master_enable_region, - pci_get_word(s->config + PCI_COMMAND) - & PCI_COMMAND_MASTER); + pci_set_master(s, pci_get_word(s->config + PCI_COMMAND) + & PCI_COMMAND_MASTER); g_free(config); return 0; @@ -1787,9 +1792,8 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int if (ranges_overlap(addr, l, PCI_COMMAND, 2)) { pci_update_irq_disabled(d, was_irq_disabled); - memory_region_set_enabled(&d->bus_master_enable_region, - (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->enabled); + pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND) & + PCI_COMMAND_MASTER) && d->enabled); } msi_write_config(d, addr, val_in, l); @@ -3100,9 +3104,8 @@ void pci_set_enabled(PCIDevice *d, bool state) d->enabled = state; pci_update_mappings(d); - memory_region_set_enabled(&d->bus_master_enable_region, - (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->enabled); + pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND) + & PCI_COMMAND_MASTER) && d->enabled); if (qdev_is_realized(&d->qdev)) { pci_device_reset(d); } diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index e41d95b0b0..eee0338568 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -90,6 +90,7 @@ struct PCIDevice { char name[64]; PCIIORegion io_regions[PCI_NUM_REGIONS]; AddressSpace bus_master_as; + bool is_master; MemoryRegion bus_master_container_region; MemoryRegion bus_master_enable_region; From 042cbc9aec7caca639dcb1a8a996406a7c572706 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:59 +0000 Subject: [PATCH 10/26] pci: Add an API to get IOMMU's min page size and virtual address width This kind of information is needed by devices implementing ATS in order to initialize their translation cache. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-8-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 17 +++++++++++++++++ include/hw/pci/pci.h | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1114ba8529..fc4954ac81 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2970,6 +2970,23 @@ void pci_device_unset_iommu_device(PCIDevice *dev) } } +int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, + uint32_t *min_page_size) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->get_iotlb_info) { + iommu_bus->iommu_ops->get_iotlb_info(iommu_bus->iommu_opaque, + addr_width, min_page_size); + return 0; + } + + return -ENODEV; +} + void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) { /* diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index c2fe6caa2c..d67ffe12db 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -429,6 +429,19 @@ typedef struct PCIIOMMUOps { * @devfn: device and function number of the PCI device. */ void (*unset_iommu_device)(PCIBus *bus, void *opaque, int devfn); + /** + * @get_iotlb_info: get properties required to initialize a device IOTLB. + * + * Callback required if devices are allowed to cache translations. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @addr_width: the address width of the IOMMU (output parameter). + * + * @min_page_size: the page size of the IOMMU (output parameter). + */ + void (*get_iotlb_info)(void *opaque, uint8_t *addr_width, + uint32_t *min_page_size); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); @@ -436,6 +449,19 @@ bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, Error **errp); void pci_device_unset_iommu_device(PCIDevice *dev); +/** + * pci_iommu_get_iotlb_info: get properties required to initialize a + * device IOTLB. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to get the information. + * @addr_width: the address width of the IOMMU (output parameter). + * @min_page_size: the page size of the IOMMU (output parameter). + */ +int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, + uint32_t *min_page_size); + /** * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus * From 7e94e45296d68982d448ae57e195efcf8f66649e Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:19:00 +0000 Subject: [PATCH 11/26] memory: Store user data pointer in the IOMMU notifiers This will help developers of ATS-capable devices to track a state. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-9-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/system/memory.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/system/memory.h b/include/system/memory.h index fbbf4cf911..fc35a0dcad 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -183,6 +183,7 @@ struct IOMMUNotifier { hwaddr start; hwaddr end; int iommu_idx; + void *opaque; QLIST_ENTRY(IOMMUNotifier) node; }; typedef struct IOMMUNotifier IOMMUNotifier; From a849ff5d6fa9d263beaecd6421fff8e21d2591c8 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:19:01 +0000 Subject: [PATCH 12/26] pci: Add a pci-level initialization function for IOMMU notifiers This is meant to be used by ATS-capable devices. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-10-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 17 +++++++++++++++++ include/hw/pci/pci.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index fc4954ac81..dfa5a0259e 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2939,6 +2939,23 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) return &address_space_memory; } +int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, + IOMMUNotify fn, void *opaque) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->init_iotlb_notifier) { + iommu_bus->iommu_ops->init_iotlb_notifier(bus, iommu_bus->iommu_opaque, + devfn, n, fn, opaque); + return 0; + } + + return -ENODEV; +} + bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, Error **errp) { diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index d67ffe12db..f3016fd76f 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -442,6 +442,26 @@ typedef struct PCIIOMMUOps { */ void (*get_iotlb_info)(void *opaque, uint8_t *addr_width, uint32_t *min_page_size); + /** + * @init_iotlb_notifier: initialize an IOMMU notifier. + * + * Optional callback. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @n: the notifier to be initialized. + * + * @fn: the callback to be installed. + * + * @user_opaque: a user pointer that can be used to track a state. + */ + void (*init_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + IOMMUNotifier *n, IOMMUNotify fn, + void *user_opaque); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); @@ -462,6 +482,19 @@ void pci_device_unset_iommu_device(PCIDevice *dev); int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, uint32_t *min_page_size); +/** + * pci_iommu_init_iotlb_notifier: initialize an IOMMU notifier. + * + * This function is used by devices before registering an IOTLB notifier. + * + * @dev: the device. + * @n: the notifier to be initialized. + * @fn: the callback to be installed. + * @opaque: a user pointer that can be used to track a state. + */ +int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, + IOMMUNotify fn, void *opaque); + /** * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus * From e9b457500adb023229a08ece3a8d7f5866dd360e Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:19:03 +0000 Subject: [PATCH 13/26] pci: Add a pci-level API for ATS Devices implementing ATS can send translation requests using pci_ats_request_translation. The invalidation events are sent back to the device using the iommu notifier managed with pci_iommu_register_iotlb_notifier / pci_iommu_unregister_iotlb_notifier. Signed-off-by: Clement Mathieu--Drif Co-authored-by: Ethan Milon Message-Id: <20250520071823.764266-11-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 81 ++++++++++++++++++++++++++++ include/hw/pci/pci.h | 126 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index dfa5a0259e..0c63cb4bbe 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2987,6 +2987,87 @@ void pci_device_unset_iommu_device(PCIDevice *dev) } } +ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + if (result_length == 0) { + return -ENOSPC; + } + + if (!pcie_ats_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->ats_request_translation) { + return iommu_bus->iommu_ops->ats_request_translation(bus, + iommu_bus->iommu_opaque, + devfn, pasid, priv_req, + exec_req, addr, length, + no_write, result, + result_length, err_count); + } + + return -ENODEV; +} + +int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->register_iotlb_notifier) { + iommu_bus->iommu_ops->register_iotlb_notifier(bus, + iommu_bus->iommu_opaque, devfn, + pasid, n); + return 0; + } + + return -ENODEV; +} + +int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->unregister_iotlb_notifier) { + iommu_bus->iommu_ops->unregister_iotlb_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid, n); + return 0; + } + + return -ENODEV; +} + int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, uint32_t *min_page_size) { diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index f3016fd76f..5d72607ed5 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -462,6 +462,80 @@ typedef struct PCIIOMMUOps { void (*init_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, IOMMUNotifier *n, IOMMUNotify fn, void *user_opaque); + /** + * @register_iotlb_notifier: setup an IOTLB invalidation notifier. + * + * Callback required if devices are allowed to cache translations. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to watch. + * + * @n: the notifier to register. + */ + void (*register_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUNotifier *n); + /** + * @unregister_iotlb_notifier: remove an IOTLB invalidation notifier. + * + * Callback required if devices are allowed to cache translations. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to stop watching. + * + * @n: the notifier to unregister. + */ + void (*unregister_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUNotifier *n); + /** + * @ats_request_translation: issue an ATS request. + * + * Callback required if devices are allowed to use the address + * translation service. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to use for the request. + * + * @priv_req: privileged mode bit (PASID TLP). + * + * @exec_req: execute request bit (PASID TLP). + * + * @addr: start address of the memory range to be translated. + * + * @length: length of the memory range in bytes. + * + * @no_write: request a read-only translation (if supported). + * + * @result: buffer in which the TLB entries will be stored. + * + * @result_length: result buffer length. + * + * @err_count: number of untranslated subregions. + * + * Returns: the number of translations stored in the result buffer, or + * -ENOMEM if the buffer is not large enough. + */ + ssize_t (*ats_request_translation)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, + size_t length, bool no_write, + IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); @@ -495,6 +569,58 @@ int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, IOMMUNotify fn, void *opaque); +/** + * pci_ats_request_translation: perform an ATS request. + * + * Returns the number of translations stored in @result in case of success, + * a negative error code otherwise. + * -ENOMEM is returned when the result buffer is not large enough to store + * all the translations. + * + * @dev: the ATS-capable PCI device. + * @pasid: the pasid of the address space in which the translation will be done. + * @priv_req: privileged mode bit (PASID TLP). + * @exec_req: execute request bit (PASID TLP). + * @addr: start address of the memory range to be translated. + * @length: length of the memory range in bytes. + * @no_write: request a read-only translation (if supported). + * @result: buffer in which the TLB entries will be stored. + * @result_length: result buffer length. + * @err_count: number of untranslated subregions. + */ +ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count); + +/** + * pci_iommu_register_iotlb_notifier: register a notifier for changes to + * IOMMU translation entries in a specific address space. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to get notified. + * @pasid: the pasid of the address space to track. + * @n: the notifier to register. + */ +int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n); + +/** + * pci_iommu_unregister_iotlb_notifier: unregister a notifier that has been + * registerd with pci_iommu_register_iotlb_notifier. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to stop notifications. + * @pasid: the pasid of the address space to stop tracking. + * @n: the notifier to unregister. + */ +int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n); + /** * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus * From f0f37daf8e67c7208641aec5e238197279ca7331 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:19:04 +0000 Subject: [PATCH 14/26] pci: Add a PCI-level API for PRI A device can send a PRI request to the IOMMU using pci_pri_request_page. The PRI response is sent back using the notifier managed with pci_pri_register_notifier and pci_pri_unregister_notifier. Signed-off-by: Clement Mathieu--Drif Co-authored-by: Ethan Milon Message-Id: <20250520071823.764266-12-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 66 ++++++++++++++++++++++ include/hw/pci/pci.h | 130 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 0c63cb4bbe..c6b5768f3a 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2987,6 +2987,72 @@ void pci_device_unset_iommu_device(PCIDevice *dev) } } +int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, bool lpig, + uint16_t prgi, bool is_read, bool is_write) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + if (!pcie_pri_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_request_page) { + return iommu_bus->iommu_ops->pri_request_page(bus, + iommu_bus->iommu_opaque, + devfn, pasid, priv_req, + exec_req, addr, lpig, prgi, + is_read, is_write); + } + + return -ENODEV; +} + +int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUPRINotifier *notifier) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_register_notifier) { + iommu_bus->iommu_ops->pri_register_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid, notifier); + return 0; + } + + return -ENODEV; +} + +void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_unregister_notifier) { + iommu_bus->iommu_ops->pri_unregister_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid); + } +} + ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, bool priv_req, bool exec_req, hwaddr addr, size_t length, diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 5d72607ed5..a6854dad2b 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -375,6 +375,28 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range); void pci_device_deassert_intx(PCIDevice *dev); +/* Page Request Interface */ +typedef enum { + IOMMU_PRI_RESP_SUCCESS, + IOMMU_PRI_RESP_INVALID_REQUEST, + IOMMU_PRI_RESP_FAILURE, +} IOMMUPRIResponseCode; + +typedef struct IOMMUPRIResponse { + IOMMUPRIResponseCode response_code; + uint16_t prgi; +} IOMMUPRIResponse; + +struct IOMMUPRINotifier; + +typedef void (*IOMMUPRINotify)(struct IOMMUPRINotifier *notifier, + IOMMUPRIResponse *response); + +typedef struct IOMMUPRINotifier { + IOMMUPRINotify notify; +} IOMMUPRINotifier; + +#define PCI_PRI_PRGI_MASK 0x1ffU /** * struct PCIIOMMUOps: callbacks structure for specific IOMMU handlers @@ -536,6 +558,72 @@ typedef struct PCIIOMMUOps { IOMMUTLBEntry *result, size_t result_length, uint32_t *err_count); + /** + * @pri_register_notifier: setup the PRI completion callback. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to track. + * + * @notifier: the notifier to register. + */ + void (*pri_register_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUPRINotifier *notifier); + /** + * @pri_unregister_notifier: remove the PRI completion callback. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to stop tracking. + */ + void (*pri_unregister_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid); + /** + * @pri_request_page: issue a PRI request. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to use for the request. + * + * @priv_req: privileged mode bit (PASID TLP). + * + * @exec_req: execute request bit (PASID TLP). + * + * @addr: untranslated address of the requested page. + * + * @lpig: last page in group. + * + * @prgi: page request group index. + * + * @is_read: request read access. + * + * @is_write: request write access. + */ + int (*pri_request_page)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, bool priv_req, bool exec_req, + hwaddr addr, bool lpig, uint16_t prgi, bool is_read, + bool is_write); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); @@ -595,6 +683,48 @@ ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, size_t result_length, uint32_t *err_count); +/** + * pci_pri_request_page: perform a PRI request. + * + * Returns 0 if the PRI request has been sent to the guest OS, + * an error code otherwise. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space in which the translation will be done. + * @priv_req: privileged mode bit (PASID TLP). + * @exec_req: execute request bit (PASID TLP). + * @addr: untranslated address of the requested page. + * @lpig: last page in group. + * @prgi: page request group index. + * @is_read: request read access. + * @is_write: request write access. + */ +int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, bool lpig, + uint16_t prgi, bool is_read, bool is_write); + +/** + * pci_pri_register_notifier: register the PRI callback for a given address + * space. + * + * Returns 0 on success, an error code otherwise. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space to track. + * @notifier: the notifier to register. + */ +int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUPRINotifier *notifier); + +/** + * pci_pri_unregister_notifier: remove the PRI callback from a given address + * space. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space to stop tracking. + */ +void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid); + /** * pci_iommu_register_iotlb_notifier: register a notifier for changes to * IOMMU translation entries in a specific address space. From 75d4680c55498d6cf4f89b31e52d97f90d7fc46e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 20 May 2025 21:01:51 +0800 Subject: [PATCH 15/26] uefi-test-tools:: Add LoongArch64 support Add support to build bios-tables-test iso image for LoongArch system. Signed-off-by: Bibo Mao Acked-by: Gerd Hoffmann Message-Id: <20250520130158.767083-2-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/uefi-test-tools/Makefile | 5 +++-- .../UefiTestToolsPkg/UefiTestToolsPkg.dsc | 6 +++++- tests/uefi-test-tools/uefi-test-build.config | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile index f4eaebd8ff..8ee6fb3571 100644 --- a/tests/uefi-test-tools/Makefile +++ b/tests/uefi-test-tools/Makefile @@ -12,7 +12,7 @@ edk2_dir := ../../roms/edk2 images_dir := ../data/uefi-boot-images -emulation_targets := arm aarch64 i386 x86_64 riscv64 +emulation_targets := arm aarch64 i386 x86_64 riscv64 loongarch64 uefi_binaries := bios-tables-test intermediate_suffixes := .efi .fat .iso.raw @@ -56,7 +56,8 @@ Build/%.iso.raw: Build/%.fat # stripped from, the argument. map_arm_to_uefi = $(subst arm,ARM,$(1)) map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1))) -map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_aarch64_to_uefi,$(1))) +map_loongarch64_to_uefi = $(subst loongarch64,LOONGARCH64,$(call map_aarch64_to_uefi,$(1))) +map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_loongarch64_to_uefi,$(1))) map_i386_to_uefi = $(subst i386,IA32,$(call map_riscv64_to_uefi,$(1))) map_x86_64_to_uefi = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1))) map_to_uefi = $(subst .,,$(call map_x86_64_to_uefi,$(1))) diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc index 0902fd3c73..facf8df1fa 100644 --- a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc +++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc @@ -19,7 +19,7 @@ PLATFORM_VERSION = 0.1 PLATFORM_NAME = UefiTestTools SKUID_IDENTIFIER = DEFAULT - SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64 + SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64|LOONGARCH64 BUILD_TARGETS = DEBUG [BuildOptions.IA32] @@ -65,6 +65,10 @@ [LibraryClasses.RISCV64] BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf +[LibraryClasses.LOONGARCH64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf + [PcdsFixedAtBuild] gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F diff --git a/tests/uefi-test-tools/uefi-test-build.config b/tests/uefi-test-tools/uefi-test-build.config index a4c61fc97a..8bf4826634 100644 --- a/tests/uefi-test-tools/uefi-test-build.config +++ b/tests/uefi-test-tools/uefi-test-build.config @@ -21,6 +21,16 @@ dest = ./Build arch = AARCH64 cpy1 = AARCH64/BiosTablesTest.efi bios-tables-test.aarch64.efi +#################################################################################### +# loongarch64 + +[build.loongarch64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = LOONGARCH64 +cpy1 = LOONGARCH64/BiosTablesTest.efi bios-tables-test.loongarch64.efi + #################################################################################### # riscv64 From 0265723eba8a612d8a94d190cef988cf8d616862 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 20 May 2025 21:01:52 +0800 Subject: [PATCH 16/26] tests/data/uefi-boot-images: Add ISO image for LoongArch system To test ACPI tables, edk2 needs to be booted with a disk image having EFI partition. This image is created using UefiTestToolsPkg. The image is generated with the following command: make -f tests/uefi-test-tools/Makefile Signed-off-by: Bibo Mao Acked-by: Gerd Hoffmann Message-Id: <20250520130158.767083-3-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- .../bios-tables-test.loongarch64.iso.qcow2 | Bin 0 -> 12800 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 diff --git a/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 new file mode 100644 index 0000000000000000000000000000000000000000..18daee0c52fcc703a0645dbcdc598aa1f101d96b GIT binary patch literal 12800 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 q*e2oa Date: Tue, 20 May 2025 21:01:53 +0800 Subject: [PATCH 17/26] tests/qtest/bios-tables-test: Use MiB macro rather hardcode value Replace 1024 * 1024 with MiB macro. Signed-off-by: Bibo Mao Message-Id: <20250520130158.767083-4-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 0a333ec435..0b2bdf9d0d 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1622,7 +1622,7 @@ static void test_acpi_aarch64_virt_tcg_memhp(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 256ULL * 1024 * 1024, + .scan_len = 256ULL * MiB, }; data.variant = ".memhp"; @@ -1717,7 +1717,7 @@ static void test_acpi_riscv64_virt_tcg_numamem(void) .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", .ram_start = 0x80000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".numamem"; @@ -1743,7 +1743,7 @@ static void test_acpi_aarch64_virt_tcg_numamem(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".numamem"; @@ -1765,7 +1765,7 @@ static void test_acpi_aarch64_virt_tcg_pxb(void) .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; /* * While using -cdrom, the cdrom would auto plugged into pxb-pcie, @@ -1841,7 +1841,7 @@ static void test_acpi_aarch64_virt_tcg_acpi_hmat(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".acpihmatvirt"; @@ -2095,7 +2095,7 @@ static void test_acpi_riscv64_virt_tcg(void) .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", .ram_start = 0x80000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; /* @@ -2117,7 +2117,7 @@ static void test_acpi_aarch64_virt_tcg(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.smbios_cpu_max_speed = 2900; @@ -2138,7 +2138,7 @@ static void test_acpi_aarch64_virt_tcg_topology(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; test_acpi_one("-cpu cortex-a57 " @@ -2223,7 +2223,7 @@ static void test_acpi_aarch64_virt_viot(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; test_acpi_one("-cpu cortex-a57 " @@ -2407,7 +2407,7 @@ static void test_acpi_aarch64_virt_oem_fields(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; char *args; From a9403bfcd93025df7b1924d0cf34fbc408955b33 Mon Sep 17 00:00:00 2001 From: Huaitong Han Date: Thu, 22 May 2025 18:05:48 +0800 Subject: [PATCH 18/26] vhost: Don't set vring call if guest notifier is unused The vring call fd is set even when the guest does not use MSI-X (e.g., in the case of virtio PMD), leading to unnecessary CPU overhead for processing interrupts. The commit 96a3d98d2c("vhost: don't set vring call if no vector") optimized the case where MSI-X is enabled but the queue vector is unset. However, there's an additional case where the guest uses INTx and the INTx_DISABLED bit in the PCI config is set, meaning that no interrupt notifier will actually be used. In such cases, the vring call fd should also be cleared to avoid redundant interrupt handling. Fixes: 96a3d98d2c("vhost: don't set vring call if no vector") Reported-by: Zhiyuan Yuan Signed-off-by: Jidong Xia Signed-off-by: Huaitong Han Message-Id: <20250522100548.212740-1-hanht2@chinatelecom.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 2 +- hw/virtio/virtio-pci.c | 7 ++++++- include/hw/pci/pci.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c6b5768f3a..9b4bf48439 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1730,7 +1730,7 @@ static void pci_update_mappings(PCIDevice *d) pci_update_vga(d); } -static inline int pci_irq_disabled(PCIDevice *d) +int pci_irq_disabled(PCIDevice *d) { return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE; } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 9b48aa8c3e..7e309d1d49 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1215,7 +1215,12 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, static bool virtio_pci_query_guest_notifiers(DeviceState *d) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - return msix_enabled(&proxy->pci_dev); + + if (msix_enabled(&proxy->pci_dev)) { + return true; + } else { + return pci_irq_disabled(&proxy->pci_dev); + } } static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index a6854dad2b..35d59d7672 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -983,6 +983,7 @@ void lsi53c8xx_handle_legacy_cmdline(DeviceState *lsi_dev); qemu_irq pci_allocate_irq(PCIDevice *pci_dev); void pci_set_irq(PCIDevice *pci_dev, int level); +int pci_irq_disabled(PCIDevice *d); static inline void pci_irq_assert(PCIDevice *pci_dev) { From 55cf1d4f10160e37440d9e727a160ac1141bbb53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:33 -0400 Subject: [PATCH 19/26] vdpa: check for iova tree initialized at net_client_start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To map the guest memory while it is migrating we need to create the iova_tree, as long as the destination uses x-svq=on. Checking to not override it. The function vhost_vdpa_net_client_stop clear it if the device is stopped. If the guest starts the device again, the iova tree is recreated by vhost_vdpa_net_data_start_first or vhost_vdpa_net_cvq_start if needed, so old behavior is kept. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-2-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 7ca8b46eee..decb826868 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -366,7 +366,9 @@ static void vhost_vdpa_net_data_start_first(VhostVDPAState *s) migration_add_notifier(&s->migration_state, vdpa_net_migration_state_notifier); - if (v->shadow_vqs_enabled) { + + /* iova_tree may be initialized by vhost_vdpa_net_load_setup */ + if (v->shadow_vqs_enabled && !v->shared->iova_tree) { v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first, v->shared->iova_range.last); } From 3312e6c8c9aa8f32019f14c74d209db17b9306eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:34 -0400 Subject: [PATCH 20/26] vdpa: reorder vhost_vdpa_set_backend_cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used directly by vhost_vdpa_init. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-3-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 60 +++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 1ab2c11fa8..6b242ca56a 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -594,6 +594,36 @@ static void vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v) v->shadow_vqs = g_steal_pointer(&shadow_vqs); } +static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + + uint64_t features; + uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 | + 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH | + 0x1ULL << VHOST_BACKEND_F_IOTLB_ASID | + 0x1ULL << VHOST_BACKEND_F_SUSPEND; + int r; + + if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) { + return -EFAULT; + } + + features &= f; + + if (vhost_vdpa_first_dev(dev)) { + r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); + if (r) { + return -EFAULT; + } + } + + dev->backend_cap = features; + v->shared->backend_cap = features; + + return 0; +} + static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) { struct vhost_vdpa *v = opaque; @@ -841,36 +871,6 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev, return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK); } -static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) -{ - struct vhost_vdpa *v = dev->opaque; - - uint64_t features; - uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 | - 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH | - 0x1ULL << VHOST_BACKEND_F_IOTLB_ASID | - 0x1ULL << VHOST_BACKEND_F_SUSPEND; - int r; - - if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) { - return -EFAULT; - } - - features &= f; - - if (vhost_vdpa_first_dev(dev)) { - r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); - if (r) { - return -EFAULT; - } - } - - dev->backend_cap = features; - v->shared->backend_cap = features; - - return 0; -} - static int vhost_vdpa_get_device_id(struct vhost_dev *dev, uint32_t *device_id) { From 32f0c7ce4c948b2e275dca3cf7fa2a00677b9c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:35 -0400 Subject: [PATCH 21/26] vdpa: set backend capabilities at vhost_vdpa_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The backend does not reset them until the vdpa file descriptor is closed so there is no harm in doing it only once. This allows the destination of a live migration to premap memory in batches, using VHOST_BACKEND_F_IOTLB_BATCH. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-4-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 6b242ca56a..e9826ede2c 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -634,6 +634,12 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) v->dev = dev; dev->opaque = opaque ; v->shared->listener = vhost_vdpa_memory_listener; + + ret = vhost_vdpa_set_backend_cap(dev); + if (unlikely(ret != 0)) { + return ret; + } + vhost_vdpa_init_svq(dev, v); error_propagate(&dev->migration_blocker, v->migration_blocker); @@ -1563,7 +1569,6 @@ const VhostOps vdpa_ops = { .vhost_set_vring_kick = vhost_vdpa_set_vring_kick, .vhost_set_vring_call = vhost_vdpa_set_vring_call, .vhost_get_features = vhost_vdpa_get_features, - .vhost_set_backend_cap = vhost_vdpa_set_backend_cap, .vhost_set_owner = vhost_vdpa_set_owner, .vhost_set_vring_endian = NULL, .vhost_backend_memslots_limit = vhost_vdpa_memslots_limit, From be2e5fbefa7c1f8243bf6db2486b950b1ab19b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:36 -0400 Subject: [PATCH 22/26] vdpa: add listener_registered MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check if the listener has been registered or not, so it needs to be registered again at start. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-5-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 7 ++++++- include/hw/virtio/vhost-vdpa.h | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index e9826ede2c..450f68f117 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -1379,7 +1379,10 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) "IOMMU and try again"); return -1; } - memory_listener_register(&v->shared->listener, dev->vdev->dma_as); + if (!v->shared->listener_registered) { + memory_listener_register(&v->shared->listener, dev->vdev->dma_as); + v->shared->listener_registered = true; + } return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); } @@ -1399,6 +1402,8 @@ static void vhost_vdpa_reset_status(struct vhost_dev *dev) vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); memory_listener_unregister(&v->shared->listener); + v->shared->listener_registered = false; + } static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index 0a9575b469..221840987e 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -51,6 +51,12 @@ typedef struct vhost_vdpa_shared { bool iotlb_batch_begin_sent; + /* + * The memory listener has been registered, so DMA maps have been sent to + * the device. + */ + bool listener_registered; + /* Vdpa must send shadow addresses as IOTLB key for data queues, not GPA */ bool shadow_data; From 9344dcbd004f951155c020d01c1bdc881e0451c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:37 -0400 Subject: [PATCH 23/26] vdpa: reorder listener assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit f6fe3e333f ("vdpa: move memory listener to vhost_vdpa_shared") this piece of code repeatedly assign shared->listener members. This was not a problem as it was not used until device start. However next patches move the listener registration to this vhost_vdpa_init function. When the listener is registered it is added to an embedded linked list, so setting its members again will cause memory corruption to the linked list node. Do the right thing and only set it in the first vdpa device. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-6-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 450f68f117..de834f2ebd 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -633,7 +633,6 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) v->dev = dev; dev->opaque = opaque ; - v->shared->listener = vhost_vdpa_memory_listener; ret = vhost_vdpa_set_backend_cap(dev); if (unlikely(ret != 0)) { @@ -675,6 +674,7 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); + v->shared->listener = vhost_vdpa_memory_listener; return 0; } From a400720365ea86602044b78dd8654911d0fe3977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:38 -0400 Subject: [PATCH 24/26] vdpa: move iova_tree allocation to net_vhost_vdpa_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are moving to keep the mapping through all the vdpa device life instead of resetting it at VirtIO reset, we need to move all its dependencies to the initialization too. In particular devices with x-svq=on need a valid iova_tree from the beginning. Simplify the code also consolidating the two creation points: the first data vq in case of SVQ active and CVQ start in case only CVQ uses it. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Suggested-by: Si-Wei Liu Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-7-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/vhost-vdpa.h | 16 ++++++++++++++- net/vhost-vdpa.c | 36 +++------------------------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index 221840987e..449bf5c840 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -43,7 +43,21 @@ typedef struct vhost_vdpa_shared { struct vhost_vdpa_iova_range iova_range; QLIST_HEAD(, vdpa_iommu) iommu_list; - /* IOVA mapping used by the Shadow Virtqueue */ + /* + * IOVA mapping used by the Shadow Virtqueue + * + * It is shared among all ASID for simplicity, whether CVQ shares ASID with + * guest or not: + * - Memory listener need access to guest's memory addresses allocated in + * the IOVA tree. + * - There should be plenty of IOVA address space for both ASID not to + * worry about collisions between them. Guest's translations are still + * validated with virtio virtqueue_pop so there is no risk for the guest + * to access memory that it shouldn't. + * + * To allocate a iova tree per ASID is doable but it complicates the code + * and it is not worth it for the moment. + */ VhostIOVATree *iova_tree; /* Copy of backend features */ diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index decb826868..58d738945d 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -235,6 +235,7 @@ static void vhost_vdpa_cleanup(NetClientState *nc) return; } qemu_close(s->vhost_vdpa.shared->device_fd); + g_clear_pointer(&s->vhost_vdpa.shared->iova_tree, vhost_iova_tree_delete); g_free(s->vhost_vdpa.shared); } @@ -362,16 +363,8 @@ static int vdpa_net_migration_state_notifier(NotifierWithReturn *notifier, static void vhost_vdpa_net_data_start_first(VhostVDPAState *s) { - struct vhost_vdpa *v = &s->vhost_vdpa; - migration_add_notifier(&s->migration_state, vdpa_net_migration_state_notifier); - - /* iova_tree may be initialized by vhost_vdpa_net_load_setup */ - if (v->shadow_vqs_enabled && !v->shared->iova_tree) { - v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first, - v->shared->iova_range.last); - } } static int vhost_vdpa_net_data_start(NetClientState *nc) @@ -418,19 +411,12 @@ static int vhost_vdpa_net_data_load(NetClientState *nc) static void vhost_vdpa_net_client_stop(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); - struct vhost_dev *dev; assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); if (s->vhost_vdpa.index == 0) { migration_remove_notifier(&s->migration_state); } - - dev = s->vhost_vdpa.dev; - if (dev->vq_index + dev->nvqs == dev->vq_index_end) { - g_clear_pointer(&s->vhost_vdpa.shared->iova_tree, - vhost_iova_tree_delete); - } } static NetClientInfo net_vhost_vdpa_info = { @@ -602,24 +588,6 @@ out: return 0; } - /* - * If other vhost_vdpa already have an iova_tree, reuse it for simplicity, - * whether CVQ shares ASID with guest or not, because: - * - Memory listener need access to guest's memory addresses allocated in - * the IOVA tree. - * - There should be plenty of IOVA address space for both ASID not to - * worry about collisions between them. Guest's translations are still - * validated with virtio virtqueue_pop so there is no risk for the guest - * to access memory that it shouldn't. - * - * To allocate a iova tree per ASID is doable but it complicates the code - * and it is not worth it for the moment. - */ - if (!v->shared->iova_tree) { - v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first, - v->shared->iova_range.last); - } - r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer, vhost_vdpa_net_cvq_cmd_page_len(), false); if (unlikely(r < 0)) { @@ -1728,6 +1696,8 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer, s->vhost_vdpa.shared->device_fd = vdpa_device_fd; s->vhost_vdpa.shared->iova_range = iova_range; s->vhost_vdpa.shared->shadow_data = svq; + s->vhost_vdpa.shared->iova_tree = vhost_iova_tree_new(iova_range.first, + iova_range.last); } else if (!is_datapath) { s->cvq_cmd_out_buffer = mmap(NULL, vhost_vdpa_net_cvq_cmd_page_len(), PROT_READ | PROT_WRITE, From 494c50dcc0995ae6eb526d2848c33cbf910ab218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:39 -0400 Subject: [PATCH 25/26] vdpa: move memory listener register to vhost_vdpa_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current memory operations like pinning may take a lot of time at the destination. Currently they are done after the source of the migration is stopped, and before the workload is resumed at the destination. This is a period where neigher traffic can flow, nor the VM workload can continue (downtime). We can do better as we know the memory layout of the guest RAM at the destination from the moment that all devices are initializaed. So moving that operation allows QEMU to communicate the kernel the maps while the workload is still running in the source, so Linux can start mapping them. As a small drawback, there is a time in the initialization where QEMU cannot respond to QMP etc. By some testing, this time is about 0.2seconds. This may be further reduced (or increased) depending on the vdpa driver and the platform hardware, and it is dominated by the cost of memory pinning. This matches the time that we move out of the called downtime window. The downtime is measured as the elapsed trace time between the last vhost_vdpa_suspend on the source and the last vhost_vdpa_set_vring_enable_one on the destination. In other words, from "guest CPUs freeze" to the instant the final Rx/Tx queue-pair is able to start moving data. Using ConnectX-6 Dx (MLX5) NICs in vhost-vDPA mode with 8 queue-pairs, the series reduces guest-visible downtime during back-to-back live migrations by more than half: - 39G VM: 4.72s -> 2.09s (-2.63s, ~56% improvement) - 128G VM: 14.72s -> 5.83s (-8.89s, ~60% improvement) Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-8-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index de834f2ebd..e20da95f30 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -894,8 +894,14 @@ static int vhost_vdpa_reset_device(struct vhost_dev *dev) ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status); trace_vhost_vdpa_reset_device(dev); + if (ret) { + return ret; + } + + memory_listener_unregister(&v->shared->listener); + v->shared->listener_registered = false; v->suspended = false; - return ret; + return 0; } static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx) @@ -1379,6 +1385,11 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) "IOMMU and try again"); return -1; } + if (v->shared->listener_registered && + dev->vdev->dma_as != v->shared->listener.address_space) { + memory_listener_unregister(&v->shared->listener); + v->shared->listener_registered = false; + } if (!v->shared->listener_registered) { memory_listener_register(&v->shared->listener, dev->vdev->dma_as); v->shared->listener_registered = true; @@ -1392,8 +1403,6 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) static void vhost_vdpa_reset_status(struct vhost_dev *dev) { - struct vhost_vdpa *v = dev->opaque; - if (!vhost_vdpa_last_dev(dev)) { return; } @@ -1401,9 +1410,6 @@ static void vhost_vdpa_reset_status(struct vhost_dev *dev) vhost_vdpa_reset_device(dev); vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); - memory_listener_unregister(&v->shared->listener); - v->shared->listener_registered = false; - } static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, @@ -1537,12 +1543,27 @@ static int vhost_vdpa_get_features(struct vhost_dev *dev, static int vhost_vdpa_set_owner(struct vhost_dev *dev) { + int r; + struct vhost_vdpa *v; + if (!vhost_vdpa_first_dev(dev)) { return 0; } trace_vhost_vdpa_set_owner(dev); - return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); + r = vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); + if (unlikely(r < 0)) { + return r; + } + + /* + * Being optimistic and listening address space memory. If the device + * uses vIOMMU, it is changed at vhost_vdpa_dev_start. + */ + v = dev->opaque; + memory_listener_register(&v->shared->listener, &address_space_memory); + v->shared->listener_registered = true; + return 0; } static int vhost_vdpa_vq_get_addr(struct vhost_dev *dev, From 0b006153b7ec66505cb2d231235aa19ca5d2ce37 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 26 May 2025 22:38:20 +0200 Subject: [PATCH 26/26] hw/i386/pc_piix: Fix RTC ISA IRQ wiring of isapc machine Commit 56b1f50e3c10 ("hw/i386/pc: Wire RTC ISA IRQs in south bridges") attempted to refactor RTC IRQ wiring which was previously done in pc_basic_device_init() but forgot about the isapc machine. Fix this by wiring in the code section dedicated exclusively to the isapc machine. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2961 Fixes: 56b1f50e3c10 ("hw/i386/pc: Wire RTC ISA IRQs in south bridges") cc: qemu-stable Signed-off-by: Bernhard Beschow Reviewed-by: Mark Cave-Ayland Message-Id: <20250526203820.1853-1-shentey@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_piix.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0dce512f18..6b6359ef65 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -285,6 +285,8 @@ static void pc_init1(MachineState *machine, const char *pci_type) pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); } else { + uint32_t irq; + isa_bus = isa_bus_new(NULL, system_memory, system_io, &error_abort); isa_bus_register_input_irqs(isa_bus, x86ms->gsi); @@ -292,6 +294,9 @@ static void pc_init1(MachineState *machine, const char *pci_type) x86ms->rtc = isa_new(TYPE_MC146818_RTC); qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); i8257_dma_init(OBJECT(machine), isa_bus, 0); pcms->hpet_enabled = false;