Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vfio_pci_rdwr.c
Go to the documentation of this file.
1 /*
2  * VFIO PCI I/O Port & MMIO access
3  *
4  * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
5  * Author: Alex Williamson <[email protected]>
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, [email protected]
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  */
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;
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 
264 out:
265  if (bar == PCI_ROM_RESOURCE)
266  pci_unmap_rom(pdev, io);
267 
268  return count ? -EFAULT : done;
269 }