├── e324fc82ea45 ├── Makefile └── reproducer.c ├── intx-race ├── Makefile ├── vfio-kprobe-unmask-always-injects.c └── vfio-pci-intx-race.c ├── .gitignore ├── run-test.sh ├── utils.h ├── Makefile ├── vfio-noiommu-pci-device-open.c ├── vfio-pci-device-open.c ├── vfio-pci-device-migration.c ├── vfio-pci-hot-reset.c ├── vfio-iommu-map-unmap.c ├── leaktest-legacy-kvm.c ├── vfio-pci-huge-fault-race.c ├── vfio-pci-device-open-sparse-mmap.c ├── vfio-huge-guest-test.c ├── vfio-pci-device-dma-map.c ├── vfio-pci-device-open-igd.c ├── vfio-iommu-stress-test.c ├── kvm-huge-guest-test.c ├── accounting-stress └── accounting-stress.c ├── utils.c ├── iommufd-pci-device-open.c └── vfio-correctness-tests.c /e324fc82ea45/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | $(CC) -o reproducer reproducer.c 3 | 4 | clean: 5 | rm -f reproducer 6 | -------------------------------------------------------------------------------- /intx-race/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := vfio-kprobe-unmask-always-injects.o 2 | 3 | KDIR := /lib/modules/$(shell uname -r)/build 4 | PWD := $(shell pwd) 5 | 6 | default: 7 | $(MAKE) -C $(KDIR) M=$(PWD) modules 8 | $(CC) -o vfio-pci-intx-race vfio-pci-intx-race.c 9 | 10 | clean: 11 | $(MAKE) -C $(KDIR) M=$(PWD) clean 12 | rm -f vfio-pci-intx-race 13 | 14 | modules_install: 15 | $(MAKE) -C $(KDIR) M=$(PWD) modules_install 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | vfio-pci-device-dma-map 3 | vfio-pci-huge-fault-race 4 | kvm-huge-guest-test 5 | leaktest-legacy-kvm 6 | vfio-correctness-tests 7 | vfio-huge-guest-test 8 | vfio-iommu-map-unmap 9 | vfio-iommu-stress-test 10 | vfio-noiommu-pci-device-open 11 | vfio-pci-device-open 12 | vfio-pci-device-open-igd 13 | vfio-pci-device-open-sparse-mmap 14 | vfio-pci-hot-reset 15 | iommufd-pci-device-open 16 | vfio-pci-device-migration 17 | .vscode 18 | -------------------------------------------------------------------------------- /run-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | me=${0##*/} 4 | 5 | device=${1:-"0000:08:10.0"} 6 | groupid=$(basename $(readlink /sys/bus/pci/devices/$device/iommu_group)) 7 | 8 | set -x 9 | 10 | ./vfio-correctness-tests $groupid 11 | ./vfio-huge-guest-test $groupid 12 | ./vfio-iommu-map-unmap $device 13 | ./vfio-iommu-stress-test $device 14 | ./vfio-noiommu-pci-device-open $device 15 | ./vfio-pci-device-dma-map $device 16 | ./vfio-pci-device-open $device 17 | ./vfio-pci-device-open-igd $device 18 | ./vfio-pci-device-open-sparse-mmap $device 19 | ./vfio-pci-hot-reset $device 20 | ./vfio-pci-huge-fault-race $device 21 | ./iommufd-pci-device-open $device 22 | ./vfio-pci-device-migration $device 23 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #ifndef VFIO_TESTSUITE_UTILS_H 11 | #define VFIO_TESTSUITE_UTILS_H 12 | 13 | #include 14 | 15 | /* 16 | * Logging 17 | */ 18 | extern int verbose; 19 | 20 | int vfio_group_attach(int groupid, int *container_out, int *group_out); 21 | int vfio_device_attach(const char *devname, int *container_out, 22 | int *device_out, int *group_out); 23 | int vfio_device_attach_iommu_type(const char *devname, int *container_out, 24 | int *device_out, int *group_out, 25 | int iommu_type); 26 | int vfio_device_iommufd_getfd(const char *devname); 27 | 28 | #define NSEC_PER_SEC 1000000000ul 29 | #define USEC_PER_SEC 1000000ul 30 | 31 | static inline unsigned long now_nsec(void) 32 | { 33 | struct timespec ts; 34 | clock_gettime(CLOCK_MONOTONIC_RAW, &ts); 35 | return NSEC_PER_SEC * (unsigned long) ts.tv_sec + ts.tv_nsec; 36 | } 37 | 38 | void *mmap_align(void *addr, size_t length, int prot, int flags, 39 | int fd, off_t offset, size_t align); 40 | 41 | #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 42 | 43 | #endif /* VFIO_TESTSUITE_UTILS_H */ 44 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -g -Wall 2 | SHARED_SRCS = utils.c 3 | HEADERS = utils.h 4 | TEST_SRCS = \ 5 | kvm-huge-guest-test.c \ 6 | leaktest-legacy-kvm.c \ 7 | vfio-correctness-tests.c \ 8 | vfio-huge-guest-test.c \ 9 | vfio-iommu-map-unmap.c \ 10 | vfio-iommu-stress-test.c \ 11 | vfio-noiommu-pci-device-open.c \ 12 | vfio-pci-device-open.c \ 13 | vfio-pci-device-open-igd.c \ 14 | vfio-pci-device-open-sparse-mmap.c \ 15 | vfio-pci-hot-reset.c \ 16 | vfio-pci-device-dma-map.c \ 17 | vfio-pci-huge-fault-race.c \ 18 | iommufd-pci-device-open.c \ 19 | vfio-pci-device-migration.c 20 | 21 | SHARED_OBJS = $(SHARED_SRCS:.c=.o) 22 | TEST_BINS = $(TEST_SRCS:.c=) 23 | ARCHIVE_BASE_NAME = vfio-tests 24 | GIT_SHA := $(shell git rev-parse --short HEAD 2>/dev/null) 25 | GIT_DIRTY := $(shell git diff --quiet 2>/dev/null || echo "-dirty") 26 | ifeq ($(GIT_SHA),) 27 | GIT_VERSION = nogit 28 | else 29 | GIT_VERSION = $(GIT_SHA)$(GIT_DIRTY) 30 | endif 31 | ARCHIVE_NAME = $(ARCHIVE_BASE_NAME)-$(GIT_VERSION) 32 | 33 | .PHONY: all clean archive 34 | 35 | all: $(TEST_BINS) 36 | 37 | $(TEST_BINS): %: %.o $(SHARED_OBJS) 38 | $(CC) $(CFLAGS) -o $@ $^ 39 | 40 | %.o: %.c $(HEADERS) Makefile 41 | $(CC) $(CFLAGS) -c $< -o $@ 42 | 43 | clean: 44 | rm -f $(SHARED_OBJS) $(TEST_SRCS:.c=.o) $(TEST_BINS) $(ARCHIVE_BASE_NAME)*.tar.gz 45 | 46 | DEVICE ?= 47 | check: 48 | ./run-test.sh $(DEVICE) 49 | 50 | archive: 51 | tar -czvf $(ARCHIVE_NAME).tar.gz Makefile $(SHARED_SRCS) $(TEST_SRCS) $(HEADERS) 52 | 53 | .PRECIOUS: $(TEST_BINS) 54 | -------------------------------------------------------------------------------- /intx-race/vfio-kprobe-unmask-always-injects.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #if 0 9 | struct virqfd { 10 | void *opaque; 11 | struct eventfd_ctx *eventfd; 12 | int (*handler)(void *, void *); 13 | void (*thread)(void *, void *); 14 | void *data; 15 | struct work_struct inject; 16 | wait_queue_t wait; 17 | poll_table pt; 18 | struct work_struct shutdown; 19 | struct virqfd **pvirqfd; 20 | }; 21 | #endif 22 | 23 | struct data { 24 | struct virqfd *virqfd; 25 | unsigned long flags; 26 | }; 27 | 28 | static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) 29 | { 30 | struct data *data = (struct data *)ri->data; 31 | wait_queue_t *wait = (wait_queue_t *)regs->di; /* only x86_64 */ 32 | void *key = (void *)regs->cx; /* only x86_64 */ 33 | 34 | data->virqfd = container_of(wait, struct virqfd, wait); 35 | data->flags = (unsigned long)key; 36 | 37 | return 0; 38 | } 39 | 40 | static int handler(struct kretprobe_instance *ri, struct pt_regs *regs) 41 | { 42 | struct data *data = (struct data *)ri->data; 43 | unsigned long flags = data->flags; 44 | struct virqfd *virqfd = data->virqfd; 45 | 46 | if ((flags & POLLIN) && virqfd->thread) 47 | schedule_work(&virqfd->inject); 48 | 49 | return 0; 50 | } 51 | 52 | static struct kretprobe kretprobe = { 53 | .handler = handler, 54 | .entry_handler = entry_handler, 55 | .data_size = sizeof(struct data), 56 | .kp = { 57 | .symbol_name = "virqfd_wakeup", 58 | }, 59 | .maxactive = NR_CPUS, 60 | }; 61 | 62 | int __init my_init(void) 63 | { 64 | return register_kretprobe(&kretprobe); 65 | } 66 | 67 | void __exit my_exit(void) 68 | { 69 | unregister_kretprobe(&kretprobe); 70 | } 71 | 72 | module_init(my_init); 73 | module_exit(my_exit); 74 | MODULE_LICENSE("GPL v2"); 75 | -------------------------------------------------------------------------------- /vfio-noiommu-pci-device-open.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | void usage(char *name) 31 | { 32 | printf("usage: %s \n", name); 33 | } 34 | 35 | int main(int argc, char **argv) 36 | { 37 | const char *devname; 38 | int i, container, device; 39 | struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; 40 | struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; 41 | 42 | if (argc < 2) { 43 | usage(argv[0]); 44 | return -1; 45 | } 46 | 47 | devname = argv[1]; 48 | 49 | if (vfio_device_attach_iommu_type(devname, &container, &device, 50 | NULL, VFIO_NOIOMMU_IOMMU)) 51 | return -1; 52 | 53 | 54 | if (ioctl(device, VFIO_DEVICE_GET_INFO, &device_info)) { 55 | printf("Failed to get device info\n"); 56 | return -1; 57 | } 58 | 59 | printf("Device supports %d regions, %d irqs\n", 60 | device_info.num_regions, device_info.num_irqs); 61 | 62 | for (i = 0; i < device_info.num_regions; i++) { 63 | printf("Region %d: ", i); 64 | region_info.index = i; 65 | if (ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®ion_info)) { 66 | printf("Failed to get info\n"); 67 | continue; 68 | } 69 | 70 | printf("size 0x%lx, offset 0x%lx, flags 0x%x\n", 71 | (unsigned long)region_info.size, 72 | (unsigned long)region_info.offset, region_info.flags); 73 | if (0 && region_info.flags & VFIO_REGION_INFO_FLAG_MMAP) { 74 | void *map = mmap(NULL, (size_t)region_info.size, 75 | PROT_READ, MAP_SHARED, device, 76 | (off_t)region_info.offset); 77 | if (map == MAP_FAILED) { 78 | printf("mmap failed\n"); 79 | continue; 80 | } 81 | 82 | printf("["); 83 | fwrite(map, 1, region_info.size > 16 ? 16 : 84 | region_info.size, stdout); 85 | printf("]\n"); 86 | munmap(map, (size_t)region_info.size); 87 | } 88 | } 89 | 90 | printf("Success\n"); 91 | printf("Press any key to exit\n"); 92 | fgetc(stdin); 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /vfio-pci-device-open.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | void usage(char *name) 31 | { 32 | printf("usage: %s \n", name); 33 | } 34 | 35 | int main(int argc, char **argv) 36 | { 37 | const char *devname; 38 | int container, device; 39 | int i; 40 | int ret; 41 | struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; 42 | struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; 43 | 44 | if (argc < 2) { 45 | usage(argv[0]); 46 | return -1; 47 | } 48 | 49 | devname = argv[1]; 50 | 51 | if (vfio_device_attach(devname, &container, &device, NULL)) 52 | return -1; 53 | 54 | ret = ioctl(device, VFIO_DEVICE_GET_INFO, &device_info); 55 | if (ret) { 56 | printf("VFIO_DEVICE_GET_INFO failed: %d (%s)\n", 57 | ret, strerror(errno)); 58 | return -1; 59 | } 60 | 61 | printf("Device %s supports %d regions, %d irqs\n", devname, 62 | device_info.num_regions, device_info.num_irqs); 63 | 64 | for (i = 0; i < device_info.num_regions; i++) { 65 | printf("Region %d: ", i); 66 | region_info.index = i; 67 | if (ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®ion_info)) { 68 | printf("Failed to get info\n"); 69 | continue; 70 | } 71 | 72 | printf("size 0x%lx, offset 0x%lx, flags 0x%x\n", 73 | (unsigned long)region_info.size, 74 | (unsigned long)region_info.offset, region_info.flags); 75 | if (region_info.flags & VFIO_REGION_INFO_FLAG_MMAP) { 76 | void *map = mmap(NULL, (size_t)region_info.size, 77 | PROT_READ, MAP_SHARED, device, 78 | (off_t)region_info.offset); 79 | if (map == MAP_FAILED) { 80 | printf("mmap failed\n"); 81 | continue; 82 | } 83 | 84 | if (verbose) { 85 | printf("["); 86 | fwrite(map, 1, region_info.size > 16 ? 16 : 87 | region_info.size, stdout); 88 | printf("]\n"); 89 | } 90 | munmap(map, (size_t)region_info.size); 91 | } 92 | } 93 | 94 | printf("Success\n"); 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /vfio-pci-device-migration.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "utils.h" 20 | 21 | void usage(char *name) 22 | { 23 | printf("usage: %s \n", name); 24 | } 25 | 26 | static bool vfio_device_dma_logging_supported(int fd) 27 | { 28 | uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature), 29 | sizeof(uint64_t))] = {}; 30 | struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; 31 | 32 | feature->argsz = sizeof(buf); 33 | feature->flags = VFIO_DEVICE_FEATURE_PROBE | 34 | VFIO_DEVICE_FEATURE_DMA_LOGGING_START; 35 | 36 | return !ioctl(fd, VFIO_DEVICE_FEATURE, feature); 37 | } 38 | 39 | static int vfio_device_query_migration_flags(int fd, uint64_t *mig_flags) 40 | { 41 | uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + 42 | sizeof(struct vfio_device_feature_migration), 43 | sizeof(uint64_t))] = {}; 44 | struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; 45 | struct vfio_device_feature_migration *mig = 46 | (struct vfio_device_feature_migration *)feature->data; 47 | 48 | feature->argsz = sizeof(buf); 49 | feature->flags = VFIO_DEVICE_FEATURE_GET | VFIO_DEVICE_FEATURE_MIGRATION; 50 | 51 | if (ioctl(fd, VFIO_DEVICE_FEATURE, feature)) { 52 | if (errno != ENOTTY) { 53 | printf("Failed to query migration features: %d %s\n", 54 | errno, strerror(errno)); 55 | return -1; 56 | } 57 | } 58 | *mig_flags = mig->flags; 59 | return 0; 60 | } 61 | 62 | int main(int argc, char **argv) 63 | { 64 | const char *devname; 65 | int device; 66 | uint64_t mig_flags; 67 | 68 | if (argc < 2) { 69 | usage(argv[0]); 70 | return -1; 71 | } 72 | 73 | devname = argv[1]; 74 | 75 | if (vfio_device_attach(devname, NULL, &device, NULL)) 76 | return -1; 77 | 78 | if (vfio_device_query_migration_flags(device, &mig_flags)) 79 | return -1; 80 | 81 | if (mig_flags) { 82 | printf("Migration features:\n"); 83 | if (mig_flags & VFIO_MIGRATION_STOP_COPY) 84 | printf("\tstop-copy\n"); 85 | if (mig_flags & VFIO_MIGRATION_P2P) 86 | printf("\tp2p\n"); 87 | if (mig_flags & VFIO_MIGRATION_PRE_COPY) 88 | printf("\tpre-copy\n"); 89 | if (vfio_device_dma_logging_supported(device)) 90 | printf("\tdirty-tracking\n"); 91 | } else { 92 | printf("No migration support\n"); 93 | } 94 | 95 | 96 | printf("Success\n"); 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /vfio-pci-hot-reset.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | void usage(char *name) 31 | { 32 | printf("usage: %s \n", name); 33 | } 34 | 35 | #define false 0 36 | #define true 1 37 | 38 | int main(int argc, char **argv) 39 | { 40 | int i, ret, container, group, device, *pfd; 41 | const char *devname; 42 | struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; 43 | struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; 44 | 45 | struct vfio_group_status group_status = { 46 | .argsz = sizeof(group_status) 47 | }; 48 | struct vfio_pci_hot_reset_info *reset_info; 49 | struct vfio_pci_dependent_device *devices; 50 | struct vfio_pci_hot_reset *reset; 51 | 52 | if (argc < 2) { 53 | usage(argv[0]); 54 | return -1; 55 | } 56 | 57 | devname = argv[1]; 58 | 59 | if (vfio_device_attach(devname, &container, &device, &group)) 60 | return -1; 61 | 62 | reset_info = malloc(sizeof(*reset_info)); 63 | if (!reset_info) { 64 | printf("Failed to alloc info struct\n"); 65 | return -ENOMEM; 66 | } 67 | 68 | reset_info->argsz = sizeof(*reset_info); 69 | 70 | ret = ioctl(device, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, reset_info); 71 | if (ret && errno == ENODEV) { 72 | printf("Device does not support hot reset\n"); 73 | return 0; 74 | } 75 | if (!ret || errno != ENOSPC) { 76 | printf("Expected fail/-ENOSPC, got %d/%d\n", ret, -errno); 77 | return -1; 78 | } 79 | 80 | printf("Dependent device count: %d\n", reset_info->count); 81 | 82 | reset_info = realloc(reset_info, sizeof(*reset_info) + 83 | (reset_info->count * sizeof(*devices))); 84 | if (!reset_info) { 85 | printf("Failed to re-alloc info struct\n"); 86 | return -ENOMEM; 87 | } 88 | 89 | reset_info->argsz = sizeof(*reset_info) + 90 | (reset_info->count * sizeof(*devices)); 91 | ret = ioctl(device, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, reset_info); 92 | if (ret) { 93 | printf("Reset Info error\n"); 94 | return ret; 95 | } 96 | 97 | devices = &reset_info->devices[0]; 98 | 99 | for (i = 0; i < reset_info->count; i++) 100 | printf("%d: %04x:%02x:%02x.%d group %d\n", i, 101 | devices[i].segment, devices[i].bus, 102 | devices[i].devfn >> 3, devices[i].devfn & 7, 103 | devices[i].group_id); 104 | 105 | printf("Attempting reset: "); 106 | fflush(stdout); 107 | 108 | reset = malloc(sizeof(*reset) + sizeof(*pfd)); 109 | pfd = &reset->group_fds[0]; 110 | 111 | *pfd = group; 112 | 113 | reset->argsz = sizeof(*reset) + sizeof(*pfd); 114 | reset->count = 1; 115 | reset->flags = 0; 116 | 117 | ret = ioctl(device, VFIO_DEVICE_PCI_HOT_RESET, reset); 118 | printf("%s\n", ret ? "Failed" : "Pass"); 119 | 120 | return ret; 121 | } 122 | -------------------------------------------------------------------------------- /vfio-iommu-map-unmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "utils.h" 17 | 18 | #define MAP_SIZE (1UL * 1024 * 1024 * 1024) 19 | #define MAP_CHUNK (4 * 1024) 20 | #define REALLOC_INTERVAL 30 21 | 22 | void usage(char *name) 23 | { 24 | printf("usage: %s ssss:bb:dd.f\n", name); 25 | printf("\tssss: PCI segment, ex. 0000\n"); 26 | printf("\tbb: PCI bus, ex. 01\n"); 27 | printf("\tdd: PCI device, ex. 06\n"); 28 | printf("\tf: PCI function, ex. 0\n"); 29 | } 30 | 31 | int main(int argc, char **argv) 32 | { 33 | const char *devname; 34 | int ret, container; 35 | unsigned long i, count; 36 | void **maps; 37 | struct vfio_group_status group_status = { 38 | .argsz = sizeof(group_status) 39 | }; 40 | struct vfio_iommu_type1_dma_map dma_map = { 41 | .argsz = sizeof(dma_map) 42 | }; 43 | struct vfio_iommu_type1_dma_unmap dma_unmap = { 44 | .argsz = sizeof(dma_unmap) 45 | }; 46 | 47 | if (argc != 2) { 48 | usage(argv[0]); 49 | return -1; 50 | } 51 | 52 | devname = argv[1]; 53 | 54 | if (vfio_device_attach(devname, &container, NULL, NULL)) 55 | return -1; 56 | 57 | /* Test code */ 58 | dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE; 59 | dma_map.size = MAP_CHUNK; 60 | dma_unmap.size = MAP_SIZE; 61 | dma_unmap.iova = 0; 62 | 63 | /* Track our mmaps for re-use */ 64 | maps = malloc(sizeof(void *) * (MAP_SIZE/dma_map.size)); 65 | if (!maps) { 66 | printf("Failed to allocate map (%s)\n", strerror(errno)); 67 | return -1; 68 | } 69 | 70 | memset(maps, 0, sizeof(void *) * (MAP_SIZE/dma_map.size)); 71 | 72 | for (count = 0;; count++) { 73 | 74 | /* Every REALLOC_INTERVAL, dump our mappings to give THP something to collapse */ 75 | if (count % REALLOC_INTERVAL == 0) { 76 | for (i = 0; i < MAP_SIZE/dma_map.size; i++) { 77 | if (maps[i]) { 78 | munmap(maps[i], dma_map.size); 79 | maps[i] = NULL; 80 | } 81 | } 82 | if (count) { 83 | printf("\t%ld\n", count); 84 | //return 0; 85 | } 86 | printf("|"); 87 | fflush(stdout); 88 | } 89 | 90 | /* Map MAP_CHUNK at a time, each chunk is pinned on map, so THP can't do anything until unmap */ 91 | for (i = dma_map.iova = 0; i < MAP_SIZE/dma_map.size; i++, dma_map.iova += dma_map.size) { 92 | if (!maps[i]) { 93 | maps[i] = mmap(NULL, dma_map.size, 94 | PROT_READ | PROT_WRITE, 95 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 96 | if (maps[i] == MAP_FAILED) { 97 | printf("Failed to mmap memory (%s)\n", strerror(errno)); 98 | return -1; 99 | } 100 | } 101 | 102 | ret = madvise(maps[i], dma_map.size, MADV_HUGEPAGE); 103 | if (ret) { 104 | printf("Madvise failed (%s)\n", strerror(errno)); 105 | } 106 | 107 | dma_map.vaddr = (unsigned long)maps[i]; 108 | 109 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 110 | if (ret) { 111 | printf("Failed to map memory (%s)\n", 112 | strerror(errno)); 113 | return ret; 114 | } 115 | } 116 | 117 | printf("+"); 118 | fflush(stdout); 119 | 120 | /* Unmap everything at once */ 121 | ret = ioctl(container, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 122 | if (ret) { 123 | printf("Failed to unmap memory (%s)\n", strerror(errno)); 124 | return ret; 125 | } 126 | 127 | printf("-"); 128 | fflush(stdout); 129 | } 130 | 131 | return 0; 132 | } 133 | -------------------------------------------------------------------------------- /leaktest-legacy-kvm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | struct kvm_userspace_memory_region { 14 | unsigned int slot; 15 | unsigned int flags; 16 | unsigned long guest_phys_addr; 17 | unsigned long memory_size; 18 | unsigned long userspace_addr; 19 | }; 20 | 21 | struct kvm_assigned_pci_dev { 22 | unsigned int assigned_dev_id; 23 | unsigned int busnr; 24 | unsigned int devfn; 25 | unsigned int flags; 26 | #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) 27 | unsigned int segnr; 28 | union { 29 | unsigned int reserved[11]; 30 | }; 31 | }; 32 | 33 | #define KVMIO 0xAE 34 | 35 | #define KVM_CREATE_VM _IO(KVMIO, 0x01) 36 | 37 | #define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46, \ 38 | struct kvm_userspace_memory_region) 39 | 40 | #define KVM_ASSIGN_PCI_DEVICE _IOR(KVMIO, 0x69, \ 41 | struct kvm_assigned_pci_dev) 42 | 43 | #define KVM_DEASSIGN_PCI_DEVICE _IOW(KVMIO, 0x72, \ 44 | struct kvm_assigned_pci_dev) 45 | 46 | #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) 47 | #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) 48 | #define PCI_FUNC(devfn) ((devfn) & 0x07) 49 | 50 | static unsigned int calc_assigned_dev_id(struct kvm_assigned_pci_dev *dev) 51 | { 52 | return dev->segnr << 16 | dev->busnr << 8 | dev->devfn; 53 | } 54 | 55 | void usage(char *name) 56 | { 57 | printf("usage: %s ssss:bb:dd.f\n", name); 58 | printf("\tssss: PCI segment, ex. 0000\n"); 59 | printf("\tbb: PCI bus, ex. 01\n"); 60 | printf("\tdd: PCI device, ex. 06\n"); 61 | printf("\tf: PCI function, ex. 0\n"); 62 | } 63 | 64 | int main(int argc, char **argv) 65 | { 66 | int kvmfd, vmfd, ret; 67 | int slot, func; 68 | struct kvm_userspace_memory_region mem = { 69 | .slot = 0, 70 | .flags = 0, 71 | .guest_phys_addr = 0, 72 | }; 73 | 74 | struct kvm_assigned_pci_dev dev = { 0 }; 75 | 76 | if (argc != 2) { 77 | usage(argv[0]); 78 | return -1; 79 | } 80 | 81 | ret = sscanf(argv[1], "%04x:%02x:%02x.%d", 82 | &dev.segnr, &dev.busnr, &slot, &func); 83 | if (ret != 4) { 84 | usage(argv[0]); 85 | return -1; 86 | } 87 | 88 | dev.devfn = PCI_DEVFN(slot, func); 89 | dev.assigned_dev_id = calc_assigned_dev_id(&dev); 90 | 91 | kvmfd = open("/dev/kvm", O_RDWR); 92 | if (kvmfd < 0) { 93 | printf("Failed to open /dev/kvm, %d (%s)\n", 94 | ret, strerror(errno)); 95 | return ret; 96 | } 97 | 98 | vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); 99 | if (vmfd < 0) { 100 | printf("Failed to create vm (%s)\n", strerror(errno)); 101 | return vmfd; 102 | } 103 | 104 | mem.userspace_addr = (unsigned long)mmap(0, 1024 * 1024 * 1024, 105 | PROT_READ | PROT_WRITE, 106 | MAP_PRIVATE | MAP_ANONYMOUS, 107 | 0, 0); 108 | if (!mem.userspace_addr) { 109 | printf("Failed to allocate vm memory\n"); 110 | return -1; 111 | } 112 | 113 | mem.memory_size = 1024 * 1024 * 1024; 114 | 115 | ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &mem); 116 | if (ret) { 117 | printf("Failed to add memory (%s)\n", strerror(errno)); 118 | return ret; 119 | } 120 | 121 | dev.flags = KVM_DEV_ASSIGN_ENABLE_IOMMU; 122 | 123 | ret = ioctl(vmfd, KVM_ASSIGN_PCI_DEVICE, &dev); 124 | if (ret) { 125 | printf("failed to assign device %d (%s)\n", ret, 126 | strerror(errno)); 127 | return ret; 128 | } 129 | 130 | mem.memory_size = 0; 131 | 132 | ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &mem); 133 | if (ret) { 134 | printf("Failed to add memory (%s)\n", strerror(errno)); 135 | return ret; 136 | } 137 | 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /e324fc82ea45/reproducer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | 18 | void usage(char *name) 19 | { 20 | printf("usage: %s ssss:bb:dd.f\n", name); 21 | printf("\tssss: PCI segment, ex. 0000\n"); 22 | printf("\tbb: PCI bus, ex. 01\n"); 23 | printf("\tdd: PCI device, ex. 06\n"); 24 | printf("\tf: PCI function, ex. 0\n"); 25 | } 26 | 27 | int main(int argc, char **argv) 28 | { 29 | int seg, bus, slot, func; 30 | int ret, container, group, device, groupid, intx, unmask; 31 | char path[50], iommu_group_path[50], *group_name; 32 | struct stat st; 33 | ssize_t len; 34 | struct vfio_group_status group_status = { 35 | .argsz = sizeof(group_status) 36 | }; 37 | struct vfio_device_info device_info = { 38 | .argsz = sizeof(device_info) 39 | }; 40 | struct vfio_irq_info irq_info = { 41 | .argsz = sizeof(irq_info), 42 | .index = VFIO_PCI_INTX_IRQ_INDEX 43 | }; 44 | struct vfio_irq_set *irq_set; 45 | int32_t *pfd; 46 | 47 | if (argc != 2) { 48 | usage(argv[0]); 49 | return -1; 50 | } 51 | 52 | /* Boilerplate vfio setup */ 53 | ret = sscanf(argv[1], "%04x:%02x:%02x.%d", &seg, &bus, &slot, &func); 54 | if (ret != 4) { 55 | usage(argv[0]); 56 | return -1; 57 | } 58 | 59 | container = open("/dev/vfio/vfio", O_RDWR); 60 | if (container < 0) { 61 | printf("Failed to open /dev/vfio/vfio, %d (%s)\n", 62 | container, strerror(errno)); 63 | return container; 64 | } 65 | 66 | snprintf(path, sizeof(path), 67 | "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", 68 | seg, bus, slot, func); 69 | 70 | ret = stat(path, &st); 71 | if (ret < 0) { 72 | printf("No such device\n"); 73 | return ret; 74 | } 75 | 76 | strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); 77 | 78 | len = readlink(path, iommu_group_path, sizeof(iommu_group_path)); 79 | if (len <= 0) { 80 | printf("No iommu_group for device\n"); 81 | return -1; 82 | } 83 | 84 | iommu_group_path[len] = 0; 85 | group_name = basename(iommu_group_path); 86 | 87 | if (sscanf(group_name, "%d", &groupid) != 1) { 88 | printf("Unknown group\n"); 89 | return -1; 90 | } 91 | 92 | snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); 93 | group = open(path, O_RDWR); 94 | if (group < 0) { 95 | printf("Failed to open %s, %d (%s)\n", 96 | path, group, strerror(errno)); 97 | return group; 98 | } 99 | 100 | ret = ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); 101 | if (ret) { 102 | printf("ioctl(VFIO_GROUP_GET_STATUS) failed\n"); 103 | return ret; 104 | } 105 | 106 | if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) { 107 | printf("Group not viable, are all devices attached to vfio?\n"); 108 | return -1; 109 | } 110 | 111 | ret = ioctl(group, VFIO_GROUP_SET_CONTAINER, &container); 112 | if (ret) { 113 | printf("Failed to set group container\n"); 114 | return ret; 115 | } 116 | 117 | ret = ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU); 118 | if (ret) { 119 | printf("Failed to set IOMMU\n"); 120 | return ret; 121 | } 122 | 123 | close(container); 124 | 125 | //snprintf(path, sizeof(path), "%04x:%02x:%02x.%d", seg, bus, slot, func); 126 | snprintf(path, sizeof(path), "THIS-IS-NOT-THE-RIGHT-NAME"); 127 | 128 | for (;;) { 129 | device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, path); 130 | if (device >= 0) { 131 | printf("Got a device named \"%s\"???\n", path); 132 | break; 133 | } 134 | } 135 | 136 | close(group); 137 | close(device); 138 | 139 | return -1; 140 | } 141 | -------------------------------------------------------------------------------- /vfio-pci-huge-fault-race.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include "utils.h" 30 | 31 | void usage(char *name) 32 | { 33 | printf("usage: %s \n", name); 34 | } 35 | 36 | static int go; 37 | 38 | static void *thread_func(void *map) 39 | { 40 | volatile unsigned long *val = map; 41 | 42 | do {} while (!go); 43 | 44 | (void)*val; 45 | return NULL; 46 | } 47 | 48 | static void do_race(int device, struct vfio_region_info *region, size_t pagesz) 49 | { 50 | int i; 51 | 52 | for (i = 0; i < 100000;) { 53 | pthread_t thread1, thread2; 54 | void *map; 55 | 56 | map = mmap_align(NULL, pagesz, PROT_READ, MAP_SHARED, 57 | device, (off_t)region->offset, pagesz); 58 | if (map == MAP_FAILED) { 59 | printf("mmap failed: %s\n", strerror(errno)); 60 | return; 61 | } 62 | 63 | go = 0; 64 | pthread_create(&thread1, NULL, thread_func, map + pagesz - getpagesize()); 65 | pthread_create(&thread2, NULL, thread_func, map); 66 | go = 1; 67 | pthread_join(thread1, NULL); 68 | pthread_join(thread2, NULL); 69 | 70 | munmap(map, pagesz); 71 | if (!(++i % 10000)) { 72 | printf("."); 73 | fflush(stdout); 74 | } 75 | } 76 | printf(" [DONE]\n"); 77 | } 78 | 79 | int main(int argc, char **argv) 80 | { 81 | const char *devname; 82 | int container, device; 83 | int region, ret; 84 | struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; 85 | struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; 86 | size_t *pgsize, pgsizes[] = { 87 | 2ul * 1024 * 1024, 88 | /* Only PMD generates this issue */ 89 | 1ul * 1024ul * 1024 * 1024, 90 | 0 91 | }; 92 | 93 | if (argc < 2) { 94 | usage(argv[0]); 95 | return -EINVAL; 96 | } 97 | 98 | devname = argv[1]; 99 | 100 | if (vfio_device_attach(devname, &container, &device, NULL)) 101 | return -1; 102 | 103 | ret = ioctl(device, VFIO_DEVICE_GET_INFO, &device_info); 104 | if (ret) { 105 | printf("VFIO_DEVICE_GET_INFO failed: %d (%s)\n", 106 | ret, strerror(errno)); 107 | return errno; 108 | } 109 | 110 | if (!(device_info.flags & VFIO_DEVICE_FLAGS_PCI) || 111 | device_info.num_regions < VFIO_PCI_BAR5_REGION_INDEX) { 112 | printf("Invalid vfio-pci device\n"); 113 | return -ENODEV; 114 | } 115 | 116 | printf("Running tests, if progress dots stop or system generates errors, the test has failed\n"); 117 | 118 | for (pgsize = &pgsizes[0]; *pgsize; pgsize++) { 119 | for (region = 0; region < VFIO_PCI_ROM_REGION_INDEX; region++) { 120 | region_info.index = region; 121 | ret = ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®ion_info); 122 | if (ret) { 123 | printf("VFIO_DEVICE_GET_REGION_INFO failed for region %d: %d (%s)\n", 124 | region_info.index, ret, strerror(errno)); 125 | return errno; 126 | } 127 | 128 | if (!(region_info.flags & VFIO_REGION_INFO_FLAG_MMAP) || 129 | region_info.size < *pgsize) 130 | continue; 131 | 132 | printf("Using BAR%d (size %ldMB) for %ldMB page size test\n", region, 133 | (unsigned long)region_info.size >> 20, *pgsize >> 20); 134 | 135 | do_race(device, ®ion_info, *pgsize); 136 | break; 137 | } 138 | 139 | if (region == VFIO_PCI_ROM_REGION_INDEX) { 140 | printf("No BAR found for %ldMB test\n", *pgsize >> 20); 141 | return -ENODEV; 142 | } 143 | } 144 | 145 | printf("Check dmesg, if there are any VM_FAULT_OOM messages, the test has failed\n"); 146 | 147 | return 0; 148 | } 149 | -------------------------------------------------------------------------------- /vfio-pci-device-open-sparse-mmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | void usage(char *name) 31 | { 32 | printf("usage: %s \n", name); 33 | } 34 | 35 | int main(int argc, char **argv) 36 | { 37 | const char *devname; 38 | int container, device; 39 | int i; 40 | int ret; 41 | struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; 42 | struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; 43 | 44 | if (argc < 2) { 45 | usage(argv[0]); 46 | return -1; 47 | } 48 | 49 | devname = argv[1]; 50 | 51 | if (vfio_device_attach(devname, &container, &device, NULL)) 52 | return -1; 53 | 54 | ret = ioctl(device, VFIO_DEVICE_GET_INFO, &device_info); 55 | if (ret) { 56 | printf("VFIO_DEVICE_GET_INFO failed: %d (%s)\n", 57 | ret, strerror(errno)); 58 | return -1; 59 | } 60 | 61 | printf("Device %s supports %d regions, %d irqs\n", devname, 62 | device_info.num_regions, device_info.num_irqs); 63 | 64 | for (i = 0; i < device_info.num_regions; i++) { 65 | struct vfio_region_info *info; 66 | 67 | info = ®ion_info; 68 | printf("Region %d: ", i); 69 | region_info.index = i; 70 | if (ioctl(device, VFIO_DEVICE_GET_REGION_INFO, info)) { 71 | printf("Failed to get info\n"); 72 | continue; 73 | } 74 | 75 | if (info->flags & VFIO_REGION_INFO_FLAG_CAPS) { 76 | info = calloc(info->argsz, 1); 77 | if (!info) { 78 | printf("Failed to alloc larger info\n"); 79 | info = ®ion_info; 80 | } else { 81 | memcpy(info, ®ion_info, sizeof(region_info)); 82 | if (ioctl(device, 83 | VFIO_DEVICE_GET_REGION_INFO, info)) { 84 | printf("Failed to re-get info\n"); 85 | continue; 86 | } 87 | } 88 | } 89 | 90 | printf("size 0x%lx, offset 0x%lx, flags 0x%x\n", 91 | (unsigned long)info->size, 92 | (unsigned long)info->offset, info->flags); 93 | if (0 && info->flags & VFIO_REGION_INFO_FLAG_MMAP) { 94 | void *map = mmap(NULL, (size_t)region_info.size, 95 | PROT_READ, MAP_SHARED, device, 96 | (off_t)region_info.offset); 97 | if (map == MAP_FAILED) { 98 | printf("mmap failed\n"); 99 | continue; 100 | } 101 | 102 | printf("["); 103 | fwrite(map, 1, region_info.size > 16 ? 16 : 104 | region_info.size, stdout); 105 | printf("]\n"); 106 | munmap(map, (size_t)region_info.size); 107 | } 108 | 109 | if (info->flags & VFIO_REGION_INFO_FLAG_CAPS) { 110 | struct vfio_info_cap_header *header; 111 | 112 | header = (void *)info + info->cap_offset; 113 | 114 | next: 115 | printf("\tCapability @%d: ID %d, version %d, next %d\n", 116 | info->cap_offset, header->id, header->version, 117 | header->next); 118 | 119 | if (header->id == VFIO_REGION_INFO_CAP_SPARSE_MMAP) { 120 | struct vfio_region_info_cap_sparse_mmap *sparse; 121 | int i; 122 | 123 | sparse = (void *)header; 124 | 125 | printf("\t\tsparse mmap cap, nr_areas %d\n", 126 | sparse->nr_areas); 127 | 128 | for (i = 0; i < sparse->nr_areas; i++) 129 | printf("\t\t\t%d: %llx-%llx\n", i, 130 | sparse->areas[i].offset, 131 | sparse->areas[i].offset + 132 | sparse->areas[i].size); 133 | } 134 | 135 | if (header->next) { 136 | header = (void *)info + header->next; 137 | goto next; 138 | } 139 | } 140 | 141 | if (info != ®ion_info) 142 | free(info); 143 | } 144 | 145 | printf("Success\n"); 146 | //printf("Press any key to exit\n"); 147 | //fgetc(stdin); 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /vfio-huge-guest-test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | #define MMAP_GB (4UL) 31 | #define MMAP_SIZE (MMAP_GB * 1024 * 1024 * 1024) 32 | #define GUEST_GB (1024UL) 33 | 34 | void usage(char *name) 35 | { 36 | printf("usage: %s [hugepage path]\n", name); 37 | } 38 | 39 | int main(int argc, char **argv) 40 | { 41 | int ret, container, groupid, fd = -1; 42 | char path[PATH_MAX], mempath[PATH_MAX] = ""; 43 | unsigned long vaddr; 44 | struct vfio_group_status group_status = { 45 | .argsz = sizeof(group_status) 46 | }; 47 | struct vfio_iommu_type1_dma_map dma_map = { 48 | .argsz = sizeof(dma_map) 49 | }; 50 | 51 | if (argc < 2) { 52 | usage(argv[0]); 53 | return -1; 54 | } 55 | 56 | ret = sscanf(argv[1], "%d", &groupid); 57 | if (ret != 1) { 58 | usage(argv[0]); 59 | return -1; 60 | } 61 | 62 | if (vfio_group_attach(groupid, &container, NULL)) 63 | return -1; 64 | 65 | if (argc > 2) { 66 | ret = sscanf(argv[2], "%s", mempath); 67 | if (ret != 1) { 68 | usage(argv[0]); 69 | return -1; 70 | } 71 | } 72 | 73 | if (strlen(mempath)) { 74 | struct statfs fs; 75 | 76 | do { 77 | ret = statfs(mempath, &fs); 78 | } while (ret != 0 && errno == EINTR); 79 | 80 | if (ret) { 81 | printf("Can't statfs on %s\n", mempath); 82 | } else 83 | printf("Using %ldK huge page size\n", fs.f_bsize >> 10); 84 | 85 | sprintf(path, "%s/%s.XXXXXX", mempath, basename(argv[0])); 86 | fd = mkstemp(path); 87 | if (fd < 0) 88 | printf("Failed to open mempath file %s (%s)\n", 89 | path, strerror(errno)); 90 | } 91 | 92 | /* 4G of host memory */ 93 | if (fd < 0) { 94 | vaddr = (unsigned long)mmap(0, MMAP_SIZE, 95 | PROT_READ | PROT_WRITE, 96 | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 97 | } else { 98 | ftruncate(fd, MMAP_SIZE); 99 | vaddr = (unsigned long)mmap(0, MMAP_SIZE, 100 | PROT_READ | PROT_WRITE, 101 | MAP_POPULATE | MAP_SHARED, fd, 0); 102 | } 103 | 104 | if ((void *)vaddr == MAP_FAILED) { 105 | printf("Failed to allocate memory\n"); 106 | return -1; 107 | } 108 | 109 | dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE; 110 | 111 | /* 640K@0, enough for anyone */ 112 | printf("Mapping 0-640K"); 113 | fflush(stdout); 114 | dma_map.vaddr = vaddr; 115 | dma_map.size = 640 * 1024; 116 | dma_map.iova = 0; 117 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 118 | if (ret) { 119 | printf("Failed to map memory (%s)\n", strerror(errno)); 120 | return ret; 121 | } 122 | printf(".\n"); 123 | 124 | /* (3G - 1M)@1M "low memory" */ 125 | printf("Mapping low memory"); 126 | fflush(stdout); 127 | dma_map.size = (3UL * 1024 * 1024 * 1024) - (1024 * 1024); 128 | dma_map.iova = 1024 * 1024; 129 | dma_map.vaddr = vaddr + dma_map.iova; 130 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 131 | if (ret) { 132 | printf("Failed to map memory (%s)\n", strerror(errno)); 133 | return ret; 134 | } 135 | printf(".\n"); 136 | 137 | /* (1TB - 4G)@4G "high memory" after the I/O hole */ 138 | printf("Mapping high memory"); 139 | fflush(stdout); 140 | dma_map.size = MMAP_SIZE; 141 | dma_map.iova = 4UL * 1024 * 1024 * 1024; 142 | dma_map.vaddr = vaddr; 143 | while (dma_map.iova < GUEST_GB * 1024 * 1024 * 1024) { 144 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 145 | if (ret) { 146 | printf("Failed to map memory (%s)\n", strerror(errno)); 147 | return ret; 148 | } 149 | printf("."); 150 | fflush(stdout); 151 | dma_map.iova += MMAP_SIZE; 152 | } 153 | printf("\n"); 154 | 155 | if (fd >= 0) 156 | unlink(path); 157 | 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /vfio-pci-device-dma-map.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | void usage(char *name) 31 | { 32 | printf("usage: %s \n", name); 33 | } 34 | 35 | #define HIGH_MEM (4ul * 1024 * 1024 * 1024) 36 | 37 | static void do_map_unmap(int container, int device, 38 | struct vfio_region_info *region, 39 | unsigned long iova_base, 40 | unsigned long dma_size) 41 | { 42 | struct vfio_iommu_type1_dma_map dma_map = { 0 }; 43 | struct vfio_iommu_type1_dma_unmap dma_unmap = { 0 }; 44 | unsigned long before, after; 45 | int ret; 46 | 47 | void *map = mmap_align(NULL, (size_t)region->size, PROT_READ, MAP_SHARED, device, 48 | (off_t)region->offset, dma_size); 49 | if (map == MAP_FAILED) { 50 | printf("mmap failed: %s\n", strerror(errno)); 51 | return; 52 | } 53 | 54 | dma_map.argsz = sizeof(dma_map); 55 | dma_map.vaddr = (__u64)map; 56 | dma_map.size = dma_size; 57 | dma_map.iova = iova_base; 58 | dma_map.flags = VFIO_DMA_MAP_FLAG_READ; 59 | 60 | before = now_nsec(); 61 | while (dma_map.iova < iova_base + region->size) { 62 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 63 | if (ret) { 64 | printf("VFIO_IOMMU_MAP_DMA failed at 0x%llx: %d (%s)\n", 65 | dma_map.iova, ret, strerror(errno)); 66 | goto unmap; 67 | } 68 | dma_map.iova += dma_size; 69 | } 70 | after = now_nsec(); 71 | 72 | printf("\tdma size %9ldK mmapped in %3ld.%03lds\n", dma_size / 1024, 73 | (after - before) / NSEC_PER_SEC, 74 | ((after - before) % NSEC_PER_SEC) / USEC_PER_SEC); 75 | 76 | unmap: 77 | dma_unmap.argsz = sizeof(dma_unmap); 78 | dma_unmap.iova = iova_base; 79 | dma_unmap.size = region->size; 80 | ret = ioctl(container, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 81 | if (ret) { 82 | printf("VFIO_IOMMU_UNMAP_DMA failed at 0x%llx: %d (%s)\n", 83 | dma_unmap.iova, ret, strerror(errno)); 84 | } 85 | 86 | munmap(map, (size_t)region->size); 87 | } 88 | 89 | int main(int argc, char **argv) 90 | { 91 | const char *devname; 92 | int container, device; 93 | int i; 94 | int ret; 95 | struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; 96 | struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; 97 | 98 | if (argc < 2) { 99 | usage(argv[0]); 100 | return -1; 101 | } 102 | 103 | devname = argv[1]; 104 | 105 | if (vfio_device_attach(devname, &container, &device, NULL)) 106 | return -1; 107 | 108 | ret = ioctl(device, VFIO_DEVICE_GET_INFO, &device_info); 109 | if (ret) { 110 | printf("VFIO_DEVICE_GET_INFO failed: %d (%s)\n", 111 | ret, strerror(errno)); 112 | return -1; 113 | } 114 | 115 | printf("Device %s supports %d regions, %d irqs\n", devname, 116 | device_info.num_regions, device_info.num_irqs); 117 | 118 | for (i = 0; i < device_info.num_regions; i++) { 119 | printf("Region %d: ", i); 120 | region_info.index = i; 121 | ret = ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®ion_info); 122 | if (ret) { 123 | printf("VFIO_DEVICE_GET_REGION_INFO failed for region %d: %d (%s)\n", 124 | region_info.index, ret, strerror(errno)); 125 | continue; 126 | } 127 | 128 | printf("size 0x%lx, offset 0x%lx, flags 0x%x\n", 129 | (unsigned long)region_info.size, 130 | (unsigned long)region_info.offset, region_info.flags); 131 | 132 | if (region_info.flags & VFIO_REGION_INFO_FLAG_MMAP) { 133 | unsigned long dma_sizes[] = { 134 | 2ul * 1024 * 1024, 135 | 1ul * 1024ul * 1024 * 1024, 136 | region_info.size, 137 | 0 138 | }; 139 | 140 | for (int j = 0; dma_sizes[j]; j++) { 141 | if (dma_sizes[j] > region_info.size) 142 | continue; 143 | do_map_unmap(container, device, ®ion_info, 144 | HIGH_MEM, dma_sizes[j]); 145 | } 146 | } 147 | } 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /vfio-pci-device-open-igd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | void usage(char *name) 31 | { 32 | printf("usage: %s \n", name); 33 | } 34 | 35 | int main(int argc, char **argv) 36 | { 37 | const char *devname; 38 | int container, device; 39 | int i; 40 | int ret; 41 | struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; 42 | struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; 43 | 44 | if (argc < 2) { 45 | usage(argv[0]); 46 | return -1; 47 | } 48 | 49 | devname = argv[1]; 50 | 51 | if (vfio_device_attach(devname, &container, &device, NULL)) 52 | return -1; 53 | 54 | ret = ioctl(device, VFIO_DEVICE_GET_INFO, &device_info); 55 | if (ret) { 56 | printf("VFIO_DEVICE_GET_INFO failed: %d (%s)\n", 57 | ret, strerror(errno)); 58 | return -1; 59 | } 60 | 61 | printf("Device %s supports %d regions, %d irqs\n", devname, 62 | device_info.num_regions, device_info.num_irqs); 63 | 64 | for (i = 0; i < device_info.num_regions; i++) { 65 | unsigned long config_offset; 66 | char sig[17]; 67 | unsigned size, tmp; 68 | 69 | printf("Region %d: ", i); 70 | region_info.index = i; 71 | if (ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®ion_info)) { 72 | printf("Failed to get info\n"); 73 | continue; 74 | } 75 | 76 | printf("size 0x%lx, offset 0x%lx, flags 0x%x\n", 77 | (unsigned long)region_info.size, 78 | (unsigned long)region_info.offset, region_info.flags); 79 | 80 | if (i == VFIO_PCI_CONFIG_REGION_INDEX) 81 | config_offset = region_info.offset; 82 | 83 | if (region_info.flags & VFIO_REGION_INFO_FLAG_CAPS) { 84 | struct vfio_region_info *region; 85 | void *buf; 86 | struct vfio_info_cap_header *header; 87 | struct vfio_region_info_cap_type *type; 88 | 89 | printf("Found caps flag, realloc %d bytes\n", 90 | region_info.argsz); 91 | 92 | 93 | buf = region = malloc(region_info.argsz); 94 | if (!region) { 95 | printf("Can't new buffer\n"); 96 | return -1; 97 | } 98 | 99 | region->argsz = region_info.argsz; 100 | region->index = i; 101 | 102 | if (ioctl(device, 103 | VFIO_DEVICE_GET_REGION_INFO, region)) { 104 | 105 | printf("Failed to re-get info\n"); 106 | return -1; 107 | } 108 | 109 | if (region->argsz != region_info.argsz) { 110 | printf("Size changed!?\n"); 111 | return -1; 112 | } 113 | 114 | if (!(region->flags & VFIO_REGION_INFO_FLAG_CAPS)) { 115 | printf("Caps disappeared?!\n"); 116 | return -1; 117 | } 118 | 119 | printf("First cap @%x\n", region->cap_offset); 120 | 121 | header = buf + region->cap_offset; 122 | 123 | printf("header ID %x version %x next %x\n", 124 | header->id, header->version, header->next); 125 | 126 | if (header->id != VFIO_REGION_INFO_CAP_TYPE) { 127 | printf("Unexpected ID, this code doesn't walk the chain\n"); 128 | return -1; 129 | } 130 | 131 | 132 | type = (struct vfio_region_info_cap_type *)header; 133 | 134 | printf("Type %x, sub-type %x\n", 135 | type->type, type->subtype); 136 | 137 | 138 | if (pread(device, sig, 16, region->offset) != 16) { 139 | printf("failed to read signature\n"); 140 | return -1; 141 | } 142 | 143 | sig[16] = 0; 144 | 145 | printf("IGD opregion signature: %s\n", sig); 146 | 147 | if (pread(device, &size, 4, region->offset + 16) != 4) { 148 | printf("failed to read size\n"); 149 | return -1; 150 | } 151 | 152 | printf("IGD opregion size %dKB\n", size); 153 | 154 | if (pread(device, &tmp, 4, config_offset + 0xfc) != 4) { 155 | printf("failed to read config\n"); 156 | return -1; 157 | } 158 | 159 | printf("IGD opregion address: %08x\n", tmp); 160 | 161 | tmp = 0; 162 | 163 | pwrite(device, &tmp, 4, config_offset + 0xfc); 164 | if (pread(device, &tmp, 4, config_offset + 0xfc) != 4) { 165 | printf("failed to re-read config\n"); 166 | return -1; 167 | } 168 | 169 | printf("IGD opregion virt address: %08x\n", tmp); 170 | 171 | free(buf); 172 | } 173 | 174 | region_info.argsz = sizeof(region_info); 175 | } 176 | 177 | printf("Success\n"); 178 | //printf("Press any key to exit\n"); 179 | //fgetc(stdin); 180 | 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /vfio-iommu-stress-test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | #include "utils.h" 25 | 26 | #define MAP_SIZE (1UL * 1024 * 1024 * 1024) 27 | #define MAP_MAX 1024 28 | #define DMA_CHUNK (2UL * 1024 * 1024) 29 | 30 | void usage(char *name) 31 | { 32 | printf("usage: %s ssss:bb:dd.f\n", name); 33 | printf("\tssss: PCI segment, ex. 0000\n"); 34 | printf("\tbb: PCI bus, ex. 01\n"); 35 | printf("\tdd: PCI device, ex. 06\n"); 36 | printf("\tf: PCI function, ex. 0\n"); 37 | } 38 | 39 | int main(int argc, char **argv) 40 | { 41 | const char *devname; 42 | int container; 43 | unsigned long i, j, vaddr; 44 | int ret; 45 | struct vfio_group_status group_status = { 46 | .argsz = sizeof(group_status) 47 | }; 48 | struct vfio_iommu_type1_dma_map dma_map = { 49 | .argsz = sizeof(dma_map) 50 | }; 51 | struct vfio_iommu_type1_dma_unmap dma_unmap = { 52 | .argsz = sizeof(dma_unmap) 53 | }; 54 | 55 | if (argc < 2) { 56 | usage(argv[0]); 57 | return -1; 58 | } 59 | 60 | devname = argv[1]; 61 | 62 | if (vfio_device_attach(devname, &container, NULL, NULL)) 63 | return -1; 64 | 65 | vaddr = (unsigned long)mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, 66 | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 67 | if (!vaddr) { 68 | printf("Failed to allocate memory\n"); 69 | return -1; 70 | } 71 | printf("%lx\n", vaddr); 72 | 73 | dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE; 74 | 75 | printf("Mapping: 0%%"); 76 | fflush(stdout); 77 | for (i = 0; i < MAP_MAX; i++) { 78 | dma_map.size = DMA_CHUNK; 79 | 80 | if (!(i % 3)) 81 | continue; 82 | 83 | for (j = 0; j < MAP_SIZE / DMA_CHUNK; j += 4) { 84 | dma_map.iova = (i * MAP_SIZE) + (j * DMA_CHUNK); 85 | dma_map.vaddr = vaddr + (j * DMA_CHUNK); 86 | 87 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 88 | if (ret) { 89 | printf("Failed to map memory %ld/%ld (%s)\n", 90 | i, j, strerror(errno)); 91 | return ret; 92 | } 93 | } 94 | 95 | #if 1 96 | for (j = 1; j < MAP_SIZE / DMA_CHUNK; j += 4) { 97 | dma_map.iova = (i * MAP_SIZE) + (j * DMA_CHUNK); 98 | dma_map.vaddr = vaddr + (j * DMA_CHUNK); 99 | 100 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 101 | if (ret) { 102 | printf("Failed to map memory %ld/%ld (%s)\n", 103 | i, j, strerror(errno)); 104 | return ret; 105 | } 106 | } 107 | 108 | for (j = 3; j < MAP_SIZE / DMA_CHUNK; j += 4) { 109 | dma_map.iova = (i * MAP_SIZE) + (j * DMA_CHUNK); 110 | dma_map.vaddr = vaddr + (j * DMA_CHUNK); 111 | 112 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 113 | if (ret) { 114 | printf("Failed to map memory %ld/%ld (%s)\n", 115 | i, j, strerror(errno)); 116 | return ret; 117 | } 118 | } 119 | 120 | for (j = 2; j < MAP_SIZE / DMA_CHUNK; j += 4) { 121 | dma_map.iova = (i * MAP_SIZE) + (j * DMA_CHUNK); 122 | dma_map.vaddr = vaddr + (j * DMA_CHUNK); 123 | 124 | ret = ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); 125 | if (ret) { 126 | printf("Failed to map memory %ld/%ld (%s)\n", 127 | i, j, strerror(errno)); 128 | return ret; 129 | } 130 | } 131 | #endif 132 | 133 | if (((i + 1) * 100)/MAP_MAX != (i * 100)/MAP_MAX) { 134 | printf("\b\b\b\b%3ld%%", (i * 100)/MAP_MAX); 135 | fflush(stdout); 136 | } 137 | } 138 | printf("\b\b\b\b100%%\n"); 139 | 140 | printf("Unmapping: 0%%"); 141 | fflush(stdout); 142 | for (i = 0; i < MAP_MAX; i++) { 143 | dma_unmap.size = DMA_CHUNK; 144 | 145 | if (!(i % 3)) 146 | continue; 147 | 148 | for (j = 0; j < MAP_SIZE / DMA_CHUNK / 2; j += 2) { 149 | dma_unmap.iova = (i * MAP_SIZE) + (j * DMA_CHUNK); 150 | 151 | ret = ioctl(container, 152 | VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 153 | if (ret) { 154 | printf("Failed to unmap memory %ld/%ld (%s)\n", 155 | i, j, strerror(errno)); 156 | return ret; 157 | } 158 | } 159 | 160 | #if 1 161 | for (j = (MAP_SIZE / DMA_CHUNK) - 1; 162 | j > MAP_SIZE / DMA_CHUNK / 2; j -= 2) { 163 | dma_unmap.iova = (i * MAP_SIZE) + (j * DMA_CHUNK); 164 | 165 | ret = ioctl(container, 166 | VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 167 | if (ret) { 168 | printf("Failed to unmap memory %ld/%ld (%s)\n", 169 | i, j, strerror(errno)); 170 | return ret; 171 | } 172 | } 173 | #endif 174 | 175 | if (((i + 1) * 100)/MAP_MAX != (i * 100)/MAP_MAX) { 176 | printf("\b\b\b\b%3ld%%", (i * 100)/MAP_MAX); 177 | fflush(stdout); 178 | } 179 | } 180 | printf("\b\b\b\b100%%\n"); 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /kvm-huge-guest-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | struct kvm_userspace_memory_region { 14 | unsigned int slot; 15 | unsigned int flags; 16 | unsigned long guest_phys_addr; 17 | unsigned long memory_size; 18 | unsigned long userspace_addr; 19 | }; 20 | 21 | struct kvm_assigned_pci_dev { 22 | unsigned int assigned_dev_id; 23 | unsigned int busnr; 24 | unsigned int devfn; 25 | unsigned int flags; 26 | #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) 27 | unsigned int segnr; 28 | union { 29 | unsigned int reserved[11]; 30 | }; 31 | }; 32 | 33 | #define KVMIO 0xAE 34 | #define KVM_CAP_NR_MEMSLOTS 10 35 | 36 | #define KVM_CHECK_EXTENSION _IO(KVMIO, 0x03) 37 | 38 | #define KVM_CREATE_VM _IO(KVMIO, 0x01) 39 | 40 | #define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46, \ 41 | struct kvm_userspace_memory_region) 42 | 43 | #define KVM_ASSIGN_PCI_DEVICE _IOR(KVMIO, 0x69, \ 44 | struct kvm_assigned_pci_dev) 45 | 46 | #define KVM_DEASSIGN_PCI_DEVICE _IOW(KVMIO, 0x72, \ 47 | struct kvm_assigned_pci_dev) 48 | 49 | #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) 50 | #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) 51 | #define PCI_FUNC(devfn) ((devfn) & 0x07) 52 | 53 | #define MMAP_GB (4UL) /* >= 4GB */ 54 | #define MMAP_SIZE (MMAP_GB * 1024 * 1024 * 1024) 55 | #define GUEST_GB (1024UL) 56 | 57 | static unsigned int calc_assigned_dev_id(struct kvm_assigned_pci_dev *dev) 58 | { 59 | return dev->segnr << 16 | dev->busnr << 8 | dev->devfn; 60 | } 61 | 62 | void usage(char *name) 63 | { 64 | printf("usage: %s ssss:bb:dd.f\n", name); 65 | printf("\tssss: PCI segment, ex. 0000\n"); 66 | printf("\tbb: PCI bus, ex. 01\n"); 67 | printf("\tdd: PCI device, ex. 06\n"); 68 | printf("\tf: PCI function, ex. 0\n"); 69 | } 70 | 71 | int main(int argc, char **argv) 72 | { 73 | int kvmfd, vmfd, ret; 74 | unsigned long vaddr, i; 75 | int slot, nr_slots, func; 76 | struct kvm_userspace_memory_region mem = { 77 | .flags = 0, 78 | }; 79 | 80 | struct kvm_assigned_pci_dev dev = { 0 }; 81 | 82 | if (argc != 2) { 83 | usage(argv[0]); 84 | return -1; 85 | } 86 | 87 | ret = sscanf(argv[1], "%04x:%02x:%02x.%d", 88 | &dev.segnr, &dev.busnr, &slot, &func); 89 | if (ret != 4) { 90 | usage(argv[0]); 91 | return -1; 92 | } 93 | 94 | dev.devfn = PCI_DEVFN(slot, func); 95 | dev.assigned_dev_id = calc_assigned_dev_id(&dev); 96 | 97 | kvmfd = open("/dev/kvm", O_RDWR); 98 | if (kvmfd < 0) { 99 | printf("Failed to open /dev/kvm, %d (%s)\n", 100 | ret, strerror(errno)); 101 | return ret; 102 | } 103 | 104 | nr_slots = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_NR_MEMSLOTS); 105 | if (nr_slots < 0) { 106 | printf("Failed to get slot count (%s)\n", strerror(errno)); 107 | return nr_slots; 108 | } 109 | 110 | vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); 111 | if (vmfd < 0) { 112 | printf("Failed to create vm (%s)\n", strerror(errno)); 113 | return vmfd; 114 | } 115 | 116 | vaddr = (unsigned long)mmap(0, MMAP_SIZE, PROT_READ | PROT_WRITE, 117 | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 118 | if (!vaddr) { 119 | printf("Failed to allocate vm memory\n"); 120 | return -1; 121 | } 122 | 123 | slot = 0; 124 | 125 | printf("Mapping 0-640K"); 126 | fflush(stdout); 127 | mem.memory_size = 640 * 1024; 128 | mem.guest_phys_addr = 0; 129 | mem.userspace_addr = vaddr; 130 | mem.slot = slot++; 131 | 132 | ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &mem); 133 | if (ret) { 134 | printf("Failed to add memory %d (%s)\n", slot - 1, 135 | strerror(errno)); 136 | return ret; 137 | } 138 | printf(".\n"); 139 | fflush(stdout); 140 | 141 | dev.flags = KVM_DEV_ASSIGN_ENABLE_IOMMU; 142 | 143 | printf("Assigning\n"); 144 | ret = ioctl(vmfd, KVM_ASSIGN_PCI_DEVICE, &dev); 145 | if (ret) { 146 | printf("failed to assign device %d (%s)\n", ret, 147 | strerror(errno)); 148 | return ret; 149 | } 150 | 151 | printf("Mapping low memory"); 152 | fflush(stdout); 153 | mem.memory_size = (4UL * 1024 * 1024 * 1024) - (1024 * 1024) - 154 | (512 * 1024 * 1024); 155 | mem.guest_phys_addr = 1024 * 1024; 156 | mem.userspace_addr = vaddr + mem.guest_phys_addr; 157 | mem.slot = slot++; 158 | 159 | ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &mem); 160 | if (ret) { 161 | printf("Failed to add memory %d (%s)\n", slot - 1, 162 | strerror(errno)); 163 | return ret; 164 | } 165 | printf(".\n"); 166 | fflush(stdout); 167 | 168 | printf("Mapping high memory"); 169 | fflush(stdout); 170 | mem.memory_size = MMAP_SIZE; 171 | mem.guest_phys_addr = 4UL * 1024 * 1024 * 1024; 172 | mem.userspace_addr = vaddr; 173 | i = slot; 174 | while (mem.guest_phys_addr < GUEST_GB * 1024 * 1024 * 1024) { 175 | slot = i; 176 | while (slot < nr_slots && 177 | mem.guest_phys_addr < GUEST_GB * 1024 * 1024 * 1024) { 178 | mem.slot = slot++; 179 | ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &mem); 180 | if (ret) { 181 | printf("Failed to add memory %d (%s)\n", 182 | slot - 1, strerror(errno)); 183 | return ret; 184 | } 185 | printf("."); 186 | fflush(stdout); 187 | mem.guest_phys_addr += MMAP_SIZE; 188 | } 189 | } 190 | printf("\n"); 191 | if (slot == nr_slots) 192 | printf("Out of slots @%ldGB\n", mem.guest_phys_addr >> 30); 193 | 194 | return 0; 195 | } 196 | -------------------------------------------------------------------------------- /accounting-stress/accounting-stress.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #define MAP_SIZE (4 * 1024) 23 | #define MLOCK_SIZE (4 * 1024) 24 | #define STACK_SIZE (1024 * 1024) 25 | 26 | static int stop = 0; 27 | 28 | static int mlock_loop(void *buf) 29 | { 30 | printf("Mlock thread starting\n"); 31 | 32 | while (!stop) { 33 | if (mlock(buf, MLOCK_SIZE)) { 34 | printf("mlock failed: %m\n"); 35 | continue; 36 | } 37 | while (munlock(buf, MLOCK_SIZE)) 38 | printf("munlock failed: %m\n"); 39 | } 40 | 41 | return 0; 42 | } 43 | 44 | static void usage(char *name) 45 | { 46 | printf("usage: %s ssss:bb:dd.f\n", name); 47 | printf("\tssss: PCI segment, ex. 0000\n"); 48 | printf("\tbb: PCI bus, ex. 01\n"); 49 | printf("\tdd: PCI device, ex. 06\n"); 50 | printf("\tf: PCI function, ex. 0\n"); 51 | } 52 | 53 | int main(int argc, char **argv) 54 | { 55 | int seg, bus, slot, func; 56 | int ret, container, group, groupid; 57 | char path[50], iommu_group_path[50], *group_name; 58 | struct stat st; 59 | ssize_t len; 60 | void *map_buf, *mlock_buf, *stack; 61 | pid_t pid; 62 | long i = 0; 63 | 64 | struct vfio_group_status group_status = { 65 | .argsz = sizeof(group_status) 66 | }; 67 | struct vfio_iommu_type1_dma_map dma_map = { 68 | .argsz = sizeof(dma_map), 69 | .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE, 70 | .size = MAP_SIZE, 71 | .iova = 0, 72 | }; 73 | struct vfio_iommu_type1_dma_unmap dma_unmap = { 74 | .argsz = sizeof(dma_unmap), 75 | .size = MAP_SIZE, 76 | .iova = 0, 77 | }; 78 | 79 | if (argc != 2) { 80 | usage(argv[0]); 81 | return -1; 82 | } 83 | 84 | /* Boilerplate vfio setup */ 85 | ret = sscanf(argv[1], "%04x:%02x:%02x.%d", &seg, &bus, &slot, &func); 86 | if (ret != 4) { 87 | usage(argv[0]); 88 | return -1; 89 | } 90 | 91 | container = open("/dev/vfio/vfio", O_RDWR); 92 | if (container < 0) { 93 | printf("Failed to open /dev/vfio/vfio, %d (%s)\n", 94 | container, strerror(errno)); 95 | return container; 96 | } 97 | 98 | snprintf(path, sizeof(path), 99 | "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", 100 | seg, bus, slot, func); 101 | 102 | ret = stat(path, &st); 103 | if (ret < 0) { 104 | printf("No such device\n"); 105 | return ret; 106 | } 107 | 108 | strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); 109 | 110 | len = readlink(path, iommu_group_path, sizeof(iommu_group_path)); 111 | if (len <= 0) { 112 | printf("No iommu_group for device\n"); 113 | return -1; 114 | } 115 | 116 | iommu_group_path[len] = 0; 117 | group_name = basename(iommu_group_path); 118 | 119 | if (sscanf(group_name, "%d", &groupid) != 1) { 120 | printf("Unknown group\n"); 121 | return -1; 122 | } 123 | 124 | snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); 125 | group = open(path, O_RDWR); 126 | if (group < 0) { 127 | printf("Failed to open %s, %d (%s)\n", 128 | path, group, strerror(errno)); 129 | return group; 130 | } 131 | 132 | ret = ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); 133 | if (ret) { 134 | printf("ioctl(VFIO_GROUP_GET_STATUS) failed\n"); 135 | return ret; 136 | } 137 | 138 | if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) { 139 | printf("Group not viable, are all devices attached to vfio?\n"); 140 | return -1; 141 | } 142 | 143 | ret = ioctl(group, VFIO_GROUP_SET_CONTAINER, &container); 144 | if (ret) { 145 | printf("Failed to set group container\n"); 146 | return ret; 147 | } 148 | 149 | ret = ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1v2_IOMMU); 150 | if (ret) { 151 | printf("Failed to set IOMMU\n"); 152 | return ret; 153 | } 154 | 155 | printf("vfio initialized\n"); 156 | 157 | map_buf = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, 158 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 159 | if (map_buf == MAP_FAILED) { 160 | printf("Failed to mmap DMA mapping buffer\n"); 161 | return -1; 162 | } 163 | 164 | dma_map.vaddr = (unsigned long)map_buf; 165 | 166 | printf("DMA buffer allocated\n"); 167 | 168 | mlock_buf = mmap(NULL, MLOCK_SIZE, PROT_READ | PROT_WRITE, 169 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 170 | if (mlock_buf == MAP_FAILED) { 171 | printf("Failed to mmap mlock buffer\n"); 172 | return -1; 173 | } 174 | 175 | printf("mlock buffer allocated\n"); 176 | 177 | stack = malloc(STACK_SIZE); 178 | if (!stack) { 179 | printf("Failed to alloc thread stack\n"); 180 | return -1; 181 | } 182 | 183 | printf("thread stack allocated\n"); 184 | 185 | pid = clone(mlock_loop, 186 | stack + STACK_SIZE, CLONE_VM | SIGCHLD, mlock_buf); 187 | if (pid == -1) { 188 | printf("Failed to clone thread\n"); 189 | return -1; 190 | } 191 | 192 | printf("Main thread commencing DMA mapping loop\n"); 193 | 194 | while (1) { 195 | if (ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map)) { 196 | printf("Failed to map memory (%m)\n"); 197 | break; 198 | } 199 | 200 | if (ioctl(container, VFIO_IOMMU_UNMAP_DMA, &dma_unmap)) { 201 | printf("Failed to unmap memory (%m)\n"); 202 | break; 203 | } 204 | i++; 205 | if (!(i % 10000)) { 206 | printf("."); 207 | fflush(stdout); 208 | } 209 | } 210 | 211 | printf("Iteration count: %ld\n", i); 212 | stop = 1; 213 | waitpid(pid, NULL, 0); 214 | 215 | return 0; 216 | } 217 | -------------------------------------------------------------------------------- /intx-race/vfio-pci-intx-race.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | 18 | void usage(char *name) 19 | { 20 | printf("usage: %s ssss:bb:dd.f\n", name); 21 | printf("\tssss: PCI segment, ex. 0000\n"); 22 | printf("\tbb: PCI bus, ex. 01\n"); 23 | printf("\tdd: PCI device, ex. 06\n"); 24 | printf("\tf: PCI function, ex. 0\n"); 25 | } 26 | 27 | int main(int argc, char **argv) 28 | { 29 | int seg, bus, slot, func; 30 | int ret, container, group, device, groupid, intx, unmask; 31 | char path[50], iommu_group_path[50], *group_name; 32 | struct stat st; 33 | ssize_t len; 34 | struct vfio_group_status group_status = { 35 | .argsz = sizeof(group_status) 36 | }; 37 | struct vfio_device_info device_info = { 38 | .argsz = sizeof(device_info) 39 | }; 40 | struct vfio_irq_info irq_info = { 41 | .argsz = sizeof(irq_info), 42 | .index = VFIO_PCI_INTX_IRQ_INDEX 43 | }; 44 | struct vfio_irq_set *irq_set; 45 | int32_t *pfd; 46 | 47 | if (argc != 2) { 48 | usage(argv[0]); 49 | return -1; 50 | } 51 | 52 | /* Boilerplate vfio setup */ 53 | ret = sscanf(argv[1], "%04x:%02x:%02x.%d", &seg, &bus, &slot, &func); 54 | if (ret != 4) { 55 | usage(argv[0]); 56 | return -1; 57 | } 58 | 59 | container = open("/dev/vfio/vfio", O_RDWR); 60 | if (container < 0) { 61 | printf("Failed to open /dev/vfio/vfio, %d (%s)\n", 62 | container, strerror(errno)); 63 | return container; 64 | } 65 | 66 | snprintf(path, sizeof(path), 67 | "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", 68 | seg, bus, slot, func); 69 | 70 | ret = stat(path, &st); 71 | if (ret < 0) { 72 | printf("No such device\n"); 73 | return ret; 74 | } 75 | 76 | strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); 77 | 78 | len = readlink(path, iommu_group_path, sizeof(iommu_group_path)); 79 | if (len <= 0) { 80 | printf("No iommu_group for device\n"); 81 | return -1; 82 | } 83 | 84 | iommu_group_path[len] = 0; 85 | group_name = basename(iommu_group_path); 86 | 87 | if (sscanf(group_name, "%d", &groupid) != 1) { 88 | printf("Unknown group\n"); 89 | return -1; 90 | } 91 | 92 | snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); 93 | group = open(path, O_RDWR); 94 | if (group < 0) { 95 | printf("Failed to open %s, %d (%s)\n", 96 | path, group, strerror(errno)); 97 | return group; 98 | } 99 | 100 | ret = ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); 101 | if (ret) { 102 | printf("ioctl(VFIO_GROUP_GET_STATUS) failed\n"); 103 | return ret; 104 | } 105 | 106 | if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) { 107 | printf("Group not viable, are all devices attached to vfio?\n"); 108 | return -1; 109 | } 110 | 111 | ret = ioctl(group, VFIO_GROUP_SET_CONTAINER, &container); 112 | if (ret) { 113 | printf("Failed to set group container\n"); 114 | return ret; 115 | } 116 | 117 | ret = ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU); 118 | if (ret) { 119 | printf("Failed to set IOMMU\n"); 120 | return ret; 121 | } 122 | 123 | close(container); 124 | 125 | snprintf(path, sizeof(path), "%04x:%02x:%02x.%d", seg, bus, slot, func); 126 | 127 | device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, path); 128 | if (device < 0) { 129 | printf("Failed to get device %s\n", path); 130 | return -1; 131 | } 132 | 133 | close(group); 134 | 135 | if (ioctl(device, VFIO_DEVICE_GET_INFO, &device_info)) { 136 | printf("Failed to get device info\n"); 137 | return -1; 138 | } 139 | 140 | if (!(device_info.flags & VFIO_DEVICE_FLAGS_PCI)) { 141 | printf("Error, not a PCI device\n"); 142 | return -1; 143 | } 144 | 145 | if (device_info.num_irqs < VFIO_PCI_INTX_IRQ_INDEX + 1) { 146 | printf("Error, device does not support INTx\n"); 147 | return -1; 148 | } 149 | 150 | if (ioctl(device, VFIO_DEVICE_GET_IRQ_INFO, &irq_info)) { 151 | printf("Failed to get IRQ info\n"); 152 | return -1; 153 | } 154 | 155 | if (irq_info.count != 1 || !(irq_info.flags & VFIO_IRQ_INFO_EVENTFD)) { 156 | printf("Unexpected IRQ info properties\n"); 157 | return -1; 158 | } 159 | 160 | irq_set = malloc(sizeof(*irq_set) + sizeof(*pfd)); 161 | if (!irq_set) { 162 | printf("Failed to malloc irq_set\n"); 163 | return -1; 164 | } 165 | 166 | irq_set->argsz = sizeof(*irq_set) + sizeof(*pfd); 167 | irq_set->index = VFIO_PCI_INTX_IRQ_INDEX; 168 | irq_set->start = 0; 169 | pfd = (int32_t *)&irq_set->data; 170 | 171 | intx = eventfd(0, EFD_CLOEXEC); 172 | if (intx < 0) { 173 | printf("Failed to get intx eventfd\n"); 174 | return -1; 175 | } 176 | 177 | unmask = eventfd(0, EFD_CLOEXEC); 178 | if (unmask < 0) { 179 | printf("Failed to get unmask eventfd\n"); 180 | return -1; 181 | } 182 | 183 | if (fork()) { 184 | 185 | printf("Enable/disable thread (%d)...\n", getpid()); 186 | 187 | while (1) { 188 | *pfd = intx; 189 | irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; 190 | irq_set->count = 1; 191 | 192 | if (ioctl(device, VFIO_DEVICE_SET_IRQS, irq_set)) 193 | printf("INTx enable (%m)\n"); 194 | 195 | *pfd = unmask; 196 | irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK; 197 | 198 | if (ioctl(device, VFIO_DEVICE_SET_IRQS, irq_set)) 199 | printf("unmask irqfd (%m)\n"); 200 | 201 | //printf("+"); 202 | //fflush(stdout); 203 | 204 | irq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER; 205 | irq_set->count = 0; 206 | 207 | if (ioctl(device, VFIO_DEVICE_SET_IRQS, irq_set)) 208 | printf("INTx disable (%m)\n"); 209 | 210 | //printf("-"); 211 | //fflush(stdout); 212 | } 213 | } else if (fork()) { 214 | uint64_t buf; 215 | 216 | printf("Consumer thread (%d)...\n", getpid()); 217 | 218 | close(unmask); 219 | 220 | while (read(intx, &buf, sizeof(buf)) == sizeof(buf)) { 221 | //printf("!"); 222 | //fflush(stdout); 223 | } 224 | } else { 225 | uint64_t buf = 1; 226 | 227 | printf("Unmask thread (%d)...\n", getpid()); 228 | 229 | close(intx); 230 | 231 | while (write(unmask, &buf, sizeof(buf)) == sizeof(buf)) { 232 | //printf("#"); 233 | //fflush(stdout); 234 | } 235 | } 236 | 237 | return 0; 238 | } 239 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | int verbose; 28 | 29 | int vfio_device_iommufd_getfd(const char *devname) 30 | { 31 | int domain, bus, dev, func; 32 | char path[PATH_MAX]; 33 | char *vfio_path = NULL; 34 | DIR *dir = NULL; 35 | struct dirent *dent; 36 | int ret; 37 | 38 | ret = sscanf(devname, "%04x:%02x:%02x.%d", &domain, &bus, &dev, &func); 39 | if (ret != 4) { 40 | printf("Invalid PCI device identifier \"%s\"\n", devname); 41 | return -1; 42 | } 43 | 44 | snprintf(path, sizeof(path), 45 | "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/vfio-dev", 46 | domain, bus, dev, func); 47 | 48 | dir = opendir(path); 49 | if (!dir) { 50 | printf("couldn't open directory %s", path); 51 | return -1; 52 | } 53 | 54 | while ((dent = readdir(dir))) { 55 | if (!strncmp(dent->d_name, "vfio", 4)) { 56 | vfio_path = dent->d_name; 57 | break; 58 | } 59 | } 60 | closedir(dir); 61 | 62 | if (!vfio_path) { 63 | printf("failed to find vfio-dev/vfioX"); 64 | return -1; 65 | } 66 | 67 | snprintf(path, sizeof(path), "/dev/vfio/devices/%s", vfio_path); 68 | ret = open(path, O_RDWR); 69 | if (ret < 0) { 70 | printf("Failed to open %s, %d (%s)\n", 71 | path, ret, strerror(errno)); 72 | } 73 | 74 | return ret; 75 | } 76 | 77 | static int vfio_device_get_groupid(const char *devname) 78 | { 79 | int domain, bus, dev, func; 80 | char group_path[PATH_MAX]; 81 | char tmp[PATH_MAX]; 82 | char *group_name = NULL; 83 | int ret, groupid; 84 | ssize_t len; 85 | 86 | ret = sscanf(devname, "%04x:%02x:%02x.%d", &domain, &bus, &dev, &func); 87 | if (ret != 4) { 88 | printf("Invalid PCI device identifier \"%s\"\n", devname); 89 | return -1; 90 | } 91 | 92 | snprintf(tmp, sizeof(tmp), 93 | "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/iommu_group", 94 | domain, bus, dev, func); 95 | 96 | len = readlink(tmp, group_path, sizeof(group_path)); 97 | if (len <= 0 || len >= sizeof(group_path)) { 98 | printf("%s: no iommu_group found\n", devname); 99 | return -1; 100 | } 101 | 102 | group_path[len] = 0; 103 | 104 | group_name = basename(group_path); 105 | if (sscanf(group_name, "%d", &groupid) != 1) { 106 | printf("failed to read %s", group_path); 107 | return -1; 108 | } 109 | 110 | printf("Using device %04x:%02x:%02x.%d in IOMMU group %d\n", 111 | domain, bus, dev, func, groupid); 112 | return groupid; 113 | } 114 | 115 | static int vfio_group_open(int groupid, bool noiommu) 116 | { 117 | int fd; 118 | char path[PATH_MAX]; 119 | int ret; 120 | struct vfio_group_status status = { .argsz = sizeof(status) }; 121 | 122 | snprintf(path, sizeof(path), "/dev/vfio/%s%d", 123 | noiommu ? "noiommu-" : "", groupid); 124 | fd = open(path, O_RDWR); 125 | if (fd < 0) { 126 | printf("Failed to open %s, %d (%s)\n", 127 | path, fd, strerror(errno)); 128 | return -1; 129 | } 130 | 131 | ret = ioctl(fd, VFIO_GROUP_GET_STATUS, &status); 132 | if (ret) { 133 | printf("failed to get group %d status: %d (%s)\n", 134 | groupid, ret, strerror(errno)); 135 | return -1; 136 | } 137 | 138 | if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) { 139 | printf("group %d is not viable", groupid); 140 | return -1; 141 | } 142 | 143 | return fd; 144 | } 145 | 146 | static int vfio_group_set_container(int group, int container, int iommu_type) 147 | { 148 | int ret; 149 | bool noiommu = VFIO_NOIOMMU_IOMMU == iommu_type; 150 | 151 | struct vfio_group_status group_status = { 152 | .argsz = sizeof(group_status) 153 | }; 154 | 155 | ret = ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); 156 | if (ret) { 157 | printf("ioctl(VFIO_GROUP_GET_STATUS) failed: %d (%s)\n", 158 | ret, strerror(errno)); 159 | return ret; 160 | } 161 | 162 | if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) { 163 | printf("Group not viable, are all devices attached to vfio?\n"); 164 | return -1; 165 | } 166 | 167 | if (verbose) { 168 | printf("pre-SET_CONTAINER:\n"); 169 | printf("VFIO_CHECK_EXTENSION VFIO_TYPE1_IOMMU: %sPresent\n", 170 | ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) ? 171 | "" : "Not "); 172 | printf("VFIO_CHECK_EXTENSION VFIO_NOIOMMU_IOMMU: %sPresent\n", 173 | ioctl(container, VFIO_CHECK_EXTENSION, VFIO_NOIOMMU_IOMMU) ? 174 | "" : "Not "); 175 | } 176 | 177 | ret = ioctl(group, VFIO_GROUP_SET_CONTAINER, &container); 178 | if (ret) { 179 | printf("Failed to set group container: %d (%s)\n", ret, strerror(errno)); 180 | return ret; 181 | } 182 | 183 | if (verbose) { 184 | printf("post-SET_CONTAINER:\n"); 185 | printf("VFIO_CHECK_EXTENSION VFIO_TYPE1_IOMMU: %sPresent\n", 186 | ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) ? 187 | "" : "Not "); 188 | printf("VFIO_CHECK_EXTENSION VFIO_NOIOMMU_IOMMU: %sPresent\n", 189 | ioctl(container, VFIO_CHECK_EXTENSION, VFIO_NOIOMMU_IOMMU) ? 190 | "" : "Not "); 191 | } 192 | 193 | ret = ioctl(container, VFIO_SET_IOMMU, noiommu ? 194 | VFIO_TYPE1_IOMMU : VFIO_NOIOMMU_IOMMU); 195 | if (!ret) { 196 | printf("Incorrectly allowed %s-iommu usage!\n", noiommu ? 197 | "no" : "type1"); 198 | return -1; 199 | } 200 | 201 | ret = ioctl(container, VFIO_SET_IOMMU, iommu_type); 202 | if (ret) { 203 | printf("Failed to set IOMMU: %d (%s)\n", ret, strerror(errno)); 204 | 205 | return ret; 206 | } 207 | 208 | return 0; 209 | } 210 | 211 | static int vfio_container_open(void) 212 | { 213 | int fd; 214 | 215 | fd = open("/dev/vfio/vfio", O_RDWR); 216 | if (fd < 0) { 217 | printf("Failed to open /dev/vfio/vfio : %d (%s)\n", 218 | fd, strerror(errno)); 219 | } 220 | return fd; 221 | } 222 | 223 | static int __vfio_group_attach(int groupid, int *container_out, int *group_out, 224 | int iommu_type) 225 | { 226 | int container, group; 227 | bool noiommu = VFIO_NOIOMMU_IOMMU == iommu_type; 228 | 229 | group = vfio_group_open(groupid, noiommu); 230 | if (group < 0) 231 | return -1; 232 | 233 | container = vfio_container_open(); 234 | if (container < 0) 235 | return -1; 236 | 237 | if (vfio_group_set_container(group, container, iommu_type)) 238 | return -1; 239 | 240 | if (container_out) 241 | *container_out = container; 242 | if (group_out) 243 | *group_out = group; 244 | return 0; 245 | } 246 | 247 | int vfio_group_attach(int groupid, int *container_out, int *group_out) 248 | { 249 | return __vfio_group_attach(groupid, container_out, group_out, 250 | VFIO_TYPE1_IOMMU); 251 | } 252 | 253 | static int __vfio_device_attach(const char *devname, int *container_out, 254 | int *device_out, int *group_out, 255 | int iommu_type) 256 | { 257 | int container, group, groupid, device; 258 | 259 | groupid = vfio_device_get_groupid(devname); 260 | if (groupid < 0) 261 | return -1; 262 | 263 | if (__vfio_group_attach(groupid, &container, &group, iommu_type) < 0) 264 | return -1; 265 | 266 | if (device_out) { 267 | device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, devname); 268 | if (device < 0) { 269 | printf("Failed to get device %s: %d (%s)\n", 270 | devname, container, strerror(errno)); 271 | return -1; 272 | } 273 | *device_out = device; 274 | } 275 | 276 | if (container_out) 277 | *container_out = container; 278 | if (group_out) 279 | *group_out = group; 280 | return 0; 281 | } 282 | 283 | int vfio_device_attach(const char *devname, int *container_out, int *device_out, 284 | int *group_out) 285 | { 286 | return __vfio_device_attach(devname, container_out, device_out, 287 | group_out, VFIO_TYPE1_IOMMU); 288 | } 289 | 290 | int vfio_device_attach_iommu_type(const char *devname, int *container_out, 291 | int *device_out, int *group_out, 292 | int iommu_type) 293 | { 294 | return __vfio_device_attach(devname, container_out, device_out, 295 | group_out, iommu_type); 296 | } 297 | 298 | #define ALIGN_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) 299 | 300 | void *mmap_align(void *addr, size_t length, int prot, int flags, 301 | int fd, off_t offset, size_t align) 302 | { 303 | void *addr_align; 304 | void *addr_base; 305 | void *map; 306 | 307 | addr_base = mmap(NULL, length + align, PROT_NONE, 308 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 309 | if (addr_base == MAP_FAILED) { 310 | return addr_base; 311 | } 312 | 313 | addr_align = (void *)ALIGN_UP((uintptr_t)addr_base, (uintptr_t)align); 314 | munmap(addr_base, addr_align - addr_base); 315 | munmap(addr_align + length, align - (addr_align - addr_base)); 316 | 317 | map = mmap(addr_align, length, prot, flags | MAP_FIXED, fd, offset); 318 | if (map != MAP_FAILED) 319 | madvise(map, length, MADV_HUGEPAGE); 320 | return map; 321 | } 322 | -------------------------------------------------------------------------------- /iommufd-pci-device-open.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "utils.h" 21 | 22 | void usage(char *name) 23 | { 24 | printf("usage: %s \n", name); 25 | } 26 | 27 | int main(int argc, char **argv) 28 | { 29 | const char *devname; 30 | int i, ret, device, iommufd; 31 | 32 | struct vfio_device_info device_info = { 33 | .argsz = sizeof(device_info) 34 | }; 35 | struct vfio_region_info region_info = { 36 | .argsz = sizeof(region_info) 37 | }; 38 | 39 | if (argc < 2) { 40 | usage(argv[0]); 41 | return -1; 42 | } 43 | 44 | devname = argv[1]; 45 | 46 | device = vfio_device_iommufd_getfd(devname); 47 | if (device < 0) 48 | return -1; 49 | 50 | struct vfio_device_bind_iommufd bind = { 51 | .argsz = sizeof(bind), 52 | .flags = 0, 53 | }; 54 | struct iommu_ioas_alloc alloc_data = { 55 | .size = sizeof(alloc_data), 56 | .flags = 0, 57 | }; 58 | struct vfio_device_attach_iommufd_pt attach_data = { 59 | .argsz = sizeof(attach_data), 60 | .flags = 0, 61 | }; 62 | struct iommu_ioas_map map = { 63 | .size = sizeof(map), 64 | .flags = IOMMU_IOAS_MAP_READABLE | 65 | IOMMU_IOAS_MAP_WRITEABLE | 66 | IOMMU_IOAS_MAP_FIXED_IOVA, 67 | .__reserved = 0, 68 | }; 69 | 70 | iommufd = open("/dev/iommu", O_RDWR); 71 | if (iommufd < 0) { 72 | printf("Failed to open /dev/iommufd, %d (%s)\n", 73 | iommufd, strerror(errno)); 74 | return iommufd; 75 | } 76 | 77 | bind.iommufd = iommufd; // negative value means vfio-noiommu mode 78 | ret = ioctl(device, VFIO_DEVICE_BIND_IOMMUFD, &bind); 79 | if (ret < 0) { 80 | printf("Failed VFIO_DEVICE_BIND_IOMMUFD %d (%s)\n", 81 | ret, strerror(errno)); 82 | return ret; 83 | } 84 | 85 | printf("Bind to IOMMUFD %d with dev_id %d\n", iommufd, bind.out_devid); 86 | 87 | if (ioctl(device, VFIO_DEVICE_GET_INFO, &device_info)) { 88 | printf("Failed to get device info\n"); 89 | return -1; 90 | } 91 | 92 | printf("Device supports %d regions, %d irqs\n", 93 | device_info.num_regions, device_info.num_irqs); 94 | 95 | for (i = 0; i < device_info.num_regions; i++) { 96 | printf("Region %d: ", i); 97 | region_info.index = i; 98 | if (ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®ion_info)) { 99 | printf("Failed to get info\n"); 100 | continue; 101 | } 102 | 103 | printf("size 0x%lx, offset 0x%lx, flags 0x%x\n", 104 | (unsigned long)region_info.size, 105 | (unsigned long)region_info.offset, region_info.flags); 106 | if (region_info.flags & VFIO_REGION_INFO_FLAG_MMAP) { 107 | void *map = mmap(NULL, (size_t)region_info.size, 108 | PROT_READ, MAP_SHARED, device, 109 | (off_t)region_info.offset); 110 | if (map == MAP_FAILED) { 111 | printf("mmap failed\n"); 112 | continue; 113 | } 114 | 115 | printf("["); 116 | fwrite(map, 1, region_info.size > 16 ? 16 : 117 | region_info.size, stdout); 118 | printf("]\n"); 119 | munmap(map, (size_t)region_info.size); 120 | } 121 | } 122 | 123 | ret = ioctl(iommufd, IOMMU_IOAS_ALLOC, &alloc_data); 124 | if (ret < 0) { 125 | printf("Failed IOMMU_IOAS_ALLOC %d (%s)\n", 126 | ret, strerror(errno)); 127 | return ret; 128 | } 129 | 130 | attach_data.pt_id = alloc_data.out_ioas_id; 131 | ret = ioctl(device, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data); 132 | if (ret < 0) { 133 | printf("Failed VFIO_DEVICE_ATTACH_IOMMUFD_PT ioas_id %d %d (%s)\n", 134 | attach_data.pt_id, ret, strerror(errno)); 135 | return ret; 136 | } 137 | 138 | printf("Attached IOMMUFD %d ioas %d hwpt %d\n", iommufd, alloc_data.out_ioas_id, attach_data.pt_id); 139 | 140 | /* Allocate some space and setup a DMA mapping */ 141 | map.user_va = (int64_t)mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE, 142 | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 143 | map.iova = 0; /* 1MB starting at 0x0 from device view */ 144 | map.length = 1024 * 1024; 145 | map.ioas_id = alloc_data.out_ioas_id;; 146 | 147 | ret = ioctl(iommufd, IOMMU_IOAS_MAP, &map); 148 | if (ret < 0) { 149 | printf("Failed VFIO_DEVICE_ATTACH_IOMMUFD_PT ioas_id %d %d (%s)\n", 150 | attach_data.pt_id, ret, strerror(errno)); 151 | return ret; 152 | } 153 | printf("Mapped user_va %llx size %llx to iova %llx in ioas %d\n", map.user_va, map.length, map.iova, map.ioas_id); 154 | 155 | struct vfio_pci_hot_reset_info *reset_info; 156 | struct vfio_pci_dependent_device *devices; 157 | struct vfio_pci_hot_reset *reset; 158 | reset_info = malloc(sizeof(*reset_info)); 159 | if (!reset_info) { 160 | printf("Failed to alloc info struct\n"); 161 | return -ENOMEM; 162 | } 163 | 164 | reset_info->argsz = sizeof(*reset_info); 165 | 166 | ret = ioctl(device, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, reset_info); 167 | if (ret && errno == ENODEV) { 168 | printf("Device does not support hot reset\n"); 169 | return 0; 170 | } 171 | if (!ret || errno != ENOSPC) { 172 | printf("Expected fail/-ENOSPC, got %d/%d\n", ret, -errno); 173 | return -1; 174 | } 175 | 176 | printf("Hot reset dependent device count: %d\n", reset_info->count); 177 | 178 | reset_info = realloc(reset_info, sizeof(*reset_info) + 179 | (reset_info->count * sizeof(*devices))); 180 | if (!reset_info) { 181 | printf("Failed to re-alloc info struct\n"); 182 | return -ENOMEM; 183 | } 184 | 185 | reset_info->argsz = sizeof(*reset_info) + 186 | (reset_info->count * sizeof(*devices)); 187 | ret = ioctl(device, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, reset_info); 188 | if (ret) { 189 | printf("Reset Info error\n"); 190 | return ret; 191 | } 192 | 193 | devices = &reset_info->devices[0]; 194 | 195 | for (i = 0; i < reset_info->count; i++) 196 | printf("%d: %04x:%02x:%02x.%d devid %d\n", i, 197 | devices[i].segment, devices[i].bus, 198 | devices[i].devfn >> 3, devices[i].devfn & 7, 199 | devices[i].devid); 200 | 201 | if (!(reset_info->flags & VFIO_PCI_HOT_RESET_FLAG_DEV_ID)) { 202 | printf("VFIO_PCI_HOT_RESET_FLAG_DEV_ID should be set for IOMMUFD\n"); 203 | return -1; 204 | } 205 | 206 | int unowned_cnt = 0; 207 | if (!(reset_info->flags & VFIO_PCI_HOT_RESET_FLAG_DEV_ID_OWNED)) { 208 | for (i = 0; i < reset_info->count; i++) { 209 | if (devices[i].devid == VFIO_PCI_DEVID_NOT_OWNED) { 210 | unowned_cnt++; 211 | printf("Cannot reset device, " 212 | "depends on device %04x:%02x:%02x.%x " 213 | "which is not owned.\n", 214 | devices[i].segment, devices[i].bus, 215 | devices[i].devfn >> 3, devices[i].devfn & 7); 216 | } 217 | } 218 | if (!unowned_cnt) { 219 | printf("flags mismatch with data field, " 220 | "VFIO_PCI_HOT_RESET_FLAG_DEV_ID_OWNED claimed but " 221 | "no VFIO_PCI_DEVID_NOT_OWNED\n"); 222 | return -1; 223 | } 224 | return 0; 225 | } 226 | 227 | 228 | printf("Attempting reset: "); 229 | fflush(stdout); 230 | 231 | /* Use zero length array for hot reset with iommufd backend */ 232 | reset = malloc(sizeof(*reset)); 233 | reset->argsz = sizeof(*reset); 234 | 235 | /* Bus reset! */ 236 | ret = ioctl(device, VFIO_DEVICE_PCI_HOT_RESET, reset); 237 | 238 | ret = ioctl(device, VFIO_DEVICE_PCI_HOT_RESET, reset); 239 | printf("Hot reset: %s\n", ret ? "Failed" : "Pass"); 240 | 241 | printf("Press any key to exit\n"); 242 | fgetc(stdin); 243 | 244 | return 0; 245 | } 246 | -------------------------------------------------------------------------------- /vfio-correctness-tests.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VFIO test suite 3 | * 4 | * Copyright (C) 2012-2025, Red Hat Inc. 5 | * 6 | * This work is licensed under the terms of the GNU GPL, version 2. See 7 | * the COPYING file in the top-level directory. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "utils.h" 29 | 30 | void usage(char *name) 31 | { 32 | printf("usage: %s [memory path]\n", name); 33 | } 34 | 35 | #define false 0 36 | #define true 1 37 | 38 | int pagesize_test(int fd, unsigned long vaddr, 39 | unsigned long size, unsigned long pagesize) 40 | { 41 | struct vfio_iommu_type1_dma_map dma_map = { 42 | .argsz = sizeof(dma_map), 43 | .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE, 44 | .size = pagesize, 45 | }; 46 | struct vfio_iommu_type1_dma_unmap dma_unmap = { 47 | .argsz = sizeof(dma_unmap), 48 | .size = pagesize, 49 | }; 50 | int ret; 51 | 52 | /* map it */ 53 | for (dma_map.vaddr = vaddr, dma_map.iova = 0; 54 | dma_map.iova < size; 55 | dma_map.iova += pagesize, dma_map.vaddr += pagesize) { 56 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 57 | if (ret) { 58 | printf("Failed to map @0x%llx(%s)\n", 59 | dma_map.iova, strerror(errno)); 60 | return ret; 61 | } 62 | } 63 | 64 | /* attempt to remap it */ 65 | for (dma_map.vaddr = vaddr, dma_map.iova = 0; 66 | dma_map.iova < size; 67 | dma_map.iova += pagesize, dma_map.vaddr += pagesize) { 68 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 69 | if (!ret) { 70 | printf("Error, allowed to remap @0x%llx(%s)\n", 71 | dma_map.iova, strerror(errno)); 72 | return ret; 73 | } 74 | } 75 | 76 | /* unmap it */ 77 | for (dma_unmap.iova = 0; 78 | dma_unmap.iova < size; 79 | dma_unmap.iova += pagesize) { 80 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 81 | if (ret || dma_unmap.size != pagesize) { 82 | printf("Failed to unmap @0x%llx(%s)\n", 83 | dma_unmap.iova, strerror(errno)); 84 | return ret; 85 | } 86 | } 87 | 88 | /* attempt to re-unmap it */ 89 | for (dma_unmap.iova = 0; 90 | dma_unmap.iova < size; 91 | dma_unmap.iova += pagesize) { 92 | dma_unmap.size = pagesize; 93 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 94 | if (ret || dma_unmap.size) { 95 | printf("Error, allowed to re-unmap @0x%llx(%s)\n", 96 | dma_unmap.iova, strerror(errno)); 97 | return ret; 98 | } 99 | } 100 | dma_unmap.size = pagesize; 101 | 102 | /* map it again, backwards*/ 103 | for (dma_map.vaddr = vaddr + size - pagesize, 104 | dma_map.iova = size - pagesize; 105 | dma_map.iova < size; 106 | dma_map.iova -= pagesize, dma_map.vaddr -= pagesize) { 107 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 108 | if (ret) { 109 | printf("Failed to backwards map @0x%llx(%s)\n", 110 | dma_map.iova, strerror(errno)); 111 | return ret; 112 | } 113 | } 114 | 115 | /* unmap it, backwards */ 116 | for (dma_unmap.iova = size - pagesize; 117 | dma_unmap.iova < size; 118 | dma_unmap.iova -= pagesize) { 119 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 120 | if (ret || dma_unmap.size != pagesize) { 121 | printf("Failed to backwards unmap @0x%llx(%s)\n", 122 | dma_unmap.iova, strerror(errno)); 123 | return ret; 124 | } 125 | } 126 | 127 | /* map it again, checker board */ 128 | for (dma_map.vaddr = vaddr, dma_map.iova = 0; 129 | dma_map.iova < size; 130 | dma_map.iova += (pagesize * 2), dma_map.vaddr += (pagesize * 2)) { 131 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 132 | if (ret) { 133 | printf("Failed even checker map @0x%llx(%s)\n", 134 | dma_map.iova, strerror(errno)); 135 | return ret; 136 | } 137 | } 138 | for (dma_map.vaddr = vaddr + pagesize, dma_map.iova = pagesize; 139 | dma_map.iova < size; 140 | dma_map.iova += (pagesize * 2), dma_map.vaddr += (pagesize * 2)) { 141 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 142 | if (ret) { 143 | printf("Failed odd checker map @0x%llx(%s)\n", 144 | dma_map.iova, strerror(errno)); 145 | return ret; 146 | } 147 | } 148 | 149 | /* unmap it, checker board */ 150 | for (dma_unmap.iova = 0; 151 | dma_unmap.iova < size; 152 | dma_unmap.iova += (pagesize * 2)) { 153 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 154 | if (ret || dma_unmap.size != pagesize) { 155 | printf("Failed even checker unmap @0x%llx(%s)\n", 156 | dma_unmap.iova, strerror(errno)); 157 | return ret; 158 | } 159 | } 160 | for (dma_unmap.iova = pagesize; 161 | dma_unmap.iova < size; 162 | dma_unmap.iova += (pagesize * 2)) { 163 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 164 | if (ret || dma_unmap.size != pagesize) { 165 | printf("Failed odd checker unmap @0x%llx(%s)\n", 166 | dma_unmap.iova, strerror(errno)); 167 | return ret; 168 | } 169 | } 170 | 171 | /* map it again, backwards checker board */ 172 | for (dma_map.vaddr = vaddr + size - pagesize, 173 | dma_map.iova = size - pagesize; 174 | dma_map.iova < size; 175 | dma_map.iova -= (pagesize * 2), dma_map.vaddr -= (pagesize * 2)) { 176 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 177 | if (ret) { 178 | printf("Failed even backward checker map @0x%llx(%s)\n", 179 | dma_map.iova, strerror(errno)); 180 | return ret; 181 | } 182 | } 183 | for (dma_map.vaddr = vaddr + size - (pagesize * 2), 184 | dma_map.iova = size - (pagesize * 2); 185 | dma_map.iova < size; 186 | dma_map.iova -= (pagesize * 2), dma_map.vaddr -= (pagesize * 2)) { 187 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 188 | if (ret) { 189 | printf("Failed odd backward checker map @0x%llx(%s)\n", 190 | dma_map.iova, strerror(errno)); 191 | return ret; 192 | } 193 | } 194 | 195 | /* unmap it, backwards checker board */ 196 | for (dma_unmap.iova = size - pagesize; 197 | dma_unmap.iova < size; 198 | dma_unmap.iova -= (pagesize * 2)) { 199 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 200 | if (ret || dma_unmap.size != pagesize) { 201 | printf("Failed even backward checker unmap @0x%llx(%s)\n", 202 | dma_unmap.iova, strerror(errno)); 203 | return ret; 204 | } 205 | } 206 | for (dma_unmap.iova = size - (pagesize * 2); 207 | dma_unmap.iova < size; 208 | dma_unmap.iova -= (pagesize * 2)) { 209 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 210 | if (ret || dma_unmap.size != pagesize) { 211 | printf("Failed odd backward checker unmap @0x%llx(%s)\n", 212 | dma_unmap.iova, strerror(errno)); 213 | return ret; 214 | } 215 | } 216 | 217 | printf("pagesize test: PASSED\n"); 218 | return 0; 219 | } 220 | 221 | int hugepage_test(int fd, unsigned long vaddr, 222 | unsigned long size, unsigned long pagesize) 223 | { 224 | struct vfio_iommu_type1_dma_map dma_map = { 225 | .argsz = sizeof(dma_map), 226 | .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE, 227 | }; 228 | struct vfio_iommu_type1_dma_unmap dma_unmap = { 229 | .argsz = sizeof(dma_unmap), 230 | }; 231 | int ret; 232 | int unmaps; 233 | unsigned long unmapped; 234 | unsigned long biggest_page; 235 | 236 | /* map it */ 237 | dma_map.vaddr = vaddr; 238 | dma_map.iova = 0; 239 | dma_map.size = size; 240 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 241 | if (ret) { 242 | printf("Failed to map @0x%llx(%s)\n", 243 | dma_map.iova, strerror(errno)); 244 | if (errno == EBUSY) 245 | printf("If this is an AMD system, this may be a known bug\n"); 246 | return ret; 247 | } 248 | 249 | /* attempt to remap it */ 250 | dma_map.vaddr = vaddr; 251 | dma_map.iova = 0; 252 | dma_map.size = size; 253 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 254 | if (!ret) { 255 | printf("Error, allowed to remap @0x%llx(%s)\n", 256 | dma_map.iova, strerror(errno)); 257 | return ret; 258 | } 259 | 260 | /* unmap it */ 261 | dma_unmap.iova = 0; 262 | dma_unmap.size = size; 263 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 264 | if (ret || dma_unmap.size != size) { 265 | printf("Failed to unmap @0x%llx(%s)\n", 266 | dma_unmap.iova, strerror(errno)); 267 | return ret; 268 | } 269 | 270 | /* map it again */ 271 | dma_map.vaddr = vaddr; 272 | dma_map.iova = 0; 273 | dma_map.size = size; 274 | ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); 275 | if (ret) { 276 | printf("Failed to map @0x%llx(%s)\n", 277 | dma_map.iova, strerror(errno)); 278 | return ret; 279 | } 280 | 281 | /* unmap it, backwards */ 282 | unmaps = unmapped = biggest_page = 0; 283 | for (dma_unmap.iova = size - pagesize; 284 | dma_unmap.iova < size; 285 | dma_unmap.iova -= pagesize) { 286 | dma_unmap.size = pagesize; 287 | ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); 288 | if (ret) { 289 | printf("Failed to unmap @0x%llx(%s)\n", 290 | dma_unmap.iova, strerror(errno)); 291 | return ret; 292 | } 293 | if (dma_unmap.size) { 294 | unmaps++; 295 | unmapped += dma_unmap.size; 296 | if (dma_unmap.size > biggest_page) 297 | biggest_page = dma_unmap.size; 298 | } 299 | } 300 | if (unmapped != size) { 301 | printf("Error, only unmapped 0x%lx of 0x%lx\n", unmapped, size); 302 | return -1; 303 | } 304 | 305 | printf("hugepage test: PASSED\n"); 306 | if (unmaps > 1) 307 | printf("(unmaps 0x%x, biggest page 0x%lx)\n", 308 | unmaps, biggest_page); 309 | return 0; 310 | } 311 | 312 | int main(int argc, char **argv) 313 | { 314 | int ret, container, groupid, fd = -1; 315 | char path[PATH_MAX], mempath[PATH_MAX] = ""; 316 | unsigned long vaddr; 317 | struct statfs fs; 318 | long hugepagesize, pagesize, mapsize; 319 | 320 | struct vfio_group_status group_status = { 321 | .argsz = sizeof(group_status) 322 | }; 323 | struct vfio_iommu_type1_dma_map dma_map = { 324 | .argsz = sizeof(dma_map) 325 | }; 326 | struct vfio_iommu_type1_dma_unmap dma_unmap = { 327 | .argsz = sizeof(dma_unmap) 328 | }; 329 | 330 | if (argc < 2) { 331 | usage(argv[0]); 332 | return -1; 333 | } 334 | 335 | ret = sscanf(argv[1], "%d", &groupid); 336 | if (ret != 1) { 337 | usage(argv[0]); 338 | return -1; 339 | } 340 | 341 | if (vfio_group_attach(groupid, &container, NULL)) 342 | return -1; 343 | 344 | if (argc > 2) { 345 | ret = sscanf(argv[2], "%s", mempath); 346 | if (ret != 1) { 347 | usage(argv[0]); 348 | return -1; 349 | } 350 | } 351 | 352 | hugepagesize = pagesize = getpagesize(); 353 | 354 | if (strlen(mempath)) { 355 | do { 356 | ret = statfs(mempath, &fs); 357 | } while (ret != 0 && errno == EINTR); 358 | 359 | if (ret) { 360 | printf("Can't statfs on %s\n", mempath); 361 | } else 362 | hugepagesize = fs.f_bsize; 363 | 364 | printf("Using %ldK page size, %ldK huge page size\n", 365 | pagesize >> 10, hugepagesize >> 10); 366 | 367 | sprintf(path, "%s/%s.XXXXXX", mempath, basename(argv[0])); 368 | fd = mkstemp(path); 369 | if (fd < 0) 370 | printf("Failed to open mempath file %s (%s)\n", 371 | path, strerror(errno)); 372 | } 373 | 374 | if (hugepagesize == pagesize) 375 | mapsize = 2 * 1024 * 1024; 376 | else 377 | mapsize = hugepagesize; 378 | 379 | if (fd < 0) { 380 | vaddr = (unsigned long)mmap(0, mapsize, 381 | PROT_READ | PROT_WRITE, 382 | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 383 | } else { 384 | ftruncate(fd, mapsize); 385 | vaddr = (unsigned long)mmap(0, mapsize, 386 | PROT_READ | PROT_WRITE, 387 | MAP_POPULATE | MAP_SHARED, fd, 0); 388 | } 389 | 390 | if (!vaddr) { 391 | printf("Failed to allocate memory\n"); 392 | return -1; 393 | } 394 | 395 | if (pagesize_test(container, vaddr, mapsize, pagesize)) { 396 | printf("pagesize test: FAILED\n"); 397 | return -1; 398 | } 399 | 400 | if (hugepage_test(container, vaddr, mapsize, pagesize)) { 401 | printf("hugepage test: FAILED\n"); 402 | return -1; 403 | } 404 | 405 | return 0; 406 | } 407 | --------------------------------------------------------------------------------