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

knowledge base:

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

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

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

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

本文提供一个完整的IOMMU功能测试软件设计及其代码实现示例。这是一个实际可编译运行的框架,包含内核模块和用户空间工具。这个完整的测试框架可以全面验证IOMMU的功能、性能、安全性和稳定性。每个测试都有明确的目的、方法和验证标准,可以适应不同硬件平台和测试需求

完整IOMMU测试软件实现

一、项目总览

测试目标

A. 基础功能测试

完整测试目录


请关注微信公众号:tech-holic


B. 安全与隔离测

C. 性能测试

图片

D. 压力与稳定性测试

图片

E. 高级功能测试

图片

F. 合规性与兼容性测试

图片

G. 安全漏洞测试

图片

测试代码流程

图片

图片

项目工程架构

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 <linux/ioctl.h>

#include <linux/types.h>

#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 <linux/module.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/uaccess.h>

#include <linux/slab.h>

#include <linux/iommu.h>

#include <linux/dma-mapping.h>

#include <linux/pci.h>

#include <linux/interrupt.h>

#include <linux/io.h>

#include <linux/mm.h>

#include <linux/highmem.h>

#include <linux/time.h>

#include <linux/debugfs.h>

#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 *domainsMAX_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(&param, 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, &param, 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(&param, 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(&param, 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(&param, 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, &param, 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 <stdint.h>

#include <stdbool.h>



#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 <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/ioctl.h>

#include <sys/mman.h>

#include <errno.h>

#include <time.h>

#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(&param, 0, sizeof(param));

    param.flags = flags;

    

    ret = ioctl(handle->fd, IOMMU_TEST_ALLOC_DOMAIN, &param);

    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(&param, 0, sizeof(param));

    param.domain_id = domain_id;

    param.iova = iova;

    param.size = size;

    param.prot = prot;

    

    ret = ioctl(handle->fd, IOMMU_TEST_MAP, &param);

    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(&param, 0, sizeof(param));

    param.domain_id = domain_id;

    param.iova = iova;

    param.size = size;

    

    ret = ioctl(handle->fd, IOMMU_TEST_UNMAP, &param);

    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(&param, 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, &param);

    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 <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <getopt.h>



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测试框架基础。

图片

相关推荐
vortex53 分钟前
Linux 传统设计哲学:通过调用名区分行为的艺术
linux·运维·网络
wanzehongsheng5 分钟前
光伏凉亭系统架构设计与工程实践探讨
系统架构
毛小茛12 分钟前
系统架构设计师概述
系统架构
嵌入式-老费15 分钟前
esp32开发与应用(esp32-s3的usb转串口功能)
linux·运维·服务器
壮Sir不壮23 分钟前
GO语言——GMP调度模型
linux·开发语言·golang·go·操作系统·线程·协程
一尘之中27 分钟前
基于架构的软件开发方法
学习·架构·ai写作
m0_6932006529 分钟前
VSCode使用ssh remote插件远程连接linux主机
linux·vscode·ssh
筵陌33 分钟前
Linux网络数据链路层
linux·网络
Aurora_Dawn_yy42 分钟前
单机部署数据同步_jdk,mysql,kafka,flink,zookeeper,达梦,starrocks
大数据·linux·starrocks·zookeeper·达梦