aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntonios Motakis <a.motakis@virtualopensystems.com>2015-03-16 16:08:47 -0400
committerAlex Williamson <alex.williamson@redhat.com>2015-03-16 16:08:47 -0400
commit6e3f264560099869f68830cb14b3b3e71e5ac76a (patch)
tree2b7ee9a62d4f88fd647b1084bd0f3f3353275df3
parente8909e67cac3ad3868dc86cc6b1445f39c71bf63 (diff)
vfio/platform: read and write support for the device fd
VFIO returns a file descriptor which we can use to manipulate the memory regions of the device. Usually, the user will mmap memory regions that are addressable on page boundaries, however for memory regions where this is not the case we cannot provide mmap functionality due to security concerns. For this reason we also allow to use read and write functions to the file descriptor pointing to the memory regions. We implement this functionality only for MMIO regions of platform devices; PIO regions are not being handled at this point. Signed-off-by: Antonios Motakis <a.motakis@virtualopensystems.com> Signed-off-by: Baptiste Reynal <b.reynal@virtualopensystems.com> Reviewed-by: Eric Auger <eric.auger@linaro.org> Tested-by: Eric Auger <eric.auger@linaro.org> Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
-rw-r--r--drivers/vfio/platform/vfio_platform_common.c150
-rw-r--r--drivers/vfio/platform/vfio_platform_private.h1
2 files changed, 151 insertions, 0 deletions
diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c
index 47f6309b4889..4df66f5fb313 100644
--- a/drivers/vfio/platform/vfio_platform_common.c
+++ b/drivers/vfio/platform/vfio_platform_common.c
@@ -51,6 +51,10 @@ static int vfio_platform_regions_init(struct vfio_platform_device *vdev)
51 switch (resource_type(res)) { 51 switch (resource_type(res)) {
52 case IORESOURCE_MEM: 52 case IORESOURCE_MEM:
53 vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_MMIO; 53 vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_MMIO;
54 vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ;
55 if (!(res->flags & IORESOURCE_READONLY))
56 vdev->regions[i].flags |=
57 VFIO_REGION_INFO_FLAG_WRITE;
54 break; 58 break;
55 case IORESOURCE_IO: 59 case IORESOURCE_IO:
56 vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_PIO; 60 vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_PIO;
@@ -70,6 +74,11 @@ err:
70 74
71static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev) 75static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev)
72{ 76{
77 int i;
78
79 for (i = 0; i < vdev->num_regions; i++)
80 iounmap(vdev->regions[i].ioaddr);
81
73 vdev->num_regions = 0; 82 vdev->num_regions = 0;
74 kfree(vdev->regions); 83 kfree(vdev->regions);
75} 84}
@@ -172,15 +181,156 @@ static long vfio_platform_ioctl(void *device_data,
172 return -ENOTTY; 181 return -ENOTTY;
173} 182}
174 183
184static ssize_t vfio_platform_read_mmio(struct vfio_platform_region reg,
185 char __user *buf, size_t count,
186 loff_t off)
187{
188 unsigned int done = 0;
189
190 if (!reg.ioaddr) {
191 reg.ioaddr =
192 ioremap_nocache(reg.addr, reg.size);
193
194 if (!reg.ioaddr)
195 return -ENOMEM;
196 }
197
198 while (count) {
199 size_t filled;
200
201 if (count >= 4 && !(off % 4)) {
202 u32 val;
203
204 val = ioread32(reg.ioaddr + off);
205 if (copy_to_user(buf, &val, 4))
206 goto err;
207
208 filled = 4;
209 } else if (count >= 2 && !(off % 2)) {
210 u16 val;
211
212 val = ioread16(reg.ioaddr + off);
213 if (copy_to_user(buf, &val, 2))
214 goto err;
215
216 filled = 2;
217 } else {
218 u8 val;
219
220 val = ioread8(reg.ioaddr + off);
221 if (copy_to_user(buf, &val, 1))
222 goto err;
223
224 filled = 1;
225 }
226
227
228 count -= filled;
229 done += filled;
230 off += filled;
231 buf += filled;
232 }
233
234 return done;
235err:
236 return -EFAULT;
237}
238
175static ssize_t vfio_platform_read(void *device_data, char __user *buf, 239static ssize_t vfio_platform_read(void *device_data, char __user *buf,
176 size_t count, loff_t *ppos) 240 size_t count, loff_t *ppos)
177{ 241{
242 struct vfio_platform_device *vdev = device_data;
243 unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos);
244 loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK;
245
246 if (index >= vdev->num_regions)
247 return -EINVAL;
248
249 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ))
250 return -EINVAL;
251
252 if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
253 return vfio_platform_read_mmio(vdev->regions[index],
254 buf, count, off);
255 else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
256 return -EINVAL; /* not implemented */
257
178 return -EINVAL; 258 return -EINVAL;
179} 259}
180 260
261static ssize_t vfio_platform_write_mmio(struct vfio_platform_region reg,
262 const char __user *buf, size_t count,
263 loff_t off)
264{
265 unsigned int done = 0;
266
267 if (!reg.ioaddr) {
268 reg.ioaddr =
269 ioremap_nocache(reg.addr, reg.size);
270
271 if (!reg.ioaddr)
272 return -ENOMEM;
273 }
274
275 while (count) {
276 size_t filled;
277
278 if (count >= 4 && !(off % 4)) {
279 u32 val;
280
281 if (copy_from_user(&val, buf, 4))
282 goto err;
283 iowrite32(val, reg.ioaddr + off);
284
285 filled = 4;
286 } else if (count >= 2 && !(off % 2)) {
287 u16 val;
288
289 if (copy_from_user(&val, buf, 2))
290 goto err;
291 iowrite16(val, reg.ioaddr + off);
292
293 filled = 2;
294 } else {
295 u8 val;
296
297 if (copy_from_user(&val, buf, 1))
298 goto err;
299 iowrite8(val, reg.ioaddr + off);
300
301 filled = 1;
302 }
303
304 count -= filled;
305 done += filled;
306 off += filled;
307 buf += filled;
308 }
309
310 return done;
311err:
312 return -EFAULT;
313}
314
181static ssize_t vfio_platform_write(void *device_data, const char __user *buf, 315static ssize_t vfio_platform_write(void *device_data, const char __user *buf,
182 size_t count, loff_t *ppos) 316 size_t count, loff_t *ppos)
183{ 317{
318 struct vfio_platform_device *vdev = device_data;
319 unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos);
320 loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK;
321
322 if (index >= vdev->num_regions)
323 return -EINVAL;
324
325 if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE))
326 return -EINVAL;
327
328 if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
329 return vfio_platform_write_mmio(vdev->regions[index],
330 buf, count, off);
331 else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
332 return -EINVAL; /* not implemented */
333
184 return -EINVAL; 334 return -EINVAL;
185} 335}
186 336
diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h
index 3551f6d97fc3..97c78f9791f1 100644
--- a/drivers/vfio/platform/vfio_platform_private.h
+++ b/drivers/vfio/platform/vfio_platform_private.h
@@ -34,6 +34,7 @@ struct vfio_platform_region {
34 u32 type; 34 u32 type;
35#define VFIO_PLATFORM_REGION_TYPE_MMIO 1 35#define VFIO_PLATFORM_REGION_TYPE_MMIO 1
36#define VFIO_PLATFORM_REGION_TYPE_PIO 2 36#define VFIO_PLATFORM_REGION_TYPE_PIO 2
37 void __iomem *ioaddr;
37}; 38};
38 39
39struct vfio_platform_device { 40struct vfio_platform_device {