diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index a46565cd8b25..3691c349ac85 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1143,6 +1143,28 @@ struct kvm_device_attr { #define KVM_DEV_VFIO_GROUP_DEL KVM_DEV_VFIO_FILE_DEL #define KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE 3 +/* PVIOMMU for guests. */ +#define KVM_DEV_VFIO_PVIOMMU 5 +#define KVM_DEV_VFIO_PVIOMMU_ATTACH 1 + +/** + * struct kvm_vfio_iommu_info + * @size: sizeof(struct kvm_vfio_iommu_info) + * @device_fd: File descriptor for VFIO device + * @out_nr_sids: Number of IOMMU endpoints for this device (returned) + * @__reserved: Must be 0 + * Probe IOMMU information for a VFIO device so it can be + * configured from userspace to have a virtual toplogy for + * KVM guests. + */ +struct kvm_vfio_iommu_info { + __u32 size; + __s32 device_fd; + __u32 out_nr_sids; + __u32 __reserved; +}; +#define KVM_DEV_VFIO_PVIOMMU_GET_INFO 2 + enum kvm_device_type { KVM_DEV_TYPE_FSL_MPIC_20 = 1, #define KVM_DEV_TYPE_FSL_MPIC_20 KVM_DEV_TYPE_FSL_MPIC_20 diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index fd57020a3d8e..5d59f53d17e8 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -6,6 +6,7 @@ * Author: Alex Williamson */ +#include #include #include #include @@ -35,6 +36,13 @@ struct kvm_vfio { bool noncoherent; }; +#ifdef CONFIG_VFIO_PKVM_IOMMU +struct kvm_pviommu { + struct kvm_device *dev; + int fd; +}; +#endif + static void kvm_vfio_file_set_kvm(struct file *file, struct kvm *kvm) { void (*fn)(struct file *file, struct kvm *kvm); @@ -348,6 +356,120 @@ static int kvm_vfio_set_file(struct kvm_device *dev, long attr, return -ENXIO; } +#ifdef CONFIG_VFIO_PKVM_IOMMU +static long pviommufd_ioctl(struct file *filp, unsigned int ioctl, + unsigned long arg) +{ + return -ENXIO; +} + +static int pviommufd_release(struct inode *i, struct file *filp) +{ + struct kvm_pviommu *pviommu = filp->private_data; + + kfree(pviommu); + return 0; +} + +static const struct file_operations pviommu_fops = { + .unlocked_ioctl = pviommufd_ioctl, + .release = pviommufd_release, +}; + +static int kvm_vfio_pviommu_attach(struct kvm_device *dev) +{ + int ret; + struct kvm_pviommu *pviommu; + struct file *filep; + int fdno; + + pviommu = kmalloc(sizeof(*pviommu), GFP_KERNEL); + if (!pviommu) + return -ENOMEM; + + pviommu->dev = dev; + + filep = anon_inode_getfile("kvm-pviommu", &pviommu_fops, pviommu, O_CLOEXEC); + if (IS_ERR(filep)) { + ret = PTR_ERR(filep); + goto out_free; + } + + fdno = get_unused_fd_flags(O_CLOEXEC); + if (fdno < 0) { + ret = fdno; + goto out_fput; + } + + /* Create pvIOMMU with this ID. */ + ret = kvm_call_hyp_nvhe(__pkvm_pviommu_attach, dev->kvm, fdno); + if (ret) + goto out_err; + + pviommu->fd = fdno; + fd_install(fdno, filep); + return pviommu->fd; +out_err: + put_unused_fd(fdno); +out_fput: + fput(filep); +out_free: + kfree(pviommu); + return ret; +} + +static int kvm_vfio_pviommu_get_info(struct kvm_device *dev, + struct kvm_vfio_iommu_info *info) +{ + int vfio_dev_fd = info->device_fd; + int ret = 0; + struct file *filp; + struct device *device; + + filp = fget(vfio_dev_fd); + if (!filp) + return -EBADF; + + device = kvm_vfio_file_get_device(filp); + if (!device) { + ret = -ENODEV; + goto err_fput; + } + + info->out_nr_sids = kvm_iommu_device_num_ids(device); +err_fput: + fput(filp); + return ret; +} + +static int kvm_vfio_pviommu(struct kvm_device *dev, long attr, + void __user *arg) +{ + int32_t __user *argp = arg; + struct kvm_vfio_iommu_info info; + int ret; + __u32 usize; + + switch (attr) { + case KVM_DEV_VFIO_PVIOMMU_ATTACH: + return kvm_vfio_pviommu_attach(dev); + case KVM_DEV_VFIO_PVIOMMU_GET_INFO: + if (copy_from_user(&usize, arg, sizeof(usize))) + return -EFAULT; + if (usize < offsetofend(struct kvm_vfio_iommu_info, __reserved)) + return -EINVAL; + if (copy_struct_from_user(&info, sizeof(info), argp, usize)) + return -EFAULT; + + ret = kvm_vfio_pviommu_get_info(dev, &info); + if (ret) + return ret; + return copy_to_user(arg, &info, usize); + } + return -ENXIO; +} +#endif + static int kvm_vfio_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { @@ -355,6 +477,11 @@ static int kvm_vfio_set_attr(struct kvm_device *dev, case KVM_DEV_VFIO_FILE: return kvm_vfio_set_file(dev, attr->attr, u64_to_user_ptr(attr->addr)); +#ifdef CONFIG_VFIO_PKVM_IOMMU + case KVM_DEV_VFIO_PVIOMMU: + return kvm_vfio_pviommu(dev, attr->attr, + u64_to_user_ptr(attr->addr)); +#endif } return -ENXIO; @@ -374,6 +501,15 @@ static int kvm_vfio_has_attr(struct kvm_device *dev, return 0; } + break; +#ifdef CONFIG_VFIO_PKVM_IOMMU + case KVM_DEV_VFIO_PVIOMMU: + switch (attr->attr) { + case KVM_DEV_VFIO_PVIOMMU_ATTACH: + case KVM_DEV_VFIO_PVIOMMU_GET_INFO: + return 0; + } +#endif break; }