xref: /linux/drivers/vfio/pci/vfio_pci_rdwr.c (revision 8bccc5b80678c69f7729ce4cd232c0aa98fa6277)
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) \
4153647510SMax Gurtovoy static int vfio_pci_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;							\
58bc93b9aeSAlex Williamson }
59bc93b9aeSAlex Williamson 
60bc93b9aeSAlex Williamson VFIO_IOWRITE(8)
61bc93b9aeSAlex Williamson VFIO_IOWRITE(16)
62bc93b9aeSAlex Williamson VFIO_IOWRITE(32)
63bc93b9aeSAlex Williamson #ifdef iowrite64
64bc93b9aeSAlex Williamson VFIO_IOWRITE(64)
65bc93b9aeSAlex Williamson #endif
66bc93b9aeSAlex Williamson 
67bc93b9aeSAlex Williamson #define VFIO_IOREAD(size) \
6853647510SMax Gurtovoy static int vfio_pci_ioread##size(struct vfio_pci_core_device *vdev,		\
69bc93b9aeSAlex Williamson 			bool test_mem, u##size *val, void __iomem *io)	\
70bc93b9aeSAlex Williamson {									\
71bc93b9aeSAlex Williamson 	if (test_mem) {							\
72bc93b9aeSAlex Williamson 		down_read(&vdev->memory_lock);				\
73bc93b9aeSAlex Williamson 		if (!__vfio_pci_memory_enabled(vdev)) {			\
74bc93b9aeSAlex Williamson 			up_read(&vdev->memory_lock);			\
75bc93b9aeSAlex Williamson 			return -EIO;					\
76bc93b9aeSAlex Williamson 		}							\
77bc93b9aeSAlex Williamson 	}								\
78bc93b9aeSAlex Williamson 									\
79bc93b9aeSAlex Williamson 	*val = vfio_ioread##size(io);					\
80bc93b9aeSAlex Williamson 									\
81bc93b9aeSAlex Williamson 	if (test_mem)							\
82bc93b9aeSAlex Williamson 		up_read(&vdev->memory_lock);				\
83bc93b9aeSAlex Williamson 									\
84bc93b9aeSAlex Williamson 	return 0;							\
85bc93b9aeSAlex Williamson }
86bc93b9aeSAlex Williamson 
87bc93b9aeSAlex Williamson VFIO_IOREAD(8)
88bc93b9aeSAlex Williamson VFIO_IOREAD(16)
89bc93b9aeSAlex Williamson VFIO_IOREAD(32)
90bc93b9aeSAlex Williamson 
9189e1f7d4SAlex Williamson /*
92906ee99dSAlex Williamson  * Read or write from an __iomem region (MMIO or I/O port) with an excluded
93906ee99dSAlex Williamson  * range which is inaccessible.  The excluded range drops writes and fills
94906ee99dSAlex Williamson  * reads with -1.  This is intended for handling MSI-X vector tables and
95906ee99dSAlex Williamson  * leftover space for ROM BARs.
9689e1f7d4SAlex Williamson  */
9753647510SMax Gurtovoy static ssize_t do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
98bc93b9aeSAlex Williamson 			void __iomem *io, char __user *buf,
99906ee99dSAlex Williamson 			loff_t off, size_t count, size_t x_start,
100906ee99dSAlex Williamson 			size_t x_end, bool iswrite)
10189e1f7d4SAlex Williamson {
102906ee99dSAlex Williamson 	ssize_t done = 0;
103bc93b9aeSAlex Williamson 	int ret;
10489e1f7d4SAlex Williamson 
10589e1f7d4SAlex Williamson 	while (count) {
10689e1f7d4SAlex Williamson 		size_t fillable, filled;
10789e1f7d4SAlex Williamson 
108906ee99dSAlex Williamson 		if (off < x_start)
109906ee99dSAlex Williamson 			fillable = min(count, (size_t)(x_start - off));
110906ee99dSAlex Williamson 		else if (off >= x_end)
111906ee99dSAlex Williamson 			fillable = count;
11289e1f7d4SAlex Williamson 		else
11389e1f7d4SAlex Williamson 			fillable = 0;
11489e1f7d4SAlex Williamson 
115906ee99dSAlex Williamson 		if (fillable >= 4 && !(off % 4)) {
11607fd7ef3SAlex Williamson 			u32 val;
11789e1f7d4SAlex Williamson 
11889e1f7d4SAlex Williamson 			if (iswrite) {
11989e1f7d4SAlex Williamson 				if (copy_from_user(&val, buf, 4))
120906ee99dSAlex Williamson 					return -EFAULT;
12189e1f7d4SAlex Williamson 
122bc93b9aeSAlex Williamson 				ret = vfio_pci_iowrite32(vdev, test_mem,
123bc93b9aeSAlex Williamson 							 val, io + off);
124bc93b9aeSAlex Williamson 				if (ret)
125bc93b9aeSAlex Williamson 					return ret;
12689e1f7d4SAlex Williamson 			} else {
127bc93b9aeSAlex Williamson 				ret = vfio_pci_ioread32(vdev, test_mem,
128bc93b9aeSAlex Williamson 							&val, io + off);
129bc93b9aeSAlex Williamson 				if (ret)
130bc93b9aeSAlex Williamson 					return ret;
13189e1f7d4SAlex Williamson 
13289e1f7d4SAlex Williamson 				if (copy_to_user(buf, &val, 4))
133906ee99dSAlex Williamson 					return -EFAULT;
13489e1f7d4SAlex Williamson 			}
13589e1f7d4SAlex Williamson 
13689e1f7d4SAlex Williamson 			filled = 4;
137906ee99dSAlex Williamson 		} else if (fillable >= 2 && !(off % 2)) {
13807fd7ef3SAlex Williamson 			u16 val;
13989e1f7d4SAlex Williamson 
14089e1f7d4SAlex Williamson 			if (iswrite) {
14189e1f7d4SAlex Williamson 				if (copy_from_user(&val, buf, 2))
142906ee99dSAlex Williamson 					return -EFAULT;
14389e1f7d4SAlex Williamson 
144bc93b9aeSAlex Williamson 				ret = vfio_pci_iowrite16(vdev, test_mem,
145bc93b9aeSAlex Williamson 							 val, io + off);
146bc93b9aeSAlex Williamson 				if (ret)
147bc93b9aeSAlex Williamson 					return ret;
14889e1f7d4SAlex Williamson 			} else {
149bc93b9aeSAlex Williamson 				ret = vfio_pci_ioread16(vdev, test_mem,
150bc93b9aeSAlex Williamson 							&val, io + off);
151bc93b9aeSAlex Williamson 				if (ret)
152bc93b9aeSAlex Williamson 					return ret;
15389e1f7d4SAlex Williamson 
15489e1f7d4SAlex Williamson 				if (copy_to_user(buf, &val, 2))
155906ee99dSAlex Williamson 					return -EFAULT;
15689e1f7d4SAlex Williamson 			}
15789e1f7d4SAlex Williamson 
15889e1f7d4SAlex Williamson 			filled = 2;
15989e1f7d4SAlex Williamson 		} else if (fillable) {
16089e1f7d4SAlex Williamson 			u8 val;
16189e1f7d4SAlex Williamson 
16289e1f7d4SAlex Williamson 			if (iswrite) {
16389e1f7d4SAlex Williamson 				if (copy_from_user(&val, buf, 1))
164906ee99dSAlex Williamson 					return -EFAULT;
16589e1f7d4SAlex Williamson 
166bc93b9aeSAlex Williamson 				ret = vfio_pci_iowrite8(vdev, test_mem,
167bc93b9aeSAlex Williamson 							val, io + off);
168bc93b9aeSAlex Williamson 				if (ret)
169bc93b9aeSAlex Williamson 					return ret;
17089e1f7d4SAlex Williamson 			} else {
171bc93b9aeSAlex Williamson 				ret = vfio_pci_ioread8(vdev, test_mem,
172bc93b9aeSAlex Williamson 						       &val, io + off);
173bc93b9aeSAlex Williamson 				if (ret)
174bc93b9aeSAlex Williamson 					return ret;
17589e1f7d4SAlex Williamson 
17689e1f7d4SAlex Williamson 				if (copy_to_user(buf, &val, 1))
177906ee99dSAlex Williamson 					return -EFAULT;
17889e1f7d4SAlex Williamson 			}
17989e1f7d4SAlex Williamson 
18089e1f7d4SAlex Williamson 			filled = 1;
18189e1f7d4SAlex Williamson 		} else {
182906ee99dSAlex Williamson 			/* Fill reads with -1, drop writes */
183906ee99dSAlex Williamson 			filled = min(count, (size_t)(x_end - off));
18489e1f7d4SAlex Williamson 			if (!iswrite) {
185906ee99dSAlex Williamson 				u8 val = 0xFF;
18689e1f7d4SAlex Williamson 				size_t i;
18789e1f7d4SAlex Williamson 
188906ee99dSAlex Williamson 				for (i = 0; i < filled; i++)
189906ee99dSAlex Williamson 					if (copy_to_user(buf + i, &val, 1))
190906ee99dSAlex Williamson 						return -EFAULT;
19189e1f7d4SAlex Williamson 			}
19289e1f7d4SAlex Williamson 		}
19389e1f7d4SAlex Williamson 
19489e1f7d4SAlex Williamson 		count -= filled;
19589e1f7d4SAlex Williamson 		done += filled;
196906ee99dSAlex Williamson 		off += filled;
19789e1f7d4SAlex Williamson 		buf += filled;
19889e1f7d4SAlex Williamson 	}
19989e1f7d4SAlex Williamson 
200906ee99dSAlex Williamson 	return done;
201906ee99dSAlex Williamson }
202906ee99dSAlex Williamson 
203*8bccc5b8SYishai Hadas int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar)
2040d77ed35SAlex Williamson {
2050d77ed35SAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
2060d77ed35SAlex Williamson 	int ret;
2070d77ed35SAlex Williamson 	void __iomem *io;
2080d77ed35SAlex Williamson 
2090d77ed35SAlex Williamson 	if (vdev->barmap[bar])
2100d77ed35SAlex Williamson 		return 0;
2110d77ed35SAlex Williamson 
2120d77ed35SAlex Williamson 	ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
2130d77ed35SAlex Williamson 	if (ret)
2140d77ed35SAlex Williamson 		return ret;
2150d77ed35SAlex Williamson 
2160d77ed35SAlex Williamson 	io = pci_iomap(pdev, bar, 0);
2170d77ed35SAlex Williamson 	if (!io) {
2180d77ed35SAlex Williamson 		pci_release_selected_regions(pdev, 1 << bar);
2190d77ed35SAlex Williamson 		return -ENOMEM;
2200d77ed35SAlex Williamson 	}
2210d77ed35SAlex Williamson 
2220d77ed35SAlex Williamson 	vdev->barmap[bar] = io;
2230d77ed35SAlex Williamson 
2240d77ed35SAlex Williamson 	return 0;
2250d77ed35SAlex Williamson }
226*8bccc5b8SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap);
2270d77ed35SAlex Williamson 
22853647510SMax Gurtovoy ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
229906ee99dSAlex Williamson 			size_t count, loff_t *ppos, bool iswrite)
230906ee99dSAlex Williamson {
231906ee99dSAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
232906ee99dSAlex Williamson 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
233906ee99dSAlex Williamson 	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
234906ee99dSAlex Williamson 	size_t x_start = 0, x_end = 0;
235906ee99dSAlex Williamson 	resource_size_t end;
236906ee99dSAlex Williamson 	void __iomem *io;
237abafbc55SAlex Williamson 	struct resource *res = &vdev->pdev->resource[bar];
238906ee99dSAlex Williamson 	ssize_t done;
239906ee99dSAlex Williamson 
240a13b6459SAlex Williamson 	if (pci_resource_start(pdev, bar))
241906ee99dSAlex Williamson 		end = pci_resource_len(pdev, bar);
242a13b6459SAlex Williamson 	else if (bar == PCI_ROM_RESOURCE &&
243a13b6459SAlex Williamson 		 pdev->resource[bar].flags & IORESOURCE_ROM_SHADOW)
244a13b6459SAlex Williamson 		end = 0x20000;
245a13b6459SAlex Williamson 	else
246a13b6459SAlex Williamson 		return -EINVAL;
247906ee99dSAlex Williamson 
248906ee99dSAlex Williamson 	if (pos >= end)
249906ee99dSAlex Williamson 		return -EINVAL;
250906ee99dSAlex Williamson 
251906ee99dSAlex Williamson 	count = min(count, (size_t)(end - pos));
252906ee99dSAlex Williamson 
253906ee99dSAlex Williamson 	if (bar == PCI_ROM_RESOURCE) {
254906ee99dSAlex Williamson 		/*
255906ee99dSAlex Williamson 		 * The ROM can fill less space than the BAR, so we start the
256906ee99dSAlex Williamson 		 * excluded range at the end of the actual ROM.  This makes
257906ee99dSAlex Williamson 		 * filling large ROM BARs much faster.
258906ee99dSAlex Williamson 		 */
259906ee99dSAlex Williamson 		io = pci_map_rom(pdev, &x_start);
260abafbc55SAlex Williamson 		if (!io) {
261abafbc55SAlex Williamson 			done = -ENOMEM;
262abafbc55SAlex Williamson 			goto out;
263abafbc55SAlex Williamson 		}
264906ee99dSAlex Williamson 		x_end = end;
2650d77ed35SAlex Williamson 	} else {
266*8bccc5b8SYishai Hadas 		int ret = vfio_pci_core_setup_barmap(vdev, bar);
267abafbc55SAlex Williamson 		if (ret) {
268abafbc55SAlex Williamson 			done = ret;
269abafbc55SAlex Williamson 			goto out;
270abafbc55SAlex Williamson 		}
271906ee99dSAlex Williamson 
272906ee99dSAlex Williamson 		io = vdev->barmap[bar];
2730d77ed35SAlex Williamson 	}
274906ee99dSAlex Williamson 
275906ee99dSAlex Williamson 	if (bar == vdev->msix_bar) {
276906ee99dSAlex Williamson 		x_start = vdev->msix_offset;
277906ee99dSAlex Williamson 		x_end = vdev->msix_offset + vdev->msix_size;
278906ee99dSAlex Williamson 	}
279906ee99dSAlex Williamson 
280bc93b9aeSAlex Williamson 	done = do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos,
281bc93b9aeSAlex Williamson 			count, x_start, x_end, iswrite);
282906ee99dSAlex Williamson 
283906ee99dSAlex Williamson 	if (done >= 0)
28489e1f7d4SAlex Williamson 		*ppos += done;
28589e1f7d4SAlex Williamson 
28689e1f7d4SAlex Williamson 	if (bar == PCI_ROM_RESOURCE)
28789e1f7d4SAlex Williamson 		pci_unmap_rom(pdev, io);
288abafbc55SAlex Williamson out:
289906ee99dSAlex Williamson 	return done;
29089e1f7d4SAlex Williamson }
29184237a82SAlex Williamson 
2926e031ec0SAlex Williamson #ifdef CONFIG_VFIO_PCI_VGA
29353647510SMax Gurtovoy ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
29484237a82SAlex Williamson 			       size_t count, loff_t *ppos, bool iswrite)
29584237a82SAlex Williamson {
29684237a82SAlex Williamson 	int ret;
29784237a82SAlex Williamson 	loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK;
29884237a82SAlex Williamson 	void __iomem *iomem = NULL;
29984237a82SAlex Williamson 	unsigned int rsrc;
30084237a82SAlex Williamson 	bool is_ioport;
30184237a82SAlex Williamson 	ssize_t done;
30284237a82SAlex Williamson 
30384237a82SAlex Williamson 	if (!vdev->has_vga)
30484237a82SAlex Williamson 		return -EINVAL;
30584237a82SAlex Williamson 
30645e86971SArnd Bergmann 	if (pos > 0xbfffful)
30745e86971SArnd Bergmann 		return -EINVAL;
30845e86971SArnd Bergmann 
30945e86971SArnd Bergmann 	switch ((u32)pos) {
31084237a82SAlex Williamson 	case 0xa0000 ... 0xbffff:
31184237a82SAlex Williamson 		count = min(count, (size_t)(0xc0000 - pos));
3124bdc0d67SChristoph Hellwig 		iomem = ioremap(0xa0000, 0xbffff - 0xa0000 + 1);
31384237a82SAlex Williamson 		off = pos - 0xa0000;
31484237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_MEM;
31584237a82SAlex Williamson 		is_ioport = false;
31684237a82SAlex Williamson 		break;
31784237a82SAlex Williamson 	case 0x3b0 ... 0x3bb:
31884237a82SAlex Williamson 		count = min(count, (size_t)(0x3bc - pos));
31984237a82SAlex Williamson 		iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1);
32084237a82SAlex Williamson 		off = pos - 0x3b0;
32184237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_IO;
32284237a82SAlex Williamson 		is_ioport = true;
32384237a82SAlex Williamson 		break;
32484237a82SAlex Williamson 	case 0x3c0 ... 0x3df:
32584237a82SAlex Williamson 		count = min(count, (size_t)(0x3e0 - pos));
32684237a82SAlex Williamson 		iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1);
32784237a82SAlex Williamson 		off = pos - 0x3c0;
32884237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_IO;
32984237a82SAlex Williamson 		is_ioport = true;
33084237a82SAlex Williamson 		break;
33184237a82SAlex Williamson 	default:
33284237a82SAlex Williamson 		return -EINVAL;
33384237a82SAlex Williamson 	}
33484237a82SAlex Williamson 
33584237a82SAlex Williamson 	if (!iomem)
33684237a82SAlex Williamson 		return -ENOMEM;
33784237a82SAlex Williamson 
33884237a82SAlex Williamson 	ret = vga_get_interruptible(vdev->pdev, rsrc);
33984237a82SAlex Williamson 	if (ret) {
34084237a82SAlex Williamson 		is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
34184237a82SAlex Williamson 		return ret;
34284237a82SAlex Williamson 	}
34384237a82SAlex Williamson 
344bc93b9aeSAlex Williamson 	/*
345bc93b9aeSAlex Williamson 	 * VGA MMIO is a legacy, non-BAR resource that hopefully allows
346bc93b9aeSAlex Williamson 	 * probing, so we don't currently worry about access in relation
347bc93b9aeSAlex Williamson 	 * to the memory enable bit in the command register.
348bc93b9aeSAlex Williamson 	 */
349bc93b9aeSAlex Williamson 	done = do_io_rw(vdev, false, iomem, buf, off, count, 0, 0, iswrite);
35084237a82SAlex Williamson 
35184237a82SAlex Williamson 	vga_put(vdev->pdev, rsrc);
35284237a82SAlex Williamson 
35384237a82SAlex Williamson 	is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
35484237a82SAlex Williamson 
35584237a82SAlex Williamson 	if (done >= 0)
35684237a82SAlex Williamson 		*ppos += done;
35784237a82SAlex Williamson 
35884237a82SAlex Williamson 	return done;
35984237a82SAlex Williamson }
3606e031ec0SAlex Williamson #endif
36130656177SAlex Williamson 
36238565c93SAlex Williamson static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd,
36338565c93SAlex Williamson 					bool test_mem)
36430656177SAlex Williamson {
36530656177SAlex Williamson 	switch (ioeventfd->count) {
36630656177SAlex Williamson 	case 1:
36738565c93SAlex Williamson 		vfio_pci_iowrite8(ioeventfd->vdev, test_mem,
368bc93b9aeSAlex Williamson 				  ioeventfd->data, ioeventfd->addr);
36930656177SAlex Williamson 		break;
37030656177SAlex Williamson 	case 2:
37138565c93SAlex Williamson 		vfio_pci_iowrite16(ioeventfd->vdev, test_mem,
372bc93b9aeSAlex Williamson 				   ioeventfd->data, ioeventfd->addr);
37330656177SAlex Williamson 		break;
37430656177SAlex Williamson 	case 4:
37538565c93SAlex Williamson 		vfio_pci_iowrite32(ioeventfd->vdev, test_mem,
376bc93b9aeSAlex Williamson 				   ioeventfd->data, ioeventfd->addr);
37730656177SAlex Williamson 		break;
37830656177SAlex Williamson #ifdef iowrite64
37930656177SAlex Williamson 	case 8:
38038565c93SAlex Williamson 		vfio_pci_iowrite64(ioeventfd->vdev, test_mem,
381bc93b9aeSAlex Williamson 				   ioeventfd->data, ioeventfd->addr);
38230656177SAlex Williamson 		break;
38330656177SAlex Williamson #endif
38430656177SAlex Williamson 	}
38538565c93SAlex Williamson }
38638565c93SAlex Williamson 
38738565c93SAlex Williamson static int vfio_pci_ioeventfd_handler(void *opaque, void *unused)
38838565c93SAlex Williamson {
38938565c93SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd = opaque;
39053647510SMax Gurtovoy 	struct vfio_pci_core_device *vdev = ioeventfd->vdev;
39138565c93SAlex Williamson 
39238565c93SAlex Williamson 	if (ioeventfd->test_mem) {
39338565c93SAlex Williamson 		if (!down_read_trylock(&vdev->memory_lock))
39438565c93SAlex Williamson 			return 1; /* Lock contended, use thread */
39538565c93SAlex Williamson 		if (!__vfio_pci_memory_enabled(vdev)) {
39638565c93SAlex Williamson 			up_read(&vdev->memory_lock);
39738565c93SAlex Williamson 			return 0;
39838565c93SAlex Williamson 		}
39938565c93SAlex Williamson 	}
40038565c93SAlex Williamson 
40138565c93SAlex Williamson 	vfio_pci_ioeventfd_do_write(ioeventfd, false);
40238565c93SAlex Williamson 
40338565c93SAlex Williamson 	if (ioeventfd->test_mem)
40438565c93SAlex Williamson 		up_read(&vdev->memory_lock);
40530656177SAlex Williamson 
40630656177SAlex Williamson 	return 0;
40730656177SAlex Williamson }
40830656177SAlex Williamson 
40938565c93SAlex Williamson static void vfio_pci_ioeventfd_thread(void *opaque, void *unused)
41038565c93SAlex Williamson {
41138565c93SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd = opaque;
41238565c93SAlex Williamson 
41338565c93SAlex Williamson 	vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem);
41438565c93SAlex Williamson }
41538565c93SAlex Williamson 
41616f4cbd9SJason Gunthorpe int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset,
41730656177SAlex Williamson 		       uint64_t data, int count, int fd)
41830656177SAlex Williamson {
41930656177SAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
42030656177SAlex Williamson 	loff_t pos = offset & VFIO_PCI_OFFSET_MASK;
42130656177SAlex Williamson 	int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset);
42230656177SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd;
42330656177SAlex Williamson 
42430656177SAlex Williamson 	/* Only support ioeventfds into BARs */
42530656177SAlex Williamson 	if (bar > VFIO_PCI_BAR5_REGION_INDEX)
42630656177SAlex Williamson 		return -EINVAL;
42730656177SAlex Williamson 
42830656177SAlex Williamson 	if (pos + count > pci_resource_len(pdev, bar))
42930656177SAlex Williamson 		return -EINVAL;
43030656177SAlex Williamson 
43130656177SAlex Williamson 	/* Disallow ioeventfds working around MSI-X table writes */
43230656177SAlex Williamson 	if (bar == vdev->msix_bar &&
43330656177SAlex Williamson 	    !(pos + count <= vdev->msix_offset ||
43430656177SAlex Williamson 	      pos >= vdev->msix_offset + vdev->msix_size))
43530656177SAlex Williamson 		return -EINVAL;
43630656177SAlex Williamson 
43730656177SAlex Williamson #ifndef iowrite64
43830656177SAlex Williamson 	if (count == 8)
43930656177SAlex Williamson 		return -EINVAL;
44030656177SAlex Williamson #endif
44130656177SAlex Williamson 
442*8bccc5b8SYishai Hadas 	ret = vfio_pci_core_setup_barmap(vdev, bar);
44330656177SAlex Williamson 	if (ret)
44430656177SAlex Williamson 		return ret;
44530656177SAlex Williamson 
44630656177SAlex Williamson 	mutex_lock(&vdev->ioeventfds_lock);
44730656177SAlex Williamson 
44830656177SAlex Williamson 	list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) {
44930656177SAlex Williamson 		if (ioeventfd->pos == pos && ioeventfd->bar == bar &&
45030656177SAlex Williamson 		    ioeventfd->data == data && ioeventfd->count == count) {
45130656177SAlex Williamson 			if (fd == -1) {
45230656177SAlex Williamson 				vfio_virqfd_disable(&ioeventfd->virqfd);
45330656177SAlex Williamson 				list_del(&ioeventfd->next);
45430656177SAlex Williamson 				vdev->ioeventfds_nr--;
45530656177SAlex Williamson 				kfree(ioeventfd);
45630656177SAlex Williamson 				ret = 0;
45730656177SAlex Williamson 			} else
45830656177SAlex Williamson 				ret = -EEXIST;
45930656177SAlex Williamson 
46030656177SAlex Williamson 			goto out_unlock;
46130656177SAlex Williamson 		}
46230656177SAlex Williamson 	}
46330656177SAlex Williamson 
46430656177SAlex Williamson 	if (fd < 0) {
46530656177SAlex Williamson 		ret = -ENODEV;
46630656177SAlex Williamson 		goto out_unlock;
46730656177SAlex Williamson 	}
46830656177SAlex Williamson 
46930656177SAlex Williamson 	if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) {
47030656177SAlex Williamson 		ret = -ENOSPC;
47130656177SAlex Williamson 		goto out_unlock;
47230656177SAlex Williamson 	}
47330656177SAlex Williamson 
4740886196cSJason Gunthorpe 	ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL_ACCOUNT);
47530656177SAlex Williamson 	if (!ioeventfd) {
47630656177SAlex Williamson 		ret = -ENOMEM;
47730656177SAlex Williamson 		goto out_unlock;
47830656177SAlex Williamson 	}
47930656177SAlex Williamson 
480bc93b9aeSAlex Williamson 	ioeventfd->vdev = vdev;
48130656177SAlex Williamson 	ioeventfd->addr = vdev->barmap[bar] + pos;
48230656177SAlex Williamson 	ioeventfd->data = data;
48330656177SAlex Williamson 	ioeventfd->pos = pos;
48430656177SAlex Williamson 	ioeventfd->bar = bar;
48530656177SAlex Williamson 	ioeventfd->count = count;
486bc93b9aeSAlex Williamson 	ioeventfd->test_mem = vdev->pdev->resource[bar].flags & IORESOURCE_MEM;
48730656177SAlex Williamson 
48830656177SAlex Williamson 	ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler,
48938565c93SAlex Williamson 				 vfio_pci_ioeventfd_thread, NULL,
49038565c93SAlex Williamson 				 &ioeventfd->virqfd, fd);
49130656177SAlex Williamson 	if (ret) {
49230656177SAlex Williamson 		kfree(ioeventfd);
49330656177SAlex Williamson 		goto out_unlock;
49430656177SAlex Williamson 	}
49530656177SAlex Williamson 
49630656177SAlex Williamson 	list_add(&ioeventfd->next, &vdev->ioeventfds_list);
49730656177SAlex Williamson 	vdev->ioeventfds_nr++;
49830656177SAlex Williamson 
49930656177SAlex Williamson out_unlock:
50030656177SAlex Williamson 	mutex_unlock(&vdev->ioeventfds_lock);
50130656177SAlex Williamson 
50230656177SAlex Williamson 	return ret;
50330656177SAlex Williamson }
504