351 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.4 */5 6#include <linux/vfio.h>7#include <linux/cdx/cdx_bus.h>8 9#include "private.h"10 11static int vfio_cdx_open_device(struct vfio_device *core_vdev)12{13 struct vfio_cdx_device *vdev =14 container_of(core_vdev, struct vfio_cdx_device, vdev);15 struct cdx_device *cdx_dev = to_cdx_device(core_vdev->dev);16 int count = cdx_dev->res_count;17 int i, ret;18 19 vdev->regions = kcalloc(count, sizeof(struct vfio_cdx_region),20 GFP_KERNEL_ACCOUNT);21 if (!vdev->regions)22 return -ENOMEM;23 24 for (i = 0; i < count; i++) {25 struct resource *res = &cdx_dev->res[i];26 27 vdev->regions[i].addr = res->start;28 vdev->regions[i].size = resource_size(res);29 vdev->regions[i].type = res->flags;30 /*31 * Only regions addressed with PAGE granularity may be32 * MMAP'ed securely.33 */34 if (!(vdev->regions[i].addr & ~PAGE_MASK) &&35 !(vdev->regions[i].size & ~PAGE_MASK))36 vdev->regions[i].flags |=37 VFIO_REGION_INFO_FLAG_MMAP;38 vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ;39 if (!(cdx_dev->res[i].flags & IORESOURCE_READONLY))40 vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_WRITE;41 }42 ret = cdx_dev_reset(core_vdev->dev);43 if (ret) {44 kfree(vdev->regions);45 vdev->regions = NULL;46 return ret;47 }48 ret = cdx_clear_master(cdx_dev);49 if (ret)50 vdev->flags &= ~BME_SUPPORT;51 else52 vdev->flags |= BME_SUPPORT;53 54 return 0;55}56 57static void vfio_cdx_close_device(struct vfio_device *core_vdev)58{59 struct vfio_cdx_device *vdev =60 container_of(core_vdev, struct vfio_cdx_device, vdev);61 62 kfree(vdev->regions);63 cdx_dev_reset(core_vdev->dev);64 vfio_cdx_irqs_cleanup(vdev);65}66 67static int vfio_cdx_bm_ctrl(struct vfio_device *core_vdev, u32 flags,68 void __user *arg, size_t argsz)69{70 size_t minsz =71 offsetofend(struct vfio_device_feature_bus_master, op);72 struct vfio_cdx_device *vdev =73 container_of(core_vdev, struct vfio_cdx_device, vdev);74 struct cdx_device *cdx_dev = to_cdx_device(core_vdev->dev);75 struct vfio_device_feature_bus_master ops;76 int ret;77 78 if (!(vdev->flags & BME_SUPPORT))79 return -ENOTTY;80 81 ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,82 sizeof(ops));83 if (ret != 1)84 return ret;85 86 if (copy_from_user(&ops, arg, minsz))87 return -EFAULT;88 89 switch (ops.op) {90 case VFIO_DEVICE_FEATURE_CLEAR_MASTER:91 return cdx_clear_master(cdx_dev);92 case VFIO_DEVICE_FEATURE_SET_MASTER:93 return cdx_set_master(cdx_dev);94 default:95 return -EINVAL;96 }97}98 99static int vfio_cdx_ioctl_feature(struct vfio_device *device, u32 flags,100 void __user *arg, size_t argsz)101{102 switch (flags & VFIO_DEVICE_FEATURE_MASK) {103 case VFIO_DEVICE_FEATURE_BUS_MASTER:104 return vfio_cdx_bm_ctrl(device, flags, arg, argsz);105 default:106 return -ENOTTY;107 }108}109 110static int vfio_cdx_ioctl_get_info(struct vfio_cdx_device *vdev,111 struct vfio_device_info __user *arg)112{113 unsigned long minsz = offsetofend(struct vfio_device_info, num_irqs);114 struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);115 struct vfio_device_info info;116 117 if (copy_from_user(&info, arg, minsz))118 return -EFAULT;119 120 if (info.argsz < minsz)121 return -EINVAL;122 123 info.flags = VFIO_DEVICE_FLAGS_CDX;124 info.flags |= VFIO_DEVICE_FLAGS_RESET;125 126 info.num_regions = cdx_dev->res_count;127 info.num_irqs = cdx_dev->num_msi ? 1 : 0;128 129 return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;130}131 132static int vfio_cdx_ioctl_get_region_info(struct vfio_cdx_device *vdev,133 struct vfio_region_info __user *arg)134{135 unsigned long minsz = offsetofend(struct vfio_region_info, offset);136 struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);137 struct vfio_region_info info;138 139 if (copy_from_user(&info, arg, minsz))140 return -EFAULT;141 142 if (info.argsz < minsz)143 return -EINVAL;144 145 if (info.index >= cdx_dev->res_count)146 return -EINVAL;147 148 /* map offset to the physical address */149 info.offset = vfio_cdx_index_to_offset(info.index);150 info.size = vdev->regions[info.index].size;151 info.flags = vdev->regions[info.index].flags;152 153 return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;154}155 156static int vfio_cdx_ioctl_get_irq_info(struct vfio_cdx_device *vdev,157 struct vfio_irq_info __user *arg)158{159 unsigned long minsz = offsetofend(struct vfio_irq_info, count);160 struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);161 struct vfio_irq_info info;162 163 if (copy_from_user(&info, arg, minsz))164 return -EFAULT;165 166 if (info.argsz < minsz)167 return -EINVAL;168 169 if (info.index >= 1)170 return -EINVAL;171 172 if (!cdx_dev->num_msi)173 return -EINVAL;174 175 info.flags = VFIO_IRQ_INFO_EVENTFD | VFIO_IRQ_INFO_NORESIZE;176 info.count = cdx_dev->num_msi;177 178 return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;179}180 181static int vfio_cdx_ioctl_set_irqs(struct vfio_cdx_device *vdev,182 struct vfio_irq_set __user *arg)183{184 unsigned long minsz = offsetofend(struct vfio_irq_set, count);185 struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);186 struct vfio_irq_set hdr;187 size_t data_size = 0;188 u8 *data = NULL;189 int ret = 0;190 191 if (copy_from_user(&hdr, arg, minsz))192 return -EFAULT;193 194 ret = vfio_set_irqs_validate_and_prepare(&hdr, cdx_dev->num_msi,195 1, &data_size);196 if (ret)197 return ret;198 199 if (data_size) {200 data = memdup_user(arg->data, data_size);201 if (IS_ERR(data))202 return PTR_ERR(data);203 }204 205 ret = vfio_cdx_set_irqs_ioctl(vdev, hdr.flags, hdr.index,206 hdr.start, hdr.count, data);207 kfree(data);208 209 return ret;210}211 212static long vfio_cdx_ioctl(struct vfio_device *core_vdev,213 unsigned int cmd, unsigned long arg)214{215 struct vfio_cdx_device *vdev =216 container_of(core_vdev, struct vfio_cdx_device, vdev);217 void __user *uarg = (void __user *)arg;218 219 switch (cmd) {220 case VFIO_DEVICE_GET_INFO:221 return vfio_cdx_ioctl_get_info(vdev, uarg);222 case VFIO_DEVICE_GET_REGION_INFO:223 return vfio_cdx_ioctl_get_region_info(vdev, uarg);224 case VFIO_DEVICE_GET_IRQ_INFO:225 return vfio_cdx_ioctl_get_irq_info(vdev, uarg);226 case VFIO_DEVICE_SET_IRQS:227 return vfio_cdx_ioctl_set_irqs(vdev, uarg);228 case VFIO_DEVICE_RESET:229 return cdx_dev_reset(core_vdev->dev);230 default:231 return -ENOTTY;232 }233}234 235static int vfio_cdx_mmap_mmio(struct vfio_cdx_region region,236 struct vm_area_struct *vma)237{238 u64 size = vma->vm_end - vma->vm_start;239 u64 pgoff, base;240 241 pgoff = vma->vm_pgoff &242 ((1U << (VFIO_CDX_OFFSET_SHIFT - PAGE_SHIFT)) - 1);243 base = pgoff << PAGE_SHIFT;244 245 if (base + size > region.size)246 return -EINVAL;247 248 vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff;249 vma->vm_page_prot = pgprot_device(vma->vm_page_prot);250 251 return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,252 size, vma->vm_page_prot);253}254 255static int vfio_cdx_mmap(struct vfio_device *core_vdev,256 struct vm_area_struct *vma)257{258 struct vfio_cdx_device *vdev =259 container_of(core_vdev, struct vfio_cdx_device, vdev);260 struct cdx_device *cdx_dev = to_cdx_device(core_vdev->dev);261 unsigned int index;262 263 index = vma->vm_pgoff >> (VFIO_CDX_OFFSET_SHIFT - PAGE_SHIFT);264 265 if (index >= cdx_dev->res_count)266 return -EINVAL;267 268 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP))269 return -EINVAL;270 271 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ) &&272 (vma->vm_flags & VM_READ))273 return -EPERM;274 275 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE) &&276 (vma->vm_flags & VM_WRITE))277 return -EPERM;278 279 return vfio_cdx_mmap_mmio(vdev->regions[index], vma);280}281 282static const struct vfio_device_ops vfio_cdx_ops = {283 .name = "vfio-cdx",284 .open_device = vfio_cdx_open_device,285 .close_device = vfio_cdx_close_device,286 .ioctl = vfio_cdx_ioctl,287 .device_feature = vfio_cdx_ioctl_feature,288 .mmap = vfio_cdx_mmap,289 .bind_iommufd = vfio_iommufd_physical_bind,290 .unbind_iommufd = vfio_iommufd_physical_unbind,291 .attach_ioas = vfio_iommufd_physical_attach_ioas,292};293 294static int vfio_cdx_probe(struct cdx_device *cdx_dev)295{296 struct vfio_cdx_device *vdev;297 struct device *dev = &cdx_dev->dev;298 int ret;299 300 vdev = vfio_alloc_device(vfio_cdx_device, vdev, dev,301 &vfio_cdx_ops);302 if (IS_ERR(vdev))303 return PTR_ERR(vdev);304 305 ret = vfio_register_group_dev(&vdev->vdev);306 if (ret)307 goto out_uninit;308 309 dev_set_drvdata(dev, vdev);310 return 0;311 312out_uninit:313 vfio_put_device(&vdev->vdev);314 return ret;315}316 317static int vfio_cdx_remove(struct cdx_device *cdx_dev)318{319 struct device *dev = &cdx_dev->dev;320 struct vfio_cdx_device *vdev = dev_get_drvdata(dev);321 322 vfio_unregister_group_dev(&vdev->vdev);323 vfio_put_device(&vdev->vdev);324 325 return 0;326}327 328static const struct cdx_device_id vfio_cdx_table[] = {329 { CDX_DEVICE_DRIVER_OVERRIDE(CDX_ANY_ID, CDX_ANY_ID,330 CDX_ID_F_VFIO_DRIVER_OVERRIDE) }, /* match all by default */331 {}332};333 334MODULE_DEVICE_TABLE(cdx, vfio_cdx_table);335 336static struct cdx_driver vfio_cdx_driver = {337 .probe = vfio_cdx_probe,338 .remove = vfio_cdx_remove,339 .match_id_table = vfio_cdx_table,340 .driver = {341 .name = "vfio-cdx",342 },343 .driver_managed_dma = true,344};345 346module_driver(vfio_cdx_driver, cdx_driver_register, cdx_driver_unregister);347 348MODULE_LICENSE("GPL");349MODULE_DESCRIPTION("VFIO for CDX devices - User Level meta-driver");350MODULE_IMPORT_NS(CDX_BUS);351