xref: /openbmc/linux/drivers/vfio/cdx/main.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1*234489acSNipun Gupta // SPDX-License-Identifier: GPL-2.0
2*234489acSNipun Gupta /*
3*234489acSNipun Gupta  * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
4*234489acSNipun Gupta  */
5*234489acSNipun Gupta 
6*234489acSNipun Gupta #include <linux/vfio.h>
7*234489acSNipun Gupta #include <linux/cdx/cdx_bus.h>
8*234489acSNipun Gupta 
9*234489acSNipun Gupta #include "private.h"
10*234489acSNipun Gupta 
vfio_cdx_open_device(struct vfio_device * core_vdev)11*234489acSNipun Gupta static int vfio_cdx_open_device(struct vfio_device *core_vdev)
12*234489acSNipun Gupta {
13*234489acSNipun Gupta 	struct vfio_cdx_device *vdev =
14*234489acSNipun Gupta 		container_of(core_vdev, struct vfio_cdx_device, vdev);
15*234489acSNipun Gupta 	struct cdx_device *cdx_dev = to_cdx_device(core_vdev->dev);
16*234489acSNipun Gupta 	int count = cdx_dev->res_count;
17*234489acSNipun Gupta 	int i;
18*234489acSNipun Gupta 
19*234489acSNipun Gupta 	vdev->regions = kcalloc(count, sizeof(struct vfio_cdx_region),
20*234489acSNipun Gupta 				GFP_KERNEL_ACCOUNT);
21*234489acSNipun Gupta 	if (!vdev->regions)
22*234489acSNipun Gupta 		return -ENOMEM;
23*234489acSNipun Gupta 
24*234489acSNipun Gupta 	for (i = 0; i < count; i++) {
25*234489acSNipun Gupta 		struct resource *res = &cdx_dev->res[i];
26*234489acSNipun Gupta 
27*234489acSNipun Gupta 		vdev->regions[i].addr = res->start;
28*234489acSNipun Gupta 		vdev->regions[i].size = resource_size(res);
29*234489acSNipun Gupta 		vdev->regions[i].type = res->flags;
30*234489acSNipun Gupta 		/*
31*234489acSNipun Gupta 		 * Only regions addressed with PAGE granularity may be
32*234489acSNipun Gupta 		 * MMAP'ed securely.
33*234489acSNipun Gupta 		 */
34*234489acSNipun Gupta 		if (!(vdev->regions[i].addr & ~PAGE_MASK) &&
35*234489acSNipun Gupta 		    !(vdev->regions[i].size & ~PAGE_MASK))
36*234489acSNipun Gupta 			vdev->regions[i].flags |=
37*234489acSNipun Gupta 					VFIO_REGION_INFO_FLAG_MMAP;
38*234489acSNipun Gupta 		vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ;
39*234489acSNipun Gupta 		if (!(cdx_dev->res[i].flags & IORESOURCE_READONLY))
40*234489acSNipun Gupta 			vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_WRITE;
41*234489acSNipun Gupta 	}
42*234489acSNipun Gupta 
43*234489acSNipun Gupta 	return 0;
44*234489acSNipun Gupta }
45*234489acSNipun Gupta 
vfio_cdx_close_device(struct vfio_device * core_vdev)46*234489acSNipun Gupta static void vfio_cdx_close_device(struct vfio_device *core_vdev)
47*234489acSNipun Gupta {
48*234489acSNipun Gupta 	struct vfio_cdx_device *vdev =
49*234489acSNipun Gupta 		container_of(core_vdev, struct vfio_cdx_device, vdev);
50*234489acSNipun Gupta 
51*234489acSNipun Gupta 	kfree(vdev->regions);
52*234489acSNipun Gupta 	cdx_dev_reset(core_vdev->dev);
53*234489acSNipun Gupta }
54*234489acSNipun Gupta 
vfio_cdx_ioctl_get_info(struct vfio_cdx_device * vdev,struct vfio_device_info __user * arg)55*234489acSNipun Gupta static int vfio_cdx_ioctl_get_info(struct vfio_cdx_device *vdev,
56*234489acSNipun Gupta 				   struct vfio_device_info __user *arg)
57*234489acSNipun Gupta {
58*234489acSNipun Gupta 	unsigned long minsz = offsetofend(struct vfio_device_info, num_irqs);
59*234489acSNipun Gupta 	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
60*234489acSNipun Gupta 	struct vfio_device_info info;
61*234489acSNipun Gupta 
62*234489acSNipun Gupta 	if (copy_from_user(&info, arg, minsz))
63*234489acSNipun Gupta 		return -EFAULT;
64*234489acSNipun Gupta 
65*234489acSNipun Gupta 	if (info.argsz < minsz)
66*234489acSNipun Gupta 		return -EINVAL;
67*234489acSNipun Gupta 
68*234489acSNipun Gupta 	info.flags = VFIO_DEVICE_FLAGS_CDX;
69*234489acSNipun Gupta 	info.flags |= VFIO_DEVICE_FLAGS_RESET;
70*234489acSNipun Gupta 
71*234489acSNipun Gupta 	info.num_regions = cdx_dev->res_count;
72*234489acSNipun Gupta 	info.num_irqs = 0;
73*234489acSNipun Gupta 
74*234489acSNipun Gupta 	return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
75*234489acSNipun Gupta }
76*234489acSNipun Gupta 
vfio_cdx_ioctl_get_region_info(struct vfio_cdx_device * vdev,struct vfio_region_info __user * arg)77*234489acSNipun Gupta static int vfio_cdx_ioctl_get_region_info(struct vfio_cdx_device *vdev,
78*234489acSNipun Gupta 					  struct vfio_region_info __user *arg)
79*234489acSNipun Gupta {
80*234489acSNipun Gupta 	unsigned long minsz = offsetofend(struct vfio_region_info, offset);
81*234489acSNipun Gupta 	struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
82*234489acSNipun Gupta 	struct vfio_region_info info;
83*234489acSNipun Gupta 
84*234489acSNipun Gupta 	if (copy_from_user(&info, arg, minsz))
85*234489acSNipun Gupta 		return -EFAULT;
86*234489acSNipun Gupta 
87*234489acSNipun Gupta 	if (info.argsz < minsz)
88*234489acSNipun Gupta 		return -EINVAL;
89*234489acSNipun Gupta 
90*234489acSNipun Gupta 	if (info.index >= cdx_dev->res_count)
91*234489acSNipun Gupta 		return -EINVAL;
92*234489acSNipun Gupta 
93*234489acSNipun Gupta 	/* map offset to the physical address */
94*234489acSNipun Gupta 	info.offset = vfio_cdx_index_to_offset(info.index);
95*234489acSNipun Gupta 	info.size = vdev->regions[info.index].size;
96*234489acSNipun Gupta 	info.flags = vdev->regions[info.index].flags;
97*234489acSNipun Gupta 
98*234489acSNipun Gupta 	return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
99*234489acSNipun Gupta }
100*234489acSNipun Gupta 
vfio_cdx_ioctl(struct vfio_device * core_vdev,unsigned int cmd,unsigned long arg)101*234489acSNipun Gupta static long vfio_cdx_ioctl(struct vfio_device *core_vdev,
102*234489acSNipun Gupta 			   unsigned int cmd, unsigned long arg)
103*234489acSNipun Gupta {
104*234489acSNipun Gupta 	struct vfio_cdx_device *vdev =
105*234489acSNipun Gupta 		container_of(core_vdev, struct vfio_cdx_device, vdev);
106*234489acSNipun Gupta 	void __user *uarg = (void __user *)arg;
107*234489acSNipun Gupta 
108*234489acSNipun Gupta 	switch (cmd) {
109*234489acSNipun Gupta 	case VFIO_DEVICE_GET_INFO:
110*234489acSNipun Gupta 		return vfio_cdx_ioctl_get_info(vdev, uarg);
111*234489acSNipun Gupta 	case VFIO_DEVICE_GET_REGION_INFO:
112*234489acSNipun Gupta 		return vfio_cdx_ioctl_get_region_info(vdev, uarg);
113*234489acSNipun Gupta 	case VFIO_DEVICE_RESET:
114*234489acSNipun Gupta 		return cdx_dev_reset(core_vdev->dev);
115*234489acSNipun Gupta 	default:
116*234489acSNipun Gupta 		return -ENOTTY;
117*234489acSNipun Gupta 	}
118*234489acSNipun Gupta }
119*234489acSNipun Gupta 
vfio_cdx_mmap_mmio(struct vfio_cdx_region region,struct vm_area_struct * vma)120*234489acSNipun Gupta static int vfio_cdx_mmap_mmio(struct vfio_cdx_region region,
121*234489acSNipun Gupta 			      struct vm_area_struct *vma)
122*234489acSNipun Gupta {
123*234489acSNipun Gupta 	u64 size = vma->vm_end - vma->vm_start;
124*234489acSNipun Gupta 	u64 pgoff, base;
125*234489acSNipun Gupta 
126*234489acSNipun Gupta 	pgoff = vma->vm_pgoff &
127*234489acSNipun Gupta 		((1U << (VFIO_CDX_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
128*234489acSNipun Gupta 	base = pgoff << PAGE_SHIFT;
129*234489acSNipun Gupta 
130*234489acSNipun Gupta 	if (base + size > region.size)
131*234489acSNipun Gupta 		return -EINVAL;
132*234489acSNipun Gupta 
133*234489acSNipun Gupta 	vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff;
134*234489acSNipun Gupta 	vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
135*234489acSNipun Gupta 
136*234489acSNipun Gupta 	return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
137*234489acSNipun Gupta 				  size, vma->vm_page_prot);
138*234489acSNipun Gupta }
139*234489acSNipun Gupta 
vfio_cdx_mmap(struct vfio_device * core_vdev,struct vm_area_struct * vma)140*234489acSNipun Gupta static int vfio_cdx_mmap(struct vfio_device *core_vdev,
141*234489acSNipun Gupta 			 struct vm_area_struct *vma)
142*234489acSNipun Gupta {
143*234489acSNipun Gupta 	struct vfio_cdx_device *vdev =
144*234489acSNipun Gupta 		container_of(core_vdev, struct vfio_cdx_device, vdev);
145*234489acSNipun Gupta 	struct cdx_device *cdx_dev = to_cdx_device(core_vdev->dev);
146*234489acSNipun Gupta 	unsigned int index;
147*234489acSNipun Gupta 
148*234489acSNipun Gupta 	index = vma->vm_pgoff >> (VFIO_CDX_OFFSET_SHIFT - PAGE_SHIFT);
149*234489acSNipun Gupta 
150*234489acSNipun Gupta 	if (index >= cdx_dev->res_count)
151*234489acSNipun Gupta 		return -EINVAL;
152*234489acSNipun Gupta 
153*234489acSNipun Gupta 	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP))
154*234489acSNipun Gupta 		return -EINVAL;
155*234489acSNipun Gupta 
156*234489acSNipun Gupta 	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ) &&
157*234489acSNipun Gupta 	    (vma->vm_flags & VM_READ))
158*234489acSNipun Gupta 		return -EPERM;
159*234489acSNipun Gupta 
160*234489acSNipun Gupta 	if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE) &&
161*234489acSNipun Gupta 	    (vma->vm_flags & VM_WRITE))
162*234489acSNipun Gupta 		return -EPERM;
163*234489acSNipun Gupta 
164*234489acSNipun Gupta 	return vfio_cdx_mmap_mmio(vdev->regions[index], vma);
165*234489acSNipun Gupta }
166*234489acSNipun Gupta 
167*234489acSNipun Gupta static const struct vfio_device_ops vfio_cdx_ops = {
168*234489acSNipun Gupta 	.name		= "vfio-cdx",
169*234489acSNipun Gupta 	.open_device	= vfio_cdx_open_device,
170*234489acSNipun Gupta 	.close_device	= vfio_cdx_close_device,
171*234489acSNipun Gupta 	.ioctl		= vfio_cdx_ioctl,
172*234489acSNipun Gupta 	.mmap		= vfio_cdx_mmap,
173*234489acSNipun Gupta 	.bind_iommufd	= vfio_iommufd_physical_bind,
174*234489acSNipun Gupta 	.unbind_iommufd	= vfio_iommufd_physical_unbind,
175*234489acSNipun Gupta 	.attach_ioas	= vfio_iommufd_physical_attach_ioas,
176*234489acSNipun Gupta };
177*234489acSNipun Gupta 
vfio_cdx_probe(struct cdx_device * cdx_dev)178*234489acSNipun Gupta static int vfio_cdx_probe(struct cdx_device *cdx_dev)
179*234489acSNipun Gupta {
180*234489acSNipun Gupta 	struct vfio_cdx_device *vdev;
181*234489acSNipun Gupta 	struct device *dev = &cdx_dev->dev;
182*234489acSNipun Gupta 	int ret;
183*234489acSNipun Gupta 
184*234489acSNipun Gupta 	vdev = vfio_alloc_device(vfio_cdx_device, vdev, dev,
185*234489acSNipun Gupta 				 &vfio_cdx_ops);
186*234489acSNipun Gupta 	if (IS_ERR(vdev))
187*234489acSNipun Gupta 		return PTR_ERR(vdev);
188*234489acSNipun Gupta 
189*234489acSNipun Gupta 	ret = vfio_register_group_dev(&vdev->vdev);
190*234489acSNipun Gupta 	if (ret)
191*234489acSNipun Gupta 		goto out_uninit;
192*234489acSNipun Gupta 
193*234489acSNipun Gupta 	dev_set_drvdata(dev, vdev);
194*234489acSNipun Gupta 	return 0;
195*234489acSNipun Gupta 
196*234489acSNipun Gupta out_uninit:
197*234489acSNipun Gupta 	vfio_put_device(&vdev->vdev);
198*234489acSNipun Gupta 	return ret;
199*234489acSNipun Gupta }
200*234489acSNipun Gupta 
vfio_cdx_remove(struct cdx_device * cdx_dev)201*234489acSNipun Gupta static int vfio_cdx_remove(struct cdx_device *cdx_dev)
202*234489acSNipun Gupta {
203*234489acSNipun Gupta 	struct device *dev = &cdx_dev->dev;
204*234489acSNipun Gupta 	struct vfio_cdx_device *vdev = dev_get_drvdata(dev);
205*234489acSNipun Gupta 
206*234489acSNipun Gupta 	vfio_unregister_group_dev(&vdev->vdev);
207*234489acSNipun Gupta 	vfio_put_device(&vdev->vdev);
208*234489acSNipun Gupta 
209*234489acSNipun Gupta 	return 0;
210*234489acSNipun Gupta }
211*234489acSNipun Gupta 
212*234489acSNipun Gupta static const struct cdx_device_id vfio_cdx_table[] = {
213*234489acSNipun Gupta 	{ CDX_DEVICE_DRIVER_OVERRIDE(CDX_ANY_ID, CDX_ANY_ID,
214*234489acSNipun Gupta 				     CDX_ID_F_VFIO_DRIVER_OVERRIDE) }, /* match all by default */
215*234489acSNipun Gupta 	{}
216*234489acSNipun Gupta };
217*234489acSNipun Gupta 
218*234489acSNipun Gupta MODULE_DEVICE_TABLE(cdx, vfio_cdx_table);
219*234489acSNipun Gupta 
220*234489acSNipun Gupta static struct cdx_driver vfio_cdx_driver = {
221*234489acSNipun Gupta 	.probe		= vfio_cdx_probe,
222*234489acSNipun Gupta 	.remove		= vfio_cdx_remove,
223*234489acSNipun Gupta 	.match_id_table	= vfio_cdx_table,
224*234489acSNipun Gupta 	.driver	= {
225*234489acSNipun Gupta 		.name	= "vfio-cdx",
226*234489acSNipun Gupta 	},
227*234489acSNipun Gupta 	.driver_managed_dma = true,
228*234489acSNipun Gupta };
229*234489acSNipun Gupta 
230*234489acSNipun Gupta module_driver(vfio_cdx_driver, cdx_driver_register, cdx_driver_unregister);
231*234489acSNipun Gupta 
232*234489acSNipun Gupta MODULE_LICENSE("GPL");
233*234489acSNipun Gupta MODULE_DESCRIPTION("VFIO for CDX devices - User Level meta-driver");
234