前言
在Linux中,有一个新的PCI/PCIe设备需要进行测试,但又不想编写Linux内核驱动程序。事实证明,Linux可以在没有驱动驱动的情况下读取和写入PCI设备的内存空间。Cheers!
Linux为PCI设备提供了一个sysfs接口。从该接口,可以对内存空间进行mmaped,然后进行读写。不需要额外编写的内核驱动参与。
示例
我们可以使用lspci命令来获取关于特定PCI设备的信息。如下:
$ vendor="10ee" # Use your device ID
$ device="7014" # Use your vendor ID
$ lspci -d $vendor:$device -nvv
04:00.0 1180: 10ee:7014
...
Region 0: Memory at f7300000 (32-bit, non-prefetchable) [size=128K]
然后我们可以查看位于/sys/bus/pci/devices/下的sysfs接口,通过lspci输出中的第一位数据给出了设备在总线上的位置,我们可以在遍历sysfs接口时使用这个位置。
$ ls -alF /sys/bus/pci/devices/0000\:04\:00.0/
total 0
drwxr-xr-x 3 root root 0 Jul 1 12:42 ./
drwxr-xr-x 8 root root 0 Jul 1 12:42 ../
-rw-r--r-- 1 root root 4096 Jul 9 12:48 broken_parity_status
-r--r--r-- 1 root root 4096 Jul 1 12:42 class
-rw-r--r-- 1 root root 4096 Jul 9 12:44 config
-r--r--r-- 1 root root 4096 Jul 1 12:42 device
...
-r--r--r-- 1 root root 4096 Jul 1 12:43 resource
-rw------- 1 root root 131072 Jul 1 12:43 resource0
...
-r--r--r-- 1 root root 4096 Jul 1 12:42 vendor
这个输出结果中,有一些有用的文件,如供应商和设备,可以确认我们打开正确的设备。同时,这些文件也有助于不用lspci命令,而以编程方式查找正确的设备。
$ cat /sys/bus/pci/devices/0000\:04\:00.0/vendor
0x10ee
$ cat /sys/bus/pci/devices/0000\:04\:00.0/device
0x7014
回顾lspci命令的输出,我们还可以看到内存资源和地址。这些在sysfs接口中表示为resource0…resourceN。这就是我们用来访问PCI内存空间的方法。
打开resource0文件(根据设备的不同,可以是0以外的数字)。
int fd = open("/sys/bus/pci/devices/0000:04:00.0/resource0", O_RDWR | O_SYNC);
然后使用lspci命令输出中的内存地址和大小来映射该文件。
void* base_address = (void*)0xf7300000;
size_t size = 128 * 1024; // 128K
void* void_memory = mmap(base_address,
size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0);
uint16_t* memory = (uint16_t*)void_memory;
现在,内存提供了对PCI内存空间进行读写的直接访问了。像访问本地内存一样访问:
// Read the value of the first register
uint16_t first_register = memory[0];
// Write a value to the third register
memory[2] = 0x0007;
结语
以上介绍了在Linux中不用编写内核驱动就可以访问PCI设备的内存空间的方法,但这并不是一个完美的方法。首先,我们需要是root才能访问这个内存空间。其次,不能使用设备的任何中断。但对于新设备的基本操作来说,这个方法不需要开发内核模块,可以快速做一些验证,甚至如果不需要中断的情况下,这也可以是软件的一种解决方案,需要root权限的问题也是可以解决的。
评论区