You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SandyBridge平台PCIe驱动64字节写入无数据包发送问题

PCIe内核驱动64字节写入失效问题(Intel SandyBridge平台)

我正在使用gcc 11.3为x86架构Intel SandyBridge平台开发PCIe内核驱动,目标实现单次64字节数据写入。目前32字节写入功能正常,但复制32字节写入代码逻辑实现64字节写入时,处理器未发送任何PCIe数据包。

正常工作的32字节写入代码

已完成内核空间到用户空间的映射,以下代码可成功向PCIe内存写入32字节数据(PCIe内存由配置为64字节负载的Intel FPGA托管):

// data指向长度为8的uint32数组(共32字节)
// addr指向PCIe内存中内核映射的write-combine地址

void write_32(uint8_t *data, uint8_t *addr)
{
    asm volatile (
                  "vmovdqa %[data_lo],  %%ymm1    \n\t"
                  "vmovdqa %%ymm1,      %[addr_lo] \n\t"

                   // 输出
                   : [addr_lo]  "=m"(* addr)

                   // 输入
                   : [data_lo]  "m"(* data)

                   // 破坏列表
                   : "memory"

                  );
}

失效的64字节写入代码

由于SandyBridge平台限制,需通过两次32字节移动实现64字节写入(理想是发送单个64字节负载,退而求其次两个32字节负载也可接受),但调用以下函数时处理器未发送任何PCIe数据包:

// data指向长度为16的uint32数组(共64字节)
// addr指向PCIe内存中内核映射的write-combine地址

static int write_64(uint8_t *data, void __iomem *addr)
{
    // 将数据移动到256位ymm寄存器
    uint8_t *data_upper      = data + 32;
    void __iomem *addr_upper = addr + 32;


    asm volatile (
                 "vmovdqa   %[data_lo], %%ymm3      \n\t"
                 "vmovdqa   %[data_hi], %%ymm4      \n\t"
                 "vmovntdq  %%ymm3,     %[addr_lo]  \n\t"
                 "vmovntdq  %%ymm4,     %[addr_hi]  \n\t"

                // 输出
                 :  [addr_lo]   "=m"(*(volatile uint8_t * __force) addr),
                    [addr_hi]   "=m"(*(volatile uint8_t * __force) addr_upper)

                 // 输入
                 : [data_lo]    "m"(*(volatile const char (*__force)[32]) data),
                   [data_hi]    "m"(*(volatile const char (*__force)[32]) data_upper)
                 // 破坏列表
                 : "ymm3",
                   "ymm4",
                   "memory"
                 
                );
    return 0;
}

内存初始化代码

static int chr_mmap(struct file *filp, struct vm_area_struct *vma)
{
    struct chr_dev_bookkeep *chr_dev_bk;
    struct dev_bookkeep *dev_bk;
    unsigned long len, pfn;
    int ret;
    pgoff_t pgoff;

    len = vma->vm_end - vma->vm_start;
    pgoff = vma->vm_pgoff;
    printk(KERN_DEBUG "Mapping %ld bytes\n", len);
    printk(KERN_DEBUG "Start: %ld   End: %ld\n", vma->vm_start, vma->vm_end);
    chr_dev_bk = filp->private_data;
    dev_bk = chr_dev_bk->dev_bk;

    if ((len == 0) || (len+pgoff) > dev_bk->kmem_info.size) {
        return -EINVAL;
    }
    vma->vm_ops = &intel_fpga_vm_ops;
    vma->vm_flags |= VM_PFNMAP | VM_DONTCOPY | VM_DONTEXPAND;
    //vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
    vma->vm_private_data = dev_bk;
    //PFN: 页帧号

    pfn = __pa(dev_bk->kmem_info.virt_addr + (pgoff<<PAGE_SHIFT))>>PAGE_SHIFT;
    ret = remap_pfn_range(vma, vma->vm_start, pfn, len, vma->vm_page_prot);
    if (ret < 0) {
        printk(KERN_DEBUG "could not remap kernel buffer to user-space.");
        return -ENXIO;
    }

    return 0;
}

已确认信息

  • 待写入的64字节数据十六进制转储与预期一致
  • 地址为32字节对齐,符合要求
  • 注释掉单个vmovntdq语句时,可向PCIe内存64字节字线的高半区或低半区写入一半数据
  • 通过lspci和cat pat_memtype_list确认,PCIe BAR分配的内存区域为write-combine类型,符合要求

单独调用两次移动操作可正常工作(即分别写入低半区和高半区),完整代码的汇编输出符合预期:两次数据移入寄存器,再两次写入内存。开启-O3优化后,处理器仍仅发送包含data_lo的单个32字节写入。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 14:37:37