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 : tmpstr = clib_file_get_resolved_basename (
189 : "/sys/bus/pci/devices/%U/iommu_group", format_vlib_pci_addr, addr);
190 0 : if (tmpstr)
191 : {
192 0 : iommu_group = atoi ((char *) tmpstr);
193 0 : vec_free (tmpstr);
194 : }
195 : else
196 : {
197 0 : err = clib_error_return (0, "Cannot find IOMMU group for PCI device ",
198 : "'%U'", format_vlib_pci_addr, addr);
199 0 : goto error;
200 : }
201 0 : vec_reset_length (s);
202 :
203 0 : s = format (s, "/sys/bus/pci/devices/%U/iommu_group/name%c",
204 : format_vlib_pci_addr, addr, 0);
205 0 : err = clib_sysfs_read ((char *) s, "%s", &tmpstr);
206 0 : if (err == 0)
207 : {
208 0 : if (strncmp ((char *) tmpstr, "vfio-noiommu", 12) == 0)
209 0 : *is_noiommu = 1;
210 :
211 0 : vec_free (tmpstr);
212 : }
213 : else
214 0 : clib_error_free (err);
215 0 : vec_reset_length (s);
216 0 : if ((err = open_vfio_iommu_group (iommu_group, *is_noiommu)))
217 0 : return err;
218 :
219 0 : g = get_vfio_iommu_group (iommu_group);
220 :
221 0 : s = format (s, "%U%c", format_vlib_pci_addr, addr, 0);
222 0 : if ((fd = ioctl (g->fd, VFIO_GROUP_GET_DEVICE_FD, (char *) s)) < 0)
223 : {
224 0 : err = clib_error_return_unix (0, "ioctl(VFIO_GROUP_GET_DEVICE_FD) '%U'",
225 : format_vlib_pci_addr, addr);
226 0 : goto error;
227 : }
228 0 : vec_reset_length (s);
229 :
230 0 : *fdp = fd;
231 :
232 0 : error:
233 0 : vec_free (s);
234 0 : return err;
235 : }
236 :
237 : clib_error_t *
238 575 : linux_vfio_init (vlib_main_t * vm)
239 : {
240 575 : linux_vfio_main_t *lvm = &vfio_main;
241 :
242 575 : lvm->log_default = vlib_log_register_class ("vfio", 0);
243 575 : lvm->container_fd = -1;
244 :
245 575 : return 0;
246 : }
247 :
248 : u8 *
249 0 : format_vfio_region_info (u8 * s, va_list * args)
250 : {
251 0 : struct vfio_region_info *r = va_arg (*args, struct vfio_region_info *);
252 :
253 0 : s = format (s, "region_info index:%u size:0x%lx offset:0x%lx flags:",
254 : r->index, r->size, r->offset);
255 :
256 0 : if (r->flags & VFIO_REGION_INFO_FLAG_READ)
257 0 : s = format (s, " rd");
258 :
259 0 : if (r->flags & VFIO_REGION_INFO_FLAG_WRITE)
260 0 : s = format (s, " wr");
261 :
262 0 : if (r->flags & VFIO_REGION_INFO_FLAG_MMAP)
263 0 : s = format (s, " mmap");
264 :
265 : #ifdef VFIO_REGION_INFO_FLAG_CAPS
266 0 : if (r->flags & VFIO_REGION_INFO_FLAG_CAPS)
267 0 : s = format (s, " caps");
268 : #endif
269 :
270 0 : s = format (s, " (0x%x)", r->flags);
271 :
272 : #ifdef VFIO_REGION_INFO_FLAG_CAPS
273 : u32 cap_offset;
274 :
275 0 : if ((r->flags & VFIO_REGION_INFO_FLAG_CAPS) == 0)
276 0 : return s;
277 :
278 0 : s = format (s, "\n caps:");
279 0 : cap_offset = r->cap_offset;
280 :
281 : do
282 : {
283 0 : struct vfio_info_cap_header *cap = (void *) r + cap_offset;
284 : #ifdef VFIO_REGION_INFO_CAP_SPARSE_MMAP
285 0 : if (cap->id == VFIO_REGION_INFO_CAP_SPARSE_MMAP)
286 0 : s = format (s, " sparse-mmap");
287 : #endif
288 : #ifdef VFIO_REGION_INFO_CAP_TYPE
289 0 : if (cap->id == VFIO_REGION_INFO_CAP_TYPE)
290 0 : s = format (s, " type");
291 : #endif
292 : #ifdef VFIO_REGION_INFO_CAP_MSIX_MAPPABLE
293 0 : if (cap->id == VFIO_REGION_INFO_CAP_MSIX_MAPPABLE)
294 0 : s = format (s, " msix-mappable");
295 : #endif
296 0 : cap_offset = cap->next;
297 : }
298 0 : while (cap_offset);
299 : #endif
300 :
301 0 : return s;
302 : }
303 :
304 : /*
305 : * fd.io coding-style-patch-verification: ON
306 : *
307 : * Local Variables:
308 : * eval: (c-set-style "gnu")
309 : * End:
310 : */
|