18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * VFIO-KVM bridge pseudo device
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Red Hat, Inc.  All rights reserved.
68c2ecf20Sopenharmony_ci *     Author: Alex Williamson <alex.williamson@redhat.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/file.h>
118c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
128c2ecf20Sopenharmony_ci#include <linux/list.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/mutex.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
178c2ecf20Sopenharmony_ci#include <linux/vfio.h>
188c2ecf20Sopenharmony_ci#include "vfio.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#ifdef CONFIG_SPAPR_TCE_IOMMU
218c2ecf20Sopenharmony_ci#include <asm/kvm_ppc.h>
228c2ecf20Sopenharmony_ci#endif
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct kvm_vfio_group {
258c2ecf20Sopenharmony_ci	struct list_head node;
268c2ecf20Sopenharmony_ci	struct vfio_group *vfio_group;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct kvm_vfio {
308c2ecf20Sopenharmony_ci	struct list_head group_list;
318c2ecf20Sopenharmony_ci	struct mutex lock;
328c2ecf20Sopenharmony_ci	bool noncoherent;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct vfio_group *kvm_vfio_group_get_external_user(struct file *filep)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct vfio_group *vfio_group;
388c2ecf20Sopenharmony_ci	struct vfio_group *(*fn)(struct file *);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	fn = symbol_get(vfio_group_get_external_user);
418c2ecf20Sopenharmony_ci	if (!fn)
428c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	vfio_group = fn(filep);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	symbol_put(vfio_group_get_external_user);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return vfio_group;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic bool kvm_vfio_external_group_match_file(struct vfio_group *group,
528c2ecf20Sopenharmony_ci					       struct file *filep)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	bool ret, (*fn)(struct vfio_group *, struct file *);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	fn = symbol_get(vfio_external_group_match_file);
578c2ecf20Sopenharmony_ci	if (!fn)
588c2ecf20Sopenharmony_ci		return false;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	ret = fn(group, filep);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	symbol_put(vfio_external_group_match_file);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return ret;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	void (*fn)(struct vfio_group *);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	fn = symbol_get(vfio_group_put_external_user);
728c2ecf20Sopenharmony_ci	if (!fn)
738c2ecf20Sopenharmony_ci		return;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	fn(vfio_group);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	symbol_put(vfio_group_put_external_user);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic void kvm_vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	void (*fn)(struct vfio_group *, struct kvm *);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	fn = symbol_get(vfio_group_set_kvm);
858c2ecf20Sopenharmony_ci	if (!fn)
868c2ecf20Sopenharmony_ci		return;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	fn(group, kvm);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	symbol_put(vfio_group_set_kvm);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic bool kvm_vfio_group_is_coherent(struct vfio_group *vfio_group)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	long (*fn)(struct vfio_group *, unsigned long);
968c2ecf20Sopenharmony_ci	long ret;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	fn = symbol_get(vfio_external_check_extension);
998c2ecf20Sopenharmony_ci	if (!fn)
1008c2ecf20Sopenharmony_ci		return false;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	ret = fn(vfio_group, VFIO_DMA_CC_IOMMU);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	symbol_put(vfio_external_check_extension);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return ret > 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci#ifdef CONFIG_SPAPR_TCE_IOMMU
1108c2ecf20Sopenharmony_cistatic int kvm_vfio_external_user_iommu_id(struct vfio_group *vfio_group)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	int (*fn)(struct vfio_group *);
1138c2ecf20Sopenharmony_ci	int ret = -EINVAL;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	fn = symbol_get(vfio_external_user_iommu_id);
1168c2ecf20Sopenharmony_ci	if (!fn)
1178c2ecf20Sopenharmony_ci		return ret;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	ret = fn(vfio_group);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	symbol_put(vfio_external_user_iommu_id);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return ret;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic struct iommu_group *kvm_vfio_group_get_iommu_group(
1278c2ecf20Sopenharmony_ci		struct vfio_group *group)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int group_id = kvm_vfio_external_user_iommu_id(group);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (group_id < 0)
1328c2ecf20Sopenharmony_ci		return NULL;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return iommu_group_get_by_id(group_id);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void kvm_spapr_tce_release_vfio_group(struct kvm *kvm,
1388c2ecf20Sopenharmony_ci		struct vfio_group *vfio_group)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct iommu_group *grp = kvm_vfio_group_get_iommu_group(vfio_group);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!grp))
1438c2ecf20Sopenharmony_ci		return;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	kvm_spapr_tce_release_iommu_group(kvm, grp);
1468c2ecf20Sopenharmony_ci	iommu_group_put(grp);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci#endif
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*
1518c2ecf20Sopenharmony_ci * Groups can use the same or different IOMMU domains.  If the same then
1528c2ecf20Sopenharmony_ci * adding a new group may change the coherency of groups we've previously
1538c2ecf20Sopenharmony_ci * been told about.  We don't want to care about any of that so we retest
1548c2ecf20Sopenharmony_ci * each group and bail as soon as we find one that's noncoherent.  This
1558c2ecf20Sopenharmony_ci * means we only ever [un]register_noncoherent_dma once for the whole device.
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_cistatic void kvm_vfio_update_coherency(struct kvm_device *dev)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct kvm_vfio *kv = dev->private;
1608c2ecf20Sopenharmony_ci	bool noncoherent = false;
1618c2ecf20Sopenharmony_ci	struct kvm_vfio_group *kvg;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	mutex_lock(&kv->lock);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	list_for_each_entry(kvg, &kv->group_list, node) {
1668c2ecf20Sopenharmony_ci		if (!kvm_vfio_group_is_coherent(kvg->vfio_group)) {
1678c2ecf20Sopenharmony_ci			noncoherent = true;
1688c2ecf20Sopenharmony_ci			break;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (noncoherent != kv->noncoherent) {
1738c2ecf20Sopenharmony_ci		kv->noncoherent = noncoherent;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		if (kv->noncoherent)
1768c2ecf20Sopenharmony_ci			kvm_arch_register_noncoherent_dma(dev->kvm);
1778c2ecf20Sopenharmony_ci		else
1788c2ecf20Sopenharmony_ci			kvm_arch_unregister_noncoherent_dma(dev->kvm);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	mutex_unlock(&kv->lock);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct kvm_vfio *kv = dev->private;
1878c2ecf20Sopenharmony_ci	struct vfio_group *vfio_group;
1888c2ecf20Sopenharmony_ci	struct kvm_vfio_group *kvg;
1898c2ecf20Sopenharmony_ci	int32_t __user *argp = (int32_t __user *)(unsigned long)arg;
1908c2ecf20Sopenharmony_ci	struct fd f;
1918c2ecf20Sopenharmony_ci	int32_t fd;
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	switch (attr) {
1958c2ecf20Sopenharmony_ci	case KVM_DEV_VFIO_GROUP_ADD:
1968c2ecf20Sopenharmony_ci		if (get_user(fd, argp))
1978c2ecf20Sopenharmony_ci			return -EFAULT;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		f = fdget(fd);
2008c2ecf20Sopenharmony_ci		if (!f.file)
2018c2ecf20Sopenharmony_ci			return -EBADF;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		vfio_group = kvm_vfio_group_get_external_user(f.file);
2048c2ecf20Sopenharmony_ci		fdput(f);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		if (IS_ERR(vfio_group))
2078c2ecf20Sopenharmony_ci			return PTR_ERR(vfio_group);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		mutex_lock(&kv->lock);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		list_for_each_entry(kvg, &kv->group_list, node) {
2128c2ecf20Sopenharmony_ci			if (kvg->vfio_group == vfio_group) {
2138c2ecf20Sopenharmony_ci				mutex_unlock(&kv->lock);
2148c2ecf20Sopenharmony_ci				kvm_vfio_group_put_external_user(vfio_group);
2158c2ecf20Sopenharmony_ci				return -EEXIST;
2168c2ecf20Sopenharmony_ci			}
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		kvg = kzalloc(sizeof(*kvg), GFP_KERNEL_ACCOUNT);
2208c2ecf20Sopenharmony_ci		if (!kvg) {
2218c2ecf20Sopenharmony_ci			mutex_unlock(&kv->lock);
2228c2ecf20Sopenharmony_ci			kvm_vfio_group_put_external_user(vfio_group);
2238c2ecf20Sopenharmony_ci			return -ENOMEM;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		list_add_tail(&kvg->node, &kv->group_list);
2278c2ecf20Sopenharmony_ci		kvg->vfio_group = vfio_group;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		kvm_arch_start_assignment(dev->kvm);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		mutex_unlock(&kv->lock);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		kvm_vfio_group_set_kvm(vfio_group, dev->kvm);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		kvm_vfio_update_coherency(dev);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		return 0;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	case KVM_DEV_VFIO_GROUP_DEL:
2408c2ecf20Sopenharmony_ci		if (get_user(fd, argp))
2418c2ecf20Sopenharmony_ci			return -EFAULT;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		f = fdget(fd);
2448c2ecf20Sopenharmony_ci		if (!f.file)
2458c2ecf20Sopenharmony_ci			return -EBADF;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		ret = -ENOENT;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		mutex_lock(&kv->lock);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		list_for_each_entry(kvg, &kv->group_list, node) {
2528c2ecf20Sopenharmony_ci			if (!kvm_vfio_external_group_match_file(kvg->vfio_group,
2538c2ecf20Sopenharmony_ci								f.file))
2548c2ecf20Sopenharmony_ci				continue;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci			list_del(&kvg->node);
2578c2ecf20Sopenharmony_ci			kvm_arch_end_assignment(dev->kvm);
2588c2ecf20Sopenharmony_ci#ifdef CONFIG_SPAPR_TCE_IOMMU
2598c2ecf20Sopenharmony_ci			kvm_spapr_tce_release_vfio_group(dev->kvm,
2608c2ecf20Sopenharmony_ci							 kvg->vfio_group);
2618c2ecf20Sopenharmony_ci#endif
2628c2ecf20Sopenharmony_ci			kvm_vfio_group_set_kvm(kvg->vfio_group, NULL);
2638c2ecf20Sopenharmony_ci			kvm_vfio_group_put_external_user(kvg->vfio_group);
2648c2ecf20Sopenharmony_ci			kfree(kvg);
2658c2ecf20Sopenharmony_ci			ret = 0;
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		mutex_unlock(&kv->lock);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		fdput(f);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		kvm_vfio_update_coherency(dev);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		return ret;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci#ifdef CONFIG_SPAPR_TCE_IOMMU
2788c2ecf20Sopenharmony_ci	case KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE: {
2798c2ecf20Sopenharmony_ci		struct kvm_vfio_spapr_tce param;
2808c2ecf20Sopenharmony_ci		struct kvm_vfio *kv = dev->private;
2818c2ecf20Sopenharmony_ci		struct vfio_group *vfio_group;
2828c2ecf20Sopenharmony_ci		struct kvm_vfio_group *kvg;
2838c2ecf20Sopenharmony_ci		struct fd f;
2848c2ecf20Sopenharmony_ci		struct iommu_group *grp;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		if (copy_from_user(&param, (void __user *)arg,
2878c2ecf20Sopenharmony_ci				sizeof(struct kvm_vfio_spapr_tce)))
2888c2ecf20Sopenharmony_ci			return -EFAULT;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		f = fdget(param.groupfd);
2918c2ecf20Sopenharmony_ci		if (!f.file)
2928c2ecf20Sopenharmony_ci			return -EBADF;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		vfio_group = kvm_vfio_group_get_external_user(f.file);
2958c2ecf20Sopenharmony_ci		fdput(f);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		if (IS_ERR(vfio_group))
2988c2ecf20Sopenharmony_ci			return PTR_ERR(vfio_group);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		grp = kvm_vfio_group_get_iommu_group(vfio_group);
3018c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!grp)) {
3028c2ecf20Sopenharmony_ci			kvm_vfio_group_put_external_user(vfio_group);
3038c2ecf20Sopenharmony_ci			return -EIO;
3048c2ecf20Sopenharmony_ci		}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci		ret = -ENOENT;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		mutex_lock(&kv->lock);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		list_for_each_entry(kvg, &kv->group_list, node) {
3118c2ecf20Sopenharmony_ci			if (kvg->vfio_group != vfio_group)
3128c2ecf20Sopenharmony_ci				continue;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci			ret = kvm_spapr_tce_attach_iommu_group(dev->kvm,
3158c2ecf20Sopenharmony_ci					param.tablefd, grp);
3168c2ecf20Sopenharmony_ci			break;
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		mutex_unlock(&kv->lock);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		iommu_group_put(grp);
3228c2ecf20Sopenharmony_ci		kvm_vfio_group_put_external_user(vfio_group);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		return ret;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci#endif /* CONFIG_SPAPR_TCE_IOMMU */
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return -ENXIO;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int kvm_vfio_set_attr(struct kvm_device *dev,
3338c2ecf20Sopenharmony_ci			     struct kvm_device_attr *attr)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	switch (attr->group) {
3368c2ecf20Sopenharmony_ci	case KVM_DEV_VFIO_GROUP:
3378c2ecf20Sopenharmony_ci		return kvm_vfio_set_group(dev, attr->attr, attr->addr);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return -ENXIO;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int kvm_vfio_has_attr(struct kvm_device *dev,
3448c2ecf20Sopenharmony_ci			     struct kvm_device_attr *attr)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	switch (attr->group) {
3478c2ecf20Sopenharmony_ci	case KVM_DEV_VFIO_GROUP:
3488c2ecf20Sopenharmony_ci		switch (attr->attr) {
3498c2ecf20Sopenharmony_ci		case KVM_DEV_VFIO_GROUP_ADD:
3508c2ecf20Sopenharmony_ci		case KVM_DEV_VFIO_GROUP_DEL:
3518c2ecf20Sopenharmony_ci#ifdef CONFIG_SPAPR_TCE_IOMMU
3528c2ecf20Sopenharmony_ci		case KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE:
3538c2ecf20Sopenharmony_ci#endif
3548c2ecf20Sopenharmony_ci			return 0;
3558c2ecf20Sopenharmony_ci		}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		break;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return -ENXIO;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void kvm_vfio_destroy(struct kvm_device *dev)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct kvm_vfio *kv = dev->private;
3668c2ecf20Sopenharmony_ci	struct kvm_vfio_group *kvg, *tmp;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	list_for_each_entry_safe(kvg, tmp, &kv->group_list, node) {
3698c2ecf20Sopenharmony_ci#ifdef CONFIG_SPAPR_TCE_IOMMU
3708c2ecf20Sopenharmony_ci		kvm_spapr_tce_release_vfio_group(dev->kvm, kvg->vfio_group);
3718c2ecf20Sopenharmony_ci#endif
3728c2ecf20Sopenharmony_ci		kvm_vfio_group_set_kvm(kvg->vfio_group, NULL);
3738c2ecf20Sopenharmony_ci		kvm_vfio_group_put_external_user(kvg->vfio_group);
3748c2ecf20Sopenharmony_ci		list_del(&kvg->node);
3758c2ecf20Sopenharmony_ci		kfree(kvg);
3768c2ecf20Sopenharmony_ci		kvm_arch_end_assignment(dev->kvm);
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	kvm_vfio_update_coherency(dev);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	kfree(kv);
3828c2ecf20Sopenharmony_ci	kfree(dev); /* alloc by kvm_ioctl_create_device, free by .destroy */
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic int kvm_vfio_create(struct kvm_device *dev, u32 type);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic struct kvm_device_ops kvm_vfio_ops = {
3888c2ecf20Sopenharmony_ci	.name = "kvm-vfio",
3898c2ecf20Sopenharmony_ci	.create = kvm_vfio_create,
3908c2ecf20Sopenharmony_ci	.destroy = kvm_vfio_destroy,
3918c2ecf20Sopenharmony_ci	.set_attr = kvm_vfio_set_attr,
3928c2ecf20Sopenharmony_ci	.has_attr = kvm_vfio_has_attr,
3938c2ecf20Sopenharmony_ci};
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int kvm_vfio_create(struct kvm_device *dev, u32 type)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct kvm_device *tmp;
3988c2ecf20Sopenharmony_ci	struct kvm_vfio *kv;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* Only one VFIO "device" per VM */
4018c2ecf20Sopenharmony_ci	list_for_each_entry(tmp, &dev->kvm->devices, vm_node)
4028c2ecf20Sopenharmony_ci		if (tmp->ops == &kvm_vfio_ops)
4038c2ecf20Sopenharmony_ci			return -EBUSY;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	kv = kzalloc(sizeof(*kv), GFP_KERNEL_ACCOUNT);
4068c2ecf20Sopenharmony_ci	if (!kv)
4078c2ecf20Sopenharmony_ci		return -ENOMEM;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&kv->group_list);
4108c2ecf20Sopenharmony_ci	mutex_init(&kv->lock);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	dev->private = kv;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ciint kvm_vfio_ops_init(void)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	return kvm_register_device_ops(&kvm_vfio_ops, KVM_DEV_TYPE_VFIO);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_civoid kvm_vfio_ops_exit(void)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	kvm_unregister_device_ops(KVM_DEV_TYPE_VFIO);
4258c2ecf20Sopenharmony_ci}
426