A device can send a PRI request to the IOMMU using pci_pri_request_page_pasid. The PRI response is sent back using the notifier managed with pci_pri_register_notifier and pci_pri_unregister_notifier.
Signed-off-by: Clément Mathieu--Drif <clement.mathieu--d...@eviden.com> --- hw/pci/pci.c | 37 ++++++++++++++++++++++++++++++++ include/exec/memory.h | 35 +++++++++++++++++++++++++++++++ include/hw/pci/pci.h | 45 +++++++++++++++++++++++++++++++++++++++ system/memory.c | 49 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 10b0708130..dd854fc18f 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2833,6 +2833,43 @@ void pci_device_unset_iommu_device(PCIDevice *dev) } } +int pci_pri_request_page_pasid(PCIDevice *dev, uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, bool lpig, + uint16_t prgi, bool is_read, bool is_write) +{ + IOMMUMemoryRegion *iommu_mr = pci_device_iommu_memory_region_pasid(dev, + pasid); + if (!iommu_mr || !pcie_pri_enabled(dev)) { + return -EPERM; + } + return memory_region_iommu_pri_request_page(iommu_mr, priv_req, exec_req, + addr, lpig, prgi, is_read, + is_write); +} + +int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUPRINotifier *notifier) +{ + IOMMUMemoryRegion *iommu_mr = pci_device_iommu_memory_region_pasid(dev, + pasid); + if (!iommu_mr || !pcie_pri_enabled(dev)) { + return -EPERM; + } + return memory_region_register_iommu_pri_notifier(MEMORY_REGION(iommu_mr), + notifier); +} + +int pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid) +{ + IOMMUMemoryRegion *iommu_mr = pci_device_iommu_memory_region_pasid(dev, + pasid); + if (!iommu_mr || !pcie_pri_enabled(dev)) { + return -EPERM; + } + memory_region_unregister_iommu_pri_notifier(MEMORY_REGION(iommu_mr)); + return 0; +} + ssize_t pci_ats_request_translation_pasid(PCIDevice *dev, uint32_t pasid, bool priv_req, bool exec_req, hwaddr addr, size_t length, bool no_write, diff --git a/include/exec/memory.h b/include/exec/memory.h index f4780d3920..71bdd7e64d 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1870,6 +1870,16 @@ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, int iommu_idx, IOMMUTLBEvent event); +/** + * Notify the device attached to a memory region by calling the PRI + * callback (if exists) + * + * @iommu_mr: the region in which the PRI request has been performed + * @response: the response to be forwarded to the device + */ +void memory_region_notify_pri_iommu(IOMMUMemoryRegion *iommu_mr, + IOMMUPRIResponse *response); + /** * memory_region_notify_iommu_one: notify a change in an IOMMU translation * entry to a single notifier @@ -1944,6 +1954,31 @@ ssize_t memory_region_iommu_ats_request_translation(IOMMUMemoryRegion *iommu_mr, size_t result_length, uint32_t *err_count); +/** + * Register a PRI callback in an IOMMU memory region + * + * Return 0 if the notifier has been installed, + * error code otherwise. + * An error occurs when the region already has a + * callback configured. + * + * @mr: the target iommu memory region + * @n: the notifier to be installed + */ +int memory_region_register_iommu_pri_notifier(MemoryRegion *mr, + IOMMUPRINotifier *n); + +/** + * Unregister a PRI callback from an IOMMU memory region + * + * @mr: the target iommu memory region + */ +void memory_region_unregister_iommu_pri_notifier(MemoryRegion *mr); + +int memory_region_iommu_pri_request_page(IOMMUMemoryRegion *iommu_mr, + bool priv_req, bool exec_req, + hwaddr addr, bool lpig, uint16_t prgi, + bool is_read, bool is_write); /** * memory_region_iommu_get_attr: return an IOMMU attr if get_attr() is * defined on the IOMMU. diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 8adba6af97..76a6031d8d 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -473,6 +473,51 @@ bool pci_iommu_init_iotlb_notifier(PCIDevice *dev, uint32_t pasid, IOMMUNotifier *n, IOMMUNotify fn, void* opaque); +/** + * Perform a PRI request + * + * This function is intended to be used by PCIe devices using SVM + * + * Return 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 made + * @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_pasid(PCIDevice *dev, uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, bool lpig, + uint16_t prgi, bool is_read, bool is_write); + +/** + * Register a PRI callback for a given address space + * + * Return 0 on success, an error code otherwise + * + * @dev: the PRI-capable PCI device + * @pasid: the pasid of the address space to install the callback in + * @notifier: the notifier to register + */ +int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUPRINotifier *notifier); + +/** + * Unregister a PRI callback from a given address space identified + * by a pasid. + * + * Return 0 if the callback has been unregistered, an error code otherwise + * + * @dev: the PRI-capable PCI device + * @pasid: the pasid of the address space to remove the callback from + */ +int pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid); + /** * pci_ats_request_translation_pasid: perform an ATS request * diff --git a/system/memory.c b/system/memory.c index d9d66ae2e1..105c02b686 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1765,6 +1765,7 @@ void memory_region_init_iommu(void *_iommu_mr, mr->terminates = true; /* then re-forwards */ QLIST_INIT(&iommu_mr->iommu_notify); iommu_mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE; + iommu_mr->pri_notifier = NULL; } static void memory_region_finalize(Object *obj) @@ -2025,6 +2026,45 @@ ssize_t memory_region_iommu_ats_request_translation(IOMMUMemoryRegion *iommu_mr, result_length, err_count); } +int memory_region_register_iommu_pri_notifier(MemoryRegion *mr, + IOMMUPRINotifier *n) +{ + IOMMUMemoryRegion *iommu_mr; + if (mr->alias) { + return memory_region_register_iommu_pri_notifier(mr->alias, n); + } + iommu_mr = IOMMU_MEMORY_REGION(mr); + if (iommu_mr->pri_notifier) { + return -EBUSY; + } + iommu_mr->pri_notifier = n; + return 0; +} + +void memory_region_unregister_iommu_pri_notifier(MemoryRegion *mr) +{ + IOMMUMemoryRegion *iommu_mr; + if (mr->alias) { + memory_region_unregister_iommu_pri_notifier(mr->alias); + return; + } + iommu_mr = IOMMU_MEMORY_REGION(mr); + iommu_mr->pri_notifier = NULL; +} + +int memory_region_iommu_pri_request_page(IOMMUMemoryRegion *iommu_mr, + bool priv_req, bool exec_req, + hwaddr addr, bool lpig, uint16_t prgi, + bool is_read, bool is_write) +{ + IOMMUMemoryRegionClass *imrc = memory_region_get_iommu_class_nocheck(iommu_mr); + if (!imrc->iommu_pri_request_page) { + return -ENODEV; + } + return imrc->iommu_pri_request_page(iommu_mr, addr, lpig, prgi, is_read, + is_write, exec_req, priv_req); +} + void memory_region_notify_iommu_one(IOMMUNotifier *notifier, IOMMUTLBEvent *event) { @@ -2085,6 +2125,15 @@ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, } } +void memory_region_notify_pri_iommu(IOMMUMemoryRegion *iommu_mr, + IOMMUPRIResponse *response) +{ + assert(memory_region_is_iommu(MEMORY_REGION(iommu_mr))); + if (iommu_mr->pri_notifier) { + iommu_mr->pri_notifier->notify(iommu_mr->pri_notifier, response); + } +} + int memory_region_iommu_get_attr(IOMMUMemoryRegion *iommu_mr, enum IOMMUMemoryRegionAttr attr, void *data) -- 2.45.1