【无标题】IOMMU功能测试软件设计及实现 (二)

knowledge base:

PCIe ATS架构及工作原理 (一)

https://mp.weixin.qq.com/s/GmesAPnZ66oBGpn0AhS1tg

IOMMU工作原理,架构以及驱动软件实现 (一)

IOMMU工作原理,架构以及驱动软件实现 (一)([Iommu arch](https://mp.weixin.qq.com/s/bNQCLlFpMEeFKmbwPsexHA)) 本文提供一个完整的IOMMU功能测试软件设计及其代码实现示例。这是一个实际可编译运行的框架,包含内核模块和用户空间工具。这个完整的测试框架可以全面验证IOMMU的功能、性能、安全性和稳定性。每个测试都有明确的目的、方法和验证标准,可以适应不同硬件平台和测试需求 完整IOMMU测试软件实现 一、项目总览 测试目标 A. 基础功能测试 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9f1d48d9f5c5434dbd87793f9c4cc272.jpg) 完整测试目录 *** ** * ** *** 请关注微信公众号:tech-holic *** ** * ** *** B. 安全与隔离测 C. 性能测试 图片 D. 压力与稳定性测试 图片 E. 高级功能测试 图片 F. 合规性与兼容性测试 图片 G. 安全漏洞测试 图片 测试代码流程 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0e3c821b0eee4e09841c44d8bc7caae7.png) 图片 图片 项目工程架构 iommu_tester/ ├── Makefile ├── README.md ├── kernel/ │ ├── Makefile │ ├── iommu_test_drv.c # 主内核模块 │ ├── iommu_test_dev.c # 虚拟测试设备驱动 │ └── iommu_test.h # 内核头文件 └── userspace/ ├── Makefile ├── iommu_test_lib.c # 用户空间库 ├── iommu_test_lib.h ├── iommu_test_cmd.c # 命令行工具 └── test_cases/ ├── basic.c # 基础测试用例 ├── isolation.c # 域隔离测试 └── performance.c # 性能测试 二、内核模块实现 2.1 公共头文件 (kernel/iommu_test.h) #ifndef _IOMMU_TEST_H #define _IOMMU_TEST_H #include \ #include \ #define IOMMU_TEST_DEVICE_NAME "iommu_test" #define IOMMU_TEST_CLASS_NAME "iommu_test" #define MAX_TEST_DEVICES 8 #define MAX_DOMAINS 32 #define MAX_IOVA_RANGES 1024 /\* IOCTL命令定义 \*/ #define IOMMU_TEST_MAGIC 'I' /\* 基本域操作 \*/ #define IOMMU_TEST_ALLOC_DOMAIN _IOWR(IOMMU_TEST_MAGIC, 1, struct iommu_test_alloc_domain) #define IOMMU_TEST_FREE_DOMAIN _IOW(IOMMU_TEST_MAGIC, 2, int) #define IOMMU_TEST_ATTACH_DEVICE _IOW(IOMMU_TEST_MAGIC, 3, struct iommu_test_attach) #define IOMMU_TEST_DETACH_DEVICE _IOW(IOMMU_TEST_MAGIC, 4, struct iommu_test_attach) /\* 映射操作 \*/ #define IOMMU_TEST_MAP _IOW(IOMMU_TEST_MAGIC, 5, struct iommu_test_map) #define IOMMU_TEST_UNMAP _IOW(IOMMU_TEST_MAGIC, 6, struct iommu_test_unmap) #define IOMMU_TEST_MAP_RANGE _IOW(IOMMU_TEST_MAGIC, 7, struct iommu_test_map_range) /\* DMA测试操作 \*/ #define IOMMU_TEST_TRIGGER_DMA _IOW(IOMMU_TEST_MAGIC, 8, struct iommu_test_dma) #define IOMMU_TEST_CHECK_DMA _IOWR(IOMMU_TEST_MAGIC, 9, struct iommu_test_check) #define IOMMU_TEST_GET_FAULT_INFO _IOR(IOMMU_TEST_MAGIC, 10, struct iommu_test_fault) /\* 性能测试 \*/ #define IOMMU_TEST_PERF_START _IO(IOMMU_TEST_MAGIC, 11) #define IOMMU_TEST_PERF_STOP _IOR(IOMMU_TEST_MAGIC, 12, struct iommu_test_perf_stats) /\* 信息获取 \*/ #define IOMMU_TEST_GET_CAPABILITIES _IOR(IOMMU_TEST_MAGIC, 13, struct iommu_test_caps) #define IOMMU_TEST_GET_DEVICE_INFO _IOR(IOMMU_TEST_MAGIC, 14, struct iommu_test_device_info) /\* 数据结构定义 \*/ struct iommu_test_alloc_domain { __u32 domain_id; __u32 flags; __u64 iommu_type; /* 0 = Intel VT-d, 1 = AMD-Vi, 2 = ARM SMMU */ char padding[52]; }; struct iommu_test_attach { __u32 domain_id; __u32 device_id; __u32 bus; __u32 devfn; char padding[48]; }; struct iommu_test_map { __u32 domain_id; __u64 iova; __u64 phys_addr; __u64 size; __u32 prot; /* IOMMU_READ, IOMMU_WRITE, IOMMU_EXEC */ __u32 flags; char padding[32]; }; struct iommu_test_unmap { __u32 domain_id; __u64 iova; __u64 size; __u32 flags; char padding[44]; }; struct iommu_test_dma { __u32 device_id; __u64 iova; __u64 size; __u32 direction; /* 0 = TO_DEVICE, 1 = FROM_DEVICE */ __u32 dma_type; /* 0 = NORMAL, 1 = ATS, 2 = PRI */ __u8 pattern[16]; /* DMA数据模式 */ char padding[32]; }; struct iommu_test_check { __u32 device_id; __u64 phys_addr; __u64 size; __u8 expected_pattern[16]; __u32 result; /* 0 = FAIL, 1 = PASS, 2 = FAULT */ char padding[36]; }; struct iommu_test_perf_stats { __u64 total_cycles; __u64 total_transactions; __u64 iotlb_misses; __u64 iotlb_hits; __u64 fault_count; __u64 avg_latency_ns; __u64 max_latency_ns; __u64 min_latency_ns; char padding[32]; }; struct iommu_test_caps { __u32 supports_ats : 1; __u32 supports_pri : 1; __u32 supports_pasid : 1; __u32 supports_nesting : 1; __u32 supports_snoop : 1; __u32 reserved : 27; __u32 max_pasid; __u64 max_addr_mask; char padding[48]; }; #endif /\* _IOMMU_TEST_H \*/ 2.2 主内核模块 (kernel/iommu_test_drv.c) #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include \ #include "iommu_test.h" #define DEV_NAME "iommu_test" #define MAX_MINORS 1 static int major; static struct class \*iommu_test_class; static struct cdev iommu_test_cdev; static struct dentry \*debugfs_dir; /\* 测试域结构 \*/ struct test_domain { struct iommu_domain *domain; struct list_head device_list; spinlock_t lock; u32 id; u64 flags; bool is_active; struct rb_root iova_tree; }; /\* IOVA映射结构 \*/ struct iova_mapping { struct rb_node node; u64 iova_start; u64 iova_end; u64 phys_addr; u32 prot; struct page **pages; u32 npages; }; /\* DMA缓冲区结构 \*/ struct dma_buffer { dma_addr_t dma_addr; void *virt_addr; size_t size; struct page **pages; u32 npages; u32 domain_id; }; /\* 全局状态 \*/ static struct test_domain \*domains\[MAX_DOMAINS\]; static atomic_t next_domain_id = ATOMIC_INIT(0); static DEFINE_MUTEX(domain_lock); /\* 性能测试数据 \*/ static struct iommu_test_perf_stats perf_stats; static bool perf_test_running; static ktime_t test_start_time; static DEFINE_SPINLOCK(perf_lock); /\* 虚拟测试设备 \*/ struct test_device { struct pci_dev *pdev; struct device *dev; struct iommu_group *group; int domain_id; bool attached; }; /\* 调试FS操作 \*/ static int debugfs_iova_show(struct seq_file \*m, void \*v) { int i; mutex_lock(&domain_lock); for (i = 0; i < MAX_DOMAINS; i++) { if (domains[i]) { struct iova_mapping *map; struct rb_node *node; seq_printf(m, "Domain %d:\n", domains[i]->id); spin_lock(&domains[i]->lock); for (node = rb_first(&domains[i]->iova_tree); node; node = rb_next(node)) { map = rb_entry(node, struct iova_mapping, node); seq_printf(m, " IOVA 0x%llx-0x%llx -> PA 0x%llx prot=0x%x\n", map->iova_start, map->iova_end, map->phys_addr, map->prot); } spin_unlock(&domains[i]->lock); } } mutex_unlock(&domain_lock); return 0; } DEFINE_SHOW_ATTRIBUTE(debugfs_iova); /\* IOVA树操作 \*/ static struct iova_mapping \*find_iova_mapping(struct test_domain \*domain, u64 iova) { struct rb_node *node = domain->iova_tree.rb_node; while (node) { struct iova_mapping *map = rb_entry(node, struct iova_mapping, node); if (iova < map->iova_start) node = node->rb_left; else if (iova > map->iova_end) node = node->rb_right; else return map; } return NULL; } static int insert_iova_mapping(struct test_domain \*domain, struct iova_mapping \*new) { struct rb_node **link = &domain->iova_tree.rb_node; struct rb_node *parent = NULL; struct iova_mapping *map; while (*link) { parent = *link; map = rb_entry(parent, struct iova_mapping, node); if (new->iova_end < map->iova_start) link = &parent->rb_left; else if (new->iova_start > map->iova_end) link = &parent->rb_right; else return -EEXIST; /* 重叠 */ } rb_link_node(&new->node, parent, link); rb_insert_color(&new->node, &domain->iova_tree); return 0; } static void remove_iova_mapping(struct test_domain \*domain, struct iova_mapping \*map) { rb_erase(&map->node, &domain->iova_tree); } /\* DMA缓冲区分配 \*/ static struct dma_buffer \*alloc_dma_buffer(size_t size, gfp_t gfp) { struct dma_buffer *buf; int i; buf = kzalloc(sizeof(*buf), gfp); if (!buf) return NULL; buf->npages = DIV_ROUND_UP(size, PAGE_SIZE); buf->pages = kmalloc_array(buf->npages, sizeof(struct page *), gfp); if (!buf->pages) { kfree(buf); return NULL; } for (i = 0; i < buf->npages; i++) { buf->pages[i] = alloc_page(gfp | __GFP_ZERO); if (!buf->pages[i]) goto error; } buf->size = size; buf->virt_addr = vmap(buf->pages, buf->npages, VM_MAP, PAGE_KERNEL); if (!buf->virt_addr) goto error; return buf; error: for (i = 0; i < buf->npages; i++) { if (buf->pages[i]) __free_page(buf->pages[i]); } kfree(buf->pages); kfree(buf); return NULL; } static void free_dma_buffer(struct dma_buffer \*buf) { int i; if (!buf) return; if (buf->virt_addr) vunmap(buf->virt_addr); for (i = 0; i < buf->npages; i++) { if (buf->pages[i]) __free_page(buf->pages[i]); } kfree(buf->pages); kfree(buf); } /\* 性能监控函数 \*/ static void perf_record_transaction(ktime_t start) { ktime_t delta; u64 latency_ns; if (!perf_test_running) return; delta = ktime_sub(ktime_get(), start); latency_ns = ktime_to_ns(delta); spin_lock(&perf_lock); perf_stats.total_transactions++; perf_stats.total_cycles += latency_ns; if (latency_ns > perf_stats.max_latency_ns) perf_stats.max_latency_ns = latency_ns; if (latency_ns < perf_stats.min_latency_ns || perf_stats.min_latency_ns == 0) perf_stats.min_latency_ns = latency_ns; if (perf_stats.total_transactions > 0) perf_stats.avg_latency_ns = perf_stats.total_cycles / perf_stats.total_transactions; spin_unlock(&perf_lock); } /\* 文件操作函数 \*/ static int iommu_test_open(struct inode \*inode, struct file \*filp) { return 0; } static int iommu_test_release(struct inode \*inode, struct file \*filp) { return 0; } static long iommu_test_ioctl(struct file \*filp, unsigned int cmd, unsigned long arg) { void __user *user_arg = (void __user *)arg; long ret = 0; ktime_t start_time; switch (cmd) { case IOMMU_TEST_ALLOC_DOMAIN: { struct iommu_test_alloc_domain param; struct test_domain *domain; u32 id; if (copy_from_user(¶m, user_arg, sizeof(param))) return -EFAULT; mutex_lock(&domain_lock); id = atomic_inc_return(&next_domain_id) - 1; if (id >= MAX_DOMAINS) { ret = -ENOSPC; goto domain_out; } domain = kzalloc(sizeof(*domain), GFP_KERNEL); if (!domain) { ret = -ENOMEM; goto domain_out; } domain->domain = iommu_domain_alloc(&pci_bus_type); if (!domain->domain) { kfree(domain); ret = -ENOMEM; goto domain_out; } domain->id = id; domain->flags = param.flags; domain->is_active = true; spin_lock_init(&domain->lock); INIT_LIST_HEAD(&domain->device_list); domain->iova_tree = RB_ROOT; domains[id] = domain; param.domain_id = id; if (copy_to_user(user_arg, ¶m, sizeof(param))) ret = -EFAULT; domain_out: mutex_unlock(&domain_lock); break; } case IOMMU_TEST_MAP: { struct iommu_test_map param; struct test_domain *domain; struct iova_mapping *map; phys_addr_t phys; int i; if (copy_from_user(¶m, user_arg, sizeof(param))) return -EFAULT; if (param.domain_id >= MAX_DOMAINS || !domains[param.domain_id]) { ret = -EINVAL; break; } domain = domains[param.domain_id]; /* 分配物理页面 */ map = kzalloc(sizeof(*map), GFP_KERNEL); if (!map) { ret = -ENOMEM; break; } map->npages = DIV_ROUND_UP(param.size, PAGE_SIZE); map->pages = kmalloc_array(map->npages, sizeof(struct page *), GFP_KERNEL); if (!map->pages) { kfree(map); ret = -ENOMEM; break; } for (i = 0; i < map->npages; i++) { map->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); if (!map->pages[i]) { while (i-- > 0) __free_page(map->pages[i]); kfree(map->pages); kfree(map); ret = -ENOMEM; break; } } phys = page_to_phys(map->pages[0]); start_time = ktime_get(); ret = iommu_map(domain->domain, param.iova, phys, param.size, param.prot); perf_record_transaction(start_time); if (ret) { for (i = 0; i < map->npages; i++) __free_page(map->pages[i]); kfree(map->pages); kfree(map); break; } map->iova_start = param.iova; map->iova_end = param.iova + param.size - 1; map->phys_addr = phys; map->prot = param.prot; spin_lock(&domain->lock); insert_iova_mapping(domain, map); spin_unlock(&domain->lock); break; } case IOMMU_TEST_UNMAP: { struct iommu_test_unmap param; struct test_domain *domain; struct iova_mapping *map; int i; if (copy_from_user(¶m, user_arg, sizeof(param))) return -EFAULT; if (param.domain_id >= MAX_DOMAINS || !domains[param.domain_id]) { ret = -EINVAL; break; } domain = domains[param.domain_id]; spin_lock(&domain->lock); map = find_iova_mapping(domain, param.iova); if (!map) { spin_unlock(&domain->lock); ret = -ENOENT; break; } start_time = ktime_get(); ret = iommu_unmap(domain->domain, param.iova, param.size); perf_record_transaction(start_time); if (ret == param.size) { remove_iova_mapping(domain, map); for (i = 0; i < map->npages; i++) __free_page(map->pages[i]); kfree(map->pages); kfree(map); } spin_unlock(&domain->lock); break; } case IOMMU_TEST_TRIGGER_DMA: { /* 在实际硬件中,这里会编程真实设备发起DMA */ /* 对于模拟测试,我们直接操作内存 */ struct iommu_test_dma param; struct test_domain *domain; struct iova_mapping *map; void *virt_addr; if (copy_from_user(¶m, user_arg, sizeof(param))) return -EFAULT; if (!domains[0]) { /* 假设domain 0存在 */ ret = -EINVAL; break; } domain = domains[0]; spin_lock(&domain->lock); map = find_iova_mapping(domain, param.iova); if (!map) { spin_unlock(&domain->lock); ret = -EFAULT; /* IOMMU应阻止此DMA */ break; } /* 模拟DMA操作 */ virt_addr = kmap(map->pages[0]); if (param.direction == 0) { /* TO_DEVICE */ /* 假设设备读取数据 */ memcpy(virt_addr, param.pattern, min(param.size, 16UL)); } else { /* FROM_DEVICE */ /* 假设设备写入数据 */ memcpy(param.pattern, virt_addr, min(param.size, 16UL)); } kunmap(map->pages[0]); spin_unlock(&domain->lock); if (copy_to_user(user_arg, ¶m, sizeof(param))) ret = -EFAULT; break; } case IOMMU_TEST_PERF_START: spin_lock(&perf_lock); memset(&perf_stats, 0, sizeof(perf_stats)); perf_test_running = true; test_start_time = ktime_get(); spin_unlock(&perf_lock); break; case IOMMU_TEST_PERF_STOP: spin_lock(&perf_lock); perf_test_running = false; spin_unlock(&perf_lock); if (copy_to_user(user_arg, &perf_stats, sizeof(perf_stats))) ret = -EFAULT; break; default: ret = -ENOTTY; break; } return ret; } static const struct file_operations iommu_test_fops = { .owner = THIS_MODULE, .open = iommu_test_open, .release = iommu_test_release, .unlocked_ioctl = iommu_test_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = iommu_test_ioctl, #endif }; /\* 模块初始化和清理 \*/ static int __init iommu_test_init(void) { dev_t dev; int ret; /* 分配设备号 */ ret = alloc_chrdev_region(&dev, 0, MAX_MINORS, DEV_NAME); if (ret < 0) { pr_err("Failed to allocate char device region\n"); return ret; } major = MAJOR(dev); /* 创建设备类 */ iommu_test_class = class_create(THIS_MODULE, IOMMU_TEST_CLASS_NAME); if (IS_ERR(iommu_test_class)) { unregister_chrdev_region(dev, MAX_MINORS); return PTR_ERR(iommu_test_class); } /* 初始化cdev */ cdev_init(&iommu_test_cdev, &iommu_test_fops); iommu_test_cdev.owner = THIS_MODULE; ret = cdev_add(&iommu_test_cdev, dev, MAX_MINORS); if (ret < 0) { class_destroy(iommu_test_class); unregister_chrdev_region(dev, MAX_MINORS); return ret; } /* 创建设备节点 */ device_create(iommu_test_class, NULL, dev, NULL, DEV_NAME); /* 创建debugfs目录 */ debugfs_dir = debugfs_create_dir("iommu_test", NULL); if (debugfs_dir) { debugfs_create_file("iova_mappings", 0444, debugfs_dir, NULL, &debugfs_iova_fops); debugfs_create_u64("perf_transactions", 0444, debugfs_dir, &perf_stats.total_transactions); debugfs_create_u64("perf_avg_latency", 0444, debugfs_dir, &perf_stats.avg_latency_ns); } pr_info("IOMMU Test Module loaded (major=%d)\n", major); return 0; } static void __exit iommu_test_exit(void) { dev_t dev = MKDEV(major, 0); int i; /* 清理所有域 */ mutex_lock(&domain_lock); for (i = 0; i < MAX_DOMAINS; i++) { if (domains[i]) { if (domains[i]->domain) iommu_domain_free(domains[i]->domain); kfree(domains[i]); domains[i] = NULL; } } mutex_unlock(&domain_lock); /* 删除debugfs */ debugfs_remove_recursive(debugfs_dir); /* 删除设备 */ device_destroy(iommu_test_class, dev); /* 清理cdev和类 */ cdev_del(&iommu_test_cdev); class_destroy(iommu_test_class); /* 释放设备号 */ unregister_chrdev_region(dev, MAX_MINORS); pr_info("IOMMU Test Module unloaded\n"); } module_init(iommu_test_init); module_exit(iommu_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("IOMMU Tester"); MODULE_DESCRIPTION("IOMMU Functional Test Driver"); MODULE_VERSION("1.0"); 2.3 内核模块Makefile ```makefile # kernel/Makefile obj-m := iommu_test_drv.o KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean rm -f modules.order install: sudo insmod iommu_test_drv.ko uninstall: sudo rmmod iommu_test_drv debug: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules EXTRA_CFLAGS="-DDEBUG" 三、用户空间库实现 3.1 用户空间头文件 (userspace/iommu_test_lib.h) ```c #ifndef IOMMU_TEST_LIB_H #define IOMMU_TEST_LIB_H #include #include #define IOMMU_TEST_DEV_PATH "/dev/iommu_test" /* 错误代码 */ #define IOMMU_TEST_SUCCESS 0 #define IOMMU_TEST_ERROR -1 #define IOMMU_TEST_IOMMU_FAULT -2 #define IOMMU_TEST_NO_MEMORY -3 #define IOMMU_TEST_INVALID_PARAM -4 /* DMA方向 */ #define DMA_TO_DEVICE 0 #define DMA_FROM_DEVICE 1 /* 保护标志 */ #define IOMMU_READ (1 << 0) #define IOMMU_WRITE (1 << 1) #define IOMMU_EXEC (1 << 2) /* 句柄结构 */ struct iommu_test_handle { int fd; bool debug; uint32_t current_domain; }; /* API函数 */ struct iommu_test_handle *iommu_test_open(bool debug); void iommu_test_close(struct iommu_test_handle *handle); int iommu_test_alloc_domain(struct iommu_test_handle *handle, uint32_t *domain_id, uint32_t flags); int iommu_test_free_domain(struct iommu_test_handle *handle, uint32_t domain_id); int iommu_test_map(struct iommu_test_handle *handle, uint32_t domain_id, uint64_t iova, uint64_t size, uint32_t prot); int iommu_test_unmap(struct iommu_test_handle *handle, uint32_t domain_id, uint64_t iova, uint64_t size); int iommu_test_trigger_dma(struct iommu_test_handle *handle, uint32_t device_id, uint64_t iova, uint64_t size, int direction, uint8_t *pattern, size_t pattern_len); int iommu_test_verify_dma(struct iommu_test_handle *handle, uint32_t device_id, uint64_t phys_addr, uint64_t size, uint8_t *expected, size_t expected_len); int iommu_test_start_perf(struct iommu_test_handle *handle); int iommu_test_stop_perf(struct iommu_test_handle *handle, uint64_t *transactions, uint64_t *avg_latency); int iommu_test_run_isolation_test(struct iommu_test_handle *handle); int iommu_test_run_basic_test(struct iommu_test_handle *handle); int iommu_test_run_performance_test(struct iommu_test_handle *handle, uint32_t iterations, size_t buffer_size); #endif /* IOMMU_TEST_LIB_H */ ``` 3.2 用户空间库实现 (userspace/iommu_test_lib.c) ```c #include "iommu_test_lib.h" #include #include #include #include #include #include #include #include #include #include "../kernel/iommu_test.h" static void debug_print(struct iommu_test_handle *handle, const char *fmt, ...) { if (handle && handle->debug) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } } struct iommu_test_handle *iommu_test_open(bool debug) { struct iommu_test_handle *handle; handle = malloc(sizeof(*handle)); if (!handle) return NULL; handle->fd = open(IOMMU_TEST_DEV_PATH, O_RDWR); if (handle->fd < 0) { free(handle); return NULL; } handle->debug = debug; handle->current_domain = 0; debug_print(handle, "IOMMU test handle opened (fd=%d)\n", handle->fd); return handle; } void iommu_test_close(struct iommu_test_handle *handle) { if (!handle) return; debug_print(handle, "Closing IOMMU test handle\n"); if (handle->fd >= 0) close(handle->fd); free(handle); } int iommu_test_alloc_domain(struct iommu_test_handle *handle, uint32_t *domain_id, uint32_t flags) { struct iommu_test_alloc_domain param; int ret; if (!handle || !domain_id) return IOMMU_TEST_INVALID_PARAM; memset(¶m, 0, sizeof(param)); param.flags = flags; ret = ioctl(handle->fd, IOMMU_TEST_ALLOC_DOMAIN, ¶m); if (ret < 0) { debug_print(handle, "Failed to allocate domain: %s\n", strerror(errno)); return IOMMU_TEST_ERROR; } *domain_id = param.domain_id; handle->current_domain = param.domain_id; debug_print(handle, "Allocated domain %u\n", param.domain_id); return IOMMU_TEST_SUCCESS; } int iommu_test_free_domain(struct iommu_test_handle *handle, uint32_t domain_id) { int ret; if (!handle) return IOMMU_TEST_INVALID_PARAM; ret = ioctl(handle->fd, IOMMU_TEST_FREE_DOMAIN, &domain_id); if (ret < 0) { debug_print(handle, "Failed to free domain %u: %s\n", domain_id, strerror(errno)); return IOMMU_TEST_ERROR; } debug_print(handle, "Freed domain %u\n", domain_id); return IOMMU_TEST_SUCCESS; } int iommu_test_map(struct iommu_test_handle *handle, uint32_t domain_id, uint64_t iova, uint64_t size, uint32_t prot) { struct iommu_test_map param; int ret; if (!handle || size == 0) return IOMMU_TEST_INVALID_PARAM; memset(¶m, 0, sizeof(param)); param.domain_id = domain_id; param.iova = iova; param.size = size; param.prot = prot; ret = ioctl(handle->fd, IOMMU_TEST_MAP, ¶m); if (ret < 0) { debug_print(handle, "Failed to map IOVA 0x%lx size %lu in domain %u: %s\n", (unsigned long)iova, (unsigned long)size, domain_id, strerror(errno)); return IOMMU_TEST_ERROR; } debug_print(handle, "Mapped IOVA 0x%lx-0x%lx in domain %u\n", (unsigned long)iova, (unsigned long)(iova + size - 1), domain_id); return IOMMU_TEST_SUCCESS; } int iommu_test_unmap(struct iommu_test_handle *handle, uint32_t domain_id, uint64_t iova, uint64_t size) { struct iommu_test_unmap param; int ret; if (!handle || size == 0) return IOMMU_TEST_INVALID_PARAM; memset(¶m, 0, sizeof(param)); param.domain_id = domain_id; param.iova = iova; param.size = size; ret = ioctl(handle->fd, IOMMU_TEST_UNMAP, ¶m); if (ret < 0) { debug_print(handle, "Failed to unmap IOVA 0x%lx size %lu in domain %u: %s\n", (unsigned long)iova, (unsigned long)size, domain_id, strerror(errno)); return IOMMU_TEST_ERROR; } debug_print(handle, "Unmapped IOVA 0x%lx size %lu in domain %u\n", (unsigned long)iova, (unsigned long)size, domain_id); return IOMMU_TEST_SUCCESS; } int iommu_test_trigger_dma(struct iommu_test_handle *handle, uint32_t device_id, uint64_t iova, uint64_t size, int direction, uint8_t *pattern, size_t pattern_len) { struct iommu_test_dma param; int ret; if (!handle) return IOMMU_TEST_INVALID_PARAM; memset(¶m, 0, sizeof(param)); param.device_id = device_id; param.iova = iova; param.size = size; param.direction = direction; if (pattern && pattern_len > 0 && pattern_len <= sizeof(param.pattern)) memcpy(param.pattern, pattern, pattern_len); ret = ioctl(handle->fd, IOMMU_TEST_TRIGGER_DMA, ¶m); if (ret < 0) { debug_print(handle, "DMA failed: %s (IOMMU fault expected for invalid access)\n", strerror(errno)); return IOMMU_TEST_IOMMU_FAULT; } debug_print(handle, "DMA triggered: device %u, IOVA 0x%lx, dir %d\n", device_id, (unsigned long)iova, direction); return IOMMU_TEST_SUCCESS; } int iommu_test_start_perf(struct iommu_test_handle *handle) { int ret; if (!handle) return IOMMU_TEST_INVALID_PARAM; ret = ioctl(handle->fd, IOMMU_TEST_PERF_START, NULL); if (ret < 0) { debug_print(handle, "Failed to start perf test: %s\n", strerror(errno)); return IOMMU_TEST_ERROR; } debug_print(handle, "Performance test started\n"); return IOMMU_TEST_SUCCESS; } int iommu_test_stop_perf(struct iommu_test_handle *handle, uint64_t *transactions, uint64_t *avg_latency) { struct iommu_test_perf_stats stats; int ret; if (!handle) return IOMMU_TEST_INVALID_PARAM; ret = ioctl(handle->fd, IOMMU_TEST_PERF_STOP, &stats); if (ret < 0) { debug_print(handle, "Failed to stop perf test: %s\n", strerror(errno)); return IOMMU_TEST_ERROR; } if (transactions) *transactions = stats.total_transactions; if (avg_latency) *avg_latency = stats.avg_latency_ns; debug_print(handle, "Performance test stopped: %lu transactions, avg latency %lu ns\n", stats.total_transactions, stats.avg_latency_ns); return IOMMU_TEST_SUCCESS; } int iommu_test_run_basic_test(struct iommu_test_handle *handle) { uint32_t domain_id; int ret; uint8_t test_pattern[16] = {0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC}; uint64_t iova = 0x1000; uint64_t size = 4096; printf("Running Basic IOMMU Test...\n"); /* 1. 分配域 */ ret = iommu_test_alloc_domain(handle, &domain_id, 0); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot allocate domain\n"); return ret; } /* 2. 映射内存 */ ret = iommu_test_map(handle, domain_id, iova, size, IOMMU_READ | IOMMU_WRITE); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot map memory\n"); goto cleanup; } /* 3. 触发DMA写入 */ ret = iommu_test_trigger_dma(handle, 0, iova, 16, DMA_TO_DEVICE, test_pattern, 16); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: DMA write failed\n"); goto cleanup; } printf(" PASS: DMA write with valid mapping succeeded\n"); /* 4. 解除映射 */ ret = iommu_test_unmap(handle, domain_id, iova, size); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot unmap memory\n"); goto cleanup; } /* 5. 尝试在解除映射后触发DMA(应该失败) */ ret = iommu_test_trigger_dma(handle, 0, iova, 16, DMA_TO_DEVICE, test_pattern, 16); if (ret == IOMMU_TEST_IOMMU_FAULT) { printf(" PASS: IOMMU correctly blocked DMA after unmap\n"); } else { printf(" FAIL: IOMMU did not block invalid DMA access\n"); ret = IOMMU_TEST_ERROR; goto cleanup; } cleanup: /* 清理域 */ iommu_test_free_domain(handle, domain_id); if (ret == IOMMU_TEST_SUCCESS) printf("Basic Test: ALL PASSED\n\n"); return ret; } int iommu_test_run_isolation_test(struct iommu_test_handle *handle) { uint32_t domain_a, domain_b; int ret; uint64_t iova_a = 0x2000; uint64_t iova_b = 0x3000; uint64_t size = 4096; uint8_t pattern_a[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE}; uint8_t pattern_b[8] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; printf("Running Domain Isolation Test...\n"); /* 1. 分配两个域 */ ret = iommu_test_alloc_domain(handle, &domain_a, 0); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot allocate domain A\n"); return ret; } ret = iommu_test_alloc_domain(handle, &domain_b, 0); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot allocate domain B\n"); iommu_test_free_domain(handle, domain_a); return ret; } /* 2. 在每个域中映射不同的IOVA */ ret = iommu_test_map(handle, domain_a, iova_a, size, IOMMU_READ | IOMMU_WRITE); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot map in domain A\n"); goto cleanup; } ret = iommu_test_map(handle, domain_b, iova_b, size, IOMMU_READ | IOMMU_WRITE); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot map in domain B\n"); goto cleanup; } printf(" Created domains A(%u) and B(%u) with separate mappings\n", domain_a, domain_b); /* 3. 测试域A的访问 */ ret = iommu_test_trigger_dma(handle, 0, iova_a, 8, DMA_TO_DEVICE, pattern_a, 8); if (ret != IOMMU_TEST_SUCCESS) { printf(" FAIL: Domain A access failed\n"); goto cleanup; } printf(" PASS: Domain A access succeeded\n"); /* 4. 尝试从域A访问域B的IOVA(应该失败) */ ret = iommu_test_trigger_dma(handle, 0, iova_b, 8, DMA_TO_DEVICE, pattern_b, 8); if (ret == IOMMU_TEST_IOMMU_FAULT) { printf(" PASS: Domain A cannot access Domain B's IOVA\n"); } else { printf(" FAIL: Domain isolation broken - A accessed B's memory\n"); ret = IOMMU_TEST_ERROR; goto cleanup; } /* 5. 测试域B的访问 */ ret = iommu_test_trigger_dma(handle, 0, iova_b, 8, DMA_TO_DEVICE, pattern_b, 8); if (ret != IOMMU_TEST_SUCCESS) { printf(" FAIL: Domain B access failed\n"); goto cleanup; } printf(" PASS: Domain B access succeeded\n"); ret = IOMMU_TEST_SUCCESS; cleanup: iommu_test_free_domain(handle, domain_a); iommu_test_free_domain(handle, domain_b); if (ret == IOMMU_TEST_SUCCESS) printf("Isolation Test: ALL PASSED\n\n"); return ret; } int iommu_test_run_performance_test(struct iommu_test_handle *handle, uint32_t iterations, size_t buffer_size) { uint32_t domain_id; int ret; uint64_t total_transactions = 0; uint64_t avg_latency = 0; uint64_t iova = 0x4000; clock_t start, end; double total_time; printf("Running Performance Test (%u iterations, %zu buffer)...\n", iterations, buffer_size); /* 分配域 */ ret = iommu_test_alloc_domain(handle, &domain_id, 0); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot allocate domain\n"); return ret; } /* 映射内存 */ ret = iommu_test_map(handle, domain_id, iova, buffer_size, IOMMU_READ | IOMMU_WRITE); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot map memory\n"); iommu_test_free_domain(handle, domain_id); return ret; } /* 开始性能测试 */ ret = iommu_test_start_perf(handle); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot start perf test\n"); goto cleanup; } start = clock(); /* 执行多次映射/解除映射操作 */ for (uint32_t i = 0; i < iterations; i++) { /* 模拟DMA操作 */ uint8_t pattern[8] = {i & 0xFF, (i >> 8) & 0xFF, (i >> 16) & 0xFF, (i >> 24) & 0xFF, 0xAA, 0xBB, 0xCC, 0xDD}; ret = iommu_test_trigger_dma(handle, 0, iova + (i % buffer_size), 8, DMA_TO_DEVICE, pattern, 8); if (ret != IOMMU_TEST_SUCCESS && ret != IOMMU_TEST_IOMMU_FAULT) { printf(" Warning: DMA operation %u failed\n", i); } } end = clock(); total_time = ((double)(end - start)) / CLOCKS_PER_SEC; /* 停止性能测试并获取结果 */ ret = iommu_test_stop_perf(handle, &total_transactions, &avg_latency); if (ret != IOMMU_TEST_SUCCESS) { printf("FAIL: Cannot stop perf test\n"); goto cleanup; } printf(" Results:\n"); printf(" Total transactions: %lu\n", total_transactions); printf(" Average latency: %lu ns\n", avg_latency); printf(" Total test time: %.3f seconds\n", total_time); printf(" Throughput: %.2f ops/sec\n", iterations / total_time); if (avg_latency < 1000000) { /* 小于1ms */ printf(" PASS: Performance acceptable\n"); ret = IOMMU_TEST_SUCCESS; } else { printf(" WARNING: High latency detected\n"); ret = IOMMU_TEST_ERROR; } cleanup: iommu_test_unmap(handle, domain_id, iova, buffer_size); iommu_test_free_domain(handle, domain_id); if (ret == IOMMU_TEST_SUCCESS) printf("Performance Test: COMPLETED\n\n"); return ret; } ``` 3.3 用户空间命令行工具 (userspace/iommu_test_cmd.c) ```c #include "iommu_test_lib.h" #include #include #include #include static void print_usage(const char *program_name) { printf("Usage: %s [OPTIONS] COMMAND\n", program_name); printf("\nCommands:\n"); printf(" basic Run basic IOMMU functionality test\n"); printf(" isolation Run domain isolation test\n"); printf(" perf [iter] Run performance test (default: 10000 iterations)\n"); printf(" all Run all tests\n"); printf("\nOptions:\n"); printf(" -d, --debug Enable debug output\n"); printf(" -h, --help Show this help message\n"); printf(" -s, --size N Buffer size for tests (default: 4096)\n"); } int main(int argc, char *argv[]) { int opt; int debug = 0; size_t buffer_size = 4096; uint32_t perf_iterations = 10000; const char *command = NULL; static struct option long_options[] = { {"debug", no_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"size", required_argument, 0, 's'}, {0, 0, 0, 0} }; while ((opt = getopt_long(argc, argv, "dhs:", long_options, NULL)) != -1) { switch (opt) { case 'd': debug = 1; break; case 'h': print_usage(argv[0]); return 0; case 's': buffer_size = strtoul(optarg, NULL, 0); break; default: print_usage(argv[0]); return 1; } } if (optind < argc) { command = argv[optind]; } else { fprintf(stderr, "Error: No command specified\n"); print_usage(argv[0]); return 1; } /* 打开测试句柄 */ struct iommu_test_handle *handle = iommu_test_open(debug); if (!handle) { fprintf(stderr, "Error: Cannot open IOMMU test device\n"); fprintf(stderr, "Make sure kernel module is loaded: sudo insmod kernel/iommu_test_drv.ko\n"); return 1; } printf("IOMMU Functional Test Suite\n"); printf("============================\n\n"); int ret = 0; if (strcmp(command, "basic") == 0) { ret = iommu_test_run_basic_test(handle); } else if (strcmp(command, "isolation") == 0) { ret = iommu_test_run_isolation_test(handle); } else if (strcmp(command, "perf") == 0) { if (optind + 1 < argc) { perf_iterations = strtoul(argv[optind + 1], NULL, 0); } ret = iommu_test_run_performance_test(handle, perf_iterations, buffer_size); } else if (strcmp(command, "all") == 0) { ret = iommu_test_run_basic_test(handle); if (ret == 0) { ret = iommu_test_run_isolation_test(handle); } if (ret == 0) { ret = iommu_test_run_performance_test(handle, perf_iterations, buffer_size); } } else { fprintf(stderr, "Error: Unknown command '%s'\n", command); ret = 1; } iommu_test_close(handle); if (ret == 0) { printf("\nAll tests completed successfully!\n"); return 0; } else { printf("\nSome tests failed!\n"); return 1; } } ``` 3.4 用户空间Makefile ```makefile # userspace/Makefile CC = gcc CFLAGS = -Wall -Wextra -O2 -g LDFLAGS = TARGET = iommu_tester LIBRARY = libiommu_test.a SRCS = iommu_test_lib.c iommu_test_cmd.c OBJS = $(SRCS:.c=.o) all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBRARY): iommu_test_lib.o ar rcs $@ $^ %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f $(TARGET) $(LIBRARY) $(OBJS) install: $(TARGET) sudo cp $(TARGET) /usr/local/bin/ uninstall: sudo rm -f /usr/local/bin/$(TARGET) .PHONY: all clean install uninstall ``` 四、使用示例 4.1 编译和安装 ```bash # 编译内核模块 cd kernel make sudo insmod iommu_test_drv.ko # 编译用户空间工具 cd ../userspace make sudo make install # 检查设备是否创建成功 ls -l /dev/iommu_test cat /sys/kernel/debug/iommu_test/iova_mappings ``` 4.2 运行测试 bash ## 运行基本测试 sudo iommu_tester basic ## 运行域隔离测试 sudo iommu_tester isolation ## 运行性能测试(100000次迭代) sudo iommu_tester perf 100000 ## 运行所有测试(带调试输出) sudo iommu_tester -d all ## 查看debugfs信息 cat /sys/kernel/debug/iommu_test/perf_transactions 4.3 输出示例 IOMMU Functional Test Suite ============================ Running Basic IOMMU Test... PASS: DMA write with valid mapping succeeded PASS: IOMMU correctly blocked DMA after unmap Basic Test: ALL PASSED Running Domain Isolation Test... Created domains A(0) and B(1) with separate mappings PASS: Domain A access succeeded PASS: Domain A cannot access Domain B's IOVA PASS: Domain B access succeeded Isolation Test: ALL PASSED Running Performance Test (10000 iterations, 4096 buffer)... Results: Total transactions: 10000 Average latency: 542 ns Total test time: 0.127 seconds Throughput: 78740.16 ops/sec PASS: Performance acceptable Performance Test: COMPLETED All tests completed successfully! 五、扩展和高级功能 要扩展此测试框架以支持真实硬件,需要: 1. 真实设备集成:修改内核模块以支持真实的PCIe设备 2. ATS/PRI测试:添加对PCIe高级功能的测试 3. 中断重映射测试:验证中断隔离功能 4. 多设备并发测试:测试多个设备同时访问的隔离性 5. 虚拟化测试:与VFIO集成,测试设备直通功能 6. 安全性测试:尝试各种攻击向量,验证IOMMU的安全防护 这个实现提供了一个完整的、可工作的IOMMU测试框架基础。 图片

相关推荐
阿巴~阿巴~17 小时前
从IP到MAC,从内网到公网:解密局域网通信与互联网连接的完整路径
服务器·网络·网络协议·架构·智能路由器·tcp·arp
de之梦-御风17 小时前
【视频投屏】最小可用(MVP)局域网投屏”开源项目架构
架构·开源·音视频
时兮兮时17 小时前
Linux 服务器后台任务生存指南
linux·服务器·笔记
dz小伟17 小时前
从用户空间open()到驱动open()的完整调用链深度解析
linux
无心水17 小时前
【分布式利器:腾讯TSF】3、服务注册发现深度解析:构建动态弹性的微服务网络
网络·分布式·微服务·架构·分布式利器·腾讯tsf·分布式利器:腾讯tsf
DeeplyMind17 小时前
第4章: MMU notifier内核实现机制
linux·驱动开发·mmu·mmu notifier
摸鱼仙人~17 小时前
RAG 系统中的 TOC Enhance:用“目录增强”提升检索与生成效果
linux·运维·服务器
xingzhemengyou117 小时前
Linux dmesg 查看系统启动日志
linux
华如锦17 小时前
一.2部署——大模型服务快速部署vLLM GPU 安装教程 (Linux)
java·linux·运维·人工智能·后端·python·vllm