xref: /openbmc/linux/drivers/vfio/pci/vfio_pci_rdwr.c (revision 861e10be)
1 /*
2  * VFIO PCI I/O Port & MMIO access
3  *
4  * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
5  *     Author: Alex Williamson <alex.williamson@redhat.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  *
11  * Derived from original vfio:
12  * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
13  * Author: Tom Lyon, pugs@cisco.com
14  */
15 
16 #include <linux/fs.h>
17 #include <linux/pci.h>
18 #include <linux/uaccess.h>
19 #include <linux/io.h>
20 
21 #include "vfio_pci_private.h"
22 
23 /* I/O Port BAR access */
24 ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf,
25 			      size_t count, loff_t *ppos, bool iswrite)
26 {
27 	struct pci_dev *pdev = vdev->pdev;
28 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
29 	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
30 	void __iomem *io;
31 	size_t done = 0;
32 
33 	if (!pci_resource_start(pdev, bar))
34 		return -EINVAL;
35 
36 	if (pos + count > pci_resource_len(pdev, bar))
37 		return -EINVAL;
38 
39 	if (!vdev->barmap[bar]) {
40 		int ret;
41 
42 		ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
43 		if (ret)
44 			return ret;
45 
46 		vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
47 
48 		if (!vdev->barmap[bar]) {
49 			pci_release_selected_regions(pdev, 1 << bar);
50 			return -EINVAL;
51 		}
52 	}
53 
54 	io = vdev->barmap[bar];
55 
56 	while (count) {
57 		int filled;
58 
59 		if (count >= 3 && !(pos % 4)) {
60 			__le32 val;
61 
62 			if (iswrite) {
63 				if (copy_from_user(&val, buf, 4))
64 					return -EFAULT;
65 
66 				iowrite32(le32_to_cpu(val), io + pos);
67 			} else {
68 				val = cpu_to_le32(ioread32(io + pos));
69 
70 				if (copy_to_user(buf, &val, 4))
71 					return -EFAULT;
72 			}
73 
74 			filled = 4;
75 
76 		} else if ((pos % 2) == 0 && count >= 2) {
77 			__le16 val;
78 
79 			if (iswrite) {
80 				if (copy_from_user(&val, buf, 2))
81 					return -EFAULT;
82 
83 				iowrite16(le16_to_cpu(val), io + pos);
84 			} else {
85 				val = cpu_to_le16(ioread16(io + pos));
86 
87 				if (copy_to_user(buf, &val, 2))
88 					return -EFAULT;
89 			}
90 
91 			filled = 2;
92 		} else {
93 			u8 val;
94 
95 			if (iswrite) {
96 				if (copy_from_user(&val, buf, 1))
97 					return -EFAULT;
98 
99 				iowrite8(val, io + pos);
100 			} else {
101 				val = ioread8(io + pos);
102 
103 				if (copy_to_user(buf, &val, 1))
104 					return -EFAULT;
105 			}
106 
107 			filled = 1;
108 		}
109 
110 		count -= filled;
111 		done += filled;
112 		buf += filled;
113 		pos += filled;
114 	}
115 
116 	*ppos += done;
117 
118 	return done;
119 }
120 
121 /*
122  * MMIO BAR access
123  * We handle two excluded ranges here as well, if the user tries to read
124  * the ROM beyond what PCI tells us is available or the MSI-X table region,
125  * we return 0xFF and writes are dropped.
126  */
127 ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf,
128 			       size_t count, loff_t *ppos, bool iswrite)
129 {
130 	struct pci_dev *pdev = vdev->pdev;
131 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
132 	int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
133 	void __iomem *io;
134 	resource_size_t end;
135 	size_t done = 0;
136 	size_t x_start = 0, x_end = 0; /* excluded range */
137 
138 	if (!pci_resource_start(pdev, bar))
139 		return -EINVAL;
140 
141 	end = pci_resource_len(pdev, bar);
142 
143 	if (pos > end)
144 		return -EINVAL;
145 
146 	if (pos == end)
147 		return 0;
148 
149 	if (pos + count > end)
150 		count = end - pos;
151 
152 	if (bar == PCI_ROM_RESOURCE) {
153 		io = pci_map_rom(pdev, &x_start);
154 		x_end = end;
155 	} else {
156 		if (!vdev->barmap[bar]) {
157 			int ret;
158 
159 			ret = pci_request_selected_regions(pdev, 1 << bar,
160 							   "vfio");
161 			if (ret)
162 				return ret;
163 
164 			vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
165 
166 			if (!vdev->barmap[bar]) {
167 				pci_release_selected_regions(pdev, 1 << bar);
168 				return -EINVAL;
169 			}
170 		}
171 
172 		io = vdev->barmap[bar];
173 
174 		if (bar == vdev->msix_bar) {
175 			x_start = vdev->msix_offset;
176 			x_end = vdev->msix_offset + vdev->msix_size;
177 		}
178 	}
179 
180 	if (!io)
181 		return -EINVAL;
182 
183 	while (count) {
184 		size_t fillable, filled;
185 
186 		if (pos < x_start)
187 			fillable = x_start - pos;
188 		else if (pos >= x_end)
189 			fillable = end - pos;
190 		else
191 			fillable = 0;
192 
193 		if (fillable >= 4 && !(pos % 4) && (count >= 4)) {
194 			__le32 val;
195 
196 			if (iswrite) {
197 				if (copy_from_user(&val, buf, 4))
198 					goto out;
199 
200 				iowrite32(le32_to_cpu(val), io + pos);
201 			} else {
202 				val = cpu_to_le32(ioread32(io + pos));
203 
204 				if (copy_to_user(buf, &val, 4))
205 					goto out;
206 			}
207 
208 			filled = 4;
209 		} else if (fillable >= 2 && !(pos % 2) && (count >= 2)) {
210 			__le16 val;
211 
212 			if (iswrite) {
213 				if (copy_from_user(&val, buf, 2))
214 					goto out;
215 
216 				iowrite16(le16_to_cpu(val), io + pos);
217 			} else {
218 				val = cpu_to_le16(ioread16(io + pos));
219 
220 				if (copy_to_user(buf, &val, 2))
221 					goto out;
222 			}
223 
224 			filled = 2;
225 		} else if (fillable) {
226 			u8 val;
227 
228 			if (iswrite) {
229 				if (copy_from_user(&val, buf, 1))
230 					goto out;
231 
232 				iowrite8(val, io + pos);
233 			} else {
234 				val = ioread8(io + pos);
235 
236 				if (copy_to_user(buf, &val, 1))
237 					goto out;
238 			}
239 
240 			filled = 1;
241 		} else {
242 			/* Drop writes, fill reads with FF */
243 			filled = min((size_t)(x_end - pos), count);
244 			if (!iswrite) {
245 				char val = 0xFF;
246 				size_t i;
247 
248 				for (i = 0; i < filled; i++) {
249 					if (put_user(val, buf + i))
250 						goto out;
251 				}
252 			}
253 
254 		}
255 
256 		count -= filled;
257 		done += filled;
258 		buf += filled;
259 		pos += filled;
260 	}
261 
262 	*ppos += done;
263 
264 out:
265 	if (bar == PCI_ROM_RESOURCE)
266 		pci_unmap_rom(pdev, io);
267 
268 	return count ? -EFAULT : done;
269 }
270