From e581022df4a3e21d0be8aa581192bc45c4ca021a Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Tue, 25 Apr 2023 10:39:32 +0000 Subject: [PATCH] ANDROID: kvm/vfio: Add pviommu group and attach/get attrs pvIOMMU would configure a paravirtualised IOMMU exposed to guests that is controlled by the hypervisor. This pvIOMMU would be used with VFIO assigned devices. As this requires both KVM and VFIO interaction, it was added in KVM-VFIO device. This patch adds pviommu_attach operation which attaches a pvIOMMU to a VM. pviommu_attach would use the KVM VM for this device and return a fd for the guest pvIOMMU. It also adds attribute KVM_DEV_VFIO_PVIOMMU_GET_INFO to probe info about iommu for a VFIO device fd, for now only number of SIDs is returned. This can be used by the VMM to then configure the virtual SID (vSID) for the IOMMU IDs. Bug: 357781595 Bug: 348382247 Bug: 236685427 Change-Id: I030f04e84fa8fd1c54c2d035f5c09f021f648d22 Signed-off-by: Mostafa Saleh --- include/uapi/linux/kvm.h | 22 +++++++ virt/kvm/vfio.c | 136 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) 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; }