| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 3b81ac2bdcff0f611cad468329cc726600bb23ba Mon Sep 17 00:00:00 2001 | 
 | 2 | From: Diana Craciun <diana.craciun@nxp.com> | 
 | 3 | Date: Fri, 27 Sep 2019 16:40:34 +0300 | 
 | 4 | Subject: [PATCH] vfio/fsl-mc: Allow userspace to MMAP fsl-mc device MMIO | 
 | 5 |  regions | 
 | 6 |  | 
 | 7 | Allow userspace to mmap device regions for direct access of | 
 | 8 | fsl-mc devices. | 
 | 9 |  | 
 | 10 | Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com> | 
 | 11 | Signed-off-by: Diana Craciun <diana.craciun@nxp.com> | 
 | 12 | --- | 
 | 13 |  drivers/vfio/fsl-mc/vfio_fsl_mc.c | 65 +++++++++++++++++++++++++++++++++++++-- | 
 | 14 |  1 file changed, 63 insertions(+), 2 deletions(-) | 
 | 15 |  | 
 | 16 | --- a/drivers/vfio/fsl-mc/vfio_fsl_mc.c | 
 | 17 | +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c | 
 | 18 | @@ -32,7 +32,10 @@ static int vfio_fsl_mc_regions_init(stru | 
 | 19 |   | 
 | 20 |  		vdev->regions[i].addr = res->start; | 
 | 21 |  		vdev->regions[i].size = PAGE_ALIGN((resource_size(res))); | 
 | 22 | -		vdev->regions[i].flags = 0; | 
 | 23 | +		vdev->regions[i].flags = VFIO_REGION_INFO_FLAG_MMAP; | 
 | 24 | +		vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ; | 
 | 25 | +		if (!(mc_dev->regions[i].flags & IORESOURCE_READONLY)) | 
 | 26 | +			vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_WRITE; | 
 | 27 |  	} | 
 | 28 |   | 
 | 29 |  	vdev->num_regions = mc_dev->obj_desc.region_count; | 
 | 30 | @@ -163,10 +166,68 @@ static ssize_t vfio_fsl_mc_write(void *d | 
 | 31 |  	return -EINVAL; | 
 | 32 |  } | 
 | 33 |   | 
 | 34 | +static int vfio_fsl_mc_mmap_mmio(struct vfio_fsl_mc_region region, | 
 | 35 | +				 struct vm_area_struct *vma) | 
 | 36 | +{ | 
 | 37 | +	u64 size = vma->vm_end - vma->vm_start; | 
 | 38 | +	u64 pgoff, base; | 
 | 39 | + | 
 | 40 | +	pgoff = vma->vm_pgoff & | 
 | 41 | +		((1U << (VFIO_FSL_MC_OFFSET_SHIFT - PAGE_SHIFT)) - 1); | 
 | 42 | +	base = pgoff << PAGE_SHIFT; | 
 | 43 | + | 
 | 44 | +	if (region.size < PAGE_SIZE || base + size > region.size) | 
 | 45 | +		return -EINVAL; | 
 | 46 | + | 
 | 47 | +	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | 
 | 48 | + | 
 | 49 | +	vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff; | 
 | 50 | + | 
 | 51 | +	return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, | 
 | 52 | +			       size, vma->vm_page_prot); | 
 | 53 | +} | 
 | 54 | + | 
 | 55 |  static int vfio_fsl_mc_mmap(void *device_data, struct vm_area_struct *vma) | 
 | 56 |  { | 
 | 57 | -	return -EINVAL; | 
 | 58 | +	struct vfio_fsl_mc_device *vdev = device_data; | 
 | 59 | +	struct fsl_mc_device *mc_dev = vdev->mc_dev; | 
 | 60 | +	unsigned long size, addr; | 
 | 61 | +	int index; | 
 | 62 | + | 
 | 63 | +	index = vma->vm_pgoff >> (VFIO_FSL_MC_OFFSET_SHIFT - PAGE_SHIFT); | 
 | 64 | + | 
 | 65 | +	if (vma->vm_end < vma->vm_start) | 
 | 66 | +		return -EINVAL; | 
 | 67 | +	if (vma->vm_start & ~PAGE_MASK) | 
 | 68 | +		return -EINVAL; | 
 | 69 | +	if (vma->vm_end & ~PAGE_MASK) | 
 | 70 | +		return -EINVAL; | 
 | 71 | +	if (!(vma->vm_flags & VM_SHARED)) | 
 | 72 | +		return -EINVAL; | 
 | 73 | +	if (index >= vdev->num_regions) | 
 | 74 | +		return -EINVAL; | 
 | 75 | + | 
 | 76 | +	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP)) | 
 | 77 | +		return -EINVAL; | 
 | 78 | + | 
 | 79 | +	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ) | 
 | 80 | +			&& (vma->vm_flags & VM_READ)) | 
 | 81 | +		return -EINVAL; | 
 | 82 | + | 
 | 83 | +	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE) | 
 | 84 | +			&& (vma->vm_flags & VM_WRITE)) | 
 | 85 | +		return -EINVAL; | 
 | 86 | + | 
 | 87 | +	addr = vdev->regions[index].addr; | 
 | 88 | +	size = vdev->regions[index].size; | 
 | 89 | + | 
 | 90 | +	vma->vm_private_data = mc_dev; | 
 | 91 | + | 
 | 92 | +	return vfio_fsl_mc_mmap_mmio(vdev->regions[index], vma); | 
 | 93 | + | 
 | 94 | +	return -EFAULT; | 
 | 95 |  } | 
 | 96 | + | 
 | 97 |  static int vfio_fsl_mc_init_device(struct vfio_fsl_mc_device *vdev) | 
 | 98 |  { | 
 | 99 |  	struct fsl_mc_device *mc_dev = vdev->mc_dev; |