在 Qemu 上编写用于 DMA 传输的 PCI 驱动程序

Pro*_*roy 5 qemu linux-device-driver dma pci

我正在 Qemu 上编写 PCI 设备,并在来宾操作系统中编写驱动程序(LKM)。虽然 Qemu 提供了一个示例 PCI 设备eduedu.txtedu.c)及其发行版,但我在编写内核模块来进行 DMA 传输时遇到了问题。这里介绍了一个基本的驱动程序,但它不支持 DMA。

我正在跟踪 link 和this的实现。我尝试将缓冲区从 IRQ 处理程序传输到 PCI 设备。设备可以读取数据(pci_dma_read),但我没有得到我应该接收的正确数据。这是进行 DMA 传输的代码段:

static int write_to_HyPerf(void *dev, void* addr, uint32_t size)
{
    /* ----------------------------------------------------------------------------
    * PCI address 'addr':
    * addr     -> DMA source address
    * 0x40000  -> DMA destination address
    * 100      -> DMA transfer count
    * 1        -> DMA command register
    * while (DMA command register & 1)
    *--------------------------------------------------------------------------------
    */
    iowrite32((u32 *)dma_handle_to_device, mmio + IO_DMA_SRC);
    iowrite32(DMA_START, mmio + IO_DMA_DST);
    iowrite32((u32 *)size, mmio + IO_DMA_XCNT);
    iowrite32(DMA_CMD | DMA_IRQ, mmio + IO_DMA_CMD);
}
Run Code Online (Sandbox Code Playgroud)

我还使用dma_alloc_coherent设置一致映射。

vaddr_to_device = dma_alloc_coherent(&(dev->dev), DMA_SIZE, &dma_handle_to_device, GFP_ATOMIC);
Run Code Online (Sandbox Code Playgroud)

完整的代码可以在这里找到。我究竟做错了什么?

Dor*_*cus 0

可能是你的驱动程序有问题。

在这种情况下,您可以使用: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/master/kernel_modules/qemu_edu.c

您可以使用此脚本中的 dd 命令从您的设备写入和读取: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/master/rootfs_overlay/lkmc/qemu_edu.sh

然后您所需要做的就是将所需的 dma 值写入正确的地址,如 edu.c 代码中所示:

    case 0x80:
        dma_rw(edu, false, &val, &edu->dma.src, false);
        break;
    case 0x88:
        dma_rw(edu, false, &val, &edu->dma.dst, false);
        break;
    case 0x90:
        dma_rw(edu, false, &val, &edu->dma.cnt, false);
        break;
    case 0x98:
        dma_rw(edu, false, &val, &edu->dma.cmd, false);
        break;
Run Code Online (Sandbox Code Playgroud)