[PATCH v1 5/8] pci: add a PCI-level API for PRI

CLEMENT MATHIEU--DRIF posted 8 patches 5 months, 4 weeks ago
[PATCH v1 5/8] pci: add a PCI-level API for PRI
Posted by CLEMENT MATHIEU--DRIF 5 months, 4 weeks ago
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--drif@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