xref: /linux/drivers/vfio/pci/vfio_pci_rdwr.c (revision 4df13a6871d9e97aeeef72244e9a954c5cf11f54)
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)
92*4df13a68SBen Segal #ifdef ioread64
93*4df13a68SBen Segal VFIO_IOREAD(64)
94*4df13a68SBen Segal #endif
95bc93b9aeSAlex Williamson 
96186bfe44SGerd Bayer #define VFIO_IORDWR(size)						\
97186bfe44SGerd Bayer static int vfio_pci_iordwr##size(struct vfio_pci_core_device *vdev,\
98186bfe44SGerd Bayer 				bool iswrite, bool test_mem,		\
99186bfe44SGerd Bayer 				void __iomem *io, char __user *buf,	\
100186bfe44SGerd Bayer 				loff_t off, size_t *filled)		\
101186bfe44SGerd Bayer {									\
102186bfe44SGerd Bayer 	u##size val;							\
103186bfe44SGerd Bayer 	int ret;							\
104186bfe44SGerd Bayer 									\
105186bfe44SGerd Bayer 	if (iswrite) {							\
106186bfe44SGerd Bayer 		if (copy_from_user(&val, buf, sizeof(val)))		\
107186bfe44SGerd Bayer 			return -EFAULT;					\
108186bfe44SGerd Bayer 									\
109186bfe44SGerd Bayer 		ret = vfio_pci_core_iowrite##size(vdev, test_mem,	\
110186bfe44SGerd Bayer 						  val, io + off);	\
111186bfe44SGerd Bayer 		if (ret)						\
112186bfe44SGerd Bayer 			return ret;					\
113186bfe44SGerd Bayer 	} else {							\
114186bfe44SGerd Bayer 		ret = vfio_pci_core_ioread##size(vdev, test_mem,	\
115186bfe44SGerd Bayer 						 &val, io + off);	\
116186bfe44SGerd Bayer 		if (ret)						\
117186bfe44SGerd Bayer 			return ret;					\
118186bfe44SGerd Bayer 									\
119186bfe44SGerd Bayer 		if (copy_to_user(buf, &val, sizeof(val)))		\
120186bfe44SGerd Bayer 			return -EFAULT;					\
121186bfe44SGerd Bayer 	}								\
122186bfe44SGerd Bayer 									\
123186bfe44SGerd Bayer 	*filled = sizeof(val);						\
124186bfe44SGerd Bayer 	return 0;							\
125186bfe44SGerd Bayer }									\
126186bfe44SGerd Bayer 
127186bfe44SGerd Bayer VFIO_IORDWR(8)
128186bfe44SGerd Bayer VFIO_IORDWR(16)
129186bfe44SGerd Bayer VFIO_IORDWR(32)
130*4df13a68SBen Segal #if defined(ioread64) && defined(iowrite64)
131*4df13a68SBen Segal VFIO_IORDWR(64)
132*4df13a68SBen Segal #endif
133*4df13a68SBen Segal 
13489e1f7d4SAlex Williamson /*
135906ee99dSAlex Williamson  * Read or write from an __iomem region (MMIO or I/O port) with an excluded
136906ee99dSAlex Williamson  * range which is inaccessible.  The excluded range drops writes and fills
137906ee99dSAlex Williamson  * reads with -1.  This is intended for handling MSI-X vector tables and
138906ee99dSAlex Williamson  * leftover space for ROM BARs.
13989e1f7d4SAlex Williamson  */
1404de676d4SAnkit Agrawal ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
141bc93b9aeSAlex Williamson 			       void __iomem *io, char __user *buf,
142906ee99dSAlex Williamson 			       loff_t off, size_t count, size_t x_start,
143906ee99dSAlex Williamson 			       size_t x_end, bool iswrite)
14489e1f7d4SAlex Williamson {
145906ee99dSAlex Williamson 	ssize_t done = 0;
146bc93b9aeSAlex Williamson 	int ret;
14789e1f7d4SAlex Williamson 
14889e1f7d4SAlex Williamson 	while (count) {
14989e1f7d4SAlex Williamson 		size_t fillable, filled;
15089e1f7d4SAlex Williamson 
151906ee99dSAlex Williamson 		if (off < x_start)
152906ee99dSAlex Williamson 			fillable = min(count, (size_t)(x_start - off));
153906ee99dSAlex Williamson 		else if (off >= x_end)
154906ee99dSAlex Williamson 			fillable = count;
15589e1f7d4SAlex Williamson 		else
15689e1f7d4SAlex Williamson 			fillable = 0;
15789e1f7d4SAlex Williamson 
158*4df13a68SBen Segal #if defined(ioread64) && defined(iowrite64)
159*4df13a68SBen Segal 		if (fillable >= 8 && !(off % 8)) {
160*4df13a68SBen Segal 			ret = vfio_pci_iordwr64(vdev, iswrite, test_mem,
161*4df13a68SBen Segal 						io, buf, off, &filled);
162*4df13a68SBen Segal 			if (ret)
163*4df13a68SBen Segal 				return ret;
164*4df13a68SBen Segal 
165*4df13a68SBen Segal 		} else
166*4df13a68SBen Segal #endif
167906ee99dSAlex Williamson 		if (fillable >= 4 && !(off % 4)) {
168186bfe44SGerd Bayer 			ret = vfio_pci_iordwr32(vdev, iswrite, test_mem,
169186bfe44SGerd Bayer 						io, buf, off, &filled);
170bc93b9aeSAlex Williamson 			if (ret)
171bc93b9aeSAlex Williamson 				return ret;
17289e1f7d4SAlex Williamson 
173906ee99dSAlex Williamson 		} else if (fillable >= 2 && !(off % 2)) {
174186bfe44SGerd Bayer 			ret = vfio_pci_iordwr16(vdev, iswrite, test_mem,
175186bfe44SGerd Bayer 						io, buf, off, &filled);
176bc93b9aeSAlex Williamson 			if (ret)
177bc93b9aeSAlex Williamson 				return ret;
17889e1f7d4SAlex Williamson 
17989e1f7d4SAlex Williamson 		} else if (fillable) {
180186bfe44SGerd Bayer 			ret = vfio_pci_iordwr8(vdev, iswrite, test_mem,
181186bfe44SGerd Bayer 					       io, buf, off, &filled);
182bc93b9aeSAlex Williamson 			if (ret)
183bc93b9aeSAlex Williamson 				return ret;
18489e1f7d4SAlex Williamson 
18589e1f7d4SAlex Williamson 		} else {
186906ee99dSAlex Williamson 			/* Fill reads with -1, drop writes */
187906ee99dSAlex Williamson 			filled = min(count, (size_t)(x_end - off));
18889e1f7d4SAlex Williamson 			if (!iswrite) {
189906ee99dSAlex Williamson 				u8 val = 0xFF;
19089e1f7d4SAlex Williamson 				size_t i;
19189e1f7d4SAlex Williamson 
192906ee99dSAlex Williamson 				for (i = 0; i < filled; i++)
193906ee99dSAlex Williamson 					if (copy_to_user(buf + i, &val, 1))
194906ee99dSAlex Williamson 						return -EFAULT;
19589e1f7d4SAlex Williamson 			}
19689e1f7d4SAlex Williamson 		}
19789e1f7d4SAlex Williamson 
19889e1f7d4SAlex Williamson 		count -= filled;
19989e1f7d4SAlex Williamson 		done += filled;
200906ee99dSAlex Williamson 		off += filled;
20189e1f7d4SAlex Williamson 		buf += filled;
20289e1f7d4SAlex Williamson 	}
20389e1f7d4SAlex Williamson 
204906ee99dSAlex Williamson 	return done;
205906ee99dSAlex Williamson }
2064de676d4SAnkit Agrawal EXPORT_SYMBOL_GPL(vfio_pci_core_do_io_rw);
207906ee99dSAlex Williamson 
2088bccc5b8SYishai Hadas int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar)
2090d77ed35SAlex Williamson {
2100d77ed35SAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
2110d77ed35SAlex Williamson 	int ret;
2120d77ed35SAlex Williamson 	void __iomem *io;
2130d77ed35SAlex Williamson 
2140d77ed35SAlex Williamson 	if (vdev->barmap[bar])
2150d77ed35SAlex Williamson 		return 0;
2160d77ed35SAlex Williamson 
2170d77ed35SAlex Williamson 	ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
2180d77ed35SAlex Williamson 	if (ret)
2190d77ed35SAlex Williamson 		return ret;
2200d77ed35SAlex Williamson 
2210d77ed35SAlex Williamson 	io = pci_iomap(pdev, bar, 0);
2220d77ed35SAlex Williamson 	if (!io) {
2230d77ed35SAlex Williamson 		pci_release_selected_regions(pdev, 1 << bar);
2240d77ed35SAlex Williamson 		return -ENOMEM;
2250d77ed35SAlex Williamson 	}
2260d77ed35SAlex Williamson 
2270d77ed35SAlex Williamson 	vdev->barmap[bar] = io;
2280d77ed35SAlex Williamson 
2290d77ed35SAlex Williamson 	return 0;
2300d77ed35SAlex Williamson }
2318bccc5b8SYishai Hadas EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap);
2320d77ed35SAlex Williamson 
23353647510SMax Gurtovoy ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
234906ee99dSAlex Williamson 			size_t count, loff_t *ppos, bool iswrite)
235906ee99dSAlex Williamson {
236906ee99dSAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
237906ee99dSAlex Williamson 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
238906ee99dSAlex Williamson 	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
239906ee99dSAlex Williamson 	size_t x_start = 0, x_end = 0;
240906ee99dSAlex Williamson 	resource_size_t end;
241906ee99dSAlex Williamson 	void __iomem *io;
242abafbc55SAlex Williamson 	struct resource *res = &vdev->pdev->resource[bar];
243906ee99dSAlex Williamson 	ssize_t done;
244906ee99dSAlex Williamson 
245a13b6459SAlex Williamson 	if (pci_resource_start(pdev, bar))
246906ee99dSAlex Williamson 		end = pci_resource_len(pdev, bar);
247a13b6459SAlex Williamson 	else if (bar == PCI_ROM_RESOURCE &&
248a13b6459SAlex Williamson 		 pdev->resource[bar].flags & IORESOURCE_ROM_SHADOW)
249a13b6459SAlex Williamson 		end = 0x20000;
250a13b6459SAlex Williamson 	else
251a13b6459SAlex Williamson 		return -EINVAL;
252906ee99dSAlex Williamson 
253906ee99dSAlex Williamson 	if (pos >= end)
254906ee99dSAlex Williamson 		return -EINVAL;
255906ee99dSAlex Williamson 
256906ee99dSAlex Williamson 	count = min(count, (size_t)(end - pos));
257906ee99dSAlex Williamson 
258906ee99dSAlex Williamson 	if (bar == PCI_ROM_RESOURCE) {
259906ee99dSAlex Williamson 		/*
260906ee99dSAlex Williamson 		 * The ROM can fill less space than the BAR, so we start the
261906ee99dSAlex Williamson 		 * excluded range at the end of the actual ROM.  This makes
262906ee99dSAlex Williamson 		 * filling large ROM BARs much faster.
263906ee99dSAlex Williamson 		 */
264906ee99dSAlex Williamson 		io = pci_map_rom(pdev, &x_start);
265abafbc55SAlex Williamson 		if (!io) {
266abafbc55SAlex Williamson 			done = -ENOMEM;
267abafbc55SAlex Williamson 			goto out;
268abafbc55SAlex Williamson 		}
269906ee99dSAlex Williamson 		x_end = end;
2700d77ed35SAlex Williamson 	} else {
2718bccc5b8SYishai Hadas 		int ret = vfio_pci_core_setup_barmap(vdev, bar);
272abafbc55SAlex Williamson 		if (ret) {
273abafbc55SAlex Williamson 			done = ret;
274abafbc55SAlex Williamson 			goto out;
275abafbc55SAlex Williamson 		}
276906ee99dSAlex Williamson 
277906ee99dSAlex Williamson 		io = vdev->barmap[bar];
2780d77ed35SAlex Williamson 	}
279906ee99dSAlex Williamson 
280906ee99dSAlex Williamson 	if (bar == vdev->msix_bar) {
281906ee99dSAlex Williamson 		x_start = vdev->msix_offset;
282906ee99dSAlex Williamson 		x_end = vdev->msix_offset + vdev->msix_size;
283906ee99dSAlex Williamson 	}
284906ee99dSAlex Williamson 
2854de676d4SAnkit Agrawal 	done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos,
286bc93b9aeSAlex Williamson 				      count, x_start, x_end, iswrite);
287906ee99dSAlex Williamson 
288906ee99dSAlex Williamson 	if (done >= 0)
28989e1f7d4SAlex Williamson 		*ppos += done;
29089e1f7d4SAlex Williamson 
29189e1f7d4SAlex Williamson 	if (bar == PCI_ROM_RESOURCE)
29289e1f7d4SAlex Williamson 		pci_unmap_rom(pdev, io);
293abafbc55SAlex Williamson out:
294906ee99dSAlex Williamson 	return done;
29589e1f7d4SAlex Williamson }
29684237a82SAlex Williamson 
2976e031ec0SAlex Williamson #ifdef CONFIG_VFIO_PCI_VGA
29853647510SMax Gurtovoy ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
29984237a82SAlex Williamson 			       size_t count, loff_t *ppos, bool iswrite)
30084237a82SAlex Williamson {
30184237a82SAlex Williamson 	int ret;
30284237a82SAlex Williamson 	loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK;
30384237a82SAlex Williamson 	void __iomem *iomem = NULL;
30484237a82SAlex Williamson 	unsigned int rsrc;
30584237a82SAlex Williamson 	bool is_ioport;
30684237a82SAlex Williamson 	ssize_t done;
30784237a82SAlex Williamson 
30884237a82SAlex Williamson 	if (!vdev->has_vga)
30984237a82SAlex Williamson 		return -EINVAL;
31084237a82SAlex Williamson 
31145e86971SArnd Bergmann 	if (pos > 0xbfffful)
31245e86971SArnd Bergmann 		return -EINVAL;
31345e86971SArnd Bergmann 
31445e86971SArnd Bergmann 	switch ((u32)pos) {
31584237a82SAlex Williamson 	case 0xa0000 ... 0xbffff:
31684237a82SAlex Williamson 		count = min(count, (size_t)(0xc0000 - pos));
3174bdc0d67SChristoph Hellwig 		iomem = ioremap(0xa0000, 0xbffff - 0xa0000 + 1);
31884237a82SAlex Williamson 		off = pos - 0xa0000;
31984237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_MEM;
32084237a82SAlex Williamson 		is_ioport = false;
32184237a82SAlex Williamson 		break;
32284237a82SAlex Williamson 	case 0x3b0 ... 0x3bb:
32384237a82SAlex Williamson 		count = min(count, (size_t)(0x3bc - pos));
32484237a82SAlex Williamson 		iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1);
32584237a82SAlex Williamson 		off = pos - 0x3b0;
32684237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_IO;
32784237a82SAlex Williamson 		is_ioport = true;
32884237a82SAlex Williamson 		break;
32984237a82SAlex Williamson 	case 0x3c0 ... 0x3df:
33084237a82SAlex Williamson 		count = min(count, (size_t)(0x3e0 - pos));
33184237a82SAlex Williamson 		iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1);
33284237a82SAlex Williamson 		off = pos - 0x3c0;
33384237a82SAlex Williamson 		rsrc = VGA_RSRC_LEGACY_IO;
33484237a82SAlex Williamson 		is_ioport = true;
33584237a82SAlex Williamson 		break;
33684237a82SAlex Williamson 	default:
33784237a82SAlex Williamson 		return -EINVAL;
33884237a82SAlex Williamson 	}
33984237a82SAlex Williamson 
34084237a82SAlex Williamson 	if (!iomem)
34184237a82SAlex Williamson 		return -ENOMEM;
34284237a82SAlex Williamson 
34384237a82SAlex Williamson 	ret = vga_get_interruptible(vdev->pdev, rsrc);
34484237a82SAlex Williamson 	if (ret) {
34584237a82SAlex Williamson 		is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
34684237a82SAlex Williamson 		return ret;
34784237a82SAlex Williamson 	}
34884237a82SAlex Williamson 
349bc93b9aeSAlex Williamson 	/*
350bc93b9aeSAlex Williamson 	 * VGA MMIO is a legacy, non-BAR resource that hopefully allows
351bc93b9aeSAlex Williamson 	 * probing, so we don't currently worry about access in relation
352bc93b9aeSAlex Williamson 	 * to the memory enable bit in the command register.
353bc93b9aeSAlex Williamson 	 */
3544de676d4SAnkit Agrawal 	done = vfio_pci_core_do_io_rw(vdev, false, iomem, buf, off, count,
3554de676d4SAnkit Agrawal 				      0, 0, iswrite);
35684237a82SAlex Williamson 
35784237a82SAlex Williamson 	vga_put(vdev->pdev, rsrc);
35884237a82SAlex Williamson 
35984237a82SAlex Williamson 	is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
36084237a82SAlex Williamson 
36184237a82SAlex Williamson 	if (done >= 0)
36284237a82SAlex Williamson 		*ppos += done;
36384237a82SAlex Williamson 
36484237a82SAlex Williamson 	return done;
36584237a82SAlex Williamson }
3666e031ec0SAlex Williamson #endif
36730656177SAlex Williamson 
36838565c93SAlex Williamson static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd,
36938565c93SAlex Williamson 					bool test_mem)
37030656177SAlex Williamson {
37130656177SAlex Williamson 	switch (ioeventfd->count) {
37230656177SAlex Williamson 	case 1:
3738486ae16SYishai Hadas 		vfio_pci_core_iowrite8(ioeventfd->vdev, test_mem,
374bc93b9aeSAlex Williamson 				       ioeventfd->data, ioeventfd->addr);
37530656177SAlex Williamson 		break;
37630656177SAlex Williamson 	case 2:
3778486ae16SYishai Hadas 		vfio_pci_core_iowrite16(ioeventfd->vdev, test_mem,
378bc93b9aeSAlex Williamson 					ioeventfd->data, ioeventfd->addr);
37930656177SAlex Williamson 		break;
38030656177SAlex Williamson 	case 4:
3818486ae16SYishai Hadas 		vfio_pci_core_iowrite32(ioeventfd->vdev, test_mem,
382bc93b9aeSAlex Williamson 					ioeventfd->data, ioeventfd->addr);
38330656177SAlex Williamson 		break;
38430656177SAlex Williamson #ifdef iowrite64
38530656177SAlex Williamson 	case 8:
3868486ae16SYishai Hadas 		vfio_pci_core_iowrite64(ioeventfd->vdev, test_mem,
387bc93b9aeSAlex Williamson 					ioeventfd->data, ioeventfd->addr);
38830656177SAlex Williamson 		break;
38930656177SAlex Williamson #endif
39030656177SAlex Williamson 	}
39138565c93SAlex Williamson }
39238565c93SAlex Williamson 
39338565c93SAlex Williamson static int vfio_pci_ioeventfd_handler(void *opaque, void *unused)
39438565c93SAlex Williamson {
39538565c93SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd = opaque;
39653647510SMax Gurtovoy 	struct vfio_pci_core_device *vdev = ioeventfd->vdev;
39738565c93SAlex Williamson 
39838565c93SAlex Williamson 	if (ioeventfd->test_mem) {
39938565c93SAlex Williamson 		if (!down_read_trylock(&vdev->memory_lock))
40038565c93SAlex Williamson 			return 1; /* Lock contended, use thread */
40138565c93SAlex Williamson 		if (!__vfio_pci_memory_enabled(vdev)) {
40238565c93SAlex Williamson 			up_read(&vdev->memory_lock);
40338565c93SAlex Williamson 			return 0;
40438565c93SAlex Williamson 		}
40538565c93SAlex Williamson 	}
40638565c93SAlex Williamson 
40738565c93SAlex Williamson 	vfio_pci_ioeventfd_do_write(ioeventfd, false);
40838565c93SAlex Williamson 
40938565c93SAlex Williamson 	if (ioeventfd->test_mem)
41038565c93SAlex Williamson 		up_read(&vdev->memory_lock);
41130656177SAlex Williamson 
41230656177SAlex Williamson 	return 0;
41330656177SAlex Williamson }
41430656177SAlex Williamson 
41538565c93SAlex Williamson static void vfio_pci_ioeventfd_thread(void *opaque, void *unused)
41638565c93SAlex Williamson {
41738565c93SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd = opaque;
41838565c93SAlex Williamson 
41938565c93SAlex Williamson 	vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem);
42038565c93SAlex Williamson }
42138565c93SAlex Williamson 
42216f4cbd9SJason Gunthorpe int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset,
42330656177SAlex Williamson 		       uint64_t data, int count, int fd)
42430656177SAlex Williamson {
42530656177SAlex Williamson 	struct pci_dev *pdev = vdev->pdev;
42630656177SAlex Williamson 	loff_t pos = offset & VFIO_PCI_OFFSET_MASK;
42730656177SAlex Williamson 	int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset);
42830656177SAlex Williamson 	struct vfio_pci_ioeventfd *ioeventfd;
42930656177SAlex Williamson 
43030656177SAlex Williamson 	/* Only support ioeventfds into BARs */
43130656177SAlex Williamson 	if (bar > VFIO_PCI_BAR5_REGION_INDEX)
43230656177SAlex Williamson 		return -EINVAL;
43330656177SAlex Williamson 
43430656177SAlex Williamson 	if (pos + count > pci_resource_len(pdev, bar))
43530656177SAlex Williamson 		return -EINVAL;
43630656177SAlex Williamson 
43730656177SAlex Williamson 	/* Disallow ioeventfds working around MSI-X table writes */
43830656177SAlex Williamson 	if (bar == vdev->msix_bar &&
43930656177SAlex Williamson 	    !(pos + count <= vdev->msix_offset ||
44030656177SAlex Williamson 	      pos >= vdev->msix_offset + vdev->msix_size))
44130656177SAlex Williamson 		return -EINVAL;
44230656177SAlex Williamson 
44330656177SAlex Williamson #ifndef iowrite64
44430656177SAlex Williamson 	if (count == 8)
44530656177SAlex Williamson 		return -EINVAL;
44630656177SAlex Williamson #endif
44730656177SAlex Williamson 
4488bccc5b8SYishai Hadas 	ret = vfio_pci_core_setup_barmap(vdev, bar);
44930656177SAlex Williamson 	if (ret)
45030656177SAlex Williamson 		return ret;
45130656177SAlex Williamson 
45230656177SAlex Williamson 	mutex_lock(&vdev->ioeventfds_lock);
45330656177SAlex Williamson 
45430656177SAlex Williamson 	list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) {
45530656177SAlex Williamson 		if (ioeventfd->pos == pos && ioeventfd->bar == bar &&
45630656177SAlex Williamson 		    ioeventfd->data == data && ioeventfd->count == count) {
45730656177SAlex Williamson 			if (fd == -1) {
45830656177SAlex Williamson 				vfio_virqfd_disable(&ioeventfd->virqfd);
45930656177SAlex Williamson 				list_del(&ioeventfd->next);
46030656177SAlex Williamson 				vdev->ioeventfds_nr--;
46130656177SAlex Williamson 				kfree(ioeventfd);
46230656177SAlex Williamson 				ret = 0;
46330656177SAlex Williamson 			} else
46430656177SAlex Williamson 				ret = -EEXIST;
46530656177SAlex Williamson 
46630656177SAlex Williamson 			goto out_unlock;
46730656177SAlex Williamson 		}
46830656177SAlex Williamson 	}
46930656177SAlex Williamson 
47030656177SAlex Williamson 	if (fd < 0) {
47130656177SAlex Williamson 		ret = -ENODEV;
47230656177SAlex Williamson 		goto out_unlock;
47330656177SAlex Williamson 	}
47430656177SAlex Williamson 
47530656177SAlex Williamson 	if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) {
47630656177SAlex Williamson 		ret = -ENOSPC;
47730656177SAlex Williamson 		goto out_unlock;
47830656177SAlex Williamson 	}
47930656177SAlex Williamson 
4800886196cSJason Gunthorpe 	ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL_ACCOUNT);
48130656177SAlex Williamson 	if (!ioeventfd) {
48230656177SAlex Williamson 		ret = -ENOMEM;
48330656177SAlex Williamson 		goto out_unlock;
48430656177SAlex Williamson 	}
48530656177SAlex Williamson 
486bc93b9aeSAlex Williamson 	ioeventfd->vdev = vdev;
48730656177SAlex Williamson 	ioeventfd->addr = vdev->barmap[bar] + pos;
48830656177SAlex Williamson 	ioeventfd->data = data;
48930656177SAlex Williamson 	ioeventfd->pos = pos;
49030656177SAlex Williamson 	ioeventfd->bar = bar;
49130656177SAlex Williamson 	ioeventfd->count = count;
492bc93b9aeSAlex Williamson 	ioeventfd->test_mem = vdev->pdev->resource[bar].flags & IORESOURCE_MEM;
49330656177SAlex Williamson 
49430656177SAlex Williamson 	ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler,
49538565c93SAlex Williamson 				 vfio_pci_ioeventfd_thread, NULL,
49638565c93SAlex Williamson 				 &ioeventfd->virqfd, fd);
49730656177SAlex Williamson 	if (ret) {
49830656177SAlex Williamson 		kfree(ioeventfd);
49930656177SAlex Williamson 		goto out_unlock;
50030656177SAlex Williamson 	}
50130656177SAlex Williamson 
50230656177SAlex Williamson 	list_add(&ioeventfd->next, &vdev->ioeventfds_list);
50330656177SAlex Williamson 	vdev->ioeventfds_nr++;
50430656177SAlex Williamson 
50530656177SAlex Williamson out_unlock:
50630656177SAlex Williamson 	mutex_unlock(&vdev->ioeventfds_lock);
50730656177SAlex Williamson 
50830656177SAlex Williamson 	return ret;
50930656177SAlex Williamson }
510