xref: /linux/drivers/vfio/pci/vfio_pci_rdwr.c (revision 186bfe44ea41fb38c7a69becd7078724d28cd03c)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
289e1f7d4SAlex Williamson /*
389e1f7d4SAlex Williamson  * VFIO PCI I/O Port & MMIO access
489e1f7d4SAlex Williamson  *
589e1f7d4SAlex Williamson  * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
689e1f7d4SAlex Williamson  *     Author: Alex Williamson <alex.williamson@redhat.com>
789e1f7d4SAlex Williamson  *
889e1f7d4SAlex Williamson  * Derived from original vfio:
989e1f7d4SAlex Williamson  * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
1089e1f7d4SAlex Williamson  * Author: Tom Lyon, pugs@cisco.com
1189e1f7d4SAlex Williamson  */
1289e1f7d4SAlex Williamson 
1389e1f7d4SAlex Williamson #include <linux/fs.h>
1489e1f7d4SAlex Williamson #include <linux/pci.h>
1589e1f7d4SAlex Williamson #include <linux/uaccess.h>
1689e1f7d4SAlex Williamson #include <linux/io.h>
1730656177SAlex Williamson #include <linux/vfio.h>
1884237a82SAlex Williamson #include <linux/vgaarb.h>
1989e1f7d4SAlex Williamson 
20e34a0425SJason Gunthorpe #include "vfio_pci_priv.h"
2189e1f7d4SAlex Williamson 
2207fd7ef3SAlex Williamson #ifdef __LITTLE_ENDIAN
2307fd7ef3SAlex Williamson #define vfio_ioread64	ioread64
2407fd7ef3SAlex Williamson #define vfio_iowrite64	iowrite64
2507fd7ef3SAlex Williamson #define vfio_ioread32	ioread32
2607fd7ef3SAlex Williamson #define vfio_iowrite32	iowrite32
2707fd7ef3SAlex Williamson #define vfio_ioread16	ioread16
2807fd7ef3SAlex Williamson #define vfio_iowrite16	iowrite16
2907fd7ef3SAlex Williamson #else
3007fd7ef3SAlex Williamson #define vfio_ioread64	ioread64be
3107fd7ef3SAlex Williamson #define vfio_iowrite64	iowrite64be
3207fd7ef3SAlex Williamson #define vfio_ioread32	ioread32be
3307fd7ef3SAlex Williamson #define vfio_iowrite32	iowrite32be
3407fd7ef3SAlex Williamson #define vfio_ioread16	ioread16be
3507fd7ef3SAlex Williamson #define vfio_iowrite16	iowrite16be
3607fd7ef3SAlex Williamson #endif
3707fd7ef3SAlex Williamson #define vfio_ioread8	ioread8
3807fd7ef3SAlex Williamson #define vfio_iowrite8	iowrite8
3907fd7ef3SAlex Williamson 
40bc93b9aeSAlex Williamson #define VFIO_IOWRITE(size) \
418486ae16SYishai Hadas int vfio_pci_core_iowrite##size(struct vfio_pci_core_device *vdev,	\
42bc93b9aeSAlex Williamson 			bool test_mem, u##size val, void __iomem *io)	\
43bc93b9aeSAlex Williamson {									\
44bc93b9aeSAlex Williamson 	if (test_mem) {							\
45bc93b9aeSAlex Williamson 		down_read(&vdev->memory_lock);				\
46bc93b9aeSAlex Williamson 		if (!__vfio_pci_memory_enabled(vdev)) {			\
47bc93b9aeSAlex Williamson 			up_read(&vdev->memory_lock);			\
48bc93b9aeSAlex Williamson 			return -EIO;					\
49bc93b9aeSAlex Williamson 		}							\
50bc93b9aeSAlex Williamson 	}								\
51bc93b9aeSAlex Williamson 									\
52bc93b9aeSAlex Williamson 	vfio_iowrite##size(val, io);					\
53bc93b9aeSAlex Williamson 									\
54bc93b9aeSAlex Williamson 	if (test_mem)							\
55bc93b9aeSAlex Williamson 		up_read(&vdev->memory_lock);				\
56bc93b9aeSAlex Williamson 									\
57bc93b9aeSAlex Williamson 	return 0;							\
588486ae16SYishai Hadas }									\
598486ae16SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_iowrite##size);
60bc93b9aeSAlex Williamson 
61bc93b9aeSAlex Williamson VFIO_IOWRITE(8)
62bc93b9aeSAlex Williamson VFIO_IOWRITE(16)
63bc93b9aeSAlex Williamson VFIO_IOWRITE(32)
64bc93b9aeSAlex Williamson #ifdef iowrite64
65bc93b9aeSAlex Williamson VFIO_IOWRITE(64)
66bc93b9aeSAlex Williamson #endif
67bc93b9aeSAlex Williamson 
68bc93b9aeSAlex Williamson #define VFIO_IOREAD(size) \
698486ae16SYishai Hadas int vfio_pci_core_ioread##size(struct vfio_pci_core_device *vdev,	\
70bc93b9aeSAlex Williamson 			bool test_mem, u##size *val, void __iomem *io)	\
71bc93b9aeSAlex Williamson {									\
72bc93b9aeSAlex Williamson 	if (test_mem) {							\
73bc93b9aeSAlex Williamson 		down_read(&vdev->memory_lock);				\
74bc93b9aeSAlex Williamson 		if (!__vfio_pci_memory_enabled(vdev)) {			\
75bc93b9aeSAlex Williamson 			up_read(&vdev->memory_lock);			\
76bc93b9aeSAlex Williamson 			return -EIO;					\
77bc93b9aeSAlex Williamson 		}							\
78bc93b9aeSAlex Williamson 	}								\
79bc93b9aeSAlex Williamson 									\
80bc93b9aeSAlex Williamson 	*val = vfio_ioread##size(io);					\
81bc93b9aeSAlex Williamson 									\
82bc93b9aeSAlex Williamson 	if (test_mem)							\
83bc93b9aeSAlex Williamson 		up_read(&vdev->memory_lock);				\
84bc93b9aeSAlex Williamson 									\
85bc93b9aeSAlex Williamson 	return 0;							\
868486ae16SYishai Hadas }									\
878486ae16SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_ioread##size);
88bc93b9aeSAlex Williamson 
89bc93b9aeSAlex Williamson VFIO_IOREAD(8)
90bc93b9aeSAlex Williamson VFIO_IOREAD(16)
91bc93b9aeSAlex Williamson VFIO_IOREAD(32)
92bc93b9aeSAlex Williamson 
93*186bfe44SGerd Bayer #define VFIO_IORDWR(size)						\
94*186bfe44SGerd Bayer static int vfio_pci_iordwr##size(struct vfio_pci_core_device *vdev,\
95*186bfe44SGerd Bayer 				bool iswrite, bool test_mem,		\
96*186bfe44SGerd Bayer 				void __iomem *io, char __user *buf,	\
97*186bfe44SGerd Bayer 				loff_t off, size_t *filled)		\
98*186bfe44SGerd Bayer {									\
99*186bfe44SGerd Bayer 	u##size val;							\
100*186bfe44SGerd Bayer 	int ret;							\
101*186bfe44SGerd Bayer 									\
102*186bfe44SGerd Bayer 	if (iswrite) {							\
103*186bfe44SGerd Bayer 		if (copy_from_user(&val, buf, sizeof(val)))		\
104*186bfe44SGerd Bayer 			return -EFAULT;					\
105*186bfe44SGerd Bayer 									\
106*186bfe44SGerd Bayer 		ret = vfio_pci_core_iowrite##size(vdev, test_mem,	\
107*186bfe44SGerd Bayer 						  val, io + off);	\
108*186bfe44SGerd Bayer 		if (ret)						\
109*186bfe44SGerd Bayer 			return ret;					\
110*186bfe44SGerd Bayer 	} else {							\
111*186bfe44SGerd Bayer 		ret = vfio_pci_core_ioread##size(vdev, test_mem,	\
112*186bfe44SGerd Bayer 						 &val, io + off);	\
113*186bfe44SGerd Bayer 		if (ret)						\
114*186bfe44SGerd Bayer 			return ret;					\
115*186bfe44SGerd Bayer 									\
116*186bfe44SGerd Bayer 		if (copy_to_user(buf, &val, sizeof(val)))		\
117*186bfe44SGerd Bayer 			return -EFAULT;					\
118*186bfe44SGerd Bayer 	}								\
119*186bfe44SGerd Bayer 									\
120*186bfe44SGerd Bayer 	*filled = sizeof(val);						\
121*186bfe44SGerd Bayer 	return 0;							\
122*186bfe44SGerd Bayer }									\
123*186bfe44SGerd Bayer 
124*186bfe44SGerd Bayer VFIO_IORDWR(8)
125*186bfe44SGerd Bayer VFIO_IORDWR(16)
126*186bfe44SGerd Bayer VFIO_IORDWR(32)
12789e1f7d4SAlex Williamson /*
128906ee99dSAlex Williamson  * Read or write from an __iomem region (MMIO or I/O port) with an excluded
129906ee99dSAlex Williamson  * range which is inaccessible.  The excluded range drops writes and fills
130906ee99dSAlex Williamson  * reads with -1.  This is intended for handling MSI-X vector tables and
131906ee99dSAlex Williamson  * leftover space for ROM BARs.
13289e1f7d4SAlex Williamson  */
1334de676d4SAnkit Agrawal ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
134bc93b9aeSAlex Williamson 			       void __iomem *io, char __user *buf,
135906ee99dSAlex Williamson 			       loff_t off, size_t count, size_t x_start,
136906ee99dSAlex Williamson 			       size_t x_end, bool iswrite)
13789e1f7d4SAlex Williamson {
138906ee99dSAlex Williamson 	ssize_t done = 0;
139bc93b9aeSAlex Williamson 	int ret;
14089e1f7d4SAlex Williamson 
14189e1f7d4SAlex Williamson 	while (count) {
14289e1f7d4SAlex Williamson 		size_t fillable, filled;
14389e1f7d4SAlex Williamson 
144906ee99dSAlex Williamson 		if (off < x_start)
145906ee99dSAlex Williamson 			fillable = min(count, (size_t)(x_start - off));
146906ee99dSAlex Williamson 		else if (off >= x_end)
147906ee99dSAlex Williamson 			fillable = count;
14889e1f7d4SAlex Williamson 		else
14989e1f7d4SAlex Williamson 			fillable = 0;
15089e1f7d4SAlex Williamson 
151906ee99dSAlex Williamson 		if (fillable >= 4 && !(off % 4)) {
152*186bfe44SGerd Bayer 			ret = vfio_pci_iordwr32(vdev, iswrite, test_mem,
153*186bfe44SGerd Bayer 						io, buf, off, &filled);
154bc93b9aeSAlex Williamson 			if (ret)
155bc93b9aeSAlex Williamson 				return ret;
15689e1f7d4SAlex Williamson 
157906ee99dSAlex Williamson 		} else if (fillable >= 2 && !(off % 2)) {
158*186bfe44SGerd Bayer 			ret = vfio_pci_iordwr16(vdev, iswrite, test_mem,
159*186bfe44SGerd Bayer 						io, buf, off, &filled);
160bc93b9aeSAlex Williamson 			if (ret)
161bc93b9aeSAlex Williamson 				return ret;
16289e1f7d4SAlex Williamson 
16389e1f7d4SAlex Williamson 		} else if (fillable) {
164*186bfe44SGerd Bayer 			ret = vfio_pci_iordwr8(vdev, iswrite, test_mem,
165*186bfe44SGerd Bayer 					       io, buf, off, &filled);
166bc93b9aeSAlex Williamson 			if (ret)
167bc93b9aeSAlex Williamson 				return ret;
16889e1f7d4SAlex Williamson 
16989e1f7d4SAlex Williamson 		} else {
170906ee99dSAlex Williamson 			/* Fill reads with -1, drop writes */
171906ee99dSAlex Williamson 			filled = min(count, (size_t)(x_end - off));
17289e1f7d4SAlex Williamson 			if (!iswrite) {
173906ee99dSAlex Williamson 				u8 val = 0xFF;
17489e1f7d4SAlex Williamson 				size_t i;
17589e1f7d4SAlex Williamson 
176906ee99dSAlex Williamson 				for (i = 0; i < filled; i++)
177906ee99dSAlex Williamson 					if (copy_to_user(buf + i, &val, 1))
178906ee99dSAlex Williamson 						return -EFAULT;
17989e1f7d4SAlex Williamson 			}
18089e1f7d4SAlex Williamson 		}
18189e1f7d4SAlex Williamson 
18289e1f7d4SAlex Williamson 		count -= filled;
18389e1f7d4SAlex Williamson 		done += filled;
184906ee99dSAlex Williamson 		off += filled;
18589e1f7d4SAlex Williamson 		buf += filled;
18689e1f7d4SAlex Williamson 	}
18789e1f7d4SAlex Williamson 
188906ee99dSAlex Williamson 	return done;
189906ee99dSAlex Williamson }
1904de676d4SAnkit Agrawal EXPORT_SYMBOL_GPL(vfio_pci_core_do_io_rw);
191906ee99dSAlex Williamson 
1928bccc5b8SYishai Hadas int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar)
1930d77ed35SAlex Williamson {
1940d77ed35SAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
1950d77ed35SAlex Williamson 	int ret;
1960d77ed35SAlex Williamson 	void __iomem *io;
1970d77ed35SAlex Williamson 
1980d77ed35SAlex Williamson 	if (vdev->barmap[bar])
1990d77ed35SAlex Williamson 		return 0;
2000d77ed35SAlex Williamson 
2010d77ed35SAlex Williamson 	ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
2020d77ed35SAlex Williamson 	if (ret)
2030d77ed35SAlex Williamson 		return ret;
2040d77ed35SAlex Williamson 
2050d77ed35SAlex Williamson 	io = pci_iomap(pdev, bar, 0);
2060d77ed35SAlex Williamson 	if (!io) {
2070d77ed35SAlex Williamson 		pci_release_selected_regions(pdev, 1 << bar);
2080d77ed35SAlex Williamson 		return -ENOMEM;
2090d77ed35SAlex Williamson 	}
2100d77ed35SAlex Williamson 
2110d77ed35SAlex Williamson 	vdev->barmap[bar] = io;
2120d77ed35SAlex Williamson 
2130d77ed35SAlex Williamson 	return 0;
2140d77ed35SAlex Williamson }
2158bccc5b8SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap);
2160d77ed35SAlex Williamson 
21753647510SMax Gurtovoy ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
218906ee99dSAlex Williamson 			size_t count, loff_t *ppos, bool iswrite)
219906ee99dSAlex Williamson {
220906ee99dSAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
221906ee99dSAlex Williamson 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
222906ee99dSAlex Williamson 	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
223906ee99dSAlex Williamson 	size_t x_start = 0, x_end = 0;
224906ee99dSAlex Williamson 	resource_size_t end;
225906ee99dSAlex Williamson 	void __iomem *io;
226abafbc55SAlex Williamson 	struct resource *res = &vdev->pdev->resource[bar];
227906ee99dSAlex Williamson 	ssize_t done;
228906ee99dSAlex Williamson 
229a13b6459SAlex Williamson 	if (pci_resource_start(pdev, bar))
230906ee99dSAlex Williamson 		end = pci_resource_len(pdev, bar);
231a13b6459SAlex Williamson 	else if (bar == PCI_ROM_RESOURCE &&
232a13b6459SAlex Williamson 		 pdev->resource[bar].flags & IORESOURCE_ROM_SHADOW)
233a13b6459SAlex Williamson 		end = 0x20000;
234a13b6459SAlex Williamson 	else
235a13b6459SAlex Williamson 		return -EINVAL;
236906ee99dSAlex Williamson 
237906ee99dSAlex Williamson 	if (pos >= end)
238906ee99dSAlex Williamson 		return -EINVAL;
239906ee99dSAlex Williamson 
240906ee99dSAlex Williamson 	count = min(count, (size_t)(end - pos));
241906ee99dSAlex Williamson 
242906ee99dSAlex Williamson 	if (bar == PCI_ROM_RESOURCE) {
243906ee99dSAlex Williamson 		/*
244906ee99dSAlex Williamson 		 * The ROM can fill less space than the BAR, so we start the
245906ee99dSAlex Williamson 		 * excluded range at the end of the actual ROM.  This makes
246906ee99dSAlex Williamson 		 * filling large ROM BARs much faster.
247906ee99dSAlex Williamson 		 */
248906ee99dSAlex Williamson 		io = pci_map_rom(pdev, &x_start);
249abafbc55SAlex Williamson 		if (!io) {
250abafbc55SAlex Williamson 			done = -ENOMEM;
251abafbc55SAlex Williamson 			goto out;
252abafbc55SAlex Williamson 		}
253906ee99dSAlex Williamson 		x_end = end;
2540d77ed35SAlex Williamson 	} else {
2558bccc5b8SYishai Hadas 		int ret = vfio_pci_core_setup_barmap(vdev, bar);
256abafbc55SAlex Williamson 		if (ret) {
257abafbc55SAlex Williamson 			done = ret;
258abafbc55SAlex Williamson 			goto out;
259abafbc55SAlex Williamson 		}
260906ee99dSAlex Williamson 
261906ee99dSAlex Williamson 		io = vdev->barmap[bar];
2620d77ed35SAlex Williamson 	}
263906ee99dSAlex Williamson 
264906ee99dSAlex Williamson 	if (bar == vdev->msix_bar) {
265906ee99dSAlex Williamson 		x_start = vdev->msix_offset;
266906ee99dSAlex Williamson 		x_end = vdev->msix_offset + vdev->msix_size;
267906ee99dSAlex Williamson 	}
268906ee99dSAlex Williamson 
2694de676d4SAnkit Agrawal 	done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos,
270bc93b9aeSAlex Williamson 				      count, x_start, x_end, iswrite);
271906ee99dSAlex Williamson 
272906ee99dSAlex Williamson 	if (done >= 0)
27389e1f7d4SAlex Williamson 		*ppos += done;
27489e1f7d4SAlex Williamson 
27589e1f7d4SAlex Williamson 	if (bar == PCI_ROM_RESOURCE)
27689e1f7d4SAlex Williamson 		pci_unmap_rom(pdev, io);
277abafbc55SAlex Williamson out:
278906ee99dSAlex Williamson 	return done;
27989e1f7d4SAlex Williamson }
28084237a82SAlex Williamson 
2816e031ec0SAlex Williamson #ifdef CONFIG_VFIO_PCI_VGA
28253647510SMax Gurtovoy ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
28384237a82SAlex Williamson 			       size_t count, loff_t *ppos, bool iswrite)
28484237a82SAlex Williamson {
28584237a82SAlex Williamson 	int ret;
28684237a82SAlex Williamson 	loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK;
28784237a82SAlex Williamson 	void __iomem *iomem = NULL;
28884237a82SAlex Williamson 	unsigned int rsrc;
28984237a82SAlex Williamson 	bool is_ioport;
29084237a82SAlex Williamson 	ssize_t done;
29184237a82SAlex Williamson 
29284237a82SAlex Williamson 	if (!vdev->has_vga)
29384237a82SAlex Williamson 		return -EINVAL;
29484237a82SAlex Williamson 
29545e86971SArnd Bergmann 	if (pos > 0xbfffful)
29645e86971SArnd Bergmann 		return -EINVAL;
29745e86971SArnd Bergmann 
29845e86971SArnd Bergmann 	switch ((u32)pos) {
29984237a82SAlex Williamson 	case 0xa0000 ... 0xbffff:
30084237a82SAlex Williamson 		count = min(count, (size_t)(0xc0000 - pos));
3014bdc0d67SChristoph Hellwig 		iomem = ioremap(0xa0000, 0xbffff - 0xa0000 + 1);
30284237a82SAlex Williamson 		off = pos - 0xa0000;
30384237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_MEM;
30484237a82SAlex Williamson 		is_ioport = false;
30584237a82SAlex Williamson 		break;
30684237a82SAlex Williamson 	case 0x3b0 ... 0x3bb:
30784237a82SAlex Williamson 		count = min(count, (size_t)(0x3bc - pos));
30884237a82SAlex Williamson 		iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1);
30984237a82SAlex Williamson 		off = pos - 0x3b0;
31084237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_IO;
31184237a82SAlex Williamson 		is_ioport = true;
31284237a82SAlex Williamson 		break;
31384237a82SAlex Williamson 	case 0x3c0 ... 0x3df:
31484237a82SAlex Williamson 		count = min(count, (size_t)(0x3e0 - pos));
31584237a82SAlex Williamson 		iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1);
31684237a82SAlex Williamson 		off = pos - 0x3c0;
31784237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_IO;
31884237a82SAlex Williamson 		is_ioport = true;
31984237a82SAlex Williamson 		break;
32084237a82SAlex Williamson 	default:
32184237a82SAlex Williamson 		return -EINVAL;
32284237a82SAlex Williamson 	}
32384237a82SAlex Williamson 
32484237a82SAlex Williamson 	if (!iomem)
32584237a82SAlex Williamson 		return -ENOMEM;
32684237a82SAlex Williamson 
32784237a82SAlex Williamson 	ret = vga_get_interruptible(vdev->pdev, rsrc);
32884237a82SAlex Williamson 	if (ret) {
32984237a82SAlex Williamson 		is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
33084237a82SAlex Williamson 		return ret;
33184237a82SAlex Williamson 	}
33284237a82SAlex Williamson 
333bc93b9aeSAlex Williamson 	/*
334bc93b9aeSAlex Williamson 	 * VGA MMIO is a legacy, non-BAR resource that hopefully allows
335bc93b9aeSAlex Williamson 	 * probing, so we don't currently worry about access in relation
336bc93b9aeSAlex Williamson 	 * to the memory enable bit in the command register.
337bc93b9aeSAlex Williamson 	 */
3384de676d4SAnkit Agrawal 	done = vfio_pci_core_do_io_rw(vdev, false, iomem, buf, off, count,
3394de676d4SAnkit Agrawal 				      0, 0, iswrite);
34084237a82SAlex Williamson 
34184237a82SAlex Williamson 	vga_put(vdev->pdev, rsrc);
34284237a82SAlex Williamson 
34384237a82SAlex Williamson 	is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
34484237a82SAlex Williamson 
34584237a82SAlex Williamson 	if (done >= 0)
34684237a82SAlex Williamson 		*ppos += done;
34784237a82SAlex Williamson 
34884237a82SAlex Williamson 	return done;
34984237a82SAlex Williamson }
3506e031ec0SAlex Williamson #endif
35130656177SAlex Williamson 
35238565c93SAlex Williamson static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd,
35338565c93SAlex Williamson 					bool test_mem)
35430656177SAlex Williamson {
35530656177SAlex Williamson 	switch (ioeventfd->count) {
35630656177SAlex Williamson 	case 1:
3578486ae16SYishai Hadas 		vfio_pci_core_iowrite8(ioeventfd->vdev, test_mem,
358bc93b9aeSAlex Williamson 				       ioeventfd->data, ioeventfd->addr);
35930656177SAlex Williamson 		break;
36030656177SAlex Williamson 	case 2:
3618486ae16SYishai Hadas 		vfio_pci_core_iowrite16(ioeventfd->vdev, test_mem,
362bc93b9aeSAlex Williamson 					ioeventfd->data, ioeventfd->addr);
36330656177SAlex Williamson 		break;
36430656177SAlex Williamson 	case 4:
3658486ae16SYishai Hadas 		vfio_pci_core_iowrite32(ioeventfd->vdev, test_mem,
366bc93b9aeSAlex Williamson 					ioeventfd->data, ioeventfd->addr);
36730656177SAlex Williamson 		break;
36830656177SAlex Williamson #ifdef iowrite64
36930656177SAlex Williamson 	case 8:
3708486ae16SYishai Hadas 		vfio_pci_core_iowrite64(ioeventfd->vdev, test_mem,
371bc93b9aeSAlex Williamson 					ioeventfd->data, ioeventfd->addr);
37230656177SAlex Williamson 		break;
37330656177SAlex Williamson #endif
37430656177SAlex Williamson 	}
37538565c93SAlex Williamson }
37638565c93SAlex Williamson 
37738565c93SAlex Williamson static int vfio_pci_ioeventfd_handler(void *opaque, void *unused)
37838565c93SAlex Williamson {
37938565c93SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd = opaque;
38053647510SMax Gurtovoy 	struct vfio_pci_core_device *vdev = ioeventfd->vdev;
38138565c93SAlex Williamson 
38238565c93SAlex Williamson 	if (ioeventfd->test_mem) {
38338565c93SAlex Williamson 		if (!down_read_trylock(&vdev->memory_lock))
38438565c93SAlex Williamson 			return 1; /* Lock contended, use thread */
38538565c93SAlex Williamson 		if (!__vfio_pci_memory_enabled(vdev)) {
38638565c93SAlex Williamson 			up_read(&vdev->memory_lock);
38738565c93SAlex Williamson 			return 0;
38838565c93SAlex Williamson 		}
38938565c93SAlex Williamson 	}
39038565c93SAlex Williamson 
39138565c93SAlex Williamson 	vfio_pci_ioeventfd_do_write(ioeventfd, false);
39238565c93SAlex Williamson 
39338565c93SAlex Williamson 	if (ioeventfd->test_mem)
39438565c93SAlex Williamson 		up_read(&vdev->memory_lock);
39530656177SAlex Williamson 
39630656177SAlex Williamson 	return 0;
39730656177SAlex Williamson }
39830656177SAlex Williamson 
39938565c93SAlex Williamson static void vfio_pci_ioeventfd_thread(void *opaque, void *unused)
40038565c93SAlex Williamson {
40138565c93SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd = opaque;
40238565c93SAlex Williamson 
40338565c93SAlex Williamson 	vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem);
40438565c93SAlex Williamson }
40538565c93SAlex Williamson 
40616f4cbd9SJason Gunthorpe int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset,
40730656177SAlex Williamson 		       uint64_t data, int count, int fd)
40830656177SAlex Williamson {
40930656177SAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
41030656177SAlex Williamson 	loff_t pos = offset & VFIO_PCI_OFFSET_MASK;
41130656177SAlex Williamson 	int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset);
41230656177SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd;
41330656177SAlex Williamson 
41430656177SAlex Williamson 	/* Only support ioeventfds into BARs */
41530656177SAlex Williamson 	if (bar > VFIO_PCI_BAR5_REGION_INDEX)
41630656177SAlex Williamson 		return -EINVAL;
41730656177SAlex Williamson 
41830656177SAlex Williamson 	if (pos + count > pci_resource_len(pdev, bar))
41930656177SAlex Williamson 		return -EINVAL;
42030656177SAlex Williamson 
42130656177SAlex Williamson 	/* Disallow ioeventfds working around MSI-X table writes */
42230656177SAlex Williamson 	if (bar == vdev->msix_bar &&
42330656177SAlex Williamson 	    !(pos + count <= vdev->msix_offset ||
42430656177SAlex Williamson 	      pos >= vdev->msix_offset + vdev->msix_size))
42530656177SAlex Williamson 		return -EINVAL;
42630656177SAlex Williamson 
42730656177SAlex Williamson #ifndef iowrite64
42830656177SAlex Williamson 	if (count == 8)
42930656177SAlex Williamson 		return -EINVAL;
43030656177SAlex Williamson #endif
43130656177SAlex Williamson 
4328bccc5b8SYishai Hadas 	ret = vfio_pci_core_setup_barmap(vdev, bar);
43330656177SAlex Williamson 	if (ret)
43430656177SAlex Williamson 		return ret;
43530656177SAlex Williamson 
43630656177SAlex Williamson 	mutex_lock(&vdev->ioeventfds_lock);
43730656177SAlex Williamson 
43830656177SAlex Williamson 	list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) {
43930656177SAlex Williamson 		if (ioeventfd->pos == pos && ioeventfd->bar == bar &&
44030656177SAlex Williamson 		    ioeventfd->data == data && ioeventfd->count == count) {
44130656177SAlex Williamson 			if (fd == -1) {
44230656177SAlex Williamson 				vfio_virqfd_disable(&ioeventfd->virqfd);
44330656177SAlex Williamson 				list_del(&ioeventfd->next);
44430656177SAlex Williamson 				vdev->ioeventfds_nr--;
44530656177SAlex Williamson 				kfree(ioeventfd);
44630656177SAlex Williamson 				ret = 0;
44730656177SAlex Williamson 			} else
44830656177SAlex Williamson 				ret = -EEXIST;
44930656177SAlex Williamson 
45030656177SAlex Williamson 			goto out_unlock;
45130656177SAlex Williamson 		}
45230656177SAlex Williamson 	}
45330656177SAlex Williamson 
45430656177SAlex Williamson 	if (fd < 0) {
45530656177SAlex Williamson 		ret = -ENODEV;
45630656177SAlex Williamson 		goto out_unlock;
45730656177SAlex Williamson 	}
45830656177SAlex Williamson 
45930656177SAlex Williamson 	if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) {
46030656177SAlex Williamson 		ret = -ENOSPC;
46130656177SAlex Williamson 		goto out_unlock;
46230656177SAlex Williamson 	}
46330656177SAlex Williamson 
4640886196cSJason Gunthorpe 	ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL_ACCOUNT);
46530656177SAlex Williamson 	if (!ioeventfd) {
46630656177SAlex Williamson 		ret = -ENOMEM;
46730656177SAlex Williamson 		goto out_unlock;
46830656177SAlex Williamson 	}
46930656177SAlex Williamson 
470bc93b9aeSAlex Williamson 	ioeventfd->vdev = vdev;
47130656177SAlex Williamson 	ioeventfd->addr = vdev->barmap[bar] + pos;
47230656177SAlex Williamson 	ioeventfd->data = data;
47330656177SAlex Williamson 	ioeventfd->pos = pos;
47430656177SAlex Williamson 	ioeventfd->bar = bar;
47530656177SAlex Williamson 	ioeventfd->count = count;
476bc93b9aeSAlex Williamson 	ioeventfd->test_mem = vdev->pdev->resource[bar].flags & IORESOURCE_MEM;
47730656177SAlex Williamson 
47830656177SAlex Williamson 	ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler,
47938565c93SAlex Williamson 				 vfio_pci_ioeventfd_thread, NULL,
48038565c93SAlex Williamson 				 &ioeventfd->virqfd, fd);
48130656177SAlex Williamson 	if (ret) {
48230656177SAlex Williamson 		kfree(ioeventfd);
48330656177SAlex Williamson 		goto out_unlock;
48430656177SAlex Williamson 	}
48530656177SAlex Williamson 
48630656177SAlex Williamson 	list_add(&ioeventfd->next, &vdev->ioeventfds_list);
48730656177SAlex Williamson 	vdev->ioeventfds_nr++;
48830656177SAlex Williamson 
48930656177SAlex Williamson out_unlock:
49030656177SAlex Williamson 	mutex_unlock(&vdev->ioeventfds_lock);
49130656177SAlex Williamson 
49230656177SAlex Williamson 	return ret;
49330656177SAlex Williamson }
494