18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * KVMGT - the implementation of Intel mediated pass-through framework for KVM
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright(c) 2014-2016 Intel Corporation. All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
88c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
98c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
108c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
118c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
148c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
158c2ecf20Sopenharmony_ci * Software.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
188c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
198c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
208c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
218c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
228c2ecf20Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
238c2ecf20Sopenharmony_ci * SOFTWARE.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Authors:
268c2ecf20Sopenharmony_ci *    Kevin Tian <kevin.tian@intel.com>
278c2ecf20Sopenharmony_ci *    Jike Song <jike.song@intel.com>
288c2ecf20Sopenharmony_ci *    Xiaoguang Chen <xiaoguang.chen@intel.com>
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/init.h>
328c2ecf20Sopenharmony_ci#include <linux/device.h>
338c2ecf20Sopenharmony_ci#include <linux/mm.h>
348c2ecf20Sopenharmony_ci#include <linux/kthread.h>
358c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
368c2ecf20Sopenharmony_ci#include <linux/types.h>
378c2ecf20Sopenharmony_ci#include <linux/list.h>
388c2ecf20Sopenharmony_ci#include <linux/rbtree.h>
398c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
408c2ecf20Sopenharmony_ci#include <linux/eventfd.h>
418c2ecf20Sopenharmony_ci#include <linux/uuid.h>
428c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
438c2ecf20Sopenharmony_ci#include <linux/vfio.h>
448c2ecf20Sopenharmony_ci#include <linux/mdev.h>
458c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#include <linux/nospec.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include "i915_drv.h"
508c2ecf20Sopenharmony_ci#include "gvt.h"
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const struct intel_gvt_ops *intel_gvt_ops;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* helper macros copied from vfio-pci */
558c2ecf20Sopenharmony_ci#define VFIO_PCI_OFFSET_SHIFT   40
568c2ecf20Sopenharmony_ci#define VFIO_PCI_OFFSET_TO_INDEX(off)   (off >> VFIO_PCI_OFFSET_SHIFT)
578c2ecf20Sopenharmony_ci#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
588c2ecf20Sopenharmony_ci#define VFIO_PCI_OFFSET_MASK    (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define EDID_BLOB_OFFSET (PAGE_SIZE/2)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define OPREGION_SIGNATURE "IntelGraphicsMem"
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistruct vfio_region;
658c2ecf20Sopenharmony_cistruct intel_vgpu_regops {
668c2ecf20Sopenharmony_ci	size_t (*rw)(struct intel_vgpu *vgpu, char *buf,
678c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos, bool iswrite);
688c2ecf20Sopenharmony_ci	void (*release)(struct intel_vgpu *vgpu,
698c2ecf20Sopenharmony_ci			struct vfio_region *region);
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct vfio_region {
738c2ecf20Sopenharmony_ci	u32				type;
748c2ecf20Sopenharmony_ci	u32				subtype;
758c2ecf20Sopenharmony_ci	size_t				size;
768c2ecf20Sopenharmony_ci	u32				flags;
778c2ecf20Sopenharmony_ci	const struct intel_vgpu_regops	*ops;
788c2ecf20Sopenharmony_ci	void				*data;
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistruct vfio_edid_region {
828c2ecf20Sopenharmony_ci	struct vfio_region_gfx_edid vfio_edid_regs;
838c2ecf20Sopenharmony_ci	void *edid_blob;
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistruct kvmgt_pgfn {
878c2ecf20Sopenharmony_ci	gfn_t gfn;
888c2ecf20Sopenharmony_ci	struct hlist_node hnode;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistruct kvmgt_guest_info {
928c2ecf20Sopenharmony_ci	struct kvm *kvm;
938c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu;
948c2ecf20Sopenharmony_ci	struct kvm_page_track_notifier_node track_node;
958c2ecf20Sopenharmony_ci#define NR_BKT (1 << 18)
968c2ecf20Sopenharmony_ci	struct hlist_head ptable[NR_BKT];
978c2ecf20Sopenharmony_ci#undef NR_BKT
988c2ecf20Sopenharmony_ci	struct dentry *debugfs_cache_entries;
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistruct gvt_dma {
1028c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu;
1038c2ecf20Sopenharmony_ci	struct rb_node gfn_node;
1048c2ecf20Sopenharmony_ci	struct rb_node dma_addr_node;
1058c2ecf20Sopenharmony_ci	gfn_t gfn;
1068c2ecf20Sopenharmony_ci	dma_addr_t dma_addr;
1078c2ecf20Sopenharmony_ci	unsigned long size;
1088c2ecf20Sopenharmony_ci	struct kref ref;
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistruct kvmgt_vdev {
1128c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu;
1138c2ecf20Sopenharmony_ci	struct mdev_device *mdev;
1148c2ecf20Sopenharmony_ci	struct vfio_region *region;
1158c2ecf20Sopenharmony_ci	int num_regions;
1168c2ecf20Sopenharmony_ci	struct eventfd_ctx *intx_trigger;
1178c2ecf20Sopenharmony_ci	struct eventfd_ctx *msi_trigger;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/*
1208c2ecf20Sopenharmony_ci	 * Two caches are used to avoid mapping duplicated pages (eg.
1218c2ecf20Sopenharmony_ci	 * scratch pages). This help to reduce dma setup overhead.
1228c2ecf20Sopenharmony_ci	 */
1238c2ecf20Sopenharmony_ci	struct rb_root gfn_cache;
1248c2ecf20Sopenharmony_ci	struct rb_root dma_addr_cache;
1258c2ecf20Sopenharmony_ci	unsigned long nr_cache_entries;
1268c2ecf20Sopenharmony_ci	struct mutex cache_lock;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	struct notifier_block iommu_notifier;
1298c2ecf20Sopenharmony_ci	struct notifier_block group_notifier;
1308c2ecf20Sopenharmony_ci	struct kvm *kvm;
1318c2ecf20Sopenharmony_ci	struct work_struct release_work;
1328c2ecf20Sopenharmony_ci	atomic_t released;
1338c2ecf20Sopenharmony_ci	struct vfio_device *vfio_device;
1348c2ecf20Sopenharmony_ci	struct vfio_group *vfio_group;
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic inline struct kvmgt_vdev *kvmgt_vdev(struct intel_vgpu *vgpu)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	return intel_vgpu_vdev(vgpu);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic inline bool handle_valid(unsigned long handle)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	return !!(handle & ~0xff);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int kvmgt_guest_init(struct mdev_device *mdev);
1488c2ecf20Sopenharmony_cistatic void intel_vgpu_release_work(struct work_struct *work);
1498c2ecf20Sopenharmony_cistatic bool kvmgt_guest_exit(struct kvmgt_guest_info *info);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic void gvt_unpin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
1528c2ecf20Sopenharmony_ci		unsigned long size)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = vgpu->gvt->gt->i915;
1558c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
1568c2ecf20Sopenharmony_ci	int total_pages;
1578c2ecf20Sopenharmony_ci	int npage;
1588c2ecf20Sopenharmony_ci	int ret;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	total_pages = roundup(size, PAGE_SIZE) / PAGE_SIZE;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	for (npage = 0; npage < total_pages; npage++) {
1638c2ecf20Sopenharmony_ci		unsigned long cur_gfn = gfn + npage;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		ret = vfio_group_unpin_pages(vdev->vfio_group, &cur_gfn, 1);
1668c2ecf20Sopenharmony_ci		drm_WARN_ON(&i915->drm, ret != 1);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/* Pin a normal or compound guest page for dma. */
1718c2ecf20Sopenharmony_cistatic int gvt_pin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
1728c2ecf20Sopenharmony_ci		unsigned long size, struct page **page)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
1758c2ecf20Sopenharmony_ci	unsigned long base_pfn = 0;
1768c2ecf20Sopenharmony_ci	int total_pages;
1778c2ecf20Sopenharmony_ci	int npage;
1788c2ecf20Sopenharmony_ci	int ret;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	total_pages = roundup(size, PAGE_SIZE) / PAGE_SIZE;
1818c2ecf20Sopenharmony_ci	/*
1828c2ecf20Sopenharmony_ci	 * We pin the pages one-by-one to avoid allocating a big arrary
1838c2ecf20Sopenharmony_ci	 * on stack to hold pfns.
1848c2ecf20Sopenharmony_ci	 */
1858c2ecf20Sopenharmony_ci	for (npage = 0; npage < total_pages; npage++) {
1868c2ecf20Sopenharmony_ci		unsigned long cur_gfn = gfn + npage;
1878c2ecf20Sopenharmony_ci		unsigned long pfn;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		ret = vfio_group_pin_pages(vdev->vfio_group, &cur_gfn, 1,
1908c2ecf20Sopenharmony_ci					   IOMMU_READ | IOMMU_WRITE, &pfn);
1918c2ecf20Sopenharmony_ci		if (ret != 1) {
1928c2ecf20Sopenharmony_ci			gvt_vgpu_err("vfio_pin_pages failed for gfn 0x%lx, ret %d\n",
1938c2ecf20Sopenharmony_ci				     cur_gfn, ret);
1948c2ecf20Sopenharmony_ci			goto err;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		if (!pfn_valid(pfn)) {
1988c2ecf20Sopenharmony_ci			gvt_vgpu_err("pfn 0x%lx is not mem backed\n", pfn);
1998c2ecf20Sopenharmony_ci			npage++;
2008c2ecf20Sopenharmony_ci			ret = -EFAULT;
2018c2ecf20Sopenharmony_ci			goto err;
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		if (npage == 0)
2058c2ecf20Sopenharmony_ci			base_pfn = pfn;
2068c2ecf20Sopenharmony_ci		else if (base_pfn + npage != pfn) {
2078c2ecf20Sopenharmony_ci			gvt_vgpu_err("The pages are not continuous\n");
2088c2ecf20Sopenharmony_ci			ret = -EINVAL;
2098c2ecf20Sopenharmony_ci			npage++;
2108c2ecf20Sopenharmony_ci			goto err;
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	*page = pfn_to_page(base_pfn);
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_cierr:
2178c2ecf20Sopenharmony_ci	gvt_unpin_guest_page(vgpu, gfn, npage * PAGE_SIZE);
2188c2ecf20Sopenharmony_ci	return ret;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic int gvt_dma_map_page(struct intel_vgpu *vgpu, unsigned long gfn,
2228c2ecf20Sopenharmony_ci		dma_addr_t *dma_addr, unsigned long size)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct device *dev = &vgpu->gvt->gt->i915->drm.pdev->dev;
2258c2ecf20Sopenharmony_ci	struct page *page = NULL;
2268c2ecf20Sopenharmony_ci	int ret;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	ret = gvt_pin_guest_page(vgpu, gfn, size, &page);
2298c2ecf20Sopenharmony_ci	if (ret)
2308c2ecf20Sopenharmony_ci		return ret;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* Setup DMA mapping. */
2338c2ecf20Sopenharmony_ci	*dma_addr = dma_map_page(dev, page, 0, size, PCI_DMA_BIDIRECTIONAL);
2348c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, *dma_addr)) {
2358c2ecf20Sopenharmony_ci		gvt_vgpu_err("DMA mapping failed for pfn 0x%lx, ret %d\n",
2368c2ecf20Sopenharmony_ci			     page_to_pfn(page), ret);
2378c2ecf20Sopenharmony_ci		gvt_unpin_guest_page(vgpu, gfn, size);
2388c2ecf20Sopenharmony_ci		return -ENOMEM;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return 0;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void gvt_dma_unmap_page(struct intel_vgpu *vgpu, unsigned long gfn,
2458c2ecf20Sopenharmony_ci		dma_addr_t dma_addr, unsigned long size)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct device *dev = &vgpu->gvt->gt->i915->drm.pdev->dev;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	dma_unmap_page(dev, dma_addr, size, PCI_DMA_BIDIRECTIONAL);
2508c2ecf20Sopenharmony_ci	gvt_unpin_guest_page(vgpu, gfn, size);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic struct gvt_dma *__gvt_cache_find_dma_addr(struct intel_vgpu *vgpu,
2548c2ecf20Sopenharmony_ci		dma_addr_t dma_addr)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct rb_node *node = kvmgt_vdev(vgpu)->dma_addr_cache.rb_node;
2578c2ecf20Sopenharmony_ci	struct gvt_dma *itr;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	while (node) {
2608c2ecf20Sopenharmony_ci		itr = rb_entry(node, struct gvt_dma, dma_addr_node);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		if (dma_addr < itr->dma_addr)
2638c2ecf20Sopenharmony_ci			node = node->rb_left;
2648c2ecf20Sopenharmony_ci		else if (dma_addr > itr->dma_addr)
2658c2ecf20Sopenharmony_ci			node = node->rb_right;
2668c2ecf20Sopenharmony_ci		else
2678c2ecf20Sopenharmony_ci			return itr;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	return NULL;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic struct gvt_dma *__gvt_cache_find_gfn(struct intel_vgpu *vgpu, gfn_t gfn)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct rb_node *node = kvmgt_vdev(vgpu)->gfn_cache.rb_node;
2758c2ecf20Sopenharmony_ci	struct gvt_dma *itr;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	while (node) {
2788c2ecf20Sopenharmony_ci		itr = rb_entry(node, struct gvt_dma, gfn_node);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		if (gfn < itr->gfn)
2818c2ecf20Sopenharmony_ci			node = node->rb_left;
2828c2ecf20Sopenharmony_ci		else if (gfn > itr->gfn)
2838c2ecf20Sopenharmony_ci			node = node->rb_right;
2848c2ecf20Sopenharmony_ci		else
2858c2ecf20Sopenharmony_ci			return itr;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci	return NULL;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int __gvt_cache_add(struct intel_vgpu *vgpu, gfn_t gfn,
2918c2ecf20Sopenharmony_ci		dma_addr_t dma_addr, unsigned long size)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct gvt_dma *new, *itr;
2948c2ecf20Sopenharmony_ci	struct rb_node **link, *parent = NULL;
2958c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	new = kzalloc(sizeof(struct gvt_dma), GFP_KERNEL);
2988c2ecf20Sopenharmony_ci	if (!new)
2998c2ecf20Sopenharmony_ci		return -ENOMEM;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	new->vgpu = vgpu;
3028c2ecf20Sopenharmony_ci	new->gfn = gfn;
3038c2ecf20Sopenharmony_ci	new->dma_addr = dma_addr;
3048c2ecf20Sopenharmony_ci	new->size = size;
3058c2ecf20Sopenharmony_ci	kref_init(&new->ref);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* gfn_cache maps gfn to struct gvt_dma. */
3088c2ecf20Sopenharmony_ci	link = &vdev->gfn_cache.rb_node;
3098c2ecf20Sopenharmony_ci	while (*link) {
3108c2ecf20Sopenharmony_ci		parent = *link;
3118c2ecf20Sopenharmony_ci		itr = rb_entry(parent, struct gvt_dma, gfn_node);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		if (gfn < itr->gfn)
3148c2ecf20Sopenharmony_ci			link = &parent->rb_left;
3158c2ecf20Sopenharmony_ci		else
3168c2ecf20Sopenharmony_ci			link = &parent->rb_right;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci	rb_link_node(&new->gfn_node, parent, link);
3198c2ecf20Sopenharmony_ci	rb_insert_color(&new->gfn_node, &vdev->gfn_cache);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* dma_addr_cache maps dma addr to struct gvt_dma. */
3228c2ecf20Sopenharmony_ci	parent = NULL;
3238c2ecf20Sopenharmony_ci	link = &vdev->dma_addr_cache.rb_node;
3248c2ecf20Sopenharmony_ci	while (*link) {
3258c2ecf20Sopenharmony_ci		parent = *link;
3268c2ecf20Sopenharmony_ci		itr = rb_entry(parent, struct gvt_dma, dma_addr_node);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		if (dma_addr < itr->dma_addr)
3298c2ecf20Sopenharmony_ci			link = &parent->rb_left;
3308c2ecf20Sopenharmony_ci		else
3318c2ecf20Sopenharmony_ci			link = &parent->rb_right;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci	rb_link_node(&new->dma_addr_node, parent, link);
3348c2ecf20Sopenharmony_ci	rb_insert_color(&new->dma_addr_node, &vdev->dma_addr_cache);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	vdev->nr_cache_entries++;
3378c2ecf20Sopenharmony_ci	return 0;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic void __gvt_cache_remove_entry(struct intel_vgpu *vgpu,
3418c2ecf20Sopenharmony_ci				struct gvt_dma *entry)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	rb_erase(&entry->gfn_node, &vdev->gfn_cache);
3468c2ecf20Sopenharmony_ci	rb_erase(&entry->dma_addr_node, &vdev->dma_addr_cache);
3478c2ecf20Sopenharmony_ci	kfree(entry);
3488c2ecf20Sopenharmony_ci	vdev->nr_cache_entries--;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic void gvt_cache_destroy(struct intel_vgpu *vgpu)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct gvt_dma *dma;
3548c2ecf20Sopenharmony_ci	struct rb_node *node = NULL;
3558c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	for (;;) {
3588c2ecf20Sopenharmony_ci		mutex_lock(&vdev->cache_lock);
3598c2ecf20Sopenharmony_ci		node = rb_first(&vdev->gfn_cache);
3608c2ecf20Sopenharmony_ci		if (!node) {
3618c2ecf20Sopenharmony_ci			mutex_unlock(&vdev->cache_lock);
3628c2ecf20Sopenharmony_ci			break;
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci		dma = rb_entry(node, struct gvt_dma, gfn_node);
3658c2ecf20Sopenharmony_ci		gvt_dma_unmap_page(vgpu, dma->gfn, dma->dma_addr, dma->size);
3668c2ecf20Sopenharmony_ci		__gvt_cache_remove_entry(vgpu, dma);
3678c2ecf20Sopenharmony_ci		mutex_unlock(&vdev->cache_lock);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic void gvt_cache_init(struct intel_vgpu *vgpu)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	vdev->gfn_cache = RB_ROOT;
3768c2ecf20Sopenharmony_ci	vdev->dma_addr_cache = RB_ROOT;
3778c2ecf20Sopenharmony_ci	vdev->nr_cache_entries = 0;
3788c2ecf20Sopenharmony_ci	mutex_init(&vdev->cache_lock);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic void kvmgt_protect_table_init(struct kvmgt_guest_info *info)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	hash_init(info->ptable);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic void kvmgt_protect_table_destroy(struct kvmgt_guest_info *info)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct kvmgt_pgfn *p;
3898c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
3908c2ecf20Sopenharmony_ci	int i;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	hash_for_each_safe(info->ptable, i, tmp, p, hnode) {
3938c2ecf20Sopenharmony_ci		hash_del(&p->hnode);
3948c2ecf20Sopenharmony_ci		kfree(p);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic struct kvmgt_pgfn *
3998c2ecf20Sopenharmony_ci__kvmgt_protect_table_find(struct kvmgt_guest_info *info, gfn_t gfn)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct kvmgt_pgfn *p, *res = NULL;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	hash_for_each_possible(info->ptable, p, hnode, gfn) {
4048c2ecf20Sopenharmony_ci		if (gfn == p->gfn) {
4058c2ecf20Sopenharmony_ci			res = p;
4068c2ecf20Sopenharmony_ci			break;
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return res;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic bool kvmgt_gfn_is_write_protected(struct kvmgt_guest_info *info,
4148c2ecf20Sopenharmony_ci				gfn_t gfn)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct kvmgt_pgfn *p;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	p = __kvmgt_protect_table_find(info, gfn);
4198c2ecf20Sopenharmony_ci	return !!p;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic void kvmgt_protect_table_add(struct kvmgt_guest_info *info, gfn_t gfn)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct kvmgt_pgfn *p;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (kvmgt_gfn_is_write_protected(info, gfn))
4278c2ecf20Sopenharmony_ci		return;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	p = kzalloc(sizeof(struct kvmgt_pgfn), GFP_ATOMIC);
4308c2ecf20Sopenharmony_ci	if (WARN(!p, "gfn: 0x%llx\n", gfn))
4318c2ecf20Sopenharmony_ci		return;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	p->gfn = gfn;
4348c2ecf20Sopenharmony_ci	hash_add(info->ptable, &p->hnode, gfn);
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic void kvmgt_protect_table_del(struct kvmgt_guest_info *info,
4388c2ecf20Sopenharmony_ci				gfn_t gfn)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct kvmgt_pgfn *p;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	p = __kvmgt_protect_table_find(info, gfn);
4438c2ecf20Sopenharmony_ci	if (p) {
4448c2ecf20Sopenharmony_ci		hash_del(&p->hnode);
4458c2ecf20Sopenharmony_ci		kfree(p);
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic size_t intel_vgpu_reg_rw_opregion(struct intel_vgpu *vgpu, char *buf,
4508c2ecf20Sopenharmony_ci		size_t count, loff_t *ppos, bool iswrite)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
4538c2ecf20Sopenharmony_ci	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
4548c2ecf20Sopenharmony_ci			VFIO_PCI_NUM_REGIONS;
4558c2ecf20Sopenharmony_ci	void *base = vdev->region[i].data;
4568c2ecf20Sopenharmony_ci	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	if (pos >= vdev->region[i].size || iswrite) {
4608c2ecf20Sopenharmony_ci		gvt_vgpu_err("invalid op or offset for Intel vgpu OpRegion\n");
4618c2ecf20Sopenharmony_ci		return -EINVAL;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci	count = min(count, (size_t)(vdev->region[i].size - pos));
4648c2ecf20Sopenharmony_ci	memcpy(buf, base + pos, count);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return count;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic void intel_vgpu_reg_release_opregion(struct intel_vgpu *vgpu,
4708c2ecf20Sopenharmony_ci		struct vfio_region *region)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic const struct intel_vgpu_regops intel_vgpu_regops_opregion = {
4758c2ecf20Sopenharmony_ci	.rw = intel_vgpu_reg_rw_opregion,
4768c2ecf20Sopenharmony_ci	.release = intel_vgpu_reg_release_opregion,
4778c2ecf20Sopenharmony_ci};
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic int handle_edid_regs(struct intel_vgpu *vgpu,
4808c2ecf20Sopenharmony_ci			struct vfio_edid_region *region, char *buf,
4818c2ecf20Sopenharmony_ci			size_t count, u16 offset, bool is_write)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct vfio_region_gfx_edid *regs = &region->vfio_edid_regs;
4848c2ecf20Sopenharmony_ci	unsigned int data;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (offset + count > sizeof(*regs))
4878c2ecf20Sopenharmony_ci		return -EINVAL;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (count != 4)
4908c2ecf20Sopenharmony_ci		return -EINVAL;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (is_write) {
4938c2ecf20Sopenharmony_ci		data = *((unsigned int *)buf);
4948c2ecf20Sopenharmony_ci		switch (offset) {
4958c2ecf20Sopenharmony_ci		case offsetof(struct vfio_region_gfx_edid, link_state):
4968c2ecf20Sopenharmony_ci			if (data == VFIO_DEVICE_GFX_LINK_STATE_UP) {
4978c2ecf20Sopenharmony_ci				if (!drm_edid_block_valid(
4988c2ecf20Sopenharmony_ci					(u8 *)region->edid_blob,
4998c2ecf20Sopenharmony_ci					0,
5008c2ecf20Sopenharmony_ci					true,
5018c2ecf20Sopenharmony_ci					NULL)) {
5028c2ecf20Sopenharmony_ci					gvt_vgpu_err("invalid EDID blob\n");
5038c2ecf20Sopenharmony_ci					return -EINVAL;
5048c2ecf20Sopenharmony_ci				}
5058c2ecf20Sopenharmony_ci				intel_gvt_ops->emulate_hotplug(vgpu, true);
5068c2ecf20Sopenharmony_ci			} else if (data == VFIO_DEVICE_GFX_LINK_STATE_DOWN)
5078c2ecf20Sopenharmony_ci				intel_gvt_ops->emulate_hotplug(vgpu, false);
5088c2ecf20Sopenharmony_ci			else {
5098c2ecf20Sopenharmony_ci				gvt_vgpu_err("invalid EDID link state %d\n",
5108c2ecf20Sopenharmony_ci					regs->link_state);
5118c2ecf20Sopenharmony_ci				return -EINVAL;
5128c2ecf20Sopenharmony_ci			}
5138c2ecf20Sopenharmony_ci			regs->link_state = data;
5148c2ecf20Sopenharmony_ci			break;
5158c2ecf20Sopenharmony_ci		case offsetof(struct vfio_region_gfx_edid, edid_size):
5168c2ecf20Sopenharmony_ci			if (data > regs->edid_max_size) {
5178c2ecf20Sopenharmony_ci				gvt_vgpu_err("EDID size is bigger than %d!\n",
5188c2ecf20Sopenharmony_ci					regs->edid_max_size);
5198c2ecf20Sopenharmony_ci				return -EINVAL;
5208c2ecf20Sopenharmony_ci			}
5218c2ecf20Sopenharmony_ci			regs->edid_size = data;
5228c2ecf20Sopenharmony_ci			break;
5238c2ecf20Sopenharmony_ci		default:
5248c2ecf20Sopenharmony_ci			/* read-only regs */
5258c2ecf20Sopenharmony_ci			gvt_vgpu_err("write read-only EDID region at offset %d\n",
5268c2ecf20Sopenharmony_ci				offset);
5278c2ecf20Sopenharmony_ci			return -EPERM;
5288c2ecf20Sopenharmony_ci		}
5298c2ecf20Sopenharmony_ci	} else {
5308c2ecf20Sopenharmony_ci		memcpy(buf, (char *)regs + offset, count);
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	return count;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic int handle_edid_blob(struct vfio_edid_region *region, char *buf,
5378c2ecf20Sopenharmony_ci			size_t count, u16 offset, bool is_write)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	if (offset + count > region->vfio_edid_regs.edid_size)
5408c2ecf20Sopenharmony_ci		return -EINVAL;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (is_write)
5438c2ecf20Sopenharmony_ci		memcpy(region->edid_blob + offset, buf, count);
5448c2ecf20Sopenharmony_ci	else
5458c2ecf20Sopenharmony_ci		memcpy(buf, region->edid_blob + offset, count);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return count;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic size_t intel_vgpu_reg_rw_edid(struct intel_vgpu *vgpu, char *buf,
5518c2ecf20Sopenharmony_ci		size_t count, loff_t *ppos, bool iswrite)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	int ret;
5548c2ecf20Sopenharmony_ci	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
5558c2ecf20Sopenharmony_ci			VFIO_PCI_NUM_REGIONS;
5568c2ecf20Sopenharmony_ci	struct vfio_edid_region *region =
5578c2ecf20Sopenharmony_ci		(struct vfio_edid_region *)kvmgt_vdev(vgpu)->region[i].data;
5588c2ecf20Sopenharmony_ci	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (pos < region->vfio_edid_regs.edid_offset) {
5618c2ecf20Sopenharmony_ci		ret = handle_edid_regs(vgpu, region, buf, count, pos, iswrite);
5628c2ecf20Sopenharmony_ci	} else {
5638c2ecf20Sopenharmony_ci		pos -= EDID_BLOB_OFFSET;
5648c2ecf20Sopenharmony_ci		ret = handle_edid_blob(region, buf, count, pos, iswrite);
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (ret < 0)
5688c2ecf20Sopenharmony_ci		gvt_vgpu_err("failed to access EDID region\n");
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	return ret;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic void intel_vgpu_reg_release_edid(struct intel_vgpu *vgpu,
5748c2ecf20Sopenharmony_ci					struct vfio_region *region)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	kfree(region->data);
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic const struct intel_vgpu_regops intel_vgpu_regops_edid = {
5808c2ecf20Sopenharmony_ci	.rw = intel_vgpu_reg_rw_edid,
5818c2ecf20Sopenharmony_ci	.release = intel_vgpu_reg_release_edid,
5828c2ecf20Sopenharmony_ci};
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic int intel_vgpu_register_reg(struct intel_vgpu *vgpu,
5858c2ecf20Sopenharmony_ci		unsigned int type, unsigned int subtype,
5868c2ecf20Sopenharmony_ci		const struct intel_vgpu_regops *ops,
5878c2ecf20Sopenharmony_ci		size_t size, u32 flags, void *data)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
5908c2ecf20Sopenharmony_ci	struct vfio_region *region;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	region = krealloc(vdev->region,
5938c2ecf20Sopenharmony_ci			(vdev->num_regions + 1) * sizeof(*region),
5948c2ecf20Sopenharmony_ci			GFP_KERNEL);
5958c2ecf20Sopenharmony_ci	if (!region)
5968c2ecf20Sopenharmony_ci		return -ENOMEM;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	vdev->region = region;
5998c2ecf20Sopenharmony_ci	vdev->region[vdev->num_regions].type = type;
6008c2ecf20Sopenharmony_ci	vdev->region[vdev->num_regions].subtype = subtype;
6018c2ecf20Sopenharmony_ci	vdev->region[vdev->num_regions].ops = ops;
6028c2ecf20Sopenharmony_ci	vdev->region[vdev->num_regions].size = size;
6038c2ecf20Sopenharmony_ci	vdev->region[vdev->num_regions].flags = flags;
6048c2ecf20Sopenharmony_ci	vdev->region[vdev->num_regions].data = data;
6058c2ecf20Sopenharmony_ci	vdev->num_regions++;
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int kvmgt_get_vfio_device(void *p_vgpu)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
6128c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	vdev->vfio_device = vfio_device_get_from_dev(
6158c2ecf20Sopenharmony_ci		mdev_dev(vdev->mdev));
6168c2ecf20Sopenharmony_ci	if (!vdev->vfio_device) {
6178c2ecf20Sopenharmony_ci		gvt_vgpu_err("failed to get vfio device\n");
6188c2ecf20Sopenharmony_ci		return -ENODEV;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci	return 0;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic int kvmgt_set_opregion(void *p_vgpu)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
6278c2ecf20Sopenharmony_ci	void *base;
6288c2ecf20Sopenharmony_ci	int ret;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* Each vgpu has its own opregion, although VFIO would create another
6318c2ecf20Sopenharmony_ci	 * one later. This one is used to expose opregion to VFIO. And the
6328c2ecf20Sopenharmony_ci	 * other one created by VFIO later, is used by guest actually.
6338c2ecf20Sopenharmony_ci	 */
6348c2ecf20Sopenharmony_ci	base = vgpu_opregion(vgpu)->va;
6358c2ecf20Sopenharmony_ci	if (!base)
6368c2ecf20Sopenharmony_ci		return -ENOMEM;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (memcmp(base, OPREGION_SIGNATURE, 16)) {
6398c2ecf20Sopenharmony_ci		memunmap(base);
6408c2ecf20Sopenharmony_ci		return -EINVAL;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	ret = intel_vgpu_register_reg(vgpu,
6448c2ecf20Sopenharmony_ci			PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
6458c2ecf20Sopenharmony_ci			VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION,
6468c2ecf20Sopenharmony_ci			&intel_vgpu_regops_opregion, OPREGION_SIZE,
6478c2ecf20Sopenharmony_ci			VFIO_REGION_INFO_FLAG_READ, base);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	return ret;
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int kvmgt_set_edid(void *p_vgpu, int port_num)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
6558c2ecf20Sopenharmony_ci	struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num);
6568c2ecf20Sopenharmony_ci	struct vfio_edid_region *base;
6578c2ecf20Sopenharmony_ci	int ret;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	base = kzalloc(sizeof(*base), GFP_KERNEL);
6608c2ecf20Sopenharmony_ci	if (!base)
6618c2ecf20Sopenharmony_ci		return -ENOMEM;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* TODO: Add multi-port and EDID extension block support */
6648c2ecf20Sopenharmony_ci	base->vfio_edid_regs.edid_offset = EDID_BLOB_OFFSET;
6658c2ecf20Sopenharmony_ci	base->vfio_edid_regs.edid_max_size = EDID_SIZE;
6668c2ecf20Sopenharmony_ci	base->vfio_edid_regs.edid_size = EDID_SIZE;
6678c2ecf20Sopenharmony_ci	base->vfio_edid_regs.max_xres = vgpu_edid_xres(port->id);
6688c2ecf20Sopenharmony_ci	base->vfio_edid_regs.max_yres = vgpu_edid_yres(port->id);
6698c2ecf20Sopenharmony_ci	base->edid_blob = port->edid->edid_block;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	ret = intel_vgpu_register_reg(vgpu,
6728c2ecf20Sopenharmony_ci			VFIO_REGION_TYPE_GFX,
6738c2ecf20Sopenharmony_ci			VFIO_REGION_SUBTYPE_GFX_EDID,
6748c2ecf20Sopenharmony_ci			&intel_vgpu_regops_edid, EDID_SIZE,
6758c2ecf20Sopenharmony_ci			VFIO_REGION_INFO_FLAG_READ |
6768c2ecf20Sopenharmony_ci			VFIO_REGION_INFO_FLAG_WRITE |
6778c2ecf20Sopenharmony_ci			VFIO_REGION_INFO_FLAG_CAPS, base);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	return ret;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic void kvmgt_put_vfio_device(void *vgpu)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev((struct intel_vgpu *)vgpu);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (WARN_ON(!vdev->vfio_device))
6878c2ecf20Sopenharmony_ci		return;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	vfio_device_put(vdev->vfio_device);
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int intel_vgpu_create(struct kobject *kobj, struct mdev_device *mdev)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = NULL;
6958c2ecf20Sopenharmony_ci	struct intel_vgpu_type *type;
6968c2ecf20Sopenharmony_ci	struct device *pdev;
6978c2ecf20Sopenharmony_ci	void *gvt;
6988c2ecf20Sopenharmony_ci	int ret;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	pdev = mdev_parent_dev(mdev);
7018c2ecf20Sopenharmony_ci	gvt = kdev_to_i915(pdev)->gvt;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	type = intel_gvt_ops->gvt_find_vgpu_type(gvt, kobject_name(kobj));
7048c2ecf20Sopenharmony_ci	if (!type) {
7058c2ecf20Sopenharmony_ci		gvt_vgpu_err("failed to find type %s to create\n",
7068c2ecf20Sopenharmony_ci						kobject_name(kobj));
7078c2ecf20Sopenharmony_ci		ret = -EINVAL;
7088c2ecf20Sopenharmony_ci		goto out;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	vgpu = intel_gvt_ops->vgpu_create(gvt, type);
7128c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(vgpu)) {
7138c2ecf20Sopenharmony_ci		ret = vgpu == NULL ? -EFAULT : PTR_ERR(vgpu);
7148c2ecf20Sopenharmony_ci		gvt_err("failed to create intel vgpu: %d\n", ret);
7158c2ecf20Sopenharmony_ci		goto out;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	INIT_WORK(&kvmgt_vdev(vgpu)->release_work, intel_vgpu_release_work);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	kvmgt_vdev(vgpu)->mdev = mdev;
7218c2ecf20Sopenharmony_ci	mdev_set_drvdata(mdev, vgpu);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	gvt_dbg_core("intel_vgpu_create succeeded for mdev: %s\n",
7248c2ecf20Sopenharmony_ci		     dev_name(mdev_dev(mdev)));
7258c2ecf20Sopenharmony_ci	ret = 0;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ciout:
7288c2ecf20Sopenharmony_ci	return ret;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic int intel_vgpu_remove(struct mdev_device *mdev)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (handle_valid(vgpu->handle))
7368c2ecf20Sopenharmony_ci		return -EBUSY;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	intel_gvt_ops->vgpu_destroy(vgpu);
7398c2ecf20Sopenharmony_ci	return 0;
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int intel_vgpu_iommu_notifier(struct notifier_block *nb,
7438c2ecf20Sopenharmony_ci				     unsigned long action, void *data)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = container_of(nb,
7468c2ecf20Sopenharmony_ci					       struct kvmgt_vdev,
7478c2ecf20Sopenharmony_ci					       iommu_notifier);
7488c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = vdev->vgpu;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
7518c2ecf20Sopenharmony_ci		struct vfio_iommu_type1_dma_unmap *unmap = data;
7528c2ecf20Sopenharmony_ci		struct gvt_dma *entry;
7538c2ecf20Sopenharmony_ci		unsigned long iov_pfn, end_iov_pfn;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci		iov_pfn = unmap->iova >> PAGE_SHIFT;
7568c2ecf20Sopenharmony_ci		end_iov_pfn = iov_pfn + unmap->size / PAGE_SIZE;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci		mutex_lock(&vdev->cache_lock);
7598c2ecf20Sopenharmony_ci		for (; iov_pfn < end_iov_pfn; iov_pfn++) {
7608c2ecf20Sopenharmony_ci			entry = __gvt_cache_find_gfn(vgpu, iov_pfn);
7618c2ecf20Sopenharmony_ci			if (!entry)
7628c2ecf20Sopenharmony_ci				continue;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci			gvt_dma_unmap_page(vgpu, entry->gfn, entry->dma_addr,
7658c2ecf20Sopenharmony_ci					   entry->size);
7668c2ecf20Sopenharmony_ci			__gvt_cache_remove_entry(vgpu, entry);
7678c2ecf20Sopenharmony_ci		}
7688c2ecf20Sopenharmony_ci		mutex_unlock(&vdev->cache_lock);
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	return NOTIFY_OK;
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic int intel_vgpu_group_notifier(struct notifier_block *nb,
7758c2ecf20Sopenharmony_ci				     unsigned long action, void *data)
7768c2ecf20Sopenharmony_ci{
7778c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = container_of(nb,
7788c2ecf20Sopenharmony_ci					       struct kvmgt_vdev,
7798c2ecf20Sopenharmony_ci					       group_notifier);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* the only action we care about */
7828c2ecf20Sopenharmony_ci	if (action == VFIO_GROUP_NOTIFY_SET_KVM) {
7838c2ecf20Sopenharmony_ci		vdev->kvm = data;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		if (!data)
7868c2ecf20Sopenharmony_ci			schedule_work(&vdev->release_work);
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	return NOTIFY_OK;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic int intel_vgpu_open(struct mdev_device *mdev)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
7958c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
7968c2ecf20Sopenharmony_ci	unsigned long events;
7978c2ecf20Sopenharmony_ci	int ret;
7988c2ecf20Sopenharmony_ci	struct vfio_group *vfio_group;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	vdev->iommu_notifier.notifier_call = intel_vgpu_iommu_notifier;
8018c2ecf20Sopenharmony_ci	vdev->group_notifier.notifier_call = intel_vgpu_group_notifier;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
8048c2ecf20Sopenharmony_ci	ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY, &events,
8058c2ecf20Sopenharmony_ci				&vdev->iommu_notifier);
8068c2ecf20Sopenharmony_ci	if (ret != 0) {
8078c2ecf20Sopenharmony_ci		gvt_vgpu_err("vfio_register_notifier for iommu failed: %d\n",
8088c2ecf20Sopenharmony_ci			ret);
8098c2ecf20Sopenharmony_ci		goto out;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	events = VFIO_GROUP_NOTIFY_SET_KVM;
8138c2ecf20Sopenharmony_ci	ret = vfio_register_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY, &events,
8148c2ecf20Sopenharmony_ci				&vdev->group_notifier);
8158c2ecf20Sopenharmony_ci	if (ret != 0) {
8168c2ecf20Sopenharmony_ci		gvt_vgpu_err("vfio_register_notifier for group failed: %d\n",
8178c2ecf20Sopenharmony_ci			ret);
8188c2ecf20Sopenharmony_ci		goto undo_iommu;
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	vfio_group = vfio_group_get_external_user_from_dev(mdev_dev(mdev));
8228c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(vfio_group)) {
8238c2ecf20Sopenharmony_ci		ret = !vfio_group ? -EFAULT : PTR_ERR(vfio_group);
8248c2ecf20Sopenharmony_ci		gvt_vgpu_err("vfio_group_get_external_user_from_dev failed\n");
8258c2ecf20Sopenharmony_ci		goto undo_register;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci	vdev->vfio_group = vfio_group;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/* Take a module reference as mdev core doesn't take
8308c2ecf20Sopenharmony_ci	 * a reference for vendor driver.
8318c2ecf20Sopenharmony_ci	 */
8328c2ecf20Sopenharmony_ci	if (!try_module_get(THIS_MODULE)) {
8338c2ecf20Sopenharmony_ci		ret = -ENODEV;
8348c2ecf20Sopenharmony_ci		goto undo_group;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	ret = kvmgt_guest_init(mdev);
8388c2ecf20Sopenharmony_ci	if (ret)
8398c2ecf20Sopenharmony_ci		goto undo_group;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	intel_gvt_ops->vgpu_activate(vgpu);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	atomic_set(&vdev->released, 0);
8448c2ecf20Sopenharmony_ci	return ret;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ciundo_group:
8478c2ecf20Sopenharmony_ci	vfio_group_put_external_user(vdev->vfio_group);
8488c2ecf20Sopenharmony_ci	vdev->vfio_group = NULL;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ciundo_register:
8518c2ecf20Sopenharmony_ci	vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
8528c2ecf20Sopenharmony_ci					&vdev->group_notifier);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ciundo_iommu:
8558c2ecf20Sopenharmony_ci	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
8568c2ecf20Sopenharmony_ci					&vdev->iommu_notifier);
8578c2ecf20Sopenharmony_ciout:
8588c2ecf20Sopenharmony_ci	return ret;
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic void intel_vgpu_release_msi_eventfd_ctx(struct intel_vgpu *vgpu)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
8648c2ecf20Sopenharmony_ci	struct eventfd_ctx *trigger;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	trigger = vdev->msi_trigger;
8678c2ecf20Sopenharmony_ci	if (trigger) {
8688c2ecf20Sopenharmony_ci		eventfd_ctx_put(trigger);
8698c2ecf20Sopenharmony_ci		vdev->msi_trigger = NULL;
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic void __intel_vgpu_release(struct intel_vgpu *vgpu)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
8768c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = vgpu->gvt->gt->i915;
8778c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
8788c2ecf20Sopenharmony_ci	int ret;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if (!handle_valid(vgpu->handle))
8818c2ecf20Sopenharmony_ci		return;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	if (atomic_cmpxchg(&vdev->released, 0, 1))
8848c2ecf20Sopenharmony_ci		return;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	intel_gvt_ops->vgpu_release(vgpu);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	ret = vfio_unregister_notifier(mdev_dev(vdev->mdev), VFIO_IOMMU_NOTIFY,
8898c2ecf20Sopenharmony_ci					&vdev->iommu_notifier);
8908c2ecf20Sopenharmony_ci	drm_WARN(&i915->drm, ret,
8918c2ecf20Sopenharmony_ci		 "vfio_unregister_notifier for iommu failed: %d\n", ret);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	ret = vfio_unregister_notifier(mdev_dev(vdev->mdev), VFIO_GROUP_NOTIFY,
8948c2ecf20Sopenharmony_ci					&vdev->group_notifier);
8958c2ecf20Sopenharmony_ci	drm_WARN(&i915->drm, ret,
8968c2ecf20Sopenharmony_ci		 "vfio_unregister_notifier for group failed: %d\n", ret);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	/* dereference module reference taken at open */
8998c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	info = (struct kvmgt_guest_info *)vgpu->handle;
9028c2ecf20Sopenharmony_ci	kvmgt_guest_exit(info);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	intel_vgpu_release_msi_eventfd_ctx(vgpu);
9058c2ecf20Sopenharmony_ci	vfio_group_put_external_user(vdev->vfio_group);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	vdev->kvm = NULL;
9088c2ecf20Sopenharmony_ci	vgpu->handle = 0;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic void intel_vgpu_release(struct mdev_device *mdev)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	__intel_vgpu_release(vgpu);
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic void intel_vgpu_release_work(struct work_struct *work)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = container_of(work, struct kvmgt_vdev,
9218c2ecf20Sopenharmony_ci					       release_work);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	__intel_vgpu_release(vdev->vgpu);
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_cistatic u64 intel_vgpu_get_bar_addr(struct intel_vgpu *vgpu, int bar)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci	u32 start_lo, start_hi;
9298c2ecf20Sopenharmony_ci	u32 mem_type;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	start_lo = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
9328c2ecf20Sopenharmony_ci			PCI_BASE_ADDRESS_MEM_MASK;
9338c2ecf20Sopenharmony_ci	mem_type = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
9348c2ecf20Sopenharmony_ci			PCI_BASE_ADDRESS_MEM_TYPE_MASK;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	switch (mem_type) {
9378c2ecf20Sopenharmony_ci	case PCI_BASE_ADDRESS_MEM_TYPE_64:
9388c2ecf20Sopenharmony_ci		start_hi = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space
9398c2ecf20Sopenharmony_ci						+ bar + 4));
9408c2ecf20Sopenharmony_ci		break;
9418c2ecf20Sopenharmony_ci	case PCI_BASE_ADDRESS_MEM_TYPE_32:
9428c2ecf20Sopenharmony_ci	case PCI_BASE_ADDRESS_MEM_TYPE_1M:
9438c2ecf20Sopenharmony_ci		/* 1M mem BAR treated as 32-bit BAR */
9448c2ecf20Sopenharmony_ci	default:
9458c2ecf20Sopenharmony_ci		/* mem unknown type treated as 32-bit BAR */
9468c2ecf20Sopenharmony_ci		start_hi = 0;
9478c2ecf20Sopenharmony_ci		break;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	return ((u64)start_hi << 32) | start_lo;
9518c2ecf20Sopenharmony_ci}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_cistatic int intel_vgpu_bar_rw(struct intel_vgpu *vgpu, int bar, u64 off,
9548c2ecf20Sopenharmony_ci			     void *buf, unsigned int count, bool is_write)
9558c2ecf20Sopenharmony_ci{
9568c2ecf20Sopenharmony_ci	u64 bar_start = intel_vgpu_get_bar_addr(vgpu, bar);
9578c2ecf20Sopenharmony_ci	int ret;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (is_write)
9608c2ecf20Sopenharmony_ci		ret = intel_gvt_ops->emulate_mmio_write(vgpu,
9618c2ecf20Sopenharmony_ci					bar_start + off, buf, count);
9628c2ecf20Sopenharmony_ci	else
9638c2ecf20Sopenharmony_ci		ret = intel_gvt_ops->emulate_mmio_read(vgpu,
9648c2ecf20Sopenharmony_ci					bar_start + off, buf, count);
9658c2ecf20Sopenharmony_ci	return ret;
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_cistatic inline bool intel_vgpu_in_aperture(struct intel_vgpu *vgpu, u64 off)
9698c2ecf20Sopenharmony_ci{
9708c2ecf20Sopenharmony_ci	return off >= vgpu_aperture_offset(vgpu) &&
9718c2ecf20Sopenharmony_ci	       off < vgpu_aperture_offset(vgpu) + vgpu_aperture_sz(vgpu);
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic int intel_vgpu_aperture_rw(struct intel_vgpu *vgpu, u64 off,
9758c2ecf20Sopenharmony_ci		void *buf, unsigned long count, bool is_write)
9768c2ecf20Sopenharmony_ci{
9778c2ecf20Sopenharmony_ci	void __iomem *aperture_va;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (!intel_vgpu_in_aperture(vgpu, off) ||
9808c2ecf20Sopenharmony_ci	    !intel_vgpu_in_aperture(vgpu, off + count)) {
9818c2ecf20Sopenharmony_ci		gvt_vgpu_err("Invalid aperture offset %llu\n", off);
9828c2ecf20Sopenharmony_ci		return -EINVAL;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	aperture_va = io_mapping_map_wc(&vgpu->gvt->gt->ggtt->iomap,
9868c2ecf20Sopenharmony_ci					ALIGN_DOWN(off, PAGE_SIZE),
9878c2ecf20Sopenharmony_ci					count + offset_in_page(off));
9888c2ecf20Sopenharmony_ci	if (!aperture_va)
9898c2ecf20Sopenharmony_ci		return -EIO;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	if (is_write)
9928c2ecf20Sopenharmony_ci		memcpy_toio(aperture_va + offset_in_page(off), buf, count);
9938c2ecf20Sopenharmony_ci	else
9948c2ecf20Sopenharmony_ci		memcpy_fromio(buf, aperture_va + offset_in_page(off), count);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	io_mapping_unmap(aperture_va);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	return 0;
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_cistatic ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf,
10028c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos, bool is_write)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
10058c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
10068c2ecf20Sopenharmony_ci	unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
10078c2ecf20Sopenharmony_ci	u64 pos = *ppos & VFIO_PCI_OFFSET_MASK;
10088c2ecf20Sopenharmony_ci	int ret = -EINVAL;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	if (index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions) {
10128c2ecf20Sopenharmony_ci		gvt_vgpu_err("invalid index: %u\n", index);
10138c2ecf20Sopenharmony_ci		return -EINVAL;
10148c2ecf20Sopenharmony_ci	}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	switch (index) {
10178c2ecf20Sopenharmony_ci	case VFIO_PCI_CONFIG_REGION_INDEX:
10188c2ecf20Sopenharmony_ci		if (is_write)
10198c2ecf20Sopenharmony_ci			ret = intel_gvt_ops->emulate_cfg_write(vgpu, pos,
10208c2ecf20Sopenharmony_ci						buf, count);
10218c2ecf20Sopenharmony_ci		else
10228c2ecf20Sopenharmony_ci			ret = intel_gvt_ops->emulate_cfg_read(vgpu, pos,
10238c2ecf20Sopenharmony_ci						buf, count);
10248c2ecf20Sopenharmony_ci		break;
10258c2ecf20Sopenharmony_ci	case VFIO_PCI_BAR0_REGION_INDEX:
10268c2ecf20Sopenharmony_ci		ret = intel_vgpu_bar_rw(vgpu, PCI_BASE_ADDRESS_0, pos,
10278c2ecf20Sopenharmony_ci					buf, count, is_write);
10288c2ecf20Sopenharmony_ci		break;
10298c2ecf20Sopenharmony_ci	case VFIO_PCI_BAR2_REGION_INDEX:
10308c2ecf20Sopenharmony_ci		ret = intel_vgpu_aperture_rw(vgpu, pos, buf, count, is_write);
10318c2ecf20Sopenharmony_ci		break;
10328c2ecf20Sopenharmony_ci	case VFIO_PCI_BAR1_REGION_INDEX:
10338c2ecf20Sopenharmony_ci	case VFIO_PCI_BAR3_REGION_INDEX:
10348c2ecf20Sopenharmony_ci	case VFIO_PCI_BAR4_REGION_INDEX:
10358c2ecf20Sopenharmony_ci	case VFIO_PCI_BAR5_REGION_INDEX:
10368c2ecf20Sopenharmony_ci	case VFIO_PCI_VGA_REGION_INDEX:
10378c2ecf20Sopenharmony_ci	case VFIO_PCI_ROM_REGION_INDEX:
10388c2ecf20Sopenharmony_ci		break;
10398c2ecf20Sopenharmony_ci	default:
10408c2ecf20Sopenharmony_ci		if (index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions)
10418c2ecf20Sopenharmony_ci			return -EINVAL;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci		index -= VFIO_PCI_NUM_REGIONS;
10448c2ecf20Sopenharmony_ci		return vdev->region[index].ops->rw(vgpu, buf, count,
10458c2ecf20Sopenharmony_ci				ppos, is_write);
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	return ret == 0 ? count : ret;
10498c2ecf20Sopenharmony_ci}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_cistatic bool gtt_entry(struct mdev_device *mdev, loff_t *ppos)
10528c2ecf20Sopenharmony_ci{
10538c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
10548c2ecf20Sopenharmony_ci	unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
10558c2ecf20Sopenharmony_ci	struct intel_gvt *gvt = vgpu->gvt;
10568c2ecf20Sopenharmony_ci	int offset;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* Only allow MMIO GGTT entry access */
10598c2ecf20Sopenharmony_ci	if (index != PCI_BASE_ADDRESS_0)
10608c2ecf20Sopenharmony_ci		return false;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	offset = (u64)(*ppos & VFIO_PCI_OFFSET_MASK) -
10638c2ecf20Sopenharmony_ci		intel_vgpu_get_bar_gpa(vgpu, PCI_BASE_ADDRESS_0);
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	return (offset >= gvt->device_info.gtt_start_offset &&
10668c2ecf20Sopenharmony_ci		offset < gvt->device_info.gtt_start_offset + gvt_ggtt_sz(gvt)) ?
10678c2ecf20Sopenharmony_ci			true : false;
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic ssize_t intel_vgpu_read(struct mdev_device *mdev, char __user *buf,
10718c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci	unsigned int done = 0;
10748c2ecf20Sopenharmony_ci	int ret;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	while (count) {
10778c2ecf20Sopenharmony_ci		size_t filled;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci		/* Only support GGTT entry 8 bytes read */
10808c2ecf20Sopenharmony_ci		if (count >= 8 && !(*ppos % 8) &&
10818c2ecf20Sopenharmony_ci			gtt_entry(mdev, ppos)) {
10828c2ecf20Sopenharmony_ci			u64 val;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci			ret = intel_vgpu_rw(mdev, (char *)&val, sizeof(val),
10858c2ecf20Sopenharmony_ci					ppos, false);
10868c2ecf20Sopenharmony_ci			if (ret <= 0)
10878c2ecf20Sopenharmony_ci				goto read_err;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci			if (copy_to_user(buf, &val, sizeof(val)))
10908c2ecf20Sopenharmony_ci				goto read_err;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci			filled = 8;
10938c2ecf20Sopenharmony_ci		} else if (count >= 4 && !(*ppos % 4)) {
10948c2ecf20Sopenharmony_ci			u32 val;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci			ret = intel_vgpu_rw(mdev, (char *)&val, sizeof(val),
10978c2ecf20Sopenharmony_ci					ppos, false);
10988c2ecf20Sopenharmony_ci			if (ret <= 0)
10998c2ecf20Sopenharmony_ci				goto read_err;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci			if (copy_to_user(buf, &val, sizeof(val)))
11028c2ecf20Sopenharmony_ci				goto read_err;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci			filled = 4;
11058c2ecf20Sopenharmony_ci		} else if (count >= 2 && !(*ppos % 2)) {
11068c2ecf20Sopenharmony_ci			u16 val;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci			ret = intel_vgpu_rw(mdev, (char *)&val, sizeof(val),
11098c2ecf20Sopenharmony_ci					ppos, false);
11108c2ecf20Sopenharmony_ci			if (ret <= 0)
11118c2ecf20Sopenharmony_ci				goto read_err;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci			if (copy_to_user(buf, &val, sizeof(val)))
11148c2ecf20Sopenharmony_ci				goto read_err;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci			filled = 2;
11178c2ecf20Sopenharmony_ci		} else {
11188c2ecf20Sopenharmony_ci			u8 val;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci			ret = intel_vgpu_rw(mdev, &val, sizeof(val), ppos,
11218c2ecf20Sopenharmony_ci					false);
11228c2ecf20Sopenharmony_ci			if (ret <= 0)
11238c2ecf20Sopenharmony_ci				goto read_err;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci			if (copy_to_user(buf, &val, sizeof(val)))
11268c2ecf20Sopenharmony_ci				goto read_err;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci			filled = 1;
11298c2ecf20Sopenharmony_ci		}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci		count -= filled;
11328c2ecf20Sopenharmony_ci		done += filled;
11338c2ecf20Sopenharmony_ci		*ppos += filled;
11348c2ecf20Sopenharmony_ci		buf += filled;
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	return done;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ciread_err:
11408c2ecf20Sopenharmony_ci	return -EFAULT;
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic ssize_t intel_vgpu_write(struct mdev_device *mdev,
11448c2ecf20Sopenharmony_ci				const char __user *buf,
11458c2ecf20Sopenharmony_ci				size_t count, loff_t *ppos)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	unsigned int done = 0;
11488c2ecf20Sopenharmony_ci	int ret;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	while (count) {
11518c2ecf20Sopenharmony_ci		size_t filled;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci		/* Only support GGTT entry 8 bytes write */
11548c2ecf20Sopenharmony_ci		if (count >= 8 && !(*ppos % 8) &&
11558c2ecf20Sopenharmony_ci			gtt_entry(mdev, ppos)) {
11568c2ecf20Sopenharmony_ci			u64 val;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci			if (copy_from_user(&val, buf, sizeof(val)))
11598c2ecf20Sopenharmony_ci				goto write_err;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci			ret = intel_vgpu_rw(mdev, (char *)&val, sizeof(val),
11628c2ecf20Sopenharmony_ci					ppos, true);
11638c2ecf20Sopenharmony_ci			if (ret <= 0)
11648c2ecf20Sopenharmony_ci				goto write_err;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci			filled = 8;
11678c2ecf20Sopenharmony_ci		} else if (count >= 4 && !(*ppos % 4)) {
11688c2ecf20Sopenharmony_ci			u32 val;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci			if (copy_from_user(&val, buf, sizeof(val)))
11718c2ecf20Sopenharmony_ci				goto write_err;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci			ret = intel_vgpu_rw(mdev, (char *)&val, sizeof(val),
11748c2ecf20Sopenharmony_ci					ppos, true);
11758c2ecf20Sopenharmony_ci			if (ret <= 0)
11768c2ecf20Sopenharmony_ci				goto write_err;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci			filled = 4;
11798c2ecf20Sopenharmony_ci		} else if (count >= 2 && !(*ppos % 2)) {
11808c2ecf20Sopenharmony_ci			u16 val;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci			if (copy_from_user(&val, buf, sizeof(val)))
11838c2ecf20Sopenharmony_ci				goto write_err;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci			ret = intel_vgpu_rw(mdev, (char *)&val,
11868c2ecf20Sopenharmony_ci					sizeof(val), ppos, true);
11878c2ecf20Sopenharmony_ci			if (ret <= 0)
11888c2ecf20Sopenharmony_ci				goto write_err;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci			filled = 2;
11918c2ecf20Sopenharmony_ci		} else {
11928c2ecf20Sopenharmony_ci			u8 val;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci			if (copy_from_user(&val, buf, sizeof(val)))
11958c2ecf20Sopenharmony_ci				goto write_err;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci			ret = intel_vgpu_rw(mdev, &val, sizeof(val),
11988c2ecf20Sopenharmony_ci					ppos, true);
11998c2ecf20Sopenharmony_ci			if (ret <= 0)
12008c2ecf20Sopenharmony_ci				goto write_err;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci			filled = 1;
12038c2ecf20Sopenharmony_ci		}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci		count -= filled;
12068c2ecf20Sopenharmony_ci		done += filled;
12078c2ecf20Sopenharmony_ci		*ppos += filled;
12088c2ecf20Sopenharmony_ci		buf += filled;
12098c2ecf20Sopenharmony_ci	}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	return done;
12128c2ecf20Sopenharmony_ciwrite_err:
12138c2ecf20Sopenharmony_ci	return -EFAULT;
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_cistatic int intel_vgpu_mmap(struct mdev_device *mdev, struct vm_area_struct *vma)
12178c2ecf20Sopenharmony_ci{
12188c2ecf20Sopenharmony_ci	unsigned int index;
12198c2ecf20Sopenharmony_ci	u64 virtaddr;
12208c2ecf20Sopenharmony_ci	unsigned long req_size, pgoff, req_start;
12218c2ecf20Sopenharmony_ci	pgprot_t pg_prot;
12228c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
12258c2ecf20Sopenharmony_ci	if (index >= VFIO_PCI_ROM_REGION_INDEX)
12268c2ecf20Sopenharmony_ci		return -EINVAL;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	if (vma->vm_end < vma->vm_start)
12298c2ecf20Sopenharmony_ci		return -EINVAL;
12308c2ecf20Sopenharmony_ci	if ((vma->vm_flags & VM_SHARED) == 0)
12318c2ecf20Sopenharmony_ci		return -EINVAL;
12328c2ecf20Sopenharmony_ci	if (index != VFIO_PCI_BAR2_REGION_INDEX)
12338c2ecf20Sopenharmony_ci		return -EINVAL;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	pg_prot = vma->vm_page_prot;
12368c2ecf20Sopenharmony_ci	virtaddr = vma->vm_start;
12378c2ecf20Sopenharmony_ci	req_size = vma->vm_end - vma->vm_start;
12388c2ecf20Sopenharmony_ci	pgoff = vma->vm_pgoff &
12398c2ecf20Sopenharmony_ci		((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
12408c2ecf20Sopenharmony_ci	req_start = pgoff << PAGE_SHIFT;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	if (!intel_vgpu_in_aperture(vgpu, req_start))
12438c2ecf20Sopenharmony_ci		return -EINVAL;
12448c2ecf20Sopenharmony_ci	if (req_start + req_size >
12458c2ecf20Sopenharmony_ci	    vgpu_aperture_offset(vgpu) + vgpu_aperture_sz(vgpu))
12468c2ecf20Sopenharmony_ci		return -EINVAL;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	pgoff = (gvt_aperture_pa_base(vgpu->gvt) >> PAGE_SHIFT) + pgoff;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	return remap_pfn_range(vma, virtaddr, pgoff, req_size, pg_prot);
12518c2ecf20Sopenharmony_ci}
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_cistatic int intel_vgpu_get_irq_count(struct intel_vgpu *vgpu, int type)
12548c2ecf20Sopenharmony_ci{
12558c2ecf20Sopenharmony_ci	if (type == VFIO_PCI_INTX_IRQ_INDEX || type == VFIO_PCI_MSI_IRQ_INDEX)
12568c2ecf20Sopenharmony_ci		return 1;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	return 0;
12598c2ecf20Sopenharmony_ci}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_cistatic int intel_vgpu_set_intx_mask(struct intel_vgpu *vgpu,
12628c2ecf20Sopenharmony_ci			unsigned int index, unsigned int start,
12638c2ecf20Sopenharmony_ci			unsigned int count, u32 flags,
12648c2ecf20Sopenharmony_ci			void *data)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	return 0;
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_cistatic int intel_vgpu_set_intx_unmask(struct intel_vgpu *vgpu,
12708c2ecf20Sopenharmony_ci			unsigned int index, unsigned int start,
12718c2ecf20Sopenharmony_ci			unsigned int count, u32 flags, void *data)
12728c2ecf20Sopenharmony_ci{
12738c2ecf20Sopenharmony_ci	return 0;
12748c2ecf20Sopenharmony_ci}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_cistatic int intel_vgpu_set_intx_trigger(struct intel_vgpu *vgpu,
12778c2ecf20Sopenharmony_ci		unsigned int index, unsigned int start, unsigned int count,
12788c2ecf20Sopenharmony_ci		u32 flags, void *data)
12798c2ecf20Sopenharmony_ci{
12808c2ecf20Sopenharmony_ci	return 0;
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic int intel_vgpu_set_msi_trigger(struct intel_vgpu *vgpu,
12848c2ecf20Sopenharmony_ci		unsigned int index, unsigned int start, unsigned int count,
12858c2ecf20Sopenharmony_ci		u32 flags, void *data)
12868c2ecf20Sopenharmony_ci{
12878c2ecf20Sopenharmony_ci	struct eventfd_ctx *trigger;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
12908c2ecf20Sopenharmony_ci		int fd = *(int *)data;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci		trigger = eventfd_ctx_fdget(fd);
12938c2ecf20Sopenharmony_ci		if (IS_ERR(trigger)) {
12948c2ecf20Sopenharmony_ci			gvt_vgpu_err("eventfd_ctx_fdget failed\n");
12958c2ecf20Sopenharmony_ci			return PTR_ERR(trigger);
12968c2ecf20Sopenharmony_ci		}
12978c2ecf20Sopenharmony_ci		kvmgt_vdev(vgpu)->msi_trigger = trigger;
12988c2ecf20Sopenharmony_ci	} else if ((flags & VFIO_IRQ_SET_DATA_NONE) && !count)
12998c2ecf20Sopenharmony_ci		intel_vgpu_release_msi_eventfd_ctx(vgpu);
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	return 0;
13028c2ecf20Sopenharmony_ci}
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_cistatic int intel_vgpu_set_irqs(struct intel_vgpu *vgpu, u32 flags,
13058c2ecf20Sopenharmony_ci		unsigned int index, unsigned int start, unsigned int count,
13068c2ecf20Sopenharmony_ci		void *data)
13078c2ecf20Sopenharmony_ci{
13088c2ecf20Sopenharmony_ci	int (*func)(struct intel_vgpu *vgpu, unsigned int index,
13098c2ecf20Sopenharmony_ci			unsigned int start, unsigned int count, u32 flags,
13108c2ecf20Sopenharmony_ci			void *data) = NULL;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	switch (index) {
13138c2ecf20Sopenharmony_ci	case VFIO_PCI_INTX_IRQ_INDEX:
13148c2ecf20Sopenharmony_ci		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
13158c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_MASK:
13168c2ecf20Sopenharmony_ci			func = intel_vgpu_set_intx_mask;
13178c2ecf20Sopenharmony_ci			break;
13188c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_UNMASK:
13198c2ecf20Sopenharmony_ci			func = intel_vgpu_set_intx_unmask;
13208c2ecf20Sopenharmony_ci			break;
13218c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_TRIGGER:
13228c2ecf20Sopenharmony_ci			func = intel_vgpu_set_intx_trigger;
13238c2ecf20Sopenharmony_ci			break;
13248c2ecf20Sopenharmony_ci		}
13258c2ecf20Sopenharmony_ci		break;
13268c2ecf20Sopenharmony_ci	case VFIO_PCI_MSI_IRQ_INDEX:
13278c2ecf20Sopenharmony_ci		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
13288c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_MASK:
13298c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_UNMASK:
13308c2ecf20Sopenharmony_ci			/* XXX Need masking support exported */
13318c2ecf20Sopenharmony_ci			break;
13328c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_TRIGGER:
13338c2ecf20Sopenharmony_ci			func = intel_vgpu_set_msi_trigger;
13348c2ecf20Sopenharmony_ci			break;
13358c2ecf20Sopenharmony_ci		}
13368c2ecf20Sopenharmony_ci		break;
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	if (!func)
13408c2ecf20Sopenharmony_ci		return -ENOTTY;
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	return func(vgpu, index, start, count, flags, data);
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_cistatic long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd,
13468c2ecf20Sopenharmony_ci			     unsigned long arg)
13478c2ecf20Sopenharmony_ci{
13488c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
13498c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
13508c2ecf20Sopenharmony_ci	unsigned long minsz;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	gvt_dbg_core("vgpu%d ioctl, cmd: %d\n", vgpu->id, cmd);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	if (cmd == VFIO_DEVICE_GET_INFO) {
13558c2ecf20Sopenharmony_ci		struct vfio_device_info info;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci		minsz = offsetofend(struct vfio_device_info, num_irqs);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci		if (copy_from_user(&info, (void __user *)arg, minsz))
13608c2ecf20Sopenharmony_ci			return -EFAULT;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci		if (info.argsz < minsz)
13638c2ecf20Sopenharmony_ci			return -EINVAL;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci		info.flags = VFIO_DEVICE_FLAGS_PCI;
13668c2ecf20Sopenharmony_ci		info.flags |= VFIO_DEVICE_FLAGS_RESET;
13678c2ecf20Sopenharmony_ci		info.num_regions = VFIO_PCI_NUM_REGIONS +
13688c2ecf20Sopenharmony_ci				vdev->num_regions;
13698c2ecf20Sopenharmony_ci		info.num_irqs = VFIO_PCI_NUM_IRQS;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci		return copy_to_user((void __user *)arg, &info, minsz) ?
13728c2ecf20Sopenharmony_ci			-EFAULT : 0;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	} else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
13758c2ecf20Sopenharmony_ci		struct vfio_region_info info;
13768c2ecf20Sopenharmony_ci		struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
13778c2ecf20Sopenharmony_ci		unsigned int i;
13788c2ecf20Sopenharmony_ci		int ret;
13798c2ecf20Sopenharmony_ci		struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
13808c2ecf20Sopenharmony_ci		int nr_areas = 1;
13818c2ecf20Sopenharmony_ci		int cap_type_id;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci		minsz = offsetofend(struct vfio_region_info, offset);
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci		if (copy_from_user(&info, (void __user *)arg, minsz))
13868c2ecf20Sopenharmony_ci			return -EFAULT;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci		if (info.argsz < minsz)
13898c2ecf20Sopenharmony_ci			return -EINVAL;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci		switch (info.index) {
13928c2ecf20Sopenharmony_ci		case VFIO_PCI_CONFIG_REGION_INDEX:
13938c2ecf20Sopenharmony_ci			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
13948c2ecf20Sopenharmony_ci			info.size = vgpu->gvt->device_info.cfg_space_size;
13958c2ecf20Sopenharmony_ci			info.flags = VFIO_REGION_INFO_FLAG_READ |
13968c2ecf20Sopenharmony_ci				     VFIO_REGION_INFO_FLAG_WRITE;
13978c2ecf20Sopenharmony_ci			break;
13988c2ecf20Sopenharmony_ci		case VFIO_PCI_BAR0_REGION_INDEX:
13998c2ecf20Sopenharmony_ci			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
14008c2ecf20Sopenharmony_ci			info.size = vgpu->cfg_space.bar[info.index].size;
14018c2ecf20Sopenharmony_ci			if (!info.size) {
14028c2ecf20Sopenharmony_ci				info.flags = 0;
14038c2ecf20Sopenharmony_ci				break;
14048c2ecf20Sopenharmony_ci			}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci			info.flags = VFIO_REGION_INFO_FLAG_READ |
14078c2ecf20Sopenharmony_ci				     VFIO_REGION_INFO_FLAG_WRITE;
14088c2ecf20Sopenharmony_ci			break;
14098c2ecf20Sopenharmony_ci		case VFIO_PCI_BAR1_REGION_INDEX:
14108c2ecf20Sopenharmony_ci			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
14118c2ecf20Sopenharmony_ci			info.size = 0;
14128c2ecf20Sopenharmony_ci			info.flags = 0;
14138c2ecf20Sopenharmony_ci			break;
14148c2ecf20Sopenharmony_ci		case VFIO_PCI_BAR2_REGION_INDEX:
14158c2ecf20Sopenharmony_ci			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
14168c2ecf20Sopenharmony_ci			info.flags = VFIO_REGION_INFO_FLAG_CAPS |
14178c2ecf20Sopenharmony_ci					VFIO_REGION_INFO_FLAG_MMAP |
14188c2ecf20Sopenharmony_ci					VFIO_REGION_INFO_FLAG_READ |
14198c2ecf20Sopenharmony_ci					VFIO_REGION_INFO_FLAG_WRITE;
14208c2ecf20Sopenharmony_ci			info.size = gvt_aperture_sz(vgpu->gvt);
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci			sparse = kzalloc(struct_size(sparse, areas, nr_areas),
14238c2ecf20Sopenharmony_ci					 GFP_KERNEL);
14248c2ecf20Sopenharmony_ci			if (!sparse)
14258c2ecf20Sopenharmony_ci				return -ENOMEM;
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci			sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
14288c2ecf20Sopenharmony_ci			sparse->header.version = 1;
14298c2ecf20Sopenharmony_ci			sparse->nr_areas = nr_areas;
14308c2ecf20Sopenharmony_ci			cap_type_id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
14318c2ecf20Sopenharmony_ci			sparse->areas[0].offset =
14328c2ecf20Sopenharmony_ci					PAGE_ALIGN(vgpu_aperture_offset(vgpu));
14338c2ecf20Sopenharmony_ci			sparse->areas[0].size = vgpu_aperture_sz(vgpu);
14348c2ecf20Sopenharmony_ci			break;
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci		case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
14378c2ecf20Sopenharmony_ci			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
14388c2ecf20Sopenharmony_ci			info.size = 0;
14398c2ecf20Sopenharmony_ci			info.flags = 0;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci			gvt_dbg_core("get region info bar:%d\n", info.index);
14428c2ecf20Sopenharmony_ci			break;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci		case VFIO_PCI_ROM_REGION_INDEX:
14458c2ecf20Sopenharmony_ci		case VFIO_PCI_VGA_REGION_INDEX:
14468c2ecf20Sopenharmony_ci			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
14478c2ecf20Sopenharmony_ci			info.size = 0;
14488c2ecf20Sopenharmony_ci			info.flags = 0;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci			gvt_dbg_core("get region info index:%d\n", info.index);
14518c2ecf20Sopenharmony_ci			break;
14528c2ecf20Sopenharmony_ci		default:
14538c2ecf20Sopenharmony_ci			{
14548c2ecf20Sopenharmony_ci				struct vfio_region_info_cap_type cap_type = {
14558c2ecf20Sopenharmony_ci					.header.id = VFIO_REGION_INFO_CAP_TYPE,
14568c2ecf20Sopenharmony_ci					.header.version = 1 };
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci				if (info.index >= VFIO_PCI_NUM_REGIONS +
14598c2ecf20Sopenharmony_ci						vdev->num_regions)
14608c2ecf20Sopenharmony_ci					return -EINVAL;
14618c2ecf20Sopenharmony_ci				info.index =
14628c2ecf20Sopenharmony_ci					array_index_nospec(info.index,
14638c2ecf20Sopenharmony_ci							VFIO_PCI_NUM_REGIONS +
14648c2ecf20Sopenharmony_ci							vdev->num_regions);
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci				i = info.index - VFIO_PCI_NUM_REGIONS;
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci				info.offset =
14698c2ecf20Sopenharmony_ci					VFIO_PCI_INDEX_TO_OFFSET(info.index);
14708c2ecf20Sopenharmony_ci				info.size = vdev->region[i].size;
14718c2ecf20Sopenharmony_ci				info.flags = vdev->region[i].flags;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci				cap_type.type = vdev->region[i].type;
14748c2ecf20Sopenharmony_ci				cap_type.subtype = vdev->region[i].subtype;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci				ret = vfio_info_add_capability(&caps,
14778c2ecf20Sopenharmony_ci							&cap_type.header,
14788c2ecf20Sopenharmony_ci							sizeof(cap_type));
14798c2ecf20Sopenharmony_ci				if (ret)
14808c2ecf20Sopenharmony_ci					return ret;
14818c2ecf20Sopenharmony_ci			}
14828c2ecf20Sopenharmony_ci		}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci		if ((info.flags & VFIO_REGION_INFO_FLAG_CAPS) && sparse) {
14858c2ecf20Sopenharmony_ci			switch (cap_type_id) {
14868c2ecf20Sopenharmony_ci			case VFIO_REGION_INFO_CAP_SPARSE_MMAP:
14878c2ecf20Sopenharmony_ci				ret = vfio_info_add_capability(&caps,
14888c2ecf20Sopenharmony_ci					&sparse->header,
14898c2ecf20Sopenharmony_ci					struct_size(sparse, areas,
14908c2ecf20Sopenharmony_ci						    sparse->nr_areas));
14918c2ecf20Sopenharmony_ci				if (ret) {
14928c2ecf20Sopenharmony_ci					kfree(sparse);
14938c2ecf20Sopenharmony_ci					return ret;
14948c2ecf20Sopenharmony_ci				}
14958c2ecf20Sopenharmony_ci				break;
14968c2ecf20Sopenharmony_ci			default:
14978c2ecf20Sopenharmony_ci				kfree(sparse);
14988c2ecf20Sopenharmony_ci				return -EINVAL;
14998c2ecf20Sopenharmony_ci			}
15008c2ecf20Sopenharmony_ci		}
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci		if (caps.size) {
15038c2ecf20Sopenharmony_ci			info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
15048c2ecf20Sopenharmony_ci			if (info.argsz < sizeof(info) + caps.size) {
15058c2ecf20Sopenharmony_ci				info.argsz = sizeof(info) + caps.size;
15068c2ecf20Sopenharmony_ci				info.cap_offset = 0;
15078c2ecf20Sopenharmony_ci			} else {
15088c2ecf20Sopenharmony_ci				vfio_info_cap_shift(&caps, sizeof(info));
15098c2ecf20Sopenharmony_ci				if (copy_to_user((void __user *)arg +
15108c2ecf20Sopenharmony_ci						  sizeof(info), caps.buf,
15118c2ecf20Sopenharmony_ci						  caps.size)) {
15128c2ecf20Sopenharmony_ci					kfree(caps.buf);
15138c2ecf20Sopenharmony_ci					kfree(sparse);
15148c2ecf20Sopenharmony_ci					return -EFAULT;
15158c2ecf20Sopenharmony_ci				}
15168c2ecf20Sopenharmony_ci				info.cap_offset = sizeof(info);
15178c2ecf20Sopenharmony_ci			}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci			kfree(caps.buf);
15208c2ecf20Sopenharmony_ci		}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci		kfree(sparse);
15238c2ecf20Sopenharmony_ci		return copy_to_user((void __user *)arg, &info, minsz) ?
15248c2ecf20Sopenharmony_ci			-EFAULT : 0;
15258c2ecf20Sopenharmony_ci	} else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
15268c2ecf20Sopenharmony_ci		struct vfio_irq_info info;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci		minsz = offsetofend(struct vfio_irq_info, count);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci		if (copy_from_user(&info, (void __user *)arg, minsz))
15318c2ecf20Sopenharmony_ci			return -EFAULT;
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci		if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
15348c2ecf20Sopenharmony_ci			return -EINVAL;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci		switch (info.index) {
15378c2ecf20Sopenharmony_ci		case VFIO_PCI_INTX_IRQ_INDEX:
15388c2ecf20Sopenharmony_ci		case VFIO_PCI_MSI_IRQ_INDEX:
15398c2ecf20Sopenharmony_ci			break;
15408c2ecf20Sopenharmony_ci		default:
15418c2ecf20Sopenharmony_ci			return -EINVAL;
15428c2ecf20Sopenharmony_ci		}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci		info.flags = VFIO_IRQ_INFO_EVENTFD;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci		info.count = intel_vgpu_get_irq_count(vgpu, info.index);
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci		if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
15498c2ecf20Sopenharmony_ci			info.flags |= (VFIO_IRQ_INFO_MASKABLE |
15508c2ecf20Sopenharmony_ci				       VFIO_IRQ_INFO_AUTOMASKED);
15518c2ecf20Sopenharmony_ci		else
15528c2ecf20Sopenharmony_ci			info.flags |= VFIO_IRQ_INFO_NORESIZE;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci		return copy_to_user((void __user *)arg, &info, minsz) ?
15558c2ecf20Sopenharmony_ci			-EFAULT : 0;
15568c2ecf20Sopenharmony_ci	} else if (cmd == VFIO_DEVICE_SET_IRQS) {
15578c2ecf20Sopenharmony_ci		struct vfio_irq_set hdr;
15588c2ecf20Sopenharmony_ci		u8 *data = NULL;
15598c2ecf20Sopenharmony_ci		int ret = 0;
15608c2ecf20Sopenharmony_ci		size_t data_size = 0;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci		minsz = offsetofend(struct vfio_irq_set, count);
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci		if (copy_from_user(&hdr, (void __user *)arg, minsz))
15658c2ecf20Sopenharmony_ci			return -EFAULT;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci		if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
15688c2ecf20Sopenharmony_ci			int max = intel_vgpu_get_irq_count(vgpu, hdr.index);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci			ret = vfio_set_irqs_validate_and_prepare(&hdr, max,
15718c2ecf20Sopenharmony_ci						VFIO_PCI_NUM_IRQS, &data_size);
15728c2ecf20Sopenharmony_ci			if (ret) {
15738c2ecf20Sopenharmony_ci				gvt_vgpu_err("intel:vfio_set_irqs_validate_and_prepare failed\n");
15748c2ecf20Sopenharmony_ci				return -EINVAL;
15758c2ecf20Sopenharmony_ci			}
15768c2ecf20Sopenharmony_ci			if (data_size) {
15778c2ecf20Sopenharmony_ci				data = memdup_user((void __user *)(arg + minsz),
15788c2ecf20Sopenharmony_ci						   data_size);
15798c2ecf20Sopenharmony_ci				if (IS_ERR(data))
15808c2ecf20Sopenharmony_ci					return PTR_ERR(data);
15818c2ecf20Sopenharmony_ci			}
15828c2ecf20Sopenharmony_ci		}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci		ret = intel_vgpu_set_irqs(vgpu, hdr.flags, hdr.index,
15858c2ecf20Sopenharmony_ci					hdr.start, hdr.count, data);
15868c2ecf20Sopenharmony_ci		kfree(data);
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci		return ret;
15898c2ecf20Sopenharmony_ci	} else if (cmd == VFIO_DEVICE_RESET) {
15908c2ecf20Sopenharmony_ci		intel_gvt_ops->vgpu_reset(vgpu);
15918c2ecf20Sopenharmony_ci		return 0;
15928c2ecf20Sopenharmony_ci	} else if (cmd == VFIO_DEVICE_QUERY_GFX_PLANE) {
15938c2ecf20Sopenharmony_ci		struct vfio_device_gfx_plane_info dmabuf;
15948c2ecf20Sopenharmony_ci		int ret = 0;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci		minsz = offsetofend(struct vfio_device_gfx_plane_info,
15978c2ecf20Sopenharmony_ci				    dmabuf_id);
15988c2ecf20Sopenharmony_ci		if (copy_from_user(&dmabuf, (void __user *)arg, minsz))
15998c2ecf20Sopenharmony_ci			return -EFAULT;
16008c2ecf20Sopenharmony_ci		if (dmabuf.argsz < minsz)
16018c2ecf20Sopenharmony_ci			return -EINVAL;
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci		ret = intel_gvt_ops->vgpu_query_plane(vgpu, &dmabuf);
16048c2ecf20Sopenharmony_ci		if (ret != 0)
16058c2ecf20Sopenharmony_ci			return ret;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci		return copy_to_user((void __user *)arg, &dmabuf, minsz) ?
16088c2ecf20Sopenharmony_ci								-EFAULT : 0;
16098c2ecf20Sopenharmony_ci	} else if (cmd == VFIO_DEVICE_GET_GFX_DMABUF) {
16108c2ecf20Sopenharmony_ci		__u32 dmabuf_id;
16118c2ecf20Sopenharmony_ci		__s32 dmabuf_fd;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci		if (get_user(dmabuf_id, (__u32 __user *)arg))
16148c2ecf20Sopenharmony_ci			return -EFAULT;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci		dmabuf_fd = intel_gvt_ops->vgpu_get_dmabuf(vgpu, dmabuf_id);
16178c2ecf20Sopenharmony_ci		return dmabuf_fd;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	}
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	return -ENOTTY;
16228c2ecf20Sopenharmony_ci}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_cistatic ssize_t
16258c2ecf20Sopenharmony_civgpu_id_show(struct device *dev, struct device_attribute *attr,
16268c2ecf20Sopenharmony_ci	     char *buf)
16278c2ecf20Sopenharmony_ci{
16288c2ecf20Sopenharmony_ci	struct mdev_device *mdev = mdev_from_dev(dev);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	if (mdev) {
16318c2ecf20Sopenharmony_ci		struct intel_vgpu *vgpu = (struct intel_vgpu *)
16328c2ecf20Sopenharmony_ci			mdev_get_drvdata(mdev);
16338c2ecf20Sopenharmony_ci		return sprintf(buf, "%d\n", vgpu->id);
16348c2ecf20Sopenharmony_ci	}
16358c2ecf20Sopenharmony_ci	return sprintf(buf, "\n");
16368c2ecf20Sopenharmony_ci}
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(vgpu_id);
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_cistatic struct attribute *intel_vgpu_attrs[] = {
16418c2ecf20Sopenharmony_ci	&dev_attr_vgpu_id.attr,
16428c2ecf20Sopenharmony_ci	NULL
16438c2ecf20Sopenharmony_ci};
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_cistatic const struct attribute_group intel_vgpu_group = {
16468c2ecf20Sopenharmony_ci	.name = "intel_vgpu",
16478c2ecf20Sopenharmony_ci	.attrs = intel_vgpu_attrs,
16488c2ecf20Sopenharmony_ci};
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_cistatic const struct attribute_group *intel_vgpu_groups[] = {
16518c2ecf20Sopenharmony_ci	&intel_vgpu_group,
16528c2ecf20Sopenharmony_ci	NULL,
16538c2ecf20Sopenharmony_ci};
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_cistatic struct mdev_parent_ops intel_vgpu_ops = {
16568c2ecf20Sopenharmony_ci	.mdev_attr_groups       = intel_vgpu_groups,
16578c2ecf20Sopenharmony_ci	.create			= intel_vgpu_create,
16588c2ecf20Sopenharmony_ci	.remove			= intel_vgpu_remove,
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	.open			= intel_vgpu_open,
16618c2ecf20Sopenharmony_ci	.release		= intel_vgpu_release,
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	.read			= intel_vgpu_read,
16648c2ecf20Sopenharmony_ci	.write			= intel_vgpu_write,
16658c2ecf20Sopenharmony_ci	.mmap			= intel_vgpu_mmap,
16668c2ecf20Sopenharmony_ci	.ioctl			= intel_vgpu_ioctl,
16678c2ecf20Sopenharmony_ci};
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_cistatic int kvmgt_host_init(struct device *dev, void *gvt, const void *ops)
16708c2ecf20Sopenharmony_ci{
16718c2ecf20Sopenharmony_ci	struct attribute_group **kvm_vgpu_type_groups;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	intel_gvt_ops = ops;
16748c2ecf20Sopenharmony_ci	if (!intel_gvt_ops->get_gvt_attrs(&kvm_vgpu_type_groups))
16758c2ecf20Sopenharmony_ci		return -EFAULT;
16768c2ecf20Sopenharmony_ci	intel_vgpu_ops.supported_type_groups = kvm_vgpu_type_groups;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	return mdev_register_device(dev, &intel_vgpu_ops);
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_cistatic void kvmgt_host_exit(struct device *dev)
16828c2ecf20Sopenharmony_ci{
16838c2ecf20Sopenharmony_ci	mdev_unregister_device(dev);
16848c2ecf20Sopenharmony_ci}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_cistatic int kvmgt_page_track_add(unsigned long handle, u64 gfn)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
16898c2ecf20Sopenharmony_ci	struct kvm *kvm;
16908c2ecf20Sopenharmony_ci	struct kvm_memory_slot *slot;
16918c2ecf20Sopenharmony_ci	int idx;
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
16948c2ecf20Sopenharmony_ci		return -ESRCH;
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	info = (struct kvmgt_guest_info *)handle;
16978c2ecf20Sopenharmony_ci	kvm = info->kvm;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&kvm->srcu);
17008c2ecf20Sopenharmony_ci	slot = gfn_to_memslot(kvm, gfn);
17018c2ecf20Sopenharmony_ci	if (!slot) {
17028c2ecf20Sopenharmony_ci		srcu_read_unlock(&kvm->srcu, idx);
17038c2ecf20Sopenharmony_ci		return -EINVAL;
17048c2ecf20Sopenharmony_ci	}
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	spin_lock(&kvm->mmu_lock);
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	if (kvmgt_gfn_is_write_protected(info, gfn))
17098c2ecf20Sopenharmony_ci		goto out;
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	kvm_slot_page_track_add_page(kvm, slot, gfn, KVM_PAGE_TRACK_WRITE);
17128c2ecf20Sopenharmony_ci	kvmgt_protect_table_add(info, gfn);
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ciout:
17158c2ecf20Sopenharmony_ci	spin_unlock(&kvm->mmu_lock);
17168c2ecf20Sopenharmony_ci	srcu_read_unlock(&kvm->srcu, idx);
17178c2ecf20Sopenharmony_ci	return 0;
17188c2ecf20Sopenharmony_ci}
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_cistatic int kvmgt_page_track_remove(unsigned long handle, u64 gfn)
17218c2ecf20Sopenharmony_ci{
17228c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
17238c2ecf20Sopenharmony_ci	struct kvm *kvm;
17248c2ecf20Sopenharmony_ci	struct kvm_memory_slot *slot;
17258c2ecf20Sopenharmony_ci	int idx;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
17288c2ecf20Sopenharmony_ci		return 0;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	info = (struct kvmgt_guest_info *)handle;
17318c2ecf20Sopenharmony_ci	kvm = info->kvm;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&kvm->srcu);
17348c2ecf20Sopenharmony_ci	slot = gfn_to_memslot(kvm, gfn);
17358c2ecf20Sopenharmony_ci	if (!slot) {
17368c2ecf20Sopenharmony_ci		srcu_read_unlock(&kvm->srcu, idx);
17378c2ecf20Sopenharmony_ci		return -EINVAL;
17388c2ecf20Sopenharmony_ci	}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	spin_lock(&kvm->mmu_lock);
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	if (!kvmgt_gfn_is_write_protected(info, gfn))
17438c2ecf20Sopenharmony_ci		goto out;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	kvm_slot_page_track_remove_page(kvm, slot, gfn, KVM_PAGE_TRACK_WRITE);
17468c2ecf20Sopenharmony_ci	kvmgt_protect_table_del(info, gfn);
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ciout:
17498c2ecf20Sopenharmony_ci	spin_unlock(&kvm->mmu_lock);
17508c2ecf20Sopenharmony_ci	srcu_read_unlock(&kvm->srcu, idx);
17518c2ecf20Sopenharmony_ci	return 0;
17528c2ecf20Sopenharmony_ci}
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_cistatic void kvmgt_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa,
17558c2ecf20Sopenharmony_ci		const u8 *val, int len,
17568c2ecf20Sopenharmony_ci		struct kvm_page_track_notifier_node *node)
17578c2ecf20Sopenharmony_ci{
17588c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info = container_of(node,
17598c2ecf20Sopenharmony_ci					struct kvmgt_guest_info, track_node);
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	if (kvmgt_gfn_is_write_protected(info, gpa_to_gfn(gpa)))
17628c2ecf20Sopenharmony_ci		intel_gvt_ops->write_protect_handler(info->vgpu, gpa,
17638c2ecf20Sopenharmony_ci						     (void *)val, len);
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic void kvmgt_page_track_flush_slot(struct kvm *kvm,
17678c2ecf20Sopenharmony_ci		struct kvm_memory_slot *slot,
17688c2ecf20Sopenharmony_ci		struct kvm_page_track_notifier_node *node)
17698c2ecf20Sopenharmony_ci{
17708c2ecf20Sopenharmony_ci	int i;
17718c2ecf20Sopenharmony_ci	gfn_t gfn;
17728c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info = container_of(node,
17738c2ecf20Sopenharmony_ci					struct kvmgt_guest_info, track_node);
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	spin_lock(&kvm->mmu_lock);
17768c2ecf20Sopenharmony_ci	for (i = 0; i < slot->npages; i++) {
17778c2ecf20Sopenharmony_ci		gfn = slot->base_gfn + i;
17788c2ecf20Sopenharmony_ci		if (kvmgt_gfn_is_write_protected(info, gfn)) {
17798c2ecf20Sopenharmony_ci			kvm_slot_page_track_remove_page(kvm, slot, gfn,
17808c2ecf20Sopenharmony_ci						KVM_PAGE_TRACK_WRITE);
17818c2ecf20Sopenharmony_ci			kvmgt_protect_table_del(info, gfn);
17828c2ecf20Sopenharmony_ci		}
17838c2ecf20Sopenharmony_ci	}
17848c2ecf20Sopenharmony_ci	spin_unlock(&kvm->mmu_lock);
17858c2ecf20Sopenharmony_ci}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_cistatic bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu, struct kvm *kvm)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	struct intel_vgpu *itr;
17908c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
17918c2ecf20Sopenharmony_ci	int id;
17928c2ecf20Sopenharmony_ci	bool ret = false;
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	mutex_lock(&vgpu->gvt->lock);
17958c2ecf20Sopenharmony_ci	for_each_active_vgpu(vgpu->gvt, itr, id) {
17968c2ecf20Sopenharmony_ci		if (!handle_valid(itr->handle))
17978c2ecf20Sopenharmony_ci			continue;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci		info = (struct kvmgt_guest_info *)itr->handle;
18008c2ecf20Sopenharmony_ci		if (kvm && kvm == info->kvm) {
18018c2ecf20Sopenharmony_ci			ret = true;
18028c2ecf20Sopenharmony_ci			goto out;
18038c2ecf20Sopenharmony_ci		}
18048c2ecf20Sopenharmony_ci	}
18058c2ecf20Sopenharmony_ciout:
18068c2ecf20Sopenharmony_ci	mutex_unlock(&vgpu->gvt->lock);
18078c2ecf20Sopenharmony_ci	return ret;
18088c2ecf20Sopenharmony_ci}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_cistatic int kvmgt_guest_init(struct mdev_device *mdev)
18118c2ecf20Sopenharmony_ci{
18128c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
18138c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu;
18148c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev;
18158c2ecf20Sopenharmony_ci	struct kvm *kvm;
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	vgpu = mdev_get_drvdata(mdev);
18188c2ecf20Sopenharmony_ci	if (handle_valid(vgpu->handle))
18198c2ecf20Sopenharmony_ci		return -EEXIST;
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	vdev = kvmgt_vdev(vgpu);
18228c2ecf20Sopenharmony_ci	kvm = vdev->kvm;
18238c2ecf20Sopenharmony_ci	if (!kvm || kvm->mm != current->mm) {
18248c2ecf20Sopenharmony_ci		gvt_vgpu_err("KVM is required to use Intel vGPU\n");
18258c2ecf20Sopenharmony_ci		return -ESRCH;
18268c2ecf20Sopenharmony_ci	}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	if (__kvmgt_vgpu_exist(vgpu, kvm))
18298c2ecf20Sopenharmony_ci		return -EEXIST;
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	info = vzalloc(sizeof(struct kvmgt_guest_info));
18328c2ecf20Sopenharmony_ci	if (!info)
18338c2ecf20Sopenharmony_ci		return -ENOMEM;
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	vgpu->handle = (unsigned long)info;
18368c2ecf20Sopenharmony_ci	info->vgpu = vgpu;
18378c2ecf20Sopenharmony_ci	info->kvm = kvm;
18388c2ecf20Sopenharmony_ci	kvm_get_kvm(info->kvm);
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	kvmgt_protect_table_init(info);
18418c2ecf20Sopenharmony_ci	gvt_cache_init(vgpu);
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	info->track_node.track_write = kvmgt_page_track_write;
18448c2ecf20Sopenharmony_ci	info->track_node.track_flush_slot = kvmgt_page_track_flush_slot;
18458c2ecf20Sopenharmony_ci	kvm_page_track_register_notifier(kvm, &info->track_node);
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	info->debugfs_cache_entries = debugfs_create_ulong(
18488c2ecf20Sopenharmony_ci						"kvmgt_nr_cache_entries",
18498c2ecf20Sopenharmony_ci						0444, vgpu->debugfs,
18508c2ecf20Sopenharmony_ci						&vdev->nr_cache_entries);
18518c2ecf20Sopenharmony_ci	return 0;
18528c2ecf20Sopenharmony_ci}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_cistatic bool kvmgt_guest_exit(struct kvmgt_guest_info *info)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci	debugfs_remove(info->debugfs_cache_entries);
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci	kvm_page_track_unregister_notifier(info->kvm, &info->track_node);
18598c2ecf20Sopenharmony_ci	kvm_put_kvm(info->kvm);
18608c2ecf20Sopenharmony_ci	kvmgt_protect_table_destroy(info);
18618c2ecf20Sopenharmony_ci	gvt_cache_destroy(info->vgpu);
18628c2ecf20Sopenharmony_ci	vfree(info);
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	return true;
18658c2ecf20Sopenharmony_ci}
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_cistatic int kvmgt_attach_vgpu(void *p_vgpu, unsigned long *handle)
18688c2ecf20Sopenharmony_ci{
18698c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	vgpu->vdev = kzalloc(sizeof(struct kvmgt_vdev), GFP_KERNEL);
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	if (!vgpu->vdev)
18748c2ecf20Sopenharmony_ci		return -ENOMEM;
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	kvmgt_vdev(vgpu)->vgpu = vgpu;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	return 0;
18798c2ecf20Sopenharmony_ci}
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_cistatic void kvmgt_detach_vgpu(void *p_vgpu)
18828c2ecf20Sopenharmony_ci{
18838c2ecf20Sopenharmony_ci	int i;
18848c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
18858c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	if (!vdev->region)
18888c2ecf20Sopenharmony_ci		return;
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	for (i = 0; i < vdev->num_regions; i++)
18918c2ecf20Sopenharmony_ci		if (vdev->region[i].ops->release)
18928c2ecf20Sopenharmony_ci			vdev->region[i].ops->release(vgpu,
18938c2ecf20Sopenharmony_ci					&vdev->region[i]);
18948c2ecf20Sopenharmony_ci	vdev->num_regions = 0;
18958c2ecf20Sopenharmony_ci	kfree(vdev->region);
18968c2ecf20Sopenharmony_ci	vdev->region = NULL;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	kfree(vdev);
18998c2ecf20Sopenharmony_ci}
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_cistatic int kvmgt_inject_msi(unsigned long handle, u32 addr, u16 data)
19028c2ecf20Sopenharmony_ci{
19038c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
19048c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu;
19058c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev;
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
19088c2ecf20Sopenharmony_ci		return -ESRCH;
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	info = (struct kvmgt_guest_info *)handle;
19118c2ecf20Sopenharmony_ci	vgpu = info->vgpu;
19128c2ecf20Sopenharmony_ci	vdev = kvmgt_vdev(vgpu);
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	/*
19158c2ecf20Sopenharmony_ci	 * When guest is poweroff, msi_trigger is set to NULL, but vgpu's
19168c2ecf20Sopenharmony_ci	 * config and mmio register isn't restored to default during guest
19178c2ecf20Sopenharmony_ci	 * poweroff. If this vgpu is still used in next vm, this vgpu's pipe
19188c2ecf20Sopenharmony_ci	 * may be enabled, then once this vgpu is active, it will get inject
19198c2ecf20Sopenharmony_ci	 * vblank interrupt request. But msi_trigger is null until msi is
19208c2ecf20Sopenharmony_ci	 * enabled by guest. so if msi_trigger is null, success is still
19218c2ecf20Sopenharmony_ci	 * returned and don't inject interrupt into guest.
19228c2ecf20Sopenharmony_ci	 */
19238c2ecf20Sopenharmony_ci	if (vdev->msi_trigger == NULL)
19248c2ecf20Sopenharmony_ci		return 0;
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	if (eventfd_signal(vdev->msi_trigger, 1) == 1)
19278c2ecf20Sopenharmony_ci		return 0;
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	return -EFAULT;
19308c2ecf20Sopenharmony_ci}
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_cistatic unsigned long kvmgt_gfn_to_pfn(unsigned long handle, unsigned long gfn)
19338c2ecf20Sopenharmony_ci{
19348c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
19358c2ecf20Sopenharmony_ci	kvm_pfn_t pfn;
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
19388c2ecf20Sopenharmony_ci		return INTEL_GVT_INVALID_ADDR;
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	info = (struct kvmgt_guest_info *)handle;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	pfn = gfn_to_pfn(info->kvm, gfn);
19438c2ecf20Sopenharmony_ci	if (is_error_noslot_pfn(pfn))
19448c2ecf20Sopenharmony_ci		return INTEL_GVT_INVALID_ADDR;
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci	return pfn;
19478c2ecf20Sopenharmony_ci}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_cistatic int kvmgt_dma_map_guest_page(unsigned long handle, unsigned long gfn,
19508c2ecf20Sopenharmony_ci		unsigned long size, dma_addr_t *dma_addr)
19518c2ecf20Sopenharmony_ci{
19528c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu;
19538c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev;
19548c2ecf20Sopenharmony_ci	struct gvt_dma *entry;
19558c2ecf20Sopenharmony_ci	int ret;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
19588c2ecf20Sopenharmony_ci		return -EINVAL;
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	vgpu = ((struct kvmgt_guest_info *)handle)->vgpu;
19618c2ecf20Sopenharmony_ci	vdev = kvmgt_vdev(vgpu);
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	mutex_lock(&vdev->cache_lock);
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci	entry = __gvt_cache_find_gfn(vgpu, gfn);
19668c2ecf20Sopenharmony_ci	if (!entry) {
19678c2ecf20Sopenharmony_ci		ret = gvt_dma_map_page(vgpu, gfn, dma_addr, size);
19688c2ecf20Sopenharmony_ci		if (ret)
19698c2ecf20Sopenharmony_ci			goto err_unlock;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci		ret = __gvt_cache_add(vgpu, gfn, *dma_addr, size);
19728c2ecf20Sopenharmony_ci		if (ret)
19738c2ecf20Sopenharmony_ci			goto err_unmap;
19748c2ecf20Sopenharmony_ci	} else if (entry->size != size) {
19758c2ecf20Sopenharmony_ci		/* the same gfn with different size: unmap and re-map */
19768c2ecf20Sopenharmony_ci		gvt_dma_unmap_page(vgpu, gfn, entry->dma_addr, entry->size);
19778c2ecf20Sopenharmony_ci		__gvt_cache_remove_entry(vgpu, entry);
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci		ret = gvt_dma_map_page(vgpu, gfn, dma_addr, size);
19808c2ecf20Sopenharmony_ci		if (ret)
19818c2ecf20Sopenharmony_ci			goto err_unlock;
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci		ret = __gvt_cache_add(vgpu, gfn, *dma_addr, size);
19848c2ecf20Sopenharmony_ci		if (ret)
19858c2ecf20Sopenharmony_ci			goto err_unmap;
19868c2ecf20Sopenharmony_ci	} else {
19878c2ecf20Sopenharmony_ci		kref_get(&entry->ref);
19888c2ecf20Sopenharmony_ci		*dma_addr = entry->dma_addr;
19898c2ecf20Sopenharmony_ci	}
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	mutex_unlock(&vdev->cache_lock);
19928c2ecf20Sopenharmony_ci	return 0;
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_cierr_unmap:
19958c2ecf20Sopenharmony_ci	gvt_dma_unmap_page(vgpu, gfn, *dma_addr, size);
19968c2ecf20Sopenharmony_cierr_unlock:
19978c2ecf20Sopenharmony_ci	mutex_unlock(&vdev->cache_lock);
19988c2ecf20Sopenharmony_ci	return ret;
19998c2ecf20Sopenharmony_ci}
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_cistatic int kvmgt_dma_pin_guest_page(unsigned long handle, dma_addr_t dma_addr)
20028c2ecf20Sopenharmony_ci{
20038c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
20048c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev;
20058c2ecf20Sopenharmony_ci	struct gvt_dma *entry;
20068c2ecf20Sopenharmony_ci	int ret = 0;
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
20098c2ecf20Sopenharmony_ci		return -ENODEV;
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	info = (struct kvmgt_guest_info *)handle;
20128c2ecf20Sopenharmony_ci	vdev = kvmgt_vdev(info->vgpu);
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	mutex_lock(&vdev->cache_lock);
20158c2ecf20Sopenharmony_ci	entry = __gvt_cache_find_dma_addr(info->vgpu, dma_addr);
20168c2ecf20Sopenharmony_ci	if (entry)
20178c2ecf20Sopenharmony_ci		kref_get(&entry->ref);
20188c2ecf20Sopenharmony_ci	else
20198c2ecf20Sopenharmony_ci		ret = -ENOMEM;
20208c2ecf20Sopenharmony_ci	mutex_unlock(&vdev->cache_lock);
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	return ret;
20238c2ecf20Sopenharmony_ci}
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_cistatic void __gvt_dma_release(struct kref *ref)
20268c2ecf20Sopenharmony_ci{
20278c2ecf20Sopenharmony_ci	struct gvt_dma *entry = container_of(ref, typeof(*entry), ref);
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	gvt_dma_unmap_page(entry->vgpu, entry->gfn, entry->dma_addr,
20308c2ecf20Sopenharmony_ci			   entry->size);
20318c2ecf20Sopenharmony_ci	__gvt_cache_remove_entry(entry->vgpu, entry);
20328c2ecf20Sopenharmony_ci}
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_cistatic void kvmgt_dma_unmap_guest_page(unsigned long handle, dma_addr_t dma_addr)
20358c2ecf20Sopenharmony_ci{
20368c2ecf20Sopenharmony_ci	struct intel_vgpu *vgpu;
20378c2ecf20Sopenharmony_ci	struct kvmgt_vdev *vdev;
20388c2ecf20Sopenharmony_ci	struct gvt_dma *entry;
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
20418c2ecf20Sopenharmony_ci		return;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	vgpu = ((struct kvmgt_guest_info *)handle)->vgpu;
20448c2ecf20Sopenharmony_ci	vdev = kvmgt_vdev(vgpu);
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci	mutex_lock(&vdev->cache_lock);
20478c2ecf20Sopenharmony_ci	entry = __gvt_cache_find_dma_addr(vgpu, dma_addr);
20488c2ecf20Sopenharmony_ci	if (entry)
20498c2ecf20Sopenharmony_ci		kref_put(&entry->ref, __gvt_dma_release);
20508c2ecf20Sopenharmony_ci	mutex_unlock(&vdev->cache_lock);
20518c2ecf20Sopenharmony_ci}
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_cistatic int kvmgt_rw_gpa(unsigned long handle, unsigned long gpa,
20548c2ecf20Sopenharmony_ci			void *buf, unsigned long len, bool write)
20558c2ecf20Sopenharmony_ci{
20568c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
20598c2ecf20Sopenharmony_ci		return -ESRCH;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	info = (struct kvmgt_guest_info *)handle;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	return vfio_dma_rw(kvmgt_vdev(info->vgpu)->vfio_group,
20648c2ecf20Sopenharmony_ci			   gpa, buf, len, write);
20658c2ecf20Sopenharmony_ci}
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_cistatic int kvmgt_read_gpa(unsigned long handle, unsigned long gpa,
20688c2ecf20Sopenharmony_ci			void *buf, unsigned long len)
20698c2ecf20Sopenharmony_ci{
20708c2ecf20Sopenharmony_ci	return kvmgt_rw_gpa(handle, gpa, buf, len, false);
20718c2ecf20Sopenharmony_ci}
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_cistatic int kvmgt_write_gpa(unsigned long handle, unsigned long gpa,
20748c2ecf20Sopenharmony_ci			void *buf, unsigned long len)
20758c2ecf20Sopenharmony_ci{
20768c2ecf20Sopenharmony_ci	return kvmgt_rw_gpa(handle, gpa, buf, len, true);
20778c2ecf20Sopenharmony_ci}
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_cistatic unsigned long kvmgt_virt_to_pfn(void *addr)
20808c2ecf20Sopenharmony_ci{
20818c2ecf20Sopenharmony_ci	return PFN_DOWN(__pa(addr));
20828c2ecf20Sopenharmony_ci}
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_cistatic bool kvmgt_is_valid_gfn(unsigned long handle, unsigned long gfn)
20858c2ecf20Sopenharmony_ci{
20868c2ecf20Sopenharmony_ci	struct kvmgt_guest_info *info;
20878c2ecf20Sopenharmony_ci	struct kvm *kvm;
20888c2ecf20Sopenharmony_ci	int idx;
20898c2ecf20Sopenharmony_ci	bool ret;
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	if (!handle_valid(handle))
20928c2ecf20Sopenharmony_ci		return false;
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	info = (struct kvmgt_guest_info *)handle;
20958c2ecf20Sopenharmony_ci	kvm = info->kvm;
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&kvm->srcu);
20988c2ecf20Sopenharmony_ci	ret = kvm_is_visible_gfn(kvm, gfn);
20998c2ecf20Sopenharmony_ci	srcu_read_unlock(&kvm->srcu, idx);
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	return ret;
21028c2ecf20Sopenharmony_ci}
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_cistatic struct intel_gvt_mpt kvmgt_mpt = {
21058c2ecf20Sopenharmony_ci	.type = INTEL_GVT_HYPERVISOR_KVM,
21068c2ecf20Sopenharmony_ci	.host_init = kvmgt_host_init,
21078c2ecf20Sopenharmony_ci	.host_exit = kvmgt_host_exit,
21088c2ecf20Sopenharmony_ci	.attach_vgpu = kvmgt_attach_vgpu,
21098c2ecf20Sopenharmony_ci	.detach_vgpu = kvmgt_detach_vgpu,
21108c2ecf20Sopenharmony_ci	.inject_msi = kvmgt_inject_msi,
21118c2ecf20Sopenharmony_ci	.from_virt_to_mfn = kvmgt_virt_to_pfn,
21128c2ecf20Sopenharmony_ci	.enable_page_track = kvmgt_page_track_add,
21138c2ecf20Sopenharmony_ci	.disable_page_track = kvmgt_page_track_remove,
21148c2ecf20Sopenharmony_ci	.read_gpa = kvmgt_read_gpa,
21158c2ecf20Sopenharmony_ci	.write_gpa = kvmgt_write_gpa,
21168c2ecf20Sopenharmony_ci	.gfn_to_mfn = kvmgt_gfn_to_pfn,
21178c2ecf20Sopenharmony_ci	.dma_map_guest_page = kvmgt_dma_map_guest_page,
21188c2ecf20Sopenharmony_ci	.dma_unmap_guest_page = kvmgt_dma_unmap_guest_page,
21198c2ecf20Sopenharmony_ci	.dma_pin_guest_page = kvmgt_dma_pin_guest_page,
21208c2ecf20Sopenharmony_ci	.set_opregion = kvmgt_set_opregion,
21218c2ecf20Sopenharmony_ci	.set_edid = kvmgt_set_edid,
21228c2ecf20Sopenharmony_ci	.get_vfio_device = kvmgt_get_vfio_device,
21238c2ecf20Sopenharmony_ci	.put_vfio_device = kvmgt_put_vfio_device,
21248c2ecf20Sopenharmony_ci	.is_valid_gfn = kvmgt_is_valid_gfn,
21258c2ecf20Sopenharmony_ci};
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_cistatic int __init kvmgt_init(void)
21288c2ecf20Sopenharmony_ci{
21298c2ecf20Sopenharmony_ci	if (intel_gvt_register_hypervisor(&kvmgt_mpt) < 0)
21308c2ecf20Sopenharmony_ci		return -ENODEV;
21318c2ecf20Sopenharmony_ci	return 0;
21328c2ecf20Sopenharmony_ci}
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_cistatic void __exit kvmgt_exit(void)
21358c2ecf20Sopenharmony_ci{
21368c2ecf20Sopenharmony_ci	intel_gvt_unregister_hypervisor();
21378c2ecf20Sopenharmony_ci}
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_cimodule_init(kvmgt_init);
21408c2ecf20Sopenharmony_cimodule_exit(kvmgt_exit);
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL and additional rights");
21438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation");
2144