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