使用vfio-pci映射时无法写入FPGA寄存器

问题描述 投票:0回答:2

我正在尝试使用 vfio-pci 驱动程序将 FPGA 映射到 PCI-e 总线上。我可以正确读取寄存器,但是,当我尝试写入寄存器时,似乎寄存器已被写入并且我可以将其读回,但是,当我停止用于读/写的进程并重新启动它时,我发现寄存器已回滚到原始值。如果我使用 uio-pci 映射 FPGA,一切正常。

这是加载vfio驱动程序的脚本:

modprobe vfio-pci enable_sriov=1 disable_idle_d3=1
VID="10ee"
DID="903f"
DBDF="0000:"`lspci -n | grep -E ${VID}:${DID} | cut -d ' ' -f1`
echo ${DBDF}
ROOT_DBDF="0000:3a:00.0"
readlink /sys/bus/pci/devices/${DBDF}/iommu_group
GROUP=`readlink /sys/bus/pci/devices/${DBDF}/iommu_group | rev | cut -d '/' -f1 | rev`
echo "GROUP " ${GROUP}
# unbind the ROOT device makes the group viable
echo ${ROOT_DBDF}  > /sys/bus/pci/devices/${ROOT_DBDF}/driver/unbind; sleep 1
echo ${DBDF}  > /sys/bus/pci/devices/${DBDF}/driver/unbind; sleep 1
echo ${VID} ${DID} > /sys/bus/pci/drivers/vfio-pci/remove_id; sleep 1
echo ${VID} ${DID} > /sys/bus/pci/drivers/vfio-pci/new_id; sleep 1
echo 8086 2030 > /sys/bus/pci/drivers/vfio-pci/new_id; sleep 1
ls -l /sys/bus/pci/devices/${DBDF}/iommu_group/devices; sleep 1
chmod 660 /dev/vfio/vfio

然后我使用以下程序来读写寄存器。


#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <linux/vfio.h>

int main(int argc, char** argv)
{

        int container, group, device, i;
        struct vfio_group_status group_status =
                                        { .argsz = sizeof(group_status) };
        struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };
        struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map) };
        struct vfio_device_info device_info = { .argsz = sizeof(device_info) };

        /* Create a new container */
        container = open("/dev/vfio/vfio", O_RDWR);

        if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION)
                printf("Unknown API version\n");
                /* Unknown API version */

        if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU))
                printf("Doesn't support IOMMU driver we want\n");

        /* Open the group */
        group = open("/dev/vfio/69", O_RDWR);

        /* Test the group is viable and available */
        ioctl(group, VFIO_GROUP_GET_STATUS, &group_status);

        if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE))
                printf("Group is not viable\n");
                /* Group is not viable (ie, not all devices bound for vfio) */

        /* Add the group to the container */
        ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);

        /* Enable the IOMMU model we want */
        ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);

        /* Get addition IOMMU info */
        ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);

        /* Allocate some space and setup a DMA mapping */
/*
        dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE,
                        MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
        dma_map.size = 1024 * 1024;
        dma_map.iova = 0;
        dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;

        ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
*/
        /* Get a file descriptor for the device */
        device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:3b:00.0");

        /* Test and setup the device */
        ioctl(device, VFIO_DEVICE_GET_INFO, &device_info);

        printf("NUM REGIONS %d\n", device_info.num_regions);

        struct vfio_region_info regs[64];
        for (i = 0; i < device_info.num_regions; i++) {
                regs[i].argsz = sizeof(struct vfio_region_info);
                regs[i].index = i;

                ioctl(device, VFIO_DEVICE_GET_REGION_INFO, &regs[i]);

                printf("region %d flags %08x offset %lld size %lld\n", i, regs[i].flags, regs[i].offset, regs[i].size);
                /* Setup mappings... read/write offsets, mmaps
                * For PCI devices, config space is a region */
        }

        volatile uint8_t* ptr = mmap(0, regs[0].size, PROT_READ | PROT_WRITE, MAP_SHARED, device, 0);

        printf("addr %p\n", ptr);

        printf("reg 0x38000 %08x\n", *(uint32_t*)(ptr + 0x38000));

        {
                uint32_t ival = *(volatile uint32_t*) (ptr + 0x38008);
                *(volatile uint32_t*) (ptr + 0x38008) = ival + 0x1000;
                printf("%08x\n", *(volatile uint32_t*) (ptr + 0x38008));
        }
        printf("reg 0x38008 %08x\n", *(volatile uint32_t*)(ptr + 0x38008));

}

mmap vfio
2个回答
0
投票

MAP_SHARED 在 mmap 调用中使用,但程序的行为就像使用了 MAP_PRIVATE


0
投票

来自对问题的评论:

问题是,当停止上面的 C 程序并重新启动时,会导致 PCI 上的“某些内容”重置,最终重置 FPGA。 - 安东尼奥·迪巴科

我使用以下组合注意到了这一点:

要调试 FGPA PCIe 重置发生的点:

  • 单步执行VFIO代码;
  • 使用映射到 PCI Express® 内核的 AXI 内存中的JTAG 调试器来监控 FPGA PCIe 内核内部的LTSSM(链路训练和状态机)状态转换

发现在以下情况下使用 VFIO 会导致 FPGA 发生 PCIe 热重置

  1. 执行
    ioctl (VFIO_GROUP_GET_DEVICE_FD)
    打开 PCIe 设备。这是
    VFIO_GROUP_GET_DEVICE_FD
    之前的 LTSSM: 紧接着,这表明已经通过“热重置”状态发生了转换:
  2. close
  3. 返回的
    device_fd
    上调用
    ioctl (VFIO_GROUP_GET_DEVICE_FD)
    。这是
    close
    之前的 LTSSM:
    close 之后,这表明发生了通过 
    Hot Reset
    状态的进一步转换:
  4. 其他一些注意事项是:

查看内核的源代码,
    vfio_pci_open()
  1. 函数调用vfio_pci_enable(),其中有: /* If reset fails because of the device lock, fail this path entirely */ ret = pci_try_reset_function(pdev); if (ret == -EAGAIN) { pci_disable_device(pdev); return ret; } 这意味着根据设计,当 VFIO 设备时,内核将调用
    pci_try_reset_function()
    来重置 PCI 设备功能 Xilinx 提供的
  2. draw_ltssm.tcl
  3. 创建了上面的图片,显示进入了
    Hot Reset
    状态,但没有退出。该脚本报告了一些意外的转换。例如。对于上面的最终图片: $ wish ../test_board/test_board.gen/sources_1/bd/msys/ip/msys_axi_pcie_0_0/msys_axi_pcie_0_0/pcie_debugger/draw_ltssm.tcl Warning: unexpected transition: From: HOT_RESET To : Timeout To Detect Warning: unexpected transition: From: HOT_RESET To : Timeout To Detect 我认为这可能是
    draw_ltssm.tcl
    脚本中的一个错误,它无法处理 LTSSM 状态之间所有可能的转换。尚未调查脚本中的这些警告,但与此问题无关。
    如果用户空间程序发出 
  4. ioctl (VFIO_DEVICE_RESET)
  5. 也生成 PCIe
    热重置
© www.soinside.com 2019 - 2024. All rights reserved.