mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 08:14:15 +08:00
d99b91a99b
Here is the big set of char/misc and other small driver subsystem changes for 6.7-rc1. Included in here are: - IIO subsystem driver updates and additions (largest part of this pull request) - FPGA subsystem driver updates - Counter subsystem driver updates - ICC subsystem driver updates - extcon subsystem driver updates - mei driver updates and additions - nvmem subsystem driver updates and additions - comedi subsystem dependency fixes - parport driver fixups - cdx subsystem driver and core updates - splice support for /dev/zero and /dev/full - other smaller driver cleanups All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZUTSzg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylH3QCfbZuG8MiglEZUd4slRLUNqcRQ5tQAn1yKpDFo l3KLkxo1UTLMXbJBWe+b =gafK -----END PGP SIGNATURE----- Merge tag 'char-misc-6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc updates from Greg KH: "Here is the big set of char/misc and other small driver subsystem changes for 6.7-rc1. Included in here are: - IIO subsystem driver updates and additions (largest part of this pull request) - FPGA subsystem driver updates - Counter subsystem driver updates - ICC subsystem driver updates - extcon subsystem driver updates - mei driver updates and additions - nvmem subsystem driver updates and additions - comedi subsystem dependency fixes - parport driver fixups - cdx subsystem driver and core updates - splice support for /dev/zero and /dev/full - other smaller driver cleanups All of these have been in linux-next for a while with no reported issues" * tag 'char-misc-6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (326 commits) cdx: add sysfs for subsystem, class and revision cdx: add sysfs for bus reset cdx: add support for bus enable and disable cdx: Register cdx bus as a device on cdx subsystem cdx: Create symbol namespaces for cdx subsystem cdx: Introduce lock to protect controller ops cdx: Remove cdx controller list from cdx bus system dts: ti: k3-am625-beagleplay: Add beaglecc1352 greybus: Add BeaglePlay Linux Driver dt-bindings: net: Add ti,cc1352p7 dt-bindings: eeprom: at24: allow NVMEM cells based on old syntax dt-bindings: nvmem: SID: allow NVMEM cells based on old syntax Revert "nvmem: add new config option" MAINTAINERS: coresight: Add missing Coresight files misc: pci_endpoint_test: Add deviceID for J721S2 PCIe EP device support firmware: xilinx: Move EXPORT_SYMBOL_GPL next to zynqmp_pm_feature definition uacce: make uacce_class constant ocxl: make ocxl_class constant cxl: make cxl_class constant misc: phantom: make phantom_class constant ...
290 lines
7.3 KiB
C
290 lines
7.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
|
|
*/
|
|
|
|
#include <linux/vfio.h>
|
|
#include <linux/cdx/cdx_bus.h>
|
|
|
|
#include "private.h"
|
|
|
|
static int vfio_cdx_open_device(struct vfio_device *core_vdev)
|
|
{
|
|
struct vfio_cdx_device *vdev =
|
|
container_of(core_vdev, struct vfio_cdx_device, vdev);
|
|
struct cdx_device *cdx_dev = to_cdx_device(core_vdev->dev);
|
|
int count = cdx_dev->res_count;
|
|
int i, ret;
|
|
|
|
vdev->regions = kcalloc(count, sizeof(struct vfio_cdx_region),
|
|
GFP_KERNEL_ACCOUNT);
|
|
if (!vdev->regions)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
struct resource *res = &cdx_dev->res[i];
|
|
|
|
vdev->regions[i].addr = res->start;
|
|
vdev->regions[i].size = resource_size(res);
|
|
vdev->regions[i].type = res->flags;
|
|
/*
|
|
* Only regions addressed with PAGE granularity may be
|
|
* MMAP'ed securely.
|
|
*/
|
|
if (!(vdev->regions[i].addr & ~PAGE_MASK) &&
|
|
!(vdev->regions[i].size & ~PAGE_MASK))
|
|
vdev->regions[i].flags |=
|
|
VFIO_REGION_INFO_FLAG_MMAP;
|
|
vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ;
|
|
if (!(cdx_dev->res[i].flags & IORESOURCE_READONLY))
|
|
vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_WRITE;
|
|
}
|
|
ret = cdx_dev_reset(core_vdev->dev);
|
|
if (ret) {
|
|
kfree(vdev->regions);
|
|
vdev->regions = NULL;
|
|
return ret;
|
|
}
|
|
ret = cdx_clear_master(cdx_dev);
|
|
if (ret)
|
|
vdev->flags &= ~BME_SUPPORT;
|
|
else
|
|
vdev->flags |= BME_SUPPORT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vfio_cdx_close_device(struct vfio_device *core_vdev)
|
|
{
|
|
struct vfio_cdx_device *vdev =
|
|
container_of(core_vdev, struct vfio_cdx_device, vdev);
|
|
|
|
kfree(vdev->regions);
|
|
cdx_dev_reset(core_vdev->dev);
|
|
}
|
|
|
|
static int vfio_cdx_bm_ctrl(struct vfio_device *core_vdev, u32 flags,
|
|
void __user *arg, size_t argsz)
|
|
{
|
|
size_t minsz =
|
|
offsetofend(struct vfio_device_feature_bus_master, op);
|
|
struct vfio_cdx_device *vdev =
|
|
container_of(core_vdev, struct vfio_cdx_device, vdev);
|
|
struct cdx_device *cdx_dev = to_cdx_device(core_vdev->dev);
|
|
struct vfio_device_feature_bus_master ops;
|
|
int ret;
|
|
|
|
if (!(vdev->flags & BME_SUPPORT))
|
|
return -ENOTTY;
|
|
|
|
ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,
|
|
sizeof(ops));
|
|
if (ret != 1)
|
|
return ret;
|
|
|
|
if (copy_from_user(&ops, arg, minsz))
|
|
return -EFAULT;
|
|
|
|
switch (ops.op) {
|
|
case VFIO_DEVICE_FEATURE_CLEAR_MASTER:
|
|
return cdx_clear_master(cdx_dev);
|
|
case VFIO_DEVICE_FEATURE_SET_MASTER:
|
|
return cdx_set_master(cdx_dev);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int vfio_cdx_ioctl_feature(struct vfio_device *device, u32 flags,
|
|
void __user *arg, size_t argsz)
|
|
{
|
|
switch (flags & VFIO_DEVICE_FEATURE_MASK) {
|
|
case VFIO_DEVICE_FEATURE_BUS_MASTER:
|
|
return vfio_cdx_bm_ctrl(device, flags, arg, argsz);
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
}
|
|
|
|
static int vfio_cdx_ioctl_get_info(struct vfio_cdx_device *vdev,
|
|
struct vfio_device_info __user *arg)
|
|
{
|
|
unsigned long minsz = offsetofend(struct vfio_device_info, num_irqs);
|
|
struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
|
|
struct vfio_device_info info;
|
|
|
|
if (copy_from_user(&info, arg, minsz))
|
|
return -EFAULT;
|
|
|
|
if (info.argsz < minsz)
|
|
return -EINVAL;
|
|
|
|
info.flags = VFIO_DEVICE_FLAGS_CDX;
|
|
info.flags |= VFIO_DEVICE_FLAGS_RESET;
|
|
|
|
info.num_regions = cdx_dev->res_count;
|
|
info.num_irqs = 0;
|
|
|
|
return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
|
|
}
|
|
|
|
static int vfio_cdx_ioctl_get_region_info(struct vfio_cdx_device *vdev,
|
|
struct vfio_region_info __user *arg)
|
|
{
|
|
unsigned long minsz = offsetofend(struct vfio_region_info, offset);
|
|
struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
|
|
struct vfio_region_info info;
|
|
|
|
if (copy_from_user(&info, arg, minsz))
|
|
return -EFAULT;
|
|
|
|
if (info.argsz < minsz)
|
|
return -EINVAL;
|
|
|
|
if (info.index >= cdx_dev->res_count)
|
|
return -EINVAL;
|
|
|
|
/* map offset to the physical address */
|
|
info.offset = vfio_cdx_index_to_offset(info.index);
|
|
info.size = vdev->regions[info.index].size;
|
|
info.flags = vdev->regions[info.index].flags;
|
|
|
|
return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
|
|
}
|
|
|
|
static long vfio_cdx_ioctl(struct vfio_device *core_vdev,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct vfio_cdx_device *vdev =
|
|
container_of(core_vdev, struct vfio_cdx_device, vdev);
|
|
void __user *uarg = (void __user *)arg;
|
|
|
|
switch (cmd) {
|
|
case VFIO_DEVICE_GET_INFO:
|
|
return vfio_cdx_ioctl_get_info(vdev, uarg);
|
|
case VFIO_DEVICE_GET_REGION_INFO:
|
|
return vfio_cdx_ioctl_get_region_info(vdev, uarg);
|
|
case VFIO_DEVICE_RESET:
|
|
return cdx_dev_reset(core_vdev->dev);
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
}
|
|
|
|
static int vfio_cdx_mmap_mmio(struct vfio_cdx_region region,
|
|
struct vm_area_struct *vma)
|
|
{
|
|
u64 size = vma->vm_end - vma->vm_start;
|
|
u64 pgoff, base;
|
|
|
|
pgoff = vma->vm_pgoff &
|
|
((1U << (VFIO_CDX_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
|
|
base = pgoff << PAGE_SHIFT;
|
|
|
|
if (base + size > region.size)
|
|
return -EINVAL;
|
|
|
|
vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff;
|
|
vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
|
|
|
|
return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
|
|
size, vma->vm_page_prot);
|
|
}
|
|
|
|
static int vfio_cdx_mmap(struct vfio_device *core_vdev,
|
|
struct vm_area_struct *vma)
|
|
{
|
|
struct vfio_cdx_device *vdev =
|
|
container_of(core_vdev, struct vfio_cdx_device, vdev);
|
|
struct cdx_device *cdx_dev = to_cdx_device(core_vdev->dev);
|
|
unsigned int index;
|
|
|
|
index = vma->vm_pgoff >> (VFIO_CDX_OFFSET_SHIFT - PAGE_SHIFT);
|
|
|
|
if (index >= cdx_dev->res_count)
|
|
return -EINVAL;
|
|
|
|
if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP))
|
|
return -EINVAL;
|
|
|
|
if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ) &&
|
|
(vma->vm_flags & VM_READ))
|
|
return -EPERM;
|
|
|
|
if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE) &&
|
|
(vma->vm_flags & VM_WRITE))
|
|
return -EPERM;
|
|
|
|
return vfio_cdx_mmap_mmio(vdev->regions[index], vma);
|
|
}
|
|
|
|
static const struct vfio_device_ops vfio_cdx_ops = {
|
|
.name = "vfio-cdx",
|
|
.open_device = vfio_cdx_open_device,
|
|
.close_device = vfio_cdx_close_device,
|
|
.ioctl = vfio_cdx_ioctl,
|
|
.device_feature = vfio_cdx_ioctl_feature,
|
|
.mmap = vfio_cdx_mmap,
|
|
.bind_iommufd = vfio_iommufd_physical_bind,
|
|
.unbind_iommufd = vfio_iommufd_physical_unbind,
|
|
.attach_ioas = vfio_iommufd_physical_attach_ioas,
|
|
};
|
|
|
|
static int vfio_cdx_probe(struct cdx_device *cdx_dev)
|
|
{
|
|
struct vfio_cdx_device *vdev;
|
|
struct device *dev = &cdx_dev->dev;
|
|
int ret;
|
|
|
|
vdev = vfio_alloc_device(vfio_cdx_device, vdev, dev,
|
|
&vfio_cdx_ops);
|
|
if (IS_ERR(vdev))
|
|
return PTR_ERR(vdev);
|
|
|
|
ret = vfio_register_group_dev(&vdev->vdev);
|
|
if (ret)
|
|
goto out_uninit;
|
|
|
|
dev_set_drvdata(dev, vdev);
|
|
return 0;
|
|
|
|
out_uninit:
|
|
vfio_put_device(&vdev->vdev);
|
|
return ret;
|
|
}
|
|
|
|
static int vfio_cdx_remove(struct cdx_device *cdx_dev)
|
|
{
|
|
struct device *dev = &cdx_dev->dev;
|
|
struct vfio_cdx_device *vdev = dev_get_drvdata(dev);
|
|
|
|
vfio_unregister_group_dev(&vdev->vdev);
|
|
vfio_put_device(&vdev->vdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct cdx_device_id vfio_cdx_table[] = {
|
|
{ CDX_DEVICE_DRIVER_OVERRIDE(CDX_ANY_ID, CDX_ANY_ID,
|
|
CDX_ID_F_VFIO_DRIVER_OVERRIDE) }, /* match all by default */
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(cdx, vfio_cdx_table);
|
|
|
|
static struct cdx_driver vfio_cdx_driver = {
|
|
.probe = vfio_cdx_probe,
|
|
.remove = vfio_cdx_remove,
|
|
.match_id_table = vfio_cdx_table,
|
|
.driver = {
|
|
.name = "vfio-cdx",
|
|
},
|
|
.driver_managed_dma = true,
|
|
};
|
|
|
|
module_driver(vfio_cdx_driver, cdx_driver_register, cdx_driver_unregister);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("VFIO for CDX devices - User Level meta-driver");
|
|
MODULE_IMPORT_NS(CDX_BUS);
|