Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[~shefty/rdma-dev.git] / drivers / vfio / pci / vfio_pci_rdwr.c
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 }