LCOV - code coverage report
Current view: top level - vlib/linux - pci.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 133 762 17.5 %
Date: 2023-10-26 01:39:38 Functions: 9 44 20.5 %

          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, &reg) < 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, &reg);
    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             :  */

Generated by: LCOV version 1.14