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 : */
|