xref: /openbmc/linux/drivers/gpu/drm/i915/gvt/kvmgt.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1f30437c5SJike Song /*
2f30437c5SJike Song  * KVMGT - the implementation of Intel mediated pass-through framework for KVM
3f30437c5SJike Song  *
4cba619cbSChristoph Hellwig  * Copyright(c) 2011-2016 Intel Corporation. All rights reserved.
5f30437c5SJike Song  *
6f30437c5SJike Song  * Permission is hereby granted, free of charge, to any person obtaining a
7f30437c5SJike Song  * copy of this software and associated documentation files (the "Software"),
8f30437c5SJike Song  * to deal in the Software without restriction, including without limitation
9f30437c5SJike Song  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10f30437c5SJike Song  * and/or sell copies of the Software, and to permit persons to whom the
11f30437c5SJike Song  * Software is furnished to do so, subject to the following conditions:
12f30437c5SJike Song  *
13f30437c5SJike Song  * The above copyright notice and this permission notice (including the next
14f30437c5SJike Song  * paragraph) shall be included in all copies or substantial portions of the
15f30437c5SJike Song  * Software.
16f30437c5SJike Song  *
17f30437c5SJike Song  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18f30437c5SJike Song  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19f30437c5SJike Song  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20f30437c5SJike Song  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21f30437c5SJike Song  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22f30437c5SJike Song  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23f30437c5SJike Song  * SOFTWARE.
24f30437c5SJike Song  *
25f30437c5SJike Song  * Authors:
26f30437c5SJike Song  *    Kevin Tian <kevin.tian@intel.com>
27f30437c5SJike Song  *    Jike Song <jike.song@intel.com>
28f30437c5SJike Song  *    Xiaoguang Chen <xiaoguang.chen@intel.com>
29cba619cbSChristoph Hellwig  *    Eddie Dong <eddie.dong@intel.com>
30cba619cbSChristoph Hellwig  *
31cba619cbSChristoph Hellwig  * Contributors:
32cba619cbSChristoph Hellwig  *    Niu Bing <bing.niu@intel.com>
33cba619cbSChristoph Hellwig  *    Zhi Wang <zhi.a.wang@intel.com>
34f30437c5SJike Song  */
35f30437c5SJike Song 
36f30437c5SJike Song #include <linux/init.h>
37f30437c5SJike Song #include <linux/mm.h>
389bf5b9ebSChristoph Hellwig #include <linux/kthread.h>
390a1b60d7SZhenyu Wang #include <linux/sched/mm.h>
40f30437c5SJike Song #include <linux/types.h>
41f30437c5SJike Song #include <linux/list.h>
42f30437c5SJike Song #include <linux/rbtree.h>
43f30437c5SJike Song #include <linux/spinlock.h>
44f30437c5SJike Song #include <linux/eventfd.h>
45659643f7SJike Song #include <linux/mdev.h>
466846dfebSChangbin Du #include <linux/debugfs.h>
47f30437c5SJike Song 
48de5372daSGustavo A. R. Silva #include <linux/nospec.h>
49de5372daSGustavo A. R. Silva 
50a4c260deSJani Nikula #include <drm/drm_edid.h>
51a4c260deSJani Nikula 
52f30437c5SJike Song #include "i915_drv.h"
538b750bf7SChristoph Hellwig #include "intel_gvt.h"
54f30437c5SJike Song #include "gvt.h"
55f30437c5SJike Song 
568b750bf7SChristoph Hellwig MODULE_IMPORT_NS(DMA_BUF);
578b750bf7SChristoph Hellwig MODULE_IMPORT_NS(I915_GVT);
588b750bf7SChristoph Hellwig 
59f30437c5SJike Song /* helper macros copied from vfio-pci */
60f30437c5SJike Song #define VFIO_PCI_OFFSET_SHIFT   40
61f30437c5SJike Song #define VFIO_PCI_OFFSET_TO_INDEX(off)   (off >> VFIO_PCI_OFFSET_SHIFT)
62f30437c5SJike Song #define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
63f30437c5SJike Song #define VFIO_PCI_OFFSET_MASK    (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
64f30437c5SJike Song 
6539c68e87SHang Yuan #define EDID_BLOB_OFFSET (PAGE_SIZE/2)
6639c68e87SHang Yuan 
67b851adeaSTina Zhang #define OPREGION_SIGNATURE "IntelGraphicsMem"
68b851adeaSTina Zhang 
69b851adeaSTina Zhang struct vfio_region;
70b851adeaSTina Zhang struct intel_vgpu_regops {
71b851adeaSTina Zhang 	size_t (*rw)(struct intel_vgpu *vgpu, char *buf,
72b851adeaSTina Zhang 			size_t count, loff_t *ppos, bool iswrite);
73b851adeaSTina Zhang 	void (*release)(struct intel_vgpu *vgpu,
74b851adeaSTina Zhang 			struct vfio_region *region);
75b851adeaSTina Zhang };
76b851adeaSTina Zhang 
77f30437c5SJike Song struct vfio_region {
78f30437c5SJike Song 	u32				type;
79f30437c5SJike Song 	u32				subtype;
80f30437c5SJike Song 	size_t				size;
81f30437c5SJike Song 	u32				flags;
82b851adeaSTina Zhang 	const struct intel_vgpu_regops	*ops;
83b851adeaSTina Zhang 	void				*data;
84f30437c5SJike Song };
85f30437c5SJike Song 
8639c68e87SHang Yuan struct vfio_edid_region {
8739c68e87SHang Yuan 	struct vfio_region_gfx_edid vfio_edid_regs;
8839c68e87SHang Yuan 	void *edid_blob;
8939c68e87SHang Yuan };
9039c68e87SHang Yuan 
91f30437c5SJike Song struct kvmgt_pgfn {
92f30437c5SJike Song 	gfn_t gfn;
93f30437c5SJike Song 	struct hlist_node hnode;
94f30437c5SJike Song };
95f30437c5SJike Song 
96f30437c5SJike Song struct gvt_dma {
97cf4ee73fSChangbin Du 	struct intel_vgpu *vgpu;
98cf4ee73fSChangbin Du 	struct rb_node gfn_node;
99cf4ee73fSChangbin Du 	struct rb_node dma_addr_node;
100f30437c5SJike Song 	gfn_t gfn;
101cf4ee73fSChangbin Du 	dma_addr_t dma_addr;
10279e542f5SChangbin Du 	unsigned long size;
103cf4ee73fSChangbin Du 	struct kref ref;
104f30437c5SJike Song };
105f30437c5SJike Song 
106978cf586SChristoph Hellwig #define vfio_dev_to_vgpu(vfio_dev) \
107978cf586SChristoph Hellwig 	container_of((vfio_dev), struct intel_vgpu, vfio_device)
108978cf586SChristoph Hellwig 
109b271e17dSSean Christopherson static void kvmgt_page_track_write(gpa_t gpa, const u8 *val, int len,
1100e09f406SChristoph Hellwig 				   struct kvm_page_track_notifier_node *node);
111c15fcf12SYan Zhao static void kvmgt_page_track_remove_region(gfn_t gfn, unsigned long nr_pages,
1120e09f406SChristoph Hellwig 					   struct kvm_page_track_notifier_node *node);
1130e09f406SChristoph Hellwig 
intel_vgpu_show_description(struct mdev_type * mtype,char * buf)114685a1537SChristoph Hellwig static ssize_t intel_vgpu_show_description(struct mdev_type *mtype, char *buf)
115145e06b5SZhenyu Wang {
116da44c340SChristoph Hellwig 	struct intel_vgpu_type *type =
117da44c340SChristoph Hellwig 		container_of(mtype, struct intel_vgpu_type, type);
118145e06b5SZhenyu Wang 
119145e06b5SZhenyu Wang 	return sprintf(buf, "low_gm_size: %dMB\nhigh_gm_size: %dMB\n"
120145e06b5SZhenyu Wang 		       "fence: %d\nresolution: %s\n"
121145e06b5SZhenyu Wang 		       "weight: %d\n",
1221aa3834fSChristoph Hellwig 		       BYTES_TO_MB(type->conf->low_mm),
1231aa3834fSChristoph Hellwig 		       BYTES_TO_MB(type->conf->high_mm),
1241aa3834fSChristoph Hellwig 		       type->conf->fence, vgpu_edid_str(type->conf->edid),
1251aa3834fSChristoph Hellwig 		       type->conf->weight);
126145e06b5SZhenyu Wang }
127145e06b5SZhenyu Wang 
gvt_unpin_guest_page(struct intel_vgpu * vgpu,unsigned long gfn,unsigned long size)12879e542f5SChangbin Du static void gvt_unpin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
12979e542f5SChangbin Du 		unsigned long size)
130b86dc6edSChuanxiao Dong {
13144abdd16SNicolin Chen 	vfio_unpin_pages(&vgpu->vfio_device, gfn << PAGE_SHIFT,
13244abdd16SNicolin Chen 			 DIV_ROUND_UP(size, PAGE_SIZE));
13379e542f5SChangbin Du }
13479e542f5SChangbin Du 
13579e542f5SChangbin Du /* Pin a normal or compound guest page for dma. */
gvt_pin_guest_page(struct intel_vgpu * vgpu,unsigned long gfn,unsigned long size,struct page ** page)13679e542f5SChangbin Du static int gvt_pin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
13779e542f5SChangbin Du 		unsigned long size, struct page **page)
13879e542f5SChangbin Du {
1392c9e8c01SNicolin Chen 	int total_pages = DIV_ROUND_UP(size, PAGE_SIZE);
14034a255e6SNicolin Chen 	struct page *base_page = NULL;
14179e542f5SChangbin Du 	int npage;
14279e542f5SChangbin Du 	int ret;
14379e542f5SChangbin Du 
14479e542f5SChangbin Du 	/*
14579e542f5SChangbin Du 	 * We pin the pages one-by-one to avoid allocating a big arrary
14679e542f5SChangbin Du 	 * on stack to hold pfns.
14779e542f5SChangbin Du 	 */
14879e542f5SChangbin Du 	for (npage = 0; npage < total_pages; npage++) {
14944abdd16SNicolin Chen 		dma_addr_t cur_iova = (gfn + npage) << PAGE_SHIFT;
15034a255e6SNicolin Chen 		struct page *cur_page;
15179e542f5SChangbin Du 
15244abdd16SNicolin Chen 		ret = vfio_pin_pages(&vgpu->vfio_device, cur_iova, 1,
15334a255e6SNicolin Chen 				     IOMMU_READ | IOMMU_WRITE, &cur_page);
154cf4ee73fSChangbin Du 		if (ret != 1) {
15544abdd16SNicolin Chen 			gvt_vgpu_err("vfio_pin_pages failed for iova %pad, ret %d\n",
15644abdd16SNicolin Chen 				     &cur_iova, ret);
15779e542f5SChangbin Du 			goto err;
158cf4ee73fSChangbin Du 		}
159b86dc6edSChuanxiao Dong 
16079e542f5SChangbin Du 		if (npage == 0)
16134a255e6SNicolin Chen 			base_page = cur_page;
162adc7b226SSean Christopherson 		else if (page_to_pfn(base_page) + npage != page_to_pfn(cur_page)) {
16379e542f5SChangbin Du 			ret = -EINVAL;
16479e542f5SChangbin Du 			npage++;
16579e542f5SChangbin Du 			goto err;
16679e542f5SChangbin Du 		}
16779e542f5SChangbin Du 	}
16879e542f5SChangbin Du 
16934a255e6SNicolin Chen 	*page = base_page;
17079e542f5SChangbin Du 	return 0;
17179e542f5SChangbin Du err:
172a15e61f3SYan Zhao 	if (npage)
17379e542f5SChangbin Du 		gvt_unpin_guest_page(vgpu, gfn, npage * PAGE_SIZE);
17479e542f5SChangbin Du 	return ret;
17579e542f5SChangbin Du }
17679e542f5SChangbin Du 
gvt_dma_map_page(struct intel_vgpu * vgpu,unsigned long gfn,dma_addr_t * dma_addr,unsigned long size)17779e542f5SChangbin Du static int gvt_dma_map_page(struct intel_vgpu *vgpu, unsigned long gfn,
17879e542f5SChangbin Du 		dma_addr_t *dma_addr, unsigned long size)
17979e542f5SChangbin Du {
1809ff06c38SThomas Zimmermann 	struct device *dev = vgpu->gvt->gt->i915->drm.dev;
18179e542f5SChangbin Du 	struct page *page = NULL;
18279e542f5SChangbin Du 	int ret;
18379e542f5SChangbin Du 
18479e542f5SChangbin Du 	ret = gvt_pin_guest_page(vgpu, gfn, size, &page);
18579e542f5SChangbin Du 	if (ret)
18679e542f5SChangbin Du 		return ret;
18779e542f5SChangbin Du 
188cf4ee73fSChangbin Du 	/* Setup DMA mapping. */
189c4f61203SCai Huoqing 	*dma_addr = dma_map_page(dev, page, 0, size, DMA_BIDIRECTIONAL);
19013bdff33SDan Carpenter 	if (dma_mapping_error(dev, *dma_addr)) {
19179e542f5SChangbin Du 		gvt_vgpu_err("DMA mapping failed for pfn 0x%lx, ret %d\n",
19279e542f5SChangbin Du 			     page_to_pfn(page), ret);
19379e542f5SChangbin Du 		gvt_unpin_guest_page(vgpu, gfn, size);
19413bdff33SDan Carpenter 		return -ENOMEM;
195cf4ee73fSChangbin Du 	}
196b86dc6edSChuanxiao Dong 
19713bdff33SDan Carpenter 	return 0;
198b86dc6edSChuanxiao Dong }
199b86dc6edSChuanxiao Dong 
gvt_dma_unmap_page(struct intel_vgpu * vgpu,unsigned long gfn,dma_addr_t dma_addr,unsigned long size)200cf4ee73fSChangbin Du static void gvt_dma_unmap_page(struct intel_vgpu *vgpu, unsigned long gfn,
20179e542f5SChangbin Du 		dma_addr_t dma_addr, unsigned long size)
202b86dc6edSChuanxiao Dong {
2039ff06c38SThomas Zimmermann 	struct device *dev = vgpu->gvt->gt->i915->drm.dev;
204b86dc6edSChuanxiao Dong 
205c4f61203SCai Huoqing 	dma_unmap_page(dev, dma_addr, size, DMA_BIDIRECTIONAL);
20679e542f5SChangbin Du 	gvt_unpin_guest_page(vgpu, gfn, size);
207b86dc6edSChuanxiao Dong }
208b86dc6edSChuanxiao Dong 
__gvt_cache_find_dma_addr(struct intel_vgpu * vgpu,dma_addr_t dma_addr)209cf4ee73fSChangbin Du static struct gvt_dma *__gvt_cache_find_dma_addr(struct intel_vgpu *vgpu,
210cf4ee73fSChangbin Du 		dma_addr_t dma_addr)
211f30437c5SJike Song {
21262980cacSChristoph Hellwig 	struct rb_node *node = vgpu->dma_addr_cache.rb_node;
213cf4ee73fSChangbin Du 	struct gvt_dma *itr;
214f30437c5SJike Song 
215f30437c5SJike Song 	while (node) {
216cf4ee73fSChangbin Du 		itr = rb_entry(node, struct gvt_dma, dma_addr_node);
217cf4ee73fSChangbin Du 
218cf4ee73fSChangbin Du 		if (dma_addr < itr->dma_addr)
219cf4ee73fSChangbin Du 			node = node->rb_left;
220cf4ee73fSChangbin Du 		else if (dma_addr > itr->dma_addr)
221cf4ee73fSChangbin Du 			node = node->rb_right;
222cf4ee73fSChangbin Du 		else
223cf4ee73fSChangbin Du 			return itr;
224cf4ee73fSChangbin Du 	}
225cf4ee73fSChangbin Du 	return NULL;
226cf4ee73fSChangbin Du }
227cf4ee73fSChangbin Du 
__gvt_cache_find_gfn(struct intel_vgpu * vgpu,gfn_t gfn)228cf4ee73fSChangbin Du static struct gvt_dma *__gvt_cache_find_gfn(struct intel_vgpu *vgpu, gfn_t gfn)
229cf4ee73fSChangbin Du {
23062980cacSChristoph Hellwig 	struct rb_node *node = vgpu->gfn_cache.rb_node;
231cf4ee73fSChangbin Du 	struct gvt_dma *itr;
232cf4ee73fSChangbin Du 
233cf4ee73fSChangbin Du 	while (node) {
234cf4ee73fSChangbin Du 		itr = rb_entry(node, struct gvt_dma, gfn_node);
235f30437c5SJike Song 
236f30437c5SJike Song 		if (gfn < itr->gfn)
237f30437c5SJike Song 			node = node->rb_left;
238f30437c5SJike Song 		else if (gfn > itr->gfn)
239f30437c5SJike Song 			node = node->rb_right;
240cf4ee73fSChangbin Du 		else
241cf4ee73fSChangbin Du 			return itr;
242f30437c5SJike Song 	}
243cf4ee73fSChangbin Du 	return NULL;
244f30437c5SJike Song }
245f30437c5SJike Song 
__gvt_cache_add(struct intel_vgpu * vgpu,gfn_t gfn,dma_addr_t dma_addr,unsigned long size)2465cd4223eSChangbin Du static int __gvt_cache_add(struct intel_vgpu *vgpu, gfn_t gfn,
24779e542f5SChangbin Du 		dma_addr_t dma_addr, unsigned long size)
248f30437c5SJike Song {
249f30437c5SJike Song 	struct gvt_dma *new, *itr;
250cf4ee73fSChangbin Du 	struct rb_node **link, *parent = NULL;
251f30437c5SJike Song 
252f30437c5SJike Song 	new = kzalloc(sizeof(struct gvt_dma), GFP_KERNEL);
253f30437c5SJike Song 	if (!new)
2545cd4223eSChangbin Du 		return -ENOMEM;
255f30437c5SJike Song 
256cf4ee73fSChangbin Du 	new->vgpu = vgpu;
257f30437c5SJike Song 	new->gfn = gfn;
258cf4ee73fSChangbin Du 	new->dma_addr = dma_addr;
25979e542f5SChangbin Du 	new->size = size;
260cf4ee73fSChangbin Du 	kref_init(&new->ref);
261f30437c5SJike Song 
262cf4ee73fSChangbin Du 	/* gfn_cache maps gfn to struct gvt_dma. */
26362980cacSChristoph Hellwig 	link = &vgpu->gfn_cache.rb_node;
264f30437c5SJike Song 	while (*link) {
265f30437c5SJike Song 		parent = *link;
266cf4ee73fSChangbin Du 		itr = rb_entry(parent, struct gvt_dma, gfn_node);
267f30437c5SJike Song 
268cf4ee73fSChangbin Du 		if (gfn < itr->gfn)
269f30437c5SJike Song 			link = &parent->rb_left;
270f30437c5SJike Song 		else
271f30437c5SJike Song 			link = &parent->rb_right;
272f30437c5SJike Song 	}
273cf4ee73fSChangbin Du 	rb_link_node(&new->gfn_node, parent, link);
27462980cacSChristoph Hellwig 	rb_insert_color(&new->gfn_node, &vgpu->gfn_cache);
275f30437c5SJike Song 
276cf4ee73fSChangbin Du 	/* dma_addr_cache maps dma addr to struct gvt_dma. */
277cf4ee73fSChangbin Du 	parent = NULL;
27862980cacSChristoph Hellwig 	link = &vgpu->dma_addr_cache.rb_node;
279cf4ee73fSChangbin Du 	while (*link) {
280cf4ee73fSChangbin Du 		parent = *link;
281cf4ee73fSChangbin Du 		itr = rb_entry(parent, struct gvt_dma, dma_addr_node);
282f30437c5SJike Song 
283cf4ee73fSChangbin Du 		if (dma_addr < itr->dma_addr)
284cf4ee73fSChangbin Du 			link = &parent->rb_left;
285cf4ee73fSChangbin Du 		else
286cf4ee73fSChangbin Du 			link = &parent->rb_right;
287cf4ee73fSChangbin Du 	}
288cf4ee73fSChangbin Du 	rb_link_node(&new->dma_addr_node, parent, link);
28962980cacSChristoph Hellwig 	rb_insert_color(&new->dma_addr_node, &vgpu->dma_addr_cache);
2906846dfebSChangbin Du 
29162980cacSChristoph Hellwig 	vgpu->nr_cache_entries++;
2925cd4223eSChangbin Du 	return 0;
293f30437c5SJike Song }
294f30437c5SJike Song 
__gvt_cache_remove_entry(struct intel_vgpu * vgpu,struct gvt_dma * entry)295f30437c5SJike Song static void __gvt_cache_remove_entry(struct intel_vgpu *vgpu,
296f30437c5SJike Song 				struct gvt_dma *entry)
297f30437c5SJike Song {
29862980cacSChristoph Hellwig 	rb_erase(&entry->gfn_node, &vgpu->gfn_cache);
29962980cacSChristoph Hellwig 	rb_erase(&entry->dma_addr_node, &vgpu->dma_addr_cache);
300f30437c5SJike Song 	kfree(entry);
30162980cacSChristoph Hellwig 	vgpu->nr_cache_entries--;
302f30437c5SJike Song }
303f30437c5SJike Song 
gvt_cache_destroy(struct intel_vgpu * vgpu)304f30437c5SJike Song static void gvt_cache_destroy(struct intel_vgpu *vgpu)
305f30437c5SJike Song {
306f30437c5SJike Song 	struct gvt_dma *dma;
307f30437c5SJike Song 	struct rb_node *node = NULL;
308f30437c5SJike Song 
309f16bd3ddSChuanxiao Dong 	for (;;) {
31062980cacSChristoph Hellwig 		mutex_lock(&vgpu->cache_lock);
31162980cacSChristoph Hellwig 		node = rb_first(&vgpu->gfn_cache);
312f16bd3ddSChuanxiao Dong 		if (!node) {
31362980cacSChristoph Hellwig 			mutex_unlock(&vgpu->cache_lock);
314f16bd3ddSChuanxiao Dong 			break;
315f16bd3ddSChuanxiao Dong 		}
316cf4ee73fSChangbin Du 		dma = rb_entry(node, struct gvt_dma, gfn_node);
31779e542f5SChangbin Du 		gvt_dma_unmap_page(vgpu, dma->gfn, dma->dma_addr, dma->size);
318f30437c5SJike Song 		__gvt_cache_remove_entry(vgpu, dma);
31962980cacSChristoph Hellwig 		mutex_unlock(&vgpu->cache_lock);
320f16bd3ddSChuanxiao Dong 	}
321f30437c5SJike Song }
322f30437c5SJike Song 
gvt_cache_init(struct intel_vgpu * vgpu)323cf4ee73fSChangbin Du static void gvt_cache_init(struct intel_vgpu *vgpu)
324cf4ee73fSChangbin Du {
32562980cacSChristoph Hellwig 	vgpu->gfn_cache = RB_ROOT;
32662980cacSChristoph Hellwig 	vgpu->dma_addr_cache = RB_ROOT;
32762980cacSChristoph Hellwig 	vgpu->nr_cache_entries = 0;
32862980cacSChristoph Hellwig 	mutex_init(&vgpu->cache_lock);
329cf4ee73fSChangbin Du }
330cf4ee73fSChangbin Du 
kvmgt_protect_table_init(struct intel_vgpu * info)33110ddb962SChristoph Hellwig static void kvmgt_protect_table_init(struct intel_vgpu *info)
332f30437c5SJike Song {
333f30437c5SJike Song 	hash_init(info->ptable);
334f30437c5SJike Song }
335f30437c5SJike Song 
kvmgt_protect_table_destroy(struct intel_vgpu * info)33610ddb962SChristoph Hellwig static void kvmgt_protect_table_destroy(struct intel_vgpu *info)
337f30437c5SJike Song {
338f30437c5SJike Song 	struct kvmgt_pgfn *p;
339f30437c5SJike Song 	struct hlist_node *tmp;
340f30437c5SJike Song 	int i;
341f30437c5SJike Song 
342f30437c5SJike Song 	hash_for_each_safe(info->ptable, i, tmp, p, hnode) {
343f30437c5SJike Song 		hash_del(&p->hnode);
344f30437c5SJike Song 		kfree(p);
345f30437c5SJike Song 	}
346f30437c5SJike Song }
347f30437c5SJike Song 
348f30437c5SJike Song static struct kvmgt_pgfn *
__kvmgt_protect_table_find(struct intel_vgpu * info,gfn_t gfn)34910ddb962SChristoph Hellwig __kvmgt_protect_table_find(struct intel_vgpu *info, gfn_t gfn)
350f30437c5SJike Song {
351f30437c5SJike Song 	struct kvmgt_pgfn *p, *res = NULL;
352f30437c5SJike Song 
3533cca6b26SSean Christopherson 	lockdep_assert_held(&info->vgpu_lock);
3543cca6b26SSean Christopherson 
355f30437c5SJike Song 	hash_for_each_possible(info->ptable, p, hnode, gfn) {
356f30437c5SJike Song 		if (gfn == p->gfn) {
357f30437c5SJike Song 			res = p;
358f30437c5SJike Song 			break;
359f30437c5SJike Song 		}
360f30437c5SJike Song 	}
361f30437c5SJike Song 
362f30437c5SJike Song 	return res;
363f30437c5SJike Song }
364f30437c5SJike Song 
kvmgt_gfn_is_write_protected(struct intel_vgpu * info,gfn_t gfn)36510ddb962SChristoph Hellwig static bool kvmgt_gfn_is_write_protected(struct intel_vgpu *info, gfn_t gfn)
366f30437c5SJike Song {
367f30437c5SJike Song 	struct kvmgt_pgfn *p;
368f30437c5SJike Song 
369f30437c5SJike Song 	p = __kvmgt_protect_table_find(info, gfn);
370f30437c5SJike Song 	return !!p;
371f30437c5SJike Song }
372f30437c5SJike Song 
kvmgt_protect_table_add(struct intel_vgpu * info,gfn_t gfn)37310ddb962SChristoph Hellwig static void kvmgt_protect_table_add(struct intel_vgpu *info, gfn_t gfn)
374f30437c5SJike Song {
375f30437c5SJike Song 	struct kvmgt_pgfn *p;
376f30437c5SJike Song 
377f30437c5SJike Song 	if (kvmgt_gfn_is_write_protected(info, gfn))
378f30437c5SJike Song 		return;
379f30437c5SJike Song 
380c55b1de0SJike Song 	p = kzalloc(sizeof(struct kvmgt_pgfn), GFP_ATOMIC);
381f30437c5SJike Song 	if (WARN(!p, "gfn: 0x%llx\n", gfn))
382f30437c5SJike Song 		return;
383f30437c5SJike Song 
384f30437c5SJike Song 	p->gfn = gfn;
385f30437c5SJike Song 	hash_add(info->ptable, &p->hnode, gfn);
386f30437c5SJike Song }
387f30437c5SJike Song 
kvmgt_protect_table_del(struct intel_vgpu * info,gfn_t gfn)38810ddb962SChristoph Hellwig static void kvmgt_protect_table_del(struct intel_vgpu *info, gfn_t gfn)
389f30437c5SJike Song {
390f30437c5SJike Song 	struct kvmgt_pgfn *p;
391f30437c5SJike Song 
392f30437c5SJike Song 	p = __kvmgt_protect_table_find(info, gfn);
393f30437c5SJike Song 	if (p) {
394f30437c5SJike Song 		hash_del(&p->hnode);
395f30437c5SJike Song 		kfree(p);
396f30437c5SJike Song 	}
397f30437c5SJike Song }
398f30437c5SJike Song 
intel_vgpu_reg_rw_opregion(struct intel_vgpu * vgpu,char * buf,size_t count,loff_t * ppos,bool iswrite)399b851adeaSTina Zhang static size_t intel_vgpu_reg_rw_opregion(struct intel_vgpu *vgpu, char *buf,
400b851adeaSTina Zhang 		size_t count, loff_t *ppos, bool iswrite)
401b851adeaSTina Zhang {
402b851adeaSTina Zhang 	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
403b851adeaSTina Zhang 			VFIO_PCI_NUM_REGIONS;
40462980cacSChristoph Hellwig 	void *base = vgpu->region[i].data;
405b851adeaSTina Zhang 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
406b851adeaSTina Zhang 
40706d63c48SJulian Stecklina 
40862980cacSChristoph Hellwig 	if (pos >= vgpu->region[i].size || iswrite) {
409b851adeaSTina Zhang 		gvt_vgpu_err("invalid op or offset for Intel vgpu OpRegion\n");
410b851adeaSTina Zhang 		return -EINVAL;
411b851adeaSTina Zhang 	}
41262980cacSChristoph Hellwig 	count = min(count, (size_t)(vgpu->region[i].size - pos));
413b851adeaSTina Zhang 	memcpy(buf, base + pos, count);
414b851adeaSTina Zhang 
415b851adeaSTina Zhang 	return count;
416b851adeaSTina Zhang }
417b851adeaSTina Zhang 
intel_vgpu_reg_release_opregion(struct intel_vgpu * vgpu,struct vfio_region * region)418b851adeaSTina Zhang static void intel_vgpu_reg_release_opregion(struct intel_vgpu *vgpu,
419b851adeaSTina Zhang 		struct vfio_region *region)
420b851adeaSTina Zhang {
421b851adeaSTina Zhang }
422b851adeaSTina Zhang 
423b851adeaSTina Zhang static const struct intel_vgpu_regops intel_vgpu_regops_opregion = {
424b851adeaSTina Zhang 	.rw = intel_vgpu_reg_rw_opregion,
425b851adeaSTina Zhang 	.release = intel_vgpu_reg_release_opregion,
426b851adeaSTina Zhang };
427b851adeaSTina Zhang 
handle_edid_regs(struct intel_vgpu * vgpu,struct vfio_edid_region * region,char * buf,size_t count,u16 offset,bool is_write)42839c68e87SHang Yuan static int handle_edid_regs(struct intel_vgpu *vgpu,
42939c68e87SHang Yuan 			struct vfio_edid_region *region, char *buf,
43039c68e87SHang Yuan 			size_t count, u16 offset, bool is_write)
43139c68e87SHang Yuan {
43239c68e87SHang Yuan 	struct vfio_region_gfx_edid *regs = &region->vfio_edid_regs;
43339c68e87SHang Yuan 	unsigned int data;
43439c68e87SHang Yuan 
43539c68e87SHang Yuan 	if (offset + count > sizeof(*regs))
43639c68e87SHang Yuan 		return -EINVAL;
43739c68e87SHang Yuan 
43839c68e87SHang Yuan 	if (count != 4)
43939c68e87SHang Yuan 		return -EINVAL;
44039c68e87SHang Yuan 
44139c68e87SHang Yuan 	if (is_write) {
44239c68e87SHang Yuan 		data = *((unsigned int *)buf);
44339c68e87SHang Yuan 		switch (offset) {
44439c68e87SHang Yuan 		case offsetof(struct vfio_region_gfx_edid, link_state):
44539c68e87SHang Yuan 			if (data == VFIO_DEVICE_GFX_LINK_STATE_UP) {
44639c68e87SHang Yuan 				if (!drm_edid_block_valid(
44739c68e87SHang Yuan 					(u8 *)region->edid_blob,
44839c68e87SHang Yuan 					0,
44939c68e87SHang Yuan 					true,
45039c68e87SHang Yuan 					NULL)) {
45139c68e87SHang Yuan 					gvt_vgpu_err("invalid EDID blob\n");
45239c68e87SHang Yuan 					return -EINVAL;
45339c68e87SHang Yuan 				}
454675e5c4aSChristoph Hellwig 				intel_vgpu_emulate_hotplug(vgpu, true);
45539c68e87SHang Yuan 			} else if (data == VFIO_DEVICE_GFX_LINK_STATE_DOWN)
456675e5c4aSChristoph Hellwig 				intel_vgpu_emulate_hotplug(vgpu, false);
45739c68e87SHang Yuan 			else {
45839c68e87SHang Yuan 				gvt_vgpu_err("invalid EDID link state %d\n",
45939c68e87SHang Yuan 					regs->link_state);
46039c68e87SHang Yuan 				return -EINVAL;
46139c68e87SHang Yuan 			}
46239c68e87SHang Yuan 			regs->link_state = data;
46339c68e87SHang Yuan 			break;
46439c68e87SHang Yuan 		case offsetof(struct vfio_region_gfx_edid, edid_size):
46539c68e87SHang Yuan 			if (data > regs->edid_max_size) {
46639c68e87SHang Yuan 				gvt_vgpu_err("EDID size is bigger than %d!\n",
46739c68e87SHang Yuan 					regs->edid_max_size);
46839c68e87SHang Yuan 				return -EINVAL;
46939c68e87SHang Yuan 			}
47039c68e87SHang Yuan 			regs->edid_size = data;
47139c68e87SHang Yuan 			break;
47239c68e87SHang Yuan 		default:
47339c68e87SHang Yuan 			/* read-only regs */
47439c68e87SHang Yuan 			gvt_vgpu_err("write read-only EDID region at offset %d\n",
47539c68e87SHang Yuan 				offset);
47639c68e87SHang Yuan 			return -EPERM;
47739c68e87SHang Yuan 		}
47839c68e87SHang Yuan 	} else {
47939c68e87SHang Yuan 		memcpy(buf, (char *)regs + offset, count);
48039c68e87SHang Yuan 	}
48139c68e87SHang Yuan 
48239c68e87SHang Yuan 	return count;
48339c68e87SHang Yuan }
48439c68e87SHang Yuan 
handle_edid_blob(struct vfio_edid_region * region,char * buf,size_t count,u16 offset,bool is_write)48539c68e87SHang Yuan static int handle_edid_blob(struct vfio_edid_region *region, char *buf,
48639c68e87SHang Yuan 			size_t count, u16 offset, bool is_write)
48739c68e87SHang Yuan {
48839c68e87SHang Yuan 	if (offset + count > region->vfio_edid_regs.edid_size)
48939c68e87SHang Yuan 		return -EINVAL;
49039c68e87SHang Yuan 
49139c68e87SHang Yuan 	if (is_write)
49239c68e87SHang Yuan 		memcpy(region->edid_blob + offset, buf, count);
49339c68e87SHang Yuan 	else
49439c68e87SHang Yuan 		memcpy(buf, region->edid_blob + offset, count);
49539c68e87SHang Yuan 
49639c68e87SHang Yuan 	return count;
49739c68e87SHang Yuan }
49839c68e87SHang Yuan 
intel_vgpu_reg_rw_edid(struct intel_vgpu * vgpu,char * buf,size_t count,loff_t * ppos,bool iswrite)49939c68e87SHang Yuan static size_t intel_vgpu_reg_rw_edid(struct intel_vgpu *vgpu, char *buf,
50039c68e87SHang Yuan 		size_t count, loff_t *ppos, bool iswrite)
50139c68e87SHang Yuan {
50239c68e87SHang Yuan 	int ret;
50339c68e87SHang Yuan 	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
50439c68e87SHang Yuan 			VFIO_PCI_NUM_REGIONS;
50562980cacSChristoph Hellwig 	struct vfio_edid_region *region = vgpu->region[i].data;
50639c68e87SHang Yuan 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
50739c68e87SHang Yuan 
50839c68e87SHang Yuan 	if (pos < region->vfio_edid_regs.edid_offset) {
50939c68e87SHang Yuan 		ret = handle_edid_regs(vgpu, region, buf, count, pos, iswrite);
51039c68e87SHang Yuan 	} else {
51139c68e87SHang Yuan 		pos -= EDID_BLOB_OFFSET;
51239c68e87SHang Yuan 		ret = handle_edid_blob(region, buf, count, pos, iswrite);
51339c68e87SHang Yuan 	}
51439c68e87SHang Yuan 
51539c68e87SHang Yuan 	if (ret < 0)
51639c68e87SHang Yuan 		gvt_vgpu_err("failed to access EDID region\n");
51739c68e87SHang Yuan 
51839c68e87SHang Yuan 	return ret;
51939c68e87SHang Yuan }
52039c68e87SHang Yuan 
intel_vgpu_reg_release_edid(struct intel_vgpu * vgpu,struct vfio_region * region)52139c68e87SHang Yuan static void intel_vgpu_reg_release_edid(struct intel_vgpu *vgpu,
52239c68e87SHang Yuan 					struct vfio_region *region)
52339c68e87SHang Yuan {
52439c68e87SHang Yuan 	kfree(region->data);
52539c68e87SHang Yuan }
52639c68e87SHang Yuan 
52739c68e87SHang Yuan static const struct intel_vgpu_regops intel_vgpu_regops_edid = {
52839c68e87SHang Yuan 	.rw = intel_vgpu_reg_rw_edid,
52939c68e87SHang Yuan 	.release = intel_vgpu_reg_release_edid,
53039c68e87SHang Yuan };
53139c68e87SHang Yuan 
intel_vgpu_register_reg(struct intel_vgpu * vgpu,unsigned int type,unsigned int subtype,const struct intel_vgpu_regops * ops,size_t size,u32 flags,void * data)532b851adeaSTina Zhang static int intel_vgpu_register_reg(struct intel_vgpu *vgpu,
533b851adeaSTina Zhang 		unsigned int type, unsigned int subtype,
534b851adeaSTina Zhang 		const struct intel_vgpu_regops *ops,
535b851adeaSTina Zhang 		size_t size, u32 flags, void *data)
536b851adeaSTina Zhang {
537b851adeaSTina Zhang 	struct vfio_region *region;
538b851adeaSTina Zhang 
53962980cacSChristoph Hellwig 	region = krealloc(vgpu->region,
54062980cacSChristoph Hellwig 			(vgpu->num_regions + 1) * sizeof(*region),
541b851adeaSTina Zhang 			GFP_KERNEL);
542b851adeaSTina Zhang 	if (!region)
543b851adeaSTina Zhang 		return -ENOMEM;
544b851adeaSTina Zhang 
54562980cacSChristoph Hellwig 	vgpu->region = region;
54662980cacSChristoph Hellwig 	vgpu->region[vgpu->num_regions].type = type;
54762980cacSChristoph Hellwig 	vgpu->region[vgpu->num_regions].subtype = subtype;
54862980cacSChristoph Hellwig 	vgpu->region[vgpu->num_regions].ops = ops;
54962980cacSChristoph Hellwig 	vgpu->region[vgpu->num_regions].size = size;
55062980cacSChristoph Hellwig 	vgpu->region[vgpu->num_regions].flags = flags;
55162980cacSChristoph Hellwig 	vgpu->region[vgpu->num_regions].data = data;
55262980cacSChristoph Hellwig 	vgpu->num_regions++;
553b851adeaSTina Zhang 	return 0;
554b851adeaSTina Zhang }
555b851adeaSTina Zhang 
intel_gvt_set_opregion(struct intel_vgpu * vgpu)556f9399b0eSChristoph Hellwig int intel_gvt_set_opregion(struct intel_vgpu *vgpu)
557b851adeaSTina Zhang {
558b851adeaSTina Zhang 	void *base;
559b851adeaSTina Zhang 	int ret;
560b851adeaSTina Zhang 
561b851adeaSTina Zhang 	/* Each vgpu has its own opregion, although VFIO would create another
562b851adeaSTina Zhang 	 * one later. This one is used to expose opregion to VFIO. And the
563b851adeaSTina Zhang 	 * other one created by VFIO later, is used by guest actually.
564b851adeaSTina Zhang 	 */
565b851adeaSTina Zhang 	base = vgpu_opregion(vgpu)->va;
566b851adeaSTina Zhang 	if (!base)
567b851adeaSTina Zhang 		return -ENOMEM;
568b851adeaSTina Zhang 
569b851adeaSTina Zhang 	if (memcmp(base, OPREGION_SIGNATURE, 16)) {
570b851adeaSTina Zhang 		memunmap(base);
571b851adeaSTina Zhang 		return -EINVAL;
572b851adeaSTina Zhang 	}
573b851adeaSTina Zhang 
574b851adeaSTina Zhang 	ret = intel_vgpu_register_reg(vgpu,
575b851adeaSTina Zhang 			PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
576b851adeaSTina Zhang 			VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION,
577b851adeaSTina Zhang 			&intel_vgpu_regops_opregion, OPREGION_SIZE,
578b851adeaSTina Zhang 			VFIO_REGION_INFO_FLAG_READ, base);
579b851adeaSTina Zhang 
580b851adeaSTina Zhang 	return ret;
581b851adeaSTina Zhang }
582b851adeaSTina Zhang 
intel_gvt_set_edid(struct intel_vgpu * vgpu,int port_num)583f9399b0eSChristoph Hellwig int intel_gvt_set_edid(struct intel_vgpu *vgpu, int port_num)
58439c68e87SHang Yuan {
58539c68e87SHang Yuan 	struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num);
58639c68e87SHang Yuan 	struct vfio_edid_region *base;
58739c68e87SHang Yuan 	int ret;
58839c68e87SHang Yuan 
58939c68e87SHang Yuan 	base = kzalloc(sizeof(*base), GFP_KERNEL);
59039c68e87SHang Yuan 	if (!base)
59139c68e87SHang Yuan 		return -ENOMEM;
59239c68e87SHang Yuan 
59339c68e87SHang Yuan 	/* TODO: Add multi-port and EDID extension block support */
59439c68e87SHang Yuan 	base->vfio_edid_regs.edid_offset = EDID_BLOB_OFFSET;
59539c68e87SHang Yuan 	base->vfio_edid_regs.edid_max_size = EDID_SIZE;
59639c68e87SHang Yuan 	base->vfio_edid_regs.edid_size = EDID_SIZE;
59739c68e87SHang Yuan 	base->vfio_edid_regs.max_xres = vgpu_edid_xres(port->id);
59839c68e87SHang Yuan 	base->vfio_edid_regs.max_yres = vgpu_edid_yres(port->id);
59939c68e87SHang Yuan 	base->edid_blob = port->edid->edid_block;
60039c68e87SHang Yuan 
60139c68e87SHang Yuan 	ret = intel_vgpu_register_reg(vgpu,
60239c68e87SHang Yuan 			VFIO_REGION_TYPE_GFX,
60339c68e87SHang Yuan 			VFIO_REGION_SUBTYPE_GFX_EDID,
60439c68e87SHang Yuan 			&intel_vgpu_regops_edid, EDID_SIZE,
60539c68e87SHang Yuan 			VFIO_REGION_INFO_FLAG_READ |
60639c68e87SHang Yuan 			VFIO_REGION_INFO_FLAG_WRITE |
60739c68e87SHang Yuan 			VFIO_REGION_INFO_FLAG_CAPS, base);
60839c68e87SHang Yuan 
60939c68e87SHang Yuan 	return ret;
61039c68e87SHang Yuan }
61139c68e87SHang Yuan 
intel_vgpu_dma_unmap(struct vfio_device * vfio_dev,u64 iova,u64 length)612ce4b4657SJason Gunthorpe static void intel_vgpu_dma_unmap(struct vfio_device *vfio_dev, u64 iova,
613ce4b4657SJason Gunthorpe 				 u64 length)
614659643f7SJike Song {
615ce4b4657SJason Gunthorpe 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
616cf4ee73fSChangbin Du 	struct gvt_dma *entry;
617ce4b4657SJason Gunthorpe 	u64 iov_pfn = iova >> PAGE_SHIFT;
618ce4b4657SJason Gunthorpe 	u64 end_iov_pfn = iov_pfn + length / PAGE_SIZE;
619659643f7SJike Song 
62062980cacSChristoph Hellwig 	mutex_lock(&vgpu->cache_lock);
621cf4ee73fSChangbin Du 	for (; iov_pfn < end_iov_pfn; iov_pfn++) {
622cf4ee73fSChangbin Du 		entry = __gvt_cache_find_gfn(vgpu, iov_pfn);
623cf4ee73fSChangbin Du 		if (!entry)
624cf4ee73fSChangbin Du 			continue;
625cf4ee73fSChangbin Du 
62679e542f5SChangbin Du 		gvt_dma_unmap_page(vgpu, entry->gfn, entry->dma_addr,
62779e542f5SChangbin Du 				   entry->size);
628cf4ee73fSChangbin Du 		__gvt_cache_remove_entry(vgpu, entry);
629cf4ee73fSChangbin Du 	}
63062980cacSChristoph Hellwig 	mutex_unlock(&vgpu->cache_lock);
631659643f7SJike Song }
632659643f7SJike Song 
__kvmgt_vgpu_exist(struct intel_vgpu * vgpu)6330e09f406SChristoph Hellwig static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu)
6340e09f406SChristoph Hellwig {
6350e09f406SChristoph Hellwig 	struct intel_vgpu *itr;
6360e09f406SChristoph Hellwig 	int id;
6370e09f406SChristoph Hellwig 	bool ret = false;
6380e09f406SChristoph Hellwig 
6390e09f406SChristoph Hellwig 	mutex_lock(&vgpu->gvt->lock);
6400e09f406SChristoph Hellwig 	for_each_active_vgpu(vgpu->gvt, itr, id) {
641a06d4b9eSZhi Wang 		if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, itr->status))
6420e09f406SChristoph Hellwig 			continue;
6430e09f406SChristoph Hellwig 
644421cfe65SMatthew Rosato 		if (vgpu->vfio_device.kvm == itr->vfio_device.kvm) {
6450e09f406SChristoph Hellwig 			ret = true;
6460e09f406SChristoph Hellwig 			goto out;
6470e09f406SChristoph Hellwig 		}
6480e09f406SChristoph Hellwig 	}
6490e09f406SChristoph Hellwig out:
6500e09f406SChristoph Hellwig 	mutex_unlock(&vgpu->gvt->lock);
6510e09f406SChristoph Hellwig 	return ret;
6520e09f406SChristoph Hellwig }
6530e09f406SChristoph Hellwig 
intel_vgpu_open_device(struct vfio_device * vfio_dev)654978cf586SChristoph Hellwig static int intel_vgpu_open_device(struct vfio_device *vfio_dev)
655659643f7SJike Song {
656978cf586SChristoph Hellwig 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
657f22b1e85SSean Christopherson 	int ret;
658659643f7SJike Song 
6590e09f406SChristoph Hellwig 	if (__kvmgt_vgpu_exist(vgpu))
660ce4b4657SJason Gunthorpe 		return -EEXIST;
6610e09f406SChristoph Hellwig 
6620e09f406SChristoph Hellwig 	vgpu->track_node.track_write = kvmgt_page_track_write;
663c15fcf12SYan Zhao 	vgpu->track_node.track_remove_region = kvmgt_page_track_remove_region;
664f22b1e85SSean Christopherson 	ret = kvm_page_track_register_notifier(vgpu->vfio_device.kvm,
665421cfe65SMatthew Rosato 					       &vgpu->track_node);
666f22b1e85SSean Christopherson 	if (ret) {
667f22b1e85SSean Christopherson 		gvt_vgpu_err("KVM is required to use Intel vGPU\n");
668f22b1e85SSean Christopherson 		return ret;
669f22b1e85SSean Christopherson 	}
6700e09f406SChristoph Hellwig 
671a06d4b9eSZhi Wang 	set_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status);
672a06d4b9eSZhi Wang 
6730e09f406SChristoph Hellwig 	debugfs_create_ulong(KVMGT_DEBUGFS_FILENAME, 0444, vgpu->debugfs,
6740e09f406SChristoph Hellwig 			     &vgpu->nr_cache_entries);
6750e09f406SChristoph Hellwig 
676675e5c4aSChristoph Hellwig 	intel_gvt_activate_vgpu(vgpu);
677b79c52aeSZhi Wang 
6780e09f406SChristoph Hellwig 	return 0;
679659643f7SJike Song }
680659643f7SJike Song 
intel_vgpu_release_msi_eventfd_ctx(struct intel_vgpu * vgpu)681d54e7934SXiong Zhang static void intel_vgpu_release_msi_eventfd_ctx(struct intel_vgpu *vgpu)
682d54e7934SXiong Zhang {
683d54e7934SXiong Zhang 	struct eventfd_ctx *trigger;
684d54e7934SXiong Zhang 
68562980cacSChristoph Hellwig 	trigger = vgpu->msi_trigger;
686d54e7934SXiong Zhang 	if (trigger) {
687d54e7934SXiong Zhang 		eventfd_ctx_put(trigger);
68862980cacSChristoph Hellwig 		vgpu->msi_trigger = NULL;
689d54e7934SXiong Zhang 	}
690d54e7934SXiong Zhang }
691d54e7934SXiong Zhang 
intel_vgpu_close_device(struct vfio_device * vfio_dev)692421cfe65SMatthew Rosato static void intel_vgpu_close_device(struct vfio_device *vfio_dev)
693659643f7SJike Song {
694421cfe65SMatthew Rosato 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
695659643f7SJike Song 
696675e5c4aSChristoph Hellwig 	intel_gvt_release_vgpu(vgpu);
697b79c52aeSZhi Wang 
698a06d4b9eSZhi Wang 	clear_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status);
699a06d4b9eSZhi Wang 
700d989bf54SGreg Kroah-Hartman 	debugfs_lookup_and_remove(KVMGT_DEBUGFS_FILENAME, vgpu->debugfs);
7010e09f406SChristoph Hellwig 
702421cfe65SMatthew Rosato 	kvm_page_track_unregister_notifier(vgpu->vfio_device.kvm,
703421cfe65SMatthew Rosato 					   &vgpu->track_node);
7043c9fd44bSSean Christopherson 
7050e09f406SChristoph Hellwig 	kvmgt_protect_table_destroy(vgpu);
7060e09f406SChristoph Hellwig 	gvt_cache_destroy(vgpu);
707364fb6b7SJike Song 
7084dc334caSYi Liu 	WARN_ON(vgpu->nr_cache_entries);
7094dc334caSYi Liu 
7104dc334caSYi Liu 	vgpu->gfn_cache = RB_ROOT;
7114dc334caSYi Liu 	vgpu->dma_addr_cache = RB_ROOT;
7124dc334caSYi Liu 
713d54e7934SXiong Zhang 	intel_vgpu_release_msi_eventfd_ctx(vgpu);
714659643f7SJike Song }
715659643f7SJike Song 
intel_vgpu_get_bar_addr(struct intel_vgpu * vgpu,int bar)7162e679d48SJani Nikula static u64 intel_vgpu_get_bar_addr(struct intel_vgpu *vgpu, int bar)
717659643f7SJike Song {
718659643f7SJike Song 	u32 start_lo, start_hi;
719659643f7SJike Song 	u32 mem_type;
720659643f7SJike Song 
721f090a00dSChangbin Du 	start_lo = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
722659643f7SJike Song 			PCI_BASE_ADDRESS_MEM_MASK;
723f090a00dSChangbin Du 	mem_type = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
724659643f7SJike Song 			PCI_BASE_ADDRESS_MEM_TYPE_MASK;
725659643f7SJike Song 
726659643f7SJike Song 	switch (mem_type) {
727659643f7SJike Song 	case PCI_BASE_ADDRESS_MEM_TYPE_64:
728659643f7SJike Song 		start_hi = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space
729f090a00dSChangbin Du 						+ bar + 4));
730659643f7SJike Song 		break;
731659643f7SJike Song 	case PCI_BASE_ADDRESS_MEM_TYPE_32:
732659643f7SJike Song 	case PCI_BASE_ADDRESS_MEM_TYPE_1M:
733659643f7SJike Song 		/* 1M mem BAR treated as 32-bit BAR */
734659643f7SJike Song 	default:
735659643f7SJike Song 		/* mem unknown type treated as 32-bit BAR */
736659643f7SJike Song 		start_hi = 0;
737659643f7SJike Song 		break;
738659643f7SJike Song 	}
739659643f7SJike Song 
740659643f7SJike Song 	return ((u64)start_hi << 32) | start_lo;
741659643f7SJike Song }
742659643f7SJike Song 
intel_vgpu_bar_rw(struct intel_vgpu * vgpu,int bar,u64 off,void * buf,unsigned int count,bool is_write)7432e679d48SJani Nikula static int intel_vgpu_bar_rw(struct intel_vgpu *vgpu, int bar, u64 off,
744f090a00dSChangbin Du 			     void *buf, unsigned int count, bool is_write)
745f090a00dSChangbin Du {
7462e679d48SJani Nikula 	u64 bar_start = intel_vgpu_get_bar_addr(vgpu, bar);
747f090a00dSChangbin Du 	int ret;
748f090a00dSChangbin Du 
749f090a00dSChangbin Du 	if (is_write)
750675e5c4aSChristoph Hellwig 		ret = intel_vgpu_emulate_mmio_write(vgpu,
751f090a00dSChangbin Du 					bar_start + off, buf, count);
752f090a00dSChangbin Du 	else
753675e5c4aSChristoph Hellwig 		ret = intel_vgpu_emulate_mmio_read(vgpu,
754f090a00dSChangbin Du 					bar_start + off, buf, count);
755f090a00dSChangbin Du 	return ret;
756f090a00dSChangbin Du }
757f090a00dSChangbin Du 
intel_vgpu_in_aperture(struct intel_vgpu * vgpu,u64 off)7582e679d48SJani Nikula static inline bool intel_vgpu_in_aperture(struct intel_vgpu *vgpu, u64 off)
759d480b28aSChangbin Du {
760d480b28aSChangbin Du 	return off >= vgpu_aperture_offset(vgpu) &&
761d480b28aSChangbin Du 	       off < vgpu_aperture_offset(vgpu) + vgpu_aperture_sz(vgpu);
762d480b28aSChangbin Du }
763d480b28aSChangbin Du 
intel_vgpu_aperture_rw(struct intel_vgpu * vgpu,u64 off,void * buf,unsigned long count,bool is_write)7642e679d48SJani Nikula static int intel_vgpu_aperture_rw(struct intel_vgpu *vgpu, u64 off,
765d480b28aSChangbin Du 		void *buf, unsigned long count, bool is_write)
766d480b28aSChangbin Du {
767196a6627SChris Wilson 	void __iomem *aperture_va;
768d480b28aSChangbin Du 
769d480b28aSChangbin Du 	if (!intel_vgpu_in_aperture(vgpu, off) ||
770d480b28aSChangbin Du 	    !intel_vgpu_in_aperture(vgpu, off + count)) {
771d480b28aSChangbin Du 		gvt_vgpu_err("Invalid aperture offset %llu\n", off);
772d480b28aSChangbin Du 		return -EINVAL;
773d480b28aSChangbin Du 	}
774d480b28aSChangbin Du 
775a61ac1e7SChris Wilson 	aperture_va = io_mapping_map_wc(&vgpu->gvt->gt->ggtt->iomap,
776d480b28aSChangbin Du 					ALIGN_DOWN(off, PAGE_SIZE),
777d480b28aSChangbin Du 					count + offset_in_page(off));
778d480b28aSChangbin Du 	if (!aperture_va)
779d480b28aSChangbin Du 		return -EIO;
780d480b28aSChangbin Du 
781d480b28aSChangbin Du 	if (is_write)
782196a6627SChris Wilson 		memcpy_toio(aperture_va + offset_in_page(off), buf, count);
783d480b28aSChangbin Du 	else
784196a6627SChris Wilson 		memcpy_fromio(buf, aperture_va + offset_in_page(off), count);
785d480b28aSChangbin Du 
786d480b28aSChangbin Du 	io_mapping_unmap(aperture_va);
787d480b28aSChangbin Du 
788d480b28aSChangbin Du 	return 0;
789d480b28aSChangbin Du }
790d480b28aSChangbin Du 
intel_vgpu_rw(struct intel_vgpu * vgpu,char * buf,size_t count,loff_t * ppos,bool is_write)7917f11e689SChristoph Hellwig static ssize_t intel_vgpu_rw(struct intel_vgpu *vgpu, char *buf,
792659643f7SJike Song 			size_t count, loff_t *ppos, bool is_write)
793659643f7SJike Song {
794659643f7SJike Song 	unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
7952e679d48SJani Nikula 	u64 pos = *ppos & VFIO_PCI_OFFSET_MASK;
796659643f7SJike Song 	int ret = -EINVAL;
797659643f7SJike Song 
798659643f7SJike Song 
79962980cacSChristoph Hellwig 	if (index >= VFIO_PCI_NUM_REGIONS + vgpu->num_regions) {
800695fbc08STina Zhang 		gvt_vgpu_err("invalid index: %u\n", index);
801659643f7SJike Song 		return -EINVAL;
802659643f7SJike Song 	}
803659643f7SJike Song 
804659643f7SJike Song 	switch (index) {
805659643f7SJike Song 	case VFIO_PCI_CONFIG_REGION_INDEX:
806659643f7SJike Song 		if (is_write)
807675e5c4aSChristoph Hellwig 			ret = intel_vgpu_emulate_cfg_write(vgpu, pos,
808659643f7SJike Song 						buf, count);
809659643f7SJike Song 		else
810675e5c4aSChristoph Hellwig 			ret = intel_vgpu_emulate_cfg_read(vgpu, pos,
811659643f7SJike Song 						buf, count);
812659643f7SJike Song 		break;
813659643f7SJike Song 	case VFIO_PCI_BAR0_REGION_INDEX:
814f090a00dSChangbin Du 		ret = intel_vgpu_bar_rw(vgpu, PCI_BASE_ADDRESS_0, pos,
815f090a00dSChangbin Du 					buf, count, is_write);
816f090a00dSChangbin Du 		break;
817f090a00dSChangbin Du 	case VFIO_PCI_BAR2_REGION_INDEX:
818d480b28aSChangbin Du 		ret = intel_vgpu_aperture_rw(vgpu, pos, buf, count, is_write);
819659643f7SJike Song 		break;
8205d5fe176SChangbin Du 	case VFIO_PCI_BAR1_REGION_INDEX:
821659643f7SJike Song 	case VFIO_PCI_BAR3_REGION_INDEX:
822659643f7SJike Song 	case VFIO_PCI_BAR4_REGION_INDEX:
823659643f7SJike Song 	case VFIO_PCI_BAR5_REGION_INDEX:
824659643f7SJike Song 	case VFIO_PCI_VGA_REGION_INDEX:
825659643f7SJike Song 	case VFIO_PCI_ROM_REGION_INDEX:
826b851adeaSTina Zhang 		break;
827659643f7SJike Song 	default:
82862980cacSChristoph Hellwig 		if (index >= VFIO_PCI_NUM_REGIONS + vgpu->num_regions)
829b851adeaSTina Zhang 			return -EINVAL;
830b851adeaSTina Zhang 
831b851adeaSTina Zhang 		index -= VFIO_PCI_NUM_REGIONS;
83262980cacSChristoph Hellwig 		return vgpu->region[index].ops->rw(vgpu, buf, count,
833b851adeaSTina Zhang 				ppos, is_write);
834659643f7SJike Song 	}
835659643f7SJike Song 
836659643f7SJike Song 	return ret == 0 ? count : ret;
837659643f7SJike Song }
838659643f7SJike Song 
gtt_entry(struct intel_vgpu * vgpu,loff_t * ppos)8397f11e689SChristoph Hellwig static bool gtt_entry(struct intel_vgpu *vgpu, loff_t *ppos)
840a26ca6adSTina Zhang {
841a26ca6adSTina Zhang 	unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
842a26ca6adSTina Zhang 	struct intel_gvt *gvt = vgpu->gvt;
843a26ca6adSTina Zhang 	int offset;
844a26ca6adSTina Zhang 
845a26ca6adSTina Zhang 	/* Only allow MMIO GGTT entry access */
846a26ca6adSTina Zhang 	if (index != PCI_BASE_ADDRESS_0)
847a26ca6adSTina Zhang 		return false;
848a26ca6adSTina Zhang 
849a26ca6adSTina Zhang 	offset = (u64)(*ppos & VFIO_PCI_OFFSET_MASK) -
850a26ca6adSTina Zhang 		intel_vgpu_get_bar_gpa(vgpu, PCI_BASE_ADDRESS_0);
851a26ca6adSTina Zhang 
852a26ca6adSTina Zhang 	return (offset >= gvt->device_info.gtt_start_offset &&
853a26ca6adSTina Zhang 		offset < gvt->device_info.gtt_start_offset + gvt_ggtt_sz(gvt)) ?
854a26ca6adSTina Zhang 			true : false;
855a26ca6adSTina Zhang }
856a26ca6adSTina Zhang 
intel_vgpu_read(struct vfio_device * vfio_dev,char __user * buf,size_t count,loff_t * ppos)857978cf586SChristoph Hellwig static ssize_t intel_vgpu_read(struct vfio_device *vfio_dev, char __user *buf,
858659643f7SJike Song 			size_t count, loff_t *ppos)
859659643f7SJike Song {
860978cf586SChristoph Hellwig 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
861659643f7SJike Song 	unsigned int done = 0;
862659643f7SJike Song 	int ret;
863659643f7SJike Song 
864659643f7SJike Song 	while (count) {
865659643f7SJike Song 		size_t filled;
866659643f7SJike Song 
867a26ca6adSTina Zhang 		/* Only support GGTT entry 8 bytes read */
868a26ca6adSTina Zhang 		if (count >= 8 && !(*ppos % 8) &&
8697f11e689SChristoph Hellwig 			gtt_entry(vgpu, ppos)) {
870a26ca6adSTina Zhang 			u64 val;
871a26ca6adSTina Zhang 
8727f11e689SChristoph Hellwig 			ret = intel_vgpu_rw(vgpu, (char *)&val, sizeof(val),
873a26ca6adSTina Zhang 					ppos, false);
874a26ca6adSTina Zhang 			if (ret <= 0)
875a26ca6adSTina Zhang 				goto read_err;
876a26ca6adSTina Zhang 
877a26ca6adSTina Zhang 			if (copy_to_user(buf, &val, sizeof(val)))
878a26ca6adSTina Zhang 				goto read_err;
879a26ca6adSTina Zhang 
880a26ca6adSTina Zhang 			filled = 8;
881a26ca6adSTina Zhang 		} else if (count >= 4 && !(*ppos % 4)) {
882659643f7SJike Song 			u32 val;
883659643f7SJike Song 
8847f11e689SChristoph Hellwig 			ret = intel_vgpu_rw(vgpu, (char *)&val, sizeof(val),
885659643f7SJike Song 					ppos, false);
886659643f7SJike Song 			if (ret <= 0)
887659643f7SJike Song 				goto read_err;
888659643f7SJike Song 
889659643f7SJike Song 			if (copy_to_user(buf, &val, sizeof(val)))
890659643f7SJike Song 				goto read_err;
891659643f7SJike Song 
892659643f7SJike Song 			filled = 4;
893659643f7SJike Song 		} else if (count >= 2 && !(*ppos % 2)) {
894659643f7SJike Song 			u16 val;
895659643f7SJike Song 
8967f11e689SChristoph Hellwig 			ret = intel_vgpu_rw(vgpu, (char *)&val, sizeof(val),
897659643f7SJike Song 					ppos, false);
898659643f7SJike Song 			if (ret <= 0)
899659643f7SJike Song 				goto read_err;
900659643f7SJike Song 
901659643f7SJike Song 			if (copy_to_user(buf, &val, sizeof(val)))
902659643f7SJike Song 				goto read_err;
903659643f7SJike Song 
904659643f7SJike Song 			filled = 2;
905659643f7SJike Song 		} else {
906659643f7SJike Song 			u8 val;
907659643f7SJike Song 
9087f11e689SChristoph Hellwig 			ret = intel_vgpu_rw(vgpu, &val, sizeof(val), ppos,
909659643f7SJike Song 					false);
910659643f7SJike Song 			if (ret <= 0)
911659643f7SJike Song 				goto read_err;
912659643f7SJike Song 
913659643f7SJike Song 			if (copy_to_user(buf, &val, sizeof(val)))
914659643f7SJike Song 				goto read_err;
915659643f7SJike Song 
916659643f7SJike Song 			filled = 1;
917659643f7SJike Song 		}
918659643f7SJike Song 
919659643f7SJike Song 		count -= filled;
920659643f7SJike Song 		done += filled;
921659643f7SJike Song 		*ppos += filled;
922659643f7SJike Song 		buf += filled;
923659643f7SJike Song 	}
924659643f7SJike Song 
925659643f7SJike Song 	return done;
926659643f7SJike Song 
927659643f7SJike Song read_err:
928659643f7SJike Song 	return -EFAULT;
929659643f7SJike Song }
930659643f7SJike Song 
intel_vgpu_write(struct vfio_device * vfio_dev,const char __user * buf,size_t count,loff_t * ppos)931978cf586SChristoph Hellwig static ssize_t intel_vgpu_write(struct vfio_device *vfio_dev,
932659643f7SJike Song 				const char __user *buf,
933659643f7SJike Song 				size_t count, loff_t *ppos)
934659643f7SJike Song {
935978cf586SChristoph Hellwig 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
936659643f7SJike Song 	unsigned int done = 0;
937659643f7SJike Song 	int ret;
938659643f7SJike Song 
939659643f7SJike Song 	while (count) {
940659643f7SJike Song 		size_t filled;
941659643f7SJike Song 
942a26ca6adSTina Zhang 		/* Only support GGTT entry 8 bytes write */
943a26ca6adSTina Zhang 		if (count >= 8 && !(*ppos % 8) &&
9447f11e689SChristoph Hellwig 			gtt_entry(vgpu, ppos)) {
945a26ca6adSTina Zhang 			u64 val;
946a26ca6adSTina Zhang 
947a26ca6adSTina Zhang 			if (copy_from_user(&val, buf, sizeof(val)))
948a26ca6adSTina Zhang 				goto write_err;
949a26ca6adSTina Zhang 
9507f11e689SChristoph Hellwig 			ret = intel_vgpu_rw(vgpu, (char *)&val, sizeof(val),
951a26ca6adSTina Zhang 					ppos, true);
952a26ca6adSTina Zhang 			if (ret <= 0)
953a26ca6adSTina Zhang 				goto write_err;
954a26ca6adSTina Zhang 
955a26ca6adSTina Zhang 			filled = 8;
956a26ca6adSTina Zhang 		} else if (count >= 4 && !(*ppos % 4)) {
957659643f7SJike Song 			u32 val;
958659643f7SJike Song 
959659643f7SJike Song 			if (copy_from_user(&val, buf, sizeof(val)))
960659643f7SJike Song 				goto write_err;
961659643f7SJike Song 
9627f11e689SChristoph Hellwig 			ret = intel_vgpu_rw(vgpu, (char *)&val, sizeof(val),
963659643f7SJike Song 					ppos, true);
964659643f7SJike Song 			if (ret <= 0)
965659643f7SJike Song 				goto write_err;
966659643f7SJike Song 
967659643f7SJike Song 			filled = 4;
968659643f7SJike Song 		} else if (count >= 2 && !(*ppos % 2)) {
969659643f7SJike Song 			u16 val;
970659643f7SJike Song 
971659643f7SJike Song 			if (copy_from_user(&val, buf, sizeof(val)))
972659643f7SJike Song 				goto write_err;
973659643f7SJike Song 
9747f11e689SChristoph Hellwig 			ret = intel_vgpu_rw(vgpu, (char *)&val,
975659643f7SJike Song 					sizeof(val), ppos, true);
976659643f7SJike Song 			if (ret <= 0)
977659643f7SJike Song 				goto write_err;
978659643f7SJike Song 
979659643f7SJike Song 			filled = 2;
980659643f7SJike Song 		} else {
981659643f7SJike Song 			u8 val;
982659643f7SJike Song 
983659643f7SJike Song 			if (copy_from_user(&val, buf, sizeof(val)))
984659643f7SJike Song 				goto write_err;
985659643f7SJike Song 
9867f11e689SChristoph Hellwig 			ret = intel_vgpu_rw(vgpu, &val, sizeof(val),
987659643f7SJike Song 					ppos, true);
988659643f7SJike Song 			if (ret <= 0)
989659643f7SJike Song 				goto write_err;
990659643f7SJike Song 
991659643f7SJike Song 			filled = 1;
992659643f7SJike Song 		}
993659643f7SJike Song 
994659643f7SJike Song 		count -= filled;
995659643f7SJike Song 		done += filled;
996659643f7SJike Song 		*ppos += filled;
997659643f7SJike Song 		buf += filled;
998659643f7SJike Song 	}
999659643f7SJike Song 
1000659643f7SJike Song 	return done;
1001659643f7SJike Song write_err:
1002659643f7SJike Song 	return -EFAULT;
1003659643f7SJike Song }
1004659643f7SJike Song 
intel_vgpu_mmap(struct vfio_device * vfio_dev,struct vm_area_struct * vma)1005978cf586SChristoph Hellwig static int intel_vgpu_mmap(struct vfio_device *vfio_dev,
1006978cf586SChristoph Hellwig 		struct vm_area_struct *vma)
1007659643f7SJike Song {
1008978cf586SChristoph Hellwig 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
1009659643f7SJike Song 	unsigned int index;
1010659643f7SJike Song 	u64 virtaddr;
101151b00d85SZhenyu Wang 	unsigned long req_size, pgoff, req_start;
1012659643f7SJike Song 	pgprot_t pg_prot;
1013659643f7SJike Song 
1014659643f7SJike Song 	index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
1015659643f7SJike Song 	if (index >= VFIO_PCI_ROM_REGION_INDEX)
1016659643f7SJike Song 		return -EINVAL;
1017659643f7SJike Song 
1018659643f7SJike Song 	if (vma->vm_end < vma->vm_start)
1019659643f7SJike Song 		return -EINVAL;
1020659643f7SJike Song 	if ((vma->vm_flags & VM_SHARED) == 0)
1021659643f7SJike Song 		return -EINVAL;
1022659643f7SJike Song 	if (index != VFIO_PCI_BAR2_REGION_INDEX)
1023659643f7SJike Song 		return -EINVAL;
1024659643f7SJike Song 
1025659643f7SJike Song 	pg_prot = vma->vm_page_prot;
1026659643f7SJike Song 	virtaddr = vma->vm_start;
1027659643f7SJike Song 	req_size = vma->vm_end - vma->vm_start;
102851b00d85SZhenyu Wang 	pgoff = vma->vm_pgoff &
102951b00d85SZhenyu Wang 		((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
103051b00d85SZhenyu Wang 	req_start = pgoff << PAGE_SHIFT;
103151b00d85SZhenyu Wang 
103251b00d85SZhenyu Wang 	if (!intel_vgpu_in_aperture(vgpu, req_start))
103351b00d85SZhenyu Wang 		return -EINVAL;
103451b00d85SZhenyu Wang 	if (req_start + req_size >
103551b00d85SZhenyu Wang 	    vgpu_aperture_offset(vgpu) + vgpu_aperture_sz(vgpu))
103651b00d85SZhenyu Wang 		return -EINVAL;
103751b00d85SZhenyu Wang 
103851b00d85SZhenyu Wang 	pgoff = (gvt_aperture_pa_base(vgpu->gvt) >> PAGE_SHIFT) + pgoff;
1039659643f7SJike Song 
1040659643f7SJike Song 	return remap_pfn_range(vma, virtaddr, pgoff, req_size, pg_prot);
1041659643f7SJike Song }
1042659643f7SJike Song 
intel_vgpu_get_irq_count(struct intel_vgpu * vgpu,int type)1043659643f7SJike Song static int intel_vgpu_get_irq_count(struct intel_vgpu *vgpu, int type)
1044659643f7SJike Song {
1045659643f7SJike Song 	if (type == VFIO_PCI_INTX_IRQ_INDEX || type == VFIO_PCI_MSI_IRQ_INDEX)
1046659643f7SJike Song 		return 1;
1047659643f7SJike Song 
1048659643f7SJike Song 	return 0;
1049659643f7SJike Song }
1050659643f7SJike Song 
intel_vgpu_set_intx_mask(struct intel_vgpu * vgpu,unsigned int index,unsigned int start,unsigned int count,u32 flags,void * data)1051659643f7SJike Song static int intel_vgpu_set_intx_mask(struct intel_vgpu *vgpu,
1052659643f7SJike Song 			unsigned int index, unsigned int start,
10532e679d48SJani Nikula 			unsigned int count, u32 flags,
1054659643f7SJike Song 			void *data)
1055659643f7SJike Song {
1056659643f7SJike Song 	return 0;
1057659643f7SJike Song }
1058659643f7SJike Song 
intel_vgpu_set_intx_unmask(struct intel_vgpu * vgpu,unsigned int index,unsigned int start,unsigned int count,u32 flags,void * data)1059659643f7SJike Song static int intel_vgpu_set_intx_unmask(struct intel_vgpu *vgpu,
1060659643f7SJike Song 			unsigned int index, unsigned int start,
10612e679d48SJani Nikula 			unsigned int count, u32 flags, void *data)
1062659643f7SJike Song {
1063659643f7SJike Song 	return 0;
1064659643f7SJike Song }
1065659643f7SJike Song 
intel_vgpu_set_intx_trigger(struct intel_vgpu * vgpu,unsigned int index,unsigned int start,unsigned int count,u32 flags,void * data)1066659643f7SJike Song static int intel_vgpu_set_intx_trigger(struct intel_vgpu *vgpu,
1067659643f7SJike Song 		unsigned int index, unsigned int start, unsigned int count,
10682e679d48SJani Nikula 		u32 flags, void *data)
1069659643f7SJike Song {
1070659643f7SJike Song 	return 0;
1071659643f7SJike Song }
1072659643f7SJike Song 
intel_vgpu_set_msi_trigger(struct intel_vgpu * vgpu,unsigned int index,unsigned int start,unsigned int count,u32 flags,void * data)1073659643f7SJike Song static int intel_vgpu_set_msi_trigger(struct intel_vgpu *vgpu,
1074659643f7SJike Song 		unsigned int index, unsigned int start, unsigned int count,
10752e679d48SJani Nikula 		u32 flags, void *data)
1076659643f7SJike Song {
1077659643f7SJike Song 	struct eventfd_ctx *trigger;
1078659643f7SJike Song 
1079659643f7SJike Song 	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
1080659643f7SJike Song 		int fd = *(int *)data;
1081659643f7SJike Song 
1082659643f7SJike Song 		trigger = eventfd_ctx_fdget(fd);
1083659643f7SJike Song 		if (IS_ERR(trigger)) {
1084695fbc08STina Zhang 			gvt_vgpu_err("eventfd_ctx_fdget failed\n");
1085659643f7SJike Song 			return PTR_ERR(trigger);
1086659643f7SJike Song 		}
108762980cacSChristoph Hellwig 		vgpu->msi_trigger = trigger;
1088d54e7934SXiong Zhang 	} else if ((flags & VFIO_IRQ_SET_DATA_NONE) && !count)
1089d54e7934SXiong Zhang 		intel_vgpu_release_msi_eventfd_ctx(vgpu);
1090659643f7SJike Song 
1091659643f7SJike Song 	return 0;
1092659643f7SJike Song }
1093659643f7SJike Song 
intel_vgpu_set_irqs(struct intel_vgpu * vgpu,u32 flags,unsigned int index,unsigned int start,unsigned int count,void * data)10942e679d48SJani Nikula static int intel_vgpu_set_irqs(struct intel_vgpu *vgpu, u32 flags,
1095659643f7SJike Song 		unsigned int index, unsigned int start, unsigned int count,
1096659643f7SJike Song 		void *data)
1097659643f7SJike Song {
1098659643f7SJike Song 	int (*func)(struct intel_vgpu *vgpu, unsigned int index,
10992e679d48SJani Nikula 			unsigned int start, unsigned int count, u32 flags,
1100659643f7SJike Song 			void *data) = NULL;
1101659643f7SJike Song 
1102659643f7SJike Song 	switch (index) {
1103659643f7SJike Song 	case VFIO_PCI_INTX_IRQ_INDEX:
1104659643f7SJike Song 		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
1105659643f7SJike Song 		case VFIO_IRQ_SET_ACTION_MASK:
1106659643f7SJike Song 			func = intel_vgpu_set_intx_mask;
1107659643f7SJike Song 			break;
1108659643f7SJike Song 		case VFIO_IRQ_SET_ACTION_UNMASK:
1109659643f7SJike Song 			func = intel_vgpu_set_intx_unmask;
1110659643f7SJike Song 			break;
1111659643f7SJike Song 		case VFIO_IRQ_SET_ACTION_TRIGGER:
1112659643f7SJike Song 			func = intel_vgpu_set_intx_trigger;
1113659643f7SJike Song 			break;
1114659643f7SJike Song 		}
1115659643f7SJike Song 		break;
1116659643f7SJike Song 	case VFIO_PCI_MSI_IRQ_INDEX:
1117659643f7SJike Song 		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
1118659643f7SJike Song 		case VFIO_IRQ_SET_ACTION_MASK:
1119659643f7SJike Song 		case VFIO_IRQ_SET_ACTION_UNMASK:
1120659643f7SJike Song 			/* XXX Need masking support exported */
1121659643f7SJike Song 			break;
1122659643f7SJike Song 		case VFIO_IRQ_SET_ACTION_TRIGGER:
1123659643f7SJike Song 			func = intel_vgpu_set_msi_trigger;
1124659643f7SJike Song 			break;
1125659643f7SJike Song 		}
1126659643f7SJike Song 		break;
1127659643f7SJike Song 	}
1128659643f7SJike Song 
1129659643f7SJike Song 	if (!func)
1130659643f7SJike Song 		return -ENOTTY;
1131659643f7SJike Song 
1132659643f7SJike Song 	return func(vgpu, index, start, count, flags, data);
1133659643f7SJike Song }
1134659643f7SJike Song 
intel_vgpu_ioctl(struct vfio_device * vfio_dev,unsigned int cmd,unsigned long arg)1135978cf586SChristoph Hellwig static long intel_vgpu_ioctl(struct vfio_device *vfio_dev, unsigned int cmd,
1136659643f7SJike Song 			     unsigned long arg)
1137659643f7SJike Song {
1138978cf586SChristoph Hellwig 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
1139659643f7SJike Song 	unsigned long minsz;
1140659643f7SJike Song 
1141659643f7SJike Song 	gvt_dbg_core("vgpu%d ioctl, cmd: %d\n", vgpu->id, cmd);
1142659643f7SJike Song 
1143659643f7SJike Song 	if (cmd == VFIO_DEVICE_GET_INFO) {
1144659643f7SJike Song 		struct vfio_device_info info;
1145659643f7SJike Song 
1146659643f7SJike Song 		minsz = offsetofend(struct vfio_device_info, num_irqs);
1147659643f7SJike Song 
1148659643f7SJike Song 		if (copy_from_user(&info, (void __user *)arg, minsz))
1149659643f7SJike Song 			return -EFAULT;
1150659643f7SJike Song 
1151659643f7SJike Song 		if (info.argsz < minsz)
1152659643f7SJike Song 			return -EINVAL;
1153659643f7SJike Song 
1154659643f7SJike Song 		info.flags = VFIO_DEVICE_FLAGS_PCI;
1155659643f7SJike Song 		info.flags |= VFIO_DEVICE_FLAGS_RESET;
1156b851adeaSTina Zhang 		info.num_regions = VFIO_PCI_NUM_REGIONS +
115762980cacSChristoph Hellwig 				vgpu->num_regions;
1158659643f7SJike Song 		info.num_irqs = VFIO_PCI_NUM_IRQS;
1159659643f7SJike Song 
1160659643f7SJike Song 		return copy_to_user((void __user *)arg, &info, minsz) ?
1161659643f7SJike Song 			-EFAULT : 0;
1162659643f7SJike Song 
1163659643f7SJike Song 	} else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
1164659643f7SJike Song 		struct vfio_region_info info;
1165659643f7SJike Song 		struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
1166de5372daSGustavo A. R. Silva 		unsigned int i;
1167de5372daSGustavo A. R. Silva 		int ret;
1168659643f7SJike Song 		struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
1169659643f7SJike Song 		int nr_areas = 1;
1170659643f7SJike Song 		int cap_type_id;
1171659643f7SJike Song 
1172659643f7SJike Song 		minsz = offsetofend(struct vfio_region_info, offset);
1173659643f7SJike Song 
1174659643f7SJike Song 		if (copy_from_user(&info, (void __user *)arg, minsz))
1175659643f7SJike Song 			return -EFAULT;
1176659643f7SJike Song 
1177659643f7SJike Song 		if (info.argsz < minsz)
1178659643f7SJike Song 			return -EINVAL;
1179659643f7SJike Song 
1180659643f7SJike Song 		switch (info.index) {
1181659643f7SJike Song 		case VFIO_PCI_CONFIG_REGION_INDEX:
1182659643f7SJike Song 			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
118302d578e5SChangbin Du 			info.size = vgpu->gvt->device_info.cfg_space_size;
1184659643f7SJike Song 			info.flags = VFIO_REGION_INFO_FLAG_READ |
1185659643f7SJike Song 				     VFIO_REGION_INFO_FLAG_WRITE;
1186659643f7SJike Song 			break;
1187659643f7SJike Song 		case VFIO_PCI_BAR0_REGION_INDEX:
1188659643f7SJike Song 			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
1189659643f7SJike Song 			info.size = vgpu->cfg_space.bar[info.index].size;
1190659643f7SJike Song 			if (!info.size) {
1191659643f7SJike Song 				info.flags = 0;
1192659643f7SJike Song 				break;
1193659643f7SJike Song 			}
1194659643f7SJike Song 
1195659643f7SJike Song 			info.flags = VFIO_REGION_INFO_FLAG_READ |
1196659643f7SJike Song 				     VFIO_REGION_INFO_FLAG_WRITE;
1197659643f7SJike Song 			break;
1198659643f7SJike Song 		case VFIO_PCI_BAR1_REGION_INDEX:
1199659643f7SJike Song 			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
1200659643f7SJike Song 			info.size = 0;
1201659643f7SJike Song 			info.flags = 0;
1202659643f7SJike Song 			break;
1203659643f7SJike Song 		case VFIO_PCI_BAR2_REGION_INDEX:
1204659643f7SJike Song 			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
1205659643f7SJike Song 			info.flags = VFIO_REGION_INFO_FLAG_CAPS |
1206659643f7SJike Song 					VFIO_REGION_INFO_FLAG_MMAP |
1207659643f7SJike Song 					VFIO_REGION_INFO_FLAG_READ |
1208659643f7SJike Song 					VFIO_REGION_INFO_FLAG_WRITE;
1209659643f7SJike Song 			info.size = gvt_aperture_sz(vgpu->gvt);
1210659643f7SJike Song 
1211cd3e0583SGustavo A. R. Silva 			sparse = kzalloc(struct_size(sparse, areas, nr_areas),
1212cd3e0583SGustavo A. R. Silva 					 GFP_KERNEL);
1213659643f7SJike Song 			if (!sparse)
1214659643f7SJike Song 				return -ENOMEM;
1215659643f7SJike Song 
1216dda01f78SAlex Williamson 			sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
1217dda01f78SAlex Williamson 			sparse->header.version = 1;
1218659643f7SJike Song 			sparse->nr_areas = nr_areas;
1219659643f7SJike Song 			cap_type_id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
1220659643f7SJike Song 			sparse->areas[0].offset =
1221659643f7SJike Song 					PAGE_ALIGN(vgpu_aperture_offset(vgpu));
1222659643f7SJike Song 			sparse->areas[0].size = vgpu_aperture_sz(vgpu);
1223659643f7SJike Song 			break;
1224659643f7SJike Song 
1225659643f7SJike Song 		case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
1226659643f7SJike Song 			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
1227659643f7SJike Song 			info.size = 0;
1228659643f7SJike Song 			info.flags = 0;
1229072ec93dSPei Zhang 
1230659643f7SJike Song 			gvt_dbg_core("get region info bar:%d\n", info.index);
1231659643f7SJike Song 			break;
1232659643f7SJike Song 
1233659643f7SJike Song 		case VFIO_PCI_ROM_REGION_INDEX:
1234659643f7SJike Song 		case VFIO_PCI_VGA_REGION_INDEX:
1235072ec93dSPei Zhang 			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
1236072ec93dSPei Zhang 			info.size = 0;
1237072ec93dSPei Zhang 			info.flags = 0;
1238072ec93dSPei Zhang 
1239659643f7SJike Song 			gvt_dbg_core("get region info index:%d\n", info.index);
1240659643f7SJike Song 			break;
1241659643f7SJike Song 		default:
1242659643f7SJike Song 			{
1243dda01f78SAlex Williamson 				struct vfio_region_info_cap_type cap_type = {
1244dda01f78SAlex Williamson 					.header.id = VFIO_REGION_INFO_CAP_TYPE,
1245dda01f78SAlex Williamson 					.header.version = 1 };
1246659643f7SJike Song 
1247659643f7SJike Song 				if (info.index >= VFIO_PCI_NUM_REGIONS +
124862980cacSChristoph Hellwig 						vgpu->num_regions)
1249659643f7SJike Song 					return -EINVAL;
1250de5372daSGustavo A. R. Silva 				info.index =
1251de5372daSGustavo A. R. Silva 					array_index_nospec(info.index,
1252de5372daSGustavo A. R. Silva 							VFIO_PCI_NUM_REGIONS +
125362980cacSChristoph Hellwig 							vgpu->num_regions);
1254659643f7SJike Song 
1255659643f7SJike Song 				i = info.index - VFIO_PCI_NUM_REGIONS;
1256659643f7SJike Song 
1257659643f7SJike Song 				info.offset =
1258659643f7SJike Song 					VFIO_PCI_INDEX_TO_OFFSET(info.index);
125962980cacSChristoph Hellwig 				info.size = vgpu->region[i].size;
126062980cacSChristoph Hellwig 				info.flags = vgpu->region[i].flags;
1261659643f7SJike Song 
126262980cacSChristoph Hellwig 				cap_type.type = vgpu->region[i].type;
126362980cacSChristoph Hellwig 				cap_type.subtype = vgpu->region[i].subtype;
1264659643f7SJike Song 
1265659643f7SJike Song 				ret = vfio_info_add_capability(&caps,
1266dda01f78SAlex Williamson 							&cap_type.header,
1267dda01f78SAlex Williamson 							sizeof(cap_type));
1268659643f7SJike Song 				if (ret)
1269659643f7SJike Song 					return ret;
1270659643f7SJike Song 			}
1271659643f7SJike Song 		}
1272659643f7SJike Song 
1273659643f7SJike Song 		if ((info.flags & VFIO_REGION_INFO_FLAG_CAPS) && sparse) {
1274659643f7SJike Song 			switch (cap_type_id) {
1275659643f7SJike Song 			case VFIO_REGION_INFO_CAP_SPARSE_MMAP:
1276659643f7SJike Song 				ret = vfio_info_add_capability(&caps,
1277cd3e0583SGustavo A. R. Silva 					&sparse->header,
1278cd3e0583SGustavo A. R. Silva 					struct_size(sparse, areas,
1279cd3e0583SGustavo A. R. Silva 						    sparse->nr_areas));
12807590ebb8SYi Wang 				if (ret) {
1281659643f7SJike Song 					kfree(sparse);
1282659643f7SJike Song 					return ret;
12837590ebb8SYi Wang 				}
1284659643f7SJike Song 				break;
1285659643f7SJike Song 			default:
12867590ebb8SYi Wang 				kfree(sparse);
1287659643f7SJike Song 				return -EINVAL;
1288659643f7SJike Song 			}
1289659643f7SJike Song 		}
1290659643f7SJike Song 
1291659643f7SJike Song 		if (caps.size) {
1292b851adeaSTina Zhang 			info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
1293659643f7SJike Song 			if (info.argsz < sizeof(info) + caps.size) {
1294659643f7SJike Song 				info.argsz = sizeof(info) + caps.size;
1295659643f7SJike Song 				info.cap_offset = 0;
1296659643f7SJike Song 			} else {
1297659643f7SJike Song 				vfio_info_cap_shift(&caps, sizeof(info));
1298659643f7SJike Song 				if (copy_to_user((void __user *)arg +
1299659643f7SJike Song 						  sizeof(info), caps.buf,
1300659643f7SJike Song 						  caps.size)) {
1301659643f7SJike Song 					kfree(caps.buf);
13027590ebb8SYi Wang 					kfree(sparse);
1303659643f7SJike Song 					return -EFAULT;
1304659643f7SJike Song 				}
1305659643f7SJike Song 				info.cap_offset = sizeof(info);
1306659643f7SJike Song 			}
1307659643f7SJike Song 
1308659643f7SJike Song 			kfree(caps.buf);
1309659643f7SJike Song 		}
1310659643f7SJike Song 
13117590ebb8SYi Wang 		kfree(sparse);
1312659643f7SJike Song 		return copy_to_user((void __user *)arg, &info, minsz) ?
1313659643f7SJike Song 			-EFAULT : 0;
1314659643f7SJike Song 	} else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
1315659643f7SJike Song 		struct vfio_irq_info info;
1316659643f7SJike Song 
1317659643f7SJike Song 		minsz = offsetofend(struct vfio_irq_info, count);
1318659643f7SJike Song 
1319659643f7SJike Song 		if (copy_from_user(&info, (void __user *)arg, minsz))
1320659643f7SJike Song 			return -EFAULT;
1321659643f7SJike Song 
1322659643f7SJike Song 		if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
1323659643f7SJike Song 			return -EINVAL;
1324659643f7SJike Song 
1325659643f7SJike Song 		switch (info.index) {
1326659643f7SJike Song 		case VFIO_PCI_INTX_IRQ_INDEX:
1327659643f7SJike Song 		case VFIO_PCI_MSI_IRQ_INDEX:
1328659643f7SJike Song 			break;
1329659643f7SJike Song 		default:
1330659643f7SJike Song 			return -EINVAL;
1331659643f7SJike Song 		}
1332659643f7SJike Song 
1333659643f7SJike Song 		info.flags = VFIO_IRQ_INFO_EVENTFD;
1334659643f7SJike Song 
1335659643f7SJike Song 		info.count = intel_vgpu_get_irq_count(vgpu, info.index);
1336659643f7SJike Song 
1337659643f7SJike Song 		if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
1338659643f7SJike Song 			info.flags |= (VFIO_IRQ_INFO_MASKABLE |
1339659643f7SJike Song 				       VFIO_IRQ_INFO_AUTOMASKED);
1340659643f7SJike Song 		else
1341659643f7SJike Song 			info.flags |= VFIO_IRQ_INFO_NORESIZE;
1342659643f7SJike Song 
1343659643f7SJike Song 		return copy_to_user((void __user *)arg, &info, minsz) ?
1344659643f7SJike Song 			-EFAULT : 0;
1345659643f7SJike Song 	} else if (cmd == VFIO_DEVICE_SET_IRQS) {
1346659643f7SJike Song 		struct vfio_irq_set hdr;
1347659643f7SJike Song 		u8 *data = NULL;
1348659643f7SJike Song 		int ret = 0;
1349659643f7SJike Song 		size_t data_size = 0;
1350659643f7SJike Song 
1351659643f7SJike Song 		minsz = offsetofend(struct vfio_irq_set, count);
1352659643f7SJike Song 
1353659643f7SJike Song 		if (copy_from_user(&hdr, (void __user *)arg, minsz))
1354659643f7SJike Song 			return -EFAULT;
1355659643f7SJike Song 
1356659643f7SJike Song 		if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
1357659643f7SJike Song 			int max = intel_vgpu_get_irq_count(vgpu, hdr.index);
1358659643f7SJike Song 
1359659643f7SJike Song 			ret = vfio_set_irqs_validate_and_prepare(&hdr, max,
1360659643f7SJike Song 						VFIO_PCI_NUM_IRQS, &data_size);
1361659643f7SJike Song 			if (ret) {
1362695fbc08STina Zhang 				gvt_vgpu_err("intel:vfio_set_irqs_validate_and_prepare failed\n");
1363659643f7SJike Song 				return -EINVAL;
1364659643f7SJike Song 			}
1365659643f7SJike Song 			if (data_size) {
1366659643f7SJike Song 				data = memdup_user((void __user *)(arg + minsz),
1367659643f7SJike Song 						   data_size);
1368659643f7SJike Song 				if (IS_ERR(data))
1369659643f7SJike Song 					return PTR_ERR(data);
1370659643f7SJike Song 			}
1371659643f7SJike Song 		}
1372659643f7SJike Song 
1373659643f7SJike Song 		ret = intel_vgpu_set_irqs(vgpu, hdr.flags, hdr.index,
1374659643f7SJike Song 					hdr.start, hdr.count, data);
1375659643f7SJike Song 		kfree(data);
1376659643f7SJike Song 
1377659643f7SJike Song 		return ret;
1378659643f7SJike Song 	} else if (cmd == VFIO_DEVICE_RESET) {
1379675e5c4aSChristoph Hellwig 		intel_gvt_reset_vgpu(vgpu);
1380659643f7SJike Song 		return 0;
1381e546e281STina Zhang 	} else if (cmd == VFIO_DEVICE_QUERY_GFX_PLANE) {
1382e546e281STina Zhang 		struct vfio_device_gfx_plane_info dmabuf;
1383e546e281STina Zhang 		int ret = 0;
1384e546e281STina Zhang 
1385e546e281STina Zhang 		minsz = offsetofend(struct vfio_device_gfx_plane_info,
1386e546e281STina Zhang 				    dmabuf_id);
1387e546e281STina Zhang 		if (copy_from_user(&dmabuf, (void __user *)arg, minsz))
1388e546e281STina Zhang 			return -EFAULT;
1389e546e281STina Zhang 		if (dmabuf.argsz < minsz)
1390e546e281STina Zhang 			return -EINVAL;
1391e546e281STina Zhang 
1392675e5c4aSChristoph Hellwig 		ret = intel_vgpu_query_plane(vgpu, &dmabuf);
1393e546e281STina Zhang 		if (ret != 0)
1394e546e281STina Zhang 			return ret;
1395e546e281STina Zhang 
1396e546e281STina Zhang 		return copy_to_user((void __user *)arg, &dmabuf, minsz) ?
1397e546e281STina Zhang 								-EFAULT : 0;
1398e546e281STina Zhang 	} else if (cmd == VFIO_DEVICE_GET_GFX_DMABUF) {
1399e546e281STina Zhang 		__u32 dmabuf_id;
1400e546e281STina Zhang 
1401e546e281STina Zhang 		if (get_user(dmabuf_id, (__u32 __user *)arg))
1402e546e281STina Zhang 			return -EFAULT;
1403675e5c4aSChristoph Hellwig 		return intel_vgpu_get_dmabuf(vgpu, dmabuf_id);
1404659643f7SJike Song 	}
1405659643f7SJike Song 
14069f591ae6SGerd Hoffmann 	return -ENOTTY;
1407659643f7SJike Song }
1408659643f7SJike Song 
14097a7a6561SZhenyu Wang static ssize_t
vgpu_id_show(struct device * dev,struct device_attribute * attr,char * buf)14107a7a6561SZhenyu Wang vgpu_id_show(struct device *dev, struct device_attribute *attr,
14117a7a6561SZhenyu Wang 	     char *buf)
14127a7a6561SZhenyu Wang {
1413978cf586SChristoph Hellwig 	struct intel_vgpu *vgpu = dev_get_drvdata(dev);
14147a7a6561SZhenyu Wang 
14157a7a6561SZhenyu Wang 	return sprintf(buf, "%d\n", vgpu->id);
14167a7a6561SZhenyu Wang }
14177a7a6561SZhenyu Wang 
14187a7a6561SZhenyu Wang static DEVICE_ATTR_RO(vgpu_id);
14197a7a6561SZhenyu Wang 
14207a7a6561SZhenyu Wang static struct attribute *intel_vgpu_attrs[] = {
14217a7a6561SZhenyu Wang 	&dev_attr_vgpu_id.attr,
14227a7a6561SZhenyu Wang 	NULL
14237a7a6561SZhenyu Wang };
14247a7a6561SZhenyu Wang 
14257a7a6561SZhenyu Wang static const struct attribute_group intel_vgpu_group = {
14267a7a6561SZhenyu Wang 	.name = "intel_vgpu",
14277a7a6561SZhenyu Wang 	.attrs = intel_vgpu_attrs,
14287a7a6561SZhenyu Wang };
14297a7a6561SZhenyu Wang 
14307a7a6561SZhenyu Wang static const struct attribute_group *intel_vgpu_groups[] = {
14317a7a6561SZhenyu Wang 	&intel_vgpu_group,
14327a7a6561SZhenyu Wang 	NULL,
14337a7a6561SZhenyu Wang };
14347a7a6561SZhenyu Wang 
intel_vgpu_init_dev(struct vfio_device * vfio_dev)1435a5ddd2a9SKevin Tian static int intel_vgpu_init_dev(struct vfio_device *vfio_dev)
1436a5ddd2a9SKevin Tian {
1437a5ddd2a9SKevin Tian 	struct mdev_device *mdev = to_mdev_device(vfio_dev->dev);
1438a5ddd2a9SKevin Tian 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
1439da44c340SChristoph Hellwig 	struct intel_vgpu_type *type =
1440da44c340SChristoph Hellwig 		container_of(mdev->type, struct intel_vgpu_type, type);
14414dc334caSYi Liu 	int ret;
1442a5ddd2a9SKevin Tian 
1443062e720cSChristoph Hellwig 	vgpu->gvt = kdev_to_i915(mdev->type->parent->dev)->gvt;
14444dc334caSYi Liu 	ret = intel_gvt_create_vgpu(vgpu, type->conf);
14454dc334caSYi Liu 	if (ret)
14464dc334caSYi Liu 		return ret;
14474dc334caSYi Liu 
14484dc334caSYi Liu 	kvmgt_protect_table_init(vgpu);
14494dc334caSYi Liu 	gvt_cache_init(vgpu);
14504dc334caSYi Liu 
14514dc334caSYi Liu 	return 0;
1452a5ddd2a9SKevin Tian }
1453a5ddd2a9SKevin Tian 
intel_vgpu_release_dev(struct vfio_device * vfio_dev)1454a5ddd2a9SKevin Tian static void intel_vgpu_release_dev(struct vfio_device *vfio_dev)
1455a5ddd2a9SKevin Tian {
1456a5ddd2a9SKevin Tian 	struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
1457a5ddd2a9SKevin Tian 
1458a5ddd2a9SKevin Tian 	intel_gvt_destroy_vgpu(vgpu);
1459a5ddd2a9SKevin Tian }
1460a5ddd2a9SKevin Tian 
1461978cf586SChristoph Hellwig static const struct vfio_device_ops intel_vgpu_dev_ops = {
1462a5ddd2a9SKevin Tian 	.init		= intel_vgpu_init_dev,
1463a5ddd2a9SKevin Tian 	.release	= intel_vgpu_release_dev,
1464dd574d9bSJason Gunthorpe 	.open_device	= intel_vgpu_open_device,
1465dd574d9bSJason Gunthorpe 	.close_device	= intel_vgpu_close_device,
1466659643f7SJike Song 	.read		= intel_vgpu_read,
1467659643f7SJike Song 	.write		= intel_vgpu_write,
1468659643f7SJike Song 	.mmap		= intel_vgpu_mmap,
1469659643f7SJike Song 	.ioctl		= intel_vgpu_ioctl,
1470ce4b4657SJason Gunthorpe 	.dma_unmap	= intel_vgpu_dma_unmap,
14714741f2e9SJason Gunthorpe 	.bind_iommufd	= vfio_iommufd_emulated_bind,
14724741f2e9SJason Gunthorpe 	.unbind_iommufd = vfio_iommufd_emulated_unbind,
14734741f2e9SJason Gunthorpe 	.attach_ioas	= vfio_iommufd_emulated_attach_ioas,
14748cfa7186SYi Liu 	.detach_ioas	= vfio_iommufd_emulated_detach_ioas,
1475659643f7SJike Song };
1476659643f7SJike Song 
intel_vgpu_probe(struct mdev_device * mdev)1477978cf586SChristoph Hellwig static int intel_vgpu_probe(struct mdev_device *mdev)
1478978cf586SChristoph Hellwig {
1479978cf586SChristoph Hellwig 	struct intel_vgpu *vgpu;
1480978cf586SChristoph Hellwig 	int ret;
1481978cf586SChristoph Hellwig 
1482a5ddd2a9SKevin Tian 	vgpu = vfio_alloc_device(intel_vgpu, vfio_device, &mdev->dev,
1483a5ddd2a9SKevin Tian 				 &intel_vgpu_dev_ops);
1484978cf586SChristoph Hellwig 	if (IS_ERR(vgpu)) {
1485978cf586SChristoph Hellwig 		gvt_err("failed to create intel vgpu: %ld\n", PTR_ERR(vgpu));
1486978cf586SChristoph Hellwig 		return PTR_ERR(vgpu);
1487978cf586SChristoph Hellwig 	}
1488978cf586SChristoph Hellwig 
1489978cf586SChristoph Hellwig 	dev_set_drvdata(&mdev->dev, vgpu);
1490978cf586SChristoph Hellwig 	ret = vfio_register_emulated_iommu_dev(&vgpu->vfio_device);
1491a5ddd2a9SKevin Tian 	if (ret)
1492a5ddd2a9SKevin Tian 		goto out_put_vdev;
1493978cf586SChristoph Hellwig 
1494978cf586SChristoph Hellwig 	gvt_dbg_core("intel_vgpu_create succeeded for mdev: %s\n",
1495978cf586SChristoph Hellwig 		     dev_name(mdev_dev(mdev)));
1496978cf586SChristoph Hellwig 	return 0;
1497a5ddd2a9SKevin Tian 
1498a5ddd2a9SKevin Tian out_put_vdev:
1499a5ddd2a9SKevin Tian 	vfio_put_device(&vgpu->vfio_device);
1500a5ddd2a9SKevin Tian 	return ret;
1501978cf586SChristoph Hellwig }
1502978cf586SChristoph Hellwig 
intel_vgpu_remove(struct mdev_device * mdev)1503978cf586SChristoph Hellwig static void intel_vgpu_remove(struct mdev_device *mdev)
1504978cf586SChristoph Hellwig {
1505978cf586SChristoph Hellwig 	struct intel_vgpu *vgpu = dev_get_drvdata(&mdev->dev);
1506978cf586SChristoph Hellwig 
1507f423fa1bSJason Gunthorpe 	vfio_unregister_group_dev(&vgpu->vfio_device);
1508a5ddd2a9SKevin Tian 	vfio_put_device(&vgpu->vfio_device);
1509978cf586SChristoph Hellwig }
1510978cf586SChristoph Hellwig 
intel_vgpu_get_available(struct mdev_type * mtype)1511f2fbc72eSChristoph Hellwig static unsigned int intel_vgpu_get_available(struct mdev_type *mtype)
1512f2fbc72eSChristoph Hellwig {
1513f2fbc72eSChristoph Hellwig 	struct intel_vgpu_type *type =
1514f2fbc72eSChristoph Hellwig 		container_of(mtype, struct intel_vgpu_type, type);
1515f2fbc72eSChristoph Hellwig 	struct intel_gvt *gvt = kdev_to_i915(mtype->parent->dev)->gvt;
1516f2fbc72eSChristoph Hellwig 	unsigned int low_gm_avail, high_gm_avail, fence_avail;
1517f2fbc72eSChristoph Hellwig 
1518f2fbc72eSChristoph Hellwig 	mutex_lock(&gvt->lock);
1519f2fbc72eSChristoph Hellwig 	low_gm_avail = gvt_aperture_sz(gvt) - HOST_LOW_GM_SIZE -
1520f2fbc72eSChristoph Hellwig 		gvt->gm.vgpu_allocated_low_gm_size;
1521f2fbc72eSChristoph Hellwig 	high_gm_avail = gvt_hidden_sz(gvt) - HOST_HIGH_GM_SIZE -
1522f2fbc72eSChristoph Hellwig 		gvt->gm.vgpu_allocated_high_gm_size;
1523f2fbc72eSChristoph Hellwig 	fence_avail = gvt_fence_sz(gvt) - HOST_FENCE -
1524f2fbc72eSChristoph Hellwig 		gvt->fence.vgpu_allocated_fence_num;
1525f2fbc72eSChristoph Hellwig 	mutex_unlock(&gvt->lock);
1526f2fbc72eSChristoph Hellwig 
1527f2fbc72eSChristoph Hellwig 	return min3(low_gm_avail / type->conf->low_mm,
1528f2fbc72eSChristoph Hellwig 		    high_gm_avail / type->conf->high_mm,
1529f2fbc72eSChristoph Hellwig 		    fence_avail / type->conf->fence);
1530f2fbc72eSChristoph Hellwig }
1531f2fbc72eSChristoph Hellwig 
1532978cf586SChristoph Hellwig static struct mdev_driver intel_vgpu_mdev_driver = {
1533290aac5dSJason Gunthorpe 	.device_api	= VFIO_DEVICE_API_PCI_STRING,
1534978cf586SChristoph Hellwig 	.driver = {
1535978cf586SChristoph Hellwig 		.name		= "intel_vgpu_mdev",
1536978cf586SChristoph Hellwig 		.owner		= THIS_MODULE,
1537978cf586SChristoph Hellwig 		.dev_groups	= intel_vgpu_groups,
1538978cf586SChristoph Hellwig 	},
1539978cf586SChristoph Hellwig 	.probe			= intel_vgpu_probe,
1540978cf586SChristoph Hellwig 	.remove			= intel_vgpu_remove,
1541f2fbc72eSChristoph Hellwig 	.get_available		= intel_vgpu_get_available,
1542685a1537SChristoph Hellwig 	.show_description	= intel_vgpu_show_description,
1543978cf586SChristoph Hellwig };
1544978cf586SChristoph Hellwig 
intel_gvt_page_track_add(struct intel_vgpu * info,u64 gfn)15454c2baaafSChristoph Hellwig int intel_gvt_page_track_add(struct intel_vgpu *info, u64 gfn)
1546f30437c5SJike Song {
154796316a06SSean Christopherson 	int r;
1548f30437c5SJike Song 
1549a06d4b9eSZhi Wang 	if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, info->status))
1550659643f7SJike Song 		return -ESRCH;
1551659643f7SJike Song 
1552f30437c5SJike Song 	if (kvmgt_gfn_is_write_protected(info, gfn))
15533cca6b26SSean Christopherson 		return 0;
1554f30437c5SJike Song 
155596316a06SSean Christopherson 	r = kvm_write_track_add_gfn(info->vfio_device.kvm, gfn);
155696316a06SSean Christopherson 	if (r)
155796316a06SSean Christopherson 		return r;
15583cca6b26SSean Christopherson 
1559f30437c5SJike Song 	kvmgt_protect_table_add(info, gfn);
1560f30437c5SJike Song 	return 0;
1561f30437c5SJike Song }
1562f30437c5SJike Song 
intel_gvt_page_track_remove(struct intel_vgpu * info,u64 gfn)15634c2baaafSChristoph Hellwig int intel_gvt_page_track_remove(struct intel_vgpu *info, u64 gfn)
1564f30437c5SJike Song {
156596316a06SSean Christopherson 	int r;
1566f30437c5SJike Song 
1567a06d4b9eSZhi Wang 	if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, info->status))
1568a06d4b9eSZhi Wang 		return -ESRCH;
1569659643f7SJike Song 
1570f30437c5SJike Song 	if (!kvmgt_gfn_is_write_protected(info, gfn))
15713cca6b26SSean Christopherson 		return 0;
1572f30437c5SJike Song 
157396316a06SSean Christopherson 	r = kvm_write_track_remove_gfn(info->vfio_device.kvm, gfn);
157496316a06SSean Christopherson 	if (r)
157596316a06SSean Christopherson 		return r;
15763cca6b26SSean Christopherson 
1577f30437c5SJike Song 	kvmgt_protect_table_del(info, gfn);
1578f30437c5SJike Song 	return 0;
1579f30437c5SJike Song }
1580f30437c5SJike Song 
kvmgt_page_track_write(gpa_t gpa,const u8 * val,int len,struct kvm_page_track_notifier_node * node)1581b271e17dSSean Christopherson static void kvmgt_page_track_write(gpa_t gpa, const u8 *val, int len,
1582f30437c5SJike Song 				   struct kvm_page_track_notifier_node *node)
1583f30437c5SJike Song {
158410ddb962SChristoph Hellwig 	struct intel_vgpu *info =
158510ddb962SChristoph Hellwig 		container_of(node, struct intel_vgpu, track_node);
1586f30437c5SJike Song 
15873cca6b26SSean Christopherson 	mutex_lock(&info->vgpu_lock);
15883cca6b26SSean Christopherson 
1589*09c8726fSSean Christopherson 	if (kvmgt_gfn_is_write_protected(info, gpa >> PAGE_SHIFT))
159010ddb962SChristoph Hellwig 		intel_vgpu_page_track_handler(info, gpa,
1591f30437c5SJike Song 						     (void *)val, len);
15923cca6b26SSean Christopherson 
15933cca6b26SSean Christopherson 	mutex_unlock(&info->vgpu_lock);
1594f30437c5SJike Song }
1595f30437c5SJike Song 
kvmgt_page_track_remove_region(gfn_t gfn,unsigned long nr_pages,struct kvm_page_track_notifier_node * node)1596c15fcf12SYan Zhao static void kvmgt_page_track_remove_region(gfn_t gfn, unsigned long nr_pages,
1597f30437c5SJike Song 					   struct kvm_page_track_notifier_node *node)
1598f30437c5SJike Song {
159916735297SSean Christopherson 	unsigned long i;
160010ddb962SChristoph Hellwig 	struct intel_vgpu *info =
160110ddb962SChristoph Hellwig 		container_of(node, struct intel_vgpu, track_node);
1602f30437c5SJike Song 
16033cca6b26SSean Christopherson 	mutex_lock(&info->vgpu_lock);
16043cca6b26SSean Christopherson 
1605c15fcf12SYan Zhao 	for (i = 0; i < nr_pages; i++) {
1606c15fcf12SYan Zhao 		if (kvmgt_gfn_is_write_protected(info, gfn + i))
1607c15fcf12SYan Zhao 			kvmgt_protect_table_del(info, gfn + i);
1608f30437c5SJike Song 	}
1609c15fcf12SYan Zhao 
16103cca6b26SSean Christopherson 	mutex_unlock(&info->vgpu_lock);
1611f30437c5SJike Song }
1612f30437c5SJike Song 
intel_vgpu_detach_regions(struct intel_vgpu * vgpu)16134c705ad0SChristoph Hellwig void intel_vgpu_detach_regions(struct intel_vgpu *vgpu)
1614f30437c5SJike Song {
16156c2d0f99SHang Yuan 	int i;
16166c2d0f99SHang Yuan 
161762980cacSChristoph Hellwig 	if (!vgpu->region)
16186c2d0f99SHang Yuan 		return;
16196c2d0f99SHang Yuan 
162062980cacSChristoph Hellwig 	for (i = 0; i < vgpu->num_regions; i++)
162162980cacSChristoph Hellwig 		if (vgpu->region[i].ops->release)
162262980cacSChristoph Hellwig 			vgpu->region[i].ops->release(vgpu,
162362980cacSChristoph Hellwig 					&vgpu->region[i]);
162462980cacSChristoph Hellwig 	vgpu->num_regions = 0;
162562980cacSChristoph Hellwig 	kfree(vgpu->region);
162662980cacSChristoph Hellwig 	vgpu->region = NULL;
1627f30437c5SJike Song }
1628f30437c5SJike Song 
intel_gvt_dma_map_guest_page(struct intel_vgpu * vgpu,unsigned long gfn,unsigned long size,dma_addr_t * dma_addr)16298398eee8SChristoph Hellwig int intel_gvt_dma_map_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
163079e542f5SChangbin Du 		unsigned long size, dma_addr_t *dma_addr)
1631cf4ee73fSChangbin Du {
1632cf4ee73fSChangbin Du 	struct gvt_dma *entry;
1633cf4ee73fSChangbin Du 	int ret;
1634cf4ee73fSChangbin Du 
1635a06d4b9eSZhi Wang 	if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
1636cf4ee73fSChangbin Du 		return -EINVAL;
1637cf4ee73fSChangbin Du 
163862980cacSChristoph Hellwig 	mutex_lock(&vgpu->cache_lock);
1639cf4ee73fSChangbin Du 
164006d63c48SJulian Stecklina 	entry = __gvt_cache_find_gfn(vgpu, gfn);
1641cf4ee73fSChangbin Du 	if (!entry) {
164279e542f5SChangbin Du 		ret = gvt_dma_map_page(vgpu, gfn, dma_addr, size);
16435cd4223eSChangbin Du 		if (ret)
16445cd4223eSChangbin Du 			goto err_unlock;
16455cd4223eSChangbin Du 
164606d63c48SJulian Stecklina 		ret = __gvt_cache_add(vgpu, gfn, *dma_addr, size);
16475cd4223eSChangbin Du 		if (ret)
16485cd4223eSChangbin Du 			goto err_unmap;
16497366aeb7SXiaolin Zhang 	} else if (entry->size != size) {
16507366aeb7SXiaolin Zhang 		/* the same gfn with different size: unmap and re-map */
16517366aeb7SXiaolin Zhang 		gvt_dma_unmap_page(vgpu, gfn, entry->dma_addr, entry->size);
16527366aeb7SXiaolin Zhang 		__gvt_cache_remove_entry(vgpu, entry);
16537366aeb7SXiaolin Zhang 
16547366aeb7SXiaolin Zhang 		ret = gvt_dma_map_page(vgpu, gfn, dma_addr, size);
16557366aeb7SXiaolin Zhang 		if (ret)
16567366aeb7SXiaolin Zhang 			goto err_unlock;
16577366aeb7SXiaolin Zhang 
165806d63c48SJulian Stecklina 		ret = __gvt_cache_add(vgpu, gfn, *dma_addr, size);
16597366aeb7SXiaolin Zhang 		if (ret)
16607366aeb7SXiaolin Zhang 			goto err_unmap;
1661cf4ee73fSChangbin Du 	} else {
1662cf4ee73fSChangbin Du 		kref_get(&entry->ref);
1663cf4ee73fSChangbin Du 		*dma_addr = entry->dma_addr;
16644a0b3444SChuanxiao Dong 	}
1665f30437c5SJike Song 
166662980cacSChristoph Hellwig 	mutex_unlock(&vgpu->cache_lock);
1667cf4ee73fSChangbin Du 	return 0;
16685cd4223eSChangbin Du 
16695cd4223eSChangbin Du err_unmap:
167079e542f5SChangbin Du 	gvt_dma_unmap_page(vgpu, gfn, *dma_addr, size);
16715cd4223eSChangbin Du err_unlock:
167262980cacSChristoph Hellwig 	mutex_unlock(&vgpu->cache_lock);
16735cd4223eSChangbin Du 	return ret;
1674cf4ee73fSChangbin Du }
1675cf4ee73fSChangbin Du 
intel_gvt_dma_pin_guest_page(struct intel_vgpu * vgpu,dma_addr_t dma_addr)167691879bbaSChristoph Hellwig int intel_gvt_dma_pin_guest_page(struct intel_vgpu *vgpu, dma_addr_t dma_addr)
16779f674c81STina Zhang {
16789f674c81STina Zhang 	struct gvt_dma *entry;
16799f674c81STina Zhang 	int ret = 0;
16809f674c81STina Zhang 
1681a06d4b9eSZhi Wang 	if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
1682a06d4b9eSZhi Wang 		return -EINVAL;
16839f674c81STina Zhang 
168410ddb962SChristoph Hellwig 	mutex_lock(&vgpu->cache_lock);
168510ddb962SChristoph Hellwig 	entry = __gvt_cache_find_dma_addr(vgpu, dma_addr);
16869f674c81STina Zhang 	if (entry)
16879f674c81STina Zhang 		kref_get(&entry->ref);
16889f674c81STina Zhang 	else
16899f674c81STina Zhang 		ret = -ENOMEM;
169010ddb962SChristoph Hellwig 	mutex_unlock(&vgpu->cache_lock);
16919f674c81STina Zhang 
16929f674c81STina Zhang 	return ret;
16939f674c81STina Zhang }
16949f674c81STina Zhang 
__gvt_dma_release(struct kref * ref)1695cf4ee73fSChangbin Du static void __gvt_dma_release(struct kref *ref)
1696cf4ee73fSChangbin Du {
1697cf4ee73fSChangbin Du 	struct gvt_dma *entry = container_of(ref, typeof(*entry), ref);
1698cf4ee73fSChangbin Du 
169979e542f5SChangbin Du 	gvt_dma_unmap_page(entry->vgpu, entry->gfn, entry->dma_addr,
170079e542f5SChangbin Du 			   entry->size);
1701cf4ee73fSChangbin Du 	__gvt_cache_remove_entry(entry->vgpu, entry);
1702cf4ee73fSChangbin Du }
1703cf4ee73fSChangbin Du 
intel_gvt_dma_unmap_guest_page(struct intel_vgpu * vgpu,dma_addr_t dma_addr)17048398eee8SChristoph Hellwig void intel_gvt_dma_unmap_guest_page(struct intel_vgpu *vgpu,
17053c340d05SChristoph Hellwig 		dma_addr_t dma_addr)
1706cf4ee73fSChangbin Du {
1707cf4ee73fSChangbin Du 	struct gvt_dma *entry;
1708cf4ee73fSChangbin Du 
1709a06d4b9eSZhi Wang 	if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
1710cf4ee73fSChangbin Du 		return;
1711cf4ee73fSChangbin Du 
171262980cacSChristoph Hellwig 	mutex_lock(&vgpu->cache_lock);
171306d63c48SJulian Stecklina 	entry = __gvt_cache_find_dma_addr(vgpu, dma_addr);
1714cf4ee73fSChangbin Du 	if (entry)
1715cf4ee73fSChangbin Du 		kref_put(&entry->ref, __gvt_dma_release);
171662980cacSChristoph Hellwig 	mutex_unlock(&vgpu->cache_lock);
1717f30437c5SJike Song }
1718f30437c5SJike Song 
init_device_info(struct intel_gvt * gvt)1719cba619cbSChristoph Hellwig static void init_device_info(struct intel_gvt *gvt)
1720cba619cbSChristoph Hellwig {
1721cba619cbSChristoph Hellwig 	struct intel_gvt_device_info *info = &gvt->device_info;
1722cba619cbSChristoph Hellwig 	struct pci_dev *pdev = to_pci_dev(gvt->gt->i915->drm.dev);
1723cba619cbSChristoph Hellwig 
1724cba619cbSChristoph Hellwig 	info->max_support_vgpus = 8;
1725cba619cbSChristoph Hellwig 	info->cfg_space_size = PCI_CFG_SPACE_EXP_SIZE;
1726cba619cbSChristoph Hellwig 	info->mmio_size = 2 * 1024 * 1024;
1727cba619cbSChristoph Hellwig 	info->mmio_bar = 0;
1728cba619cbSChristoph Hellwig 	info->gtt_start_offset = 8 * 1024 * 1024;
1729cba619cbSChristoph Hellwig 	info->gtt_entry_size = 8;
1730cba619cbSChristoph Hellwig 	info->gtt_entry_size_shift = 3;
1731cba619cbSChristoph Hellwig 	info->gmadr_bytes_in_cmd = 8;
1732cba619cbSChristoph Hellwig 	info->max_surface_size = 36 * 1024 * 1024;
1733cba619cbSChristoph Hellwig 	info->msi_cap_offset = pdev->msi_cap;
1734cba619cbSChristoph Hellwig }
1735cba619cbSChristoph Hellwig 
intel_gvt_test_and_emulate_vblank(struct intel_gvt * gvt)1736cba619cbSChristoph Hellwig static void intel_gvt_test_and_emulate_vblank(struct intel_gvt *gvt)
1737cba619cbSChristoph Hellwig {
1738cba619cbSChristoph Hellwig 	struct intel_vgpu *vgpu;
1739cba619cbSChristoph Hellwig 	int id;
1740cba619cbSChristoph Hellwig 
1741cba619cbSChristoph Hellwig 	mutex_lock(&gvt->lock);
1742cba619cbSChristoph Hellwig 	idr_for_each_entry((&(gvt)->vgpu_idr), (vgpu), (id)) {
1743cba619cbSChristoph Hellwig 		if (test_and_clear_bit(INTEL_GVT_REQUEST_EMULATE_VBLANK + id,
1744cba619cbSChristoph Hellwig 				       (void *)&gvt->service_request)) {
1745a06d4b9eSZhi Wang 			if (test_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status))
1746cba619cbSChristoph Hellwig 				intel_vgpu_emulate_vblank(vgpu);
1747cba619cbSChristoph Hellwig 		}
1748cba619cbSChristoph Hellwig 	}
1749cba619cbSChristoph Hellwig 	mutex_unlock(&gvt->lock);
1750cba619cbSChristoph Hellwig }
1751cba619cbSChristoph Hellwig 
gvt_service_thread(void * data)1752cba619cbSChristoph Hellwig static int gvt_service_thread(void *data)
1753cba619cbSChristoph Hellwig {
1754cba619cbSChristoph Hellwig 	struct intel_gvt *gvt = (struct intel_gvt *)data;
1755cba619cbSChristoph Hellwig 	int ret;
1756cba619cbSChristoph Hellwig 
1757cba619cbSChristoph Hellwig 	gvt_dbg_core("service thread start\n");
1758cba619cbSChristoph Hellwig 
1759cba619cbSChristoph Hellwig 	while (!kthread_should_stop()) {
1760cba619cbSChristoph Hellwig 		ret = wait_event_interruptible(gvt->service_thread_wq,
1761cba619cbSChristoph Hellwig 				kthread_should_stop() || gvt->service_request);
1762cba619cbSChristoph Hellwig 
1763cba619cbSChristoph Hellwig 		if (kthread_should_stop())
1764cba619cbSChristoph Hellwig 			break;
1765cba619cbSChristoph Hellwig 
1766cba619cbSChristoph Hellwig 		if (WARN_ONCE(ret, "service thread is waken up by signal.\n"))
1767cba619cbSChristoph Hellwig 			continue;
1768cba619cbSChristoph Hellwig 
1769cba619cbSChristoph Hellwig 		intel_gvt_test_and_emulate_vblank(gvt);
1770cba619cbSChristoph Hellwig 
1771cba619cbSChristoph Hellwig 		if (test_bit(INTEL_GVT_REQUEST_SCHED,
1772cba619cbSChristoph Hellwig 				(void *)&gvt->service_request) ||
1773cba619cbSChristoph Hellwig 			test_bit(INTEL_GVT_REQUEST_EVENT_SCHED,
1774cba619cbSChristoph Hellwig 					(void *)&gvt->service_request)) {
1775cba619cbSChristoph Hellwig 			intel_gvt_schedule(gvt);
1776cba619cbSChristoph Hellwig 		}
1777cba619cbSChristoph Hellwig 	}
1778cba619cbSChristoph Hellwig 
1779cba619cbSChristoph Hellwig 	return 0;
1780cba619cbSChristoph Hellwig }
1781cba619cbSChristoph Hellwig 
clean_service_thread(struct intel_gvt * gvt)1782cba619cbSChristoph Hellwig static void clean_service_thread(struct intel_gvt *gvt)
1783cba619cbSChristoph Hellwig {
1784cba619cbSChristoph Hellwig 	kthread_stop(gvt->service_thread);
1785cba619cbSChristoph Hellwig }
1786cba619cbSChristoph Hellwig 
init_service_thread(struct intel_gvt * gvt)1787cba619cbSChristoph Hellwig static int init_service_thread(struct intel_gvt *gvt)
1788cba619cbSChristoph Hellwig {
1789cba619cbSChristoph Hellwig 	init_waitqueue_head(&gvt->service_thread_wq);
1790cba619cbSChristoph Hellwig 
1791cba619cbSChristoph Hellwig 	gvt->service_thread = kthread_run(gvt_service_thread,
1792cba619cbSChristoph Hellwig 			gvt, "gvt_service_thread");
1793cba619cbSChristoph Hellwig 	if (IS_ERR(gvt->service_thread)) {
1794cba619cbSChristoph Hellwig 		gvt_err("fail to start service thread.\n");
1795cba619cbSChristoph Hellwig 		return PTR_ERR(gvt->service_thread);
1796cba619cbSChristoph Hellwig 	}
1797cba619cbSChristoph Hellwig 	return 0;
1798cba619cbSChristoph Hellwig }
1799cba619cbSChristoph Hellwig 
1800cba619cbSChristoph Hellwig /**
1801cba619cbSChristoph Hellwig  * intel_gvt_clean_device - clean a GVT device
1802cba619cbSChristoph Hellwig  * @i915: i915 private
1803cba619cbSChristoph Hellwig  *
1804cba619cbSChristoph Hellwig  * This function is called at the driver unloading stage, to free the
1805cba619cbSChristoph Hellwig  * resources owned by a GVT device.
1806cba619cbSChristoph Hellwig  *
1807cba619cbSChristoph Hellwig  */
intel_gvt_clean_device(struct drm_i915_private * i915)1808cba619cbSChristoph Hellwig static void intel_gvt_clean_device(struct drm_i915_private *i915)
1809cba619cbSChristoph Hellwig {
1810cba619cbSChristoph Hellwig 	struct intel_gvt *gvt = fetch_and_zero(&i915->gvt);
1811cba619cbSChristoph Hellwig 
1812cba619cbSChristoph Hellwig 	if (drm_WARN_ON(&i915->drm, !gvt))
1813cba619cbSChristoph Hellwig 		return;
1814cba619cbSChristoph Hellwig 
181589345d51SChristoph Hellwig 	mdev_unregister_parent(&gvt->parent);
1816cba619cbSChristoph Hellwig 	intel_gvt_destroy_idle_vgpu(gvt->idle_vgpu);
1817cba619cbSChristoph Hellwig 	intel_gvt_clean_vgpu_types(gvt);
1818cba619cbSChristoph Hellwig 
1819cba619cbSChristoph Hellwig 	intel_gvt_debugfs_clean(gvt);
1820cba619cbSChristoph Hellwig 	clean_service_thread(gvt);
1821cba619cbSChristoph Hellwig 	intel_gvt_clean_cmd_parser(gvt);
1822cba619cbSChristoph Hellwig 	intel_gvt_clean_sched_policy(gvt);
1823cba619cbSChristoph Hellwig 	intel_gvt_clean_workload_scheduler(gvt);
1824cba619cbSChristoph Hellwig 	intel_gvt_clean_gtt(gvt);
1825cba619cbSChristoph Hellwig 	intel_gvt_free_firmware(gvt);
1826cba619cbSChristoph Hellwig 	intel_gvt_clean_mmio_info(gvt);
1827cba619cbSChristoph Hellwig 	idr_destroy(&gvt->vgpu_idr);
1828cba619cbSChristoph Hellwig 
1829cba619cbSChristoph Hellwig 	kfree(i915->gvt);
1830cba619cbSChristoph Hellwig }
1831cba619cbSChristoph Hellwig 
1832cba619cbSChristoph Hellwig /**
1833cba619cbSChristoph Hellwig  * intel_gvt_init_device - initialize a GVT device
1834cba619cbSChristoph Hellwig  * @i915: drm i915 private data
1835cba619cbSChristoph Hellwig  *
1836cba619cbSChristoph Hellwig  * This function is called at the initialization stage, to initialize
1837cba619cbSChristoph Hellwig  * necessary GVT components.
1838cba619cbSChristoph Hellwig  *
1839cba619cbSChristoph Hellwig  * Returns:
1840cba619cbSChristoph Hellwig  * Zero on success, negative error code if failed.
1841cba619cbSChristoph Hellwig  *
1842cba619cbSChristoph Hellwig  */
intel_gvt_init_device(struct drm_i915_private * i915)1843cba619cbSChristoph Hellwig static int intel_gvt_init_device(struct drm_i915_private *i915)
1844cba619cbSChristoph Hellwig {
1845cba619cbSChristoph Hellwig 	struct intel_gvt *gvt;
1846cba619cbSChristoph Hellwig 	struct intel_vgpu *vgpu;
1847cba619cbSChristoph Hellwig 	int ret;
1848cba619cbSChristoph Hellwig 
1849cba619cbSChristoph Hellwig 	if (drm_WARN_ON(&i915->drm, i915->gvt))
1850cba619cbSChristoph Hellwig 		return -EEXIST;
1851cba619cbSChristoph Hellwig 
1852cba619cbSChristoph Hellwig 	gvt = kzalloc(sizeof(struct intel_gvt), GFP_KERNEL);
1853cba619cbSChristoph Hellwig 	if (!gvt)
1854cba619cbSChristoph Hellwig 		return -ENOMEM;
1855cba619cbSChristoph Hellwig 
1856cba619cbSChristoph Hellwig 	gvt_dbg_core("init gvt device\n");
1857cba619cbSChristoph Hellwig 
1858cba619cbSChristoph Hellwig 	idr_init_base(&gvt->vgpu_idr, 1);
1859cba619cbSChristoph Hellwig 	spin_lock_init(&gvt->scheduler.mmio_context_lock);
1860cba619cbSChristoph Hellwig 	mutex_init(&gvt->lock);
1861cba619cbSChristoph Hellwig 	mutex_init(&gvt->sched_lock);
1862cba619cbSChristoph Hellwig 	gvt->gt = to_gt(i915);
1863cba619cbSChristoph Hellwig 	i915->gvt = gvt;
1864cba619cbSChristoph Hellwig 
1865cba619cbSChristoph Hellwig 	init_device_info(gvt);
1866cba619cbSChristoph Hellwig 
1867cba619cbSChristoph Hellwig 	ret = intel_gvt_setup_mmio_info(gvt);
1868cba619cbSChristoph Hellwig 	if (ret)
1869cba619cbSChristoph Hellwig 		goto out_clean_idr;
1870cba619cbSChristoph Hellwig 
1871cba619cbSChristoph Hellwig 	intel_gvt_init_engine_mmio_context(gvt);
1872cba619cbSChristoph Hellwig 
1873cba619cbSChristoph Hellwig 	ret = intel_gvt_load_firmware(gvt);
1874cba619cbSChristoph Hellwig 	if (ret)
1875cba619cbSChristoph Hellwig 		goto out_clean_mmio_info;
1876cba619cbSChristoph Hellwig 
1877cba619cbSChristoph Hellwig 	ret = intel_gvt_init_irq(gvt);
1878cba619cbSChristoph Hellwig 	if (ret)
1879cba619cbSChristoph Hellwig 		goto out_free_firmware;
1880cba619cbSChristoph Hellwig 
1881cba619cbSChristoph Hellwig 	ret = intel_gvt_init_gtt(gvt);
1882cba619cbSChristoph Hellwig 	if (ret)
1883cba619cbSChristoph Hellwig 		goto out_free_firmware;
1884cba619cbSChristoph Hellwig 
1885cba619cbSChristoph Hellwig 	ret = intel_gvt_init_workload_scheduler(gvt);
1886cba619cbSChristoph Hellwig 	if (ret)
1887cba619cbSChristoph Hellwig 		goto out_clean_gtt;
1888cba619cbSChristoph Hellwig 
1889cba619cbSChristoph Hellwig 	ret = intel_gvt_init_sched_policy(gvt);
1890cba619cbSChristoph Hellwig 	if (ret)
1891cba619cbSChristoph Hellwig 		goto out_clean_workload_scheduler;
1892cba619cbSChristoph Hellwig 
1893cba619cbSChristoph Hellwig 	ret = intel_gvt_init_cmd_parser(gvt);
1894cba619cbSChristoph Hellwig 	if (ret)
1895cba619cbSChristoph Hellwig 		goto out_clean_sched_policy;
1896cba619cbSChristoph Hellwig 
1897cba619cbSChristoph Hellwig 	ret = init_service_thread(gvt);
1898cba619cbSChristoph Hellwig 	if (ret)
1899cba619cbSChristoph Hellwig 		goto out_clean_cmd_parser;
1900cba619cbSChristoph Hellwig 
1901cba619cbSChristoph Hellwig 	ret = intel_gvt_init_vgpu_types(gvt);
1902cba619cbSChristoph Hellwig 	if (ret)
1903cba619cbSChristoph Hellwig 		goto out_clean_thread;
1904cba619cbSChristoph Hellwig 
1905cba619cbSChristoph Hellwig 	vgpu = intel_gvt_create_idle_vgpu(gvt);
1906cba619cbSChristoph Hellwig 	if (IS_ERR(vgpu)) {
1907cba619cbSChristoph Hellwig 		ret = PTR_ERR(vgpu);
1908cba619cbSChristoph Hellwig 		gvt_err("failed to create idle vgpu\n");
1909cba619cbSChristoph Hellwig 		goto out_clean_types;
1910cba619cbSChristoph Hellwig 	}
1911cba619cbSChristoph Hellwig 	gvt->idle_vgpu = vgpu;
1912cba619cbSChristoph Hellwig 
1913cba619cbSChristoph Hellwig 	intel_gvt_debugfs_init(gvt);
1914cba619cbSChristoph Hellwig 
1915da44c340SChristoph Hellwig 	ret = mdev_register_parent(&gvt->parent, i915->drm.dev,
1916da44c340SChristoph Hellwig 				   &intel_vgpu_mdev_driver,
1917da44c340SChristoph Hellwig 				   gvt->mdev_types, gvt->num_types);
1918cba619cbSChristoph Hellwig 	if (ret)
1919cba619cbSChristoph Hellwig 		goto out_destroy_idle_vgpu;
1920cba619cbSChristoph Hellwig 
1921cba619cbSChristoph Hellwig 	gvt_dbg_core("gvt device initialization is done\n");
1922cba619cbSChristoph Hellwig 	return 0;
1923cba619cbSChristoph Hellwig 
1924cba619cbSChristoph Hellwig out_destroy_idle_vgpu:
1925cba619cbSChristoph Hellwig 	intel_gvt_destroy_idle_vgpu(gvt->idle_vgpu);
1926cba619cbSChristoph Hellwig 	intel_gvt_debugfs_clean(gvt);
1927cba619cbSChristoph Hellwig out_clean_types:
1928cba619cbSChristoph Hellwig 	intel_gvt_clean_vgpu_types(gvt);
1929cba619cbSChristoph Hellwig out_clean_thread:
1930cba619cbSChristoph Hellwig 	clean_service_thread(gvt);
1931cba619cbSChristoph Hellwig out_clean_cmd_parser:
1932cba619cbSChristoph Hellwig 	intel_gvt_clean_cmd_parser(gvt);
1933cba619cbSChristoph Hellwig out_clean_sched_policy:
1934cba619cbSChristoph Hellwig 	intel_gvt_clean_sched_policy(gvt);
1935cba619cbSChristoph Hellwig out_clean_workload_scheduler:
1936cba619cbSChristoph Hellwig 	intel_gvt_clean_workload_scheduler(gvt);
1937cba619cbSChristoph Hellwig out_clean_gtt:
1938cba619cbSChristoph Hellwig 	intel_gvt_clean_gtt(gvt);
1939cba619cbSChristoph Hellwig out_free_firmware:
1940cba619cbSChristoph Hellwig 	intel_gvt_free_firmware(gvt);
1941cba619cbSChristoph Hellwig out_clean_mmio_info:
1942cba619cbSChristoph Hellwig 	intel_gvt_clean_mmio_info(gvt);
1943cba619cbSChristoph Hellwig out_clean_idr:
1944cba619cbSChristoph Hellwig 	idr_destroy(&gvt->vgpu_idr);
1945cba619cbSChristoph Hellwig 	kfree(gvt);
1946cba619cbSChristoph Hellwig 	i915->gvt = NULL;
1947cba619cbSChristoph Hellwig 	return ret;
1948cba619cbSChristoph Hellwig }
1949cba619cbSChristoph Hellwig 
intel_gvt_pm_resume(struct drm_i915_private * i915)1950cba619cbSChristoph Hellwig static void intel_gvt_pm_resume(struct drm_i915_private *i915)
1951cba619cbSChristoph Hellwig {
1952cba619cbSChristoph Hellwig 	struct intel_gvt *gvt = i915->gvt;
1953cba619cbSChristoph Hellwig 
1954cba619cbSChristoph Hellwig 	intel_gvt_restore_fence(gvt);
1955cba619cbSChristoph Hellwig 	intel_gvt_restore_mmio(gvt);
1956cba619cbSChristoph Hellwig 	intel_gvt_restore_ggtt(gvt);
1957cba619cbSChristoph Hellwig }
1958cba619cbSChristoph Hellwig 
1959cba619cbSChristoph Hellwig static const struct intel_vgpu_ops intel_gvt_vgpu_ops = {
1960cba619cbSChristoph Hellwig 	.init_device	= intel_gvt_init_device,
1961cba619cbSChristoph Hellwig 	.clean_device	= intel_gvt_clean_device,
1962cba619cbSChristoph Hellwig 	.pm_resume	= intel_gvt_pm_resume,
1963cba619cbSChristoph Hellwig };
1964cba619cbSChristoph Hellwig 
kvmgt_init(void)1965f30437c5SJike Song static int __init kvmgt_init(void)
1966f30437c5SJike Song {
1967978cf586SChristoph Hellwig 	int ret;
1968978cf586SChristoph Hellwig 
1969978cf586SChristoph Hellwig 	ret = intel_gvt_set_ops(&intel_gvt_vgpu_ops);
1970978cf586SChristoph Hellwig 	if (ret)
1971978cf586SChristoph Hellwig 		return ret;
1972978cf586SChristoph Hellwig 
1973978cf586SChristoph Hellwig 	ret = mdev_register_driver(&intel_vgpu_mdev_driver);
1974978cf586SChristoph Hellwig 	if (ret)
1975978cf586SChristoph Hellwig 		intel_gvt_clear_ops(&intel_gvt_vgpu_ops);
1976978cf586SChristoph Hellwig 	return ret;
1977f30437c5SJike Song }
1978f30437c5SJike Song 
kvmgt_exit(void)1979f30437c5SJike Song static void __exit kvmgt_exit(void)
1980f30437c5SJike Song {
1981978cf586SChristoph Hellwig 	mdev_unregister_driver(&intel_vgpu_mdev_driver);
19828b750bf7SChristoph Hellwig 	intel_gvt_clear_ops(&intel_gvt_vgpu_ops);
1983f30437c5SJike Song }
1984f30437c5SJike Song 
1985f30437c5SJike Song module_init(kvmgt_init);
1986f30437c5SJike Song module_exit(kvmgt_exit);
1987f30437c5SJike Song 
1988f30437c5SJike Song MODULE_LICENSE("GPL and additional rights");
1989f30437c5SJike Song MODULE_AUTHOR("Intel Corporation");
1990