C Linux内核:AXI互连,在哪里以及如何处理?

C Linux内核:AXI互连,在哪里以及如何处理?,c,linux,linux-kernel,linux-device-driver,hardware,C,Linux,Linux Kernel,Linux Device Driver,Hardware,我正在为我的公司编写一个Linux驱动程序,以便将我们的硬件移植到GNU/Linux桌面。我根本不是一个硬件爱好者,我正在努力理解内核和硬件之间的通信是如何进行的 我们基本上有一个AXI互连,其中一些IP是有线连接的(我们使用的是运行Linux的Xilinx板) 我已经能够向硬件发送请求,它工作得很好,但我觉得我遗漏了一些东西。 在内核中,由于ioremap(),我将物理地址映射到虚拟地址,并且我自己实现了读/写操作,如下所示: static void\uu iomem*邮箱; 静态ssize\

我正在为我的公司编写一个Linux驱动程序,以便将我们的硬件移植到GNU/Linux桌面。我根本不是一个硬件爱好者,我正在努力理解内核和硬件之间的通信是如何进行的

我们基本上有一个AXI互连,其中一些IP是有线连接的(我们使用的是运行Linux的Xilinx板)

我已经能够向硬件发送请求,它工作得很好,但我觉得我遗漏了一些东西。 在内核中,由于
ioremap()
,我将物理地址映射到虚拟地址,并且我自己实现了读/写操作,如下所示:

static void\uu iomem*邮箱;
静态ssize\u t邮箱读取(结构文件*filp,字符*user*buf,大小\u t计数,loff\u t*f\u pos)
{
ssize_t retval=0;
uint32_t mlb_数据=0;
if(向下可中断(&sem))
返回-ERESTARTSYS;
//*f_pos必须是4的倍数
//*f_位置必须在边界内
//计数必须为4(邮箱一次仅支持读取4个字节)
如果(*f|u pos%4 |*f|pos>=邮箱大小|计数!=大小(mlb|u数据)){
retval=-EINVAL;
出去;
}
mlb_data=readl(邮箱+*f_位置);
if(复制到用户(buf和mlb数据,计数)){
retval=-EFAULT;
出去;
}
*f_pos+=计数;
retval=计数;
输出:
up&sem;
返回返回;
}
int邮箱初始化(开发设备)
{
邮箱=ioremap\u nocache(MLB\u基本地址,邮箱大小);
如果(!邮箱){
printk(KERN_ERR DRIVER_NAME):无法映射邮箱,ioremap失败。\n“;
返回错误;
}
返回0;
}
在用户端,我尝试这样读/写:

int dev_fd=open(“/dev/”驱动程序名,O_RDWR);
如果(dev_fd<0)返回任何值;
int数据;
pread(dev_fd和data,sizeof data,0);
关闭(dev_fd);
它工作得很好,但我不明白它怎么会这么简单,所有的AXI东西都在哪里处理?我认为这是我必须要做的事情,但我惊讶地发现一切都已经很好了

我发现一切都是透明的,这真的很好,但问题是我想实现一些错误处理,而我不知道如何去做

例如,如果我试图使用(内部使用)从不受支持的地址读取,我会得到一个总线错误:

root@petalinux:~#devmem 0xCAFEBABE
总线错误
但是如果我尝试通过我自己的角色设备做同样的事情,它就会挂起。似乎没有收到任何SIGBUS

我不确定我是否说得很清楚,所以这里是我的两个问题:

  • Linux内核中的AXI在哪里处理?它可能是Xilinx驱动程序“覆盖”了我的吗
  • 如何通过向userland应用程序发送SIGBUS来处理像
    /dev/mem
    这样的硬件故障
  • 查看Xilinx AXI以太网驱动程序(),设置IOEM cookie(
    mailbox
    )会导致
    readl()
    writel()
    memcpy\u fromio()
    等正确处理访问

    如何在硬件级别执行此操作的详细信息取决于硬件体系结构。例如,mach-ipx4xx使用宏来确定(通过
    inl()
    )还是/应该使用

  • loff\u t
    是有符号的,当偏移量无效时,返回-EFAULT而不是-EINVAL可能更有意义:

    //*f_位置必须在范围内
    如果(*f|pos<0 | |*f|pos>=邮箱大小){
    retval=-EFAULT;
    出去;
    }
    //*f_pos必须是4的倍数,并且
    //计数必须为4(邮箱一次仅支持读取4个字节)
    if((*f_位置&3)|计数!=mlb_数据的大小){
    retval=-EINVAL;
    出去;
    }
    
  • >P>而不是<代码> Read()>代码>,考虑使用更新的<代码> IORAD32()/<代码>(或<代码> IORAD22BE())/>代码>,如果设备总是使用大Endiad字节排序)。有关详细信息,请参阅

    在许多体系结构上,
    ioread32()
    只调用
    readl()
    ,因此这不是一个功能更改;这更多的是通过保持当前推荐的内部内核接口,更易于长期维护

    (关于“[某些]体系结构不能使用此通用接口”的评论是针对体系结构维护人员的,他们不能仅仅依赖于通用接口,而是需要在特定于arch的文件中为其硬件体系结构实现宏。驱动程序开发人员可以依赖这些宏的可用性和正常工作。)

  • 在这里提升SIGBUS并没有真正意义,因为用户空间并不试图直接访问内存,而是使用一个系统调用;返回-EFAULT在这里绝对更合适。但是,要提高SIGBUS,您需要

    struct内核\u siginfo;
    清除_siginfo(&info);//重要!这样就不会泄露到用户空间。
    info.si_signo=SIGBUS;
    info.si_code=BUS_ADRALN;//原因,请参见include/uapi/asm generic/siginfo.h
    info.si_errno=0;
    info.si_addr=addr;//错误地址
    info.si_addr_lsb=lsb;//地址的最低有效位
    强制信号信息(&info);
    

  • 我希望这会有所帮助,并且即使只有少数用户,您也会将GPL驱动程序推到上游。Greg KH过去尤其在这方面提供了帮助。

    我想,就Linux而言,“AXI东西”只包括读取/写入邮箱寄存器,其余的都是在硬件逻辑中完成的。非常感谢你令人惊讶的回答,我没想到会有这么好的回答!如果你愿意,我还有几个问题要问你:在ARM64上,这是[]数组中的第16个条目。发生时,调用do_sea()函数;那个叫。如果导致故障的代码在用户空间中运行,则该进程将被终止;但是如果代码在内核中,您将得到一个oops。由于arm64上的数组是静态常量,我们无法修改它。您的用户空间