我正在研究QEMU的edu device (source),它在QEMU中提供了一个基本的“教育”PCI设备,可以作为PCI设备从QEMU的来宾系统中进行访问。
我一直在尝试通过UIO通用驱动程序将其与UIO驱动程序(Userspace I/O)配合使用,以此作为练习,以便更好地理解QEMU和Linux中的PCI设备。
我的总体目标是为FPGA实现一个Linux驱动程序。FPGA作为PCI-E设备连接到ARM Cortex-A53 CPU,提供几个不同的内存块,这些内存块将被视为设备配置的寄存器。我最初使用PCI是为了熟悉x86_64驱动程序,希望也能熟悉UIO。注意:有人建议我使用vfio,但我相信这依赖于IOMMU支持,我不确定我的目标平台是否支持IOMMU。
我在内存区域映射方面遇到了一些问题。UIO PCI驱动程序(我认为)是为了在/sys/class/uio/uio0/map
中为每个可寻址区域创建条目,但是据我所知,当UIO驱动程序绑定到edu设备时,没有自动检测或设置区域。
我从一个yocto生成的“相当标准的”Linux4.9 x86_64发行版开始我的新编译的QEMU (./configure --target-list=x86_64-softmmu
):
$ ./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
然后在访客内检测到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驱动程序是有效的?
发布于 2018-03-21 05:39:31
事实证明,documentation有点模棱两可,足以让我自己和另一个人感到困惑:
此long and windy thread说明ui_pci_generic
驱动程序实际上不会将PCI BAR区域映射到maps
目录。相反,其目的是使用标准的PCI sysfs接口:
因此,我能够通过/sys/class/uio/uio0/device/resource0
的mmap访问PCI设备的内存。
然而,尝试在/dev/uio0
上执行阻塞读取仍然会导致“无效参数”错误,所以我还不确定如何使用这个sysfs接口等待或处理中断。
发布于 2020-02-20 15:51:05
除了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
...
https://stackoverflow.com/questions/49309162
复制相似问题