@@ -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,
@@ -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.
@@ -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
*
@@ -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)
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(+)