Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2020 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : /*
7 : : * vfio-user transport for PCI devices.
8 : : */
9 : :
10 : : #include "spdk/stdinc.h"
11 : : #include "spdk/log.h"
12 : : #include "spdk/env.h"
13 : : #include "spdk/queue.h"
14 : : #include "spdk/util.h"
15 : : #include "spdk/vfio_user_pci.h"
16 : :
17 : : #include "vfio_user_internal.h"
18 : :
19 : : static uint32_t g_vfio_dev_id;
20 : :
21 : : int
22 : 662 : spdk_vfio_user_pci_bar_access(struct vfio_device *dev, uint32_t index, uint64_t offset,
23 : : size_t len, void *buf, bool is_write)
24 : : {
25 : 662 : struct vfio_pci_region *region = &dev->regions[index];
26 : : uint32_t i;
27 : :
28 [ - + ]: 662 : if (offset + len > region->size) {
29 : 0 : return -EINVAL;
30 : : }
31 : :
32 [ + + + - ]: 662 : if (!region->nr_mmaps || (offset < region->mmaps[0].offset)) {
33 : 662 : return vfio_user_dev_mmio_access(dev, index, offset, len, buf, is_write);
34 : : }
35 : :
36 : : /* SPARSE MMAP */
37 [ # # ]: 0 : for (i = 0; i < region->nr_mmaps; i++) {
38 [ # # ]: 0 : if ((offset >= region->mmaps[i].offset) &&
39 [ # # ]: 0 : (offset + len <= region->mmaps[i].offset + region->mmaps[i].size)) {
40 [ # # ]: 0 : assert(region->mmaps[i].mem != NULL);
41 : 0 : void *bar_addr = region->mmaps[i].mem + offset - region->mmaps[i].offset;
42 [ # # ]: 0 : if (is_write) {
43 [ # # # # ]: 0 : memcpy(bar_addr, buf, len);
44 : : } else {
45 [ # # # # ]: 0 : memcpy(buf, bar_addr, len);
46 : : }
47 : 0 : return 0;
48 : : }
49 : : }
50 : :
51 : 0 : return -EFAULT;
52 : : }
53 : :
54 : : static int
55 : 56 : vfio_add_mr(struct vfio_device *dev, struct vfio_memory_region *mr)
56 : : {
57 [ - + ]: 56 : if (dev->nr_mrs == VFIO_MAXIMUM_MEMORY_REGIONS) {
58 : 0 : SPDK_ERRLOG("Maximum supported memory regions %d\n", VFIO_MAXIMUM_MEMORY_REGIONS);
59 : 0 : return -EINVAL;
60 : : }
61 : :
62 : 56 : TAILQ_INSERT_TAIL(&dev->mrs_head, mr, link);
63 : 56 : dev->nr_mrs++;
64 : :
65 [ - + + + ]: 56 : SPDK_DEBUGLOG(vfio_pci, "Add memory region: FD %d, VADDR 0x%lx, IOVA 0x%lx, Size 0x%lx\n",
66 : : mr->fd, mr->vaddr, mr->iova, mr->size);
67 : :
68 : 56 : return 0;
69 : : }
70 : :
71 : : static struct vfio_memory_region *
72 : 112 : vfio_get_mr(struct vfio_device *dev, uint64_t addr, size_t len)
73 : : {
74 : : struct vfio_memory_region *mr, *tmp_mr;
75 : :
76 [ + + ]: 112 : if (dev->nr_mrs == 0) {
77 : 37 : return false;
78 : : }
79 : :
80 [ + + ]: 175 : TAILQ_FOREACH_SAFE(mr, &dev->mrs_head, link, tmp_mr) {
81 [ + + - + ]: 156 : if ((mr->vaddr == addr) || (mr->iova == addr)) {
82 : 56 : return mr;
83 : : }
84 : : }
85 : :
86 : 19 : return false;
87 : : }
88 : :
89 : : static void
90 : 56 : vfio_remove_mr(struct vfio_device *dev, uint64_t addr, size_t len)
91 : : {
92 : : struct vfio_memory_region *mr, *tmp_mr;
93 : :
94 [ + - ]: 74 : TAILQ_FOREACH_SAFE(mr, &dev->mrs_head, link, tmp_mr) {
95 [ + + - + ]: 74 : if ((mr->vaddr == addr) || (mr->iova == addr)) {
96 [ - + + + ]: 56 : SPDK_DEBUGLOG(vfio_pci, "Remove memory region: FD %d, VADDR 0x%lx, IOVA 0x%lx, Size 0x%lx\n",
97 : : mr->fd, mr->vaddr, mr->iova, mr->size);
98 [ + + ]: 56 : TAILQ_REMOVE(&dev->mrs_head, mr, link);
99 [ - + ]: 56 : assert(dev->nr_mrs > 0);
100 : 56 : dev->nr_mrs--;
101 : 56 : free(mr);
102 : 56 : return;
103 : : }
104 : : }
105 : : }
106 : :
107 : : static int
108 : 112 : vfio_mr_map_notify(void *cb_ctx, struct spdk_mem_map *map,
109 : : enum spdk_mem_map_notify_action action,
110 : : void *vaddr, size_t size)
111 : : {
112 : : int ret;
113 : 112 : struct vfio_device *dev = cb_ctx;
114 : : struct vfio_memory_region *mr;
115 : 4 : uint64_t offset;
116 : :
117 : 112 : mr = vfio_get_mr(dev, (uint64_t)vaddr, size);
118 [ + + ]: 112 : if (action == SPDK_MEM_MAP_NOTIFY_UNREGISTER) {
119 [ - + ]: 56 : if (!mr) {
120 : 0 : SPDK_ERRLOG("Memory region VADDR %p doesn't exist\n", vaddr);
121 : 0 : return -EEXIST;
122 : : }
123 : :
124 : 56 : ret = vfio_user_dev_dma_map_unmap(dev, mr, false);
125 : : /* remove the memory region */
126 : 56 : vfio_remove_mr(dev, (uint64_t)vaddr, size);
127 : 56 : return ret;
128 : : }
129 : :
130 : : /* SPDK_MEM_MAP_NOTIFY_REGISTER */
131 [ - + ]: 56 : if (mr != NULL) {
132 : 0 : SPDK_ERRLOG("Memory region VADDR 0x%lx already exist\n", mr->vaddr);
133 : 0 : return -EEXIST;
134 : : }
135 : :
136 : 56 : mr = calloc(1, sizeof(*mr));
137 [ - + ]: 56 : if (mr == NULL) {
138 : 0 : return -ENOMEM;
139 : : }
140 : 56 : mr->vaddr = (uint64_t)(uintptr_t)vaddr;
141 : 56 : mr->iova = mr->vaddr;
142 : 56 : mr->size = size;
143 : 56 : mr->fd = spdk_mem_get_fd_and_offset(vaddr, &offset);
144 [ - + ]: 56 : if (mr->fd < 0) {
145 : 0 : SPDK_ERRLOG("Error to get the memory map offset\n");
146 : 0 : free(mr);
147 : 0 : return -EFAULT;
148 : : }
149 : 56 : mr->offset = offset;
150 : :
151 : 56 : ret = vfio_add_mr(dev, mr);
152 [ - + ]: 56 : if (ret) {
153 : 0 : free(mr);
154 : 0 : return ret;
155 : : }
156 : :
157 : 56 : return vfio_user_dev_dma_map_unmap(dev, mr, true);
158 : : }
159 : :
160 : : static int
161 : 37 : vfio_device_dma_map(struct vfio_device *device)
162 : : {
163 : 37 : const struct spdk_mem_map_ops vfio_map_ops = {
164 : : .notify_cb = vfio_mr_map_notify,
165 : : .are_contiguous = NULL,
166 : : };
167 : :
168 : 37 : device->map = spdk_mem_map_alloc((uint64_t)NULL, &vfio_map_ops, device);
169 [ - + ]: 37 : if (device->map == NULL) {
170 : 0 : SPDK_ERRLOG("Failed to allocate memory map structure\n");
171 : 0 : return -EFAULT;
172 : : }
173 : :
174 : 37 : return 0;
175 : : }
176 : :
177 : : static struct vfio_info_cap_header *
178 : 72 : vfio_device_get_info_cap(struct vfio_region_info *info, int cap)
179 : : {
180 : : struct vfio_info_cap_header *h;
181 : : size_t offset;
182 : :
183 [ - + ]: 72 : if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) == 0) {
184 : 0 : return NULL;
185 : : }
186 : :
187 : 72 : offset = info->cap_offset;
188 [ + - ]: 107 : while (offset != 0) {
189 : 107 : h = (struct vfio_info_cap_header *)((uintptr_t)info + offset);
190 [ + + ]: 107 : if (h->id == cap) {
191 : 72 : return h;
192 : : }
193 : 35 : offset = h->next;
194 : : }
195 : :
196 : 0 : return NULL;
197 : : }
198 : :
199 : : static int
200 : 72 : vfio_device_setup_sparse_mmaps(struct vfio_device *device, int index,
201 : : struct vfio_region_info *info, int *fds)
202 : : {
203 : : struct vfio_info_cap_header *hdr;
204 : : struct vfio_region_info_cap_sparse_mmap *sparse;
205 : 72 : struct vfio_pci_region *region = &device->regions[index];
206 : 72 : uint32_t i, j = 0;
207 : 72 : int rc = 0, prot = 0;
208 : :
209 : 72 : hdr = vfio_device_get_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP);
210 [ - + ]: 72 : if (!hdr) {
211 : 0 : SPDK_NOTICELOG("Device doesn't have sparse mmap\n");
212 : 0 : return -EEXIST;
213 : : }
214 : :
215 : 72 : sparse = SPDK_CONTAINEROF(hdr, struct vfio_region_info_cap_sparse_mmap, header);
216 [ + + ]: 144 : for (i = 0; i < sparse->nr_areas; i++) {
217 [ + - ]: 72 : if (sparse->areas[i].size) {
218 : 72 : region->mmaps[j].offset = sparse->areas[i].offset;
219 : 72 : region->mmaps[j].size = sparse->areas[i].size;
220 : 72 : prot |= info->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
221 : 72 : prot |= info->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
222 [ + - ]: 72 : if (*fds) {
223 : 72 : region->mmaps[j].mem = mmap(NULL, region->mmaps[j].size, prot, MAP_SHARED,
224 : 72 : fds[i], region->offset + region->mmaps[j].offset);
225 [ - + ]: 72 : if (region->mmaps[j].mem == MAP_FAILED) {
226 : 0 : SPDK_ERRLOG("Device SPARSE MMAP failed\n");
227 : 0 : rc = -EIO;
228 : 0 : goto out;
229 : : }
230 : : } else {
231 [ # # # # ]: 0 : SPDK_DEBUGLOG(vfio_pci, "No valid fd, skip mmap for bar %d region %u\n", index, i);
232 : : }
233 [ - + + + ]: 72 : SPDK_DEBUGLOG(vfio_pci, "Sparse region %u, Size 0x%llx, Offset 0x%llx, Map addr %p\n",
234 : : i, sparse->areas[i].size, sparse->areas[i].offset,
235 : : region->mmaps[j].mem);
236 : 72 : j++;
237 : : }
238 : : }
239 : 72 : device->regions[index].nr_mmaps = j;
240 : 72 : out:
241 [ + + ]: 144 : for (i = 0; i < sparse->nr_areas; i++) {
242 : 72 : close(fds[i]);
243 : : }
244 : :
245 : 72 : return rc;
246 : : }
247 : :
248 : : static int
249 : 0 : vfio_device_map_region(struct vfio_device *device, struct vfio_pci_region *region, int fd)
250 : : {
251 : 0 : int prot = 0;
252 : :
253 : 0 : prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
254 : 0 : prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
255 : :
256 : 0 : region->mmaps[0].offset = 0;
257 : 0 : region->mmaps[0].size = region->size;
258 : :
259 : 0 : region->mmaps[0].mem = mmap(NULL, region->size, prot, MAP_SHARED,
260 : 0 : fd, region->offset);
261 : 0 : close(fd);
262 [ # # ]: 0 : if (region->mmaps[0].mem == MAP_FAILED) {
263 : 0 : SPDK_ERRLOG("Device Region MMAP failed\n");
264 : 0 : return -EFAULT;
265 : : }
266 [ # # # # ]: 0 : SPDK_DEBUGLOG(vfio_pci, "Memory mapped to %p\n", region->mmaps[0].mem);
267 : 0 : region->nr_mmaps = 1;
268 : :
269 : 0 : return 0;
270 : : }
271 : :
272 : : static int
273 : 37 : vfio_device_map_bars_and_config_region(struct vfio_device *device)
274 : : {
275 : : uint32_t i;
276 : : int ret;
277 : 37 : size_t len = 4096;
278 : 2 : int fds[VFIO_MAXIMUM_SPARSE_MMAP_REGIONS];
279 : : struct vfio_region_info *info;
280 : : uint8_t *buf;
281 : :
282 : 37 : buf = calloc(1, len);
283 [ - + ]: 37 : if (!buf) {
284 : 0 : return -ENOMEM;
285 : : }
286 : :
287 : 37 : info = (struct vfio_region_info *)buf;
288 [ + + ]: 407 : for (i = 0; i < device->pci_regions; i++) {
289 [ - + ]: 370 : memset(info, 0, len);
290 [ - + ]: 370 : memset(fds, 0, sizeof(fds));
291 : :
292 : 370 : info->index = i;
293 : 370 : ret = vfio_user_get_dev_region_info(device, info, len, fds, VFIO_MAXIMUM_SPARSE_MMAP_REGIONS);
294 [ - + ]: 370 : if (ret) {
295 : 0 : SPDK_ERRLOG("Device setup bar %d failed\n", ret);
296 : 0 : free(buf);
297 : 0 : return ret;
298 : : }
299 : :
300 : 370 : device->regions[i].size = info->size;
301 : 370 : device->regions[i].offset = info->offset;
302 : 370 : device->regions[i].flags = info->flags;
303 : :
304 [ - + + + ]: 370 : SPDK_DEBUGLOG(vfio_pci, "Bar %d, Size 0x%llx, Offset 0x%llx, Flags 0x%x, Cap offset %u\n",
305 : : i, info->size, info->offset, info->flags, info->cap_offset);
306 : :
307 : : /* Setup MMAP if any */
308 [ + + + + ]: 370 : if (info->size && (info->flags & VFIO_REGION_INFO_FLAG_MMAP)) {
309 : : /* try to map sparse memory region first */
310 : 72 : ret = vfio_device_setup_sparse_mmaps(device, i, info, fds);
311 [ - + ]: 72 : if (ret < 0) {
312 : 0 : ret = vfio_device_map_region(device, &device->regions[i], fds[0]);
313 : : }
314 : :
315 [ - + ]: 72 : if (ret != 0) {
316 : 0 : SPDK_ERRLOG("Setup Device %s region %d failed\n", device->name, i);
317 : 0 : free(buf);
318 : 0 : return ret;
319 : : }
320 : : }
321 : : }
322 : :
323 : 37 : free(buf);
324 : 37 : return 0;
325 : : }
326 : :
327 : : static void
328 : 37 : vfio_device_unmap_bars(struct vfio_device *dev)
329 : : {
330 : : uint32_t i, j;
331 : : struct vfio_pci_region *region;
332 : :
333 [ + + ]: 407 : for (i = 0; i < dev->pci_regions; i++) {
334 : 370 : region = &dev->regions[i];
335 [ + + ]: 442 : for (j = 0; j < region->nr_mmaps; j++) {
336 [ + - ]: 72 : if (region->mmaps[j].mem) {
337 : 72 : munmap(region->mmaps[j].mem, region->mmaps[j].size);
338 : : }
339 : : }
340 : : }
341 [ - + ]: 37 : memset(dev->regions, 0, sizeof(dev->regions));
342 : 37 : }
343 : :
344 : : struct vfio_device *
345 : 37 : spdk_vfio_user_setup(const char *path)
346 : : {
347 : : int ret;
348 : 37 : struct vfio_device *device = NULL;
349 : 37 : struct vfio_user_device_info dev_info = {};
350 : :
351 : 37 : device = calloc(1, sizeof(*device));
352 [ - + ]: 37 : if (!device) {
353 : 0 : return NULL;
354 : : }
355 : 37 : TAILQ_INIT(&device->mrs_head);
356 [ - + ]: 37 : snprintf(device->path, PATH_MAX, "%s", path);
357 [ - + ]: 37 : snprintf(device->name, sizeof(device->name), "vfio-user%u", g_vfio_dev_id++);
358 : :
359 : 37 : ret = vfio_user_dev_setup(device);
360 [ - + ]: 37 : if (ret) {
361 : 0 : free(device);
362 : 0 : SPDK_ERRLOG("Error to setup vfio-user via path %s\n", path);
363 : 0 : return NULL;
364 : : }
365 : :
366 : 37 : ret = vfio_user_get_dev_info(device, &dev_info, sizeof(dev_info));
367 [ - + ]: 37 : if (ret) {
368 : 0 : SPDK_ERRLOG("Device get info failed\n");
369 : 0 : goto cleanup;
370 : : }
371 : 37 : device->pci_regions = dev_info.num_regions;
372 : 37 : device->flags = dev_info.flags;
373 : :
374 : 37 : ret = vfio_device_map_bars_and_config_region(device);
375 [ - + ]: 37 : if (ret) {
376 : 0 : goto cleanup;
377 : : }
378 : :
379 : : /* Register DMA Region */
380 : 37 : ret = vfio_device_dma_map(device);
381 [ - + ]: 37 : if (ret) {
382 : 0 : SPDK_ERRLOG("Container DMA map failed\n");
383 : 0 : goto cleanup;
384 : : }
385 : :
386 [ - + + + ]: 37 : SPDK_DEBUGLOG(vfio_pci, "Device %s, Path %s Setup Successfully\n", device->name, device->path);
387 : :
388 : 37 : return device;
389 : :
390 : 0 : cleanup:
391 : 0 : close(device->fd);
392 : 0 : free(device);
393 : 0 : return NULL;
394 : : }
395 : :
396 : : void
397 : 37 : spdk_vfio_user_release(struct vfio_device *dev)
398 : : {
399 [ - + + + ]: 37 : SPDK_DEBUGLOG(vfio_pci, "Release file %s\n", dev->path);
400 : :
401 : 37 : vfio_device_unmap_bars(dev);
402 [ + - ]: 37 : if (dev->map) {
403 : 37 : spdk_mem_map_free(&dev->map);
404 : : }
405 : 37 : close(dev->fd);
406 : :
407 : 37 : free(dev);
408 : 37 : }
409 : :
410 : : void *
411 : 35 : spdk_vfio_user_get_bar_addr(struct vfio_device *dev, uint32_t index, uint64_t offset, uint32_t len)
412 : : {
413 : 35 : struct vfio_pci_region *region = &dev->regions[index];
414 : : uint32_t i;
415 : :
416 [ + - - + ]: 35 : if (!region->size || !(region->flags & VFIO_REGION_INFO_FLAG_MMAP)) {
417 : 0 : return NULL;
418 : : }
419 : :
420 [ + - ]: 35 : for (i = 0; i < region->nr_mmaps; i++) {
421 [ + - + - ]: 35 : if (region->mmaps[i].mem && (region->mmaps[i].offset <= offset) &&
422 [ + - ]: 35 : ((offset + len) <= (region->mmaps[i].offset + region->mmaps[i].size))) {
423 : 35 : return (void *)((uintptr_t)region->mmaps[i].mem + offset - region->mmaps[i].offset);
424 : : }
425 : : }
426 : :
427 : 0 : return NULL;
428 : : }
429 : :
430 : : /* For fuzzing only */
431 : : int
432 : 0 : spdk_vfio_user_dev_send_request(struct vfio_device *dev, enum vfio_user_command command,
433 : : void *arg, size_t arg_len, size_t buf_len, int *fds,
434 : : int max_fds)
435 : : {
436 : 0 : return vfio_user_dev_send_request(dev, command, arg, arg_len, buf_len, fds, max_fds);
437 : : }
438 : :
439 : 2423 : SPDK_LOG_REGISTER_COMPONENT(vfio_pci)
|