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 0 : 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 0 : struct vfio_pci_region *region = &dev->regions[index];
26 : uint32_t i;
27 :
28 0 : if (offset + len > region->size) {
29 0 : return -EINVAL;
30 : }
31 :
32 0 : if (!region->nr_mmaps || (offset < region->mmaps[0].offset)) {
33 0 : 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 0 : vfio_add_mr(struct vfio_device *dev, struct vfio_memory_region *mr)
56 : {
57 0 : 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 0 : TAILQ_INSERT_TAIL(&dev->mrs_head, mr, link);
63 0 : dev->nr_mrs++;
64 :
65 0 : 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 0 : return 0;
69 : }
70 :
71 : static struct vfio_memory_region *
72 0 : vfio_get_mr(struct vfio_device *dev, uint64_t addr, size_t len)
73 : {
74 : struct vfio_memory_region *mr, *tmp_mr;
75 :
76 0 : if (dev->nr_mrs == 0) {
77 0 : return false;
78 : }
79 :
80 0 : TAILQ_FOREACH_SAFE(mr, &dev->mrs_head, link, tmp_mr) {
81 0 : if ((mr->vaddr == addr) || (mr->iova == addr)) {
82 0 : return mr;
83 : }
84 : }
85 :
86 0 : return false;
87 : }
88 :
89 : static void
90 0 : vfio_remove_mr(struct vfio_device *dev, uint64_t addr, size_t len)
91 : {
92 : struct vfio_memory_region *mr, *tmp_mr;
93 :
94 0 : TAILQ_FOREACH_SAFE(mr, &dev->mrs_head, link, tmp_mr) {
95 0 : if ((mr->vaddr == addr) || (mr->iova == addr)) {
96 0 : 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 0 : TAILQ_REMOVE(&dev->mrs_head, mr, link);
99 0 : assert(dev->nr_mrs > 0);
100 0 : dev->nr_mrs--;
101 0 : free(mr);
102 0 : return;
103 : }
104 : }
105 : }
106 :
107 : static int
108 0 : 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 0 : struct vfio_device *dev = cb_ctx;
114 : struct vfio_memory_region *mr;
115 0 : uint64_t offset;
116 :
117 0 : mr = vfio_get_mr(dev, (uint64_t)vaddr, size);
118 0 : if (action == SPDK_MEM_MAP_NOTIFY_UNREGISTER) {
119 0 : if (!mr) {
120 0 : SPDK_ERRLOG("Memory region VADDR %p doesn't exist\n", vaddr);
121 0 : return -EEXIST;
122 : }
123 :
124 0 : ret = vfio_user_dev_dma_map_unmap(dev, mr, false);
125 : /* remove the memory region */
126 0 : vfio_remove_mr(dev, (uint64_t)vaddr, size);
127 0 : return ret;
128 : }
129 :
130 : /* SPDK_MEM_MAP_NOTIFY_REGISTER */
131 0 : if (mr != NULL) {
132 0 : SPDK_ERRLOG("Memory region VADDR 0x%lx already exist\n", mr->vaddr);
133 0 : return -EEXIST;
134 : }
135 :
136 0 : mr = calloc(1, sizeof(*mr));
137 0 : if (mr == NULL) {
138 0 : return -ENOMEM;
139 : }
140 0 : mr->vaddr = (uint64_t)(uintptr_t)vaddr;
141 0 : mr->iova = mr->vaddr;
142 0 : mr->size = size;
143 0 : mr->fd = spdk_mem_get_fd_and_offset(vaddr, &offset);
144 0 : 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 0 : mr->offset = offset;
150 :
151 0 : ret = vfio_add_mr(dev, mr);
152 0 : if (ret) {
153 0 : free(mr);
154 0 : return ret;
155 : }
156 :
157 0 : return vfio_user_dev_dma_map_unmap(dev, mr, true);
158 : }
159 :
160 : static int
161 0 : vfio_device_dma_map(struct vfio_device *device)
162 : {
163 0 : const struct spdk_mem_map_ops vfio_map_ops = {
164 : .notify_cb = vfio_mr_map_notify,
165 : .are_contiguous = NULL,
166 : };
167 :
168 0 : device->map = spdk_mem_map_alloc((uint64_t)NULL, &vfio_map_ops, device);
169 0 : if (device->map == NULL) {
170 0 : SPDK_ERRLOG("Failed to allocate memory map structure\n");
171 0 : return -EFAULT;
172 : }
173 :
174 0 : return 0;
175 : }
176 :
177 : static struct vfio_info_cap_header *
178 0 : 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 0 : if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) == 0) {
184 0 : return NULL;
185 : }
186 :
187 0 : offset = info->cap_offset;
188 0 : while (offset != 0) {
189 0 : h = (struct vfio_info_cap_header *)((uintptr_t)info + offset);
190 0 : if (h->id == cap) {
191 0 : return h;
192 : }
193 0 : offset = h->next;
194 : }
195 :
196 0 : return NULL;
197 : }
198 :
199 : static int
200 0 : 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 0 : struct vfio_pci_region *region = &device->regions[index];
206 0 : uint32_t i, j = 0;
207 0 : int rc = 0, prot = 0;
208 :
209 0 : hdr = vfio_device_get_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP);
210 0 : if (!hdr) {
211 0 : SPDK_NOTICELOG("Device doesn't have sparse mmap\n");
212 0 : return -EEXIST;
213 : }
214 :
215 0 : sparse = SPDK_CONTAINEROF(hdr, struct vfio_region_info_cap_sparse_mmap, header);
216 0 : for (i = 0; i < sparse->nr_areas; i++) {
217 0 : if (sparse->areas[i].size) {
218 0 : region->mmaps[j].offset = sparse->areas[i].offset;
219 0 : region->mmaps[j].size = sparse->areas[i].size;
220 0 : prot |= info->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
221 0 : prot |= info->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
222 0 : if (*fds) {
223 0 : region->mmaps[j].mem = mmap(NULL, region->mmaps[j].size, prot, MAP_SHARED,
224 0 : fds[i], region->offset + region->mmaps[j].offset);
225 0 : 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 0 : 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 0 : j++;
237 : }
238 : }
239 0 : device->regions[index].nr_mmaps = j;
240 0 : out:
241 0 : for (i = 0; i < sparse->nr_areas; i++) {
242 0 : close(fds[i]);
243 : }
244 :
245 0 : 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 0 : vfio_device_map_bars_and_config_region(struct vfio_device *device)
274 : {
275 : uint32_t i;
276 : int ret;
277 0 : size_t len = 4096;
278 0 : int fds[VFIO_MAXIMUM_SPARSE_MMAP_REGIONS];
279 : struct vfio_region_info *info;
280 : uint8_t *buf;
281 :
282 0 : buf = calloc(1, len);
283 0 : if (!buf) {
284 0 : return -ENOMEM;
285 : }
286 :
287 0 : info = (struct vfio_region_info *)buf;
288 0 : for (i = 0; i < device->pci_regions; i++) {
289 0 : memset(info, 0, len);
290 0 : memset(fds, 0, sizeof(fds));
291 :
292 0 : info->index = i;
293 0 : ret = vfio_user_get_dev_region_info(device, info, len, fds, VFIO_MAXIMUM_SPARSE_MMAP_REGIONS);
294 0 : if (ret) {
295 0 : SPDK_ERRLOG("Device setup bar %d failed\n", ret);
296 0 : free(buf);
297 0 : return ret;
298 : }
299 :
300 0 : device->regions[i].size = info->size;
301 0 : device->regions[i].offset = info->offset;
302 0 : device->regions[i].flags = info->flags;
303 :
304 0 : 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 0 : if (info->size && (info->flags & VFIO_REGION_INFO_FLAG_MMAP)) {
309 : /* try to map sparse memory region first */
310 0 : ret = vfio_device_setup_sparse_mmaps(device, i, info, fds);
311 0 : if (ret < 0) {
312 0 : ret = vfio_device_map_region(device, &device->regions[i], fds[0]);
313 : }
314 :
315 0 : 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 0 : free(buf);
324 0 : return 0;
325 : }
326 :
327 : static void
328 0 : vfio_device_unmap_bars(struct vfio_device *dev)
329 : {
330 : uint32_t i, j;
331 : struct vfio_pci_region *region;
332 :
333 0 : for (i = 0; i < dev->pci_regions; i++) {
334 0 : region = &dev->regions[i];
335 0 : for (j = 0; j < region->nr_mmaps; j++) {
336 0 : if (region->mmaps[j].mem) {
337 0 : munmap(region->mmaps[j].mem, region->mmaps[j].size);
338 : }
339 : }
340 : }
341 0 : memset(dev->regions, 0, sizeof(dev->regions));
342 0 : }
343 :
344 : struct vfio_device *
345 0 : spdk_vfio_user_setup(const char *path)
346 : {
347 : int ret;
348 0 : struct vfio_device *device = NULL;
349 0 : struct vfio_user_device_info dev_info = {};
350 :
351 0 : device = calloc(1, sizeof(*device));
352 0 : if (!device) {
353 0 : return NULL;
354 : }
355 0 : TAILQ_INIT(&device->mrs_head);
356 0 : snprintf(device->path, PATH_MAX, "%s", path);
357 0 : snprintf(device->name, sizeof(device->name), "vfio-user%u", g_vfio_dev_id++);
358 :
359 0 : ret = vfio_user_dev_setup(device);
360 0 : 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 0 : ret = vfio_user_get_dev_info(device, &dev_info, sizeof(dev_info));
367 0 : if (ret) {
368 0 : SPDK_ERRLOG("Device get info failed\n");
369 0 : goto cleanup;
370 : }
371 0 : device->pci_regions = dev_info.num_regions;
372 0 : device->flags = dev_info.flags;
373 :
374 0 : ret = vfio_device_map_bars_and_config_region(device);
375 0 : if (ret) {
376 0 : goto cleanup;
377 : }
378 :
379 : /* Register DMA Region */
380 0 : ret = vfio_device_dma_map(device);
381 0 : if (ret) {
382 0 : SPDK_ERRLOG("Container DMA map failed\n");
383 0 : goto cleanup;
384 : }
385 :
386 0 : SPDK_DEBUGLOG(vfio_pci, "Device %s, Path %s Setup Successfully\n", device->name, device->path);
387 :
388 0 : return device;
389 :
390 0 : cleanup:
391 0 : close(device->fd);
392 0 : free(device);
393 0 : return NULL;
394 : }
395 :
396 : void
397 0 : spdk_vfio_user_release(struct vfio_device *dev)
398 : {
399 0 : SPDK_DEBUGLOG(vfio_pci, "Release file %s\n", dev->path);
400 :
401 0 : vfio_device_unmap_bars(dev);
402 0 : if (dev->map) {
403 0 : spdk_mem_map_free(&dev->map);
404 : }
405 0 : close(dev->fd);
406 :
407 0 : free(dev);
408 0 : }
409 :
410 : void *
411 0 : spdk_vfio_user_get_bar_addr(struct vfio_device *dev, uint32_t index, uint64_t offset, uint32_t len)
412 : {
413 0 : struct vfio_pci_region *region = &dev->regions[index];
414 : uint32_t i;
415 :
416 0 : if (!region->size || !(region->flags & VFIO_REGION_INFO_FLAG_MMAP)) {
417 0 : return NULL;
418 : }
419 :
420 0 : for (i = 0; i < region->nr_mmaps; i++) {
421 0 : if (region->mmaps[i].mem && (region->mmaps[i].offset <= offset) &&
422 0 : ((offset + len) <= (region->mmaps[i].offset + region->mmaps[i].size))) {
423 0 : 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 0 : SPDK_LOG_REGISTER_COMPONENT(vfio_pci)
|