首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >通过用户空间I/O (UIO) Linux驱动程序与QEMU edu设备接口

通过用户空间I/O (UIO) Linux驱动程序与QEMU edu设备接口
EN

Stack Overflow用户
提问于 2018-03-16 05:03:37
回答 2查看 1.2K关注 1票数 3

我正在研究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):

代码语言:javascript
运行
复制
$ ./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设备:

代码语言:javascript
运行
复制
# 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设备:

代码语言:javascript
运行
复制
# 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

代码语言:javascript
运行
复制
# 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

代码语言:javascript
运行
复制
# ./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驱动程序是有效的?

EN

回答 2

Stack Overflow用户

发布于 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接口等待或处理中断。

票数 2
EN

Stack Overflow用户

发布于 2020-02-20 15:51:05

除了OP回答之外,似乎还使用了/dev/uio0来接收和计数uio_pci_generic模块上的中断。

example code of using uio_pci_generic显示了以下内容:

代码语言:javascript
运行
复制
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中断:

代码语言:javascript
运行
复制
#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;
}

结果如下所示:

代码语言:javascript
运行
复制
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
...
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49309162

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档