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 <smostafa@google.com>
This commit is contained in:
Mostafa Saleh
2023-04-25 10:39:32 +00:00
committed by Carlos Llamas
parent 989f5620cf
commit e581022df4
2 changed files with 158 additions and 0 deletions
+22
View File
@@ -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
+136
View File
@@ -6,6 +6,7 @@
* Author: Alex Williamson <alex.williamson@redhat.com>
*/
#include <linux/anon_inodes.h>
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/kvm_host.h>
@@ -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;
}