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