blob: bc3e15ea00d85750ef6898cfb09b651975d9f584 [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>
Damjan Marion1ba0fa42018-03-04 17:19:08 +010045#include <vlib/linux/vfio.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070046
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <fcntl.h>
50#include <dirent.h>
Damjan Mariona42cd342016-04-13 18:03:20 +020051#include <sys/ioctl.h>
52#include <net/if.h>
53#include <linux/ethtool.h>
54#include <linux/sockios.h>
Damjan Marioncef87f12017-10-05 15:32:41 +020055#include <linux/vfio.h>
56#include <sys/eventfd.h>
57
58static const char *sysfs_pci_dev_path = "/sys/bus/pci/devices";
59static const char *sysfs_pci_drv_path = "/sys/bus/pci/drivers";
Damjan Marion2752b892017-12-11 15:55:56 +010060static char *sysfs_mod_vfio_noiommu =
61 "/sys/module/vfio/parameters/enable_unsafe_noiommu_mode";
Damjan Mariona42cd342016-04-13 18:03:20 +020062
Dave Barach9b8ffd92016-07-08 08:13:45 -040063typedef struct
64{
Damjan Marion2060db82018-03-04 17:37:15 +010065 int fd;
66 void *addr;
67 size_t size;
68} linux_pci_region_t;
69
Damjan Marion599a16b2018-03-04 19:35:23 +010070typedef struct
71{
72 int fd;
73 u32 clib_file_index;
74 union
75 {
Damjan Mariond5ded2d2018-03-05 10:18:50 +010076 pci_intx_handler_function_t *intx_handler;
Damjan Marion599a16b2018-03-04 19:35:23 +010077 pci_msix_handler_function_t *msix_handler;
78 };
79} linux_pci_irq_t;
Damjan Marion2060db82018-03-04 17:37:15 +010080
81typedef enum
82{
83 LINUX_PCI_DEVICE_TYPE_UNKNOWN,
84 LINUX_PCI_DEVICE_TYPE_UIO,
85 LINUX_PCI_DEVICE_TYPE_VFIO,
86} linux_pci_device_type_t;
87
88typedef struct
89{
90 linux_pci_device_type_t type;
Damjan Marioncef87f12017-10-05 15:32:41 +020091 vlib_pci_dev_handle_t handle;
92 vlib_pci_addr_t addr;
Damjan Marion5a206ea2016-05-12 22:11:03 +020093
94 /* Resource file descriptors. */
Damjan Marion2060db82018-03-04 17:37:15 +010095 linux_pci_region_t *regions;
Damjan Marion5a206ea2016-05-12 22:11:03 +020096
97 /* File descriptor for config space read/write. */
98 int config_fd;
Damjan Marionf9a968e2018-02-25 23:26:22 +010099 u64 config_offset;
Damjan Marion5a206ea2016-05-12 22:11:03 +0200100
Damjan Marionf9a968e2018-02-25 23:26:22 +0100101 /* Device File descriptor */
102 int fd;
Damjan Marion5a206ea2016-05-12 22:11:03 +0200103
104 /* Minor device for uio device. */
105 u32 uio_minor;
106
Damjan Marion56dd5432017-09-08 19:52:02 +0200107 /* Index given by clib_file_add. */
108 u32 clib_file_index;
Damjan Marion5a206ea2016-05-12 22:11:03 +0200109
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100110 /* Interrupt handlers */
111 linux_pci_irq_t intx_irq;
Damjan Marion599a16b2018-03-04 19:35:23 +0100112 linux_pci_irq_t *msix_irqs;
Damjan Marioncef87f12017-10-05 15:32:41 +0200113
114 /* private data */
115 uword private_data;
116
Damjan Marion5a206ea2016-05-12 22:11:03 +0200117} linux_pci_device_t;
118
119/* Pool of PCI devices. */
Dave Barach9b8ffd92016-07-08 08:13:45 -0400120typedef struct
121{
122 vlib_main_t *vlib_main;
123 linux_pci_device_t *linux_pci_devices;
Damjan Marioncef87f12017-10-05 15:32:41 +0200124
Damjan Marion5a206ea2016-05-12 22:11:03 +0200125} linux_pci_main_t;
126
127extern linux_pci_main_t linux_pci_main;
128
Damjan Marion599a16b2018-03-04 19:35:23 +0100129static linux_pci_device_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200130linux_pci_get_device (vlib_pci_dev_handle_t h)
131{
132 linux_pci_main_t *lpm = &linux_pci_main;
133 return pool_elt_at_index (lpm->linux_pci_devices, h);
134}
135
136uword
137vlib_pci_get_private_data (vlib_pci_dev_handle_t h)
138{
139 linux_pci_device_t *d = linux_pci_get_device (h);
140 return d->private_data;
141}
142
143void
144vlib_pci_set_private_data (vlib_pci_dev_handle_t h, uword private_data)
145{
146 linux_pci_device_t *d = linux_pci_get_device (h);
147 d->private_data = private_data;
148}
149
150vlib_pci_addr_t *
151vlib_pci_get_addr (vlib_pci_dev_handle_t h)
152{
Damjan Marion599a16b2018-03-04 19:35:23 +0100153 linux_pci_device_t *d = linux_pci_get_device (h);
154 return &d->addr;
Damjan Marioncef87f12017-10-05 15:32:41 +0200155}
156
Damjan Marion5a206ea2016-05-12 22:11:03 +0200157/* Call to allocate/initialize the pci subsystem.
158 This is not an init function so that users can explicitly enable
159 pci only when it's needed. */
Dave Barach9b8ffd92016-07-08 08:13:45 -0400160clib_error_t *pci_bus_init (vlib_main_t * vm);
Damjan Marion5a206ea2016-05-12 22:11:03 +0200161
Ed Warnickecb9cada2015-12-08 15:45:58 -0700162linux_pci_main_t linux_pci_main;
163
Damjan Marioncef87f12017-10-05 15:32:41 +0200164vlib_pci_device_info_t *
165vlib_pci_get_device_info (vlib_pci_addr_t * addr, clib_error_t ** error)
166{
Damjan Marion1ba0fa42018-03-04 17:19:08 +0100167 linux_vfio_main_t *lvm = &vfio_main;
Damjan Marioncef87f12017-10-05 15:32:41 +0200168 clib_error_t *err;
169 vlib_pci_device_info_t *di;
170 u8 *f = 0;
171 u32 tmp;
172 int fd;
173
174 di = clib_mem_alloc (sizeof (vlib_pci_device_info_t));
175 memset (di, 0, sizeof (vlib_pci_device_info_t));
176 di->addr.as_u32 = addr->as_u32;
177
178 u8 *dev_dir_name = format (0, "%s/%U", sysfs_pci_dev_path,
179 format_vlib_pci_addr, addr);
180
181 f = format (0, "%v/config%c", dev_dir_name, 0);
182 fd = open ((char *) f, O_RDWR);
183
184 /* Try read-only access if write fails. */
185 if (fd < 0)
186 fd = open ((char *) f, O_RDONLY);
187
188 if (fd < 0)
189 {
190 err = clib_error_return_unix (0, "open `%s'", f);
191 goto error;
192 }
193
194 /* You can only read more that 64 bytes of config space as root; so we try to
195 read the full space but fall back to just the first 64 bytes. */
Damjan Marion44bcc202018-03-04 16:44:26 +0100196 if (read (fd, &di->config_data, sizeof (di->config_data)) <
197 sizeof (di->config0))
Damjan Marioncef87f12017-10-05 15:32:41 +0200198 {
199 err = clib_error_return_unix (0, "read `%s'", f);
200 close (fd);
201 goto error;
202 }
203
204 {
205 static pci_config_header_t all_ones;
206 if (all_ones.vendor_id == 0)
207 memset (&all_ones, ~0, sizeof (all_ones));
208
209 if (!memcmp (&di->config0.header, &all_ones, sizeof (all_ones)))
210 {
211 err = clib_error_return (0, "invalid PCI config for `%s'", f);
212 close (fd);
213 goto error;
214 }
215 }
216
217 if (di->config0.header.header_type == 0)
218 pci_config_type0_little_to_host (&di->config0);
219 else
220 pci_config_type1_little_to_host (&di->config1);
221
222 di->numa_node = -1;
223 vec_reset_length (f);
224 f = format (f, "%v/numa_node%c", dev_dir_name, 0);
225 err = clib_sysfs_read ((char *) f, "%u", &di->numa_node);
226 if (err)
227 {
228 di->numa_node = -1;
229 clib_error_free (err);
230 }
231
232 vec_reset_length (f);
233 f = format (f, "%v/class%c", dev_dir_name, 0);
234 err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
235 if (err)
236 goto error;
237 di->device_class = tmp >> 8;
238
239 vec_reset_length (f);
240 f = format (f, "%v/vendor%c", dev_dir_name, 0);
241 err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
242 if (err)
243 goto error;
244 di->vendor_id = tmp;
245
246 vec_reset_length (f);
247 f = format (f, "%v/device%c", dev_dir_name, 0);
248 err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
249 if (err)
250 goto error;
251 di->device_id = tmp;
252
253 vec_reset_length (f);
254 f = format (f, "%v/driver%c", dev_dir_name, 0);
255 di->driver_name = clib_sysfs_link_to_name ((char *) f);
256
257 di->iommu_group = -1;
Damjan Marion1ba0fa42018-03-04 17:19:08 +0100258 if (lvm->container_fd != -1)
Damjan Marioncef87f12017-10-05 15:32:41 +0200259 {
260 u8 *tmpstr;
261 vec_reset_length (f);
262 f = format (f, "%v/iommu_group%c", dev_dir_name, 0);
263 tmpstr = clib_sysfs_link_to_name ((char *) f);
264 if (tmpstr)
265 {
266 di->iommu_group = atoi ((char *) tmpstr);
267 vec_free (tmpstr);
268 }
269 }
270
Damjan Marion96504182017-12-10 23:54:46 +0100271 close (fd);
272
Damjan Marioncef87f12017-10-05 15:32:41 +0200273 vec_reset_length (f);
274 f = format (f, "%v/vpd%c", dev_dir_name, 0);
275 fd = open ((char *) f, O_RDONLY);
276 if (fd >= 0)
277 {
278 while (1)
279 {
280 u8 tag[3];
281 u8 *data = 0;
Damjan Marion96504182017-12-10 23:54:46 +0100282 uword len;
Damjan Marioncef87f12017-10-05 15:32:41 +0200283
284 if (read (fd, &tag, 3) != 3)
285 break;
286
287 if (tag[0] != 0x82 && tag[0] != 0x90 && tag[0] != 0x91)
288 break;
289
290 len = (tag[2] << 8) | tag[1];
291 vec_validate (data, len);
292
293 if (read (fd, data, len) != len)
294 {
295 vec_free (data);
296 break;
297 }
298 if (tag[0] == 0x82)
299 di->product_name = data;
300 else if (tag[0] == 0x90)
301 di->vpd_r = data;
302 else if (tag[0] == 0x91)
303 di->vpd_w = data;
304
305 data = 0;
306 }
307 close (fd);
308 }
309
310 goto done;
311
312error:
313 vlib_pci_free_device_info (di);
314 di = 0;
315
316done:
317 vec_free (f);
318 vec_free (dev_dir_name);
319 if (error)
320 *error = err;
321 else
322 clib_error_free (err);
323 return di;
324}
325
Damjan Marion2752b892017-12-11 15:55:56 +0100326static int
327directory_exists (char *path)
328{
329 struct stat s = { 0 };
330 if (stat (path, &s) == -1)
331 return 0;
332
333 return S_ISDIR (s.st_mode);
334}
335
Damjan Mariona42cd342016-04-13 18:03:20 +0200336clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200337vlib_pci_bind_to_uio (vlib_pci_addr_t * addr, char *uio_drv_name)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700338{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400339 clib_error_t *error = 0;
Damjan Mariona7f74572017-05-23 18:32:21 +0200340 u8 *s = 0, *driver_name = 0;
Damjan Mariona42cd342016-04-13 18:03:20 +0200341 DIR *dir = 0;
342 struct dirent *e;
Damjan Marioncef87f12017-10-05 15:32:41 +0200343 vlib_pci_device_info_t *di;
Damjan Mariona7f74572017-05-23 18:32:21 +0200344 int fd, clear_driver_override = 0;
Damjan Marioncef87f12017-10-05 15:32:41 +0200345 u8 *dev_dir_name = format (0, "%s/%U", sysfs_pci_dev_path,
346 format_vlib_pci_addr, addr);
347
348 di = vlib_pci_get_device_info (addr, &error);
349
350 if (error)
351 return error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700352
Damjan Marion2752b892017-12-11 15:55:56 +0100353 if (strncmp ("auto", uio_drv_name, 5) == 0)
354 {
355 int vfio_pci_loaded = 0;
356
357 if (directory_exists ("/sys/module/vfio_pci"))
358 vfio_pci_loaded = 1;
359
360 if (di->iommu_group != -1)
361 {
362 /* device is bound to IOMMU group */
363 if (!vfio_pci_loaded)
364 {
365 error = clib_error_return (0, "Skipping PCI device %U: device "
366 "is bound to IOMMU group and "
367 "vfio-pci driver is not loaded",
368 format_vlib_pci_addr, addr);
369 goto done;
370 }
371 else
372 uio_drv_name = "vfio-pci";
373 }
374 else
375 {
376 /* device is not bound to IOMMU group so we have multiple options */
377 if (vfio_pci_loaded &&
378 (error = clib_sysfs_write (sysfs_mod_vfio_noiommu, "Y")) == 0)
379 uio_drv_name = "vfio-pci";
380 else if (directory_exists ("/sys/module/uio_pci_generic"))
381 uio_drv_name = "uio_pci_generic";
382 else if (directory_exists ("/sys/module/igb_uio"))
383 uio_drv_name = "igb_uio";
384 else
385 {
386 clib_error_free (error);
387 error = clib_error_return (0, "Skipping PCI device %U: missing "
388 "kernel VFIO or UIO driver",
389 format_vlib_pci_addr, addr);
390 goto done;
391 }
392 clib_error_free (error);
393 }
394 }
395
Damjan Mariona7f74572017-05-23 18:32:21 +0200396 s = format (s, "%v/driver%c", dev_dir_name, 0);
Damjan Marion01914ce2017-09-14 19:04:50 +0200397 driver_name = clib_sysfs_link_to_name ((char *) s);
Damjan Mariona42cd342016-04-13 18:03:20 +0200398 vec_reset_length (s);
399
Damjan Mariona7f74572017-05-23 18:32:21 +0200400 if (driver_name &&
401 ((strcmp ("vfio-pci", (char *) driver_name) == 0) ||
402 (strcmp ("uio_pci_generic", (char *) driver_name) == 0) ||
403 (strcmp ("igb_uio", (char *) driver_name) == 0)))
Damjan Marion3c785e02017-05-08 18:37:54 +0200404 goto done;
Damjan Marion3c785e02017-05-08 18:37:54 +0200405
Damjan Mariona42cd342016-04-13 18:03:20 +0200406 /* walk trough all linux interfaces and if interface belonging to
407 this device is founf check if interface is admin up */
408 dir = opendir ("/sys/class/net");
Damjan Marioncef87f12017-10-05 15:32:41 +0200409 s = format (s, "%U%c", format_vlib_pci_addr, addr, 0);
Damjan Mariona42cd342016-04-13 18:03:20 +0200410
411 if (!dir)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700412 {
Damjan Mariona42cd342016-04-13 18:03:20 +0200413 error = clib_error_return (0, "Skipping PCI device %U: failed to "
414 "read /sys/class/net",
Damjan Marioncef87f12017-10-05 15:32:41 +0200415 format_vlib_pci_addr, addr);
Damjan Mariona42cd342016-04-13 18:03:20 +0200416 goto done;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700417 }
418
Dave Barach9b8ffd92016-07-08 08:13:45 -0400419 fd = socket (PF_INET, SOCK_DGRAM, 0);
Chris Luke370e9e32016-07-07 11:01:17 -0400420 if (fd < 0)
421 {
422 error = clib_error_return_unix (0, "socket");
423 goto done;
424 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200425
Dave Barach9b8ffd92016-07-08 08:13:45 -0400426 while ((e = readdir (dir)))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700427 {
Damjan Mariona42cd342016-04-13 18:03:20 +0200428 struct ifreq ifr;
429 struct ethtool_drvinfo drvinfo;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700430
Dave Barach9b8ffd92016-07-08 08:13:45 -0400431 if (e->d_name[0] == '.') /* skip . and .. */
Damjan Mariona42cd342016-04-13 18:03:20 +0200432 continue;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700433
Dave Barach9b8ffd92016-07-08 08:13:45 -0400434 memset (&ifr, 0, sizeof ifr);
435 memset (&drvinfo, 0, sizeof drvinfo);
Damjan Mariona42cd342016-04-13 18:03:20 +0200436 ifr.ifr_data = (char *) &drvinfo;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400437 strncpy (ifr.ifr_name, e->d_name, IFNAMSIZ - 1);
Damjan Mariona42cd342016-04-13 18:03:20 +0200438 drvinfo.cmd = ETHTOOL_GDRVINFO;
Chris Luke370e9e32016-07-07 11:01:17 -0400439 if (ioctl (fd, SIOCETHTOOL, &ifr) < 0)
Dave Barach9b8ffd92016-07-08 08:13:45 -0400440 {
John Loe2821212016-07-13 18:13:02 -0400441 /* Some interfaces (eg "lo") don't support this ioctl */
442 if ((errno != ENOTSUP) && (errno != ENODEV))
Ed Warnicke853e7202016-08-12 11:42:26 -0700443 clib_unix_warning ("ioctl fetch intf %s bus info error",
444 e->d_name);
John Loe2821212016-07-13 18:13:02 -0400445 continue;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400446 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200447
448 if (strcmp ((char *) s, drvinfo.bus_info))
449 continue;
450
Dave Barach9b8ffd92016-07-08 08:13:45 -0400451 memset (&ifr, 0, sizeof (ifr));
Chris Luke370e9e32016-07-07 11:01:17 -0400452 strncpy (ifr.ifr_name, e->d_name, IFNAMSIZ - 1);
453 if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
Dave Barach9b8ffd92016-07-08 08:13:45 -0400454 {
455 error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
456 e->d_name);
457 close (fd);
458 goto done;
459 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200460
461 if (ifr.ifr_flags & IFF_UP)
462 {
463 error = clib_error_return (0, "Skipping PCI device %U as host "
464 "interface %s is up",
Damjan Marioncef87f12017-10-05 15:32:41 +0200465 format_vlib_pci_addr, addr, e->d_name);
Dave Barach9b8ffd92016-07-08 08:13:45 -0400466 close (fd);
Damjan Mariona42cd342016-04-13 18:03:20 +0200467 goto done;
468 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700469 }
470
Damjan Mariona42cd342016-04-13 18:03:20 +0200471 close (fd);
472 vec_reset_length (s);
473
474 s = format (s, "%v/driver/unbind%c", dev_dir_name, 0);
Damjan Marioncef87f12017-10-05 15:32:41 +0200475 clib_sysfs_write ((char *) s, "%U", format_vlib_pci_addr, addr);
Damjan Mariona42cd342016-04-13 18:03:20 +0200476 vec_reset_length (s);
477
Damjan Mariona7f74572017-05-23 18:32:21 +0200478 s = format (s, "%v/driver_override%c", dev_dir_name, 0);
479 if (access ((char *) s, F_OK) == 0)
480 {
Damjan Marioncef87f12017-10-05 15:32:41 +0200481 clib_sysfs_write ((char *) s, "%s", uio_drv_name);
Damjan Mariona7f74572017-05-23 18:32:21 +0200482 clear_driver_override = 1;
483 }
484 else
485 {
486 vec_reset_length (s);
Damjan Marioncef87f12017-10-05 15:32:41 +0200487 s = format (s, "%s/%s/new_id%c", sysfs_pci_drv_path, uio_drv_name, 0);
488 clib_sysfs_write ((char *) s, "0x%04x 0x%04x", di->vendor_id,
489 di->device_id);
Damjan Mariona7f74572017-05-23 18:32:21 +0200490 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200491 vec_reset_length (s);
492
Damjan Marioncef87f12017-10-05 15:32:41 +0200493 s = format (s, "%s/%s/bind%c", sysfs_pci_drv_path, uio_drv_name, 0);
494 clib_sysfs_write ((char *) s, "%U", format_vlib_pci_addr, addr);
Damjan Mariona7f74572017-05-23 18:32:21 +0200495 vec_reset_length (s);
496
497 if (clear_driver_override)
498 {
499 s = format (s, "%v/driver_override%c", dev_dir_name, 0);
Damjan Marion01914ce2017-09-14 19:04:50 +0200500 clib_sysfs_write ((char *) s, "%c", 0);
Damjan Mariona7f74572017-05-23 18:32:21 +0200501 vec_reset_length (s);
502 }
Damjan Mariona42cd342016-04-13 18:03:20 +0200503
504done:
505 closedir (dir);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700506 vec_free (s);
Damjan Mariona42cd342016-04-13 18:03:20 +0200507 vec_free (dev_dir_name);
Damjan Mariona7f74572017-05-23 18:32:21 +0200508 vec_free (driver_name);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700509 return error;
510}
511
Ed Warnickecb9cada2015-12-08 15:45:58 -0700512
513static clib_error_t *
Dave Barach9b8ffd92016-07-08 08:13:45 -0400514scan_uio_dir (void *arg, u8 * path_name, u8 * file_name)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700515{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400516 linux_pci_device_t *l = arg;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700517 unformat_input_t input;
518
519 unformat_init_string (&input, (char *) file_name, vec_len (file_name));
520
Dave Barach9b8ffd92016-07-08 08:13:45 -0400521 if (!unformat (&input, "uio%d", &l->uio_minor))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700522 abort ();
523
524 unformat_free (&input);
525 return 0;
526}
527
Dave Barach9b8ffd92016-07-08 08:13:45 -0400528static clib_error_t *
Damjan Marion599a16b2018-03-04 19:35:23 +0100529vfio_set_irqs (linux_pci_device_t * p, u32 index, u32 start, u32 count,
530 u32 flags, int *efds)
531{
532 int data_len = efds ? count * sizeof (int) : 0;
533 u8 buf[sizeof (struct vfio_irq_set) + data_len];
534 struct vfio_irq_info irq_info = { 0 };
535 struct vfio_irq_set *irq_set = (struct vfio_irq_set *) buf;
536
537
538 irq_info.argsz = sizeof (struct vfio_irq_info);
539 irq_info.index = index;
540
541 if (ioctl (p->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info) < 0)
542 return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_IRQ_INFO) "
543 "'%U'", format_vlib_pci_addr, &p->addr);
544
545 if (irq_info.count < start + count)
546 return clib_error_return_unix (0, "vfio_set_irq: unexistng interrupt on "
547 "'%U'", format_vlib_pci_addr, &p->addr);
548
549
550 if (efds)
551 {
552 flags |= VFIO_IRQ_SET_DATA_EVENTFD;
553 clib_memcpy (&irq_set->data, efds, data_len);
554 }
555 else
556 flags |= VFIO_IRQ_SET_DATA_NONE;
557
558 ASSERT ((flags & (VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_DATA_EVENTFD)) !=
559 (VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_DATA_EVENTFD));
560
561 irq_set->argsz = sizeof (struct vfio_irq_set) + data_len;
562 irq_set->index = index;
563 irq_set->start = start;
564 irq_set->count = count;
565 irq_set->flags = flags;
566
567 if (ioctl (p->fd, VFIO_DEVICE_SET_IRQS, irq_set) < 0)
568 return clib_error_return_unix (0, "%U:ioctl(VFIO_DEVICE_SET_IRQS) "
569 "[index = %u, start = %u, count = %u, "
570 "flags = 0x%x]",
571 format_vlib_pci_addr, &p->addr,
572 index, start, count, flags);
573 return 0;
574}
575
576static clib_error_t *
Damjan Marion56dd5432017-09-08 19:52:02 +0200577linux_pci_uio_read_ready (clib_file_t * uf)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700578{
Damjan Marion5a206ea2016-05-12 22:11:03 +0200579 int __attribute__ ((unused)) rv;
Damjan Marioncef87f12017-10-05 15:32:41 +0200580 vlib_pci_dev_handle_t h = uf->private_data;
581 linux_pci_device_t *p = linux_pci_get_device (h);
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100582 linux_pci_irq_t *irq = &p->intx_irq;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700583
Damjan Marion5a206ea2016-05-12 22:11:03 +0200584 u32 icount;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400585 rv = read (uf->file_descriptor, &icount, 4);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700586
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100587 if (irq->intx_handler)
588 irq->intx_handler (h);
Damjan Marion5a206ea2016-05-12 22:11:03 +0200589
Damjan Marioncef87f12017-10-05 15:32:41 +0200590 vlib_pci_intr_enable (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700591
592 return /* no error */ 0;
593}
594
Dave Barach9b8ffd92016-07-08 08:13:45 -0400595static clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200596linux_pci_vfio_unmask_intx (linux_pci_device_t * d)
597{
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100598 return vfio_set_irqs (d, VFIO_PCI_INTX_IRQ_INDEX, 0, 1,
Damjan Marion599a16b2018-03-04 19:35:23 +0100599 VFIO_IRQ_SET_ACTION_UNMASK, 0);
600}
601
Damjan Marioncef87f12017-10-05 15:32:41 +0200602static clib_error_t *
Damjan Marion56dd5432017-09-08 19:52:02 +0200603linux_pci_uio_error_ready (clib_file_t * uf)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700604{
605 u32 error_index = (u32) uf->private_data;
606
607 return clib_error_return (0, "pci device %d: error", error_index);
608}
609
Damjan Marioncef87f12017-10-05 15:32:41 +0200610static clib_error_t *
Damjan Marion599a16b2018-03-04 19:35:23 +0100611linux_pci_vfio_msix_read_ready (clib_file_t * uf)
612{
613 int __attribute__ ((unused)) rv;
614 vlib_pci_dev_handle_t h = uf->private_data >> 16;
615 u16 line = uf->private_data & 0xffff;
616 linux_pci_device_t *p = linux_pci_get_device (h);
617 linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, line);
618
619 u64 icount;
620 rv = read (uf->file_descriptor, &icount, sizeof (icount));
621
622 if (irq->msix_handler)
623 irq->msix_handler (h, line);
624
625 return /* no error */ 0;
626}
627
628static clib_error_t *
629linux_pci_vfio_intx_read_ready (clib_file_t * uf)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700630{
Damjan Marioncef87f12017-10-05 15:32:41 +0200631 int __attribute__ ((unused)) rv;
632 vlib_pci_dev_handle_t h = uf->private_data;
633 linux_pci_device_t *p = linux_pci_get_device (h);
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100634 linux_pci_irq_t *irq = &p->intx_irq;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700635
Damjan Marioncef87f12017-10-05 15:32:41 +0200636 u64 icount;
637 rv = read (uf->file_descriptor, &icount, sizeof (icount));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700638
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100639 if (irq->intx_handler)
640 irq->intx_handler (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700641
Damjan Marioncef87f12017-10-05 15:32:41 +0200642 linux_pci_vfio_unmask_intx (p);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700643
Damjan Marioncef87f12017-10-05 15:32:41 +0200644 return /* no error */ 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700645}
646
Damjan Marioncef87f12017-10-05 15:32:41 +0200647static clib_error_t *
648linux_pci_vfio_error_ready (clib_file_t * uf)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700649{
Damjan Marioncef87f12017-10-05 15:32:41 +0200650 u32 error_index = (u32) uf->private_data;
651
652 return clib_error_return (0, "pci device %d: error", error_index);
653}
654
655static clib_error_t *
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100656add_device_uio (linux_pci_device_t * p, vlib_pci_device_info_t * di,
657 pci_device_registration_t * r)
Damjan Marioncef87f12017-10-05 15:32:41 +0200658{
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100659 linux_pci_main_t *lpm = &linux_pci_main;
Damjan Marioncef87f12017-10-05 15:32:41 +0200660 clib_error_t *err = 0;
Damjan Marioncef87f12017-10-05 15:32:41 +0200661 u8 *s = 0;
662
663 p->addr.as_u32 = di->addr.as_u32;
Damjan Marionf9a968e2018-02-25 23:26:22 +0100664 p->fd = -1;
Damjan Marion2060db82018-03-04 17:37:15 +0100665 p->type = LINUX_PCI_DEVICE_TYPE_UIO;
Damjan Marioncef87f12017-10-05 15:32:41 +0200666
667 s = format (s, "%s/%U/config%c", sysfs_pci_dev_path,
668 format_vlib_pci_addr, &di->addr, 0);
669
670 p->config_fd = open ((char *) s, O_RDWR);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100671 p->config_offset = 0;
Damjan Marioncef87f12017-10-05 15:32:41 +0200672 vec_reset_length (s);
673
674 if (p->config_fd == -1)
675 {
676 err = clib_error_return_unix (0, "open '%s'", s);
677 goto error;
678 }
679
680 s = format (0, "%s/%U/uio", sysfs_pci_dev_path,
681 format_vlib_pci_addr, &di->addr);
682 foreach_directory_file ((char *) s, scan_uio_dir, p, /* scan_dirs */
683 1);
684 vec_reset_length (s);
685
686 s = format (s, "/dev/uio%d%c", p->uio_minor, 0);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100687 p->fd = open ((char *) s, O_RDWR);
688 if (p->fd < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200689 {
690 err = clib_error_return_unix (0, "open '%s'", s);
691 goto error;
692 }
693
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100694 if (r && r->interrupt_handler)
695 vlib_pci_register_intx_handler (p->handle, r->interrupt_handler);
Damjan Marioncef87f12017-10-05 15:32:41 +0200696
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100697 if (r && r->init_function)
698 err = r->init_function (lpm->vlib_main, p->handle);
Damjan Marioncef87f12017-10-05 15:32:41 +0200699
700error:
701 free (s);
702 if (err)
703 {
Damjan Marion96504182017-12-10 23:54:46 +0100704 if (p->config_fd != -1)
705 close (p->config_fd);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100706 if (p->fd != -1)
707 close (p->fd);
Damjan Marioncef87f12017-10-05 15:32:41 +0200708 }
709 return err;
710}
711
Damjan Marion599a16b2018-03-04 19:35:23 +0100712clib_error_t *
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100713vlib_pci_register_intx_handler (vlib_pci_dev_handle_t h,
714 pci_intx_handler_function_t * intx_handler)
715{
716 linux_pci_device_t *p = linux_pci_get_device (h);
717 clib_file_t t = { 0 };
718 linux_pci_irq_t *irq = &p->intx_irq;
719 ASSERT (irq->fd == -1);
720
721 if (p->type == LINUX_PCI_DEVICE_TYPE_VFIO)
722 {
723 struct vfio_irq_info irq_info = { 0 };
724 irq_info.argsz = sizeof (struct vfio_irq_info);
725 irq_info.index = VFIO_PCI_INTX_IRQ_INDEX;
726 if (ioctl (p->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info) < 0)
727 return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_IRQ_INFO) '"
728 "%U'", format_vlib_pci_addr, &p->addr);
729 if (irq_info.count != 1)
730 return clib_error_return (0, "INTx interrupt does not exist on device"
731 "'%U'", format_vlib_pci_addr, &p->addr);
732
733 irq->fd = eventfd (0, EFD_NONBLOCK);
734 if (irq->fd == -1)
735 return clib_error_return_unix (0, "eventfd");
736 t.file_descriptor = irq->fd;
737 t.read_function = linux_pci_vfio_intx_read_ready;
738 }
739 else if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
740 {
741 t.file_descriptor = p->fd;
742 t.read_function = linux_pci_uio_read_ready;
743 }
744 else
745 return 0;
746
747 t.error_function = linux_pci_uio_error_ready;
748 t.private_data = p->handle;
749 t.description = format (0, "PCI %U INTx", format_vlib_pci_addr, &p->addr);
750 irq->clib_file_index = clib_file_add (&file_main, &t);
751 irq->intx_handler = intx_handler;
752 return 0;
753}
754
755clib_error_t *
Damjan Marion599a16b2018-03-04 19:35:23 +0100756vlib_pci_register_msix_handler (vlib_pci_dev_handle_t h, u32 start, u32 count,
757 pci_msix_handler_function_t * msix_handler)
758{
759 clib_error_t *err = 0;
760 linux_pci_device_t *p = linux_pci_get_device (h);
761 u32 i;
762
763 if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
764 return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
765 "support");
766
767 /* *INDENT-OFF* */
768 vec_validate_init_empty (p->msix_irqs, start + count - 1, (linux_pci_irq_t)
769 { .fd = -1});
770 /* *INDENT-ON* */
771
772 for (i = start; i < start + count; i++)
773 {
774 clib_file_t t = { 0 };
775 linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
776 ASSERT (irq->fd == -1);
777
778 irq->fd = eventfd (0, EFD_NONBLOCK);
779 if (irq->fd == -1)
780 {
781 err = clib_error_return_unix (0, "eventfd");
782 goto error;
783 }
784
785 t.read_function = linux_pci_vfio_msix_read_ready;
786 t.file_descriptor = irq->fd;
787 t.error_function = linux_pci_vfio_error_ready;
788 t.private_data = p->handle << 16 | i;
789 t.description = format (0, "PCI %U MSI-X #%u", format_vlib_pci_addr,
790 &p->addr, i);
791 irq->clib_file_index = clib_file_add (&file_main, &t);
792 irq->msix_handler = msix_handler;
793 }
794
795 return 0;
796
797error:
798 while (i-- > start)
799 {
800 linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
801 if (irq->fd != -1)
802 {
803 clib_file_del_by_index (&file_main, irq->clib_file_index);
804 close (irq->fd);
805 irq->fd = -1;
806 }
807 }
808 return err;
809}
810
811clib_error_t *
812vlib_pci_enable_msix_irq (vlib_pci_dev_handle_t h, u16 start, u16 count)
813{
814 linux_pci_device_t *p = linux_pci_get_device (h);
815 int fds[count];
816 int i;
817
818 if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
819 return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
820 "support");
821
822 for (i = start; i < start + count; i++)
823 {
824 linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
825 fds[i] = irq->fd;
826 }
827
828 return vfio_set_irqs (p, VFIO_PCI_MSIX_IRQ_INDEX, start, count,
829 VFIO_IRQ_SET_ACTION_TRIGGER, fds);
830}
831
832clib_error_t *
833vlib_pci_disable_msix_irq (vlib_pci_dev_handle_t h, u16 start, u16 count)
834{
835 linux_pci_device_t *p = linux_pci_get_device (h);
836 int i, fds[count];
837
838 if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
839 return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
840 "support");
841
842 for (i = start; i < start + count; i++)
843 fds[i] = -1;
844
845 return vfio_set_irqs (p, VFIO_PCI_MSIX_IRQ_INDEX, start, count,
846 VFIO_IRQ_SET_ACTION_TRIGGER, fds);
847}
848
Damjan Marioncef87f12017-10-05 15:32:41 +0200849static clib_error_t *
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100850add_device_vfio (linux_pci_device_t * p, vlib_pci_device_info_t * di,
851 pci_device_registration_t * r)
Damjan Marioncef87f12017-10-05 15:32:41 +0200852{
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100853 linux_pci_main_t *lpm = &linux_pci_main;
Damjan Marionf9a968e2018-02-25 23:26:22 +0100854 struct vfio_device_info device_info = { 0 };
Damjan Marioncef87f12017-10-05 15:32:41 +0200855 clib_error_t *err = 0;
856 u8 *s = 0;
Damjan Marioncef87f12017-10-05 15:32:41 +0200857
858 p->addr.as_u32 = di->addr.as_u32;
Damjan Marion2060db82018-03-04 17:37:15 +0100859 p->type = LINUX_PCI_DEVICE_TYPE_VFIO;
Damjan Marioncef87f12017-10-05 15:32:41 +0200860
861 if (di->driver_name == 0 ||
862 (strcmp ("vfio-pci", (char *) di->driver_name) != 0))
863 return clib_error_return (0, "Device '%U' (iommu group %d) not bound to "
864 "vfio-pci", format_vlib_pci_addr, &di->addr,
865 di->iommu_group);
866
Damjan Marion1ba0fa42018-03-04 17:19:08 +0100867 if ((err = linux_vfio_group_get_device_fd (&p->addr, &p->fd)))
Damjan Marioncef87f12017-10-05 15:32:41 +0200868 return err;
869
Damjan Marioncef87f12017-10-05 15:32:41 +0200870 device_info.argsz = sizeof (device_info);
Damjan Marionf9a968e2018-02-25 23:26:22 +0100871 if (ioctl (p->fd, VFIO_DEVICE_GET_INFO, &device_info) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200872 {
873 err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_INFO) '%U'",
874 format_vlib_pci_addr, &di->addr);
875 goto error;
876 }
877
Damjan Marioncef87f12017-10-05 15:32:41 +0200878 /* reset if device supports it */
879 if (device_info.flags & VFIO_DEVICE_FLAGS_RESET)
Damjan Marionf9a968e2018-02-25 23:26:22 +0100880 if (ioctl (p->fd, VFIO_DEVICE_RESET) < 0)
Damjan Marioncef87f12017-10-05 15:32:41 +0200881 {
882 err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_RESET) '%U'",
883 format_vlib_pci_addr, &di->addr);
884 goto error;
885 }
886
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100887 if (r && r->interrupt_handler)
Damjan Marioncef87f12017-10-05 15:32:41 +0200888 {
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100889 vlib_pci_register_intx_handler (p->handle, r->interrupt_handler);
Damjan Marioncef87f12017-10-05 15:32:41 +0200890 linux_pci_vfio_unmask_intx (p);
891 }
892
Damjan Mariond5ded2d2018-03-05 10:18:50 +0100893 if (r && r->init_function)
894 err = r->init_function (lpm->vlib_main, p->handle);
Damjan Marioncef87f12017-10-05 15:32:41 +0200895
896error:
897 vec_free (s);
898 if (err)
899 {
Damjan Marionf9a968e2018-02-25 23:26:22 +0100900 if (p->fd != -1)
901 close (p->fd);
Damjan Marion96504182017-12-10 23:54:46 +0100902 if (p->config_fd != -1)
903 close (p->config_fd);
Damjan Marioncef87f12017-10-05 15:32:41 +0200904 }
905 return err;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700906}
907
908/* Configuration space read/write. */
909clib_error_t *
Damjan Marioncef87f12017-10-05 15:32:41 +0200910vlib_pci_read_write_config (vlib_pci_dev_handle_t h,
Damjan Marion5a206ea2016-05-12 22:11:03 +0200911 vlib_read_or_write_t read_or_write,
Dave Barach9b8ffd92016-07-08 08:13:45 -0400912 uword address, void *data, u32 n_bytes)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700913{
Damjan Marioncef87f12017-10-05 15:32:41 +0200914 linux_pci_device_t *p = linux_pci_get_device (h);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700915 int n;
916
Ed Warnickecb9cada2015-12-08 15:45:58 -0700917 if (read_or_write == VLIB_READ)
Damjan Marionf9a968e2018-02-25 23:26:22 +0100918 n = pread (p->config_fd, data, n_bytes, p->config_offset + address);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700919 else
Damjan Marionf9a968e2018-02-25 23:26:22 +0100920 n = pwrite (p->config_fd, data, n_bytes, p->config_offset + address);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700921
922 if (n != n_bytes)
923 return clib_error_return_unix (0, "%s",
924 read_or_write == VLIB_READ
925 ? "read" : "write");
926
927 return 0;
928}
929
930static clib_error_t *
Damjan Marion2060db82018-03-04 17:37:15 +0100931vlib_pci_map_region_int (vlib_pci_dev_handle_t h,
932 u32 bar, u8 * addr, void **result)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700933{
Damjan Marioncef87f12017-10-05 15:32:41 +0200934 linux_pci_device_t *p = linux_pci_get_device (h);
Damjan Marion2060db82018-03-04 17:37:15 +0100935 int fd = -1;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400936 clib_error_t *error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700937 int flags = MAP_SHARED;
Damjan Marion2060db82018-03-04 17:37:15 +0100938 u64 size = 0, offset = 0;
939
940 ASSERT (bar <= 5);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700941
942 error = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700943
Damjan Marion2060db82018-03-04 17:37:15 +0100944 if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700945 {
Damjan Marion2060db82018-03-04 17:37:15 +0100946 u8 *file_name;
947 struct stat stat_buf;
948 file_name = format (0, "%s/%U/resource%d%c", sysfs_pci_dev_path,
949 format_vlib_pci_addr, &p->addr, bar, 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700950
Damjan Marion2060db82018-03-04 17:37:15 +0100951 fd = open ((char *) file_name, O_RDWR);
952 if (fd < 0)
953 {
954 error = clib_error_return_unix (0, "open `%s'", file_name);
955 vec_free (file_name);
956 return error;
957 }
958
959 if (fstat (fd, &stat_buf) < 0)
960 {
961 error = clib_error_return_unix (0, "fstat `%s'", file_name);
962 vec_free (file_name);
963 close (fd);
964 return error;
965 }
966
967 vec_free (file_name);
968 if (addr != 0)
969 flags |= MAP_FIXED;
970 size = stat_buf.st_size;
971 offset = 0;
972 }
973 else if (p->type == LINUX_PCI_DEVICE_TYPE_VFIO)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700974 {
Damjan Marion2060db82018-03-04 17:37:15 +0100975 struct vfio_region_info reg = { 0 };
976 reg.argsz = sizeof (struct vfio_region_info);
977 reg.index = bar;
978 if (ioctl (p->fd, VFIO_DEVICE_GET_REGION_INFO, &reg) < 0)
979 return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_INFO) "
980 "'%U'", format_vlib_pci_addr,
981 &p->addr);
982 fd = p->fd;
983 size = reg.size;
984 offset = reg.offset;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700985 }
Damjan Marion2060db82018-03-04 17:37:15 +0100986 else
987 ASSERT (0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700988
Damjan Marion2060db82018-03-04 17:37:15 +0100989 *result = mmap (addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700990 if (*result == (void *) -1)
991 {
Damjan Marion2060db82018-03-04 17:37:15 +0100992 error = clib_error_return_unix (0, "mmap `BAR%u'", bar);
993 if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700994 close (fd);
Damjan Marion2060db82018-03-04 17:37:15 +0100995 return error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700996 }
Damjan Marion2060db82018-03-04 17:37:15 +0100997
998 /* *INDENT-OFF* */
999 vec_validate_init_empty (p->regions, bar,
1000 (linux_pci_region_t) { .fd = -1});
1001 /* *INDENT-ON* */
1002 if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
1003 p->regions[bar].fd = fd;
1004 p->regions[bar].addr = *result;
1005 p->regions[bar].size = size;
1006 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001007}
1008
1009clib_error_t *
Damjan Marion2060db82018-03-04 17:37:15 +01001010vlib_pci_map_region (vlib_pci_dev_handle_t h, u32 resource, void **result)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001011{
Damjan Marion2060db82018-03-04 17:37:15 +01001012 return (vlib_pci_map_region_int (h, resource, 0 /* addr */ , result));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001013}
1014
1015clib_error_t *
Damjan Marion2060db82018-03-04 17:37:15 +01001016vlib_pci_map_region_fixed (vlib_pci_dev_handle_t h, u32 resource, u8 * addr,
1017 void **result)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001018{
Damjan Marion2060db82018-03-04 17:37:15 +01001019 return (vlib_pci_map_region_int (h, resource, addr, result));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001020}
1021
Dave Barach9b8ffd92016-07-08 08:13:45 -04001022void
Damjan Mariond5ded2d2018-03-05 10:18:50 +01001023init_device_from_registered (vlib_pci_device_info_t * di)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001024{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001025 vlib_pci_main_t *pm = &pci_main;
Damjan Marioncef87f12017-10-05 15:32:41 +02001026 linux_pci_main_t *lpm = &linux_pci_main;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001027 pci_device_registration_t *r;
1028 pci_device_id_t *i;
Damjan Marioncef87f12017-10-05 15:32:41 +02001029 clib_error_t *err = 0;
1030 linux_pci_device_t *p;
1031
1032 pool_get (lpm->linux_pci_devices, p);
1033 p->handle = p - lpm->linux_pci_devices;
Damjan Mariond5ded2d2018-03-05 10:18:50 +01001034 p->intx_irq.fd = -1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001035
Damjan Marion5a206ea2016-05-12 22:11:03 +02001036 r = pm->pci_device_registrations;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001037
1038 while (r)
1039 {
1040 for (i = r->supported_devices; i->vendor_id != 0; i++)
Damjan Marion3a593822018-02-17 14:06:53 +01001041 if (i->vendor_id == di->vendor_id && i->device_id == di->device_id)
Dave Barach9b8ffd92016-07-08 08:13:45 -04001042 {
Damjan Marioncef87f12017-10-05 15:32:41 +02001043 if (di->iommu_group != -1)
Damjan Mariond5ded2d2018-03-05 10:18:50 +01001044 err = add_device_vfio (p, di, r);
Damjan Marioncef87f12017-10-05 15:32:41 +02001045 else
Damjan Mariond5ded2d2018-03-05 10:18:50 +01001046 err = add_device_uio (p, di, r);
Damjan Marion5a206ea2016-05-12 22:11:03 +02001047
Damjan Marioncef87f12017-10-05 15:32:41 +02001048 if (err)
1049 clib_error_report (err);
1050 else
1051 return;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001052 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001053 r = r->next_registration;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001054 }
Damjan Marioncef87f12017-10-05 15:32:41 +02001055
Ed Warnickecb9cada2015-12-08 15:45:58 -07001056 /* No driver, close the PCI config-space FD */
Damjan Marioncef87f12017-10-05 15:32:41 +02001057 memset (p, 0, sizeof (linux_pci_device_t));
1058 pool_put (lpm->linux_pci_devices, p);
1059}
1060
1061static clib_error_t *
1062scan_pci_addr (void *arg, u8 * dev_dir_name, u8 * ignored)
1063{
1064 vlib_pci_addr_t addr, **addrv = arg;
1065 unformat_input_t input;
1066 clib_error_t *err = 0;
1067
1068 unformat_init_string (&input, (char *) dev_dir_name,
1069 vec_len (dev_dir_name));
1070
1071 if (!unformat (&input, "/sys/bus/pci/devices/%U",
1072 unformat_vlib_pci_addr, &addr))
1073 err = clib_error_return (0, "unformat error `%v`", dev_dir_name);
1074
1075 unformat_free (&input);
1076
1077 if (err)
1078 return err;
1079
1080 vec_add1 (*addrv, addr);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001081 return 0;
1082}
1083
Damjan Marioncef87f12017-10-05 15:32:41 +02001084static int
1085pci_addr_cmp (void *v1, void *v2)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001086{
Damjan Marioncef87f12017-10-05 15:32:41 +02001087 vlib_pci_addr_t *a1 = v1;
1088 vlib_pci_addr_t *a2 = v2;
1089
1090 if (a1->domain > a2->domain)
1091 return 1;
1092 if (a1->domain < a2->domain)
1093 return -1;
1094 if (a1->bus > a2->bus)
1095 return 1;
1096 if (a1->bus < a2->bus)
1097 return -1;
1098 if (a1->slot > a2->slot)
1099 return 1;
1100 if (a1->slot < a2->slot)
1101 return -1;
1102 if (a1->function > a2->function)
1103 return 1;
1104 if (a1->function < a2->function)
1105 return -1;
1106 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001107}
1108
Damjan Marioncef87f12017-10-05 15:32:41 +02001109vlib_pci_addr_t *
1110vlib_pci_get_all_dev_addrs ()
Ed Warnickecb9cada2015-12-08 15:45:58 -07001111{
Damjan Marioncef87f12017-10-05 15:32:41 +02001112 vlib_pci_addr_t *addrs = 0;
1113 clib_error_t *err;
1114 err = foreach_directory_file ((char *) sysfs_pci_dev_path, scan_pci_addr,
1115 &addrs, /* scan_dirs */ 0);
1116 if (err)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001117 {
Damjan Marioncef87f12017-10-05 15:32:41 +02001118 vec_free (addrs);
1119 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001120 }
1121
Damjan Marioncef87f12017-10-05 15:32:41 +02001122 vec_sort_with_function (addrs, pci_addr_cmp);
Damjan Mariona42cd342016-04-13 18:03:20 +02001123
Damjan Marioncef87f12017-10-05 15:32:41 +02001124 return addrs;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001125}
1126
Dave Barach9b8ffd92016-07-08 08:13:45 -04001127clib_error_t *
1128linux_pci_init (vlib_main_t * vm)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001129{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001130 vlib_pci_main_t *pm = &pci_main;
Damjan Marioncef87f12017-10-05 15:32:41 +02001131 vlib_pci_addr_t *addr = 0, *addrs;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001132 clib_error_t *error;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001133
1134 pm->vlib_main = vm;
1135
1136 if ((error = vlib_call_init_function (vm, unix_input_init)))
1137 return error;
1138
Dave Barach9b8ffd92016-07-08 08:13:45 -04001139 ASSERT (sizeof (vlib_pci_addr_t) == sizeof (u32));
Damjan Mariona42cd342016-04-13 18:03:20 +02001140
Damjan Marioncef87f12017-10-05 15:32:41 +02001141 addrs = vlib_pci_get_all_dev_addrs ();
1142 /* *INDENT-OFF* */
1143 vec_foreach (addr, addrs)
1144 {
1145 vlib_pci_device_info_t *d;
1146 if ((d = vlib_pci_get_device_info (addr, 0)))
1147 {
Damjan Mariond5ded2d2018-03-05 10:18:50 +01001148 init_device_from_registered (d);
Damjan Marioncef87f12017-10-05 15:32:41 +02001149 vlib_pci_free_device_info (d);
1150 }
1151 }
1152 /* *INDENT-ON* */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001153
1154 return error;
1155}
1156
Damjan Marion5a206ea2016-05-12 22:11:03 +02001157VLIB_INIT_FUNCTION (linux_pci_init);
Dave Barach9b8ffd92016-07-08 08:13:45 -04001158
1159/*
1160 * fd.io coding-style-patch-verification: ON
1161 *
1162 * Local Variables:
1163 * eval: (c-set-style "gnu")
1164 * End:
1165 */