Line data Source code
1 : /*
2 : * Copyright (c) 2016 Cisco and/or its affiliates.
3 : * 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 :
40 : #include <vppinfra/linux/sysfs.h>
41 :
42 : #include <vlib/vlib.h>
43 : #include <vlib/pci/pci.h>
44 : #include <vlib/unix/unix.h>
45 : #include <vlib/linux/vfio.h>
46 :
47 : #include <sys/types.h>
48 : #include <sys/stat.h>
49 : #include <fcntl.h>
50 : #include <dirent.h>
51 : #include <sys/ioctl.h>
52 : #include <net/if.h>
53 : #include <linux/ethtool.h>
54 : #include <linux/sockios.h>
55 : #include <linux/vfio.h>
56 : #include <sys/eventfd.h>
57 :
58 : #define SYSFS_DEVICES_PCI "/sys/devices/pci"
59 : static const char *sysfs_pci_dev_path = "/sys/bus/pci/devices";
60 : static const char *sysfs_pci_drv_path = "/sys/bus/pci/drivers";
61 : static char *sysfs_mod_vfio_noiommu =
62 : "/sys/module/vfio/parameters/enable_unsafe_noiommu_mode";
63 :
64 575 : VLIB_REGISTER_LOG_CLASS (pci_log, static) = {
65 : .class_name = "pci",
66 : .subclass_name = "linux",
67 : };
68 :
69 : #define log_debug(p, f, ...) \
70 : vlib_log (VLIB_LOG_LEVEL_DEBUG, pci_log.class, "%U: " f, \
71 : format_vlib_pci_log, p->handle, ##__VA_ARGS__)
72 : #define log_err(p, f, ...) \
73 : vlib_log (VLIB_LOG_LEVEL_ERR, pci_log.class, "%U: " f, format_vlib_pci_log, \
74 : p->handle, ##__VA_ARGS__)
75 :
76 : typedef struct
77 : {
78 : int fd;
79 : void *addr;
80 : size_t size;
81 : } linux_pci_region_t;
82 :
83 : typedef struct
84 : {
85 : int fd;
86 : u32 clib_file_index;
87 : union
88 : {
89 : pci_intx_handler_function_t *intx_handler;
90 : pci_msix_handler_function_t *msix_handler;
91 : };
92 : } linux_pci_irq_t;
93 :
94 : typedef enum
95 : {
96 : LINUX_PCI_DEVICE_TYPE_UNKNOWN,
97 : LINUX_PCI_DEVICE_TYPE_UIO,
98 : LINUX_PCI_DEVICE_TYPE_VFIO,
99 : } linux_pci_device_type_t;
100 :
101 : typedef struct
102 : {
103 : linux_pci_device_type_t type;
104 : vlib_pci_dev_handle_t handle;
105 : vlib_pci_addr_t addr;
106 : u32 numa_node;
107 :
108 : /* Resource file descriptors. */
109 : linux_pci_region_t *regions;
110 :
111 : /* File descriptor for config space read/write. */
112 : int config_fd;
113 : u64 config_offset;
114 :
115 : /* Device File descriptor */
116 : int fd;
117 :
118 : /* read/write file descriptor for io bar */
119 : int io_fd;
120 : u64 io_offset;
121 :
122 : /* Minor device for uio device. */
123 : u32 uio_minor;
124 :
125 : /* Interrupt handlers */
126 : linux_pci_irq_t intx_irq;
127 : linux_pci_irq_t *msix_irqs;
128 :
129 : /* private data */
130 : uword private_data;
131 :
132 : u8 supports_va_dma;
133 :
134 : } linux_pci_device_t;
135 :
136 : /* Pool of PCI devices. */
137 : typedef struct
138 : {
139 : vlib_main_t *vlib_main;
140 : linux_pci_device_t *linux_pci_devices;
141 :
142 : } linux_pci_main_t;
143 :
144 : extern linux_pci_main_t linux_pci_main;
145 :
146 : static linux_pci_device_t *
147 0 : linux_pci_get_device (vlib_pci_dev_handle_t h)
148 : {
149 0 : linux_pci_main_t *lpm = &linux_pci_main;
150 0 : return pool_elt_at_index (lpm->linux_pci_devices, h);
151 : }
152 :
153 : uword
154 0 : vlib_pci_get_private_data (vlib_main_t * vm, vlib_pci_dev_handle_t h)
155 : {
156 0 : linux_pci_device_t *d = linux_pci_get_device (h);
157 0 : return d->private_data;
158 : }
159 :
160 : void
161 0 : vlib_pci_set_private_data (vlib_main_t * vm, vlib_pci_dev_handle_t h,
162 : uword private_data)
163 : {
164 0 : linux_pci_device_t *d = linux_pci_get_device (h);
165 0 : d->private_data = private_data;
166 0 : }
167 :
168 : vlib_pci_addr_t *
169 0 : vlib_pci_get_addr (vlib_main_t * vm, vlib_pci_dev_handle_t h)
170 : {
171 0 : linux_pci_device_t *d = linux_pci_get_device (h);
172 0 : return &d->addr;
173 : }
174 :
175 : u32
176 0 : vlib_pci_get_numa_node (vlib_main_t * vm, vlib_pci_dev_handle_t h)
177 : {
178 0 : linux_pci_device_t *d = linux_pci_get_device (h);
179 0 : return d->numa_node;
180 : }
181 :
182 : u32
183 0 : vlib_pci_get_num_msix_interrupts (vlib_main_t * vm, vlib_pci_dev_handle_t h)
184 : {
185 0 : linux_pci_device_t *d = linux_pci_get_device (h);
186 :
187 0 : if (d->type == LINUX_PCI_DEVICE_TYPE_VFIO)
188 : {
189 0 : struct vfio_irq_info ii = { 0 };
190 :
191 0 : ii.argsz = sizeof (struct vfio_irq_info);
192 0 : ii.index = VFIO_PCI_MSIX_IRQ_INDEX;
193 0 : if (ioctl (d->fd, VFIO_DEVICE_GET_IRQ_INFO, &ii) < 0)
194 0 : return 0;
195 0 : return ii.count;
196 : }
197 0 : return 0;
198 : }
199 :
200 : /* Call to allocate/initialize the pci subsystem.
201 : This is not an init function so that users can explicitly enable
202 : pci only when it's needed. */
203 : clib_error_t *pci_bus_init (vlib_main_t * vm);
204 :
205 : linux_pci_main_t linux_pci_main;
206 :
207 : vlib_pci_device_info_t *
208 170775 : vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr,
209 : clib_error_t ** error)
210 : {
211 : clib_error_t *err;
212 : vlib_pci_device_info_t *di;
213 170775 : u8 *f = 0;
214 : u32 tmp;
215 : int fd;
216 : u8 *tmpstr;
217 170775 : clib_bitmap_t *bmp = 0;
218 :
219 170775 : di = clib_mem_alloc (sizeof (vlib_pci_device_info_t));
220 170775 : clib_memset (di, 0, sizeof (vlib_pci_device_info_t));
221 170775 : di->addr.as_u32 = addr->as_u32;
222 :
223 170775 : u8 *dev_dir_name = format (0, "%s/%U", sysfs_pci_dev_path,
224 : format_vlib_pci_addr, addr);
225 :
226 170775 : f = format (0, "%v/config%c", dev_dir_name, 0);
227 170775 : fd = open ((char *) f, O_RDWR);
228 :
229 : /* Try read-only access if write fails. */
230 170775 : if (fd < 0)
231 0 : fd = open ((char *) f, O_RDONLY);
232 :
233 170775 : if (fd < 0)
234 : {
235 0 : err = clib_error_return_unix (0, "open `%s'", f);
236 0 : goto error;
237 : }
238 :
239 : /* You can only read more that 64 bytes of config space as root; so we try to
240 : read the full space but fall back to just the first 64 bytes. */
241 170775 : if (read (fd, &di->config, sizeof (di->config)) <
242 : sizeof (vlib_pci_config_hdr_t))
243 : {
244 0 : err = clib_error_return_unix (0, "read `%s'", f);
245 0 : close (fd);
246 0 : goto error;
247 : }
248 :
249 170775 : di->numa_node = -1;
250 170775 : vec_reset_length (f);
251 170775 : f = format (f, "%v/numa_node%c", dev_dir_name, 0);
252 170775 : err = clib_sysfs_read ((char *) f, "%d", &di->numa_node);
253 170775 : if (err)
254 : {
255 0 : di->numa_node = -1;
256 0 : clib_error_free (err);
257 : }
258 170775 : if (di->numa_node == -1)
259 : {
260 : /* if '/sys/bus/pci/devices/<device id>/numa_node' returns -1 and
261 : it is a SMP system, set numa_node to 0. */
262 0 : if ((err = clib_sysfs_read ("/sys/devices/system/node/online", "%U",
263 : unformat_bitmap_list, &bmp)))
264 0 : clib_error_free (err);
265 0 : if (clib_bitmap_count_set_bits (bmp) == 1)
266 0 : di->numa_node = 0;
267 : }
268 :
269 170775 : vec_reset_length (f);
270 170775 : f = format (f, "%v/class%c", dev_dir_name, 0);
271 170775 : err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
272 170775 : if (err)
273 0 : goto error;
274 170775 : di->device_class = tmp >> 8;
275 :
276 170775 : vec_reset_length (f);
277 170775 : f = format (f, "%v/vendor%c", dev_dir_name, 0);
278 170775 : err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
279 170775 : if (err)
280 0 : goto error;
281 170775 : di->vendor_id = tmp;
282 :
283 170775 : vec_reset_length (f);
284 170775 : f = format (f, "%v/device%c", dev_dir_name, 0);
285 170775 : err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
286 170775 : if (err)
287 0 : goto error;
288 170775 : di->device_id = tmp;
289 :
290 170775 : vec_reset_length (f);
291 170775 : f = format (f, "%v/revision%c", dev_dir_name, 0);
292 170775 : err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
293 170775 : if (err)
294 0 : goto error;
295 170775 : di->revision = tmp;
296 :
297 170775 : di->driver_name =
298 170775 : clib_file_get_resolved_basename ("%v/driver", dev_dir_name);
299 170775 : if (!di->driver_name)
300 132250 : di->driver_name = format (0, "<NONE>%c", 0);
301 :
302 170775 : di->iommu_group = -1;
303 170775 : tmpstr = clib_file_get_resolved_basename ("%v/iommu_group", dev_dir_name);
304 170775 : if (tmpstr)
305 : {
306 0 : di->iommu_group = atoi ((char *) tmpstr);
307 0 : vec_free (tmpstr);
308 : }
309 :
310 170775 : vec_reset_length (f);
311 170775 : f = format (f, "%v/iommu_group/name%c", dev_dir_name, 0);
312 170775 : err = clib_sysfs_read ((char *) f, "%s", &tmpstr);
313 170775 : if (err == 0)
314 : {
315 0 : if (strncmp ((char *) tmpstr, "vfio-noiommu", 12) == 0)
316 0 : di->flags |= VLIB_PCI_DEVICE_INFO_F_NOIOMMU;
317 0 : vec_free (tmpstr);
318 : }
319 : else
320 170775 : clib_error_free (err);
321 :
322 170775 : close (fd);
323 :
324 170775 : vec_reset_length (f);
325 170775 : f = format (f, "%v/vpd%c", dev_dir_name, 0);
326 170775 : fd = open ((char *) f, O_RDONLY);
327 170775 : if (fd >= 0)
328 : {
329 : while (1)
330 2300 : {
331 : u8 tag[3];
332 3450 : u8 *data = 0;
333 : uword len;
334 :
335 3450 : if (read (fd, &tag, 3) != 3)
336 1150 : break;
337 :
338 2300 : if (tag[0] != 0x82 && tag[0] != 0x90 && tag[0] != 0x91)
339 0 : break;
340 :
341 2300 : len = (tag[2] << 8) | tag[1];
342 2300 : vec_validate (data, len - 1);
343 :
344 2300 : if (read (fd, data, len) != len)
345 : {
346 0 : vec_free (data);
347 0 : break;
348 : }
349 2300 : if (tag[0] == 0x82)
350 1150 : di->product_name = data;
351 1150 : else if (tag[0] == 0x90)
352 1150 : di->vpd_r = data;
353 0 : else if (tag[0] == 0x91)
354 0 : di->vpd_w = data;
355 :
356 2300 : data = 0;
357 : }
358 1150 : close (fd);
359 : }
360 :
361 170775 : goto done;
362 :
363 0 : error:
364 0 : vlib_pci_free_device_info (di);
365 0 : di = 0;
366 :
367 170775 : done:
368 170775 : vec_free (bmp);
369 170775 : vec_free (f);
370 170775 : vec_free (dev_dir_name);
371 170775 : if (error)
372 0 : *error = err;
373 : else
374 170775 : clib_error_free (err);
375 170775 : return di;
376 : }
377 :
378 : clib_error_t *__attribute__ ((weak))
379 0 : vlib_pci_get_device_root_bus (vlib_pci_addr_t *addr, vlib_pci_addr_t *root_bus)
380 : {
381 0 : u8 *rel_path = 0, *abs_path = 0, *link_path = 0;
382 : unformat_input_t input;
383 0 : int fd = open (sysfs_pci_dev_path, O_RDONLY);
384 0 : ssize_t size = 0;
385 0 : u32 domain = 0, bus;
386 0 : clib_error_t *err = NULL;
387 :
388 0 : if (fd < 0)
389 0 : return clib_error_return_unix (0, "failed to open %s", sysfs_pci_dev_path);
390 :
391 0 : vec_alloc (rel_path, PATH_MAX);
392 0 : vec_alloc (abs_path, PATH_MAX);
393 :
394 0 : link_path =
395 0 : format (0, "%s/%U", sysfs_pci_dev_path, format_vlib_pci_addr, addr);
396 0 : size = readlinkat (fd, (char *) link_path, (char *) rel_path, PATH_MAX);
397 0 : if (size < 0)
398 : {
399 0 : err = clib_error_return_unix (0, "failed to read %s", rel_path);
400 0 : goto done;
401 : }
402 :
403 0 : rel_path[size] = '\0';
404 0 : vec_free (link_path);
405 :
406 0 : link_path = format (0, "%s/%s", sysfs_pci_dev_path, rel_path);
407 0 : if (!realpath ((char *) link_path, (char *) abs_path))
408 : {
409 0 : err = clib_error_return_unix (0, "failed to resolve %s", link_path);
410 0 : goto done;
411 : }
412 :
413 0 : unformat_init_string (&input, (char *) abs_path,
414 0 : clib_strnlen ((char *) abs_path, PATH_MAX));
415 :
416 0 : if (!unformat (&input, SYSFS_DEVICES_PCI "%x:%x/%s", &domain, &bus,
417 : link_path))
418 : {
419 0 : err = clib_error_return (0, "unknown input '%U'", format_unformat_error,
420 : input);
421 0 : goto done;
422 : }
423 :
424 0 : root_bus->domain = domain;
425 0 : root_bus->bus = bus;
426 :
427 0 : done:
428 0 : vec_free (abs_path);
429 0 : vec_free (link_path);
430 0 : vec_free (rel_path);
431 0 : close (fd);
432 :
433 0 : return err;
434 : }
435 :
436 : static int
437 0 : directory_exists (char *path)
438 : {
439 0 : struct stat s = { 0 };
440 0 : if (stat (path, &s) == -1)
441 0 : return 0;
442 :
443 0 : return S_ISDIR (s.st_mode);
444 : }
445 :
446 : clib_error_t *
447 0 : vlib_pci_bind_to_uio (vlib_main_t *vm, vlib_pci_addr_t *addr,
448 : char *uio_drv_name, int force)
449 : {
450 0 : clib_error_t *error = 0;
451 0 : u8 *s = 0, *driver_name = 0;
452 0 : DIR *dir = 0;
453 : struct dirent *e;
454 : vlib_pci_device_info_t *di;
455 0 : int fd, clear_driver_override = 0;
456 0 : u8 *dev_dir_name = format (0, "%s/%U", sysfs_pci_dev_path,
457 : format_vlib_pci_addr, addr);
458 :
459 0 : di = vlib_pci_get_device_info (vm, addr, &error);
460 :
461 0 : if (error)
462 0 : return error;
463 :
464 0 : if (strncmp ("auto", uio_drv_name, 5) == 0)
465 : {
466 0 : int vfio_pci_loaded = 0;
467 :
468 0 : if (directory_exists ("/sys/module/vfio_pci"))
469 0 : vfio_pci_loaded = 1;
470 :
471 0 : if (di->iommu_group != -1)
472 : {
473 : /* device is bound to IOMMU group */
474 0 : if (!vfio_pci_loaded)
475 : {
476 0 : error = clib_error_return (0, "Skipping PCI device %U: device "
477 : "is bound to IOMMU group and "
478 : "vfio-pci driver is not loaded",
479 : format_vlib_pci_addr, addr);
480 0 : goto err0;
481 : }
482 : else
483 0 : uio_drv_name = "vfio-pci";
484 : }
485 : else
486 : {
487 : /* device is not bound to IOMMU group so we have multiple options */
488 0 : if (vfio_pci_loaded &&
489 0 : (error = clib_sysfs_write (sysfs_mod_vfio_noiommu, "Y")) == 0)
490 0 : uio_drv_name = "vfio-pci";
491 0 : else if (directory_exists ("/sys/module/uio_pci_generic"))
492 0 : uio_drv_name = "uio_pci_generic";
493 0 : else if (directory_exists ("/sys/module/igb_uio"))
494 0 : uio_drv_name = "igb_uio";
495 : else
496 : {
497 0 : clib_error_free (error);
498 0 : error = clib_error_return (0, "Skipping PCI device %U: missing "
499 : "kernel VFIO or UIO driver",
500 : format_vlib_pci_addr, addr);
501 0 : goto err0;
502 : }
503 0 : clib_error_free (error);
504 : }
505 : }
506 :
507 0 : driver_name = clib_file_get_resolved_basename ("%v/driver", dev_dir_name);
508 :
509 0 : if (driver_name &&
510 0 : ((strcmp ("vfio-pci", (char *) driver_name) == 0) ||
511 0 : (strcmp ("uio_pci_generic", (char *) driver_name) == 0) ||
512 0 : (strcmp ("igb_uio", (char *) driver_name) == 0)))
513 0 : goto err0;
514 :
515 0 : if (!force)
516 : {
517 : /* walk trough all linux interfaces and if interface belonging to
518 : this device is found check if interface is admin up */
519 0 : dir = opendir ("/sys/class/net");
520 0 : s = format (s, "%U%c", format_vlib_pci_addr, addr, 0);
521 :
522 0 : if (!dir)
523 : {
524 0 : error = clib_error_return (0,
525 : "Skipping PCI device %U: failed to "
526 : "read /sys/class/net",
527 : format_vlib_pci_addr, addr);
528 0 : goto err0;
529 : }
530 :
531 0 : fd = socket (PF_INET, SOCK_DGRAM, 0);
532 0 : if (fd < 0)
533 : {
534 0 : error = clib_error_return_unix (0, "socket");
535 0 : goto err1;
536 : }
537 :
538 0 : while ((e = readdir (dir)))
539 : {
540 : struct ifreq ifr;
541 : struct ethtool_drvinfo drvinfo;
542 :
543 0 : if (e->d_name[0] == '.') /* skip . and .. */
544 0 : continue;
545 :
546 0 : clib_memset (&ifr, 0, sizeof ifr);
547 0 : clib_memset (&drvinfo, 0, sizeof drvinfo);
548 0 : ifr.ifr_data = (char *) &drvinfo;
549 0 : clib_strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name) - 1);
550 :
551 0 : drvinfo.cmd = ETHTOOL_GDRVINFO;
552 0 : if (ioctl (fd, SIOCETHTOOL, &ifr) < 0)
553 : {
554 : /* Some interfaces (eg "lo") don't support this ioctl */
555 0 : if ((errno != ENOTSUP) && (errno != ENODEV))
556 0 : clib_unix_warning ("ioctl fetch intf %s bus info error",
557 : e->d_name);
558 0 : continue;
559 : }
560 :
561 0 : if (strcmp ((char *) s, drvinfo.bus_info))
562 0 : continue;
563 :
564 0 : clib_memset (&ifr, 0, sizeof (ifr));
565 0 : clib_strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name) - 1);
566 :
567 0 : if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
568 : {
569 0 : error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
570 : e->d_name);
571 0 : close (fd);
572 0 : goto err1;
573 : }
574 :
575 0 : if (ifr.ifr_flags & IFF_UP)
576 : {
577 0 : vlib_log (VLIB_LOG_LEVEL_WARNING, pci_main.log_default,
578 : "Skipping PCI device %U as host "
579 : "interface %s is up",
580 0 : format_vlib_pci_addr, addr, e->d_name);
581 0 : close (fd);
582 0 : goto err1;
583 : }
584 : }
585 :
586 0 : close (fd);
587 0 : vec_reset_length (s);
588 : }
589 :
590 0 : s = format (s, "%v/driver/unbind%c", dev_dir_name, 0);
591 0 : clib_sysfs_write ((char *) s, "%U", format_vlib_pci_addr, addr);
592 0 : vec_reset_length (s);
593 :
594 0 : s = format (s, "%v/driver_override%c", dev_dir_name, 0);
595 0 : if (access ((char *) s, F_OK) == 0)
596 : {
597 0 : clib_sysfs_write ((char *) s, "%s", uio_drv_name);
598 0 : clear_driver_override = 1;
599 : }
600 : else
601 : {
602 0 : vec_reset_length (s);
603 0 : s = format (s, "%s/%s/new_id%c", sysfs_pci_drv_path, uio_drv_name, 0);
604 0 : clib_sysfs_write ((char *) s, "0x%04x 0x%04x", di->vendor_id,
605 0 : di->device_id);
606 : }
607 0 : vec_reset_length (s);
608 :
609 0 : s = format (s, "%s/%s/bind%c", sysfs_pci_drv_path, uio_drv_name, 0);
610 0 : clib_sysfs_write ((char *) s, "%U", format_vlib_pci_addr, addr);
611 0 : vec_reset_length (s);
612 :
613 0 : if (clear_driver_override)
614 : {
615 0 : s = format (s, "%v/driver_override%c", dev_dir_name, 0);
616 0 : clib_sysfs_write ((char *) s, "%c", 0);
617 0 : vec_reset_length (s);
618 : }
619 :
620 0 : err1:
621 0 : closedir (dir);
622 0 : err0:
623 0 : vec_free (s);
624 0 : vec_free (dev_dir_name);
625 0 : vec_free (driver_name);
626 0 : return error;
627 : }
628 :
629 :
630 : static clib_error_t *
631 0 : scan_uio_dir (void *arg, u8 * path_name, u8 * file_name)
632 : {
633 0 : linux_pci_device_t *l = arg;
634 : unformat_input_t input;
635 :
636 0 : unformat_init_string (&input, (char *) file_name, vec_len (file_name));
637 :
638 0 : if (!unformat (&input, "uio%d", &l->uio_minor))
639 0 : abort ();
640 :
641 0 : unformat_free (&input);
642 0 : return 0;
643 : }
644 :
645 : static clib_error_t *
646 0 : vfio_set_irqs (vlib_main_t * vm, linux_pci_device_t * p, u32 index, u32 start,
647 : u32 count, u32 flags, int *efds)
648 0 : {
649 0 : int data_len = efds ? count * sizeof (int) : 0;
650 0 : u8 buf[sizeof (struct vfio_irq_set) + data_len];
651 0 : struct vfio_irq_info ii = { 0 };
652 0 : struct vfio_irq_set *irq_set = (struct vfio_irq_set *) buf;
653 :
654 :
655 0 : ii.argsz = sizeof (struct vfio_irq_info);
656 0 : ii.index = index;
657 :
658 0 : if (ioctl (p->fd, VFIO_DEVICE_GET_IRQ_INFO, &ii) < 0)
659 0 : return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_IRQ_INFO) "
660 : "'%U'", format_vlib_pci_addr, &p->addr);
661 :
662 0 : log_debug (p, "%s index:%u count:%u flags: %s%s%s%s(0x%x)", __func__,
663 : ii.index, ii.count,
664 : ii.flags & VFIO_IRQ_INFO_EVENTFD ? "eventfd " : "",
665 : ii.flags & VFIO_IRQ_INFO_MASKABLE ? "maskable " : "",
666 : ii.flags & VFIO_IRQ_INFO_AUTOMASKED ? "automasked " : "",
667 : ii.flags & VFIO_IRQ_INFO_NORESIZE ? "noresize " : "", ii.flags);
668 :
669 0 : if (ii.count < start + count)
670 0 : return clib_error_return_unix (0, "vfio_set_irq: unexistng interrupt on "
671 : "'%U'", format_vlib_pci_addr, &p->addr);
672 :
673 :
674 0 : if (efds)
675 : {
676 0 : flags |= VFIO_IRQ_SET_DATA_EVENTFD;
677 0 : clib_memcpy_fast (&irq_set->data, efds, data_len);
678 : }
679 : else
680 0 : flags |= VFIO_IRQ_SET_DATA_NONE;
681 :
682 0 : ASSERT ((flags & (VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_DATA_EVENTFD)) !=
683 : (VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_DATA_EVENTFD));
684 :
685 0 : irq_set->argsz = sizeof (struct vfio_irq_set) + data_len;
686 0 : irq_set->index = index;
687 0 : irq_set->start = start;
688 0 : irq_set->count = count;
689 0 : irq_set->flags = flags;
690 :
691 0 : if (ioctl (p->fd, VFIO_DEVICE_SET_IRQS, irq_set) < 0)
692 0 : return clib_error_return_unix (0, "%U:ioctl(VFIO_DEVICE_SET_IRQS) "
693 : "[index = %u, start = %u, count = %u, "
694 : "flags = 0x%x]",
695 : format_vlib_pci_addr, &p->addr,
696 : index, start, count, flags);
697 0 : return 0;
698 : }
699 :
700 : static clib_error_t *
701 0 : linux_pci_uio_read_ready (clib_file_t * uf)
702 : {
703 0 : vlib_main_t *vm = vlib_get_main ();
704 : int __attribute__ ((unused)) rv;
705 0 : vlib_pci_dev_handle_t h = uf->private_data;
706 0 : linux_pci_device_t *p = linux_pci_get_device (h);
707 0 : linux_pci_irq_t *irq = &p->intx_irq;
708 :
709 : u32 icount;
710 0 : rv = read (uf->file_descriptor, &icount, 4);
711 :
712 0 : if (irq->intx_handler)
713 0 : irq->intx_handler (vm, h);
714 :
715 0 : vlib_pci_intr_enable (vm, h);
716 :
717 0 : return /* no error */ 0;
718 : }
719 :
720 : static clib_error_t *
721 0 : linux_pci_vfio_unmask_intx (vlib_main_t * vm, linux_pci_device_t * d)
722 : {
723 0 : return vfio_set_irqs (vm, d, VFIO_PCI_INTX_IRQ_INDEX, 0, 1,
724 : VFIO_IRQ_SET_ACTION_UNMASK, 0);
725 : }
726 :
727 : static clib_error_t *
728 0 : linux_pci_uio_error_ready (clib_file_t * uf)
729 : {
730 0 : u32 error_index = (u32) uf->private_data;
731 :
732 0 : return clib_error_return (0, "pci device %d: error", error_index);
733 : }
734 :
735 : static clib_error_t *
736 0 : linux_pci_vfio_msix_read_ready (clib_file_t * uf)
737 : {
738 0 : vlib_main_t *vm = vlib_get_main ();
739 : int __attribute__ ((unused)) rv;
740 0 : vlib_pci_dev_handle_t h = uf->private_data >> 16;
741 0 : u16 line = uf->private_data & 0xffff;
742 0 : linux_pci_device_t *p = linux_pci_get_device (h);
743 0 : linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, line);
744 :
745 : u64 icount;
746 0 : rv = read (uf->file_descriptor, &icount, sizeof (icount));
747 :
748 0 : if (irq->msix_handler)
749 0 : irq->msix_handler (vm, h, line);
750 :
751 0 : return /* no error */ 0;
752 : }
753 :
754 : static clib_error_t *
755 0 : linux_pci_vfio_intx_read_ready (clib_file_t * uf)
756 : {
757 0 : vlib_main_t *vm = vlib_get_main ();
758 : int __attribute__ ((unused)) rv;
759 0 : vlib_pci_dev_handle_t h = uf->private_data;
760 0 : linux_pci_device_t *p = linux_pci_get_device (h);
761 0 : linux_pci_irq_t *irq = &p->intx_irq;
762 :
763 : u64 icount;
764 0 : rv = read (uf->file_descriptor, &icount, sizeof (icount));
765 :
766 0 : if (irq->intx_handler)
767 0 : irq->intx_handler (vm, h);
768 :
769 0 : linux_pci_vfio_unmask_intx (vm, p);
770 :
771 0 : return /* no error */ 0;
772 : }
773 :
774 : static clib_error_t *
775 0 : linux_pci_vfio_error_ready (clib_file_t * uf)
776 : {
777 0 : u32 error_index = (u32) uf->private_data;
778 :
779 0 : return clib_error_return (0, "pci device %d: error", error_index);
780 : }
781 :
782 : static clib_error_t *
783 0 : add_device_uio (vlib_main_t * vm, linux_pci_device_t * p,
784 : vlib_pci_device_info_t * di, pci_device_registration_t * r)
785 : {
786 0 : linux_pci_main_t *lpm = &linux_pci_main;
787 0 : clib_error_t *err = 0;
788 0 : u8 *s = 0;
789 :
790 0 : p->fd = -1;
791 0 : p->type = LINUX_PCI_DEVICE_TYPE_UIO;
792 :
793 0 : s = format (s, "%s/%U/config%c", sysfs_pci_dev_path,
794 : format_vlib_pci_addr, &di->addr, 0);
795 :
796 0 : p->config_fd = open ((char *) s, O_RDWR);
797 0 : p->config_offset = 0;
798 0 : vec_reset_length (s);
799 :
800 0 : if (p->config_fd == -1)
801 : {
802 0 : err = clib_error_return_unix (0, "open '%s'", s);
803 0 : goto error;
804 : }
805 :
806 0 : s = format (0, "%s/%U/uio%c", sysfs_pci_dev_path,
807 : format_vlib_pci_addr, &di->addr, 0);
808 0 : foreach_directory_file ((char *) s, scan_uio_dir, p, /* scan_dirs */
809 : 1);
810 0 : vec_reset_length (s);
811 :
812 0 : s = format (s, "/dev/uio%d%c", p->uio_minor, 0);
813 0 : p->fd = open ((char *) s, O_RDWR);
814 0 : if (p->fd < 0)
815 : {
816 0 : err = clib_error_return_unix (0, "open '%s'", s);
817 0 : goto error;
818 : }
819 :
820 0 : if (r && r->interrupt_handler)
821 0 : vlib_pci_register_intx_handler (vm, p->handle, r->interrupt_handler);
822 :
823 0 : if (r && r->init_function)
824 0 : err = r->init_function (lpm->vlib_main, p->handle);
825 :
826 0 : error:
827 0 : vec_free (s);
828 0 : if (err)
829 : {
830 0 : if (p->config_fd != -1)
831 0 : close (p->config_fd);
832 0 : if (p->fd != -1)
833 0 : close (p->fd);
834 : }
835 0 : return err;
836 : }
837 :
838 : clib_error_t *
839 0 : vlib_pci_register_intx_handler (vlib_main_t * vm, vlib_pci_dev_handle_t h,
840 : pci_intx_handler_function_t * intx_handler)
841 : {
842 0 : linux_pci_device_t *p = linux_pci_get_device (h);
843 0 : clib_file_t t = { 0 };
844 0 : linux_pci_irq_t *irq = &p->intx_irq;
845 0 : ASSERT (irq->fd == -1);
846 :
847 0 : if (p->type == LINUX_PCI_DEVICE_TYPE_VFIO)
848 : {
849 0 : struct vfio_irq_info ii = { 0 };
850 0 : ii.argsz = sizeof (struct vfio_irq_info);
851 0 : ii.index = VFIO_PCI_INTX_IRQ_INDEX;
852 0 : if (ioctl (p->fd, VFIO_DEVICE_GET_IRQ_INFO, &ii) < 0)
853 0 : return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_IRQ_INFO) '"
854 : "%U'", format_vlib_pci_addr, &p->addr);
855 0 : log_debug (
856 : p, "%s index:%u count:%u flags: %s%s%s%s(0x%x)", __func__, ii.index,
857 : ii.count, ii.flags & VFIO_IRQ_INFO_EVENTFD ? "eventfd " : "",
858 : ii.flags & VFIO_IRQ_INFO_MASKABLE ? "maskable " : "",
859 : ii.flags & VFIO_IRQ_INFO_AUTOMASKED ? "automasked " : "",
860 : ii.flags & VFIO_IRQ_INFO_NORESIZE ? "noresize " : "", ii.flags);
861 0 : if (ii.count != 1)
862 0 : return clib_error_return (0, "INTx interrupt does not exist on device"
863 : "'%U'", format_vlib_pci_addr, &p->addr);
864 :
865 0 : irq->fd = eventfd (0, EFD_NONBLOCK);
866 0 : if (irq->fd == -1)
867 0 : return clib_error_return_unix (0, "eventfd");
868 :
869 0 : t.file_descriptor = irq->fd;
870 0 : t.read_function = linux_pci_vfio_intx_read_ready;
871 : }
872 0 : else if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
873 : {
874 0 : t.file_descriptor = p->fd;
875 0 : t.read_function = linux_pci_uio_read_ready;
876 : }
877 : else
878 0 : return 0;
879 :
880 0 : t.error_function = linux_pci_uio_error_ready;
881 0 : t.private_data = p->handle;
882 0 : t.description = format (0, "PCI %U INTx", format_vlib_pci_addr, &p->addr);
883 0 : irq->clib_file_index = clib_file_add (&file_main, &t);
884 0 : irq->intx_handler = intx_handler;
885 0 : return 0;
886 : }
887 :
888 : clib_error_t *
889 0 : vlib_pci_register_msix_handler (vlib_main_t * vm, vlib_pci_dev_handle_t h,
890 : u32 start, u32 count,
891 : pci_msix_handler_function_t * msix_handler)
892 : {
893 0 : clib_error_t *err = 0;
894 0 : linux_pci_device_t *p = linux_pci_get_device (h);
895 : u32 i;
896 :
897 0 : if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
898 0 : return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
899 : "support");
900 :
901 : /* *INDENT-OFF* */
902 0 : vec_validate_init_empty (p->msix_irqs, start + count - 1, (linux_pci_irq_t)
903 : { .fd = -1});
904 : /* *INDENT-ON* */
905 :
906 0 : for (i = start; i < start + count; i++)
907 : {
908 0 : clib_file_t t = { 0 };
909 0 : linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
910 0 : ASSERT (irq->fd == -1);
911 :
912 0 : irq->fd = eventfd (0, EFD_NONBLOCK);
913 0 : if (irq->fd == -1)
914 : {
915 0 : err = clib_error_return_unix (0, "eventfd");
916 0 : goto error;
917 : }
918 :
919 0 : t.read_function = linux_pci_vfio_msix_read_ready;
920 0 : t.file_descriptor = irq->fd;
921 0 : t.error_function = linux_pci_vfio_error_ready;
922 0 : t.private_data = p->handle << 16 | i;
923 0 : t.description = format (0, "PCI %U MSI-X #%u", format_vlib_pci_addr,
924 : &p->addr, i);
925 0 : irq->clib_file_index = clib_file_add (&file_main, &t);
926 0 : irq->msix_handler = msix_handler;
927 : }
928 :
929 0 : return 0;
930 :
931 0 : error:
932 0 : while (i-- > start)
933 : {
934 0 : linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
935 0 : if (irq->fd != -1)
936 : {
937 0 : clib_file_del_by_index (&file_main, irq->clib_file_index);
938 0 : close (irq->fd);
939 0 : irq->fd = -1;
940 : }
941 : }
942 0 : return err;
943 : }
944 :
945 : clib_error_t *
946 0 : vlib_pci_enable_msix_irq (vlib_main_t * vm, vlib_pci_dev_handle_t h,
947 : u16 start, u16 count)
948 0 : {
949 0 : linux_pci_device_t *p = linux_pci_get_device (h);
950 0 : int fds[count];
951 : int i;
952 :
953 0 : if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
954 0 : return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
955 : "support");
956 :
957 0 : for (i = start; i < start + count; i++)
958 : {
959 0 : linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i);
960 0 : fds[i] = irq->fd;
961 : }
962 :
963 0 : return vfio_set_irqs (vm, p, VFIO_PCI_MSIX_IRQ_INDEX, start, count,
964 : VFIO_IRQ_SET_ACTION_TRIGGER, fds);
965 : }
966 :
967 : uword
968 0 : vlib_pci_get_msix_file_index (vlib_main_t * vm, vlib_pci_dev_handle_t h,
969 : u16 index)
970 : {
971 0 : linux_pci_device_t *p = linux_pci_get_device (h);
972 0 : linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, index);
973 0 : if (irq->fd == -1)
974 0 : return ~0;
975 0 : return irq->clib_file_index;
976 : }
977 :
978 : clib_error_t *
979 0 : vlib_pci_disable_msix_irq (vlib_main_t * vm, vlib_pci_dev_handle_t h,
980 : u16 start, u16 count)
981 0 : {
982 0 : linux_pci_device_t *p = linux_pci_get_device (h);
983 0 : int i, fds[count];
984 :
985 0 : if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO)
986 0 : return clib_error_return (0, "vfio driver is needed for MSI-X interrupt "
987 : "support");
988 :
989 0 : for (i = start; i < start + count; i++)
990 0 : fds[i] = -1;
991 :
992 0 : return vfio_set_irqs (vm, p, VFIO_PCI_MSIX_IRQ_INDEX, start, count,
993 : VFIO_IRQ_SET_ACTION_TRIGGER, fds);
994 : }
995 :
996 : static clib_error_t *
997 0 : add_device_vfio (vlib_main_t * vm, linux_pci_device_t * p,
998 : vlib_pci_device_info_t * di, pci_device_registration_t * r)
999 : {
1000 0 : linux_pci_main_t *lpm = &linux_pci_main;
1001 0 : struct vfio_device_info device_info = { 0 };
1002 0 : struct vfio_region_info reg = { 0 };
1003 0 : clib_error_t *err = 0;
1004 0 : u8 *s = 0;
1005 : int is_noiommu;
1006 :
1007 0 : p->type = LINUX_PCI_DEVICE_TYPE_VFIO;
1008 :
1009 0 : if ((err = linux_vfio_group_get_device_fd (&p->addr, &p->fd, &is_noiommu)))
1010 0 : return err;
1011 :
1012 0 : if (is_noiommu == 0)
1013 0 : p->supports_va_dma = 1;
1014 :
1015 0 : device_info.argsz = sizeof (device_info);
1016 0 : if (ioctl (p->fd, VFIO_DEVICE_GET_INFO, &device_info) < 0)
1017 : {
1018 0 : err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_INFO) '%U'",
1019 : format_vlib_pci_addr, &di->addr);
1020 0 : goto error;
1021 : }
1022 :
1023 0 : reg.argsz = sizeof (struct vfio_region_info);
1024 0 : reg.index = VFIO_PCI_CONFIG_REGION_INDEX;
1025 0 : if (ioctl (p->fd, VFIO_DEVICE_GET_REGION_INFO, ®) < 0)
1026 : {
1027 0 : err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_INFO) '%U'",
1028 : format_vlib_pci_addr, &di->addr);
1029 0 : goto error;
1030 : }
1031 :
1032 0 : log_debug (p, "%s %U", __func__, format_vfio_region_info, ®);
1033 :
1034 0 : p->config_offset = reg.offset;
1035 0 : p->config_fd = p->fd;
1036 :
1037 : /* reset if device supports it */
1038 0 : if (device_info.flags & VFIO_DEVICE_FLAGS_RESET)
1039 0 : if (ioctl (p->fd, VFIO_DEVICE_RESET) < 0)
1040 : {
1041 0 : err = clib_error_return_unix (0, "ioctl(VFIO_DEVICE_RESET) '%U'",
1042 : format_vlib_pci_addr, &di->addr);
1043 0 : goto error;
1044 : }
1045 :
1046 0 : if (r && r->interrupt_handler)
1047 : {
1048 0 : vlib_pci_register_intx_handler (vm, p->handle, r->interrupt_handler);
1049 0 : linux_pci_vfio_unmask_intx (vm, p);
1050 : }
1051 :
1052 0 : if (p->supports_va_dma)
1053 : {
1054 : vlib_buffer_pool_t *bp;
1055 : /* *INDENT-OFF* */
1056 0 : vec_foreach (bp, vm->buffer_main->buffer_pools)
1057 : {
1058 : u32 i;
1059 : vlib_physmem_map_t *pm;
1060 0 : pm = vlib_physmem_get_map (vm, bp->physmem_map_index);
1061 0 : for (i = 0; i < pm->n_pages; i++)
1062 0 : vfio_map_physmem_page (vm, pm->base + (i << pm->log2_page_size));
1063 : }
1064 : /* *INDENT-ON* */
1065 : }
1066 :
1067 0 : if (r && r->init_function)
1068 0 : err = r->init_function (lpm->vlib_main, p->handle);
1069 :
1070 0 : error:
1071 0 : vec_free (s);
1072 0 : if (err)
1073 : {
1074 0 : if (p->fd != -1)
1075 0 : close (p->fd);
1076 0 : if (p->config_fd != -1 && p->config_fd != p->fd)
1077 0 : close (p->config_fd);
1078 0 : p->config_fd = p->fd = -1;
1079 : }
1080 0 : return err;
1081 : }
1082 :
1083 : /* Configuration space read/write. */
1084 : clib_error_t *
1085 0 : vlib_pci_read_write_config (vlib_main_t * vm, vlib_pci_dev_handle_t h,
1086 : vlib_read_or_write_t read_or_write,
1087 : uword address, void *data, u32 n_bytes)
1088 : {
1089 0 : linux_pci_device_t *p = linux_pci_get_device (h);
1090 : int n;
1091 :
1092 0 : if (read_or_write == VLIB_READ)
1093 0 : n = pread (p->config_fd, data, n_bytes, p->config_offset + address);
1094 : else
1095 0 : n = pwrite (p->config_fd, data, n_bytes, p->config_offset + address);
1096 :
1097 0 : if (n != n_bytes)
1098 0 : return clib_error_return_unix (0, "%s",
1099 : read_or_write == VLIB_READ
1100 : ? "read" : "write");
1101 :
1102 0 : return 0;
1103 : }
1104 :
1105 : static clib_error_t *
1106 0 : vlib_pci_region (vlib_main_t * vm, vlib_pci_dev_handle_t h, u32 bar, int *fd,
1107 : u64 * size, u64 * offset)
1108 : {
1109 0 : linux_pci_device_t *p = linux_pci_get_device (h);
1110 0 : clib_error_t *error = 0;
1111 0 : int _fd = -1;
1112 0 : u64 _size = 0, _offset = 0;
1113 :
1114 0 : ASSERT (bar <= 5);
1115 :
1116 0 : error = 0;
1117 :
1118 0 : if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
1119 : {
1120 : u8 *file_name;
1121 : struct stat stat_buf;
1122 0 : file_name = format (0, "%s/%U/resource%d%c", sysfs_pci_dev_path,
1123 : format_vlib_pci_addr, &p->addr, bar, 0);
1124 :
1125 0 : _fd = open ((char *) file_name, O_RDWR);
1126 0 : if (_fd < 0)
1127 : {
1128 0 : error = clib_error_return_unix (0, "open `%s'", file_name);
1129 0 : vec_free (file_name);
1130 0 : return error;
1131 : }
1132 :
1133 0 : if (fstat (_fd, &stat_buf) < 0)
1134 : {
1135 0 : error = clib_error_return_unix (0, "fstat `%s'", file_name);
1136 0 : vec_free (file_name);
1137 0 : close (_fd);
1138 0 : return error;
1139 : }
1140 :
1141 0 : vec_free (file_name);
1142 0 : _size = stat_buf.st_size;
1143 0 : _offset = 0;
1144 : }
1145 0 : else if (p->type == LINUX_PCI_DEVICE_TYPE_VFIO)
1146 : {
1147 : struct vfio_region_info *r;
1148 0 : u32 sz = sizeof (struct vfio_region_info);
1149 0 : again:
1150 0 : r = clib_mem_alloc (sz);
1151 0 : clib_memset (r, 0, sz);
1152 0 : r->argsz = sz;
1153 0 : r->index = bar;
1154 0 : if (ioctl (p->fd, VFIO_DEVICE_GET_REGION_INFO, r) < 0)
1155 0 : return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_INFO) "
1156 : "'%U'", format_vlib_pci_addr,
1157 : &p->addr);
1158 0 : if (sz != r->argsz)
1159 : {
1160 0 : sz = r->argsz;
1161 0 : clib_mem_free (r);
1162 0 : goto again;
1163 : }
1164 0 : _fd = p->fd;
1165 0 : _size = r->size;
1166 0 : _offset = r->offset;
1167 0 : log_debug (p, "%s %U", __func__, format_vfio_region_info, r);
1168 0 : clib_mem_free (r);
1169 : }
1170 : else
1171 0 : ASSERT (0);
1172 :
1173 0 : *fd = _fd;
1174 0 : *size = _size;
1175 0 : *offset = _offset;
1176 :
1177 0 : return error;
1178 : }
1179 :
1180 : static clib_error_t *
1181 0 : vlib_pci_map_region_int (vlib_main_t * vm, vlib_pci_dev_handle_t h,
1182 : u32 bar, u8 * addr, void **result)
1183 : {
1184 0 : linux_pci_device_t *p = linux_pci_get_device (h);
1185 0 : int fd = -1;
1186 : clib_error_t *error;
1187 0 : u64 size = 0, offset = 0;
1188 : vlib_pci_config_reg_command_t command;
1189 :
1190 0 : log_debug (p, "map region %u to va %p", bar, addr);
1191 :
1192 0 : if ((error = vlib_pci_read_config_u16 (vm, h, 4, &command.as_u16)))
1193 0 : return error;
1194 :
1195 0 : if (!(command.mem_space))
1196 : {
1197 0 : log_debug (p, "setting memory enable bit");
1198 0 : command.mem_space = 1;
1199 0 : if ((error = vlib_pci_write_config_u16 (vm, h, 4, &command.as_u16)))
1200 0 : return error;
1201 : }
1202 :
1203 0 : if ((error = vlib_pci_region (vm, h, bar, &fd, &size, &offset)))
1204 0 : return error;
1205 :
1206 0 : *result = clib_mem_vm_map_shared (addr, size, fd, offset,
1207 : "PCIe %U region %u", format_vlib_pci_addr,
1208 : vlib_pci_get_addr (vm, h), bar);
1209 0 : if (*result == CLIB_MEM_VM_MAP_FAILED)
1210 : {
1211 0 : error = clib_error_return_unix (0, "mmap `BAR%u'", bar);
1212 0 : if (p->type == LINUX_PCI_DEVICE_TYPE_UIO && (fd != -1))
1213 0 : close (fd);
1214 0 : return error;
1215 : }
1216 :
1217 : /* *INDENT-OFF* */
1218 0 : vec_validate_init_empty (p->regions, bar,
1219 : (linux_pci_region_t) { .fd = -1});
1220 : /* *INDENT-ON* */
1221 0 : if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
1222 0 : p->regions[bar].fd = fd;
1223 0 : p->regions[bar].addr = *result;
1224 0 : p->regions[bar].size = size;
1225 0 : return 0;
1226 : }
1227 :
1228 : clib_error_t *
1229 0 : vlib_pci_map_region (vlib_main_t * vm, vlib_pci_dev_handle_t h, u32 resource,
1230 : void **result)
1231 : {
1232 0 : return (vlib_pci_map_region_int (vm, h, resource, 0 /* addr */ , result));
1233 : }
1234 :
1235 : clib_error_t *
1236 0 : vlib_pci_map_region_fixed (vlib_main_t * vm, vlib_pci_dev_handle_t h,
1237 : u32 resource, u8 * addr, void **result)
1238 : {
1239 0 : return (vlib_pci_map_region_int (vm, h, resource, addr, result));
1240 : }
1241 :
1242 : clib_error_t *
1243 0 : vlib_pci_io_region (vlib_main_t * vm, vlib_pci_dev_handle_t h, u32 resource)
1244 : {
1245 0 : linux_pci_device_t *p = linux_pci_get_device (h);
1246 0 : clib_error_t *error = 0;
1247 0 : int fd = -1;
1248 0 : u64 size = 0, offset = 0;
1249 :
1250 0 : if ((error = vlib_pci_region (vm, h, resource, &fd, &size, &offset)))
1251 0 : return error;
1252 :
1253 0 : p->io_fd = fd;
1254 0 : p->io_offset = offset;
1255 0 : return error;
1256 : }
1257 :
1258 : clib_error_t *
1259 0 : vlib_pci_read_write_io (vlib_main_t * vm, vlib_pci_dev_handle_t h,
1260 : vlib_read_or_write_t read_or_write,
1261 : uword offset, void *data, u32 length)
1262 : {
1263 0 : linux_pci_device_t *p = linux_pci_get_device (h);
1264 0 : int n = 0;
1265 :
1266 0 : if (read_or_write == VLIB_READ)
1267 0 : n = pread (p->io_fd, data, length, p->io_offset + offset);
1268 : else
1269 0 : n = pwrite (p->io_fd, data, length, p->io_offset + offset);
1270 :
1271 0 : if (n != length)
1272 0 : return clib_error_return_unix (0, "%s",
1273 : read_or_write == VLIB_READ
1274 : ? "read" : "write");
1275 0 : return 0;
1276 : }
1277 :
1278 : clib_error_t *
1279 0 : vlib_pci_map_dma (vlib_main_t * vm, vlib_pci_dev_handle_t h, void *ptr)
1280 : {
1281 0 : linux_pci_device_t *p = linux_pci_get_device (h);
1282 :
1283 0 : if (!p->supports_va_dma)
1284 0 : return 0;
1285 :
1286 0 : return vfio_map_physmem_page (vm, ptr);
1287 : }
1288 :
1289 : int
1290 0 : vlib_pci_supports_virtual_addr_dma (vlib_main_t * vm, vlib_pci_dev_handle_t h)
1291 : {
1292 0 : linux_pci_device_t *p = linux_pci_get_device (h);
1293 :
1294 0 : return p->supports_va_dma != 0;
1295 : }
1296 :
1297 : clib_error_t *
1298 0 : vlib_pci_device_open (vlib_main_t * vm, vlib_pci_addr_t * addr,
1299 : pci_device_id_t ids[], vlib_pci_dev_handle_t * handle)
1300 : {
1301 0 : linux_pci_main_t *lpm = &linux_pci_main;
1302 : vlib_pci_device_info_t *di;
1303 : linux_pci_device_t *p;
1304 0 : clib_error_t *err = 0;
1305 : pci_device_id_t *i;
1306 :
1307 0 : di = vlib_pci_get_device_info (vm, addr, &err);
1308 :
1309 0 : if (err)
1310 0 : return err;
1311 :
1312 0 : if (ids)
1313 : {
1314 0 : for (i = ids; i->vendor_id != 0; i++)
1315 0 : if (i->vendor_id == di->vendor_id && i->device_id == di->device_id)
1316 0 : break;
1317 :
1318 0 : if (i->vendor_id == 0)
1319 : {
1320 0 : vlib_pci_free_device_info (di);
1321 0 : return clib_error_return (0, "Wrong vendor or device id");
1322 : }
1323 : }
1324 :
1325 0 : pool_get (lpm->linux_pci_devices, p);
1326 0 : p->handle = p - lpm->linux_pci_devices;
1327 0 : p->addr.as_u32 = di->addr.as_u32;
1328 0 : p->intx_irq.fd = -1;
1329 0 : p->intx_irq.clib_file_index = -1;
1330 0 : p->numa_node = di->numa_node;
1331 : /*
1332 : * pci io bar read/write fd
1333 : */
1334 0 : p->io_fd = -1;
1335 :
1336 0 : log_debug (p, "open vid:0x%04x did:0x%04x driver:%s iommu_group:%d",
1337 : di->vendor_id, di->device_id, di->driver_name, di->iommu_group);
1338 :
1339 0 : if (clib_strncmp ("vfio-pci", (char *) di->driver_name, 8) == 0)
1340 0 : err = add_device_vfio (vm, p, di, 0);
1341 0 : else if (clib_strncmp ("uio_pci_generic", (char *) di->driver_name, 8) == 0)
1342 0 : err = add_device_uio (vm, p, di, 0);
1343 : else
1344 0 : err = clib_error_create ("device not bound to 'vfio-pci' or "
1345 : "'uio_pci_generic' kernel module");
1346 0 : if (err)
1347 0 : goto error;
1348 :
1349 0 : *handle = p->handle;
1350 :
1351 0 : error:
1352 0 : vlib_pci_free_device_info (di);
1353 0 : if (err)
1354 : {
1355 0 : log_err (p, "%U", format_clib_error, err);
1356 0 : clib_memset (p, 0, sizeof (linux_pci_device_t));
1357 0 : pool_put (lpm->linux_pci_devices, p);
1358 : }
1359 :
1360 0 : return err;
1361 : }
1362 :
1363 : void
1364 0 : vlib_pci_device_close (vlib_main_t * vm, vlib_pci_dev_handle_t h)
1365 : {
1366 0 : linux_pci_main_t *lpm = &linux_pci_main;
1367 0 : linux_pci_device_t *p = linux_pci_get_device (h);
1368 : linux_pci_irq_t *irq;
1369 : linux_pci_region_t *res;
1370 0 : clib_error_t *err = 0;
1371 :
1372 0 : if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
1373 : {
1374 0 : irq = &p->intx_irq;
1375 0 : if (irq->clib_file_index != -1)
1376 0 : clib_file_del_by_index (&file_main, irq->clib_file_index);
1377 0 : close (p->config_fd);
1378 0 : if (p->io_fd != -1)
1379 0 : close (p->io_fd);
1380 : }
1381 0 : else if (p->type == LINUX_PCI_DEVICE_TYPE_VFIO)
1382 : {
1383 0 : irq = &p->intx_irq;
1384 : /* close INTx irqs */
1385 0 : if (irq->fd != -1)
1386 : {
1387 0 : err = vfio_set_irqs (vm, p, VFIO_PCI_INTX_IRQ_INDEX, 0, 0,
1388 : VFIO_IRQ_SET_ACTION_TRIGGER, 0);
1389 0 : clib_error_free (err);
1390 0 : if (irq->clib_file_index != -1)
1391 0 : clib_file_del_by_index (&file_main, irq->clib_file_index);
1392 0 : close (irq->fd);
1393 : }
1394 :
1395 : /* close MSI-X irqs */
1396 0 : if (vec_len (p->msix_irqs))
1397 : {
1398 0 : err = vfio_set_irqs (vm, p, VFIO_PCI_MSIX_IRQ_INDEX, 0, 0,
1399 : VFIO_IRQ_SET_ACTION_TRIGGER, 0);
1400 0 : clib_error_free (err);
1401 : /* *INDENT-OFF* */
1402 0 : vec_foreach (irq, p->msix_irqs)
1403 : {
1404 0 : if (irq->fd == -1)
1405 0 : continue;
1406 0 : clib_file_del_by_index (&file_main, irq->clib_file_index);
1407 0 : close (irq->fd);
1408 : }
1409 : /* *INDENT-ON* */
1410 0 : vec_free (p->msix_irqs);
1411 : }
1412 : }
1413 :
1414 : /* *INDENT-OFF* */
1415 0 : vec_foreach (res, p->regions)
1416 : {
1417 0 : if (res->size == 0)
1418 0 : continue;
1419 0 : clib_mem_vm_unmap (res->addr);
1420 0 : if (res->fd != -1)
1421 0 : close (res->fd);
1422 : }
1423 : /* *INDENT-ON* */
1424 0 : vec_free (p->regions);
1425 :
1426 0 : close (p->fd);
1427 0 : clib_memset (p, 0, sizeof (linux_pci_device_t));
1428 0 : pool_put (lpm->linux_pci_devices, p);
1429 0 : }
1430 :
1431 : void
1432 170775 : init_device_from_registered (vlib_main_t * vm, vlib_pci_device_info_t * di)
1433 : {
1434 170775 : vlib_pci_main_t *pm = &pci_main;
1435 170775 : linux_pci_main_t *lpm = &linux_pci_main;
1436 : pci_device_registration_t *r;
1437 : pci_device_id_t *i;
1438 170775 : clib_error_t *err = 0;
1439 : linux_pci_device_t *p;
1440 :
1441 170775 : pool_get (lpm->linux_pci_devices, p);
1442 170775 : p->handle = p - lpm->linux_pci_devices;
1443 170775 : p->intx_irq.fd = -1;
1444 :
1445 170775 : r = pm->pci_device_registrations;
1446 :
1447 170775 : while (r)
1448 : {
1449 0 : for (i = r->supported_devices; i->vendor_id != 0; i++)
1450 0 : if (i->vendor_id == di->vendor_id && i->device_id == di->device_id)
1451 : {
1452 0 : if (di->iommu_group != -1)
1453 0 : err = add_device_vfio (vm, p, di, r);
1454 : else
1455 0 : err = add_device_uio (vm, p, di, r);
1456 :
1457 0 : if (err)
1458 0 : clib_error_report (err);
1459 : else
1460 0 : return;
1461 : }
1462 0 : r = r->next_registration;
1463 : }
1464 :
1465 : /* No driver, close the PCI config-space FD */
1466 170775 : clib_memset (p, 0, sizeof (linux_pci_device_t));
1467 170775 : pool_put (lpm->linux_pci_devices, p);
1468 : }
1469 :
1470 : static clib_error_t *
1471 170775 : scan_pci_addr (void *arg, u8 * dev_dir_name, u8 * ignored)
1472 : {
1473 170775 : vlib_pci_addr_t addr, **addrv = arg;
1474 : unformat_input_t input;
1475 170775 : clib_error_t *err = 0;
1476 :
1477 341550 : unformat_init_string (&input, (char *) dev_dir_name,
1478 170775 : vec_len (dev_dir_name));
1479 :
1480 170775 : if (!unformat (&input, "/sys/bus/pci/devices/%U",
1481 : unformat_vlib_pci_addr, &addr))
1482 0 : err = clib_error_return (0, "unformat error `%v`", dev_dir_name);
1483 :
1484 170775 : unformat_free (&input);
1485 :
1486 170775 : if (err)
1487 0 : return err;
1488 :
1489 170775 : vec_add1 (*addrv, addr);
1490 170775 : return 0;
1491 : }
1492 :
1493 : static int
1494 1202320 : pci_addr_cmp (void *v1, void *v2)
1495 : {
1496 1202320 : vlib_pci_addr_t *a1 = v1;
1497 1202320 : vlib_pci_addr_t *a2 = v2;
1498 :
1499 1202320 : if (a1->domain > a2->domain)
1500 0 : return 1;
1501 1202320 : if (a1->domain < a2->domain)
1502 0 : return -1;
1503 1202320 : if (a1->bus > a2->bus)
1504 248400 : return 1;
1505 953925 : if (a1->bus < a2->bus)
1506 230000 : return -1;
1507 723925 : if (a1->slot > a2->slot)
1508 232300 : return 1;
1509 491625 : if (a1->slot < a2->slot)
1510 242075 : return -1;
1511 249550 : if (a1->function > a2->function)
1512 152375 : return 1;
1513 97175 : if (a1->function < a2->function)
1514 97175 : return -1;
1515 0 : return 0;
1516 : }
1517 :
1518 : vlib_pci_addr_t *
1519 575 : vlib_pci_get_all_dev_addrs ()
1520 : {
1521 575 : vlib_pci_addr_t *addrs = 0;
1522 : clib_error_t *err;
1523 575 : err = foreach_directory_file ((char *) sysfs_pci_dev_path, scan_pci_addr,
1524 : &addrs, /* scan_dirs */ 0);
1525 575 : if (err)
1526 : {
1527 0 : vec_free (addrs);
1528 0 : return 0;
1529 : }
1530 :
1531 575 : vec_sort_with_function (addrs, pci_addr_cmp);
1532 :
1533 575 : return addrs;
1534 : }
1535 :
1536 : clib_error_t *
1537 575 : linux_pci_init (vlib_main_t * vm)
1538 : {
1539 575 : vlib_pci_main_t *pm = &pci_main;
1540 575 : vlib_pci_addr_t *addr = 0, *addrs;
1541 :
1542 575 : pm->vlib_main = vm;
1543 :
1544 : ASSERT (sizeof (vlib_pci_addr_t) == sizeof (u32));
1545 :
1546 575 : addrs = vlib_pci_get_all_dev_addrs ();
1547 : /* *INDENT-OFF* */
1548 171350 : vec_foreach (addr, addrs)
1549 : {
1550 : vlib_pci_device_info_t *d;
1551 170775 : if ((d = vlib_pci_get_device_info (vm, addr, 0)))
1552 : {
1553 170775 : init_device_from_registered (vm, d);
1554 170775 : vlib_pci_free_device_info (d);
1555 : }
1556 : }
1557 : /* *INDENT-ON* */
1558 :
1559 575 : return 0;
1560 : }
1561 :
1562 : /* *INDENT-OFF* */
1563 1727 : VLIB_INIT_FUNCTION (linux_pci_init) =
1564 : {
1565 : .runs_after = VLIB_INITS("unix_input_init"),
1566 : };
1567 : /* *INDENT-ON* */
1568 :
1569 : /*
1570 : * fd.io coding-style-patch-verification: ON
1571 : *
1572 : * Local Variables:
1573 : * eval: (c-set-style "gnu")
1574 : * End:
1575 : */
|