xref: /spdk/lib/vfio_user/host/vfio_user_pci.c (revision fad37e0916dc57859e947682d574673a9b12ac8d)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2020 Intel Corporation.
370b31195SChangpeng Liu  *   All rights reserved.
470b31195SChangpeng Liu  */
570b31195SChangpeng Liu 
670b31195SChangpeng Liu /*
770b31195SChangpeng Liu  * vfio-user transport for PCI devices.
870b31195SChangpeng Liu  */
970b31195SChangpeng Liu 
1070b31195SChangpeng Liu #include "spdk/stdinc.h"
1170b31195SChangpeng Liu #include "spdk/log.h"
1270b31195SChangpeng Liu #include "spdk/env.h"
1370b31195SChangpeng Liu #include "spdk/queue.h"
1470b31195SChangpeng Liu #include "spdk/util.h"
1570b31195SChangpeng Liu #include "spdk/vfio_user_pci.h"
1670b31195SChangpeng Liu 
1770b31195SChangpeng Liu #include "vfio_user_internal.h"
1870b31195SChangpeng Liu 
1970b31195SChangpeng Liu static uint32_t g_vfio_dev_id;
2070b31195SChangpeng Liu 
2170b31195SChangpeng Liu int
spdk_vfio_user_pci_bar_access(struct vfio_device * dev,uint32_t index,uint64_t offset,size_t len,void * buf,bool is_write)2270b31195SChangpeng Liu spdk_vfio_user_pci_bar_access(struct vfio_device *dev, uint32_t index, uint64_t offset,
2370b31195SChangpeng Liu 			      size_t len, void *buf, bool is_write)
2470b31195SChangpeng Liu {
2570b31195SChangpeng Liu 	struct vfio_pci_region *region = &dev->regions[index];
2670b31195SChangpeng Liu 	uint32_t i;
2770b31195SChangpeng Liu 
2870b31195SChangpeng Liu 	if (offset + len > region->size) {
2970b31195SChangpeng Liu 		return -EINVAL;
3070b31195SChangpeng Liu 	}
3170b31195SChangpeng Liu 
3270b31195SChangpeng Liu 	if (!region->nr_mmaps || (offset < region->mmaps[0].offset)) {
3370b31195SChangpeng Liu 		return vfio_user_dev_mmio_access(dev, index, offset, len, buf, is_write);
3470b31195SChangpeng Liu 	}
3570b31195SChangpeng Liu 
3670b31195SChangpeng Liu 	/* SPARSE MMAP */
3770b31195SChangpeng Liu 	for (i = 0; i < region->nr_mmaps; i++) {
3870b31195SChangpeng Liu 		if ((offset >= region->mmaps[i].offset) &&
3970b31195SChangpeng Liu 		    (offset + len <= region->mmaps[i].offset + region->mmaps[i].size)) {
4070b31195SChangpeng Liu 			assert(region->mmaps[i].mem != NULL);
41a773ed9aSJun Zeng 			void *bar_addr = region->mmaps[i].mem + offset - region->mmaps[i].offset;
4270b31195SChangpeng Liu 			if (is_write) {
4370b31195SChangpeng Liu 				memcpy(bar_addr, buf, len);
4470b31195SChangpeng Liu 			} else {
4570b31195SChangpeng Liu 				memcpy(buf, bar_addr, len);
4670b31195SChangpeng Liu 			}
4770b31195SChangpeng Liu 			return 0;
4870b31195SChangpeng Liu 		}
4970b31195SChangpeng Liu 	}
5070b31195SChangpeng Liu 
5170b31195SChangpeng Liu 	return -EFAULT;
5270b31195SChangpeng Liu }
5370b31195SChangpeng Liu 
5470b31195SChangpeng Liu static int
vfio_add_mr(struct vfio_device * dev,struct vfio_memory_region * mr)5570b31195SChangpeng Liu vfio_add_mr(struct vfio_device *dev, struct vfio_memory_region *mr)
5670b31195SChangpeng Liu {
5770b31195SChangpeng Liu 	if (dev->nr_mrs == VFIO_MAXIMUM_MEMORY_REGIONS) {
5870b31195SChangpeng Liu 		SPDK_ERRLOG("Maximum supported memory regions %d\n", VFIO_MAXIMUM_MEMORY_REGIONS);
5970b31195SChangpeng Liu 		return -EINVAL;
6070b31195SChangpeng Liu 	}
6170b31195SChangpeng Liu 
6270b31195SChangpeng Liu 	TAILQ_INSERT_TAIL(&dev->mrs_head, mr, link);
6370b31195SChangpeng Liu 	dev->nr_mrs++;
6470b31195SChangpeng Liu 
6570b31195SChangpeng Liu 	SPDK_DEBUGLOG(vfio_pci, "Add memory region: FD %d, VADDR 0x%lx, IOVA 0x%lx, Size 0x%lx\n",
6670b31195SChangpeng Liu 		      mr->fd, mr->vaddr, mr->iova, mr->size);
6770b31195SChangpeng Liu 
6870b31195SChangpeng Liu 	return 0;
6970b31195SChangpeng Liu }
7070b31195SChangpeng Liu 
7170b31195SChangpeng Liu static struct vfio_memory_region *
vfio_get_mr(struct vfio_device * dev,uint64_t addr,size_t len)7270b31195SChangpeng Liu vfio_get_mr(struct vfio_device *dev, uint64_t addr, size_t len)
7370b31195SChangpeng Liu {
7470b31195SChangpeng Liu 	struct vfio_memory_region *mr, *tmp_mr;
7570b31195SChangpeng Liu 
7670b31195SChangpeng Liu 	if (dev->nr_mrs == 0) {
7770b31195SChangpeng Liu 		return false;
7870b31195SChangpeng Liu 	}
7970b31195SChangpeng Liu 
8070b31195SChangpeng Liu 	TAILQ_FOREACH_SAFE(mr, &dev->mrs_head, link, tmp_mr) {
8170b31195SChangpeng Liu 		if ((mr->vaddr == addr) || (mr->iova == addr)) {
8270b31195SChangpeng Liu 			return mr;
8370b31195SChangpeng Liu 		}
8470b31195SChangpeng Liu 	}
8570b31195SChangpeng Liu 
8670b31195SChangpeng Liu 	return false;
8770b31195SChangpeng Liu }
8870b31195SChangpeng Liu 
8970b31195SChangpeng Liu static void
vfio_remove_mr(struct vfio_device * dev,uint64_t addr,size_t len)9070b31195SChangpeng Liu vfio_remove_mr(struct vfio_device *dev, uint64_t addr, size_t len)
9170b31195SChangpeng Liu {
9270b31195SChangpeng Liu 	struct vfio_memory_region *mr, *tmp_mr;
9370b31195SChangpeng Liu 
9470b31195SChangpeng Liu 	TAILQ_FOREACH_SAFE(mr, &dev->mrs_head, link, tmp_mr) {
9570b31195SChangpeng Liu 		if ((mr->vaddr == addr) || (mr->iova == addr)) {
9670b31195SChangpeng Liu 			SPDK_DEBUGLOG(vfio_pci, "Remove memory region: FD %d, VADDR 0x%lx, IOVA 0x%lx, Size 0x%lx\n",
9770b31195SChangpeng Liu 				      mr->fd, mr->vaddr, mr->iova, mr->size);
9870b31195SChangpeng Liu 			TAILQ_REMOVE(&dev->mrs_head, mr, link);
9970b31195SChangpeng Liu 			assert(dev->nr_mrs > 0);
10070b31195SChangpeng Liu 			dev->nr_mrs--;
10170b31195SChangpeng Liu 			free(mr);
10270b31195SChangpeng Liu 			return;
10370b31195SChangpeng Liu 		}
10470b31195SChangpeng Liu 	}
10570b31195SChangpeng Liu }
10670b31195SChangpeng Liu 
10770b31195SChangpeng Liu static int
vfio_mr_map_notify(void * cb_ctx,struct spdk_mem_map * map,enum spdk_mem_map_notify_action action,void * vaddr,size_t size)10870b31195SChangpeng Liu vfio_mr_map_notify(void *cb_ctx, struct spdk_mem_map *map,
10970b31195SChangpeng Liu 		   enum spdk_mem_map_notify_action action,
11070b31195SChangpeng Liu 		   void *vaddr, size_t size)
11170b31195SChangpeng Liu {
11270b31195SChangpeng Liu 	int ret;
11370b31195SChangpeng Liu 	struct vfio_device *dev = cb_ctx;
11470b31195SChangpeng Liu 	struct vfio_memory_region *mr;
11570b31195SChangpeng Liu 	uint64_t offset;
11670b31195SChangpeng Liu 
11770b31195SChangpeng Liu 	mr = vfio_get_mr(dev, (uint64_t)vaddr, size);
11870b31195SChangpeng Liu 	if (action == SPDK_MEM_MAP_NOTIFY_UNREGISTER) {
11970b31195SChangpeng Liu 		if (!mr) {
12070b31195SChangpeng Liu 			SPDK_ERRLOG("Memory region VADDR %p doesn't exist\n", vaddr);
12170b31195SChangpeng Liu 			return -EEXIST;
12270b31195SChangpeng Liu 		}
12370b31195SChangpeng Liu 
12470b31195SChangpeng Liu 		ret = vfio_user_dev_dma_map_unmap(dev, mr, false);
12570b31195SChangpeng Liu 		/* remove the memory region */
12670b31195SChangpeng Liu 		vfio_remove_mr(dev, (uint64_t)vaddr, size);
12770b31195SChangpeng Liu 		return ret;
12870b31195SChangpeng Liu 	}
12970b31195SChangpeng Liu 
13070b31195SChangpeng Liu 	/* SPDK_MEM_MAP_NOTIFY_REGISTER */
13170b31195SChangpeng Liu 	if (mr != NULL) {
13270b31195SChangpeng Liu 		SPDK_ERRLOG("Memory region VADDR 0x%lx already exist\n", mr->vaddr);
13370b31195SChangpeng Liu 		return -EEXIST;
13470b31195SChangpeng Liu 	}
13570b31195SChangpeng Liu 
13670b31195SChangpeng Liu 	mr = calloc(1, sizeof(*mr));
13770b31195SChangpeng Liu 	if (mr == NULL) {
13870b31195SChangpeng Liu 		return -ENOMEM;
13970b31195SChangpeng Liu 	}
14070b31195SChangpeng Liu 	mr->vaddr = (uint64_t)(uintptr_t)vaddr;
14170b31195SChangpeng Liu 	mr->iova = mr->vaddr;
14270b31195SChangpeng Liu 	mr->size = size;
14370b31195SChangpeng Liu 	mr->fd = spdk_mem_get_fd_and_offset(vaddr, &offset);
14470b31195SChangpeng Liu 	if (mr->fd < 0) {
14570b31195SChangpeng Liu 		SPDK_ERRLOG("Error to get the memory map offset\n");
14670b31195SChangpeng Liu 		free(mr);
14770b31195SChangpeng Liu 		return -EFAULT;
14870b31195SChangpeng Liu 	}
14970b31195SChangpeng Liu 	mr->offset = offset;
15070b31195SChangpeng Liu 
15170b31195SChangpeng Liu 	ret = vfio_add_mr(dev, mr);
15270b31195SChangpeng Liu 	if (ret) {
15370b31195SChangpeng Liu 		free(mr);
15470b31195SChangpeng Liu 		return ret;
15570b31195SChangpeng Liu 	}
15670b31195SChangpeng Liu 
15770b31195SChangpeng Liu 	return vfio_user_dev_dma_map_unmap(dev, mr, true);
15870b31195SChangpeng Liu }
15970b31195SChangpeng Liu 
16070b31195SChangpeng Liu static int
vfio_device_dma_map(struct vfio_device * device)16170b31195SChangpeng Liu vfio_device_dma_map(struct vfio_device *device)
16270b31195SChangpeng Liu {
16370b31195SChangpeng Liu 	const struct spdk_mem_map_ops vfio_map_ops = {
16470b31195SChangpeng Liu 		.notify_cb = vfio_mr_map_notify,
16570b31195SChangpeng Liu 		.are_contiguous = NULL,
16670b31195SChangpeng Liu 	};
16770b31195SChangpeng Liu 
16870b31195SChangpeng Liu 	device->map = spdk_mem_map_alloc((uint64_t)NULL, &vfio_map_ops, device);
16970b31195SChangpeng Liu 	if (device->map == NULL) {
17070b31195SChangpeng Liu 		SPDK_ERRLOG("Failed to allocate memory map structure\n");
17170b31195SChangpeng Liu 		return -EFAULT;
17270b31195SChangpeng Liu 	}
17370b31195SChangpeng Liu 
17470b31195SChangpeng Liu 	return 0;
17570b31195SChangpeng Liu }
17670b31195SChangpeng Liu 
17770b31195SChangpeng Liu static struct vfio_info_cap_header *
vfio_device_get_info_cap(struct vfio_region_info * info,int cap)17870b31195SChangpeng Liu vfio_device_get_info_cap(struct vfio_region_info *info, int cap)
17970b31195SChangpeng Liu {
18070b31195SChangpeng Liu 	struct vfio_info_cap_header *h;
18170b31195SChangpeng Liu 	size_t offset;
18270b31195SChangpeng Liu 
18370b31195SChangpeng Liu 	if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) == 0) {
18470b31195SChangpeng Liu 		return NULL;
18570b31195SChangpeng Liu 	}
18670b31195SChangpeng Liu 
18770b31195SChangpeng Liu 	offset = info->cap_offset;
18870b31195SChangpeng Liu 	while (offset != 0) {
18970b31195SChangpeng Liu 		h = (struct vfio_info_cap_header *)((uintptr_t)info + offset);
19070b31195SChangpeng Liu 		if (h->id == cap) {
19170b31195SChangpeng Liu 			return h;
19270b31195SChangpeng Liu 		}
19370b31195SChangpeng Liu 		offset = h->next;
19470b31195SChangpeng Liu 	}
19570b31195SChangpeng Liu 
19670b31195SChangpeng Liu 	return NULL;
19770b31195SChangpeng Liu }
19870b31195SChangpeng Liu 
19970b31195SChangpeng Liu static int
vfio_device_setup_sparse_mmaps(struct vfio_device * device,int index,struct vfio_region_info * info,int * fds)20070b31195SChangpeng Liu vfio_device_setup_sparse_mmaps(struct vfio_device *device, int index,
20170b31195SChangpeng Liu 			       struct vfio_region_info *info, int *fds)
20270b31195SChangpeng Liu {
20370b31195SChangpeng Liu 	struct vfio_info_cap_header *hdr;
20470b31195SChangpeng Liu 	struct vfio_region_info_cap_sparse_mmap *sparse;
20570b31195SChangpeng Liu 	struct vfio_pci_region *region = &device->regions[index];
20670b31195SChangpeng Liu 	uint32_t i, j = 0;
207*fad37e09SKonrad Sztyber 	int rc = 0, prot = 0;
20870b31195SChangpeng Liu 
20970b31195SChangpeng Liu 	hdr = vfio_device_get_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP);
21070b31195SChangpeng Liu 	if (!hdr) {
21170b31195SChangpeng Liu 		SPDK_NOTICELOG("Device doesn't have sparse mmap\n");
21270b31195SChangpeng Liu 		return -EEXIST;
21370b31195SChangpeng Liu 	}
21470b31195SChangpeng Liu 
21570b31195SChangpeng Liu 	sparse = SPDK_CONTAINEROF(hdr, struct vfio_region_info_cap_sparse_mmap, header);
21670b31195SChangpeng Liu 	for (i = 0; i < sparse->nr_areas; i++) {
21770b31195SChangpeng Liu 		if (sparse->areas[i].size) {
21870b31195SChangpeng Liu 			region->mmaps[j].offset = sparse->areas[i].offset;
21970b31195SChangpeng Liu 			region->mmaps[j].size = sparse->areas[i].size;
22070b31195SChangpeng Liu 			prot |= info->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
22170b31195SChangpeng Liu 			prot |= info->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
22270b31195SChangpeng Liu 			if (*fds) {
22370b31195SChangpeng Liu 				region->mmaps[j].mem = mmap(NULL, region->mmaps[j].size, prot, MAP_SHARED,
22470b31195SChangpeng Liu 							    fds[i], region->offset + region->mmaps[j].offset);
22570b31195SChangpeng Liu 				if (region->mmaps[j].mem == MAP_FAILED) {
22670b31195SChangpeng Liu 					SPDK_ERRLOG("Device SPARSE MMAP failed\n");
227*fad37e09SKonrad Sztyber 					rc = -EIO;
228*fad37e09SKonrad Sztyber 					goto out;
22970b31195SChangpeng Liu 				}
23070b31195SChangpeng Liu 			} else {
23170b31195SChangpeng Liu 				SPDK_DEBUGLOG(vfio_pci, "No valid fd, skip mmap for bar %d region %u\n", index, i);
23270b31195SChangpeng Liu 			}
23370b31195SChangpeng Liu 			SPDK_DEBUGLOG(vfio_pci, "Sparse region %u, Size 0x%llx, Offset 0x%llx, Map addr %p\n",
23470b31195SChangpeng Liu 				      i, sparse->areas[i].size, sparse->areas[i].offset,
23570b31195SChangpeng Liu 				      region->mmaps[j].mem);
23670b31195SChangpeng Liu 			j++;
23770b31195SChangpeng Liu 		}
23870b31195SChangpeng Liu 	}
23970b31195SChangpeng Liu 	device->regions[index].nr_mmaps = j;
240*fad37e09SKonrad Sztyber out:
241*fad37e09SKonrad Sztyber 	for (i = 0; i < sparse->nr_areas; i++) {
242*fad37e09SKonrad Sztyber 		close(fds[i]);
243*fad37e09SKonrad Sztyber 	}
24470b31195SChangpeng Liu 
245*fad37e09SKonrad Sztyber 	return rc;
24670b31195SChangpeng Liu }
24770b31195SChangpeng Liu 
24870b31195SChangpeng Liu static int
vfio_device_map_region(struct vfio_device * device,struct vfio_pci_region * region,int fd)24970b31195SChangpeng Liu vfio_device_map_region(struct vfio_device *device, struct vfio_pci_region *region, int fd)
25070b31195SChangpeng Liu {
25170b31195SChangpeng Liu 	int prot = 0;
25270b31195SChangpeng Liu 
25370b31195SChangpeng Liu 	prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
25470b31195SChangpeng Liu 	prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
25570b31195SChangpeng Liu 
25670b31195SChangpeng Liu 	region->mmaps[0].offset = 0;
25770b31195SChangpeng Liu 	region->mmaps[0].size = region->size;
25870b31195SChangpeng Liu 
25970b31195SChangpeng Liu 	region->mmaps[0].mem = mmap(NULL, region->size, prot, MAP_SHARED,
26070b31195SChangpeng Liu 				    fd, region->offset);
261*fad37e09SKonrad Sztyber 	close(fd);
26270b31195SChangpeng Liu 	if (region->mmaps[0].mem == MAP_FAILED) {
26370b31195SChangpeng Liu 		SPDK_ERRLOG("Device Region MMAP failed\n");
26470b31195SChangpeng Liu 		return -EFAULT;
26570b31195SChangpeng Liu 	}
26670b31195SChangpeng Liu 	SPDK_DEBUGLOG(vfio_pci, "Memory mapped to %p\n", region->mmaps[0].mem);
26770b31195SChangpeng Liu 	region->nr_mmaps = 1;
26870b31195SChangpeng Liu 
26970b31195SChangpeng Liu 	return 0;
27070b31195SChangpeng Liu }
27170b31195SChangpeng Liu 
27270b31195SChangpeng Liu static int
vfio_device_map_bars_and_config_region(struct vfio_device * device)27370b31195SChangpeng Liu vfio_device_map_bars_and_config_region(struct vfio_device *device)
27470b31195SChangpeng Liu {
27570b31195SChangpeng Liu 	uint32_t i;
27670b31195SChangpeng Liu 	int ret;
27770b31195SChangpeng Liu 	size_t len = 4096;
27870b31195SChangpeng Liu 	int fds[VFIO_MAXIMUM_SPARSE_MMAP_REGIONS];
27970b31195SChangpeng Liu 	struct vfio_region_info *info;
28070b31195SChangpeng Liu 	uint8_t *buf;
28170b31195SChangpeng Liu 
28270b31195SChangpeng Liu 	buf = calloc(1, len);
28370b31195SChangpeng Liu 	if (!buf) {
28470b31195SChangpeng Liu 		return -ENOMEM;
28570b31195SChangpeng Liu 	}
28670b31195SChangpeng Liu 
28770b31195SChangpeng Liu 	info = (struct vfio_region_info *)buf;
28870b31195SChangpeng Liu 	for (i = 0; i < device->pci_regions; i++) {
28970b31195SChangpeng Liu 		memset(info, 0, len);
29070b31195SChangpeng Liu 		memset(fds, 0, sizeof(fds));
29170b31195SChangpeng Liu 
29270b31195SChangpeng Liu 		info->index = i;
29370b31195SChangpeng Liu 		ret = vfio_user_get_dev_region_info(device, info, len, fds, VFIO_MAXIMUM_SPARSE_MMAP_REGIONS);
29470b31195SChangpeng Liu 		if (ret) {
29570b31195SChangpeng Liu 			SPDK_ERRLOG("Device setup bar %d failed\n", ret);
29670b31195SChangpeng Liu 			free(buf);
29770b31195SChangpeng Liu 			return ret;
29870b31195SChangpeng Liu 		}
29970b31195SChangpeng Liu 
30070b31195SChangpeng Liu 		device->regions[i].size = info->size;
30170b31195SChangpeng Liu 		device->regions[i].offset = info->offset;
30270b31195SChangpeng Liu 		device->regions[i].flags = info->flags;
30370b31195SChangpeng Liu 
30470b31195SChangpeng Liu 		SPDK_DEBUGLOG(vfio_pci, "Bar %d, Size 0x%llx, Offset 0x%llx, Flags 0x%x, Cap offset %u\n",
30570b31195SChangpeng Liu 			      i, info->size, info->offset, info->flags, info->cap_offset);
30670b31195SChangpeng Liu 
30770b31195SChangpeng Liu 		/* Setup MMAP if any */
30870b31195SChangpeng Liu 		if (info->size && (info->flags & VFIO_REGION_INFO_FLAG_MMAP)) {
30970b31195SChangpeng Liu 			/* try to map sparse memory region first */
31070b31195SChangpeng Liu 			ret = vfio_device_setup_sparse_mmaps(device, i, info, fds);
31170b31195SChangpeng Liu 			if (ret < 0) {
31270b31195SChangpeng Liu 				ret = vfio_device_map_region(device, &device->regions[i], fds[0]);
31370b31195SChangpeng Liu 			}
31470b31195SChangpeng Liu 
31570b31195SChangpeng Liu 			if (ret != 0) {
31670b31195SChangpeng Liu 				SPDK_ERRLOG("Setup Device %s region %d failed\n", device->name, i);
31770b31195SChangpeng Liu 				free(buf);
31870b31195SChangpeng Liu 				return ret;
31970b31195SChangpeng Liu 			}
32070b31195SChangpeng Liu 		}
32170b31195SChangpeng Liu 	}
32270b31195SChangpeng Liu 
32370b31195SChangpeng Liu 	free(buf);
32470b31195SChangpeng Liu 	return 0;
32570b31195SChangpeng Liu }
32670b31195SChangpeng Liu 
32770b31195SChangpeng Liu static void
vfio_device_unmap_bars(struct vfio_device * dev)32870b31195SChangpeng Liu vfio_device_unmap_bars(struct vfio_device *dev)
32970b31195SChangpeng Liu {
33070b31195SChangpeng Liu 	uint32_t i, j;
33170b31195SChangpeng Liu 	struct vfio_pci_region *region;
33270b31195SChangpeng Liu 
33370b31195SChangpeng Liu 	for (i = 0; i < dev->pci_regions; i++) {
33470b31195SChangpeng Liu 		region = &dev->regions[i];
33570b31195SChangpeng Liu 		for (j = 0; j < region->nr_mmaps; j++) {
33670b31195SChangpeng Liu 			if (region->mmaps[j].mem) {
33770b31195SChangpeng Liu 				munmap(region->mmaps[j].mem, region->mmaps[j].size);
33870b31195SChangpeng Liu 			}
33970b31195SChangpeng Liu 		}
34070b31195SChangpeng Liu 	}
34170b31195SChangpeng Liu 	memset(dev->regions, 0, sizeof(dev->regions));
34270b31195SChangpeng Liu }
34370b31195SChangpeng Liu 
34470b31195SChangpeng Liu struct vfio_device *
spdk_vfio_user_setup(const char * path)34570b31195SChangpeng Liu spdk_vfio_user_setup(const char *path)
34670b31195SChangpeng Liu {
34770b31195SChangpeng Liu 	int ret;
34870b31195SChangpeng Liu 	struct vfio_device *device = NULL;
34970b31195SChangpeng Liu 	struct vfio_user_device_info dev_info = {};
35070b31195SChangpeng Liu 
35170b31195SChangpeng Liu 	device = calloc(1, sizeof(*device));
35270b31195SChangpeng Liu 	if (!device) {
35370b31195SChangpeng Liu 		return NULL;
35470b31195SChangpeng Liu 	}
35570b31195SChangpeng Liu 	TAILQ_INIT(&device->mrs_head);
35670b31195SChangpeng Liu 	snprintf(device->path, PATH_MAX, "%s", path);
35770b31195SChangpeng Liu 	snprintf(device->name, sizeof(device->name), "vfio-user%u", g_vfio_dev_id++);
35870b31195SChangpeng Liu 
35970b31195SChangpeng Liu 	ret = vfio_user_dev_setup(device);
36070b31195SChangpeng Liu 	if (ret) {
36170b31195SChangpeng Liu 		free(device);
36270b31195SChangpeng Liu 		SPDK_ERRLOG("Error to setup vfio-user via path %s\n", path);
36370b31195SChangpeng Liu 		return NULL;
36470b31195SChangpeng Liu 	}
36570b31195SChangpeng Liu 
36670b31195SChangpeng Liu 	ret = vfio_user_get_dev_info(device, &dev_info, sizeof(dev_info));
36770b31195SChangpeng Liu 	if (ret) {
36870b31195SChangpeng Liu 		SPDK_ERRLOG("Device get info failed\n");
36970b31195SChangpeng Liu 		goto cleanup;
37070b31195SChangpeng Liu 	}
37170b31195SChangpeng Liu 	device->pci_regions = dev_info.num_regions;
37270b31195SChangpeng Liu 	device->flags = dev_info.flags;
37370b31195SChangpeng Liu 
37470b31195SChangpeng Liu 	ret = vfio_device_map_bars_and_config_region(device);
37570b31195SChangpeng Liu 	if (ret) {
37670b31195SChangpeng Liu 		goto cleanup;
37770b31195SChangpeng Liu 	}
37870b31195SChangpeng Liu 
37970b31195SChangpeng Liu 	/* Register DMA Region */
38070b31195SChangpeng Liu 	ret = vfio_device_dma_map(device);
38170b31195SChangpeng Liu 	if (ret) {
38270b31195SChangpeng Liu 		SPDK_ERRLOG("Container DMA map failed\n");
38370b31195SChangpeng Liu 		goto cleanup;
38470b31195SChangpeng Liu 	}
38570b31195SChangpeng Liu 
38670b31195SChangpeng Liu 	SPDK_DEBUGLOG(vfio_pci, "Device %s, Path %s Setup Successfully\n", device->name, device->path);
38770b31195SChangpeng Liu 
38870b31195SChangpeng Liu 	return device;
38970b31195SChangpeng Liu 
39070b31195SChangpeng Liu cleanup:
39170b31195SChangpeng Liu 	close(device->fd);
39270b31195SChangpeng Liu 	free(device);
39370b31195SChangpeng Liu 	return NULL;
39470b31195SChangpeng Liu }
39570b31195SChangpeng Liu 
39670b31195SChangpeng Liu void
spdk_vfio_user_release(struct vfio_device * dev)39770b31195SChangpeng Liu spdk_vfio_user_release(struct vfio_device *dev)
39870b31195SChangpeng Liu {
39970b31195SChangpeng Liu 	SPDK_DEBUGLOG(vfio_pci, "Release file %s\n", dev->path);
40070b31195SChangpeng Liu 
40170b31195SChangpeng Liu 	vfio_device_unmap_bars(dev);
40270b31195SChangpeng Liu 	if (dev->map) {
40370b31195SChangpeng Liu 		spdk_mem_map_free(&dev->map);
40470b31195SChangpeng Liu 	}
40570b31195SChangpeng Liu 	close(dev->fd);
40670b31195SChangpeng Liu 
40770b31195SChangpeng Liu 	free(dev);
40870b31195SChangpeng Liu }
40970b31195SChangpeng Liu 
41070b31195SChangpeng Liu void *
spdk_vfio_user_get_bar_addr(struct vfio_device * dev,uint32_t index,uint64_t offset,uint32_t len)41170b31195SChangpeng Liu spdk_vfio_user_get_bar_addr(struct vfio_device *dev, uint32_t index, uint64_t offset, uint32_t len)
41270b31195SChangpeng Liu {
41370b31195SChangpeng Liu 	struct vfio_pci_region *region = &dev->regions[index];
41470b31195SChangpeng Liu 	uint32_t i;
41570b31195SChangpeng Liu 
41670b31195SChangpeng Liu 	if (!region->size || !(region->flags & VFIO_REGION_INFO_FLAG_MMAP)) {
41770b31195SChangpeng Liu 		return NULL;
41870b31195SChangpeng Liu 	}
41970b31195SChangpeng Liu 
42070b31195SChangpeng Liu 	for (i = 0; i < region->nr_mmaps; i++) {
42170b31195SChangpeng Liu 		if (region->mmaps[i].mem && (region->mmaps[i].offset <= offset) &&
42270b31195SChangpeng Liu 		    ((offset + len) <= (region->mmaps[i].offset + region->mmaps[i].size))) {
42370b31195SChangpeng Liu 			return (void *)((uintptr_t)region->mmaps[i].mem + offset - region->mmaps[i].offset);
42470b31195SChangpeng Liu 		}
42570b31195SChangpeng Liu 	}
42670b31195SChangpeng Liu 
42770b31195SChangpeng Liu 	return NULL;
42870b31195SChangpeng Liu }
42970b31195SChangpeng Liu 
4305fb57441SSebastian Brzezinka /* For fuzzing only */
4315fb57441SSebastian Brzezinka int
spdk_vfio_user_dev_send_request(struct vfio_device * dev,enum vfio_user_command command,void * arg,size_t arg_len,size_t buf_len,int * fds,int max_fds)4325fb57441SSebastian Brzezinka spdk_vfio_user_dev_send_request(struct vfio_device *dev, enum vfio_user_command command,
4335fb57441SSebastian Brzezinka 				void *arg, size_t arg_len, size_t buf_len, int *fds,
4345fb57441SSebastian Brzezinka 				int max_fds)
4355fb57441SSebastian Brzezinka {
4365fb57441SSebastian Brzezinka 	return vfio_user_dev_send_request(dev, command, arg, arg_len, buf_len, fds, max_fds);
4375fb57441SSebastian Brzezinka }
4385fb57441SSebastian Brzezinka 
43970b31195SChangpeng Liu SPDK_LOG_REGISTER_COMPONENT(vfio_pci)
440