blob: 8954bccd93f14a65972dd180079c79463157d223 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
Damjan Marion5a206ea2016-05-12 22:11:03 +02002 * Copyright (c) 2016 Cisco and/or its affiliates.
Ed Warnickecb9cada2015-12-08 15:45:58 -07003 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 * pci.c: Linux user space PCI bus management.
17 *
18 * Copyright (c) 2008 Eliot Dresselhaus
19 *
20 * Permission is hereby granted, free of charge, to any person obtaining
21 * a copy of this software and associated documentation files (the
22 * "Software"), to deal in the Software without restriction, including
23 * without limitation the rights to use, copy, modify, merge, publish,
24 * distribute, sublicense, and/or sell copies of the Software, and to
25 * permit persons to whom the Software is furnished to do so, subject to
26 * the following conditions:
27 *
28 * The above copyright notice and this permission notice shall be
29 * included in all copies or substantial portions of the Software.
30 *
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 */
39
Damjan Marion01914ce2017-09-14 19:04:50 +020040#include <vppinfra/linux/sysfs.h>
41
Ed Warnickecb9cada2015-12-08 15:45:58 -070042#include <vlib/vlib.h>
43#include <vlib/pci/pci.h>
44#include <vlib/unix/unix.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070045
46#include <sys/types.h>
47#include <sys/stat.h>
48#include <fcntl.h>
49#include <dirent.h>
Damjan Mariona42cd342016-04-13 18:03:20 +020050#include <sys/ioctl.h>
51#include <net/if.h>
52#include <linux/ethtool.h>
53#include <linux/sockios.h>
Damjan Marioncef87f12017-10-05 15:32:41 +020054#include <linux/vfio.h>
55#include <sys/eventfd.h>
56
57static const char *sysfs_pci_dev_path = "/sys/bus/pci/devices";
58static const char *sysfs_pci_drv_path = "/sys/bus/pci/drivers";
Damjan Marion2752b892017-12-11 15:55:56 +010059static char *sysfs_mod_vfio_noiommu =
60 "/sys/module/vfio/parameters/enable_unsafe_noiommu_mode";
Damjan Mariona42cd342016-04-13 18:03:20 +020061
Dave Barach9b8ffd92016-07-08 08:13:45 -040062typedef struct
63{
Damjan Marioncef87f12017-10-05 15:32:41 +020064 vlib_pci_dev_handle_t handle;
65 vlib_pci_addr_t addr;
Damjan Marion5a206ea2016-05-12 22:11:03 +020066
67 /* Resource file descriptors. */
Dave Barach9b8ffd92016-07-08 08:13:45 -040068 int *resource_fds;
Damjan Marion5a206ea2016-05-12 22:11:03 +020069
70 /* File descriptor for config space read/write. */
71 int config_fd;
Damjan Marionf9a968e2018-02-25 23:26:22 +010072 u64 config_offset;
Damjan Marion5a206ea2016-05-12 22:11:03 +020073
Damjan Marionf9a968e2018-02-25 23:26:22 +010074 /* Device File descriptor */
75 int fd;
Damjan Marion5a206ea2016-05-12 22:11:03 +020076
77 /* Minor device for uio device. */
78 u32 uio_minor;
79
Damjan Marion56dd5432017-09-08 19:52:02 +020080 /* Index given by clib_file_add. */
81 u32 clib_file_index;
Damjan Marion5a206ea2016-05-12 22:11:03 +020082
Damjan Marioncef87f12017-10-05 15:32:41 +020083 /* Interrupt handler */
84 void (*interrupt_handler) (vlib_pci_dev_handle_t h);
85
86 /* private data */
87 uword private_data;
88
Damjan Marion5a206ea2016-05-12 22:11:03 +020089} linux_pci_device_t;
90
Damjan Marioncef87f12017-10-05 15:32:41 +020091typedef struct
92{
93 int group;
94 int fd;
95 int refcnt;
96} linux_pci_vfio_iommu_group_t;
97
Damjan Marion5a206ea2016-05-12 22:11:03 +020098/* Pool of PCI devices. */
Dave Barach9b8ffd92016-07-08 08:13:45 -040099typedef struct
100{
101 vlib_main_t *vlib_main;
102 linux_pci_device_t *linux_pci_devices;
Damjan Marioncef87f12017-10-05 15:32:41 +0200103
104 /* VFIO */
105 int vfio_container_fd;
106 int vfio_iommu_mode;
107
108 /* pool of IOMMU groups */
109 linux_pci_vfio_iommu_group_t *iommu_groups;
110
111 /* iommu group pool index by group id hash */
112 uword *iommu_pool_index_by_group;
113
Damjan Marion5a206ea2016-05-12 22:11:03 +0200114} linux_pci_main_t;
115
116extern linux_pci_main_t linux_pci_main;
117
Damjan Marioncef87f12017-10-05 15:32:41 +0200118linux_pci_device_t *
119linux_pci_get_device (vlib_pci_dev_handle_t h)
120{
121 linux_pci_main_t *lpm = &linux_pci_main;
122 return pool_elt_at_index (lpm->linux_pci_devices, h);
123}
124
125uword
126vlib_pci_get_private_data (vlib_pci_dev_handle_t h)
127{
128 linux_pci_device_t *d = linux_pci_get_device (h);
129 return d->private_data;
130}
131
132void
133vlib_pci_set_private_data (vlib_pci_dev_handle_t h, uword private_data)
134{
135 linux_pci_device_t *d = linux_pci_get_device (h);
136 d->private_data = private_data;
137}
138
139vlib_pci_addr_t *
140vlib_pci_get_addr (vlib_pci_dev_handle_t h)
141{
142 linux_pci_device_t *lpm = linux_pci_get_device (h);
143 return &lpm->addr;
144}
145
Damjan Marion5a206ea2016-05-12 22:11:03 +0200146/* Call to allocate/initialize the pci subsystem.
147 This is not an init function so that users can explicitly enable
148 pci only when it's needed. */
Dave Barach9b8ffd92016-07-08 08:13:45 -0400149clib_error_t *pci_bus_init (vlib_main_t * vm);
Damjan Marion5a206ea2016-05-12 22:11:03 +0200150
Ed Warnickecb9cada2015-12-08 15:45:58 -0700151linux_pci_main_t linux_pci_main;
152
Damjan Marioncef87f12017-10-05 15:32:41 +0200153vlib_pci_device_info_t *
154vlib_pci_get_device_info (vlib_pci_addr_t * addr, clib_error_t ** error)
155{
156 linux_pci_main_t *lpm = &linux_pci_main;
157 clib_error_t *err;
158 vlib_pci_device_info_t *di;
159 u8 *f = 0;
160 u32 tmp;
161 int fd;
162
163 di = clib_mem_alloc (sizeof (vlib_pci_device_info_t));
164 memset (di, 0, sizeof (vlib_pci_device_info_t));
165 di->addr.as_u32 = addr->as_u32;
166
167 u8 *dev_dir_name = format (0, "%s/%U", sysfs_pci_dev_path,
168 format_vlib_pci_addr, addr);
169
170 f = format (0, "%v/config%c", dev_dir_name, 0);
171 fd = open ((char *) f, O_RDWR);
172
173 /* Try read-only access if write fails. */
174 if (fd < 0)
175 fd = open ((char *) f, O_RDONLY);
176
177 if (fd < 0)
178 {
179 err = clib_error_return_unix (0, "open `%s'", f);
180 goto error;
181 }
182
183 /* You can only read more that 64 bytes of config space as root; so we try to
184 read the full space but fall back to just the first 64 bytes. */
185 if (read (fd, &di->config_data, sizeof (di->config_data)) !=
186 sizeof (di->config_data)
187 && read (fd, &di->config0,
188 sizeof (di->config0)) != sizeof (di->config0))
189 {
190 err = clib_error_return_unix (0, "read `%s'", f);
191 close (fd);
192 goto error;
193 }
194
195 {
196 static pci_config_header_t all_ones;
197 if (all_ones.vendor_id == 0)
198 memset (&all_ones, ~0, sizeof (all_ones));
199
200 if (!memcmp (&di->config0.header, &all_ones, sizeof (all_ones)))
201 {
202 err = clib_error_return (0, "invalid PCI config for `%s'", f);
203 close (fd);
204 goto error;
205 }
206 }
207
208 if (di->config0.header.header_type == 0)
209 pci_config_type0_little_to_host (&di->config0);
210 else
211 pci_config_type1_little_to_host (&di->config1);
212
213 di->numa_node = -1;
214 vec_reset_length (f);
215 f = format (f, "%v/numa_node%c", dev_dir_name, 0);
216 err = clib_sysfs_read ((char *) f, "%u", &di->numa_node);
217 if (err)
218 {
219 di->numa_node = -1;
220 clib_error_free (err);
221 }
222
223 vec_reset_length (f);
224 f = format (f, "%v/class%c", dev_dir_name, 0);
225 err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
226 if (err)
227 goto error;
228 di->device_class = tmp >> 8;
229
230 vec_reset_length (f);
231 f = format (f, "%v/vendor%c", dev_dir_name, 0);
232 err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
233 if (err)
234 goto error;
235 di->vendor_id = tmp;
236
237 vec_reset_length (f);
238 f = format (f, "%v/device%c", dev_dir_name, 0);
239 err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
240 if (err)
241 goto error;
242 di->device_id = tmp;
243
244 vec_reset_length (f);
245 f = format (f, "%v/driver%c", dev_dir_name, 0);
246 di->driver_name = clib_sysfs_link_to_name ((char *) f);
247
248 di->iommu_group = -1;
249 if (lpm->vfio_container_fd != -1)
250 {
251 u8 *tmpstr;
252 vec_reset_length (f);
253 f = format (f, "%v/iommu_group%c", dev_dir_name, 0);
254 tmpstr = clib_sysfs_link_to_name ((char *) f);
255 if (tmpstr)
256 {
257 di->iommu_group = atoi ((char *) tmpstr);
258 vec_free (tmpstr);
259 }
260 }
261
Damjan Marion96504182017-12-10 23:54:46 +0100262 close (fd);
263
Damjan Marioncef87f12017-10-05 15:32:41 +0200264 vec_reset_length (f);
265 f = format (f, "%v/vpd%c", dev_dir_name, 0);
266 fd = open ((char *) f, O_RDONLY);
267 if (fd >= 0)
268 {
269 while (1)
270 {
271 u8 tag[3];
272 u8 *data = 0;
Damjan Marion96504182017-12-10 23:54:46 +0100273 uword len;
Damjan Marioncef87f12017-10-05 15:32:41 +0200274
275 if (read (fd, &tag, 3) != 3)
276 break;
277
278 if (tag[0] != 0x82 && tag[0] != 0x90 && tag[0] != 0x91)
279 break;
280
281 len = (tag[2] << 8) | tag[1];
282 vec_validate (data, len);
283
284 if (read (fd, data, len) != len)
285 {
286 vec_free (data);
287 break;
288 }
289 if (tag[0] == 0x82)
290 di->product_name = data;
291 else if (tag[0] == 0x90)
292 di->vpd_r = data;
293 else if (tag[0] == 0x91)
294 di->vpd_w = data;
295
296 data = 0;
297 }
298 close (fd);
299 }
300
301 goto done;
302
303error:
304 vlib_pci_free_device_info (di);
305 di = 0;
306
307done:
308 vec_free (f);
309 vec_free (dev_dir_name);
310 if (error)
311 *error = err;
312 else
313 clib_error_free (err);
314 return di;
315}
316
Damjan Marion2752b892017-12-11 15:55:56 +0100317static int
318directory_exists (char *path)
319{
320 struct stat s = { 0 };
321 if (stat (path, &s) == -1)
322 return 0;
323
324 return S_ISDIR (s.st_mode);
325}
326
Damjan Mariona42cd342016-04-13 18:03:20 +0200327clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200328vlib_pci_bind_to_uio (vlib_pci_addr_t * addr, char *uio_drv_name)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700329{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400330 clib_error_t *error = 0;
Damjan Mariona7f74572017-05-23 18:32:21 +0200331 u8 *s = 0, *driver_name = 0;
Damjan Mariona42cd342016-04-13 18:03:20 +0200332 DIR *dir = 0;
333 struct dirent *e;
Damjan Marioncef87f12017-10-05 15:32:41 +0200334 vlib_pci_device_info_t *di;
Damjan Mariona7f74572017-05-23 18:32:21 +0200335 int fd, clear_driver_override = 0;
Damjan Marioncef87f12017-10-05 15:32:41 +0200336 u8 *dev_dir_name = format (0, "%s/%U", sysfs_pci_dev_path,
337 format_vlib_pci_addr, addr);
338
339 di = vlib_pci_get_device_info (addr, &error);
340
341 if (error)
342 return error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700343
Damjan Marion2752b892017-12-11 15:55:56 +0100344 if (strncmp ("auto", uio_drv_name, 5) == 0)
345 {
346 int vfio_pci_loaded = 0;
347
348 if (directory_exists ("/sys/module/vfio_pci"))
349 vfio_pci_loaded = 1;
350
351 if (di->iommu_group != -1)
352 {
353 /* device is bound to IOMMU group */
354 if (!vfio_pci_loaded)
355 {
356 error = clib_error_return (0, "Skipping PCI device %U: device "
357 "is bound to IOMMU group and "
358 "vfio-pci driver is not loaded",
359 format_vlib_pci_addr, addr);
360 goto done;
361 }
362 else
363 uio_drv_name = "vfio-pci";
364 }
365 else
366 {
367 /* device is not bound to IOMMU group so we have multiple options */
368 if (vfio_pci_loaded &&
369 (error = clib_sysfs_write (sysfs_mod_vfio_noiommu, "Y")) == 0)
370 uio_drv_name = "vfio-pci";
371 else if (directory_exists ("/sys/module/uio_pci_generic"))
372 uio_drv_name = "uio_pci_generic";
373 else if (directory_exists ("/sys/module/igb_uio"))
374 uio_drv_name = "igb_uio";
375 else
376 {
377 clib_error_free (error);
378 error = clib_error_return (0, "Skipping PCI device %U: missing "
379 "kernel VFIO or UIO driver",
380 format_vlib_pci_addr, addr);
381 goto done;
382 }
383 clib_error_free (error);
384 }
385 }
386
Damjan Mariona7f74572017-05-23 18:32:21 +0200387 s = format (s, "%v/driver%c", dev_dir_name, 0);
Damjan Marion01914ce2017-09-14 19:04:50 +0200388 driver_name = clib_sysfs_link_to_name ((char *) s);
Damjan Mariona42cd342016-04-13 18:03:20 +0200389 vec_reset_length (s);
390
Damjan Mariona7f74572017-05-23 18:32:21 +0200391 if (driver_name &&
392 ((strcmp ("vfio-pci", (char *) driver_name) == 0) ||
393 (strcmp ("uio_pci_generic", (char *) driver_name) == 0) ||
394 (strcmp ("igb_uio", (char *) driver_name) == 0)))
Damjan Marion3c785e02017-05-08 18:37:54 +0200395 goto done;
Damjan Marion3c785e02017-05-08 18:37:54 +0200396
Damjan Mariona42cd342016-04-13 18:03:20 +0200397 /* walk trough all linux interfaces and if interface belonging to
398 this device is founf check if interface is admin up */
399 dir = opendir ("/sys/class/net");
Damjan Marioncef87f12017-10-05 15:32:41 +0200400 s = format (s, "%U%c", format_vlib_pci_addr, addr, 0);
Damjan Mariona42cd342016-04-13 18:03:20 +0200401
402 if (!dir)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700403 {
Damjan Mariona42cd342016-04-13 18:03:20 +0200404 error = clib_error_return (0, "Skipping PCI device %U: failed to "
405 "read /sys/class/net",
Damjan Marioncef87f12017-10-05 15:32:41 +0200406 format_vlib_pci_addr, addr);
Damjan Mariona42cd342016-04-13 18:03:20 +0200407 goto done;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700408 }
409
Dave Barach9b8ffd92016-07-08 08:13:45 -0400410 fd = socket (PF_INET, SOCK_DGRAM, 0);
Chris Luke370e9e32016-07-07 11:01:17 -0400411 if (fd < 0)
412 {
413 error = clib_error_return_unix (0, "socket");
414 goto done;
415 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200416
Dave Barach9b8ffd92016-07-08 08:13:45 -0400417 while ((e = readdir (dir)))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700418 {
Damjan Mariona42cd342016-04-13 18:03:20 +0200419 struct ifreq ifr;
420 struct ethtool_drvinfo drvinfo;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700421
Dave Barach9b8ffd92016-07-08 08:13:45 -0400422 if (e->d_name[0] == '.') /* skip . and .. */
Damjan Mariona42cd342016-04-13 18:03:20 +0200423 continue;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700424
Dave Barach9b8ffd92016-07-08 08:13:45 -0400425 memset (&ifr, 0, sizeof ifr);
426 memset (&drvinfo, 0, sizeof drvinfo);
Damjan Mariona42cd342016-04-13 18:03:20 +0200427 ifr.ifr_data = (char *) &drvinfo;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400428 strncpy (ifr.ifr_name, e->d_name, IFNAMSIZ - 1);
Damjan Mariona42cd342016-04-13 18:03:20 +0200429 drvinfo.cmd = ETHTOOL_GDRVINFO;
Chris Luke370e9e32016-07-07 11:01:17 -0400430 if (ioctl (fd, SIOCETHTOOL, &ifr) < 0)
Dave Barach9b8ffd92016-07-08 08:13:45 -0400431 {
John Loe2821212016-07-13 18:13:02 -0400432 /* Some interfaces (eg "lo") don't support this ioctl */
433 if ((errno != ENOTSUP) && (errno != ENODEV))
Ed Warnicke853e7202016-08-12 11:42:26 -0700434 clib_unix_warning ("ioctl fetch intf %s bus info error",
435 e->d_name);
John Loe2821212016-07-13 18:13:02 -0400436 continue;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400437 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200438
439 if (strcmp ((char *) s, drvinfo.bus_info))
440 continue;
441
Dave Barach9b8ffd92016-07-08 08:13:45 -0400442 memset (&ifr, 0, sizeof (ifr));
Chris Luke370e9e32016-07-07 11:01:17 -0400443 strncpy (ifr.ifr_name, e->d_name, IFNAMSIZ - 1);
444 if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
Dave Barach9b8ffd92016-07-08 08:13:45 -0400445 {
446 error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
447 e->d_name);
448 close (fd);
449 goto done;
450 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200451
452 if (ifr.ifr_flags & IFF_UP)
453 {
454 error = clib_error_return (0, "Skipping PCI device %U as host "
455 "interface %s is up",
Damjan Marioncef87f12017-10-05 15:32:41 +0200456 format_vlib_pci_addr, addr, e->d_name);
Dave Barach9b8ffd92016-07-08 08:13:45 -0400457 close (fd);
Damjan Mariona42cd342016-04-13 18:03:20 +0200458 goto done;
459 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700460 }
461
Damjan Mariona42cd342016-04-13 18:03:20 +0200462 close (fd);
463 vec_reset_length (s);
464
465 s = format (s, "%v/driver/unbind%c", dev_dir_name, 0);
Damjan Marioncef87f12017-10-05 15:32:41 +0200466 clib_sysfs_write ((char *) s, "%U", format_vlib_pci_addr, addr);
Damjan Mariona42cd342016-04-13 18:03:20 +0200467 vec_reset_length (s);
468
Damjan Mariona7f74572017-05-23 18:32:21 +0200469 s = format (s, "%v/driver_override%c", dev_dir_name, 0);
470 if (access ((char *) s, F_OK) == 0)
471 {
Damjan Marioncef87f12017-10-05 15:32:41 +0200472 clib_sysfs_write ((char *) s, "%s", uio_drv_name);
Damjan Mariona7f74572017-05-23 18:32:21 +0200473 clear_driver_override = 1;
474 }
475 else
476 {
477 vec_reset_length (s);
Damjan Marioncef87f12017-10-05 15:32:41 +0200478 s = format (s, "%s/%s/new_id%c", sysfs_pci_drv_path, uio_drv_name, 0);
479 clib_sysfs_write ((char *) s, "0x%04x 0x%04x", di->vendor_id,
480 di->device_id);
Damjan Mariona7f74572017-05-23 18:32:21 +0200481 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200482 vec_reset_length (s);
483
Damjan Marioncef87f12017-10-05 15:32:41 +0200484 s = format (s, "%s/%s/bind%c", sysfs_pci_drv_path, uio_drv_name, 0);
485 clib_sysfs_write ((char *) s, "%U", format_vlib_pci_addr, addr);
Damjan Mariona7f74572017-05-23 18:32:21 +0200486 vec_reset_length (s);
487
488 if (clear_driver_override)
489 {
490 s = format (s, "%v/driver_override%c", dev_dir_name, 0);
Damjan Marion01914ce2017-09-14 19:04:50 +0200491 clib_sysfs_write ((char *) s, "%c", 0);
Damjan Mariona7f74572017-05-23 18:32:21 +0200492 vec_reset_length (s);
493 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200494
495done:
496 closedir (dir);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700497 vec_free (s);
Damjan Mariona42cd342016-04-13 18:03:20 +0200498 vec_free (dev_dir_name);
Damjan Mariona7f74572017-05-23 18:32:21 +0200499 vec_free (driver_name);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700500 return error;
501}
502
Ed Warnickecb9cada2015-12-08 15:45:58 -0700503
504static clib_error_t *
Dave Barach9b8ffd92016-07-08 08:13:45 -0400505scan_uio_dir (void *arg, u8 * path_name, u8 * file_name)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700506{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400507 linux_pci_device_t *l = arg;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700508 unformat_input_t input;
509
510 unformat_init_string (&input, (char *) file_name, vec_len (file_name));
511
Dave Barach9b8ffd92016-07-08 08:13:45 -0400512 if (!unformat (&input, "uio%d", &l->uio_minor))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700513 abort ();
514
515 unformat_free (&input);
516 return 0;
517}
518
Dave Barach9b8ffd92016-07-08 08:13:45 -0400519static clib_error_t *
Damjan Marion56dd5432017-09-08 19:52:02 +0200520linux_pci_uio_read_ready (clib_file_t * uf)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700521{
Damjan Marion5a206ea2016-05-12 22:11:03 +0200522 int __attribute__ ((unused)) rv;
Damjan Marioncef87f12017-10-05 15:32:41 +0200523 vlib_pci_dev_handle_t h = uf->private_data;
524 linux_pci_device_t *p = linux_pci_get_device (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700525
Damjan Marion5a206ea2016-05-12 22:11:03 +0200526 u32 icount;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400527 rv = read (uf->file_descriptor, &icount, 4);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700528
Damjan Marioncef87f12017-10-05 15:32:41 +0200529 if (p->interrupt_handler)
530 p->interrupt_handler (h);
Damjan Marion5a206ea2016-05-12 22:11:03 +0200531
Damjan Marioncef87f12017-10-05 15:32:41 +0200532 vlib_pci_intr_enable (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700533
534 return /* no error */ 0;
535}
536
Dave Barach9b8ffd92016-07-08 08:13:45 -0400537static clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200538linux_pci_vfio_unmask_intx (linux_pci_device_t * d)
539{
540 clib_error_t *err = 0;
541 struct vfio_irq_set i = {
542 .argsz = sizeof (struct vfio_irq_set),
543 .start = 0,
544 .count = 1,
545 .index = VFIO_PCI_INTX_IRQ_INDEX,
546 .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK,
547 };
548
Damjan Marionf9a968e2018-02-25 23:26:22 +0100549 if (ioctl (d->fd, VFIO_DEVICE_SET_IRQS, &i) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200550 {
551 err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_SET_IRQS) '%U'",
552 format_vlib_pci_addr, &d->addr);
553 }
554 return err;
555}
556
557static clib_error_t *
Damjan Marion56dd5432017-09-08 19:52:02 +0200558linux_pci_uio_error_ready (clib_file_t * uf)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700559{
560 u32 error_index = (u32) uf->private_data;
561
562 return clib_error_return (0, "pci device %d: error", error_index);
563}
564
Damjan Marioncef87f12017-10-05 15:32:41 +0200565static clib_error_t *
566linux_pci_vfio_read_ready (clib_file_t * uf)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700567{
Damjan Marioncef87f12017-10-05 15:32:41 +0200568 int __attribute__ ((unused)) rv;
569 vlib_pci_dev_handle_t h = uf->private_data;
570 linux_pci_device_t *p = linux_pci_get_device (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700571
Damjan Marioncef87f12017-10-05 15:32:41 +0200572 u64 icount;
573 rv = read (uf->file_descriptor, &icount, sizeof (icount));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700574
Damjan Marioncef87f12017-10-05 15:32:41 +0200575 if (p->interrupt_handler)
576 p->interrupt_handler (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700577
Damjan Marioncef87f12017-10-05 15:32:41 +0200578 linux_pci_vfio_unmask_intx (p);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700579
Damjan Marioncef87f12017-10-05 15:32:41 +0200580 return /* no error */ 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700581}
582
Damjan Marioncef87f12017-10-05 15:32:41 +0200583static clib_error_t *
584linux_pci_vfio_error_ready (clib_file_t * uf)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700585{
Damjan Marioncef87f12017-10-05 15:32:41 +0200586 u32 error_index = (u32) uf->private_data;
587
588 return clib_error_return (0, "pci device %d: error", error_index);
589}
590
591static clib_error_t *
592add_device_uio (vlib_main_t * vm, linux_pci_device_t * p,
593 vlib_pci_device_info_t * di, pci_device_registration_t * r)
594{
595 clib_error_t *err = 0;
596 clib_file_t t = { 0 };
597 u8 *s = 0;
598
599 p->addr.as_u32 = di->addr.as_u32;
Damjan Marionf9a968e2018-02-25 23:26:22 +0100600 p->fd = -1;
Damjan Marioncef87f12017-10-05 15:32:41 +0200601
602 s = format (s, "%s/%U/config%c", sysfs_pci_dev_path,
603 format_vlib_pci_addr, &di->addr, 0);
604
605 p->config_fd = open ((char *) s, O_RDWR);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100606 p->config_offset = 0;
Damjan Marioncef87f12017-10-05 15:32:41 +0200607 vec_reset_length (s);
608
609 if (p->config_fd == -1)
610 {
611 err = clib_error_return_unix (0, "open '%s'", s);
612 goto error;
613 }
614
615 s = format (0, "%s/%U/uio", sysfs_pci_dev_path,
616 format_vlib_pci_addr, &di->addr);
617 foreach_directory_file ((char *) s, scan_uio_dir, p, /* scan_dirs */
618 1);
619 vec_reset_length (s);
620
621 s = format (s, "/dev/uio%d%c", p->uio_minor, 0);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100622 p->fd = open ((char *) s, O_RDWR);
623 if (p->fd < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200624 {
625 err = clib_error_return_unix (0, "open '%s'", s);
626 goto error;
627 }
628
629 t.read_function = linux_pci_uio_read_ready;
Damjan Marionf9a968e2018-02-25 23:26:22 +0100630 t.file_descriptor = p->fd;
Damjan Marioncef87f12017-10-05 15:32:41 +0200631 t.error_function = linux_pci_uio_error_ready;
632 t.private_data = p->handle;
633
634 p->clib_file_index = clib_file_add (&file_main, &t);
635 p->interrupt_handler = r->interrupt_handler;
636 err = r->init_function (vm, p->handle);
637
638error:
639 free (s);
640 if (err)
641 {
Damjan Marion96504182017-12-10 23:54:46 +0100642 if (p->config_fd != -1)
643 close (p->config_fd);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100644 if (p->fd != -1)
645 close (p->fd);
Damjan Marioncef87f12017-10-05 15:32:41 +0200646 }
647 return err;
648}
649
650static linux_pci_vfio_iommu_group_t *
651get_vfio_iommu_group (int group)
652{
653 linux_pci_main_t *lpm = &linux_pci_main;
654 uword *p;
655
656 p = hash_get (lpm->iommu_pool_index_by_group, group);
657
658 return p ? pool_elt_at_index (lpm->iommu_groups, p[0]) : 0;
659}
660
661static clib_error_t *
662open_vfio_iommu_group (int group)
663{
664 linux_pci_main_t *lpm = &linux_pci_main;
665 linux_pci_vfio_iommu_group_t *g;
666 clib_error_t *err = 0;
667 struct vfio_group_status group_status;
668 u8 *s = 0;
669 int fd;
670
671 g = get_vfio_iommu_group (group);
672 if (g)
673 {
674 g->refcnt++;
675 return 0;
676 }
677 s = format (s, "/dev/vfio/%u%c", group, 0);
678 fd = open ((char *) s, O_RDWR);
679 if (fd < 0)
Damjan Marion96504182017-12-10 23:54:46 +0100680 return clib_error_return_unix (0, "open '%s'", s);
Damjan Marioncef87f12017-10-05 15:32:41 +0200681
682 group_status.argsz = sizeof (group_status);
683 if (ioctl (fd, VFIO_GROUP_GET_STATUS, &group_status) < 0)
684 {
685 err = clib_error_return_unix (0, "ioctl(VFIO_GROUP_GET_STATUS) '%s'",
686 s);
687 goto error;
688 }
689
690 if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE))
691 {
692 err = clib_error_return (0, "iommu group %d is not viable (not all "
693 "devices in this group bound to vfio-pci)",
694 group);
695 goto error;
696 }
697
698 if (ioctl (fd, VFIO_GROUP_SET_CONTAINER, &lpm->vfio_container_fd) < 0)
699 {
700 err = clib_error_return_unix (0, "ioctl(VFIO_GROUP_SET_CONTAINER) '%s'",
701 s);
702 goto error;
703 }
704
705 if (lpm->vfio_iommu_mode == 0)
706 {
707 if (ioctl (lpm->vfio_container_fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU) <
708 0)
709 {
710 err = clib_error_return_unix (0, "ioctl(VFIO_SET_IOMMU) "
711 "'/dev/vfio/vfio'");
712 goto error;
713 }
714 lpm->vfio_iommu_mode = VFIO_TYPE1_IOMMU;
715 }
716
717
718 pool_get (lpm->iommu_groups, g);
719 g->fd = fd;
720 g->refcnt = 1;
721 hash_set (lpm->iommu_pool_index_by_group, group, g - lpm->iommu_groups);
722 vec_free (s);
723 return 0;
724error:
725 close (fd);
726 return err;
727}
728
729static clib_error_t *
730add_device_vfio (vlib_main_t * vm, linux_pci_device_t * p,
731 vlib_pci_device_info_t * di, pci_device_registration_t * r)
732{
733 linux_pci_vfio_iommu_group_t *g;
Damjan Marionf9a968e2018-02-25 23:26:22 +0100734 struct vfio_device_info device_info = { 0 };
735 struct vfio_region_info reg = { 0 };
Damjan Marioncef87f12017-10-05 15:32:41 +0200736 struct vfio_irq_info irq_info = { 0 };
737 clib_error_t *err = 0;
738 u8 *s = 0;
Damjan Marioncef87f12017-10-05 15:32:41 +0200739
740 p->addr.as_u32 = di->addr.as_u32;
741
742 if (di->driver_name == 0 ||
743 (strcmp ("vfio-pci", (char *) di->driver_name) != 0))
744 return clib_error_return (0, "Device '%U' (iommu group %d) not bound to "
745 "vfio-pci", format_vlib_pci_addr, &di->addr,
746 di->iommu_group);
747
748 if ((err = open_vfio_iommu_group (di->iommu_group)))
749 return err;
750
751 g = get_vfio_iommu_group (di->iommu_group);
752
753 s = format (s, "%U%c", format_vlib_pci_addr, &di->addr, 0);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100754 if ((p->fd = ioctl (g->fd, VFIO_GROUP_GET_DEVICE_FD, (char *) s)) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200755 {
756 err = clib_error_return_unix (0, "ioctl(VFIO_GROUP_GET_DEVICE_FD) '%U'",
757 format_vlib_pci_addr, &di->addr);
758 goto error;
759 }
760 vec_reset_length (s);
Damjan Marioncef87f12017-10-05 15:32:41 +0200761
762 device_info.argsz = sizeof (device_info);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100763 if (ioctl (p->fd, VFIO_DEVICE_GET_INFO, &device_info) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200764 {
765 err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_INFO) '%U'",
766 format_vlib_pci_addr, &di->addr);
767 goto error;
768 }
769
770 irq_info.argsz = sizeof (struct vfio_irq_info);
771 irq_info.index = VFIO_PCI_INTX_IRQ_INDEX;
Damjan Marionf9a968e2018-02-25 23:26:22 +0100772 if (ioctl (p->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200773 {
774 err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_IRQ_INFO) "
775 "'%U'", format_vlib_pci_addr, &di->addr);
776 goto error;
777 }
778
779 /* reset if device supports it */
780 if (device_info.flags & VFIO_DEVICE_FLAGS_RESET)
Damjan Marionf9a968e2018-02-25 23:26:22 +0100781 if (ioctl (p->fd, VFIO_DEVICE_RESET) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200782 {
783 err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_RESET) '%U'",
784 format_vlib_pci_addr, &di->addr);
785 goto error;
786 }
787
788 if ((irq_info.flags & VFIO_IRQ_INFO_EVENTFD) && irq_info.count == 1)
789 {
790 u8 buf[sizeof (struct vfio_irq_set) + sizeof (int)] = { 0 };
791 struct vfio_irq_set *irq_set = (struct vfio_irq_set *) buf;
792 clib_file_t t = { 0 };
793 int efd = eventfd (0, EFD_NONBLOCK);
794
795 irq_set->argsz = sizeof (buf);
796 irq_set->count = 1;
797 irq_set->index = VFIO_PCI_INTX_IRQ_INDEX;
798 irq_set->flags =
799 VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
800 clib_memcpy (&irq_set->data, &efd, sizeof (int));
801
Damjan Marionf9a968e2018-02-25 23:26:22 +0100802 if (ioctl (p->fd, VFIO_DEVICE_SET_IRQS, irq_set) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200803 {
804 err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_SET_IRQS) '%U'",
805 format_vlib_pci_addr, &di->addr);
806 goto error;
807 }
808
809 t.read_function = linux_pci_vfio_read_ready;
810 t.file_descriptor = efd;
811 t.error_function = linux_pci_vfio_error_ready;
812 t.private_data = p->handle;
813 p->clib_file_index = clib_file_add (&file_main, &t);
814
815 /* unmask the interrupt */
816 linux_pci_vfio_unmask_intx (p);
817 }
818
819 p->interrupt_handler = r->interrupt_handler;
820
Damjan Marionf9a968e2018-02-25 23:26:22 +0100821 reg.argsz = sizeof (struct vfio_region_info);
822 reg.index = VFIO_PCI_CONFIG_REGION_INDEX;
823 if (ioctl (p->fd, VFIO_DEVICE_GET_REGION_INFO, &reg) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200824 {
Damjan Marionf9a968e2018-02-25 23:26:22 +0100825 err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_REGION_INFO) "
826 "'%U'", format_vlib_pci_addr, &di->addr);
Damjan Marioncef87f12017-10-05 15:32:41 +0200827 goto error;
828 }
Damjan Marionf9a968e2018-02-25 23:26:22 +0100829 p->config_offset = reg.offset;
830 p->config_fd = p->fd;
Damjan Marioncef87f12017-10-05 15:32:41 +0200831
832 err = r->init_function (vm, p->handle);
833
834error:
835 vec_free (s);
836 if (err)
837 {
Damjan Marionf9a968e2018-02-25 23:26:22 +0100838 if (p->fd != -1)
839 close (p->fd);
Damjan Marion96504182017-12-10 23:54:46 +0100840 if (p->config_fd != -1)
841 close (p->config_fd);
Damjan Marioncef87f12017-10-05 15:32:41 +0200842 }
843 return err;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700844}
845
846/* Configuration space read/write. */
847clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200848vlib_pci_read_write_config (vlib_pci_dev_handle_t h,
Damjan Marion5a206ea2016-05-12 22:11:03 +0200849 vlib_read_or_write_t read_or_write,
Dave Barach9b8ffd92016-07-08 08:13:45 -0400850 uword address, void *data, u32 n_bytes)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700851{
Damjan Marioncef87f12017-10-05 15:32:41 +0200852 linux_pci_device_t *p = linux_pci_get_device (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700853 int n;
854
Ed Warnickecb9cada2015-12-08 15:45:58 -0700855 if (read_or_write == VLIB_READ)
Damjan Marionf9a968e2018-02-25 23:26:22 +0100856 n = pread (p->config_fd, data, n_bytes, p->config_offset + address);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700857 else
Damjan Marionf9a968e2018-02-25 23:26:22 +0100858 n = pwrite (p->config_fd, data, n_bytes, p->config_offset + address);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700859
860 if (n != n_bytes)
861 return clib_error_return_unix (0, "%s",
862 read_or_write == VLIB_READ
863 ? "read" : "write");
864
865 return 0;
866}
867
868static clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200869vlib_pci_map_resource_int (vlib_pci_dev_handle_t h,
870 u32 resource, u8 * addr, void **result)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700871{
Damjan Marioncef87f12017-10-05 15:32:41 +0200872 linux_pci_device_t *p = linux_pci_get_device (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700873 struct stat stat_buf;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400874 u8 *file_name;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700875 int fd;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400876 clib_error_t *error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700877 int flags = MAP_SHARED;
878
879 error = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700880
Damjan Marioncef87f12017-10-05 15:32:41 +0200881 file_name = format (0, "%s/%U/resource%d%c", sysfs_pci_dev_path,
882 format_vlib_pci_addr, &p->addr, resource, 0);
883
Ed Warnickecb9cada2015-12-08 15:45:58 -0700884 fd = open ((char *) file_name, O_RDWR);
885 if (fd < 0)
886 {
887 error = clib_error_return_unix (0, "open `%s'", file_name);
888 goto done;
889 }
890
891 if (fstat (fd, &stat_buf) < 0)
892 {
893 error = clib_error_return_unix (0, "fstat `%s'", file_name);
894 goto done;
895 }
896
897 vec_validate (p->resource_fds, resource);
898 p->resource_fds[resource] = fd;
899 if (addr != 0)
900 flags |= MAP_FIXED;
901
902 *result = mmap (addr,
903 /* size */ stat_buf.st_size,
Dave Barach9b8ffd92016-07-08 08:13:45 -0400904 PROT_READ | PROT_WRITE, flags,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700905 /* file */ fd,
906 /* offset */ 0);
907 if (*result == (void *) -1)
908 {
909 error = clib_error_return_unix (0, "mmap `%s'", file_name);
910 goto done;
911 }
912
Dave Barach9b8ffd92016-07-08 08:13:45 -0400913done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700914 if (error)
915 {
Chris Luke370e9e32016-07-07 11:01:17 -0400916 if (fd >= 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700917 close (fd);
918 }
919 vec_free (file_name);
920 return error;
921}
922
923clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200924vlib_pci_map_resource (vlib_pci_dev_handle_t h, u32 resource, void **result)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700925{
Damjan Marioncef87f12017-10-05 15:32:41 +0200926 return (vlib_pci_map_resource_int (h, resource, 0 /* addr */ , result));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700927}
928
929clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200930vlib_pci_map_resource_fixed (vlib_pci_dev_handle_t h,
Dave Barach9b8ffd92016-07-08 08:13:45 -0400931 u32 resource, u8 * addr, void **result)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700932{
Damjan Marioncef87f12017-10-05 15:32:41 +0200933 return (vlib_pci_map_resource_int (h, resource, addr, result));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700934}
935
Dave Barach9b8ffd92016-07-08 08:13:45 -0400936void
Damjan Marioncef87f12017-10-05 15:32:41 +0200937init_device_from_registered (vlib_main_t * vm, vlib_pci_device_info_t * di)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700938{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400939 vlib_pci_main_t *pm = &pci_main;
Damjan Marioncef87f12017-10-05 15:32:41 +0200940 linux_pci_main_t *lpm = &linux_pci_main;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400941 pci_device_registration_t *r;
942 pci_device_id_t *i;
Damjan Marioncef87f12017-10-05 15:32:41 +0200943 clib_error_t *err = 0;
944 linux_pci_device_t *p;
945
946 pool_get (lpm->linux_pci_devices, p);
947 p->handle = p - lpm->linux_pci_devices;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700948
Damjan Marion5a206ea2016-05-12 22:11:03 +0200949 r = pm->pci_device_registrations;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700950
951 while (r)
952 {
953 for (i = r->supported_devices; i->vendor_id != 0; i++)
Damjan Marion3a593822018-02-17 14:06:53 +0100954 if (i->vendor_id == di->vendor_id && i->device_id == di->device_id)
Dave Barach9b8ffd92016-07-08 08:13:45 -0400955 {
Damjan Marioncef87f12017-10-05 15:32:41 +0200956 if (di->iommu_group != -1)
957 err = add_device_vfio (vm, p, di, r);
958 else
959 err = add_device_uio (vm, p, di, r);
Damjan Marion5a206ea2016-05-12 22:11:03 +0200960
Damjan Marioncef87f12017-10-05 15:32:41 +0200961 if (err)
962 clib_error_report (err);
963 else
964 return;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400965 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700966 r = r->next_registration;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400967 }
Damjan Marioncef87f12017-10-05 15:32:41 +0200968
Ed Warnickecb9cada2015-12-08 15:45:58 -0700969 /* No driver, close the PCI config-space FD */
Damjan Marioncef87f12017-10-05 15:32:41 +0200970 memset (p, 0, sizeof (linux_pci_device_t));
971 pool_put (lpm->linux_pci_devices, p);
972}
973
974static clib_error_t *
975scan_pci_addr (void *arg, u8 * dev_dir_name, u8 * ignored)
976{
977 vlib_pci_addr_t addr, **addrv = arg;
978 unformat_input_t input;
979 clib_error_t *err = 0;
980
981 unformat_init_string (&input, (char *) dev_dir_name,
982 vec_len (dev_dir_name));
983
984 if (!unformat (&input, "/sys/bus/pci/devices/%U",
985 unformat_vlib_pci_addr, &addr))
986 err = clib_error_return (0, "unformat error `%v`", dev_dir_name);
987
988 unformat_free (&input);
989
990 if (err)
991 return err;
992
993 vec_add1 (*addrv, addr);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700994 return 0;
995}
996
Damjan Marioncef87f12017-10-05 15:32:41 +0200997static int
998pci_addr_cmp (void *v1, void *v2)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700999{
Damjan Marioncef87f12017-10-05 15:32:41 +02001000 vlib_pci_addr_t *a1 = v1;
1001 vlib_pci_addr_t *a2 = v2;
1002
1003 if (a1->domain > a2->domain)
1004 return 1;
1005 if (a1->domain < a2->domain)
1006 return -1;
1007 if (a1->bus > a2->bus)
1008 return 1;
1009 if (a1->bus < a2->bus)
1010 return -1;
1011 if (a1->slot > a2->slot)
1012 return 1;
1013 if (a1->slot < a2->slot)
1014 return -1;
1015 if (a1->function > a2->function)
1016 return 1;
1017 if (a1->function < a2->function)
1018 return -1;
1019 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001020}
1021
Damjan Marioncef87f12017-10-05 15:32:41 +02001022vlib_pci_addr_t *
1023vlib_pci_get_all_dev_addrs ()
Ed Warnickecb9cada2015-12-08 15:45:58 -07001024{
Damjan Marioncef87f12017-10-05 15:32:41 +02001025 vlib_pci_addr_t *addrs = 0;
1026 clib_error_t *err;
1027 err = foreach_directory_file ((char *) sysfs_pci_dev_path, scan_pci_addr,
1028 &addrs, /* scan_dirs */ 0);
1029 if (err)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001030 {
Damjan Marioncef87f12017-10-05 15:32:41 +02001031 vec_free (addrs);
1032 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001033 }
1034
Damjan Marioncef87f12017-10-05 15:32:41 +02001035 vec_sort_with_function (addrs, pci_addr_cmp);
Damjan Mariona42cd342016-04-13 18:03:20 +02001036
Damjan Marioncef87f12017-10-05 15:32:41 +02001037 return addrs;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001038}
1039
Dave Barach9b8ffd92016-07-08 08:13:45 -04001040clib_error_t *
1041linux_pci_init (vlib_main_t * vm)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001042{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001043 vlib_pci_main_t *pm = &pci_main;
Damjan Marioncef87f12017-10-05 15:32:41 +02001044 linux_pci_main_t *lpm = &linux_pci_main;
1045 vlib_pci_addr_t *addr = 0, *addrs;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001046 clib_error_t *error;
Damjan Marioncef87f12017-10-05 15:32:41 +02001047 int fd;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001048
1049 pm->vlib_main = vm;
1050
1051 if ((error = vlib_call_init_function (vm, unix_input_init)))
1052 return error;
1053
Dave Barach9b8ffd92016-07-08 08:13:45 -04001054 ASSERT (sizeof (vlib_pci_addr_t) == sizeof (u32));
Damjan Mariona42cd342016-04-13 18:03:20 +02001055
Damjan Marioncef87f12017-10-05 15:32:41 +02001056 fd = open ("/dev/vfio/vfio", O_RDWR);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001057
Damjan Marioncef87f12017-10-05 15:32:41 +02001058 if ((fd != -1) && (ioctl (fd, VFIO_GET_API_VERSION) != VFIO_API_VERSION))
1059 {
1060 close (fd);
1061 fd = -1;
1062 }
1063
1064 if ((fd != -1) && (ioctl (fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) == 0))
1065 {
1066 close (fd);
1067 fd = -1;
1068 }
1069
1070 lpm->vfio_container_fd = fd;
1071 lpm->iommu_pool_index_by_group = hash_create (0, sizeof (uword));
1072
1073 addrs = vlib_pci_get_all_dev_addrs ();
1074 /* *INDENT-OFF* */
1075 vec_foreach (addr, addrs)
1076 {
1077 vlib_pci_device_info_t *d;
1078 if ((d = vlib_pci_get_device_info (addr, 0)))
1079 {
1080 init_device_from_registered (vm, d);
1081 vlib_pci_free_device_info (d);
1082 }
1083 }
1084 /* *INDENT-ON* */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001085
1086 return error;
1087}
1088
Damjan Marion5a206ea2016-05-12 22:11:03 +02001089VLIB_INIT_FUNCTION (linux_pci_init);
Dave Barach9b8ffd92016-07-08 08:13:45 -04001090
1091/*
1092 * fd.io coding-style-patch-verification: ON
1093 *
1094 * Local Variables:
1095 * eval: (c-set-style "gnu")
1096 * End:
1097 */