Commit e57dd79b authored by Eric Auger's avatar Eric Auger Committed by Zheng Zengkai
Browse files

vfio/pci: Allow to mmap the fault queue

virt inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I401IF


CVE: NA

------------------------------

The DMA FAULT region contains the fault ring buffer.
There is benefit to let the userspace mmap this area.
Expose this mmappable area through a sparse mmap entry
and implement the mmap operation.

Signed-off-by: default avatarEric Auger <eric.auger@redhat.com>
Signed-off-by: default avatarKunkun <Jiang&lt;jiangkunkun@huawei.com>
Reviewed-by: default avatarKeqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent f7c0c57b
Loading
Loading
Loading
Loading
+58 −3
Original line number Diff line number Diff line
@@ -316,21 +316,75 @@ static void vfio_pci_dma_fault_release(struct vfio_pci_device *vdev,
	kfree(vdev->fault_pages);
}

static int vfio_pci_dma_fault_mmap(struct vfio_pci_device *vdev,
				   struct vfio_pci_region *region,
				   struct vm_area_struct *vma)
{
	u64 phys_len, req_len, pgoff, req_start;
	unsigned long long addr;
	unsigned int ret;

	phys_len = region->size;

	req_len = vma->vm_end - vma->vm_start;
	pgoff = vma->vm_pgoff &
		((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
	req_start = pgoff << PAGE_SHIFT;

	/* only the second page of the producer fault region is mmappable */
	if (req_start < PAGE_SIZE)
		return -EINVAL;

	if (req_start + req_len > phys_len)
		return -EINVAL;

	addr = virt_to_phys(vdev->fault_pages);
	vma->vm_private_data = vdev;
	vma->vm_pgoff = (addr >> PAGE_SHIFT) + pgoff;

	ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
			      req_len, vma->vm_page_prot);
	return ret;
}

static int vfio_pci_dma_fault_add_capability(struct vfio_pci_device *vdev,
					     struct vfio_pci_region *region,
					     struct vfio_info_cap *caps)
{
	struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
	struct vfio_region_info_cap_fault cap = {
		.header.id = VFIO_REGION_INFO_CAP_DMA_FAULT,
		.header.version = 1,
		.version = 1,
	};
	return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
	size_t size = sizeof(*sparse) + sizeof(*sparse->areas);
	int ret;

	ret = vfio_info_add_capability(caps, &cap.header, sizeof(cap));
	if (ret)
		return ret;

	sparse = kzalloc(size, GFP_KERNEL);
	if (!sparse)
		return -ENOMEM;

	sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
	sparse->header.version = 1;
	sparse->nr_areas = 1;
	sparse->areas[0].offset = PAGE_SIZE;
	sparse->areas[0].size = region->size - PAGE_SIZE;

	ret = vfio_info_add_capability(caps, &sparse->header, size);
	if (ret)
		kfree(sparse);

	return ret;
}

static const struct vfio_pci_regops vfio_pci_dma_fault_regops = {
	.rw		= vfio_pci_dma_fault_rw,
	.release	= vfio_pci_dma_fault_release,
	.mmap		= vfio_pci_dma_fault_mmap,
	.add_capability = vfio_pci_dma_fault_add_capability,
};

@@ -404,7 +458,8 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device *vdev)
		VFIO_REGION_TYPE_NESTED,
		VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT,
		&vfio_pci_dma_fault_regops, size,
		VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE,
		VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE |
		VFIO_REGION_INFO_FLAG_MMAP,
		vdev->fault_pages);
	if (ret)
		goto out;
@@ -412,7 +467,7 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device *vdev)
	header = (struct vfio_region_dma_fault *)vdev->fault_pages;
	header->entry_size = sizeof(struct iommu_fault);
	header->nb_entries = DMA_FAULT_RING_LENGTH;
	header->offset = sizeof(struct vfio_region_dma_fault);
	header->offset = PAGE_SIZE;

	ret = iommu_register_device_fault_handler(&vdev->pdev->dev,
					vfio_pci_iommu_dev_fault_handler,