我正在查看QEMU的edu device(source),它提供了一个基本的"教育" QEMU中的PCI设备,可以从Linux等QEMU客户端作为PCI设备访问。
我一直试图通过UIO PCI通用驱动程序使用UIO驱动程序(用户空间I / O),以便更好地了解QEMU和Linux中的PCI设备。
我的总体目标是为FPGA实现Linux驱动程序。 FPGA作为PCI-E器件连接到ARM Cortex-A53 CPU,提供几个不同的存储器块,这些存储器将被视为器件配置的寄存器。我最初使用x86_64 QEMU熟悉PCI驱动程序,希望是UIO。注意:我已经向我建议vfio,但我相信这依赖于IOMMU的支持,而我目前的平台上并不确定。
我在内存区域映射方面遇到了一些麻烦。 UIO PCI驱动程序(我认为)意味着在/sys/class/uio/uio0/map
为每个可寻址区域创建条目,但据我所知,当UIO驱动程序绑定到edu时,没有自动检测或设置区域设备
我开始新编译的QEMU(./configure --target-list=x86_64-softmmu
),其中包含yocto生成的#34;相当标准的#34; Linux 4.9 x86_64发行版:
$ ./x86_64-softmmu/qemu-system-x86_64 --device edu -m 512 -nographic -serial mon:stdio -append 'console=ttyS0 root=/dev/hda' -kernel bzImage -hda image-qemu.ext3
然后在guest虚拟机中检测到edu PCI设备:
# lspci
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
00:02.0 VGA compatible controller: Device 1234:1111 (rev 02)
00:03.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)
00:04.0 Unclassified device [00ff]: Device 1234:11e8 (rev 10)
加载uio_pci_generic
模块并将其绑定到edu设备:
# modprobe uio_pci_generic
# echo "1234 11e8" > /sys/bus/pci/drivers/uio_pci_generic/new_id
# ls -l /sys/bus/pci/devices/0000\:00\:04.0/driver
lrwxrwxrwx 1 root root 0 Mar 15 01:50 /sys/bus/pci/devices/0000:00:04.0/driver -> ../../../bus/pci/drivers/uio_pci_generic
仔细查看设备,注意内存地址fea00000
:
# lspci -v -s 00:04.0
00:04.0 Unclassified device [00ff]: Device 1234:11e8 (rev 10)
Subsystem: Red Hat, Inc Device 1100
Flags: fast devsel, IRQ 10
Memory at fea00000 (32-bit, non-prefetchable) [size=1M]
Capabilities: [40] MSI: Enable- Count=1/1 Maskable- 64bit+
Kernel driver in use: uio_pci_generic
我从源代码构建了lsuio:
# ./lsuio -m -v
uio0: name=uio_pci_generic, version=0.01.0, events=0
Device attributes:
vendor=0x1234
uevent=DRIVER=uio_pci_generic
subsystem_vendor=0x1af4
subsystem_device=0x1100
resource=0x00000000fea00000 0x00000000feafffff 0x0000000000040200
msi_bus=1
modalias=pci:v00001234d000011E8sv00001AF4sd00001100bc00scFFi00
local_cpus=1
local_cpulist=0
irq=10
enable=1
driver_override=(null)
dma_mask_bits=32
device=0x11e8
d3cold_allowed=0
consistent_dma_mask_bits=32
config=4è
class=0x00ff00
broken_parity_status=0
# ls /sys/class/uio/uio0/ -l
total 0
-r--r--r-- 1 root root 4096 Mar 15 01:53 dev
lrwxrwxrwx 1 root root 0 Mar 15 01:53 device -> ../../../0000:00:04.0
-r--r--r-- 1 root root 4096 Mar 15 01:53 event
-r--r--r-- 1 root root 4096 Mar 15 01:53 name
drwxr-xr-x 2 root root 0 Mar 15 01:53 power
lrwxrwxrwx 1 root root 0 Mar 15 01:53 subsystem -> ../../../../../class/uio
-rw-r--r-- 1 root root 4096 Mar 15 01:22 uevent
-r--r--r-- 1 root root 4096 Mar 15 01:53 version
根据这一点,应该有一个可映射的区域,从0xfea00000
开始我认为,但没有" map"目录出现,我还没有找到原因。尝试访问/dev/uio0
(读取或mmap)会导致错误22:"无效参数"。打开文件并扫描到末尾显示块设备的大小为零。
首先,我是否需要手动创建这些区域映射,或者UIO驱动程序是否应自动设置这些区域映射? edu设备是否需要做一些额外的事情才能实现这一目标?
其次,是否有其他QEMU PCI设备可以与UIO配合使用?理想情况下,有一个有效的Linux驱动程序,所以我可以尝试理解QEMU设备端和相应的Linux驱动程序端。
在最后一点上,是否有人知道edu设备有效的Linux驱动程序?
答案 0 :(得分:0)
事实证明documentation有点含糊不清,至少让我和其他人混淆:
此long and windy thread说明ui_pci_generic
驱动程序实际上并未将PCI BAR区域映射到maps
目录。相反,目的是使用标准的PCI sysfs接口:
因此,我可以通过/sys/class/uio/uio0/device/resource0
的mmap访问PCI设备的内存。
但是,尝试对/dev/uio0
执行阻塞读取仍然会导致“无效参数”错误,因此我还不确定如何使用此sysfs接口等待或处理中断。
答案 1 :(得分:0)
要添加到OP答案中,似乎/dev/uio0
用于接收和计数uio_pci_generic
模块上的中断。
example code of using uio_pci_generic
显示如下:
uiofd = open("/dev/uio0", O_RDONLY);
...
/* Wait for next interrupt. */
err = read(uiofd, &icount, 4);
icount
是接收到的中断数。
使用qemu's edu
device时,可以使用resource0
访问映射的IO,并使用/dev/uio0
等待中断。
这是一个用户空间示例(上述example code的扩展),该示例使用uio_pci_generic
来写入和读取edu
设备的“卡片活跃度检查”,该设备反转输入,并通过写入“中断引发寄存器”来触发edu
中断:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <sys/mman.h>
#define EDU_IO_SIZE 0x100
#define EDU_CARD_VERSION_ADDR 0x0
#define EDU_CARD_LIVENESS_ADDR 0x1
#define EDU_RAISE_INT_ADDR 0x18
#define EDU_CLEAR_INT_ADDR 0x19
int main()
{
int uiofd;
int configfd;
int bar0fd;
int resetfd;
int err;
int i;
unsigned icount;
unsigned char command_high;
volatile uint32_t *bar0;
uiofd = open("/dev/uio0", O_RDWR);
if (uiofd < 0) {
perror("uio open:");
return errno;
}
configfd = open("/sys/class/uio/uio0/device/config", O_RDWR);
if (configfd < 0) {
perror("config open:");
return errno;
}
/* Read and cache command value */
err = pread(configfd, &command_high, 1, 5);
if (err != 1) {
perror("command config read:");
return errno;
}
command_high &= ~0x4;
/* Map edu's MMIO */
bar0fd = open("/sys/class/uio/uio0/device/resource0", O_RDWR);
if (bar0fd < 0) {
perror("bar0fd open:");
return errno;
}
/* mmap the device's BAR */
bar0 = (volatile uint32_t *)mmap(NULL, EDU_IO_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, bar0fd, 0);
if (bar0 == MAP_FAILED) {
perror("Error mapping bar0!");
return errno;
}
fprintf(stdout, "Version = %08X\n", bar0[EDU_CARD_VERSION_ADDR]);
/* Test the invertor function */
i = 0x12345678;
bar0[EDU_CARD_LIVENESS_ADDR] = i;
fprintf(stdout, "Inversion: %08X --> %08X\n", i, bar0[EDU_CARD_LIVENESS_ADDR]);
/* Clear previous interrupt */
bar0[EDU_CLEAR_INT_ADDR] = 0xABCDABCD;
/* Raise an interrupt */
bar0[EDU_RAISE_INT_ADDR] = 0xABCDABCD;
for(i = 0;; ++i) {
/* Print out a message, for debugging. */
if (i == 0)
fprintf(stderr, "Started uio test driver.\n");
else
fprintf(stderr, "Interrupts: %d\n", icount);
/****************************************/
/* Here we got an interrupt from the
device. Do something to it. */
/****************************************/
/* Re-enable interrupts. */
err = pwrite(configfd, &command_high, 1, 5);
if (err != 1) {
perror("config write:");
break;
}
/* Clear previous interrupt */
bar0[EDU_CLEAR_INT_ADDR] = 0xABCDABCD;
/* Raise an interrupt */
bar0[EDU_RAISE_INT_ADDR] = 0xABCDABCD;
/* Wait for next interrupt. */
err = read(uiofd, &icount, 4);
if (err != 4) {
perror("uio read:");
break;
}
}
return errno;
}
结果看起来像这样:
Version = 010000ED
Inversion: 12345678 --> EDCBA987
Started uio test driver.
Interrupts: 3793548
Interrupts: 3793549
Interrupts: 3793550
Interrupts: 3793551
Interrupts: 3793552
Interrupts: 3793553
Interrupts: 3793554
Interrupts: 3793555
Interrupts: 3793556
...