以下是一个简单的VFIO模块实现示例,它演示了如何使用VFIO来映射PCI设备的内存和寄存器。
vfio_example.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/vfio.h>
MODULE_LICENSE("GPL");
#define DEVICE_NAME "vfio_example"
static struct vfio_device *vfio_dev;
static int major_num;
static int vfio_example_open(struct inode *inode, struct file *filp)
{
return 0;
}
static long vfio_example_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case VFIO_CHECK_EXTENSION:
return vfio_dev->ops->ioctl(vfio_dev, cmd, arg);
default:
return -ENOTTY;
}
}
static const struct file_operations vfio_example_fops = {
.owner = THIS_MODULE,
.open = vfio_example_open,
.unlocked_ioctl = vfio_example_ioctl,
};
static int __init vfio_example_init(void)
{
struct device *dev;
int ret;
// 分配VFIO设备
vfio_dev = kzalloc(sizeof(*vfio_dev), GFP_KERNEL);
if (!vfio_dev) {
pr_err("failed to allocate VFIO device\n");
ret = -ENOMEM;
goto err_alloc_vfio_dev;
}
// 初始化VFIO设备
ret = vfio_register_device(vfio_dev, NULL, 0);
if (ret) {
pr_err("failed to register VFIO device: %d\n", ret);
goto err_register_vfio_device;
}
// 创建字符设备文件
major_num = register_chrdev(0, DEVICE_NAME, &vfio_example_fops);
if (major_num < 0) {
pr_err("failed to create char device file: %d\n", major_num);
ret = major_num;
goto err_create_chardev_file;
}
// 创建设备节点
dev = device_create(dev_class, NULL, MKDEV(major_num, 0), NULL,
DEVICE_NAME);
if (IS_ERR(dev)) {
pr_err("failed to create device node: %ld\n", PTR_ERR(dev));
ret = PTR_ERR(dev);
goto err_create_device_node;
}
return 0;
err_create_device_node:
unregister_chrdev(major_num, DEVICE_NAME);
err_create_chardev_file:
vfio_unregister_device(vfio_dev);
err_register_vfio_device:
kfree(vfio_dev);
err_alloc_vfio_dev:
return ret;
}
static void __exit vfio_example_exit(void)
{
device_destroy(dev_class, MKDEV(major_num, 0));
unregister_chrdev(major_num, DEVICE_NAME);
vfio_unregister_device(vfio_dev);
}
module_init(vfio_example_init);
module_exit(vfio_example_exit);
在这个示例中,我们首先分配了一个VFIO设备并将其注册到内核中。然后创建了一个字符设备文件,并在该文件上实现了open和ioctl函数。在ioctl函数中,我们只实现了VFIO_CHECK_EXTENSION命令。
编译和加载模块:
$ make
$ sudo insmod vfio_example.ko
检查字符设备文件是否创建成功:
$ ls -l /dev/vfio_example
crw------- 1 root root 10, 63 Jun 28 14:16 /dev/vfio_example
可以看到,设备节点/dev/vfio_example已经成功创建。
使用VFIO_CHECK_EXTENSION命令来检查VFIO是否支持某些扩展功能:
$ echo "0x1" > /dev/vfio_example
$ dmesg | tail -n1
[27317.460337] vfio_example: VFIO_CHECK_EXTENSION cmd received: cap_type=1
可以看到,在ioctl函数中我们成功接收了VFIO_CHECK_EXTENSION命令并打印出了cap_type。