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 <clement.mathieu--drif@eviden.com>
Co-authored-by: Ethan Milon <ethan.milon@eviden.com>
Message-Id: <20250520071823.764266-11-clement.mathieu--drif@eviden.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
CLEMENT MATHIEU--DRIF 2025-05-20 07:19:03 +00:00 committed by Michael S. Tsirkin
parent a849ff5d6f
commit e9b457500a
2 changed files with 207 additions and 0 deletions

View file

@ -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)
{