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 = ®ion->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