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) 92*4df13a68SBen Segal #ifdef ioread64 93*4df13a68SBen Segal VFIO_IOREAD(64) 94*4df13a68SBen Segal #endif 95bc93b9aeSAlex Williamson 96186bfe44SGerd Bayer #define VFIO_IORDWR(size) \ 97186bfe44SGerd Bayer static int vfio_pci_iordwr##size(struct vfio_pci_core_device *vdev,\ 98186bfe44SGerd Bayer bool iswrite, bool test_mem, \ 99186bfe44SGerd Bayer void __iomem *io, char __user *buf, \ 100186bfe44SGerd Bayer loff_t off, size_t *filled) \ 101186bfe44SGerd Bayer { \ 102186bfe44SGerd Bayer u##size val; \ 103186bfe44SGerd Bayer int ret; \ 104186bfe44SGerd Bayer \ 105186bfe44SGerd Bayer if (iswrite) { \ 106186bfe44SGerd Bayer if (copy_from_user(&val, buf, sizeof(val))) \ 107186bfe44SGerd Bayer return -EFAULT; \ 108186bfe44SGerd Bayer \ 109186bfe44SGerd Bayer ret = vfio_pci_core_iowrite##size(vdev, test_mem, \ 110186bfe44SGerd Bayer val, io + off); \ 111186bfe44SGerd Bayer if (ret) \ 112186bfe44SGerd Bayer return ret; \ 113186bfe44SGerd Bayer } else { \ 114186bfe44SGerd Bayer ret = vfio_pci_core_ioread##size(vdev, test_mem, \ 115186bfe44SGerd Bayer &val, io + off); \ 116186bfe44SGerd Bayer if (ret) \ 117186bfe44SGerd Bayer return ret; \ 118186bfe44SGerd Bayer \ 119186bfe44SGerd Bayer if (copy_to_user(buf, &val, sizeof(val))) \ 120186bfe44SGerd Bayer return -EFAULT; \ 121186bfe44SGerd Bayer } \ 122186bfe44SGerd Bayer \ 123186bfe44SGerd Bayer *filled = sizeof(val); \ 124186bfe44SGerd Bayer return 0; \ 125186bfe44SGerd Bayer } \ 126186bfe44SGerd Bayer 127186bfe44SGerd Bayer VFIO_IORDWR(8) 128186bfe44SGerd Bayer VFIO_IORDWR(16) 129186bfe44SGerd Bayer VFIO_IORDWR(32) 130*4df13a68SBen Segal #if defined(ioread64) && defined(iowrite64) 131*4df13a68SBen Segal VFIO_IORDWR(64) 132*4df13a68SBen Segal #endif 133*4df13a68SBen Segal 13489e1f7d4SAlex Williamson /* 135906ee99dSAlex Williamson * Read or write from an __iomem region (MMIO or I/O port) with an excluded 136906ee99dSAlex Williamson * range which is inaccessible. The excluded range drops writes and fills 137906ee99dSAlex Williamson * reads with -1. This is intended for handling MSI-X vector tables and 138906ee99dSAlex Williamson * leftover space for ROM BARs. 13989e1f7d4SAlex Williamson */ 1404de676d4SAnkit Agrawal ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, 141bc93b9aeSAlex Williamson void __iomem *io, char __user *buf, 142906ee99dSAlex Williamson loff_t off, size_t count, size_t x_start, 143906ee99dSAlex Williamson size_t x_end, bool iswrite) 14489e1f7d4SAlex Williamson { 145906ee99dSAlex Williamson ssize_t done = 0; 146bc93b9aeSAlex Williamson int ret; 14789e1f7d4SAlex Williamson 14889e1f7d4SAlex Williamson while (count) { 14989e1f7d4SAlex Williamson size_t fillable, filled; 15089e1f7d4SAlex Williamson 151906ee99dSAlex Williamson if (off < x_start) 152906ee99dSAlex Williamson fillable = min(count, (size_t)(x_start - off)); 153906ee99dSAlex Williamson else if (off >= x_end) 154906ee99dSAlex Williamson fillable = count; 15589e1f7d4SAlex Williamson else 15689e1f7d4SAlex Williamson fillable = 0; 15789e1f7d4SAlex Williamson 158*4df13a68SBen Segal #if defined(ioread64) && defined(iowrite64) 159*4df13a68SBen Segal if (fillable >= 8 && !(off % 8)) { 160*4df13a68SBen Segal ret = vfio_pci_iordwr64(vdev, iswrite, test_mem, 161*4df13a68SBen Segal io, buf, off, &filled); 162*4df13a68SBen Segal if (ret) 163*4df13a68SBen Segal return ret; 164*4df13a68SBen Segal 165*4df13a68SBen Segal } else 166*4df13a68SBen Segal #endif 167906ee99dSAlex Williamson if (fillable >= 4 && !(off % 4)) { 168186bfe44SGerd Bayer ret = vfio_pci_iordwr32(vdev, iswrite, test_mem, 169186bfe44SGerd Bayer io, buf, off, &filled); 170bc93b9aeSAlex Williamson if (ret) 171bc93b9aeSAlex Williamson return ret; 17289e1f7d4SAlex Williamson 173906ee99dSAlex Williamson } else if (fillable >= 2 && !(off % 2)) { 174186bfe44SGerd Bayer ret = vfio_pci_iordwr16(vdev, iswrite, test_mem, 175186bfe44SGerd Bayer io, buf, off, &filled); 176bc93b9aeSAlex Williamson if (ret) 177bc93b9aeSAlex Williamson return ret; 17889e1f7d4SAlex Williamson 17989e1f7d4SAlex Williamson } else if (fillable) { 180186bfe44SGerd Bayer ret = vfio_pci_iordwr8(vdev, iswrite, test_mem, 181186bfe44SGerd Bayer io, buf, off, &filled); 182bc93b9aeSAlex Williamson if (ret) 183bc93b9aeSAlex Williamson return ret; 18489e1f7d4SAlex Williamson 18589e1f7d4SAlex Williamson } else { 186906ee99dSAlex Williamson /* Fill reads with -1, drop writes */ 187906ee99dSAlex Williamson filled = min(count, (size_t)(x_end - off)); 18889e1f7d4SAlex Williamson if (!iswrite) { 189906ee99dSAlex Williamson u8 val = 0xFF; 19089e1f7d4SAlex Williamson size_t i; 19189e1f7d4SAlex Williamson 192906ee99dSAlex Williamson for (i = 0; i < filled; i++) 193906ee99dSAlex Williamson if (copy_to_user(buf + i, &val, 1)) 194906ee99dSAlex Williamson return -EFAULT; 19589e1f7d4SAlex Williamson } 19689e1f7d4SAlex Williamson } 19789e1f7d4SAlex Williamson 19889e1f7d4SAlex Williamson count -= filled; 19989e1f7d4SAlex Williamson done += filled; 200906ee99dSAlex Williamson off += filled; 20189e1f7d4SAlex Williamson buf += filled; 20289e1f7d4SAlex Williamson } 20389e1f7d4SAlex Williamson 204906ee99dSAlex Williamson return done; 205906ee99dSAlex Williamson } 2064de676d4SAnkit Agrawal EXPORT_SYMBOL_GPL(vfio_pci_core_do_io_rw); 207906ee99dSAlex Williamson 2088bccc5b8SYishai Hadas int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar) 2090d77ed35SAlex Williamson { 2100d77ed35SAlex Williamson struct pci_dev *pdev = vdev->pdev; 2110d77ed35SAlex Williamson int ret; 2120d77ed35SAlex Williamson void __iomem *io; 2130d77ed35SAlex Williamson 2140d77ed35SAlex Williamson if (vdev->barmap[bar]) 2150d77ed35SAlex Williamson return 0; 2160d77ed35SAlex Williamson 2170d77ed35SAlex Williamson ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); 2180d77ed35SAlex Williamson if (ret) 2190d77ed35SAlex Williamson return ret; 2200d77ed35SAlex Williamson 2210d77ed35SAlex Williamson io = pci_iomap(pdev, bar, 0); 2220d77ed35SAlex Williamson if (!io) { 2230d77ed35SAlex Williamson pci_release_selected_regions(pdev, 1 << bar); 2240d77ed35SAlex Williamson return -ENOMEM; 2250d77ed35SAlex Williamson } 2260d77ed35SAlex Williamson 2270d77ed35SAlex Williamson vdev->barmap[bar] = io; 2280d77ed35SAlex Williamson 2290d77ed35SAlex Williamson return 0; 2300d77ed35SAlex Williamson } 2318bccc5b8SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap); 2320d77ed35SAlex Williamson 23353647510SMax Gurtovoy ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf, 234906ee99dSAlex Williamson size_t count, loff_t *ppos, bool iswrite) 235906ee99dSAlex Williamson { 236906ee99dSAlex Williamson struct pci_dev *pdev = vdev->pdev; 237906ee99dSAlex Williamson loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 238906ee99dSAlex Williamson int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 239906ee99dSAlex Williamson size_t x_start = 0, x_end = 0; 240906ee99dSAlex Williamson resource_size_t end; 241906ee99dSAlex Williamson void __iomem *io; 242abafbc55SAlex Williamson struct resource *res = &vdev->pdev->resource[bar]; 243906ee99dSAlex Williamson ssize_t done; 244906ee99dSAlex Williamson 245a13b6459SAlex Williamson if (pci_resource_start(pdev, bar)) 246906ee99dSAlex Williamson end = pci_resource_len(pdev, bar); 247a13b6459SAlex Williamson else if (bar == PCI_ROM_RESOURCE && 248a13b6459SAlex Williamson pdev->resource[bar].flags & IORESOURCE_ROM_SHADOW) 249a13b6459SAlex Williamson end = 0x20000; 250a13b6459SAlex Williamson else 251a13b6459SAlex Williamson return -EINVAL; 252906ee99dSAlex Williamson 253906ee99dSAlex Williamson if (pos >= end) 254906ee99dSAlex Williamson return -EINVAL; 255906ee99dSAlex Williamson 256906ee99dSAlex Williamson count = min(count, (size_t)(end - pos)); 257906ee99dSAlex Williamson 258906ee99dSAlex Williamson if (bar == PCI_ROM_RESOURCE) { 259906ee99dSAlex Williamson /* 260906ee99dSAlex Williamson * The ROM can fill less space than the BAR, so we start the 261906ee99dSAlex Williamson * excluded range at the end of the actual ROM. This makes 262906ee99dSAlex Williamson * filling large ROM BARs much faster. 263906ee99dSAlex Williamson */ 264906ee99dSAlex Williamson io = pci_map_rom(pdev, &x_start); 265abafbc55SAlex Williamson if (!io) { 266abafbc55SAlex Williamson done = -ENOMEM; 267abafbc55SAlex Williamson goto out; 268abafbc55SAlex Williamson } 269906ee99dSAlex Williamson x_end = end; 2700d77ed35SAlex Williamson } else { 2718bccc5b8SYishai Hadas int ret = vfio_pci_core_setup_barmap(vdev, bar); 272abafbc55SAlex Williamson if (ret) { 273abafbc55SAlex Williamson done = ret; 274abafbc55SAlex Williamson goto out; 275abafbc55SAlex Williamson } 276906ee99dSAlex Williamson 277906ee99dSAlex Williamson io = vdev->barmap[bar]; 2780d77ed35SAlex Williamson } 279906ee99dSAlex Williamson 280906ee99dSAlex Williamson if (bar == vdev->msix_bar) { 281906ee99dSAlex Williamson x_start = vdev->msix_offset; 282906ee99dSAlex Williamson x_end = vdev->msix_offset + vdev->msix_size; 283906ee99dSAlex Williamson } 284906ee99dSAlex Williamson 2854de676d4SAnkit Agrawal done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos, 286bc93b9aeSAlex Williamson count, x_start, x_end, iswrite); 287906ee99dSAlex Williamson 288906ee99dSAlex Williamson if (done >= 0) 28989e1f7d4SAlex Williamson *ppos += done; 29089e1f7d4SAlex Williamson 29189e1f7d4SAlex Williamson if (bar == PCI_ROM_RESOURCE) 29289e1f7d4SAlex Williamson pci_unmap_rom(pdev, io); 293abafbc55SAlex Williamson out: 294906ee99dSAlex Williamson return done; 29589e1f7d4SAlex Williamson } 29684237a82SAlex Williamson 2976e031ec0SAlex Williamson #ifdef CONFIG_VFIO_PCI_VGA 29853647510SMax Gurtovoy ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf, 29984237a82SAlex Williamson size_t count, loff_t *ppos, bool iswrite) 30084237a82SAlex Williamson { 30184237a82SAlex Williamson int ret; 30284237a82SAlex Williamson loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK; 30384237a82SAlex Williamson void __iomem *iomem = NULL; 30484237a82SAlex Williamson unsigned int rsrc; 30584237a82SAlex Williamson bool is_ioport; 30684237a82SAlex Williamson ssize_t done; 30784237a82SAlex Williamson 30884237a82SAlex Williamson if (!vdev->has_vga) 30984237a82SAlex Williamson return -EINVAL; 31084237a82SAlex Williamson 31145e86971SArnd Bergmann if (pos > 0xbfffful) 31245e86971SArnd Bergmann return -EINVAL; 31345e86971SArnd Bergmann 31445e86971SArnd Bergmann switch ((u32)pos) { 31584237a82SAlex Williamson case 0xa0000 ... 0xbffff: 31684237a82SAlex Williamson count = min(count, (size_t)(0xc0000 - pos)); 3174bdc0d67SChristoph Hellwig iomem = ioremap(0xa0000, 0xbffff - 0xa0000 + 1); 31884237a82SAlex Williamson off = pos - 0xa0000; 31984237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_MEM; 32084237a82SAlex Williamson is_ioport = false; 32184237a82SAlex Williamson break; 32284237a82SAlex Williamson case 0x3b0 ... 0x3bb: 32384237a82SAlex Williamson count = min(count, (size_t)(0x3bc - pos)); 32484237a82SAlex Williamson iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1); 32584237a82SAlex Williamson off = pos - 0x3b0; 32684237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_IO; 32784237a82SAlex Williamson is_ioport = true; 32884237a82SAlex Williamson break; 32984237a82SAlex Williamson case 0x3c0 ... 0x3df: 33084237a82SAlex Williamson count = min(count, (size_t)(0x3e0 - pos)); 33184237a82SAlex Williamson iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1); 33284237a82SAlex Williamson off = pos - 0x3c0; 33384237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_IO; 33484237a82SAlex Williamson is_ioport = true; 33584237a82SAlex Williamson break; 33684237a82SAlex Williamson default: 33784237a82SAlex Williamson return -EINVAL; 33884237a82SAlex Williamson } 33984237a82SAlex Williamson 34084237a82SAlex Williamson if (!iomem) 34184237a82SAlex Williamson return -ENOMEM; 34284237a82SAlex Williamson 34384237a82SAlex Williamson ret = vga_get_interruptible(vdev->pdev, rsrc); 34484237a82SAlex Williamson if (ret) { 34584237a82SAlex Williamson is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 34684237a82SAlex Williamson return ret; 34784237a82SAlex Williamson } 34884237a82SAlex Williamson 349bc93b9aeSAlex Williamson /* 350bc93b9aeSAlex Williamson * VGA MMIO is a legacy, non-BAR resource that hopefully allows 351bc93b9aeSAlex Williamson * probing, so we don't currently worry about access in relation 352bc93b9aeSAlex Williamson * to the memory enable bit in the command register. 353bc93b9aeSAlex Williamson */ 3544de676d4SAnkit Agrawal done = vfio_pci_core_do_io_rw(vdev, false, iomem, buf, off, count, 3554de676d4SAnkit Agrawal 0, 0, iswrite); 35684237a82SAlex Williamson 35784237a82SAlex Williamson vga_put(vdev->pdev, rsrc); 35884237a82SAlex Williamson 35984237a82SAlex Williamson is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 36084237a82SAlex Williamson 36184237a82SAlex Williamson if (done >= 0) 36284237a82SAlex Williamson *ppos += done; 36384237a82SAlex Williamson 36484237a82SAlex Williamson return done; 36584237a82SAlex Williamson } 3666e031ec0SAlex Williamson #endif 36730656177SAlex Williamson 36838565c93SAlex Williamson static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd, 36938565c93SAlex Williamson bool test_mem) 37030656177SAlex Williamson { 37130656177SAlex Williamson switch (ioeventfd->count) { 37230656177SAlex Williamson case 1: 3738486ae16SYishai Hadas vfio_pci_core_iowrite8(ioeventfd->vdev, test_mem, 374bc93b9aeSAlex Williamson ioeventfd->data, ioeventfd->addr); 37530656177SAlex Williamson break; 37630656177SAlex Williamson case 2: 3778486ae16SYishai Hadas vfio_pci_core_iowrite16(ioeventfd->vdev, test_mem, 378bc93b9aeSAlex Williamson ioeventfd->data, ioeventfd->addr); 37930656177SAlex Williamson break; 38030656177SAlex Williamson case 4: 3818486ae16SYishai Hadas vfio_pci_core_iowrite32(ioeventfd->vdev, test_mem, 382bc93b9aeSAlex Williamson ioeventfd->data, ioeventfd->addr); 38330656177SAlex Williamson break; 38430656177SAlex Williamson #ifdef iowrite64 38530656177SAlex Williamson case 8: 3868486ae16SYishai Hadas vfio_pci_core_iowrite64(ioeventfd->vdev, test_mem, 387bc93b9aeSAlex Williamson ioeventfd->data, ioeventfd->addr); 38830656177SAlex Williamson break; 38930656177SAlex Williamson #endif 39030656177SAlex Williamson } 39138565c93SAlex Williamson } 39238565c93SAlex Williamson 39338565c93SAlex Williamson static int vfio_pci_ioeventfd_handler(void *opaque, void *unused) 39438565c93SAlex Williamson { 39538565c93SAlex Williamson struct vfio_pci_ioeventfd *ioeventfd = opaque; 39653647510SMax Gurtovoy struct vfio_pci_core_device *vdev = ioeventfd->vdev; 39738565c93SAlex Williamson 39838565c93SAlex Williamson if (ioeventfd->test_mem) { 39938565c93SAlex Williamson if (!down_read_trylock(&vdev->memory_lock)) 40038565c93SAlex Williamson return 1; /* Lock contended, use thread */ 40138565c93SAlex Williamson if (!__vfio_pci_memory_enabled(vdev)) { 40238565c93SAlex Williamson up_read(&vdev->memory_lock); 40338565c93SAlex Williamson return 0; 40438565c93SAlex Williamson } 40538565c93SAlex Williamson } 40638565c93SAlex Williamson 40738565c93SAlex Williamson vfio_pci_ioeventfd_do_write(ioeventfd, false); 40838565c93SAlex Williamson 40938565c93SAlex Williamson if (ioeventfd->test_mem) 41038565c93SAlex Williamson up_read(&vdev->memory_lock); 41130656177SAlex Williamson 41230656177SAlex Williamson return 0; 41330656177SAlex Williamson } 41430656177SAlex Williamson 41538565c93SAlex Williamson static void vfio_pci_ioeventfd_thread(void *opaque, void *unused) 41638565c93SAlex Williamson { 41738565c93SAlex Williamson struct vfio_pci_ioeventfd *ioeventfd = opaque; 41838565c93SAlex Williamson 41938565c93SAlex Williamson vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem); 42038565c93SAlex Williamson } 42138565c93SAlex Williamson 42216f4cbd9SJason Gunthorpe int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset, 42330656177SAlex Williamson uint64_t data, int count, int fd) 42430656177SAlex Williamson { 42530656177SAlex Williamson struct pci_dev *pdev = vdev->pdev; 42630656177SAlex Williamson loff_t pos = offset & VFIO_PCI_OFFSET_MASK; 42730656177SAlex Williamson int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset); 42830656177SAlex Williamson struct vfio_pci_ioeventfd *ioeventfd; 42930656177SAlex Williamson 43030656177SAlex Williamson /* Only support ioeventfds into BARs */ 43130656177SAlex Williamson if (bar > VFIO_PCI_BAR5_REGION_INDEX) 43230656177SAlex Williamson return -EINVAL; 43330656177SAlex Williamson 43430656177SAlex Williamson if (pos + count > pci_resource_len(pdev, bar)) 43530656177SAlex Williamson return -EINVAL; 43630656177SAlex Williamson 43730656177SAlex Williamson /* Disallow ioeventfds working around MSI-X table writes */ 43830656177SAlex Williamson if (bar == vdev->msix_bar && 43930656177SAlex Williamson !(pos + count <= vdev->msix_offset || 44030656177SAlex Williamson pos >= vdev->msix_offset + vdev->msix_size)) 44130656177SAlex Williamson return -EINVAL; 44230656177SAlex Williamson 44330656177SAlex Williamson #ifndef iowrite64 44430656177SAlex Williamson if (count == 8) 44530656177SAlex Williamson return -EINVAL; 44630656177SAlex Williamson #endif 44730656177SAlex Williamson 4488bccc5b8SYishai Hadas ret = vfio_pci_core_setup_barmap(vdev, bar); 44930656177SAlex Williamson if (ret) 45030656177SAlex Williamson return ret; 45130656177SAlex Williamson 45230656177SAlex Williamson mutex_lock(&vdev->ioeventfds_lock); 45330656177SAlex Williamson 45430656177SAlex Williamson list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) { 45530656177SAlex Williamson if (ioeventfd->pos == pos && ioeventfd->bar == bar && 45630656177SAlex Williamson ioeventfd->data == data && ioeventfd->count == count) { 45730656177SAlex Williamson if (fd == -1) { 45830656177SAlex Williamson vfio_virqfd_disable(&ioeventfd->virqfd); 45930656177SAlex Williamson list_del(&ioeventfd->next); 46030656177SAlex Williamson vdev->ioeventfds_nr--; 46130656177SAlex Williamson kfree(ioeventfd); 46230656177SAlex Williamson ret = 0; 46330656177SAlex Williamson } else 46430656177SAlex Williamson ret = -EEXIST; 46530656177SAlex Williamson 46630656177SAlex Williamson goto out_unlock; 46730656177SAlex Williamson } 46830656177SAlex Williamson } 46930656177SAlex Williamson 47030656177SAlex Williamson if (fd < 0) { 47130656177SAlex Williamson ret = -ENODEV; 47230656177SAlex Williamson goto out_unlock; 47330656177SAlex Williamson } 47430656177SAlex Williamson 47530656177SAlex Williamson if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) { 47630656177SAlex Williamson ret = -ENOSPC; 47730656177SAlex Williamson goto out_unlock; 47830656177SAlex Williamson } 47930656177SAlex Williamson 4800886196cSJason Gunthorpe ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL_ACCOUNT); 48130656177SAlex Williamson if (!ioeventfd) { 48230656177SAlex Williamson ret = -ENOMEM; 48330656177SAlex Williamson goto out_unlock; 48430656177SAlex Williamson } 48530656177SAlex Williamson 486bc93b9aeSAlex Williamson ioeventfd->vdev = vdev; 48730656177SAlex Williamson ioeventfd->addr = vdev->barmap[bar] + pos; 48830656177SAlex Williamson ioeventfd->data = data; 48930656177SAlex Williamson ioeventfd->pos = pos; 49030656177SAlex Williamson ioeventfd->bar = bar; 49130656177SAlex Williamson ioeventfd->count = count; 492bc93b9aeSAlex Williamson ioeventfd->test_mem = vdev->pdev->resource[bar].flags & IORESOURCE_MEM; 49330656177SAlex Williamson 49430656177SAlex Williamson ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler, 49538565c93SAlex Williamson vfio_pci_ioeventfd_thread, NULL, 49638565c93SAlex Williamson &ioeventfd->virqfd, fd); 49730656177SAlex Williamson if (ret) { 49830656177SAlex Williamson kfree(ioeventfd); 49930656177SAlex Williamson goto out_unlock; 50030656177SAlex Williamson } 50130656177SAlex Williamson 50230656177SAlex Williamson list_add(&ioeventfd->next, &vdev->ioeventfds_list); 50330656177SAlex Williamson vdev->ioeventfds_nr++; 50430656177SAlex Williamson 50530656177SAlex Williamson out_unlock: 50630656177SAlex Williamson mutex_unlock(&vdev->ioeventfds_lock); 50730656177SAlex Williamson 50830656177SAlex Williamson return ret; 50930656177SAlex Williamson } 510