LCOV - code coverage report
Current view: top level - vlib/linux - pci.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 138 767 18.0 %
Date: 2023-07-05 22:20:52 Functions: 8 43 18.6 %

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

Generated by: LCOV version 1.14