aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/vfio/pci/vfio_pci_rdwr.c
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2012-07-31 10:16:24 -0400
committerAlex Williamson <alex.williamson@redhat.com>2012-07-31 10:16:24 -0400
commit89e1f7d4c66d85f42c3d52ea3866eb10cadf6153 (patch)
tree6bea54ae5eaea48c17d309855d36d801259b64d1 /drivers/vfio/pci/vfio_pci_rdwr.c
parent73fa0d10d077d9521ee2dace2307ae2c9a965336 (diff)
vfio: Add PCI device driver
Add PCI device support for VFIO. PCI devices expose regions for accessing config space, I/O port space, and MMIO areas of the device. PCI config access is virtualized in the kernel, allowing us to ensure the integrity of the system, by preventing various accesses while reducing duplicate support across various userspace drivers. I/O port supports read/write access while MMIO also supports mmap of sufficiently sized regions. Support for INTx, MSI, and MSI-X interrupts are provided using eventfds to userspace. Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/vfio/pci/vfio_pci_rdwr.c')
-rw-r--r--drivers/vfio/pci/vfio_pci_rdwr.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
new file mode 100644
index 000000000000..4362d9e7baa3
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -0,0 +1,269 @@
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 */
24ssize_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 */
127ssize_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 if (!iswrite) {
244 char val = 0xFF;
245 size_t i;
246
247 for (i = 0; i < x_end - pos; i++) {
248 if (put_user(val, buf + i))
249 goto out;
250 }
251 }
252
253 filled = x_end - pos;
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)
266 pci_unmap_rom(pdev, io);
267
268 return count ? -EFAULT : done;
269}