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