xref: /linux/tools/testing/selftests/vfio/lib/vfio_pci_device.c (revision 39bcf0f7d415fee440d2eba877b9b618cbd6d824)
119faf6fdSDavid Matlack // SPDX-License-Identifier: GPL-2.0-only
2118e073eSDavid Matlack #include <dirent.h>
319faf6fdSDavid Matlack #include <fcntl.h>
419faf6fdSDavid Matlack #include <libgen.h>
5*16950b60SAlex Mastro #include <stdint.h>
619faf6fdSDavid Matlack #include <stdlib.h>
719faf6fdSDavid Matlack #include <string.h>
819faf6fdSDavid Matlack #include <unistd.h>
919faf6fdSDavid Matlack 
1019faf6fdSDavid Matlack #include <sys/eventfd.h>
1119faf6fdSDavid Matlack #include <sys/ioctl.h>
1219faf6fdSDavid Matlack #include <sys/mman.h>
1319faf6fdSDavid Matlack 
1461cbfe50SDavid Matlack #include <uapi/linux/types.h>
1519faf6fdSDavid Matlack #include <linux/limits.h>
1619faf6fdSDavid Matlack #include <linux/mman.h>
1719faf6fdSDavid Matlack #include <linux/types.h>
1819faf6fdSDavid Matlack #include <linux/vfio.h>
1961cbfe50SDavid Matlack #include <linux/iommufd.h>
2019faf6fdSDavid Matlack 
2119faf6fdSDavid Matlack #include "../../../kselftest.h"
2219faf6fdSDavid Matlack #include <vfio_util.h>
2319faf6fdSDavid Matlack 
2419faf6fdSDavid Matlack #define PCI_SYSFS_PATH	"/sys/bus/pci/devices"
2519faf6fdSDavid Matlack 
2619faf6fdSDavid Matlack #define ioctl_assert(_fd, _op, _arg) do {						       \
2719faf6fdSDavid Matlack 	void *__arg = (_arg);								       \
2819faf6fdSDavid Matlack 	int __ret = ioctl((_fd), (_op), (__arg));					       \
2919faf6fdSDavid Matlack 	VFIO_ASSERT_EQ(__ret, 0, "ioctl(%s, %s, %s) returned %d\n", #_fd, #_op, #_arg, __ret); \
3019faf6fdSDavid Matlack } while (0)
3119faf6fdSDavid Matlack 
__to_iova(struct vfio_pci_device * device,void * vaddr)32346cd58fSDavid Matlack iova_t __to_iova(struct vfio_pci_device *device, void *vaddr)
33346cd58fSDavid Matlack {
34346cd58fSDavid Matlack 	struct vfio_dma_region *region;
35346cd58fSDavid Matlack 
36346cd58fSDavid Matlack 	list_for_each_entry(region, &device->dma_regions, link) {
37346cd58fSDavid Matlack 		if (vaddr < region->vaddr)
38346cd58fSDavid Matlack 			continue;
39346cd58fSDavid Matlack 
40346cd58fSDavid Matlack 		if (vaddr >= region->vaddr + region->size)
41346cd58fSDavid Matlack 			continue;
42346cd58fSDavid Matlack 
43346cd58fSDavid Matlack 		return region->iova + (vaddr - region->vaddr);
44346cd58fSDavid Matlack 	}
45346cd58fSDavid Matlack 
46346cd58fSDavid Matlack 	return INVALID_IOVA;
47346cd58fSDavid Matlack }
48346cd58fSDavid Matlack 
to_iova(struct vfio_pci_device * device,void * vaddr)49346cd58fSDavid Matlack iova_t to_iova(struct vfio_pci_device *device, void *vaddr)
50346cd58fSDavid Matlack {
51346cd58fSDavid Matlack 	iova_t iova;
52346cd58fSDavid Matlack 
53346cd58fSDavid Matlack 	iova = __to_iova(device, vaddr);
54346cd58fSDavid Matlack 	VFIO_ASSERT_NE(iova, INVALID_IOVA, "%p is not mapped into device.\n", vaddr);
55346cd58fSDavid Matlack 
56346cd58fSDavid Matlack 	return iova;
57346cd58fSDavid Matlack }
58346cd58fSDavid Matlack 
vfio_pci_irq_set(struct vfio_pci_device * device,u32 index,u32 vector,u32 count,int * fds)5919faf6fdSDavid Matlack static void vfio_pci_irq_set(struct vfio_pci_device *device,
6019faf6fdSDavid Matlack 			     u32 index, u32 vector, u32 count, int *fds)
6119faf6fdSDavid Matlack {
6219faf6fdSDavid Matlack 	u8 buf[sizeof(struct vfio_irq_set) + sizeof(int) * count] = {};
6319faf6fdSDavid Matlack 	struct vfio_irq_set *irq = (void *)&buf;
6419faf6fdSDavid Matlack 	int *irq_fds = (void *)&irq->data;
6519faf6fdSDavid Matlack 
6619faf6fdSDavid Matlack 	irq->argsz = sizeof(buf);
6719faf6fdSDavid Matlack 	irq->flags = VFIO_IRQ_SET_ACTION_TRIGGER;
6819faf6fdSDavid Matlack 	irq->index = index;
6919faf6fdSDavid Matlack 	irq->start = vector;
7019faf6fdSDavid Matlack 	irq->count = count;
7119faf6fdSDavid Matlack 
7219faf6fdSDavid Matlack 	if (count) {
7319faf6fdSDavid Matlack 		irq->flags |= VFIO_IRQ_SET_DATA_EVENTFD;
7419faf6fdSDavid Matlack 		memcpy(irq_fds, fds, sizeof(int) * count);
7519faf6fdSDavid Matlack 	} else {
7619faf6fdSDavid Matlack 		irq->flags |= VFIO_IRQ_SET_DATA_NONE;
7719faf6fdSDavid Matlack 	}
7819faf6fdSDavid Matlack 
7919faf6fdSDavid Matlack 	ioctl_assert(device->fd, VFIO_DEVICE_SET_IRQS, irq);
8019faf6fdSDavid Matlack }
8119faf6fdSDavid Matlack 
vfio_pci_irq_trigger(struct vfio_pci_device * device,u32 index,u32 vector)8219faf6fdSDavid Matlack void vfio_pci_irq_trigger(struct vfio_pci_device *device, u32 index, u32 vector)
8319faf6fdSDavid Matlack {
8419faf6fdSDavid Matlack 	struct vfio_irq_set irq = {
8519faf6fdSDavid Matlack 		.argsz = sizeof(irq),
8619faf6fdSDavid Matlack 		.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_NONE,
8719faf6fdSDavid Matlack 		.index = index,
8819faf6fdSDavid Matlack 		.start = vector,
8919faf6fdSDavid Matlack 		.count = 1,
9019faf6fdSDavid Matlack 	};
9119faf6fdSDavid Matlack 
9219faf6fdSDavid Matlack 	ioctl_assert(device->fd, VFIO_DEVICE_SET_IRQS, &irq);
9319faf6fdSDavid Matlack }
9419faf6fdSDavid Matlack 
check_supported_irq_index(u32 index)9519faf6fdSDavid Matlack static void check_supported_irq_index(u32 index)
9619faf6fdSDavid Matlack {
9719faf6fdSDavid Matlack 	/* VFIO selftests only supports MSI and MSI-x for now. */
9819faf6fdSDavid Matlack 	VFIO_ASSERT_TRUE(index == VFIO_PCI_MSI_IRQ_INDEX ||
9919faf6fdSDavid Matlack 			 index == VFIO_PCI_MSIX_IRQ_INDEX,
10019faf6fdSDavid Matlack 			 "Unsupported IRQ index: %u\n", index);
10119faf6fdSDavid Matlack }
10219faf6fdSDavid Matlack 
vfio_pci_irq_enable(struct vfio_pci_device * device,u32 index,u32 vector,int count)10319faf6fdSDavid Matlack void vfio_pci_irq_enable(struct vfio_pci_device *device, u32 index, u32 vector,
10419faf6fdSDavid Matlack 			 int count)
10519faf6fdSDavid Matlack {
10619faf6fdSDavid Matlack 	int i;
10719faf6fdSDavid Matlack 
10819faf6fdSDavid Matlack 	check_supported_irq_index(index);
10919faf6fdSDavid Matlack 
11019faf6fdSDavid Matlack 	for (i = vector; i < vector + count; i++) {
11119faf6fdSDavid Matlack 		VFIO_ASSERT_LT(device->msi_eventfds[i], 0);
11219faf6fdSDavid Matlack 		device->msi_eventfds[i] = eventfd(0, 0);
11319faf6fdSDavid Matlack 		VFIO_ASSERT_GE(device->msi_eventfds[i], 0);
11419faf6fdSDavid Matlack 	}
11519faf6fdSDavid Matlack 
11619faf6fdSDavid Matlack 	vfio_pci_irq_set(device, index, vector, count, device->msi_eventfds + vector);
11719faf6fdSDavid Matlack }
11819faf6fdSDavid Matlack 
vfio_pci_irq_disable(struct vfio_pci_device * device,u32 index)11919faf6fdSDavid Matlack void vfio_pci_irq_disable(struct vfio_pci_device *device, u32 index)
12019faf6fdSDavid Matlack {
12119faf6fdSDavid Matlack 	int i;
12219faf6fdSDavid Matlack 
12319faf6fdSDavid Matlack 	check_supported_irq_index(index);
12419faf6fdSDavid Matlack 
12519faf6fdSDavid Matlack 	for (i = 0; i < ARRAY_SIZE(device->msi_eventfds); i++) {
12619faf6fdSDavid Matlack 		if (device->msi_eventfds[i] < 0)
12719faf6fdSDavid Matlack 			continue;
12819faf6fdSDavid Matlack 
12919faf6fdSDavid Matlack 		VFIO_ASSERT_EQ(close(device->msi_eventfds[i]), 0);
13019faf6fdSDavid Matlack 		device->msi_eventfds[i] = -1;
13119faf6fdSDavid Matlack 	}
13219faf6fdSDavid Matlack 
13319faf6fdSDavid Matlack 	vfio_pci_irq_set(device, index, 0, 0, NULL);
13419faf6fdSDavid Matlack }
13519faf6fdSDavid Matlack 
vfio_pci_irq_get(struct vfio_pci_device * device,u32 index,struct vfio_irq_info * irq_info)13619faf6fdSDavid Matlack static void vfio_pci_irq_get(struct vfio_pci_device *device, u32 index,
13719faf6fdSDavid Matlack 			     struct vfio_irq_info *irq_info)
13819faf6fdSDavid Matlack {
13919faf6fdSDavid Matlack 	irq_info->argsz = sizeof(*irq_info);
14019faf6fdSDavid Matlack 	irq_info->index = index;
14119faf6fdSDavid Matlack 
14219faf6fdSDavid Matlack 	ioctl_assert(device->fd, VFIO_DEVICE_GET_IRQ_INFO, irq_info);
14319faf6fdSDavid Matlack }
14419faf6fdSDavid Matlack 
vfio_iommu_dma_map(struct vfio_pci_device * device,struct vfio_dma_region * region)145*16950b60SAlex Mastro static int vfio_iommu_dma_map(struct vfio_pci_device *device,
146346cd58fSDavid Matlack 			       struct vfio_dma_region *region)
14719faf6fdSDavid Matlack {
14861cbfe50SDavid Matlack 	struct vfio_iommu_type1_dma_map args = {
14961cbfe50SDavid Matlack 		.argsz = sizeof(args),
15019faf6fdSDavid Matlack 		.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
151346cd58fSDavid Matlack 		.vaddr = (u64)region->vaddr,
152346cd58fSDavid Matlack 		.iova = region->iova,
153346cd58fSDavid Matlack 		.size = region->size,
15419faf6fdSDavid Matlack 	};
15519faf6fdSDavid Matlack 
156*16950b60SAlex Mastro 	if (ioctl(device->container_fd, VFIO_IOMMU_MAP_DMA, &args))
157*16950b60SAlex Mastro 		return -errno;
158*16950b60SAlex Mastro 
159*16950b60SAlex Mastro 	return 0;
16061cbfe50SDavid Matlack }
16161cbfe50SDavid Matlack 
iommufd_dma_map(struct vfio_pci_device * device,struct vfio_dma_region * region)162*16950b60SAlex Mastro static int iommufd_dma_map(struct vfio_pci_device *device,
16361cbfe50SDavid Matlack 			    struct vfio_dma_region *region)
16461cbfe50SDavid Matlack {
16561cbfe50SDavid Matlack 	struct iommu_ioas_map args = {
16661cbfe50SDavid Matlack 		.size = sizeof(args),
16761cbfe50SDavid Matlack 		.flags = IOMMU_IOAS_MAP_READABLE |
16861cbfe50SDavid Matlack 			 IOMMU_IOAS_MAP_WRITEABLE |
16961cbfe50SDavid Matlack 			 IOMMU_IOAS_MAP_FIXED_IOVA,
17061cbfe50SDavid Matlack 		.user_va = (u64)region->vaddr,
17161cbfe50SDavid Matlack 		.iova = region->iova,
17261cbfe50SDavid Matlack 		.length = region->size,
17361cbfe50SDavid Matlack 		.ioas_id = device->ioas_id,
17461cbfe50SDavid Matlack 	};
17561cbfe50SDavid Matlack 
176*16950b60SAlex Mastro 	if (ioctl(device->iommufd, IOMMU_IOAS_MAP, &args))
177*16950b60SAlex Mastro 		return -errno;
178*16950b60SAlex Mastro 
179*16950b60SAlex Mastro 	return 0;
18061cbfe50SDavid Matlack }
18161cbfe50SDavid Matlack 
__vfio_pci_dma_map(struct vfio_pci_device * device,struct vfio_dma_region * region)182*16950b60SAlex Mastro int __vfio_pci_dma_map(struct vfio_pci_device *device,
18361cbfe50SDavid Matlack 		      struct vfio_dma_region *region)
18461cbfe50SDavid Matlack {
185*16950b60SAlex Mastro 	int ret;
186*16950b60SAlex Mastro 
18761cbfe50SDavid Matlack 	if (device->iommufd)
188*16950b60SAlex Mastro 		ret = iommufd_dma_map(device, region);
18961cbfe50SDavid Matlack 	else
190*16950b60SAlex Mastro 		ret = vfio_iommu_dma_map(device, region);
191*16950b60SAlex Mastro 
192*16950b60SAlex Mastro 	if (ret)
193*16950b60SAlex Mastro 		return ret;
194346cd58fSDavid Matlack 
195346cd58fSDavid Matlack 	list_add(&region->link, &device->dma_regions);
196*16950b60SAlex Mastro 
197*16950b60SAlex Mastro 	return 0;
19819faf6fdSDavid Matlack }
19919faf6fdSDavid Matlack 
vfio_iommu_dma_unmap(int fd,u64 iova,u64 size,u32 flags,u64 * unmapped)200*16950b60SAlex Mastro static int vfio_iommu_dma_unmap(int fd, u64 iova, u64 size, u32 flags,
201*16950b60SAlex Mastro 				u64 *unmapped)
20261cbfe50SDavid Matlack {
20361cbfe50SDavid Matlack 	struct vfio_iommu_type1_dma_unmap args = {
20461cbfe50SDavid Matlack 		.argsz = sizeof(args),
205*16950b60SAlex Mastro 		.iova = iova,
206*16950b60SAlex Mastro 		.size = size,
207*16950b60SAlex Mastro 		.flags = flags,
20861cbfe50SDavid Matlack 	};
20961cbfe50SDavid Matlack 
210*16950b60SAlex Mastro 	if (ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &args))
211*16950b60SAlex Mastro 		return -errno;
212*16950b60SAlex Mastro 
213*16950b60SAlex Mastro 	if (unmapped)
214*16950b60SAlex Mastro 		*unmapped = args.size;
215*16950b60SAlex Mastro 
216*16950b60SAlex Mastro 	return 0;
21761cbfe50SDavid Matlack }
21861cbfe50SDavid Matlack 
iommufd_dma_unmap(int fd,u64 iova,u64 length,u32 ioas_id,u64 * unmapped)219*16950b60SAlex Mastro static int iommufd_dma_unmap(int fd, u64 iova, u64 length, u32 ioas_id,
220*16950b60SAlex Mastro 			     u64 *unmapped)
22161cbfe50SDavid Matlack {
22261cbfe50SDavid Matlack 	struct iommu_ioas_unmap args = {
22361cbfe50SDavid Matlack 		.size = sizeof(args),
224*16950b60SAlex Mastro 		.iova = iova,
225*16950b60SAlex Mastro 		.length = length,
226*16950b60SAlex Mastro 		.ioas_id = ioas_id,
22761cbfe50SDavid Matlack 	};
22861cbfe50SDavid Matlack 
229*16950b60SAlex Mastro 	if (ioctl(fd, IOMMU_IOAS_UNMAP, &args))
230*16950b60SAlex Mastro 		return -errno;
231*16950b60SAlex Mastro 
232*16950b60SAlex Mastro 	if (unmapped)
233*16950b60SAlex Mastro 		*unmapped = args.length;
234*16950b60SAlex Mastro 
235*16950b60SAlex Mastro 	return 0;
23661cbfe50SDavid Matlack }
23761cbfe50SDavid Matlack 
__vfio_pci_dma_unmap(struct vfio_pci_device * device,struct vfio_dma_region * region,u64 * unmapped)238*16950b60SAlex Mastro int __vfio_pci_dma_unmap(struct vfio_pci_device *device,
239*16950b60SAlex Mastro 			 struct vfio_dma_region *region, u64 *unmapped)
24019faf6fdSDavid Matlack {
241*16950b60SAlex Mastro 	int ret;
242346cd58fSDavid Matlack 
243*16950b60SAlex Mastro 	if (device->iommufd)
244*16950b60SAlex Mastro 		ret = iommufd_dma_unmap(device->iommufd, region->iova,
245*16950b60SAlex Mastro 					region->size, device->ioas_id,
246*16950b60SAlex Mastro 					unmapped);
247*16950b60SAlex Mastro 	else
248*16950b60SAlex Mastro 		ret = vfio_iommu_dma_unmap(device->container_fd, region->iova,
249*16950b60SAlex Mastro 					   region->size, 0, unmapped);
250*16950b60SAlex Mastro 
251*16950b60SAlex Mastro 	if (ret)
252*16950b60SAlex Mastro 		return ret;
253*16950b60SAlex Mastro 
254*16950b60SAlex Mastro 	list_del_init(&region->link);
255*16950b60SAlex Mastro 
256*16950b60SAlex Mastro 	return 0;
257*16950b60SAlex Mastro }
258*16950b60SAlex Mastro 
__vfio_pci_dma_unmap_all(struct vfio_pci_device * device,u64 * unmapped)259*16950b60SAlex Mastro int __vfio_pci_dma_unmap_all(struct vfio_pci_device *device, u64 *unmapped)
260*16950b60SAlex Mastro {
261*16950b60SAlex Mastro 	int ret;
262*16950b60SAlex Mastro 	struct vfio_dma_region *curr, *next;
263*16950b60SAlex Mastro 
264*16950b60SAlex Mastro 	if (device->iommufd)
265*16950b60SAlex Mastro 		ret = iommufd_dma_unmap(device->iommufd, 0, UINT64_MAX,
266*16950b60SAlex Mastro 					device->ioas_id, unmapped);
267*16950b60SAlex Mastro 	else
268*16950b60SAlex Mastro 		ret = vfio_iommu_dma_unmap(device->container_fd, 0, 0,
269*16950b60SAlex Mastro 					   VFIO_DMA_UNMAP_FLAG_ALL, unmapped);
270*16950b60SAlex Mastro 
271*16950b60SAlex Mastro 	if (ret)
272*16950b60SAlex Mastro 		return ret;
273*16950b60SAlex Mastro 
274*16950b60SAlex Mastro 	list_for_each_entry_safe(curr, next, &device->dma_regions, link)
275*16950b60SAlex Mastro 		list_del_init(&curr->link);
276*16950b60SAlex Mastro 
277*16950b60SAlex Mastro 	return 0;
27819faf6fdSDavid Matlack }
27919faf6fdSDavid Matlack 
vfio_pci_region_get(struct vfio_pci_device * device,int index,struct vfio_region_info * info)28019faf6fdSDavid Matlack static void vfio_pci_region_get(struct vfio_pci_device *device, int index,
28119faf6fdSDavid Matlack 				struct vfio_region_info *info)
28219faf6fdSDavid Matlack {
28319faf6fdSDavid Matlack 	memset(info, 0, sizeof(*info));
28419faf6fdSDavid Matlack 
28519faf6fdSDavid Matlack 	info->argsz = sizeof(*info);
28619faf6fdSDavid Matlack 	info->index = index;
28719faf6fdSDavid Matlack 
28819faf6fdSDavid Matlack 	ioctl_assert(device->fd, VFIO_DEVICE_GET_REGION_INFO, info);
28919faf6fdSDavid Matlack }
29019faf6fdSDavid Matlack 
vfio_pci_bar_map(struct vfio_pci_device * device,int index)29119faf6fdSDavid Matlack static void vfio_pci_bar_map(struct vfio_pci_device *device, int index)
29219faf6fdSDavid Matlack {
29319faf6fdSDavid Matlack 	struct vfio_pci_bar *bar = &device->bars[index];
29419faf6fdSDavid Matlack 	int prot = 0;
29519faf6fdSDavid Matlack 
29619faf6fdSDavid Matlack 	VFIO_ASSERT_LT(index, PCI_STD_NUM_BARS);
29719faf6fdSDavid Matlack 	VFIO_ASSERT_NULL(bar->vaddr);
29819faf6fdSDavid Matlack 	VFIO_ASSERT_TRUE(bar->info.flags & VFIO_REGION_INFO_FLAG_MMAP);
29919faf6fdSDavid Matlack 
30019faf6fdSDavid Matlack 	if (bar->info.flags & VFIO_REGION_INFO_FLAG_READ)
30119faf6fdSDavid Matlack 		prot |= PROT_READ;
30219faf6fdSDavid Matlack 	if (bar->info.flags & VFIO_REGION_INFO_FLAG_WRITE)
30319faf6fdSDavid Matlack 		prot |= PROT_WRITE;
30419faf6fdSDavid Matlack 
30519faf6fdSDavid Matlack 	bar->vaddr = mmap(NULL, bar->info.size, prot, MAP_FILE | MAP_SHARED,
30619faf6fdSDavid Matlack 			  device->fd, bar->info.offset);
30719faf6fdSDavid Matlack 	VFIO_ASSERT_NE(bar->vaddr, MAP_FAILED);
30819faf6fdSDavid Matlack }
30919faf6fdSDavid Matlack 
vfio_pci_bar_unmap(struct vfio_pci_device * device,int index)31019faf6fdSDavid Matlack static void vfio_pci_bar_unmap(struct vfio_pci_device *device, int index)
31119faf6fdSDavid Matlack {
31219faf6fdSDavid Matlack 	struct vfio_pci_bar *bar = &device->bars[index];
31319faf6fdSDavid Matlack 
31419faf6fdSDavid Matlack 	VFIO_ASSERT_LT(index, PCI_STD_NUM_BARS);
31519faf6fdSDavid Matlack 	VFIO_ASSERT_NOT_NULL(bar->vaddr);
31619faf6fdSDavid Matlack 
31719faf6fdSDavid Matlack 	VFIO_ASSERT_EQ(munmap(bar->vaddr, bar->info.size), 0);
31819faf6fdSDavid Matlack 	bar->vaddr = NULL;
31919faf6fdSDavid Matlack }
32019faf6fdSDavid Matlack 
vfio_pci_bar_unmap_all(struct vfio_pci_device * device)32119faf6fdSDavid Matlack static void vfio_pci_bar_unmap_all(struct vfio_pci_device *device)
32219faf6fdSDavid Matlack {
32319faf6fdSDavid Matlack 	int i;
32419faf6fdSDavid Matlack 
32519faf6fdSDavid Matlack 	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
32619faf6fdSDavid Matlack 		if (device->bars[i].vaddr)
32719faf6fdSDavid Matlack 			vfio_pci_bar_unmap(device, i);
32819faf6fdSDavid Matlack 	}
32919faf6fdSDavid Matlack }
33019faf6fdSDavid Matlack 
vfio_pci_config_access(struct vfio_pci_device * device,bool write,size_t config,size_t size,void * data)33119faf6fdSDavid Matlack void vfio_pci_config_access(struct vfio_pci_device *device, bool write,
33219faf6fdSDavid Matlack 			    size_t config, size_t size, void *data)
33319faf6fdSDavid Matlack {
33419faf6fdSDavid Matlack 	struct vfio_region_info *config_space = &device->config_space;
33519faf6fdSDavid Matlack 	int ret;
33619faf6fdSDavid Matlack 
33719faf6fdSDavid Matlack 	if (write)
33819faf6fdSDavid Matlack 		ret = pwrite(device->fd, data, size, config_space->offset + config);
33919faf6fdSDavid Matlack 	else
34019faf6fdSDavid Matlack 		ret = pread(device->fd, data, size, config_space->offset + config);
34119faf6fdSDavid Matlack 
34219faf6fdSDavid Matlack 	VFIO_ASSERT_EQ(ret, size, "Failed to %s PCI config space: 0x%lx\n",
34319faf6fdSDavid Matlack 		       write ? "write to" : "read from", config);
34419faf6fdSDavid Matlack }
34519faf6fdSDavid Matlack 
vfio_pci_device_reset(struct vfio_pci_device * device)346a0fd0af5SJosh Hilke void vfio_pci_device_reset(struct vfio_pci_device *device)
347a0fd0af5SJosh Hilke {
348a0fd0af5SJosh Hilke 	ioctl_assert(device->fd, VFIO_DEVICE_RESET, NULL);
349a0fd0af5SJosh Hilke }
350a0fd0af5SJosh Hilke 
vfio_pci_get_group_from_dev(const char * bdf)35119faf6fdSDavid Matlack static unsigned int vfio_pci_get_group_from_dev(const char *bdf)
35219faf6fdSDavid Matlack {
35319faf6fdSDavid Matlack 	char dev_iommu_group_path[PATH_MAX] = {0};
35419faf6fdSDavid Matlack 	char sysfs_path[PATH_MAX] = {0};
35519faf6fdSDavid Matlack 	unsigned int group;
35619faf6fdSDavid Matlack 	int ret;
35719faf6fdSDavid Matlack 
35819faf6fdSDavid Matlack 	snprintf(sysfs_path, PATH_MAX, "%s/%s/iommu_group", PCI_SYSFS_PATH, bdf);
35919faf6fdSDavid Matlack 
36019faf6fdSDavid Matlack 	ret = readlink(sysfs_path, dev_iommu_group_path, sizeof(dev_iommu_group_path));
36119faf6fdSDavid Matlack 	VFIO_ASSERT_NE(ret, -1, "Failed to get the IOMMU group for device: %s\n", bdf);
36219faf6fdSDavid Matlack 
36319faf6fdSDavid Matlack 	ret = sscanf(basename(dev_iommu_group_path), "%u", &group);
36419faf6fdSDavid Matlack 	VFIO_ASSERT_EQ(ret, 1, "Failed to get the IOMMU group for device: %s\n", bdf);
36519faf6fdSDavid Matlack 
36619faf6fdSDavid Matlack 	return group;
36719faf6fdSDavid Matlack }
36819faf6fdSDavid Matlack 
vfio_pci_group_setup(struct vfio_pci_device * device,const char * bdf)36919faf6fdSDavid Matlack static void vfio_pci_group_setup(struct vfio_pci_device *device, const char *bdf)
37019faf6fdSDavid Matlack {
37119faf6fdSDavid Matlack 	struct vfio_group_status group_status = {
37219faf6fdSDavid Matlack 		.argsz = sizeof(group_status),
37319faf6fdSDavid Matlack 	};
37419faf6fdSDavid Matlack 	char group_path[32];
37519faf6fdSDavid Matlack 	int group;
37619faf6fdSDavid Matlack 
37719faf6fdSDavid Matlack 	group = vfio_pci_get_group_from_dev(bdf);
37819faf6fdSDavid Matlack 	snprintf(group_path, sizeof(group_path), "/dev/vfio/%d", group);
37919faf6fdSDavid Matlack 
38019faf6fdSDavid Matlack 	device->group_fd = open(group_path, O_RDWR);
38119faf6fdSDavid Matlack 	VFIO_ASSERT_GE(device->group_fd, 0, "open(%s) failed\n", group_path);
38219faf6fdSDavid Matlack 
38319faf6fdSDavid Matlack 	ioctl_assert(device->group_fd, VFIO_GROUP_GET_STATUS, &group_status);
38419faf6fdSDavid Matlack 	VFIO_ASSERT_TRUE(group_status.flags & VFIO_GROUP_FLAGS_VIABLE);
38519faf6fdSDavid Matlack 
38619faf6fdSDavid Matlack 	ioctl_assert(device->group_fd, VFIO_GROUP_SET_CONTAINER, &device->container_fd);
38719faf6fdSDavid Matlack }
38819faf6fdSDavid Matlack 
vfio_pci_container_setup(struct vfio_pci_device * device,const char * bdf)38961cbfe50SDavid Matlack static void vfio_pci_container_setup(struct vfio_pci_device *device, const char *bdf)
39019faf6fdSDavid Matlack {
3915df9bd62SDavid Matlack 	unsigned long iommu_type = device->iommu_mode->iommu_type;
39261cbfe50SDavid Matlack 	const char *path = device->iommu_mode->container_path;
39361cbfe50SDavid Matlack 	int version;
39419faf6fdSDavid Matlack 	int ret;
39519faf6fdSDavid Matlack 
39661cbfe50SDavid Matlack 	device->container_fd = open(path, O_RDWR);
39761cbfe50SDavid Matlack 	VFIO_ASSERT_GE(device->container_fd, 0, "open(%s) failed\n", path);
39861cbfe50SDavid Matlack 
39961cbfe50SDavid Matlack 	version = ioctl(device->container_fd, VFIO_GET_API_VERSION);
40061cbfe50SDavid Matlack 	VFIO_ASSERT_EQ(version, VFIO_API_VERSION, "Unsupported version: %d\n", version);
40161cbfe50SDavid Matlack 
40261cbfe50SDavid Matlack 	vfio_pci_group_setup(device, bdf);
403346cd58fSDavid Matlack 
40419faf6fdSDavid Matlack 	ret = ioctl(device->container_fd, VFIO_CHECK_EXTENSION, iommu_type);
40519faf6fdSDavid Matlack 	VFIO_ASSERT_GT(ret, 0, "VFIO IOMMU type %lu not supported\n", iommu_type);
40619faf6fdSDavid Matlack 
40719faf6fdSDavid Matlack 	ioctl_assert(device->container_fd, VFIO_SET_IOMMU, (void *)iommu_type);
40819faf6fdSDavid Matlack 
40919faf6fdSDavid Matlack 	device->fd = ioctl(device->group_fd, VFIO_GROUP_GET_DEVICE_FD, bdf);
41019faf6fdSDavid Matlack 	VFIO_ASSERT_GE(device->fd, 0);
41161cbfe50SDavid Matlack }
41261cbfe50SDavid Matlack 
vfio_pci_device_setup(struct vfio_pci_device * device)41361cbfe50SDavid Matlack static void vfio_pci_device_setup(struct vfio_pci_device *device)
41461cbfe50SDavid Matlack {
41561cbfe50SDavid Matlack 	int i;
41619faf6fdSDavid Matlack 
41719faf6fdSDavid Matlack 	device->info.argsz = sizeof(device->info);
41819faf6fdSDavid Matlack 	ioctl_assert(device->fd, VFIO_DEVICE_GET_INFO, &device->info);
41919faf6fdSDavid Matlack 
42019faf6fdSDavid Matlack 	vfio_pci_region_get(device, VFIO_PCI_CONFIG_REGION_INDEX, &device->config_space);
42119faf6fdSDavid Matlack 
42219faf6fdSDavid Matlack 	/* Sanity check VFIO does not advertise mmap for config space */
42319faf6fdSDavid Matlack 	VFIO_ASSERT_TRUE(!(device->config_space.flags & VFIO_REGION_INFO_FLAG_MMAP),
42419faf6fdSDavid Matlack 			 "PCI config space should not support mmap()\n");
42519faf6fdSDavid Matlack 
42619faf6fdSDavid Matlack 	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
42719faf6fdSDavid Matlack 		struct vfio_pci_bar *bar = device->bars + i;
42819faf6fdSDavid Matlack 
42919faf6fdSDavid Matlack 		vfio_pci_region_get(device, i, &bar->info);
43019faf6fdSDavid Matlack 		if (bar->info.flags & VFIO_REGION_INFO_FLAG_MMAP)
43119faf6fdSDavid Matlack 			vfio_pci_bar_map(device, i);
43219faf6fdSDavid Matlack 	}
43319faf6fdSDavid Matlack 
43419faf6fdSDavid Matlack 	vfio_pci_irq_get(device, VFIO_PCI_MSI_IRQ_INDEX, &device->msi_info);
43519faf6fdSDavid Matlack 	vfio_pci_irq_get(device, VFIO_PCI_MSIX_IRQ_INDEX, &device->msix_info);
43619faf6fdSDavid Matlack 
43719faf6fdSDavid Matlack 	for (i = 0; i < ARRAY_SIZE(device->msi_eventfds); i++)
43819faf6fdSDavid Matlack 		device->msi_eventfds[i] = -1;
43919faf6fdSDavid Matlack }
44019faf6fdSDavid Matlack 
vfio_pci_get_cdev_path(const char * bdf)441118e073eSDavid Matlack const char *vfio_pci_get_cdev_path(const char *bdf)
442118e073eSDavid Matlack {
443118e073eSDavid Matlack 	char dir_path[PATH_MAX];
444118e073eSDavid Matlack 	struct dirent *entry;
445118e073eSDavid Matlack 	char *cdev_path;
446118e073eSDavid Matlack 	DIR *dir;
447118e073eSDavid Matlack 
448118e073eSDavid Matlack 	cdev_path = calloc(PATH_MAX, 1);
449118e073eSDavid Matlack 	VFIO_ASSERT_NOT_NULL(cdev_path);
450118e073eSDavid Matlack 
451118e073eSDavid Matlack 	snprintf(dir_path, sizeof(dir_path), "/sys/bus/pci/devices/%s/vfio-dev/", bdf);
452118e073eSDavid Matlack 
453118e073eSDavid Matlack 	dir = opendir(dir_path);
454118e073eSDavid Matlack 	VFIO_ASSERT_NOT_NULL(dir, "Failed to open directory %s\n", dir_path);
455118e073eSDavid Matlack 
456118e073eSDavid Matlack 	while ((entry = readdir(dir)) != NULL) {
457118e073eSDavid Matlack 		/* Find the file that starts with "vfio" */
458118e073eSDavid Matlack 		if (strncmp("vfio", entry->d_name, 4))
459118e073eSDavid Matlack 			continue;
460118e073eSDavid Matlack 
461118e073eSDavid Matlack 		snprintf(cdev_path, PATH_MAX, "/dev/vfio/devices/%s", entry->d_name);
462118e073eSDavid Matlack 		break;
463118e073eSDavid Matlack 	}
464118e073eSDavid Matlack 
465118e073eSDavid Matlack 	VFIO_ASSERT_NE(cdev_path[0], 0, "Failed to find vfio cdev file.\n");
466118e073eSDavid Matlack 	VFIO_ASSERT_EQ(closedir(dir), 0);
467118e073eSDavid Matlack 
468118e073eSDavid Matlack 	return cdev_path;
469118e073eSDavid Matlack }
470118e073eSDavid Matlack 
471892aff14SDavid Matlack /* Reminder: Keep in sync with FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(). */
4725df9bd62SDavid Matlack static const struct vfio_iommu_mode iommu_modes[] = {
4735df9bd62SDavid Matlack 	{
4745df9bd62SDavid Matlack 		.name = "vfio_type1_iommu",
4755df9bd62SDavid Matlack 		.container_path = "/dev/vfio/vfio",
4765df9bd62SDavid Matlack 		.iommu_type = VFIO_TYPE1_IOMMU,
4775df9bd62SDavid Matlack 	},
4780969c685SDavid Matlack 	{
4790969c685SDavid Matlack 		.name = "vfio_type1v2_iommu",
4800969c685SDavid Matlack 		.container_path = "/dev/vfio/vfio",
4810969c685SDavid Matlack 		.iommu_type = VFIO_TYPE1v2_IOMMU,
4820969c685SDavid Matlack 	},
483d1a17495SDavid Matlack 	{
484d1a17495SDavid Matlack 		.name = "iommufd_compat_type1",
485d1a17495SDavid Matlack 		.container_path = "/dev/iommu",
486d1a17495SDavid Matlack 		.iommu_type = VFIO_TYPE1_IOMMU,
487d1a17495SDavid Matlack 	},
488d1a17495SDavid Matlack 	{
489d1a17495SDavid Matlack 		.name = "iommufd_compat_type1v2",
490d1a17495SDavid Matlack 		.container_path = "/dev/iommu",
491d1a17495SDavid Matlack 		.iommu_type = VFIO_TYPE1v2_IOMMU,
492d1a17495SDavid Matlack 	},
49361cbfe50SDavid Matlack 	{
49461cbfe50SDavid Matlack 		.name = "iommufd",
49561cbfe50SDavid Matlack 	},
4965df9bd62SDavid Matlack };
4975df9bd62SDavid Matlack 
4988afcbe20SDavid Matlack const char *default_iommu_mode = "iommufd";
4995df9bd62SDavid Matlack 
lookup_iommu_mode(const char * iommu_mode)5005df9bd62SDavid Matlack static const struct vfio_iommu_mode *lookup_iommu_mode(const char *iommu_mode)
5015df9bd62SDavid Matlack {
5025df9bd62SDavid Matlack 	int i;
5035df9bd62SDavid Matlack 
5045df9bd62SDavid Matlack 	if (!iommu_mode)
5055df9bd62SDavid Matlack 		iommu_mode = default_iommu_mode;
5065df9bd62SDavid Matlack 
5075df9bd62SDavid Matlack 	for (i = 0; i < ARRAY_SIZE(iommu_modes); i++) {
5085df9bd62SDavid Matlack 		if (strcmp(iommu_mode, iommu_modes[i].name))
5095df9bd62SDavid Matlack 			continue;
5105df9bd62SDavid Matlack 
5115df9bd62SDavid Matlack 		return &iommu_modes[i];
5125df9bd62SDavid Matlack 	}
5135df9bd62SDavid Matlack 
5145df9bd62SDavid Matlack 	VFIO_FAIL("Unrecognized IOMMU mode: %s\n", iommu_mode);
5155df9bd62SDavid Matlack }
5165df9bd62SDavid Matlack 
vfio_device_bind_iommufd(int device_fd,int iommufd)51761cbfe50SDavid Matlack static void vfio_device_bind_iommufd(int device_fd, int iommufd)
51861cbfe50SDavid Matlack {
51961cbfe50SDavid Matlack 	struct vfio_device_bind_iommufd args = {
52061cbfe50SDavid Matlack 		.argsz = sizeof(args),
52161cbfe50SDavid Matlack 		.iommufd = iommufd,
52261cbfe50SDavid Matlack 	};
52361cbfe50SDavid Matlack 
52461cbfe50SDavid Matlack 	ioctl_assert(device_fd, VFIO_DEVICE_BIND_IOMMUFD, &args);
52561cbfe50SDavid Matlack }
52661cbfe50SDavid Matlack 
iommufd_ioas_alloc(int iommufd)52761cbfe50SDavid Matlack static u32 iommufd_ioas_alloc(int iommufd)
52861cbfe50SDavid Matlack {
52961cbfe50SDavid Matlack 	struct iommu_ioas_alloc args = {
53061cbfe50SDavid Matlack 		.size = sizeof(args),
53161cbfe50SDavid Matlack 	};
53261cbfe50SDavid Matlack 
53361cbfe50SDavid Matlack 	ioctl_assert(iommufd, IOMMU_IOAS_ALLOC, &args);
53461cbfe50SDavid Matlack 	return args.out_ioas_id;
53561cbfe50SDavid Matlack }
53661cbfe50SDavid Matlack 
vfio_device_attach_iommufd_pt(int device_fd,u32 pt_id)53761cbfe50SDavid Matlack static void vfio_device_attach_iommufd_pt(int device_fd, u32 pt_id)
53861cbfe50SDavid Matlack {
53961cbfe50SDavid Matlack 	struct vfio_device_attach_iommufd_pt args = {
54061cbfe50SDavid Matlack 		.argsz = sizeof(args),
54161cbfe50SDavid Matlack 		.pt_id = pt_id,
54261cbfe50SDavid Matlack 	};
54361cbfe50SDavid Matlack 
54461cbfe50SDavid Matlack 	ioctl_assert(device_fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &args);
54561cbfe50SDavid Matlack }
54661cbfe50SDavid Matlack 
vfio_pci_iommufd_setup(struct vfio_pci_device * device,const char * bdf)54761cbfe50SDavid Matlack static void vfio_pci_iommufd_setup(struct vfio_pci_device *device, const char *bdf)
54861cbfe50SDavid Matlack {
54961cbfe50SDavid Matlack 	const char *cdev_path = vfio_pci_get_cdev_path(bdf);
55061cbfe50SDavid Matlack 
55161cbfe50SDavid Matlack 	device->fd = open(cdev_path, O_RDWR);
55261cbfe50SDavid Matlack 	VFIO_ASSERT_GE(device->fd, 0);
55361cbfe50SDavid Matlack 	free((void *)cdev_path);
55461cbfe50SDavid Matlack 
55561cbfe50SDavid Matlack 	/*
55661cbfe50SDavid Matlack 	 * Require device->iommufd to be >0 so that a simple non-0 check can be
55761cbfe50SDavid Matlack 	 * used to check if iommufd is enabled. In practice open() will never
55861cbfe50SDavid Matlack 	 * return 0 unless stdin is closed.
55961cbfe50SDavid Matlack 	 */
56061cbfe50SDavid Matlack 	device->iommufd = open("/dev/iommu", O_RDWR);
56161cbfe50SDavid Matlack 	VFIO_ASSERT_GT(device->iommufd, 0);
56261cbfe50SDavid Matlack 
56361cbfe50SDavid Matlack 	vfio_device_bind_iommufd(device->fd, device->iommufd);
56461cbfe50SDavid Matlack 	device->ioas_id = iommufd_ioas_alloc(device->iommufd);
56561cbfe50SDavid Matlack 	vfio_device_attach_iommufd_pt(device->fd, device->ioas_id);
56661cbfe50SDavid Matlack }
56761cbfe50SDavid Matlack 
vfio_pci_device_init(const char * bdf,const char * iommu_mode)5685df9bd62SDavid Matlack struct vfio_pci_device *vfio_pci_device_init(const char *bdf, const char *iommu_mode)
56919faf6fdSDavid Matlack {
57019faf6fdSDavid Matlack 	struct vfio_pci_device *device;
57119faf6fdSDavid Matlack 
57219faf6fdSDavid Matlack 	device = calloc(1, sizeof(*device));
57319faf6fdSDavid Matlack 	VFIO_ASSERT_NOT_NULL(device);
57419faf6fdSDavid Matlack 
57561cbfe50SDavid Matlack 	INIT_LIST_HEAD(&device->dma_regions);
57661cbfe50SDavid Matlack 
5775df9bd62SDavid Matlack 	device->iommu_mode = lookup_iommu_mode(iommu_mode);
5785df9bd62SDavid Matlack 
57961cbfe50SDavid Matlack 	if (device->iommu_mode->container_path)
58061cbfe50SDavid Matlack 		vfio_pci_container_setup(device, bdf);
58161cbfe50SDavid Matlack 	else
58261cbfe50SDavid Matlack 		vfio_pci_iommufd_setup(device, bdf);
58319faf6fdSDavid Matlack 
58461cbfe50SDavid Matlack 	vfio_pci_device_setup(device);
5851b197032SDavid Matlack 	vfio_pci_driver_probe(device);
5861b197032SDavid Matlack 
58719faf6fdSDavid Matlack 	return device;
58819faf6fdSDavid Matlack }
58919faf6fdSDavid Matlack 
vfio_pci_device_cleanup(struct vfio_pci_device * device)59019faf6fdSDavid Matlack void vfio_pci_device_cleanup(struct vfio_pci_device *device)
59119faf6fdSDavid Matlack {
59219faf6fdSDavid Matlack 	int i;
59319faf6fdSDavid Matlack 
5941b197032SDavid Matlack 	if (device->driver.initialized)
5951b197032SDavid Matlack 		vfio_pci_driver_remove(device);
5961b197032SDavid Matlack 
59719faf6fdSDavid Matlack 	vfio_pci_bar_unmap_all(device);
59819faf6fdSDavid Matlack 
59919faf6fdSDavid Matlack 	VFIO_ASSERT_EQ(close(device->fd), 0);
60019faf6fdSDavid Matlack 
60119faf6fdSDavid Matlack 	for (i = 0; i < ARRAY_SIZE(device->msi_eventfds); i++) {
60219faf6fdSDavid Matlack 		if (device->msi_eventfds[i] < 0)
60319faf6fdSDavid Matlack 			continue;
60419faf6fdSDavid Matlack 
60519faf6fdSDavid Matlack 		VFIO_ASSERT_EQ(close(device->msi_eventfds[i]), 0);
60619faf6fdSDavid Matlack 	}
60719faf6fdSDavid Matlack 
60861cbfe50SDavid Matlack 	if (device->iommufd) {
60961cbfe50SDavid Matlack 		VFIO_ASSERT_EQ(close(device->iommufd), 0);
61061cbfe50SDavid Matlack 	} else {
61119faf6fdSDavid Matlack 		VFIO_ASSERT_EQ(close(device->group_fd), 0);
61219faf6fdSDavid Matlack 		VFIO_ASSERT_EQ(close(device->container_fd), 0);
61361cbfe50SDavid Matlack 	}
61419faf6fdSDavid Matlack 
61519faf6fdSDavid Matlack 	free(device);
61619faf6fdSDavid Matlack }
61719faf6fdSDavid Matlack 
is_bdf(const char * str)61819faf6fdSDavid Matlack static bool is_bdf(const char *str)
61919faf6fdSDavid Matlack {
62019faf6fdSDavid Matlack 	unsigned int s, b, d, f;
62119faf6fdSDavid Matlack 	int length, count;
62219faf6fdSDavid Matlack 
62319faf6fdSDavid Matlack 	count = sscanf(str, "%4x:%2x:%2x.%2x%n", &s, &b, &d, &f, &length);
62419faf6fdSDavid Matlack 	return count == 4 && length == strlen(str);
62519faf6fdSDavid Matlack }
62619faf6fdSDavid Matlack 
vfio_selftests_get_bdf(int * argc,char * argv[])62719faf6fdSDavid Matlack const char *vfio_selftests_get_bdf(int *argc, char *argv[])
62819faf6fdSDavid Matlack {
62919faf6fdSDavid Matlack 	char *bdf;
63019faf6fdSDavid Matlack 
63119faf6fdSDavid Matlack 	if (*argc > 1 && is_bdf(argv[*argc - 1]))
63219faf6fdSDavid Matlack 		return argv[--(*argc)];
63319faf6fdSDavid Matlack 
63419faf6fdSDavid Matlack 	bdf = getenv("VFIO_SELFTESTS_BDF");
63519faf6fdSDavid Matlack 	if (bdf) {
63619faf6fdSDavid Matlack 		VFIO_ASSERT_TRUE(is_bdf(bdf), "Invalid BDF: %s\n", bdf);
63719faf6fdSDavid Matlack 		return bdf;
63819faf6fdSDavid Matlack 	}
63919faf6fdSDavid Matlack 
64019faf6fdSDavid Matlack 	fprintf(stderr, "Unable to determine which device to use, skipping test.\n");
64119faf6fdSDavid Matlack 	fprintf(stderr, "\n");
64219faf6fdSDavid Matlack 	fprintf(stderr, "To pass the device address via environment variable:\n");
64319faf6fdSDavid Matlack 	fprintf(stderr, "\n");
64419faf6fdSDavid Matlack 	fprintf(stderr, "    export VFIO_SELFTESTS_BDF=segment:bus:device.function\n");
64519faf6fdSDavid Matlack 	fprintf(stderr, "    %s [options]\n", argv[0]);
64619faf6fdSDavid Matlack 	fprintf(stderr, "\n");
64719faf6fdSDavid Matlack 	fprintf(stderr, "To pass the device address via argv:\n");
64819faf6fdSDavid Matlack 	fprintf(stderr, "\n");
64919faf6fdSDavid Matlack 	fprintf(stderr, "    %s [options] segment:bus:device.function\n", argv[0]);
65019faf6fdSDavid Matlack 	fprintf(stderr, "\n");
65119faf6fdSDavid Matlack 	exit(KSFT_SKIP);
65219faf6fdSDavid Matlack }
653