1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 289e1f7d4SAlex Williamson /* 389e1f7d4SAlex Williamson * VFIO PCI I/O Port & MMIO access 489e1f7d4SAlex Williamson * 589e1f7d4SAlex Williamson * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 689e1f7d4SAlex Williamson * Author: Alex Williamson <alex.williamson@redhat.com> 789e1f7d4SAlex Williamson * 889e1f7d4SAlex Williamson * Derived from original vfio: 989e1f7d4SAlex Williamson * Copyright 2010 Cisco Systems, Inc. All rights reserved. 1089e1f7d4SAlex Williamson * Author: Tom Lyon, pugs@cisco.com 1189e1f7d4SAlex Williamson */ 1289e1f7d4SAlex Williamson 1389e1f7d4SAlex Williamson #include <linux/fs.h> 1489e1f7d4SAlex Williamson #include <linux/pci.h> 1589e1f7d4SAlex Williamson #include <linux/uaccess.h> 1689e1f7d4SAlex Williamson #include <linux/io.h> 1730656177SAlex Williamson #include <linux/vfio.h> 1884237a82SAlex Williamson #include <linux/vgaarb.h> 1989e1f7d4SAlex Williamson 20e34a0425SJason Gunthorpe #include "vfio_pci_priv.h" 2189e1f7d4SAlex Williamson 2207fd7ef3SAlex Williamson #ifdef __LITTLE_ENDIAN 2307fd7ef3SAlex Williamson #define vfio_ioread64 ioread64 2407fd7ef3SAlex Williamson #define vfio_iowrite64 iowrite64 2507fd7ef3SAlex Williamson #define vfio_ioread32 ioread32 2607fd7ef3SAlex Williamson #define vfio_iowrite32 iowrite32 2707fd7ef3SAlex Williamson #define vfio_ioread16 ioread16 2807fd7ef3SAlex Williamson #define vfio_iowrite16 iowrite16 2907fd7ef3SAlex Williamson #else 3007fd7ef3SAlex Williamson #define vfio_ioread64 ioread64be 3107fd7ef3SAlex Williamson #define vfio_iowrite64 iowrite64be 3207fd7ef3SAlex Williamson #define vfio_ioread32 ioread32be 3307fd7ef3SAlex Williamson #define vfio_iowrite32 iowrite32be 3407fd7ef3SAlex Williamson #define vfio_ioread16 ioread16be 3507fd7ef3SAlex Williamson #define vfio_iowrite16 iowrite16be 3607fd7ef3SAlex Williamson #endif 3707fd7ef3SAlex Williamson #define vfio_ioread8 ioread8 3807fd7ef3SAlex Williamson #define vfio_iowrite8 iowrite8 3907fd7ef3SAlex Williamson 40bc93b9aeSAlex Williamson #define VFIO_IOWRITE(size) \ 418486ae16SYishai Hadas int vfio_pci_core_iowrite##size(struct vfio_pci_core_device *vdev, \ 42bc93b9aeSAlex Williamson bool test_mem, u##size val, void __iomem *io) \ 43bc93b9aeSAlex Williamson { \ 44bc93b9aeSAlex Williamson if (test_mem) { \ 45bc93b9aeSAlex Williamson down_read(&vdev->memory_lock); \ 46bc93b9aeSAlex Williamson if (!__vfio_pci_memory_enabled(vdev)) { \ 47bc93b9aeSAlex Williamson up_read(&vdev->memory_lock); \ 48bc93b9aeSAlex Williamson return -EIO; \ 49bc93b9aeSAlex Williamson } \ 50bc93b9aeSAlex Williamson } \ 51bc93b9aeSAlex Williamson \ 52bc93b9aeSAlex Williamson vfio_iowrite##size(val, io); \ 53bc93b9aeSAlex Williamson \ 54bc93b9aeSAlex Williamson if (test_mem) \ 55bc93b9aeSAlex Williamson up_read(&vdev->memory_lock); \ 56bc93b9aeSAlex Williamson \ 57bc93b9aeSAlex Williamson return 0; \ 588486ae16SYishai Hadas } \ 598486ae16SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_iowrite##size); 60bc93b9aeSAlex Williamson 61bc93b9aeSAlex Williamson VFIO_IOWRITE(8) 62bc93b9aeSAlex Williamson VFIO_IOWRITE(16) 63bc93b9aeSAlex Williamson VFIO_IOWRITE(32) 64bc93b9aeSAlex Williamson #ifdef iowrite64 65bc93b9aeSAlex Williamson VFIO_IOWRITE(64) 66bc93b9aeSAlex Williamson #endif 67bc93b9aeSAlex Williamson 68bc93b9aeSAlex Williamson #define VFIO_IOREAD(size) \ 698486ae16SYishai Hadas int vfio_pci_core_ioread##size(struct vfio_pci_core_device *vdev, \ 70bc93b9aeSAlex Williamson bool test_mem, u##size *val, void __iomem *io) \ 71bc93b9aeSAlex Williamson { \ 72bc93b9aeSAlex Williamson if (test_mem) { \ 73bc93b9aeSAlex Williamson down_read(&vdev->memory_lock); \ 74bc93b9aeSAlex Williamson if (!__vfio_pci_memory_enabled(vdev)) { \ 75bc93b9aeSAlex Williamson up_read(&vdev->memory_lock); \ 76bc93b9aeSAlex Williamson return -EIO; \ 77bc93b9aeSAlex Williamson } \ 78bc93b9aeSAlex Williamson } \ 79bc93b9aeSAlex Williamson \ 80bc93b9aeSAlex Williamson *val = vfio_ioread##size(io); \ 81bc93b9aeSAlex Williamson \ 82bc93b9aeSAlex Williamson if (test_mem) \ 83bc93b9aeSAlex Williamson up_read(&vdev->memory_lock); \ 84bc93b9aeSAlex Williamson \ 85bc93b9aeSAlex Williamson return 0; \ 868486ae16SYishai Hadas } \ 878486ae16SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_ioread##size); 88bc93b9aeSAlex Williamson 89bc93b9aeSAlex Williamson VFIO_IOREAD(8) 90bc93b9aeSAlex Williamson VFIO_IOREAD(16) 91bc93b9aeSAlex Williamson VFIO_IOREAD(32) 92bc93b9aeSAlex Williamson 93*186bfe44SGerd Bayer #define VFIO_IORDWR(size) \ 94*186bfe44SGerd Bayer static int vfio_pci_iordwr##size(struct vfio_pci_core_device *vdev,\ 95*186bfe44SGerd Bayer bool iswrite, bool test_mem, \ 96*186bfe44SGerd Bayer void __iomem *io, char __user *buf, \ 97*186bfe44SGerd Bayer loff_t off, size_t *filled) \ 98*186bfe44SGerd Bayer { \ 99*186bfe44SGerd Bayer u##size val; \ 100*186bfe44SGerd Bayer int ret; \ 101*186bfe44SGerd Bayer \ 102*186bfe44SGerd Bayer if (iswrite) { \ 103*186bfe44SGerd Bayer if (copy_from_user(&val, buf, sizeof(val))) \ 104*186bfe44SGerd Bayer return -EFAULT; \ 105*186bfe44SGerd Bayer \ 106*186bfe44SGerd Bayer ret = vfio_pci_core_iowrite##size(vdev, test_mem, \ 107*186bfe44SGerd Bayer val, io + off); \ 108*186bfe44SGerd Bayer if (ret) \ 109*186bfe44SGerd Bayer return ret; \ 110*186bfe44SGerd Bayer } else { \ 111*186bfe44SGerd Bayer ret = vfio_pci_core_ioread##size(vdev, test_mem, \ 112*186bfe44SGerd Bayer &val, io + off); \ 113*186bfe44SGerd Bayer if (ret) \ 114*186bfe44SGerd Bayer return ret; \ 115*186bfe44SGerd Bayer \ 116*186bfe44SGerd Bayer if (copy_to_user(buf, &val, sizeof(val))) \ 117*186bfe44SGerd Bayer return -EFAULT; \ 118*186bfe44SGerd Bayer } \ 119*186bfe44SGerd Bayer \ 120*186bfe44SGerd Bayer *filled = sizeof(val); \ 121*186bfe44SGerd Bayer return 0; \ 122*186bfe44SGerd Bayer } \ 123*186bfe44SGerd Bayer 124*186bfe44SGerd Bayer VFIO_IORDWR(8) 125*186bfe44SGerd Bayer VFIO_IORDWR(16) 126*186bfe44SGerd Bayer VFIO_IORDWR(32) 12789e1f7d4SAlex Williamson /* 128906ee99dSAlex Williamson * Read or write from an __iomem region (MMIO or I/O port) with an excluded 129906ee99dSAlex Williamson * range which is inaccessible. The excluded range drops writes and fills 130906ee99dSAlex Williamson * reads with -1. This is intended for handling MSI-X vector tables and 131906ee99dSAlex Williamson * leftover space for ROM BARs. 13289e1f7d4SAlex Williamson */ 1334de676d4SAnkit Agrawal ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, 134bc93b9aeSAlex Williamson void __iomem *io, char __user *buf, 135906ee99dSAlex Williamson loff_t off, size_t count, size_t x_start, 136906ee99dSAlex Williamson size_t x_end, bool iswrite) 13789e1f7d4SAlex Williamson { 138906ee99dSAlex Williamson ssize_t done = 0; 139bc93b9aeSAlex Williamson int ret; 14089e1f7d4SAlex Williamson 14189e1f7d4SAlex Williamson while (count) { 14289e1f7d4SAlex Williamson size_t fillable, filled; 14389e1f7d4SAlex Williamson 144906ee99dSAlex Williamson if (off < x_start) 145906ee99dSAlex Williamson fillable = min(count, (size_t)(x_start - off)); 146906ee99dSAlex Williamson else if (off >= x_end) 147906ee99dSAlex Williamson fillable = count; 14889e1f7d4SAlex Williamson else 14989e1f7d4SAlex Williamson fillable = 0; 15089e1f7d4SAlex Williamson 151906ee99dSAlex Williamson if (fillable >= 4 && !(off % 4)) { 152*186bfe44SGerd Bayer ret = vfio_pci_iordwr32(vdev, iswrite, test_mem, 153*186bfe44SGerd Bayer io, buf, off, &filled); 154bc93b9aeSAlex Williamson if (ret) 155bc93b9aeSAlex Williamson return ret; 15689e1f7d4SAlex Williamson 157906ee99dSAlex Williamson } else if (fillable >= 2 && !(off % 2)) { 158*186bfe44SGerd Bayer ret = vfio_pci_iordwr16(vdev, iswrite, test_mem, 159*186bfe44SGerd Bayer io, buf, off, &filled); 160bc93b9aeSAlex Williamson if (ret) 161bc93b9aeSAlex Williamson return ret; 16289e1f7d4SAlex Williamson 16389e1f7d4SAlex Williamson } else if (fillable) { 164*186bfe44SGerd Bayer ret = vfio_pci_iordwr8(vdev, iswrite, test_mem, 165*186bfe44SGerd Bayer io, buf, off, &filled); 166bc93b9aeSAlex Williamson if (ret) 167bc93b9aeSAlex Williamson return ret; 16889e1f7d4SAlex Williamson 16989e1f7d4SAlex Williamson } else { 170906ee99dSAlex Williamson /* Fill reads with -1, drop writes */ 171906ee99dSAlex Williamson filled = min(count, (size_t)(x_end - off)); 17289e1f7d4SAlex Williamson if (!iswrite) { 173906ee99dSAlex Williamson u8 val = 0xFF; 17489e1f7d4SAlex Williamson size_t i; 17589e1f7d4SAlex Williamson 176906ee99dSAlex Williamson for (i = 0; i < filled; i++) 177906ee99dSAlex Williamson if (copy_to_user(buf + i, &val, 1)) 178906ee99dSAlex Williamson return -EFAULT; 17989e1f7d4SAlex Williamson } 18089e1f7d4SAlex Williamson } 18189e1f7d4SAlex Williamson 18289e1f7d4SAlex Williamson count -= filled; 18389e1f7d4SAlex Williamson done += filled; 184906ee99dSAlex Williamson off += filled; 18589e1f7d4SAlex Williamson buf += filled; 18689e1f7d4SAlex Williamson } 18789e1f7d4SAlex Williamson 188906ee99dSAlex Williamson return done; 189906ee99dSAlex Williamson } 1904de676d4SAnkit Agrawal EXPORT_SYMBOL_GPL(vfio_pci_core_do_io_rw); 191906ee99dSAlex Williamson 1928bccc5b8SYishai Hadas int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar) 1930d77ed35SAlex Williamson { 1940d77ed35SAlex Williamson struct pci_dev *pdev = vdev->pdev; 1950d77ed35SAlex Williamson int ret; 1960d77ed35SAlex Williamson void __iomem *io; 1970d77ed35SAlex Williamson 1980d77ed35SAlex Williamson if (vdev->barmap[bar]) 1990d77ed35SAlex Williamson return 0; 2000d77ed35SAlex Williamson 2010d77ed35SAlex Williamson ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); 2020d77ed35SAlex Williamson if (ret) 2030d77ed35SAlex Williamson return ret; 2040d77ed35SAlex Williamson 2050d77ed35SAlex Williamson io = pci_iomap(pdev, bar, 0); 2060d77ed35SAlex Williamson if (!io) { 2070d77ed35SAlex Williamson pci_release_selected_regions(pdev, 1 << bar); 2080d77ed35SAlex Williamson return -ENOMEM; 2090d77ed35SAlex Williamson } 2100d77ed35SAlex Williamson 2110d77ed35SAlex Williamson vdev->barmap[bar] = io; 2120d77ed35SAlex Williamson 2130d77ed35SAlex Williamson return 0; 2140d77ed35SAlex Williamson } 2158bccc5b8SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap); 2160d77ed35SAlex Williamson 21753647510SMax Gurtovoy ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf, 218906ee99dSAlex Williamson size_t count, loff_t *ppos, bool iswrite) 219906ee99dSAlex Williamson { 220906ee99dSAlex Williamson struct pci_dev *pdev = vdev->pdev; 221906ee99dSAlex Williamson loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 222906ee99dSAlex Williamson int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 223906ee99dSAlex Williamson size_t x_start = 0, x_end = 0; 224906ee99dSAlex Williamson resource_size_t end; 225906ee99dSAlex Williamson void __iomem *io; 226abafbc55SAlex Williamson struct resource *res = &vdev->pdev->resource[bar]; 227906ee99dSAlex Williamson ssize_t done; 228906ee99dSAlex Williamson 229a13b6459SAlex Williamson if (pci_resource_start(pdev, bar)) 230906ee99dSAlex Williamson end = pci_resource_len(pdev, bar); 231a13b6459SAlex Williamson else if (bar == PCI_ROM_RESOURCE && 232a13b6459SAlex Williamson pdev->resource[bar].flags & IORESOURCE_ROM_SHADOW) 233a13b6459SAlex Williamson end = 0x20000; 234a13b6459SAlex Williamson else 235a13b6459SAlex Williamson return -EINVAL; 236906ee99dSAlex Williamson 237906ee99dSAlex Williamson if (pos >= end) 238906ee99dSAlex Williamson return -EINVAL; 239906ee99dSAlex Williamson 240906ee99dSAlex Williamson count = min(count, (size_t)(end - pos)); 241906ee99dSAlex Williamson 242906ee99dSAlex Williamson if (bar == PCI_ROM_RESOURCE) { 243906ee99dSAlex Williamson /* 244906ee99dSAlex Williamson * The ROM can fill less space than the BAR, so we start the 245906ee99dSAlex Williamson * excluded range at the end of the actual ROM. This makes 246906ee99dSAlex Williamson * filling large ROM BARs much faster. 247906ee99dSAlex Williamson */ 248906ee99dSAlex Williamson io = pci_map_rom(pdev, &x_start); 249abafbc55SAlex Williamson if (!io) { 250abafbc55SAlex Williamson done = -ENOMEM; 251abafbc55SAlex Williamson goto out; 252abafbc55SAlex Williamson } 253906ee99dSAlex Williamson x_end = end; 2540d77ed35SAlex Williamson } else { 2558bccc5b8SYishai Hadas int ret = vfio_pci_core_setup_barmap(vdev, bar); 256abafbc55SAlex Williamson if (ret) { 257abafbc55SAlex Williamson done = ret; 258abafbc55SAlex Williamson goto out; 259abafbc55SAlex Williamson } 260906ee99dSAlex Williamson 261906ee99dSAlex Williamson io = vdev->barmap[bar]; 2620d77ed35SAlex Williamson } 263906ee99dSAlex Williamson 264906ee99dSAlex Williamson if (bar == vdev->msix_bar) { 265906ee99dSAlex Williamson x_start = vdev->msix_offset; 266906ee99dSAlex Williamson x_end = vdev->msix_offset + vdev->msix_size; 267906ee99dSAlex Williamson } 268906ee99dSAlex Williamson 2694de676d4SAnkit Agrawal done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos, 270bc93b9aeSAlex Williamson count, x_start, x_end, iswrite); 271906ee99dSAlex Williamson 272906ee99dSAlex Williamson if (done >= 0) 27389e1f7d4SAlex Williamson *ppos += done; 27489e1f7d4SAlex Williamson 27589e1f7d4SAlex Williamson if (bar == PCI_ROM_RESOURCE) 27689e1f7d4SAlex Williamson pci_unmap_rom(pdev, io); 277abafbc55SAlex Williamson out: 278906ee99dSAlex Williamson return done; 27989e1f7d4SAlex Williamson } 28084237a82SAlex Williamson 2816e031ec0SAlex Williamson #ifdef CONFIG_VFIO_PCI_VGA 28253647510SMax Gurtovoy ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf, 28384237a82SAlex Williamson size_t count, loff_t *ppos, bool iswrite) 28484237a82SAlex Williamson { 28584237a82SAlex Williamson int ret; 28684237a82SAlex Williamson loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK; 28784237a82SAlex Williamson void __iomem *iomem = NULL; 28884237a82SAlex Williamson unsigned int rsrc; 28984237a82SAlex Williamson bool is_ioport; 29084237a82SAlex Williamson ssize_t done; 29184237a82SAlex Williamson 29284237a82SAlex Williamson if (!vdev->has_vga) 29384237a82SAlex Williamson return -EINVAL; 29484237a82SAlex Williamson 29545e86971SArnd Bergmann if (pos > 0xbfffful) 29645e86971SArnd Bergmann return -EINVAL; 29745e86971SArnd Bergmann 29845e86971SArnd Bergmann switch ((u32)pos) { 29984237a82SAlex Williamson case 0xa0000 ... 0xbffff: 30084237a82SAlex Williamson count = min(count, (size_t)(0xc0000 - pos)); 3014bdc0d67SChristoph Hellwig iomem = ioremap(0xa0000, 0xbffff - 0xa0000 + 1); 30284237a82SAlex Williamson off = pos - 0xa0000; 30384237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_MEM; 30484237a82SAlex Williamson is_ioport = false; 30584237a82SAlex Williamson break; 30684237a82SAlex Williamson case 0x3b0 ... 0x3bb: 30784237a82SAlex Williamson count = min(count, (size_t)(0x3bc - pos)); 30884237a82SAlex Williamson iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1); 30984237a82SAlex Williamson off = pos - 0x3b0; 31084237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_IO; 31184237a82SAlex Williamson is_ioport = true; 31284237a82SAlex Williamson break; 31384237a82SAlex Williamson case 0x3c0 ... 0x3df: 31484237a82SAlex Williamson count = min(count, (size_t)(0x3e0 - pos)); 31584237a82SAlex Williamson iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1); 31684237a82SAlex Williamson off = pos - 0x3c0; 31784237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_IO; 31884237a82SAlex Williamson is_ioport = true; 31984237a82SAlex Williamson break; 32084237a82SAlex Williamson default: 32184237a82SAlex Williamson return -EINVAL; 32284237a82SAlex Williamson } 32384237a82SAlex Williamson 32484237a82SAlex Williamson if (!iomem) 32584237a82SAlex Williamson return -ENOMEM; 32684237a82SAlex Williamson 32784237a82SAlex Williamson ret = vga_get_interruptible(vdev->pdev, rsrc); 32884237a82SAlex Williamson if (ret) { 32984237a82SAlex Williamson is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 33084237a82SAlex Williamson return ret; 33184237a82SAlex Williamson } 33284237a82SAlex Williamson 333bc93b9aeSAlex Williamson /* 334bc93b9aeSAlex Williamson * VGA MMIO is a legacy, non-BAR resource that hopefully allows 335bc93b9aeSAlex Williamson * probing, so we don't currently worry about access in relation 336bc93b9aeSAlex Williamson * to the memory enable bit in the command register. 337bc93b9aeSAlex Williamson */ 3384de676d4SAnkit Agrawal done = vfio_pci_core_do_io_rw(vdev, false, iomem, buf, off, count, 3394de676d4SAnkit Agrawal 0, 0, iswrite); 34084237a82SAlex Williamson 34184237a82SAlex Williamson vga_put(vdev->pdev, rsrc); 34284237a82SAlex Williamson 34384237a82SAlex Williamson is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 34484237a82SAlex Williamson 34584237a82SAlex Williamson if (done >= 0) 34684237a82SAlex Williamson *ppos += done; 34784237a82SAlex Williamson 34884237a82SAlex Williamson return done; 34984237a82SAlex Williamson } 3506e031ec0SAlex Williamson #endif 35130656177SAlex Williamson 35238565c93SAlex Williamson static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd, 35338565c93SAlex Williamson bool test_mem) 35430656177SAlex Williamson { 35530656177SAlex Williamson switch (ioeventfd->count) { 35630656177SAlex Williamson case 1: 3578486ae16SYishai Hadas vfio_pci_core_iowrite8(ioeventfd->vdev, test_mem, 358bc93b9aeSAlex Williamson ioeventfd->data, ioeventfd->addr); 35930656177SAlex Williamson break; 36030656177SAlex Williamson case 2: 3618486ae16SYishai Hadas vfio_pci_core_iowrite16(ioeventfd->vdev, test_mem, 362bc93b9aeSAlex Williamson ioeventfd->data, ioeventfd->addr); 36330656177SAlex Williamson break; 36430656177SAlex Williamson case 4: 3658486ae16SYishai Hadas vfio_pci_core_iowrite32(ioeventfd->vdev, test_mem, 366bc93b9aeSAlex Williamson ioeventfd->data, ioeventfd->addr); 36730656177SAlex Williamson break; 36830656177SAlex Williamson #ifdef iowrite64 36930656177SAlex Williamson case 8: 3708486ae16SYishai Hadas vfio_pci_core_iowrite64(ioeventfd->vdev, test_mem, 371bc93b9aeSAlex Williamson ioeventfd->data, ioeventfd->addr); 37230656177SAlex Williamson break; 37330656177SAlex Williamson #endif 37430656177SAlex Williamson } 37538565c93SAlex Williamson } 37638565c93SAlex Williamson 37738565c93SAlex Williamson static int vfio_pci_ioeventfd_handler(void *opaque, void *unused) 37838565c93SAlex Williamson { 37938565c93SAlex Williamson struct vfio_pci_ioeventfd *ioeventfd = opaque; 38053647510SMax Gurtovoy struct vfio_pci_core_device *vdev = ioeventfd->vdev; 38138565c93SAlex Williamson 38238565c93SAlex Williamson if (ioeventfd->test_mem) { 38338565c93SAlex Williamson if (!down_read_trylock(&vdev->memory_lock)) 38438565c93SAlex Williamson return 1; /* Lock contended, use thread */ 38538565c93SAlex Williamson if (!__vfio_pci_memory_enabled(vdev)) { 38638565c93SAlex Williamson up_read(&vdev->memory_lock); 38738565c93SAlex Williamson return 0; 38838565c93SAlex Williamson } 38938565c93SAlex Williamson } 39038565c93SAlex Williamson 39138565c93SAlex Williamson vfio_pci_ioeventfd_do_write(ioeventfd, false); 39238565c93SAlex Williamson 39338565c93SAlex Williamson if (ioeventfd->test_mem) 39438565c93SAlex Williamson up_read(&vdev->memory_lock); 39530656177SAlex Williamson 39630656177SAlex Williamson return 0; 39730656177SAlex Williamson } 39830656177SAlex Williamson 39938565c93SAlex Williamson static void vfio_pci_ioeventfd_thread(void *opaque, void *unused) 40038565c93SAlex Williamson { 40138565c93SAlex Williamson struct vfio_pci_ioeventfd *ioeventfd = opaque; 40238565c93SAlex Williamson 40338565c93SAlex Williamson vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem); 40438565c93SAlex Williamson } 40538565c93SAlex Williamson 40616f4cbd9SJason Gunthorpe int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset, 40730656177SAlex Williamson uint64_t data, int count, int fd) 40830656177SAlex Williamson { 40930656177SAlex Williamson struct pci_dev *pdev = vdev->pdev; 41030656177SAlex Williamson loff_t pos = offset & VFIO_PCI_OFFSET_MASK; 41130656177SAlex Williamson int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset); 41230656177SAlex Williamson struct vfio_pci_ioeventfd *ioeventfd; 41330656177SAlex Williamson 41430656177SAlex Williamson /* Only support ioeventfds into BARs */ 41530656177SAlex Williamson if (bar > VFIO_PCI_BAR5_REGION_INDEX) 41630656177SAlex Williamson return -EINVAL; 41730656177SAlex Williamson 41830656177SAlex Williamson if (pos + count > pci_resource_len(pdev, bar)) 41930656177SAlex Williamson return -EINVAL; 42030656177SAlex Williamson 42130656177SAlex Williamson /* Disallow ioeventfds working around MSI-X table writes */ 42230656177SAlex Williamson if (bar == vdev->msix_bar && 42330656177SAlex Williamson !(pos + count <= vdev->msix_offset || 42430656177SAlex Williamson pos >= vdev->msix_offset + vdev->msix_size)) 42530656177SAlex Williamson return -EINVAL; 42630656177SAlex Williamson 42730656177SAlex Williamson #ifndef iowrite64 42830656177SAlex Williamson if (count == 8) 42930656177SAlex Williamson return -EINVAL; 43030656177SAlex Williamson #endif 43130656177SAlex Williamson 4328bccc5b8SYishai Hadas ret = vfio_pci_core_setup_barmap(vdev, bar); 43330656177SAlex Williamson if (ret) 43430656177SAlex Williamson return ret; 43530656177SAlex Williamson 43630656177SAlex Williamson mutex_lock(&vdev->ioeventfds_lock); 43730656177SAlex Williamson 43830656177SAlex Williamson list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) { 43930656177SAlex Williamson if (ioeventfd->pos == pos && ioeventfd->bar == bar && 44030656177SAlex Williamson ioeventfd->data == data && ioeventfd->count == count) { 44130656177SAlex Williamson if (fd == -1) { 44230656177SAlex Williamson vfio_virqfd_disable(&ioeventfd->virqfd); 44330656177SAlex Williamson list_del(&ioeventfd->next); 44430656177SAlex Williamson vdev->ioeventfds_nr--; 44530656177SAlex Williamson kfree(ioeventfd); 44630656177SAlex Williamson ret = 0; 44730656177SAlex Williamson } else 44830656177SAlex Williamson ret = -EEXIST; 44930656177SAlex Williamson 45030656177SAlex Williamson goto out_unlock; 45130656177SAlex Williamson } 45230656177SAlex Williamson } 45330656177SAlex Williamson 45430656177SAlex Williamson if (fd < 0) { 45530656177SAlex Williamson ret = -ENODEV; 45630656177SAlex Williamson goto out_unlock; 45730656177SAlex Williamson } 45830656177SAlex Williamson 45930656177SAlex Williamson if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) { 46030656177SAlex Williamson ret = -ENOSPC; 46130656177SAlex Williamson goto out_unlock; 46230656177SAlex Williamson } 46330656177SAlex Williamson 4640886196cSJason Gunthorpe ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL_ACCOUNT); 46530656177SAlex Williamson if (!ioeventfd) { 46630656177SAlex Williamson ret = -ENOMEM; 46730656177SAlex Williamson goto out_unlock; 46830656177SAlex Williamson } 46930656177SAlex Williamson 470bc93b9aeSAlex Williamson ioeventfd->vdev = vdev; 47130656177SAlex Williamson ioeventfd->addr = vdev->barmap[bar] + pos; 47230656177SAlex Williamson ioeventfd->data = data; 47330656177SAlex Williamson ioeventfd->pos = pos; 47430656177SAlex Williamson ioeventfd->bar = bar; 47530656177SAlex Williamson ioeventfd->count = count; 476bc93b9aeSAlex Williamson ioeventfd->test_mem = vdev->pdev->resource[bar].flags & IORESOURCE_MEM; 47730656177SAlex Williamson 47830656177SAlex Williamson ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler, 47938565c93SAlex Williamson vfio_pci_ioeventfd_thread, NULL, 48038565c93SAlex Williamson &ioeventfd->virqfd, fd); 48130656177SAlex Williamson if (ret) { 48230656177SAlex Williamson kfree(ioeventfd); 48330656177SAlex Williamson goto out_unlock; 48430656177SAlex Williamson } 48530656177SAlex Williamson 48630656177SAlex Williamson list_add(&ioeventfd->next, &vdev->ioeventfds_list); 48730656177SAlex Williamson vdev->ioeventfds_nr++; 48830656177SAlex Williamson 48930656177SAlex Williamson out_unlock: 49030656177SAlex Williamson mutex_unlock(&vdev->ioeventfds_lock); 49130656177SAlex Williamson 49230656177SAlex Williamson return ret; 49330656177SAlex Williamson } 494