18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VFIO PCI I/O Port & MMIO access 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci * Author: Alex Williamson <alex.williamson@redhat.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Derived from original vfio: 98c2ecf20Sopenharmony_ci * Copyright 2010 Cisco Systems, Inc. All rights reserved. 108c2ecf20Sopenharmony_ci * Author: Tom Lyon, pugs@cisco.com 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/fs.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/vfio.h> 188c2ecf20Sopenharmony_ci#include <linux/vgaarb.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "vfio_pci_private.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN 238c2ecf20Sopenharmony_ci#define vfio_ioread64 ioread64 248c2ecf20Sopenharmony_ci#define vfio_iowrite64 iowrite64 258c2ecf20Sopenharmony_ci#define vfio_ioread32 ioread32 268c2ecf20Sopenharmony_ci#define vfio_iowrite32 iowrite32 278c2ecf20Sopenharmony_ci#define vfio_ioread16 ioread16 288c2ecf20Sopenharmony_ci#define vfio_iowrite16 iowrite16 298c2ecf20Sopenharmony_ci#else 308c2ecf20Sopenharmony_ci#define vfio_ioread64 ioread64be 318c2ecf20Sopenharmony_ci#define vfio_iowrite64 iowrite64be 328c2ecf20Sopenharmony_ci#define vfio_ioread32 ioread32be 338c2ecf20Sopenharmony_ci#define vfio_iowrite32 iowrite32be 348c2ecf20Sopenharmony_ci#define vfio_ioread16 ioread16be 358c2ecf20Sopenharmony_ci#define vfio_iowrite16 iowrite16be 368c2ecf20Sopenharmony_ci#endif 378c2ecf20Sopenharmony_ci#define vfio_ioread8 ioread8 388c2ecf20Sopenharmony_ci#define vfio_iowrite8 iowrite8 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define VFIO_IOWRITE(size) \ 418c2ecf20Sopenharmony_cistatic int vfio_pci_iowrite##size(struct vfio_pci_device *vdev, \ 428c2ecf20Sopenharmony_ci bool test_mem, u##size val, void __iomem *io) \ 438c2ecf20Sopenharmony_ci{ \ 448c2ecf20Sopenharmony_ci if (test_mem) { \ 458c2ecf20Sopenharmony_ci down_read(&vdev->memory_lock); \ 468c2ecf20Sopenharmony_ci if (!__vfio_pci_memory_enabled(vdev)) { \ 478c2ecf20Sopenharmony_ci up_read(&vdev->memory_lock); \ 488c2ecf20Sopenharmony_ci return -EIO; \ 498c2ecf20Sopenharmony_ci } \ 508c2ecf20Sopenharmony_ci } \ 518c2ecf20Sopenharmony_ci \ 528c2ecf20Sopenharmony_ci vfio_iowrite##size(val, io); \ 538c2ecf20Sopenharmony_ci \ 548c2ecf20Sopenharmony_ci if (test_mem) \ 558c2ecf20Sopenharmony_ci up_read(&vdev->memory_lock); \ 568c2ecf20Sopenharmony_ci \ 578c2ecf20Sopenharmony_ci return 0; \ 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ciVFIO_IOWRITE(8) 618c2ecf20Sopenharmony_ciVFIO_IOWRITE(16) 628c2ecf20Sopenharmony_ciVFIO_IOWRITE(32) 638c2ecf20Sopenharmony_ci#ifdef iowrite64 648c2ecf20Sopenharmony_ciVFIO_IOWRITE(64) 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define VFIO_IOREAD(size) \ 688c2ecf20Sopenharmony_cistatic int vfio_pci_ioread##size(struct vfio_pci_device *vdev, \ 698c2ecf20Sopenharmony_ci bool test_mem, u##size *val, void __iomem *io) \ 708c2ecf20Sopenharmony_ci{ \ 718c2ecf20Sopenharmony_ci if (test_mem) { \ 728c2ecf20Sopenharmony_ci down_read(&vdev->memory_lock); \ 738c2ecf20Sopenharmony_ci if (!__vfio_pci_memory_enabled(vdev)) { \ 748c2ecf20Sopenharmony_ci up_read(&vdev->memory_lock); \ 758c2ecf20Sopenharmony_ci return -EIO; \ 768c2ecf20Sopenharmony_ci } \ 778c2ecf20Sopenharmony_ci } \ 788c2ecf20Sopenharmony_ci \ 798c2ecf20Sopenharmony_ci *val = vfio_ioread##size(io); \ 808c2ecf20Sopenharmony_ci \ 818c2ecf20Sopenharmony_ci if (test_mem) \ 828c2ecf20Sopenharmony_ci up_read(&vdev->memory_lock); \ 838c2ecf20Sopenharmony_ci \ 848c2ecf20Sopenharmony_ci return 0; \ 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciVFIO_IOREAD(8) 888c2ecf20Sopenharmony_ciVFIO_IOREAD(16) 898c2ecf20Sopenharmony_ciVFIO_IOREAD(32) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* 928c2ecf20Sopenharmony_ci * Read or write from an __iomem region (MMIO or I/O port) with an excluded 938c2ecf20Sopenharmony_ci * range which is inaccessible. The excluded range drops writes and fills 948c2ecf20Sopenharmony_ci * reads with -1. This is intended for handling MSI-X vector tables and 958c2ecf20Sopenharmony_ci * leftover space for ROM BARs. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic ssize_t do_io_rw(struct vfio_pci_device *vdev, bool test_mem, 988c2ecf20Sopenharmony_ci void __iomem *io, char __user *buf, 998c2ecf20Sopenharmony_ci loff_t off, size_t count, size_t x_start, 1008c2ecf20Sopenharmony_ci size_t x_end, bool iswrite) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci ssize_t done = 0; 1038c2ecf20Sopenharmony_ci int ret; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci while (count) { 1068c2ecf20Sopenharmony_ci size_t fillable, filled; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (off < x_start) 1098c2ecf20Sopenharmony_ci fillable = min(count, (size_t)(x_start - off)); 1108c2ecf20Sopenharmony_ci else if (off >= x_end) 1118c2ecf20Sopenharmony_ci fillable = count; 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci fillable = 0; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (fillable >= 4 && !(off % 4)) { 1168c2ecf20Sopenharmony_ci u32 val; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (iswrite) { 1198c2ecf20Sopenharmony_ci if (copy_from_user(&val, buf, 4)) 1208c2ecf20Sopenharmony_ci return -EFAULT; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ret = vfio_pci_iowrite32(vdev, test_mem, 1238c2ecf20Sopenharmony_ci val, io + off); 1248c2ecf20Sopenharmony_ci if (ret) 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci ret = vfio_pci_ioread32(vdev, test_mem, 1288c2ecf20Sopenharmony_ci &val, io + off); 1298c2ecf20Sopenharmony_ci if (ret) 1308c2ecf20Sopenharmony_ci return ret; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (copy_to_user(buf, &val, 4)) 1338c2ecf20Sopenharmony_ci return -EFAULT; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci filled = 4; 1378c2ecf20Sopenharmony_ci } else if (fillable >= 2 && !(off % 2)) { 1388c2ecf20Sopenharmony_ci u16 val; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (iswrite) { 1418c2ecf20Sopenharmony_ci if (copy_from_user(&val, buf, 2)) 1428c2ecf20Sopenharmony_ci return -EFAULT; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ret = vfio_pci_iowrite16(vdev, test_mem, 1458c2ecf20Sopenharmony_ci val, io + off); 1468c2ecf20Sopenharmony_ci if (ret) 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci } else { 1498c2ecf20Sopenharmony_ci ret = vfio_pci_ioread16(vdev, test_mem, 1508c2ecf20Sopenharmony_ci &val, io + off); 1518c2ecf20Sopenharmony_ci if (ret) 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (copy_to_user(buf, &val, 2)) 1558c2ecf20Sopenharmony_ci return -EFAULT; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci filled = 2; 1598c2ecf20Sopenharmony_ci } else if (fillable) { 1608c2ecf20Sopenharmony_ci u8 val; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (iswrite) { 1638c2ecf20Sopenharmony_ci if (copy_from_user(&val, buf, 1)) 1648c2ecf20Sopenharmony_ci return -EFAULT; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci ret = vfio_pci_iowrite8(vdev, test_mem, 1678c2ecf20Sopenharmony_ci val, io + off); 1688c2ecf20Sopenharmony_ci if (ret) 1698c2ecf20Sopenharmony_ci return ret; 1708c2ecf20Sopenharmony_ci } else { 1718c2ecf20Sopenharmony_ci ret = vfio_pci_ioread8(vdev, test_mem, 1728c2ecf20Sopenharmony_ci &val, io + off); 1738c2ecf20Sopenharmony_ci if (ret) 1748c2ecf20Sopenharmony_ci return ret; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (copy_to_user(buf, &val, 1)) 1778c2ecf20Sopenharmony_ci return -EFAULT; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci filled = 1; 1818c2ecf20Sopenharmony_ci } else { 1828c2ecf20Sopenharmony_ci /* Fill reads with -1, drop writes */ 1838c2ecf20Sopenharmony_ci filled = min(count, (size_t)(x_end - off)); 1848c2ecf20Sopenharmony_ci if (!iswrite) { 1858c2ecf20Sopenharmony_ci u8 val = 0xFF; 1868c2ecf20Sopenharmony_ci size_t i; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci for (i = 0; i < filled; i++) 1898c2ecf20Sopenharmony_ci if (copy_to_user(buf + i, &val, 1)) 1908c2ecf20Sopenharmony_ci return -EFAULT; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci count -= filled; 1958c2ecf20Sopenharmony_ci done += filled; 1968c2ecf20Sopenharmony_ci off += filled; 1978c2ecf20Sopenharmony_ci buf += filled; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return done; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 2068c2ecf20Sopenharmony_ci int ret; 2078c2ecf20Sopenharmony_ci void __iomem *io; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (vdev->barmap[bar]) 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci io = pci_iomap(pdev, bar, 0); 2178c2ecf20Sopenharmony_ci if (!io) { 2188c2ecf20Sopenharmony_ci pci_release_selected_regions(pdev, 1 << bar); 2198c2ecf20Sopenharmony_ci return -ENOMEM; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci vdev->barmap[bar] = io; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cissize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, 2288c2ecf20Sopenharmony_ci size_t count, loff_t *ppos, bool iswrite) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 2318c2ecf20Sopenharmony_ci loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 2328c2ecf20Sopenharmony_ci int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 2338c2ecf20Sopenharmony_ci size_t x_start = 0, x_end = 0; 2348c2ecf20Sopenharmony_ci resource_size_t end; 2358c2ecf20Sopenharmony_ci void __iomem *io; 2368c2ecf20Sopenharmony_ci struct resource *res = &vdev->pdev->resource[bar]; 2378c2ecf20Sopenharmony_ci ssize_t done; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (pci_resource_start(pdev, bar)) 2408c2ecf20Sopenharmony_ci end = pci_resource_len(pdev, bar); 2418c2ecf20Sopenharmony_ci else if (bar == PCI_ROM_RESOURCE && 2428c2ecf20Sopenharmony_ci pdev->resource[bar].flags & IORESOURCE_ROM_SHADOW) 2438c2ecf20Sopenharmony_ci end = 0x20000; 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci return -EINVAL; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (pos >= end) 2488c2ecf20Sopenharmony_ci return -EINVAL; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci count = min(count, (size_t)(end - pos)); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (bar == PCI_ROM_RESOURCE) { 2538c2ecf20Sopenharmony_ci /* 2548c2ecf20Sopenharmony_ci * The ROM can fill less space than the BAR, so we start the 2558c2ecf20Sopenharmony_ci * excluded range at the end of the actual ROM. This makes 2568c2ecf20Sopenharmony_ci * filling large ROM BARs much faster. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci io = pci_map_rom(pdev, &x_start); 2598c2ecf20Sopenharmony_ci if (!io) { 2608c2ecf20Sopenharmony_ci done = -ENOMEM; 2618c2ecf20Sopenharmony_ci goto out; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci x_end = end; 2648c2ecf20Sopenharmony_ci } else { 2658c2ecf20Sopenharmony_ci int ret = vfio_pci_setup_barmap(vdev, bar); 2668c2ecf20Sopenharmony_ci if (ret) { 2678c2ecf20Sopenharmony_ci done = ret; 2688c2ecf20Sopenharmony_ci goto out; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci io = vdev->barmap[bar]; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (bar == vdev->msix_bar) { 2758c2ecf20Sopenharmony_ci x_start = vdev->msix_offset; 2768c2ecf20Sopenharmony_ci x_end = vdev->msix_offset + vdev->msix_size; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci done = do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos, 2808c2ecf20Sopenharmony_ci count, x_start, x_end, iswrite); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (done >= 0) 2838c2ecf20Sopenharmony_ci *ppos += done; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (bar == PCI_ROM_RESOURCE) 2868c2ecf20Sopenharmony_ci pci_unmap_rom(pdev, io); 2878c2ecf20Sopenharmony_ciout: 2888c2ecf20Sopenharmony_ci return done; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cissize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, 2928c2ecf20Sopenharmony_ci size_t count, loff_t *ppos, bool iswrite) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci int ret; 2958c2ecf20Sopenharmony_ci loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK; 2968c2ecf20Sopenharmony_ci void __iomem *iomem = NULL; 2978c2ecf20Sopenharmony_ci unsigned int rsrc; 2988c2ecf20Sopenharmony_ci bool is_ioport; 2998c2ecf20Sopenharmony_ci ssize_t done; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (!vdev->has_vga) 3028c2ecf20Sopenharmony_ci return -EINVAL; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (pos > 0xbfffful) 3058c2ecf20Sopenharmony_ci return -EINVAL; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci switch ((u32)pos) { 3088c2ecf20Sopenharmony_ci case 0xa0000 ... 0xbffff: 3098c2ecf20Sopenharmony_ci count = min(count, (size_t)(0xc0000 - pos)); 3108c2ecf20Sopenharmony_ci iomem = ioremap(0xa0000, 0xbffff - 0xa0000 + 1); 3118c2ecf20Sopenharmony_ci off = pos - 0xa0000; 3128c2ecf20Sopenharmony_ci rsrc = VGA_RSRC_LEGACY_MEM; 3138c2ecf20Sopenharmony_ci is_ioport = false; 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci case 0x3b0 ... 0x3bb: 3168c2ecf20Sopenharmony_ci count = min(count, (size_t)(0x3bc - pos)); 3178c2ecf20Sopenharmony_ci iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1); 3188c2ecf20Sopenharmony_ci off = pos - 0x3b0; 3198c2ecf20Sopenharmony_ci rsrc = VGA_RSRC_LEGACY_IO; 3208c2ecf20Sopenharmony_ci is_ioport = true; 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci case 0x3c0 ... 0x3df: 3238c2ecf20Sopenharmony_ci count = min(count, (size_t)(0x3e0 - pos)); 3248c2ecf20Sopenharmony_ci iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1); 3258c2ecf20Sopenharmony_ci off = pos - 0x3c0; 3268c2ecf20Sopenharmony_ci rsrc = VGA_RSRC_LEGACY_IO; 3278c2ecf20Sopenharmony_ci is_ioport = true; 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci default: 3308c2ecf20Sopenharmony_ci return -EINVAL; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (!iomem) 3348c2ecf20Sopenharmony_ci return -ENOMEM; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ret = vga_get_interruptible(vdev->pdev, rsrc); 3378c2ecf20Sopenharmony_ci if (ret) { 3388c2ecf20Sopenharmony_ci is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * VGA MMIO is a legacy, non-BAR resource that hopefully allows 3448c2ecf20Sopenharmony_ci * probing, so we don't currently worry about access in relation 3458c2ecf20Sopenharmony_ci * to the memory enable bit in the command register. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci done = do_io_rw(vdev, false, iomem, buf, off, count, 0, 0, iswrite); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci vga_put(vdev->pdev, rsrc); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (done >= 0) 3548c2ecf20Sopenharmony_ci *ppos += done; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return done; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd, 3608c2ecf20Sopenharmony_ci bool test_mem) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci switch (ioeventfd->count) { 3638c2ecf20Sopenharmony_ci case 1: 3648c2ecf20Sopenharmony_ci vfio_pci_iowrite8(ioeventfd->vdev, test_mem, 3658c2ecf20Sopenharmony_ci ioeventfd->data, ioeventfd->addr); 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci case 2: 3688c2ecf20Sopenharmony_ci vfio_pci_iowrite16(ioeventfd->vdev, test_mem, 3698c2ecf20Sopenharmony_ci ioeventfd->data, ioeventfd->addr); 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case 4: 3728c2ecf20Sopenharmony_ci vfio_pci_iowrite32(ioeventfd->vdev, test_mem, 3738c2ecf20Sopenharmony_ci ioeventfd->data, ioeventfd->addr); 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci#ifdef iowrite64 3768c2ecf20Sopenharmony_ci case 8: 3778c2ecf20Sopenharmony_ci vfio_pci_iowrite64(ioeventfd->vdev, test_mem, 3788c2ecf20Sopenharmony_ci ioeventfd->data, ioeventfd->addr); 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci#endif 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int vfio_pci_ioeventfd_handler(void *opaque, void *unused) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct vfio_pci_ioeventfd *ioeventfd = opaque; 3878c2ecf20Sopenharmony_ci struct vfio_pci_device *vdev = ioeventfd->vdev; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (ioeventfd->test_mem) { 3908c2ecf20Sopenharmony_ci if (!down_read_trylock(&vdev->memory_lock)) 3918c2ecf20Sopenharmony_ci return 1; /* Lock contended, use thread */ 3928c2ecf20Sopenharmony_ci if (!__vfio_pci_memory_enabled(vdev)) { 3938c2ecf20Sopenharmony_ci up_read(&vdev->memory_lock); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci vfio_pci_ioeventfd_do_write(ioeventfd, false); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (ioeventfd->test_mem) 4018c2ecf20Sopenharmony_ci up_read(&vdev->memory_lock); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic void vfio_pci_ioeventfd_thread(void *opaque, void *unused) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct vfio_pci_ioeventfd *ioeventfd = opaque; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cilong vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset, 4148c2ecf20Sopenharmony_ci uint64_t data, int count, int fd) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 4178c2ecf20Sopenharmony_ci loff_t pos = offset & VFIO_PCI_OFFSET_MASK; 4188c2ecf20Sopenharmony_ci int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset); 4198c2ecf20Sopenharmony_ci struct vfio_pci_ioeventfd *ioeventfd; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* Only support ioeventfds into BARs */ 4228c2ecf20Sopenharmony_ci if (bar > VFIO_PCI_BAR5_REGION_INDEX) 4238c2ecf20Sopenharmony_ci return -EINVAL; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (pos + count > pci_resource_len(pdev, bar)) 4268c2ecf20Sopenharmony_ci return -EINVAL; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Disallow ioeventfds working around MSI-X table writes */ 4298c2ecf20Sopenharmony_ci if (bar == vdev->msix_bar && 4308c2ecf20Sopenharmony_ci !(pos + count <= vdev->msix_offset || 4318c2ecf20Sopenharmony_ci pos >= vdev->msix_offset + vdev->msix_size)) 4328c2ecf20Sopenharmony_ci return -EINVAL; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci#ifndef iowrite64 4358c2ecf20Sopenharmony_ci if (count == 8) 4368c2ecf20Sopenharmony_ci return -EINVAL; 4378c2ecf20Sopenharmony_ci#endif 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ret = vfio_pci_setup_barmap(vdev, bar); 4408c2ecf20Sopenharmony_ci if (ret) 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci mutex_lock(&vdev->ioeventfds_lock); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) { 4468c2ecf20Sopenharmony_ci if (ioeventfd->pos == pos && ioeventfd->bar == bar && 4478c2ecf20Sopenharmony_ci ioeventfd->data == data && ioeventfd->count == count) { 4488c2ecf20Sopenharmony_ci if (fd == -1) { 4498c2ecf20Sopenharmony_ci vfio_virqfd_disable(&ioeventfd->virqfd); 4508c2ecf20Sopenharmony_ci list_del(&ioeventfd->next); 4518c2ecf20Sopenharmony_ci vdev->ioeventfds_nr--; 4528c2ecf20Sopenharmony_ci kfree(ioeventfd); 4538c2ecf20Sopenharmony_ci ret = 0; 4548c2ecf20Sopenharmony_ci } else 4558c2ecf20Sopenharmony_ci ret = -EEXIST; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci goto out_unlock; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (fd < 0) { 4628c2ecf20Sopenharmony_ci ret = -ENODEV; 4638c2ecf20Sopenharmony_ci goto out_unlock; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) { 4678c2ecf20Sopenharmony_ci ret = -ENOSPC; 4688c2ecf20Sopenharmony_ci goto out_unlock; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL); 4728c2ecf20Sopenharmony_ci if (!ioeventfd) { 4738c2ecf20Sopenharmony_ci ret = -ENOMEM; 4748c2ecf20Sopenharmony_ci goto out_unlock; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ioeventfd->vdev = vdev; 4788c2ecf20Sopenharmony_ci ioeventfd->addr = vdev->barmap[bar] + pos; 4798c2ecf20Sopenharmony_ci ioeventfd->data = data; 4808c2ecf20Sopenharmony_ci ioeventfd->pos = pos; 4818c2ecf20Sopenharmony_ci ioeventfd->bar = bar; 4828c2ecf20Sopenharmony_ci ioeventfd->count = count; 4838c2ecf20Sopenharmony_ci ioeventfd->test_mem = vdev->pdev->resource[bar].flags & IORESOURCE_MEM; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler, 4868c2ecf20Sopenharmony_ci vfio_pci_ioeventfd_thread, NULL, 4878c2ecf20Sopenharmony_ci &ioeventfd->virqfd, fd); 4888c2ecf20Sopenharmony_ci if (ret) { 4898c2ecf20Sopenharmony_ci kfree(ioeventfd); 4908c2ecf20Sopenharmony_ci goto out_unlock; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci list_add(&ioeventfd->next, &vdev->ioeventfds_list); 4948c2ecf20Sopenharmony_ci vdev->ioeventfds_nr++; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ciout_unlock: 4978c2ecf20Sopenharmony_ci mutex_unlock(&vdev->ioeventfds_lock); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return ret; 5008c2ecf20Sopenharmony_ci} 501