aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2013-02-14 16:02:12 -0500
committerAlex Williamson <alex.williamson@redhat.com>2013-02-14 16:02:12 -0500
commit906ee99dd2a5c819c1171ce5eaf6c080c027e58c (patch)
treec89c9d8fdb61d7e2843c28c9c52faaf8e9089917
parent5b279a11d32998aad1e45fe9de225302b6a8e8ba (diff)
vfio-pci: Cleanup BAR access
We can actually handle MMIO and I/O port from the same access function since PCI already does abstraction of this. The ROM BAR only requires a minor difference, so it gets included too. vfio_pci_config_readwrite gets renamed for consistency. Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
-rw-r--r--drivers/vfio/pci/vfio_pci.c26
-rw-r--r--drivers/vfio/pci/vfio_pci_config.c5
-rw-r--r--drivers/vfio/pci/vfio_pci_private.h15
-rw-r--r--drivers/vfio/pci/vfio_pci_rdwr.c234
4 files changed, 90 insertions, 190 deletions
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 469e110d7ea6..bb8c8c2be960 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -371,31 +371,21 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
371{ 371{
372 unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 372 unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
373 struct vfio_pci_device *vdev = device_data; 373 struct vfio_pci_device *vdev = device_data;
374 struct pci_dev *pdev = vdev->pdev;
375 374
376 if (index >= VFIO_PCI_NUM_REGIONS) 375 if (index >= VFIO_PCI_NUM_REGIONS)
377 return -EINVAL; 376 return -EINVAL;
378 377
379 switch (index) { 378 switch (index) {
380 case VFIO_PCI_CONFIG_REGION_INDEX: 379 case VFIO_PCI_CONFIG_REGION_INDEX:
381 return vfio_pci_config_readwrite(vdev, buf, count, 380 return vfio_pci_config_rw(vdev, buf, count, ppos, iswrite);
382 ppos, iswrite); 381
383 case VFIO_PCI_ROM_REGION_INDEX: 382 case VFIO_PCI_ROM_REGION_INDEX:
384 if (iswrite) 383 if (iswrite)
385 return -EINVAL; 384 return -EINVAL;
386 return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); 385 return vfio_pci_bar_rw(vdev, buf, count, ppos, false);
387 386
388 case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: 387 case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
389 { 388 return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite);
390 unsigned long flags = pci_resource_flags(pdev, index);
391
392 if (flags & IORESOURCE_IO)
393 return vfio_pci_io_readwrite(vdev, buf, count,
394 ppos, iswrite);
395 if (flags & IORESOURCE_MEM)
396 return vfio_pci_mem_readwrite(vdev, buf, count,
397 ppos, iswrite);
398 }
399 } 389 }
400 390
401 return -EINVAL; 391 return -EINVAL;
@@ -404,13 +394,19 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
404static ssize_t vfio_pci_read(void *device_data, char __user *buf, 394static ssize_t vfio_pci_read(void *device_data, char __user *buf,
405 size_t count, loff_t *ppos) 395 size_t count, loff_t *ppos)
406{ 396{
397 if (!count)
398 return 0;
399
407 return vfio_pci_rw(device_data, buf, count, ppos, false); 400 return vfio_pci_rw(device_data, buf, count, ppos, false);
408} 401}
409 402
410static ssize_t vfio_pci_write(void *device_data, const char __user *buf, 403static ssize_t vfio_pci_write(void *device_data, const char __user *buf,
411 size_t count, loff_t *ppos) 404 size_t count, loff_t *ppos)
412{ 405{
413 return vfio_pci_rw(device_data, buf, count, ppos, true); 406 if (!count)
407 return 0;
408
409 return vfio_pci_rw(device_data, (char __user *)buf, count, ppos, true);
414} 410}
415 411
416static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) 412static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index c975d91e1ccc..f1dde2c0fe99 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -1501,9 +1501,8 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
1501 return ret; 1501 return ret;
1502} 1502}
1503 1503
1504ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, 1504ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, char __user *buf,
1505 char __user *buf, size_t count, 1505 size_t count, loff_t *ppos, bool iswrite)
1506 loff_t *ppos, bool iswrite)
1507{ 1506{
1508 size_t done = 0; 1507 size_t done = 0;
1509 int ret = 0; 1508 int ret = 0;
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
index 611827cba8cd..00d19b953ce4 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -70,15 +70,12 @@ extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev,
70 uint32_t flags, unsigned index, 70 uint32_t flags, unsigned index,
71 unsigned start, unsigned count, void *data); 71 unsigned start, unsigned count, void *data);
72 72
73extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, 73extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev,
74 char __user *buf, size_t count, 74 char __user *buf, size_t count,
75 loff_t *ppos, bool iswrite); 75 loff_t *ppos, bool iswrite);
76extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, 76
77 char __user *buf, size_t count, 77extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
78 loff_t *ppos, bool iswrite); 78 size_t count, loff_t *ppos, bool iswrite);
79extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev,
80 char __user *buf, size_t count,
81 loff_t *ppos, bool iswrite);
82 79
83extern int vfio_pci_init_perm_bits(void); 80extern int vfio_pci_init_perm_bits(void);
84extern void vfio_pci_uninit_perm_bits(void); 81extern void vfio_pci_uninit_perm_bits(void);
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index f72323ef618f..e9d78eb91ed7 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -20,250 +20,158 @@
20 20
21#include "vfio_pci_private.h" 21#include "vfio_pci_private.h"
22 22
23/* I/O Port BAR access */ 23/*
24ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf, 24 * Read or write from an __iomem region (MMIO or I/O port) with an excluded
25 size_t count, loff_t *ppos, bool iswrite) 25 * range which is inaccessible. The excluded range drops writes and fills
26 * reads with -1. This is intended for handling MSI-X vector tables and
27 * leftover space for ROM BARs.
28 */
29static ssize_t do_io_rw(void __iomem *io, char __user *buf,
30 loff_t off, size_t count, size_t x_start,
31 size_t x_end, bool iswrite)
26{ 32{
27 struct pci_dev *pdev = vdev->pdev; 33 ssize_t done = 0;
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 34
56 while (count) { 35 while (count) {
57 int filled; 36 size_t fillable, filled;
37
38 if (off < x_start)
39 fillable = min(count, (size_t)(x_start - off));
40 else if (off >= x_end)
41 fillable = count;
42 else
43 fillable = 0;
58 44
59 if (count >= 3 && !(pos % 4)) { 45 if (fillable >= 4 && !(off % 4)) {
60 __le32 val; 46 __le32 val;
61 47
62 if (iswrite) { 48 if (iswrite) {
63 if (copy_from_user(&val, buf, 4)) 49 if (copy_from_user(&val, buf, 4))
64 return -EFAULT; 50 return -EFAULT;
65 51
66 iowrite32(le32_to_cpu(val), io + pos); 52 iowrite32(le32_to_cpu(val), io + off);
67 } else { 53 } else {
68 val = cpu_to_le32(ioread32(io + pos)); 54 val = cpu_to_le32(ioread32(io + off));
69 55
70 if (copy_to_user(buf, &val, 4)) 56 if (copy_to_user(buf, &val, 4))
71 return -EFAULT; 57 return -EFAULT;
72 } 58 }
73 59
74 filled = 4; 60 filled = 4;
75 61 } else if (fillable >= 2 && !(off % 2)) {
76 } else if ((pos % 2) == 0 && count >= 2) {
77 __le16 val; 62 __le16 val;
78 63
79 if (iswrite) { 64 if (iswrite) {
80 if (copy_from_user(&val, buf, 2)) 65 if (copy_from_user(&val, buf, 2))
81 return -EFAULT; 66 return -EFAULT;
82 67
83 iowrite16(le16_to_cpu(val), io + pos); 68 iowrite16(le16_to_cpu(val), io + off);
84 } else { 69 } else {
85 val = cpu_to_le16(ioread16(io + pos)); 70 val = cpu_to_le16(ioread16(io + off));
86 71
87 if (copy_to_user(buf, &val, 2)) 72 if (copy_to_user(buf, &val, 2))
88 return -EFAULT; 73 return -EFAULT;
89 } 74 }
90 75
91 filled = 2; 76 filled = 2;
92 } else { 77 } else if (fillable) {
93 u8 val; 78 u8 val;
94 79
95 if (iswrite) { 80 if (iswrite) {
96 if (copy_from_user(&val, buf, 1)) 81 if (copy_from_user(&val, buf, 1))
97 return -EFAULT; 82 return -EFAULT;
98 83
99 iowrite8(val, io + pos); 84 iowrite8(val, io + off);
100 } else { 85 } else {
101 val = ioread8(io + pos); 86 val = ioread8(io + off);
102 87
103 if (copy_to_user(buf, &val, 1)) 88 if (copy_to_user(buf, &val, 1))
104 return -EFAULT; 89 return -EFAULT;
105 } 90 }
106 91
107 filled = 1; 92 filled = 1;
93 } else {
94 /* Fill reads with -1, drop writes */
95 filled = min(count, (size_t)(x_end - off));
96 if (!iswrite) {
97 u8 val = 0xFF;
98 size_t i;
99
100 for (i = 0; i < filled; i++)
101 if (copy_to_user(buf + i, &val, 1))
102 return -EFAULT;
103 }
108 } 104 }
109 105
110 count -= filled; 106 count -= filled;
111 done += filled; 107 done += filled;
108 off += filled;
112 buf += filled; 109 buf += filled;
113 pos += filled;
114 } 110 }
115 111
116 *ppos += done;
117
118 return done; 112 return done;
119} 113}
120 114
121/* 115ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
122 * MMIO BAR access 116 size_t count, loff_t *ppos, bool iswrite)
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 */
127ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf,
128 size_t count, loff_t *ppos, bool iswrite)
129{ 117{
130 struct pci_dev *pdev = vdev->pdev; 118 struct pci_dev *pdev = vdev->pdev;
131 loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 119 loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
132 int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 120 int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
133 void __iomem *io; 121 size_t x_start = 0, x_end = 0;
134 resource_size_t end; 122 resource_size_t end;
135 size_t done = 0; 123 void __iomem *io;
136 size_t x_start = 0, x_end = 0; /* excluded range */ 124 ssize_t done;
137 125
138 if (!pci_resource_start(pdev, bar)) 126 if (!pci_resource_start(pdev, bar))
139 return -EINVAL; 127 return -EINVAL;
140 128
141 end = pci_resource_len(pdev, bar); 129 end = pci_resource_len(pdev, bar);
142 130
143 if (pos > end) 131 if (pos >= end)
144 return -EINVAL; 132 return -EINVAL;
145 133
146 if (pos == end) 134 count = min(count, (size_t)(end - pos));
147 return 0;
148
149 if (pos + count > end)
150 count = end - pos;
151 135
152 if (bar == PCI_ROM_RESOURCE) { 136 if (bar == PCI_ROM_RESOURCE) {
137 /*
138 * The ROM can fill less space than the BAR, so we start the
139 * excluded range at the end of the actual ROM. This makes
140 * filling large ROM BARs much faster.
141 */
153 io = pci_map_rom(pdev, &x_start); 142 io = pci_map_rom(pdev, &x_start);
143 if (!io)
144 return -ENOMEM;
154 x_end = end; 145 x_end = end;
155 } else { 146 } else if (!vdev->barmap[bar]) {
156 if (!vdev->barmap[bar]) { 147 int ret;
157 int ret;
158
159 ret = pci_request_selected_regions(pdev, 1 << bar,
160 "vfio");
161 if (ret)
162 return ret;
163 148
164 vdev->barmap[bar] = pci_iomap(pdev, bar, 0); 149 ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
150 if (ret)
151 return ret;
165 152
166 if (!vdev->barmap[bar]) { 153 io = pci_iomap(pdev, bar, 0);
167 pci_release_selected_regions(pdev, 1 << bar); 154 if (!io) {
168 return -EINVAL; 155 pci_release_selected_regions(pdev, 1 << bar);
169 } 156 return -ENOMEM;
170 } 157 }
171 158
159 vdev->barmap[bar] = io;
160 } else
172 io = vdev->barmap[bar]; 161 io = vdev->barmap[bar];
173 162
174 if (bar == vdev->msix_bar) { 163 if (bar == vdev->msix_bar) {
175 x_start = vdev->msix_offset; 164 x_start = vdev->msix_offset;
176 x_end = vdev->msix_offset + vdev->msix_size; 165 x_end = vdev->msix_offset + vdev->msix_size;
177 }
178 } 166 }
179 167
180 if (!io) 168 done = do_io_rw(io, buf, pos, count, x_start, x_end, iswrite);
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 169
248 for (i = 0; i < filled; i++) { 170 if (done >= 0)
249 if (put_user(val, buf + i)) 171 *ppos += done;
250 goto out;
251 }
252 }
253 172
254 }
255
256 count -= filled;
257 done += filled;
258 buf += filled;
259 pos += filled;
260 }
261
262 *ppos += done;
263
264out:
265 if (bar == PCI_ROM_RESOURCE) 173 if (bar == PCI_ROM_RESOURCE)
266 pci_unmap_rom(pdev, io); 174 pci_unmap_rom(pdev, io);
267 175
268 return count ? -EFAULT : done; 176 return done;
269} 177}