This patch includes the following items: * add vfio_register_notifier() for vfio notifier initialization * add new notifier flag IOMMU_NOTIFIER_SVM_PASIDT_BIND = 0x4 * add vfio_iommu_bind_pasid_tbl_notify() to link guest pasid table to host This patch doesn't register new notifier in vfio memory region listener region_add callback. The reason is as below: On VT-d, when virtual intel_iommu is exposed to guest, the vfio memory listener listens to address_space_memory. When guest Intel IOMMU driver enables address translation, vfio memory listener may switch to listen to vtd_address_space. But there is special case. If virtual intel_iommu reports ecap.PT=1 to guest and meanwhile guest Intel IOMMU driver sets "pt" mode for the assigned, vfio memory listener would keep listen to address_space_memory to make sure there is GPA->HPA mapping in pIOMMU. Thus region_add would not be triggered. While for the newly added notifier, it requires to be registered once virtual intel_iommu is exposed to guest. Signed-off-by: Liu, Yi L <yi.l.liu@xxxxxxxxxxxxxxx> --- hw/vfio/common.c | 37 +++++++++++++++++++++++------- hw/vfio/pci.c | 53 ++++++++++++++++++++++++++++++++++++++++++- include/exec/memory.h | 8 +++++++ include/hw/vfio/vfio-common.h | 5 ++++ 4 files changed, 94 insertions(+), 9 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 14473f1..e270255 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -294,6 +294,25 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section) section->offset_within_address_space & (1ULL << 63); } +VFIOGuestIOMMU *vfio_register_notifier(VFIOContainer *container, + MemoryRegion *mr, + hwaddr offset, + IOMMUNotifier *n) +{ + VFIOGuestIOMMU *giommu; + + giommu = g_malloc0(sizeof(*giommu)); + giommu->iommu = mr; + giommu->iommu_offset = offset; + giommu->container = container; + giommu->n = *n; + + QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); + memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); + + return giommu; +} + /* Called with rcu_read_lock held. */ static bool vfio_get_vaddr(IOMMUTLBEntry *iotlb, void **vaddr, bool *read_only) @@ -466,6 +485,8 @@ static void vfio_listener_region_add(MemoryListener *listener, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; + IOMMUNotifier n; + hwaddr iommu_offset; trace_vfio_listener_region_add_iommu(iova, end); /* @@ -474,21 +495,21 @@ static void vfio_listener_region_add(MemoryListener *listener, * would be the right place to wire that up (tell the KVM * device emulation the VFIO iommu handles to use). */ - giommu = g_malloc0(sizeof(*giommu)); - giommu->iommu = section->mr; - giommu->iommu_offset = section->offset_within_address_space - - section->offset_within_region; - giommu->container = container; + iommu_offset = section->offset_within_address_space - + section->offset_within_region; llend = int128_add(int128_make64(section->offset_within_region), section->size); llend = int128_sub(llend, int128_one()); - iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, + iommu_notifier_init(&n, vfio_iommu_map_notify, IOMMU_NOTIFIER_ALL, section->offset_within_region, int128_get64(llend)); - QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); - memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); + giommu = vfio_register_notifier(container, + section->mr, + iommu_offset, + &n); + memory_region_iommu_replay(giommu->iommu, &giommu->n, false); return; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 332f41d..9e13472 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2594,11 +2594,38 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } +static void vfio_iommu_bind_pasid_tbl_notify(IOMMUNotifier *n, void *data) +{ + VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); + VFIOContainer *container = giommu->container; + IOMMUNotifierData *iommu_data = (IOMMUNotifierData *) data; + struct vfio_device_svm *vfio_svm; + int argsz; + + argsz = sizeof(*vfio_svm) + iommu_data->payload_size; + vfio_svm = g_malloc0(argsz); + vfio_svm->argsz = argsz; + vfio_svm->flags = VFIO_SVM_BIND_PASIDTBL; + vfio_svm->length = iommu_data->payload_size; + memcpy(&vfio_svm->data, iommu_data->payload, + iommu_data->payload_size); + + rcu_read_lock(); + if (ioctl(container->fd, VFIO_IOMMU_SVM_BIND_TASK, vfio_svm) != 0) { + error_report("vfio_iommu_bind_pasid_tbl_notify:" + " bind failed, contanier: %p", container); + } + rcu_read_unlock(); + g_free(vfio_svm); +} + static void vfio_realize(PCIDevice *pdev, Error **errp) { VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); VFIODevice *vbasedev_iter; VFIOGroup *group; + AddressSpace *as; + MemoryRegion *subregion; char *tmp, group_path[PATH_MAX], *group_name; Error *err = NULL; ssize_t len; @@ -2650,7 +2677,8 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) trace_vfio_realize(vdev->vbasedev.name, groupid); - group = vfio_get_group(groupid, pci_device_iommu_address_space(pdev), errp); + as = pci_device_iommu_address_space(pdev); + group = vfio_get_group(groupid, as, errp); if (!group) { goto error; } @@ -2833,6 +2861,29 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_register_req_notifier(vdev); vfio_setup_resetfn_quirk(vdev); + /* Check if vIOMMU exists */ + QTAILQ_FOREACH(subregion, &as->root->subregions, subregions_link) { + if (memory_region_is_iommu(subregion)) { + IOMMUNotifier n1; + + /* + FIXME: current iommu notifier is actually designed for + IOMMUTLB MAP/UNMAP. However, vIOMMU emulator may need + notifiers other than MAP/UNMAP, so it'll be better to + split the non-IOMMUTLB notifier from the current IOMMUTLB + notifier framewrok. + */ + iommu_notifier_init(&n1, vfio_iommu_bind_pasid_tbl_notify, + IOMMU_NOTIFIER_SVM_PASIDT_BIND, + 0, + 0); + vfio_register_notifier(group->container, + subregion, + 0, + &n1); + } + } + return; out_teardown: diff --git a/include/exec/memory.h b/include/exec/memory.h index 1faca3b..d2f24cc 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -65,6 +65,12 @@ struct IOMMUTLBEntry { IOMMUAccessFlags perm; }; +struct IOMMUNotifierData { + uint64_t payload_size; + uint8_t *payload; +}; +typedef struct IOMMUNotifierData IOMMUNotifierData; + /* * Bitmap for different IOMMUNotifier capabilities. Each notifier can * register with one or multiple IOMMU Notifier capability bit(s). @@ -75,6 +81,8 @@ typedef enum { IOMMU_NOTIFIER_UNMAP = 0x1, /* Notify entry changes (newly created entries) */ IOMMU_NOTIFIER_MAP = 0x2, + /* Notify PASID Table Binding */ + IOMMU_NOTIFIER_SVM_PASIDT_BIND = 0x4, } IOMMUNotifierFlag; #define IOMMU_NOTIFIER_ALL (IOMMU_NOTIFIER_MAP | IOMMU_NOTIFIER_UNMAP) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index c582de1..195795c 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -160,6 +160,11 @@ void vfio_put_group(VFIOGroup *group); int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vbasedev, Error **errp); +VFIOGuestIOMMU *vfio_register_notifier(VFIOContainer *container, + MemoryRegion *mr, + hwaddr offset, + IOMMUNotifier *n); + extern const MemoryRegionOps vfio_region_ops; extern QLIST_HEAD(vfio_group_head, VFIOGroup) vfio_group_list; extern QLIST_HEAD(vfio_as_head, VFIOAddressSpace) vfio_address_spaces; -- 1.9.1