LCOV - code coverage report
Current view: top level - vlib/linux - vfio.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 5 137 3.6 %
Date: 2023-07-05 22:20:52 Functions: 1 6 16.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2018 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             : #include <unistd.h>
      17             : #include <sys/types.h>
      18             : #include <sys/stat.h>
      19             : #include <fcntl.h>
      20             : #include <linux/vfio.h>
      21             : #include <sys/ioctl.h>
      22             : 
      23             : #include <vppinfra/linux/sysfs.h>
      24             : 
      25             : #include <vlib/vlib.h>
      26             : #include <vlib/unix/unix.h>
      27             : #include <vlib/pci/pci.h>
      28             : #include <vlib/linux/vfio.h>
      29             : #include <vlib/physmem.h>
      30             : 
      31             : #ifndef VFIO_NOIOMMU_IOMMU
      32             : #define VFIO_NOIOMMU_IOMMU 8
      33             : #endif
      34             : 
      35             : linux_vfio_main_t vfio_main;
      36             : 
      37             : clib_error_t *
      38           0 : vfio_map_physmem_page (vlib_main_t * vm, void *addr)
      39             : {
      40           0 :   vlib_physmem_main_t *vpm = &vm->physmem_main;
      41           0 :   linux_vfio_main_t *lvm = &vfio_main;
      42           0 :   struct vfio_iommu_type1_dma_map dm = { 0 };
      43           0 :   uword log2_page_size = vpm->pmalloc_main->def_log2_page_sz;
      44           0 :   uword physmem_start = pointer_to_uword (vpm->pmalloc_main->base);
      45             : 
      46           0 :   if (lvm->container_fd == -1)
      47           0 :     return clib_error_return (0, "No cointainer fd");
      48             : 
      49           0 :   u32 page_index = vlib_physmem_get_page_index (vm, addr);
      50             : 
      51           0 :   if (clib_bitmap_get (lvm->physmem_pages_mapped, page_index))
      52             :     {
      53           0 :       vlib_log_debug (lvm->log_default, "map DMA va:%p page:%u already "
      54             :                       "mapped", addr, page_index);
      55           0 :       return 0;
      56             :     }
      57             : 
      58           0 :   dm.argsz = sizeof (struct vfio_iommu_type1_dma_map);
      59           0 :   dm.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
      60           0 :   dm.vaddr = physmem_start + (page_index << log2_page_size);
      61           0 :   dm.size = 1ULL << log2_page_size;
      62           0 :   dm.iova = dm.vaddr;
      63           0 :   vlib_log_debug (lvm->log_default, "map DMA page:%u va:0x%lx iova:%lx "
      64             :                   "size:0x%lx", page_index, dm.vaddr, dm.iova, dm.size);
      65             : 
      66           0 :   if (ioctl (lvm->container_fd, VFIO_IOMMU_MAP_DMA, &dm) == -1)
      67             :     {
      68           0 :       vlib_log_err (lvm->log_default, "map DMA page:%u va:0x%lx iova:%lx "
      69             :                     "size:0x%lx failed, error %s (errno %d)", page_index,
      70             :                     dm.vaddr, dm.iova, dm.size, strerror (errno), errno);
      71           0 :       return clib_error_return_unix (0, "physmem DMA map failed");
      72             :     }
      73             : 
      74           0 :   lvm->physmem_pages_mapped = clib_bitmap_set (lvm->physmem_pages_mapped,
      75             :                                                page_index, 1);
      76           0 :   return 0;
      77             : }
      78             : 
      79             : static linux_pci_vfio_iommu_group_t *
      80           0 : get_vfio_iommu_group (int group)
      81             : {
      82           0 :   linux_vfio_main_t *lvm = &vfio_main;
      83             :   uword *p;
      84             : 
      85           0 :   p = hash_get (lvm->iommu_pool_index_by_group, group);
      86             : 
      87           0 :   return p ? pool_elt_at_index (lvm->iommu_groups, p[0]) : 0;
      88             : }
      89             : 
      90             : static clib_error_t *
      91           0 : open_vfio_iommu_group (int group, int is_noiommu)
      92             : {
      93           0 :   linux_vfio_main_t *lvm = &vfio_main;
      94             :   linux_pci_vfio_iommu_group_t *g;
      95           0 :   clib_error_t *err = 0;
      96             :   struct vfio_group_status group_status;
      97           0 :   u8 *s = 0;
      98             :   int fd;
      99             : 
     100           0 :   if (lvm->container_fd == -1)
     101             :     {
     102           0 :       if ((fd = open ("/dev/vfio/vfio", O_RDWR)) == -1)
     103           0 :         return clib_error_return_unix (0, "failed to open VFIO container");
     104             : 
     105           0 :       if (ioctl (fd, VFIO_GET_API_VERSION) != VFIO_API_VERSION)
     106             :         {
     107           0 :           close (fd);
     108           0 :           return clib_error_return_unix (0, "incompatible VFIO version");
     109             :         }
     110             : 
     111           0 :       lvm->iommu_pool_index_by_group = hash_create (0, sizeof (uword));
     112           0 :       lvm->container_fd = fd;
     113             :     }
     114             : 
     115           0 :   g = get_vfio_iommu_group (group);
     116           0 :   if (g)
     117             :     {
     118           0 :       g->refcnt++;
     119           0 :       return 0;
     120             :     }
     121           0 :   s = format (s, "/dev/vfio/%s%u%c", is_noiommu ? "noiommu-" : "", group, 0);
     122           0 :   fd = open ((char *) s, O_RDWR);
     123           0 :   if (fd < 0)
     124           0 :     return clib_error_return_unix (0, "open '%s'", s);
     125             : 
     126           0 :   group_status.argsz = sizeof (group_status);
     127           0 :   if (ioctl (fd, VFIO_GROUP_GET_STATUS, &group_status) < 0)
     128             :     {
     129           0 :       err = clib_error_return_unix (0, "ioctl(VFIO_GROUP_GET_STATUS) '%s'",
     130             :                                     s);
     131           0 :       goto error;
     132             :     }
     133             : 
     134           0 :   if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE))
     135             :     {
     136           0 :       err = clib_error_return (0, "iommu group %d is not viable (not all "
     137             :                                "devices in this group bound to vfio-pci)",
     138             :                                group);
     139           0 :       goto error;
     140             :     }
     141             : 
     142           0 :   if (ioctl (fd, VFIO_GROUP_SET_CONTAINER, &lvm->container_fd) < 0)
     143             :     {
     144           0 :       err = clib_error_return_unix (0, "ioctl(VFIO_GROUP_SET_CONTAINER) '%s'",
     145             :                                     s);
     146           0 :       goto error;
     147             :     }
     148             : 
     149           0 :   if (lvm->iommu_mode == 0)
     150             :     {
     151           0 :       if (is_noiommu)
     152           0 :         lvm->iommu_mode = VFIO_NOIOMMU_IOMMU;
     153             :       else
     154           0 :         lvm->iommu_mode = VFIO_TYPE1_IOMMU;
     155             : 
     156           0 :       if (ioctl (lvm->container_fd, VFIO_SET_IOMMU, lvm->iommu_mode) < 0)
     157             :         {
     158           0 :           err = clib_error_return_unix (0, "ioctl(VFIO_SET_IOMMU) "
     159             :                                         "'/dev/vfio/vfio'");
     160           0 :           goto error;
     161             :         }
     162             :     }
     163             : 
     164             : 
     165           0 :   pool_get (lvm->iommu_groups, g);
     166           0 :   g->fd = fd;
     167           0 :   g->refcnt = 1;
     168           0 :   hash_set (lvm->iommu_pool_index_by_group, group, g - lvm->iommu_groups);
     169           0 :   vec_free (s);
     170           0 :   return 0;
     171           0 : error:
     172           0 :   close (fd);
     173           0 :   return err;
     174             : }
     175             : 
     176             : clib_error_t *
     177           0 : linux_vfio_group_get_device_fd (vlib_pci_addr_t * addr, int *fdp,
     178             :                                 int *is_noiommu)
     179             : {
     180           0 :   clib_error_t *err = 0;
     181             :   linux_pci_vfio_iommu_group_t *g;
     182           0 :   u8 *s = 0;
     183             :   int iommu_group;
     184             :   u8 *tmpstr;
     185             :   int fd;
     186             : 
     187           0 :   *is_noiommu = 0;
     188           0 :   s =
     189           0 :     format (s, "/sys/bus/pci/devices/%U/iommu_group%c", format_vlib_pci_addr,
     190             :             addr, 0);
     191           0 :   tmpstr = clib_sysfs_link_to_name ((char *) s);
     192           0 :   if (tmpstr)
     193             :     {
     194           0 :       iommu_group = atoi ((char *) tmpstr);
     195           0 :       vec_free (tmpstr);
     196             :     }
     197             :   else
     198             :     {
     199           0 :       err = clib_error_return (0, "Cannot find IOMMU group for PCI device ",
     200             :                                "'%U'", format_vlib_pci_addr, addr);
     201           0 :       goto error;
     202             :     }
     203           0 :   vec_reset_length (s);
     204             : 
     205           0 :   s = format (s, "/sys/bus/pci/devices/%U/iommu_group/name%c",
     206             :               format_vlib_pci_addr, addr, 0);
     207           0 :   err = clib_sysfs_read ((char *) s, "%s", &tmpstr);
     208           0 :   if (err == 0)
     209             :     {
     210           0 :       if (strncmp ((char *) tmpstr, "vfio-noiommu", 12) == 0)
     211           0 :         *is_noiommu = 1;
     212             : 
     213           0 :       vec_free (tmpstr);
     214             :     }
     215             :   else
     216           0 :     clib_error_free (err);
     217           0 :   vec_reset_length (s);
     218           0 :   if ((err = open_vfio_iommu_group (iommu_group, *is_noiommu)))
     219           0 :     return err;
     220             : 
     221           0 :   g = get_vfio_iommu_group (iommu_group);
     222             : 
     223           0 :   s = format (s, "%U%c", format_vlib_pci_addr, addr, 0);
     224           0 :   if ((fd = ioctl (g->fd, VFIO_GROUP_GET_DEVICE_FD, (char *) s)) < 0)
     225             :     {
     226           0 :       err = clib_error_return_unix (0, "ioctl(VFIO_GROUP_GET_DEVICE_FD) '%U'",
     227             :                                     format_vlib_pci_addr, addr);
     228           0 :       goto error;
     229             :     }
     230           0 :   vec_reset_length (s);
     231             : 
     232           0 :   *fdp = fd;
     233             : 
     234           0 : error:
     235           0 :   vec_free (s);
     236           0 :   return err;
     237             : }
     238             : 
     239             : clib_error_t *
     240         559 : linux_vfio_init (vlib_main_t * vm)
     241             : {
     242         559 :   linux_vfio_main_t *lvm = &vfio_main;
     243             : 
     244         559 :   lvm->log_default = vlib_log_register_class ("vfio", 0);
     245         559 :   lvm->container_fd = -1;
     246             : 
     247         559 :   return 0;
     248             : }
     249             : 
     250             : u8 *
     251           0 : format_vfio_region_info (u8 * s, va_list * args)
     252             : {
     253           0 :   struct vfio_region_info *r = va_arg (*args, struct vfio_region_info *);
     254             : 
     255           0 :   s = format (s, "region_info index:%u size:0x%lx offset:0x%lx flags:",
     256             :               r->index, r->size, r->offset);
     257             : 
     258           0 :   if (r->flags & VFIO_REGION_INFO_FLAG_READ)
     259           0 :     s = format (s, " rd");
     260             : 
     261           0 :   if (r->flags & VFIO_REGION_INFO_FLAG_WRITE)
     262           0 :     s = format (s, " wr");
     263             : 
     264           0 :   if (r->flags & VFIO_REGION_INFO_FLAG_MMAP)
     265           0 :     s = format (s, " mmap");
     266             : 
     267             : #ifdef VFIO_REGION_INFO_FLAG_CAPS
     268           0 :   if (r->flags & VFIO_REGION_INFO_FLAG_CAPS)
     269           0 :     s = format (s, " caps");
     270             : #endif
     271             : 
     272           0 :   s = format (s, " (0x%x)", r->flags);
     273             : 
     274             : #ifdef VFIO_REGION_INFO_FLAG_CAPS
     275             :   u32 cap_offset;
     276             : 
     277           0 :   if ((r->flags & VFIO_REGION_INFO_FLAG_CAPS) == 0)
     278           0 :     return s;
     279             : 
     280           0 :   s = format (s, "\n caps:");
     281           0 :   cap_offset = r->cap_offset;
     282             : 
     283             :   do
     284             :     {
     285           0 :       struct vfio_info_cap_header *cap = (void *) r + cap_offset;
     286             : #ifdef VFIO_REGION_INFO_CAP_SPARSE_MMAP
     287           0 :       if (cap->id == VFIO_REGION_INFO_CAP_SPARSE_MMAP)
     288           0 :         s = format (s, " sparse-mmap");
     289             : #endif
     290             : #ifdef VFIO_REGION_INFO_CAP_TYPE
     291           0 :       if (cap->id == VFIO_REGION_INFO_CAP_TYPE)
     292           0 :         s = format (s, " type");
     293             : #endif
     294             : #ifdef VFIO_REGION_INFO_CAP_MSIX_MAPPABLE
     295           0 :       if (cap->id == VFIO_REGION_INFO_CAP_MSIX_MAPPABLE)
     296           0 :         s = format (s, " msix-mappable");
     297             : #endif
     298           0 :       cap_offset = cap->next;
     299             :     }
     300           0 :   while (cap_offset);
     301             : #endif
     302             : 
     303           0 :   return s;
     304             : }
     305             : 
     306             : /*
     307             :  * fd.io coding-style-patch-verification: ON
     308             :  *
     309             :  * Local Variables:
     310             :  * eval: (c-set-style "gnu")
     311             :  * End:
     312             :  */

Generated by: LCOV version 1.14