162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VFIO PCI I/O Port & MMIO access 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 662306a36Sopenharmony_ci * Author: Alex Williamson <alex.williamson@redhat.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Derived from original vfio: 962306a36Sopenharmony_ci * Copyright 2010 Cisco Systems, Inc. All rights reserved. 1062306a36Sopenharmony_ci * Author: Tom Lyon, pugs@cisco.com 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/uaccess.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/vfio.h> 1862306a36Sopenharmony_ci#include <linux/vgaarb.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "vfio_pci_priv.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 2362306a36Sopenharmony_ci#define vfio_ioread64 ioread64 2462306a36Sopenharmony_ci#define vfio_iowrite64 iowrite64 2562306a36Sopenharmony_ci#define vfio_ioread32 ioread32 2662306a36Sopenharmony_ci#define vfio_iowrite32 iowrite32 2762306a36Sopenharmony_ci#define vfio_ioread16 ioread16 2862306a36Sopenharmony_ci#define vfio_iowrite16 iowrite16 2962306a36Sopenharmony_ci#else 3062306a36Sopenharmony_ci#define vfio_ioread64 ioread64be 3162306a36Sopenharmony_ci#define vfio_iowrite64 iowrite64be 3262306a36Sopenharmony_ci#define vfio_ioread32 ioread32be 3362306a36Sopenharmony_ci#define vfio_iowrite32 iowrite32be 3462306a36Sopenharmony_ci#define vfio_ioread16 ioread16be 3562306a36Sopenharmony_ci#define vfio_iowrite16 iowrite16be 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci#define vfio_ioread8 ioread8 3862306a36Sopenharmony_ci#define vfio_iowrite8 iowrite8 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define VFIO_IOWRITE(size) \ 4162306a36Sopenharmony_cistatic int vfio_pci_iowrite##size(struct vfio_pci_core_device *vdev, \ 4262306a36Sopenharmony_ci bool test_mem, u##size val, void __iomem *io) \ 4362306a36Sopenharmony_ci{ \ 4462306a36Sopenharmony_ci if (test_mem) { \ 4562306a36Sopenharmony_ci down_read(&vdev->memory_lock); \ 4662306a36Sopenharmony_ci if (!__vfio_pci_memory_enabled(vdev)) { \ 4762306a36Sopenharmony_ci up_read(&vdev->memory_lock); \ 4862306a36Sopenharmony_ci return -EIO; \ 4962306a36Sopenharmony_ci } \ 5062306a36Sopenharmony_ci } \ 5162306a36Sopenharmony_ci \ 5262306a36Sopenharmony_ci vfio_iowrite##size(val, io); \ 5362306a36Sopenharmony_ci \ 5462306a36Sopenharmony_ci if (test_mem) \ 5562306a36Sopenharmony_ci up_read(&vdev->memory_lock); \ 5662306a36Sopenharmony_ci \ 5762306a36Sopenharmony_ci return 0; \ 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciVFIO_IOWRITE(8) 6162306a36Sopenharmony_ciVFIO_IOWRITE(16) 6262306a36Sopenharmony_ciVFIO_IOWRITE(32) 6362306a36Sopenharmony_ci#ifdef iowrite64 6462306a36Sopenharmony_ciVFIO_IOWRITE(64) 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define VFIO_IOREAD(size) \ 6862306a36Sopenharmony_cistatic int vfio_pci_ioread##size(struct vfio_pci_core_device *vdev, \ 6962306a36Sopenharmony_ci bool test_mem, u##size *val, void __iomem *io) \ 7062306a36Sopenharmony_ci{ \ 7162306a36Sopenharmony_ci if (test_mem) { \ 7262306a36Sopenharmony_ci down_read(&vdev->memory_lock); \ 7362306a36Sopenharmony_ci if (!__vfio_pci_memory_enabled(vdev)) { \ 7462306a36Sopenharmony_ci up_read(&vdev->memory_lock); \ 7562306a36Sopenharmony_ci return -EIO; \ 7662306a36Sopenharmony_ci } \ 7762306a36Sopenharmony_ci } \ 7862306a36Sopenharmony_ci \ 7962306a36Sopenharmony_ci *val = vfio_ioread##size(io); \ 8062306a36Sopenharmony_ci \ 8162306a36Sopenharmony_ci if (test_mem) \ 8262306a36Sopenharmony_ci up_read(&vdev->memory_lock); \ 8362306a36Sopenharmony_ci \ 8462306a36Sopenharmony_ci return 0; \ 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciVFIO_IOREAD(8) 8862306a36Sopenharmony_ciVFIO_IOREAD(16) 8962306a36Sopenharmony_ciVFIO_IOREAD(32) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * Read or write from an __iomem region (MMIO or I/O port) with an excluded 9362306a36Sopenharmony_ci * range which is inaccessible. The excluded range drops writes and fills 9462306a36Sopenharmony_ci * reads with -1. This is intended for handling MSI-X vector tables and 9562306a36Sopenharmony_ci * leftover space for ROM BARs. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic ssize_t do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, 9862306a36Sopenharmony_ci void __iomem *io, char __user *buf, 9962306a36Sopenharmony_ci loff_t off, size_t count, size_t x_start, 10062306a36Sopenharmony_ci size_t x_end, bool iswrite) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci ssize_t done = 0; 10362306a36Sopenharmony_ci int ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci while (count) { 10662306a36Sopenharmony_ci size_t fillable, filled; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (off < x_start) 10962306a36Sopenharmony_ci fillable = min(count, (size_t)(x_start - off)); 11062306a36Sopenharmony_ci else if (off >= x_end) 11162306a36Sopenharmony_ci fillable = count; 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci fillable = 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (fillable >= 4 && !(off % 4)) { 11662306a36Sopenharmony_ci u32 val; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (iswrite) { 11962306a36Sopenharmony_ci if (copy_from_user(&val, buf, 4)) 12062306a36Sopenharmony_ci return -EFAULT; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ret = vfio_pci_iowrite32(vdev, test_mem, 12362306a36Sopenharmony_ci val, io + off); 12462306a36Sopenharmony_ci if (ret) 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci ret = vfio_pci_ioread32(vdev, test_mem, 12862306a36Sopenharmony_ci &val, io + off); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (copy_to_user(buf, &val, 4)) 13362306a36Sopenharmony_ci return -EFAULT; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci filled = 4; 13762306a36Sopenharmony_ci } else if (fillable >= 2 && !(off % 2)) { 13862306a36Sopenharmony_ci u16 val; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (iswrite) { 14162306a36Sopenharmony_ci if (copy_from_user(&val, buf, 2)) 14262306a36Sopenharmony_ci return -EFAULT; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ret = vfio_pci_iowrite16(vdev, test_mem, 14562306a36Sopenharmony_ci val, io + off); 14662306a36Sopenharmony_ci if (ret) 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci } else { 14962306a36Sopenharmony_ci ret = vfio_pci_ioread16(vdev, test_mem, 15062306a36Sopenharmony_ci &val, io + off); 15162306a36Sopenharmony_ci if (ret) 15262306a36Sopenharmony_ci return ret; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (copy_to_user(buf, &val, 2)) 15562306a36Sopenharmony_ci return -EFAULT; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci filled = 2; 15962306a36Sopenharmony_ci } else if (fillable) { 16062306a36Sopenharmony_ci u8 val; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (iswrite) { 16362306a36Sopenharmony_ci if (copy_from_user(&val, buf, 1)) 16462306a36Sopenharmony_ci return -EFAULT; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci ret = vfio_pci_iowrite8(vdev, test_mem, 16762306a36Sopenharmony_ci val, io + off); 16862306a36Sopenharmony_ci if (ret) 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci ret = vfio_pci_ioread8(vdev, test_mem, 17262306a36Sopenharmony_ci &val, io + off); 17362306a36Sopenharmony_ci if (ret) 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (copy_to_user(buf, &val, 1)) 17762306a36Sopenharmony_ci return -EFAULT; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci filled = 1; 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci /* Fill reads with -1, drop writes */ 18362306a36Sopenharmony_ci filled = min(count, (size_t)(x_end - off)); 18462306a36Sopenharmony_ci if (!iswrite) { 18562306a36Sopenharmony_ci u8 val = 0xFF; 18662306a36Sopenharmony_ci size_t i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (i = 0; i < filled; i++) 18962306a36Sopenharmony_ci if (copy_to_user(buf + i, &val, 1)) 19062306a36Sopenharmony_ci return -EFAULT; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci count -= filled; 19562306a36Sopenharmony_ci done += filled; 19662306a36Sopenharmony_ci off += filled; 19762306a36Sopenharmony_ci buf += filled; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return done; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int vfio_pci_setup_barmap(struct vfio_pci_core_device *vdev, int bar) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 20662306a36Sopenharmony_ci int ret; 20762306a36Sopenharmony_ci void __iomem *io; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (vdev->barmap[bar]) 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); 21362306a36Sopenharmony_ci if (ret) 21462306a36Sopenharmony_ci return ret; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci io = pci_iomap(pdev, bar, 0); 21762306a36Sopenharmony_ci if (!io) { 21862306a36Sopenharmony_ci pci_release_selected_regions(pdev, 1 << bar); 21962306a36Sopenharmony_ci return -ENOMEM; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci vdev->barmap[bar] = io; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cissize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf, 22862306a36Sopenharmony_ci size_t count, loff_t *ppos, bool iswrite) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 23162306a36Sopenharmony_ci loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 23262306a36Sopenharmony_ci int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 23362306a36Sopenharmony_ci size_t x_start = 0, x_end = 0; 23462306a36Sopenharmony_ci resource_size_t end; 23562306a36Sopenharmony_ci void __iomem *io; 23662306a36Sopenharmony_ci struct resource *res = &vdev->pdev->resource[bar]; 23762306a36Sopenharmony_ci ssize_t done; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (pci_resource_start(pdev, bar)) 24062306a36Sopenharmony_ci end = pci_resource_len(pdev, bar); 24162306a36Sopenharmony_ci else if (bar == PCI_ROM_RESOURCE && 24262306a36Sopenharmony_ci pdev->resource[bar].flags & IORESOURCE_ROM_SHADOW) 24362306a36Sopenharmony_ci end = 0x20000; 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci return -EINVAL; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (pos >= end) 24862306a36Sopenharmony_ci return -EINVAL; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci count = min(count, (size_t)(end - pos)); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (bar == PCI_ROM_RESOURCE) { 25362306a36Sopenharmony_ci /* 25462306a36Sopenharmony_ci * The ROM can fill less space than the BAR, so we start the 25562306a36Sopenharmony_ci * excluded range at the end of the actual ROM. This makes 25662306a36Sopenharmony_ci * filling large ROM BARs much faster. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci io = pci_map_rom(pdev, &x_start); 25962306a36Sopenharmony_ci if (!io) { 26062306a36Sopenharmony_ci done = -ENOMEM; 26162306a36Sopenharmony_ci goto out; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci x_end = end; 26462306a36Sopenharmony_ci } else { 26562306a36Sopenharmony_ci int ret = vfio_pci_setup_barmap(vdev, bar); 26662306a36Sopenharmony_ci if (ret) { 26762306a36Sopenharmony_ci done = ret; 26862306a36Sopenharmony_ci goto out; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci io = vdev->barmap[bar]; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (bar == vdev->msix_bar) { 27562306a36Sopenharmony_ci x_start = vdev->msix_offset; 27662306a36Sopenharmony_ci x_end = vdev->msix_offset + vdev->msix_size; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci done = do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos, 28062306a36Sopenharmony_ci count, x_start, x_end, iswrite); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (done >= 0) 28362306a36Sopenharmony_ci *ppos += done; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (bar == PCI_ROM_RESOURCE) 28662306a36Sopenharmony_ci pci_unmap_rom(pdev, io); 28762306a36Sopenharmony_ciout: 28862306a36Sopenharmony_ci return done; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#ifdef CONFIG_VFIO_PCI_VGA 29262306a36Sopenharmony_cissize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf, 29362306a36Sopenharmony_ci size_t count, loff_t *ppos, bool iswrite) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int ret; 29662306a36Sopenharmony_ci loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK; 29762306a36Sopenharmony_ci void __iomem *iomem = NULL; 29862306a36Sopenharmony_ci unsigned int rsrc; 29962306a36Sopenharmony_ci bool is_ioport; 30062306a36Sopenharmony_ci ssize_t done; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!vdev->has_vga) 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (pos > 0xbfffful) 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci switch ((u32)pos) { 30962306a36Sopenharmony_ci case 0xa0000 ... 0xbffff: 31062306a36Sopenharmony_ci count = min(count, (size_t)(0xc0000 - pos)); 31162306a36Sopenharmony_ci iomem = ioremap(0xa0000, 0xbffff - 0xa0000 + 1); 31262306a36Sopenharmony_ci off = pos - 0xa0000; 31362306a36Sopenharmony_ci rsrc = VGA_RSRC_LEGACY_MEM; 31462306a36Sopenharmony_ci is_ioport = false; 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci case 0x3b0 ... 0x3bb: 31762306a36Sopenharmony_ci count = min(count, (size_t)(0x3bc - pos)); 31862306a36Sopenharmony_ci iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1); 31962306a36Sopenharmony_ci off = pos - 0x3b0; 32062306a36Sopenharmony_ci rsrc = VGA_RSRC_LEGACY_IO; 32162306a36Sopenharmony_ci is_ioport = true; 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci case 0x3c0 ... 0x3df: 32462306a36Sopenharmony_ci count = min(count, (size_t)(0x3e0 - pos)); 32562306a36Sopenharmony_ci iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1); 32662306a36Sopenharmony_ci off = pos - 0x3c0; 32762306a36Sopenharmony_ci rsrc = VGA_RSRC_LEGACY_IO; 32862306a36Sopenharmony_ci is_ioport = true; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci default: 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!iomem) 33562306a36Sopenharmony_ci return -ENOMEM; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci ret = vga_get_interruptible(vdev->pdev, rsrc); 33862306a36Sopenharmony_ci if (ret) { 33962306a36Sopenharmony_ci is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 34062306a36Sopenharmony_ci return ret; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* 34462306a36Sopenharmony_ci * VGA MMIO is a legacy, non-BAR resource that hopefully allows 34562306a36Sopenharmony_ci * probing, so we don't currently worry about access in relation 34662306a36Sopenharmony_ci * to the memory enable bit in the command register. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci done = do_io_rw(vdev, false, iomem, buf, off, count, 0, 0, iswrite); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci vga_put(vdev->pdev, rsrc); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (done >= 0) 35562306a36Sopenharmony_ci *ppos += done; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return done; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci#endif 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd, 36262306a36Sopenharmony_ci bool test_mem) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci switch (ioeventfd->count) { 36562306a36Sopenharmony_ci case 1: 36662306a36Sopenharmony_ci vfio_pci_iowrite8(ioeventfd->vdev, test_mem, 36762306a36Sopenharmony_ci ioeventfd->data, ioeventfd->addr); 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case 2: 37062306a36Sopenharmony_ci vfio_pci_iowrite16(ioeventfd->vdev, test_mem, 37162306a36Sopenharmony_ci ioeventfd->data, ioeventfd->addr); 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci case 4: 37462306a36Sopenharmony_ci vfio_pci_iowrite32(ioeventfd->vdev, test_mem, 37562306a36Sopenharmony_ci ioeventfd->data, ioeventfd->addr); 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci#ifdef iowrite64 37862306a36Sopenharmony_ci case 8: 37962306a36Sopenharmony_ci vfio_pci_iowrite64(ioeventfd->vdev, test_mem, 38062306a36Sopenharmony_ci ioeventfd->data, ioeventfd->addr); 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci#endif 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int vfio_pci_ioeventfd_handler(void *opaque, void *unused) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct vfio_pci_ioeventfd *ioeventfd = opaque; 38962306a36Sopenharmony_ci struct vfio_pci_core_device *vdev = ioeventfd->vdev; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (ioeventfd->test_mem) { 39262306a36Sopenharmony_ci if (!down_read_trylock(&vdev->memory_lock)) 39362306a36Sopenharmony_ci return 1; /* Lock contended, use thread */ 39462306a36Sopenharmony_ci if (!__vfio_pci_memory_enabled(vdev)) { 39562306a36Sopenharmony_ci up_read(&vdev->memory_lock); 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci vfio_pci_ioeventfd_do_write(ioeventfd, false); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (ioeventfd->test_mem) 40362306a36Sopenharmony_ci up_read(&vdev->memory_lock); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic void vfio_pci_ioeventfd_thread(void *opaque, void *unused) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct vfio_pci_ioeventfd *ioeventfd = opaque; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciint vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset, 41662306a36Sopenharmony_ci uint64_t data, int count, int fd) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct pci_dev *pdev = vdev->pdev; 41962306a36Sopenharmony_ci loff_t pos = offset & VFIO_PCI_OFFSET_MASK; 42062306a36Sopenharmony_ci int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset); 42162306a36Sopenharmony_ci struct vfio_pci_ioeventfd *ioeventfd; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Only support ioeventfds into BARs */ 42462306a36Sopenharmony_ci if (bar > VFIO_PCI_BAR5_REGION_INDEX) 42562306a36Sopenharmony_ci return -EINVAL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (pos + count > pci_resource_len(pdev, bar)) 42862306a36Sopenharmony_ci return -EINVAL; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Disallow ioeventfds working around MSI-X table writes */ 43162306a36Sopenharmony_ci if (bar == vdev->msix_bar && 43262306a36Sopenharmony_ci !(pos + count <= vdev->msix_offset || 43362306a36Sopenharmony_ci pos >= vdev->msix_offset + vdev->msix_size)) 43462306a36Sopenharmony_ci return -EINVAL; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci#ifndef iowrite64 43762306a36Sopenharmony_ci if (count == 8) 43862306a36Sopenharmony_ci return -EINVAL; 43962306a36Sopenharmony_ci#endif 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci ret = vfio_pci_setup_barmap(vdev, bar); 44262306a36Sopenharmony_ci if (ret) 44362306a36Sopenharmony_ci return ret; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci mutex_lock(&vdev->ioeventfds_lock); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) { 44862306a36Sopenharmony_ci if (ioeventfd->pos == pos && ioeventfd->bar == bar && 44962306a36Sopenharmony_ci ioeventfd->data == data && ioeventfd->count == count) { 45062306a36Sopenharmony_ci if (fd == -1) { 45162306a36Sopenharmony_ci vfio_virqfd_disable(&ioeventfd->virqfd); 45262306a36Sopenharmony_ci list_del(&ioeventfd->next); 45362306a36Sopenharmony_ci vdev->ioeventfds_nr--; 45462306a36Sopenharmony_ci kfree(ioeventfd); 45562306a36Sopenharmony_ci ret = 0; 45662306a36Sopenharmony_ci } else 45762306a36Sopenharmony_ci ret = -EEXIST; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci goto out_unlock; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (fd < 0) { 46462306a36Sopenharmony_ci ret = -ENODEV; 46562306a36Sopenharmony_ci goto out_unlock; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) { 46962306a36Sopenharmony_ci ret = -ENOSPC; 47062306a36Sopenharmony_ci goto out_unlock; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL_ACCOUNT); 47462306a36Sopenharmony_ci if (!ioeventfd) { 47562306a36Sopenharmony_ci ret = -ENOMEM; 47662306a36Sopenharmony_ci goto out_unlock; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci ioeventfd->vdev = vdev; 48062306a36Sopenharmony_ci ioeventfd->addr = vdev->barmap[bar] + pos; 48162306a36Sopenharmony_ci ioeventfd->data = data; 48262306a36Sopenharmony_ci ioeventfd->pos = pos; 48362306a36Sopenharmony_ci ioeventfd->bar = bar; 48462306a36Sopenharmony_ci ioeventfd->count = count; 48562306a36Sopenharmony_ci ioeventfd->test_mem = vdev->pdev->resource[bar].flags & IORESOURCE_MEM; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler, 48862306a36Sopenharmony_ci vfio_pci_ioeventfd_thread, NULL, 48962306a36Sopenharmony_ci &ioeventfd->virqfd, fd); 49062306a36Sopenharmony_ci if (ret) { 49162306a36Sopenharmony_ci kfree(ioeventfd); 49262306a36Sopenharmony_ci goto out_unlock; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci list_add(&ioeventfd->next, &vdev->ioeventfds_list); 49662306a36Sopenharmony_ci vdev->ioeventfds_nr++; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ciout_unlock: 49962306a36Sopenharmony_ci mutex_unlock(&vdev->ioeventfds_lock); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci} 503